From: Mario Date: Fri, 13 Nov 2015 14:29:42 +0000 (+1000) Subject: Merge branch 'master' into Mario/bulldozer X-Git-Tag: xonotic-v0.8.2~1654^2~19 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=c741e6b3f012aa525f23e1df30d1d933b383fbc5;hp=8283cec24d38987c03b3a86fa1a9246d7e65842e Merge branch 'master' into Mario/bulldozer --- diff --git a/check-translations.sh b/check-translations.sh index facee4a7bc..a7406532d8 100755 --- a/check-translations.sh +++ b/check-translations.sh @@ -36,7 +36,8 @@ fi if [ x"$mode" = x"txt" ]; then { - echo "en English \"English\"" + item=`grep "^en " languages.txt` + echo "$item" for X in common.*.po; do [ -f "$X" ] || continue if [ -n "$language" ]; then @@ -67,9 +68,9 @@ if [ x"$mode" = x"txt" ]; then if [ "$p" -lt 50 ]; then continue fi - item="$l $l \"$l (0%)\"" + item="$l $l \"$l\" 0%" fi - printf "%s\n" "$item" | sed -e "s/([0-9][0-9]*%)/($p%)/" + printf "%s\n" "$item" | sed -e "s/[0-9][0-9]*%/$p%/" done } | tr '"' '\t' | sort -k3 | tr '\t' '"' fi diff --git a/languages.txt b/languages.txt index a43c6cff46..9f0d1743b1 100644 --- a/languages.txt +++ b/languages.txt @@ -1,19 +1,19 @@ -ast Asturian "Asturianu (60%)" -de German "Deutsch (90%)" -de_CH German "Deutsch (Schweiz) (90%)" -en_AU en_AU "en_AU (77%)" -en English "English" -es Spanish "Español (68%)" -fr French "Français (98%)" -it Italian "Italiano (97%)" -hu Hungarian "Magyar (50%)" -nl Dutch "Nederlands (45%)" -pl Polish "Polski (60%)" -pt Portuguese "Português (42%)" -ro Romanian "Romana (90%)" -fi Finnish "Suomi (35%)" -el Greek "Ελληνική (25%)" -be Belarusian "Беларуская (65%)" -bg Bulgarian "Български (65%)" -ru Russian "Русский (93%)" -uk Ukrainian "Українська (60%)" +ast Asturian "Asturianu" 60% +de German "Deutsch" 90% +de_CH German "Deutsch (Schweiz)" 90% +en English "English" +en_AU English "English (Australia)" 77% +es Spanish "Español" 68% +fr French "Français" 98% +it Italian "Italiano" 97% +hu Hungarian "Magyar" 50% +nl Dutch "Nederlands" 45% +pl Polish "Polski" 60% +pt Portuguese "Português" 42% +ro Romanian "Romana" 90% +fi Finnish "Suomi" 35% +el Greek "Ελληνική" 25% +be Belarusian "Беларуская" 65% +bg Bulgarian "Български" 65% +ru Russian "Русский" 93% +uk Ukrainian "Українська" 60% \ No newline at end of file diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index e20557fa74..fa8f2b0055 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -1,14 +1,24 @@ #include "announcer.qh" +#include "mutators/events.qh" + #include "../common/notifications.qh" #include "../common/stats.qh" bool announcer_1min; bool announcer_5min; +string AnnouncerOption() +{ + string ret = autocvar_cl_announcer; + MUTATOR_CALLHOOK(AnnouncerOption, ret); + ret = ret_string; + return ret; +} + void Announcer_Countdown() { SELFPARAM(); - float starttime = getstatf(STAT_GAMESTARTTIME); + float starttime = STAT(GAMESTARTTIME); float roundstarttime = getstatf(STAT_ROUNDSTARTTIME); if(roundstarttime == -1) { @@ -59,7 +69,7 @@ void Announcer_Countdown() float previous_game_starttime; void Announcer_Gamestart() { - float startTime = getstatf(STAT_GAMESTARTTIME); + float startTime = STAT(GAMESTARTTIME); float roundstarttime = getstatf(STAT_ROUNDSTARTTIME); if(roundstarttime > startTime) startTime = roundstarttime; @@ -68,19 +78,18 @@ void Announcer_Gamestart() { if(time < startTime) { - entity e = find(world, classname, "announcer_countdown"); - if (!e) + static entity announcer_countdown; + if (!announcer_countdown) { - e = spawn(); - e.classname = "announcer_countdown"; - e.think = Announcer_Countdown; + announcer_countdown = new(announcer_countdown); + announcer_countdown.think = Announcer_Countdown; } if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle - if(time > e.nextthink) // don't play it again if countdown was already going + if(time > announcer_countdown.nextthink) // don't play it again if countdown was already going Local_Notification(MSG_ANNCE, ANNCE_PREPARE); - e.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime + announcer_countdown.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime } } @@ -92,12 +101,12 @@ void Announcer_Gamestart() void Announcer_Time() { float timelimit = getstatf(STAT_TIMELIMIT); - float timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time); + float timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time); float warmup_timeleft = 0; if(warmup_stage) if(autocvar_g_warmup_limit > 0) - warmup_timeleft = max(0, autocvar_g_warmup_limit + getstatf(STAT_GAMESTARTTIME) - time); + warmup_timeleft = max(0, autocvar_g_warmup_limit + STAT(GAMESTARTTIME) - time); // 5 minute check if(autocvar_cl_announcer_maptime >= 2) diff --git a/qcsrc/client/announcer.qh b/qcsrc/client/announcer.qh index 64be1433af..314c6602d8 100644 --- a/qcsrc/client/announcer.qh +++ b/qcsrc/client/announcer.qh @@ -3,4 +3,6 @@ void Announcer(); +string AnnouncerOption(); + #endif diff --git a/qcsrc/client/commands/cl_cmd.qc b/qcsrc/client/commands/cl_cmd.qc index bed70f57fc..63e0c56126 100644 --- a/qcsrc/client/commands/cl_cmd.qc +++ b/qcsrc/client/commands/cl_cmd.qc @@ -8,8 +8,7 @@ #include "../autocvars.qh" #include "../defs.qh" -#include "../hud.qh" -#include "../hud_config.qh" +#include "../hud/all.qh" #include "../main.qh" #include "../mapvoting.qh" #include "../miscfunctions.qh" @@ -22,7 +21,7 @@ void DrawDebugModel(entity this) { - if(time - floor(time) > 0.5) + if (time - floor(time) > 0.5) { PolyDrawModel(self); self.drawmask = 0; @@ -45,7 +44,7 @@ void LocalCommand_blurtest(int request) // Anyway, to enable it, just compile the client with -DBLURTEST and then you can use the command. #ifdef BLURTEST - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -66,7 +65,7 @@ void LocalCommand_blurtest(int request) } } #else - if(request) + if (request) { LOG_INFO("Blurtest is not enabled on this client.\n"); return; @@ -76,7 +75,7 @@ void LocalCommand_blurtest(int request) void LocalCommand_boxparticles(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -87,7 +86,7 @@ void LocalCommand_boxparticles(int request, int argc) { int index = stoi(argv(2)); entity own; - if(index <= 0) + if (index <= 0) own = entitybyindex(-index); else own = findfloat(world, entnum, index); @@ -104,7 +103,9 @@ void LocalCommand_boxparticles(int request, int argc) } default: + { LOG_INFO("Incorrect parameters for ^2boxparticles^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 lv_cmd boxparticles effectname own org_from org_to, dir_from, dir_to, countmultiplier, flags\n"); @@ -127,14 +128,14 @@ void LocalCommand_boxparticles(int request, int argc) void LocalCommand_create_scrshot_ent(int request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { string filename = strcat(MapInfo_Map_bspname, "_scrshot_ent.txt"); int fh = fopen(filename, FILE_WRITE); - if(fh >= 0) + if (fh >= 0) { fputs(fh, "{\n"); fputs(fh, strcat("\"classname\" \"info_autoscreenshot\"\n")); @@ -165,20 +166,18 @@ void LocalCommand_create_scrshot_ent(int request) void LocalCommand_debugmodel(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { string modelname = argv(1); - entity debugmodel_entity; - debugmodel_entity = spawn(); + entity debugmodel_entity = new(debugmodel); precache_model(modelname); _setmodel(debugmodel_entity, modelname); setorigin(debugmodel_entity, view_origin); debugmodel_entity.angles = view_angles; debugmodel_entity.draw = DrawDebugModel; - debugmodel_entity.classname = "debugmodel"; return; } @@ -195,14 +194,14 @@ void LocalCommand_debugmodel(int request, int argc) void LocalCommand_handlevote(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { int vote_selection; string vote_string; - if(InterpretBoolean(argv(1))) + if (InterpretBoolean(argv(1))) { vote_selection = 2; vote_string = "yes"; @@ -213,9 +212,9 @@ void LocalCommand_handlevote(int request, int argc) vote_string = "no"; } - if(vote_selection) + if (vote_selection) { - if(uid2name_dialog) // handled by "uid2name" option + if (uid2name_dialog) // handled by "uid2name" option { vote_active = 0; vote_prev = 0; @@ -230,7 +229,9 @@ void LocalCommand_handlevote(int request, int argc) } default: + { LOG_INFO("Incorrect parameters for ^2handlevote^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd handlevote vote\n"); @@ -252,11 +253,11 @@ void HUD_Radar_Show_Maximized(bool doshow, bool clickable); void LocalCommand_hud(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - switch(argv(1)) + switch (argv(1)) { case "configure": { @@ -266,16 +267,23 @@ void LocalCommand_hud(int request, int argc) case "quickmenu": { - if(QuickMenu_IsOpened()) + if (argv(2) == "help") + { + LOG_INFO(" quickmenu [[default | file | \"\"] submenu]\n"); + LOG_INFO("Called without options (or with \"\") loads either the default quickmenu or a quickmenu file if hud_panel_quickmenu_file is set to a valid filename.\n"); + LOG_INFO("A submenu name can be given to open the quickmenu directly in a submenu; it requires to specify 'default', 'file' or '\"\"' option.\n"); + return; + } + if (QuickMenu_IsOpened()) QuickMenu_Close(); else - QuickMenu_Open(argv(2), argv(3)); // mode, submenu + QuickMenu_Open(argv(2), argv(3)); // mode, submenu return; } case "minigame": { - if(HUD_MinigameMenu_IsOpened()) + if (HUD_MinigameMenu_IsOpened()) HUD_MinigameMenu_Close(); else HUD_MinigameMenu_Open(); @@ -284,14 +292,14 @@ void LocalCommand_hud(int request, int argc) case "save": { - if(argv(2)) + if (argv(2)) { HUD_Panel_ExportCfg(argv(2)); return; } else { - break; // go to usage, we're missing the paramater needed here. + break; // go to usage, we're missing the paramater needed here. } } @@ -309,23 +317,25 @@ void LocalCommand_hud(int request, int argc) case "radar": { - if(argv(2)) - HUD_Radar_Show_Maximized(InterpretBoolean(argv(2)),0); + if (argv(2)) + HUD_Radar_Show_Maximized(InterpretBoolean(argv(2)), 0); else - HUD_Radar_Show_Maximized(!hud_panel_radar_maximized,0); + HUD_Radar_Show_Maximized(!hud_panel_radar_maximized, 0); return; } case "clickradar": { - HUD_Radar_Show_Maximized(!hud_panel_radar_mouse,1); + HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1); return; } } } default: + { LOG_INFO("Incorrect parameters for ^2hud^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd hud action [configname | radartoggle | layout]\n"); @@ -333,10 +343,7 @@ void LocalCommand_hud(int request, int argc) LOG_INFO(" 'configname' is the name to save to for \"save\" action,\n"); LOG_INFO(" 'radartoggle' is to control hud_panel_radar_maximized for \"radar\" action,\n"); LOG_INFO(" and 'layout' is how to organize the scoreboard columns for the set action.\n"); - LOG_INFO(" quickmenu [[default | file | \"\"] submenu]\n"); - LOG_INFO(" Called without options (or with "") loads either the default quickmenu or a quickmenu file if hud_panel_quickmenu_file is set to a valid filename.\n"); - LOG_INFO(" Submenu option allows to open quickmenu directly in a submenu, it requires to specify 'default', 'file' or '\"\"' option.\n"); - LOG_INFO(" Full list of commands here: \"configure, minigame, save, scoreboard_columns_help, scoreboard_columns_set, radar.\"\n"); + LOG_INFO(" Full list of commands here: \"configure, quickmenu, minigame, save, scoreboard_columns_help, scoreboard_columns_set, radar.\"\n"); return; } } @@ -344,11 +351,11 @@ void LocalCommand_hud(int request, int argc) void LocalCommand_localprint(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { centerprint_hud(argv(1)); return; @@ -356,7 +363,9 @@ void LocalCommand_localprint(int request, int argc) } default: + { LOG_INFO("Incorrect parameters for ^2localprint^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd localprint \"message\"\n"); @@ -368,11 +377,11 @@ void LocalCommand_localprint(int request, int argc) void LocalCommand_mv_download(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { Cmd_MapVote_MapDownload(argc); return; @@ -380,7 +389,9 @@ void LocalCommand_mv_download(int request, int argc) } default: + { LOG_INFO("Incorrect parameters for ^2mv_download^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd mv_download mapid\n"); @@ -392,20 +403,22 @@ void LocalCommand_mv_download(int request, int argc) void LocalCommand_find(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity client; - for(client = world; (client = find(client, classname, argv(1))); ) + for (client = world; (client = find(client, classname, argv(1))); ) LOG_INFO(etos(client), "\n"); return; } default: + { LOG_INFO("Incorrect parameters for ^2find^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd find classname\n"); @@ -417,19 +430,19 @@ void LocalCommand_find(int request, int argc) void LocalCommand_sendcvar(int request, int argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { // W_FixWeaponOrder will trash argv, so save what we need. string thiscvar = strzone(argv(1)); string s = cvar_string(thiscvar); - if(thiscvar == "cl_weaponpriority") + if (thiscvar == "cl_weaponpriority") s = W_FixWeaponOrder(W_NumberWeaponOrder(s), 1); - else if(substring(thiscvar, 0, 17) == "cl_weaponpriority" && strlen(thiscvar) == 18) + else if (substring(thiscvar, 0, 17) == "cl_weaponpriority" && strlen(thiscvar) == 18) s = W_FixWeaponOrder(W_NumberWeaponOrder(s), 0); localcmd("cmd sentcvar ", thiscvar, " \"", s, "\"\n"); @@ -439,7 +452,9 @@ void LocalCommand_sendcvar(int request, int argc) } default: + { LOG_INFO("Incorrect parameters for ^2sendcvar^7\n"); + } case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 cl_cmd sendcvar \n"); @@ -453,22 +468,22 @@ void LocalCommand_sendcvar(int request, int argc) ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void LocalCommand_(int request) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; - } - - default: - case CMD_REQUEST_USAGE: - { - print("\nUsage:^3 cl_cmd \n"); - print(" No arguments required.\n"); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 cl_cmd \n"); + print(" No arguments required.\n"); + return; + } + } } */ @@ -478,7 +493,7 @@ void LocalCommand_(int request) // ================================== // Normally do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define CLIENT_COMMANDS(request,arguments) \ +#define CLIENT_COMMANDS(request, arguments) \ CLIENT_COMMAND("blurtest", LocalCommand_blurtest(request), "Feature for testing blur postprocessing") \ CLIENT_COMMAND("boxparticles", LocalCommand_boxparticles(request, arguments), "Spawn particles manually") \ CLIENT_COMMAND("create_scrshot_ent", LocalCommand_create_scrshot_ent(request), "Create an entity at this location for automatic screenshots") \ @@ -493,19 +508,17 @@ void LocalCommand_(int request) void LocalCommand_macro_help() { - #define CLIENT_COMMAND(name,function,description) \ - { if(strtolower(description) != "") { LOG_INFO(" ^2", name, "^7: ", description, "\n"); } } + #define CLIENT_COMMAND(name, function, description) \ + { if (strtolower(description) != "") { LOG_INFO(" ^2", name, "^7: ", description, "\n"); } } CLIENT_COMMANDS(0, 0); #undef CLIENT_COMMAND - - return; } bool LocalCommand_macro_command(int argc) { - #define CLIENT_COMMAND(name,function,description) \ - { if(name == strtolower(argv(0))) { function; return true; } } + #define CLIENT_COMMAND(name, function, description) \ + { if (name == strtolower(argv(0))) { function; return true; } } CLIENT_COMMANDS(CMD_REQUEST_COMMAND, argc); #undef CLIENT_COMMAND @@ -515,8 +528,8 @@ bool LocalCommand_macro_command(int argc) bool LocalCommand_macro_usage(int argc) { - #define CLIENT_COMMAND(name,function,description) \ - { if(name == strtolower(argv(1))) { function; return true; } } + #define CLIENT_COMMAND(name, function, description) \ + { if (name == strtolower(argv(1))) { function; return true; } } CLIENT_COMMANDS(CMD_REQUEST_USAGE, argc); #undef CLIENT_COMMAND @@ -526,13 +539,11 @@ bool LocalCommand_macro_usage(int argc) void LocalCommand_macro_write_aliases(int fh) { - #define CLIENT_COMMAND(name,function,description) \ - { if(strtolower(description) != "") { CMD_Write_Alias("qc_cmd_cl", name, description); } } + #define CLIENT_COMMAND(name, function, description) \ + { if (strtolower(description) != "") { CMD_Write_Alias("qc_cmd_cl", name, description); } } CLIENT_COMMANDS(0, 0); #undef CLIENT_COMMAND - - return; } @@ -552,7 +563,7 @@ void GameCommand(string command) string s = strtolower(argv(0)); if (s == "help") { - if(argc == 1) + if (argc == 1) { LOG_INFO("\nClient console commands:\n"); LocalCommand_macro_help(); @@ -565,25 +576,23 @@ void GameCommand(string command) return; } - else if(GenericCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it + else if (GenericCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it { return; } - else if(LocalCommand_macro_usage(argc)) // now try for normal commands too + else if (LocalCommand_macro_usage(argc)) // now try for normal commands too { return; } } // continue as usual and scan for normal commands - if (GenericCommand(command)// handled by common/command/generic.qc - || LocalCommand_macro_command(argc) // handled by one of the above LocalCommand_* functions - || MUTATOR_CALLHOOK(CSQC_ConsoleCommand, s, argc, command) // handled by a mutator - ) return; + if (GenericCommand(command) // handled by common/command/generic.qc + || LocalCommand_macro_command(argc) // handled by one of the above LocalCommand_* functions + || MUTATOR_CALLHOOK(CSQC_ConsoleCommand, s, argc, command) // handled by a mutator + ) return; // nothing above caught the command, must be invalid LOG_INFO(((command != "") ? strcat("Unknown client command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try cl_cmd help.\n"); - - return; } @@ -622,7 +631,7 @@ void GameCommand(string command) void ConsoleCommand_macro_init() { // first init normal commands - #define CONSOLE_COMMAND(name,execution) \ + #define CONSOLE_COMMAND(name, execution) \ { registercommand(name); } CONSOLE_COMMANDS_NORMAL(); @@ -630,22 +639,22 @@ void ConsoleCommand_macro_init() // then init movement commands #ifndef CAMERATEST - if(isdemo()) + if (isdemo()) { #endif - #define CONSOLE_COMMAND(name,execution) \ - { registercommand(name); } + #define CONSOLE_COMMAND(name, execution) \ + registercommand(name); - CONSOLE_COMMANDS_MOVEMENT(); + CONSOLE_COMMANDS_MOVEMENT(); #undef CONSOLE_COMMAND #ifndef CAMERATEST - } +} #endif } bool ConsoleCommand_macro_normal(string s, int argc) { - #define CONSOLE_COMMAND(name,execution) \ + #define CONSOLE_COMMAND(name, execution) \ { if (name == s) { { execution } return true; } } CONSOLE_COMMANDS_NORMAL(); @@ -656,9 +665,9 @@ bool ConsoleCommand_macro_normal(string s, int argc) bool ConsoleCommand_macro_movement(string s, int argc) { - if(camera_active) + if (camera_active) { - #define CONSOLE_COMMAND(name,execution) \ + #define CONSOLE_COMMAND(name, execution) \ { if (name == s) { { execution } return true; } } CONSOLE_COMMANDS_MOVEMENT(); @@ -679,7 +688,7 @@ bool CSQC_ConsoleCommand(string command) int argc = tokenize_console(command); string s = strtolower(argv(0)); // Return value should be true if CSQC handled the command, otherwise return false to have the engine handle it. - return (ConsoleCommand_macro_normal(s, argc) - || ConsoleCommand_macro_movement(s, argc) - ); + return ConsoleCommand_macro_normal(s, argc) + || ConsoleCommand_macro_movement(s, argc) + ; } diff --git a/qcsrc/client/controlpoint.qc b/qcsrc/client/controlpoint.qc deleted file mode 100644 index 575bce513c..0000000000 --- a/qcsrc/client/controlpoint.qc +++ /dev/null @@ -1,200 +0,0 @@ -#include "controlpoint.qh" - -#include "teamradar.qh" -#include "../common/movetypes/movetypes.qh" - -.vector colormod; -.float alpha; -.int count; -.float pain_finished; - -.bool iscaptured; - -.vector cp_origin, cp_bob_origin; -.float cp_bob_spd; - -.vector cp_bob_dmg; - -.vector punchangle; - -.float max_health; - -.entity icon_realmodel; - -void cpicon_draw(entity this) -{ - if(time < this.move_time) { return; } - - if(this.cp_bob_dmg_z > 0) - this.cp_bob_dmg_z = this.cp_bob_dmg_z - 3 * frametime; - else - this.cp_bob_dmg_z = 0; - this.cp_bob_origin_z = 4 * PI * (1 - cos(this.cp_bob_spd)); - this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime; - this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1)); - - if(!this.iscaptured) this.alpha = this.health / this.max_health; - - if(this.iscaptured) - { - if (this.punchangle_x > 0) - { - this.punchangle_x = this.punchangle_x - 60 * frametime; - if (this.punchangle_x < 0) - this.punchangle_x = 0; - } - else if (this.punchangle_x < 0) - { - this.punchangle_x = this.punchangle_x + 60 * frametime; - if (this.punchangle_x > 0) - this.punchangle_x = 0; - } - - if (this.punchangle_y > 0) - { - this.punchangle_y = this.punchangle_y - 60 * frametime; - if (this.punchangle_y < 0) - this.punchangle_y = 0; - } - else if (this.punchangle_y < 0) - { - this.punchangle_y = this.punchangle_y + 60 * frametime; - if (this.punchangle_y > 0) - this.punchangle_y = 0; - } - - if (this.punchangle_z > 0) - { - this.punchangle_z = this.punchangle_z - 60 * frametime; - if (this.punchangle_z < 0) - this.punchangle_z = 0; - } - else if (this.punchangle_z < 0) - { - this.punchangle_z = this.punchangle_z + 60 * frametime; - if (this.punchangle_z > 0) - this.punchangle_z = 0; - } - - this.angles_x = this.punchangle_x; - this.angles_y = this.punchangle_y + this.move_angles_y; - this.angles_z = this.punchangle_z; - this.move_angles_y = this.move_angles_y + 45 * frametime; - } - - setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg); -} - -void cpicon_damage(entity this, float hp) -{ - if(!this.iscaptured) { return; } - - if(hp < this.max_health * 0.25) - setmodel(this, MDL_ONS_CP3); - else if(hp < this.max_health * 0.50) - setmodel(this, MDL_ONS_CP2); - else if(hp < this.max_health * 0.75) - setmodel(this, MDL_ONS_CP1); - else if(hp <= this.max_health || hp >= this.max_health) - setmodel(this, MDL_ONS_CP); - - this.punchangle = (2 * randomvec() - '1 1 1') * 45; - - this.cp_bob_dmg_z = (2 * random() - 1) * 15; - this.pain_finished = time + 1; - this.colormod = '2 2 2'; - - setsize(this, CPICON_MIN, CPICON_MAX); -} - -void cpicon_construct(entity this) -{ - this.netname = "Control Point Icon"; - - setmodel(this, MDL_ONS_CP); - setsize(this, CPICON_MIN, CPICON_MAX); - - if(this.icon_realmodel == world) - { - this.icon_realmodel = spawn(); - setmodel(this.icon_realmodel, MDL_Null); - setorigin(this.icon_realmodel, this.origin); - setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX); - this.icon_realmodel.movetype = MOVETYPE_NOCLIP; - this.icon_realmodel.solid = SOLID_NOT; - this.icon_realmodel.move_origin = this.icon_realmodel.origin; - } - - if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; } - - this.move_movetype = MOVETYPE_NOCLIP; - this.solid = SOLID_NOT; - this.movetype = MOVETYPE_NOCLIP; - this.move_origin = this.origin; - this.move_time = time; - this.drawmask = MASK_NORMAL; - this.alpha = 1; - this.draw = cpicon_draw; - this.cp_origin = this.origin; - this.cp_bob_origin = '0 0 0.1'; - this.cp_bob_spd = 0; -} - -.vector glowmod; -void cpicon_changeteam(entity this) -{ - if(this.team) - { - this.glowmod = Team_ColorRGB(this.team - 1); - this.teamradar_color = Team_ColorRGB(this.team - 1); - this.colormap = 1024 + (this.team - 1) * 17; - } - else - { - this.colormap = 1024; - this.glowmod = '1 1 0'; - this.teamradar_color = '1 1 0'; - } -} - -void ent_cpicon(entity this) -{ - int sf = ReadByte(); - - if(sf & CPSF_SETUP) - { - this.origin_x = ReadCoord(); - this.origin_y = ReadCoord(); - this.origin_z = ReadCoord(); - setorigin(this, this.origin); - - this.health = ReadByte(); - this.max_health = ReadByte(); - this.count = ReadByte(); - this.team = ReadByte(); - this.iscaptured = ReadByte(); - - if(!this.count) - this.count = (this.health - this.max_health) * frametime; - - cpicon_changeteam(this); - cpicon_construct(this); - } - - if(sf & CPSF_STATUS) - { - int _tmp = ReadByte(); - if(_tmp != this.team) - { - this.team = _tmp; - cpicon_changeteam(this); - } - - _tmp = ReadByte(); - - if(_tmp != this.health) - cpicon_damage(this, _tmp); - - this.health = _tmp; - } -} diff --git a/qcsrc/client/controlpoint.qh b/qcsrc/client/controlpoint.qh deleted file mode 100644 index 5f2e89b4d7..0000000000 --- a/qcsrc/client/controlpoint.qh +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CLIENT_CONTROLPOINT_H -#define CLIENT_CONTROLPOINT_H - -const vector CPICON_MIN = '-32 -32 -9'; -const vector CPICON_MAX = '32 32 25'; - -const int CPSF_STATUS = 4; -const int CPSF_SETUP = 8; - -void ent_cpicon(entity this); - -#endif diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index e2e0e9341a..7eb672ba5f 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -19,7 +19,7 @@ void CSQCModel_Hook_PreDraw(bool isplayer); .int lodmodelindex0; .int lodmodelindex1; .int lodmodelindex2; -void CSQCPlayer_LOD_Apply(void) +void CSQCPlayer_LOD_Apply() {SELFPARAM(); // LOD model loading if(self.lodmodelindex0 != self.modelindex) @@ -108,14 +108,14 @@ int forceplayermodels_goodmodelindex; .vector glowmod; .vector old_glowmod; -void CSQCPlayer_ModelAppearance_PreUpdate(void) +void CSQCPlayer_ModelAppearance_PreUpdate() {SELFPARAM(); self.model = self.forceplayermodels_savemodel; self.modelindex = self.forceplayermodels_savemodelindex; self.skin = self.forceplayermodels_saveskin; self.colormap = self.forceplayermodels_savecolormap; } -void CSQCPlayer_ModelAppearance_PostUpdate(void) +void CSQCPlayer_ModelAppearance_PostUpdate() {SELFPARAM(); self.forceplayermodels_savemodel = self.model; self.forceplayermodels_savemodelindex = self.modelindex; @@ -136,8 +136,7 @@ void CSQCPlayer_ModelAppearance_Apply(bool islocalplayer) // which one is ALWAYS good? if (!forceplayermodels_goodmodel) { - entity e; - e = spawn(); + entity e = spawn(); precache_model(cvar_defstring("_cl_playermodel")); _setmodel(e, cvar_defstring("_cl_playermodel")); forceplayermodels_goodmodel = e.model; @@ -168,8 +167,7 @@ void CSQCPlayer_ModelAppearance_Apply(bool islocalplayer) forceplayermodels_attempted = 1; // only if this failed, find it out on our own - entity e; - e = spawn(); + entity e = spawn(); _setmodel(e, autocvar__cl_playermodel); // this is harmless, see below forceplayermodels_modelisgoodmodel = fexists(e.model); forceplayermodels_model = e.model; @@ -180,8 +178,7 @@ void CSQCPlayer_ModelAppearance_Apply(bool islocalplayer) if(autocvar_cl_forcemyplayermodel != "" && autocvar_cl_forcemyplayermodel != forceplayermodels_mymodel) { - entity e; - e = spawn(); + entity e = spawn(); _setmodel(e, autocvar_cl_forcemyplayermodel); // this is harmless, see below forceplayermodels_myisgoodmodel = fexists(e.model); forceplayermodels_mymodel = e.model; @@ -313,7 +310,7 @@ void CSQCPlayer_ModelAppearance_Apply(bool islocalplayer) .int csqcmodel_framecount; #define IS_DEAD_FRAME(f) ((f) == 0 || (f) == 1) -void CSQCPlayer_FallbackFrame_PreUpdate(void) +void CSQCPlayer_FallbackFrame_PreUpdate() {SELFPARAM(); self.frame = self.csqcmodel_saveframe; self.frame2 = self.csqcmodel_saveframe2; @@ -372,7 +369,7 @@ int CSQCPlayer_FallbackFrame(int f) LOG_INFOF("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, self.model); return f; } -void CSQCPlayer_FallbackFrame_Apply(void) +void CSQCPlayer_FallbackFrame_Apply() {SELFPARAM(); self.frame = CSQCPlayer_FallbackFrame(self.frame); self.frame2 = CSQCPlayer_FallbackFrame(self.frame2); @@ -386,7 +383,7 @@ void CSQCPlayer_FallbackFrame_Apply(void) .entity tag_entity; .int tag_entity_lastmodelindex; .int tag_index; -void CSQCModel_AutoTagIndex_Apply(void) +void CSQCModel_AutoTagIndex_Apply() {SELFPARAM(); if(self.tag_entity && wasfreed(self.tag_entity)) self.tag_entity = world; @@ -481,14 +478,14 @@ const int MF_TRACER3 = BIT(7); // purple trail .int csqcmodel_effects; .int csqcmodel_modelflags; .int csqcmodel_traileffect; -void CSQCModel_Effects_PreUpdate(void) +void CSQCModel_Effects_PreUpdate() {SELFPARAM(); self.effects = self.csqcmodel_effects; self.modelflags = self.csqcmodel_modelflags; self.traileffect = self.csqcmodel_traileffect; } -void Reset_ArcBeam(void); -void CSQCModel_Effects_PostUpdate(void) +void Reset_ArcBeam(); +void CSQCModel_Effects_PostUpdate() {SELFPARAM(); if (self == csqcplayer) { if (self.csqcmodel_teleported) { @@ -504,7 +501,7 @@ void CSQCModel_Effects_PostUpdate(void) Projectile_ResetTrail(self, self.origin); } .int snd_looping; -void CSQCModel_Effects_Apply(void) +void CSQCModel_Effects_Apply() {SELFPARAM(); int eff = self.csqcmodel_effects & ~CSQCMODEL_EF_RESPAWNGHOST; int tref = self.csqcmodel_traileffect; @@ -532,9 +529,9 @@ void CSQCModel_Effects_Apply(void) if(eff & EF_FULLBRIGHT) self.renderflags |= RF_FULLBRIGHT; if(eff & EF_FLAME) - pointparticles(particleeffectnum(EFFECT_EF_FLAME), self.origin, '0 0 0', bound(0, frametime, 0.1)); + pointparticles(EFFECT_EF_FLAME, self.origin, '0 0 0', bound(0, frametime, 0.1)); if(eff & EF_STARDUST) - pointparticles(particleeffectnum(EFFECT_EF_STARDUST), self.origin, '0 0 0', bound(0, frametime, 0.1)); + pointparticles(EFFECT_EF_STARDUST, self.origin, '0 0 0', bound(0, frametime, 0.1)); if(eff & EF_NOSHADOW) self.renderflags |= RF_NOSHADOW; if(eff & EF_NODEPTHTEST) diff --git a/qcsrc/client/damage.qc b/qcsrc/client/damage.qc deleted file mode 100644 index 34890e034e..0000000000 --- a/qcsrc/client/damage.qc +++ /dev/null @@ -1,355 +0,0 @@ -#include "damage.qh" - -#include "gibs.qh" -#include "../common/deathtypes/all.qh" -#include "../common/movetypes/movetypes.qh" -#include "../common/vehicles/all.qh" -#include "../common/weapons/all.qh" - -.entity tag_entity; - -.float cnt; -.int state; -.bool isplayermodel; - -void DamageEffect_Think() -{SELFPARAM(); - // if particle distribution is enabled, slow ticrate by total number of damages - if(autocvar_cl_damageeffect_distribute) - self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages; - else - self.nextthink = time + autocvar_cl_damageeffect_ticrate; - - if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask) - { - // time is up or the player got gibbed / disconnected - self.owner.total_damages = max(0, self.owner.total_damages - 1); - remove(self); - return; - } - if(self.state && !self.owner.csqcmodel_isdead) - { - // if the player was dead but is now alive, it means he respawned - // if so, clear his damage effects, or damages from his dead body will be copied back - self.owner.total_damages = max(0, self.owner.total_damages - 1); - remove(self); - return; - } - self.state = self.owner.csqcmodel_isdead; - if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum) && !autocvar_chase_active) - return; // if we aren't using a third person camera, hide our own effects - - // now generate the particles - vector org; - org = gettaginfo(self, 0); // origin at attached location - pointparticles(self.team, org, '0 0 0', 1); -} - -void DamageEffect(vector hitorg, float thedamage, int type, int specnum) -{SELFPARAM(); - // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets) - - int nearestbone = 0; - float life; - string specstr, effectname; - entity e; - - if(!autocvar_cl_damageeffect || autocvar_cl_gentle || autocvar_cl_gentle_damage) - return; - if(!self || !self.modelindex || !self.drawmask) - return; - - // if this is a rigged mesh, the effect will show on the bone where damage was dealt - // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it - // if there's no skeleton, object origin will automatically be selected - FOR_EACH_TAG(self) - { - if(!tagnum) - continue; // skip empty bones - // blacklist bones positioned outside the mesh, or the effect will be floating - // TODO: Do we have to do it this way? Why do these bones exist at all? - if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R") - continue; // player model bone blacklist - - // now choose the bone closest to impact origin - if(nearestbone == 0 || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, nearestbone))) - nearestbone = tagnum; - } - gettaginfo(self, nearestbone); // set gettaginfo_name - - // return if we reached our damage effect limit or damages are disabled - // TODO: When the limit is reached, it would be better if the oldest damage was removed instead of not adding a new one - if(nearestbone) - { - if(self.total_damages >= autocvar_cl_damageeffect_bones) - return; // allow multiple damages on skeletal models - } - else - { - if(autocvar_cl_damageeffect < 2 || self.total_damages) - return; // allow a single damage on non-skeletal models - } - - life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max); - - effectname = DEATH_WEAPONOF(type).netname; - - if(substring(effectname, strlen(effectname) - 5, 5) == "BLOOD") - { - if(self.isplayermodel) - { - specstr = species_prefix(specnum); - specstr = substring(specstr, 0, strlen(specstr) - 1); - effectname = strreplace("BLOOD", specstr, effectname); - } - else { return; } // objects don't bleed - } - - e = spawn(); - setmodel(e, MDL_Null); // necessary to attach and read origin - setattachment(e, self, gettaginfo_name); // attach to the given bone - e.classname = "damage"; - e.owner = self; - e.cnt = time + life; - e.team = _particleeffectnum(effectname); - e.think = DamageEffect_Think; - e.nextthink = time; - self.total_damages += 1; -} - -void Ent_DamageInfo(float isNew) -{SELFPARAM(); - float thedamage, rad, edge, thisdmg; - bool hitplayer = false; - int species, forcemul; - vector force, thisforce; - - w_deathtype = ReadShort(); - w_issilent = (w_deathtype & 0x8000); - w_deathtype = (w_deathtype & 0x7FFF); - - w_org.x = ReadCoord(); - w_org.y = ReadCoord(); - w_org.z = ReadCoord(); - - thedamage = ReadByte(); - rad = ReadByte(); - edge = ReadByte(); - force = decompressShortVector(ReadShort()); - species = ReadByte(); - - if (!isNew) - return; - - if(rad < 0) - { - rad = -rad; - forcemul = -1; - } - else - forcemul = 1; - - for(entity e = findradius(w_org, rad + MAX_DAMAGEEXTRARADIUS); e; e = e.chain) - { - setself(e); - // attached ents suck - if(self.tag_entity) - continue; - - vector nearest = NearestPointOnBox(self, w_org); - if(rad) - { - thisdmg = ((vlen (nearest - w_org) - bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad); - if(thisdmg >= 1) - continue; - if(thisdmg < 0) - thisdmg = 0; - if(thedamage) - { - thisdmg = thedamage + (edge - thedamage) * thisdmg; - thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org); - } - else - { - thisdmg = 0; - thisforce = forcemul * vlen(force) * normalize(self.origin - w_org); - } - } - else - { - if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) - continue; - - thisdmg = thedamage; - thisforce = forcemul * force; - } - - if(self.damageforcescale) - if(vlen(thisforce)) - { - self.move_velocity = self.move_velocity + damage_explosion_calcpush(self.damageforcescale * thisforce, self.move_velocity, autocvar_g_balance_damagepush_speedfactor); - self.move_flags &= ~FL_ONGROUND; - } - - if(w_issilent) - self.silent = 1; - - if(self.event_damage) - self.event_damage(thisdmg, w_deathtype, w_org, thisforce); - - DamageEffect(w_org, thisdmg, w_deathtype, species); - - if(self.isplayermodel) - hitplayer = true; // this impact damaged a player - } - setself(this); - - if(DEATH_ISVEHICLE(w_deathtype)) - { - traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); - if(trace_plane_normal != '0 0 0') - w_backoff = trace_plane_normal; - else - w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16)); - - setorigin(self, w_org + w_backoff * 2); // for sound() calls - - switch(DEATH_ENT(w_deathtype)) - { - case DEATH_VH_CRUSH: - break; - - // spiderbot - case DEATH_VH_SPID_MINIGUN: - sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_SPIDERBOT_MINIGUN_IMPACT), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_SPID_ROCKET: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_SPIDERBOT_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_SPID_DEATH: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_VH_WAKI_GUN: - sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_RACER_IMPACT), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_WAKI_ROCKET: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_RACER_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_WAKI_DEATH: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_VH_RAPT_CANNON: - sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_RAPTOR_CANNON_IMPACT), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_RAPT_FRAGMENT: - float i; - vector ang, vel; - for(i = 1; i < 4; ++i) - { - vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128; - ang = vectoangles(vel); - RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i)); - } - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_RAPTOR_BOMB_SPREAD), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_RAPT_BOMB: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_RAPTOR_BOMB_IMPACT), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_RAPT_DEATH: - sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_EXPLOSION_BIG), self.origin, w_backoff * 1000, 1); - break; - case DEATH_VH_BUMB_GUN: - sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_BIGPLASMA_IMPACT), self.origin, w_backoff * 1000, 1); - break; - } - } - - - if(DEATH_ISTURRET(w_deathtype)) - { - traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); - if(trace_plane_normal != '0 0 0') - w_backoff = trace_plane_normal; - else - w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16)); - - setorigin(self, w_org + w_backoff * 2); // for sound() calls - - switch(DEATH_ENT(w_deathtype)) - { - case DEATH_TURRET_EWHEEL: - sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_TURRET_FLAC: - pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), w_org, '0 0 0', 1); - sound(self, CH_SHOTS, SND_HAGEXP_RANDOM(), VOL_BASE, ATTEN_NORM); - break; - - case DEATH_TURRET_MLRS: - case DEATH_TURRET_HK: - case DEATH_TURRET_WALK_ROCKET: - case DEATH_TURRET_HELLION: - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_TURRET_MACHINEGUN: - case DEATH_TURRET_WALK_GUN: - sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_TURRET_PLASMA: - sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_ELECTRO_IMPACT), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_TURRET_WALK_MELEE: - sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_MIN); - pointparticles(particleeffectnum(EFFECT_TE_SPARK), self.origin, w_backoff * 1000, 1); - break; - - case DEATH_TURRET_PHASER: - break; - - case DEATH_TURRET_TESLA: - te_smallflash(self.origin); - break; - - } - } - - // TODO spawn particle effects and sounds based on w_deathtype - if(!DEATH_ISSPECIAL(w_deathtype)) - if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit - { - Weapon hitwep = DEATH_WEAPONOF(w_deathtype); - w_random = prandom(); - - traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); - if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER) - w_backoff = trace_plane_normal; - else - w_backoff = -1 * normalize(force); - setorigin(self, w_org + w_backoff * 2); // for sound() calls - - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) { - hitwep.wr_impacteffect(hitwep); - } - } -} diff --git a/qcsrc/client/damage.qh b/qcsrc/client/damage.qh deleted file mode 100644 index 3f11b9a49e..0000000000 --- a/qcsrc/client/damage.qh +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CLIENT_DAMAGE_H -#define CLIENT_DAMAGE_H - -.float total_damages; // number of effects which currently are attached to a player - -void Ent_DamageInfo(float isNew); - -#endif diff --git a/qcsrc/client/defs.qh b/qcsrc/client/defs.qh index 6325a8ab3c..98f25cc903 100644 --- a/qcsrc/client/defs.qh +++ b/qcsrc/client/defs.qh @@ -23,8 +23,8 @@ float dmg_take; #endif // Basic variables -.float enttype; // entity type sent from server -.int sv_entnum; // entity number sent from server +.int enttype; // entity type sent from server +.int sv_entnum; // entity number sent from server .int team; .int team_size; diff --git a/qcsrc/client/effects.qc b/qcsrc/client/effects.qc deleted file mode 100644 index 21fa1ce037..0000000000 --- a/qcsrc/client/effects.qc +++ /dev/null @@ -1,100 +0,0 @@ -#include "effects.qh" - -/* -.vector fx_start; -.vector fx_end; -.float fx_with; -.string fx_texture; -.float fx_lifetime; - -void b_draw() -{ - //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin); - Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, (self.fx_with/256), 0, '1 1 1', 1, DRAWFLAG_ADDITIVE, view_origin); - -} -void b_make(vector s,vector e, string t,float l,float z) -{ - entity b; - b = spawn(); - b.fx_texture = t; - b.fx_start = s; - b.fx_end = e; - b.fx_with = z; - b.think = SUB_Remove; - b.nextthink = time + l; - b.draw = b_draw; - - //b.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; -} -*/ - -void cl_effects_lightningarc(vector from, vector to,float seglength,float drifts,float drifte,float branchfactor,float branchfactor_add) -{ - vector direction,dirnew, pos, pos_l; - float length, steps, steplength, i,drift; - - length = vlen(from - to); - if(length < 1) - return; - - // Use at most 16 te_lightning1 segments, as these eat up beam list segments. - // TODO: Change this to R_BeginPolygon code, then we no longer have this limit. - steps = min(16, floor(length / seglength)); - if(steps < 1) - { - te_lightning1(world,from,to); - return; - } - - steplength = length / steps; - direction = normalize(to - from); - pos_l = from; - if(length > seglength) - { - for(i = 1; i < steps; i += 1) - { - drift = drifts * (1 - (i / steps)) + drifte * (i / steps); - dirnew = normalize(direction * (1 - drift) + randomvec() * drift); - pos = pos_l + dirnew * steplength; - te_lightning1(world,pos_l,pos); - // WTF endless recursion if branchfactor is 1.0 (possibly due to adding branchfactor_add). FIXME - // if(random() < branchfactor) - // cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add); - - pos_l = pos; - } - te_lightning1(world,pos_l,to); - - } - else - te_lightning1(world,from,to); - -} - -void Net_ReadLightningarc() -{ - vector from, to; - - from.x = ReadCoord(); from.y = ReadCoord(); from.z = ReadCoord(); - to.x = ReadCoord(); to.y = ReadCoord(); to.z = ReadCoord(); - - if(autocvar_cl_effects_lightningarc_simple) - { - te_lightning1(world,from,to); - } - else - { - float seglength, drifts, drifte, branchfactor, branchfactor_add; - - seglength = autocvar_cl_effects_lightningarc_segmentlength; - drifts = autocvar_cl_effects_lightningarc_drift_start; - drifte = autocvar_cl_effects_lightningarc_drift_end; - branchfactor = autocvar_cl_effects_lightningarc_branchfactor_start; - branchfactor_add = autocvar_cl_effects_lightningarc_branchfactor_add; - - cl_effects_lightningarc(from,to,seglength,drifts,drifte,branchfactor,branchfactor_add); - } - -} -void Net_ReadArc() { Net_ReadLightningarc(); } diff --git a/qcsrc/client/effects.qh b/qcsrc/client/effects.qh deleted file mode 100644 index 2d93f41d71..0000000000 --- a/qcsrc/client/effects.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CLIENT_EFFECTS_H -#define CLIENT_EFFECTS_H - -void Net_ReadArc(); - -#endif diff --git a/qcsrc/client/generator.qc b/qcsrc/client/generator.qc deleted file mode 100644 index d7114d1bf4..0000000000 --- a/qcsrc/client/generator.qc +++ /dev/null @@ -1,227 +0,0 @@ -#include "generator.qh" - -#include "teamradar.qh" -#include "../common/movetypes/movetypes.qh" - -.float alpha; -.float scale; -.int count; -.float max_health; - -void ons_generator_ray_draw(entity this) -{ - if(time < self.move_time) - return; - - self.move_time = time + 0.05; - - if(self.count > 10) - { - remove(self); - return; - } - - if(self.count > 5) - self.alpha -= 0.1; - else - self.alpha += 0.1; - - self.scale += 0.2; - self.count +=1; -} - -void ons_generator_ray_spawn(vector org) -{ - entity e; - e = spawn(); - e.classname = "ons_ray"; - setmodel(e, MDL_ONS_RAY); - setorigin(e, org); - e.angles = randomvec() * 360; - e.move_origin = org; - e.movetype = MOVETYPE_NONE; - e.alpha = 0; - e.scale = random() * 5 + 8; - e.move_time = time + 0.05; - e.drawmask = MASK_NORMAL; - e.draw = ons_generator_ray_draw; -} - -void generator_draw(entity this) -{ - if(time < self.move_time) - return; - - if(self.health > 0) - { - // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - self.health / self.max_health) - if(random() < 0.01) - { - pointparticles(particleeffectnum(EFFECT_ELECTRO_BALLEXPLODE), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); - sound(self, CH_TRIGGER, SND_ONS_ELECTRICITY_EXPLODE, VOL_BASE, ATTEN_NORM); - } - else - pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_DAMAGED), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); - - self.move_time = time + 0.1; - - return; - } - - if(self.count <= 0) - return; - - vector org; - int i; - - // White shockwave - if(self.count==40||self.count==20) - { - sound(self, CH_TRIGGER, SND_ONS_SHOCKWAVE, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_ELECTRO_COMBO), self.origin, '0 0 0', 6); - } - - // rays - if(random() > 0.25) - { - ons_generator_ray_spawn(self.origin); - } - - // Spawn fire balls - for(i=0;i < 10;++i) - { - org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); - pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_GIB), org, '0 0 0', 1); - } - - // Short explosion sound + small explosion - if(random() < 0.25) - { - te_explosion(self.origin); - sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - } - - // Particles - org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); - pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_EXPLODE), org, '0 0 0', 1); - - // Final explosion - if(self.count==1) - { - org = self.origin; - te_explosion(org); - pointparticles(particleeffectnum(EFFECT_ONS_GENERATOR_EXPLODE2), org, '0 0 0', 1); - sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - } - - self.move_time = time + 0.05; - - self.count -= 1; -} - -void generator_damage(float hp) -{SELFPARAM(); - if(hp <= 0) - setmodel(self, MDL_ONS_GEN_DEAD); - else if(hp < self.max_health * 0.10) - setmodel(self, MDL_ONS_GEN9); - else if(hp < self.max_health * 0.20) - setmodel(self, MDL_ONS_GEN8); - else if(hp < self.max_health * 0.30) - setmodel(self, MDL_ONS_GEN7); - else if(hp < self.max_health * 0.40) - setmodel(self, MDL_ONS_GEN6); - else if(hp < self.max_health * 0.50) - setmodel(self, MDL_ONS_GEN5); - else if(hp < self.max_health * 0.60) - setmodel(self, MDL_ONS_GEN4); - else if(hp < self.max_health * 0.70) - setmodel(self, MDL_ONS_GEN3); - else if(hp < self.max_health * 0.80) - setmodel(self, MDL_ONS_GEN2); - else if(hp < self.max_health * 0.90) - setmodel(self, MDL_ONS_GEN1); - else if(hp <= self.max_health || hp >= self.max_health) - setmodel(self, MDL_ONS_GEN); - - setsize(self, GENERATOR_MIN, GENERATOR_MAX); -} - -void generator_construct() -{SELFPARAM(); - self.netname = "Generator"; - self.classname = "onslaught_generator"; - - setorigin(self, self.origin); - setmodel(self, MDL_ONS_GEN); - setsize(self, GENERATOR_MIN, GENERATOR_MAX); - - self.move_movetype = MOVETYPE_NOCLIP; - self.solid = SOLID_BBOX; - self.movetype = MOVETYPE_NOCLIP; - self.move_origin = self.origin; - self.move_time = time; - self.drawmask = MASK_NORMAL; - self.alpha = 1; - self.draw = generator_draw; -} - -.vector glowmod; -void generator_changeteam() -{SELFPARAM(); - if(self.team) - { - self.glowmod = Team_ColorRGB(self.team - 1); - self.teamradar_color = Team_ColorRGB(self.team - 1); - self.colormap = 1024 + (self.team - 1) * 17; - } - else - { - self.colormap = 1024; - self.glowmod = '1 1 0'; - self.teamradar_color = '1 1 0'; - } -} - -void ent_generator() -{SELFPARAM(); - int sf = ReadByte(); - - if(sf & GSF_SETUP) - { - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - setorigin(self, self.origin); - - self.health = ReadByte(); - self.max_health = ReadByte(); - self.count = ReadByte(); - self.team = ReadByte(); - - if(!self.count) - self.count = 40; - - generator_changeteam(); - generator_construct(); - } - - if(sf & GSF_STATUS) - { - int _tmp; - _tmp = ReadByte(); - if(_tmp != self.team) - { - self.team = _tmp; - generator_changeteam(); - } - - _tmp = ReadByte(); - - if(_tmp != self.health) - generator_damage(_tmp); - - self.health = _tmp; - } -} diff --git a/qcsrc/client/generator.qh b/qcsrc/client/generator.qh deleted file mode 100644 index c60aff4009..0000000000 --- a/qcsrc/client/generator.qh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CLIENT_GENERATOR_H -#define CLIENT_GENERATOR_H -const vector GENERATOR_MIN = '-52 -52 -14'; -const vector GENERATOR_MAX = '52 52 75'; - -const int GSF_STATUS = 4; -const int GSF_SETUP = 8; - -void ent_generator(); -#endif diff --git a/qcsrc/client/gibs.qc b/qcsrc/client/gibs.qc deleted file mode 100644 index 4afa5eb88a..0000000000 --- a/qcsrc/client/gibs.qc +++ /dev/null @@ -1,270 +0,0 @@ -#include "gibs.qh" - -#include "rubble.qh" -#include "../common/movetypes/movetypes.qh" - -.float scale; -.float alpha; -.float cnt; -.float gravity; - -void Gib_Delete() -{SELFPARAM(); - remove(self); -} - -string species_prefix(int specnum) -{ - switch(specnum) - { - case SPECIES_HUMAN: return ""; - case SPECIES_ALIEN: return "alien_"; - case SPECIES_ROBOT_SHINY: return "robot_"; - case SPECIES_ROBOT_RUSTY: return "robot_"; // use the same effects, only different gibs - case SPECIES_ROBOT_SOLID: return "robot_"; // use the same effects, only different gibs - case SPECIES_ANIMAL: return "animal_"; - case SPECIES_RESERVED: return "reserved_"; - default: return ""; - } -} - -void Gib_setmodel(entity gib, string mdlname, int specnum) -{ - switch(specnum) - { - case SPECIES_ROBOT_RUSTY: - case SPECIES_ROBOT_SHINY: - case SPECIES_ROBOT_SOLID: - if(specnum != SPECIES_ROBOT_SOLID || mdlname == "models/gibs/chunk.mdl") - { - if(mdlname == "models/gibs/bloodyskull.md3") - setmodel(gib, MDL_GIB_ROBO); - else - setmodel(gib, MDL_GIB_ROBO_RANDOM()); - if(specnum == SPECIES_ROBOT_SHINY) - { - gib.skin = 1; - gib.colormod = '2 2 2'; - } - gib.scale = 1; - break; - } - default: - _setmodel(gib, mdlname); - gib.skin = specnum; - break; - } -} - -void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany) -{ - float i, pmod; - pmod = autocvar_cl_particles_quality; - for (i = 0; i < 50 * pmod; ++i) - pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50); -} - -void SUB_RemoveOnNoImpact() -{ - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - Gib_Delete(); -} - -void Gib_Touch() -{SELFPARAM(); - // TODO maybe bounce of walls, make more gibs, etc. - - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { - Gib_Delete(); - return; - } - - if(!self.silent) - sound(self, CH_PAIN, SND_GIB_SPLAT_RANDOM(), VOL_BASE, ATTEN_NORM); - pointparticles(_particleeffectnum(strcat(species_prefix(self.cnt), "blood")), self.origin + '0 0 1', '0 0 30', 10); - - Gib_Delete(); -} - -void Gib_Draw(entity this) -{ - vector oldorg; - oldorg = self.origin; - - Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); - if(wasfreed(self)) - return; - - if(self.touch == Gib_Touch) // don't do this for the "chunk" thingie... - // TODO somehow make it spray in a direction dependent on self.angles - trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_SLIGHTBLOOD.eent_eff_name)), oldorg, self.origin); - else - trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_BLOOD.eent_eff_name)), oldorg, self.origin); - - self.renderflags = 0; - - // make gibs die faster at low view quality - // if view_quality is 0.5, we want to have them die twice as fast - self.nextthink -= frametime * (1 / bound(0.01, view_quality, 1.00) - 1); - - self.alpha = bound(0, self.nextthink - time, 1); - - if(self.alpha < ALPHA_MIN_VISIBLE) - { - self.drawmask = 0; - Gib_Delete(); - } -} - -void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent) -{ - entity gib; - - // TODO remove some gibs according to cl_nogibs - gib = RubbleNew("gib"); - gib.classname = "gib"; - gib.move_movetype = MOVETYPE_BOUNCE; - gib.gravity = 1; - gib.solid = SOLID_CORPSE; - gib.cnt = specnum; - gib.silent = issilent; - Gib_setmodel(gib, mdlname, specnum); - - setsize (gib, '-8 -8 -8', '8 8 8'); - - gib.draw = Gib_Draw; - if(destroyontouch) - gib.move_touch = Gib_Touch; - else - gib.move_touch = SUB_RemoveOnNoImpact; - - // don't spawn gibs inside solid - just don't - if(org != safeorg) - { - tracebox(safeorg, gib.mins, gib.maxs, org, MOVE_NOMONSTERS, gib); - org = trace_endpos; - } - - gib.move_origin = org; - setorigin(gib, org); - gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up; - gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale; - gib.move_time = time; - gib.damageforcescale = autocvar_cl_gibs_damageforcescale; - - gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); - gib.drawmask = MASK_NORMAL; - - RubbleLimit("gib", autocvar_cl_gibs_maxcount, Gib_Delete); -} - -void Ent_GibSplash(bool isNew) -{SELFPARAM(); - int amount, type, specnum; - vector org, vel; - string specstr; - bool issilent; - string gentle_prefix = "morphed_"; - - float randomvalue; - int c; - - type = ReadByte(); // gibbage type - amount = ReadByte() / 16.0; // gibbage amount - org.x = ReadShort() * 4 + 2; - org.y = ReadShort() * 4 + 2; - org.z = ReadShort() * 4 + 2; - vel = decompressShortVector(ReadShort()); - - float cl_gentle_gibs = autocvar_cl_gentle_gibs; - if(cl_gentle_gibs || autocvar_cl_gentle) - type |= 0x80; // set gentle bit - - if(type & 0x80) - { - if(cl_gentle_gibs == 2) - gentle_prefix = ""; - else if(cl_gentle_gibs == 3) - gentle_prefix = "happy_"; - } - else if(autocvar_cl_particlegibs) - { - type |= 0x80; - gentle_prefix = "particlegibs_"; - } - - if (!(cl_gentle_gibs || autocvar_cl_gentle)) - amount *= 1 - autocvar_cl_nogibs; - - if(autocvar_ekg) - amount *= 5; - - if(amount <= 0 || !isNew) - return; - - setorigin(self, org); // for the sounds - - specnum = (type & 0x78) / 8; // blood/gibmodel type: using four bits (0..7, bit indexes 3,4,5) - issilent = (type & 0x40); - type = type & 0x87; // remove the species bits: bit 7 = gentle, bit 0,1,2 = kind of gib - specstr = species_prefix(specnum); - - switch(type) - { - case 0x01: - if(!issilent) - sound (self, CH_PAIN, SND_GIB, VOL_BASE, ATTEN_NORM); - - if(prandom() < amount) - TossGib ("models/gibs/eye.md3", org, org, vel, prandomvec() * 150, specnum, 0, issilent); - new_te_bloodshower(_particleeffectnum(strcat(specstr, "bloodshower")), org, 1200, amount); - if(prandom() < amount) - TossGib ("models/gibs/bloodyskull.md3", org, org + 16 * prandomvec(), vel, prandomvec() * 100, specnum, 0, issilent); - - for(c = 0; c < amount; ++c) - { - randomvalue = amount - c; - - if(prandom() < randomvalue) - TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/chest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/smallchest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/leg1.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/leg2.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent); - - // these splat on impact - if(prandom() < randomvalue) - TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); - if(prandom() < randomvalue) - TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); - } - break; - case 0x02: - pointparticles(_particleeffectnum(strcat(specstr, "blood")), org, vel, amount * 16); - break; - case 0x03: - if(prandom() < amount) - TossGib ("models/gibs/chunk.mdl", org, org, vel, prandomvec() * (prandom() * 30 + 20), specnum, 1, issilent); // TODO maybe adjust to more randomization? - break; - case 0x81: - pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_dissolve")), org, vel, amount); - break; - case 0x82: - pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_hit")), org, vel, amount * 16); - break; - case 0x83: - // no gibs in gentle mode, sorry - break; - } -} diff --git a/qcsrc/client/gibs.qh b/qcsrc/client/gibs.qh deleted file mode 100644 index eb63aa1fe5..0000000000 --- a/qcsrc/client/gibs.qh +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CLIENT_GIBS_H -#define CLIENT_GIBS_H - -.vector colormod; - -.bool silent; - -void Gib_Delete(); - -string species_prefix(int specnum); - -void Gib_setmodel(entity gib, string mdlname, int specnum); - -void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany); - -void SUB_RemoveOnNoImpact(); - -void Gib_Touch(); - -void Gib_Draw(entity this); - -void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent); - -void Ent_GibSplash(bool isNew); - -#endif diff --git a/qcsrc/client/hook.qc b/qcsrc/client/hook.qc index 5b8d093120..cee2a0cd83 100644 --- a/qcsrc/client/hook.qc +++ b/qcsrc/client/hook.qc @@ -4,7 +4,7 @@ #include "../lib/warpzone/common.qh" entityclass(Hook); -class(Hook) .float HookType; // ENT_CLIENT_* +class(Hook) .entity HookType; // ENT_CLIENT_* class(Hook) .vector origin; class(Hook) .vector velocity; class(Hook) .float HookSilent; @@ -48,10 +48,10 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: vs = hook_shotorigin[s]; break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: vs = lightning_shotorigin[s]; break; } @@ -61,11 +61,11 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z; b = self.origin; break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: if(self.HookRange) b = view_origin + view_forward * self.HookRange; else @@ -81,11 +81,11 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: a = self.velocity; b = self.origin; break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: a = self.origin; b = self.velocity; break; @@ -97,7 +97,7 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: intensity = 1; offset = 0; switch(t) @@ -109,7 +109,7 @@ void Draw_GrapplingHook(entity this) default: tex = "particles/hook_white"; rgb = getcsqcplayercolor(self.sv_entnum); break; } break; - case ENT_CLIENT_ARC_BEAM: // todo + case NET_ENT_CLIENT_ARC_BEAM: // todo intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2); offset = Noise_Brown(self, frametime) * 10; tex = "particles/lgbeam"; @@ -121,7 +121,7 @@ void Draw_GrapplingHook(entity this) Draw_GrapplingHook_trace_callback_rnd = offset; Draw_GrapplingHook_trace_callback_rgb = rgb; Draw_GrapplingHook_trace_callback_a = intensity; - WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((self.HookType == ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback); + WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((self.HookType == NET_ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback); Draw_GrapplingHook_trace_callback_tex = string_null; atrans = WarpZone_TransformOrigin(WarpZone_trace_transform, a); @@ -129,7 +129,7 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: if(vlen(trace_endpos - atrans) > 0.5) { setorigin(self, trace_endpos); // hook endpoint! @@ -141,7 +141,7 @@ void Draw_GrapplingHook(entity this) self.drawmask = 0; } break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: setorigin(self, a); // beam origin! break; } @@ -149,10 +149,10 @@ void Draw_GrapplingHook(entity this) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: break; - case ENT_CLIENT_ARC_BEAM: - pointparticles(particleeffectnum(EFFECT_ARC_LIGHTNING2), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect + case NET_ENT_CLIENT_ARC_BEAM: + pointparticles(EFFECT_ARC_LIGHTNING2, trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect break; } } @@ -162,9 +162,9 @@ void Remove_GrapplingHook() sound (self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); } -void Ent_ReadHook(float bIsNew, float type) -{SELFPARAM(); - self.HookType = type; +NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew) +{ + self.HookType = NET_ENT_CLIENT_HOOK; int sf = ReadByte(); @@ -181,10 +181,10 @@ void Ent_ReadHook(float bIsNew, float type) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: self.HookRange = 0; break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: self.HookRange = ReadCoord(); break; } @@ -213,18 +213,19 @@ void Ent_ReadHook(float bIsNew, float type) switch(self.HookType) { default: - case ENT_CLIENT_HOOK: + case NET_ENT_CLIENT_HOOK: // for the model setmodel(self, MDL_HOOK); self.drawmask = MASK_NORMAL; break; - case ENT_CLIENT_ARC_BEAM: + case NET_ENT_CLIENT_ARC_BEAM: sound (self, CH_SHOTS_SINGLE, SND_LGBEAM_FLY, VOL_BASE, ATTEN_NORM); break; } } self.teleport_time = time + 10; + return true; } // TODO: hook: temporarily transform self.origin for drawing the model along warpzones! diff --git a/qcsrc/client/hook.qh b/qcsrc/client/hook.qh index 9e6ee548cf..36624255f9 100644 --- a/qcsrc/client/hook.qh +++ b/qcsrc/client/hook.qh @@ -3,6 +3,4 @@ void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg); -void Ent_ReadHook(float bIsNew, float type); - #endif diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc deleted file mode 100644 index a00a9c3a4c..0000000000 --- a/qcsrc/client/hud.qc +++ /dev/null @@ -1,4855 +0,0 @@ -#include "hud.qh" - -#include "hud_config.qh" -#include "mapvoting.qh" -#include "scoreboard.qh" -#include "teamradar.qh" -#include "t_items.qh" -#include "../common/buffs/all.qh" -#include "../common/deathtypes/all.qh" -#include "../common/items/all.qc" -#include "../common/mapinfo.qh" -#include "../common/mutators/mutator/waypoints/all.qh" -#include "../common/nades/all.qh" -#include "../common/stats.qh" -#include "../lib/csqcmodel/cl_player.qh" -// TODO: remove -#include "../server/mutators/mutator/gamemode_ctf.qc" - - -/* -================== -Misc HUD functions -================== -*/ - -vector HUD_Get_Num_Color (float x, float maxvalue) -{ - float blinkingamt; - vector color; - if(x >= maxvalue) { - color.x = sin(2*M_PI*time); - color.y = 1; - color.z = sin(2*M_PI*time); - } - else if(x > maxvalue * 0.75) { - color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0 - color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1 - color.z = 0; - } - else if(x > maxvalue * 0.5) { - color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4 - color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9 - color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0 - } - else if(x > maxvalue * 0.25) { - color.x = 1; - color.y = 1; - color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1 - } - else if(x > maxvalue * 0.1) { - color.x = 1; - color.y = (x-20)*90/27/100; // green value between 0 -> 1 - color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2 - } - else { - color.x = 1; - color.y = 0; - color.z = 0; - } - - blinkingamt = (1 - x/maxvalue/0.25); - if(blinkingamt > 0) - { - color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time); - color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time); - color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time); - } - return color; -} - -float HUD_GetRowCount(int item_count, vector size, float item_aspect) -{ - float aspect = size_y / size_x; - return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count); -} - -vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect) -{ - float columns, rows; - float ratio, best_ratio = 0; - float best_columns = 1, best_rows = 1; - bool vertical = (psize.x / psize.y >= item_aspect); - if(vertical) - { - psize = eX * psize.y + eY * psize.x; - item_aspect = 1 / item_aspect; - } - - rows = ceil(sqrt(item_count)); - columns = ceil(item_count/rows); - while(columns >= 1) - { - ratio = (psize.x/columns) / (psize.y/rows); - if(ratio > item_aspect) - ratio = item_aspect * item_aspect / ratio; - - if(ratio <= best_ratio) - break; // ratio starts decreasing by now, skip next configurations - - best_columns = columns; - best_rows = rows; - best_ratio = ratio; - - if(columns == 1) - break; - - --columns; - rows = ceil(item_count/columns); - } - - if(vertical) - return eX * best_rows + eY * best_columns; - else - return eX * best_columns + eY * best_rows; -} - -// return the string of the onscreen race timer -string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname) -{ - string col; - string timestr; - string cpname; - string lapstr; - lapstr = ""; - - if(theirtime == 0) // goal hit - { - if(mytime > 0) - { - timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS)); - col = "^1"; - } - else if(mytime == 0) - { - timestr = "+0.0"; - col = "^3"; - } - else - { - timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS)); - col = "^2"; - } - - if(lapdelta > 0) - { - lapstr = sprintf(_(" (-%dL)"), lapdelta); - col = "^2"; - } - else if(lapdelta < 0) - { - lapstr = sprintf(_(" (+%dL)"), -lapdelta); - col = "^1"; - } - } - else if(theirtime > 0) // anticipation - { - if(mytime >= theirtime) - timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS)); - else - timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime)); - col = "^3"; - } - else - { - col = "^7"; - timestr = ""; - } - - if(cp == 254) - cpname = _("Start line"); - else if(cp == 255) - cpname = _("Finish line"); - else if(cp) - cpname = sprintf(_("Intermediate %d"), cp); - else - cpname = _("Finish line"); - - if(theirtime < 0) - return strcat(col, cpname); - else if(theirname == "") - return strcat(col, sprintf("%s (%s)", cpname, timestr)); - else - return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr))); -} - -// Check if the given name already exist in race rankings? In that case, where? (otherwise return 0) -int race_CheckName(string net_name) -{ - int i; - for (i=RANKINGS_CNT-1;i>=0;--i) - if(grecordholder[i] == net_name) - return i+1; - return 0; -} - -/* -================== -HUD panels -================== -*/ - -//basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu -void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag) -{ - if(!length_ratio || !theAlpha) - return; - if(length_ratio > 1) - length_ratio = 1; - if (baralign == 3) - { - if(length_ratio < -1) - length_ratio = -1; - } - else if(length_ratio < 0) - return; - - vector square; - vector width, height; - if(vertical) { - pic = strcat(hud_skin_path, "/", pic, "_vertical"); - if(precache_pic(pic) == "") { - pic = "gfx/hud/default/progressbar_vertical"; - } - - if (baralign == 1) // bottom align - theOrigin.y += (1 - length_ratio) * theSize.y; - else if (baralign == 2) // center align - theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y; - else if (baralign == 3) // center align, positive values down, negative up - { - theSize.y *= 0.5; - if (length_ratio > 0) - theOrigin.y += theSize.y; - else - { - theOrigin.y += (1 + length_ratio) * theSize.y; - length_ratio = -length_ratio; - } - } - theSize.y *= length_ratio; - - vector bH; - width = eX * theSize.x; - height = eY * theSize.y; - if(theSize.y <= theSize.x * 2) - { - // button not high enough - // draw just upper and lower part then - square = eY * theSize.y * 0.5; - bH = eY * (0.25 * theSize.y / (theSize.x * 2)); - drawsubpic(theOrigin, square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag); - drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag); - } - else - { - square = eY * theSize.x; - drawsubpic(theOrigin, width + square, pic, '0 0 0', '1 0.25 0', theColor, theAlpha, drawflag); - drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5 0', theColor, theAlpha, drawflag); - drawsubpic(theOrigin + height - square, width + square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag); - } - } else { - pic = strcat(hud_skin_path, "/", pic); - if(precache_pic(pic) == "") { - pic = "gfx/hud/default/progressbar"; - } - - if (baralign == 1) // right align - theOrigin.x += (1 - length_ratio) * theSize.x; - else if (baralign == 2) // center align - theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x; - else if (baralign == 3) // center align, positive values on the right, negative on the left - { - theSize.x *= 0.5; - if (length_ratio > 0) - theOrigin.x += theSize.x; - else - { - theOrigin.x += (1 + length_ratio) * theSize.x; - length_ratio = -length_ratio; - } - } - theSize.x *= length_ratio; - - vector bW; - width = eX * theSize.x; - height = eY * theSize.y; - if(theSize.x <= theSize.y * 2) - { - // button not wide enough - // draw just left and right part then - square = eX * theSize.x * 0.5; - bW = eX * (0.25 * theSize.x / (theSize.y * 2)); - drawsubpic(theOrigin, square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag); - drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag); - } - else - { - square = eX * theSize.y; - drawsubpic(theOrigin, height + square, pic, '0 0 0', '0.25 1 0', theColor, theAlpha, drawflag); - drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0.25 0 0', '0.5 1 0', theColor, theAlpha, drawflag); - drawsubpic(theOrigin + width - square, height + square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag); - } - } -} - -void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag) -{ - if(!theAlpha) - return; - - string pic; - pic = strcat(hud_skin_path, "/num_leading"); - if(precache_pic(pic) == "") { - pic = "gfx/hud/default/num_leading"; - } - - drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag); - if(mySize.x/mySize.y > 2) - drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag); - drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag); -} - -// Weapon icons (#0) -// -entity weaponorder[Weapons_MAX]; -void weaponorder_swap(int i, int j, entity pass) -{ - entity h = weaponorder[i]; - weaponorder[i] = weaponorder[j]; - weaponorder[j] = h; -} - -string weaponorder_cmp_str; -int weaponorder_cmp(int i, int j, entity pass) -{ - int ai, aj; - ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].weapon), 0); - aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].weapon), 0); - return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string) -} - -void HUD_Weapons(void) -{SELFPARAM(); - // declarations - WepSet weapons_stat = WepSet_GetFromStat(); - int i; - float f, a; - float screen_ar; - vector center = '0 0 0'; - int weapon_count, weapon_id; - int row, column, rows = 0, columns = 0; - bool vertical_order = true; - float aspect = autocvar_hud_panel_weapons_aspect; - - float timeout = autocvar_hud_panel_weapons_timeout; - float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0); - float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0); - - vector barsize = '0 0 0', baroffset = '0 0 0'; - vector ammo_color = '1 0 1'; - float ammo_alpha = 1; - - float when = max(1, autocvar_hud_panel_weapons_complainbubble_time); - float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime); - - vector weapon_pos, weapon_size = '0 0 0'; - vector color; - - // check to see if we want to continue - if(hud != HUD_NORMAL) return; - - if(!autocvar__hud_configure) - { - if((!autocvar_hud_panel_weapons) || (spectatee_status == -1)) - return; - if(timeout && time >= weapontime + timeout + timeout_effect_length) - if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin))) - { - weaponprevtime = time; - return; - } - } - - // update generic hud functions - HUD_Panel_UpdateCvars(); - - // figure out weapon order (how the weapons are sorted) // TODO make this configurable - if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0]) - { - int weapon_cnt; - if(weaponorder_bypriority) - strunzone(weaponorder_bypriority); - if(weaponorder_byimpulse) - strunzone(weaponorder_byimpulse); - - weaponorder_bypriority = strzone(autocvar_cl_weaponpriority); - weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority)))); - weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " "); - - weapon_cnt = 0; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - { - setself(get_weaponinfo(i)); - if(self.impulse >= 0) - { - weaponorder[weapon_cnt] = self; - ++weapon_cnt; - } - } - for(i = weapon_cnt; i < Weapons_MAX; ++i) - weaponorder[i] = world; - heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world); - - weaponorder_cmp_str = string_null; - } - - if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime) - complain_weapon = 0; - - if(autocvar__hud_configure) - { - if(!weapons_stat) - for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5)) - weapons_stat |= WepSet_FromWeapon(i); - - #if 0 - /// debug code - if(cvar("wep_add")) - { - weapons_stat = '0 0 0'; - float countw = 1 + floor((floor(time * cvar("wep_add"))) % (Weapons_COUNT - 1)); - for(i = WEP_FIRST; i <= countw; ++i) - weapons_stat |= WepSet_FromWeapon(i); - } - #endif - } - - // determine which weapons are going to be shown - if (autocvar_hud_panel_weapons_onlyowned) - { - if(autocvar__hud_configure) - { - if(menu_enabled != 2) - HUD_Panel_DrawBg(1); // also draw the bg of the entire panel - } - - // do we own this weapon? - weapon_count = 0; - for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) - if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon)) - ++weapon_count; - - - // might as well commit suicide now, no reason to live ;) - if (weapon_count == 0) - return; - - vector old_panel_size = panel_size; - vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding; - - // get the all-weapons layout - int nHidden = 0; - WepSet weapons_stat = WepSet_GetFromStat(); - for (int i = WEP_FIRST; i <= WEP_LAST; ++i) { - WepSet weapons_wep = WepSet_FromWeapon(i); - if (weapons_stat & weapons_wep) continue; - Weapon w = get_weaponinfo(i); - if (w.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1; - } - vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, padded_panel_size, aspect); - columns = table_size.x; - rows = table_size.y; - weapon_size.x = padded_panel_size.x / columns; - weapon_size.y = padded_panel_size.y / rows; - - // NOTE: although weapons should aways look the same even if onlyowned is enabled, - // we enlarge them a bit when possible to better match the desired aspect ratio - if(padded_panel_size.x / padded_panel_size.y < aspect) - { - // maximum number of rows that allows to display items with the desired aspect ratio - int max_rows = floor(padded_panel_size.y / (weapon_size.x / aspect)); - columns = min(columns, ceil(weapon_count / max_rows)); - rows = ceil(weapon_count / columns); - weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect); - weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y); - vertical_order = false; - } - else - { - int max_columns = floor(padded_panel_size.x / (weapon_size.y * aspect)); - rows = min(rows, ceil(weapon_count / max_columns)); - columns = ceil(weapon_count / rows); - weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y); - weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect); - vertical_order = true; - } - - // reduce size of the panel - panel_size.x = columns * weapon_size.x; - panel_size.y = rows * weapon_size.y; - panel_size += '2 2 0' * panel_bg_padding; - - // center the resized panel, or snap it to the screen edge when close enough - if(panel_pos.x > vid_conwidth * 0.001) - { - if(panel_pos.x + old_panel_size.x > vid_conwidth * 0.999) - panel_pos.x += old_panel_size.x - panel_size.x; - else - panel_pos.x += (old_panel_size.x - panel_size.x) / 2; - } - else if(old_panel_size.x > vid_conwidth * 0.999) - panel_pos.x += (old_panel_size.x - panel_size.x) / 2; - - if(panel_pos.y > vid_conheight * 0.001) - { - if(panel_pos.y + old_panel_size.y > vid_conheight * 0.999) - panel_pos.y += old_panel_size.y - panel_size.y; - else - panel_pos.y += (old_panel_size.y - panel_size.y) / 2; - } - else if(old_panel_size.y > vid_conheight * 0.999) - panel_pos.y += (old_panel_size.y - panel_size.y) / 2; - } - else - weapon_count = (Weapons_COUNT - 1); - - // animation for fading in/out the panel respectively when not in use - if(!autocvar__hud_configure) - { - if (timeout && time >= weapontime + timeout) // apply timeout effect if needed - { - f = bound(0, (time - (weapontime + timeout)) / timeout_effect_length, 1); - - // fade the panel alpha - if(autocvar_hud_panel_weapons_timeout_effect == 1) - { - panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * f + (1 - f)); - panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * f + (1 - f)); - } - else if(autocvar_hud_panel_weapons_timeout_effect == 3) - { - panel_bg_alpha *= (1 - f); - panel_fg_alpha *= (1 - f); - } - - // move the panel off the screen - if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3) - { - f *= f; // for a cooler movement - center.x = panel_pos.x + panel_size.x/2; - center.y = panel_pos.y + panel_size.y/2; - screen_ar = vid_conwidth/vid_conheight; - if (center.x/center.y < screen_ar) //bottom left - { - if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom - panel_pos.y += f * (vid_conheight - panel_pos.y); - else //left - panel_pos.x -= f * (panel_pos.x + panel_size.x); - } - else //top right - { - if ((vid_conwidth - center.x)/center.y < screen_ar) //right - panel_pos.x += f * (vid_conwidth - panel_pos.x); - else //top - panel_pos.y -= f * (panel_pos.y + panel_size.y); - } - if(f == 1) - center.x = -1; // mark the panel as off screen - } - weaponprevtime = time - (1 - f) * timein_effect_length; - } - else if (timeout && time < weaponprevtime + timein_effect_length) // apply timein effect if needed - { - f = bound(0, (time - weaponprevtime) / timein_effect_length, 1); - - // fade the panel alpha - if(autocvar_hud_panel_weapons_timeout_effect == 1) - { - panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * (1 - f) + f); - panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * (1 - f) + f); - } - else if(autocvar_hud_panel_weapons_timeout_effect == 3) - { - panel_bg_alpha *= (f); - panel_fg_alpha *= (f); - } - - // move the panel back on screen - if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3) - { - f *= f; // for a cooler movement - f = 1 - f; - center.x = panel_pos.x + panel_size.x/2; - center.y = panel_pos.y + panel_size.y/2; - screen_ar = vid_conwidth/vid_conheight; - if (center.x/center.y < screen_ar) //bottom left - { - if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom - panel_pos.y += f * (vid_conheight - panel_pos.y); - else //left - panel_pos.x -= f * (panel_pos.x + panel_size.x); - } - else //top right - { - if ((vid_conwidth - center.x)/center.y < screen_ar) //right - panel_pos.x += f * (vid_conwidth - panel_pos.x); - else //top - panel_pos.y -= f * (panel_pos.y + panel_size.y); - } - } - } - } - - // draw the background, then change the virtual size of it to better fit other items inside - HUD_Panel_DrawBg(1); - - if(center.x == -1) - return; - - if(panel_bg_padding) - { - panel_pos += '1 1 0' * panel_bg_padding; - panel_size -= '2 2 0' * panel_bg_padding; - } - - // after the sizing and animations are done, update the other values - - if(!rows) // if rows is > 0 onlyowned code has already updated these vars - { - vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1), panel_size, aspect); - columns = table_size.x; - rows = table_size.y; - weapon_size.x = panel_size.x / columns; - weapon_size.y = panel_size.y / rows; - vertical_order = (panel_size.x / panel_size.y >= aspect); - } - - // calculate position/size for visual bar displaying ammount of ammo status - if (autocvar_hud_panel_weapons_ammo) - { - ammo_color = stov(autocvar_hud_panel_weapons_ammo_color); - ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha; - - if(weapon_size.x/weapon_size.y > aspect) - { - barsize.x = aspect * weapon_size.y; - barsize.y = weapon_size.y; - baroffset.x = (weapon_size.x - barsize.x) / 2; - } - else - { - barsize.y = 1/aspect * weapon_size.x; - barsize.x = weapon_size.x; - baroffset.y = (weapon_size.y - barsize.y) / 2; - } - } - if(autocvar_hud_panel_weapons_accuracy) - Accuracy_LoadColors(); - - // draw items - row = column = 0; - vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1); - vector noncurrent_pos = '0 0 0'; - vector noncurrent_size = weapon_size * bound(0, autocvar_hud_panel_weapons_noncurrent_scale, 1); - float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1); - bool isCurrent; - - for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) - { - // retrieve information about the current weapon to be drawn - setself(weaponorder[i]); - weapon_id = self.impulse; - isCurrent = (self.weapon == switchweapon); - - // skip if this weapon doesn't exist - if(!self || weapon_id < 0) { continue; } - - // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon - if(autocvar_hud_panel_weapons_onlyowned) - if (!((weapons_stat & WepSet_FromWeapon(self.weapon)) || (self.weapon == complain_weapon))) - continue; - - // figure out the drawing position of weapon - weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y); - noncurrent_pos.x = weapon_pos.x + (weapon_size.x - noncurrent_size.x) / 2; - noncurrent_pos.y = weapon_pos.y + (weapon_size.y - noncurrent_size.y) / 2; - - // draw background behind currently selected weapon - if(isCurrent) - drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - - // draw the weapon accuracy - if(autocvar_hud_panel_weapons_accuracy) - { - float panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST]; - if(panel_weapon_accuracy >= 0) - { - color = Accuracy_GetColor(panel_weapon_accuracy); - drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL); - } - } - - // drawing all the weapon items - if(weapons_stat & WepSet_FromWeapon(self.weapon)) - { - // draw the weapon image - if(isCurrent) - drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - else - drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '1 1 1', noncurrent_alpha, DRAWFLAG_NORMAL); - - // draw weapon label string - switch(autocvar_hud_panel_weapons_label) - { - case 1: // weapon number - drawstring(weapon_pos, ftos(weapon_id), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - break; - - case 2: // bind - drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("weapon_group_", ftos(weapon_id))), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - break; - - case 3: // weapon name - drawstring(weapon_pos, strtolower(self.message), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - break; - - default: // nothing - break; - } - - // draw ammo status bar - if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none)) - { - float ammo_full; - a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have? - - if(a > 0) - { - switch(self.ammo_field) - { - case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break; - case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break; - case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break; - case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break; - case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break; - case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break; - default: ammo_full = 60; - } - - drawsetcliparea( - weapon_pos.x + baroffset.x, - weapon_pos.y + baroffset.y, - barsize.x * bound(0, a/ammo_full, 1), - barsize.y - ); - - drawpic_aspect_skin( - weapon_pos, - "weapon_ammo", - weapon_size, - ammo_color, - ammo_alpha, - DRAWFLAG_NORMAL - ); - - drawresetcliparea(); - } - } - } - else // draw a "ghost weapon icon" if you don't have the weapon - { - drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '0.2 0.2 0.2', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL); - } - - // draw the complain message - if(self.weapon == complain_weapon) - { - if(fadetime) - a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1)); - else - a = ((complain_weapon_time + when > time) ? 1 : 0); - - string s; - if(complain_weapon_type == 0) { - s = _("Out of ammo"); - color = stov(autocvar_hud_panel_weapons_complainbubble_color_outofammo); - } - else if(complain_weapon_type == 1) { - s = _("Don't have"); - color = stov(autocvar_hud_panel_weapons_complainbubble_color_donthave); - } - else { - s = _("Unavailable"); - color = stov(autocvar_hud_panel_weapons_complainbubble_color_unavailable); - } - float padding = autocvar_hud_panel_weapons_complainbubble_padding; - drawpic_aspect_skin(weapon_pos + '1 1 0' * padding, "weapon_complainbubble", weapon_size - '2 2 0' * padding, color, a * panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - - #if 0 - /// debug code - if(!autocvar_hud_panel_weapons_onlyowned) - { - drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL); - drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } - #endif - - // continue with new position for the next weapon - if(vertical_order) - { - ++column; - if(column >= columns) - { - column = 0; - ++row; - } - } - else - { - ++row; - if(row >= rows) - { - row = 0; - ++column; - } - } - } -} - -// Ammo (#1) -void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color) -{ - HUD_Panel_DrawProgressBar( - myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, - mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, - autocvar_hud_panel_ammo_progressbar_name, - progress, 0, 0, color, - autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); -} - -void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time) -{ - float bonusNades = getstatf(STAT_NADE_BONUS); - float bonusProgress = getstatf(STAT_NADE_BONUS_SCORE); - float bonusType = getstati(STAT_NADE_BONUS_TYPE); - vector nadeColor = Nades[bonusType].m_color; - string nadeIcon = Nades[bonusType].m_icon; - - vector iconPos, textPos; - - if(autocvar_hud_panel_ammo_iconalign) - { - iconPos = myPos + eX * 2 * mySize.y; - textPos = myPos; - } - else - { - iconPos = myPos; - textPos = myPos + eX * mySize.y; - } - - if(bonusNades > 0 || bonusProgress > 0) - { - DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor); - - if(autocvar_hud_panel_ammo_text) - drawstring_aspect(textPos, ftos(bonusNades), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - - if(draw_expanding) - drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, expand_time); - - drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } -} - -void DrawAmmoItem(vector myPos, vector mySize, .int ammoType, bool isCurrent, bool isInfinite) -{ - if(ammoType == ammo_none) - return; - - // Initialize variables - - int ammo; - if(autocvar__hud_configure) - { - isCurrent = (ammoType == ammo_rockets); // Rockets always current - ammo = 60; - } - else - ammo = getstati(GetAmmoStat(ammoType)); - - if(!isCurrent) - { - float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1); - myPos = myPos + (mySize - mySize * scale) * 0.5; - mySize = mySize * scale; - } - - vector iconPos, textPos; - if(autocvar_hud_panel_ammo_iconalign) - { - iconPos = myPos + eX * 2 * mySize.y; - textPos = myPos; - } - else - { - iconPos = myPos; - textPos = myPos + eX * mySize.y; - } - - bool isShadowed = (ammo <= 0 && !isCurrent && !isInfinite); - - vector iconColor = isShadowed ? '0 0 0' : '1 1 1'; - vector textColor; - if(isInfinite) - textColor = '0.2 0.95 0'; - else if(isShadowed) - textColor = '0 0 0'; - else if(ammo < 10) - textColor = '0.8 0.04 0'; - else - textColor = '1 1 1'; - - float alpha; - if(isCurrent) - alpha = panel_fg_alpha; - else if(isShadowed) - alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5; - else - alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1); - - string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E) - - // Draw item - - if(isCurrent) - drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - - if(ammo > 0 && autocvar_hud_panel_ammo_progressbar) - HUD_Panel_DrawProgressBar(myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, autocvar_hud_panel_ammo_progressbar_name, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL); - - if(autocvar_hud_panel_ammo_text) - drawstring_aspect(textPos, text, eX * (2/3) * mySize.x + eY * mySize.y, textColor, alpha, DRAWFLAG_NORMAL); - - drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize.y, iconColor, alpha, DRAWFLAG_NORMAL); -} - -int nade_prevstatus; -int nade_prevframe; -float nade_statuschange_time; -void HUD_Ammo(void) -{ - if(hud != HUD_NORMAL) return; - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_ammo) return; - if(spectatee_status == -1) return; - } - - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - int rows = 0, columns, row, column; - float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE); - bool draw_nades = (nade_cnt > 0 || nade_score > 0); - float nade_statuschange_elapsedtime; - int total_ammo_count; - - vector ammo_size; - if (autocvar_hud_panel_ammo_onlycurrent) - total_ammo_count = 1; - else - total_ammo_count = AMMO_COUNT; - - if(draw_nades) - { - ++total_ammo_count; - if (nade_cnt != nade_prevframe) - { - nade_statuschange_time = time; - nade_prevstatus = nade_prevframe; - nade_prevframe = nade_cnt; - } - } - else - nade_prevstatus = nade_prevframe = nade_statuschange_time = 0; - - rows = HUD_GetRowCount(total_ammo_count, mySize, 3); - columns = ceil((total_ammo_count)/rows); - ammo_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - - vector offset = '0 0 0'; // fteqcc sucks - float newSize; - if(ammo_size.x/ammo_size.y > 3) - { - newSize = 3 * ammo_size.y; - offset.x = ammo_size.x - newSize; - pos.x += offset.x/2; - ammo_size.x = newSize; - } - else - { - newSize = 1/3 * ammo_size.x; - offset.y = ammo_size.y - newSize; - pos.y += offset.y/2; - ammo_size.y = newSize; - } - - int i; - bool infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO); - row = column = 0; - if(autocvar_hud_panel_ammo_onlycurrent) - { - if(autocvar__hud_configure) - { - DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false); - } - else - { - DrawAmmoItem( - pos, - ammo_size, - (get_weaponinfo(switchweapon)).ammo_field, - true, - infinite_ammo - ); - } - - ++row; - if(row >= rows) - { - row = 0; - column = column + 1; - } - } - else - { - .int ammotype; - row = column = 0; - for(i = 0; i < AMMO_COUNT; ++i) - { - ammotype = GetAmmoFieldFromNum(i); - DrawAmmoItem( - pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), - ammo_size, - ammotype, - ((get_weaponinfo(switchweapon)).ammo_field == ammotype), - infinite_ammo - ); - - ++row; - if(row >= rows) - { - row = 0; - column = column + 1; - } - } - } - - if (draw_nades) - { - nade_statuschange_elapsedtime = time - nade_statuschange_time; - - float f = bound(0, nade_statuschange_elapsedtime*2, 1); - - DrawAmmoNades(pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f); - } - - draw_endBoldFont(); -} - -void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha, float fadelerp) -{ - vector newPos = '0 0 0', newSize = '0 0 0'; - vector picpos, numpos; - - if (vertical) - { - if(mySize.y/mySize.x > 2) - { - newSize.y = 2 * mySize.x; - newSize.x = mySize.x; - - newPos.y = myPos.y + (mySize.y - newSize.y) / 2; - newPos.x = myPos.x; - } - else - { - newSize.x = 1/2 * mySize.y; - newSize.y = mySize.y; - - newPos.x = myPos.x + (mySize.x - newSize.x) / 2; - newPos.y = myPos.y; - } - - if(icon_right_align) - { - numpos = newPos; - picpos = newPos + eY * newSize.x; - } - else - { - picpos = newPos; - numpos = newPos + eY * newSize.x; - } - - newSize.y /= 2; - drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); - // make number smaller than icon, it looks better - // reduce only y to draw numbers with different number of digits with the same y size - numpos.y += newSize.y * ((1 - 0.7) / 2); - newSize.y *= 0.7; - drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); - return; - } - - if(mySize.x/mySize.y > 3) - { - newSize.x = 3 * mySize.y; - newSize.y = mySize.y; - - newPos.x = myPos.x + (mySize.x - newSize.x) / 2; - newPos.y = myPos.y; - } - else - { - newSize.y = 1/3 * mySize.x; - newSize.x = mySize.x; - - newPos.y = myPos.y + (mySize.y - newSize.y) / 2; - newPos.x = myPos.x; - } - - if(icon_right_align) // right align - { - numpos = newPos; - picpos = newPos + eX * 2 * newSize.y; - } - else // left align - { - numpos = newPos + eX * newSize.y; - picpos = newPos; - } - - // NOTE: newSize_x is always equal to 3 * mySize_y so we can use - // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y - drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); - drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); -} - -void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha) -{ - DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0); -} - -// Powerups (#2) -// - -// Powerup item fields (reusing existing fields) -.string message; // Human readable name -.string netname; // Icon name -.vector colormod; // Color -.float count; // Time left -.float lifetime; // Maximum time - -entity powerupItems; -int powerupItemsCount; - -void resetPowerupItems() -{ - entity item; - for(item = powerupItems; item; item = item.chain) - item.count = 0; - - powerupItemsCount = 0; -} - -void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime) -{ - if(!powerupItems) - powerupItems = spawn(); - - entity item; - for(item = powerupItems; item.count; item = item.chain) - if(!item.chain) - item.chain = spawn(); - - item.message = name; - item.netname = icon; - item.colormod = color; - item.count = currentTime; - item.lifetime = lifeTime; - - ++powerupItemsCount; -} - -int getPowerupItemAlign(int align, int column, int row, int columns, int rows, bool isVertical) -{ - if(align < 2) - return align; - - bool isTop = isVertical && rows > 1 && row == 0; - bool isBottom = isVertical && rows > 1 && row == rows-1; - bool isLeft = !isVertical && columns > 1 && column == 0; - bool isRight = !isVertical && columns > 1 && column == columns-1; - - if(isTop || isLeft) return (align == 2) ? 1 : 0; - if(isBottom || isRight) return (align == 2) ? 0 : 1; - - return 2; -} - -void HUD_Powerups() -{ - int allItems = getstati(STAT_ITEMS, 0, 24); - int allBuffs = getstati(STAT_BUFFS, 0, 24); - int strengthTime, shieldTime, superTime; - - // Initialize items - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_powerups) return; - if(spectatee_status == -1) return; - if(getstati(STAT_HEALTH) <= 0) return; - if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return; - - strengthTime = bound(0, getstatf(STAT_STRENGTH_FINISHED) - time, 99); - shieldTime = bound(0, getstatf(STAT_INVINCIBLE_FINISHED) - time, 99); - superTime = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99); - - if(allItems & IT_UNLIMITED_SUPERWEAPONS) - superTime = 99; - - // Prevent stuff to show up on mismatch that will be fixed next frame - if(!(allItems & IT_SUPERWEAPON)) - superTime = 0; - } - else - { - strengthTime = 15; - shieldTime = 27; - superTime = 13; - allBuffs = 0; - } - - // Add items to linked list - resetPowerupItems(); - - if(strengthTime) - addPowerupItem("Strength", "strength", autocvar_hud_progressbar_strength_color, strengthTime, 30); - if(shieldTime) - addPowerupItem("Shield", "shield", autocvar_hud_progressbar_shield_color, shieldTime, 30); - if(superTime) - addPowerupItem("Superweapons", "superweapons", autocvar_hud_progressbar_superweapons_color, superTime, 30); - - FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( - addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, getstatf(STAT_BUFF_TIME) - time, 99), 60); - )); - - if(!powerupItemsCount) - return; - - // Draw panel background - HUD_Panel_UpdateCvars(); - HUD_Panel_DrawBg(1); - - // Set drawing area - vector pos = panel_pos; - vector size = panel_size; - bool isVertical = size.y > size.x; - - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - size -= '2 2 0' * panel_bg_padding; - } - - // Find best partitioning of the drawing area - const float DESIRED_ASPECT = 6; - float aspect = 0, a; - int columns = 0, c; - int rows = 0, r; - int i = 1; - - do - { - c = floor(powerupItemsCount / i); - r = ceil(powerupItemsCount / c); - a = isVertical ? (size.y/r) / (size.x/c) : (size.x/c) / (size.y/r); - - if(i == 1 || fabs(DESIRED_ASPECT - a) < fabs(DESIRED_ASPECT - aspect)) - { - aspect = a; - columns = c; - rows = r; - } - } - while(++i <= powerupItemsCount); - - // Prevent single items from getting too wide - if(powerupItemsCount == 1 && aspect > DESIRED_ASPECT) - { - if(isVertical) - { - size.y *= 0.5; - pos.y += size.y * 0.5; - } - else - { - size.x *= 0.5; - pos.x += size.x * 0.5; - } - } - - // Draw items from linked list - vector itemPos = pos; - vector itemSize = eX * (size.x / columns) + eY * (size.y / rows); - vector textColor = '1 1 1'; - - int fullSeconds = 0; - int align = 0; - int column = 0; - int row = 0; - - draw_beginBoldFont(); - for(entity item = powerupItems; item.count; item = item.chain) - { - itemPos = eX * (pos.x + column * itemSize.x) + eY * (pos.y + row * itemSize.y); - - // Draw progressbar - if(autocvar_hud_panel_powerups_progressbar) - { - align = getPowerupItemAlign(autocvar_hud_panel_powerups_baralign, column, row, columns, rows, isVertical); - HUD_Panel_DrawProgressBar(itemPos, itemSize, "progressbar", item.count / item.lifetime, isVertical, align, item.colormod, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - // Draw icon and text - if(autocvar_hud_panel_powerups_text) - { - align = getPowerupItemAlign(autocvar_hud_panel_powerups_iconalign, column, row, columns, rows, isVertical); - fullSeconds = ceil(item.count); - textColor = '0.6 0.6 0.6' + (item.colormod * 0.4); - - if(item.count > 1) - DrawNumIcon(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha); - if(item.count <= 5) - DrawNumIcon_expanding(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha, bound(0, (fullSeconds - item.count) / 0.5, 1)); - } - - // Determine next section - if(isVertical) - { - if(++column >= columns) - { - column = 0; - ++row; - } - } - else - { - if(++row >= rows) - { - row = 0; - ++column; - } - } - } - draw_endBoldFont(); -} - -// Health/armor (#3) -// - - -void HUD_HealthArmor(void) -{ - int armor, health, fuel; - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_healtharmor) return; - if(hud != HUD_NORMAL) return; - if(spectatee_status == -1) return; - - health = getstati(STAT_HEALTH); - if(health <= 0) - { - prev_health = -1; - return; - } - armor = getstati(STAT_ARMOR); - - // code to check for spectatee_status changes is in Ent_ClientData() - // prev_p_health and prev_health can be set to -1 there - - if (prev_p_health == -1) - { - // no effect - health_beforedamage = 0; - armor_beforedamage = 0; - health_damagetime = 0; - armor_damagetime = 0; - prev_health = health; - prev_armor = armor; - old_p_health = health; - old_p_armor = armor; - prev_p_health = health; - prev_p_armor = armor; - } - else if (prev_health == -1) - { - //start the load effect - health_damagetime = 0; - armor_damagetime = 0; - prev_health = 0; - prev_armor = 0; - } - fuel = getstati(STAT_FUEL); - } - else - { - health = 150; - armor = 75; - fuel = 20; - } - - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - int baralign = autocvar_hud_panel_healtharmor_baralign; - int iconalign = autocvar_hud_panel_healtharmor_iconalign; - - int maxhealth = autocvar_hud_panel_healtharmor_maxhealth; - int maxarmor = autocvar_hud_panel_healtharmor_maxarmor; - if(autocvar_hud_panel_healtharmor == 2) // combined health and armor display - { - vector v; - v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id); - - float x; - x = floor(v.x + 1); - - float maxtotal = maxhealth + maxarmor; - string biggercount; - if(v.z) // NOT fully armored - { - biggercount = "health"; - if(autocvar_hud_panel_healtharmor_progressbar) - HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - if(armor) - if(autocvar_hud_panel_healtharmor_text) - drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL); - } - else - { - biggercount = "armor"; - if(autocvar_hud_panel_healtharmor_progressbar) - HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - if(health) - if(autocvar_hud_panel_healtharmor_text) - drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } - if(autocvar_hud_panel_healtharmor_text) - DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1); - - if(fuel) - HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL); - } - else - { - float panel_ar = mySize.x/mySize.y; - bool is_vertical = (panel_ar < 1); - vector health_offset = '0 0 0', armor_offset = '0 0 0'; - if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1)) - { - mySize.x *= 0.5; - if (autocvar_hud_panel_healtharmor_flip) - health_offset.x = mySize.x; - else - armor_offset.x = mySize.x; - } - else - { - mySize.y *= 0.5; - if (autocvar_hud_panel_healtharmor_flip) - health_offset.y = mySize.y; - else - armor_offset.y = mySize.y; - } - - bool health_baralign, armor_baralign, fuel_baralign; - bool health_iconalign, armor_iconalign; - if (autocvar_hud_panel_healtharmor_flip) - { - armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1); - health_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1); - fuel_baralign = health_baralign; - armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1); - health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1); - } - else - { - health_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1); - armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1); - fuel_baralign = armor_baralign; - health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1); - armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1); - } - - //if(health) - { - if(autocvar_hud_panel_healtharmor_progressbar) - { - float p_health, pain_health_alpha; - p_health = health; - pain_health_alpha = 1; - if (autocvar_hud_panel_healtharmor_progressbar_gfx) - { - if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0) - { - if (fabs(prev_health - health) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth) - { - if (time - old_p_healthtime < 1) - old_p_health = prev_p_health; - else - old_p_health = prev_health; - old_p_healthtime = time; - } - if (time - old_p_healthtime < 1) - { - p_health += (old_p_health - health) * (1 - (time - old_p_healthtime)); - prev_p_health = p_health; - } - } - if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0) - { - if (prev_health - health >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage) - { - if (time - health_damagetime >= 1) - health_beforedamage = prev_health; - health_damagetime = time; - } - if (time - health_damagetime < 1) - { - float health_damagealpha = 1 - (time - health_damagetime)*(time - health_damagetime); - HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, health_beforedamage/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * health_damagealpha, DRAWFLAG_NORMAL); - } - } - prev_health = health; - - if (health <= autocvar_hud_panel_healtharmor_progressbar_gfx_lowhealth) - { - float BLINK_FACTOR = 0.15; - float BLINK_BASE = 0.85; - float BLINK_FREQ = 9; - pain_health_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); - } - } - HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, p_health/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * pain_health_alpha, DRAWFLAG_NORMAL); - } - if(autocvar_hud_panel_healtharmor_text) - DrawNumIcon(pos + health_offset, mySize, health, "health", is_vertical, health_iconalign, HUD_Get_Num_Color(health, maxhealth), 1); - } - - if(armor) - { - if(autocvar_hud_panel_healtharmor_progressbar) - { - float p_armor; - p_armor = armor; - if (autocvar_hud_panel_healtharmor_progressbar_gfx) - { - if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0) - { - if (fabs(prev_armor - armor) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth) - { - if (time - old_p_armortime < 1) - old_p_armor = prev_p_armor; - else - old_p_armor = prev_armor; - old_p_armortime = time; - } - if (time - old_p_armortime < 1) - { - p_armor += (old_p_armor - armor) * (1 - (time - old_p_armortime)); - prev_p_armor = p_armor; - } - } - if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0) - { - if (prev_armor - armor >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage) - { - if (time - armor_damagetime >= 1) - armor_beforedamage = prev_armor; - armor_damagetime = time; - } - if (time - armor_damagetime < 1) - { - float armor_damagealpha = 1 - (time - armor_damagetime)*(time - armor_damagetime); - HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, armor_beforedamage/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * armor_damagealpha, DRAWFLAG_NORMAL); - } - } - prev_armor = armor; - } - HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, p_armor/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - if(autocvar_hud_panel_healtharmor_text) - DrawNumIcon(pos + armor_offset, mySize, armor, "armor", is_vertical, armor_iconalign, HUD_Get_Num_Color(armor, maxarmor), 1); - } - - if(fuel) - { - if (is_vertical) - mySize.x *= 0.2 / 2; //if vertical always halve x to not cover too much numbers with 3 digits - else - mySize.y *= 0.2; - if (panel_ar >= 4) - mySize.x *= 2; //restore full panel size - else if (panel_ar < 1/4) - mySize.y *= 2; //restore full panel size - HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL); - } - } - - draw_endBoldFont(); -} - -// Notification area (#4) -// - -void HUD_Notify_Push(string icon, string attacker, string victim) -{ - if (icon == "") - return; - - ++notify_count; - --notify_index; - - if (notify_index == -1) - notify_index = NOTIFY_MAX_ENTRIES-1; - - // Free old strings - if (notify_attackers[notify_index]) - strunzone(notify_attackers[notify_index]); - - if (notify_victims[notify_index]) - strunzone(notify_victims[notify_index]); - - if (notify_icons[notify_index]) - strunzone(notify_icons[notify_index]); - - // Allocate new strings - if (victim != "") - { - notify_attackers[notify_index] = strzone(attacker); - notify_victims[notify_index] = strzone(victim); - } - else - { - // In case of a notification without a victim, the attacker - // is displayed on the victim's side. Instead of special - // treatment later on, we can simply switch them here. - notify_attackers[notify_index] = string_null; - notify_victims[notify_index] = strzone(attacker); - } - - notify_icons[notify_index] = strzone(icon); - notify_times[notify_index] = time; -} - -void HUD_Notify(void) -{ - if (!autocvar__hud_configure) - if (!autocvar_hud_panel_notify) - return; - - HUD_Panel_UpdateCvars(); - HUD_Panel_DrawBg(1); - - if (!autocvar__hud_configure) - if (notify_count == 0) - return; - - vector pos, size; - pos = panel_pos; - size = panel_size; - - if (panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - size -= '2 2 0' * panel_bg_padding; - } - - float fade_start = max(0, autocvar_hud_panel_notify_time); - float fade_time = max(0, autocvar_hud_panel_notify_fadetime); - float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect); - - int entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size.y / size.x), NOTIFY_MAX_ENTRIES); - float entry_height = size.y / entry_count; - - float panel_width_half = size.x * 0.5; - float icon_width_half = entry_height * icon_aspect / 2; - float name_maxwidth = panel_width_half - icon_width_half - size.x * NOTIFY_ICON_MARGIN; - - vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize; - vector icon_size = (eX * icon_aspect + eY) * entry_height; - vector icon_left = eX * (panel_width_half - icon_width_half); - vector attacker_right = eX * name_maxwidth; - vector victim_left = eX * (size.x - name_maxwidth); - - vector attacker_pos, victim_pos, icon_pos; - string attacker, victim, icon; - int i, j, count, step, limit; - float alpha; - - if (autocvar_hud_panel_notify_flip) - { - // Order items from the top down - i = 0; - step = +1; - limit = entry_count; - } - else - { - // Order items from the bottom up - i = entry_count - 1; - step = -1; - limit = -1; - } - - for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count) - { - if(autocvar__hud_configure) - { - attacker = sprintf(_("Player %d"), count + 1); - victim = sprintf(_("Player %d"), count + 2); - icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2; - alpha = bound(0, 1.2 - count / entry_count, 1); - } - else - { - if (j == NOTIFY_MAX_ENTRIES) - j = 0; - - if (notify_times[j] + fade_start > time) - alpha = 1; - else if (fade_time != 0) - { - alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1); - if (alpha == 0) - break; - } - else - break; - - attacker = notify_attackers[j]; - victim = notify_victims[j]; - icon = notify_icons[j]; - } - - if (icon != "" && victim != "") - { - vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size.y)); - - icon_pos = pos + icon_left + eY * i * entry_height; - drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL); - - victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors); - victim_pos = pos + victim_left + name_top; - drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL); - - if (attacker != "") - { - attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors); - attacker_pos = pos + attacker_right - eX * stringwidth(attacker, true, font_size) + name_top; - drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL); - } - } - } - - notify_count = count; -} - -void HUD_Timer(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_timer) return; - } - - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - string timer; - float timelimit, elapsedTime, timeleft, minutesLeft; - - timelimit = getstatf(STAT_TIMELIMIT); - - timeleft = max(0, timelimit * 60 + getstatf(STAT_GAMESTARTTIME) - time); - timeleft = ceil(timeleft); - - minutesLeft = floor(timeleft / 60); - - vector timer_color; - if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit - timer_color = '1 1 1'; //white - else if(minutesLeft >= 1) - timer_color = '1 1 0'; //yellow - else - timer_color = '1 0 0'; //red - - if (autocvar_hud_panel_timer_increment || timelimit == 0 || warmup_stage) { - if (time < getstatf(STAT_GAMESTARTTIME)) { - //while restart is still active, show 00:00 - timer = seconds_tostring(0); - } else { - elapsedTime = floor(time - getstatf(STAT_GAMESTARTTIME)); //127 - timer = seconds_tostring(elapsedTime); - } - } else { - timer = seconds_tostring(timeleft); - } - - drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL); - - draw_endBoldFont(); -} - -// Radar (#6) -// - -float HUD_Radar_Clickable() -{ - return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden; -} - -void HUD_Radar_Show_Maximized(bool doshow,float clickable) -{ - hud_panel_radar_maximized = doshow; - hud_panel_radar_temp_hidden = 0; - - if ( doshow ) - { - if (clickable) - { - if(autocvar_hud_cursormode) - setcursormode(1); - hud_panel_radar_mouse = 1; - } - } - else if ( hud_panel_radar_mouse ) - { - hud_panel_radar_mouse = 0; - mouseClicked = 0; - if(autocvar_hud_cursormode) - if(!mv_active) - setcursormode(0); - } -} -void HUD_Radar_Hide_Maximized() -{ - HUD_Radar_Show_Maximized(false,false); -} - - -float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary) -{ - if(!hud_panel_radar_maximized || !hud_panel_radar_mouse || - autocvar__hud_configure || mv_active) - return false; - - if(bInputType == 3) - { - mousepos_x = nPrimary; - mousepos_y = nSecondary; - return true; - } - - if(nPrimary == K_MOUSE1) - { - if(bInputType == 0) // key pressed - mouseClicked |= S_MOUSE1; - else if(bInputType == 1) // key released - mouseClicked -= (mouseClicked & S_MOUSE1); - } - else if(nPrimary == K_MOUSE2) - { - if(bInputType == 0) // key pressed - mouseClicked |= S_MOUSE2; - else if(bInputType == 1) // key released - mouseClicked -= (mouseClicked & S_MOUSE2); - } - else if ( nPrimary == K_ESCAPE && bInputType == 0 ) - { - HUD_Radar_Hide_Maximized(); - } - else - { - // allow console/use binds to work without hiding the map - string con_keys; - float keys; - float i; - con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ; - keys = tokenize(con_keys); // findkeysforcommand returns data for this - for (i = 0; i < keys; ++i) - { - if(nPrimary == stof(argv(i))) - return false; - } - - if ( getstati(STAT_HEALTH) <= 0 ) - { - // Show scoreboard - if ( bInputType < 2 ) - { - con_keys = findkeysforcommand("+showscores", 0); - keys = tokenize(con_keys); - for (i = 0; i < keys; ++i) - { - if ( nPrimary == stof(argv(i)) ) - { - hud_panel_radar_temp_hidden = bInputType == 0; - return false; - } - } - } - } - else if ( bInputType == 0 ) - HUD_Radar_Hide_Maximized(); - - return false; - } - - return true; -} - -void HUD_Radar_Mouse() -{ - if ( !hud_panel_radar_mouse ) return; - if(mv_active) return; - - if ( intermission ) - { - HUD_Radar_Hide_Maximized(); - return; - } - - if(mouseClicked & S_MOUSE2) - { - HUD_Radar_Hide_Maximized(); - return; - } - - if(!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos_x = bound(0, mousepos_x, vid_conwidth); - mousepos_y = bound(0, mousepos_y, vid_conheight); - } - - HUD_Panel_UpdateCvars(); - - - panel_size = autocvar_hud_panel_radar_maximized_size; - panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth; - panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight; - panel_pos_x = (vid_conwidth - panel_size_x) / 2; - panel_pos_y = (vid_conheight - panel_size_y) / 2; - - if(mouseClicked & S_MOUSE1) - { - // click outside - if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x || - mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y ) - { - HUD_Radar_Hide_Maximized(); - return; - } - vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z); - localcmd(sprintf("cmd ons_spawn %f %f %f",pos_x,pos_y,pos_z)); - - HUD_Radar_Hide_Maximized(); - return; - } - - - const vector cursor_size = '32 32 0'; - drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursor_size, '1 1 1', 0.8, DRAWFLAG_NORMAL); -} - -void HUD_Radar(void) -{ - if (!autocvar__hud_configure) - { - if (hud_panel_radar_maximized) - { - if (!hud_draw_maximized) return; - } - else - { - if (autocvar_hud_panel_radar == 0) return; - if (autocvar_hud_panel_radar != 2 && !teamplay) return; - if(radar_panel_modified) - { - panel.update_time = time; // forces reload of panel attributes - radar_panel_modified = false; - } - } - } - - if ( hud_panel_radar_temp_hidden ) - return; - - HUD_Panel_UpdateCvars(); - - float f = 0; - - if (hud_panel_radar_maximized && !autocvar__hud_configure) - { - panel_size = autocvar_hud_panel_radar_maximized_size; - panel_size.x = bound(0.2, panel_size.x, 1) * vid_conwidth; - panel_size.y = bound(0.2, panel_size.y, 1) * vid_conheight; - panel_pos.x = (vid_conwidth - panel_size.x) / 2; - panel_pos.y = (vid_conheight - panel_size.y) / 2; - - string panel_bg; - panel_bg = strcat(hud_skin_path, "/border_default"); // always use the default border when maximized - if(precache_pic(panel_bg) == "") - panel_bg = "gfx/hud/default/border_default"; // fallback - if(!radar_panel_modified && panel_bg != panel.current_panel_bg) - radar_panel_modified = true; - if(panel.current_panel_bg) - strunzone(panel.current_panel_bg); - panel.current_panel_bg = strzone(panel_bg); - - switch(hud_panel_radar_maximized_zoommode) - { - default: - case 0: - f = current_zoomfraction; - break; - case 1: - f = 1 - current_zoomfraction; - break; - case 2: - f = 0; - break; - case 3: - f = 1; - break; - } - - switch(hud_panel_radar_maximized_rotation) - { - case 0: - teamradar_angle = view_angles.y - 90; - break; - default: - teamradar_angle = 90 * hud_panel_radar_maximized_rotation; - break; - } - } - if (!hud_panel_radar_maximized && !autocvar__hud_configure) - { - switch(hud_panel_radar_zoommode) - { - default: - case 0: - f = current_zoomfraction; - break; - case 1: - f = 1 - current_zoomfraction; - break; - case 2: - f = 0; - break; - case 3: - f = 1; - break; - } - - switch(hud_panel_radar_rotation) - { - case 0: - teamradar_angle = view_angles.y - 90; - break; - default: - teamradar_angle = 90 * hud_panel_radar_rotation; - break; - } - } - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - int color2; - entity tm; - float scale2d, normalsize, bigsize; - - teamradar_origin2d = pos + 0.5 * mySize; - teamradar_size2d = mySize; - - if(minimapname == "") - return; - - teamradar_loadcvars(); - - scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin); - teamradar_size2d = mySize; - - teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center - - // pixels per world qu to match the teamradar_size2d_x range in the longest dimension - if((hud_panel_radar_rotation == 0 && !hud_panel_radar_maximized) || (hud_panel_radar_maximized_rotation == 0 && hud_panel_radar_maximized)) - { - // max-min distance must fit the radar in any rotation - bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_scale)); - } - else - { - vector c0, c1, c2, c3, span; - c0 = rotate(mi_min, teamradar_angle * DEG2RAD); - c1 = rotate(mi_max, teamradar_angle * DEG2RAD); - c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD); - c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD); - span = '0 0 0'; - span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x); - span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y); - - // max-min distance must fit the radar in x=x, y=y - bigsize = min( - teamradar_size2d.x * scale2d / (1.05 * span.x), - teamradar_size2d.y * scale2d / (1.05 * span.y) - ); - } - - normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / hud_panel_radar_scale; - if(bigsize > normalsize) - normalsize = bigsize; - - teamradar_size = - f * bigsize - + (1 - f) * normalsize; - teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord( - f * mi_center - + (1 - f) * view_origin); - - drawsetcliparea( - pos.x, - pos.y, - mySize.x, - mySize.y - ); - - draw_teamradar_background(hud_panel_radar_foreground_alpha); - - for(tm = world; (tm = find(tm, classname, "radarlink")); ) - draw_teamradar_link(tm.origin, tm.velocity, tm.team); - - vector coord; - vector brightcolor; - for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); ) - { - if ( hud_panel_radar_mouse ) - if ( tm.health > 0 ) - if ( tm.team == myteam+1 ) - { - coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin)); - if ( vlen(mousepos-coord) < 8 ) - { - brightcolor_x = min(1,tm.teamradar_color_x*1.5); - brightcolor_y = min(1,tm.teamradar_color_y*1.5); - brightcolor_z = min(1,tm.teamradar_color_z*1.5); - drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0); - } - } - entity icon = RadarIcons[tm.teamradar_icon]; - draw_teamradar_icon(tm.origin, icon, tm, spritelookupcolor(tm, icon.netname, tm.teamradar_color), panel_fg_alpha); - } - for(tm = world; (tm = find(tm, classname, "entcs_receiver")); ) - { - color2 = GetPlayerColor(tm.sv_entnum); - //if(color == NUM_SPECTATOR || color == color2) - draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2)); - } - draw_teamradar_player(view_origin, view_angles, '1 1 1'); - - drawresetcliparea(); - - if ( hud_panel_radar_mouse ) - { - string message = "Click to select teleport destination"; - - if ( getstati(STAT_HEALTH) <= 0 ) - { - message = "Click to select spawn location"; - } - - drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, true, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2, - message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL); - - hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y; - } -} - -// Score (#7) -// -void HUD_UpdatePlayerTeams(); -void HUD_Score_Rankings(vector pos, vector mySize, entity me) -{ - float score; - entity tm = world, pl; - int SCOREPANEL_MAX_ENTRIES = 6; - float SCOREPANEL_ASPECTRATIO = 2; - int entries = bound(1, floor(SCOREPANEL_MAX_ENTRIES * mySize.y/mySize.x * SCOREPANEL_ASPECTRATIO), SCOREPANEL_MAX_ENTRIES); - vector fontsize = '1 1 0' * (mySize.y/entries); - - vector rgb, score_color; - rgb = '1 1 1'; - score_color = '1 1 1'; - - float name_size = mySize.x*0.75; - float spacing_size = mySize.x*0.04; - const float highlight_alpha = 0.2; - int i = 0, first_pl = 0; - bool me_printed = false; - string s; - if (autocvar__hud_configure) - { - float players_per_team = 0; - if (team_count) - { - // show team scores in the first line - float score_size = mySize.x / team_count; - players_per_team = max(2, ceil((entries - 1) / team_count)); - for(i=0; i= 5) - distribution_color = eY; - else if(distribution >= 0) - distribution_color = '1 1 1'; - else if(distribution >= -5) - distribution_color = '1 1 0'; - else - distribution_color = eX; - - string distribution_str; - distribution_str = ftos(distribution); - draw_beginBoldFont(); - if (distribution >= 0) - { - if (distribution > 0) - distribution_str = strcat("+", distribution_str); - HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } - drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(pos + eX * 0.75 * mySize.x, distribution_str, eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); - draw_endBoldFont(); - } else { // teamgames - float row, column, rows = 0, columns = 0; - vector offset = '0 0 0'; - vector score_pos, score_size; //for scores other than myteam - if(autocvar_hud_panel_score_rankings) - { - HUD_Score_Rankings(pos, mySize, me); - return; - } - if(spectatee_status == -1) - { - rows = HUD_GetRowCount(team_count, mySize, 3); - columns = ceil(team_count/rows); - score_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - - float newSize; - if(score_size.x/score_size.y > 3) - { - newSize = 3 * score_size.y; - offset.x = score_size.x - newSize; - pos.x += offset.x/2; - score_size.x = newSize; - } - else - { - newSize = 1/3 * score_size.x; - offset.y = score_size.y - newSize; - pos.y += offset.y/2; - score_size.y = newSize; - } - } - else - score_size = eX * mySize.x*(1/4) + eY * mySize.y*(1/3); - - float max_fragcount; - max_fragcount = -99; - draw_beginBoldFont(); - row = column = 0; - for(tm = teams.sort_next; tm; tm = tm.sort_next) { - if(tm.team == NUM_SPECTATOR) - continue; - score = tm.(teamscores[ts_primary]); - if(autocvar__hud_configure) - score = 123; - - if (score > max_fragcount) - max_fragcount = score; - - if (spectatee_status == -1) - { - score_pos = pos + eX * column * (score_size.x + offset.x) + eY * row * (score_size.y + offset.y); - if (max_fragcount == score) - HUD_Panel_DrawHighlight(score_pos, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(score_pos, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); - ++row; - if(row >= rows) - { - row = 0; - ++column; - } - } - else if(tm.team == myteam) { - if (max_fragcount == score) - HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); - } else { - if (max_fragcount == score) - HUD_Panel_DrawHighlight(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); - ++rows; - } - } - draw_endBoldFont(); - } -} - -// Race timer (#8) -// -void HUD_RaceTimer (void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_racetimer) return; - if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; - if(spectatee_status == -1) return; - } - - HUD_Panel_UpdateCvars(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - // always force 4:1 aspect - vector newSize = '0 0 0'; - if(mySize.x/mySize.y > 4) - { - newSize.x = 4 * mySize.y; - newSize.y = mySize.y; - - pos.x = pos.x + (mySize.x - newSize.x) / 2; - } - else - { - newSize.y = 1/4 * mySize.x; - newSize.x = mySize.x; - - pos.y = pos.y + (mySize.y - newSize.y) / 2; - } - mySize = newSize; - - float a, t; - string s, forcetime; - - if(autocvar__hud_configure) - { - s = "0:13:37"; - draw_beginBoldFont(); - drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - draw_endBoldFont(); - s = _("^1Intermediate 1 (+15.42)"); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); - s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint"); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.80 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); - } - else if(race_checkpointtime) - { - a = bound(0, 2 - (time - race_checkpointtime), 1); - s = ""; - forcetime = ""; - if(a > 0) // just hit a checkpoint? - { - if(race_checkpoint != 254) - { - if(race_time && race_previousbesttime) - s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname); - else - s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname); - if(race_time) - forcetime = TIME_ENCODED_TOSTRING(race_time); - } - } - else - { - if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254) - { - a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1); - if(a > 0) // next one? - { - s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname); - } - } - } - - if(s != "" && a > 0) - { - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - - if(race_penaltytime) - { - a = bound(0, 2 - (time - race_penaltyeventtime), 1); - if(a > 0) - { - s = sprintf(_("^1PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.8 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - } - - draw_beginBoldFont(); - - if(forcetime != "") - { - a = bound(0, (time - race_checkpointtime) / 0.5, 1); - drawstring_expanding(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y), forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a); - } - else - a = 1; - - if(race_laptime && race_checkpoint != 255) - { - s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime)); - drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - - draw_endBoldFont(); - } - else - { - if(race_mycheckpointtime) - { - a = bound(0, 2 - (time - race_mycheckpointtime), 1); - s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -(race_mycheckpointenemy == ""), race_mycheckpointlapsdelta, race_mycheckpointenemy); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - if(race_othercheckpointtime && race_othercheckpointenemy != "") - { - a = bound(0, 2 - (time - race_othercheckpointtime), 1); - s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -(race_othercheckpointenemy == ""), race_othercheckpointlapsdelta, race_othercheckpointenemy); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - - if(race_penaltytime && !race_penaltyaccumulator) - { - t = race_penaltytime * 0.1 + race_penaltyeventtime; - a = bound(0, (1 + t - time), 1); - if(a > 0) - { - if(time < t) - s = sprintf(_("^1PENALTY: %.1f (%s)"), (t - time) * 0.1, race_penaltyreason); - else - s = sprintf(_("^2PENALTY: %.1f (%s)"), 0, race_penaltyreason); - drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - } - } -} - -// Vote window (#9) -// - -void HUD_Vote(void) -{ - if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS))) - { - vote_active = 1; - if (autocvar__hud_configure) - { - vote_yescount = 0; - vote_nocount = 0; - LOG_INFO(_("^1You must answer before entering hud configure mode\n")); - cvar_set("_hud_configure", "0"); - } - if(vote_called_vote) - strunzone(vote_called_vote); - vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats")); - uid2name_dialog = 1; - } - - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_vote) return; - - panel_fg_alpha = autocvar_hud_panel_fg_alpha; - panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha; - - if(panel_bg_alpha_str == "") { - panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); - } - panel_bg_alpha = stof(panel_bg_alpha_str); - } - else - { - vote_yescount = 3; - vote_nocount = 2; - vote_needed = 4; - } - - string s; - float a; - if(vote_active != vote_prev) { - vote_change = time; - vote_prev = vote_active; - } - - if(vote_active || autocvar__hud_configure) - vote_alpha = bound(0, (time - vote_change) * 2, 1); - else - vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1); - - if(!vote_alpha) - return; - - HUD_Panel_UpdateCvars(); - - if(uid2name_dialog) - { - panel_pos = eX * 0.3 * vid_conwidth + eY * 0.1 * vid_conheight; - panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight; - } - - // these must be below above block - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1); - HUD_Panel_DrawBg(a); - a = panel_fg_alpha * a; - - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - // always force 3:1 aspect - vector newSize = '0 0 0'; - if(mySize.x/mySize.y > 3) - { - newSize.x = 3 * mySize.y; - newSize.y = mySize.y; - - pos.x = pos.x + (mySize.x - newSize.x) / 2; - } - else - { - newSize.y = 1/3 * mySize.x; - newSize.x = mySize.x; - - pos.y = pos.y + (mySize.y - newSize.y) / 2; - } - mySize = newSize; - - s = _("A vote has been called for:"); - if(uid2name_dialog) - s = _("Allow servers to store and display your name?"); - drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors); - if(autocvar__hud_configure) - s = _("^1Configure the HUD"); - drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL); - - // print the yes/no counts - s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount); - drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL); - s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount); - drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL); - - // draw the progress bar backgrounds - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - - // draw the highlights - if(vote_highlighted == 1) { - drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - } - else if(vote_highlighted == -1) { - drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - } - - // draw the progress bars - if(vote_yescount && vote_needed) - { - drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - } - - if(vote_nocount && vote_needed) - { - drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); - } - - drawresetcliparea(); -} - -// Mod icons panel (#10) -// - -bool mod_active; // is there any active mod icon? - -void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) -{ - int stat = -1; - string pic = ""; - vector color = '0 0 0'; - switch(i) - { - case 0: - stat = getstati(STAT_REDALIVE); - pic = "player_red.tga"; - color = '1 0 0'; - break; - case 1: - stat = getstati(STAT_BLUEALIVE); - pic = "player_blue.tga"; - color = '0 0 1'; - break; - case 2: - stat = getstati(STAT_YELLOWALIVE); - pic = "player_yellow.tga"; - color = '1 1 0'; - break; - default: - case 3: - stat = getstati(STAT_PINKALIVE); - pic = "player_pink.tga"; - color = '1 0 1'; - break; - } - - 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) - { - drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), eX * 0.3 * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL); - } - else - drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL); -} - -// Clan Arena and Freeze Tag HUD modicons -void HUD_Mod_CA(vector myPos, vector mySize) -{ - mod_active = 1; // required in each mod function that always shows something - - int layout; - if(gametype == MAPINFO_TYPE_CA) - layout = autocvar_hud_panel_modicons_ca_layout; - else //if(gametype == MAPINFO_TYPE_FREEZETAG) - layout = autocvar_hud_panel_modicons_freezetag_layout; - int rows, columns; - float aspect_ratio; - aspect_ratio = (layout) ? 2 : 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 = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - for(i=0; i= rows) - { - row = 0; - ++column; - } - } -} - -// 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(void) -{ - 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; -} - -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, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed - bool ctf_oneflag; // one-flag CTF mode enabled/disabled - int stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24); - float fs, fs2, fs3, size1, size2; - vector e1, e2; - - 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); - - mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag); - - if (autocvar__hud_configure) { - redflag = 1; - blueflag = 2; - if (team_count >= 3) - yellowflag = 2; - if (team_count >= 4) - 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) do { \ - 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; \ - } while (0) - 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, team##_icon_prevstatus; \ - int team##_alpha, team##_alpha_prevstatus; \ - team##_alpha = team##_alpha_prevstatus = 1; \ - do { \ - 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; \ - } \ - } while (0) - X(red, myteam != NUM_TEAM_1); - X(blue, myteam != NUM_TEAM_2); - X(yellow, myteam != NUM_TEAM_3); - X(pink, myteam != NUM_TEAM_4); - X(neutral, true); - #undef X - - if (ctf_oneflag) { - // hacky, but these aren't needed - red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null; - fs = fs2 = fs3 = 1; - } else switch (team_count) { - 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) do { \ - f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \ - 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); \ - } while (0) - 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 = getstati(STAT_KH_KEYS); - 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 = eX * mySize.x * 0.5 + eY * 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 = eX * mySize.x / all_keys + eY * mySize.y; - for(i = 0; i < all_keys; ++i) - KH_SLOTS[i] = pos + eX * slot_size.x * i; - } - else - { - // Vertical arrangement - slot_size = eX * mySize.x + eY * 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 blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1 - float alpha; - alpha = 1; - - if(carrying_keys) - 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 = getstati(STAT_ITEMS, 0, 24); - 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 = eX * 0.5 * mySize.x + eY * mySize.y; - } else { - kaball_pos = pos + eY * 0.25 * mySize.y; - kaball_size = eY * 0.5 * mySize.y + eX * mySize.x; - } - - 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", eX * mySize.x + eY * 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 = getstati(STAT_ITEMS, 0, 24); - nb_pb_starttime = getstatf(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 -float srecordtime_prev; // last remembered srecordtime -float srecordtime_change_time; // time when srecordtime last changed - -float race_status_time; -int race_status_prev; -string race_status_name_prev; -void HUD_Mod_Race(vector pos, vector mySize) -{ - mod_active = 1; // race should never hide the mod icons panel - entity me; - me = playerslots[player_localnum]; - float t, score; - float f; // yet another function has this - score = me.(scores[ps_primary]); - - if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD - return; // no records in the actual race - - // clientside personal record - string rr; - if(gametype == MAPINFO_TYPE_CTS) - rr = CTS_RECORD; - else - rr = RACE_RECORD; - t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time"))); - - if(score && (score < t || !t)) { - db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score)); - if(autocvar_cl_autodemo_delete_keeprecords) - { - f = autocvar_cl_autodemo_delete; - f &= ~1; - cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record! - } - } - - if(t != crecordtime_prev) { - crecordtime_prev = t; - crecordtime_change_time = time; - } - - vector textPos, medalPos; - float squareSize; - if(mySize.x > mySize.y) { - // text on left side - squareSize = min(mySize.y, mySize.x/2); - textPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eY * 0.5 * (mySize.y - squareSize); - medalPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eX * 0.5 * mySize.x + eY * 0.5 * (mySize.y - squareSize); - } else { - // text on top - squareSize = min(mySize.x, mySize.y/2); - textPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eX * 0.5 * (mySize.x - squareSize); - medalPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eY * 0.5 * mySize.y + eX * 0.5 * (mySize.x - squareSize); - } - - f = time - crecordtime_change_time; - - if (f > 1) { - drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } else { - drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect_expanding(pos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); - drawstring_aspect_expanding(pos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); - } - - // server record - t = race_server_record; - if(t != srecordtime_prev) { - srecordtime_prev = t; - srecordtime_change_time = time; - } - f = time - srecordtime_change_time; - - if (f > 1) { - drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } else { - drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect_expanding(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); - drawstring_aspect_expanding(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); - } - - if (race_status != race_status_prev || race_status_name != race_status_name_prev) { - race_status_time = time + 5; - race_status_prev = race_status; - if (race_status_name_prev) - strunzone(race_status_name_prev); - race_status_name_prev = strzone(race_status_name); - } - - // race "awards" - float a; - a = bound(0, race_status_time - time, 1); - - string s; - s = textShortenToWidth(race_status_name, squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors); - - float rank; - if(race_status > 0) - rank = race_CheckName(race_status_name); - else - rank = 0; - string rankname; - rankname = count_ordinal(rank); - - vector namepos; - namepos = medalPos + '0 0.8 0' * squareSize; - vector rankpos; - rankpos = medalPos + '0 0.15 0' * squareSize; - - if(race_status == 0) - drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - else if(race_status == 1) { - drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - } else if(race_status == 2) { - if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank) - drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - else - drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - } else if(race_status == 3) { - drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); - drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); - } - - if (race_status_time - time <= 0) { - race_status_prev = -1; - race_status = -1; - if(race_status_name) - strunzone(race_status_name); - race_status_name = string_null; - if(race_status_name_prev) - strunzone(race_status_name_prev); - race_status_name_prev = string_null; - } -} - -void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) -{ - float stat = -1; - string pic = ""; - vector color = '0 0 0'; - switch(i) - { - case 0: - stat = getstatf(STAT_DOM_PPS_RED); - pic = "dom_icon_red"; - color = '1 0 0'; - break; - case 1: - stat = getstatf(STAT_DOM_PPS_BLUE); - pic = "dom_icon_blue"; - color = '0 0 1'; - break; - case 2: - stat = getstatf(STAT_DOM_PPS_YELLOW); - pic = "dom_icon_yellow"; - color = '1 1 0'; - break; - default: - case 3: - stat = getstatf(STAT_DOM_PPS_PINK); - pic = "dom_icon_pink"; - color = '1 0 1'; - break; - } - float pps_ratio = stat / getstatf(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), eX * (2/3) * mySize.x + eY * 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)), "%" ), eX * (2/3) * mySize.x + eY * 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 = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - for(i=0; i= rows) - { - row = 0; - ++column; - } - } -} - -void HUD_ModIcons_SetFunc() -{ - switch(gametype) - { - case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break; - case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break; - case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break; - case MAPINFO_TYPE_CTS: - case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break; - case MAPINFO_TYPE_CA: - case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break; - case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break; - case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break; - } -} - -int mod_prev; // previous state of mod_active to check for a change -float mod_alpha; -float mod_change; // "time" when mod_active changed - -void HUD_ModIcons(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_modicons) return; - if(!HUD_ModIcons_GameType) return; - } - - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); - - if(mod_active != mod_prev) { - mod_change = time; - mod_prev = mod_active; - } - - if(mod_active || autocvar__hud_configure) - mod_alpha = bound(0, (time - mod_change) * 2, 1); - else - mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1); - - if(mod_alpha) - HUD_Panel_DrawBg(mod_alpha); - - if(panel_bg_padding) - { - panel_pos += '1 1 0' * panel_bg_padding; - panel_size -= '2 2 0' * panel_bg_padding; - } - - if(autocvar__hud_configure) - HUD_Mod_CTF(panel_pos, panel_size); - else - HUD_ModIcons_GameType(panel_pos, panel_size); - - draw_endBoldFont(); -} - -// Draw pressed keys (#11) -// -void HUD_PressedKeys(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_pressedkeys) return; - if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return; - } - - HUD_Panel_UpdateCvars(); - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - // force custom aspect - float aspect = autocvar_hud_panel_pressedkeys_aspect; - if(aspect) - { - vector newSize = '0 0 0'; - if(mySize.x/mySize.y > aspect) - { - newSize.x = aspect * mySize.y; - newSize.y = mySize.y; - - pos.x = pos.x + (mySize.x - newSize.x) / 2; - } - else - { - newSize.y = 1/aspect * mySize.x; - newSize.x = mySize.x; - - pos.y = pos.y + (mySize.y - newSize.y) / 2; - } - mySize = newSize; - } - - vector keysize; - keysize = eX * mySize.x * (1/3.0) + eY * mySize.y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack)); - float pressedkeys; - pressedkeys = getstatf(STAT_PRESSED_KEYS); - - if(autocvar_hud_panel_pressedkeys_attack) - { - drawpic_aspect_skin(pos + eX * keysize.x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawpic_aspect_skin(pos + eX * keysize.x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - pos.y += keysize.y; - } - - drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - pos.y += keysize.y; - drawpic_aspect_skin(pos, ((pressedkeys & KEY_LEFT) ? "key_left_inv.tga" : "key_left.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_BACKWARD) ? "key_backward_inv.tga" : "key_backward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_RIGHT) ? "key_right_inv.tga" : "key_right.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); -} - -// Handle chat as a panel (#12) -// -void HUD_Chat(void) -{ - if(!autocvar__hud_configure) - { - if (!autocvar_hud_panel_chat) - { - if (!autocvar_con_chatrect) - cvar_set("con_chatrect", "0"); - return; - } - if(autocvar__con_chat_maximized) - { - if(!hud_draw_maximized) return; - } - else if(chat_panel_modified) - { - panel.update_time = time; // forces reload of panel attributes - chat_panel_modified = false; - } - } - - HUD_Panel_UpdateCvars(); - - if(intermission == 2) - { - // reserve some more space to the mapvote panel - // by resizing and moving chat panel to the bottom - panel_size.y = min(panel_size.y, vid_conheight * 0.2); - panel_pos.y = vid_conheight - panel_size.y - panel_bg_border * 2; - chat_posy = panel_pos.y; - chat_sizey = panel_size.y; - } - if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized - { - panel_pos.y = panel_bg_border; - panel_size.y = vid_conheight - panel_bg_border * 2; - if(panel.current_panel_bg == "0") // force a border when maximized - { - string panel_bg; - panel_bg = strcat(hud_skin_path, "/border_default"); - if(precache_pic(panel_bg) == "") - panel_bg = "gfx/hud/default/border_default"; - if(panel.current_panel_bg) - strunzone(panel.current_panel_bg); - panel.current_panel_bg = strzone(panel_bg); - chat_panel_modified = true; - } - panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75 - } - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - if (!autocvar_con_chatrect) - cvar_set("con_chatrect", "1"); - - cvar_set("con_chatrect_x", ftos(pos.x/vid_conwidth)); - cvar_set("con_chatrect_y", ftos(pos.y/vid_conheight)); - - cvar_set("con_chatwidth", ftos(mySize.x/vid_conwidth)); - cvar_set("con_chat", ftos(floor(mySize.y/autocvar_con_chatsize - 0.5))); - - if(autocvar__hud_configure) - { - vector chatsize; - chatsize = '1 1 0' * autocvar_con_chatsize; - cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such - float i, a; - for(i = 0; i < autocvar_con_chat; ++i) - { - if(i == autocvar_con_chat - 1) - a = panel_fg_alpha; - else - a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45); - drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL); - pos.y += chatsize.y; - } - } -} - -// Engine info panel (#13) -// -float prevfps; -float prevfps_time; -int framecounter; - -float frametimeavg; -float frametimeavg1; // 1 frame ago -float frametimeavg2; // 2 frames ago -void HUD_EngineInfo(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_engineinfo) return; - } - - HUD_Panel_UpdateCvars(); - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - float currentTime = gettime(GETTIME_REALTIME); - if(cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage")) - { - float currentframetime = currentTime - prevfps_time; - frametimeavg = (frametimeavg + frametimeavg1 + frametimeavg2 + currentframetime)/4; // average three frametimes into framecounter for slightly more stable fps readings :P - frametimeavg2 = frametimeavg1; - frametimeavg1 = frametimeavg; - - float weight; - weight = cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight"); - if(currentframetime > 0.0001) // filter out insane values which sometimes seem to occur and throw off the average? If you are getting 10,000 fps or more, then you don't need a framerate counter. - { - if(fabs(prevfps - (1/frametimeavg)) > prevfps * cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold")) // if there was a big jump in fps, just force prevfps at current (1/currentframetime) to make big updates instant - prevfps = (1/currentframetime); - prevfps = (1 - weight) * prevfps + weight * (1/frametimeavg); // framecounter just used so there's no need for a new variable, think of it as "frametime average" - } - prevfps_time = currentTime; - } - else - { - framecounter += 1; - if(currentTime - prevfps_time > autocvar_hud_panel_engineinfo_framecounter_time) - { - prevfps = framecounter/(currentTime - prevfps_time); - framecounter = 0; - prevfps_time = currentTime; - } - } - - vector color; - color = HUD_Get_Num_Color (prevfps, 100); - drawstring_aspect(pos, sprintf(_("FPS: %.*f"), autocvar_hud_panel_engineinfo_framecounter_decimals, prevfps), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL); -} - -// Info messages panel (#14) -// -#define drawInfoMessage(s) do { \ - if(autocvar_hud_panel_infomessages_flip) \ - o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \ - drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \ - o.y += fontsize.y; \ -} while(0) -void HUD_InfoMessages(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_infomessages) return; - } - - HUD_Panel_UpdateCvars(); - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - // always force 5:1 aspect - vector newSize = '0 0 0'; - if(mySize.x/mySize.y > 5) - { - newSize.x = 5 * mySize.y; - newSize.y = mySize.y; - - pos.x = pos.x + (mySize.x - newSize.x) / 2; - } - else - { - newSize.y = 1/5 * mySize.x; - newSize.x = mySize.x; - - pos.y = pos.y + (mySize.y - newSize.y) / 2; - } - - mySize = newSize; - entity tm; - vector o; - o = pos; - - vector fontsize; - fontsize = '0.20 0.20 0' * mySize.y; - - float a; - a = panel_fg_alpha; - - string s; - if(!autocvar__hud_configure) - { - if(spectatee_status && !intermission) - { - a = 1; - if(spectatee_status == -1) - s = _("^1Observing"); - else - s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(current_player)); - drawInfoMessage(s); - - if(spectatee_status == -1) - s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire")); - else - s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); - drawInfoMessage(s); - - if(spectatee_status == -1) - s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); - else - s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2")); - drawInfoMessage(s); - - s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info")); - drawInfoMessage(s); - - if(gametype == MAPINFO_TYPE_LMS) - { - entity sk; - sk = playerslots[player_localnum]; - if(sk.(scores[ps_primary]) >= 666) - s = _("^1Match has already begun"); - else if(sk.(scores[ps_primary]) > 0) - s = _("^1You have no more lives left"); - else - s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); - } - else - s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); - drawInfoMessage(s); - - //show restart countdown: - if (time < getstatf(STAT_GAMESTARTTIME)) { - float countdown; - //we need to ceil, otherwise the countdown would be off by .5 when using round() - countdown = ceil(getstatf(STAT_GAMESTARTTIME) - time); - s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown); - drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); - o.y += fontsize.y; - } - } - if(warmup_stage && !intermission) - { - s = _("^2Currently in ^1warmup^2 stage!"); - drawInfoMessage(s); - } - - string blinkcolor; - if(time % 1 >= 0.5) - blinkcolor = "^1"; - else - blinkcolor = "^3"; - - if(ready_waiting && !intermission && !spectatee_status) - { - if(ready_waiting_for_me) - { - if(warmup_stage) - s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); - else - s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); - } - else - { - if(warmup_stage) - s = _("^2Waiting for others to ready up to end warmup..."); - else - s = _("^2Waiting for others to ready up..."); - } - drawInfoMessage(s); - } - else if(warmup_stage && !intermission && !spectatee_status) - { - s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready")); - drawInfoMessage(s); - } - - if(teamplay && !intermission && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger) - { - float ts_min = 0, ts_max = 0; - tm = teams.sort_next; - if (tm) - { - for (; tm.sort_next; tm = tm.sort_next) - { - if(!tm.team_size || tm.team == NUM_SPECTATOR) - continue; - if(!ts_min) ts_min = tm.team_size; - else ts_min = min(ts_min, tm.team_size); - if(!ts_max) ts_max = tm.team_size; - else ts_max = max(ts_max, tm.team_size); - } - if ((ts_max - ts_min) > 1) - { - s = strcat(blinkcolor, _("Teamnumbers are unbalanced!")); - tm = GetTeam(myteam, false); - if (tm) - if (tm.team != NUM_SPECTATOR) - if (tm.team_size == ts_max) - s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor)); - drawInfoMessage(s); - } - } - } - } - else - { - s = _("^7Press ^3ESC ^7to show HUD options."); - drawInfoMessage(s); - s = _("^3Doubleclick ^7a panel for panel-specific options."); - drawInfoMessage(s); - s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and"); - drawInfoMessage(s); - s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments."); - drawInfoMessage(s); - } -} - -// Physics panel (#15) -// -vector acc_prevspeed; -float acc_prevtime, acc_avg, top_speed, top_speed_time; -float physics_update_time, discrete_speed, discrete_acceleration; -void HUD_Physics(void) -{ - if(!autocvar__hud_configure) - { - if(!autocvar_hud_panel_physics) return; - if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return; - if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; - } - - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); - - HUD_Panel_DrawBg(1); - if(panel_bg_padding) - { - panel_pos += '1 1 0' * panel_bg_padding; - panel_size -= '2 2 0' * panel_bg_padding; - } - - float acceleration_progressbar_scale = 0; - if(autocvar_hud_panel_physics_progressbar && autocvar_hud_panel_physics_acceleration_progressbar_scale > 1) - acceleration_progressbar_scale = autocvar_hud_panel_physics_acceleration_progressbar_scale; - - float text_scale; - if (autocvar_hud_panel_physics_text_scale <= 0) - text_scale = 1; - else - text_scale = min(autocvar_hud_panel_physics_text_scale, 1); - - //compute speed - float speed, conversion_factor; - string unit; - - switch(autocvar_hud_panel_physics_speed_unit) - { - default: - case 1: - unit = _(" qu/s"); - conversion_factor = 1.0; - break; - case 2: - unit = _(" m/s"); - conversion_factor = 0.0254; - break; - case 3: - unit = _(" km/h"); - conversion_factor = 0.0254 * 3.6; - break; - case 4: - unit = _(" mph"); - conversion_factor = 0.0254 * 3.6 * 0.6213711922; - break; - case 5: - unit = _(" knots"); - conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h - break; - } - - vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel); - - float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 ); - if (autocvar__hud_configure) - speed = floor( max_speed * 0.65 + 0.5 ); - else if(autocvar_hud_panel_physics_speed_vertical) - speed = floor( vlen(vel) * conversion_factor + 0.5 ); - else - speed = floor( vlen(vel - vel.z * '0 0 1') * conversion_factor + 0.5 ); - - //compute acceleration - float acceleration, f; - if (autocvar__hud_configure) - acceleration = autocvar_hud_panel_physics_acceleration_max * 0.3; - else - { - // 1 m/s = 0.0254 qu/s; 1 g = 9.80665 m/s^2 - f = time - acc_prevtime; - if(autocvar_hud_panel_physics_acceleration_vertical) - acceleration = (vlen(vel) - vlen(acc_prevspeed)); - else - acceleration = (vlen(vel - '0 0 1' * vel.z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed.z)); - - acceleration = acceleration * (1 / max(0.0001, f)) * (0.0254 / 9.80665); - - acc_prevspeed = vel; - acc_prevtime = time; - - if(autocvar_hud_panel_physics_acceleration_movingaverage) - { - f = bound(0, f * 10, 1); - acc_avg = acc_avg * (1 - f) + acceleration * f; - acceleration = acc_avg; - } - } - - int acc_decimals = 2; - if(time > physics_update_time) - { - // workaround for ftos_decimals returning a negative 0 - if(discrete_acceleration > -1 / pow(10, acc_decimals) && discrete_acceleration < 0) - discrete_acceleration = 0; - discrete_acceleration = acceleration; - discrete_speed = speed; - physics_update_time += autocvar_hud_panel_physics_update_interval; - } - - //compute layout - float panel_ar = panel_size.x/panel_size.y; - vector speed_offset = '0 0 0', acceleration_offset = '0 0 0'; - if (panel_ar >= 5 && !acceleration_progressbar_scale) - { - panel_size.x *= 0.5; - if (autocvar_hud_panel_physics_flip) - speed_offset.x = panel_size.x; - else - acceleration_offset.x = panel_size.x; - } - else - { - panel_size.y *= 0.5; - if (autocvar_hud_panel_physics_flip) - speed_offset.y = panel_size.y; - else - acceleration_offset.y = panel_size.y; - } - int speed_baralign, acceleration_baralign; - if (autocvar_hud_panel_physics_baralign == 1) - acceleration_baralign = speed_baralign = 1; - else if(autocvar_hud_panel_physics_baralign == 4) - acceleration_baralign = speed_baralign = 2; - else if (autocvar_hud_panel_physics_flip) - { - acceleration_baralign = (autocvar_hud_panel_physics_baralign == 2); - speed_baralign = (autocvar_hud_panel_physics_baralign == 3); - } - else - { - speed_baralign = (autocvar_hud_panel_physics_baralign == 2); - acceleration_baralign = (autocvar_hud_panel_physics_baralign == 3); - } - if (autocvar_hud_panel_physics_acceleration_progressbar_mode == 0) - acceleration_baralign = 3; //override hud_panel_physics_baralign value for acceleration - - //draw speed - if(speed) - if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2) - HUD_Panel_DrawProgressBar(panel_pos + speed_offset, panel_size, "progressbar", speed/max_speed, 0, speed_baralign, autocvar_hud_progressbar_speed_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - vector tmp_offset = '0 0 0', tmp_size = '0 0 0'; - if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2) - { - tmp_size.x = panel_size.x * 0.75; - tmp_size.y = panel_size.y * text_scale; - if (speed_baralign) - tmp_offset.x = panel_size.x - tmp_size.x; - //else - //tmp_offset_x = 0; - tmp_offset.y = (panel_size.y - tmp_size.y) / 2; - drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(discrete_speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - - //draw speed unit - if (speed_baralign) - tmp_offset.x = 0; - else - tmp_offset.x = tmp_size.x; - if (autocvar_hud_panel_physics_speed_unit_show) - { - //tmp_offset_y = 0; - tmp_size.x = panel_size.x * (1 - 0.75); - tmp_size.y = panel_size.y * 0.4 * text_scale; - tmp_offset.y = (panel_size.y * 0.4 - tmp_size.y) / 2; - drawstring_aspect(panel_pos + speed_offset + tmp_offset, unit, tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } - } - - //compute and draw top speed - if (autocvar_hud_panel_physics_topspeed) - if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2) - { - if (autocvar__hud_configure) - { - top_speed = floor( max_speed * 0.75 + 0.5 ); - f = 1; - } - else - { - if (speed >= top_speed) - { - top_speed = speed; - top_speed_time = time; - } - if (top_speed != 0) - { - f = max(1, autocvar_hud_panel_physics_topspeed_time); - // divide by f to make it start from 1 - f = cos( ((time - top_speed_time) / f) * PI/2 ); - } - else //hide top speed 0, it would be stupid - f = 0; - } - if (f > 0) - { - //top speed progressbar peak - if(speed < top_speed) - if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2) - { - float peak_offsetX; - vector peak_size = '0 0 0'; - if (speed_baralign == 0) - peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x; - else if (speed_baralign == 1) - peak_offsetX = (1 - min(top_speed, max_speed)/max_speed) * panel_size.x; - else // if (speed_baralign == 2) - peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x * 0.5; - peak_size.x = floor(panel_size.x * 0.01 + 1.5); - peak_size.y = panel_size.y; - if (speed_baralign == 2) // draw two peaks, on both sides - { - drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x + peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x - peak_offsetX + peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - else - drawfill(panel_pos + speed_offset + eX * (peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - //top speed - tmp_offset.y = panel_size.y * 0.4; - tmp_size.x = panel_size.x * (1 - 0.75); - tmp_size.y = (panel_size.y - tmp_offset.y) * text_scale; - tmp_offset.y += (panel_size.y - tmp_offset.y - tmp_size.y) / 2; - drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(top_speed), tmp_size, '1 0 0', f * panel_fg_alpha, DRAWFLAG_NORMAL); - } - else - top_speed = 0; - } - - //draw acceleration - if(acceleration) - if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 3) - { - vector progressbar_color; - if(acceleration < 0) - progressbar_color = autocvar_hud_progressbar_acceleration_neg_color; - else - progressbar_color = autocvar_hud_progressbar_acceleration_color; - - f = acceleration/autocvar_hud_panel_physics_acceleration_max; - if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear) - f = (f >= 0 ? sqrt(f) : -sqrt(-f)); - - if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds - { - tmp_size = acceleration_progressbar_scale * panel_size.x * eX + panel_size.y * eY; - - if (acceleration_baralign == 1) - tmp_offset.x = panel_size.x - tmp_size.x; - else if (acceleration_baralign == 2 || acceleration_baralign == 3) - tmp_offset.x = (panel_size.x - tmp_size.x) / 2; - else - tmp_offset.x = 0; - tmp_offset.y = 0; - } - else - { - tmp_size = panel_size; - tmp_offset = '0 0 0'; - } - - HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - if(autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3) - { - tmp_size.x = panel_size.x; - tmp_size.y = panel_size.y * text_scale; - tmp_offset.x = 0; - tmp_offset.y = (panel_size.y - tmp_size.y) / 2; - - drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(discrete_acceleration, acc_decimals), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - } - - draw_endBoldFont(); -} - -// CenterPrint (#16) -// -const int CENTERPRINT_MAX_MSGS = 10; -const int CENTERPRINT_MAX_ENTRIES = 50; -const float CENTERPRINT_SPACING = 0.7; -int cpm_index; -string centerprint_messages[CENTERPRINT_MAX_MSGS]; -int centerprint_msgID[CENTERPRINT_MAX_MSGS]; -float centerprint_time[CENTERPRINT_MAX_MSGS]; -float centerprint_expire_time[CENTERPRINT_MAX_MSGS]; -int centerprint_countdown_num[CENTERPRINT_MAX_MSGS]; -bool centerprint_showing; - -void centerprint_generic(int new_id, string strMessage, float duration, int countdown_num) -{ - //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num); - int i, j; - - if(strMessage == "" && new_id == 0) - return; - - // strip trailing newlines - j = strlen(strMessage) - 1; - while(substring(strMessage, j, 1) == "\n" && j >= 0) - --j; - if (j < strlen(strMessage) - 1) - strMessage = substring(strMessage, 0, j + 1); - - if(strMessage == "" && new_id == 0) - return; - - // strip leading newlines - j = 0; - while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage)) - ++j; - if (j > 0) - strMessage = substring(strMessage, j, strlen(strMessage) - j); - - if(strMessage == "" && new_id == 0) - return; - - if (!centerprint_showing) - centerprint_showing = true; - - for (i=0, j=cpm_index; i time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time) - centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out); - return; - } - break; // found a msg with the same id, at position j - } - } - - if (i == CENTERPRINT_MAX_MSGS) - { - // a msg with the same id was not found, add the msg at the next position - --cpm_index; - if (cpm_index == -1) - cpm_index = CENTERPRINT_MAX_MSGS - 1; - j = cpm_index; - } - if(centerprint_messages[j]) - strunzone(centerprint_messages[j]); - centerprint_messages[j] = strzone(strMessage); - centerprint_msgID[j] = new_id; - if (duration < 0) - { - centerprint_time[j] = -1; - centerprint_expire_time[j] = time; - } - else - { - if(duration == 0) - duration = max(1, autocvar_hud_panel_centerprint_time); - centerprint_time[j] = duration; - centerprint_expire_time[j] = time + duration; - } - centerprint_countdown_num[j] = countdown_num; -} - -void centerprint_hud(string strMessage) -{ - centerprint_generic(0, strMessage, autocvar_hud_panel_centerprint_time, 0); -} - -void reset_centerprint_messages(void) -{ - int i; - for (i=0; i hud_configure_cp_generation_time) - { - if(highlightedPanel == HUD_PANEL(CENTERPRINT)) - { - float r; - r = random(); - if (r > 0.8) - centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10); - else if (r > 0.55) - centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0); - else - centerprint_hud(sprintf("Message at time %s", seconds_tostring(time))); - hud_configure_cp_generation_time = time + 1 + random()*4; - } - else - { - centerprint_generic(0, sprintf("Centerprint message", seconds_tostring(time)), 10, 0); - hud_configure_cp_generation_time = time + 10 - random()*3; - } - } - } - - // this panel fades only when the menu does - float hud_fade_alpha_save = 0; - if(scoreboard_fade_alpha) - { - hud_fade_alpha_save = hud_fade_alpha; - hud_fade_alpha = 1 - autocvar__menu_alpha; - } - HUD_Panel_UpdateCvars(); - - if ( HUD_Radar_Clickable() ) - { - if (hud_panel_radar_bottom >= 0.96 * vid_conheight) - return; - - panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x); - panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom); - } - else if(scoreboard_fade_alpha) - { - hud_fade_alpha = hud_fade_alpha_save; - - // move the panel below the scoreboard - if (scoreboard_bottom >= 0.96 * vid_conheight) - return; - vector target_pos; - - target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x); - - if(target_pos.y > panel_pos.y) - { - panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha); - panel_size.y = min(panel_size.y, vid_conheight - scoreboard_bottom); - } - } - - HUD_Panel_DrawBg(1); - - if (!centerprint_showing) - return; - - if(panel_bg_padding) - { - panel_pos += '1 1 0' * panel_bg_padding; - panel_size -= '2 2 0' * panel_bg_padding; - } - - int entries; - float height; - vector fontsize; - // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES); - // height = panel_size_y/entries; - // fontsize = '1 1 0' * height; - height = vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale; - fontsize = '1 1 0' * height; - entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES); - - int i, j, k, n, g; - float a, sz, align, current_msg_posY = 0, msg_size; - vector pos; - string ts; - bool all_messages_expired = true; - - pos = panel_pos; - if (autocvar_hud_panel_centerprint_flip) - pos.y += panel_size.y; - align = bound(0, autocvar_hud_panel_centerprint_align, 1); - for (g=0, i=0, j=cpm_index; i 0) - { - centerprint_countdown_num[j] = centerprint_countdown_num[j] - 1; - if (centerprint_countdown_num[j] == 0) - continue; - centerprint_expire_time[j] = centerprint_expire_time[j] + centerprint_time[j]; - } - else if(centerprint_time[j] != -1) - continue; - } - - all_messages_expired = false; - - // fade the centerprint_hud in/out - if(centerprint_time[j] < 0) // Expired but forced. Expire time is the fade-in time. - a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in); - else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time) // Regularily printed. Not fading out yet. - a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in); - else // Expiring soon, so fade it out. - a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out); - - // while counting down show it anyway in order to hold the current message position - if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show. - continue; - if (a > 1) - a = 1; - - // set the size from fading in/out before subsequent fading - sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize); - - // also fade it based on positioning - if(autocvar_hud_panel_centerprint_fade_subsequent) - { - a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha - a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message - } - a *= panel_fg_alpha; - - // finally set the size based on the new theAlpha from subsequent fading - sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize)); - drawfontscale = sz * '1 1 0'; - - if (centerprint_countdown_num[j]) - n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n"); - else - n = tokenizebyseparator(centerprint_messages[j], "\n"); - - if (autocvar_hud_panel_centerprint_flip) - { - // check if the message can be entirely shown - for(k = 0; k < n; ++k) - { - getWrappedLine_remaining = argv(k); - while(getWrappedLine_remaining) - { - ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors); - if (ts != "") - pos.y -= fontsize.y; - else - pos.y -= fontsize.y * CENTERPRINT_SPACING/2; - } - } - current_msg_posY = pos.y; // save starting pos (first line) of the current message - } - - msg_size = pos.y; - for(k = 0; k < n; ++k) - { - getWrappedLine_remaining = argv(k); - while(getWrappedLine_remaining) - { - ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors); - if (ts != "") - { - if (align) - pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize)) * align; - if (a > 0.5/255.0) // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker. - drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL); - pos.y += fontsize.y; - } - else - pos.y += fontsize.y * CENTERPRINT_SPACING/2; - } - } - - ++g; // move next position number up - - msg_size = pos.y - msg_size; - if (autocvar_hud_panel_centerprint_flip) - { - pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y; - if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages - pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz)); - - if (pos.y < panel_pos.y) // check if the next message can be shown - { - drawfontscale = '1 1 0'; - return; - } - } - else - { - pos.y += CENTERPRINT_SPACING * fontsize.y; - if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages - pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz)); - - if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown - { - drawfontscale = '1 1 0'; - return; - } - } - } - drawfontscale = '1 1 0'; - if (all_messages_expired) - { - centerprint_showing = false; - reset_centerprint_messages(); - } -} - - -// Minigame -// -#include "../common/minigames/cl_minigames_hud.qc" - - -// QuickMenu (#23) -// -#include "quickmenu.qc" - - -/* -================== -Main HUD system -================== -*/ - -void HUD_Vehicle() -{ - if(autocvar__hud_configure) return; - if(intermission == 2) return; - - if(hud == HUD_BUMBLEBEE_GUN) - CSQC_BUMBLE_GUN_HUD(); - else { - Vehicle info = get_vehicleinfo(hud); - info.vr_hud(info); - } -} - -bool HUD_Panel_CheckFlags(int showflags) -{ - if ( HUD_Minigame_Showpanels() ) - return showflags & PANEL_SHOW_MINIGAME; - if(intermission == 2) - return showflags & PANEL_SHOW_MAPVOTE; - return showflags & PANEL_SHOW_MAINGAME; -} - -void HUD_Panel_Draw(entity panent) -{ - panel = panent; - if(autocvar__hud_configure) - { - if(panel.panel_configflags & PANEL_CONFIG_MAIN) - panel.panel_draw(); - } - else if(HUD_Panel_CheckFlags(panel.panel_showflags)) - panel.panel_draw(); -} - -void HUD_Reset(void) -{ - // reset gametype specific icons - if(gametype == MAPINFO_TYPE_CTF) - HUD_Mod_CTF_Reset(); -} - -void HUD_Main(void) -{ - int i; - // global hud theAlpha fade - if(menu_enabled == 1) - hud_fade_alpha = 1; - else - hud_fade_alpha = (1 - autocvar__menu_alpha); - - if(scoreboard_fade_alpha) - hud_fade_alpha = (1 - scoreboard_fade_alpha); - - HUD_Configure_Frame(); - - // panels that we want to be active together with the scoreboard - // they must fade only when the menu does - if(scoreboard_fade_alpha == 1) - { - HUD_Panel_Draw(HUD_PANEL(CENTERPRINT)); - return; - } - - if(!autocvar__hud_configure && !hud_fade_alpha) - { - hud_fade_alpha = 1; - HUD_Panel_Draw(HUD_PANEL(VOTE)); - hud_fade_alpha = 0; - return; - } - - // Drawing stuff - if (hud_skin_prev != autocvar_hud_skin) - { - if (hud_skin_path) - strunzone(hud_skin_path); - hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); - if (hud_skin_prev) - strunzone(hud_skin_prev); - hud_skin_prev = strzone(autocvar_hud_skin); - } - - // draw the dock - if(autocvar_hud_dock != "" && autocvar_hud_dock != "0") - { - int f; - vector color; - float hud_dock_color_team = autocvar_hud_dock_color_team; - if((teamplay) && hud_dock_color_team) { - if(autocvar__hud_configure && myteam == NUM_SPECTATOR) - color = '1 0 0' * hud_dock_color_team; - else - color = myteamcolors * hud_dock_color_team; - } - else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) { - color = '1 0 0' * hud_dock_color_team; - } - else - { - string hud_dock_color = autocvar_hud_dock_color; - if(hud_dock_color == "shirt") { - f = stof(getplayerkeyvalue(current_player, "colors")); - color = colormapPaletteColor(floor(f / 16), 0); - } - else if(hud_dock_color == "pants") { - f = stof(getplayerkeyvalue(current_player, "colors")); - color = colormapPaletteColor(f % 16, 1); - } - else - color = stov(hud_dock_color); - } - - string pic; - pic = strcat(hud_skin_path, "/", autocvar_hud_dock); - if(precache_pic(pic) == "") { - pic = strcat(hud_skin_path, "/dock_medium"); - if(precache_pic(pic) == "") { - pic = "gfx/hud/default/dock_medium"; - } - } - drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock... - } - - // cache the panel order into the panel_order array - if(autocvar__hud_panelorder != hud_panelorder_prev) { - for(i = 0; i < hud_panels_COUNT; ++i) - panel_order[i] = -1; - string s = ""; - int p_num; - bool warning = false; - int argc = tokenize_console(autocvar__hud_panelorder); - if (argc > hud_panels_COUNT) - warning = true; - //first detect wrong/missing panel numbers - for(i = 0; i < hud_panels_COUNT; ++i) { - p_num = stoi(argv(i)); - if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number? - if (panel_order[p_num] == -1) //found for the first time? - s = strcat(s, ftos(p_num), " "); - panel_order[p_num] = 1; //mark as found - } - else - warning = true; - } - for(i = 0; i < hud_panels_COUNT; ++i) { - if (panel_order[i] == -1) { - warning = true; - s = strcat(s, ftos(i), " "); //add missing panel number - } - } - if (warning) - LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n"); - - cvar_set("_hud_panelorder", s); - if(hud_panelorder_prev) - strunzone(hud_panelorder_prev); - hud_panelorder_prev = strzone(s); - - //now properly set panel_order - tokenize_console(s); - for(i = 0; i < hud_panels_COUNT; ++i) { - panel_order[i] = stof(argv(i)); - } - } - - hud_draw_maximized = 0; - // draw panels in the order specified by panel_order array - for(i = hud_panels_COUNT - 1; i >= 0; --i) - HUD_Panel_Draw(hud_panels[panel_order[i]]); - - HUD_Vehicle(); - - hud_draw_maximized = 1; // panels that may be maximized must check this var - // draw maximized panels on top - if(hud_panel_radar_maximized) - HUD_Panel_Draw(HUD_PANEL(RADAR)); - if(autocvar__con_chat_maximized) - HUD_Panel_Draw(HUD_PANEL(CHAT)); - if(hud_panel_quickmenu) - HUD_Panel_Draw(HUD_PANEL(QUICKMENU)); - - if (scoreboard_active || intermission == 2) - HUD_Reset(); - - HUD_Configure_PostDraw(); - - hud_configure_prev = autocvar__hud_configure; -} diff --git a/qcsrc/client/hud.qh b/qcsrc/client/hud.qh deleted file mode 100644 index ba9cb171e9..0000000000 --- a/qcsrc/client/hud.qh +++ /dev/null @@ -1,418 +0,0 @@ -#ifndef CLIENT_HUD_H -#define CLIENT_HUD_H - -#include "../common/weapons/all.qh" - -bool HUD_Radar_Clickable(); -void HUD_Radar_Mouse(); - -REGISTRY(hud_panels, 24) -REGISTER_REGISTRY(Registerhud_panels) - -#define REGISTER_HUD_PANEL(id, draw_func, name, configflags, showflags) \ - void draw_func(); \ - REGISTER(Registerhud_panels, HUD_PANEL, hud_panels, id, m_id, new(hud_panel)) { \ - this.panel_id = this.m_id; \ - this.panel_draw = draw_func; \ - this.panel_name = #name; \ - this.panel_configflags = configflags; \ - this.panel_showflags = showflags; \ - } - -#define HUD_PANEL(NAME) HUD_PANEL_##NAME - -// draw the background/borders -#define HUD_Panel_DrawBg(theAlpha) do { \ - if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \ - draw_BorderPicture(panel_pos - '1 1 0' * panel_bg_border, panel.current_panel_bg, panel_size + '1 1 0' * 2 * panel_bg_border, panel_bg_color, panel_bg_alpha * theAlpha, '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER));\ -} while(0) - -int panel_order[hud_panels_MAX]; -string hud_panelorder_prev; - -bool hud_draw_maximized; -bool hud_panel_radar_maximized; -bool hud_panel_radar_mouse; -float hud_panel_radar_bottom; -bool hud_panel_radar_temp_hidden; -bool chat_panel_modified; -bool radar_panel_modified; - -float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary); -void HUD_Radar_Hide_Maximized(); - -void HUD_Reset (void); -void HUD_Main (void); - -int vote_yescount; -int vote_nocount; -int vote_needed; -int vote_highlighted; // currently selected vote - -int vote_active; // is there an active vote? -int vote_prev; // previous state of vote_active to check for a change -float vote_alpha; -float vote_change; // "time" when vote_active changed - -float hud_panel_quickmenu; - -vector mousepos; -vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click) -vector panel_click_resizeorigin; // coordinates for opposite point when resizing -float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright -entity highlightedPanel; -float highlightedAction; // 0 = nothing, 1 = move, 2 = resize - -const float BORDER_MULTIPLIER = 0.25; -float scoreboard_bottom; -int weapon_accuracy[Weapons_MAX]; - -int complain_weapon; -string complain_weapon_name; -float complain_weapon_type; -float complain_weapon_time; - -int ps_primary, ps_secondary; -int ts_primary, ts_secondary; - -int last_switchweapon; -int last_activeweapon; -float weapontime; -float weaponprevtime; - -float teamnagger; - -float hud_configure_checkcollisions; -float hud_configure_prev; -vector hud_configure_gridSize; -vector hud_configure_realGridSize; - -int hudShiftState; -const int S_SHIFT = 1; -const int S_CTRL = 2; -const int S_ALT = 4; - -float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel - -float hud_fade_alpha; - -string hud_skin_path; -string hud_skin_prev; - -vector myteamcolors; - -entity highlightedPanel_backup; -vector panel_pos_backup; -vector panel_size_backup; - -vector panel_size_copied; - -entity panel; -entityclass(HUDPanel); -class(HUDPanel) .string panel_name; -class(HUDPanel) .int panel_id; -class(HUDPanel) .vector current_panel_pos; -class(HUDPanel) .vector current_panel_size; -class(HUDPanel) .string current_panel_bg; -class(HUDPanel) .float current_panel_bg_alpha; -class(HUDPanel) .float current_panel_bg_border; -class(HUDPanel) .vector current_panel_bg_color; -class(HUDPanel) .float current_panel_bg_color_team; -class(HUDPanel) .float current_panel_bg_padding; -class(HUDPanel) .float current_panel_fg_alpha; -class(HUDPanel) .float update_time; -float panel_enabled; -vector panel_pos; -vector panel_size; -string panel_bg_str; // "_str" vars contain the raw value of the cvar, non-"_str" contains what hud.qc code should use -vector panel_bg_color; -string panel_bg_color_str; -float panel_bg_color_team; -string panel_bg_color_team_str; -float panel_fg_alpha; -float panel_bg_alpha; -string panel_bg_alpha_str; -float panel_bg_border; -string panel_bg_border_str; -float panel_bg_padding; -string panel_bg_padding_str; - -class(HUDPanel) .void() panel_draw; - -// chat panel can be reduced / moved while the mapvote is active -// let know the mapvote panel about chat pos and size -float chat_posy; -float chat_sizey; - -float current_player; - -float stringwidth_colors(string s, vector theSize); -float stringwidth_nocolors(string s, vector theSize); -float GetPlayerColorForce(int i); -int GetPlayerColor(int i); -string GetPlayerName(int i); -void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag); - -.int panel_showflags; -const int PANEL_SHOW_NEVER = 0x00; -const int PANEL_SHOW_MAINGAME = 0x01; -const int PANEL_SHOW_MINIGAME = 0x02; -const int PANEL_SHOW_MAPVOTE = 0x04; -const int PANEL_SHOW_ALWAYS = 0xff; -bool HUD_Panel_CheckFlags(int showflags); - -.int panel_configflags; -const int PANEL_CONFIG_NO = 0x00; -const int PANEL_CONFIG_MAIN = 0x01; - - -// prev_* vars contain the health/armor at the previous FRAME -// set to -1 when player is dead or was not playing -int prev_health, prev_armor; -float health_damagetime, armor_damagetime; -int health_beforedamage, armor_beforedamage; -// old_p_* vars keep track of previous values when smoothing value changes of the progressbar -int old_p_health, old_p_armor; -float old_p_healthtime, old_p_armortime; -// prev_p_* vars contain the health/armor progressbar value at the previous FRAME -// set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h) -int prev_p_health, prev_p_armor; - -void HUD_ItemsTime(); - -REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE ) -REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -// always add new panels to the end of list - -// Because calling lots of functions in QC apparently cuts fps in half on many machines: -// ---------------------- -// MACRO HELL STARTS HERE -// ---------------------- -// Little help for the poor people who have to make sense of this: Start from the bottom ;) - -// Get value for panel.current_panel_bg: if "" fetch default, else use panel_bg_str -// comment on last line of macro: // we probably want to see a background in config mode at all times... -#define HUD_Panel_GetBg() do { \ - string panel_bg; \ - if (!autocvar__hud_configure && panel_bg_str == "0") { \ - panel_bg = "0"; \ - } else { \ - if (panel_bg_str == "") { \ - panel_bg_str = autocvar_hud_panel_bg; \ - } \ - if (panel_bg_str == "0" && !autocvar__hud_configure) { \ - panel_bg = "0"; \ - } else { \ - if (panel_bg_str == "0" && autocvar__hud_configure) \ - panel_bg_alpha_str = "0"; \ - panel_bg = strcat(hud_skin_path, "/", panel_bg_str); \ - if (precache_pic(panel_bg) == "") { \ - panel_bg = strcat(hud_skin_path, "/", "border_default"); \ - if (precache_pic(panel_bg) == "") { \ - panel_bg = strcat("gfx/hud/default/", "border_default"); \ - } \ - } \ - } \ - } \ - if (panel.current_panel_bg) \ - strunzone(panel.current_panel_bg); \ - panel.current_panel_bg = strzone(panel_bg); \ -} while(0) - -// Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector. -#define HUD_Panel_GetColor() do { \ - if ((teamplay) && panel_bg_color_team) { \ - if (autocvar__hud_configure && myteam == NUM_SPECTATOR) \ - panel_bg_color = '1 0 0' * panel_bg_color_team; \ - else \ - panel_bg_color = myteamcolors * panel_bg_color_team; \ - } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) { \ - panel_bg_color = '1 0 0' * panel_bg_color_team; \ - } else { \ - if (panel_bg_color_str == "") { \ - panel_bg_color = autocvar_hud_panel_bg_color; \ - } else { \ - if (panel_bg_color_str == "shirt") { \ - panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \ - } else if (panel_bg_color_str == "pants") { \ - panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \ - } else { \ - panel_bg_color = stov(panel_bg_color_str); \ - } \ - } \ - } \ -} while(0) - -// Get value for panel_bg_color_team: if "" fetch default, else use panel_bg_color_team_str -#define HUD_Panel_GetColorTeam() do { \ - if (panel_bg_color_team_str == "") { \ - panel_bg_color_team = autocvar_hud_panel_bg_color_team; \ - } else { \ - panel_bg_color_team = stof(panel_bg_color_team_str); \ - } \ -} while(0) - -// Get value for panel_bg_alpha: if "" fetch default, else use panel_bg_alpha. Also do various menu dialog fadeout/in checks, and minalpha checks -// comment on line 3 of macro: // do not set a minalpha cap when showing the config dialog for this panel -#define HUD_Panel_GetBgAlpha() do { \ - if (panel_bg_alpha_str == "") { \ - panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); \ - } \ - panel_bg_alpha = stof(panel_bg_alpha_str); \ - if (autocvar__hud_configure) { \ - if (!panel_enabled) \ - panel_bg_alpha = 0.25; \ - else if (menu_enabled == 2 && panel == highlightedPanel) \ - panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\ - else \ - panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha); \ - } \ -} while(0) - -// Get value for panel_fg_alpha. Also do various minalpha checks -// comment on line 2 of macro: // ALWAYS show disabled panels at 0.25 alpha when in config mode -#define HUD_Panel_GetFgAlpha() do { \ - panel_fg_alpha = autocvar_hud_panel_fg_alpha; \ - if (autocvar__hud_configure && !panel_enabled) \ - panel_fg_alpha = 0.25; \ -} while(0) - -// Get border. See comments above, it's similar. -#define HUD_Panel_GetBorder() do { \ - if (panel_bg_border_str == "") { \ - panel_bg_border = autocvar_hud_panel_bg_border; \ - } else { \ - panel_bg_border = stof(panel_bg_border_str); \ - } \ -} while(0) - -// Get padding. See comments above, it's similar. -// last line is a port of the old function, basically always make sure the panel contents are at least 5 pixels tall/wide, to disallow extreme padding values -#define HUD_Panel_GetPadding() do { \ - if (panel_bg_padding_str == "") { \ - panel_bg_padding = autocvar_hud_panel_bg_padding; \ - } else { \ - panel_bg_padding = stof(panel_bg_padding_str); \ - } \ - panel_bg_padding = min(min(panel_size.x, panel_size.y)/2 - 5, panel_bg_padding); \ -} while(0) - -// return smoothly faded pos and size of given panel when a dialog is active -// don't center too wide panels, it doesn't work with different resolutions -#define HUD_Panel_UpdatePosSize_ForMenu() do { \ - vector menu_enable_size = panel_size; \ - float max_panel_width = 0.52 * vid_conwidth; \ - if(panel_size.x > max_panel_width) \ - { \ - menu_enable_size.x = max_panel_width; \ - menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \ - } \ - vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \ - panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \ - panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \ -} while(0) - -// Scale the pos and size vectors to absolute coordinates -#define HUD_Panel_ScalePosSize() do { \ - panel_pos.x *= vid_conwidth; panel_pos.y *= vid_conheight; \ - panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight; \ -} while(0) - -// NOTE: in hud_configure mode cvars must be reloaded every frame -#define HUD_Panel_UpdateCvars() do { \ - if (panel.update_time <= time) { \ - if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ - panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ - panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ - HUD_Panel_ScalePosSize(); \ - panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \ - panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \ - panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \ - panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \ - panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ - panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \ - HUD_Panel_GetBg(); \ - if (panel.current_panel_bg != "0") { \ - HUD_Panel_GetColorTeam(); \ - HUD_Panel_GetColor(); \ - HUD_Panel_GetBgAlpha(); \ - HUD_Panel_GetBorder(); \ - } \ - HUD_Panel_GetFgAlpha(); \ - HUD_Panel_GetPadding(); \ - panel.current_panel_bg_alpha = panel_bg_alpha; \ - panel.current_panel_fg_alpha = panel_fg_alpha; \ - if (menu_enabled == 2 && panel == highlightedPanel) { \ - HUD_Panel_UpdatePosSize_ForMenu(); \ - } else { \ - panel_bg_alpha *= hud_fade_alpha; \ - panel_fg_alpha *= hud_fade_alpha; \ - } \ - panel.current_panel_pos = panel_pos; \ - panel.current_panel_size = panel_size; \ - panel.current_panel_bg_border = panel_bg_border; \ - panel.current_panel_bg_color = panel_bg_color; \ - panel.current_panel_bg_color_team = panel_bg_color_team; \ - panel.current_panel_bg_padding = panel_bg_padding; \ - panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \ - } else { \ - panel_pos = panel.current_panel_pos; \ - panel_size = panel.current_panel_size; \ - panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha; \ - panel_bg_border = panel.current_panel_bg_border; \ - panel_bg_color = panel.current_panel_bg_color; \ - panel_bg_color_team = panel.current_panel_bg_color_team; \ - panel_bg_padding = panel.current_panel_bg_padding; \ - panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha; \ - } \ -} while(0) - -#define HUD_Panel_UpdatePosSize() do { \ - panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ - panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ - panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ - HUD_Panel_ScalePosSize(); \ - if (menu_enabled == 2 && panel == highlightedPanel) { \ - HUD_Panel_UpdatePosSize_ForMenu(); \ - } \ - panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ - HUD_Panel_GetBorder(); \ -} while(0) - -const int NOTIFY_MAX_ENTRIES = 10; -const float NOTIFY_ICON_MARGIN = 0.02; - -int notify_index; -int notify_count; -float notify_times[NOTIFY_MAX_ENTRIES]; -string notify_attackers[NOTIFY_MAX_ENTRIES]; -string notify_victims[NOTIFY_MAX_ENTRIES]; -string notify_icons[NOTIFY_MAX_ENTRIES]; - -void HUD_Notify_Push(string icon, string attacker, string victim); - -var void HUD_ModIcons_GameType(vector pos, vector size); -void HUD_ModIcons_SetFunc(); -#endif diff --git a/qcsrc/client/hud/all.inc b/qcsrc/client/hud/all.inc new file mode 100644 index 0000000000..aa36eec409 --- /dev/null +++ b/qcsrc/client/hud/all.inc @@ -0,0 +1,21 @@ +#include "panel/weapons.qc" +#include "panel/ammo.qc" +#include "panel/powerups.qc" +#include "panel/healtharmor.qc" +#include "panel/notify.qc" +#include "panel/timer.qc" +#include "panel/radar.qc" +#include "panel/score.qc" +#include "panel/racetimer.qc" +#include "panel/vote.qc" +#include "panel/modicons.qc" +#include "panel/pressedkeys.qc" +#include "panel/chat.qc" +#include "panel/engineinfo.qc" +#include "panel/infomessages.qc" +#include "panel/physics.qc" +#include "panel/centerprint.qc" +#include "panel/minigame.qc" +// #include "panel/mapvote.qc" +// #include "panel/itemstime.qc" +#include "panel/quickmenu.qc" diff --git a/qcsrc/client/hud/all.qc b/qcsrc/client/hud/all.qc new file mode 100644 index 0000000000..680b0230bb --- /dev/null +++ b/qcsrc/client/hud/all.qc @@ -0,0 +1,2 @@ +#include "hud.qc" +#include "hud_config.qc" diff --git a/qcsrc/client/hud/all.qh b/qcsrc/client/hud/all.qh new file mode 100644 index 0000000000..4f8cee5c84 --- /dev/null +++ b/qcsrc/client/hud/all.qh @@ -0,0 +1,2 @@ +#include "hud.qh" +#include "hud_config.qh" diff --git a/qcsrc/client/hud/hud.qc b/qcsrc/client/hud/hud.qc new file mode 100644 index 0000000000..75e678275d --- /dev/null +++ b/qcsrc/client/hud/hud.qc @@ -0,0 +1,600 @@ +#include "hud.qh" + +#include "hud_config.qh" +#include "mapvoting.qh" +#include "scoreboard.qh" +#include "teamradar.qh" +#include "t_items.qh" +#include "../common/deathtypes/all.qh" +#include "../common/items/all.qc" +#include "../common/mapinfo.qh" +#include "../common/mutators/mutator/waypoints/all.qh" +#include "../common/stats.qh" +#include "../lib/csqcmodel/cl_player.qh" +// TODO: remove +#include "../server/mutators/mutator/gamemode_ctf.qc" + + +/* +================== +Misc HUD functions +================== +*/ + +vector HUD_Get_Num_Color (float x, float maxvalue) +{ + float blinkingamt; + vector color; + if(x >= maxvalue) { + color.x = sin(2*M_PI*time); + color.y = 1; + color.z = sin(2*M_PI*time); + } + else if(x > maxvalue * 0.75) { + color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0 + color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1 + color.z = 0; + } + else if(x > maxvalue * 0.5) { + color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4 + color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9 + color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0 + } + else if(x > maxvalue * 0.25) { + color.x = 1; + color.y = 1; + color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1 + } + else if(x > maxvalue * 0.1) { + color.x = 1; + color.y = (x-20)*90/27/100; // green value between 0 -> 1 + color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2 + } + else { + color.x = 1; + color.y = 0; + color.z = 0; + } + + blinkingamt = (1 - x/maxvalue/0.25); + if(blinkingamt > 0) + { + color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time); + color.y = color.y - color.y * blinkingamt * sin(2*M_PI*time); + color.z = color.z - color.z * blinkingamt * sin(2*M_PI*time); + } + return color; +} + +float HUD_GetRowCount(int item_count, vector size, float item_aspect) +{ + float aspect = size_y / size_x; + return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count); +} + +vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect) +{ + float columns, rows; + float ratio, best_ratio = 0; + float best_columns = 1, best_rows = 1; + bool vertical = (psize.x / psize.y >= item_aspect); + if(vertical) + { + psize = eX * psize.y + eY * psize.x; + item_aspect = 1 / item_aspect; + } + + rows = ceil(sqrt(item_count)); + columns = ceil(item_count/rows); + while(columns >= 1) + { + ratio = (psize.x/columns) / (psize.y/rows); + if(ratio > item_aspect) + ratio = item_aspect * item_aspect / ratio; + + if(ratio <= best_ratio) + break; // ratio starts decreasing by now, skip next configurations + + best_columns = columns; + best_rows = rows; + best_ratio = ratio; + + if(columns == 1) + break; + + --columns; + rows = ceil(item_count/columns); + } + + if(vertical) + return eX * best_rows + eY * best_columns; + else + return eX * best_columns + eY * best_rows; +} + +// return the string of the onscreen race timer +string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname) +{ + string col; + string timestr; + string cpname; + string lapstr; + lapstr = ""; + + if(theirtime == 0) // goal hit + { + if(mytime > 0) + { + timestr = strcat("+", ftos_decimals(+mytime, TIME_DECIMALS)); + col = "^1"; + } + else if(mytime == 0) + { + timestr = "+0.0"; + col = "^3"; + } + else + { + timestr = strcat("-", ftos_decimals(-mytime, TIME_DECIMALS)); + col = "^2"; + } + + if(lapdelta > 0) + { + lapstr = sprintf(_(" (-%dL)"), lapdelta); + col = "^2"; + } + else if(lapdelta < 0) + { + lapstr = sprintf(_(" (+%dL)"), -lapdelta); + col = "^1"; + } + } + else if(theirtime > 0) // anticipation + { + if(mytime >= theirtime) + timestr = strcat("+", ftos_decimals(mytime - theirtime, TIME_DECIMALS)); + else + timestr = TIME_ENCODED_TOSTRING(TIME_ENCODE(theirtime)); + col = "^3"; + } + else + { + col = "^7"; + timestr = ""; + } + + if(cp == 254) + cpname = _("Start line"); + else if(cp == 255) + cpname = _("Finish line"); + else if(cp) + cpname = sprintf(_("Intermediate %d"), cp); + else + cpname = _("Finish line"); + + if(theirtime < 0) + return strcat(col, cpname); + else if(theirname == "") + return strcat(col, sprintf("%s (%s)", cpname, timestr)); + else + return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(theirname, col, lapstr))); +} + +// Check if the given name already exist in race rankings? In that case, where? (otherwise return 0) +int race_CheckName(string net_name) +{ + int i; + for (i=RANKINGS_CNT-1;i>=0;--i) + if(grecordholder[i] == net_name) + return i+1; + return 0; +} + +/* +================== +HUD panels +================== +*/ + +//basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu +void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag) +{ + if(!length_ratio || !theAlpha) + return; + if(length_ratio > 1) + length_ratio = 1; + if (baralign == 3) + { + if(length_ratio < -1) + length_ratio = -1; + } + else if(length_ratio < 0) + return; + + vector square; + vector width, height; + if(vertical) { + pic = strcat(hud_skin_path, "/", pic, "_vertical"); + if(precache_pic(pic) == "") { + pic = "gfx/hud/default/progressbar_vertical"; + } + + if (baralign == 1) // bottom align + theOrigin.y += (1 - length_ratio) * theSize.y; + else if (baralign == 2) // center align + theOrigin.y += 0.5 * (1 - length_ratio) * theSize.y; + else if (baralign == 3) // center align, positive values down, negative up + { + theSize.y *= 0.5; + if (length_ratio > 0) + theOrigin.y += theSize.y; + else + { + theOrigin.y += (1 + length_ratio) * theSize.y; + length_ratio = -length_ratio; + } + } + theSize.y *= length_ratio; + + vector bH; + width = eX * theSize.x; + height = eY * theSize.y; + if(theSize.y <= theSize.x * 2) + { + // button not high enough + // draw just upper and lower part then + square = eY * theSize.y * 0.5; + bH = eY * (0.25 * theSize.y / (theSize.x * 2)); + drawsubpic(theOrigin, square + width, pic, '0 0 0', eX + bH, theColor, theAlpha, drawflag); + drawsubpic(theOrigin + square, square + width, pic, eY - bH, eX + bH, theColor, theAlpha, drawflag); + } + else + { + square = eY * theSize.x; + drawsubpic(theOrigin, width + square, pic, '0 0 0', '1 0.25 0', theColor, theAlpha, drawflag); + drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0 0.25 0', '1 0.5 0', theColor, theAlpha, drawflag); + drawsubpic(theOrigin + height - square, width + square, pic, '0 0.75 0', '1 0.25 0', theColor, theAlpha, drawflag); + } + } else { + pic = strcat(hud_skin_path, "/", pic); + if(precache_pic(pic) == "") { + pic = "gfx/hud/default/progressbar"; + } + + if (baralign == 1) // right align + theOrigin.x += (1 - length_ratio) * theSize.x; + else if (baralign == 2) // center align + theOrigin.x += 0.5 * (1 - length_ratio) * theSize.x; + else if (baralign == 3) // center align, positive values on the right, negative on the left + { + theSize.x *= 0.5; + if (length_ratio > 0) + theOrigin.x += theSize.x; + else + { + theOrigin.x += (1 + length_ratio) * theSize.x; + length_ratio = -length_ratio; + } + } + theSize.x *= length_ratio; + + vector bW; + width = eX * theSize.x; + height = eY * theSize.y; + if(theSize.x <= theSize.y * 2) + { + // button not wide enough + // draw just left and right part then + square = eX * theSize.x * 0.5; + bW = eX * (0.25 * theSize.x / (theSize.y * 2)); + drawsubpic(theOrigin, square + height, pic, '0 0 0', eY + bW, theColor, theAlpha, drawflag); + drawsubpic(theOrigin + square, square + height, pic, eX - bW, eY + bW, theColor, theAlpha, drawflag); + } + else + { + square = eX * theSize.y; + drawsubpic(theOrigin, height + square, pic, '0 0 0', '0.25 1 0', theColor, theAlpha, drawflag); + drawsubpic(theOrigin + square, theSize - 2 * square, pic, '0.25 0 0', '0.5 1 0', theColor, theAlpha, drawflag); + drawsubpic(theOrigin + width - square, height + square, pic, '0.75 0 0', '0.25 1 0', theColor, theAlpha, drawflag); + } + } +} + +void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag) +{ + if(!theAlpha) + return; + + string pic; + pic = strcat(hud_skin_path, "/num_leading"); + if(precache_pic(pic) == "") { + pic = "gfx/hud/default/num_leading"; + } + + drawsubpic(pos, eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0 0 0', '0.25 1 0', color, theAlpha, drawflag); + if(mySize.x/mySize.y > 2) + drawsubpic(pos + eX * mySize.y, eX * (mySize.x - 2 * mySize.y) + eY * mySize.y, pic, '0.25 0 0', '0.5 1 0', color, theAlpha, drawflag); + drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag); +} + +void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha, float fadelerp) +{ + vector newPos = '0 0 0', newSize = '0 0 0'; + vector picpos, numpos; + + if (vertical) + { + if(mySize.y/mySize.x > 2) + { + newSize.y = 2 * mySize.x; + newSize.x = mySize.x; + + newPos.y = myPos.y + (mySize.y - newSize.y) / 2; + newPos.x = myPos.x; + } + else + { + newSize.x = 1/2 * mySize.y; + newSize.y = mySize.y; + + newPos.x = myPos.x + (mySize.x - newSize.x) / 2; + newPos.y = myPos.y; + } + + if(icon_right_align) + { + numpos = newPos; + picpos = newPos + eY * newSize.x; + } + else + { + picpos = newPos; + numpos = newPos + eY * newSize.x; + } + + newSize.y /= 2; + drawpic_aspect_skin(picpos, icon, newSize, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); + // make number smaller than icon, it looks better + // reduce only y to draw numbers with different number of digits with the same y size + numpos.y += newSize.y * ((1 - 0.7) / 2); + newSize.y *= 0.7; + drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); + return; + } + + if(mySize.x/mySize.y > 3) + { + newSize.x = 3 * mySize.y; + newSize.y = mySize.y; + + newPos.x = myPos.x + (mySize.x - newSize.x) / 2; + newPos.y = myPos.y; + } + else + { + newSize.y = 1/3 * mySize.x; + newSize.x = mySize.x; + + newPos.y = myPos.y + (mySize.y - newSize.y) / 2; + newPos.x = myPos.x; + } + + if(icon_right_align) // right align + { + numpos = newPos; + picpos = newPos + eX * 2 * newSize.y; + } + else // left align + { + numpos = newPos + eX * newSize.y; + picpos = newPos; + } + + // NOTE: newSize_x is always equal to 3 * mySize_y so we can use + // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y + drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); + drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); +} + +void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha) +{ + DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0); +} + +#include "all.inc" + +/* +================== +Main HUD system +================== +*/ + +void HUD_Vehicle() +{ + if(autocvar__hud_configure) return; + if(intermission == 2) return; + + if(hud == HUD_BUMBLEBEE_GUN) + CSQC_BUMBLE_GUN_HUD(); + else { + Vehicle info = get_vehicleinfo(hud); + info.vr_hud(info); + } +} + +bool HUD_Panel_CheckFlags(int showflags) +{ + if ( HUD_Minigame_Showpanels() ) + return showflags & PANEL_SHOW_MINIGAME; + if(intermission == 2) + return showflags & PANEL_SHOW_MAPVOTE; + return showflags & PANEL_SHOW_MAINGAME; +} + +void HUD_Panel_Draw(entity panent) +{ + panel = panent; + if(autocvar__hud_configure) + { + if(panel.panel_configflags & PANEL_CONFIG_MAIN) + panel.panel_draw(); + } + else if(HUD_Panel_CheckFlags(panel.panel_showflags)) + panel.panel_draw(); +} + +void HUD_Reset() +{ + // reset gametype specific icons + if(gametype == MAPINFO_TYPE_CTF) + HUD_Mod_CTF_Reset(); +} + +void HUD_Main() +{ + int i; + // global hud theAlpha fade + if(menu_enabled == 1) + hud_fade_alpha = 1; + else + hud_fade_alpha = (1 - autocvar__menu_alpha); + + if(scoreboard_fade_alpha) + hud_fade_alpha = (1 - scoreboard_fade_alpha); + + HUD_Configure_Frame(); + + // panels that we want to be active together with the scoreboard + // they must fade only when the menu does + if(scoreboard_fade_alpha == 1) + { + HUD_Panel_Draw(HUD_PANEL(CENTERPRINT)); + return; + } + + if(!autocvar__hud_configure && !hud_fade_alpha) + { + hud_fade_alpha = 1; + HUD_Panel_Draw(HUD_PANEL(VOTE)); + hud_fade_alpha = 0; + return; + } + + // Drawing stuff + if (hud_skin_prev != autocvar_hud_skin) + { + if (hud_skin_path) + strunzone(hud_skin_path); + hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); + if (hud_skin_prev) + strunzone(hud_skin_prev); + hud_skin_prev = strzone(autocvar_hud_skin); + } + + // draw the dock + if(autocvar_hud_dock != "" && autocvar_hud_dock != "0") + { + int f; + vector color; + float hud_dock_color_team = autocvar_hud_dock_color_team; + if((teamplay) && hud_dock_color_team) { + if(autocvar__hud_configure && myteam == NUM_SPECTATOR) + color = '1 0 0' * hud_dock_color_team; + else + color = myteamcolors * hud_dock_color_team; + } + else if(autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && hud_dock_color_team) { + color = '1 0 0' * hud_dock_color_team; + } + else + { + string hud_dock_color = autocvar_hud_dock_color; + if(hud_dock_color == "shirt") { + f = stof(getplayerkeyvalue(current_player, "colors")); + color = colormapPaletteColor(floor(f / 16), 0); + } + else if(hud_dock_color == "pants") { + f = stof(getplayerkeyvalue(current_player, "colors")); + color = colormapPaletteColor(f % 16, 1); + } + else + color = stov(hud_dock_color); + } + + string pic; + pic = strcat(hud_skin_path, "/", autocvar_hud_dock); + if(precache_pic(pic) == "") { + pic = strcat(hud_skin_path, "/dock_medium"); + if(precache_pic(pic) == "") { + pic = "gfx/hud/default/dock_medium"; + } + } + drawpic('0 0 0', pic, eX * vid_conwidth + eY * vid_conheight, color, autocvar_hud_dock_alpha * hud_fade_alpha, DRAWFLAG_NORMAL); // no aspect ratio forcing on dock... + } + + // cache the panel order into the panel_order array + if(autocvar__hud_panelorder != hud_panelorder_prev) { + for(i = 0; i < hud_panels_COUNT; ++i) + panel_order[i] = -1; + string s = ""; + int p_num; + bool warning = false; + int argc = tokenize_console(autocvar__hud_panelorder); + if (argc > hud_panels_COUNT) + warning = true; + //first detect wrong/missing panel numbers + for(i = 0; i < hud_panels_COUNT; ++i) { + p_num = stoi(argv(i)); + if (p_num >= 0 && p_num < hud_panels_COUNT) { //correct panel number? + if (panel_order[p_num] == -1) //found for the first time? + s = strcat(s, ftos(p_num), " "); + panel_order[p_num] = 1; //mark as found + } + else + warning = true; + } + for(i = 0; i < hud_panels_COUNT; ++i) { + if (panel_order[i] == -1) { + warning = true; + s = strcat(s, ftos(i), " "); //add missing panel number + } + } + if (warning) + LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n"); + + cvar_set("_hud_panelorder", s); + if(hud_panelorder_prev) + strunzone(hud_panelorder_prev); + hud_panelorder_prev = strzone(s); + + //now properly set panel_order + tokenize_console(s); + for(i = 0; i < hud_panels_COUNT; ++i) { + panel_order[i] = stof(argv(i)); + } + } + + hud_draw_maximized = 0; + // draw panels in the order specified by panel_order array + for(i = hud_panels_COUNT - 1; i >= 0; --i) + HUD_Panel_Draw(hud_panels_from(panel_order[i])); + + HUD_Vehicle(); + + hud_draw_maximized = 1; // panels that may be maximized must check this var + // draw maximized panels on top + if(hud_panel_radar_maximized) + HUD_Panel_Draw(HUD_PANEL(RADAR)); + if(autocvar__con_chat_maximized) + HUD_Panel_Draw(HUD_PANEL(CHAT)); + if(hud_panel_quickmenu) + HUD_Panel_Draw(HUD_PANEL(QUICKMENU)); + + if (scoreboard_active || intermission == 2) + HUD_Reset(); + + HUD_Configure_PostDraw(); + + hud_configure_prev = autocvar__hud_configure; +} diff --git a/qcsrc/client/hud/hud.qh b/qcsrc/client/hud/hud.qh new file mode 100644 index 0000000000..7762d16c03 --- /dev/null +++ b/qcsrc/client/hud/hud.qh @@ -0,0 +1,420 @@ +#ifndef CLIENT_HUD_H +#define CLIENT_HUD_H + +#include "../common/weapons/all.qh" + +bool HUD_Radar_Clickable(); +void HUD_Radar_Mouse(); + +REGISTRY(hud_panels, BITS(6)) +#define hud_panels_from(i) _hud_panels_from(i, NULL) +REGISTER_REGISTRY(hud_panels) + +#define REGISTER_HUD_PANEL(id, draw_func, name, configflags, showflags) \ + void draw_func(); \ + REGISTER(hud_panels, HUD_PANEL, id, m_id, new(hud_panel)) { \ + make_pure(this); \ + this.panel_id = this.m_id; \ + this.panel_draw = draw_func; \ + this.panel_name = #name; \ + this.panel_configflags = configflags; \ + this.panel_showflags = showflags; \ + } + +#define HUD_PANEL(NAME) HUD_PANEL_##NAME + +// draw the background/borders +#define HUD_Panel_DrawBg(theAlpha) do { \ + if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \ + draw_BorderPicture(panel_pos - '1 1 0' * panel_bg_border, panel.current_panel_bg, panel_size + '1 1 0' * 2 * panel_bg_border, panel_bg_color, panel_bg_alpha * theAlpha, '1 1 0' * (panel_bg_border/BORDER_MULTIPLIER));\ +} while(0) + +int panel_order[hud_panels_MAX]; +string hud_panelorder_prev; + +bool hud_draw_maximized; +bool hud_panel_radar_maximized; +bool hud_panel_radar_mouse; +float hud_panel_radar_bottom; +bool hud_panel_radar_temp_hidden; +bool chat_panel_modified; +bool radar_panel_modified; + +float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary); +void HUD_Radar_Hide_Maximized(); + +void HUD_Reset (); +void HUD_Main (); + +int vote_yescount; +int vote_nocount; +int vote_needed; +int vote_highlighted; // currently selected vote + +int vote_active; // is there an active vote? +int vote_prev; // previous state of vote_active to check for a change +float vote_alpha; +float vote_change; // "time" when vote_active changed + +float hud_panel_quickmenu; + +vector mousepos; +vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click) +vector panel_click_resizeorigin; // coordinates for opposite point when resizing +float resizeCorner; // 1 = topleft, 2 = topright, 3 = bottomleft, 4 = bottomright +entity highlightedPanel; +float highlightedAction; // 0 = nothing, 1 = move, 2 = resize + +const float BORDER_MULTIPLIER = 0.25; +float scoreboard_bottom; +int weapon_accuracy[Weapons_MAX]; + +int complain_weapon; +string complain_weapon_name; +float complain_weapon_type; +float complain_weapon_time; + +int ps_primary, ps_secondary; +int ts_primary, ts_secondary; + +int last_switchweapon; +int last_activeweapon; +float weapontime; +float weaponprevtime; + +float teamnagger; + +float hud_configure_checkcollisions; +float hud_configure_prev; +vector hud_configure_gridSize; +vector hud_configure_realGridSize; + +int hudShiftState; +const int S_SHIFT = 1; +const int S_CTRL = 2; +const int S_ALT = 4; + +float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel + +float hud_fade_alpha; + +string hud_skin_path; +string hud_skin_prev; + +vector myteamcolors; + +entity highlightedPanel_backup; +vector panel_pos_backup; +vector panel_size_backup; + +vector panel_size_copied; + +entity panel; +entityclass(HUDPanel); +class(HUDPanel) .string panel_name; +class(HUDPanel) .int panel_id; +class(HUDPanel) .vector current_panel_pos; +class(HUDPanel) .vector current_panel_size; +class(HUDPanel) .string current_panel_bg; +class(HUDPanel) .float current_panel_bg_alpha; +class(HUDPanel) .float current_panel_bg_border; +class(HUDPanel) .vector current_panel_bg_color; +class(HUDPanel) .float current_panel_bg_color_team; +class(HUDPanel) .float current_panel_bg_padding; +class(HUDPanel) .float current_panel_fg_alpha; +class(HUDPanel) .float update_time; +float panel_enabled; +vector panel_pos; +vector panel_size; +string panel_bg_str; // "_str" vars contain the raw value of the cvar, non-"_str" contains what hud.qc code should use +vector panel_bg_color; +string panel_bg_color_str; +float panel_bg_color_team; +string panel_bg_color_team_str; +float panel_fg_alpha; +float panel_bg_alpha; +string panel_bg_alpha_str; +float panel_bg_border; +string panel_bg_border_str; +float panel_bg_padding; +string panel_bg_padding_str; + +class(HUDPanel) .void() panel_draw; + +// chat panel can be reduced / moved while the mapvote is active +// let know the mapvote panel about chat pos and size +float chat_posy; +float chat_sizey; + +float current_player; + +float stringwidth_colors(string s, vector theSize); +float stringwidth_nocolors(string s, vector theSize); +float GetPlayerColorForce(int i); +int GetPlayerColor(int i); +string GetPlayerName(int i); +void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag); + +.int panel_showflags; +const int PANEL_SHOW_NEVER = 0x00; +const int PANEL_SHOW_MAINGAME = 0x01; +const int PANEL_SHOW_MINIGAME = 0x02; +const int PANEL_SHOW_MAPVOTE = 0x04; +const int PANEL_SHOW_ALWAYS = 0xff; +bool HUD_Panel_CheckFlags(int showflags); + +.int panel_configflags; +const int PANEL_CONFIG_NO = 0x00; +const int PANEL_CONFIG_MAIN = 0x01; + + +// prev_* vars contain the health/armor at the previous FRAME +// set to -1 when player is dead or was not playing +int prev_health, prev_armor; +float health_damagetime, armor_damagetime; +int health_beforedamage, armor_beforedamage; +// old_p_* vars keep track of previous values when smoothing value changes of the progressbar +int old_p_health, old_p_armor; +float old_p_healthtime, old_p_armortime; +// prev_p_* vars contain the health/armor progressbar value at the previous FRAME +// set to -1 to forcedly stop effects when we switch spectated player (e.g. from playerX: 70h to playerY: 50h) +int prev_p_health, prev_p_armor; + +void HUD_ItemsTime(); + +REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) +REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) +REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) +REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) +REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) +REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) +REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) +REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) +REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) +REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_ALWAYS ) +REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE ) +REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +// always add new panels to the end of list + +// Because calling lots of functions in QC apparently cuts fps in half on many machines: +// ---------------------- +// MACRO HELL STARTS HERE +// ---------------------- +// Little help for the poor people who have to make sense of this: Start from the bottom ;) + +// Get value for panel.current_panel_bg: if "" fetch default, else use panel_bg_str +// comment on last line of macro: // we probably want to see a background in config mode at all times... +#define HUD_Panel_GetBg() do { \ + string panel_bg; \ + if (!autocvar__hud_configure && panel_bg_str == "0") { \ + panel_bg = "0"; \ + } else { \ + if (panel_bg_str == "") { \ + panel_bg_str = autocvar_hud_panel_bg; \ + } \ + if (panel_bg_str == "0" && !autocvar__hud_configure) { \ + panel_bg = "0"; \ + } else { \ + if (panel_bg_str == "0" && autocvar__hud_configure) \ + panel_bg_alpha_str = "0"; \ + panel_bg = strcat(hud_skin_path, "/", panel_bg_str); \ + if (precache_pic(panel_bg) == "") { \ + panel_bg = strcat(hud_skin_path, "/", "border_default"); \ + if (precache_pic(panel_bg) == "") { \ + panel_bg = strcat("gfx/hud/default/", "border_default"); \ + } \ + } \ + } \ + } \ + if (panel.current_panel_bg) \ + strunzone(panel.current_panel_bg); \ + panel.current_panel_bg = strzone(panel_bg); \ +} while(0) + +// Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector. +#define HUD_Panel_GetColor() do { \ + if ((teamplay) && panel_bg_color_team) { \ + if (autocvar__hud_configure && myteam == NUM_SPECTATOR) \ + panel_bg_color = '1 0 0' * panel_bg_color_team; \ + else \ + panel_bg_color = myteamcolors * panel_bg_color_team; \ + } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) { \ + panel_bg_color = '1 0 0' * panel_bg_color_team; \ + } else { \ + if (panel_bg_color_str == "") { \ + panel_bg_color = autocvar_hud_panel_bg_color; \ + } else { \ + if (panel_bg_color_str == "shirt") { \ + panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \ + } else if (panel_bg_color_str == "pants") { \ + panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \ + } else { \ + panel_bg_color = stov(panel_bg_color_str); \ + } \ + } \ + } \ +} while(0) + +// Get value for panel_bg_color_team: if "" fetch default, else use panel_bg_color_team_str +#define HUD_Panel_GetColorTeam() do { \ + if (panel_bg_color_team_str == "") { \ + panel_bg_color_team = autocvar_hud_panel_bg_color_team; \ + } else { \ + panel_bg_color_team = stof(panel_bg_color_team_str); \ + } \ +} while(0) + +// Get value for panel_bg_alpha: if "" fetch default, else use panel_bg_alpha. Also do various menu dialog fadeout/in checks, and minalpha checks +// comment on line 3 of macro: // do not set a minalpha cap when showing the config dialog for this panel +#define HUD_Panel_GetBgAlpha() do { \ + if (panel_bg_alpha_str == "") { \ + panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); \ + } \ + panel_bg_alpha = stof(panel_bg_alpha_str); \ + if (autocvar__hud_configure) { \ + if (!panel_enabled) \ + panel_bg_alpha = 0.25; \ + else if (menu_enabled == 2 && panel == highlightedPanel) \ + panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\ + else \ + panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha); \ + } \ +} while(0) + +// Get value for panel_fg_alpha. Also do various minalpha checks +// comment on line 2 of macro: // ALWAYS show disabled panels at 0.25 alpha when in config mode +#define HUD_Panel_GetFgAlpha() do { \ + panel_fg_alpha = autocvar_hud_panel_fg_alpha; \ + if (autocvar__hud_configure && !panel_enabled) \ + panel_fg_alpha = 0.25; \ +} while(0) + +// Get border. See comments above, it's similar. +#define HUD_Panel_GetBorder() do { \ + if (panel_bg_border_str == "") { \ + panel_bg_border = autocvar_hud_panel_bg_border; \ + } else { \ + panel_bg_border = stof(panel_bg_border_str); \ + } \ +} while(0) + +// Get padding. See comments above, it's similar. +// last line is a port of the old function, basically always make sure the panel contents are at least 5 pixels tall/wide, to disallow extreme padding values +#define HUD_Panel_GetPadding() do { \ + if (panel_bg_padding_str == "") { \ + panel_bg_padding = autocvar_hud_panel_bg_padding; \ + } else { \ + panel_bg_padding = stof(panel_bg_padding_str); \ + } \ + panel_bg_padding = min(min(panel_size.x, panel_size.y)/2 - 5, panel_bg_padding); \ +} while(0) + +// return smoothly faded pos and size of given panel when a dialog is active +// don't center too wide panels, it doesn't work with different resolutions +#define HUD_Panel_UpdatePosSize_ForMenu() do { \ + vector menu_enable_size = panel_size; \ + float max_panel_width = 0.52 * vid_conwidth; \ + if(panel_size.x > max_panel_width) \ + { \ + menu_enable_size.x = max_panel_width; \ + menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \ + } \ + vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \ + panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \ + panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \ +} while(0) + +// Scale the pos and size vectors to absolute coordinates +#define HUD_Panel_ScalePosSize() do { \ + panel_pos.x *= vid_conwidth; panel_pos.y *= vid_conheight; \ + panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight; \ +} while(0) + +// NOTE: in hud_configure mode cvars must be reloaded every frame +#define HUD_Panel_UpdateCvars() do { \ + if (panel.update_time <= time) { \ + if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ + panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ + panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ + HUD_Panel_ScalePosSize(); \ + panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \ + panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \ + panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \ + panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \ + panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ + panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \ + HUD_Panel_GetBg(); \ + if (panel.current_panel_bg != "0") { \ + HUD_Panel_GetColorTeam(); \ + HUD_Panel_GetColor(); \ + HUD_Panel_GetBgAlpha(); \ + HUD_Panel_GetBorder(); \ + } \ + HUD_Panel_GetFgAlpha(); \ + HUD_Panel_GetPadding(); \ + panel.current_panel_bg_alpha = panel_bg_alpha; \ + panel.current_panel_fg_alpha = panel_fg_alpha; \ + if (menu_enabled == 2 && panel == highlightedPanel) { \ + HUD_Panel_UpdatePosSize_ForMenu(); \ + } else { \ + panel_bg_alpha *= hud_fade_alpha; \ + panel_fg_alpha *= hud_fade_alpha; \ + } \ + panel.current_panel_pos = panel_pos; \ + panel.current_panel_size = panel_size; \ + panel.current_panel_bg_border = panel_bg_border; \ + panel.current_panel_bg_color = panel_bg_color; \ + panel.current_panel_bg_color_team = panel_bg_color_team; \ + panel.current_panel_bg_padding = panel_bg_padding; \ + panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \ + } else { \ + panel_pos = panel.current_panel_pos; \ + panel_size = panel.current_panel_size; \ + panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha; \ + panel_bg_border = panel.current_panel_bg_border; \ + panel_bg_color = panel.current_panel_bg_color; \ + panel_bg_color_team = panel.current_panel_bg_color_team; \ + panel_bg_padding = panel.current_panel_bg_padding; \ + panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha; \ + } \ +} while(0) + +#define HUD_Panel_UpdatePosSize() do { \ + panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ + panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ + panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ + HUD_Panel_ScalePosSize(); \ + if (menu_enabled == 2 && panel == highlightedPanel) { \ + HUD_Panel_UpdatePosSize_ForMenu(); \ + } \ + panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ + HUD_Panel_GetBorder(); \ +} while(0) + +const int NOTIFY_MAX_ENTRIES = 10; +const float NOTIFY_ICON_MARGIN = 0.02; + +int notify_index; +int notify_count; +float notify_times[NOTIFY_MAX_ENTRIES]; +string notify_attackers[NOTIFY_MAX_ENTRIES]; +string notify_victims[NOTIFY_MAX_ENTRIES]; +string notify_icons[NOTIFY_MAX_ENTRIES]; + +void HUD_Notify_Push(string icon, string attacker, string victim); + +var void HUD_ModIcons_GameType(vector pos, vector size); +void HUD_ModIcons_SetFunc(); +#endif diff --git a/qcsrc/client/hud/hud_config.qc b/qcsrc/client/hud/hud_config.qc new file mode 100644 index 0000000000..6b05078d51 --- /dev/null +++ b/qcsrc/client/hud/hud_config.qc @@ -0,0 +1,1297 @@ +#include "hud_config.qh" + +#include "hud.qh" + +#define HUD_Write(s) fputs(fh, s) +// q: quoted, n: not quoted +#define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n")) +#define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n")) +#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf)) +#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf)) +// Save the config +void HUD_Panel_ExportCfg(string cfgname) +{ + float fh; + string filename = strcat("hud_", autocvar_hud_skin, "_", cfgname, ".cfg"); + fh = fopen(filename, FILE_WRITE); + if(fh >= 0) + { + HUD_Write_Cvar_q("hud_skin"); + HUD_Write_Cvar_q("hud_panel_bg"); + HUD_Write_Cvar_q("hud_panel_bg_color"); + HUD_Write_Cvar_q("hud_panel_bg_color_team"); + HUD_Write_Cvar_q("hud_panel_bg_alpha"); + HUD_Write_Cvar_q("hud_panel_bg_border"); + HUD_Write_Cvar_q("hud_panel_bg_padding"); + HUD_Write_Cvar_q("hud_panel_fg_alpha"); + HUD_Write("\n"); + + HUD_Write_Cvar_q("hud_dock"); + HUD_Write_Cvar_q("hud_dock_color"); + HUD_Write_Cvar_q("hud_dock_color_team"); + HUD_Write_Cvar_q("hud_dock_alpha"); + HUD_Write("\n"); + + HUD_Write_Cvar_q("hud_progressbar_alpha"); + HUD_Write_Cvar_q("hud_progressbar_strength_color"); + HUD_Write_Cvar_q("hud_progressbar_shield_color"); + HUD_Write_Cvar_q("hud_progressbar_health_color"); + HUD_Write_Cvar_q("hud_progressbar_armor_color"); + HUD_Write_Cvar_q("hud_progressbar_fuel_color"); + HUD_Write_Cvar_q("hud_progressbar_nexball_color"); + HUD_Write_Cvar_q("hud_progressbar_speed_color"); + HUD_Write_Cvar_q("hud_progressbar_acceleration_color"); + HUD_Write_Cvar_q("hud_progressbar_acceleration_neg_color"); + HUD_Write("\n"); + + HUD_Write_Cvar_q("_hud_panelorder"); + HUD_Write("\n"); + + HUD_Write_Cvar_q("hud_configure_grid"); + HUD_Write_Cvar_q("hud_configure_grid_xsize"); + HUD_Write_Cvar_q("hud_configure_grid_ysize"); + HUD_Write("\n"); + + // common cvars for all panels + for (int i = 0; i < hud_panels_COUNT; ++i) + { + panel = hud_panels_from(i); + + HUD_Write_PanelCvar_n(""); + HUD_Write_PanelCvar_q("_pos"); + HUD_Write_PanelCvar_q("_size"); + HUD_Write_PanelCvar_q("_bg"); + HUD_Write_PanelCvar_q("_bg_color"); + HUD_Write_PanelCvar_q("_bg_color_team"); + HUD_Write_PanelCvar_q("_bg_alpha"); + HUD_Write_PanelCvar_q("_bg_border"); + HUD_Write_PanelCvar_q("_bg_padding"); + switch(panel) { + case HUD_PANEL_WEAPONS: + HUD_Write_PanelCvar_q("_accuracy"); + HUD_Write_PanelCvar_q("_label"); + HUD_Write_PanelCvar_q("_label_scale"); + HUD_Write_PanelCvar_q("_complainbubble"); + HUD_Write_PanelCvar_q("_complainbubble_padding"); + HUD_Write_PanelCvar_q("_complainbubble_time"); + HUD_Write_PanelCvar_q("_complainbubble_fadetime"); + HUD_Write_PanelCvar_q("_complainbubble_color_outofammo"); + HUD_Write_PanelCvar_q("_complainbubble_color_donthave"); + HUD_Write_PanelCvar_q("_complainbubble_color_unavailable"); + HUD_Write_PanelCvar_q("_ammo"); + HUD_Write_PanelCvar_q("_ammo_color"); + HUD_Write_PanelCvar_q("_ammo_alpha"); + HUD_Write_PanelCvar_q("_aspect"); + HUD_Write_PanelCvar_q("_timeout"); + HUD_Write_PanelCvar_q("_timeout_effect"); + HUD_Write_PanelCvar_q("_timeout_fadebgmin"); + HUD_Write_PanelCvar_q("_timeout_fadefgmin"); + HUD_Write_PanelCvar_q("_timeout_speed_in"); + HUD_Write_PanelCvar_q("_timeout_speed_out"); + HUD_Write_PanelCvar_q("_onlyowned"); + HUD_Write_PanelCvar_q("_noncurrent_alpha"); + HUD_Write_PanelCvar_q("_noncurrent_scale"); + break; + case HUD_PANEL_AMMO: + HUD_Write_PanelCvar_q("_onlycurrent"); + HUD_Write_PanelCvar_q("_noncurrent_alpha"); + HUD_Write_PanelCvar_q("_noncurrent_scale"); + HUD_Write_PanelCvar_q("_iconalign"); + HUD_Write_PanelCvar_q("_progressbar"); + HUD_Write_PanelCvar_q("_progressbar_name"); + HUD_Write_PanelCvar_q("_progressbar_xoffset"); + HUD_Write_PanelCvar_q("_text"); + break; + case HUD_PANEL_POWERUPS: + HUD_Write_PanelCvar_q("_iconalign"); + HUD_Write_PanelCvar_q("_baralign"); + HUD_Write_PanelCvar_q("_progressbar"); + HUD_Write_PanelCvar_q("_text"); + break; + case HUD_PANEL_HEALTHARMOR: + HUD_Write_PanelCvar_q("_flip"); + HUD_Write_PanelCvar_q("_iconalign"); + HUD_Write_PanelCvar_q("_baralign"); + HUD_Write_PanelCvar_q("_progressbar"); + HUD_Write_PanelCvar_q("_progressbar_health"); + HUD_Write_PanelCvar_q("_progressbar_armor"); + HUD_Write_PanelCvar_q("_progressbar_gfx"); + HUD_Write_PanelCvar_q("_progressbar_gfx_smooth"); + HUD_Write_PanelCvar_q("_text"); + break; + case HUD_PANEL_NOTIFY: + HUD_Write_PanelCvar_q("_flip"); + HUD_Write_PanelCvar_q("_fontsize"); + HUD_Write_PanelCvar_q("_time"); + HUD_Write_PanelCvar_q("_fadetime"); + HUD_Write_PanelCvar_q("_icon_aspect"); + break; + case HUD_PANEL_TIMER: + HUD_Write_PanelCvar_q("_increment"); + break; + case HUD_PANEL_RADAR: + HUD_Write_PanelCvar_q("_foreground_alpha"); + HUD_Write_PanelCvar_q("_rotation"); + HUD_Write_PanelCvar_q("_zoommode"); + HUD_Write_PanelCvar_q("_scale"); + HUD_Write_PanelCvar_q("_maximized_scale"); + HUD_Write_PanelCvar_q("_maximized_size"); + HUD_Write_PanelCvar_q("_maximized_rotation"); + HUD_Write_PanelCvar_q("_maximized_zoommode"); + break; + case HUD_PANEL_SCORE: + HUD_Write_PanelCvar_q("_rankings"); + break; + case HUD_PANEL_VOTE: + HUD_Write_PanelCvar_q("_alreadyvoted_alpha"); + break; + case HUD_PANEL_MODICONS: + HUD_Write_PanelCvar_q("_ca_layout"); + HUD_Write_PanelCvar_q("_dom_layout"); + HUD_Write_PanelCvar_q("_freezetag_layout"); + break; + case HUD_PANEL_PRESSEDKEYS: + HUD_Write_PanelCvar_q("_aspect"); + HUD_Write_PanelCvar_q("_attack"); + break; + case HUD_PANEL_ENGINEINFO: + HUD_Write_PanelCvar_q("_framecounter_time"); + HUD_Write_PanelCvar_q("_framecounter_decimals"); + break; + case HUD_PANEL_INFOMESSAGES: + HUD_Write_PanelCvar_q("_flip"); + break; + case HUD_PANEL_PHYSICS: + HUD_Write_PanelCvar_q("_speed_unit"); + HUD_Write_PanelCvar_q("_speed_unit_show"); + HUD_Write_PanelCvar_q("_speed_max"); + HUD_Write_PanelCvar_q("_speed_vertical"); + HUD_Write_PanelCvar_q("_topspeed"); + HUD_Write_PanelCvar_q("_topspeed_time"); + HUD_Write_PanelCvar_q("_acceleration_max"); + HUD_Write_PanelCvar_q("_acceleration_vertical"); + HUD_Write_PanelCvar_q("_flip"); + HUD_Write_PanelCvar_q("_baralign"); + HUD_Write_PanelCvar_q("_progressbar"); + HUD_Write_PanelCvar_q("_progressbar_acceleration_mode"); + HUD_Write_PanelCvar_q("_progressbar_acceleration_scale"); + HUD_Write_PanelCvar_q("_progressbar_acceleration_nonlinear"); + HUD_Write_PanelCvar_q("_text"); + HUD_Write_PanelCvar_q("_text_scale"); + break; + case HUD_PANEL_CENTERPRINT: + HUD_Write_PanelCvar_q("_align"); + HUD_Write_PanelCvar_q("_flip"); + HUD_Write_PanelCvar_q("_fontscale"); + HUD_Write_PanelCvar_q("_time"); + HUD_Write_PanelCvar_q("_fade_in"); + HUD_Write_PanelCvar_q("_fade_out"); + HUD_Write_PanelCvar_q("_fade_subsequent"); + HUD_Write_PanelCvar_q("_fade_subsequent_passone"); + HUD_Write_PanelCvar_q("_fade_subsequent_passone_minalpha"); + HUD_Write_PanelCvar_q("_fade_subsequent_passtwo"); + HUD_Write_PanelCvar_q("_fade_subsequent_passtwo_minalpha"); + HUD_Write_PanelCvar_q("_fade_subsequent_minfontsize"); + HUD_Write_PanelCvar_q("_fade_minfontsize"); + break; + case HUD_PANEL_ITEMSTIME: + HUD_Write_PanelCvar_q("_iconalign"); + HUD_Write_PanelCvar_q("_progressbar"); + HUD_Write_PanelCvar_q("_progressbar_name"); + HUD_Write_PanelCvar_q("_progressbar_reduced"); + HUD_Write_PanelCvar_q("_text"); + HUD_Write_PanelCvar_q("_ratio"); + HUD_Write_PanelCvar_q("_dynamicsize"); + case HUD_PANEL_QUICKMENU: + HUD_Write_PanelCvar_q("_align"); + break; + } + HUD_Write("\n"); + } + HUD_Write("menu_sync\n"); // force the menu to reread the cvars, so that the dialogs are updated + + LOG_INFOF(_("^2Successfully exported to %s! (Note: It's saved in data/data/)\n"), filename); + fclose(fh); + } + else + LOG_INFOF(_("^1Couldn't write to %s\n"), filename); +} + +void HUD_Configure_Exit_Force() +{ + if (menu_enabled) + { + menu_enabled = 0; + localcmd("togglemenu\n"); + } + cvar_set("_hud_configure", "0"); +} + +// check if move will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector +vector HUD_Panel_CheckMove(vector myPos, vector mySize) +{ + vector myCenter, targCenter; + vector myTarget = myPos; + int i; + for (i = 0; i < hud_panels_COUNT; ++i) { + panel = hud_panels_from(i); + if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; + if(panel == highlightedPanel) continue; + HUD_Panel_UpdatePosSize(); + if(!panel_enabled) continue; + + panel_pos -= '1 1 0' * panel_bg_border; + panel_size += '2 2 0' * panel_bg_border; + + if(myPos.y + mySize.y < panel_pos.y) + continue; + if(myPos.y > panel_pos.y + panel_size.y) + continue; + + if(myPos.x + mySize.x < panel_pos.x) + continue; + if(myPos.x > panel_pos.x + panel_size.x) + continue; + + // OK, there IS a collision. + + myCenter.x = myPos.x + 0.5 * mySize.x; + myCenter.y = myPos.y + 0.5 * mySize.y; + + targCenter.x = panel_pos.x + 0.5 * panel_size.x; + targCenter.y = panel_pos.y + 0.5 * panel_size.y; + + if(myCenter.x < targCenter.x && myCenter.y < targCenter.y) // top left (of the target panel) + { + if(myPos.x + mySize.x - panel_pos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side + myTarget.x = panel_pos.x - mySize.x; + else // push it upwards + myTarget.y = panel_pos.y - mySize.y; + } + else if(myCenter.x > targCenter.x && myCenter.y < targCenter.y) // top right + { + if(panel_pos.x + panel_size.x - myPos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side + myTarget.x = panel_pos.x + panel_size.x; + else // push it upwards + myTarget.y = panel_pos.y - mySize.y; + } + else if(myCenter.x < targCenter.x && myCenter.y > targCenter.y) // bottom left + { + if(myPos.x + mySize.x - panel_pos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side + myTarget.x = panel_pos.x - mySize.x; + else // push it downwards + myTarget.y = panel_pos.y + panel_size.y; + } + else if(myCenter.x > targCenter.x && myCenter.y > targCenter.y) // bottom right + { + if(panel_pos.x + panel_size.x - myPos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side + myTarget.x = panel_pos.x + panel_size.x; + else // push it downwards + myTarget.y = panel_pos.y + panel_size.y; + } + //if(cvar("hud_configure_checkcollisions_debug")) + //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL); + } + + return myTarget; +} + +void HUD_Panel_SetPos(vector pos) +{ + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + vector mySize; + mySize = panel_size; + + //if(cvar("hud_configure_checkcollisions_debug")) + //drawfill(pos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL); + + if(autocvar_hud_configure_grid) + { + pos.x = floor((pos.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x; + pos.y = floor((pos.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y; + } + + if(hud_configure_checkcollisions) + pos = HUD_Panel_CheckMove(pos, mySize); + + pos.x = bound(0, pos.x, vid_conwidth - mySize.x); + pos.y = bound(0, pos.y, vid_conheight - mySize.y); + + string s; + s = strcat(ftos(pos.x/vid_conwidth), " ", ftos(pos.y/vid_conheight)); + + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s); +} + +// check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector +vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) { + vector targEndPos; + vector dist; + float ratio = mySize.x/mySize.y; + int i; + for (i = 0; i < hud_panels_COUNT; ++i) { + panel = hud_panels_from(i); + if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; + if(panel == highlightedPanel) continue; + HUD_Panel_UpdatePosSize(); + if(!panel_enabled) continue; + + panel_pos -= '1 1 0' * panel_bg_border; + panel_size += '2 2 0' * panel_bg_border; + + targEndPos = panel_pos + panel_size; + + // resizeorigin is WITHIN target panel, just abort any collision testing against that particular panel to produce expected behaviour! + if(resizeorigin.x > panel_pos.x && resizeorigin.x < targEndPos.x && resizeorigin.y > panel_pos.y && resizeorigin.y < targEndPos.y) + continue; + + if (resizeCorner == 1) + { + // check if this panel is on our way + if (resizeorigin.x <= panel_pos.x) + continue; + if (resizeorigin.y <= panel_pos.y) + continue; + if (targEndPos.x <= resizeorigin.x - mySize.x) + continue; + if (targEndPos.y <= resizeorigin.y - mySize.y) + continue; + + // there is a collision: + // detect which side of the panel we are facing is actually limiting the resizing + // (which side the resize direction finds for first) and reduce the size up to there + // + // dist is the distance between resizeorigin and the "analogous" point of the panel + // in this case between resizeorigin (bottom-right point) and the bottom-right point of the panel + dist.x = resizeorigin.x - targEndPos.x; + dist.y = resizeorigin.y - targEndPos.y; + if (dist.y <= 0 || dist.x / dist.y > ratio) + mySize.x = min(mySize.x, dist.x); + else + mySize.y = min(mySize.y, dist.y); + } + else if (resizeCorner == 2) + { + if (resizeorigin.x >= targEndPos.x) + continue; + if (resizeorigin.y <= panel_pos.y) + continue; + if (panel_pos.x >= resizeorigin.x + mySize.x) + continue; + if (targEndPos.y <= resizeorigin.y - mySize.y) + continue; + + dist.x = panel_pos.x - resizeorigin.x; + dist.y = resizeorigin.y - targEndPos.y; + if (dist.y <= 0 || dist.x / dist.y > ratio) + mySize.x = min(mySize.x, dist.x); + else + mySize.y = min(mySize.y, dist.y); + } + else if (resizeCorner == 3) + { + if (resizeorigin.x <= panel_pos.x) + continue; + if (resizeorigin.y >= targEndPos.y) + continue; + if (targEndPos.x <= resizeorigin.x - mySize.x) + continue; + if (panel_pos.y >= resizeorigin.y + mySize.y) + continue; + + dist.x = resizeorigin.x - targEndPos.x; + dist.y = panel_pos.y - resizeorigin.y; + if (dist.y <= 0 || dist.x / dist.y > ratio) + mySize.x = min(mySize.x, dist.x); + else + mySize.y = min(mySize.y, dist.y); + } + else if (resizeCorner == 4) + { + if (resizeorigin.x >= targEndPos.x) + continue; + if (resizeorigin.y >= targEndPos.y) + continue; + if (panel_pos.x >= resizeorigin.x + mySize.x) + continue; + if (panel_pos.y >= resizeorigin.y + mySize.y) + continue; + + dist.x = panel_pos.x - resizeorigin.x; + dist.y = panel_pos.y - resizeorigin.y; + if (dist.y <= 0 || dist.x / dist.y > ratio) + mySize.x = min(mySize.x, dist.x); + else + mySize.y = min(mySize.y, dist.y); + } + //if(cvar("hud_configure_checkcollisions_debug")) + //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL); + } + + return mySize; +} + +void HUD_Panel_SetPosSize(vector mySize) +{ + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + vector resizeorigin = panel_click_resizeorigin; + vector myPos; + + // minimum panel size cap + mySize.x = max(0.025 * vid_conwidth, mySize.x); + mySize.y = max(0.025 * vid_conheight, mySize.y); + + if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small. + { + mySize.x = max(17 * autocvar_con_chatsize, mySize.x); + mySize.y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize.y); + } + + // collision testing| + // -----------------+ + + // we need to know pos at this stage, but it might still change later if we hit a screen edge/other panel (?) + if(resizeCorner == 1) { + myPos.x = resizeorigin.x - mySize.x; + myPos.y = resizeorigin.y - mySize.y; + } else if(resizeCorner == 2) { + myPos.x = resizeorigin.x; + myPos.y = resizeorigin.y - mySize.y; + } else if(resizeCorner == 3) { + myPos.x = resizeorigin.x - mySize.x; + myPos.y = resizeorigin.y; + } else { // resizeCorner == 4 + myPos.x = resizeorigin.x; + myPos.y = resizeorigin.y; + } + + // left/top screen edges + if(myPos.x < 0) + mySize.x = mySize.x + myPos.x; + if(myPos.y < 0) + mySize.y = mySize.y + myPos.y; + + // bottom/right screen edges + if(myPos.x + mySize.x > vid_conwidth) + mySize.x = vid_conwidth - myPos.x; + if(myPos.y + mySize.y > vid_conheight) + mySize.y = vid_conheight - myPos.y; + + //if(cvar("hud_configure_checkcollisions_debug")) + //drawfill(myPos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL); + + // before checkresize, otherwise panel can be snapped partially inside another panel or panel aspect ratio can be broken + if(autocvar_hud_configure_grid) + { + mySize.x = floor((mySize.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x; + mySize.y = floor((mySize.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y; + } + + if(hud_configure_checkcollisions) + mySize = HUD_Panel_CheckResize(mySize, resizeorigin); + + // minimum panel size cap, do this once more so we NEVER EVER EVER have a panel smaller than this, JUST IN CASE above code still makes the panel eg negative (impossible to resize back without changing cvars manually then) + mySize.x = max(0.025 * vid_conwidth, mySize.x); + mySize.y = max(0.025 * vid_conheight, mySize.y); + + // do another pos check, as size might have changed by now + if(resizeCorner == 1) { + myPos.x = resizeorigin.x - mySize.x; + myPos.y = resizeorigin.y - mySize.y; + } else if(resizeCorner == 2) { + myPos.x = resizeorigin.x; + myPos.y = resizeorigin.y - mySize.y; + } else if(resizeCorner == 3) { + myPos.x = resizeorigin.x - mySize.x; + myPos.y = resizeorigin.y; + } else { // resizeCorner == 4 + myPos.x = resizeorigin.x; + myPos.y = resizeorigin.y; + } + + //if(cvar("hud_configure_checkcollisions_debug")) + //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL); + + string s; + s = strcat(ftos(mySize.x/vid_conwidth), " ", ftos(mySize.y/vid_conheight)); + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s); + + s = strcat(ftos(myPos.x/vid_conwidth), " ", ftos(myPos.y/vid_conheight)); + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s); +} + +float pressed_key_time; +vector highlightedPanel_initial_pos, highlightedPanel_initial_size; +void HUD_Panel_Arrow_Action(float nPrimary) +{ + if(!highlightedPanel) + return; + + hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions); + + float step; + if(autocvar_hud_configure_grid) + { + if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW) + { + if (hudShiftState & S_SHIFT) + step = hud_configure_realGridSize.y; + else + step = 2 * hud_configure_realGridSize.y; + } + else + { + if (hudShiftState & S_SHIFT) + step = hud_configure_realGridSize.x; + else + step = 2 * hud_configure_realGridSize.x; + } + } + else + { + if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW) + step = vid_conheight; + else + step = vid_conwidth; + if (hudShiftState & S_SHIFT) + step = (step / 256); // more precision + else + step = (step / 64) * (1 + 2 * (time - pressed_key_time)); + } + + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + + highlightedPanel_initial_pos = panel_pos; + highlightedPanel_initial_size = panel_size; + + if (hudShiftState & S_ALT) // resize + { + if(nPrimary == K_UPARROW) + resizeCorner = 1; + else if(nPrimary == K_RIGHTARROW) + resizeCorner = 2; + else if(nPrimary == K_LEFTARROW) + resizeCorner = 3; + else // if(nPrimary == K_DOWNARROW) + resizeCorner = 4; + + // ctrl+arrow reduces the size, instead of increasing it + // Note that ctrl disables collisions check too, but it's fine + // since we don't collide with anything reducing the size + if (hudShiftState & S_CTRL) { + step = -step; + resizeCorner = 5 - resizeCorner; + } + + vector mySize; + mySize = panel_size; + panel_click_resizeorigin = panel_pos; + if(resizeCorner == 1) { + panel_click_resizeorigin += mySize; + mySize.y += step; + } else if(resizeCorner == 2) { + panel_click_resizeorigin.y += mySize.y; + mySize.x += step; + } else if(resizeCorner == 3) { + panel_click_resizeorigin.x += mySize.x; + mySize.x += step; + } else { // resizeCorner == 4 + mySize.y += step; + } + HUD_Panel_SetPosSize(mySize); + } + else // move + { + vector pos; + pos = panel_pos; + if(nPrimary == K_UPARROW) + pos.y -= step; + else if(nPrimary == K_DOWNARROW) + pos.y += step; + else if(nPrimary == K_LEFTARROW) + pos.x -= step; + else // if(nPrimary == K_RIGHTARROW) + pos.x += step; + + HUD_Panel_SetPos(pos); + } + + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + + if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size) + { + // backup! + panel_pos_backup = highlightedPanel_initial_pos; + panel_size_backup = highlightedPanel_initial_size; + highlightedPanel_backup = highlightedPanel; + } +} + +void HUD_Panel_EnableMenu(); +entity tab_panels[hud_panels_MAX]; +entity tab_panel; +vector tab_panel_pos; +float tab_backward; +void HUD_Panel_FirstInDrawQ(float id); +void reset_tab_panels() +{ + int i; + for(i = 0; i < hud_panels_COUNT; ++i) + tab_panels[i] = world; +} +float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary) +{ + string s; + + if(bInputType == 2) + return false; + + if(!autocvar__hud_configure) + return false; + + if(bInputType == 3) + { + mousepos.x = nPrimary; + mousepos.y = nSecondary; + return true; + } + + // block any input while a menu dialog is fading + // don't block mousepos read as it leads to cursor jumps in the interaction with the menu + if(autocvar__menu_alpha) + { + hudShiftState = 0; + mouseClicked = 0; + return true; + } + + // allow console bind to work + string con_keys; + float keys; + con_keys = findkeysforcommand("toggleconsole", 0); + keys = tokenize(con_keys); // findkeysforcommand returns data for this + + bool hit_con_bind = false; + int i; + for (i = 0; i < keys; ++i) + { + if(nPrimary == stof(argv(i))) + hit_con_bind = true; + } + + if(bInputType == 0) { + if(nPrimary == K_ALT) hudShiftState |= S_ALT; + if(nPrimary == K_CTRL) hudShiftState |= S_CTRL; + if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT; + } + else if(bInputType == 1) { + if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT); + if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL); + if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT); + } + + if(nPrimary == K_CTRL) + { + if (bInputType == 1) //ctrl has been released + { + if (tab_panel) + { + //switch to selected panel + highlightedPanel = tab_panel; + highlightedAction = 0; + HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id); + } + tab_panel = world; + reset_tab_panels(); + } + } + + if(nPrimary == K_MOUSE1) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE1; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE1); + } + else if(nPrimary == K_MOUSE2) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE2; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE2); + } + else if(nPrimary == K_ESCAPE) + { + if (bInputType == 1) + return true; + menu_enabled = 1; + localcmd("menu_showhudexit\n"); + } + else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL) + { + if (bInputType == 1) + return true; + if (!menu_enabled) + cvar_set("_hud_configure", "0"); + } + else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel + { + if (bInputType == 1 || mouseClicked) + return true; + + // FIXME minor bug: if a panel is highlighted, has the same pos_x and + // lays in the same level of another panel then the next consecutive + // CTRL TAB presses will reselect once more the highlighted panel + + entity starting_panel; + entity old_tab_panel = tab_panel; + if (!tab_panel) //first press of TAB + { + if (highlightedPanel) + { + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + } + else + panel_pos = '0 0 0'; + starting_panel = highlightedPanel; + tab_panel_pos = panel_pos; //to compute level + } + else + { + if ( ((!tab_backward) && (hudShiftState & S_SHIFT)) || (tab_backward && !(hudShiftState & S_SHIFT)) ) //tab direction changed? + reset_tab_panels(); + starting_panel = tab_panel; + } + tab_backward = (hudShiftState & S_SHIFT); + + float k, level = 0, start_posX; + vector candidate_pos = '0 0 0'; + const float LEVELS_NUM = 4; + float level_height = vid_conheight / LEVELS_NUM; +:find_tab_panel + level = floor(tab_panel_pos.y / level_height) * level_height; //starting level + candidate_pos.x = (!tab_backward) ? vid_conwidth : 0; + start_posX = tab_panel_pos.x; + tab_panel = world; + k=0; + while(++k) + { + for(i = 0; i < hud_panels_COUNT; ++i) + { + panel = hud_panels_from(i); + if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) + continue; + if (panel == tab_panels[i] || panel == starting_panel) + continue; + HUD_Panel_UpdatePosSize(); + if (panel_pos.y >= level && (panel_pos.y - level) < level_height) + if ( ( !tab_backward && panel_pos.x >= start_posX && (panel_pos.x < candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y <= candidate_pos.y)) ) + || ( tab_backward && panel_pos.x <= start_posX && (panel_pos.x > candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y >= candidate_pos.y)) ) ) + { + tab_panel = panel; + tab_panel_pos = candidate_pos = panel_pos; + } + } + if (tab_panel) + break; + if (k == LEVELS_NUM) //tab_panel not found + { + reset_tab_panels(); + if (!old_tab_panel) + { + tab_panel = world; + return true; + } + starting_panel = old_tab_panel; + old_tab_panel = world; + goto find_tab_panel; //u must find tab_panel! + } + if (!tab_backward) + { + level = (level + level_height) % vid_conheight; + start_posX = 0; + candidate_pos.x = vid_conwidth; + } + else + { + level = (level - level_height) % vid_conheight; + start_posX = vid_conwidth; + candidate_pos.x = 0; + } + } + + tab_panels[tab_panel.panel_id] = tab_panel; + } + else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock + { + if (bInputType == 1 || mouseClicked) + return true; + + if (highlightedPanel) + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name)))); + else + cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : ""); + } + else if(nPrimary == 'c' && hudShiftState & S_CTRL) // copy highlighted panel size + { + if (bInputType == 1 || mouseClicked) + return true; + + if (highlightedPanel) + { + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + panel_size_copied = panel_size; + } + } + else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel + { + if (bInputType == 1 || mouseClicked) + return true; + + if (panel_size_copied == '0 0 0' || !highlightedPanel) + return true; + + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + + // reduce size if it'd go beyond screen boundaries + vector tmp_size = panel_size_copied; + if (panel_pos.x + panel_size_copied.x > vid_conwidth) + tmp_size.x = vid_conwidth - panel_pos.x; + if (panel_pos.y + panel_size_copied.y > vid_conheight) + tmp_size.y = vid_conheight - panel_pos.y; + + if (panel_size == tmp_size) + return true; + + // backup first! + panel_pos_backup = panel_pos; + panel_size_backup = panel_size; + highlightedPanel_backup = highlightedPanel; + + s = strcat(ftos(tmp_size.x/vid_conwidth), " ", ftos(tmp_size.y/vid_conheight)); + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s); + } + else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action + { + if (bInputType == 1 || mouseClicked) + return true; + //restore previous values + if (highlightedPanel_backup) + { + s = strcat(ftos(panel_pos_backup.x/vid_conwidth), " ", ftos(panel_pos_backup.y/vid_conheight)); + cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s); + s = strcat(ftos(panel_size_backup.x/vid_conwidth), " ", ftos(panel_size_backup.y/vid_conheight)); + cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s); + highlightedPanel_backup = world; + } + } + else if(nPrimary == 's' && hudShiftState & S_CTRL) // save config + { + if (bInputType == 1 || mouseClicked) + return true; + localcmd("hud save myconfig\n"); + } + else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW) + { + if (bInputType == 1) + { + pressed_key_time = 0; + return true; + } + else if (pressed_key_time == 0) + pressed_key_time = time; + + if (!mouseClicked) + HUD_Panel_Arrow_Action(nPrimary); //move or resize panel + } + else if(nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER) + { + if (bInputType == 1) + return true; + if (highlightedPanel) + HUD_Panel_EnableMenu(); + } + else if(hit_con_bind || nPrimary == K_PAUSE) + return false; + + return true; +} + +float HUD_Panel_Check_Mouse_Pos(float allow_move) +{ + int i, j = 0; + while(j < hud_panels_COUNT) + { + i = panel_order[j]; + j += 1; + + panel = hud_panels_from(i); + if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; + HUD_Panel_UpdatePosSize(); + + float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize + + // move + if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) + { + return 1; + } + // resize from topleft border + else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) + { + return 2; + } + // resize from topright border + else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) + { + return 3; + } + // resize from bottomleft border + else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border) + { + return 3; + } + // resize from bottomright border + else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border) + { + return 2; + } + } + return 0; +} + +// move a panel to the beginning of the panel order array (which means it gets drawn last, on top of everything else) +void HUD_Panel_FirstInDrawQ(float id) +{ + int i; + int place = -1; + // find out where in the array our current id is, save into place + for(i = 0; i < hud_panels_COUNT; ++i) + { + if(panel_order[i] == id) + { + place = i; + break; + } + } + // place last if we didn't find a place for it yet (probably new panel, or screwed up cvar) + if(place == -1) + place = hud_panels_COUNT - 1; + + // move all ids up by one step in the array until "place" + for(i = place; i > 0; --i) + { + panel_order[i] = panel_order[i-1]; + } + // now save the new top id + panel_order[0] = id; + + // let's save them into the cvar by some strcat trickery + string s = ""; + for(i = 0; i < hud_panels_COUNT; ++i) + { + s = strcat(s, ftos(panel_order[i]), " "); + } + cvar_set("_hud_panelorder", s); + if(hud_panelorder_prev) + strunzone(hud_panelorder_prev); + hud_panelorder_prev = strzone(autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here +} + +void HUD_Panel_Highlight(float allow_move) +{ + int i, j = 0; + + while(j < hud_panels_COUNT) + { + i = panel_order[j]; + j += 1; + + panel = hud_panels_from(i); + if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) + continue; + HUD_Panel_UpdatePosSize(); + + float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize + + // move + if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) + { + highlightedPanel = hud_panels_from(i); + HUD_Panel_FirstInDrawQ(i); + highlightedAction = 1; + panel_click_distance = mousepos - panel_pos; + return; + } + // resize from topleft border + else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) + { + highlightedPanel = hud_panels_from(i); + HUD_Panel_FirstInDrawQ(i); + highlightedAction = 2; + resizeCorner = 1; + panel_click_distance = mousepos - panel_pos; + panel_click_resizeorigin = panel_pos + panel_size; + return; + } + // resize from topright border + else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) + { + highlightedPanel = hud_panels_from(i); + HUD_Panel_FirstInDrawQ(i); + highlightedAction = 2; + resizeCorner = 2; + panel_click_distance.x = panel_size.x - mousepos.x + panel_pos.x; + panel_click_distance.y = mousepos.y - panel_pos.y; + panel_click_resizeorigin = panel_pos + eY * panel_size.y; + return; + } + // resize from bottomleft border + else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border) + { + highlightedPanel = hud_panels_from(i); + HUD_Panel_FirstInDrawQ(i); + highlightedAction = 2; + resizeCorner = 3; + panel_click_distance.x = mousepos.x - panel_pos.x; + panel_click_distance.y = panel_size.y - mousepos.y + panel_pos.y; + panel_click_resizeorigin = panel_pos + eX * panel_size.x; + return; + } + // resize from bottomright border + else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border) + { + highlightedPanel = hud_panels_from(i); + HUD_Panel_FirstInDrawQ(i); + highlightedAction = 2; + resizeCorner = 4; + panel_click_distance = panel_size - mousepos + panel_pos; + panel_click_resizeorigin = panel_pos; + return; + } + } + highlightedPanel = world; + highlightedAction = 0; +} + +void HUD_Panel_EnableMenu() +{ + menu_enabled = 2; + localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n"); +} +float mouse_over_panel; +void HUD_Panel_Mouse() +{ + if(autocvar__menu_alpha == 1) + return; + + if (!autocvar_hud_cursormode) + { + mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; + + mousepos.x = bound(0, mousepos.x, vid_conwidth); + mousepos.y = bound(0, mousepos.y, vid_conheight); + } + + if(mouseClicked) + { + if(prevMouseClicked == 0) + { + if (tab_panel) + { + //stop ctrl-tab selection + tab_panel = world; + reset_tab_panels(); + } + HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin + // and calls HUD_Panel_UpdatePosSize() for the highlighted panel + if (highlightedPanel) + { + highlightedPanel_initial_pos = panel_pos; + highlightedPanel_initial_size = panel_size; + } + // doubleclick check + if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos) + { + mouseClicked = 0; // to prevent spam, I guess. + HUD_Panel_EnableMenu(); + } + else + { + if (mouseClicked & S_MOUSE1) + { + prevMouseClickedTime = time; + prevMouseClickedPos = mousepos; + } + mouse_over_panel = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1); + } + } + else + { + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + } + + if (highlightedPanel) + { + drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL); + if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size) + { + hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions); + // backup! + panel_pos_backup = highlightedPanel_initial_pos; + panel_size_backup = highlightedPanel_initial_size; + highlightedPanel_backup = highlightedPanel; + } + else + // in case the clicked panel is inside another panel and we aren't + // moving it, avoid the immediate "fix" of its position/size + // (often unwanted and hateful) by disabling collisions check + hud_configure_checkcollisions = false; + } + + if(highlightedAction == 1) + HUD_Panel_SetPos(mousepos - panel_click_distance); + else if(highlightedAction == 2) + { + vector mySize = '0 0 0'; + if(resizeCorner == 1) { + mySize.x = panel_click_resizeorigin.x - (mousepos.x - panel_click_distance.x); + mySize.y = panel_click_resizeorigin.y - (mousepos.y - panel_click_distance.y); + } else if(resizeCorner == 2) { + mySize.x = mousepos.x + panel_click_distance.x - panel_click_resizeorigin.x; + mySize.y = panel_click_distance.y + panel_click_resizeorigin.y - mousepos.y; + } else if(resizeCorner == 3) { + mySize.x = panel_click_resizeorigin.x + panel_click_distance.x - mousepos.x; + mySize.y = mousepos.y + panel_click_distance.y - panel_click_resizeorigin.y; + } else { // resizeCorner == 4 + mySize.x = mousepos.x - (panel_click_resizeorigin.x - panel_click_distance.x); + mySize.y = mousepos.y - (panel_click_resizeorigin.y - panel_click_distance.y); + } + HUD_Panel_SetPosSize(mySize); + } + } + else + { + if(prevMouseClicked) + highlightedAction = 0; + if(menu_enabled == 2) + mouse_over_panel = 0; + else + mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true); + if (mouse_over_panel && !tab_panel) + drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL); + } + // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel + const vector cursorsize = '32 32 0'; + float cursor_alpha = 1 - autocvar__menu_alpha; + + if(!mouse_over_panel) + drawpic(mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); + else if(mouse_over_panel == 1) + drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_move.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); + else if(mouse_over_panel == 2) + drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); + else + drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize2.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); + + prevMouseClicked = mouseClicked; +} +void HUD_Configure_DrawGrid() +{ + float i; + if(autocvar_hud_configure_grid && autocvar_hud_configure_grid_alpha) + { + hud_configure_gridSize.x = bound(0.005, cvar("hud_configure_grid_xsize"), 0.2); + hud_configure_gridSize.y = bound(0.005, cvar("hud_configure_grid_ysize"), 0.2); + hud_configure_realGridSize.x = hud_configure_gridSize.x * vid_conwidth; + hud_configure_realGridSize.y = hud_configure_gridSize.y * vid_conheight; + vector s; + // x-axis + s = eX + eY * vid_conheight; + for(i = 1; i < 1/hud_configure_gridSize.x; ++i) + drawfill(eX * i * hud_configure_realGridSize.x, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL); + // y-axis + s = eY + eX * vid_conwidth; + for(i = 1; i < 1/hud_configure_gridSize.y; ++i) + drawfill(eY * i * hud_configure_realGridSize.y, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL); + } +} + +float _menu_alpha_prev; +void HUD_Configure_Frame() +{ + int i; + if(autocvar__hud_configure) + { + if(isdemo() || intermission == 2) + { + HUD_Configure_Exit_Force(); + return; + } + + if(!hud_configure_prev) + { + if(autocvar_hud_cursormode) + setcursormode(1); + hudShiftState = 0; + for(i = hud_panels_COUNT - 1; i >= 0; --i) + hud_panels_from(panel_order[i]).update_time = time; + } + + // NOTE this check is necessary because _menu_alpha isn't updated the frame the menu gets enabled + if(autocvar__menu_alpha != _menu_alpha_prev) + { + if(autocvar__menu_alpha == 0) + menu_enabled = 0; + _menu_alpha_prev = autocvar__menu_alpha; + } + + HUD_Configure_DrawGrid(); + } + else if(hud_configure_prev) + { + if(menu_enabled) + menu_enabled = 0; + if(autocvar_hud_cursormode) + setcursormode(0); + } +} + +const float hlBorderSize = 2; +const string hlBorder = "gfx/hud/default/border_highlighted"; +const string hlBorder2 = "gfx/hud/default/border_highlighted2"; +void HUD_Panel_HlBorder(float myBorder, vector color, float theAlpha) +{ + drawfill(panel_pos - '1 1 0' * myBorder, panel_size + '2 2 0' * myBorder, '0 0.5 1', .5 * theAlpha, DRAWFLAG_NORMAL); + drawpic_tiled(panel_pos - '1 1 0' * myBorder, hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); + drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * (panel_size.y + 2 * myBorder - hlBorderSize), hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); + drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize, hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); + drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize + eX * (panel_size.x + 2 * myBorder - hlBorderSize), hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); +} + +void HUD_Configure_PostDraw() +{ + if(autocvar__hud_configure) + { + if(tab_panel) + { + panel = tab_panel; + HUD_Panel_UpdatePosSize(); + drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL); + } + if(highlightedPanel) + { + panel = highlightedPanel; + HUD_Panel_UpdatePosSize(); + HUD_Panel_HlBorder(panel_bg_border * hlBorderSize, '0 0.5 1', 0.4 * (1 - autocvar__menu_alpha)); + } + } +} diff --git a/qcsrc/client/hud/hud_config.qh b/qcsrc/client/hud/hud_config.qh new file mode 100644 index 0000000000..05792286da --- /dev/null +++ b/qcsrc/client/hud/hud_config.qh @@ -0,0 +1,22 @@ +#ifndef CLIENT_HUD_CONFIG_H +#define CLIENT_HUD_CONFIG_H + +const int S_MOUSE1 = 1; +const int S_MOUSE2 = 2; +const int S_MOUSE3 = 4; +int mouseClicked; +int prevMouseClicked; // previous state +float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks +vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks + +void HUD_Panel_ExportCfg(string cfgname); + +void HUD_Panel_Mouse(); + +void HUD_Configure_Frame(); + +void HUD_Configure_PostDraw(); + +float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary); + +#endif diff --git a/qcsrc/client/hud/panel/ammo.qc b/qcsrc/client/hud/panel/ammo.qc new file mode 100644 index 0000000000..236a585985 --- /dev/null +++ b/qcsrc/client/hud/panel/ammo.qc @@ -0,0 +1,222 @@ +// Ammo (#1) + +void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color) +{ + HUD_Panel_DrawProgressBar( + myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, + mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, + autocvar_hud_panel_ammo_progressbar_name, + progress, 0, 0, color, + autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); +} + +void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time); // TODO: mutator + +void DrawAmmoItem(vector myPos, vector mySize, .int ammoType, bool isCurrent, bool isInfinite) +{ + if(ammoType == ammo_none) + return; + + // Initialize variables + + int ammo; + if(autocvar__hud_configure) + { + isCurrent = (ammoType == ammo_rockets); // Rockets always current + ammo = 60; + } + else + ammo = getstati(GetAmmoStat(ammoType)); + + if(!isCurrent) + { + float scale = bound(0, autocvar_hud_panel_ammo_noncurrent_scale, 1); + myPos = myPos + (mySize - mySize * scale) * 0.5; + mySize = mySize * scale; + } + + vector iconPos, textPos; + if(autocvar_hud_panel_ammo_iconalign) + { + iconPos = myPos + eX * 2 * mySize.y; + textPos = myPos; + } + else + { + iconPos = myPos; + textPos = myPos + eX * mySize.y; + } + + bool isShadowed = (ammo <= 0 && !isCurrent && !isInfinite); + + vector iconColor = isShadowed ? '0 0 0' : '1 1 1'; + vector textColor; + if(isInfinite) + textColor = '0.2 0.95 0'; + else if(isShadowed) + textColor = '0 0 0'; + else if(ammo < 10) + textColor = '0.8 0.04 0'; + else + textColor = '1 1 1'; + + float alpha; + if(isCurrent) + alpha = panel_fg_alpha; + else if(isShadowed) + alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1) * 0.5; + else + alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_ammo_noncurrent_alpha, 1); + + string text = isInfinite ? "\xE2\x88\x9E" : ftos(ammo); // Use infinity symbol (U+221E) + + // Draw item + + if(isCurrent) + drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + if(ammo > 0 && autocvar_hud_panel_ammo_progressbar) + HUD_Panel_DrawProgressBar(myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize.x, autocvar_hud_panel_ammo_progressbar_name, ammo/autocvar_hud_panel_ammo_maxammo, 0, 0, textColor, autocvar_hud_progressbar_alpha * alpha, DRAWFLAG_NORMAL); + + if(autocvar_hud_panel_ammo_text) + drawstring_aspect(textPos, text, eX * (2/3) * mySize.x + eY * mySize.y, textColor, alpha, DRAWFLAG_NORMAL); + + drawpic_aspect_skin(iconPos, GetAmmoPicture(ammoType), '1 1 0' * mySize.y, iconColor, alpha, DRAWFLAG_NORMAL); +} + +int nade_prevstatus; +int nade_prevframe; +float nade_statuschange_time; + +void HUD_Ammo() +{ + if(hud != HUD_NORMAL) return; + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_ammo) return; + if(spectatee_status == -1) return; + } + + HUD_Panel_UpdateCvars(); + + draw_beginBoldFont(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + int rows = 0, columns, row, column; + float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE); + bool draw_nades = (nade_cnt > 0 || nade_score > 0); + float nade_statuschange_elapsedtime; + int total_ammo_count; + + vector ammo_size; + if (autocvar_hud_panel_ammo_onlycurrent) + total_ammo_count = 1; + else + total_ammo_count = AMMO_COUNT; + + if(draw_nades) + { + ++total_ammo_count; + if (nade_cnt != nade_prevframe) + { + nade_statuschange_time = time; + nade_prevstatus = nade_prevframe; + nade_prevframe = nade_cnt; + } + } + else + nade_prevstatus = nade_prevframe = nade_statuschange_time = 0; + + rows = HUD_GetRowCount(total_ammo_count, mySize, 3); + columns = ceil((total_ammo_count)/rows); + ammo_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + + vector offset = '0 0 0'; // fteqcc sucks + float newSize; + if(ammo_size.x/ammo_size.y > 3) + { + newSize = 3 * ammo_size.y; + offset.x = ammo_size.x - newSize; + pos.x += offset.x/2; + ammo_size.x = newSize; + } + else + { + newSize = 1/3 * ammo_size.x; + offset.y = ammo_size.y - newSize; + pos.y += offset.y/2; + ammo_size.y = newSize; + } + + int i; + bool infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO); + row = column = 0; + if(autocvar_hud_panel_ammo_onlycurrent) + { + if(autocvar__hud_configure) + { + DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false); + } + else + { + DrawAmmoItem( + pos, + ammo_size, + (get_weaponinfo(switchweapon)).ammo_field, + true, + infinite_ammo + ); + } + + ++row; + if(row >= rows) + { + row = 0; + column = column + 1; + } + } + else + { + .int ammotype; + row = column = 0; + for(i = 0; i < AMMO_COUNT; ++i) + { + ammotype = GetAmmoFieldFromNum(i); + DrawAmmoItem( + pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), + ammo_size, + ammotype, + ((get_weaponinfo(switchweapon)).ammo_field == ammotype), + infinite_ammo + ); + + ++row; + if(row >= rows) + { + row = 0; + column = column + 1; + } + } + } + + if (draw_nades) + { + nade_statuschange_elapsedtime = time - nade_statuschange_time; + + float f = bound(0, nade_statuschange_elapsedtime*2, 1); + + DrawAmmoNades(pos + eX * column * (ammo_size.x + offset.x) + eY * row * (ammo_size.y + offset.y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f); + } + + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/centerprint.qc b/qcsrc/client/hud/panel/centerprint.qc new file mode 100644 index 0000000000..3169d925d2 --- /dev/null +++ b/qcsrc/client/hud/panel/centerprint.qc @@ -0,0 +1,335 @@ +// CenterPrint (#16) + +const int CENTERPRINT_MAX_MSGS = 10; +const int CENTERPRINT_MAX_ENTRIES = 50; +const float CENTERPRINT_SPACING = 0.7; +int cpm_index; +string centerprint_messages[CENTERPRINT_MAX_MSGS]; +int centerprint_msgID[CENTERPRINT_MAX_MSGS]; +float centerprint_time[CENTERPRINT_MAX_MSGS]; +float centerprint_expire_time[CENTERPRINT_MAX_MSGS]; +int centerprint_countdown_num[CENTERPRINT_MAX_MSGS]; +bool centerprint_showing; + +void centerprint_generic(int new_id, string strMessage, float duration, int countdown_num) +{ + //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num); + int i, j; + + if(strMessage == "" && new_id == 0) + return; + + // strip trailing newlines + j = strlen(strMessage) - 1; + while(substring(strMessage, j, 1) == "\n" && j >= 0) + --j; + if (j < strlen(strMessage) - 1) + strMessage = substring(strMessage, 0, j + 1); + + if(strMessage == "" && new_id == 0) + return; + + // strip leading newlines + j = 0; + while(substring(strMessage, j, 1) == "\n" && j < strlen(strMessage)) + ++j; + if (j > 0) + strMessage = substring(strMessage, j, strlen(strMessage) - j); + + if(strMessage == "" && new_id == 0) + return; + + if (!centerprint_showing) + centerprint_showing = true; + + for (i=0, j=cpm_index; i time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time) + centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out); + return; + } + break; // found a msg with the same id, at position j + } + } + + if (i == CENTERPRINT_MAX_MSGS) + { + // a msg with the same id was not found, add the msg at the next position + --cpm_index; + if (cpm_index == -1) + cpm_index = CENTERPRINT_MAX_MSGS - 1; + j = cpm_index; + } + if(centerprint_messages[j]) + strunzone(centerprint_messages[j]); + centerprint_messages[j] = strzone(strMessage); + centerprint_msgID[j] = new_id; + if (duration < 0) + { + centerprint_time[j] = -1; + centerprint_expire_time[j] = time; + } + else + { + if(duration == 0) + duration = max(1, autocvar_hud_panel_centerprint_time); + centerprint_time[j] = duration; + centerprint_expire_time[j] = time + duration; + } + centerprint_countdown_num[j] = countdown_num; +} + +void centerprint_hud(string strMessage) +{ + centerprint_generic(0, strMessage, autocvar_hud_panel_centerprint_time, 0); +} + +void reset_centerprint_messages() +{ + int i; + for (i=0; i hud_configure_cp_generation_time) + { + if(highlightedPanel == HUD_PANEL(CENTERPRINT)) + { + float r; + r = random(); + if (r > 0.8) + centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10); + else if (r > 0.55) + centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0); + else + centerprint_hud(sprintf("Message at time %s", seconds_tostring(time))); + hud_configure_cp_generation_time = time + 1 + random()*4; + } + else + { + centerprint_generic(0, sprintf("Centerprint message", seconds_tostring(time)), 10, 0); + hud_configure_cp_generation_time = time + 10 - random()*3; + } + } + } + + // this panel fades only when the menu does + float hud_fade_alpha_save = 0; + if(scoreboard_fade_alpha) + { + hud_fade_alpha_save = hud_fade_alpha; + hud_fade_alpha = 1 - autocvar__menu_alpha; + } + HUD_Panel_UpdateCvars(); + + if ( HUD_Radar_Clickable() ) + { + if (hud_panel_radar_bottom >= 0.96 * vid_conheight) + return; + + panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x); + panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom); + } + else if(scoreboard_fade_alpha) + { + hud_fade_alpha = hud_fade_alpha_save; + + // move the panel below the scoreboard + if (scoreboard_bottom >= 0.96 * vid_conheight) + return; + vector target_pos; + + target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x); + + if(target_pos.y > panel_pos.y) + { + panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha); + panel_size.y = min(panel_size.y, vid_conheight - scoreboard_bottom); + } + } + + HUD_Panel_DrawBg(1); + + if (!centerprint_showing) + return; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + int entries; + float height; + vector fontsize; + // entries = bound(1, floor(CENTERPRINT_MAX_ENTRIES * 4 * panel_size_y/panel_size_x), CENTERPRINT_MAX_ENTRIES); + // height = panel_size_y/entries; + // fontsize = '1 1 0' * height; + height = vid_conheight/50 * autocvar_hud_panel_centerprint_fontscale; + fontsize = '1 1 0' * height; + entries = bound(1, floor(panel_size.y/height), CENTERPRINT_MAX_ENTRIES); + + int i, j, k, n, g; + float a, sz, align, current_msg_posY = 0, msg_size; + vector pos; + string ts; + bool all_messages_expired = true; + + pos = panel_pos; + if (autocvar_hud_panel_centerprint_flip) + pos.y += panel_size.y; + align = bound(0, autocvar_hud_panel_centerprint_align, 1); + for (g=0, i=0, j=cpm_index; i 0) + { + centerprint_countdown_num[j] = centerprint_countdown_num[j] - 1; + if (centerprint_countdown_num[j] == 0) + continue; + centerprint_expire_time[j] = centerprint_expire_time[j] + centerprint_time[j]; + } + else if(centerprint_time[j] != -1) + continue; + } + + all_messages_expired = false; + + // fade the centerprint_hud in/out + if(centerprint_time[j] < 0) // Expired but forced. Expire time is the fade-in time. + a = (time - centerprint_expire_time[j]) / max(0.0001, autocvar_hud_panel_centerprint_fade_in); + else if(centerprint_expire_time[j] - autocvar_hud_panel_centerprint_fade_out > time) // Regularily printed. Not fading out yet. + a = (time - (centerprint_expire_time[j] - centerprint_time[j])) / max(0.0001, autocvar_hud_panel_centerprint_fade_in); + else // Expiring soon, so fade it out. + a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out); + + // while counting down show it anyway in order to hold the current message position + if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show. + continue; + if (a > 1) + a = 1; + + // set the size from fading in/out before subsequent fading + sz = autocvar_hud_panel_centerprint_fade_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_minfontsize); + + // also fade it based on positioning + if(autocvar_hud_panel_centerprint_fade_subsequent) + { + a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha + a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message + } + a *= panel_fg_alpha; + + // finally set the size based on the new theAlpha from subsequent fading + sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize)); + drawfontscale = sz * '1 1 0'; + + if (centerprint_countdown_num[j]) + n = tokenizebyseparator(strreplace("^COUNT", count_seconds(centerprint_countdown_num[j]), centerprint_messages[j]), "\n"); + else + n = tokenizebyseparator(centerprint_messages[j], "\n"); + + if (autocvar_hud_panel_centerprint_flip) + { + // check if the message can be entirely shown + for(k = 0; k < n; ++k) + { + getWrappedLine_remaining = argv(k); + while(getWrappedLine_remaining) + { + ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors); + if (ts != "") + pos.y -= fontsize.y; + else + pos.y -= fontsize.y * CENTERPRINT_SPACING/2; + } + } + current_msg_posY = pos.y; // save starting pos (first line) of the current message + } + + msg_size = pos.y; + for(k = 0; k < n; ++k) + { + getWrappedLine_remaining = argv(k); + while(getWrappedLine_remaining) + { + ts = getWrappedLine(panel_size.x * sz, fontsize, stringwidth_colors); + if (ts != "") + { + if (align) + pos.x = panel_pos.x + (panel_size.x - stringwidth(ts, true, fontsize)) * align; + if (a > 0.5/255.0) // Otherwise guaranteed invisible - don't show. This is checked a second time after some multiplications with other factors were done so temporary changes of these cannot cause flicker. + drawcolorcodedstring(pos + eY * 0.5 * (1 - sz) * fontsize.y, ts, fontsize, a, DRAWFLAG_NORMAL); + pos.y += fontsize.y; + } + else + pos.y += fontsize.y * CENTERPRINT_SPACING/2; + } + } + + ++g; // move next position number up + + msg_size = pos.y - msg_size; + if (autocvar_hud_panel_centerprint_flip) + { + pos.y = current_msg_posY - CENTERPRINT_SPACING * fontsize.y; + if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages + pos.y += (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz)); + + if (pos.y < panel_pos.y) // check if the next message can be shown + { + drawfontscale = '1 1 0'; + return; + } + } + else + { + pos.y += CENTERPRINT_SPACING * fontsize.y; + if (a < 1 && centerprint_msgID[j] == 0) // messages with id can be replaced just after they are faded out, so never move over them the next messages + pos.y -= (msg_size + CENTERPRINT_SPACING * fontsize.y) * (1 - sqrt(sz)); + + if(pos.y > panel_pos.y + panel_size.y - fontsize.y) // check if the next message can be shown + { + drawfontscale = '1 1 0'; + return; + } + } + } + drawfontscale = '1 1 0'; + if (all_messages_expired) + { + centerprint_showing = false; + reset_centerprint_messages(); + } +} diff --git a/qcsrc/client/hud/panel/chat.qc b/qcsrc/client/hud/panel/chat.qc new file mode 100644 index 0000000000..02df4ed629 --- /dev/null +++ b/qcsrc/client/hud/panel/chat.qc @@ -0,0 +1,89 @@ +/** Handle chat as a panel (#12) */ +void HUD_Chat() +{ + if(!autocvar__hud_configure) + { + if (!autocvar_hud_panel_chat) + { + if (!autocvar_con_chatrect) + cvar_set("con_chatrect", "0"); + return; + } + if(autocvar__con_chat_maximized) + { + if(!hud_draw_maximized) return; + } + else if(chat_panel_modified) + { + panel.update_time = time; // forces reload of panel attributes + chat_panel_modified = false; + } + } + + HUD_Panel_UpdateCvars(); + + if(intermission == 2) + { + // reserve some more space to the mapvote panel + // by resizing and moving chat panel to the bottom + panel_size.y = min(panel_size.y, vid_conheight * 0.2); + panel_pos.y = vid_conheight - panel_size.y - panel_bg_border * 2; + chat_posy = panel_pos.y; + chat_sizey = panel_size.y; + } + if(autocvar__con_chat_maximized && !autocvar__hud_configure) // draw at full screen height if maximized + { + panel_pos.y = panel_bg_border; + panel_size.y = vid_conheight - panel_bg_border * 2; + if(panel.current_panel_bg == "0") // force a border when maximized + { + string panel_bg; + panel_bg = strcat(hud_skin_path, "/border_default"); + if(precache_pic(panel_bg) == "") + panel_bg = "gfx/hud/default/border_default"; + if(panel.current_panel_bg) + strunzone(panel.current_panel_bg); + panel.current_panel_bg = strzone(panel_bg); + chat_panel_modified = true; + } + panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75 + } + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + if (!autocvar_con_chatrect) + cvar_set("con_chatrect", "1"); + + cvar_set("con_chatrect_x", ftos(pos.x/vid_conwidth)); + cvar_set("con_chatrect_y", ftos(pos.y/vid_conheight)); + + cvar_set("con_chatwidth", ftos(mySize.x/vid_conwidth)); + cvar_set("con_chat", ftos(floor(mySize.y/autocvar_con_chatsize - 0.5))); + + if(autocvar__hud_configure) + { + vector chatsize; + chatsize = '1 1 0' * autocvar_con_chatsize; + cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such + float i, a; + for(i = 0; i < autocvar_con_chat; ++i) + { + if(i == autocvar_con_chat - 1) + a = panel_fg_alpha; + else + a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45); + drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL); + pos.y += chatsize.y; + } + } +} diff --git a/qcsrc/client/hud/panel/engineinfo.qc b/qcsrc/client/hud/panel/engineinfo.qc new file mode 100644 index 0000000000..01e7ae3da9 --- /dev/null +++ b/qcsrc/client/hud/panel/engineinfo.qc @@ -0,0 +1,61 @@ +// Engine info panel (#13) + +float prevfps; +float prevfps_time; +int framecounter; + +float frametimeavg; +float frametimeavg1; // 1 frame ago +float frametimeavg2; // 2 frames ago +void HUD_EngineInfo() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_engineinfo) return; + } + + HUD_Panel_UpdateCvars(); + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + float currentTime = gettime(GETTIME_REALTIME); + if(cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage")) + { + float currentframetime = currentTime - prevfps_time; + frametimeavg = (frametimeavg + frametimeavg1 + frametimeavg2 + currentframetime)/4; // average three frametimes into framecounter for slightly more stable fps readings :P + frametimeavg2 = frametimeavg1; + frametimeavg1 = frametimeavg; + + float weight; + weight = cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight"); + if(currentframetime > 0.0001) // filter out insane values which sometimes seem to occur and throw off the average? If you are getting 10,000 fps or more, then you don't need a framerate counter. + { + if(fabs(prevfps - (1/frametimeavg)) > prevfps * cvar("hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold")) // if there was a big jump in fps, just force prevfps at current (1/currentframetime) to make big updates instant + prevfps = (1/currentframetime); + prevfps = (1 - weight) * prevfps + weight * (1/frametimeavg); // framecounter just used so there's no need for a new variable, think of it as "frametime average" + } + prevfps_time = currentTime; + } + else + { + framecounter += 1; + if(currentTime - prevfps_time > autocvar_hud_panel_engineinfo_framecounter_time) + { + prevfps = framecounter/(currentTime - prevfps_time); + framecounter = 0; + prevfps_time = currentTime; + } + } + + vector color; + color = HUD_Get_Num_Color (prevfps, 100); + drawstring_aspect(pos, sprintf(_("FPS: %.*f"), autocvar_hud_panel_engineinfo_framecounter_decimals, prevfps), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL); +} diff --git a/qcsrc/client/hud/panel/healtharmor.qc b/qcsrc/client/hud/panel/healtharmor.qc new file mode 100644 index 0000000000..83043293c5 --- /dev/null +++ b/qcsrc/client/hud/panel/healtharmor.qc @@ -0,0 +1,264 @@ +/** Health/armor (#3) */ +void HUD_HealthArmor() +{ + int armor, health, fuel; + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_healtharmor) return; + if(hud != HUD_NORMAL) return; + if(spectatee_status == -1) return; + + health = getstati(STAT_HEALTH); + if(health <= 0) + { + prev_health = -1; + return; + } + armor = getstati(STAT_ARMOR); + + // code to check for spectatee_status changes is in Ent_ClientData() + // prev_p_health and prev_health can be set to -1 there + + if (prev_p_health == -1) + { + // no effect + health_beforedamage = 0; + armor_beforedamage = 0; + health_damagetime = 0; + armor_damagetime = 0; + prev_health = health; + prev_armor = armor; + old_p_health = health; + old_p_armor = armor; + prev_p_health = health; + prev_p_armor = armor; + } + else if (prev_health == -1) + { + //start the load effect + health_damagetime = 0; + armor_damagetime = 0; + prev_health = 0; + prev_armor = 0; + } + fuel = getstati(STAT_FUEL); + } + else + { + health = 150; + armor = 75; + fuel = 20; + } + + HUD_Panel_UpdateCvars(); + + draw_beginBoldFont(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + int baralign = autocvar_hud_panel_healtharmor_baralign; + int iconalign = autocvar_hud_panel_healtharmor_iconalign; + + int maxhealth = autocvar_hud_panel_healtharmor_maxhealth; + int maxarmor = autocvar_hud_panel_healtharmor_maxarmor; + if(autocvar_hud_panel_healtharmor == 2) // combined health and armor display + { + vector v; + v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id); + + float x; + x = floor(v.x + 1); + + float maxtotal = maxhealth + maxarmor; + string biggercount; + if(v.z) // NOT fully armored + { + biggercount = "health"; + if(autocvar_hud_panel_healtharmor_progressbar) + HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + if(armor) + if(autocvar_hud_panel_healtharmor_text) + drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL); + } + else + { + biggercount = "armor"; + if(autocvar_hud_panel_healtharmor_progressbar) + HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + if(health) + if(autocvar_hud_panel_healtharmor_text) + drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + if(autocvar_hud_panel_healtharmor_text) + DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1); + + if(fuel) + HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL); + } + else + { + float panel_ar = mySize.x/mySize.y; + bool is_vertical = (panel_ar < 1); + vector health_offset = '0 0 0', armor_offset = '0 0 0'; + if (panel_ar >= 4 || (panel_ar >= 1/4 && panel_ar < 1)) + { + mySize.x *= 0.5; + if (autocvar_hud_panel_healtharmor_flip) + health_offset.x = mySize.x; + else + armor_offset.x = mySize.x; + } + else + { + mySize.y *= 0.5; + if (autocvar_hud_panel_healtharmor_flip) + health_offset.y = mySize.y; + else + armor_offset.y = mySize.y; + } + + bool health_baralign, armor_baralign, fuel_baralign; + bool health_iconalign, armor_iconalign; + if (autocvar_hud_panel_healtharmor_flip) + { + armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1); + health_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1); + fuel_baralign = health_baralign; + armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1); + health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1); + } + else + { + health_baralign = (autocvar_hud_panel_healtharmor_baralign == 2 || autocvar_hud_panel_healtharmor_baralign == 1); + armor_baralign = (autocvar_hud_panel_healtharmor_baralign == 3 || autocvar_hud_panel_healtharmor_baralign == 1); + fuel_baralign = armor_baralign; + health_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 2 || autocvar_hud_panel_healtharmor_iconalign == 1); + armor_iconalign = (autocvar_hud_panel_healtharmor_iconalign == 3 || autocvar_hud_panel_healtharmor_iconalign == 1); + } + + //if(health) + { + if(autocvar_hud_panel_healtharmor_progressbar) + { + float p_health, pain_health_alpha; + p_health = health; + pain_health_alpha = 1; + if (autocvar_hud_panel_healtharmor_progressbar_gfx) + { + if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0) + { + if (fabs(prev_health - health) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth) + { + if (time - old_p_healthtime < 1) + old_p_health = prev_p_health; + else + old_p_health = prev_health; + old_p_healthtime = time; + } + if (time - old_p_healthtime < 1) + { + p_health += (old_p_health - health) * (1 - (time - old_p_healthtime)); + prev_p_health = p_health; + } + } + if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0) + { + if (prev_health - health >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage) + { + if (time - health_damagetime >= 1) + health_beforedamage = prev_health; + health_damagetime = time; + } + if (time - health_damagetime < 1) + { + float health_damagealpha = 1 - (time - health_damagetime)*(time - health_damagetime); + HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, health_beforedamage/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * health_damagealpha, DRAWFLAG_NORMAL); + } + } + prev_health = health; + + if (health <= autocvar_hud_panel_healtharmor_progressbar_gfx_lowhealth) + { + float BLINK_FACTOR = 0.15; + float BLINK_BASE = 0.85; + float BLINK_FREQ = 9; + pain_health_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); + } + } + HUD_Panel_DrawProgressBar(pos + health_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_health, p_health/maxhealth, is_vertical, health_baralign, autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * pain_health_alpha, DRAWFLAG_NORMAL); + } + if(autocvar_hud_panel_healtharmor_text) + DrawNumIcon(pos + health_offset, mySize, health, "health", is_vertical, health_iconalign, HUD_Get_Num_Color(health, maxhealth), 1); + } + + if(armor) + { + if(autocvar_hud_panel_healtharmor_progressbar) + { + float p_armor; + p_armor = armor; + if (autocvar_hud_panel_healtharmor_progressbar_gfx) + { + if (autocvar_hud_panel_healtharmor_progressbar_gfx_smooth > 0) + { + if (fabs(prev_armor - armor) >= autocvar_hud_panel_healtharmor_progressbar_gfx_smooth) + { + if (time - old_p_armortime < 1) + old_p_armor = prev_p_armor; + else + old_p_armor = prev_armor; + old_p_armortime = time; + } + if (time - old_p_armortime < 1) + { + p_armor += (old_p_armor - armor) * (1 - (time - old_p_armortime)); + prev_p_armor = p_armor; + } + } + if (autocvar_hud_panel_healtharmor_progressbar_gfx_damage > 0) + { + if (prev_armor - armor >= autocvar_hud_panel_healtharmor_progressbar_gfx_damage) + { + if (time - armor_damagetime >= 1) + armor_beforedamage = prev_armor; + armor_damagetime = time; + } + if (time - armor_damagetime < 1) + { + float armor_damagealpha = 1 - (time - armor_damagetime)*(time - armor_damagetime); + HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, armor_beforedamage/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha * armor_damagealpha, DRAWFLAG_NORMAL); + } + } + prev_armor = armor; + } + HUD_Panel_DrawProgressBar(pos + armor_offset, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, p_armor/maxarmor, is_vertical, armor_baralign, autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + if(autocvar_hud_panel_healtharmor_text) + DrawNumIcon(pos + armor_offset, mySize, armor, "armor", is_vertical, armor_iconalign, HUD_Get_Num_Color(armor, maxarmor), 1); + } + + if(fuel) + { + if (is_vertical) + mySize.x *= 0.2 / 2; //if vertical always halve x to not cover too much numbers with 3 digits + else + mySize.y *= 0.2; + if (panel_ar >= 4) + mySize.x *= 2; //restore full panel size + else if (panel_ar < 1/4) + mySize.y *= 2; //restore full panel size + HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", fuel/100, is_vertical, fuel_baralign, autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL); + } + } + + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/infomessages.qc b/qcsrc/client/hud/panel/infomessages.qc new file mode 100644 index 0000000000..2bcad64260 --- /dev/null +++ b/qcsrc/client/hud/panel/infomessages.qc @@ -0,0 +1,183 @@ +// Info messages panel (#14) + +#define drawInfoMessage(s) do { \ + if(autocvar_hud_panel_infomessages_flip) \ + o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \ + drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \ + o.y += fontsize.y; \ +} while(0) +void HUD_InfoMessages() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_infomessages) return; + } + + HUD_Panel_UpdateCvars(); + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + // always force 5:1 aspect + vector newSize = '0 0 0'; + if(mySize.x/mySize.y > 5) + { + newSize.x = 5 * mySize.y; + newSize.y = mySize.y; + + pos.x = pos.x + (mySize.x - newSize.x) / 2; + } + else + { + newSize.y = 1/5 * mySize.x; + newSize.x = mySize.x; + + pos.y = pos.y + (mySize.y - newSize.y) / 2; + } + + mySize = newSize; + entity tm; + vector o; + o = pos; + + vector fontsize; + fontsize = '0.20 0.20 0' * mySize.y; + + float a; + a = panel_fg_alpha; + + string s; + if(!autocvar__hud_configure) + { + if(spectatee_status && !intermission) + { + a = 1; + if(spectatee_status == -1) + s = _("^1Observing"); + else + s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(current_player)); + drawInfoMessage(s); + + if(spectatee_status == -1) + s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire")); + else + s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); + drawInfoMessage(s); + + if(spectatee_status == -1) + s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); + else + s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2")); + drawInfoMessage(s); + + s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info")); + drawInfoMessage(s); + + if(gametype == MAPINFO_TYPE_LMS) + { + entity sk; + sk = playerslots[player_localnum]; + if(sk.(scores[ps_primary]) >= 666) + s = _("^1Match has already begun"); + else if(sk.(scores[ps_primary]) > 0) + s = _("^1You have no more lives left"); + else + s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); + } + else + s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); + drawInfoMessage(s); + + //show restart countdown: + if (time < STAT(GAMESTARTTIME)) { + float countdown; + //we need to ceil, otherwise the countdown would be off by .5 when using round() + countdown = ceil(STAT(GAMESTARTTIME) - time); + s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown); + drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); + o.y += fontsize.y; + } + } + if(warmup_stage && !intermission) + { + s = _("^2Currently in ^1warmup^2 stage!"); + drawInfoMessage(s); + } + + string blinkcolor; + if(time % 1 >= 0.5) + blinkcolor = "^1"; + else + blinkcolor = "^3"; + + if(ready_waiting && !intermission && !spectatee_status) + { + if(ready_waiting_for_me) + { + if(warmup_stage) + s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); + else + s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); + } + else + { + if(warmup_stage) + s = _("^2Waiting for others to ready up to end warmup..."); + else + s = _("^2Waiting for others to ready up..."); + } + drawInfoMessage(s); + } + else if(warmup_stage && !intermission && !spectatee_status) + { + s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready")); + drawInfoMessage(s); + } + + if(teamplay && !intermission && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger) + { + float ts_min = 0, ts_max = 0; + tm = teams.sort_next; + if (tm) + { + for (; tm.sort_next; tm = tm.sort_next) + { + if(!tm.team_size || tm.team == NUM_SPECTATOR) + continue; + if(!ts_min) ts_min = tm.team_size; + else ts_min = min(ts_min, tm.team_size); + if(!ts_max) ts_max = tm.team_size; + else ts_max = max(ts_max, tm.team_size); + } + if ((ts_max - ts_min) > 1) + { + s = strcat(blinkcolor, _("Teamnumbers are unbalanced!")); + tm = GetTeam(myteam, false); + if (tm) + if (tm.team != NUM_SPECTATOR) + if (tm.team_size == ts_max) + s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor)); + drawInfoMessage(s); + } + } + } + } + else + { + s = _("^7Press ^3ESC ^7to show HUD options."); + drawInfoMessage(s); + s = _("^3Doubleclick ^7a panel for panel-specific options."); + drawInfoMessage(s); + s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and"); + drawInfoMessage(s); + s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments."); + drawInfoMessage(s); + } +} diff --git a/qcsrc/client/hud/panel/minigame.qc b/qcsrc/client/hud/panel/minigame.qc new file mode 100644 index 0000000000..dbacddbe20 --- /dev/null +++ b/qcsrc/client/hud/panel/minigame.qc @@ -0,0 +1,3 @@ +// Minigame + +#include "../../../common/minigames/cl_minigames_hud.qc" diff --git a/qcsrc/client/hud/panel/modicons.qc b/qcsrc/client/hud/panel/modicons.qc new file mode 100644 index 0000000000..a8cb7c2660 --- /dev/null +++ b/qcsrc/client/hud/panel/modicons.qc @@ -0,0 +1,776 @@ +// Mod icons panel (#10) + +bool mod_active; // is there any active mod icon? + +void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) +{ + int stat = -1; + string pic = ""; + vector color = '0 0 0'; + switch(i) + { + case 0: + stat = getstati(STAT_REDALIVE); + pic = "player_red.tga"; + color = '1 0 0'; + break; + case 1: + stat = getstati(STAT_BLUEALIVE); + pic = "player_blue.tga"; + color = '0 0 1'; + break; + case 2: + stat = getstati(STAT_YELLOWALIVE); + pic = "player_yellow.tga"; + color = '1 1 0'; + break; + default: + case 3: + stat = getstati(STAT_PINKALIVE); + pic = "player_pink.tga"; + color = '1 0 1'; + break; + } + + 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) + { + drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(myPos + eX * 0.7 * mySize.x, ftos(stat), eX * 0.3 * mySize.x + eY * mySize.y, color, panel_fg_alpha, DRAWFLAG_NORMAL); + } + else + drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL); +} + +// Clan Arena and Freeze Tag HUD modicons +void HUD_Mod_CA(vector myPos, vector mySize) +{ + mod_active = 1; // required in each mod function that always shows something + + int layout; + if(gametype == MAPINFO_TYPE_CA) + layout = autocvar_hud_panel_modicons_ca_layout; + else //if(gametype == MAPINFO_TYPE_FREEZETAG) + layout = autocvar_hud_panel_modicons_freezetag_layout; + int rows, columns; + float aspect_ratio; + aspect_ratio = (layout) ? 2 : 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 = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + for(i=0; i= rows) + { + row = 0; + ++column; + } + } +} + +// 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; +} + +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, blueflag_statuschange_elapsedtime, yellowflag_statuschange_elapsedtime, pinkflag_statuschange_elapsedtime, neutralflag_statuschange_elapsedtime; // time since the status changed + bool ctf_oneflag; // one-flag CTF mode enabled/disabled + int stat_items = getstati(STAT_CTF_FLAGSTATUS, 0, 24); + float fs, fs2, fs3, size1, size2; + vector e1, e2; + + 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); + + mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag); + + if (autocvar__hud_configure) { + redflag = 1; + blueflag = 2; + if (team_count >= 3) + yellowflag = 2; + if (team_count >= 4) + 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) do { \ + 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; \ + } while (0) + 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, team##_icon_prevstatus; \ + int team##_alpha, team##_alpha_prevstatus; \ + team##_alpha = team##_alpha_prevstatus = 1; \ + do { \ + 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; \ + } \ + } while (0) + X(red, myteam != NUM_TEAM_1); + X(blue, myteam != NUM_TEAM_2); + X(yellow, myteam != NUM_TEAM_3); + X(pink, myteam != NUM_TEAM_4); + X(neutral, true); + #undef X + + if (ctf_oneflag) { + // hacky, but these aren't needed + red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null; + fs = fs2 = fs3 = 1; + } else switch (team_count) { + 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) do { \ + f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \ + 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); \ + } while (0) + 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); + 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 = eX * mySize.x * 0.5 + eY * 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 = eX * mySize.x / all_keys + eY * mySize.y; + for(i = 0; i < all_keys; ++i) + KH_SLOTS[i] = pos + eX * slot_size.x * i; + } + else + { + // Vertical arrangement + slot_size = eX * mySize.x + eY * 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 blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1 + float alpha; + alpha = 1; + + if(carrying_keys) + 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 = getstati(STAT_ITEMS, 0, 24); + 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 = eX * 0.5 * mySize.x + eY * mySize.y; + } else { + kaball_pos = pos + eY * 0.25 * mySize.y; + kaball_size = eY * 0.5 * mySize.y + eX * mySize.x; + } + + 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", eX * mySize.x + eY * 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 = getstati(STAT_ITEMS, 0, 24); + nb_pb_starttime = getstatf(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 +float srecordtime_prev; // last remembered srecordtime +float srecordtime_change_time; // time when srecordtime last changed + +float race_status_time; +int race_status_prev; +string race_status_name_prev; +void HUD_Mod_Race(vector pos, vector mySize) +{ + mod_active = 1; // race should never hide the mod icons panel + entity me; + me = playerslots[player_localnum]; + float t, score; + float f; // yet another function has this + score = me.(scores[ps_primary]); + + if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD + return; // no records in the actual race + + // clientside personal record + string rr; + if(gametype == MAPINFO_TYPE_CTS) + rr = CTS_RECORD; + else + rr = RACE_RECORD; + t = stof(db_get(ClientProgsDB, strcat(shortmapname, rr, "time"))); + + if(score && (score < t || !t)) { + db_put(ClientProgsDB, strcat(shortmapname, rr, "time"), ftos(score)); + if(autocvar_cl_autodemo_delete_keeprecords) + { + f = autocvar_cl_autodemo_delete; + f &= ~1; + cvar_set("cl_autodemo_delete", ftos(f)); // don't delete demo with new record! + } + } + + if(t != crecordtime_prev) { + crecordtime_prev = t; + crecordtime_change_time = time; + } + + vector textPos, medalPos; + float squareSize; + if(mySize.x > mySize.y) { + // text on left side + squareSize = min(mySize.y, mySize.x/2); + textPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eY * 0.5 * (mySize.y - squareSize); + medalPos = pos + eX * 0.5 * max(0, mySize.x/2 - squareSize) + eX * 0.5 * mySize.x + eY * 0.5 * (mySize.y - squareSize); + } else { + // text on top + squareSize = min(mySize.x, mySize.y/2); + textPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eX * 0.5 * (mySize.x - squareSize); + medalPos = pos + eY * 0.5 * max(0, mySize.y/2 - squareSize) + eY * 0.5 * mySize.y + eX * 0.5 * (mySize.x - squareSize); + } + + f = time - crecordtime_change_time; + + if (f > 1) { + drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } else { + drawstring_aspect(textPos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(textPos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect_expanding(pos, _("Personal best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); + drawstring_aspect_expanding(pos + eY * 0.25 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); + } + + // server record + t = race_server_record; + if(t != srecordtime_prev) { + srecordtime_prev = t; + srecordtime_change_time = time; + } + f = time - srecordtime_change_time; + + if (f > 1) { + drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } else { + drawstring_aspect(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect_expanding(textPos + eY * 0.5 * squareSize, _("Server best"), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); + drawstring_aspect_expanding(textPos + eY * 0.75 * squareSize, TIME_ENCODED_TOSTRING(t), eX * squareSize + eY * 0.25 * squareSize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, f); + } + + if (race_status != race_status_prev || race_status_name != race_status_name_prev) { + race_status_time = time + 5; + race_status_prev = race_status; + if (race_status_name_prev) + strunzone(race_status_name_prev); + race_status_name_prev = strzone(race_status_name); + } + + // race "awards" + float a; + a = bound(0, race_status_time - time, 1); + + string s; + s = textShortenToWidth(race_status_name, squareSize, '1 1 0' * 0.1 * squareSize, stringwidth_colors); + + float rank; + if(race_status > 0) + rank = race_CheckName(race_status_name); + else + rank = 0; + string rankname; + rankname = count_ordinal(rank); + + vector namepos; + namepos = medalPos + '0 0.8 0' * squareSize; + vector rankpos; + rankpos = medalPos + '0 0.15 0' * squareSize; + + if(race_status == 0) + drawpic_aspect_skin(medalPos, "race_newfail", '1 1 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + else if(race_status == 1) { + drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newtime", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + } else if(race_status == 2) { + if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank) + drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + else + drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + } else if(race_status == 3) { + drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrecordserver", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL); + drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + + if (race_status_time - time <= 0) { + race_status_prev = -1; + race_status = -1; + if(race_status_name) + strunzone(race_status_name); + race_status_name = string_null; + if(race_status_name_prev) + strunzone(race_status_name_prev); + race_status_name_prev = string_null; + } +} + +void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) +{ + float stat = -1; + string pic = ""; + vector color = '0 0 0'; + switch(i) + { + case 0: + stat = getstatf(STAT_DOM_PPS_RED); + pic = "dom_icon_red"; + color = '1 0 0'; + break; + case 1: + stat = getstatf(STAT_DOM_PPS_BLUE); + pic = "dom_icon_blue"; + color = '0 0 1'; + break; + case 2: + stat = getstatf(STAT_DOM_PPS_YELLOW); + pic = "dom_icon_yellow"; + color = '1 1 0'; + break; + default: + case 3: + stat = getstatf(STAT_DOM_PPS_PINK); + pic = "dom_icon_pink"; + color = '1 0 1'; + break; + } + float pps_ratio = stat / getstatf(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), eX * (2/3) * mySize.x + eY * 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)), "%" ), eX * (2/3) * mySize.x + eY * 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 = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + for(i=0; i= rows) + { + row = 0; + ++column; + } + } +} + +void HUD_ModIcons_SetFunc() +{ + switch(gametype) + { + case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break; + case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break; + case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break; + case MAPINFO_TYPE_CTS: + case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break; + case MAPINFO_TYPE_CA: + case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break; + case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break; + case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break; + } +} + +int mod_prev; // previous state of mod_active to check for a change +float mod_alpha; +float mod_change; // "time" when mod_active changed + +void HUD_ModIcons() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_modicons) return; + if(!HUD_ModIcons_GameType) return; + } + + HUD_Panel_UpdateCvars(); + + draw_beginBoldFont(); + + if(mod_active != mod_prev) { + mod_change = time; + mod_prev = mod_active; + } + + if(mod_active || autocvar__hud_configure) + mod_alpha = bound(0, (time - mod_change) * 2, 1); + else + mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1); + + if(mod_alpha) + HUD_Panel_DrawBg(mod_alpha); + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + if(autocvar__hud_configure) + HUD_Mod_CTF(panel_pos, panel_size); + else + HUD_ModIcons_GameType(panel_pos, panel_size); + + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/notify.qc b/qcsrc/client/hud/panel/notify.qc new file mode 100644 index 0000000000..821c993faf --- /dev/null +++ b/qcsrc/client/hud/panel/notify.qc @@ -0,0 +1,154 @@ +// Notification area (#4) + +void HUD_Notify_Push(string icon, string attacker, string victim) +{ + if (icon == "") + return; + + ++notify_count; + --notify_index; + + if (notify_index == -1) + notify_index = NOTIFY_MAX_ENTRIES-1; + + // Free old strings + if (notify_attackers[notify_index]) + strunzone(notify_attackers[notify_index]); + + if (notify_victims[notify_index]) + strunzone(notify_victims[notify_index]); + + if (notify_icons[notify_index]) + strunzone(notify_icons[notify_index]); + + // Allocate new strings + if (victim != "") + { + notify_attackers[notify_index] = strzone(attacker); + notify_victims[notify_index] = strzone(victim); + } + else + { + // In case of a notification without a victim, the attacker + // is displayed on the victim's side. Instead of special + // treatment later on, we can simply switch them here. + notify_attackers[notify_index] = string_null; + notify_victims[notify_index] = strzone(attacker); + } + + notify_icons[notify_index] = strzone(icon); + notify_times[notify_index] = time; +} + +void HUD_Notify() +{ + if (!autocvar__hud_configure) + if (!autocvar_hud_panel_notify) + return; + + HUD_Panel_UpdateCvars(); + HUD_Panel_DrawBg(1); + + if (!autocvar__hud_configure) + if (notify_count == 0) + return; + + vector pos, size; + pos = panel_pos; + size = panel_size; + + if (panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + size -= '2 2 0' * panel_bg_padding; + } + + float fade_start = max(0, autocvar_hud_panel_notify_time); + float fade_time = max(0, autocvar_hud_panel_notify_fadetime); + float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect); + + int entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size.y / size.x), NOTIFY_MAX_ENTRIES); + float entry_height = size.y / entry_count; + + float panel_width_half = size.x * 0.5; + float icon_width_half = entry_height * icon_aspect / 2; + float name_maxwidth = panel_width_half - icon_width_half - size.x * NOTIFY_ICON_MARGIN; + + vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize; + vector icon_size = (eX * icon_aspect + eY) * entry_height; + vector icon_left = eX * (panel_width_half - icon_width_half); + vector attacker_right = eX * name_maxwidth; + vector victim_left = eX * (size.x - name_maxwidth); + + vector attacker_pos, victim_pos, icon_pos; + string attacker, victim, icon; + int i, j, count, step, limit; + float alpha; + + if (autocvar_hud_panel_notify_flip) + { + // Order items from the top down + i = 0; + step = +1; + limit = entry_count; + } + else + { + // Order items from the bottom up + i = entry_count - 1; + step = -1; + limit = -1; + } + + for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count) + { + if(autocvar__hud_configure) + { + attacker = sprintf(_("Player %d"), count + 1); + victim = sprintf(_("Player %d"), count + 2); + icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2; + alpha = bound(0, 1.2 - count / entry_count, 1); + } + else + { + if (j == NOTIFY_MAX_ENTRIES) + j = 0; + + if (notify_times[j] + fade_start > time) + alpha = 1; + else if (fade_time != 0) + { + alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1); + if (alpha == 0) + break; + } + else + break; + + attacker = notify_attackers[j]; + victim = notify_victims[j]; + icon = notify_icons[j]; + } + + if (icon != "" && victim != "") + { + vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size.y)); + + icon_pos = pos + icon_left + eY * i * entry_height; + drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL); + + victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors); + victim_pos = pos + victim_left + name_top; + drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL); + + if (attacker != "") + { + attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors); + attacker_pos = pos + attacker_right - eX * stringwidth(attacker, true, font_size) + name_top; + drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL); + } + } + } + + notify_count = count; +} diff --git a/qcsrc/client/hud/panel/physics.qc b/qcsrc/client/hud/panel/physics.qc new file mode 100644 index 0000000000..e59b5e7164 --- /dev/null +++ b/qcsrc/client/hud/panel/physics.qc @@ -0,0 +1,287 @@ +// Physics panel (#15) + +vector acc_prevspeed; +float acc_prevtime, acc_avg, top_speed, top_speed_time; +float physics_update_time, discrete_speed, discrete_acceleration; +void HUD_Physics() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_physics) return; + if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return; + if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + } + + HUD_Panel_UpdateCvars(); + + draw_beginBoldFont(); + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + float acceleration_progressbar_scale = 0; + if(autocvar_hud_panel_physics_progressbar && autocvar_hud_panel_physics_acceleration_progressbar_scale > 1) + acceleration_progressbar_scale = autocvar_hud_panel_physics_acceleration_progressbar_scale; + + float text_scale; + if (autocvar_hud_panel_physics_text_scale <= 0) + text_scale = 1; + else + text_scale = min(autocvar_hud_panel_physics_text_scale, 1); + + //compute speed + float speed, conversion_factor; + string unit; + + switch(autocvar_hud_panel_physics_speed_unit) + { + default: + case 1: + unit = _(" qu/s"); + conversion_factor = 1.0; + break; + case 2: + unit = _(" m/s"); + conversion_factor = 0.0254; + break; + case 3: + unit = _(" km/h"); + conversion_factor = 0.0254 * 3.6; + break; + case 4: + unit = _(" mph"); + conversion_factor = 0.0254 * 3.6 * 0.6213711922; + break; + case 5: + unit = _(" knots"); + conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h + break; + } + + vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel); + + float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 ); + if (autocvar__hud_configure) + speed = floor( max_speed * 0.65 + 0.5 ); + else if(autocvar_hud_panel_physics_speed_vertical) + speed = floor( vlen(vel) * conversion_factor + 0.5 ); + else + speed = floor( vlen(vel - vel.z * '0 0 1') * conversion_factor + 0.5 ); + + //compute acceleration + float acceleration, f; + if (autocvar__hud_configure) + acceleration = autocvar_hud_panel_physics_acceleration_max * 0.3; + else + { + // 1 m/s = 0.0254 qu/s; 1 g = 9.80665 m/s^2 + f = time - acc_prevtime; + if(autocvar_hud_panel_physics_acceleration_vertical) + acceleration = (vlen(vel) - vlen(acc_prevspeed)); + else + acceleration = (vlen(vel - '0 0 1' * vel.z) - vlen(acc_prevspeed - '0 0 1' * acc_prevspeed.z)); + + acceleration = acceleration * (1 / max(0.0001, f)) * (0.0254 / 9.80665); + + acc_prevspeed = vel; + acc_prevtime = time; + + if(autocvar_hud_panel_physics_acceleration_movingaverage) + { + f = bound(0, f * 10, 1); + acc_avg = acc_avg * (1 - f) + acceleration * f; + acceleration = acc_avg; + } + } + + int acc_decimals = 2; + if(time > physics_update_time) + { + // workaround for ftos_decimals returning a negative 0 + if(discrete_acceleration > -1 / pow(10, acc_decimals) && discrete_acceleration < 0) + discrete_acceleration = 0; + discrete_acceleration = acceleration; + discrete_speed = speed; + physics_update_time += autocvar_hud_panel_physics_update_interval; + } + + //compute layout + float panel_ar = panel_size.x/panel_size.y; + vector speed_offset = '0 0 0', acceleration_offset = '0 0 0'; + if (panel_ar >= 5 && !acceleration_progressbar_scale) + { + panel_size.x *= 0.5; + if (autocvar_hud_panel_physics_flip) + speed_offset.x = panel_size.x; + else + acceleration_offset.x = panel_size.x; + } + else + { + panel_size.y *= 0.5; + if (autocvar_hud_panel_physics_flip) + speed_offset.y = panel_size.y; + else + acceleration_offset.y = panel_size.y; + } + int speed_baralign, acceleration_baralign; + if (autocvar_hud_panel_physics_baralign == 1) + acceleration_baralign = speed_baralign = 1; + else if(autocvar_hud_panel_physics_baralign == 4) + acceleration_baralign = speed_baralign = 2; + else if (autocvar_hud_panel_physics_flip) + { + acceleration_baralign = (autocvar_hud_panel_physics_baralign == 2); + speed_baralign = (autocvar_hud_panel_physics_baralign == 3); + } + else + { + speed_baralign = (autocvar_hud_panel_physics_baralign == 2); + acceleration_baralign = (autocvar_hud_panel_physics_baralign == 3); + } + if (autocvar_hud_panel_physics_acceleration_progressbar_mode == 0) + acceleration_baralign = 3; //override hud_panel_physics_baralign value for acceleration + + //draw speed + if(speed) + if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2) + HUD_Panel_DrawProgressBar(panel_pos + speed_offset, panel_size, "progressbar", speed/max_speed, 0, speed_baralign, autocvar_hud_progressbar_speed_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + vector tmp_offset = '0 0 0', tmp_size = '0 0 0'; + if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2) + { + tmp_size.x = panel_size.x * 0.75; + tmp_size.y = panel_size.y * text_scale; + if (speed_baralign) + tmp_offset.x = panel_size.x - tmp_size.x; + //else + //tmp_offset_x = 0; + tmp_offset.y = (panel_size.y - tmp_size.y) / 2; + drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(discrete_speed), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + //draw speed unit + if (speed_baralign) + tmp_offset.x = 0; + else + tmp_offset.x = tmp_size.x; + if (autocvar_hud_panel_physics_speed_unit_show) + { + //tmp_offset_y = 0; + tmp_size.x = panel_size.x * (1 - 0.75); + tmp_size.y = panel_size.y * 0.4 * text_scale; + tmp_offset.y = (panel_size.y * 0.4 - tmp_size.y) / 2; + drawstring_aspect(panel_pos + speed_offset + tmp_offset, unit, tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + } + + //compute and draw top speed + if (autocvar_hud_panel_physics_topspeed) + if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 2) + { + if (autocvar__hud_configure) + { + top_speed = floor( max_speed * 0.75 + 0.5 ); + f = 1; + } + else + { + if (speed >= top_speed) + { + top_speed = speed; + top_speed_time = time; + } + if (top_speed != 0) + { + f = max(1, autocvar_hud_panel_physics_topspeed_time); + // divide by f to make it start from 1 + f = cos( ((time - top_speed_time) / f) * PI/2 ); + } + else //hide top speed 0, it would be stupid + f = 0; + } + if (f > 0) + { + //top speed progressbar peak + if(speed < top_speed) + if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 2) + { + float peak_offsetX; + vector peak_size = '0 0 0'; + if (speed_baralign == 0) + peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x; + else if (speed_baralign == 1) + peak_offsetX = (1 - min(top_speed, max_speed)/max_speed) * panel_size.x; + else // if (speed_baralign == 2) + peak_offsetX = min(top_speed, max_speed)/max_speed * panel_size.x * 0.5; + peak_size.x = floor(panel_size.x * 0.01 + 1.5); + peak_size.y = panel_size.y; + if (speed_baralign == 2) // draw two peaks, on both sides + { + drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x + peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + drawfill(panel_pos + speed_offset + eX * (0.5 * panel_size.x - peak_offsetX + peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + else + drawfill(panel_pos + speed_offset + eX * (peak_offsetX - peak_size.x), peak_size, autocvar_hud_progressbar_speed_color, f * autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + //top speed + tmp_offset.y = panel_size.y * 0.4; + tmp_size.x = panel_size.x * (1 - 0.75); + tmp_size.y = (panel_size.y - tmp_offset.y) * text_scale; + tmp_offset.y += (panel_size.y - tmp_offset.y - tmp_size.y) / 2; + drawstring_aspect(panel_pos + speed_offset + tmp_offset, ftos(top_speed), tmp_size, '1 0 0', f * panel_fg_alpha, DRAWFLAG_NORMAL); + } + else + top_speed = 0; + } + + //draw acceleration + if(acceleration) + if(autocvar_hud_panel_physics_progressbar == 1 || autocvar_hud_panel_physics_progressbar == 3) + { + vector progressbar_color; + if(acceleration < 0) + progressbar_color = autocvar_hud_progressbar_acceleration_neg_color; + else + progressbar_color = autocvar_hud_progressbar_acceleration_color; + + f = acceleration/autocvar_hud_panel_physics_acceleration_max; + if (autocvar_hud_panel_physics_acceleration_progressbar_nonlinear) + f = (f >= 0 ? sqrt(f) : -sqrt(-f)); + + if (acceleration_progressbar_scale) // allow progressbar to go out of panel bounds + { + tmp_size = acceleration_progressbar_scale * panel_size.x * eX + panel_size.y * eY; + + if (acceleration_baralign == 1) + tmp_offset.x = panel_size.x - tmp_size.x; + else if (acceleration_baralign == 2 || acceleration_baralign == 3) + tmp_offset.x = (panel_size.x - tmp_size.x) / 2; + else + tmp_offset.x = 0; + tmp_offset.y = 0; + } + else + { + tmp_size = panel_size; + tmp_offset = '0 0 0'; + } + + HUD_Panel_DrawProgressBar(panel_pos + acceleration_offset + tmp_offset, tmp_size, "accelbar", f, 0, acceleration_baralign, progressbar_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + if(autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3) + { + tmp_size.x = panel_size.x; + tmp_size.y = panel_size.y * text_scale; + tmp_offset.x = 0; + tmp_offset.y = (panel_size.y - tmp_size.y) / 2; + + drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(discrete_acceleration, acc_decimals), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/powerups.qc b/qcsrc/client/hud/panel/powerups.qc new file mode 100644 index 0000000000..10446d6436 --- /dev/null +++ b/qcsrc/client/hud/panel/powerups.qc @@ -0,0 +1,211 @@ +// Powerups (#2) + +// Powerup item fields (reusing existing fields) +.string message; // Human readable name +.string netname; // Icon name +.vector colormod; // Color +.float count; // Time left +.float lifetime; // Maximum time + +entity powerupItems; +int powerupItemsCount; + +void resetPowerupItems() +{ + entity item; + for(item = powerupItems; item; item = item.chain) + item.count = 0; + + powerupItemsCount = 0; +} + +void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime) +{ + if(!powerupItems) + powerupItems = spawn(); + + entity item; + for(item = powerupItems; item.count; item = item.chain) + if(!item.chain) + item.chain = spawn(); + + item.message = name; + item.netname = icon; + item.colormod = color; + item.count = currentTime; + item.lifetime = lifeTime; + + ++powerupItemsCount; +} + +int getPowerupItemAlign(int align, int column, int row, int columns, int rows, bool isVertical) +{ + if(align < 2) + return align; + + bool isTop = isVertical && rows > 1 && row == 0; + bool isBottom = isVertical && rows > 1 && row == rows-1; + bool isLeft = !isVertical && columns > 1 && column == 0; + bool isRight = !isVertical && columns > 1 && column == columns-1; + + if(isTop || isLeft) return (align == 2) ? 1 : 0; + if(isBottom || isRight) return (align == 2) ? 0 : 1; + + return 2; +} + +void HUD_Powerups() +{ + int allItems = getstati(STAT_ITEMS, 0, 24); + int allBuffs = getstati(STAT_BUFFS, 0, 24); + int strengthTime, shieldTime, superTime; + + // Initialize items + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_powerups) return; + if(spectatee_status == -1) return; + if(getstati(STAT_HEALTH) <= 0) return; + if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return; + + strengthTime = bound(0, STAT(STRENGTH_FINISHED) - time, 99); + shieldTime = bound(0, STAT(INVINCIBLE_FINISHED) - time, 99); + superTime = bound(0, getstatf(STAT_SUPERWEAPONS_FINISHED) - time, 99); + + if(allItems & IT_UNLIMITED_SUPERWEAPONS) + superTime = 99; + + // Prevent stuff to show up on mismatch that will be fixed next frame + if(!(allItems & IT_SUPERWEAPON)) + superTime = 0; + } + else + { + strengthTime = 15; + shieldTime = 27; + superTime = 13; + allBuffs = 0; + } + + // Add items to linked list + resetPowerupItems(); + + if(strengthTime) + addPowerupItem("Strength", "strength", autocvar_hud_progressbar_strength_color, strengthTime, 30); + if(shieldTime) + addPowerupItem("Shield", "shield", autocvar_hud_progressbar_shield_color, shieldTime, 30); + if(superTime) + addPowerupItem("Superweapons", "superweapons", autocvar_hud_progressbar_superweapons_color, superTime, 30); + + MUTATOR_CALLHOOK(HUD_Powerups_add); + + if(!powerupItemsCount) + return; + + // Draw panel background + HUD_Panel_UpdateCvars(); + HUD_Panel_DrawBg(1); + + // Set drawing area + vector pos = panel_pos; + vector size = panel_size; + bool isVertical = size.y > size.x; + + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + size -= '2 2 0' * panel_bg_padding; + } + + // Find best partitioning of the drawing area + const float DESIRED_ASPECT = 6; + float aspect = 0, a; + int columns = 0, c; + int rows = 0, r; + int i = 1; + + do + { + c = floor(powerupItemsCount / i); + r = ceil(powerupItemsCount / c); + a = isVertical ? (size.y/r) / (size.x/c) : (size.x/c) / (size.y/r); + + if(i == 1 || fabs(DESIRED_ASPECT - a) < fabs(DESIRED_ASPECT - aspect)) + { + aspect = a; + columns = c; + rows = r; + } + } + while(++i <= powerupItemsCount); + + // Prevent single items from getting too wide + if(powerupItemsCount == 1 && aspect > DESIRED_ASPECT) + { + if(isVertical) + { + size.y *= 0.5; + pos.y += size.y * 0.5; + } + else + { + size.x *= 0.5; + pos.x += size.x * 0.5; + } + } + + // Draw items from linked list + vector itemPos = pos; + vector itemSize = eX * (size.x / columns) + eY * (size.y / rows); + vector textColor = '1 1 1'; + + int fullSeconds = 0; + int align = 0; + int column = 0; + int row = 0; + + draw_beginBoldFont(); + for(entity item = powerupItems; item.count; item = item.chain) + { + itemPos = eX * (pos.x + column * itemSize.x) + eY * (pos.y + row * itemSize.y); + + // Draw progressbar + if(autocvar_hud_panel_powerups_progressbar) + { + align = getPowerupItemAlign(autocvar_hud_panel_powerups_baralign, column, row, columns, rows, isVertical); + HUD_Panel_DrawProgressBar(itemPos, itemSize, "progressbar", item.count / item.lifetime, isVertical, align, item.colormod, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + // Draw icon and text + if(autocvar_hud_panel_powerups_text) + { + align = getPowerupItemAlign(autocvar_hud_panel_powerups_iconalign, column, row, columns, rows, isVertical); + fullSeconds = ceil(item.count); + textColor = '0.6 0.6 0.6' + (item.colormod * 0.4); + + if(item.count > 1) + DrawNumIcon(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha); + if(item.count <= 5) + DrawNumIcon_expanding(itemPos, itemSize, fullSeconds, item.netname, isVertical, align, textColor, panel_fg_alpha, bound(0, (fullSeconds - item.count) / 0.5, 1)); + } + + // Determine next section + if(isVertical) + { + if(++column >= columns) + { + column = 0; + ++row; + } + } + else + { + if(++row >= rows) + { + row = 0; + ++column; + } + } + } + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/pressedkeys.qc b/qcsrc/client/hud/panel/pressedkeys.qc new file mode 100644 index 0000000000..94cc328779 --- /dev/null +++ b/qcsrc/client/hud/panel/pressedkeys.qc @@ -0,0 +1,63 @@ +/** Draw pressed keys (#11) */ +void HUD_PressedKeys() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_pressedkeys) return; + if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return; + } + + HUD_Panel_UpdateCvars(); + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + // force custom aspect + float aspect = autocvar_hud_panel_pressedkeys_aspect; + if(aspect) + { + vector newSize = '0 0 0'; + if(mySize.x/mySize.y > aspect) + { + newSize.x = aspect * mySize.y; + newSize.y = mySize.y; + + pos.x = pos.x + (mySize.x - newSize.x) / 2; + } + else + { + newSize.y = 1/aspect * mySize.x; + newSize.x = mySize.x; + + pos.y = pos.y + (mySize.y - newSize.y) / 2; + } + mySize = newSize; + } + + vector keysize; + keysize = eX * mySize.x * (1/3.0) + eY * mySize.y * (1/(3.0 - !autocvar_hud_panel_pressedkeys_attack)); + float pressedkeys; + pressedkeys = getstatf(STAT_PRESSED_KEYS); + + if(autocvar_hud_panel_pressedkeys_attack) + { + drawpic_aspect_skin(pos + eX * keysize.x * 0.5, ((pressedkeys & KEY_ATCK) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize.x * 1.5, ((pressedkeys & KEY_ATCK2) ? "key_atck_inv.tga" : "key_atck.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + pos.y += keysize.y; + } + + drawpic_aspect_skin(pos, ((pressedkeys & KEY_CROUCH) ? "key_crouch_inv.tga" : "key_crouch.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_FORWARD) ? "key_forward_inv.tga" : "key_forward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_JUMP) ? "key_jump_inv.tga" : "key_jump.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + pos.y += keysize.y; + drawpic_aspect_skin(pos, ((pressedkeys & KEY_LEFT) ? "key_left_inv.tga" : "key_left.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize.x, ((pressedkeys & KEY_BACKWARD) ? "key_backward_inv.tga" : "key_backward.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos + eX * keysize.x * 2, ((pressedkeys & KEY_RIGHT) ? "key_right_inv.tga" : "key_right.tga"), keysize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); +} diff --git a/qcsrc/client/hud/panel/quickmenu.qc b/qcsrc/client/hud/panel/quickmenu.qc new file mode 100644 index 0000000000..128d54aaab --- /dev/null +++ b/qcsrc/client/hud/panel/quickmenu.qc @@ -0,0 +1,3 @@ +// QuickMenu (#23) + +#include "../../quickmenu.qc" diff --git a/qcsrc/client/hud/panel/racetimer.qc b/qcsrc/client/hud/panel/racetimer.qc new file mode 100644 index 0000000000..1fa216b967 --- /dev/null +++ b/qcsrc/client/hud/panel/racetimer.qc @@ -0,0 +1,147 @@ +/** Race timer (#8) */ +void HUD_RaceTimer () +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_racetimer) return; + if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(spectatee_status == -1) return; + } + + HUD_Panel_UpdateCvars(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + // always force 4:1 aspect + vector newSize = '0 0 0'; + if(mySize.x/mySize.y > 4) + { + newSize.x = 4 * mySize.y; + newSize.y = mySize.y; + + pos.x = pos.x + (mySize.x - newSize.x) / 2; + } + else + { + newSize.y = 1/4 * mySize.x; + newSize.x = mySize.x; + + pos.y = pos.y + (mySize.y - newSize.y) / 2; + } + mySize = newSize; + + float a, t; + string s, forcetime; + + if(autocvar__hud_configure) + { + s = "0:13:37"; + draw_beginBoldFont(); + drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.60 0.60 0' * mySize.y), s, '0.60 0.60 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); + s = _("^1Intermediate 1 (+15.42)"); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.60 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); + s = sprintf(_("^1PENALTY: %.1f (%s)"), 2, "missing a checkpoint"); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.20 * mySize.y) + eY * 0.80 * mySize.y, s, '1 1 0' * 0.20 * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); + } + else if(race_checkpointtime) + { + a = bound(0, 2 - (time - race_checkpointtime), 1); + s = ""; + forcetime = ""; + if(a > 0) // just hit a checkpoint? + { + if(race_checkpoint != 254) + { + if(race_time && race_previousbesttime) + s = MakeRaceString(race_checkpoint, TIME_DECODE(race_time) - TIME_DECODE(race_previousbesttime), 0, 0, race_previousbestname); + else + s = MakeRaceString(race_checkpoint, 0, -1, 0, race_previousbestname); + if(race_time) + forcetime = TIME_ENCODED_TOSTRING(race_time); + } + } + else + { + if(race_laptime && race_nextbesttime && race_nextcheckpoint != 254) + { + a = bound(0, 2 - ((race_laptime + TIME_DECODE(race_nextbesttime)) - (time + TIME_DECODE(race_penaltyaccumulator))), 1); + if(a > 0) // next one? + { + s = MakeRaceString(race_nextcheckpoint, (time + TIME_DECODE(race_penaltyaccumulator)) - race_laptime, TIME_DECODE(race_nextbesttime), 0, race_nextbestname); + } + } + } + + if(s != "" && a > 0) + { + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + + if(race_penaltytime) + { + a = bound(0, 2 - (time - race_penaltyeventtime), 1); + if(a > 0) + { + s = sprintf(_("^1PENALTY: %.1f (%s)"), race_penaltytime * 0.1, race_penaltyreason); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.8 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + } + + draw_beginBoldFont(); + + if(forcetime != "") + { + a = bound(0, (time - race_checkpointtime) / 0.5, 1); + drawstring_expanding(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(forcetime, false, '1 1 0' * 0.6 * mySize.y), forcetime, '1 1 0' * 0.6 * mySize.y, '1 1 1', panel_fg_alpha, 0, a); + } + else + a = 1; + + if(race_laptime && race_checkpoint != 255) + { + s = TIME_ENCODED_TOSTRING(TIME_ENCODE(time + TIME_DECODE(race_penaltyaccumulator) - race_laptime)); + drawstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, false, '0.6 0.6 0' * mySize.y), s, '0.6 0.6 0' * mySize.y, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + + draw_endBoldFont(); + } + else + { + if(race_mycheckpointtime) + { + a = bound(0, 2 - (time - race_mycheckpointtime), 1); + s = MakeRaceString(race_mycheckpoint, TIME_DECODE(race_mycheckpointdelta), -(race_mycheckpointenemy == ""), race_mycheckpointlapsdelta, race_mycheckpointenemy); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + if(race_othercheckpointtime && race_othercheckpointenemy != "") + { + a = bound(0, 2 - (time - race_othercheckpointtime), 1); + s = MakeRaceString(race_othercheckpoint, -TIME_DECODE(race_othercheckpointdelta), -(race_othercheckpointenemy == ""), race_othercheckpointlapsdelta, race_othercheckpointenemy); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + + if(race_penaltytime && !race_penaltyaccumulator) + { + t = race_penaltytime * 0.1 + race_penaltyeventtime; + a = bound(0, (1 + t - time), 1); + if(a > 0) + { + if(time < t) + s = sprintf(_("^1PENALTY: %.1f (%s)"), (t - time) * 0.1, race_penaltyreason); + else + s = sprintf(_("^2PENALTY: %.1f (%s)"), 0, race_penaltyreason); + drawcolorcodedstring(pos + eX * 0.5 * mySize.x - '0.5 0 0' * stringwidth(s, true, '1 1 0' * 0.2 * mySize.y) + eY * 0.6 * mySize.y, s, '1 1 0' * 0.2 * mySize.y, panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + } + } +} diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc new file mode 100644 index 0000000000..ad3e79ce53 --- /dev/null +++ b/qcsrc/client/hud/panel/radar.qc @@ -0,0 +1,384 @@ +// Radar (#6) + +float HUD_Radar_Clickable() +{ + return hud_panel_radar_mouse && !hud_panel_radar_temp_hidden; +} + +void HUD_Radar_Show_Maximized(bool doshow,float clickable) +{ + hud_panel_radar_maximized = doshow; + hud_panel_radar_temp_hidden = 0; + + if ( doshow ) + { + if (clickable) + { + if(autocvar_hud_cursormode) + setcursormode(1); + hud_panel_radar_mouse = 1; + } + } + else if ( hud_panel_radar_mouse ) + { + hud_panel_radar_mouse = 0; + mouseClicked = 0; + if(autocvar_hud_cursormode) + if(!mv_active) + setcursormode(0); + } +} +void HUD_Radar_Hide_Maximized() +{ + HUD_Radar_Show_Maximized(false,false); +} + + +float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary) +{ + if(!hud_panel_radar_maximized || !hud_panel_radar_mouse || + autocvar__hud_configure || mv_active) + return false; + + if(bInputType == 3) + { + mousepos_x = nPrimary; + mousepos_y = nSecondary; + return true; + } + + if(nPrimary == K_MOUSE1) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE1; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE1); + } + else if(nPrimary == K_MOUSE2) + { + if(bInputType == 0) // key pressed + mouseClicked |= S_MOUSE2; + else if(bInputType == 1) // key released + mouseClicked -= (mouseClicked & S_MOUSE2); + } + else if ( nPrimary == K_ESCAPE && bInputType == 0 ) + { + HUD_Radar_Hide_Maximized(); + } + else + { + // allow console/use binds to work without hiding the map + string con_keys; + float keys; + float i; + con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ; + keys = tokenize(con_keys); // findkeysforcommand returns data for this + for (i = 0; i < keys; ++i) + { + if(nPrimary == stof(argv(i))) + return false; + } + + if ( getstati(STAT_HEALTH) <= 0 ) + { + // Show scoreboard + if ( bInputType < 2 ) + { + con_keys = findkeysforcommand("+showscores", 0); + keys = tokenize(con_keys); + for (i = 0; i < keys; ++i) + { + if ( nPrimary == stof(argv(i)) ) + { + hud_panel_radar_temp_hidden = bInputType == 0; + return false; + } + } + } + } + else if ( bInputType == 0 ) + HUD_Radar_Hide_Maximized(); + + return false; + } + + return true; +} + +void HUD_Radar_Mouse() +{ + if ( !hud_panel_radar_mouse ) return; + if(mv_active) return; + + if ( intermission ) + { + HUD_Radar_Hide_Maximized(); + return; + } + + if(mouseClicked & S_MOUSE2) + { + HUD_Radar_Hide_Maximized(); + return; + } + + if(!autocvar_hud_cursormode) + { + mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; + + mousepos_x = bound(0, mousepos_x, vid_conwidth); + mousepos_y = bound(0, mousepos_y, vid_conheight); + } + + HUD_Panel_UpdateCvars(); + + + panel_size = autocvar_hud_panel_radar_maximized_size; + panel_size_x = bound(0.2, panel_size_x, 1) * vid_conwidth; + panel_size_y = bound(0.2, panel_size_y, 1) * vid_conheight; + panel_pos_x = (vid_conwidth - panel_size_x) / 2; + panel_pos_y = (vid_conheight - panel_size_y) / 2; + + if(mouseClicked & S_MOUSE1) + { + // click outside + if ( mousepos_x < panel_pos_x || mousepos_x > panel_pos_x + panel_size_x || + mousepos_y < panel_pos_y || mousepos_y > panel_pos_y + panel_size_y ) + { + HUD_Radar_Hide_Maximized(); + return; + } + vector pos = teamradar_texcoord_to_3dcoord(teamradar_2dcoord_to_texcoord(mousepos),view_origin_z); + localcmd(sprintf("cmd ons_spawn %f %f %f",pos_x,pos_y,pos_z)); + + HUD_Radar_Hide_Maximized(); + return; + } + + + const vector cursor_size = '32 32 0'; + drawpic(mousepos-'8 4 0', strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursor_size, '1 1 1', 0.8, DRAWFLAG_NORMAL); +} + +void HUD_Radar() +{ + if (!autocvar__hud_configure) + { + if (hud_panel_radar_maximized) + { + if (!hud_draw_maximized) return; + } + else + { + if (autocvar_hud_panel_radar == 0) return; + if (autocvar_hud_panel_radar != 2 && !teamplay) return; + if(radar_panel_modified) + { + panel.update_time = time; // forces reload of panel attributes + radar_panel_modified = false; + } + } + } + + if ( hud_panel_radar_temp_hidden ) + return; + + HUD_Panel_UpdateCvars(); + + float f = 0; + + if (hud_panel_radar_maximized && !autocvar__hud_configure) + { + panel_size = autocvar_hud_panel_radar_maximized_size; + panel_size.x = bound(0.2, panel_size.x, 1) * vid_conwidth; + panel_size.y = bound(0.2, panel_size.y, 1) * vid_conheight; + panel_pos.x = (vid_conwidth - panel_size.x) / 2; + panel_pos.y = (vid_conheight - panel_size.y) / 2; + + string panel_bg; + panel_bg = strcat(hud_skin_path, "/border_default"); // always use the default border when maximized + if(precache_pic(panel_bg) == "") + panel_bg = "gfx/hud/default/border_default"; // fallback + if(!radar_panel_modified && panel_bg != panel.current_panel_bg) + radar_panel_modified = true; + if(panel.current_panel_bg) + strunzone(panel.current_panel_bg); + panel.current_panel_bg = strzone(panel_bg); + + switch(hud_panel_radar_maximized_zoommode) + { + default: + case 0: + f = current_zoomfraction; + break; + case 1: + f = 1 - current_zoomfraction; + break; + case 2: + f = 0; + break; + case 3: + f = 1; + break; + } + + switch(hud_panel_radar_maximized_rotation) + { + case 0: + teamradar_angle = view_angles.y - 90; + break; + default: + teamradar_angle = 90 * hud_panel_radar_maximized_rotation; + break; + } + } + if (!hud_panel_radar_maximized && !autocvar__hud_configure) + { + switch(hud_panel_radar_zoommode) + { + default: + case 0: + f = current_zoomfraction; + break; + case 1: + f = 1 - current_zoomfraction; + break; + case 2: + f = 0; + break; + case 3: + f = 1; + break; + } + + switch(hud_panel_radar_rotation) + { + case 0: + teamradar_angle = view_angles.y - 90; + break; + default: + teamradar_angle = 90 * hud_panel_radar_rotation; + break; + } + } + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + int color2; + entity tm; + float scale2d, normalsize, bigsize; + + teamradar_origin2d = pos + 0.5 * mySize; + teamradar_size2d = mySize; + + if(minimapname == "") + return; + + teamradar_loadcvars(); + + scale2d = vlen_maxnorm2d(mi_picmax - mi_picmin); + teamradar_size2d = mySize; + + teamradar_extraclip_mins = teamradar_extraclip_maxs = '0 0 0'; // we always center + + // pixels per world qu to match the teamradar_size2d_x range in the longest dimension + if((hud_panel_radar_rotation == 0 && !hud_panel_radar_maximized) || (hud_panel_radar_maximized_rotation == 0 && hud_panel_radar_maximized)) + { + // max-min distance must fit the radar in any rotation + bigsize = vlen_minnorm2d(teamradar_size2d) * scale2d / (1.05 * vlen2d(mi_scale)); + } + else + { + vector c0, c1, c2, c3, span; + c0 = rotate(mi_min, teamradar_angle * DEG2RAD); + c1 = rotate(mi_max, teamradar_angle * DEG2RAD); + c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD); + c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD); + span = '0 0 0'; + span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x); + span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y); + + // max-min distance must fit the radar in x=x, y=y + bigsize = min( + teamradar_size2d.x * scale2d / (1.05 * span.x), + teamradar_size2d.y * scale2d / (1.05 * span.y) + ); + } + + normalsize = vlen_maxnorm2d(teamradar_size2d) * scale2d / hud_panel_radar_scale; + if(bigsize > normalsize) + normalsize = bigsize; + + teamradar_size = + f * bigsize + + (1 - f) * normalsize; + teamradar_origin3d_in_texcoord = teamradar_3dcoord_to_texcoord( + f * mi_center + + (1 - f) * view_origin); + + drawsetcliparea( + pos.x, + pos.y, + mySize.x, + mySize.y + ); + + draw_teamradar_background(hud_panel_radar_foreground_alpha); + + for(tm = world; (tm = find(tm, classname, "radarlink")); ) + draw_teamradar_link(tm.origin, tm.velocity, tm.team); + + vector coord; + vector brightcolor; + for(tm = world; (tm = findflags(tm, teamradar_icon, 0xFFFFFF)); ) + { + if ( hud_panel_radar_mouse ) + if ( tm.health > 0 ) + if ( tm.team == myteam+1 ) + { + coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(tm.origin)); + if ( vlen(mousepos-coord) < 8 ) + { + brightcolor_x = min(1,tm.teamradar_color_x*1.5); + brightcolor_y = min(1,tm.teamradar_color_y*1.5); + brightcolor_z = min(1,tm.teamradar_color_z*1.5); + drawpic(coord - '8 8 0', "gfx/teamradar_icon_glow", '16 16 0', brightcolor, panel_fg_alpha, 0); + } + } + entity icon = RadarIcons_from(tm.teamradar_icon); + draw_teamradar_icon(tm.origin, icon, tm, spritelookupcolor(tm, icon.netname, tm.teamradar_color), panel_fg_alpha); + } + for(tm = world; (tm = find(tm, classname, "entcs_receiver")); ) + { + color2 = GetPlayerColor(tm.sv_entnum); + //if(color == NUM_SPECTATOR || color == color2) + draw_teamradar_player(tm.origin, tm.angles, Team_ColorRGB(color2)); + } + draw_teamradar_player(view_origin, view_angles, '1 1 1'); + + drawresetcliparea(); + + if ( hud_panel_radar_mouse ) + { + string message = "Click to select teleport destination"; + + if ( getstati(STAT_HEALTH) <= 0 ) + { + message = "Click to select spawn location"; + } + + drawcolorcodedstring(pos + '0.5 0 0' * (mySize_x - stringwidth(message, true, hud_fontsize)) - '0 1 0' * hud_fontsize_y * 2, + message, hud_fontsize, hud_panel_radar_foreground_alpha, DRAWFLAG_NORMAL); + + hud_panel_radar_bottom = pos_y + mySize_y + hud_fontsize_y; + } +} diff --git a/qcsrc/client/hud/panel/score.qc b/qcsrc/client/hud/panel/score.qc new file mode 100644 index 0000000000..dd3000c4cb --- /dev/null +++ b/qcsrc/client/hud/panel/score.qc @@ -0,0 +1,305 @@ +// Score (#7) + +void HUD_UpdatePlayerTeams(); +void HUD_Score_Rankings(vector pos, vector mySize, entity me) +{ + float score; + entity tm = world, pl; + int SCOREPANEL_MAX_ENTRIES = 6; + float SCOREPANEL_ASPECTRATIO = 2; + int entries = bound(1, floor(SCOREPANEL_MAX_ENTRIES * mySize.y/mySize.x * SCOREPANEL_ASPECTRATIO), SCOREPANEL_MAX_ENTRIES); + vector fontsize = '1 1 0' * (mySize.y/entries); + + vector rgb, score_color; + rgb = '1 1 1'; + score_color = '1 1 1'; + + float name_size = mySize.x*0.75; + float spacing_size = mySize.x*0.04; + const float highlight_alpha = 0.2; + int i = 0, first_pl = 0; + bool me_printed = false; + string s; + if (autocvar__hud_configure) + { + float players_per_team = 0; + if (team_count) + { + // show team scores in the first line + float score_size = mySize.x / team_count; + players_per_team = max(2, ceil((entries - 1) / team_count)); + for(i=0; i= 5) + distribution_color = eY; + else if(distribution >= 0) + distribution_color = '1 1 1'; + else if(distribution >= -5) + distribution_color = '1 1 0'; + else + distribution_color = eX; + + string distribution_str; + distribution_str = ftos(distribution); + draw_beginBoldFont(); + if (distribution >= 0) + { + if (distribution > 0) + distribution_str = strcat("+", distribution_str); + HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(pos + eX * 0.75 * mySize.x, distribution_str, eX * 0.25 * mySize.x + eY * (1/3) * mySize.y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); + } else { // teamgames + float row, column, rows = 0, columns = 0; + vector offset = '0 0 0'; + vector score_pos, score_size; //for scores other than myteam + if(autocvar_hud_panel_score_rankings) + { + HUD_Score_Rankings(pos, mySize, me); + return; + } + if(spectatee_status == -1) + { + rows = HUD_GetRowCount(team_count, mySize, 3); + columns = ceil(team_count/rows); + score_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + + float newSize; + if(score_size.x/score_size.y > 3) + { + newSize = 3 * score_size.y; + offset.x = score_size.x - newSize; + pos.x += offset.x/2; + score_size.x = newSize; + } + else + { + newSize = 1/3 * score_size.x; + offset.y = score_size.y - newSize; + pos.y += offset.y/2; + score_size.y = newSize; + } + } + else + score_size = eX * mySize.x*(1/4) + eY * mySize.y*(1/3); + + float max_fragcount; + max_fragcount = -99; + draw_beginBoldFont(); + row = column = 0; + for(tm = teams.sort_next; tm; tm = tm.sort_next) { + if(tm.team == NUM_SPECTATOR) + continue; + score = tm.(teamscores[ts_primary]); + if(autocvar__hud_configure) + score = 123; + + if (score > max_fragcount) + max_fragcount = score; + + if (spectatee_status == -1) + { + score_pos = pos + eX * column * (score_size.x + offset.x) + eY * row * (score_size.y + offset.y); + if (max_fragcount == score) + HUD_Panel_DrawHighlight(score_pos, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(score_pos, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); + ++row; + if(row >= rows) + { + row = 0; + ++column; + } + } + else if(tm.team == myteam) { + if (max_fragcount == score) + HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize.x + eY * mySize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); + } else { + if (max_fragcount == score) + HUD_Panel_DrawHighlight(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, score_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(pos + eX * 0.75 * mySize.x + eY * (1/3) * rows * mySize.y, ftos(score), score_size, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); + ++rows; + } + } + draw_endBoldFont(); + } +} diff --git a/qcsrc/client/hud/panel/timer.qc b/qcsrc/client/hud/panel/timer.qc new file mode 100644 index 0000000000..f709b41ec9 --- /dev/null +++ b/qcsrc/client/hud/panel/timer.qc @@ -0,0 +1,56 @@ +void HUD_Timer() +{ + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_timer) return; + } + + HUD_Panel_UpdateCvars(); + + draw_beginBoldFont(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + HUD_Panel_DrawBg(1); + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + string timer; + float timelimit, elapsedTime, timeleft, minutesLeft; + + timelimit = getstatf(STAT_TIMELIMIT); + + timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time); + timeleft = ceil(timeleft); + + minutesLeft = floor(timeleft / 60); + + vector timer_color; + if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit + timer_color = '1 1 1'; //white + else if(minutesLeft >= 1) + timer_color = '1 1 0'; //yellow + else + timer_color = '1 0 0'; //red + + if (autocvar_hud_panel_timer_increment || timelimit == 0 || warmup_stage) { + if (time < STAT(GAMESTARTTIME)) { + //while restart is still active, show 00:00 + timer = seconds_tostring(0); + } else { + elapsedTime = floor(time - STAT(GAMESTARTTIME)); //127 + timer = seconds_tostring(elapsedTime); + } + } else { + timer = seconds_tostring(timeleft); + } + + drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_endBoldFont(); +} diff --git a/qcsrc/client/hud/panel/vote.qc b/qcsrc/client/hud/panel/vote.qc new file mode 100644 index 0000000000..5f286b24a4 --- /dev/null +++ b/qcsrc/client/hud/panel/vote.qc @@ -0,0 +1,137 @@ +/** Vote window (#9) */ +void HUD_Vote() +{ + if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS))) + { + vote_active = 1; + if (autocvar__hud_configure) + { + vote_yescount = 0; + vote_nocount = 0; + LOG_INFO(_("^1You must answer before entering hud configure mode\n")); + cvar_set("_hud_configure", "0"); + } + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats")); + uid2name_dialog = 1; + } + + if(!autocvar__hud_configure) + { + if(!autocvar_hud_panel_vote) return; + + panel_fg_alpha = autocvar_hud_panel_fg_alpha; + panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha; + + if(panel_bg_alpha_str == "") { + panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); + } + panel_bg_alpha = stof(panel_bg_alpha_str); + } + else + { + vote_yescount = 3; + vote_nocount = 2; + vote_needed = 4; + } + + string s; + float a; + if(vote_active != vote_prev) { + vote_change = time; + vote_prev = vote_active; + } + + if(vote_active || autocvar__hud_configure) + vote_alpha = bound(0, (time - vote_change) * 2, 1); + else + vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1); + + if(!vote_alpha) + return; + + HUD_Panel_UpdateCvars(); + + if(uid2name_dialog) + { + panel_pos = eX * 0.3 * vid_conwidth + eY * 0.1 * vid_conheight; + panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight; + } + + // these must be below above block + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1); + HUD_Panel_DrawBg(a); + a = panel_fg_alpha * a; + + if(panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + // always force 3:1 aspect + vector newSize = '0 0 0'; + if(mySize.x/mySize.y > 3) + { + newSize.x = 3 * mySize.y; + newSize.y = mySize.y; + + pos.x = pos.x + (mySize.x - newSize.x) / 2; + } + else + { + newSize.y = 1/3 * mySize.x; + newSize.x = mySize.x; + + pos.y = pos.y + (mySize.y - newSize.y) / 2; + } + mySize = newSize; + + s = _("A vote has been called for:"); + if(uid2name_dialog) + s = _("Allow servers to store and display your name?"); + drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors); + if(autocvar__hud_configure) + s = _("^1Configure the HUD"); + drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL); + + // print the yes/no counts + s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount); + drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL); + s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount); + drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL); + + // draw the progress bar backgrounds + drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + + // draw the highlights + if(vote_highlighted == 1) { + drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y); + drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + } + else if(vote_highlighted == -1) { + drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y); + drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + } + + // draw the progress bars + if(vote_yescount && vote_needed) + { + drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y); + drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + } + + if(vote_nocount && vote_needed) + { + drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y); + drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + } + + drawresetcliparea(); +} diff --git a/qcsrc/client/hud/panel/weapons.qc b/qcsrc/client/hud/panel/weapons.qc new file mode 100644 index 0000000000..4d1d7227b5 --- /dev/null +++ b/qcsrc/client/hud/panel/weapons.qc @@ -0,0 +1,509 @@ +// Weapon icons (#0) + +entity weaponorder[Weapons_MAX]; +void weaponorder_swap(int i, int j, entity pass) +{ + entity h = weaponorder[i]; + weaponorder[i] = weaponorder[j]; + weaponorder[j] = h; +} + +string weaponorder_cmp_str; +int weaponorder_cmp(int i, int j, entity pass) +{ + int ai, aj; + ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].weapon), 0); + aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].weapon), 0); + return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string) +} + +void HUD_Weapons() +{ + SELFPARAM(); + // declarations + WepSet weapons_stat = WepSet_GetFromStat(); + int i; + float f, a; + float screen_ar; + vector center = '0 0 0'; + int weapon_count, weapon_id; + int row, column, rows = 0, columns = 0; + bool vertical_order = true; + float aspect = autocvar_hud_panel_weapons_aspect; + + float timeout = autocvar_hud_panel_weapons_timeout; + float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0); + float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0); + + vector barsize = '0 0 0', baroffset = '0 0 0'; + vector ammo_color = '1 0 1'; + float ammo_alpha = 1; + + float when = max(1, autocvar_hud_panel_weapons_complainbubble_time); + float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime); + + vector weapon_pos, weapon_size = '0 0 0'; + vector color; + + // check to see if we want to continue + if(hud != HUD_NORMAL) return; + + if(!autocvar__hud_configure) + { + if((!autocvar_hud_panel_weapons) || (spectatee_status == -1)) + return; + if(timeout && time >= weapontime + timeout + timeout_effect_length) + if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin))) + { + weaponprevtime = time; + return; + } + } + + // update generic hud functions + HUD_Panel_UpdateCvars(); + + // figure out weapon order (how the weapons are sorted) // TODO make this configurable + if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0]) + { + int weapon_cnt; + if(weaponorder_bypriority) + strunzone(weaponorder_bypriority); + if(weaponorder_byimpulse) + strunzone(weaponorder_byimpulse); + + weaponorder_bypriority = strzone(autocvar_cl_weaponpriority); + weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority)))); + weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " "); + + weapon_cnt = 0; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + setself(get_weaponinfo(i)); + if(self.impulse >= 0) + { + weaponorder[weapon_cnt] = self; + ++weapon_cnt; + } + } + for(i = weapon_cnt; i < Weapons_MAX; ++i) + weaponorder[i] = world; + heapsort(weapon_cnt, weaponorder_swap, weaponorder_cmp, world); + + weaponorder_cmp_str = string_null; + } + + if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime) + complain_weapon = 0; + + if(autocvar__hud_configure) + { + if(!weapons_stat) + for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5)) + weapons_stat |= WepSet_FromWeapon(i); + + #if 0 + /// debug code + if(cvar("wep_add")) + { + weapons_stat = '0 0 0'; + float countw = 1 + floor((floor(time * cvar("wep_add"))) % (Weapons_COUNT - 1)); + for(i = WEP_FIRST; i <= countw; ++i) + weapons_stat |= WepSet_FromWeapon(i); + } + #endif + } + + // determine which weapons are going to be shown + if (autocvar_hud_panel_weapons_onlyowned) + { + if(autocvar__hud_configure) + { + if(menu_enabled != 2) + HUD_Panel_DrawBg(1); // also draw the bg of the entire panel + } + + // do we own this weapon? + weapon_count = 0; + for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon)) + ++weapon_count; + + + // might as well commit suicide now, no reason to live ;) + if (weapon_count == 0) + return; + + vector old_panel_size = panel_size; + vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding; + + // get the all-weapons layout + int nHidden = 0; + WepSet weapons_stat = WepSet_GetFromStat(); + for (int i = WEP_FIRST; i <= WEP_LAST; ++i) { + WepSet weapons_wep = WepSet_FromWeapon(i); + if (weapons_stat & weapons_wep) continue; + Weapon w = get_weaponinfo(i); + if (w.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1; + } + vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, padded_panel_size, aspect); + columns = table_size.x; + rows = table_size.y; + weapon_size.x = padded_panel_size.x / columns; + weapon_size.y = padded_panel_size.y / rows; + + // NOTE: although weapons should aways look the same even if onlyowned is enabled, + // we enlarge them a bit when possible to better match the desired aspect ratio + if(padded_panel_size.x / padded_panel_size.y < aspect) + { + // maximum number of rows that allows to display items with the desired aspect ratio + int max_rows = floor(padded_panel_size.y / (weapon_size.x / aspect)); + columns = min(columns, ceil(weapon_count / max_rows)); + rows = ceil(weapon_count / columns); + weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect); + weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y); + vertical_order = false; + } + else + { + int max_columns = floor(padded_panel_size.x / (weapon_size.y * aspect)); + rows = min(rows, ceil(weapon_count / max_columns)); + columns = ceil(weapon_count / rows); + weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y); + weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect); + vertical_order = true; + } + + // reduce size of the panel + panel_size.x = columns * weapon_size.x; + panel_size.y = rows * weapon_size.y; + panel_size += '2 2 0' * panel_bg_padding; + + // center the resized panel, or snap it to the screen edge when close enough + if(panel_pos.x > vid_conwidth * 0.001) + { + if(panel_pos.x + old_panel_size.x > vid_conwidth * 0.999) + panel_pos.x += old_panel_size.x - panel_size.x; + else + panel_pos.x += (old_panel_size.x - panel_size.x) / 2; + } + else if(old_panel_size.x > vid_conwidth * 0.999) + panel_pos.x += (old_panel_size.x - panel_size.x) / 2; + + if(panel_pos.y > vid_conheight * 0.001) + { + if(panel_pos.y + old_panel_size.y > vid_conheight * 0.999) + panel_pos.y += old_panel_size.y - panel_size.y; + else + panel_pos.y += (old_panel_size.y - panel_size.y) / 2; + } + else if(old_panel_size.y > vid_conheight * 0.999) + panel_pos.y += (old_panel_size.y - panel_size.y) / 2; + } + else + weapon_count = (Weapons_COUNT - 1); + + // animation for fading in/out the panel respectively when not in use + if(!autocvar__hud_configure) + { + if (timeout && time >= weapontime + timeout) // apply timeout effect if needed + { + f = bound(0, (time - (weapontime + timeout)) / timeout_effect_length, 1); + + // fade the panel alpha + if(autocvar_hud_panel_weapons_timeout_effect == 1) + { + panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * f + (1 - f)); + panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * f + (1 - f)); + } + else if(autocvar_hud_panel_weapons_timeout_effect == 3) + { + panel_bg_alpha *= (1 - f); + panel_fg_alpha *= (1 - f); + } + + // move the panel off the screen + if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3) + { + f *= f; // for a cooler movement + center.x = panel_pos.x + panel_size.x/2; + center.y = panel_pos.y + panel_size.y/2; + screen_ar = vid_conwidth/vid_conheight; + if (center.x/center.y < screen_ar) //bottom left + { + if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom + panel_pos.y += f * (vid_conheight - panel_pos.y); + else //left + panel_pos.x -= f * (panel_pos.x + panel_size.x); + } + else //top right + { + if ((vid_conwidth - center.x)/center.y < screen_ar) //right + panel_pos.x += f * (vid_conwidth - panel_pos.x); + else //top + panel_pos.y -= f * (panel_pos.y + panel_size.y); + } + if(f == 1) + center.x = -1; // mark the panel as off screen + } + weaponprevtime = time - (1 - f) * timein_effect_length; + } + else if (timeout && time < weaponprevtime + timein_effect_length) // apply timein effect if needed + { + f = bound(0, (time - weaponprevtime) / timein_effect_length, 1); + + // fade the panel alpha + if(autocvar_hud_panel_weapons_timeout_effect == 1) + { + panel_bg_alpha *= (autocvar_hud_panel_weapons_timeout_fadebgmin * (1 - f) + f); + panel_fg_alpha *= (autocvar_hud_panel_weapons_timeout_fadefgmin * (1 - f) + f); + } + else if(autocvar_hud_panel_weapons_timeout_effect == 3) + { + panel_bg_alpha *= (f); + panel_fg_alpha *= (f); + } + + // move the panel back on screen + if (autocvar_hud_panel_weapons_timeout_effect == 2 || autocvar_hud_panel_weapons_timeout_effect == 3) + { + f *= f; // for a cooler movement + f = 1 - f; + center.x = panel_pos.x + panel_size.x/2; + center.y = panel_pos.y + panel_size.y/2; + screen_ar = vid_conwidth/vid_conheight; + if (center.x/center.y < screen_ar) //bottom left + { + if ((vid_conwidth - center.x)/center.y < screen_ar) //bottom + panel_pos.y += f * (vid_conheight - panel_pos.y); + else //left + panel_pos.x -= f * (panel_pos.x + panel_size.x); + } + else //top right + { + if ((vid_conwidth - center.x)/center.y < screen_ar) //right + panel_pos.x += f * (vid_conwidth - panel_pos.x); + else //top + panel_pos.y -= f * (panel_pos.y + panel_size.y); + } + } + } + } + + // draw the background, then change the virtual size of it to better fit other items inside + HUD_Panel_DrawBg(1); + + if(center.x == -1) + return; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + // after the sizing and animations are done, update the other values + + if(!rows) // if rows is > 0 onlyowned code has already updated these vars + { + vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1), panel_size, aspect); + columns = table_size.x; + rows = table_size.y; + weapon_size.x = panel_size.x / columns; + weapon_size.y = panel_size.y / rows; + vertical_order = (panel_size.x / panel_size.y >= aspect); + } + + // calculate position/size for visual bar displaying ammount of ammo status + if (autocvar_hud_panel_weapons_ammo) + { + ammo_color = stov(autocvar_hud_panel_weapons_ammo_color); + ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha; + + if(weapon_size.x/weapon_size.y > aspect) + { + barsize.x = aspect * weapon_size.y; + barsize.y = weapon_size.y; + baroffset.x = (weapon_size.x - barsize.x) / 2; + } + else + { + barsize.y = 1/aspect * weapon_size.x; + barsize.x = weapon_size.x; + baroffset.y = (weapon_size.y - barsize.y) / 2; + } + } + if(autocvar_hud_panel_weapons_accuracy) + Accuracy_LoadColors(); + + // draw items + row = column = 0; + vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1); + vector noncurrent_pos = '0 0 0'; + vector noncurrent_size = weapon_size * bound(0, autocvar_hud_panel_weapons_noncurrent_scale, 1); + float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1); + bool isCurrent; + + for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + { + // retrieve information about the current weapon to be drawn + setself(weaponorder[i]); + weapon_id = self.impulse; + isCurrent = (self.weapon == switchweapon); + + // skip if this weapon doesn't exist + if(!self || weapon_id < 0) { continue; } + + // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon + if(autocvar_hud_panel_weapons_onlyowned) + if (!((weapons_stat & WepSet_FromWeapon(self.weapon)) || (self.weapon == complain_weapon))) + continue; + + // figure out the drawing position of weapon + weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y); + noncurrent_pos.x = weapon_pos.x + (weapon_size.x - noncurrent_size.x) / 2; + noncurrent_pos.y = weapon_pos.y + (weapon_size.y - noncurrent_size.y) / 2; + + // draw background behind currently selected weapon + if(isCurrent) + drawpic_aspect_skin(weapon_pos, "weapon_current_bg", weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + // draw the weapon accuracy + if(autocvar_hud_panel_weapons_accuracy) + { + float panel_weapon_accuracy = weapon_accuracy[self.weapon-WEP_FIRST]; + if(panel_weapon_accuracy >= 0) + { + color = Accuracy_GetColor(panel_weapon_accuracy); + drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL); + } + } + + // drawing all the weapon items + if(weapons_stat & WepSet_FromWeapon(self.weapon)) + { + // draw the weapon image + if(isCurrent) + drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + else + drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '1 1 1', noncurrent_alpha, DRAWFLAG_NORMAL); + + // draw weapon label string + switch(autocvar_hud_panel_weapons_label) + { + case 1: // weapon number + drawstring(weapon_pos, ftos(weapon_id), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + break; + + case 2: // bind + drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("weapon_group_", ftos(weapon_id))), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + break; + + case 3: // weapon name + drawstring(weapon_pos, strtolower(self.m_name), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + break; + + default: // nothing + break; + } + + // draw ammo status bar + if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none)) + { + float ammo_full; + a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have? + + if(a > 0) + { + switch(self.ammo_field) + { + case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break; + case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break; + case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break; + case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break; + case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break; + case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break; + default: ammo_full = 60; + } + + drawsetcliparea( + weapon_pos.x + baroffset.x, + weapon_pos.y + baroffset.y, + barsize.x * bound(0, a/ammo_full, 1), + barsize.y + ); + + drawpic_aspect_skin( + weapon_pos, + "weapon_ammo", + weapon_size, + ammo_color, + ammo_alpha, + DRAWFLAG_NORMAL + ); + + drawresetcliparea(); + } + } + } + else // draw a "ghost weapon icon" if you don't have the weapon + { + drawpic_aspect_skin(noncurrent_pos, self.model2, noncurrent_size, '0.2 0.2 0.2', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL); + } + + // draw the complain message + if(self.weapon == complain_weapon) + { + if(fadetime) + a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1)); + else + a = ((complain_weapon_time + when > time) ? 1 : 0); + + string s; + if(complain_weapon_type == 0) { + s = _("Out of ammo"); + color = stov(autocvar_hud_panel_weapons_complainbubble_color_outofammo); + } + else if(complain_weapon_type == 1) { + s = _("Don't have"); + color = stov(autocvar_hud_panel_weapons_complainbubble_color_donthave); + } + else { + s = _("Unavailable"); + color = stov(autocvar_hud_panel_weapons_complainbubble_color_unavailable); + } + float padding = autocvar_hud_panel_weapons_complainbubble_padding; + drawpic_aspect_skin(weapon_pos + '1 1 0' * padding, "weapon_complainbubble", weapon_size - '2 2 0' * padding, color, a * panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL); + } + + #if 0 + /// debug code + if(!autocvar_hud_panel_weapons_onlyowned) + { + drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL); + drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + #endif + + // continue with new position for the next weapon + if(vertical_order) + { + ++column; + if(column >= columns) + { + column = 0; + ++row; + } + } + else + { + ++row; + if(row >= rows) + { + row = 0; + ++column; + } + } + } +} diff --git a/qcsrc/client/hud_config.qc b/qcsrc/client/hud_config.qc deleted file mode 100644 index 48b3c821d2..0000000000 --- a/qcsrc/client/hud_config.qc +++ /dev/null @@ -1,1297 +0,0 @@ -#include "hud_config.qh" - -#include "hud.qh" - -#define HUD_Write(s) fputs(fh, s) -// q: quoted, n: not quoted -#define HUD_Write_Cvar_n(cvar) HUD_Write(strcat("seta ", cvar, " ", cvar_string(cvar), "\n")) -#define HUD_Write_Cvar_q(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n")) -#define HUD_Write_PanelCvar_n(cvar_suf) HUD_Write_Cvar_n(strcat("hud_panel_", panel.panel_name, cvar_suf)) -#define HUD_Write_PanelCvar_q(cvar_suf) HUD_Write_Cvar_q(strcat("hud_panel_", panel.panel_name, cvar_suf)) -// Save the config -void HUD_Panel_ExportCfg(string cfgname) -{ - float fh; - string filename = strcat("hud_", autocvar_hud_skin, "_", cfgname, ".cfg"); - fh = fopen(filename, FILE_WRITE); - if(fh >= 0) - { - HUD_Write_Cvar_q("hud_skin"); - HUD_Write_Cvar_q("hud_panel_bg"); - HUD_Write_Cvar_q("hud_panel_bg_color"); - HUD_Write_Cvar_q("hud_panel_bg_color_team"); - HUD_Write_Cvar_q("hud_panel_bg_alpha"); - HUD_Write_Cvar_q("hud_panel_bg_border"); - HUD_Write_Cvar_q("hud_panel_bg_padding"); - HUD_Write_Cvar_q("hud_panel_fg_alpha"); - HUD_Write("\n"); - - HUD_Write_Cvar_q("hud_dock"); - HUD_Write_Cvar_q("hud_dock_color"); - HUD_Write_Cvar_q("hud_dock_color_team"); - HUD_Write_Cvar_q("hud_dock_alpha"); - HUD_Write("\n"); - - HUD_Write_Cvar_q("hud_progressbar_alpha"); - HUD_Write_Cvar_q("hud_progressbar_strength_color"); - HUD_Write_Cvar_q("hud_progressbar_shield_color"); - HUD_Write_Cvar_q("hud_progressbar_health_color"); - HUD_Write_Cvar_q("hud_progressbar_armor_color"); - HUD_Write_Cvar_q("hud_progressbar_fuel_color"); - HUD_Write_Cvar_q("hud_progressbar_nexball_color"); - HUD_Write_Cvar_q("hud_progressbar_speed_color"); - HUD_Write_Cvar_q("hud_progressbar_acceleration_color"); - HUD_Write_Cvar_q("hud_progressbar_acceleration_neg_color"); - HUD_Write("\n"); - - HUD_Write_Cvar_q("_hud_panelorder"); - HUD_Write("\n"); - - HUD_Write_Cvar_q("hud_configure_grid"); - HUD_Write_Cvar_q("hud_configure_grid_xsize"); - HUD_Write_Cvar_q("hud_configure_grid_ysize"); - HUD_Write("\n"); - - // common cvars for all panels - for (int i = 0; i < hud_panels_COUNT; ++i) - { - panel = hud_panels[i]; - - HUD_Write_PanelCvar_n(""); - HUD_Write_PanelCvar_q("_pos"); - HUD_Write_PanelCvar_q("_size"); - HUD_Write_PanelCvar_q("_bg"); - HUD_Write_PanelCvar_q("_bg_color"); - HUD_Write_PanelCvar_q("_bg_color_team"); - HUD_Write_PanelCvar_q("_bg_alpha"); - HUD_Write_PanelCvar_q("_bg_border"); - HUD_Write_PanelCvar_q("_bg_padding"); - switch(panel) { - case HUD_PANEL_WEAPONS: - HUD_Write_PanelCvar_q("_accuracy"); - HUD_Write_PanelCvar_q("_label"); - HUD_Write_PanelCvar_q("_label_scale"); - HUD_Write_PanelCvar_q("_complainbubble"); - HUD_Write_PanelCvar_q("_complainbubble_padding"); - HUD_Write_PanelCvar_q("_complainbubble_time"); - HUD_Write_PanelCvar_q("_complainbubble_fadetime"); - HUD_Write_PanelCvar_q("_complainbubble_color_outofammo"); - HUD_Write_PanelCvar_q("_complainbubble_color_donthave"); - HUD_Write_PanelCvar_q("_complainbubble_color_unavailable"); - HUD_Write_PanelCvar_q("_ammo"); - HUD_Write_PanelCvar_q("_ammo_color"); - HUD_Write_PanelCvar_q("_ammo_alpha"); - HUD_Write_PanelCvar_q("_aspect"); - HUD_Write_PanelCvar_q("_timeout"); - HUD_Write_PanelCvar_q("_timeout_effect"); - HUD_Write_PanelCvar_q("_timeout_fadebgmin"); - HUD_Write_PanelCvar_q("_timeout_fadefgmin"); - HUD_Write_PanelCvar_q("_timeout_speed_in"); - HUD_Write_PanelCvar_q("_timeout_speed_out"); - HUD_Write_PanelCvar_q("_onlyowned"); - HUD_Write_PanelCvar_q("_noncurrent_alpha"); - HUD_Write_PanelCvar_q("_noncurrent_scale"); - break; - case HUD_PANEL_AMMO: - HUD_Write_PanelCvar_q("_onlycurrent"); - HUD_Write_PanelCvar_q("_noncurrent_alpha"); - HUD_Write_PanelCvar_q("_noncurrent_scale"); - HUD_Write_PanelCvar_q("_iconalign"); - HUD_Write_PanelCvar_q("_progressbar"); - HUD_Write_PanelCvar_q("_progressbar_name"); - HUD_Write_PanelCvar_q("_progressbar_xoffset"); - HUD_Write_PanelCvar_q("_text"); - break; - case HUD_PANEL_POWERUPS: - HUD_Write_PanelCvar_q("_iconalign"); - HUD_Write_PanelCvar_q("_baralign"); - HUD_Write_PanelCvar_q("_progressbar"); - HUD_Write_PanelCvar_q("_text"); - break; - case HUD_PANEL_HEALTHARMOR: - HUD_Write_PanelCvar_q("_flip"); - HUD_Write_PanelCvar_q("_iconalign"); - HUD_Write_PanelCvar_q("_baralign"); - HUD_Write_PanelCvar_q("_progressbar"); - HUD_Write_PanelCvar_q("_progressbar_health"); - HUD_Write_PanelCvar_q("_progressbar_armor"); - HUD_Write_PanelCvar_q("_progressbar_gfx"); - HUD_Write_PanelCvar_q("_progressbar_gfx_smooth"); - HUD_Write_PanelCvar_q("_text"); - break; - case HUD_PANEL_NOTIFY: - HUD_Write_PanelCvar_q("_flip"); - HUD_Write_PanelCvar_q("_fontsize"); - HUD_Write_PanelCvar_q("_time"); - HUD_Write_PanelCvar_q("_fadetime"); - HUD_Write_PanelCvar_q("_icon_aspect"); - break; - case HUD_PANEL_TIMER: - HUD_Write_PanelCvar_q("_increment"); - break; - case HUD_PANEL_RADAR: - HUD_Write_PanelCvar_q("_foreground_alpha"); - HUD_Write_PanelCvar_q("_rotation"); - HUD_Write_PanelCvar_q("_zoommode"); - HUD_Write_PanelCvar_q("_scale"); - HUD_Write_PanelCvar_q("_maximized_scale"); - HUD_Write_PanelCvar_q("_maximized_size"); - HUD_Write_PanelCvar_q("_maximized_rotation"); - HUD_Write_PanelCvar_q("_maximized_zoommode"); - break; - case HUD_PANEL_SCORE: - HUD_Write_PanelCvar_q("_rankings"); - break; - case HUD_PANEL_VOTE: - HUD_Write_PanelCvar_q("_alreadyvoted_alpha"); - break; - case HUD_PANEL_MODICONS: - HUD_Write_PanelCvar_q("_ca_layout"); - HUD_Write_PanelCvar_q("_dom_layout"); - HUD_Write_PanelCvar_q("_freezetag_layout"); - break; - case HUD_PANEL_PRESSEDKEYS: - HUD_Write_PanelCvar_q("_aspect"); - HUD_Write_PanelCvar_q("_attack"); - break; - case HUD_PANEL_ENGINEINFO: - HUD_Write_PanelCvar_q("_framecounter_time"); - HUD_Write_PanelCvar_q("_framecounter_decimals"); - break; - case HUD_PANEL_INFOMESSAGES: - HUD_Write_PanelCvar_q("_flip"); - break; - case HUD_PANEL_PHYSICS: - HUD_Write_PanelCvar_q("_speed_unit"); - HUD_Write_PanelCvar_q("_speed_unit_show"); - HUD_Write_PanelCvar_q("_speed_max"); - HUD_Write_PanelCvar_q("_speed_vertical"); - HUD_Write_PanelCvar_q("_topspeed"); - HUD_Write_PanelCvar_q("_topspeed_time"); - HUD_Write_PanelCvar_q("_acceleration_max"); - HUD_Write_PanelCvar_q("_acceleration_vertical"); - HUD_Write_PanelCvar_q("_flip"); - HUD_Write_PanelCvar_q("_baralign"); - HUD_Write_PanelCvar_q("_progressbar"); - HUD_Write_PanelCvar_q("_progressbar_acceleration_mode"); - HUD_Write_PanelCvar_q("_progressbar_acceleration_scale"); - HUD_Write_PanelCvar_q("_progressbar_acceleration_nonlinear"); - HUD_Write_PanelCvar_q("_text"); - HUD_Write_PanelCvar_q("_text_scale"); - break; - case HUD_PANEL_CENTERPRINT: - HUD_Write_PanelCvar_q("_align"); - HUD_Write_PanelCvar_q("_flip"); - HUD_Write_PanelCvar_q("_fontscale"); - HUD_Write_PanelCvar_q("_time"); - HUD_Write_PanelCvar_q("_fade_in"); - HUD_Write_PanelCvar_q("_fade_out"); - HUD_Write_PanelCvar_q("_fade_subsequent"); - HUD_Write_PanelCvar_q("_fade_subsequent_passone"); - HUD_Write_PanelCvar_q("_fade_subsequent_passone_minalpha"); - HUD_Write_PanelCvar_q("_fade_subsequent_passtwo"); - HUD_Write_PanelCvar_q("_fade_subsequent_passtwo_minalpha"); - HUD_Write_PanelCvar_q("_fade_subsequent_minfontsize"); - HUD_Write_PanelCvar_q("_fade_minfontsize"); - break; - case HUD_PANEL_ITEMSTIME: - HUD_Write_PanelCvar_q("_iconalign"); - HUD_Write_PanelCvar_q("_progressbar"); - HUD_Write_PanelCvar_q("_progressbar_name"); - HUD_Write_PanelCvar_q("_progressbar_reduced"); - HUD_Write_PanelCvar_q("_text"); - HUD_Write_PanelCvar_q("_ratio"); - HUD_Write_PanelCvar_q("_dynamicsize"); - case HUD_PANEL_QUICKMENU: - HUD_Write_PanelCvar_q("_align"); - break; - } - HUD_Write("\n"); - } - HUD_Write("menu_sync\n"); // force the menu to reread the cvars, so that the dialogs are updated - - LOG_INFOF(_("^2Successfully exported to %s! (Note: It's saved in data/data/)\n"), filename); - fclose(fh); - } - else - LOG_INFOF(_("^1Couldn't write to %s\n"), filename); -} - -void HUD_Configure_Exit_Force() -{ - if (menu_enabled) - { - menu_enabled = 0; - localcmd("togglemenu\n"); - } - cvar_set("_hud_configure", "0"); -} - -// check if move will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector -vector HUD_Panel_CheckMove(vector myPos, vector mySize) -{ - vector myCenter, targCenter; - vector myTarget = myPos; - int i; - for (i = 0; i < hud_panels_COUNT; ++i) { - panel = hud_panels[i]; - if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; - if(panel == highlightedPanel) continue; - HUD_Panel_UpdatePosSize(); - if(!panel_enabled) continue; - - panel_pos -= '1 1 0' * panel_bg_border; - panel_size += '2 2 0' * panel_bg_border; - - if(myPos.y + mySize.y < panel_pos.y) - continue; - if(myPos.y > panel_pos.y + panel_size.y) - continue; - - if(myPos.x + mySize.x < panel_pos.x) - continue; - if(myPos.x > panel_pos.x + panel_size.x) - continue; - - // OK, there IS a collision. - - myCenter.x = myPos.x + 0.5 * mySize.x; - myCenter.y = myPos.y + 0.5 * mySize.y; - - targCenter.x = panel_pos.x + 0.5 * panel_size.x; - targCenter.y = panel_pos.y + 0.5 * panel_size.y; - - if(myCenter.x < targCenter.x && myCenter.y < targCenter.y) // top left (of the target panel) - { - if(myPos.x + mySize.x - panel_pos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side - myTarget.x = panel_pos.x - mySize.x; - else // push it upwards - myTarget.y = panel_pos.y - mySize.y; - } - else if(myCenter.x > targCenter.x && myCenter.y < targCenter.y) // top right - { - if(panel_pos.x + panel_size.x - myPos.x < myPos.y + mySize.y - panel_pos.y) // push it to the side - myTarget.x = panel_pos.x + panel_size.x; - else // push it upwards - myTarget.y = panel_pos.y - mySize.y; - } - else if(myCenter.x < targCenter.x && myCenter.y > targCenter.y) // bottom left - { - if(myPos.x + mySize.x - panel_pos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side - myTarget.x = panel_pos.x - mySize.x; - else // push it downwards - myTarget.y = panel_pos.y + panel_size.y; - } - else if(myCenter.x > targCenter.x && myCenter.y > targCenter.y) // bottom right - { - if(panel_pos.x + panel_size.x - myPos.x < panel_pos.y + panel_size.y - myPos.y) // push it to the side - myTarget.x = panel_pos.x + panel_size.x; - else // push it downwards - myTarget.y = panel_pos.y + panel_size.y; - } - //if(cvar("hud_configure_checkcollisions_debug")) - //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL); - } - - return myTarget; -} - -void HUD_Panel_SetPos(vector pos) -{ - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - vector mySize; - mySize = panel_size; - - //if(cvar("hud_configure_checkcollisions_debug")) - //drawfill(pos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL); - - if(autocvar_hud_configure_grid) - { - pos.x = floor((pos.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x; - pos.y = floor((pos.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y; - } - - if(hud_configure_checkcollisions) - pos = HUD_Panel_CheckMove(pos, mySize); - - pos.x = bound(0, pos.x, vid_conwidth - mySize.x); - pos.y = bound(0, pos.y, vid_conheight - mySize.y); - - string s; - s = strcat(ftos(pos.x/vid_conwidth), " ", ftos(pos.y/vid_conheight)); - - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s); -} - -// check if resize will result in panel being moved into another panel. If so, return snapped vector, otherwise return the given vector -vector HUD_Panel_CheckResize(vector mySize, vector resizeorigin) { - vector targEndPos; - vector dist; - float ratio = mySize.x/mySize.y; - int i; - for (i = 0; i < hud_panels_COUNT; ++i) { - panel = hud_panels[i]; - if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; - if(panel == highlightedPanel) continue; - HUD_Panel_UpdatePosSize(); - if(!panel_enabled) continue; - - panel_pos -= '1 1 0' * panel_bg_border; - panel_size += '2 2 0' * panel_bg_border; - - targEndPos = panel_pos + panel_size; - - // resizeorigin is WITHIN target panel, just abort any collision testing against that particular panel to produce expected behaviour! - if(resizeorigin.x > panel_pos.x && resizeorigin.x < targEndPos.x && resizeorigin.y > panel_pos.y && resizeorigin.y < targEndPos.y) - continue; - - if (resizeCorner == 1) - { - // check if this panel is on our way - if (resizeorigin.x <= panel_pos.x) - continue; - if (resizeorigin.y <= panel_pos.y) - continue; - if (targEndPos.x <= resizeorigin.x - mySize.x) - continue; - if (targEndPos.y <= resizeorigin.y - mySize.y) - continue; - - // there is a collision: - // detect which side of the panel we are facing is actually limiting the resizing - // (which side the resize direction finds for first) and reduce the size up to there - // - // dist is the distance between resizeorigin and the "analogous" point of the panel - // in this case between resizeorigin (bottom-right point) and the bottom-right point of the panel - dist.x = resizeorigin.x - targEndPos.x; - dist.y = resizeorigin.y - targEndPos.y; - if (dist.y <= 0 || dist.x / dist.y > ratio) - mySize.x = min(mySize.x, dist.x); - else - mySize.y = min(mySize.y, dist.y); - } - else if (resizeCorner == 2) - { - if (resizeorigin.x >= targEndPos.x) - continue; - if (resizeorigin.y <= panel_pos.y) - continue; - if (panel_pos.x >= resizeorigin.x + mySize.x) - continue; - if (targEndPos.y <= resizeorigin.y - mySize.y) - continue; - - dist.x = panel_pos.x - resizeorigin.x; - dist.y = resizeorigin.y - targEndPos.y; - if (dist.y <= 0 || dist.x / dist.y > ratio) - mySize.x = min(mySize.x, dist.x); - else - mySize.y = min(mySize.y, dist.y); - } - else if (resizeCorner == 3) - { - if (resizeorigin.x <= panel_pos.x) - continue; - if (resizeorigin.y >= targEndPos.y) - continue; - if (targEndPos.x <= resizeorigin.x - mySize.x) - continue; - if (panel_pos.y >= resizeorigin.y + mySize.y) - continue; - - dist.x = resizeorigin.x - targEndPos.x; - dist.y = panel_pos.y - resizeorigin.y; - if (dist.y <= 0 || dist.x / dist.y > ratio) - mySize.x = min(mySize.x, dist.x); - else - mySize.y = min(mySize.y, dist.y); - } - else if (resizeCorner == 4) - { - if (resizeorigin.x >= targEndPos.x) - continue; - if (resizeorigin.y >= targEndPos.y) - continue; - if (panel_pos.x >= resizeorigin.x + mySize.x) - continue; - if (panel_pos.y >= resizeorigin.y + mySize.y) - continue; - - dist.x = panel_pos.x - resizeorigin.x; - dist.y = panel_pos.y - resizeorigin.y; - if (dist.y <= 0 || dist.x / dist.y > ratio) - mySize.x = min(mySize.x, dist.x); - else - mySize.y = min(mySize.y, dist.y); - } - //if(cvar("hud_configure_checkcollisions_debug")) - //drawfill(panel_pos, panel_size, '1 1 0', .3, DRAWFLAG_NORMAL); - } - - return mySize; -} - -void HUD_Panel_SetPosSize(vector mySize) -{ - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - vector resizeorigin = panel_click_resizeorigin; - vector myPos; - - // minimum panel size cap - mySize.x = max(0.025 * vid_conwidth, mySize.x); - mySize.y = max(0.025 * vid_conheight, mySize.y); - - if(highlightedPanel == HUD_PANEL(CHAT)) // some panels have their own restrictions, like the chat panel (which actually only moves the engine chat print around). Looks bad if it's too small. - { - mySize.x = max(17 * autocvar_con_chatsize, mySize.x); - mySize.y = max(2 * autocvar_con_chatsize + 2 * panel_bg_padding, mySize.y); - } - - // collision testing| - // -----------------+ - - // we need to know pos at this stage, but it might still change later if we hit a screen edge/other panel (?) - if(resizeCorner == 1) { - myPos.x = resizeorigin.x - mySize.x; - myPos.y = resizeorigin.y - mySize.y; - } else if(resizeCorner == 2) { - myPos.x = resizeorigin.x; - myPos.y = resizeorigin.y - mySize.y; - } else if(resizeCorner == 3) { - myPos.x = resizeorigin.x - mySize.x; - myPos.y = resizeorigin.y; - } else { // resizeCorner == 4 - myPos.x = resizeorigin.x; - myPos.y = resizeorigin.y; - } - - // left/top screen edges - if(myPos.x < 0) - mySize.x = mySize.x + myPos.x; - if(myPos.y < 0) - mySize.y = mySize.y + myPos.y; - - // bottom/right screen edges - if(myPos.x + mySize.x > vid_conwidth) - mySize.x = vid_conwidth - myPos.x; - if(myPos.y + mySize.y > vid_conheight) - mySize.y = vid_conheight - myPos.y; - - //if(cvar("hud_configure_checkcollisions_debug")) - //drawfill(myPos, mySize, '1 1 1', .2, DRAWFLAG_NORMAL); - - // before checkresize, otherwise panel can be snapped partially inside another panel or panel aspect ratio can be broken - if(autocvar_hud_configure_grid) - { - mySize.x = floor((mySize.x/vid_conwidth)/hud_configure_gridSize.x + 0.5) * hud_configure_realGridSize.x; - mySize.y = floor((mySize.y/vid_conheight)/hud_configure_gridSize.y + 0.5) * hud_configure_realGridSize.y; - } - - if(hud_configure_checkcollisions) - mySize = HUD_Panel_CheckResize(mySize, resizeorigin); - - // minimum panel size cap, do this once more so we NEVER EVER EVER have a panel smaller than this, JUST IN CASE above code still makes the panel eg negative (impossible to resize back without changing cvars manually then) - mySize.x = max(0.025 * vid_conwidth, mySize.x); - mySize.y = max(0.025 * vid_conheight, mySize.y); - - // do another pos check, as size might have changed by now - if(resizeCorner == 1) { - myPos.x = resizeorigin.x - mySize.x; - myPos.y = resizeorigin.y - mySize.y; - } else if(resizeCorner == 2) { - myPos.x = resizeorigin.x; - myPos.y = resizeorigin.y - mySize.y; - } else if(resizeCorner == 3) { - myPos.x = resizeorigin.x - mySize.x; - myPos.y = resizeorigin.y; - } else { // resizeCorner == 4 - myPos.x = resizeorigin.x; - myPos.y = resizeorigin.y; - } - - //if(cvar("hud_configure_checkcollisions_debug")) - //drawfill(myPos, mySize, '0 1 0', .3, DRAWFLAG_NORMAL); - - string s; - s = strcat(ftos(mySize.x/vid_conwidth), " ", ftos(mySize.y/vid_conheight)); - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s); - - s = strcat(ftos(myPos.x/vid_conwidth), " ", ftos(myPos.y/vid_conheight)); - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_pos"), s); -} - -float pressed_key_time; -vector highlightedPanel_initial_pos, highlightedPanel_initial_size; -void HUD_Panel_Arrow_Action(float nPrimary) -{ - if(!highlightedPanel) - return; - - hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions); - - float step; - if(autocvar_hud_configure_grid) - { - if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW) - { - if (hudShiftState & S_SHIFT) - step = hud_configure_realGridSize.y; - else - step = 2 * hud_configure_realGridSize.y; - } - else - { - if (hudShiftState & S_SHIFT) - step = hud_configure_realGridSize.x; - else - step = 2 * hud_configure_realGridSize.x; - } - } - else - { - if (nPrimary == K_UPARROW || nPrimary == K_DOWNARROW) - step = vid_conheight; - else - step = vid_conwidth; - if (hudShiftState & S_SHIFT) - step = (step / 256); // more precision - else - step = (step / 64) * (1 + 2 * (time - pressed_key_time)); - } - - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - - highlightedPanel_initial_pos = panel_pos; - highlightedPanel_initial_size = panel_size; - - if (hudShiftState & S_ALT) // resize - { - if(nPrimary == K_UPARROW) - resizeCorner = 1; - else if(nPrimary == K_RIGHTARROW) - resizeCorner = 2; - else if(nPrimary == K_LEFTARROW) - resizeCorner = 3; - else // if(nPrimary == K_DOWNARROW) - resizeCorner = 4; - - // ctrl+arrow reduces the size, instead of increasing it - // Note that ctrl disables collisions check too, but it's fine - // since we don't collide with anything reducing the size - if (hudShiftState & S_CTRL) { - step = -step; - resizeCorner = 5 - resizeCorner; - } - - vector mySize; - mySize = panel_size; - panel_click_resizeorigin = panel_pos; - if(resizeCorner == 1) { - panel_click_resizeorigin += mySize; - mySize.y += step; - } else if(resizeCorner == 2) { - panel_click_resizeorigin.y += mySize.y; - mySize.x += step; - } else if(resizeCorner == 3) { - panel_click_resizeorigin.x += mySize.x; - mySize.x += step; - } else { // resizeCorner == 4 - mySize.y += step; - } - HUD_Panel_SetPosSize(mySize); - } - else // move - { - vector pos; - pos = panel_pos; - if(nPrimary == K_UPARROW) - pos.y -= step; - else if(nPrimary == K_DOWNARROW) - pos.y += step; - else if(nPrimary == K_LEFTARROW) - pos.x -= step; - else // if(nPrimary == K_RIGHTARROW) - pos.x += step; - - HUD_Panel_SetPos(pos); - } - - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - - if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size) - { - // backup! - panel_pos_backup = highlightedPanel_initial_pos; - panel_size_backup = highlightedPanel_initial_size; - highlightedPanel_backup = highlightedPanel; - } -} - -void HUD_Panel_EnableMenu(); -entity tab_panels[hud_panels_MAX]; -entity tab_panel; -vector tab_panel_pos; -float tab_backward; -void HUD_Panel_FirstInDrawQ(float id); -void reset_tab_panels() -{ - int i; - for(i = 0; i < hud_panels_COUNT; ++i) - tab_panels[i] = world; -} -float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary) -{ - string s; - - if(bInputType == 2) - return false; - - if(!autocvar__hud_configure) - return false; - - if(bInputType == 3) - { - mousepos.x = nPrimary; - mousepos.y = nSecondary; - return true; - } - - // block any input while a menu dialog is fading - // don't block mousepos read as it leads to cursor jumps in the interaction with the menu - if(autocvar__menu_alpha) - { - hudShiftState = 0; - mouseClicked = 0; - return true; - } - - // allow console bind to work - string con_keys; - float keys; - con_keys = findkeysforcommand("toggleconsole", 0); - keys = tokenize(con_keys); // findkeysforcommand returns data for this - - bool hit_con_bind = false; - int i; - for (i = 0; i < keys; ++i) - { - if(nPrimary == stof(argv(i))) - hit_con_bind = true; - } - - if(bInputType == 0) { - if(nPrimary == K_ALT) hudShiftState |= S_ALT; - if(nPrimary == K_CTRL) hudShiftState |= S_CTRL; - if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT; - } - else if(bInputType == 1) { - if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT); - if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL); - if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT); - } - - if(nPrimary == K_CTRL) - { - if (bInputType == 1) //ctrl has been released - { - if (tab_panel) - { - //switch to selected panel - highlightedPanel = tab_panel; - highlightedAction = 0; - HUD_Panel_FirstInDrawQ(highlightedPanel.panel_id); - } - tab_panel = world; - reset_tab_panels(); - } - } - - if(nPrimary == K_MOUSE1) - { - if(bInputType == 0) // key pressed - mouseClicked |= S_MOUSE1; - else if(bInputType == 1) // key released - mouseClicked -= (mouseClicked & S_MOUSE1); - } - else if(nPrimary == K_MOUSE2) - { - if(bInputType == 0) // key pressed - mouseClicked |= S_MOUSE2; - else if(bInputType == 1) // key released - mouseClicked -= (mouseClicked & S_MOUSE2); - } - else if(nPrimary == K_ESCAPE) - { - if (bInputType == 1) - return true; - menu_enabled = 1; - localcmd("menu_showhudexit\n"); - } - else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL) - { - if (bInputType == 1) - return true; - if (!menu_enabled) - cvar_set("_hud_configure", "0"); - } - else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel - { - if (bInputType == 1 || mouseClicked) - return true; - - // FIXME minor bug: if a panel is highlighted, has the same pos_x and - // lays in the same level of another panel then the next consecutive - // CTRL TAB presses will reselect once more the highlighted panel - - entity starting_panel; - entity old_tab_panel = tab_panel; - if (!tab_panel) //first press of TAB - { - if (highlightedPanel) - { - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - } - else - panel_pos = '0 0 0'; - starting_panel = highlightedPanel; - tab_panel_pos = panel_pos; //to compute level - } - else - { - if ( ((!tab_backward) && (hudShiftState & S_SHIFT)) || (tab_backward && !(hudShiftState & S_SHIFT)) ) //tab direction changed? - reset_tab_panels(); - starting_panel = tab_panel; - } - tab_backward = (hudShiftState & S_SHIFT); - - float k, level = 0, start_posX; - vector candidate_pos = '0 0 0'; - const float LEVELS_NUM = 4; - float level_height = vid_conheight / LEVELS_NUM; -:find_tab_panel - level = floor(tab_panel_pos.y / level_height) * level_height; //starting level - candidate_pos.x = (!tab_backward) ? vid_conwidth : 0; - start_posX = tab_panel_pos.x; - tab_panel = world; - k=0; - while(++k) - { - for(i = 0; i < hud_panels_COUNT; ++i) - { - panel = hud_panels[i]; - if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) - continue; - if (panel == tab_panels[i] || panel == starting_panel) - continue; - HUD_Panel_UpdatePosSize(); - if (panel_pos.y >= level && (panel_pos.y - level) < level_height) - if ( ( !tab_backward && panel_pos.x >= start_posX && (panel_pos.x < candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y <= candidate_pos.y)) ) - || ( tab_backward && panel_pos.x <= start_posX && (panel_pos.x > candidate_pos.x || (panel_pos.x == candidate_pos.x && panel_pos.y >= candidate_pos.y)) ) ) - { - tab_panel = panel; - tab_panel_pos = candidate_pos = panel_pos; - } - } - if (tab_panel) - break; - if (k == LEVELS_NUM) //tab_panel not found - { - reset_tab_panels(); - if (!old_tab_panel) - { - tab_panel = world; - return true; - } - starting_panel = old_tab_panel; - old_tab_panel = world; - goto find_tab_panel; //u must find tab_panel! - } - if (!tab_backward) - { - level = (level + level_height) % vid_conheight; - start_posX = 0; - candidate_pos.x = vid_conwidth; - } - else - { - level = (level - level_height) % vid_conheight; - start_posX = vid_conwidth; - candidate_pos.x = 0; - } - } - - tab_panels[tab_panel.panel_id] = tab_panel; - } - else if(nPrimary == K_SPACE && hudShiftState & S_CTRL) // enable/disable highlighted panel or dock - { - if (bInputType == 1 || mouseClicked) - return true; - - if (highlightedPanel) - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name)))); - else - cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : ""); - } - else if(nPrimary == 'c' && hudShiftState & S_CTRL) // copy highlighted panel size - { - if (bInputType == 1 || mouseClicked) - return true; - - if (highlightedPanel) - { - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - panel_size_copied = panel_size; - } - } - else if(nPrimary == 'v' && hudShiftState & S_CTRL) // past copied size on the highlighted panel - { - if (bInputType == 1 || mouseClicked) - return true; - - if (panel_size_copied == '0 0 0' || !highlightedPanel) - return true; - - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - - // reduce size if it'd go beyond screen boundaries - vector tmp_size = panel_size_copied; - if (panel_pos.x + panel_size_copied.x > vid_conwidth) - tmp_size.x = vid_conwidth - panel_pos.x; - if (panel_pos.y + panel_size_copied.y > vid_conheight) - tmp_size.y = vid_conheight - panel_pos.y; - - if (panel_size == tmp_size) - return true; - - // backup first! - panel_pos_backup = panel_pos; - panel_size_backup = panel_size; - highlightedPanel_backup = highlightedPanel; - - s = strcat(ftos(tmp_size.x/vid_conwidth), " ", ftos(tmp_size.y/vid_conheight)); - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name, "_size"), s); - } - else if(nPrimary == 'z' && hudShiftState & S_CTRL) // undo last action - { - if (bInputType == 1 || mouseClicked) - return true; - //restore previous values - if (highlightedPanel_backup) - { - s = strcat(ftos(panel_pos_backup.x/vid_conwidth), " ", ftos(panel_pos_backup.y/vid_conheight)); - cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_pos"), s); - s = strcat(ftos(panel_size_backup.x/vid_conwidth), " ", ftos(panel_size_backup.y/vid_conheight)); - cvar_set(strcat("hud_panel_", highlightedPanel_backup.panel_name, "_size"), s); - highlightedPanel_backup = world; - } - } - else if(nPrimary == 's' && hudShiftState & S_CTRL) // save config - { - if (bInputType == 1 || mouseClicked) - return true; - localcmd("hud save myconfig\n"); - } - else if(nPrimary == K_UPARROW || nPrimary == K_DOWNARROW || nPrimary == K_LEFTARROW || nPrimary == K_RIGHTARROW) - { - if (bInputType == 1) - { - pressed_key_time = 0; - return true; - } - else if (pressed_key_time == 0) - pressed_key_time = time; - - if (!mouseClicked) - HUD_Panel_Arrow_Action(nPrimary); //move or resize panel - } - else if(nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER) - { - if (bInputType == 1) - return true; - if (highlightedPanel) - HUD_Panel_EnableMenu(); - } - else if(hit_con_bind || nPrimary == K_PAUSE) - return false; - - return true; -} - -float HUD_Panel_Check_Mouse_Pos(float allow_move) -{ - int i, j = 0; - while(j < hud_panels_COUNT) - { - i = panel_order[j]; - j += 1; - - panel = hud_panels[i]; - if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) continue; - HUD_Panel_UpdatePosSize(); - - float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize - - // move - if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) - { - return 1; - } - // resize from topleft border - else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) - { - return 2; - } - // resize from topright border - else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) - { - return 3; - } - // resize from bottomleft border - else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border) - { - return 3; - } - // resize from bottomright border - else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border) - { - return 2; - } - } - return 0; -} - -// move a panel to the beginning of the panel order array (which means it gets drawn last, on top of everything else) -void HUD_Panel_FirstInDrawQ(float id) -{ - int i; - int place = -1; - // find out where in the array our current id is, save into place - for(i = 0; i < hud_panels_COUNT; ++i) - { - if(panel_order[i] == id) - { - place = i; - break; - } - } - // place last if we didn't find a place for it yet (probably new panel, or screwed up cvar) - if(place == -1) - place = hud_panels_COUNT - 1; - - // move all ids up by one step in the array until "place" - for(i = place; i > 0; --i) - { - panel_order[i] = panel_order[i-1]; - } - // now save the new top id - panel_order[0] = id; - - // let's save them into the cvar by some strcat trickery - string s = ""; - for(i = 0; i < hud_panels_COUNT; ++i) - { - s = strcat(s, ftos(panel_order[i]), " "); - } - cvar_set("_hud_panelorder", s); - if(hud_panelorder_prev) - strunzone(hud_panelorder_prev); - hud_panelorder_prev = strzone(autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here -} - -void HUD_Panel_Highlight(float allow_move) -{ - int i, j = 0; - - while(j < hud_panels_COUNT) - { - i = panel_order[j]; - j += 1; - - panel = hud_panels[i]; - if(!(panel.panel_configflags & PANEL_CONFIG_MAIN)) - continue; - HUD_Panel_UpdatePosSize(); - - float border = max(8, panel_bg_border); // FORCED border so a small border size doesn't mean you can't resize - - // move - if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) - { - highlightedPanel = hud_panels[i]; - HUD_Panel_FirstInDrawQ(i); - highlightedAction = 1; - panel_click_distance = mousepos - panel_pos; - return; - } - // resize from topleft border - else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) - { - highlightedPanel = hud_panels[i]; - HUD_Panel_FirstInDrawQ(i); - highlightedAction = 2; - resizeCorner = 1; - panel_click_distance = mousepos - panel_pos; - panel_click_resizeorigin = panel_pos + panel_size; - return; - } - // resize from topright border - else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) - { - highlightedPanel = hud_panels[i]; - HUD_Panel_FirstInDrawQ(i); - highlightedAction = 2; - resizeCorner = 2; - panel_click_distance.x = panel_size.x - mousepos.x + panel_pos.x; - panel_click_distance.y = mousepos.y - panel_pos.y; - panel_click_resizeorigin = panel_pos + eY * panel_size.y; - return; - } - // resize from bottomleft border - else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border) - { - highlightedPanel = hud_panels[i]; - HUD_Panel_FirstInDrawQ(i); - highlightedAction = 2; - resizeCorner = 3; - panel_click_distance.x = mousepos.x - panel_pos.x; - panel_click_distance.y = panel_size.y - mousepos.y + panel_pos.y; - panel_click_resizeorigin = panel_pos + eX * panel_size.x; - return; - } - // resize from bottomright border - else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border) - { - highlightedPanel = hud_panels[i]; - HUD_Panel_FirstInDrawQ(i); - highlightedAction = 2; - resizeCorner = 4; - panel_click_distance = panel_size - mousepos + panel_pos; - panel_click_resizeorigin = panel_pos; - return; - } - } - highlightedPanel = world; - highlightedAction = 0; -} - -void HUD_Panel_EnableMenu() -{ - menu_enabled = 2; - localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n"); -} -float mouse_over_panel; -void HUD_Panel_Mouse() -{ - if(autocvar__menu_alpha == 1) - return; - - if (!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos.x = bound(0, mousepos.x, vid_conwidth); - mousepos.y = bound(0, mousepos.y, vid_conheight); - } - - if(mouseClicked) - { - if(prevMouseClicked == 0) - { - if (tab_panel) - { - //stop ctrl-tab selection - tab_panel = world; - reset_tab_panels(); - } - HUD_Panel_Highlight(mouseClicked & S_MOUSE1); // sets highlightedPanel, highlightedAction, panel_click_distance, panel_click_resizeorigin - // and calls HUD_Panel_UpdatePosSize() for the highlighted panel - if (highlightedPanel) - { - highlightedPanel_initial_pos = panel_pos; - highlightedPanel_initial_size = panel_size; - } - // doubleclick check - if ((mouseClicked & S_MOUSE1) && time - prevMouseClickedTime < 0.4 && highlightedPanel && prevMouseClickedPos == mousepos) - { - mouseClicked = 0; // to prevent spam, I guess. - HUD_Panel_EnableMenu(); - } - else - { - if (mouseClicked & S_MOUSE1) - { - prevMouseClickedTime = time; - prevMouseClickedPos = mousepos; - } - mouse_over_panel = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1); - } - } - else - { - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - } - - if (highlightedPanel) - { - drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL); - if (highlightedPanel_initial_pos != panel_pos || highlightedPanel_initial_size != panel_size) - { - hud_configure_checkcollisions = (!(hudShiftState & S_CTRL) && autocvar_hud_configure_checkcollisions); - // backup! - panel_pos_backup = highlightedPanel_initial_pos; - panel_size_backup = highlightedPanel_initial_size; - highlightedPanel_backup = highlightedPanel; - } - else - // in case the clicked panel is inside another panel and we aren't - // moving it, avoid the immediate "fix" of its position/size - // (often unwanted and hateful) by disabling collisions check - hud_configure_checkcollisions = false; - } - - if(highlightedAction == 1) - HUD_Panel_SetPos(mousepos - panel_click_distance); - else if(highlightedAction == 2) - { - vector mySize = '0 0 0'; - if(resizeCorner == 1) { - mySize.x = panel_click_resizeorigin.x - (mousepos.x - panel_click_distance.x); - mySize.y = panel_click_resizeorigin.y - (mousepos.y - panel_click_distance.y); - } else if(resizeCorner == 2) { - mySize.x = mousepos.x + panel_click_distance.x - panel_click_resizeorigin.x; - mySize.y = panel_click_distance.y + panel_click_resizeorigin.y - mousepos.y; - } else if(resizeCorner == 3) { - mySize.x = panel_click_resizeorigin.x + panel_click_distance.x - mousepos.x; - mySize.y = mousepos.y + panel_click_distance.y - panel_click_resizeorigin.y; - } else { // resizeCorner == 4 - mySize.x = mousepos.x - (panel_click_resizeorigin.x - panel_click_distance.x); - mySize.y = mousepos.y - (panel_click_resizeorigin.y - panel_click_distance.y); - } - HUD_Panel_SetPosSize(mySize); - } - } - else - { - if(prevMouseClicked) - highlightedAction = 0; - if(menu_enabled == 2) - mouse_over_panel = 0; - else - mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true); - if (mouse_over_panel && !tab_panel) - drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL); - } - // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel - const vector cursorsize = '32 32 0'; - float cursor_alpha = 1 - autocvar__menu_alpha; - - if(!mouse_over_panel) - drawpic(mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); - else if(mouse_over_panel == 1) - drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_move.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); - else if(mouse_over_panel == 2) - drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); - else - drawpic(mousepos - cursorsize * 0.5, strcat("gfx/menu/", autocvar_menu_skin, "/cursor_resize2.tga"), cursorsize, '1 1 1', cursor_alpha, DRAWFLAG_NORMAL); - - prevMouseClicked = mouseClicked; -} -void HUD_Configure_DrawGrid() -{ - float i; - if(autocvar_hud_configure_grid && autocvar_hud_configure_grid_alpha) - { - hud_configure_gridSize.x = bound(0.005, cvar("hud_configure_grid_xsize"), 0.2); - hud_configure_gridSize.y = bound(0.005, cvar("hud_configure_grid_ysize"), 0.2); - hud_configure_realGridSize.x = hud_configure_gridSize.x * vid_conwidth; - hud_configure_realGridSize.y = hud_configure_gridSize.y * vid_conheight; - vector s; - // x-axis - s = eX + eY * vid_conheight; - for(i = 1; i < 1/hud_configure_gridSize.x; ++i) - drawfill(eX * i * hud_configure_realGridSize.x, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL); - // y-axis - s = eY + eX * vid_conwidth; - for(i = 1; i < 1/hud_configure_gridSize.y; ++i) - drawfill(eY * i * hud_configure_realGridSize.y, s, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL); - } -} - -float _menu_alpha_prev; -void HUD_Configure_Frame() -{ - int i; - if(autocvar__hud_configure) - { - if(isdemo() || intermission == 2) - { - HUD_Configure_Exit_Force(); - return; - } - - if(!hud_configure_prev) - { - if(autocvar_hud_cursormode) - setcursormode(1); - hudShiftState = 0; - for(i = hud_panels_COUNT - 1; i >= 0; --i) - hud_panels[panel_order[i]].update_time = time; - } - - // NOTE this check is necessary because _menu_alpha isn't updated the frame the menu gets enabled - if(autocvar__menu_alpha != _menu_alpha_prev) - { - if(autocvar__menu_alpha == 0) - menu_enabled = 0; - _menu_alpha_prev = autocvar__menu_alpha; - } - - HUD_Configure_DrawGrid(); - } - else if(hud_configure_prev) - { - if(menu_enabled) - menu_enabled = 0; - if(autocvar_hud_cursormode) - setcursormode(0); - } -} - -const float hlBorderSize = 2; -const string hlBorder = "gfx/hud/default/border_highlighted"; -const string hlBorder2 = "gfx/hud/default/border_highlighted2"; -void HUD_Panel_HlBorder(float myBorder, vector color, float theAlpha) -{ - drawfill(panel_pos - '1 1 0' * myBorder, panel_size + '2 2 0' * myBorder, '0 0.5 1', .5 * theAlpha, DRAWFLAG_NORMAL); - drawpic_tiled(panel_pos - '1 1 0' * myBorder, hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); - drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * (panel_size.y + 2 * myBorder - hlBorderSize), hlBorder, '8 1 0' * hlBorderSize, eX * (panel_size.x + 2 * myBorder) + eY * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); - drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize, hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); - drawpic_tiled(panel_pos - '1 1 0' * myBorder + eY * hlBorderSize + eX * (panel_size.x + 2 * myBorder - hlBorderSize), hlBorder2, '1 8 0' * hlBorderSize, eY * (panel_size.y + 2 * myBorder - 2 * hlBorderSize) + eX * hlBorderSize, color, theAlpha, DRAWFLAG_NORMAL); -} - -void HUD_Configure_PostDraw() -{ - if(autocvar__hud_configure) - { - if(tab_panel) - { - panel = tab_panel; - HUD_Panel_UpdatePosSize(); - drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .2, DRAWFLAG_NORMAL); - } - if(highlightedPanel) - { - panel = highlightedPanel; - HUD_Panel_UpdatePosSize(); - HUD_Panel_HlBorder(panel_bg_border * hlBorderSize, '0 0.5 1', 0.4 * (1 - autocvar__menu_alpha)); - } - } -} diff --git a/qcsrc/client/hud_config.qh b/qcsrc/client/hud_config.qh deleted file mode 100644 index 05792286da..0000000000 --- a/qcsrc/client/hud_config.qh +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef CLIENT_HUD_CONFIG_H -#define CLIENT_HUD_CONFIG_H - -const int S_MOUSE1 = 1; -const int S_MOUSE2 = 2; -const int S_MOUSE3 = 4; -int mouseClicked; -int prevMouseClicked; // previous state -float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks -vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks - -void HUD_Panel_ExportCfg(string cfgname); - -void HUD_Panel_Mouse(); - -void HUD_Configure_Frame(); - -void HUD_Configure_PostDraw(); - -float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary); - -#endif diff --git a/qcsrc/client/laser.qc b/qcsrc/client/laser.qc deleted file mode 100644 index 1c63a0dc24..0000000000 --- a/qcsrc/client/laser.qc +++ /dev/null @@ -1,128 +0,0 @@ -#include "laser.qh" - -#include "../lib/csqcmodel/interpolate.qh" - -// a laser goes from origin in direction angles -// it has color 'colormod' -// and stops when something is in the way -entityclass(Laser); -class(Laser) .int cnt; // end effect -class(Laser) .vector colormod; -class(Laser) .int state; // on-off -class(Laser) .int count; // flags for the laser -class(Laser) .vector velocity; -class(Laser) .float alpha; -class(Laser) .float scale; // scaling factor of the thickness -class(Laser) .float modelscale; // scaling factor of the dlight - -void Draw_Laser(entity this) -{ - if(!self.state) - return; - InterpolateOrigin_Do(); - if(self.count & 0x80) - { - if(self.count & 0x10) - { - trace_endpos = self.velocity; - trace_dphitq3surfaceflags = 0; - } - else - traceline(self.origin, self.velocity, 0, self); - } - else - { - if(self.count & 0x10) - { - makevectors(self.angles); - trace_endpos = self.origin + v_forward * 1048576; - trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY; - } - else - { - makevectors(self.angles); - traceline(self.origin, self.origin + v_forward * 32768, 0, self); - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - trace_endpos = self.origin + v_forward * 1048576; - } - } - if(self.scale != 0) - { - if(self.alpha) - { - Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL, view_origin); - } - else - { - Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin); - } - } - if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) - { - if(self.cnt >= 0) - pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); - if(self.colormod != '0 0 0' && self.modelscale != 0) - adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5); - } -} - -void Ent_Laser() -{ - InterpolateOrigin_Undo(); - - // 30 bytes, or 13 bytes for just moving - int f = ReadByte(); - self.count = (f & 0xF0); - - if(self.count & 0x80) - self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; - else - self.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; - - if(f & 1) - { - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - setorigin(self, self.origin); - } - if(f & 8) - { - self.colormod_x = ReadByte() / 255.0; - self.colormod_y = ReadByte() / 255.0; - self.colormod_z = ReadByte() / 255.0; - if(f & 0x40) - self.alpha = ReadByte() / 255.0; - else - self.alpha = 0; - self.scale = 2; - self.modelscale = 50; - if(f & 0x20) - { - self.scale *= ReadByte() / 16.0; // beam radius - self.modelscale *= ReadByte() / 16.0; // dlight radius - } - if((f & 0x80) || !(f & 0x10)) - self.cnt = ReadShort() - 1; // effect number - else - self.cnt = 0; - } - if(f & 2) - { - if(f & 0x80) - { - self.velocity_x = ReadCoord(); - self.velocity_y = ReadCoord(); - self.velocity_z = ReadCoord(); - } - else - { - self.angles_x = ReadAngle(); - self.angles_y = ReadAngle(); - } - } - if(f & 4) - self.state = ReadByte(); - InterpolateOrigin_Note(); - self.draw = Draw_Laser; -} diff --git a/qcsrc/client/laser.qh b/qcsrc/client/laser.qh deleted file mode 100644 index 32c6cd81e4..0000000000 --- a/qcsrc/client/laser.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CLIENT_LASER_H -#define CLIENT_LASER_H - -void Ent_Laser(); - -#endif diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index d43c0bbbef..1d543af260 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -1,18 +1,10 @@ #include "main.qh" -#include "controlpoint.qh" -#include "damage.qh" -#include "effects.qh" -#include "generator.qh" -#include "gibs.qh" +#include "../common/effects/qc/all.qh" #include "hook.qh" -#include "hud.qh" -#include "hud_config.qh" -#include "laser.qh" +#include "hud/all.qh" #include "mapvoting.qh" -#include "modeleffects.qh" #include "mutators/events.qh" -#include "particles.qh" #include "quickmenu.qh" #include "scoreboard.qh" #include "shownames.qh" @@ -27,7 +19,6 @@ #include "../common/minigames/cl_minigames_hud.qh" #include "../common/net_notice.qh" #include "../common/triggers/include.qh" -#include "../common/turrets/cl_turrets.qh" #include "../common/vehicles/all.qh" #include "../lib/csqcmodel/cl_model.qh" #include "../lib/csqcmodel/interpolate.qh" @@ -37,60 +28,34 @@ // BEGIN REQUIRED CSQC FUNCTIONS //include "main.qh" -entity clearentity_ent; -void clearentity(entity e) -{ - if (!clearentity_ent) - { - clearentity_ent = spawn(); - clearentity_ent.classname = "clearentity"; - } - int n = e.entnum; - copyentity(clearentity_ent, e); - e.entnum = n; -} - #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED -void menu_show_error() -{ - drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0); -} // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load) // Useful for precaching things -void menu_sub_null() -{ -} - -void draw_null(entity this) { } - -string forcefog; void ConsoleCommand_macro_init(); -void CSQC_Init(void) +void CSQC_Init() { prvm_language = strzone(cvar_string("prvm_language")); #ifdef WATERMARK - LOG_TRACEF("^4CSQC Build information: ^1%s\n", WATERMARK); + LOG_INFOF("^4CSQC Build information: ^1%s\n", WATERMARK); #endif - int i; - binddb = db_create(); tempdb = db_create(); ClientProgsDB = db_load("client.db"); compressShortVector_init(); draw_endBoldFont(); - menu_visible = false; - menu_show = menu_show_error; - menu_action = func_null; - for(i = 0; i < 255; ++i) - if(getplayerkeyvalue(i, "viewentity") == "") - break; - maxclients = i; + { + int i = 0; + for ( ; i < 255; ++i) + if (getplayerkeyvalue(i, "viewentity") == "") + break; + maxclients = i; + } //registercommand("hud_configure"); //registercommand("hud_save"); @@ -114,7 +79,7 @@ void CSQC_Init(void) gametype = 0; // hud_fields uses strunzone on the titles! - for(i = 0; i < MAX_HUD_FIELDS; ++i) + for(int i = 0; i < MAX_HUD_FIELDS; ++i) hud_title[i] = strzone("(null)"); Cmd_HUD_SetFields(0); @@ -131,44 +96,42 @@ void CSQC_Init(void) // needs to be done so early because of the constants they create static_init(); static_init_late(); + static_init_precache(); // precaches - Projectile_Precache(); - Tuba_Precache(); - if(autocvar_cl_reticle) { precache_pic("gfx/reticle_normal"); // weapon reticles are precached in weapon files } - get_mi_min_max_texcoords(1); // try the CLEVER way first - minimapname = strcat("gfx/", mi_shortname, "_radar.tga"); - shortmapname = mi_shortname; - - if(precache_pic(minimapname) == "") { - // but maybe we have a non-clever minimap - minimapname = strcat("gfx/", mi_shortname, "_mini.tga"); - if(precache_pic(minimapname) == "") - minimapname = ""; // FAIL - else - get_mi_min_max_texcoords(0); // load new texcoords - } + get_mi_min_max_texcoords(1); // try the CLEVER way first + minimapname = strcat("gfx/", mi_shortname, "_radar.tga"); + shortmapname = mi_shortname; - mi_center = (mi_min + mi_max) * 0.5; - mi_scale = mi_max - mi_min; - minimapname = strzone(minimapname); + if (precache_pic(minimapname) == "") + { + // but maybe we have a non-clever minimap + minimapname = strcat("gfx/", mi_shortname, "_mini.tga"); + if (precache_pic(minimapname) == "") + minimapname = ""; // FAIL + else + get_mi_min_max_texcoords(0); // load new texcoords + } - WarpZone_Init(); + mi_center = (mi_min + mi_max) * 0.5; + mi_scale = mi_max - mi_min; + minimapname = strzone(minimapname); + } hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin"))); } // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) -void Shutdown(void) +void Shutdown() { WarpZone_Shutdown(); @@ -274,7 +237,8 @@ float SetTeam(entity o, int Team) } void Playerchecker_Think() -{SELFPARAM(); +{ + SELFPARAM(); int i; entity e; for(i = 0; i < maxclients; ++i) @@ -297,7 +261,10 @@ void Playerchecker_Think() { // player connected if (!e) - playerslots[i] = e = spawn(); + { + playerslots[i] = e = new(playerslot); + make_pure(e); + } e.sv_entnum = i; e.ping = 0; e.ping_packetloss = 0; @@ -309,15 +276,15 @@ void Playerchecker_Think() } } } - self.nextthink = time + 0.2; + this.nextthink = time + 0.2; } void Porto_Init(); void TrueAim_Init(); -void PostInit(void) +void PostInit() { - entity playerchecker; - playerchecker = spawn(); + entity playerchecker = new(playerchecker); + make_pure(playerchecker); playerchecker.think = Playerchecker_Think; playerchecker.nextthink = time + 0.2; @@ -336,16 +303,13 @@ void PostInit(void) // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos. float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) { - float bSkipKey; - bSkipKey = false; - if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary)) return true; if (QuickMenu_InputEvent(bInputType, nPrimary, nSecondary)) return true; - if ( HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary) ) + if (HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary)) return true; if (MapVote_InputEvent(bInputType, nPrimary, nSecondary)) @@ -354,11 +318,7 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) if (HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary)) return true; - if(menu_visible && menu_action) - if(menu_action(bInputType, nPrimary, nSecondary)) - return true; - - return bSkipKey; + return false; } // END REQUIRED CSQC FUNCTIONS @@ -368,37 +328,42 @@ float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) // BEGIN OPTIONAL CSQC FUNCTIONS void Ent_RemoveEntCS() -{SELFPARAM(); - entcs_receiver[self.sv_entnum] = NULL; +{ + SELFPARAM(); + entcs_receiver[this.sv_entnum] = NULL; } -void Ent_ReadEntCS() -{SELFPARAM(); + +NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew) +{ + make_pure(this); + this.classname = "entcs_receiver"; InterpolateOrigin_Undo(); - self.classname = "entcs_receiver"; int sf = ReadByte(); if(sf & BIT(0)) - self.sv_entnum = ReadByte(); + this.sv_entnum = ReadByte(); if (sf & BIT(1)) { - self.origin_x = ReadShort(); - self.origin_y = ReadShort(); - self.origin_z = ReadShort(); - setorigin(self, self.origin); + this.origin_x = ReadShort(); + this.origin_y = ReadShort(); + this.origin_z = ReadShort(); + setorigin(this, this.origin); } if (sf & BIT(2)) { - self.angles_y = ReadByte() * 360.0 / 256; - self.angles_x = self.angles_z = 0; + this.angles_y = ReadByte() * 360.0 / 256; + this.angles_x = this.angles_z = 0; } if (sf & BIT(3)) - self.healthvalue = ReadByte() * 10; + this.healthvalue = ReadByte() * 10; if (sf & BIT(4)) - self.armorvalue = ReadByte() * 10; + this.armorvalue = ReadByte() * 10; - entcs_receiver[self.sv_entnum] = self; - self.entremove = Ent_RemoveEntCS; - self.iflags |= IFLAG_ORIGIN; + return = true; + + entcs_receiver[this.sv_entnum] = this; + this.entremove = Ent_RemoveEntCS; + this.iflags |= IFLAG_ORIGIN; InterpolateOrigin_Note(); } @@ -406,44 +371,49 @@ void Ent_ReadEntCS() void Ent_Remove(); void Ent_RemovePlayerScore() -{SELFPARAM(); - if(self.owner) { - SetTeam(self.owner, -1); - self.owner.gotscores = 0; +{ + SELFPARAM(); + if(this.owner) { + SetTeam(this.owner, -1); + this.owner.gotscores = 0; for(int i = 0; i < MAX_SCORE; ++i) { - self.owner.(scores[i]) = 0; // clear all scores + this.owner.(scores[i]) = 0; // clear all scores } } } -void Ent_ReadPlayerScore() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_SCORES, bool isnew) +{ + make_pure(this); int i, n; bool isNew; entity o; // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN // (no I've never heard of M-x replace-string, sed, or anything like that) - isNew = !self.owner; // workaround for DP bug + isNew = !this.owner; // workaround for DP bug n = ReadByte()-1; #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED - if(!isNew && n != self.sv_entnum) + if(!isNew && n != this.sv_entnum) { //print("A CSQC entity changed its owner!\n"); - LOG_INFOF("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname); + LOG_INFOF("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(this), this.classname); isNew = true; Ent_Remove(); - self.enttype = ENT_CLIENT_SCORES; } #endif - self.sv_entnum = n; + this.sv_entnum = n; - if (!(playerslots[self.sv_entnum])) - playerslots[self.sv_entnum] = spawn(); - o = self.owner = playerslots[self.sv_entnum]; - o.sv_entnum = self.sv_entnum; + o = playerslots[this.sv_entnum]; + if (!o) + { + o = playerslots[this.sv_entnum] = new(playerslot); + make_pure(o); + } + this.owner = o; + o.sv_entnum = this.sv_entnum; o.gotscores = 1; //if (!o.sort_prev) @@ -468,19 +438,22 @@ void Ent_ReadPlayerScore() o.(scores[i]) = ReadChar(); } + return = true; + if(o.sort_prev) HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet! - self.entremove = Ent_RemovePlayerScore; + this.entremove = Ent_RemovePlayerScore; } -void Ent_ReadTeamScore() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew) +{ + make_pure(this); int i; entity o; - self.team = ReadByte(); - o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted + this.team = ReadByte(); + o = this.owner = GetTeam(this.team, true); // these team numbers can always be trusted int sf, lf; #if MAX_TEAMSCORE <= 8 @@ -500,11 +473,14 @@ void Ent_ReadTeamScore() o.(teamscores[i]) = ReadChar(); } + return = true; + HUD_UpdateTeamPos(o); } -void Ent_ClientData() +NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew) { + make_pure(this); float newspectatee_status; int f = ReadByte(); @@ -532,6 +508,8 @@ void Ent_ClientData() else angles_held_status = 0; + return = true; + if(newspectatee_status != spectatee_status) { // clear race stuff @@ -552,8 +530,9 @@ void Ent_ClientData() // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum } -void Ent_Nagger() +NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew) { + make_pure(this); int i, j, b, f; int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS @@ -600,6 +579,8 @@ void Ent_Nagger() } } + return = true; + ready_waiting = (nags & BIT(0)); ready_waiting_for_me = (nags & BIT(1)); vote_waiting = (nags & BIT(2)); @@ -607,8 +588,9 @@ void Ent_Nagger() warmup_stage = (nags & BIT(4)); } -void Ent_EliminatedPlayers() +NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew) { + make_pure(this); int i, j, b, f; int sf = ReadByte(); @@ -626,53 +608,51 @@ void Ent_EliminatedPlayers() playerslots[j].eliminated = 0; } } + return true; } -void Ent_RandomSeed() +NET_HANDLE(ENT_CLIENT_RANDOMSEED, bool isnew) { - float s; + make_pure(this); prandom_debug(); - s = ReadShort(); + float s = ReadShort(); psrandom(s); + return true; } -void Ent_ReadAccuracy(void) +NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew) { - int f, w; + make_pure(this); int sf = ReadInt24_t(); - if(sf == 0) - { - for(w = 0; w <= WEP_LAST - WEP_FIRST; ++w) + if (sf == 0) { + for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) weapon_accuracy[w] = -1; - return; + return true; } - for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) - { - if(sf & f) - { + int f = 1; + for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) { + if (sf & f) { int b = ReadByte(); - if(b == 0) + if (b == 0) weapon_accuracy[w] = -1; - else if(b == 255) + else if (b == 255) weapon_accuracy[w] = 1.0; // no better error handling yet, sorry else weapon_accuracy[w] = (b - 1.0) / 100.0; } - if(f == 0x800000) - f = 1; - else - f *= 2; + f = (f == 0x800000) ? 1 : f * 2; } + return true; } void Spawn_Draw(entity this) { - pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1)); + __pointparticles(this.cnt, this.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1)); } -void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new) +{ float teamnum = (ReadByte() - 1); vector spn_origin; spn_origin.x = ReadShort(); @@ -681,19 +661,19 @@ void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint //if(is_new) //{ - self.origin = spn_origin; - setsize(self, PL_MIN_CONST, PL_MAX_CONST); + this.origin = spn_origin; + setsize(this, PL_MIN_CONST, PL_MAX_CONST); //droptofloor(); /*if(autocvar_cl_spawn_point_model) // needs a model first { - self.mdl = "models/spawnpoint.md3"; - self.colormod = Team_ColorRGB(teamnum); - precache_model(self.mdl); - setmodel(self, self.mdl); - self.drawmask = MASK_NORMAL; - //self.movetype = MOVETYPE_NOCLIP; - //self.draw = Spawn_Draw; + this.mdl = "models/spawnpoint.md3"; + this.colormod = Team_ColorRGB(teamnum); + precache_model(this.mdl); + setmodel(this, this.mdl); + this.drawmask = MASK_NORMAL; + //this.movetype = MOVETYPE_NOCLIP; + //this.draw = Spawn_Draw; }*/ if(autocvar_cl_spawn_point_particles) { @@ -701,24 +681,25 @@ void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint { switch(teamnum) { - case NUM_TEAM_1: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_RED); break; - case NUM_TEAM_2: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_BLUE); break; - case NUM_TEAM_3: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_YELLOW); break; - case NUM_TEAM_4: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_PINK); break; - default: self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); break; + case NUM_TEAM_1: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_RED); break; + case NUM_TEAM_2: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_BLUE); break; + case NUM_TEAM_3: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_YELLOW); break; + case NUM_TEAM_4: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_PINK); break; + default: this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); break; } } - else { self.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); } + else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); } - self.draw = Spawn_Draw; + this.draw = Spawn_Draw; } //} - //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt); + //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(this.origin), teamnum, this.cnt); + return true; } -void Ent_ReadSpawnEvent(float is_new) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new) +{ // If entnum is 0, ONLY do the local spawn actions // this way the server can disable the sending of // spawn origin or such to clients if wanted. @@ -726,9 +707,9 @@ void Ent_ReadSpawnEvent(float is_new) if(entnum) { - self.origin_x = ReadShort(); - self.origin_y = ReadShort(); - self.origin_z = ReadShort(); + this.origin_x = ReadShort(); + this.origin_y = ReadShort(); + this.origin_z = ReadShort(); if(is_new) { @@ -738,19 +719,20 @@ void Ent_ReadSpawnEvent(float is_new) { switch(teamnum) { - case NUM_TEAM_1: pointparticles(particleeffectnum(EFFECT_SPAWN_RED), self.origin, '0 0 0', 1); break; - case NUM_TEAM_2: pointparticles(particleeffectnum(EFFECT_SPAWN_BLUE), self.origin, '0 0 0', 1); break; - case NUM_TEAM_3: pointparticles(particleeffectnum(EFFECT_SPAWN_YELLOW), self.origin, '0 0 0', 1); break; - case NUM_TEAM_4: pointparticles(particleeffectnum(EFFECT_SPAWN_PINK), self.origin, '0 0 0', 1); break; - default: pointparticles(particleeffectnum(EFFECT_SPAWN_NEUTRAL), self.origin, '0 0 0', 1); break; + case NUM_TEAM_1: pointparticles(EFFECT_SPAWN_RED, this.origin, '0 0 0', 1); break; + case NUM_TEAM_2: pointparticles(EFFECT_SPAWN_BLUE, this.origin, '0 0 0', 1); break; + case NUM_TEAM_3: pointparticles(EFFECT_SPAWN_YELLOW, this.origin, '0 0 0', 1); break; + case NUM_TEAM_4: pointparticles(EFFECT_SPAWN_PINK, this.origin, '0 0 0', 1); break; + default: pointparticles(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); break; } } if(autocvar_cl_spawn_event_sound) { - sound(self, CH_TRIGGER, SND_SPAWN, VOL_BASE, ATTEN_NORM); + sound(this, CH_TRIGGER, SND_SPAWN, VOL_BASE, ATTEN_NORM); } } } + return = true; // local spawn actions if(is_new && (!entnum || (entnum == player_localentnum))) @@ -765,21 +747,18 @@ void Ent_ReadSpawnEvent(float is_new) } } HUD_Radar_Hide_Maximized(); - //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum); + //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(this.origin), entnum, player_localentnum); } // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. -void Ent_RadarLink(); -void Ent_Init(); -void Ent_ScoresInfo(); -void CSQC_Ent_Update(float bIsNewEntity) -{SELFPARAM(); +void CSQC_Ent_Update(bool isnew) +{ + SELFPARAM(); + this.sourceLocLine = __LINE__; + this.sourceLocFile = __FILE__; int t = ReadByte(); - if(autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t); - // set up the "time" global for received entities to be correct for interpolation purposes float savetime = time; if(servertime) @@ -794,143 +773,79 @@ void CSQC_Ent_Update(float bIsNewEntity) } #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED - if(self.enttype) + if (this.enttype) { - if(t != self.enttype || bIsNewEntity) + if (t != this.enttype || isnew) { - LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t); + LOG_INFOF("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(this), this.entnum, this.enttype, t); Ent_Remove(); - clearentity(self); - bIsNewEntity = 1; + clearentity(this); + isnew = true; } } else { - if(!bIsNewEntity) + if (!isnew) { - LOG_INFOF("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t); - bIsNewEntity = 1; + LOG_INFOF("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(this), this.entnum, t); + isnew = true; } } #endif - self.enttype = t; + this.enttype = t; bool done = false; FOREACH(LinkedEntities, it.m_id == t, LAMBDA( - it.m_read(self, bIsNewEntity); - done = true; + if (isnew) this.classname = it.netname; + if (autocvar_developer_csqcentities) + LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t); + done = it.m_read(this, isnew); break; )); + time = savetime; if (!done) - switch(t) { - case ENT_CLIENT_MUTATOR: { - int mutID = ReadMutator(); - if (!MUTATOR_CALLHOOK(CSQC_Ent_Update, mutID, bIsNewEntity)) - error(sprintf("Unknown mutator type in CSQC_Ent_Update (mutID: %d, edict: %d, classname: %s)\n", mutID, num_for_edict(self), self.classname)); - break; - } - case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break; - case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break; - case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break; - case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break; - case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break; - case ENT_CLIENT_LASER: Ent_Laser(); break; - case ENT_CLIENT_NAGGER: Ent_Nagger(); break; - case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break; - case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; - case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; - case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(bIsNewEntity); break; - case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break; - case ENT_CLIENT_INIT: Ent_Init(); break; - case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break; - case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break; - case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break; - case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break; - case ENT_CLIENT_WALL: Ent_Wall(); break; - case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break; - case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break; - case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break; - case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); break; - case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break; - case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break; - case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break; - case ENT_CLIENT_INVENTORY: Inventory_Read(self); break; - case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break; - case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; - case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; - case ENT_CLIENT_TURRET: ent_turret(); break; - case ENT_CLIENT_GENERATOR: ent_generator(); break; - case ENT_CLIENT_CONTROLPOINT_ICON: ent_cpicon(this); break; - case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; - case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; - case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; - case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; - case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break; - case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; - case ENT_CLIENT_MINIGAME: ent_read_minigame(); break; - case ENT_CLIENT_VIEWLOC: ent_viewloc(); break; - case ENT_CLIENT_VIEWLOC_TRIGGER: ent_viewloc_trigger(); break; - case ENT_CLIENT_LADDER: ent_func_ladder(); break; - case ENT_CLIENT_TRIGGER_PUSH: ent_trigger_push(); break; - case ENT_CLIENT_TARGET_PUSH: ent_target_push(); break; - case ENT_CLIENT_CONVEYOR: ent_conveyor(); break; - case ENT_CLIENT_DOOR: ent_door(); break; - case ENT_CLIENT_PLAT: ent_plat(); break; - case ENT_CLIENT_SWAMP: ent_swamp(); break; - case ENT_CLIENT_CORNER: ent_corner(); break; - case ENT_CLIENT_KEYLOCK: ent_keylock(); break; - case ENT_CLIENT_TRAIN: ent_train(); break; - case ENT_CLIENT_TRIGGER_IMPULSE: ent_trigger_impulse(); break; - case ENT_CLIENT_EFFECT: Read_Effect(bIsNewEntity); break; - - default: - //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); - error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname)); - break; + LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t); } - - time = savetime; } + // Destructor, but does NOT deallocate the entity by calling remove(). Also // used when an entity changes its type. For an entity that someone interacts // with others, make sure it can no longer do so. void Ent_Remove() -{SELFPARAM(); - if(self.entremove) - self.entremove(); +{ + SELFPARAM(); + if(this.entremove) this.entremove(); - if(self.skeletonindex) + if(this.skeletonindex) { - skel_delete(self.skeletonindex); - self.skeletonindex = 0; + skel_delete(this.skeletonindex); + this.skeletonindex = 0; } - if(self.snd_looping > 0) + if(this.snd_looping > 0) { - sound(self, self.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation); - self.snd_looping = 0; + sound(this, this.snd_looping, SND_Null, VOL_BASE, autocvar_g_jetpack_attenuation); + this.snd_looping = 0; } - self.enttype = 0; - self.classname = ""; - self.draw = draw_null; - self.entremove = menu_sub_null; + this.enttype = 0; + this.classname = ""; + this.draw = func_null; + this.entremove = func_null; // TODO possibly set more stuff to defaults } -// CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(self) as well. +// CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(this) as well. void CSQC_Ent_Remove() -{SELFPARAM(); - if(autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype); - - if(wasfreed(self)) +{ + SELFPARAM(); + if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype); + if (wasfreed(this)) { - LOG_INFO("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); + LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); return; } - if(self.enttype) - Ent_Remove(); - remove(self); + if (this.enttype) Ent_Remove(); + remove(this); } void Gamemode_Init() @@ -945,70 +860,79 @@ void Gamemode_Init() // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. void CSQC_Parse_StuffCmd(string strMessage) { - if(autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage); - + if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage); localcmd(strMessage); } // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. void CSQC_Parse_Print(string strMessage) { - if(autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Parse_Print(\"%s\")\n", strMessage); - + if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_Print(\"%s\")\n", strMessage); print(ColorTranslateRGB(strMessage)); } // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided. void CSQC_Parse_CenterPrint(string strMessage) { - if(autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage); - + if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage); centerprint_hud(strMessage); } -string notranslate_fogcmd1 = "\nfog "; -string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n"; -void Fog_Force() +// CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. +// You must ALWAYS first acquire the temporary ID, which is sent as a byte. +// Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. +bool CSQC_Parse_TempEntity() { - // TODO somehow thwart prvm_globalset client ... + // Acquire TE ID + int nTEID = ReadByte(); + + FOREACH(TempEntities, it.m_id == nTEID, LAMBDA( + if (autocvar_developer_csqcentities) + LOG_INFOF("CSQC_Parse_TempEntity() nTEID=%s (%d)\n", it.netname, nTEID); + return it.m_read(NULL, true); + )); - if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog) - { localcmd("\nr_drawfog 0\n"); } - else if(forcefog != "") - { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); } + if (autocvar_developer_csqcentities) + LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID); + + // No special logic for this temporary entity; return 0 so the engine can handle it + return false; +} + +/** TODO somehow thwart prvm_globalset client ... */ +string forcefog; +void Fog_Force() +{ + if (autocvar_cl_orthoview && autocvar_cl_orthoview_nofog) + localcmd("\nr_drawfog 0\n"); + else if (forcefog != "") + localcmd(sprintf("\nfog %s\nr_fog_exp2 0\nr_drawfog 1\n", forcefog)); } void Gamemode_Init(); -void Ent_ScoresInfo() -{SELFPARAM(); - int i; - self.classname = "ent_client_scores_info"; +NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew) +{ + make_pure(this); gametype = ReadInt24_t(); HUD_ModIcons_SetFunc(); - for(i = 0; i < MAX_SCORE; ++i) + for (int i = 0; i < MAX_SCORE; ++i) { - if(scores_label[i]) - strunzone(scores_label[i]); + if (scores_label[i]) strunzone(scores_label[i]); scores_label[i] = strzone(ReadString()); scores_flags[i] = ReadByte(); } - for(i = 0; i < MAX_TEAMSCORE; ++i) + for (int i = 0; i < MAX_TEAMSCORE; ++i) { - if(teamscores_label[i]) - strunzone(teamscores_label[i]); + if (teamscores_label[i]) strunzone(teamscores_label[i]); teamscores_label[i] = strzone(ReadString()); teamscores_flags[i] = ReadByte(); } + return = true; HUD_InitScores(); Gamemode_Init(); } -void Ent_Init() -{SELFPARAM(); - self.classname = "ent_client_init"; - +NET_HANDLE(ENT_CLIENT_INIT, bool isnew) +{ nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); @@ -1020,8 +944,7 @@ void Ent_Init() arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); - if(forcefog) - strunzone(forcefog); + if (forcefog) strunzone(forcefog); forcefog = strzone(ReadString()); armorblockpercent = ReadByte() / 255.0; @@ -1042,18 +965,18 @@ void Ent_Init() g_trueaim_minrange = ReadCoord(); g_balance_porto_secondary = ReadByte(); + return = true; + + MUTATOR_CALLHOOK(Ent_Init); - if(!postinit) - PostInit(); + if (!postinit) PostInit(); } -void Net_ReadRace() +NET_HANDLE(TE_CSQC_RACE, bool isNew) { - float b; + int b = ReadByte(); - b = ReadByte(); - - switch(b) + switch (b) { case RACE_NET_CHECKPOINT_HIT_QUALIFYING: race_checkpoint = ReadByte(); @@ -1201,36 +1124,36 @@ void Net_ReadRace() strunzone(race_status_name); race_status_name = strzone(ReadString()); } + return true; } -void Net_TeamNagger() +NET_HANDLE(TE_CSQC_TEAMNAGGER, bool isNew) { teamnagger = 1; + return true; } -void Net_ReadPingPLReport() +NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew) { - int e, pi, pl, ml; - e = ReadByte(); - pi = ReadShort(); - pl = ReadByte(); - ml = ReadByte(); - if (!(playerslots[e])) - return; - playerslots[e].ping = pi; - playerslots[e].ping_packetloss = pl / 255.0; - playerslots[e].ping_movementloss = ml / 255.0; + int i = ReadByte(); + int pi = ReadShort(); + int pl = ReadByte(); + int ml = ReadByte(); + return = true; + entity e = playerslots[i]; + if (!e) return; + e.ping = pi; + e.ping_packetloss = pl / 255.0; + e.ping_movementloss = ml / 255.0; } -void Net_WeaponComplain() +NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) { complain_weapon = ReadByte(); - - if(complain_weapon_name) - strunzone(complain_weapon_name); + if (complain_weapon_name) strunzone(complain_weapon_name); complain_weapon_name = strzone(WEP_NAME(complain_weapon)); - complain_weapon_type = ReadByte(); + return = true; complain_weapon_time = time; weapontime = time; // ping the weapon panel @@ -1243,66 +1166,6 @@ void Net_WeaponComplain() } } -// CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. -// You must ALWAYS first acquire the temporary ID, which is sent as a byte. -// Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. -bool CSQC_Parse_TempEntity() -{ - // Acquire TE ID - int nTEID = ReadByte(); - - if (autocvar_developer_csqcentities) - LOG_INFOF("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID); - - FOREACH(TempEntities, it.m_id == nTEID, LAMBDA( - it.m_read(NULL, true); - return true; - )); - switch (nTEID) - { - case TE_CSQC_MUTATOR: - int mutID = ReadMutator(); - if (MUTATOR_CALLHOOK(CSQC_Parse_TempEntity, mutID)) - return true; - case TE_CSQC_TARGET_MUSIC: - Net_TargetMusic(); - return true; - case TE_CSQC_PICTURE: - Net_MapVote_Picture(); - return true; - case TE_CSQC_RACE: - Net_ReadRace(); - return true; - case TE_CSQC_VORTEXBEAMPARTICLE: - Net_ReadVortexBeamParticle(); - return true; - case TE_CSQC_TEAMNAGGER: - Net_TeamNagger(); - return true; - case TE_CSQC_ARC: - Net_ReadArc(); - return true; - case TE_CSQC_PINGPLREPORT: - Net_ReadPingPLReport(); - return true; - case TE_CSQC_WEAPONCOMPLAIN: - Net_WeaponComplain(); - return true; - case TE_CSQC_VEHICLESETUP: - Net_VehicleSetup(); - return true; - case TE_CSQC_SVNOTICE: - cl_notice_read(); - return true; - case TE_CSQC_SHOCKWAVEPARTICLE: - Net_ReadShockwaveParticle(); - return true; - default: - // No special logic for this temporary entity; return 0 so the engine can handle it - return false; - } -} - string getcommandkey(string text, string command) { string keys; diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index acd0bcb1ef..48f7621879 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -13,13 +13,6 @@ const float DATABUF_PING = 0; #define DATABUF_NEXT (5*maxclients) -void() menu_show_error; -void() menu_sub_null; - -float menu_visible; -var void() menu_show; -var float(float bInputType, float nPrimary, float nSecondary) menu_action; - // -------------------------------------------------------------------------- // Onslaught @@ -93,7 +86,7 @@ entity teamslots[17]; // 17 teams (including "spectator team") .void(entity) draw; .void(entity) draw2d; -.void(void) entremove; +.void() entremove; float drawframetime; vector view_origin, view_forward, view_right, view_up; @@ -103,7 +96,7 @@ float button_attack2; int activeweapon; int switchingweapon; -int switchweapon; +#define switchweapon STAT(SWITCHWEAPON) float current_viewzoom; float zoomin_effect; float warmup_stage; diff --git a/qcsrc/client/mapvoting.qc b/qcsrc/client/mapvoting.qc index 21bb421a96..a134357aaa 100644 --- a/qcsrc/client/mapvoting.qc +++ b/qcsrc/client/mapvoting.qc @@ -1,6 +1,6 @@ #include "mapvoting.qh" -#include "hud.qh" +#include "hud/all.qh" #include "scoreboard.qh" #include "../common/mapinfo.qh" @@ -851,9 +851,11 @@ void MapVote_UpdateVotes() mv_ownvote = ReadByte()-1; } -void Ent_MapVote() +NET_HANDLE(ENT_CLIENT_MAPVOTE, bool isnew) { + make_pure(self); int sf = ReadByte(); + return = true; if(sf & 1) MapVote_Init(); @@ -865,6 +867,12 @@ void Ent_MapVote() MapVote_UpdateVotes(); } +NET_HANDLE(TE_CSQC_PICTURE, bool isNew) +{ + Net_MapVote_Picture(); + return true; +} + void Net_MapVote_Picture() { int type = ReadByte(); diff --git a/qcsrc/client/mapvoting.qh b/qcsrc/client/mapvoting.qh index c1c27ad5b1..1c84419482 100644 --- a/qcsrc/client/mapvoting.qh +++ b/qcsrc/client/mapvoting.qh @@ -8,8 +8,6 @@ void Cmd_MapVote_MapDownload(float argc); float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary); -void Ent_MapVote(); - void Net_MapVote_Picture(); float mv_active; diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index 7983e740f1..ab4cb1f156 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -1,6 +1,6 @@ #include "miscfunctions.qh" -#include "hud.qh" +#include "hud/all.qh" #include "../common/command/generic.qh" @@ -123,7 +123,8 @@ entity GetTeam(int Team, bool add) return teamslots[num]; if (!add) return world; - entity tm = spawn(); + entity tm = new(team); + make_pure(tm); tm.team = Team; teamslots[num] = tm; RegisterTeam(tm); @@ -155,15 +156,6 @@ float PreviewExists(string name) return false; } -vector rotate(vector v, float a) -{ - vector w = '0 0 0'; - // FTEQCC SUCKS AGAIN - w.x = v.x * cos(a) + v.y * sin(a); - w.y = -1 * v.x * sin(a) + v.y * cos(a); - return w; -} - // decolorizes and team colors the player name when needed string playername(string thename, float teamid) { @@ -198,10 +190,6 @@ vector project_3d_to_2d(vector vec) return vec; } -void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8) -{ -} - float expandingbox_sizefactor_from_fadelerp(float fadelerp) { return 1.2 / (1.2 - fadelerp); @@ -286,7 +274,6 @@ void drawstring_expanding(vector position, string text, vector theScale, vector sz = expandingbox_sizefactor_from_fadelerp(fadelerp); drawfontscale = sz * '1 1 0'; - dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); drawstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, false, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), rgb, theAlpha * (1 - fadelerp), flag); // width parameter: // (scale_x * sz / drawfontscale_x) * drawfontscale_x * SIZE1 / (scale_x * sz) @@ -306,7 +293,6 @@ void drawcolorcodedstring_expanding(vector position, string text, vector theScal sz = expandingbox_sizefactor_from_fadelerp(fadelerp); drawfontscale = sz * '1 1 0'; - dummyfunction(0, 0, 0, 0, 0, 0, 0, 0); drawcolorcodedstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth(text, true, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), theAlpha * (1 - fadelerp), flag); drawfontscale = '1 1 0'; } @@ -547,6 +533,7 @@ float getplayerisdead(float pl) return false; } +/** engine callback */ void URI_Get_Callback(int id, float status, string data) { if(url_URI_Get_Callback(id, status, data)) @@ -568,16 +555,6 @@ void URI_Get_Callback(int id, float status, string data) } } -void draw_beginBoldFont() -{ - drawfont = FONT_USER+2; -} - -void draw_endBoldFont() -{ - drawfont = FONT_USER+1; -} - void Accuracy_LoadLevels() { if(autocvar_accuracy_color_levels != acc_color_levels) diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index b223d88b9e..60048d0497 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -44,7 +44,8 @@ float cvar_or(string cv, float v); vector project_3d_to_2d(vector vec); -void dummyfunction(float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8); +#define draw_beginBoldFont() do { drawfont = FONT_USER + 2; } while (0) +#define draw_endBoldFont() do { drawfont = FONT_USER + 1; } while (0) float expandingbox_sizefactor_from_fadelerp(float fadelerp); @@ -151,13 +152,6 @@ vector getcsqcplayercolor(float pl); float getplayerisdead(float pl); -void URI_Get_Callback(int id, float status, string data); - -void draw_beginBoldFont(); - -void draw_endBoldFont(); - - const int MAX_ACCURACY_LEVELS = 10; float acc_lev[MAX_ACCURACY_LEVELS]; vector acc_col[MAX_ACCURACY_LEVELS]; diff --git a/qcsrc/client/modeleffects.qc b/qcsrc/client/modeleffects.qc deleted file mode 100644 index d870d81e98..0000000000 --- a/qcsrc/client/modeleffects.qc +++ /dev/null @@ -1,72 +0,0 @@ -#include "modeleffects.qh" - -.float cnt; -.float scale; -.float alpha; - -void ModelEffect_Draw(entity this) -{ - self.angles = self.angles + frametime * self.avelocity; - setorigin(self, self.origin + frametime * self.velocity); - self.scale = self.scale1 + (self.scale2 - self.scale1) * (time - self.teleport_time) / (self.lifetime + self.fadetime - self.teleport_time); - self.alpha = self.cnt * bound(0, 1 - (time - self.lifetime) / self.fadetime, 1); - if(self.alpha < ALPHA_MIN_VISIBLE) - { - remove(self); - return; - } - self.drawmask = MASK_NORMAL; - if(self.scale <= 0) - { - self.drawmask = 0; - return; - } -} - -void Ent_ModelEffect(bool isNew) -{SELFPARAM(); - self.classname = "modeleffect_spawner"; - - int f = ReadByte(); - - entity e = spawn(); - e.classname = "modeleffect"; - e.model = "from network"; - e.modelindex = ReadShort(); - e.skin = ReadByte(); - e.frame = ReadByte(); - e.frame1time = time; - e.origin_x = ReadCoord(); - e.origin_y = ReadCoord(); - e.origin_z = ReadCoord(); - setorigin(e, e.origin); - if(f & 1) - { - e.velocity_x = ReadCoord(); - e.velocity_y = ReadCoord(); - e.velocity_z = ReadCoord(); - } - if(f & 2) - { - e.angles_x = ReadAngle(); - e.angles_y = ReadAngle(); - e.angles_z = ReadAngle(); - } - if(f & 4) - { - e.avelocity_x = ReadAngle(); - e.avelocity_y = ReadAngle(); - e.avelocity_z = ReadAngle(); - } - e.scale1 = ReadShort() / 256.0; - e.scale2 = ReadShort() / 256.0; - e.lifetime = time + ReadByte() * 0.01; - e.fadetime = ReadByte() * 0.01; - e.teleport_time = time; - e.cnt = ReadByte() / 255.0; // actually alpha - - e.draw = ModelEffect_Draw; - - if(!isNew) - remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then -} diff --git a/qcsrc/client/modeleffects.qh b/qcsrc/client/modeleffects.qh deleted file mode 100644 index 32641f394e..0000000000 --- a/qcsrc/client/modeleffects.qh +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CLIENT_MODELEFFECTS_H -#define CLIENT_MODELEFFECTS_H - -entityclass(ModelEffect); -class(ModelEffect) .float frame1time; -class(ModelEffect) .float lifetime, fadetime; -class(ModelEffect) .float teleport_time; -class(ModelEffect) .float scale1, scale2; - -void ModelEffect_Draw(entity this); - -void Ent_ModelEffect(bool isNew); -#endif diff --git a/qcsrc/client/mutators/events.qh b/qcsrc/client/mutators/events.qh index a79e0b4712..baaba14aef 100644 --- a/qcsrc/client/mutators/events.qh +++ b/qcsrc/client/mutators/events.qh @@ -29,49 +29,25 @@ string cmd_string; * } */ #define EV_CSQC_ConsoleCommand(i, o) \ - /** command name */ i(string, cmd_name) \ - /** also, argv() can be used */ i(int, cmd_argc) \ - /** whole command, use only if you really have to */ i(string, cmd_string) \ - /**/ + /** command name */ i(string, cmd_name) \ + /** also, argv() can be used */ i(int, cmd_argc) \ + /** whole command, use only if you really have to */ i(string, cmd_string) \ + /**/ MUTATOR_HOOKABLE(CSQC_ConsoleCommand, EV_CSQC_ConsoleCommand); /* Called when the crosshair is being updated */ MUTATOR_HOOKABLE(UpdateCrosshair, EV_NO_ARGS); -/** - * Called when a temp entity is parsed - * NOTE: hooks MUST start with: - * if (MUTATOR_RETURNVALUE) return; - * if (!ReadMutatorEquals(mutator_argv_int_0, name_of_mutator)) return; - * return = true; - */ -#define EV_CSQC_Parse_TempEntity(i, o) \ - /** mutator id */ i(int, mutator_argv_int_0) \ - /**/ -MUTATOR_HOOKABLE(CSQC_Parse_TempEntity, EV_CSQC_Parse_TempEntity); - -/** - * Called when a shared entity is updated - * if (MUTATOR_RETURNVALUE) return; - * if (!ReadMutatorEquals(mutator_argv_int_0, name_of_mutator)) return; - * return = true; - */ -#define EV_CSQC_Ent_Update(i, o) \ - /** mutator id */ i(int, mutator_argv_int_0) \ - /** bIsNewEntity */ i(bool, mutator_argv_bool_0) \ - /**/ -MUTATOR_HOOKABLE(CSQC_Ent_Update, EV_CSQC_Ent_Update); - /** Called when a projectile is linked with CSQC */ #define EV_Ent_Projectile(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(Ent_Projectile, EV_Ent_Projectile); /** Called when a projectile's properties are being modified */ #define EV_EditProjectile(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(EditProjectile, EV_EditProjectile); /* Called when projectiles are precached */ @@ -79,26 +55,26 @@ MUTATOR_HOOKABLE(PrecacheProjectiles, EV_NO_ARGS); /** Called when updating the attached tags index */ #define EV_TagIndex_Update(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(TagIndex_Update, EV_TagIndex_Update); /** Called when setting the attached tags */ #define EV_TagIndex_Apply(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(TagIndex_Apply, EV_TagIndex_Apply); /** Called when setting up skeleton bones */ #define EV_Skeleton_CheckBones(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(Skeleton_CheckBones, EV_Skeleton_CheckBones); /** Called when setting up bones from the loaded model */ #define EV_Skeleton_CheckModel(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(Skeleton_CheckModel, EV_Skeleton_CheckModel); /** Called when clearing the global parameters for a model */ @@ -106,27 +82,59 @@ MUTATOR_HOOKABLE(ClearModelParams, EV_NO_ARGS); /** Called when getting the global parameters for a model */ #define EV_GetModelParams(i, o) \ - /** entity id */ i(string, checkmodel_input) \ - /** entity id */ i(string, checkmodel_command) \ - /**/ + /** entity id */ i(string, checkmodel_input) \ + /** entity id */ i(string, checkmodel_command) \ + /**/ string checkmodel_input, checkmodel_command; MUTATOR_HOOKABLE(GetModelParams, EV_GetModelParams); /** called when a player presses the jump key */ #define EV_PlayerJump(i, o) \ - /**/ i(float, player_multijump) \ - /**/ i(float, player_jumpheight) \ - /**/ o(float, player_multijump) \ - /**/ o(float, player_jumpheight) \ - /**/ + /**/ i(float, player_multijump) \ + /**/ i(float, player_jumpheight) \ + /**/ o(float, player_multijump) \ + /**/ o(float, player_jumpheight) \ + /**/ float player_multijump; float player_jumpheight; MUTATOR_HOOKABLE(PlayerJump, EV_PlayerJump); /** Called checking if 3rd person mode should be forced on */ #define EV_WantEventchase(i, o) \ - /** entity id */ i(entity, __self) \ - /**/ + /** entity id */ i(entity, __self) \ + /**/ MUTATOR_HOOKABLE(WantEventchase, EV_WantEventchase); +#define EV_AnnouncerOption(i, o) \ + /**/ i(string, ret_string) \ + /**/ o(string, ret_string) \ + /**/ +MUTATOR_HOOKABLE(AnnouncerOption, EV_AnnouncerOption); + +MUTATOR_HOOKABLE(Ent_Init, EV_NO_ARGS); + +#define EV_HUD_Draw_overlay(i, o) \ + /**/ o(vector, MUTATOR_ARGV_0_vector) \ + /**/ o(float, MUTATOR_ARGV_0_float) \ + /**/ +MUTATOR_HOOKABLE(HUD_Draw_overlay, EV_HUD_Draw_overlay); + +MUTATOR_HOOKABLE(HUD_Powerups_add, EV_NO_ARGS); + +/** Return true to not draw any vortex beam */ +#define EV_Particles_VortexBeam(i, o) \ + /**/ i(vector, vbeam_shotorg) \ + /**/ i(vector, vbeam_endpos) \ + /**/ +vector vbeam_shotorg; +vector vbeam_endpos; +MUTATOR_HOOKABLE(Particles_VortexBeam, EV_Particles_VortexBeam); + +/** Return true to not draw any impact effect */ +#define EV_Weapon_ImpactEffect(i, o) \ + /**/ i(entity, w_hitwep) \ + /**/ +entity w_hitwep; +MUTATOR_HOOKABLE(Weapon_ImpactEffect, EV_Weapon_ImpactEffect); + #endif diff --git a/qcsrc/client/particles.qc b/qcsrc/client/particles.qc deleted file mode 100644 index 0c7f7a39f8..0000000000 --- a/qcsrc/client/particles.qc +++ /dev/null @@ -1,25 +0,0 @@ -#include "particles.qh" - -#include "../common/stats.qh" - -#include "../lib/warpzone/common.qh" - -void Net_ReadVortexBeamParticle() -{ - vector shotorg, endpos; - float charge; - shotorg.x = ReadCoord(); shotorg.y = ReadCoord(); shotorg.z = ReadCoord(); - endpos.x = ReadCoord(); endpos.y = ReadCoord(); endpos.z = ReadCoord(); - charge = ReadByte() / 255.0; - - pointparticles(particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH), shotorg, normalize(endpos - shotorg) * 1000, 1); - - //draw either the old v2.3 beam or the new beam - charge = sqrt(charge); // divide evenly among trail spacing and alpha - particles_alphamin = particles_alphamax = particles_fade = charge; - - if (autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) - WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); - else - WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); -} diff --git a/qcsrc/client/particles.qh b/qcsrc/client/particles.qh deleted file mode 100644 index 3735641afa..0000000000 --- a/qcsrc/client/particles.qh +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CLIENT_PARTICLES_H -#define CLIENT_PARTICLES_H -.int dphitcontentsmask; - -entityclass(PointParticles); -class(PointParticles) .int cnt; // effect number -class(PointParticles) .vector velocity; // particle velocity -class(PointParticles) .float waterlevel; // direction jitter -class(PointParticles) .int count; // count multiplier -class(PointParticles) .int impulse; // density -class(PointParticles) .string noise; // sound -class(PointParticles) .float atten; -class(PointParticles) .float volume; -class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle -class(PointParticles) .vector movedir; // trace direction - -void Draw_PointParticles(entity this); - -void Ent_PointParticles_Remove(); - -void Ent_PointParticles(); - -class(PointParticles) .float glow_color; // palette index - -void Net_ReadVortexBeamParticle(); -#endif diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index 5c110e2b89..1e939b7703 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -1,25 +1,18 @@ #include "../lib/_all.inc" #include "_all.qh" +#include "../common/effects/qc/all.qc" + #include "announcer.qc" #include "bgmscript.qc" -#include "controlpoint.qc" #include "csqcmodel_hooks.qc" -#include "damage.qc" -#include "effects.qc" -#include "generator.qc" -#include "gibs.qc" #include "hook.qc" -#include "hud.qc" -#include "hud_config.qc" +#include "hud/all.qc" #include "main.qc" #include "mapvoting.qc" #include "miscfunctions.qc" -#include "modeleffects.qc" #include "movelib.qc" -#include "particles.qc" #include "player_skeleton.qc" -#include "rubble.qc" #include "scoreboard.qc" #include "shownames.qc" #include "teamradar.qc" @@ -47,14 +40,12 @@ #include "../common/minigames/minigames.qc" #include "../common/minigames/cl_minigames.qc" -#include "../common/buffs/all.qc" #include "../common/deathtypes/all.qc" #include "../common/effects/all.qc" #include "../common/gamemodes/all.qc" #include "../common/items/all.qc" #include "../common/monsters/all.qc" #include "../common/mutators/all.qc" -#include "../common/nades/all.qc" #include "../common/turrets/all.qc" #include "../common/vehicles/all.qc" #include "../common/weapons/all.qc" @@ -67,12 +58,6 @@ #include "../lib/csqcmodel/cl_player.qc" #include "../lib/csqcmodel/interpolate.qc" -// TODO: move to common -#include "../server/mutators/mutator/mutator_multijump.qc" -#define IMPLEMENTATION -#include "../server/mutators/mutator/mutator_multijump.qc" -#undef IMPLEMENTATION - #include "../lib/warpzone/anglestransform.qc" #include "../lib/warpzone/client.qc" #include "../lib/warpzone/common.qc" diff --git a/qcsrc/client/quickmenu.qc b/qcsrc/client/quickmenu.qc index 1f7be0c500..a4fdfea382 100644 --- a/qcsrc/client/quickmenu.qc +++ b/qcsrc/client/quickmenu.qc @@ -1,7 +1,6 @@ #include "quickmenu.qh" -#include "hud.qh" -#include "hud_config.qh" +#include "hud/all.qh" #include "mapvoting.qh" // QUICKMENU_MAXLINES must be <= 10 @@ -75,12 +74,12 @@ bool QuickMenu_Open(string mode, string submenu) if(mode == "file") { if(autocvar_hud_panel_quickmenu_file == "" || autocvar_hud_panel_quickmenu_file == "0") - printf("No file name is set in hud_panel_quickmenu_file, loading default quickmenu\n"); + LOG_INFO("No file name is set in hud_panel_quickmenu_file, loading default quickmenu\n"); else { fh = fopen(autocvar_hud_panel_quickmenu_file, FILE_READ); if(fh < 0) - printf("Couldn't open file \"%s\", loading default quickmenu\n", autocvar_hud_panel_quickmenu_file); + LOG_INFOF("Couldn't open file \"%s\", loading default quickmenu\n", autocvar_hud_panel_quickmenu_file); } if(fh < 0) mode = "default"; @@ -152,7 +151,7 @@ bool QuickMenu_Open(string mode, string submenu) } else { - printf("Unrecognized mode %s\n", mode); + LOG_WARNINGF("Unrecognized mode %s\n", mode); return false; } @@ -286,7 +285,7 @@ bool QuickMenu_Page_Load(string target_submenu, int new_page) // printf("^1 skipping %s\n", s); } if(QuickMenu_Buffer_Index == QuickMenu_Buffer_Size) - printf("Couldn't find submenu \"%s\"\n", z_submenu); + LOG_WARNINGF("Couldn't find submenu \"%s\"\n", z_submenu); } // only the last page can contain up to QUICKMENU_MAXLINES entries @@ -574,7 +573,7 @@ void HUD_Quickmenu_DrawEntry(vector pos, string desc, string option, vector font drawcolorcodedstring(pos, entry, fontsize, panel_fg_alpha, DRAWFLAG_ADDITIVE); } -void HUD_QuickMenu(void) +void HUD_QuickMenu() { if(!autocvar__hud_configure) { @@ -859,9 +858,9 @@ void QuickMenu_Default(string target_submenu) if(target_submenu != "" && !target_submenu_found) { - printf("Couldn't find submenu \"%s\"\n", target_submenu); + LOG_WARNINGF("Couldn't find submenu \"%s\"\n", target_submenu); if(prvm_language != "en") - printf("^3Warning: submenu must be in English\n", target_submenu); + LOG_WARNINGF("^3Warning: submenu must be in English\n", target_submenu); QuickMenu_Buffer_Size = 0; } } diff --git a/qcsrc/client/rubble.qc b/qcsrc/client/rubble.qc deleted file mode 100644 index 5af2e23507..0000000000 --- a/qcsrc/client/rubble.qc +++ /dev/null @@ -1,54 +0,0 @@ -#include "rubble.qh" - -// LordHavoc: rewrote this file, it was really bad code - -void RubbleLimit(string cname, float limit, void() deleteproc) -{SELFPARAM(); - entity e; - entity oldest; - float c; - float oldesttime; - - // remove rubble of the same type if it's at the limit - // remove multiple rubble if the limit has been decreased - while(1) - { - e = findchain(classname,cname); - if (e == world) - break; - // walk the list and count the entities, find the oldest - // initialize our search with the first entity - c = 1; - oldest = e; - oldesttime = e.creationtime; - e = e.chain; - // compare to all other matching entities - while (e) - { - c = c + 1; - if (oldesttime > e.creationtime) - { - oldesttime = e.creationtime; - oldest = e; - } - e = e.chain; - } - - // stop if there are less than the limit already - if (c <= limit) - break; - - // delete this oldest one and search again - WITH(entity, self, oldest, deleteproc()); - } -} - -entity RubbleNew(string cname) -{ - entity e; - // spawn a new entity and return it - e = spawn(); - e.classname = cname; - e.creationtime = time; - return e; -} diff --git a/qcsrc/client/rubble.qh b/qcsrc/client/rubble.qh deleted file mode 100644 index 9441168025..0000000000 --- a/qcsrc/client/rubble.qh +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CLIENT_RUBBLE_H -#define CLIENT_RUBBLE_H -entityclass(Rubble); -class(Rubble) .float creationtime; -void RubbleLimit(string cname, float limit, void() deleteproc); -entity RubbleNew(string cname); -#endif diff --git a/qcsrc/client/scoreboard.qc b/qcsrc/client/scoreboard.qc index adbac93c8f..681183594f 100644 --- a/qcsrc/client/scoreboard.qc +++ b/qcsrc/client/scoreboard.qc @@ -1,7 +1,7 @@ #include "scoreboard.qh" #include "quickmenu.qh" -#include "hud.qh" +#include "hud/all.qh" #include "../common/constants.qh" #include "../common/mapinfo.qh" @@ -33,6 +33,8 @@ string TranslateScoresLabel(string l) case "captime": return CTX(_("SCO^captime")); case "deaths": return CTX(_("SCO^deaths")); case "destroyed": return CTX(_("SCO^destroyed")); + case "dmg": return CTX(_("SCO^dmg")); + case "dmgtaken": return CTX(_("SCO^dmgtaken")); case "drops": return CTX(_("SCO^drops")); case "faults": return CTX(_("SCO^faults")); case "fckills": return CTX(_("SCO^fckills")); @@ -260,6 +262,8 @@ void Cmd_HUD_Help() LOG_INFO(_("^3suicides^7 Number of suicides\n")); LOG_INFO(_("^3frags^7 kills - suicides\n")); LOG_INFO(_("^3kd^7 The kill-death ratio\n")); + LOG_INFO(_("^3dmg^7 The total damage done\n")); + LOG_INFO(_("^3dmgtaken^7 The total damage taken\n")); LOG_INFO(_("^3sum^7 frags - deaths\n")); LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n")); LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n")); @@ -300,6 +304,7 @@ void Cmd_HUD_Help() #define HUD_DefaultColumnLayout() \ "ping pl name | " \ "-teams,rc,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,rc,ka/suicides +ft,tdm/suicides -rc,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \ +"dmg dmgtaken " \ "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes " \ "+lms/lives +lms/rank " \ "+kh/caps +kh/pushes +kh/destroyed " \ @@ -394,6 +399,8 @@ void Cmd_HUD_SetFields(float argc) case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break; case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break; case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break; + case "dmg": hud_field[hud_num_fields] = SP_DMG; break; + case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break; default: { for(j = 0; j < MAX_SCORE; ++j) @@ -598,6 +605,20 @@ string HUD_GetField(entity pl, int field) } return ftos(f); + case SP_DMG: + num = pl.(scores[SP_DMG]); + denom = 1000; + + str = sprintf("%.1f k", num/denom); + return str; + + case SP_DMGTAKEN: + num = pl.(scores[SP_DMGTAKEN]); + denom = 1000; + + str = sprintf("%.1f k", num/denom); + return str; + default: tmp = pl.(scores[field]); f = scores_flags[field]; @@ -974,35 +995,28 @@ float HUD_WouldDrawScoreboard() { float average_accuracy; vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) -{SELFPARAM(); +{ + SELFPARAM(); WepSet weapons_stat = WepSet_GetFromStat(); WepSet weapons_inmap = WepSet_GetFromStat_InMap(); float initial_posx = pos.x; - int i; - float weapon_stats; int disownedcnt = 0; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - { + for (int i = WEP_FIRST; i <= WEP_LAST; ++i) { setself(get_weaponinfo(i)); - if(!self.weapon) - continue; + if (!self.weapon) continue; - weapon_stats = weapon_accuracy[i-WEP_FIRST]; + int weapon_stats = weapon_accuracy[i - WEP_FIRST]; - if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i))) + if (weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i))) ++disownedcnt; } int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt; + if (weapon_cnt <= 0) return pos; - if(weapon_cnt <= 0) - return pos; - - int rows; - if(autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5)) + int rows = 1; + if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5)) rows = 2; - else - rows = 1; int columnns = ceil(weapon_cnt / rows); float height = 40; @@ -1023,24 +1037,24 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL); // column highlighting - for(i = 0; i < columnns; ++i) + for (int i = 0; i < columnns; ++i) { - if(!(i % 2)) + if ((i % 2) == 0) drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL); } // row highlighting - for(i = 0; i < rows; ++i) + for (int i = 0; i < rows; ++i) { drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL); } average_accuracy = 0; int weapons_with_stats = 0; - if(rows == 2) + if (rows == 2) pos.x += weapon_width / 2; - if(autocvar_scoreboard_accuracy_nocolors) + if (autocvar_scoreboard_accuracy_nocolors) rgb = '1 1 1'; else Accuracy_LoadColors(); @@ -1048,19 +1062,17 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) float oldposx = pos.x; vector tmpos = pos; - int column; - for(i = WEP_FIRST, column = 0; i <= WEP_LAST; ++i) - { + int column = 0; + for (int i = WEP_FIRST; i <= WEP_LAST; ++i) { setself(get_weaponinfo(i)); - if (!self.weapon) - continue; - weapon_stats = weapon_accuracy[i-WEP_FIRST]; + if (!self.weapon) continue; + int weapon_stats = weapon_accuracy[i - WEP_FIRST]; - if(weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i))) + if (weapon_stats < 0 && !(weapons_stat & WepSet_FromWeapon(i) || weapons_inmap & WepSet_FromWeapon(i))) continue; float weapon_alpha; - if(weapon_stats >= 0) + if (weapon_stats >= 0) weapon_alpha = scoreboard_alpha_fg; else weapon_alpha = 0.2 * scoreboard_alpha_fg; @@ -1068,7 +1080,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) // weapon icon drawpic_aspect_skin(tmpos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); // the accuracy - if(weapon_stats >= 0) { + if (weapon_stats >= 0) { weapons_with_stats += 1; average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy @@ -1085,7 +1097,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) } tmpos.x += weapon_width * rows; pos.x += weapon_width * rows; - if(rows == 2 && column == columnns - 1) { + if (rows == 2 && column == columnns - 1) { tmpos.x = oldposx; tmpos.y += height; pos.y += height; @@ -1093,7 +1105,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) ++column; } - if(weapons_with_stats) + if (weapons_with_stats) average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5); pos.y += height; @@ -1349,7 +1361,7 @@ void HUD_DrawScoreboard() } pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size); } - else if(autocvar_scoreboard_accuracy && spectatee_status == 0 && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) { + else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) { if(teamplay) pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size); else diff --git a/qcsrc/client/scoreboard.qh b/qcsrc/client/scoreboard.qh index 5b5f812a0a..4e43cb65fd 100644 --- a/qcsrc/client/scoreboard.qh +++ b/qcsrc/client/scoreboard.qh @@ -11,5 +11,5 @@ void HUD_DrawScoreboard(); void HUD_InitScores(); void HUD_UpdatePlayerPos(entity pl); void HUD_UpdateTeamPos(entity Team); -float HUD_WouldDrawScoreboard(void); +float HUD_WouldDrawScoreboard(); #endif diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 80a15ad38b..3005215453 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -1,6 +1,6 @@ #include "shownames.qh" -#include "hud.qh" +#include "hud/all.qh" #include "../common/constants.qh" #include "../common/mapinfo.qh" @@ -202,8 +202,7 @@ void Draw_ShowNames_All() e = shownames_ent[i]; if(!e) { - e = spawn(); - e.classname = "shownames_tag"; + e = new(shownames_tag); e.sv_entnum = i+1; shownames_ent[i] = e; } diff --git a/qcsrc/client/t_items.qc b/qcsrc/client/t_items.qc index f4096b87a6..b0e1315f69 100644 --- a/qcsrc/client/t_items.qc +++ b/qcsrc/client/t_items.qc @@ -1,5 +1,4 @@ -#include "../common/buffs/all.qh" #include "../common/movetypes/movetypes.qh" #include "../common/weapons/all.qh" #include "../lib/csqcmodel/cl_model.qh" diff --git a/qcsrc/client/teamradar.qc b/qcsrc/client/teamradar.qc index c866a1b73e..af43dae037 100644 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@ -1,6 +1,6 @@ #include "teamradar.qh" -#include "hud.qh" +#include "hud/all.qh" #include "../common/mutators/mutator/waypoints/all.qh" @@ -58,12 +58,6 @@ vector teamradar_texcoord_to_3dcoord(vector in,float z) return out; } -vector yinvert(vector v) -{ - v.y = 1 - v.y; - return v; -} - void draw_teamradar_background(float fg) { float fga; @@ -200,8 +194,8 @@ void teamradar_loadcvars() // radar links -void Ent_RadarLink() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_RADARLINK, bool isnew) +{ int sendflags = ReadByte(); InterpolateOrigin_Undo(); @@ -229,5 +223,7 @@ void Ent_RadarLink() self.team = ReadByte(); } + return = true; + InterpolateOrigin_Note(); } diff --git a/qcsrc/client/teamradar.qh b/qcsrc/client/teamradar.qh index b882f39fbd..ca7ec87b59 100644 --- a/qcsrc/client/teamradar.qh +++ b/qcsrc/client/teamradar.qh @@ -35,8 +35,6 @@ vector teamradar_texcoord_to_2dcoord(vector in); vector teamradar_texcoord_to_3dcoord(vector in,float z); -vector yinvert(vector v); - void draw_teamradar_background(float fg); void draw_teamradar_player(vector coord3d, vector pangles, vector rgb); @@ -47,8 +45,4 @@ void draw_teamradar_link(vector start, vector end, int colors); void teamradar_loadcvars(); -// radar links - -void Ent_RadarLink(); - #endif diff --git a/qcsrc/client/tuba.qc b/qcsrc/client/tuba.qc index ed2964936a..8f8fc4db5f 100644 --- a/qcsrc/client/tuba.qc +++ b/qcsrc/client/tuba.qc @@ -3,7 +3,7 @@ #include "../common/constants.qh" -#define TUBA_STARTNOTE(i, n) W_Sound(strcat("tuba", (i ? ftos(i) : ""), "_loopnote", ftos(n))) +#define TUBA_STARTNOTE(i, n) _Sound_fixpath(W_Sound(strcat("tuba", (i ? ftos(i) : ""), "_loopnote", ftos(n)))) const int TUBA_MIN = -18; const int TUBA_MAX = 27; @@ -103,8 +103,8 @@ void Ent_TubaNote_StopSound() self.enemy = world; } -void Ent_TubaNote(bool isNew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TUBANOTE, bool isNew) +{ bool upd = false; int f = ReadByte(); if (f & 1) { @@ -118,11 +118,9 @@ void Ent_TubaNote(bool isNew) Ent_TubaNote_StopSound(); } } else { - self.enemy = spawn(); - self.enemy.classname = "tuba_note"; + self.enemy = new(tuba_note); if (Tuba_PitchStep) { - self.enemy.enemy = spawn(); - self.enemy.enemy.classname = "tuba_note_2"; + self.enemy.enemy = new(tuba_note_2); } isNew = true; } @@ -154,9 +152,10 @@ void Ent_TubaNote(bool isNew) if (upd) { Ent_TubaNote_UpdateSound(); } + return true; } -void Tuba_Precache() +PRECACHE(Tuba) { Tuba_PitchStep = autocvar_g_balance_tuba_pitchstep; if (Tuba_PitchStep) { diff --git a/qcsrc/client/tuba.qh b/qcsrc/client/tuba.qh index e936dcd6ec..0a310c359a 100644 --- a/qcsrc/client/tuba.qh +++ b/qcsrc/client/tuba.qh @@ -1,7 +1,5 @@ #ifndef CLIENT_TUBA_H #define CLIENT_TUBA_H -void Ent_TubaNote(bool isNew); -void Tuba_Precache(); entityclass(Tuba); diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index c7144372a3..699e2d8ade 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -1,8 +1,7 @@ #include "announcer.qh" #include "hook.qh" -#include "hud.qh" -#include "hud_config.qh" +#include "hud/all.qh" #include "mapvoting.qh" #include "scoreboard.qh" #include "shownames.qh" @@ -11,9 +10,9 @@ #include "mutators/events.qh" #include "../common/constants.qh" +#include "../common/debug.qh" #include "../common/mapinfo.qh" #include "../common/gamemodes/all.qh" -#include "../common/nades/all.qh" #include "../common/stats.qh" #include "../common/triggers/target/music.qh" #include "../common/teams.qh" @@ -110,8 +109,8 @@ void Porto_Draw(entity this) void Porto_Init() { - porto = spawn(); - porto.classname = "porto"; + porto = new(porto); + make_pure(porto); porto.draw = Porto_Draw; porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; } @@ -278,11 +277,11 @@ const float SHOTTYPE_HITENEMY = 4; void TrueAim_Init() { - trueaim = spawn(); - trueaim.classname = "trueaim"; + trueaim = new(trueaim); + make_pure(trueaim); trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - trueaim_rifle = spawn(); - trueaim_rifle.classname = "trueaim_rifle"; + trueaim_rifle = new(trueaim_rifle); + make_pure(trueaim_rifle); trueaim_rifle.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE; } @@ -398,7 +397,7 @@ float TrueAimCheck() return SHOTTYPE_HITWORLD; } -void PostInit(void); +void PostInit(); void CSQC_Demo_Camera(); float HUD_WouldDrawScoreboard(); float camera_mode; @@ -830,7 +829,7 @@ void HUD_Crosshair() vortex_charge = getstatf(STAT_VORTEX_CHARGE); vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL); - float arc_heat = getstatf(STAT_ARC_HEAT); + float arc_heat = STAT(ARC_HEAT); if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game vortex_charge_movingavg = vortex_charge; @@ -1005,10 +1004,18 @@ void HUD_Crosshair() void HUD_Draw() { - if(getstati(STAT_FROZEN)) - drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); - else if (getstatf(STAT_HEALING_ORB)>time) - drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, NADE_TYPE_HEAL.m_color, autocvar_hud_colorflash_alpha*getstatf(STAT_HEALING_ORB_ALPHA), DRAWFLAG_ADDITIVE); + vector rgb = '0 0 0'; + float a = 1; + if (MUTATOR_CALLHOOK(HUD_Draw_overlay)) + { + rgb = MUTATOR_ARGV(0, vector); + a = MUTATOR_ARGV(0, float); + } + else if(getstati(STAT_FROZEN)) + { + rgb = ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'); + } + drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, rgb, autocvar_hud_colorflash_alpha * a, DRAWFLAG_ADDITIVE); if(!intermission) if(getstatf(STAT_NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death { @@ -1064,6 +1071,7 @@ void CSQC_UpdateView(float w, float h) ++framecount; + stats_get(); hud = getstati(STAT_HUD); if(hud != HUD_NORMAL && lasthud == HUD_NORMAL) @@ -1387,9 +1395,6 @@ void CSQC_UpdateView(float w, float h) ColorTranslateMode = autocvar_cl_stripcolorcodes; - // next WANTED weapon (for HUD) - switchweapon = getstati(STAT_SWITCHWEAPON); - // currently switching-to weapon (for crosshair) switchingweapon = getstati(STAT_SWITCHINGWEAPON); @@ -1516,13 +1521,11 @@ void CSQC_UpdateView(float w, float h) if(!nightvision_noise) { - nightvision_noise = spawn(); - nightvision_noise.classname = "nightvision_noise"; + nightvision_noise = new(nightvision_noise); } if(!nightvision_noise2) { - nightvision_noise2 = spawn(); - nightvision_noise2.classname = "nightvision_noise2"; + nightvision_noise2 = new(nightvision_noise2); } // color tint in yellow @@ -1797,7 +1800,7 @@ void CSQC_UpdateView(float w, float h) } // edge detection postprocess handling done second (used by hud_powerup) - float sharpen_intensity = 0, strength_finished = getstatf(STAT_STRENGTH_FINISHED), invincible_finished = getstatf(STAT_INVINCIBLE_FINISHED); + float sharpen_intensity = 0, strength_finished = STAT(STRENGTH_FINISHED), invincible_finished = STAT(INVINCIBLE_FINISHED); if (strength_finished - time > 0) { sharpen_intensity += (strength_finished - time); } if (invincible_finished - time > 0) { sharpen_intensity += (invincible_finished - time); } @@ -1823,9 +1826,6 @@ void CSQC_UpdateView(float w, float h) else if(cvar("r_glsl_postprocess") == 2) cvar_set("r_glsl_postprocess", "0"); - if(menu_visible) - menu_show(); - /*if(gametype == MAPINFO_TYPE_CTF) { ctf_view(); @@ -1836,6 +1836,7 @@ void CSQC_UpdateView(float w, float h) WITH(entity, self, e, e.draw2d(e)); } Draw_ShowNames_All(); + Debug_Draw(); scoreboard_active = HUD_WouldDrawScoreboard(); diff --git a/qcsrc/client/wall.qc b/qcsrc/client/wall.qc index 9da9bb5b65..2623470c16 100644 --- a/qcsrc/client/wall.qc +++ b/qcsrc/client/wall.qc @@ -114,8 +114,8 @@ void Ent_Wall_Remove() self.bgmscript = string_null; } -void Ent_Wall() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_WALL, bool isnew) +{ int f; var .vector fld; @@ -222,6 +222,8 @@ void Ent_Wall() BGMScript_InitEntity(self); } + return = true; + InterpolateOrigin_Note(); self.saved = self.(fld); diff --git a/qcsrc/client/wall.qh b/qcsrc/client/wall.qh index e05bb615ed..16f87fdd97 100644 --- a/qcsrc/client/wall.qh +++ b/qcsrc/client/wall.qh @@ -20,5 +20,4 @@ void Ent_Wall_Draw(entity this); void Ent_Wall_Remove(); -void Ent_Wall(); #endif diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc index 807d95cb4c..9609760c4a 100644 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@ -6,7 +6,6 @@ #include "../mutators/events.qh" #include "../../common/constants.qh" -#include "../../common/nades/all.qh" #include "../../common/movetypes/movetypes.qh" #include "../../lib/csqcmodel/interpolate.qh" @@ -18,7 +17,8 @@ .vector colormod; void SUB_Stop() -{SELFPARAM(); +{ + SELFPARAM(); self.move_velocity = self.move_avelocity = '0 0 0'; self.move_movetype = MOVETYPE_NONE; } @@ -37,17 +37,19 @@ void Projectile_DrawTrail(entity this, vector to) this.trail_oldtime = time; // force the effect even for stationary firemine - if(this.cnt == PROJECTILE_FIREMINE) - if(from == to) + if (this.cnt == PROJECTILE_FIREMINE) + if (from == to) from.z += 1; if (this.traileffect) { particles_alphamin = particles_alphamax = particles_fade = sqrt(this.alpha); - boxparticles(particleeffectnum(Effects[this.traileffect]), this, from, to, this.velocity, this.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL); + boxparticles(particleeffectnum(Effects_from(this.traileffect)), this, from, to, this.velocity, this.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL); } } +bool Projectile_isnade(int proj); // TODO: remove + void Projectile_Draw(entity this) { vector rot; @@ -59,20 +61,20 @@ void Projectile_Draw(entity this) f = self.move_flags; - if(self.count & 0x80) + if (self.count & 0x80) { - //self.move_flags &= ~FL_ONGROUND; - if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY) + // self.move_flags &= ~FL_ONGROUND; + if (self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY) Movetype_Physics_NoMatchServer(); - // the trivial movetypes do not have to match the - // server's ticrate as they are ticrate independent - // NOTE: this assumption is only true if MOVETYPE_FLY - // projectiles detonate on impact. If they continue - // moving, we might still be ticrate dependent. + // the trivial movetypes do not have to match the + // server's ticrate as they are ticrate independent + // NOTE: this assumption is only true if MOVETYPE_FLY + // projectiles detonate on impact. If they continue + // moving, we might still be ticrate dependent. else Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); - if(!(self.move_flags & FL_ONGROUND)) - if(self.velocity != '0 0 0') + if (!(self.move_flags & FL_ONGROUND)) + if (self.velocity != '0 0 0') self.move_angles = self.angles = vectoangles(self.velocity); } else @@ -80,7 +82,7 @@ void Projectile_Draw(entity this) InterpolateOrigin_Do(); } - if(self.count & 0x80) + if (self.count & 0x80) { drawn = (time >= self.spawntime - 0.02); t = max(time, self.spawntime); @@ -91,27 +93,27 @@ void Projectile_Draw(entity this) t = time; } - if(!(f & FL_ONGROUND)) + if (!(f & FL_ONGROUND)) { rot = '0 0 0'; - switch(self.cnt) + switch (self.cnt) { /* case PROJECTILE_GRENADE: - rot = '-2000 0 0'; // forward - break; + rot = '-2000 0 0'; // forward + break; */ case PROJECTILE_GRENADE_BOUNCING: rot = '0 -1000 0'; // sideways break; case PROJECTILE_HOOKBOMB: - rot = '1000 0 0'; // forward + rot = '1000 0 0'; // forward break; default: break; } - if(Nade_FromProjectile(self.cnt) != NADE_TYPE_Null) + if (Projectile_isnade(self.cnt)) rot = self.avelocity; self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime))); @@ -124,12 +126,12 @@ void Projectile_Draw(entity this) a = 1 - (time - self.fade_time) * self.fade_rate; self.alpha = bound(0, self.alphamod * a, 1); - if(self.alpha <= 0) + if (self.alpha <= 0) drawn = 0; self.renderflags = 0; trailorigin = self.origin; - switch(self.cnt) + switch (self.cnt) { case PROJECTILE_GRENADE: case PROJECTILE_GRENADE_BOUNCING: @@ -139,20 +141,20 @@ void Projectile_Draw(entity this) break; } - if(Nade_FromProjectile(self.cnt) != NADE_TYPE_Null) + if (Projectile_isnade(self.cnt)) trailorigin += v_up * 4; - if(drawn) + if (drawn) Projectile_DrawTrail(self, trailorigin); else Projectile_ResetTrail(self, trailorigin); self.drawmask = 0; - if(!drawn) + if (!drawn) return; - switch(self.cnt) + switch (self.cnt) { // Possibly add dlights here. default: @@ -163,8 +165,9 @@ void Projectile_Draw(entity this) } void loopsound(entity e, int ch, string samp, float vol, float attn) -{SELFPARAM(); - if(self.silent) +{ + SELFPARAM(); + if (self.silent) return; _sound(e, ch, samp, vol, attn); @@ -172,18 +175,17 @@ void loopsound(entity e, int ch, string samp, float vol, float attn) } void Ent_RemoveProjectile() -{SELFPARAM(); - if(self.count & 0x80) +{ + SELFPARAM(); + if (self.count & 0x80) { tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self); Projectile_DrawTrail(self, trace_endpos); } } -void Ent_Projectile() -{SELFPARAM(); - int f; - +NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) +{ // projectile properties: // kind (interpolated, or clientside) // @@ -198,21 +200,21 @@ void Ent_Projectile() // // projectiles don't send angles, because they always follow the velocity - f = ReadByte(); + int f = ReadByte(); self.count = (f & 0x80); self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN; self.solid = SOLID_TRIGGER; - //self.effects = EF_NOMODELFLAGS; + // self.effects = EF_NOMODELFLAGS; // this should make collisions with bmodels more exact, but it leads to // projectiles no longer being able to lie on a bmodel self.move_nomonsters = MOVE_WORLDONLY; - if(f & 0x40) + if (f & 0x40) self.move_flags |= FL_ONGROUND; else self.move_flags &= ~FL_ONGROUND; - if(!self.move_time) + if (!self.move_time) { // for some unknown reason, we don't need to care for // sv_gameplayfix_delayprojectiles here. @@ -220,38 +222,40 @@ void Ent_Projectile() self.spawntime = time; } else + { self.move_time = max(self.move_time, time); + } - if(!(self.count & 0x80)) + if (!(self.count & 0x80)) InterpolateOrigin_Undo(); - if(f & 1) + if (f & 1) { self.origin_x = ReadCoord(); self.origin_y = ReadCoord(); self.origin_z = ReadCoord(); setorigin(self, self.origin); - if(self.count & 0x80) + if (self.count & 0x80) { self.velocity_x = ReadCoord(); self.velocity_y = ReadCoord(); self.velocity_z = ReadCoord(); - if(f & 0x10) + if (f & 0x10) self.gravity = ReadCoord(); else - self.gravity = 0; // none + self.gravity = 0; // none self.move_origin = self.origin; self.move_velocity = self.velocity; } - if(time == self.spawntime || (self.count & 0x80) || (f & 0x08)) + if (time == self.spawntime || (self.count & 0x80) || (f & 0x08)) { self.trail_oldorigin = self.origin; - if(!(self.count & 0x80)) + if (!(self.count & 0x80)) InterpolateOrigin_Reset(); } - if(f & 0x20) + if (f & 0x20) { self.fade_time = time + ReadByte() * ticrate; self.fade_rate = 1 / (ReadByte() * ticrate); @@ -265,7 +269,7 @@ void Ent_Projectile() self.team = ReadByte() - 1; } - if(f & 2) + if (f & 2) { self.cnt = ReadByte(); @@ -274,8 +278,9 @@ void Ent_Projectile() self.scale = 1; self.traileffect = 0; - switch (self.cnt) { -#define CASE(id) case PROJECTILE_##id: setmodel(self, MDL_PROJECTILE_##id); + switch (self.cnt) + { + #define CASE(id) case PROJECTILE_##id: setmodel(self, MDL_PROJECTILE_##id); CASE(ELECTRO) self.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break; CASE(ROCKET) self.traileffect = EFFECT_TR_ROCKET.m_id; self.scale = 2; break; CASE(CRYLINK) self.traileffect = EFFECT_TR_CRYLINKPLASMA.m_id; break; @@ -291,7 +296,6 @@ void Ent_Projectile() CASE(HOOKBOMB) self.traileffect = EFFECT_TR_KNIGHTSPIKE.m_id; break; CASE(HAGAR) self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break; CASE(HAGAR_BOUNCING) self.traileffect = EFFECT_HAGAR_ROCKET.m_id; self.scale = 0.75; break; - CASE(NAPALM_FOUNTAIN) // fallthrough // sself.modelindex = 0; self.traileffect = _particleeffectnum("torch_small"); break; CASE(FIREBALL) self.modelindex = 0; self.traileffect = EFFECT_FIREBALL.m_id; break; // particle effect is good enough CASE(FIREMINE) self.modelindex = 0; self.traileffect = EFFECT_FIREMINE.m_id; break; // particle effect is good enough CASE(TAG) self.traileffect = EFFECT_TR_ROCKET.m_id; break; @@ -317,16 +321,9 @@ void Ent_Projectile() CASE(ROCKETMINSTA_LASER) self.traileffect = EFFECT_ROCKETMINSTA_LASER(self.team).m_id; break; #undef CASE default: - if(MUTATOR_CALLHOOK(Ent_Projectile, self)) + if (MUTATOR_CALLHOOK(Ent_Projectile, self)) break; - if (Nade_FromProjectile(self.cnt) != NADE_TYPE_Null) - { - setmodel(self, MDL_PROJECTILE_NADE); - entity trail = Nade_TrailEffect(self.cnt, self.team); - if (trail.eent_eff_name) self.traileffect = trail.m_id; - break; - } error("Received invalid CSQC projectile, can't work with this!"); break; } @@ -338,7 +335,7 @@ void Ent_Projectile() self.move_movetype = MOVETYPE_TOSS; self.alphamod = 1; - switch(self.cnt) + switch (self.cnt) { case PROJECTILE_ELECTRO: // only new engines support sound moving with object @@ -398,7 +395,6 @@ void Ent_Projectile() self.move_movetype = MOVETYPE_BOUNCE; self.move_touch = func_null; break; - case PROJECTILE_NAPALM_FOUNTAIN: case PROJECTILE_FIREBALL: loopsound(self, CH_SHOTS_SINGLE, SND(FIREBALL_FLY2), VOL_BASE, ATTEN_NORM); self.mins = '-16 -16 -16'; @@ -424,76 +420,61 @@ void Ent_Projectile() self.mins = '-4 -4 -4'; self.maxs = '4 4 4'; break; - case PROJECTILE_RAPTORBOMB: + case PROJECTILE_RAPTORBOMB: self.mins = '-3 -3 -3'; self.maxs = '3 3 3'; break; - case PROJECTILE_RAPTORBOMBLET: + case PROJECTILE_RAPTORBOMBLET: break; - case PROJECTILE_RAPTORCANNON: + case PROJECTILE_RAPTORCANNON: break; - case PROJECTILE_SPIDERROCKET: - loopsound(self, CH_SHOTS_SINGLE, SND(TAG_ROCKET_FLY), VOL_BASE, ATTEN_NORM); - break; - case PROJECTILE_WAKIROCKET: - loopsound(self, CH_SHOTS_SINGLE, SND(TAG_ROCKET_FLY), VOL_BASE, ATTEN_NORM); + case PROJECTILE_SPIDERROCKET: + loopsound(self, CH_SHOTS_SINGLE, SND(TAG_ROCKET_FLY), VOL_BASE, ATTEN_NORM); break; - /* - case PROJECTILE_WAKICANNON: + case PROJECTILE_WAKIROCKET: + loopsound(self, CH_SHOTS_SINGLE, SND(TAG_ROCKET_FLY), VOL_BASE, ATTEN_NORM); break; + /* + case PROJECTILE_WAKICANNON: + break; case PROJECTILE_BUMBLE_GUN: - // only new engines support sound moving with object - loopsound(self, CH_SHOTS_SINGLE, SND(ELECTRO_FLY), VOL_BASE, ATTEN_NORM); - self.mins = '0 0 -4'; - self.maxs = '0 0 -4'; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; - self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; - break; + // only new engines support sound moving with object + loopsound(self, CH_SHOTS_SINGLE, SND(ELECTRO_FLY), VOL_BASE, ATTEN_NORM); + self.mins = '0 0 -4'; + self.maxs = '0 0 -4'; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; + self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; + break; */ default: break; } - if(Nade_FromProjectile(self.cnt) != NADE_TYPE_Null) - { - entity nade_type = Nade_FromProjectile(self.cnt); - self.mins = '-16 -16 -16'; - self.maxs = '16 16 16'; - self.colormod = nade_type.m_color; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.scale = 1.5; - self.avelocity = randomvec() * 720; - - if(nade_type == NADE_TYPE_TRANSLOCATE || nade_type == NADE_TYPE_SPAWN) - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - else - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - } - MUTATOR_CALLHOOK(EditProjectile, self); setsize(self, self.mins, self.maxs); } - if(self.gravity) + return = true; + + if (self.gravity) { - if(self.move_movetype == MOVETYPE_FLY) + if (self.move_movetype == MOVETYPE_FLY) self.move_movetype = MOVETYPE_TOSS; - if(self.move_movetype == MOVETYPE_BOUNCEMISSILE) + if (self.move_movetype == MOVETYPE_BOUNCEMISSILE) self.move_movetype = MOVETYPE_BOUNCE; } else { - if(self.move_movetype == MOVETYPE_TOSS) + if (self.move_movetype == MOVETYPE_TOSS) self.move_movetype = MOVETYPE_FLY; - if(self.move_movetype == MOVETYPE_BOUNCE) + if (self.move_movetype == MOVETYPE_BOUNCE) self.move_movetype = MOVETYPE_BOUNCEMISSILE; } - if(!(self.count & 0x80)) + if (!(self.count & 0x80)) InterpolateOrigin_Note(); self.classname = "csqcprojectile"; @@ -501,7 +482,7 @@ void Ent_Projectile() self.entremove = Ent_RemoveProjectile; } -void Projectile_Precache() +PRECACHE(Projectiles) { MUTATOR_CALLHOOK(PrecacheProjectiles); } diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh index 5a0deb4990..699f48938e 100644 --- a/qcsrc/client/weapons/projectile.qh +++ b/qcsrc/client/weapons/projectile.qh @@ -2,20 +2,20 @@ #define CLIENT_WEAPONS_PROJECTILE_H entityclass(Projectile); -class(Projectile) .int traileffect; +class(Projectile).int traileffect; -class(Projectile) .vector iorigin1, iorigin2; -class(Projectile) .float spawntime; -class(Projectile) .vector trail_oldorigin; -class(Projectile) .float trail_oldtime; -class(Projectile) .float fade_time, fade_rate; +class(Projectile).vector iorigin1, iorigin2; +class(Projectile).float spawntime; +class(Projectile).vector trail_oldorigin; +class(Projectile).float trail_oldtime; +class(Projectile).float fade_time, fade_rate; -class(Projectile) .float alphamod; -class(Projectile) .int count; // set if clientside projectile -class(Projectile) .int cnt; // sound index -class(Projectile) .float gravity; -class(Projectile) .int snd_looping; -class(Projectile) .bool silent; +class(Projectile).float alphamod; +class(Projectile).int count; // set if clientside projectile +class(Projectile).int cnt; // sound index +class(Projectile).float gravity; +class(Projectile).int snd_looping; +class(Projectile).bool silent; void SUB_Stop(); @@ -29,8 +29,4 @@ void loopsound(entity e, int ch, string samp, float vol, float attn); void Ent_RemoveProjectile(); -void Ent_Projectile(); - -void Projectile_Precache(); - #endif diff --git a/qcsrc/common/buffs/all.inc b/qcsrc/common/buffs/all.inc deleted file mode 100644 index 25fa72236c..0000000000 --- a/qcsrc/common/buffs/all.inc +++ /dev/null @@ -1,118 +0,0 @@ -REGISTER_BUFF(AMMO) { - this.m_prettyName = _("Ammo"); - this.m_name = "ammo"; - this.m_skin = 3; - this.m_color = '0.76 1 0.1'; -} -BUFF_SPAWNFUNCS(ammo, BUFF_AMMO) -BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_AMMO) - -REGISTER_BUFF(RESISTANCE) { - this.m_prettyName = _("Resistance"); - this.m_name = "resistance"; - this.m_skin = 0; - this.m_color = '0.36 1 0.07'; -} -BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE) -BUFF_SPAWNFUNC_Q3TA_COMPAT(resistance, BUFF_RESISTANCE) - -REGISTER_BUFF(SPEED) { - this.m_prettyName = _("Speed"); - this.m_name = "speed"; - this.m_skin = 9; - this.m_color = '0.1 1 0.84'; -} -BUFF_SPAWNFUNCS(speed, BUFF_SPEED) -BUFF_SPAWNFUNC_Q3TA_COMPAT(haste, BUFF_SPEED) -BUFF_SPAWNFUNC_Q3TA_COMPAT(scout, BUFF_SPEED) - -REGISTER_BUFF(MEDIC) { - this.m_prettyName = _("Medic"); - this.m_name = "medic"; - this.m_skin = 1; - this.m_color = '1 0.12 0'; -} -BUFF_SPAWNFUNCS(medic, BUFF_MEDIC) -BUFF_SPAWNFUNC_Q3TA_COMPAT(doubler, BUFF_MEDIC) -BUFF_SPAWNFUNC_Q3TA_COMPAT(medic, BUFF_MEDIC) - -REGISTER_BUFF(BASH) { - this.m_prettyName = _("Bash"); - this.m_name = "bash"; - this.m_skin = 5; - this.m_color = '1 0.39 0'; -} -BUFF_SPAWNFUNCS(bash, BUFF_BASH) - -REGISTER_BUFF(VAMPIRE) { - this.m_prettyName = _("Vampire"); - this.m_name = "vampire"; - this.m_skin = 2; - this.m_color = '1 0 0.24'; -} -BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE) - -REGISTER_BUFF(DISABILITY) { - this.m_prettyName = _("Disability"); - this.m_name = "disability"; - this.m_skin = 7; - this.m_color = '0.94 0.3 1'; -} -BUFF_SPAWNFUNCS(disability, BUFF_DISABILITY) - -REGISTER_BUFF(VENGEANCE) { - this.m_prettyName = _("Vengeance"); - this.m_name = "vengeance"; - this.m_skin = 15; - this.m_color = '1 0.23 0.61'; -} -BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE) - -REGISTER_BUFF(JUMP) { - this.m_prettyName = _("Jump"); - this.m_name = "jump"; - this.m_skin = 10; - this.m_color = '0.24 0.78 1'; -} -BUFF_SPAWNFUNCS(jump, BUFF_JUMP) - -REGISTER_BUFF(FLIGHT) { - this.m_prettyName = _("Flight"); - this.m_name = "flight"; - this.m_skin = 11; - this.m_color = '0.33 0.56 1'; -} -BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT) - -REGISTER_BUFF(INVISIBLE) { - this.m_prettyName = _("Invisible"); - this.m_name = "invisible"; - this.m_skin = 12; - this.m_color = '0.5 0.5 1'; -} -BUFF_SPAWNFUNCS(invisible, BUFF_INVISIBLE) -BUFF_SPAWNFUNC_Q3TA_COMPAT(invis, BUFF_INVISIBLE) - -REGISTER_BUFF(INFERNO) { - this.m_prettyName = _("Inferno"); - this.m_name = "inferno"; - this.m_skin = 16; - this.m_color = '1 0.62 0'; -} -BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO) - -REGISTER_BUFF(SWAPPER) { - this.m_prettyName = _("Swapper"); - this.m_name = "swapper"; - this.m_skin = 17; - this.m_color = '0.63 0.36 1'; -} -BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER) - -REGISTER_BUFF(MAGNET) { - this.m_prettyName = _("Magnet"); - this.m_name = "magnet"; - this.m_skin = 18; - this.m_color = '1 0.95 0.18'; -} -BUFF_SPAWNFUNCS(magnet, BUFF_MAGNET) diff --git a/qcsrc/common/buffs/all.qc b/qcsrc/common/buffs/all.qc deleted file mode 100644 index c6a6f49f02..0000000000 --- a/qcsrc/common/buffs/all.qc +++ /dev/null @@ -1,7 +0,0 @@ -#if defined(CSQC) - #include "../../client/defs.qh" -#elif defined(MENUQC) -#elif defined(SVQC) -#endif -#include "all.qh" - diff --git a/qcsrc/common/buffs/all.qh b/qcsrc/common/buffs/all.qh deleted file mode 100644 index 7099034705..0000000000 --- a/qcsrc/common/buffs/all.qh +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef BUFFS_ALL_H -#define BUFFS_ALL_H -// Welcome to the stuff behind the scenes -// Below, you will find the list of buffs -// Add new buffs here! -// Note: Buffs also need spawnfuncs, which are set below - -#include "../teams.qh" -#include "../util.qh" - -REGISTRY(Buffs, BIT(4)) -REGISTER_REGISTRY(RegisterBuffs) - -#define REGISTER_BUFF(id) \ - REGISTER(RegisterBuffs, BUFF, Buffs, id, m_id, NEW(Buff)); \ - REGISTER_INIT_POST(BUFF, id) { \ - this.netname = this.m_name; \ - this.m_itemid = BIT(this.m_id - 1); \ - this.m_sprite = strzone(strcat("buff-", this.m_name)); \ - } \ - REGISTER_INIT(BUFF, id) - -#include "../items/item/pickup.qh" -CLASS(Buff, Pickup) - /** bit index */ - ATTRIB(Buff, m_itemid, int, 0) - ATTRIB(Buff, m_name, string, "buff") - ATTRIB(Buff, m_color, vector, '1 1 1') - ATTRIB(Buff, m_prettyName, string, "Buff") - ATTRIB(Buff, m_skin, int, 0) - ATTRIB(Buff, m_sprite, string, "") - METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) { - returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name)); - } -#ifdef SVQC - METHOD(Buff, m_time, float(entity)); - float Buff_m_time(entity this) { return cvar(strcat("g_buffs_", this.netname, "_time")); } -#endif -ENDCLASS(Buff) - -#ifdef SVQC - .int buffs; - void buff_Init(entity ent); - void buff_Init_Compat(entity ent, entity replacement); - #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ - self.buffs = b.m_itemid; \ - self.team = t; \ - buff_Init(self); \ - } - #define BUFF_SPAWNFUNCS(e, b) \ - BUFF_SPAWNFUNC(e, b, 0) \ - BUFF_SPAWNFUNC(e##_team1, b, NUM_TEAM_1) \ - BUFF_SPAWNFUNC(e##_team2, b, NUM_TEAM_2) \ - BUFF_SPAWNFUNC(e##_team3, b, NUM_TEAM_3) \ - BUFF_SPAWNFUNC(e##_team4, b, NUM_TEAM_4) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(self, r); } -#else - #define BUFF_SPAWNFUNC(e, b, t) - #define BUFF_SPAWNFUNCS(e, b) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) -#endif - -REGISTER_BUFF(Null); -BUFF_SPAWNFUNCS(random, BUFF_Null) - -#include "all.inc" - -#endif diff --git a/qcsrc/common/command/all.qh b/qcsrc/common/command/all.qh index 756b36d598..d1eb3cf76e 100644 --- a/qcsrc/common/command/all.qh +++ b/qcsrc/common/command/all.qh @@ -2,16 +2,17 @@ #define COMMON_COMMANDS_ALL_H #include "command.qh" -REGISTRY(GENERIC_COMMANDS, 50) -REGISTER_REGISTRY(RegisterGENERIC_COMMANDS) -REGISTRY_SORT(GENERIC_COMMANDS, m_name, 0) +REGISTRY(GENERIC_COMMANDS, BITS(7)) +#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL) +REGISTER_REGISTRY(GENERIC_COMMANDS) +REGISTRY_SORT(GENERIC_COMMANDS, 0) #define GENERIC_COMMAND(id, description) \ CLASS(genericcommand_##id, Command) \ ATTRIB(genericcommand_##id, m_name, string, #id); \ ATTRIB(genericcommand_##id, m_description, string, description); \ ENDCLASS(genericcommand_##id) \ - REGISTER(RegisterGENERIC_COMMANDS, CMD_G, GENERIC_COMMANDS, id, m_id, NEW(genericcommand_##id)); \ + REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \ METHOD(genericcommand_##id, m_invokecmd, void(int request, int arguments, string command)) STATIC_INIT(GENERIC_COMMANDS_aliases) { diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 5412c22b4b..9a50894c04 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -28,7 +28,7 @@ // ========================================================= // used by generic commands for better help/usage information -string GetProgramCommandPrefix(void) +string GetProgramCommandPrefix() { #ifdef SVQC return "sv_cmd"; diff --git a/qcsrc/common/command/generic.qh b/qcsrc/common/command/generic.qh index e419824ec8..be84c407ca 100644 --- a/qcsrc/common/command/generic.qh +++ b/qcsrc/common/command/generic.qh @@ -22,7 +22,7 @@ void GenericCommand_macro_write_aliases(float fh); float GenericCommand(string command); // Returns command prefix specific for whatever program it is compiled in -string GetProgramCommandPrefix(void); +string GetProgramCommandPrefix(); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file #define CMD_Write(s) fputs(fh, s) diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 254246bdff..8d8e8df19f 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -26,33 +26,12 @@ // Revision 22: hook shot origin #define CSQC_REVISION 22 -const int AS_STRING = 1; -const int AS_INT = 2; -const int AS_FLOAT_TRUNCATED = 2; -const int AS_FLOAT = 8; - -const int TE_CSQC_MUTATOR = 99; -#define MUTATOR_HASH(s) crc16(true, s) -#define WriteMutator(to, s) do { \ - WriteByte(to, TE_CSQC_MUTATOR); \ - WriteLong(to, MUTATOR_HASH(#s)); \ -} while (0) -#define ReadMutator() ReadLong() -#define ReadMutatorEquals(read, s) (read == MUTATOR_HASH(#s)) -const int TE_CSQC_PICTURE = 100; -const int TE_CSQC_RACE = 101; -const int TE_CSQC_VORTEXBEAMPARTICLE = 103; -const int TE_CSQC_ARC = 104; -const int TE_CSQC_TEAMNAGGER = 105; -const int TE_CSQC_PINGPLREPORT = 106; -const int TE_CSQC_TARGET_MUSIC = 107; -const int TE_CSQC_WEAPONCOMPLAIN = 108; -const int TE_CSQC_VORTEX_SCOPE = 109; -const int TE_CSQC_MINELAYER_MAXMINES = 110; -const int TE_CSQC_HAGAR_MAXROCKETS = 111; -const int TE_CSQC_VEHICLESETUP = 112; -const int TE_CSQC_SVNOTICE = 113; -const int TE_CSQC_SHOCKWAVEPARTICLE = 114; +REGISTER_NET_TEMP(TE_CSQC_PICTURE) +REGISTER_NET_TEMP(TE_CSQC_RACE) +REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER) +REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT) +REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN) +REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP) const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder const int RACE_NET_CHECKPOINT_CLEAR = 1; @@ -69,66 +48,39 @@ const int RACE_NET_SERVER_RANKINGS = 11; const int RACE_NET_SERVER_STATUS = 12; const int RANKINGS_CNT = 15; -const int ENT_CLIENT = 0; -const int ENT_CLIENT_DEAD = 1; -const int ENT_CLIENT_ENTCS = 2; -const int ENT_CLIENT_SCORES_INFO = 3; -const int ENT_CLIENT_SCORES = 4; -const int ENT_CLIENT_TEAMSCORES = 5; -const int ENT_CLIENT_POINTPARTICLES = 6; -const int ENT_CLIENT_RAINSNOW = 7; -const int ENT_CLIENT_LASER = 8; -const int ENT_CLIENT_NAGGER = 9; // flags [votecalledvote] -const int ENT_CLIENT_RADARLINK = 11; // flags [startorigin] [endorigin] [startcolor+16*endcolor] -const int ENT_CLIENT_PROJECTILE = 12; -const int ENT_CLIENT_GIBSPLASH = 13; -const int ENT_CLIENT_DAMAGEINFO = 14; -const int ENT_CLIENT_INIT = 16; -const int ENT_CLIENT_MAPVOTE = 17; -const int ENT_CLIENT_CLIENTDATA = 18; -const int ENT_CLIENT_RANDOMSEED = 19; -const int ENT_CLIENT_WALL = 20; -const int ENT_CLIENT_SPIDERBOT = 21; -const int ENT_CLIENT_MODELEFFECT = 22; -const int ENT_CLIENT_TUBANOTE = 23; -const int ENT_CLIENT_WARPZONE = 24; -const int ENT_CLIENT_WARPZONE_CAMERA = 25; -const int ENT_CLIENT_TRIGGER_MUSIC = 26; -const int ENT_CLIENT_HOOK = 27; -const int ENT_CLIENT_INVENTORY = 28; -const int ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers -const int ENT_CLIENT_ACCURACY = 30; -const int ENT_CLIENT_SHOWNAMES = 31; -const int ENT_CLIENT_WARPZONE_TELEPORTED = 32; -const int ENT_CLIENT_MODEL = 33; -const int ENT_CLIENT_ITEM = 34; -const int ENT_CLIENT_BUMBLE_RAYGUN = 35; -const int ENT_CLIENT_SPAWNPOINT = 36; -const int ENT_CLIENT_SPAWNEVENT = 37; -const int ENT_CLIENT_NOTIFICATION = 38; -const int ENT_CLIENT_ELIMINATEDPLAYERS = 39; -const int ENT_CLIENT_TURRET = 40; -const int ENT_CLIENT_AUXILIARYXHAIR = 50; -const int ENT_CLIENT_VEHICLE = 60; -const int ENT_CLIENT_LADDER = 61; -const int ENT_CLIENT_TRIGGER_PUSH = 62; -const int ENT_CLIENT_TARGET_PUSH = 63; -const int ENT_CLIENT_CONVEYOR = 64; -const int ENT_CLIENT_DOOR = 65; -const int ENT_CLIENT_TRAIN = 66; -const int ENT_CLIENT_PLAT = 67; -const int ENT_CLIENT_TRIGGER_IMPULSE = 68; -const int ENT_CLIENT_SWAMP = 69; -const int ENT_CLIENT_CORNER = 70; -const int ENT_CLIENT_KEYLOCK = 71; -const int ENT_CLIENT_GENERATOR = 72; -const int ENT_CLIENT_CONTROLPOINT_ICON = 73; -const int ENT_CLIENT_EFFECT = 74; -const int ENT_CLIENT_MINIGAME = 75; -const int ENT_CLIENT_VIEWLOC = 78; -const int ENT_CLIENT_VIEWLOC_TRIGGER = 79; - -const int ENT_CLIENT_MUTATOR = TE_CSQC_MUTATOR; // 99 +REGISTER_NET_LINKED(_ENT_CLIENT_INIT) +#ifdef CSQC +NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { return true; } +#endif +/** Sent as a temp entity from a persistent linked entity */ +REGISTER_NET_TEMP(ENT_CLIENT_INIT) + +REGISTER_NET_LINKED(ENT_CLIENT_ENTCS) +REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO) +REGISTER_NET_LINKED(ENT_CLIENT_SCORES) +REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES) +REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote] +REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor] +REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE) +REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE) +REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA) +REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED) +REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY) +REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS) + +REGISTER_NET_LINKED(ENT_CLIENT_MODEL) + +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE) +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA) +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED) + +REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM) +REGISTER_NET_LINKED(ENT_CLIENT_HOOK) +REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE) + +REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT) +REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT) +REGISTER_NET_LINKED(ENT_CLIENT_WALL) const int SPRITERULE_DEFAULT = 0; const int SPRITERULE_TEAMPLAY = 1; @@ -155,10 +107,6 @@ const int CVAR_READONLY = 4; /////////////////////////// // csqc communication stuff -const int CTF_STATE_ATTACK = 1; -const int CTF_STATE_DEFEND = 2; -const int CTF_STATE_COMMANDER = 3; - const int HUD_NORMAL = 0; const int HUD_BUMBLEBEE_GUN = 25; @@ -204,7 +152,7 @@ const int SFL_SORT_PRIO_MASK = 12; /** * Score indices */ -#define MAX_SCORE 10 +#define MAX_SCORE 12 #define MAX_TEAMSCORE 2 const int ST_SCORE = 0; @@ -212,36 +160,10 @@ const int SP_KILLS = 0; const int SP_DEATHS = 1; const int SP_SUICIDES = 2; const int SP_SCORE = 3; +const int SP_DMG = 10; +const int SP_DMGTAKEN = 11; // game mode specific indices are not in common/, but in server/scores_rules.qc! -const int CH_INFO = 0; -const int CH_TRIGGER = -3; -const int CH_WEAPON_A = -1; -const int CH_WEAPON_SINGLE = 1; -const int CH_VOICE = -2; -const int CH_BGM_SINGLE = 8; -const int CH_AMBIENT = -9; -const int CH_TRIGGER_SINGLE = 3; -const int CH_SHOTS = -4; -const int CH_SHOTS_SINGLE = 4; -const int CH_WEAPON_B = -1; -const int CH_PAIN = -6; -const int CH_PAIN_SINGLE = 6; -const int CH_PLAYER = -7; -const int CH_PLAYER_SINGLE = 7; -const int CH_TUBA_SINGLE = 5; - -const float ATTEN_NONE = 0; -const float ATTEN_MIN = 0.015625; -const float ATTEN_NORM = 0.5; -const float ATTEN_LARGE = 1; -const float ATTEN_IDLE = 2; -const float ATTEN_STATIC = 3; -const float ATTEN_MAX = 3.984375; - -const float VOL_BASE = 0.7; -const float VOL_BASEVOICE = 1.0; - // WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc const int PROJECTILE_ELECTRO = 1; const int PROJECTILE_ROCKET = 2; diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index eacc1947e1..29a0bf44a5 100644 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@ -76,7 +76,9 @@ CSQCPlayer_SetViewLocation(); // force updates of player entities that often even if unchanged +#ifndef CSQCPLAYER_FORCE_UPDATES #define CSQCPLAYER_FORCE_UPDATES 0.25 +#endif // mod must define: //vector PL_MIN = ...; diff --git a/qcsrc/common/deathtypes/all.qc b/qcsrc/common/deathtypes/all.qc index 4fdafa9a82..823a20f8e0 100644 --- a/qcsrc/common/deathtypes/all.qc +++ b/qcsrc/common/deathtypes/all.qc @@ -3,7 +3,7 @@ string Deathtype_Name(int deathtype) { if (DEATH_ISSPECIAL(deathtype)) { - entity deathent = Deathtypes[deathtype - DT_FIRST]; + entity deathent = Deathtypes_from(deathtype - DT_FIRST); if (!deathent) { backtrace("Deathtype_Name: Could not find deathtype entity!\n"); return ""; } return deathent.nent_name; } diff --git a/qcsrc/common/deathtypes/all.qh b/qcsrc/common/deathtypes/all.qh index 2bf1169bf7..dcf64b11f3 100644 --- a/qcsrc/common/deathtypes/all.qh +++ b/qcsrc/common/deathtypes/all.qh @@ -3,15 +3,18 @@ #include "../notifications.qh" -REGISTRY(Deathtypes, BIT(6)) -REGISTER_REGISTRY(RegisterDeathtypes) +REGISTRY(Deathtypes, BITS(8)) +#define Deathtypes_from(i) _Deathtypes_from(i, NULL) +REGISTER_REGISTRY(Deathtypes) +REGISTRY_CHECK(Deathtypes) .entity death_msgself; .entity death_msgmurder; .string death_msgextra; #define REGISTER_DEATHTYPE(id, msg_death, msg_death_by, extra) \ - REGISTER(RegisterDeathtypes, DEATH, Deathtypes, id, m_id, new(deathtype)) { \ + REGISTER(Deathtypes, DEATH, id, m_id, new(deathtype)) { \ + make_pure(this); \ this.m_id += DT_FIRST; \ this.nent_name = #id; \ this.death_msgextra = extra; \ @@ -32,11 +35,11 @@ const int DEATH_HITTYPEMASK = HITTYPE_SECONDARY | HITTYPE_SPLASH | HITTYPE_BOUNC const int DT_FIRST = BIT(13); #define DEATH_ISSPECIAL(t) (t >= DT_FIRST) -#define DEATH_IS(t, dt) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]) == dt) -#define DEATH_ENT(t) (DEATH_ISSPECIAL(t) ? (Deathtypes[t - DT_FIRST]) : NULL) -#define DEATH_ISVEHICLE(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "vehicle") -#define DEATH_ISTURRET(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "turret") -#define DEATH_ISMONSTER(t) (DEATH_ISSPECIAL(t) && (Deathtypes[t - DT_FIRST]).death_msgextra == "monster") +#define DEATH_IS(t, dt) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)) == dt) +#define DEATH_ENT(t) (DEATH_ISSPECIAL(t) ? (Deathtypes_from(t - DT_FIRST)) : NULL) +#define DEATH_ISVEHICLE(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "vehicle") +#define DEATH_ISTURRET(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "turret") +#define DEATH_ISMONSTER(t) (DEATH_ISSPECIAL(t) && (Deathtypes_from(t - DT_FIRST)).death_msgextra == "monster") #define DEATH_WEAPONOF(t) (DEATH_ISSPECIAL(t) ? WEP_Null : get_weaponinfo((t) & DEATH_WEAPONMASK)) #define DEATH_ISWEAPON(t, w) (DEATH_WEAPONOF(t) == (w)) diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh new file mode 100644 index 0000000000..7301489f32 --- /dev/null +++ b/qcsrc/common/debug.qh @@ -0,0 +1,113 @@ +#ifndef DEBUG_H +#define DEBUG_H + +.bool debug; +.int sv_entnum; +REGISTER_NET_TEMP(net_debug) +#ifdef CSQC + NET_HANDLE(net_debug, bool isNew) + { + Net_Accept(net_debug); + this.sv_entnum = ReadShort(); + if (ReadByte()) make_pure(this); + this.origin_x = ReadCoord(); + this.origin_y = ReadCoord(); + this.origin_z = ReadCoord(); + setorigin(this, this.origin); + this.debug = true; // identify server entities by this + this.classname = strzone(ReadString()); + this.sourceLocFile = strzone(ReadString()); + this.sourceLocLine = ReadInt24_t(); + return true; + } +#endif + +#ifdef SVQC + bool debug_send(entity this, entity to, int sf) + { + int channel = MSG_ONE; + msg_entity = to; + WriteHeader(channel, net_debug); + WriteShort(channel, num_for_edict(this)); + WriteByte(channel, is_pure(this)); + WriteCoord(channel, this.origin.x); + WriteCoord(channel, this.origin.y); + WriteCoord(channel, this.origin.z); + WriteString(channel, this.classname); + WriteString(channel, this.sourceLocFile); + WriteInt24_t(channel, this.sourceLocLine); + return true; + } +#endif + +bool autocvar_debugdraw; + +#ifdef CSQC + .int debugdraw_last; + vector project_3d_to_2d(vector vec); + void Debug_Draw() + { + if (!autocvar_debugdraw) return; + static int debugdraw_frame; + ++debugdraw_frame; + const int size = 8; + for (entity e1 = NULL; (e1 = nextent(e1)); ) + { + if (e1.debugdraw_last == debugdraw_frame) continue; + int ofs = 0; + for (entity e = findradius(e1.origin, 100); e; e = e.chain) + { + if (e.debugdraw_last == debugdraw_frame) continue; + e.debugdraw_last = debugdraw_frame; + vector rgb = (e.debug) ? '0 0 1' : '1 0 0'; + if (is_pure(e)) + { + if (autocvar_debugdraw < 2) continue; + rgb.y = 1; + } + vector pos = project_3d_to_2d(e.origin); + if (pos.z < 0) continue; + pos.z = 0; + pos.y += ofs * size; + drawcolorcodedstring2(pos, + sprintf("%d: '%s'@%s:%d", (e.debug ? e.sv_entnum : num_for_edict(e)), + e.classname, e.sourceLocFile, e.sourceLocLine), + size * '1 1 0', rgb, 0.5, DRAWFLAG_NORMAL); + ++ofs; + } + } + } +#endif + +#ifdef SVQC + GENERIC_COMMAND(debugdraw_sv, "Dump all server entities") + { + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (!autocvar_debugdraw) return; + int n = 1000; + int rem = n; + for (entity e = NULL; (e = findfloat(e, debug, 0)) && rem > 0; ) + { + if (autocvar_debugdraw < 2 && is_pure(e)) continue; + debug_send(e, nextent(NULL), 0); + e.debug = true; + --rem; + } + LOG_INFOF("%d server entities sent\n", n - rem); + return; + } + + default: + case CMD_REQUEST_USAGE: + { + LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " debugdraw_sv")); + return; + } + } + } +#endif + +#endif diff --git a/qcsrc/common/effects/all.inc b/qcsrc/common/effects/all.inc index d53ded907a..b34180056a 100644 --- a/qcsrc/common/effects/all.inc +++ b/qcsrc/common/effects/all.inc @@ -23,6 +23,9 @@ EFFECT(0, ARC_BEAM, "arc_beam") EFFECT(0, ARC_BEAM_HEAL, "arc_beam_heal") EFFECT(0, ARC_BEAM_HEAL_IMPACT, "arc_beam_healimpact") EFFECT(0, ARC_BEAM_HEAL_IMPACT2, "healray_impact") +EFFECT(0, ARC_OVERHEAT, "arc_overheat") +EFFECT(0, ARC_OVERHEAT_FIRE, "arc_overheat_fire") +EFFECT(0, ARC_SMOKE, "arc_smoke") EFFECT(0, ARC_LIGHTNING, "arc_lightning") EFFECT(0, ARC_LIGHTNING2, "electro_lightning") @@ -98,8 +101,6 @@ EFFECT(0, HOOK_MUZZLEFLASH, "grapple_muzzleflash") EFFECT(0, SEEKER_MUZZLEFLASH, "seeker_muzzleflash") -EFFECT(0, FLAK_BOUNCE, "flak_bounce") - EFFECT(1, FIREBALL, "fireball") EFFECT(0, FIREBALL_BFGDAMAGE, "fireball_bfgdamage") EFFECT(0, FIREBALL_EXPLODE, "fireball_explode") @@ -146,54 +147,6 @@ EFFECT(0, SPAWN_PINK, "spawn_event_pink") EFFECT(0, SPAWNPOINT_NEUTRAL, "spawn_point_neutral") EFFECT(0, SPAWN_NEUTRAL, "spawn_event_neutral") -EFFECT(0, NADE_EXPLODE_RED, "nade_red_explode") -EFFECT(0, NADE_EXPLODE_BLUE, "nade_blue_explode") -EFFECT(0, NADE_EXPLODE_YELLOW, "nade_yellow_explode") -EFFECT(0, NADE_EXPLODE_PINK, "nade_pink_explode") -EFFECT(0, NADE_EXPLODE_NEUTRAL, "nade_neutral_explode") -entity EFFECT_NADE_EXPLODE(int teamid) -{ - switch (teamid) { - case NUM_TEAM_1: return EFFECT_NADE_EXPLODE_RED; - case NUM_TEAM_2: return EFFECT_NADE_EXPLODE_BLUE; - case NUM_TEAM_3: return EFFECT_NADE_EXPLODE_YELLOW; - case NUM_TEAM_4: return EFFECT_NADE_EXPLODE_PINK; - default: return EFFECT_NADE_EXPLODE_NEUTRAL; - } -} - -EFFECT(1, NADE_TRAIL_RED, "nade_red") -EFFECT(1, NADE_TRAIL_BLUE, "nade_blue") -EFFECT(1, NADE_TRAIL_YELLOW, "nade_yellow") -EFFECT(1, NADE_TRAIL_PINK, "nade_pink") -EFFECT(1, NADE_TRAIL_NEUTRAL, "nade_neutral") -entity EFFECT_NADE_TRAIL(int teamid) -{ - switch (teamid) { - case NUM_TEAM_1: return EFFECT_NADE_TRAIL_RED; - case NUM_TEAM_2: return EFFECT_NADE_TRAIL_BLUE; - case NUM_TEAM_3: return EFFECT_NADE_TRAIL_YELLOW; - case NUM_TEAM_4: return EFFECT_NADE_TRAIL_PINK; - default: return EFFECT_NADE_TRAIL_NEUTRAL; - } -} - -EFFECT(1, NADE_TRAIL_BURN_RED, "nade_red_burn") -EFFECT(1, NADE_TRAIL_BURN_BLUE, "nade_blue_burn") -EFFECT(1, NADE_TRAIL_BURN_YELLOW, "nade_yellow_burn") -EFFECT(1, NADE_TRAIL_BURN_PINK, "nade_pink_burn") -EFFECT(1, NADE_TRAIL_BURN_NEUTRAL, "nade_neutral_burn") -entity EFFECT_NADE_TRAIL_BURN(int teamid) -{ - switch (teamid) { - case NUM_TEAM_1: return EFFECT_NADE_TRAIL_BURN_RED; - case NUM_TEAM_2: return EFFECT_NADE_TRAIL_BURN_BLUE; - case NUM_TEAM_3: return EFFECT_NADE_TRAIL_BURN_YELLOW; - case NUM_TEAM_4: return EFFECT_NADE_TRAIL_BURN_PINK; - default: return EFFECT_NADE_TRAIL_BURN_NEUTRAL; - } -} - EFFECT(0, ICEORGLASS, "iceorglass") EFFECT(0, ICEFIELD, "icefield") EFFECT(0, FIREFIELD, "firefield") @@ -222,6 +175,16 @@ EFFECT(1, PASS_BLUE, "blue_pass") EFFECT(1, PASS_YELLOW, "yellow_pass") EFFECT(1, PASS_PINK, "pink_pass") EFFECT(1, PASS_NEUTRAL, "neutral_pass") +entity EFFECT_PASS(int teamid) +{ + switch (teamid) { + case NUM_TEAM_1: return EFFECT_PASS_RED; + case NUM_TEAM_2: return EFFECT_PASS_BLUE; + case NUM_TEAM_3: return EFFECT_PASS_YELLOW; + case NUM_TEAM_4: return EFFECT_PASS_PINK; + default: return EFFECT_PASS_NEUTRAL; + } +} EFFECT(0, CAP_RED, "red_cap") EFFECT(0, CAP_BLUE, "blue_cap") diff --git a/qcsrc/common/effects/all.qc b/qcsrc/common/effects/all.qc index e90f3c615b..347a2111b5 100644 --- a/qcsrc/common/effects/all.qc +++ b/qcsrc/common/effects/all.qc @@ -1,15 +1,17 @@ #include "all.qh" +REGISTER_NET_TEMP(net_effect) #ifdef CSQC -void Read_Effect(bool is_new) +NET_HANDLE(net_effect, bool isNew) { int net_name = (Effects_COUNT >= 255) ? ReadShort() : ReadByte(); - entity eff = Effects[net_name]; + entity eff = Effects_from(net_name); - vector v, vel = '0 0 0'; + vector vel = '0 0 0'; int eff_cnt = 1; bool eff_trail = eff.eent_eff_trail; + vector v; v_x = ReadCoord(); v_y = ReadCoord(); v_z = ReadCoord(); @@ -25,38 +27,38 @@ void Read_Effect(bool is_new) if(!eff_trail) eff_cnt = ReadByte(); - if(is_new) - { - if(eff_trail) - WarpZone_TrailParticles(world, particleeffectnum(eff), v, vel); - else - pointparticles(particleeffectnum(eff), v, vel, eff_cnt); - } + if(eff_trail) + WarpZone_TrailParticles(world, particleeffectnum(eff), v, vel); + else + pointparticles(eff, v, vel, eff_cnt); + return true; } #endif #ifdef SVQC bool Net_Write_Effect(entity this, entity client, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_EFFECT); + int channel = MSG_ONE; + msg_entity = client; + WriteHeader(channel, net_effect); (Effects_COUNT >= 255) - ? WriteShort(MSG_ENTITY, self.m_id) - : WriteByte(MSG_ENTITY, self.m_id); - WriteCoord(MSG_ENTITY, self.eent_net_location_x); - WriteCoord(MSG_ENTITY, self.eent_net_location_y); - WriteCoord(MSG_ENTITY, self.eent_net_location_z); + ? WriteShort(channel, this.m_id) + : WriteByte(channel, this.m_id); + WriteCoord(channel, this.eent_net_location_x); + WriteCoord(channel, this.eent_net_location_y); + WriteCoord(channel, this.eent_net_location_z); // attempt to save a tiny bit more bandwidth by not sending velocity if it isn't set - if(self.eent_net_velocity) + if(this.eent_net_velocity) { - WriteByte(MSG_ENTITY, true); - WriteCoord(MSG_ENTITY, self.eent_net_velocity_x); - WriteCoord(MSG_ENTITY, self.eent_net_velocity_y); - WriteCoord(MSG_ENTITY, self.eent_net_velocity_z); + WriteByte(channel, true); + WriteCoord(channel, this.eent_net_velocity_x); + WriteCoord(channel, this.eent_net_velocity_y); + WriteCoord(channel, this.eent_net_velocity_z); } - else { WriteByte(MSG_ENTITY, false); } + else { WriteByte(channel, false); } - if(!self.eent_eff_trail) { WriteByte(MSG_ENTITY, self.eent_net_count); } + if(!this.eent_eff_trail) { WriteByte(channel, this.eent_net_count); } return true; } @@ -64,9 +66,9 @@ void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt) { if(!eff) { return; } if(!eff.eent_eff_trail && !eff_cnt) { return; } // effect has no count! - entity net_eff = spawn(); + entity net_eff = new(net_effect); + make_pure(net_eff); net_eff.owner = eff; - net_eff.classname = "net_effect"; //net_eff.eent_broadcast = broadcast; net_eff.m_id = eff.m_id; net_eff.eent_net_velocity = eff_vel; @@ -74,10 +76,8 @@ void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt) net_eff.eent_net_count = eff_cnt; net_eff.eent_eff_trail = eff.eent_eff_trail; - net_eff.think = SUB_Remove; - net_eff.nextthink = time + 0.2; // don't need to keep this long - - Net_LinkEntity(net_eff, false, 0, Net_Write_Effect); + entity e; FOR_EACH_REALCLIENT(e) Net_Write_Effect(net_eff, e, 0); + remove(net_eff); } void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt) @@ -88,6 +88,6 @@ void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt) return; )); // revert to engine handling - pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt); + __pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt); } #endif diff --git a/qcsrc/common/effects/all.qh b/qcsrc/common/effects/all.qh index 7046af11bc..e0e9a3ca86 100644 --- a/qcsrc/common/effects/all.qh +++ b/qcsrc/common/effects/all.qh @@ -3,39 +3,19 @@ #include "effect.qh" -#ifdef CSQC -void Read_Effect(bool is_new); -#elif defined(SVQC) +#ifdef SVQC void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt); void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt); #endif -REGISTRY(Effects, BIT(8)) -REGISTER_REGISTRY(RegisterEffects) +REGISTRY(Effects, BITS(8)) +#define Effects_from(i) _Effects_from(i, EFFECT_Null) +REGISTER_REGISTRY(Effects) +REGISTRY_CHECK(Effects) #define EFFECT(istrail, name, realname) \ - REGISTER(RegisterEffects, EFFECT, Effects, name, m_id, Create_Effect_Entity(realname, istrail)); + REGISTER(Effects, EFFECT, name, m_id, Create_Effect_Entity(realname, istrail)); -void RegisterEffects_First() -{ - #ifdef SVQC - #define dedi (server_is_dedicated ? "a dedicated " : "") - #else - #define dedi "" - #endif - - LOG_TRACEF("Beginning effect initialization on %s%s program...\n", dedi, PROGNAME); - #undef dedi -} - -void RegisterEffects_Done() -{ - LOG_TRACE("Effects initialization successful!\n"); -} - -// NOW we actually activate the declarations -ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_First) EFFECT(0, Null, string_null) #include "all.inc" -ACCUMULATE_FUNCTION(RegisterEffects, RegisterEffects_Done) #endif diff --git a/qcsrc/common/effects/effect.qh b/qcsrc/common/effects/effect.qh index bd9cd99e56..0ae9b48936 100644 --- a/qcsrc/common/effects/effect.qh +++ b/qcsrc/common/effects/effect.qh @@ -1,7 +1,21 @@ #ifndef EFFECT_H #define EFFECT_H -#define particleeffectnum(e) _particleeffectnum(e.eent_eff_name) +#define particleeffectnum(e) \ + _particleeffectnum(e.eent_eff_name) + +#if defined(SVQC) + #define pointparticles(effect, org, vel, howmany) \ + Send_Effect(effect, org, vel, howmany) + #define trailparticles(e, effect, org, vel) \ + ((!e) ? Send_Effect(effect, org, vel, 0) \ + : __trailparticles(e, particleeffectnum(effect), org, vel)) +#elif defined(CSQC) + #define pointparticles(effect, org, vel, howmany) \ + __pointparticles(particleeffectnum(effect), org, vel, howmany) + #define trailparticles(e, effect, org, vel) \ + __trailparticles(e, particleeffectnum(effect), org, vel) +#endif .int m_id; .string eent_eff_name; @@ -14,6 +28,7 @@ entity Create_Effect_Entity(string eff_name, bool eff_trail) { entity this = new(effect_entity); + make_pure(this); this.eent_eff_name = eff_name; this.eent_eff_trail = eff_trail; return this; diff --git a/qcsrc/common/effects/effectinfo.qc b/qcsrc/common/effects/effectinfo.qc index deff4dc156..f4df3237d4 100644 --- a/qcsrc/common/effects/effectinfo.qc +++ b/qcsrc/common/effects/effectinfo.qc @@ -319,11 +319,12 @@ GENERIC_COMMAND(dumpeffectinfo, "Dump all effectinfo to effectinfo_dump.txt") } -REGISTRY(EffectInfos, BIT(9)) -REGISTER_REGISTRY(RegisterEffectInfos) +REGISTRY(EffectInfos, BITS(9)) +#define EffectInfos_from(i) _EffectInfos_from(i, NULL) +REGISTER_REGISTRY(EffectInfos) #define EFFECTINFO(name) \ [[accumulate]] void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { } \ - REGISTER(RegisterEffectInfos, EFFECTINFO, EffectInfos, name, m_id, NEW(EffectInfoGroup)) { \ + REGISTER(EffectInfos, EFFECTINFO, name, m_id, NEW(EffectInfoGroup)) { \ effectinfo_##name(this, NULL); \ } diff --git a/qcsrc/common/effects/qc/all.inc b/qcsrc/common/effects/qc/all.inc new file mode 100644 index 0000000000..cda1a638c4 --- /dev/null +++ b/qcsrc/common/effects/qc/all.inc @@ -0,0 +1,5 @@ +#include "casings.qc" +#include "damageeffects.qc" +#include "gibs.qc" +#include "lightningarc.qc" +#include "modeleffects.qc" diff --git a/qcsrc/common/effects/qc/all.qc b/qcsrc/common/effects/qc/all.qc new file mode 100644 index 0000000000..f0fc7195a8 --- /dev/null +++ b/qcsrc/common/effects/qc/all.qc @@ -0,0 +1,5 @@ +#include "all.qh" + +#define IMPLEMENTATION +#include "all.inc" +#undef IMPLEMENTATION diff --git a/qcsrc/common/effects/qc/all.qh b/qcsrc/common/effects/qc/all.qh new file mode 100644 index 0000000000..5b9b364313 --- /dev/null +++ b/qcsrc/common/effects/qc/all.qh @@ -0,0 +1,4 @@ +#ifndef EFFECTS_QC +#define EFFECTS_QC +#include "all.inc" +#endif diff --git a/qcsrc/common/effects/qc/casings.qc b/qcsrc/common/effects/qc/casings.qc new file mode 100644 index 0000000000..815555df6a --- /dev/null +++ b/qcsrc/common/effects/qc/casings.qc @@ -0,0 +1,178 @@ +#ifdef IMPLEMENTATION + +#include "../../util.qh" + +#ifdef CSQC +#include "../../movetypes/movetypes.qh" +#include "rubble.qh" +#endif + +REGISTER_NET_TEMP(casings) + +#ifdef SVQC +void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner) +{SELFPARAM(); + .entity weaponentity = weaponentities[0]; // TODO: parameter + entity wep = self.(weaponentity); + vector org = self.origin + self.view_ofs + wep.spawnorigin.x * v_forward - wep.spawnorigin.y * v_right + wep.spawnorigin.z * v_up; + + if (!sound_allowed(MSG_BROADCAST, casingowner)) + casingtype |= 0x80; + + WriteHeader(MSG_ALL, casings); + WriteByte(MSG_ALL, casingtype); + WriteCoord(MSG_ALL, org.x); + WriteCoord(MSG_ALL, org.y); + WriteCoord(MSG_ALL, org.z); + WriteShort(MSG_ALL, compressShortVector(vel)); // actually compressed velocity + WriteByte(MSG_ALL, ang.x * 256 / 360); + WriteByte(MSG_ALL, ang.y * 256 / 360); + WriteByte(MSG_ALL, ang.z * 256 / 360); +} +#endif + +#ifdef CSQC +entityclass(Casing); +class(Casing) .float alpha; +class(Casing) .bool silent; +class(Casing) .int state; +class(Casing) .float cnt; + +void Casing_Delete() +{SELFPARAM(); + remove(self); +} + +void Casing_Draw(entity this) +{ + if (self.move_flags & FL_ONGROUND) + { + self.move_angles_x = 0; + self.move_angles_z = 0; + self.flags &= ~FL_ONGROUND; + } + + Movetype_Physics_MatchTicrate(autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy); + if (wasfreed(self)) + return; // deleted by touch function + + self.renderflags = 0; + self.alpha = bound(0, self.cnt - time, 1); + + if (self.alpha < ALPHA_MIN_VISIBLE) + { + Casing_Delete(); + self.drawmask = 0; + } +} + +SOUND(BRASS1, W_Sound("brass1")); +SOUND(BRASS2, W_Sound("brass2")); +SOUND(BRASS3, W_Sound("brass3")); +Sound SND_BRASS_RANDOM() { + return Sounds_from(SND_BRASS1.m_id + floor(prandom() * 3)); +} +SOUND(CASINGS1, W_Sound("casings1")); +SOUND(CASINGS2, W_Sound("casings2")); +SOUND(CASINGS3, W_Sound("casings3")); +Sound SND_CASINGS_RANDOM() { + return Sounds_from(SND_CASINGS1.m_id + floor(prandom() * 3)); +} + +void Casing_Touch() +{SELFPARAM(); + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + Casing_Delete(); + return; + } + + if (!self.silent) + if (!trace_ent || trace_ent.solid == SOLID_BSP) + { + if (vlen(self.velocity) > 50) + { + if (time >= self.nextthink) + { + Sound s; + switch (self.state) + { + case 1: + s = SND_CASINGS_RANDOM(); + break; + default: + s = SND_BRASS_RANDOM(); + break; + } + + sound (self, CH_SHOTS, s, VOL_BASE, ATTEN_LARGE); + } + } + } + + self.nextthink = time + 0.2; +} + +void Casing_Damage(float thisdmg, int hittype, vector org, vector thisforce) +{SELFPARAM(); + if (thisforce.z < 0) + thisforce.z = 0; + self.move_velocity = self.move_velocity + thisforce + '0 0 100'; + self.move_flags &= ~FL_ONGROUND; +} + +NET_HANDLE(casings, bool isNew) +{ + int _state = ReadByte(); + vector org; + org_x = ReadCoord(); + org_y = ReadCoord(); + org_z = ReadCoord(); + vector vel = decompressShortVector(ReadShort()); + vector ang; + ang_x = ReadByte() * 360 / 256; + ang_y = ReadByte() * 360 / 256; + ang_z = ReadByte() * 360 / 256; + return = true; + + if (!autocvar_cl_casings) return; + + Casing casing = RubbleNew("casing"); + casing.silent = (_state & 0x80); + casing.state = (_state & 0x7F); + casing.origin = org; + setorigin(casing, casing.origin); + casing.velocity = vel; + casing.angles = ang; + casing.drawmask = MASK_NORMAL; + + casing.draw = Casing_Draw; + casing.move_origin = casing.origin; + casing.move_velocity = casing.velocity + 2 * prandomvec(); + casing.move_angles = casing.angles; + casing.move_avelocity = '0 250 0' + 100 * prandomvec(); + casing.move_movetype = MOVETYPE_BOUNCE; + casing.move_touch = Casing_Touch; + casing.move_time = time; + casing.event_damage = Casing_Damage; + casing.solid = SOLID_TRIGGER; + + switch (casing.state) + { + case 1: + setmodel(casing, MDL_CASING_SHELL); + casing.cnt = time + autocvar_cl_casings_shell_time; + break; + default: + setmodel(casing, MDL_CASING_BULLET); + casing.cnt = time + autocvar_cl_casings_bronze_time; + break; + } + + setsize(casing, '0 0 -1', '0 0 -1'); + + RubbleLimit("casing", autocvar_cl_casings_maxcount, Casing_Delete); +} + +#endif +#endif diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc new file mode 100644 index 0000000000..e77f63f69c --- /dev/null +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -0,0 +1,437 @@ +#ifndef DAMAGEEFFECTS_H +#define DAMAGEEFFECTS_H + +#ifdef CSQC +#include "../../deathtypes/all.qh" +#include "../../movetypes/movetypes.qh" +#include "../../../client/mutators/events.qh" +#include "../../vehicles/all.qh" +#include "../../weapons/all.qh" +#endif + +#endif + +#ifdef IMPLEMENTATION + +REGISTER_NET_LINKED(ENT_CLIENT_DAMAGEINFO) + +#ifdef SVQC + +bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO); + WriteShort(MSG_ENTITY, self.projectiledeathtype); + WriteCoord(MSG_ENTITY, floor(self.origin.x)); + WriteCoord(MSG_ENTITY, floor(self.origin.y)); + WriteCoord(MSG_ENTITY, floor(self.origin.z)); + WriteByte(MSG_ENTITY, bound(1, self.dmg, 255)); + WriteByte(MSG_ENTITY, bound(0, self.dmg_radius, 255)); + WriteByte(MSG_ENTITY, bound(1, self.dmg_edge, 255)); + WriteShort(MSG_ENTITY, self.oldorigin.x); + WriteByte(MSG_ENTITY, self.species); + return true; +} + +void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner) +{ + // TODO maybe call this from non-edgedamage too? + // TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info? + + entity e; + + if(!sound_allowed(MSG_BROADCAST, dmgowner)) + deathtype |= 0x8000; + + e = new(damageinfo); + make_pure(e); + setorigin(e, org); + e.projectiledeathtype = deathtype; + e.dmg = coredamage; + e.dmg_edge = edgedamage; + e.dmg_radius = rad; + e.dmg_force = vlen(force); + e.velocity = force; + e.oldorigin_x = compressShortVector(e.velocity); + e.species = bloodtype; + + Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity); +} + +#endif + +#ifdef CSQC + +/** number of effects which currently are attached to a player */ +.int total_damages; + +.entity tag_entity; + +.float cnt; +.int state; +.bool isplayermodel; + +void DamageEffect_Think() +{SELFPARAM(); + // if particle distribution is enabled, slow ticrate by total number of damages + if(autocvar_cl_damageeffect_distribute) + self.nextthink = time + autocvar_cl_damageeffect_ticrate * self.owner.total_damages; + else + self.nextthink = time + autocvar_cl_damageeffect_ticrate; + + if(time >= self.cnt || !self.owner || !self.owner.modelindex || !self.owner.drawmask) + { + // time is up or the player got gibbed / disconnected + self.owner.total_damages = max(0, self.owner.total_damages - 1); + remove(self); + return; + } + if(self.state && !self.owner.csqcmodel_isdead) + { + // if the player was dead but is now alive, it means he respawned + // if so, clear his damage effects, or damages from his dead body will be copied back + self.owner.total_damages = max(0, self.owner.total_damages - 1); + remove(self); + return; + } + self.state = self.owner.csqcmodel_isdead; + if(self.owner.isplayermodel && (self.owner.entnum == player_localentnum) && !autocvar_chase_active) + return; // if we aren't using a third person camera, hide our own effects + + // now generate the particles + vector org; + org = gettaginfo(self, 0); // origin at attached location + __pointparticles(self.team, org, '0 0 0', 1); +} + +string species_prefix(int specnum) +{ + switch(specnum) + { + case SPECIES_HUMAN: return ""; + case SPECIES_ALIEN: return "alien_"; + case SPECIES_ROBOT_SHINY: return "robot_"; + case SPECIES_ROBOT_RUSTY: return "robot_"; // use the same effects, only different gibs + case SPECIES_ROBOT_SOLID: return "robot_"; // use the same effects, only different gibs + case SPECIES_ANIMAL: return "animal_"; + case SPECIES_RESERVED: return "reserved_"; + default: return ""; + } +} + +void DamageEffect(vector hitorg, float thedamage, int type, int specnum) +{SELFPARAM(); + // particle effects for players and objects damaged by weapons (eg: flames coming out of victims shot with rockets) + + int nearestbone = 0; + float life; + string specstr, effectname; + entity e; + + if(!autocvar_cl_damageeffect || autocvar_cl_gentle || autocvar_cl_gentle_damage) + return; + if(!self || !self.modelindex || !self.drawmask) + return; + + // if this is a rigged mesh, the effect will show on the bone where damage was dealt + // we do this by choosing the skeletal bone closest to the impact, and attaching our entity to it + // if there's no skeleton, object origin will automatically be selected + FOR_EACH_TAG(self) + { + if(!tagnum) + continue; // skip empty bones + // blacklist bones positioned outside the mesh, or the effect will be floating + // TODO: Do we have to do it this way? Why do these bones exist at all? + if(gettaginfo_name == "master" || gettaginfo_name == "knee_L" || gettaginfo_name == "knee_R" || gettaginfo_name == "leg_L" || gettaginfo_name == "leg_R") + continue; // player model bone blacklist + + // now choose the bone closest to impact origin + if(nearestbone == 0 || vlen(hitorg - gettaginfo(self, tagnum)) <= vlen(hitorg - gettaginfo(self, nearestbone))) + nearestbone = tagnum; + } + gettaginfo(self, nearestbone); // set gettaginfo_name + + // return if we reached our damage effect limit or damages are disabled + // TODO: When the limit is reached, it would be better if the oldest damage was removed instead of not adding a new one + if(nearestbone) + { + if(self.total_damages >= autocvar_cl_damageeffect_bones) + return; // allow multiple damages on skeletal models + } + else + { + if(autocvar_cl_damageeffect < 2 || self.total_damages) + return; // allow a single damage on non-skeletal models + } + + life = bound(autocvar_cl_damageeffect_lifetime_min, thedamage * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max); + + effectname = DEATH_WEAPONOF(type).netname; + + if(substring(effectname, strlen(effectname) - 5, 5) == "BLOOD") + { + if(self.isplayermodel) + { + specstr = species_prefix(specnum); + specstr = substring(specstr, 0, strlen(specstr) - 1); + effectname = strreplace("BLOOD", specstr, effectname); + } + else { return; } // objects don't bleed + } + + e = new(damage); + make_pure(e); + setmodel(e, MDL_Null); // necessary to attach and read origin + setattachment(e, self, gettaginfo_name); // attach to the given bone + e.owner = self; + e.cnt = time + life; + e.team = _particleeffectnum(effectname); + e.think = DamageEffect_Think; + e.nextthink = time; + self.total_damages += 1; +} + +NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) +{ + make_pure(this); + float thedamage, rad, edge, thisdmg; + bool hitplayer = false; + int species, forcemul; + vector force, thisforce; + + w_deathtype = ReadShort(); + w_issilent = (w_deathtype & 0x8000); + w_deathtype = (w_deathtype & 0x7FFF); + + w_org.x = ReadCoord(); + w_org.y = ReadCoord(); + w_org.z = ReadCoord(); + + thedamage = ReadByte(); + rad = ReadByte(); + edge = ReadByte(); + force = decompressShortVector(ReadShort()); + species = ReadByte(); + + return = true; + + if (!isNew) + return; + + if(rad < 0) + { + rad = -rad; + forcemul = -1; + } + else + forcemul = 1; + + for(entity e = findradius(w_org, rad + MAX_DAMAGEEXTRARADIUS); e; e = e.chain) + { + setself(e); + // attached ents suck + if(self.tag_entity) + continue; + + vector nearest = NearestPointOnBox(self, w_org); + if(rad) + { + thisdmg = ((vlen (nearest - w_org) - bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad); + if(thisdmg >= 1) + continue; + if(thisdmg < 0) + thisdmg = 0; + if(thedamage) + { + thisdmg = thedamage + (edge - thedamage) * thisdmg; + thisforce = forcemul * vlen(force) * (thisdmg / thedamage) * normalize(self.origin - w_org); + } + else + { + thisdmg = 0; + thisforce = forcemul * vlen(force) * normalize(self.origin - w_org); + } + } + else + { + if(vlen(nearest - w_org) > bound(MIN_DAMAGEEXTRARADIUS, self.damageextraradius, MAX_DAMAGEEXTRARADIUS)) + continue; + + thisdmg = thedamage; + thisforce = forcemul * force; + } + + if(self.damageforcescale) + if(vlen(thisforce)) + { + self.move_velocity = self.move_velocity + damage_explosion_calcpush(self.damageforcescale * thisforce, self.move_velocity, autocvar_g_balance_damagepush_speedfactor); + self.move_flags &= ~FL_ONGROUND; + } + + if(w_issilent) + self.silent = 1; + + if(self.event_damage) + self.event_damage(thisdmg, w_deathtype, w_org, thisforce); + + DamageEffect(w_org, thisdmg, w_deathtype, species); + + if(self.isplayermodel) + hitplayer = true; // this impact damaged a player + } + setself(this); + + if(DEATH_ISVEHICLE(w_deathtype)) + { + traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); + if(trace_plane_normal != '0 0 0') + w_backoff = trace_plane_normal; + else + w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16)); + + setorigin(self, w_org + w_backoff * 2); // for sound() calls + + switch(DEATH_ENT(w_deathtype)) + { + case DEATH_VH_CRUSH: + break; + + // spiderbot + case DEATH_VH_SPID_MINIGUN: + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_SPIDERBOT_MINIGUN_IMPACT, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_SPID_ROCKET: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_SPIDERBOT_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_SPID_DEATH: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_VH_WAKI_GUN: + sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_RACER_IMPACT, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_WAKI_ROCKET: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_RACER_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_WAKI_DEATH: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_VH_RAPT_CANNON: + sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_RAPTOR_CANNON_IMPACT, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_RAPT_FRAGMENT: + float i; + vector ang, vel; + for(i = 1; i < 4; ++i) + { + vel = normalize(w_org - (w_org + normalize(force) * 16)) + randomvec() * 128; + ang = vectoangles(vel); + RaptorCBShellfragToss(w_org, vel, ang + '0 0 1' * (120 * i)); + } + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_RAPTOR_BOMB_SPREAD, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_RAPT_BOMB: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_RAPTOR_BOMB_IMPACT, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_RAPT_DEATH: + sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_EXPLOSION_BIG, self.origin, w_backoff * 1000, 1); + break; + case DEATH_VH_BUMB_GUN: + sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_BIGPLASMA_IMPACT, self.origin, w_backoff * 1000, 1); + break; + } + } + + + if(DEATH_ISTURRET(w_deathtype)) + { + traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); + if(trace_plane_normal != '0 0 0') + w_backoff = trace_plane_normal; + else + w_backoff = -1 * normalize(w_org - (w_org + normalize(force) * 16)); + + setorigin(self, w_org + w_backoff * 2); // for sound() calls + + switch(DEATH_ENT(w_deathtype)) + { + case DEATH_TURRET_EWHEEL: + sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_BLASTER_IMPACT, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_TURRET_FLAC: + pointparticles(EFFECT_HAGAR_EXPLODE, w_org, '0 0 0', 1); + sound(self, CH_SHOTS, SND_HAGEXP_RANDOM(), VOL_BASE, ATTEN_NORM); + break; + + case DEATH_TURRET_MLRS: + case DEATH_TURRET_HK: + case DEATH_TURRET_WALK_ROCKET: + case DEATH_TURRET_HELLION: + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_TURRET_MACHINEGUN: + case DEATH_TURRET_WALK_GUN: + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_MACHINEGUN_IMPACT, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_TURRET_PLASMA: + sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_ELECTRO_IMPACT, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_TURRET_WALK_MELEE: + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_MIN); + pointparticles(EFFECT_TE_SPARK, self.origin, w_backoff * 1000, 1); + break; + + case DEATH_TURRET_PHASER: + break; + + case DEATH_TURRET_TESLA: + te_smallflash(self.origin); + break; + + } + } + + // TODO spawn particle effects and sounds based on w_deathtype + if(!DEATH_ISSPECIAL(w_deathtype)) + if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit + { + Weapon hitwep = DEATH_WEAPONOF(w_deathtype); + w_random = prandom(); + + traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); + if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER) + w_backoff = trace_plane_normal; + else + w_backoff = -1 * normalize(force); + setorigin(self, w_org + w_backoff * 2); // for sound() calls + + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + { + if(!MUTATOR_CALLHOOK(Weapon_ImpactEffect, hitwep)) + hitwep.wr_impacteffect(hitwep); + } + } +} + +#endif + +#endif diff --git a/qcsrc/common/effects/qc/gibs.qc b/qcsrc/common/effects/qc/gibs.qc new file mode 100644 index 0000000000..ab8dbb8ccf --- /dev/null +++ b/qcsrc/common/effects/qc/gibs.qc @@ -0,0 +1,316 @@ +#ifdef IMPLEMENTATION +REGISTER_NET_TEMP(net_gibsplash) + +#ifdef SVQC + +.int state; + +bool Violence_GibSplash_SendEntity(entity this, entity to, int sf) +{ + int channel = MSG_ONE; + msg_entity = to; + WriteHeader(channel, net_gibsplash); + WriteByte(channel, this.state); // actually type + WriteByte(channel, bound(1, this.cnt * 16, 255)); // gibbage amount multiplier + WriteShort(channel, floor(this.origin.x / 4)); // not using a coord here, as gibs don't need this accuracy + WriteShort(channel, floor(this.origin.y / 4)); // not using a coord here, as gibs don't need this accuracy + WriteShort(channel, floor(this.origin.z / 4)); // not using a coord here, as gibs don't need this accuracy + WriteShort(channel, this.oldorigin.x); // acrually compressed velocity + return true; +} + +void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker) +{SELFPARAM(); + if(g_cts) // no gibs in CTS + return; + + entity e = new(gibsplash); + e.cnt = amount; + e.state = type; // should stay smaller than 15 + if(!sound_allowed(MSG_BROADCAST, gibowner) || !sound_allowed(MSG_BROADCAST, attacker)) + e.state |= 0x40; // "silence" bit + e.state |= 8 * self.species; // gib type, ranges from 0 to 15 + + // if this is a copied dead body, send the num of its player instead + // TODO: remove this field, read from model txt files + if(self.classname == "body") + e.team = num_for_edict(self.enemy); + else + e.team = num_for_edict(self); + + setorigin(e, org); + e.velocity = dir; + + e.oldorigin_x = compressShortVector(e.velocity); + + entity cl; FOR_EACH_REALCLIENT(cl) Violence_GibSplash_SendEntity(e, cl, 0); + remove(e); +} + +void Violence_GibSplash(entity source, float type, float amount, entity attacker) +{ + Violence_GibSplash_At(source.origin + source.view_ofs, source.velocity, type, amount, source, attacker); +} +#endif + +#ifdef CSQC + +.vector colormod; +.bool silent; + +#include "rubble.qh" +#include "../common/movetypes/movetypes.qh" + +.float scale; +.float alpha; +.float cnt; +.float gravity; + +void Gib_Delete() +{SELFPARAM(); + remove(self); +} + +string species_prefix(int specnum); + +void Gib_setmodel(entity gib, string mdlname, int specnum) +{ + switch(specnum) + { + case SPECIES_ROBOT_RUSTY: + case SPECIES_ROBOT_SHINY: + case SPECIES_ROBOT_SOLID: + if(specnum != SPECIES_ROBOT_SOLID || mdlname == "models/gibs/chunk.mdl") + { + if(mdlname == "models/gibs/bloodyskull.md3") + setmodel(gib, MDL_GIB_ROBO); + else + setmodel(gib, MDL_GIB_ROBO_RANDOM()); + if(specnum == SPECIES_ROBOT_SHINY) + { + gib.skin = 1; + gib.colormod = '2 2 2'; + } + gib.scale = 1; + break; + } + default: + _setmodel(gib, mdlname); + gib.skin = specnum; + break; + } +} + +void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany) +{ + float i, pmod; + pmod = autocvar_cl_particles_quality; + for (i = 0; i < 50 * pmod; ++i) + __pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50); +} + +void SUB_RemoveOnNoImpact() +{ + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + Gib_Delete(); +} + +void Gib_Touch() +{SELFPARAM(); + // TODO maybe bounce of walls, make more gibs, etc. + + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + Gib_Delete(); + return; + } + + if(!self.silent) + sound(self, CH_PAIN, SND_GIB_SPLAT_RANDOM(), VOL_BASE, ATTEN_NORM); + __pointparticles(_particleeffectnum(strcat(species_prefix(self.cnt), "blood")), self.origin + '0 0 1', '0 0 30', 10); + + Gib_Delete(); +} + +void Gib_Draw(entity this) +{ + vector oldorg; + oldorg = self.origin; + + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + if(wasfreed(self)) + return; + + if(self.touch == Gib_Touch) // don't do this for the "chunk" thingie... + // TODO somehow make it spray in a direction dependent on self.angles + __trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_SLIGHTBLOOD.eent_eff_name)), oldorg, self.origin); + else + __trailparticles(self, _particleeffectnum(strcat(species_prefix(self.cnt), EFFECT_TR_BLOOD.eent_eff_name)), oldorg, self.origin); + + self.renderflags = 0; + + // make gibs die faster at low view quality + // if view_quality is 0.5, we want to have them die twice as fast + self.nextthink -= frametime * (1 / bound(0.01, view_quality, 1.00) - 1); + + self.alpha = bound(0, self.nextthink - time, 1); + + if(self.alpha < ALPHA_MIN_VISIBLE) + { + self.drawmask = 0; + Gib_Delete(); + } +} + +void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector vrand, int specnum, bool destroyontouch, bool issilent) +{ + entity gib; + + // TODO remove some gibs according to cl_nogibs + gib = RubbleNew("gib"); + gib.move_movetype = MOVETYPE_BOUNCE; + gib.gravity = 1; + gib.solid = SOLID_CORPSE; + gib.cnt = specnum; + gib.silent = issilent; + Gib_setmodel(gib, mdlname, specnum); + + setsize (gib, '-8 -8 -8', '8 8 8'); + + gib.draw = Gib_Draw; + if(destroyontouch) + gib.move_touch = Gib_Touch; + else + gib.move_touch = SUB_RemoveOnNoImpact; + + // don't spawn gibs inside solid - just don't + if(org != safeorg) + { + tracebox(safeorg, gib.mins, gib.maxs, org, MOVE_NOMONSTERS, gib); + org = trace_endpos; + } + + gib.move_origin = org; + setorigin(gib, org); + gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up; + gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale; + gib.move_time = time; + gib.damageforcescale = autocvar_cl_gibs_damageforcescale; + + gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); + gib.drawmask = MASK_NORMAL; + + RubbleLimit("gib", autocvar_cl_gibs_maxcount, Gib_Delete); +} + +NET_HANDLE(net_gibsplash, bool isNew) +{ + Net_Accept(net_gibsplash); + + string gentle_prefix = "morphed_"; + + int type = ReadByte(); // gibbage type + int amount = ReadByte() / 16.0; // gibbage amount + vector org; + org.x = ReadShort() * 4 + 2; + org.y = ReadShort() * 4 + 2; + org.z = ReadShort() * 4 + 2; + vector vel = decompressShortVector(ReadShort()); + + return = true; + + float cl_gentle_gibs = autocvar_cl_gentle_gibs; + if(cl_gentle_gibs || autocvar_cl_gentle) + type |= 0x80; // set gentle bit + + if(type & 0x80) + { + if(cl_gentle_gibs == 2) + gentle_prefix = ""; + else if(cl_gentle_gibs == 3) + gentle_prefix = "happy_"; + } + else if(autocvar_cl_particlegibs) + { + type |= 0x80; + gentle_prefix = "particlegibs_"; + } + + if (!(cl_gentle_gibs || autocvar_cl_gentle)) + amount *= 1 - autocvar_cl_nogibs; + + if(autocvar_ekg) + amount *= 5; + + if(amount <= 0 || !isNew) + return; + + setorigin(this, org); // for the sounds + + int specnum = (type & 0x78) / 8; // blood/gibmodel type: using four bits (0..7, bit indexes 3,4,5) + bool issilent = (type & 0x40); + type = type & 0x87; // remove the species bits: bit 7 = gentle, bit 0,1,2 = kind of gib + string specstr = species_prefix(specnum); + + switch(type) + { + case 0x01: + if(!issilent) + sound (this, CH_PAIN, SND_GIB, VOL_BASE, ATTEN_NORM); + + if(prandom() < amount) + TossGib ("models/gibs/eye.md3", org, org, vel, prandomvec() * 150, specnum, 0, issilent); + new_te_bloodshower(_particleeffectnum(strcat(specstr, "bloodshower")), org, 1200, amount); + if(prandom() < amount) + TossGib ("models/gibs/bloodyskull.md3", org, org + 16 * prandomvec(), vel, prandomvec() * 100, specnum, 0, issilent); + + for(int c = 0; c < amount; ++c) + { + int randomvalue = amount - c; + + if(prandom() < randomvalue) + TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/arm.md3", org, org + 16 * prandomvec() + '0 0 8', vel, prandomvec() * (prandom() * 120 + 90), specnum,0, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/chest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/smallchest.md3", org, org + 16 * prandomvec(), vel, prandomvec() * (prandom() * 120 + 80), specnum,0, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/leg1.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/leg2.md3", org, org + 16 * prandomvec() + '0 0 -5', vel, prandomvec() * (prandom() * 120 + 85), specnum,0, issilent); + + // these splat on impact + if(prandom() < randomvalue) + TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); + if(prandom() < randomvalue) + TossGib ("models/gibs/chunk.mdl", org, org + 16 * prandomvec(), vel, prandomvec() * 450, specnum,1, issilent); + } + break; + case 0x02: + __pointparticles(_particleeffectnum(strcat(specstr, "blood")), org, vel, amount * 16); + break; + case 0x03: + if(prandom() < amount) + TossGib ("models/gibs/chunk.mdl", org, org, vel, prandomvec() * (prandom() * 30 + 20), specnum, 1, issilent); // TODO maybe adjust to more randomization? + break; + case 0x81: + __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_dissolve")), org, vel, amount); + break; + case 0x82: + __pointparticles(_particleeffectnum(strcat(gentle_prefix, "damage_hit")), org, vel, amount * 16); + break; + case 0x83: + // no gibs in gentle mode, sorry + break; + } + remove(this); +} +#endif + +#endif diff --git a/qcsrc/common/effects/qc/lightningarc.qc b/qcsrc/common/effects/qc/lightningarc.qc new file mode 100644 index 0000000000..5c6b60e0dd --- /dev/null +++ b/qcsrc/common/effects/qc/lightningarc.qc @@ -0,0 +1,119 @@ +#ifdef IMPLEMENTATION +REGISTER_NET_TEMP(TE_CSQC_ARC) + +#if defined(SVQC) + + void te_csqc_lightningarc(vector from, vector to) + { + WriteHeader(MSG_BROADCAST, TE_CSQC_ARC); + + WriteCoord(MSG_BROADCAST, from.x); + WriteCoord(MSG_BROADCAST, from.y); + WriteCoord(MSG_BROADCAST, from.z); + WriteCoord(MSG_BROADCAST, to.x); + WriteCoord(MSG_BROADCAST, to.y); + WriteCoord(MSG_BROADCAST, to.z); + } + +#elif defined(CSQC) + +/* +.vector fx_start; +.vector fx_end; +.float fx_with; +.string fx_texture; +.float fx_lifetime; + +void b_draw() +{ + //Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, 0, time * 3, '1 1 1', 0.7, DRAWFLAG_ADDITIVE, view_origin); + Draw_CylindricLine(self.fx_start, self.fx_end, self.fx_with, self.fx_texture, (self.fx_with/256), 0, '1 1 1', 1, DRAWFLAG_ADDITIVE, view_origin); + +} +void b_make(vector s,vector e, string t,float l,float z) +{ + entity b; + b = spawn(); + b.fx_texture = t; + b.fx_start = s; + b.fx_end = e; + b.fx_with = z; + b.think = SUB_Remove; + b.nextthink = time + l; + b.draw = b_draw; + + //b.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; +} +*/ + + void cl_effects_lightningarc(vector from, vector to, float seglength, float drifts, float drifte, + float branchfactor, float branchfactor_add) + { + float length = vlen(from - to); + if (length < 1) return; + + // Use at most 16 te_lightning1 segments, as these eat up beam list segments. + // TODO: Change this to R_BeginPolygon code, then we no longer have this limit. + int steps = min(16, floor(length / seglength)); + if (steps < 1) + { + te_lightning1(world, from, to); + return; + } + + float steplength = length / steps; + vector direction = normalize(to - from); + vector pos_l = from; + if (length > seglength) + { + for (int i = 1; i < steps; i += 1) + { + float drift = drifts * (1 - (i / steps)) + drifte * (i / steps); + vector dirnew = normalize(direction * (1 - drift) + randomvec() * drift); + vector pos = pos_l + dirnew * steplength; + te_lightning1(world, pos_l, pos); + // WTF endless recursion if branchfactor is 1.0 (possibly due to adding branchfactor_add). FIXME + // if(random() < branchfactor) + // cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add); + + pos_l = pos; + } + te_lightning1(world, pos_l, to); + } + else + { + te_lightning1(world, from, to); + } + } + + NET_HANDLE(TE_CSQC_ARC, bool isNew) + { + vector from; + from.x = ReadCoord(); + from.y = ReadCoord(); + from.z = ReadCoord(); + vector to; + to.x = ReadCoord(); + to.y = ReadCoord(); + to.z = ReadCoord(); + return = true; + + if (autocvar_cl_effects_lightningarc_simple) + { + te_lightning1(world, from, to); + } + else + { + float seglength = autocvar_cl_effects_lightningarc_segmentlength; + float drifts = autocvar_cl_effects_lightningarc_drift_start; + float drifte = autocvar_cl_effects_lightningarc_drift_end; + float branchfactor = autocvar_cl_effects_lightningarc_branchfactor_start; + float branchfactor_add = autocvar_cl_effects_lightningarc_branchfactor_add; + + cl_effects_lightningarc(from, to, seglength, drifts, drifte, branchfactor, branchfactor_add); + } + } + +#endif + +#endif diff --git a/qcsrc/common/effects/qc/modeleffects.qc b/qcsrc/common/effects/qc/modeleffects.qc new file mode 100644 index 0000000000..41c5ba04f3 --- /dev/null +++ b/qcsrc/common/effects/qc/modeleffects.qc @@ -0,0 +1,163 @@ +#ifdef IMPLEMENTATION + +REGISTER_NET_LINKED(ENT_CLIENT_MODELEFFECT) + +#ifdef SVQC + +.float scale2; + +bool modeleffect_SendEntity(entity this, entity to, int sf) +{ + float f; + WriteHeader(MSG_ENTITY, ENT_CLIENT_MODELEFFECT); + + f = 0; + if(self.velocity != '0 0 0') + f |= 1; + if(self.angles != '0 0 0') + f |= 2; + if(self.avelocity != '0 0 0') + f |= 4; + + WriteByte(MSG_ENTITY, f); + WriteShort(MSG_ENTITY, self.modelindex); + WriteByte(MSG_ENTITY, self.skin); + WriteByte(MSG_ENTITY, self.frame); + WriteCoord(MSG_ENTITY, self.origin.x); + WriteCoord(MSG_ENTITY, self.origin.y); + WriteCoord(MSG_ENTITY, self.origin.z); + if(f & 1) + { + WriteCoord(MSG_ENTITY, self.velocity.x); + WriteCoord(MSG_ENTITY, self.velocity.y); + WriteCoord(MSG_ENTITY, self.velocity.z); + } + if(f & 2) + { + WriteCoord(MSG_ENTITY, self.angles.x); + WriteCoord(MSG_ENTITY, self.angles.y); + WriteCoord(MSG_ENTITY, self.angles.z); + } + if(f & 4) + { + WriteCoord(MSG_ENTITY, self.avelocity.x); + WriteCoord(MSG_ENTITY, self.avelocity.y); + WriteCoord(MSG_ENTITY, self.avelocity.z); + } + WriteShort(MSG_ENTITY, self.scale * 256.0); + WriteShort(MSG_ENTITY, self.scale2 * 256.0); + WriteByte(MSG_ENTITY, self.teleport_time * 100.0); + WriteByte(MSG_ENTITY, self.fade_time * 100.0); + WriteByte(MSG_ENTITY, self.alpha * 255.0); + + return true; +} + +void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2) +{ + entity e = new(modeleffect); + _setmodel(e, m); + e.frame = f; + setorigin(e, o); + e.velocity = v; + e.angles = ang; + e.avelocity = angv; + e.alpha = a; + e.teleport_time = t1; + e.fade_time = t2; + e.skin = s; + if(s0 >= 0) + e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z); + else + e.scale = -s0; + if(s2 >= 0) + e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z); + else + e.scale2 = -s2; + float sz = max(e.scale, e.scale2); + setsize(e, e.mins * sz, e.maxs * sz); + Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity); +} + +#endif + +#ifdef CSQC + +entityclass(ModelEffect); +class(ModelEffect) .float frame1time; +class(ModelEffect) .float lifetime, fadetime; +class(ModelEffect) .float teleport_time; +class(ModelEffect) .float scale1, scale2; + +.float cnt; +.float scale; +.float alpha; + +void ModelEffect_Draw(entity this) +{ + self.angles = self.angles + frametime * self.avelocity; + setorigin(self, self.origin + frametime * self.velocity); + self.scale = self.scale1 + (self.scale2 - self.scale1) * (time - self.teleport_time) / (self.lifetime + self.fadetime - self.teleport_time); + self.alpha = self.cnt * bound(0, 1 - (time - self.lifetime) / self.fadetime, 1); + if(self.alpha < ALPHA_MIN_VISIBLE) + { + remove(self); + return; + } + self.drawmask = MASK_NORMAL; + if(self.scale <= 0) + { + self.drawmask = 0; + return; + } +} + +NET_HANDLE(ENT_CLIENT_MODELEFFECT, bool isnew) +{ + make_pure(self); + + int f = ReadByte(); + + entity e = new(modeleffect); + e.model = "from network"; + e.modelindex = ReadShort(); + e.skin = ReadByte(); + e.frame = ReadByte(); + e.frame1time = time; + e.origin_x = ReadCoord(); + e.origin_y = ReadCoord(); + e.origin_z = ReadCoord(); + setorigin(e, e.origin); + if(f & 1) + { + e.velocity_x = ReadCoord(); + e.velocity_y = ReadCoord(); + e.velocity_z = ReadCoord(); + } + if(f & 2) + { + e.angles_x = ReadAngle(); + e.angles_y = ReadAngle(); + e.angles_z = ReadAngle(); + } + if(f & 4) + { + e.avelocity_x = ReadAngle(); + e.avelocity_y = ReadAngle(); + e.avelocity_z = ReadAngle(); + } + e.scale1 = ReadShort() / 256.0; + e.scale2 = ReadShort() / 256.0; + e.lifetime = time + ReadByte() * 0.01; + e.fadetime = ReadByte() * 0.01; + e.teleport_time = time; + e.cnt = ReadByte() / 255.0; // actually alpha + + e.draw = ModelEffect_Draw; + + if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then + return true; +} +#endif + +#endif diff --git a/qcsrc/common/effects/qc/rubble.qh b/qcsrc/common/effects/qc/rubble.qh new file mode 100644 index 0000000000..1a7cad850b --- /dev/null +++ b/qcsrc/common/effects/qc/rubble.qh @@ -0,0 +1,60 @@ +#ifndef RUBBLE_H +#define RUBBLE_H + +#ifdef CSQC + +entityclass(Rubble); +class(Rubble).float creationtime; + +void RubbleLimit(string cname, float limit, void() deleteproc) +{ + SELFPARAM(); + entity e; + entity oldest; + float c; + float oldesttime; + + // remove rubble of the same type if it's at the limit + // remove multiple rubble if the limit has been decreased + while (1) + { + e = findchain(classname, cname); + if (e == world) break; + // walk the list and count the entities, find the oldest + // initialize our search with the first entity + c = 1; + oldest = e; + oldesttime = e.creationtime; + e = e.chain; + // compare to all other matching entities + while (e) + { + c = c + 1; + if (oldesttime > e.creationtime) + { + oldesttime = e.creationtime; + oldest = e; + } + e = e.chain; + } + + // stop if there are less than the limit already + if (c <= limit) break; + + // delete this oldest one and search again + WITH(entity, self, oldest, deleteproc()); + } +} + +entity RubbleNew(string cname) +{ + // spawn a new entity and return it + entity e = spawn(); + e.classname = cname; + e.creationtime = time; + return e; +} + +#endif + +#endif diff --git a/qcsrc/common/gamemodes/all.inc b/qcsrc/common/gamemodes/all.inc index 4c24bebf5b..bcdcd7c4c0 100644 --- a/qcsrc/common/gamemodes/all.inc +++ b/qcsrc/common/gamemodes/all.inc @@ -1 +1,2 @@ -#include "gamemode/nexball/nexball.qc" +#include "gamemode/nexball/module.inc" +#include "gamemode/onslaught/module.inc" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/module.inc b/qcsrc/common/gamemodes/gamemode/nexball/module.inc new file mode 100644 index 0000000000..0d809629de --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/module.inc @@ -0,0 +1,2 @@ +#include "nexball.qc" +#include "weapon.qc" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 4349a1e5e2..b4becb3979 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -78,14 +78,14 @@ void LogNB(string mode, entity actor) GameLogEcho(s); } -void ball_restart(void) +void ball_restart() {SELFPARAM(); if(self.owner) DropBall(self, self.owner.origin, '0 0 0'); ResetBall(); } -void nexball_setstatus(void) +void nexball_setstatus() {SELFPARAM(); self.items &= ~IT_KEY1; if(self.ballcarried) @@ -103,7 +103,7 @@ void nexball_setstatus(void) } } -void relocate_nexball(void) +void relocate_nexball() {SELFPARAM(); tracebox(self.origin, BALL_MINS, BALL_MAXS, self.origin, true, self); if(trace_startsolid) @@ -120,7 +120,7 @@ void relocate_nexball(void) } } -void DropOwner(void) +void DropOwner() {SELFPARAM(); entity ownr; ownr = self.owner; @@ -132,16 +132,16 @@ void DropOwner(void) void GiveBall(entity plyr, entity ball) {SELFPARAM(); - entity ownr; - - if((ownr = ball.owner)) + .entity weaponentity = weaponentities[0]; // TODO: find ballstealer + entity ownr = ball.owner; + if(ownr) { ownr.effects &= ~autocvar_g_nexball_basketball_effects_default; ownr.ballcarried = world; if(ownr.metertime) { ownr.metertime = 0; - ownr.weaponentity.state = WS_READY; + ownr.(weaponentity).state = WS_READY; } WaypointSprite_Kill(ownr.waypointsprite_attachedforcarrier); } @@ -179,8 +179,8 @@ void GiveBall(entity plyr, entity ball) ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold; } - plyr.weaponentity.weapons = plyr.weapons; - plyr.weaponentity.switchweapon = plyr.weapon; + plyr.(weaponentity).weapons = plyr.weapons; + plyr.(weaponentity).switchweapon = plyr.weapon; plyr.weapons = WEPSET(NEXBALL); setself(plyr); Weapon w = WEP_NEXBALL; @@ -210,7 +210,8 @@ void DropBall(entity ball, vector org, vector vel) if(ball.owner.metertime) { ball.owner.metertime = 0; - ball.owner.weaponentity.state = WS_READY; + .entity weaponentity = weaponentities[0]; // TODO: find ballstealer + ball.owner.(weaponentity).state = WS_READY; } WaypointSprite_Kill(ball.owner.waypointsprite_attachedforcarrier); @@ -221,7 +222,7 @@ void DropBall(entity ball, vector org, vector vel) ball.owner = world; } -void InitBall(void) +void InitBall() {SELFPARAM(); if(gameover) return; self.flags &= ~FL_ONGROUND; @@ -241,7 +242,7 @@ void InitBall(void) LogNB("init", world); } -void ResetBall(void) +void ResetBall() {SELFPARAM(); if(self.cnt < 2) // step 1 { @@ -277,7 +278,7 @@ void ResetBall(void) } } -void football_touch(void) +void football_touch() {SELFPARAM(); if(other.solid == SOLID_BSP) { @@ -323,7 +324,7 @@ void football_touch(void) self.avelocity = -250 * v_forward; // maybe there is a way to make it look better? } -void basketball_touch(void) +void basketball_touch() {SELFPARAM(); if(other.ballcarried) { @@ -345,7 +346,7 @@ void basketball_touch(void) } } -void GoalTouch(void) +void GoalTouch() {SELFPARAM(); entity ball; float isclient, pscore, otherteam; @@ -450,16 +451,14 @@ spawnfunc(nexball_team) void nb_spawnteam(string teamname, float teamcolor) { LOG_TRACE("^2spawned team ", teamname, "\n"); - entity e; - e = spawn(); - e.classname = "nexball_team"; + entity e = new(nexball_team); e.netname = teamname; e.cnt = teamcolor; e.team = e.cnt + 1; nb_teams += 1; } -void nb_spawnteams(void) +void nb_spawnteams() { bool t_red = false, t_blue = false, t_yellow = false, t_pink = false; entity e; @@ -499,7 +498,7 @@ void nb_spawnteams(void) } } -void nb_delayedinit(void) +void nb_delayedinit() { if(find(world, classname, "nexball_team") == world) nb_spawnteams(); @@ -511,7 +510,7 @@ void nb_delayedinit(void) // spawnfuncs // //=======================// -void SpawnBall(void) +void SpawnBall() {SELFPARAM(); if(!g_nexball) { remove(self); return; } @@ -544,13 +543,13 @@ void SpawnBall(void) if(!autocvar_g_nexball_sound_bounce) self.noise = ""; else if(self.noise == "") - self.noise = SND(NB_BOUNCE); + self.noise = strzone(SND(NB_BOUNCE)); //bounce sound placeholder (FIXME) if(self.noise1 == "") - self.noise1 = SND(NB_DROP); + self.noise1 = strzone(SND(NB_DROP)); //ball drop sound placeholder (FIXME) if(self.noise2 == "") - self.noise2 = SND(NB_STEAL); + self.noise2 = strzone(SND(NB_STEAL)); //stealing sound placeholder (FIXME) if(self.noise) precache_sound(self.noise); precache_sound(self.noise1); @@ -607,7 +606,7 @@ float nb_Goal_Customize() return true; } -void SpawnGoal(void) +void SpawnGoal() {SELFPARAM(); if(!g_nexball) { remove(self); return; } @@ -652,7 +651,7 @@ spawnfunc(nexball_fault) { self.team = GOAL_FAULT; if(self.noise == "") - self.noise = SND(TYPEHIT); + self.noise = strzone(SND(TYPEHIT)); SpawnGoal(); } @@ -660,7 +659,7 @@ spawnfunc(nexball_out) { self.team = GOAL_OUT; if(self.noise == "") - self.noise = SND(TYPEHIT); + self.noise = strzone(SND(TYPEHIT)); SpawnGoal(); } @@ -718,7 +717,7 @@ void W_Nexball_Think() self.nextthink = time; } -void W_Nexball_Touch(void) +void W_Nexball_Touch() {SELFPARAM(); entity ball, attacker; attacker = self.owner; @@ -787,7 +786,7 @@ void W_Nexball_Attack(float t) vector trigger_push_calculatevelocity(vector org, entity tgt, float ht); -void W_Nexball_Attack2(void) +void W_Nexball_Attack2() {SELFPARAM(); if(self.ballcarried.enemy) { @@ -803,10 +802,9 @@ void W_Nexball_Attack2(void) return; W_SetupShot(self, false, 2, SND(NB_SHOOT2), CH_WEAPON_A, 0); - entity missile = spawn(); + entity missile = new(ballstealer); missile.owner = self; - missile.classname = "ballstealer"; missile.movetype = MOVETYPE_FLY; PROJECTILE_MAKETRIGGER(missile); @@ -854,34 +852,34 @@ float ball_customize() return true; } - METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, bool fire1, bool fire2)) + METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire)) { - if(fire1) - if(weapon_prepareattack(thiswep, actor, false, autocvar_g_balance_nexball_primary_refire)) + if(fire & 1) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_balance_nexball_primary_refire)) if(autocvar_g_nexball_basketball_meter) { if(self.ballcarried && !self.metertime) self.metertime = time; else - weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } else { W_Nexball_Attack(-1); - weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } - if(fire2) - if(weapon_prepareattack(thiswep, actor, true, autocvar_g_balance_nexball_secondary_refire)) + if(fire & 2) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire)) { W_Nexball_Attack2(); - weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready); } - if(!fire1 && self.metertime && self.ballcarried) + if(!(fire & 1) && self.metertime && self.ballcarried) { W_Nexball_Attack(time - self.metertime); // DropBall or stealing will set metertime back to 0 - weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } } METHOD(BallStealer, wr_setup, void(BallStealer thiswep)) @@ -967,15 +965,16 @@ MUTATOR_HOOKFUNCTION(nb, PlayerPreThink) } else { - if(self.weaponentity.weapons) + .entity weaponentity = weaponentities[0]; // TODO + if(self.(weaponentity).weapons) { - self.weapons = self.weaponentity.weapons; + self.weapons = self.(weaponentity).weapons; Weapon w = WEP_NEXBALL; w.wr_resetplayer(w); - self.switchweapon = self.weaponentity.switchweapon; + self.switchweapon = self.(weaponentity).switchweapon; W_SwitchWeapon(self.switchweapon); - self.weaponentity.weapons = '0 0 0'; + self.(weaponentity).weapons = '0 0 0'; } } @@ -996,7 +995,8 @@ MUTATOR_HOOKFUNCTION(nb, PlayerSpawn) { SELFPARAM(); this.metertime = 0; - this.weaponentity.weapons = '0 0 0'; + .entity weaponentity = weaponentities[0]; + this.(weaponentity).weapons = '0 0 0'; if (nexball_mode & NBM_BASKETBALL) this.weapons |= WEPSET(NEXBALL); @@ -1066,10 +1066,6 @@ MUTATOR_HOOKFUNCTION(nb, SendWaypoint) REGISTER_MUTATOR(nb, g_nexball) { - ActivateTeamplay(); - SetLimits(autocvar_g_nexball_goallimit, autocvar_g_nexball_goalleadlimit, -1, -1); - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { g_nexball_meter_period = autocvar_g_nexball_meter_period; @@ -1089,6 +1085,10 @@ REGISTER_MUTATOR(nb, g_nexball) InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE); WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + ActivateTeamplay(); + SetLimits(autocvar_g_nexball_goallimit, autocvar_g_nexball_goalleadlimit, -1, -1); + have_team_spawns = -1; // request team spawns } MUTATOR_ONROLLBACK_OR_REMOVE diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh index 58bf6ec4dd..9dd8042be3 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh @@ -1,8 +1,6 @@ #ifndef GAMEMODE_NEXBALL_H #define GAMEMODE_NEXBALL_H -#include "weapon.qc" - #ifdef SVQC //EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME const float BALL_EFFECTMASK = 1229; diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc index ae882bb32a..c6aa4be215 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc @@ -5,7 +5,7 @@ CLASS(BallStealer, PortoLaunch) /* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(BallStealer, impulse, int, 0); /* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); -/* wepname */ ATTRIB(BallStealer, message, string, _("Ball Stealer")); +/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); ENDCLASS(BallStealer) REGISTER_WEAPON(NEXBALL, NEW(BallStealer)); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc new file mode 100644 index 0000000000..28ac85d79c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc @@ -0,0 +1,198 @@ +#include "cl_controlpoint.qh" + +.vector colormod; +.float alpha; +.int count; +.float pain_finished; + +.bool iscaptured; + +.vector cp_origin, cp_bob_origin; +.float cp_bob_spd; + +.vector cp_bob_dmg; + +.vector punchangle; + +.float max_health; + +.entity icon_realmodel; + +void cpicon_draw(entity this) +{ + if(time < this.move_time) { return; } + + if(this.cp_bob_dmg_z > 0) + this.cp_bob_dmg_z = this.cp_bob_dmg_z - 3 * frametime; + else + this.cp_bob_dmg_z = 0; + this.cp_bob_origin_z = 4 * PI * (1 - cos(this.cp_bob_spd)); + this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime; + this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1)); + + if(!this.iscaptured) this.alpha = this.health / this.max_health; + + if(this.iscaptured) + { + if (this.punchangle_x > 0) + { + this.punchangle_x = this.punchangle_x - 60 * frametime; + if (this.punchangle_x < 0) + this.punchangle_x = 0; + } + else if (this.punchangle_x < 0) + { + this.punchangle_x = this.punchangle_x + 60 * frametime; + if (this.punchangle_x > 0) + this.punchangle_x = 0; + } + + if (this.punchangle_y > 0) + { + this.punchangle_y = this.punchangle_y - 60 * frametime; + if (this.punchangle_y < 0) + this.punchangle_y = 0; + } + else if (this.punchangle_y < 0) + { + this.punchangle_y = this.punchangle_y + 60 * frametime; + if (this.punchangle_y > 0) + this.punchangle_y = 0; + } + + if (this.punchangle_z > 0) + { + this.punchangle_z = this.punchangle_z - 60 * frametime; + if (this.punchangle_z < 0) + this.punchangle_z = 0; + } + else if (this.punchangle_z < 0) + { + this.punchangle_z = this.punchangle_z + 60 * frametime; + if (this.punchangle_z > 0) + this.punchangle_z = 0; + } + + this.angles_x = this.punchangle_x; + this.angles_y = this.punchangle_y + this.move_angles_y; + this.angles_z = this.punchangle_z; + this.move_angles_y = this.move_angles_y + 45 * frametime; + } + + setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg); +} + +void cpicon_damage(entity this, float hp) +{ + if(!this.iscaptured) { return; } + + if(hp < this.max_health * 0.25) + setmodel(this, MDL_ONS_CP3); + else if(hp < this.max_health * 0.50) + setmodel(this, MDL_ONS_CP2); + else if(hp < this.max_health * 0.75) + setmodel(this, MDL_ONS_CP1); + else if(hp <= this.max_health || hp >= this.max_health) + setmodel(this, MDL_ONS_CP); + + this.punchangle = (2 * randomvec() - '1 1 1') * 45; + + this.cp_bob_dmg_z = (2 * random() - 1) * 15; + this.pain_finished = time + 1; + this.colormod = '2 2 2'; + + setsize(this, CPICON_MIN, CPICON_MAX); +} + +void cpicon_construct(entity this) +{ + this.netname = "Control Point Icon"; + + setmodel(this, MDL_ONS_CP); + setsize(this, CPICON_MIN, CPICON_MAX); + + if(this.icon_realmodel == world) + { + this.icon_realmodel = spawn(); + setmodel(this.icon_realmodel, MDL_Null); + setorigin(this.icon_realmodel, this.origin); + setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX); + this.icon_realmodel.movetype = MOVETYPE_NOCLIP; + this.icon_realmodel.solid = SOLID_NOT; + this.icon_realmodel.move_origin = this.icon_realmodel.origin; + } + + if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; } + + this.move_movetype = MOVETYPE_NOCLIP; + this.solid = SOLID_NOT; + this.movetype = MOVETYPE_NOCLIP; + this.move_origin = this.origin; + this.move_time = time; + this.drawmask = MASK_NORMAL; + this.alpha = 1; + this.draw = cpicon_draw; + this.cp_origin = this.origin; + this.cp_bob_origin = '0 0 0.1'; + this.cp_bob_spd = 0; +} + +.vector glowmod; +void cpicon_changeteam(entity this) +{ + if(this.team) + { + this.glowmod = Team_ColorRGB(this.team - 1); + this.teamradar_color = Team_ColorRGB(this.team - 1); + this.colormap = 1024 + (this.team - 1) * 17; + } + else + { + this.colormap = 1024; + this.glowmod = '1 1 0'; + this.teamradar_color = '1 1 0'; + } +} + +NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) +{ + return = true; + int sf = ReadByte(); + + if(sf & CPSF_SETUP) + { + this.origin_x = ReadCoord(); + this.origin_y = ReadCoord(); + this.origin_z = ReadCoord(); + setorigin(this, this.origin); + + this.health = ReadByte(); + this.max_health = ReadByte(); + this.count = ReadByte(); + this.team = ReadByte(); + this.iscaptured = ReadByte(); + + if(!this.count) + this.count = (this.health - this.max_health) * frametime; + + cpicon_changeteam(this); + cpicon_construct(this); + } + + if(sf & CPSF_STATUS) + { + int _tmp = ReadByte(); + if(_tmp != this.team) + { + this.team = _tmp; + cpicon_changeteam(this); + } + + _tmp = ReadByte(); + + if(_tmp != this.health) + cpicon_damage(this, _tmp); + + this.health = _tmp; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh new file mode 100644 index 0000000000..15586ea5dc --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh @@ -0,0 +1,10 @@ +#ifndef CLIENT_CONTROLPOINT_H +#define CLIENT_CONTROLPOINT_H + +const vector CPICON_MIN = '-32 -32 -9'; +const vector CPICON_MAX = '32 32 25'; + +const int CPSF_STATUS = 4; +const int CPSF_SETUP = 8; + +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc new file mode 100644 index 0000000000..f5b61117d1 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc @@ -0,0 +1,223 @@ +#include "cl_generator.qh" + +.float alpha; +.float scale; +.int count; +.float max_health; + +void ons_generator_ray_draw(entity this) +{ + if(time < self.move_time) + return; + + self.move_time = time + 0.05; + + if(self.count > 10) + { + remove(self); + return; + } + + if(self.count > 5) + self.alpha -= 0.1; + else + self.alpha += 0.1; + + self.scale += 0.2; + self.count +=1; +} + +void ons_generator_ray_spawn(vector org) +{ + entity e = new(ons_ray); + setmodel(e, MDL_ONS_RAY); + setorigin(e, org); + e.angles = randomvec() * 360; + e.move_origin = org; + e.movetype = MOVETYPE_NONE; + e.alpha = 0; + e.scale = random() * 5 + 8; + e.move_time = time + 0.05; + e.drawmask = MASK_NORMAL; + e.draw = ons_generator_ray_draw; +} + +void generator_draw(entity this) +{ + if(time < self.move_time) + return; + + if(self.health > 0) + { + // damaged fx (less probable the more damaged is the generator) + if(random() < 0.9 - self.health / self.max_health) + if(random() < 0.01) + { + pointparticles(EFFECT_ELECTRO_BALLEXPLODE, self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); + sound(self, CH_TRIGGER, SND_ONS_ELECTRICITY_EXPLODE, VOL_BASE, ATTEN_NORM); + } + else + pointparticles(EFFECT_ONS_GENERATOR_DAMAGED, self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1); + + self.move_time = time + 0.1; + + return; + } + + if(self.count <= 0) + return; + + vector org; + int i; + + // White shockwave + if(self.count==40||self.count==20) + { + sound(self, CH_TRIGGER, SND_ONS_SHOCKWAVE, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_ELECTRO_COMBO, self.origin, '0 0 0', 6); + } + + // rays + if(random() > 0.25) + { + ons_generator_ray_spawn(self.origin); + } + + // Spawn fire balls + for(i=0;i < 10;++i) + { + org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20'); + pointparticles(EFFECT_ONS_GENERATOR_GIB, org, '0 0 0', 1); + } + + // Short explosion sound + small explosion + if(random() < 0.25) + { + te_explosion(self.origin); + sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + } + + // Particles + org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8'); + pointparticles(EFFECT_ONS_GENERATOR_EXPLODE, org, '0 0 0', 1); + + // Final explosion + if(self.count==1) + { + org = self.origin; + te_explosion(org); + pointparticles(EFFECT_ONS_GENERATOR_EXPLODE2, org, '0 0 0', 1); + sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + } + + self.move_time = time + 0.05; + + self.count -= 1; +} + +void generator_damage(float hp) +{SELFPARAM(); + if(hp <= 0) + setmodel(self, MDL_ONS_GEN_DEAD); + else if(hp < self.max_health * 0.10) + setmodel(self, MDL_ONS_GEN9); + else if(hp < self.max_health * 0.20) + setmodel(self, MDL_ONS_GEN8); + else if(hp < self.max_health * 0.30) + setmodel(self, MDL_ONS_GEN7); + else if(hp < self.max_health * 0.40) + setmodel(self, MDL_ONS_GEN6); + else if(hp < self.max_health * 0.50) + setmodel(self, MDL_ONS_GEN5); + else if(hp < self.max_health * 0.60) + setmodel(self, MDL_ONS_GEN4); + else if(hp < self.max_health * 0.70) + setmodel(self, MDL_ONS_GEN3); + else if(hp < self.max_health * 0.80) + setmodel(self, MDL_ONS_GEN2); + else if(hp < self.max_health * 0.90) + setmodel(self, MDL_ONS_GEN1); + else if(hp <= self.max_health || hp >= self.max_health) + setmodel(self, MDL_ONS_GEN); + + setsize(self, GENERATOR_MIN, GENERATOR_MAX); +} + +void generator_construct() +{SELFPARAM(); + self.netname = "Generator"; + self.classname = "onslaught_generator"; + + setorigin(self, self.origin); + setmodel(self, MDL_ONS_GEN); + setsize(self, GENERATOR_MIN, GENERATOR_MAX); + + self.move_movetype = MOVETYPE_NOCLIP; + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NOCLIP; + self.move_origin = self.origin; + self.move_time = time; + self.drawmask = MASK_NORMAL; + self.alpha = 1; + self.draw = generator_draw; +} + +.vector glowmod; +void generator_changeteam() +{SELFPARAM(); + if(self.team) + { + self.glowmod = Team_ColorRGB(self.team - 1); + self.teamradar_color = Team_ColorRGB(self.team - 1); + self.colormap = 1024 + (self.team - 1) * 17; + } + else + { + self.colormap = 1024; + self.glowmod = '1 1 0'; + self.teamradar_color = '1 1 0'; + } +} + +NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) +{ + return = true; + int sf = ReadByte(); + + if(sf & GSF_SETUP) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.health = ReadByte(); + self.max_health = ReadByte(); + self.count = ReadByte(); + self.team = ReadByte(); + + if(!self.count) + self.count = 40; + + generator_changeteam(); + generator_construct(); + } + + if(sf & GSF_STATUS) + { + int _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + generator_changeteam(); + } + + _tmp = ReadByte(); + + if(_tmp != self.health) + generator_damage(_tmp); + + self.health = _tmp; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh new file mode 100644 index 0000000000..3c0cf28697 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh @@ -0,0 +1,9 @@ +#ifndef CLIENT_GENERATOR_H +#define CLIENT_GENERATOR_H +const vector GENERATOR_MIN = '-52 -52 -14'; +const vector GENERATOR_MAX = '52 52 75'; + +const int GSF_STATUS = 4; +const int GSF_SETUP = 8; + +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc b/qcsrc/common/gamemodes/gamemode/onslaught/module.inc new file mode 100644 index 0000000000..fee33b14e2 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/module.inc @@ -0,0 +1,24 @@ +#ifndef ONS_CONSTANTS + #define ONS_CONSTANTS + REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR) + REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON) +#endif + +#if defined(SVQC) + #include "onslaught.qc" + #ifndef IMPLEMENTATION + #include "sv_controlpoint.qh" + #include "sv_generator.qh" + #else + #include "sv_controlpoint.qc" + #include "sv_generator.qc" + #endif +#elif defined(CSQC) + #ifndef IMPLEMENTATION + #include "cl_controlpoint.qh" + #include "cl_generator.qh" + #else + #include "cl_controlpoint.qc" + #include "cl_generator.qc" + #endif +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc new file mode 100644 index 0000000000..04b5a3f7bf --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -0,0 +1,2326 @@ +#ifndef GAMEMODE_ONSLAUGHT_H +#define GAMEMODE_ONSLAUGHT_H + +float autocvar_g_onslaught_point_limit; +void ons_Initialize(); + +REGISTER_MUTATOR(ons, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ons_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ons_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return false; +} + +#ifdef SVQC + +.entity ons_toucher; // player who touched the control point + +// control point / generator constants +const float ONS_CP_THINKRATE = 0.2; +const float GEN_THINKRATE = 1; +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) +const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); +const vector CPICON_OFFSET = ('0 0 96'); + +// list of generators on the map +entity ons_worldgeneratorlist; +.entity ons_worldgeneratornext; +.entity ons_stalegeneratornext; + +// list of control points on the map +entity ons_worldcplist; +.entity ons_worldcpnext; +.entity ons_stalecpnext; + +// list of links on the map +entity ons_worldlinklist; +.entity ons_worldlinknext; +.entity ons_stalelinknext; + +// definitions +.entity sprite; +.string target2; +.int iscaptured; +.int islinked; +.int isshielded; +.float lasthealth; +.int lastteam; +.int lastshielded; +.int lastcaptured; + +.bool waslinked; + +bool ons_stalemate; + +.float teleport_antispam; + +.bool ons_roundlost; + +// waypoint sprites +.entity bot_basewaypoint; // generator waypointsprite + +.bool isgenneighbor[17]; +.bool iscpneighbor[17]; +float ons_notification_time[17]; + +.float ons_overtime_damagedelay; + +.vector ons_deathloc; + +.entity ons_spawn_by; + +// declarations for functions used outside gamemode_onslaught.qc +void ons_Generator_UpdateSprite(entity e); +void ons_ControlPoint_UpdateSprite(entity e); +bool ons_ControlPoint_Attackable(entity cp, int teamnumber); + +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet +float ons_captureshield_force; // push force of the shield + +// bot player logic +const int HAVOCBOT_ONS_ROLE_NONE = 0; +const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; +const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; +const int HAVOCBOT_ONS_ROLE_OFFENSE = 8; + +.entity havocbot_ons_target; + +.int havocbot_role_flags; +.float havocbot_attack_time; + +void havocbot_role_ons_defense(); +void havocbot_role_ons_offense(); +void havocbot_role_ons_assistant(); + +void havocbot_ons_reset_role(entity bot); +void havocbot_goalrating_items(float ratingscale, vector org, float sradius); +void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius); + +// score rule declarations +const int ST_ONS_CAPS = 1; +const int SP_ONS_CAPS = 4; +const int SP_ONS_TAKES = 6; + +#endif +#endif + +#ifdef IMPLEMENTATION + +#include "sv_controlpoint.qh" +#include "sv_generator.qh" + +bool g_onslaught; + +float autocvar_g_onslaught_debug; +float autocvar_g_onslaught_teleport_wait; +bool autocvar_g_onslaught_spawn_at_controlpoints; +bool autocvar_g_onslaught_spawn_at_generator; +float autocvar_g_onslaught_cp_proxydecap; +float autocvar_g_onslaught_cp_proxydecap_distance = 512; +float autocvar_g_onslaught_cp_proxydecap_dps = 100; +float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; +float autocvar_g_onslaught_spawn_at_controlpoints_random; +float autocvar_g_onslaught_spawn_at_generator_chance; +float autocvar_g_onslaught_spawn_at_generator_random; +float autocvar_g_onslaught_cp_buildhealth; +float autocvar_g_onslaught_cp_buildtime; +float autocvar_g_onslaught_cp_health; +float autocvar_g_onslaught_cp_regen; +float autocvar_g_onslaught_gen_health; +float autocvar_g_onslaught_shield_force = 100; +float autocvar_g_onslaught_allow_vehicle_touch; +float autocvar_g_onslaught_round_timelimit; +float autocvar_g_onslaught_warmup; +float autocvar_g_onslaught_teleport_radius; +float autocvar_g_onslaught_spawn_choose; +float autocvar_g_onslaught_click_radius; + +void FixSize(entity e); + +// ======================= +// CaptureShield Functions +// ======================= + +bool ons_CaptureShield_Customize() +{SELFPARAM(); + entity e = WaypointSprite_getviewentity(other); + + if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; } + if(SAME_TEAM(self, e)) { return false; } + + return true; +} + +void ons_CaptureShield_Touch() +{SELFPARAM(); + if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; } + if(!IS_PLAYER(other)) { return; } + if(SAME_TEAM(other, self)) { return; } + + vector mymid = (self.absmin + self.absmax) * 0.5; + vector othermid = (other.absmin + other.absmax) * 0.5; + + Damage(other, self, self, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force); + + if(IS_REAL_CLIENT(other)) + { + play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + + if(self.enemy.classname == "onslaught_generator") + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); + else + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); + } +} + +void ons_CaptureShield_Reset() +{SELFPARAM(); + self.colormap = self.enemy.colormap; + self.team = self.enemy.team; +} + +void ons_CaptureShield_Spawn(entity generator, bool is_generator) +{ + entity shield = new(ons_captureshield); + + shield.enemy = generator; + shield.team = generator.team; + shield.colormap = generator.colormap; + shield.reset = ons_CaptureShield_Reset; + shield.touch = ons_CaptureShield_Touch; + shield.customizeentityforclient = ons_CaptureShield_Customize; + shield.effects = EF_ADDITIVE; + shield.movetype = MOVETYPE_NOCLIP; + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 1; + shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); + + precache_model(shield.model); + setorigin(shield, generator.origin); + _setmodel(shield, shield.model); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} + + +// ========== +// Junk Pile +// ========== + +void ons_debug(string input) +{ + switch(autocvar_g_onslaught_debug) + { + case 1: LOG_TRACE(input); break; + case 2: LOG_INFO(input); break; + } +} + +void setmodel_fixsize(entity e, Model m) +{ + setmodel(e, m); + FixSize(e); +} + +void onslaught_updatelinks() +{ + entity l; + // first check if the game has ended + ons_debug("--- updatelinks ---\n"); + // mark generators as being shielded and networked + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.iscaptured) + ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n")); + else + ons_debug(strcat(etos(l), " (generator) is destroyed\n")); + l.islinked = l.iscaptured; + l.isshielded = l.iscaptured; + l.sprite.SendFlags |= 16; + } + // mark points as shielded and not networked + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.islinked = false; + l.isshielded = true; + int i; + for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } + ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n")); + l.sprite.SendFlags |= 16; + } + // flow power outward from the generators through the network + bool stop = false; + while (!stop) + { + stop = true; + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + // if both points are captured by the same team, and only one of + // them is powered, mark the other one as powered as well + if (l.enemy.iscaptured && l.goalentity.iscaptured) + if (l.enemy.islinked != l.goalentity.islinked) + if(SAME_TEAM(l.enemy, l.goalentity)) + { + if (!l.goalentity.islinked) + { + stop = false; + l.goalentity.islinked = true; + ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n")); + } + else if (!l.enemy.islinked) + { + stop = false; + l.enemy.islinked = true; + ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n")); + } + } + } + } + // now that we know which points are powered we can mark their neighbors + // as unshielded if team differs + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + if (l.goalentity.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n")); + l.enemy.isshielded = false; + } + if(l.goalentity.classname == "onslaught_generator") + l.enemy.isgenneighbor[l.goalentity.team] = true; + else + l.enemy.iscpneighbor[l.goalentity.team] = true; + } + if (l.enemy.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n")); + l.goalentity.isshielded = false; + } + if(l.enemy.classname == "onslaught_generator") + l.goalentity.isgenneighbor[l.enemy.team] = true; + else + l.goalentity.iscpneighbor[l.enemy.team] = true; + } + } + // now update the generators + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.isshielded) + { + ons_debug(strcat(etos(l), " (generator) is shielded\n")); + l.takedamage = DAMAGE_NO; + l.bot_attack = false; + } + else + { + ons_debug(strcat(etos(l), " (generator) is not shielded\n")); + l.takedamage = DAMAGE_AIM; + l.bot_attack = true; + } + + ons_Generator_UpdateSprite(l); + } + // now update the takedamage and alpha variables on control point icons + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + if (l.isshielded) + { + ons_debug(strcat(etos(l), " (point) is shielded\n")); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_NO; + l.goalentity.bot_attack = false; + } + } + else + { + ons_debug(strcat(etos(l), " (point) is not shielded\n")); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_AIM; + l.goalentity.bot_attack = true; + } + } + ons_ControlPoint_UpdateSprite(l); + } + l = findchain(classname, "ons_captureshield"); + while(l) + { + l.team = l.enemy.team; + l.colormap = l.enemy.colormap; + l = l.chain; + } +} + + +// =================== +// Main Link Functions +// =================== + +bool ons_Link_Send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & 1) + { + WriteCoord(MSG_ENTITY, self.goalentity.origin_x); + WriteCoord(MSG_ENTITY, self.goalentity.origin_y); + WriteCoord(MSG_ENTITY, self.goalentity.origin_z); + } + if(sendflags & 2) + { + WriteCoord(MSG_ENTITY, self.enemy.origin_x); + WriteCoord(MSG_ENTITY, self.enemy.origin_y); + WriteCoord(MSG_ENTITY, self.enemy.origin_z); + } + if(sendflags & 4) + { + WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 + } + return true; +} + +void ons_Link_CheckUpdate() +{SELFPARAM(); + // TODO check if the two sides have moved (currently they won't move anyway) + float cc = 0, cc1 = 0, cc2 = 0; + + if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; } + if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; } + + cc = cc1 + cc2; + + if(cc != self.clientcolors) + { + self.clientcolors = cc; + self.SendFlags |= 4; + } + + self.nextthink = time; +} + +void ons_DelayedLinkSetup() +{SELFPARAM(); + self.goalentity = find(world, targetname, self.target); + self.enemy = find(world, targetname, self.target2); + if(!self.goalentity) { objerror("can not find target\n"); } + if(!self.enemy) { objerror("can not find target2\n"); } + + ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n")); + self.SendFlags |= 3; + self.think = ons_Link_CheckUpdate; + self.nextthink = time; +} + + +// ============================= +// Main Control Point Functions +// ============================= + +int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) +{ + if(cp.isgenneighbor[teamnumber]) { return 2; } + if(cp.iscpneighbor[teamnumber]) { return 1; } + + return 0; +} + +int ons_ControlPoint_Attackable(entity cp, int teamnumber) + // -2: SAME TEAM, attackable by enemy! + // -1: SAME TEAM! + // 0: off limits + // 1: attack it + // 2: touch it + // 3: attack it (HIGH PRIO) + // 4: touch it (HIGH PRIO) +{ + int a; + + if(cp.isshielded) + { + return 0; + } + else if(cp.goalentity) + { + // if there's already an icon built, nothing happens + if(cp.team == teamnumber) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a) // attackable by enemy? + return -2; // EMERGENCY! + return -1; + } + // we know it can be linked, so no need to check + // but... + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a == 2) // near our generator? + return 3; // EMERGENCY! + return 1; + } + else + { + // free point + if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t + if(a == 2) + return 4; // GET THIS ONE NOW! + else + return 2; // TOUCH ME + } + } + return 0; +} + +void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(damage <= 0) { return; } + + if (self.owner.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + self.pain_finished = time + 1; + attacker.typehitsound += 1; // play both sounds (shield is way too quiet) + } + + return; + } + + if(IS_PLAYER(attacker)) + if(time - ons_notification_time[self.team] > 10) + { + play2team(self.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); + ons_notification_time[self.team] = time; + } + + self.health = self.health - damage; + if(self.owner.iscaptured) + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + else + WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE)); + self.pain_finished = time + 1; + // particles on every hit + pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); + else + sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); + + if (self.health < 0) + { + sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname); + + PlayerScore_Add(attacker, SP_ONS_TAKES, 1); + PlayerScore_Add(attacker, SP_SCORE, 10); + + self.owner.goalentity = world; + self.owner.islinked = false; + self.owner.iscaptured = false; + self.owner.team = 0; + self.owner.colormap = 1024; + + WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + setself(self.owner); + activator = self; + SUB_UseTargets (); + setself(this); + + self.owner.waslinked = self.owner.islinked; + if(self.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel_fixsize(self.owner, MDL_ONS_CP_PAD1); + //setsize(self, '-32 -32 0', '32 32 8'); + + remove(self); + } + + self.SendFlags |= CPSF_STATUS; +} + +void ons_ControlPoint_Icon_Think() +{SELFPARAM(); + self.nextthink = time + ONS_CP_THINKRATE; + + if(autocvar_g_onslaught_cp_proxydecap) + { + int _enemy_count = 0; + int _friendly_count = 0; + float _dist; + entity _player; + + FOR_EACH_PLAYER(_player) + { + if(!_player.deadflag) + { + _dist = vlen(_player.origin - self.origin); + if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) + { + if(SAME_TEAM(_player, self)) + ++_friendly_count; + else + ++_enemy_count; + } + } + } + + _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); + _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); + + self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); + self.SendFlags |= CPSF_STATUS; + if(self.health <= 0) + { + ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0'); + return; + } + } + + if (time > self.pain_finished + 5) + { + if(self.health < self.max_health) + { + self.health = self.health + self.count; + if (self.health >= self.max_health) + self.health = self.max_health; + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + } + } + + if(self.owner.islinked != self.owner.waslinked) + { + // unteam the spawnpoint if needed + int t = self.owner.team; + if(!self.owner.islinked) + self.owner.team = 0; + + setself(self.owner); + activator = self; + SUB_UseTargets (); + setself(this); + + self.owner.team = t; + + self.owner.waslinked = self.owner.islinked; + } + + // damaged fx + if(random() < 0.6 - self.health / self.max_health) + { + Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); + + if(random() > 0.8) + sound(self, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); + else if (random() > 0.5) + sound(self, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); + } +} + +void ons_ControlPoint_Icon_BuildThink() +{SELFPARAM(); + int a; + + self.nextthink = time + ONS_CP_THINKRATE; + + // only do this if there is power + a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team); + if(!a) + return; + + self.health = self.health + self.count; + + self.SendFlags |= CPSF_STATUS; + + if (self.health >= self.max_health) + { + self.health = self.max_health; + self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on + self.think = ons_ControlPoint_Icon_Think; + sound(self, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); + self.owner.iscaptured = true; + self.solid = SOLID_BBOX; + + Send_Effect(EFFECT_CAP(self.owner.team), self.owner.origin, '0 0 0', 1); + + WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.owner.sprite, self.health); + + if(IS_PLAYER(self.owner.ons_toucher)) + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message); + Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message); + Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message); + PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1); + PlayerTeamScore_AddScore(self.owner.ons_toucher, 10); + } + + self.owner.ons_toucher = world; + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + setself(self.owner); + activator = self; + SUB_UseTargets (); + setself(this); + + self.SendFlags |= CPSF_SETUP; + } + if(self.owner.model != MDL_ONS_CP_PAD2.model_str()) + setmodel_fixsize(self.owner, MDL_ONS_CP_PAD2); + + if(random() < 0.9 - self.health / self.max_health) + Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1); +} + +void onslaught_controlpoint_icon_link(entity e, void() spawnproc); + +void ons_ControlPoint_Icon_Spawn(entity cp, entity player) +{ + entity e = new(onslaught_controlpoint_icon); + + setsize(e, CPICON_MIN, CPICON_MAX); + setorigin(e, cp.origin + CPICON_OFFSET); + + e.owner = cp; + e.max_health = autocvar_g_onslaught_cp_health; + e.health = autocvar_g_onslaught_cp_buildhealth; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_AIM; + e.bot_attack = true; + e.event_damage = ons_ControlPoint_Icon_Damage; + e.team = player.team; + e.colormap = 1024 + (e.team - 1) * 17; + e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + + sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); + + cp.goalentity = e; + cp.team = e.team; + cp.colormap = e.colormap; + + Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); + + WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); + cp.sprite.SendFlags |= 16; + + onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); +} + +entity ons_ControlPoint_Waypoint(entity e) +{ + if(e.team) + { + int a = ons_ControlPoint_Attackable(e, e.team); + + if(a == -2) { return WP_OnsCPDefend; } // defend now + if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch + if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack + } + else + return WP_OnsCP; + + return WP_Null; +} + +void ons_ControlPoint_UpdateSprite(entity e) +{ + entity s1 = ons_ControlPoint_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + bool sh; + sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); + + if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) + { + if(e.iscaptured) // don't mess up build bars! + { + if(sh) + { + WaypointSprite_UpdateMaxHealth(e.sprite, 0); + } + else + { + WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); + WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + } + } + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + + e.lastteam = e.team + 2; + e.lastshielded = sh; + e.lastcaptured = e.iscaptured; + } +} + +void ons_ControlPoint_Touch() +{SELFPARAM(); + entity toucher = other; + int attackable; + + if(IS_VEHICLE(toucher) && toucher.owner) + if(autocvar_g_onslaught_allow_vehicle_touch) + toucher = toucher.owner; + else + return; + + if(!IS_PLAYER(toucher)) { return; } + if(toucher.frozen) { return; } + if(toucher.deadflag != DEAD_NO) { return; } + + if ( SAME_TEAM(self,toucher) ) + if ( self.iscaptured ) + { + if(time <= toucher.teleport_antispam) + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); + } + + attackable = ons_ControlPoint_Attackable(self, toucher.team); + if(attackable != 2 && attackable != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + ons_ControlPoint_Icon_Spawn(self, toucher); + + self.ons_toucher = toucher; + + onslaught_updatelinks(); +} + +void ons_ControlPoint_Think() +{SELFPARAM(); + self.nextthink = time + ONS_CP_THINKRATE; + CSQCMODEL_AUTOUPDATE(self); +} + +void ons_ControlPoint_Reset() +{SELFPARAM(); + if(self.goalentity) + remove(self.goalentity); + + self.goalentity = world; + self.team = 0; + self.colormap = 1024; + self.iscaptured = false; + self.islinked = false; + self.isshielded = true; + self.think = ons_ControlPoint_Think; + self.ons_toucher = world; + self.nextthink = time + ONS_CP_THINKRATE; + setmodel_fixsize(self, MDL_ONS_CP_PAD1); + + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); + + activator = self; + SUB_UseTargets(); // to reset the structures, playerspawns etc. + + CSQCMODEL_AUTOUPDATE(self); +} + +void ons_DelayedControlPoint_Setup() +{SELFPARAM(); + onslaught_updatelinks(); + + // captureshield setup + ons_CaptureShield_Spawn(self, false); + + CSQCMODEL_AUTOINIT(self); +} + +void ons_ControlPoint_Setup(entity cp) +{SELFPARAM(); + // declarations + setself(cp); // for later usage with droptofloor() + + // main setup + cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist + ons_worldcplist = cp; + + cp.netname = "Control point"; + cp.team = 0; + cp.solid = SOLID_BBOX; + cp.movetype = MOVETYPE_NONE; + cp.touch = ons_ControlPoint_Touch; + cp.think = ons_ControlPoint_Think; + cp.nextthink = time + ONS_CP_THINKRATE; + cp.reset = ons_ControlPoint_Reset; + cp.colormap = 1024; + cp.iscaptured = false; + cp.islinked = false; + cp.isshielded = true; + + if(cp.message == "") { cp.message = "a"; } + + // appearence + setmodel_fixsize(cp, MDL_ONS_CP_PAD1); + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location + { + cp.noalign = true; + cp.movetype = MOVETYPE_NONE; + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + setorigin(cp, cp.origin + '0 0 20'); + cp.noalign = false; + setself(cp); + droptofloor(); + cp.movetype = MOVETYPE_TOSS; + } + + // waypointsprites + WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + + InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); +} + + +// ========================= +// Main Generator Functions +// ========================= + +entity ons_Generator_Waypoint(entity e) +{ + if (e.isshielded) + return WP_OnsGenShielded; + return WP_OnsGen; +} + +void ons_Generator_UpdateSprite(entity e) +{ + entity s1 = ons_Generator_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) + { + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + } +} + +void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(damage <= 0) { return; } + if(warmup_stage || gameover) { return; } + if(!round_handler_IsRoundStarted()) { return; } + + if (attacker != self) + { + if (self.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > self.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + attacker.typehitsound += 1; + self.pain_finished = time + 1; + } + return; + } + if (time > self.pain_finished) + { + self.pain_finished = time + 10; + entity head; + FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); } + play2team(self.team, SND(ONS_GENERATOR_UNDERATTACK)); + } + } + self.health = self.health - damage; + WaypointSprite_UpdateHealth(self.sprite, self.health); + // choose an animation frame based on health + self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); + // see if the generator is still functional, or dying + if (self.health > 0) + { + self.lasthealth = self.health; + } + else + { + if (attacker == self) + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_)); + else + { + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_)); + PlayerScore_Add(attacker, SP_SCORE, 100); + } + self.iscaptured = false; + self.islinked = false; + self.isshielded = false; + self.takedamage = DAMAGE_NO; // can't be hurt anymore + self.event_damage = func_null; // won't do anything if hurt + self.count = 0; // reset counter + self.think = func_null; + self.nextthink = 0; + //self.think(); // do the first explosion now + + WaypointSprite_UpdateMaxHealth(self.sprite, 0); + WaypointSprite_Ping(self.sprite); + //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor + + onslaught_updatelinks(); + } + + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + } + else + { + // particles on every hit + Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); + + //sound on every hit + if (random() < 0.5) + sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); + else + sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); + } + + self.SendFlags |= GSF_STATUS; +} + +void ons_GeneratorThink() +{SELFPARAM(); + entity e; + self.nextthink = time + GEN_THINKRATE; + if (!gameover) + { + if(!self.isshielded && self.wait < time) + { + self.wait = time + 5; + FOR_EACH_REALPLAYER(e) + { + if(SAME_TEAM(e, self)) + { + Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + soundto(MSG_ONE, e, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? + } + else + Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_)); + } + } + } +} + +void ons_GeneratorReset() +{SELFPARAM(); + self.team = self.team_saved; + self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; + self.takedamage = DAMAGE_AIM; + self.bot_attack = true; + self.iscaptured = true; + self.islinked = true; + self.isshielded = true; + self.event_damage = ons_GeneratorDamage; + self.think = ons_GeneratorThink; + self.nextthink = time + GEN_THINKRATE; + + Net_LinkEntity(self, false, 0, generator_send); + + self.SendFlags = GSF_SETUP; // just incase + self.SendFlags |= GSF_STATUS; + + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); +} + +void ons_DelayedGeneratorSetup() +{SELFPARAM(); + // bot waypoints + waypoint_spawnforitem_force(self, self.origin); + self.nearestwaypointtimeout = 0; // activate waypointing again + self.bot_basewaypoint = self.nearestwaypoint; + + // captureshield setup + ons_CaptureShield_Spawn(self, true); + + onslaught_updatelinks(); + + Net_LinkEntity(self, false, 0, generator_send); +} + + +void onslaught_generator_touch() +{SELFPARAM(); + if ( IS_PLAYER(other) ) + if ( SAME_TEAM(self,other) ) + if ( self.iscaptured ) + { + Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT); + } +} + +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc +{SELFPARAM(); + // declarations + int teamnumber = gen.team; + setself(gen); // for later usage with droptofloor() + + // main setup + gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist + ons_worldgeneratorlist = gen; + + gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); + gen.classname = "onslaught_generator"; + gen.solid = SOLID_BBOX; + gen.team_saved = teamnumber; + gen.movetype = MOVETYPE_NONE; + gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.takedamage = DAMAGE_AIM; + gen.bot_attack = true; + gen.event_damage = ons_GeneratorDamage; + gen.reset = ons_GeneratorReset; + gen.think = ons_GeneratorThink; + gen.nextthink = time + GEN_THINKRATE; + gen.iscaptured = true; + gen.islinked = true; + gen.isshielded = true; + gen.touch = onslaught_generator_touch; + + // appearence + // model handled by CSQC + setsize(gen, GENERATOR_MIN, GENERATOR_MAX); + setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); + gen.colormap = 1024 + (teamnumber - 1) * 17; + + // generator placement + setself(gen); + droptofloor(); + + // waypointsprites + WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE); + WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); + WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); + WaypointSprite_UpdateHealth(self.sprite, self.health); + + InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); +} + + +// =============== +// Round Handler +// =============== + +int total_generators; +void Onslaught_count_generators() +{ + entity e; + total_generators = redowned = blueowned = yellowowned = pinkowned = 0; + for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) + { + ++total_generators; + redowned += (e.team == NUM_TEAM_1 && e.health > 0); + blueowned += (e.team == NUM_TEAM_2 && e.health > 0); + yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); + pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); + } +} + +int Onslaught_GetWinnerTeam() +{ + int winner_team = 0; + if(redowned > 0) + winner_team = NUM_TEAM_1; + if(blueowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(yellowowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(pinkowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_4; + } + if(winner_team) + return winner_team; + return -1; // no generators left? +} + +void nades_Clear(entity e); + +#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) +#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) +bool Onslaught_CheckWinner() +{ + entity e; + + if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) + { + ons_stalemate = true; + + if (!wpforenemy_announced) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); + sound(world, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); + + wpforenemy_announced = true; + } + + entity tmp_entity; // temporary entity + float d; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) + { + // tmp_entity.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + d = 1; + for(e = ons_worldcplist; e; e = e.ons_worldcpnext) + { + if(DIFF_TEAM(e, tmp_entity)) + if(e.islinked) + d = d + 1; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * tmp_entity.max_health; + else + d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); + + tmp_entity.sprite.SendFlags |= 16; + + tmp_entity.ons_overtime_damagedelay = time + 1; + } + } + else { wpforenemy_announced = false; ons_stalemate = false; } + + Onslaught_count_generators(); + + if(ONS_OWNED_GENERATORS_OK()) + return 0; + + int winner_team = Onslaught_GetWinnerTeam(); + + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); + TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); + } + + ons_stalemate = false; + + play2all(SND(CTF_CAPTURE(winner_team))); + + round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); + + FOR_EACH_PLAYER(e) + { + e.ons_roundlost = true; + e.player_blocked = true; + + nades_Clear(e); + } + + return 1; +} + +bool Onslaught_CheckPlayers() +{ + return 1; +} + +void Onslaught_RoundStart() +{ + entity tmp_entity; + FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; } + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + tmp_entity.sprite.SendFlags |= 16; + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + tmp_entity.sprite.SendFlags |= 16; +} + + +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius) +{SELFPARAM(); + entity head; + float t, c; + int i; + bool needarmor = false, needweapons = false; + + // Needs armor/health? + if(self.health<100) + needarmor = true; + + // Needs weapons? + c = 0; + for(i = WEP_FIRST; i <= WEP_LAST ; ++i) + { + // Find weapon + if(self.weapons & WepSet_FromWeapon(i)) + if(++c>=4) + break; + } + + if(c<4) + needweapons = true; + + if(!needweapons && !needarmor) + return; + + ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n")); + ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n")); + + // See what is around + head = findchainfloat(bot_pickup, true); + while (head) + { + // gather health and armor only + if (head.solid) + if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) + if (vlen(head.origin - org) < sradius) + { + t = head.bot_pickupevalfunc(self, head); + if (t > 0) + navigation_routerating(head, t * ratingscale, 500); + } + head = head.chain; + } +} + +void havocbot_role_ons_setrole(entity bot, int role) +{ + ons_debug(strcat(bot.netname," switched to ")); + switch(role) + { + case HAVOCBOT_ONS_ROLE_DEFENSE: + ons_debug("defense"); + bot.havocbot_role = havocbot_role_ons_defense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_ASSISTANT: + ons_debug("assistant"); + bot.havocbot_role = havocbot_role_ons_assistant; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_OFFENSE: + ons_debug("offense"); + bot.havocbot_role = havocbot_role_ons_offense; + bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; + bot.havocbot_role_timeout = 0; + break; + } + ons_debug("\n"); +} + +int havocbot_ons_teamcount(entity bot, int role) +{SELFPARAM(); + int c = 0; + entity head; + + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + if(head.havocbot_role_flags & role) + ++c; + + return c; +} + +void havocbot_goalrating_ons_controlpoints_attack(float ratingscale) +{SELFPARAM(); + entity cp, cp1, cp2, best, pl, wp; + float radius, bestvalue; + int c; + bool found; + + // Filter control points + for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) + { + cp2.wpcost = c = 0; + cp2.wpconsidered = false; + + if(cp2.isshielded) + continue; + + // Ignore owned controlpoints + if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team])) + continue; + + // Count team mates interested in this control point + // (easier and cleaner than keeping counters per cp and teams) + FOR_EACH_PLAYER(pl) + if(SAME_TEAM(pl, self)) + if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(pl.havocbot_ons_target==cp2) + ++c; + + // NOTE: probably decrease the cost of attackable control points + cp2.wpcost = c; + cp2.wpconsidered = true; + } + + // We'll consider only the best case + bestvalue = 99999999999; + cp = world; + for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) + { + if (!cp1.wpconsidered) + continue; + + if(cp1.wpcost self.havocbot_role_timeout) + { + havocbot_ons_reset_role(self); + return; + } + + if(self.havocbot_attack_time>time) + return; + + if (self.bot_strategytime < time) + { + navigation_goalrating_start(); + havocbot_goalrating_enemyplayers(20000, self.origin, 650); + if(!havocbot_goalrating_ons_generator_attack(20000)) + havocbot_goalrating_ons_controlpoints_attack(20000); + havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000); + navigation_goalrating_end(); + + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + } +} + +void havocbot_role_ons_assistant() +{SELFPARAM(); + havocbot_ons_reset_role(self); +} + +void havocbot_role_ons_defense() +{SELFPARAM(); + havocbot_ons_reset_role(self); +} + +void havocbot_ons_reset_role(entity bot) +{SELFPARAM(); + entity head; + int c = 0; + + if(self.deadflag != DEAD_NO) + return; + + bot.havocbot_ons_target = world; + + // TODO: Defend control points or generator if necessary + + // if there is only me on the team switch to offense + c = 0; + FOR_EACH_PLAYER(head) + if(SAME_TEAM(head, self)) + ++c; + + if(c==1) + { + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); + return; + } + + havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); +} + + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity ons_Nearest_ControlPoint(vector pos, float max_dist) +{SELFPARAM(); + entity tmp_entity, closest_target = world; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist) + if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) + closest_target = tmp_entity; + tmp_entity = tmp_entity.chain; + } + + return closest_target; +} + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist) +{SELFPARAM(); + entity tmp_entity, closest_target = world; + vector delta; + float smallest_distance = 0, distance; + + tmp_entity = findchain(classname, "onslaught_controlpoint"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + delta = tmp_entity.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(tmp_entity, self)) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == world || distance <= smallest_distance ) + { + closest_target = tmp_entity; + smallest_distance = distance; + } + + tmp_entity = tmp_entity.chain; + } + + return closest_target; +} +/** + * find the number of control points and generators in the same team as self + */ +int ons_Count_SelfControlPoints() +{SELFPARAM(); + entity tmp_entity; + tmp_entity = findchain(classname, "onslaught_controlpoint"); + int n = 0; + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + if(tmp_entity.iscaptured) + n++; + tmp_entity = tmp_entity.chain; + } + tmp_entity = findchain(classname, "onslaught_generator"); + while(tmp_entity) + { + if(SAME_TEAM(tmp_entity, self)) + n++; + tmp_entity = tmp_entity.chain; + } + return n; +} + +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created + * return false on failure + */ +bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) +{ + if ( !tele_target ) + return false; + + int i; + vector loc; + float theta; + // narrow the range for each iteration to increase chances that a spawnpoint + // can be found even if there's little room around the control point + float iteration_scale = 1; + for(i = 0; i < 16; ++i) + { + iteration_scale -= i / 16; + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range * iteration_scale; + + loc += tele_target.origin + '0 0 128' * iteration_scale; + + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + if ( tele_effects ) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); + makevectors(player.angles); + player.fixangle = true; + player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; + + if ( tele_effects ) + Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); + return true; + } + } + } + + return false; +} + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ons, reset_map_global) +{SELFPARAM(); + entity e; + FOR_EACH_PLAYER(e) + { + e.ons_roundlost = false; + e.ons_deathloc = '0 0 0'; + WITH(entity, self, e, PutClientInServer()); + } + return false; +} + +MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) +{SELFPARAM(); + self.ons_deathloc = '0 0 0'; + return false; +} + +MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) +{SELFPARAM(); + self.ons_deathloc = '0 0 0'; + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) +{SELFPARAM(); + if(!round_handler_IsRoundStarted()) + { + self.player_blocked = true; + return false; + } + + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( self.ons_spawn_by ) + if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) + { + self.ons_spawn_by = world; + return false; + } + + if(autocvar_g_onslaught_spawn_at_controlpoints) + if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + { + if(SAME_TEAM(tmp_entity, self)) + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 96' * iteration_scale; + loc += ('0 1 0' * random()) * 128 * iteration_scale; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + if(autocvar_g_onslaught_spawn_at_generator) + if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_generator_random; + entity tmp_entity, closest_target = world; + vector spawn_loc = self.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else + { + if(SAME_TEAM(tmp_entity, self)) + if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) + closest_target = tmp_entity; + } + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 128' * iteration_scale; + loc += ('0 1 0' * random()) * 256 * iteration_scale; + tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(self, loc); + self.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerDies) +{SELFPARAM(); + frag_target.ons_deathloc = frag_target.origin; + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( ons_Count_SelfControlPoints() > 1 ) + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, MonsterMove) +{SELFPARAM(); + entity e = find(world, targetname, self.target); + if (e != world) + self.team = e.team; + + return false; +} + +void ons_MonsterSpawn_Delayed() +{SELFPARAM(); + entity e, own = self.owner; + + if(!own) { remove(self); return; } + + if(own.targetname) + { + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + + activator = e; + own.use(); + } + } + + remove(self); +} + +MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) +{SELFPARAM(); + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); + + return false; +} + +void ons_TurretSpawn_Delayed() +{SELFPARAM(); + entity e, own = self.owner; + + if(!own) { remove(self); return; } + + if(own.targetname) + { + e = find(world, target, own.targetname); + if(e != world) + { + own.team = e.team; + own.active = ACTIVE_NOT; + + activator = e; + own.use(); + } + } + + remove(self); +} + +MUTATOR_HOOKFUNCTION(ons, TurretSpawn) +{SELFPARAM(); + entity e = spawn(); + e.owner = self; + InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) +{SELFPARAM(); + havocbot_ons_reset_role(self); + return true; +} + +MUTATOR_HOOKFUNCTION(ons, GetTeamCount) +{ + // onslaught is special + for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + switch(tmp_entity.team) + { + case NUM_TEAM_1: c1 = 0; break; + case NUM_TEAM_2: c2 = 0; break; + case NUM_TEAM_3: c3 = 0; break; + case NUM_TEAM_4: c4 = 0; break; + } + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ons, SpectateCopy) +{SELFPARAM(); + self.ons_roundlost = other.ons_roundlost; // make spectators see it too + return false; +} + +MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + + if ( cmd_name == "ons_spawn" ) + { + vector pos = self.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(self) ) + { + if ( !self.frozen ) + { + entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); + + if ( !source_point && self.health > 0 ) + { + sprint(self, "\nYou need to be next to a control point\n"); + return 1; + } + + + entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius); + + if ( closest_target == world ) + { + sprint(self, "\nNo control point found\n"); + return 1; + } + + if ( self.health <= 0 ) + { + self.ons_spawn_by = closest_target; + self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; + } + else + { + if ( source_point == closest_target ) + { + sprint(self, "\nTeleporting to the same point\n"); + return 1; + } + + if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) ) + sprint(self, "\nUnable to teleport there\n"); + } + + return 1; + } + + sprint(self, "\nNo teleportation for you\n"); + } + + return 1; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle) + { + entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); + if ( source_point ) + { + stuffcmd(self, "qc_cmd_cl hud clickradar\n"); + return true; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayHitsound) +{ + return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) + || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); +} + +MUTATOR_HOOKFUNCTION(ons, SendWaypoint) +{ + if(wp_sendflags & 16) + { + if(self.owner.classname == "onslaught_controlpoint") + { + entity wp_owner = self.owner; + entity e = WaypointSprite_getviewentity(wp_sendto); + if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } + if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } + } + if(self.owner.classname == "onslaught_generator") + { + entity wp_owner = self.owner; + if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } + if(wp_owner.health <= 0) { wp_flag |= 2; } + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) +{ + if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + { + ret_float = -3; + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, TurretThink) +{ + // ONS uses somewhat backwards linking. + if(self.target) + { + entity e = find(world, targetname, self.target); + if (e != world) + self.team = e.team; + } + + if(self.team != self.tur_head.team) + turret_respawn(); + + return false; +} + + +// ========== +// Spawnfuncs +// ========== + +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) + Link between control points. + + This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. + +keys: +"target" - first control point. +"target2" - second control point. + */ +spawnfunc(onslaught_link) +{ + if(!g_onslaught) { remove(self); return; } + + if (self.target == "" || self.target2 == "") + objerror("target and target2 must be set\n"); + + self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist + ons_worldlinklist = self; + + InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); + Net_LinkEntity(self, false, 0, ons_Link_Send); +} + +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) + Control point. Be sure to give this enough clearance so that the shootable part has room to exist + + This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + +keys: +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) + */ + +spawnfunc(onslaught_controlpoint) +{ + if(!g_onslaught) { remove(self); return; } + + ons_ControlPoint_Setup(self); +} + +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) + Base generator. + + spawnfunc_onslaught_link entities can target this. + +keys: +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. + */ +spawnfunc(onslaught_generator) +{ + if(!g_onslaught) { remove(self); return; } + if(!self.team) { objerror("team must be set"); } + + ons_GeneratorSetup(self); +} + +// scoreboard setup +void ons_ScoreRules() +{ + CheckAllowedTeams(world); + ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); + ScoreRules_basics_end(); +} + +void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up +{ + ons_ScoreRules(); + + round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); + round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); +} + +void ons_Initialize() +{ + g_onslaught = true; + ons_captureshield_force = autocvar_g_onslaught_shield_force; + + addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost); + + InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE); +} + +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc new file mode 100644 index 0000000000..4924287189 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc @@ -0,0 +1,40 @@ +#include "sv_controlpoint.qh" + +.bool iscaptured; + +bool cpicon_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON); + WriteByte(MSG_ENTITY, sf); + if(sf & CPSF_SETUP) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteByte(MSG_ENTITY, self.health); + WriteByte(MSG_ENTITY, self.max_health); + WriteByte(MSG_ENTITY, self.count); + WriteByte(MSG_ENTITY, self.team); + WriteByte(MSG_ENTITY, self.owner.iscaptured); + } + + if(sf & CPSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + + return true; +} + +void onslaught_controlpoint_icon_link(entity e, void() spawnproc) +{ + Net_LinkEntity(e, true, 0, cpicon_send); + e.think = spawnproc; + e.nextthink = time * sys_frametime; +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh new file mode 100644 index 0000000000..d76f0ea069 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh @@ -0,0 +1,10 @@ +#ifndef CONTROLPOINT_H +#define CONTROLPOINT_H + +const vector CPICON_MIN = '-32 -32 -9'; +const vector CPICON_MAX = '32 32 25'; + +const int CPSF_STATUS = 4; +const int CPSF_SETUP = 8; + +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc new file mode 100644 index 0000000000..cf8d234a3a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc @@ -0,0 +1,37 @@ +#include "sv_generator.qh" + +bool generator_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_GENERATOR); + WriteByte(MSG_ENTITY, sf); + if(sf & GSF_SETUP) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteByte(MSG_ENTITY, self.health); + WriteByte(MSG_ENTITY, self.max_health); + WriteByte(MSG_ENTITY, self.count); + WriteByte(MSG_ENTITY, self.team); + } + + if(sf & GSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + + return true; +} + +void generator_link(void() spawnproc) +{SELFPARAM(); + Net_LinkEntity(self, true, 0, generator_send); + self.think = spawnproc; + self.nextthink = time; +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh new file mode 100644 index 0000000000..003c2b1d68 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh @@ -0,0 +1,10 @@ +#ifndef GENERATOR_H +#define GENERATOR_H +const vector GENERATOR_MIN = '-52 -52 -14'; +const vector GENERATOR_MAX = '52 52 75'; + +const int GSF_STATUS = 4; +const int GSF_SETUP = 8; + +bool generator_send(entity this, entity to, int sf); +#endif diff --git a/qcsrc/common/items/all.qh b/qcsrc/common/items/all.qh index a825b4ed16..ab86ea6736 100644 --- a/qcsrc/common/items/all.qh +++ b/qcsrc/common/items/all.qh @@ -5,12 +5,14 @@ #include "item.qh" -REGISTRY(Items, BIT(5)) -REGISTER_REGISTRY(RegisterItems) +REGISTRY(Items, BITS(5)) +#define Items_from(i) _Items_from(i, NULL) +REGISTER_REGISTRY(Items) /** If you register a new item, make sure to add it to all.inc */ -#define REGISTER_ITEM(id, class) REGISTER(RegisterItems, ITEM, Items, id, m_id, NEW(class)) +#define REGISTER_ITEM(id, class) REGISTER(Items, ITEM, id, m_id, NEW(class)) -REGISTRY_SORT(Items, m_name, 0) +REGISTRY_SORT(Items, 0) +REGISTRY_CHECK(Items) STATIC_INIT(Items) { FOREACH(Items, true, LAMBDA(it.m_id = i)); } void Dump_Items(); @@ -34,5 +36,3 @@ string Item_Model(string item_mdl); #endif #endif - -#include "inventory.qh" diff --git a/qcsrc/common/items/inventory.qh b/qcsrc/common/items/inventory.qh index a6d266ae5f..f748dda884 100644 --- a/qcsrc/common/items/inventory.qh +++ b/qcsrc/common/items/inventory.qh @@ -11,16 +11,20 @@ class(Inventory) .int inv_items[Items_MAX]; /** Player inventory; Inventories also have one inventory for storing the previous state */ .Inventory inventory; +REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY) + #ifdef CSQC -void Inventory_Read(Inventory data) +NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew) { + make_pure(this); const int bits = ReadInt24_t(); FOREACH(Items, bits & BIT(it.m_id), LAMBDA( .int fld = inv_items[it.m_id]; - int prev = data.(fld); - int next = data.(fld) = ReadByte(); + int prev = this.(fld); + int next = this.(fld) = ReadByte(); LOG_TRACEF("%s: %.0f -> %.0f\n", it.m_name, prev, next); )); + return true; } #endif @@ -42,7 +46,7 @@ void Inventory_Write(Inventory data) #ifdef SVQC bool Inventory_Send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_INVENTORY); + WriteHeader(MSG_ENTITY, ENT_CLIENT_INVENTORY); entity e = self.owner; if (IS_SPEC(e)) e = e.enemy; Inventory data = e.inventory; @@ -53,7 +57,7 @@ bool Inventory_Send(entity this, entity to, int sf) void Inventory_new(entity e) { Inventory inv = new(Inventory), bak = new(Inventory); - inv.classname = "inventory", bak.classname = "inventory"; + make_pure(inv); make_pure(bak); inv.inventory = bak; inv.drawonlytoclient = e; Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send); diff --git a/qcsrc/common/items/item/armor.qc b/qcsrc/common/items/item/armor.qc index 13a774ebce..5bd550838a 100644 --- a/qcsrc/common/items/item/armor.qc +++ b/qcsrc/common/items/item/armor.qc @@ -5,13 +5,14 @@ #ifndef MENUQC MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3")); +SOUND(ArmorSmall, "misc/armor1"); #endif REGISTER_ITEM(ArmorSmall, Armor) { #ifndef MENUQC this.m_model = MDL_ArmorSmall_ITEM; + this.m_sound = SND_ArmorSmall; #endif - this.m_sound = "misc/armor1.wav"; this.m_name = "5 Armor"; this.m_icon = "armor"; #ifdef SVQC @@ -24,13 +25,14 @@ REGISTER_ITEM(ArmorSmall, Armor) { #ifndef MENUQC MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3")); +SOUND(ArmorMedium, "misc/armor10"); #endif REGISTER_ITEM(ArmorMedium, Armor) { #ifndef MENUQC this.m_model = MDL_ArmorMedium_ITEM; + this.m_sound = SND_ArmorMedium; #endif - this.m_sound = "misc/armor10.wav"; this.m_name = "25 Armor"; this.m_icon = "armor"; #ifdef SVQC @@ -43,13 +45,14 @@ REGISTER_ITEM(ArmorMedium, Armor) { #ifndef MENUQC MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3")); +SOUND(ArmorLarge, "misc/armor17_5"); #endif REGISTER_ITEM(ArmorLarge, Armor) { #ifndef MENUQC this.m_model = MDL_ArmorLarge_ITEM; + this.m_sound = SND_ArmorLarge; #endif - this.m_sound = "misc/armor17_5.wav"; this.m_name = "50 Armor"; this.m_icon = "armor"; this.m_color = '0 1 0'; @@ -64,13 +67,14 @@ REGISTER_ITEM(ArmorLarge, Armor) { #ifndef MENUQC MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3")); +SOUND(ArmorMega, "misc/armor25"); #endif REGISTER_ITEM(ArmorMega, Armor) { #ifndef MENUQC this.m_model = MDL_ArmorMega_ITEM; + this.m_sound = SND_ArmorMega; #endif - this.m_sound = "misc/armor25.wav"; this.m_name = "100 Armor"; this.m_icon = "item_large_armor"; this.m_color = '0 1 0'; diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index 48a80f886a..adb93e7c94 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -3,6 +3,8 @@ #include "pickup.qh" CLASS(Armor, Pickup) #ifdef SVQC + ATTRIB(Armor, m_mins, vector, '-16 -16 0') + ATTRIB(Armor, m_maxs, vector, '16 16 48') ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc) #endif ENDCLASS(Armor) diff --git a/qcsrc/common/items/item/health.qc b/qcsrc/common/items/item/health.qc index b81bde7afc..6a2b5b1389 100644 --- a/qcsrc/common/items/item/health.qc +++ b/qcsrc/common/items/item/health.qc @@ -5,13 +5,14 @@ #ifndef MENUQC MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3")); +SOUND(HealthSmall, "misc/minihealth"); #endif REGISTER_ITEM(HealthSmall, Health) { #ifndef MENUQC this.m_model = MDL_HealthSmall_ITEM; + this.m_sound = SND_HealthSmall; #endif - this.m_sound = "misc/minihealth.wav"; this.m_name = "5 Health"; this.m_icon = "health"; #ifdef SVQC @@ -24,13 +25,14 @@ REGISTER_ITEM(HealthSmall, Health) { #ifndef MENUQC MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3")); +SOUND(HealthMedium, "misc/mediumhealth"); #endif REGISTER_ITEM(HealthMedium, Health) { #ifndef MENUQC this.m_model = MDL_HealthMedium_ITEM; + this.m_sound = SND_HealthMedium; #endif - this.m_sound = "misc/mediumhealth.wav"; this.m_name = "25 Health"; this.m_icon = "health"; #ifdef SVQC @@ -43,13 +45,14 @@ REGISTER_ITEM(HealthMedium, Health) { #ifndef MENUQC MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3")); +SOUND(HealthLarge, "misc/mediumhealth"); #endif REGISTER_ITEM(HealthLarge, Health) { #ifndef MENUQC this.m_model = MDL_HealthLarge_ITEM; + this.m_sound = SND_HealthLarge; #endif - this.m_sound = "misc/mediumhealth.wav"; this.m_name = "50 Health"; this.m_icon = "health"; this.m_color = '1 0 0'; @@ -64,13 +67,14 @@ REGISTER_ITEM(HealthLarge, Health) { #ifndef MENUQC MODEL(HealthMega_ITEM, Item_Model("g_h100.md3")); +SOUND(HealthMega, "misc/megahealth"); #endif REGISTER_ITEM(HealthMega, Health) { #ifndef MENUQC this.m_model = MDL_HealthMega_ITEM; + this.m_sound = SND_HealthMega; #endif - this.m_sound = "misc/megahealth.wav"; this.m_name = "100 Health"; this.m_icon = "item_mega_health"; this.m_color = '1 0 0'; diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index 15f1f8dd91..f7bfb5c938 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -3,6 +3,8 @@ #include "pickup.qh" CLASS(Health, Pickup) #ifdef SVQC + ATTRIB(Health, m_mins, vector, '-16 -16 0') + ATTRIB(Health, m_maxs, vector, '16 16 48') ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc) #endif ENDCLASS(Health) diff --git a/qcsrc/common/items/item/pickup.qc b/qcsrc/common/items/item/pickup.qc index ade99c543a..fc958709e8 100644 --- a/qcsrc/common/items/item/pickup.qc +++ b/qcsrc/common/items/item/pickup.qc @@ -2,12 +2,6 @@ #ifdef SVQC bool ITEM_HANDLE(Pickup, entity this, entity item, entity player) { - bool b = this.giveTo(this, item, player); - if (b) { - LOG_TRACEF("entity %i picked up %s\n", player, this.m_name); - player.inventory.inv_items[this.m_id]++; - Inventory_update(player); - } - return b; + return this.giveTo(this, item, player); } #endif diff --git a/qcsrc/common/items/item/pickup.qh b/qcsrc/common/items/item/pickup.qh index 6f60337c19..1996327887 100644 --- a/qcsrc/common/items/item/pickup.qh +++ b/qcsrc/common/items/item/pickup.qh @@ -1,46 +1,38 @@ #ifndef PICKUP_H #define PICKUP_H +#include "../inventory.qh" #include "../item.qh" CLASS(Pickup, GameItem) #ifndef MENUQC ATTRIB(Pickup, m_model, Model, NULL) + ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP) #endif - ATTRIB(Pickup, m_sound, string, "misc/itempickup.wav") ATTRIB(Pickup, m_name, string, string_null) METHOD(Pickup, show, void(entity this)); void Pickup_show(entity this) { LOG_INFOF("%s: %s\n", etos(this), this.m_name); } #ifdef SVQC + ATTRIB(Pickup, m_mins, vector, '-16 -16 0') + ATTRIB(Pickup, m_maxs, vector, '16 16 32') ATTRIB(Pickup, m_botvalue, int, 0) ATTRIB(Pickup, m_itemflags, int, 0) ATTRIB(Pickup, m_itemid, int, 0) + float generic_pickupevalfunc(entity player, entity item); ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc) ATTRIB(Pickup, m_respawntime, float(), func_null) ATTRIB(Pickup, m_respawntimejitter, float(), func_null) - METHOD(Pickup, giveTo, bool(entity this, entity item, entity player)); - bool Pickup_giveTo(entity this, entity item, entity player) { return Item_GiveTo(item, player); } + float Item_GiveTo(entity item, entity player); + METHOD(Pickup, giveTo, bool(entity this, entity item, entity player)) + { + bool b = Item_GiveTo(item, player); + if (b) { + LOG_TRACEF("entity %i picked up %s\n", player, this.m_name); + player.inventory.inv_items[this.m_id]++; + Inventory_update(player); + } + return b; + } bool ITEM_HANDLE(Pickup, entity this, entity item, entity player); #endif ENDCLASS(Pickup) -#ifdef SVQC -// For g_pickup_respawntime -#include "../../../server/defs.qh" -// Getters to dynamically retrieve the values of g_pickup_respawntime* -GETTER(float, g_pickup_respawntime_weapon) -GETTER(float, g_pickup_respawntime_superweapon) -GETTER(float, g_pickup_respawntime_ammo) -GETTER(float, g_pickup_respawntime_short) -GETTER(float, g_pickup_respawntime_medium) -GETTER(float, g_pickup_respawntime_long) -GETTER(float, g_pickup_respawntime_powerup) -GETTER(float, g_pickup_respawntimejitter_weapon) -GETTER(float, g_pickup_respawntimejitter_superweapon) -GETTER(float, g_pickup_respawntimejitter_ammo) -GETTER(float, g_pickup_respawntimejitter_short) -GETTER(float, g_pickup_respawntimejitter_medium) -GETTER(float, g_pickup_respawntimejitter_long) -GETTER(float, g_pickup_respawntimejitter_powerup) - -#endif - #endif diff --git a/qcsrc/common/items/item/powerup.qc b/qcsrc/common/items/item/powerup.qc index a1b2f14037..7deba67df5 100644 --- a/qcsrc/common/items/item/powerup.qc +++ b/qcsrc/common/items/item/powerup.qc @@ -6,13 +6,14 @@ #ifndef MENUQC MODEL(Strength_ITEM, Item_Model("g_strength.md3")); +SOUND(Strength, "misc/powerup"); #endif REGISTER_ITEM(Strength, Powerup) { #ifndef MENUQC this.m_model = MDL_Strength_ITEM; + this.m_sound = SND_Strength; #endif - this.m_sound = "misc/powerup.wav"; this.m_name = "Strength Powerup"; this.m_icon = "strength"; this.m_color = '0 0 1'; @@ -23,13 +24,14 @@ REGISTER_ITEM(Strength, Powerup) { #ifndef MENUQC MODEL(Shield_ITEM, Item_Model("g_invincible.md3")); +SOUND(Shield, "misc/powerup_shield"); #endif REGISTER_ITEM(Shield, Powerup) { #ifndef MENUQC this.m_model = MDL_Shield_ITEM; + this.m_sound = SND_Shield; #endif - this.m_sound = "misc/powerup_shield.wav"; this.m_name = "Shield"; this.m_icon = "shield"; this.m_color = '1 0 1'; diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh index d78bd5563e..f443ab0af2 100644 --- a/qcsrc/common/items/item/powerup.qh +++ b/qcsrc/common/items/item/powerup.qh @@ -8,6 +8,8 @@ #include "pickup.qh" CLASS(Powerup, Pickup) #ifdef SVQC + ATTRIB(Powerup, m_mins, vector, '-16 -16 0') + ATTRIB(Powerup, m_maxs, vector, '16 16 48') ATTRIB(Powerup, m_botvalue, int, 100000) ATTRIB(Powerup, m_itemflags, int, FL_POWERUP) ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup)) diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 4f856670b8..5ca1c3803d 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -1,13 +1,11 @@ #if defined(CSQC) #include "../client/defs.qh" #include "util.qh" - #include "buffs/all.qh" #include "weapons/all.qh" #include "mapinfo.qh" #elif defined(MENUQC) #elif defined(SVQC) #include "util.qh" - #include "buffs/all.qh" #include "monsters/all.qh" #include "mapinfo.qh" #endif @@ -1019,6 +1017,11 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p { MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST; } + else if(t == "gameversion_min") + { + if (cvar("gameversion") < stof(s)) + MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST; + } else if(t == "type") { t = car(s); s = cdr(s); diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 4107290fc0..702798bbf8 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -42,13 +42,15 @@ CLASS(Gametype, Object) } ENDCLASS(Gametype) -REGISTRY(Gametypes, BIT(4)) -REGISTER_REGISTRY(RegisterGametypes) +REGISTRY(Gametypes, BITS(4)) +#define Gametypes_from(i) _Gametypes_from(i, NULL) +REGISTER_REGISTRY(Gametypes) +REGISTRY_CHECK(Gametypes) int MAPINFO_TYPE_ALL; #define REGISTER_GAMETYPE(hname, sname, g_name, NAME, gteamplay, mutators, defaults, gdescription) \ int MAPINFO_TYPE_##NAME; \ bool NAME##_mapinfo(string k, string v) { return = false; } \ - REGISTER(RegisterGametypes, MAPINFO_TYPE, Gametypes, g_name, m_id, \ + REGISTER(Gametypes, MAPINFO_TYPE, g_name, m_id, \ NEW(Gametype, hname, #sname, #g_name, gteamplay, #sname " " mutators, defaults, gdescription) \ ) { \ /* same as `1 << m_id` */ \ @@ -79,7 +81,7 @@ REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,false,"","timelimit=20 qualifying_tim } #define g_race IS_GAMETYPE(RACE) -REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"","timelimit=20",_("Race for fastest time.")); +REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"cloaked","timelimit=20",_("Race for fastest time.")); #define g_cts IS_GAMETYPE(CTS) REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")) diff --git a/qcsrc/common/minigames/cl_minigames.qc b/qcsrc/common/minigames/cl_minigames.qc index 38614c7d46..3ea50caad3 100644 --- a/qcsrc/common/minigames/cl_minigames.qc +++ b/qcsrc/common/minigames/cl_minigames.qc @@ -153,8 +153,6 @@ void minigame_player_entremove() deactivate_minigame(); } -vector ReadVector2D() { vector v; v_x = ReadCoord(); v_y = ReadCoord(); v_z = 0; return v; } -vector ReadVector() { vector v; v_x = ReadCoord(); v_y = ReadCoord(); v_z = ReadCoord(); return v; } string() ReadString_Raw = #366; string ReadString_Zoned() { return strzone(ReadString_Raw()); } #define ReadString ReadString_Zoned @@ -178,8 +176,8 @@ void minigame_read_owner() if ( !self.owner ) LOG_TRACE("Got a minigame entity without a minigame!\n"); } -void ent_read_minigame() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) +{ float sf = ReadByte(); if ( sf & MINIG_SF_CREATE ) { @@ -241,6 +239,7 @@ void ent_read_minigame() " classname:",self.classname," enttype:",ftos(self.enttype) ); LOG_DEBUG(" sf:",ftos(sf)," netname:",self.netname,"\n\n"); } + return true; } #undef ReadString #undef FIELD diff --git a/qcsrc/common/minigames/cl_minigames.qh b/qcsrc/common/minigames/cl_minigames.qh index 016e8a3edf..f21c6e482d 100644 --- a/qcsrc/common/minigames/cl_minigames.qh +++ b/qcsrc/common/minigames/cl_minigames.qh @@ -95,9 +95,6 @@ float minigame_isactive() #define minigame_cmd(...) minigame_cmd_workaround(0,__VA_ARGS__) void minigame_cmd_workaround(float dummy, string...cmdargc); -// Read a minigame entity from the server -void ent_read_minigame(); - // Prompt the player to play in the current minigame // (ie: it's their turn and they should get back to the minigame) void minigame_prompt(); @@ -114,15 +111,17 @@ void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_ar while( (entityvar = findentity(entityvar,owner,active_minigame)) ) -REGISTRY(Minigames, BIT(3)) -REGISTER_REGISTRY(RegisterMinigames) +REGISTRY(Minigames, BITS(3)) +#define Minigames_from(i) _Minigames_from(i, NULL) +REGISTER_REGISTRY(Minigames) +REGISTRY_CHECK(Minigames) #define REGISTER_MINIGAME(name,nicename) \ - REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, spawn()); \ + REGISTER(Minigames, MINIGAME, name, m_id, new(minigame_descriptor)); \ void name##_hud_board(vector, vector); \ void name##_hud_status(vector, vector); \ int name##_client_event(entity, string, ...); \ REGISTER_INIT_POST(MINIGAME, name) { \ - this.classname = "minigame_descriptor"; \ + make_pure(this); \ this.netname = strzone(strtolower(#name)); \ this.message = nicename; \ this.minigame_hud_board = name##_hud_board; \ diff --git a/qcsrc/common/minigames/minigame/nmm.qc b/qcsrc/common/minigames/minigame/nmm.qc index 1e4da05075..93c88f6570 100644 --- a/qcsrc/common/minigames/minigame/nmm.qc +++ b/qcsrc/common/minigames/minigame/nmm.qc @@ -79,9 +79,8 @@ string nmm_tile_build_vmill(entity tile) void nmm_spawn_tile(string id, entity minig, int distance) { // TODO global variable + list_next for simpler tile loops - entity e = spawn(); + entity e = new(minigame_nmm_tile); e.origin = minigame_tile_pos(id,7,7); - e.classname = "minigame_nmm_tile"; e.netname = id; e.owner = minig; e.team = 0; diff --git a/qcsrc/common/minigames/minigame/ttt.qc b/qcsrc/common/minigames/minigame/ttt.qc index 8c5005ed25..c3c76376c1 100644 --- a/qcsrc/common/minigames/minigame/ttt.qc +++ b/qcsrc/common/minigames/minigame/ttt.qc @@ -642,8 +642,7 @@ int ttt_client_event(entity minigame, string event, ...) if ( spawnai ) { - entity aiplayer = spawn(); - aiplayer.classname = "minigame_player"; + entity aiplayer = new(minigame_player); aiplayer.owner = minigame; aiplayer.team = ai; aiplayer.minigame_playerslot = 0; diff --git a/qcsrc/common/minigames/minigames.qc b/qcsrc/common/minigames/minigames.qc index bc854952e7..8a29bc297f 100644 --- a/qcsrc/common/minigames/minigames.qc +++ b/qcsrc/common/minigames/minigames.qc @@ -1,5 +1,7 @@ #include "minigames.qh" +REGISTER_NET_LINKED(ENT_CLIENT_MINIGAME) + entity minigame_get_descriptor(string id) { FOREACH(Minigames, true, LAMBDA( diff --git a/qcsrc/common/minigames/sv_minigames.qc b/qcsrc/common/minigames/sv_minigames.qc index 56d0ece1a2..f69fc15b54 100644 --- a/qcsrc/common/minigames/sv_minigames.qc +++ b/qcsrc/common/minigames/sv_minigames.qc @@ -51,8 +51,6 @@ void minigame_rmplayer(entity minigame_session, entity player) #define FIELD(Flags, Type,Name) if ( sf & (Flags) ) Write##Type(MSG_ENTITY, self.Name); -#define WriteVector(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y); WriteCoord(to,Name##_z) -#define WriteVector2D(to,Name) WriteCoord(to,Name##_x); WriteCoord(to,Name##_y) #define MSLE(Name,Fields) \ else if ( self.classname == #Name ) { \ if ( sf & MINIG_SF_CREATE ) WriteString(MSG_ENTITY,self.owner.netname); \ @@ -62,7 +60,7 @@ void minigame_rmplayer(entity minigame_session, entity player) // only use on minigame entities or entities with a minigame owner bool minigame_SendEntity(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_MINIGAME); + WriteHeader(MSG_ENTITY, ENT_CLIENT_MINIGAME); WriteByte(MSG_ENTITY, sf); if ( sf & MINIG_SF_CREATE ) @@ -131,12 +129,11 @@ int minigame_addplayer(entity minigame_session, entity player) return 0; minigame_rmplayer(player.active_minigame,player); } - entity player_pointer = spawn(); + entity player_pointer = new(minigame_player); int mgteam = minigame_session.minigame_event(minigame_session,"join",player,player_pointer); if ( mgteam ) { - player_pointer.classname = "minigame_player"; player_pointer.owner = minigame_session; player_pointer.minigame_players = player; player_pointer.team = mgteam; @@ -171,8 +168,7 @@ entity start_minigame(entity player, string minigame ) entity e = minigame_get_descriptor(minigame); if ( e ) { - entity minig = spawn(); - minig.classname = "minigame"; + entity minig = new(minigame); minig.netname = strzone(strcat(e.netname,"_",ftos(num_for_edict(minig)))); minig.descriptor = e; minig.minigame_event = e.minigame_event; diff --git a/qcsrc/common/minigames/sv_minigames.qh b/qcsrc/common/minigames/sv_minigames.qh index e36d690d9b..eb6825ed60 100644 --- a/qcsrc/common/minigames/sv_minigames.qh +++ b/qcsrc/common/minigames/sv_minigames.qh @@ -46,13 +46,15 @@ entity minigame_sessions; bool minigame_SendEntity(entity this, entity to, int sf); -REGISTRY(Minigames, BIT(3)) -REGISTER_REGISTRY(RegisterMinigames) +REGISTRY(Minigames, BITS(3)) +#define Minigames_from(i) _Minigames_from(i, NULL) +REGISTER_REGISTRY(Minigames) +REGISTRY_CHECK(Minigames) #define REGISTER_MINIGAME(name,nicename) \ - REGISTER(RegisterMinigames, MINIGAME, Minigames, name, m_id, spawn()); \ + REGISTER(Minigames, MINIGAME, name, m_id, new(minigame_descriptor)); \ int name##_server_event(entity, string, ...); \ REGISTER_INIT_POST(MINIGAME, name) { \ - this.classname = "minigame_descriptor"; \ + make_pure(this); \ this.netname = strzone(strtolower(#name)); \ this.message = nicename; \ this.minigame_event = name##_server_event; \ diff --git a/qcsrc/common/models/all.inc b/qcsrc/common/models/all.inc index f675d8a9e1..354df9a41c 100644 --- a/qcsrc/common/models/all.inc +++ b/qcsrc/common/models/all.inc @@ -5,6 +5,7 @@ string W_Model(string w_mdl); MODEL(CTF_SHIELD, "models/ctf/shield.md3"); MODEL(CTF_CAPTURE, "models/ctf/shockwavetransring.md3"); +MODEL(CTF_FLAG, "models/ctf/flags.md3"); MODEL(DOM_NEUTRAL, "models/domination/dom_unclaimed.md3"); MODEL(DOM_RED, "models/domination/dom_red.md3"); @@ -144,7 +145,7 @@ MODEL(GIB_ROBO_7, "models/gibs/robo7.md3"); MODEL(GIB_ROBO_8, "models/gibs/robo8.md3"); Model MDL_GIB_ROBO_RANDOM() { int i = floor(random() * 8); - return Models[MDL_GIB_ROBO_1.m_id + i]; + return Models_from(MDL_GIB_ROBO_1.m_id + i); } MODEL(CASING_SHELL, "models/casing_shell.mdl"); @@ -346,7 +347,7 @@ MODEL(9, "models/sprites/9.spr32"); MODEL(10, "models/sprites/10.spr32"); Model MDL_NUM(int i) { if ((i >= 0 && i <= 10)) - return Models[MDL_0.m_id + i]; + return Models_from(MDL_0.m_id + i); return MDL_Null; } diff --git a/qcsrc/common/models/all.qh b/qcsrc/common/models/all.qh index a7ab908807..ac95c2c840 100644 --- a/qcsrc/common/models/all.qh +++ b/qcsrc/common/models/all.qh @@ -3,14 +3,15 @@ #include "model.qh" -REGISTRY(Models, BIT(9)) -REGISTER_REGISTRY(RegisterModels) +REGISTRY(Models, BITS(9)) +#define Models_from(i) _Models_from(i, MDL_Null) +REGISTER_REGISTRY(Models) #define MODEL(name, path) \ string MDL_##name##_get() { return path; } \ - REGISTER(RegisterModels, MDL, Models, name, m_id, NEW(Model, MDL_##name##_get)) + REGISTER(Models, MDL, name, m_id, NEW(Model, MDL_##name##_get)) -STATIC_INIT(RegisterModels_precache) { +PRECACHE(Models) { FOREACH(Models, true, LAMBDA({ it.model_precache(it); })); diff --git a/qcsrc/common/monsters/all.qc b/qcsrc/common/monsters/all.qc index f4b2a4bf0b..f5c973bd07 100644 --- a/qcsrc/common/monsters/all.qc +++ b/qcsrc/common/monsters/all.qc @@ -1,6 +1,17 @@ #ifndef MONSTERS_ALL_C #define MONSTERS_ALL_C +string M_Model(string m_mdl) +{ + string output = strcat("models/monsters/", m_mdl); +#ifdef SVQC + MUTATOR_CALLHOOK(MonsterModel, m_mdl, output); + return monster_model_output; +#else + return output; +#endif +} + #include "all.qh" #define IMPLEMENTATION diff --git a/qcsrc/common/monsters/all.qh b/qcsrc/common/monsters/all.qh index 05eb4346db..b771984f21 100644 --- a/qcsrc/common/monsters/all.qh +++ b/qcsrc/common/monsters/all.qh @@ -3,23 +3,20 @@ #include "monster.qh" -REGISTRY(Monsters, BIT(4)) -REGISTER_REGISTRY(RegisterMonsters) +string M_Model(string m_mdl); + +REGISTRY(Monsters, BITS(5)) +#define Monsters_from(i) _Monsters_from(i, MON_Null) +#define get_monsterinfo(i) Monsters_from(i) +REGISTER_REGISTRY(Monsters) +REGISTRY_CHECK(Monsters) const int MON_FIRST = 1; #define MON_LAST (Monsters_COUNT - 1) /** If you register a new monster, make sure to add it to all.inc */ -#define REGISTER_MONSTER(id, inst) REGISTER(RegisterMonsters, MON, Monsters, id, monsterid, inst) +#define REGISTER_MONSTER(id, inst) REGISTER(Monsters, MON, id, monsterid, inst) REGISTER_MONSTER(Null, NEW(Monster)); -Monster get_monsterinfo(int id) -{ - if (id >= MON_FIRST && id <= MON_LAST) { - Monster m = Monsters[id]; - if (m) return m; - } - return MON_Null; -} #include "all.inc" diff --git a/qcsrc/common/monsters/monster.qh b/qcsrc/common/monsters/monster.qh index b4559269d7..bfbd544e53 100644 --- a/qcsrc/common/monsters/monster.qh +++ b/qcsrc/common/monsters/monster.qh @@ -22,6 +22,7 @@ const int MON_FLAG_RANGED = 512; // monster shoots projectiles const int MON_FLAG_MELEE = 1024; const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes +const int MONSTER_SIZE_QUAKE = 8192; // entity properties of monsterinfo: .bool(int, entity targ) monster_attackfunc; diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 7388ee66f5..38b34c425e 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -2,7 +2,7 @@ #define MAGE_H #ifndef MENUQC -MODEL(MON_MAGE, "models/monsters/mage.dpm"); +MODEL(MON_MAGE, M_Model("mage.dpm")); #endif CLASS(Mage, Monster) @@ -29,7 +29,7 @@ CLASS(MageSpike, PortoLaunch) /* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(MageSpike, impulse, int, 9); /* refname */ ATTRIB(MageSpike, netname, string, "magespike"); -/* wepname */ ATTRIB(MageSpike, message, string, _("Mage spike")); +/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike")); ENDCLASS(MageSpike) REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); @@ -39,22 +39,23 @@ REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); #ifdef SVQC +SOUND(MageSpike_FIRE, W_Sound("electro_fire")); void M_Mage_Attack_Spike(vector dir); void M_Mage_Attack_Push(); -METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, bool fire1, bool fire2)) { - if (fire1) - if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, false, 0.2)) { +METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, .entity weaponentity, int fire)) { + if (fire & 1) + if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range; actor.enemy = Monster_FindTarget(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(MageSpike_FIRE), CH_WEAPON_B, 0); if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin); M_Mage_Attack_Spike(w_shotdir); - weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } - if (fire2) - if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, true, 0.5)) { + if (fire & 2) + if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, true, 0.5)) { M_Mage_Attack_Push(); - weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready); } } @@ -297,7 +298,7 @@ void M_Mage_Defend_Heal() if(washealed) { setanim(self, self.anim_shoot, true, true, true); - self.attack_finished_single = time + (autocvar_g_monster_mage_heal_delay); + self.attack_finished_single[0] = time + (autocvar_g_monster_mage_heal_delay); self.anim_finished = time + 1.5; } } @@ -309,7 +310,7 @@ void M_Mage_Attack_Push() Send_Effect(EFFECT_TE_EXPLOSION, self.origin, '0 0 0', 1); setanim(self, self.anim_shoot, true, true, true); - self.attack_finished_single = time + (autocvar_g_monster_mage_attack_push_delay); + self.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_push_delay); } void M_Mage_Attack_Teleport() @@ -334,7 +335,7 @@ void M_Mage_Attack_Teleport() self.fixangle = true; self.velocity *= 0.5; - self.attack_finished_single = time + 0.2; + self.attack_finished_single[0] = time + 0.2; } void M_Mage_Defend_Shield_Remove() @@ -350,12 +351,13 @@ void M_Mage_Defend_Shield() self.armorvalue = (autocvar_g_monster_mage_shield_blockpercent); self.mage_shield_time = time + (autocvar_g_monster_mage_shield_time); setanim(self, self.anim_shoot, true, true, true); - self.attack_finished_single = time + 1; + self.attack_finished_single[0] = time + 1; self.anim_finished = time + 1; } float M_Mage_Attack(float attack_type, entity targ) {SELFPARAM(); + .entity weaponentity = weaponentities[0]; switch(attack_type) { case MONSTER_ATTACK_MELEE: @@ -363,7 +365,8 @@ float M_Mage_Attack(float attack_type, entity targ) if(random() <= 0.7) { Weapon wep = WEP_MAGE_SPIKE; - wep.wr_think(wep, self, false, true); + + wep.wr_think(wep, self, weaponentity, 2); return true; } @@ -382,10 +385,10 @@ float M_Mage_Attack(float attack_type, entity targ) else { setanim(self, self.anim_shoot, true, true, true); - self.attack_finished_single = time + (autocvar_g_monster_mage_attack_spike_delay); + self.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay); self.anim_finished = time + 1; Weapon wep = WEP_MAGE_SPIKE; - wep.wr_think(wep, self, true, false); + wep.wr_think(wep, self, weaponentity, 1); return true; } } @@ -421,7 +424,7 @@ spawnfunc(monster_mage) { Monster_Spawn(MON_MAGE.monsterid); } } if(self.health < (autocvar_g_monster_mage_heal_minhealth) || need_help) - if(time >= self.attack_finished_single) + if(time >= self.attack_finished_single[0]) if(random() < 0.5) M_Mage_Defend_Heal(); diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index 203b597cc0..67b6808cc7 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -2,7 +2,7 @@ #define SHAMBLER_H #ifndef MENUQC -MODEL(MON_SHAMBLER, "models/monsters/shambler.mdl"); +MODEL(MON_SHAMBLER, M_Model("shambler.mdl")); #endif CLASS(Shambler, Monster) @@ -77,12 +77,12 @@ void M_Shambler_Attack_Swing() if(r && Monster_Attack_Melee(self.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((r) ? self.anim_melee2 : self.anim_melee3), self.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW.m_id, true)) { Monster_Delay(1, 0, 0.5, M_Shambler_Attack_Swing); - self.attack_finished_single += 0.5; - self.anim_finished = self.attack_finished_single; + self.attack_finished_single[0] += 0.5; + self.anim_finished = self.attack_finished_single[0]; } } -#include "../../../server/csqceffects.qh" +#include "../../effects/qc/all.qh" void M_Shambler_Attack_Lightning_Explode() {SELFPARAM(); @@ -149,9 +149,8 @@ void M_Shambler_Attack_Lightning() monster_makevectors(self.enemy); - gren = spawn (); + gren = new(grenade); gren.owner = gren.realowner = self; - gren.classname = "grenade"; gren.bot_dodge = true; gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage); gren.movetype = MOVETYPE_BOUNCE; @@ -202,7 +201,7 @@ float M_Shambler_Attack(float attack_type, entity targ) { setanim(self, self.anim_melee2, true, true, false); Monster_Delay(1, 0, 0.7, M_Shambler_Attack_Smash); - self.attack_finished_single = time + 1.1; + self.attack_finished_single[0] = time + 1.1; self.anim_finished = time + 1.1; self.state = MONSTER_ATTACK_MELEE; // kinda a melee attack self.shambler_lastattack = time + 3 + random() * 1.5; @@ -212,7 +211,7 @@ float M_Shambler_Attack(float attack_type, entity targ) { setanim(self, self.anim_shoot, true, true, false); self.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general - self.attack_finished_single = time + 1.1; + self.attack_finished_single[0] = time + 1.1; self.anim_finished = 1.1; self.shambler_lastattack = time + 3 + random() * 1.5; Monster_Delay(1, 0, 0.6, M_Shambler_Attack_Lightning); diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 208ae10799..10ee911d95 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -2,7 +2,7 @@ #define SPIDER_H #ifndef MENUQC -MODEL(MON_SPIDER, "models/monsters/spider.dpm"); +MODEL(MON_SPIDER, M_Model("spider.dpm")); #endif CLASS(Spider, Monster) @@ -28,7 +28,7 @@ CLASS(SpiderAttack, PortoLaunch) /* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(SpiderAttack, impulse, int, 9); /* refname */ ATTRIB(SpiderAttack, netname, string, "spider"); -/* wepname */ ATTRIB(SpiderAttack, message, string, _("Spider attack")); +/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); ENDCLASS(SpiderAttack) REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); @@ -50,31 +50,32 @@ float autocvar_g_monster_spider_attack_bite_delay; void M_Spider_Attack_Web(); -METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(SpiderAttack_FIRE, W_Sound("electro_fire")); +METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, false, autocvar_g_monster_spider_attack_web_delay)) { + if (fire & 1) + if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay)) { if (!isPlayer) { actor.spider_web_delay = time + 3; setanim(actor, actor.anim_shoot, true, true, true); - actor.attack_finished_single = time + (autocvar_g_monster_spider_attack_web_delay); + actor.attack_finished_single[0] = time + (autocvar_g_monster_spider_attack_web_delay); actor.anim_finished = time + 1; } if (isPlayer) actor.enemy = Monster_FindTarget(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(SpiderAttack_FIRE), CH_WEAPON_B, 0); if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin); M_Spider_Attack_Web(); - weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); return; } - if (fire2) - if (!isPlayer || weapon_prepareattack(thiswep, actor, true, 0.5)) { + if (fire & 2) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, true, 0.5)) { if (isPlayer) { actor.enemy = Monster_FindTarget(actor); actor.attack_range = 60; } Monster_Attack_Melee(actor.enemy, (autocvar_g_monster_spider_attack_bite_damage), ((random() > 0.5) ? self.anim_melee : self.anim_shoot), self.attack_range, (autocvar_g_monster_spider_attack_bite_delay), DEATH_MONSTER_SPIDER.m_id, true); - weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready); } } @@ -121,8 +122,7 @@ void M_Spider_Attack_Web() sound(self, CH_SHOTS, SND_ELECTRO_FIRE2, VOL_BASE, ATTEN_NORM); - entity proj = spawn (); - proj.classname = "plasma"; + entity proj = new(plasma); proj.owner = proj.realowner = self; proj.use = M_Spider_Attack_Web_Explode; proj.think = adaptor_think2use_hittype_splash; @@ -155,17 +155,18 @@ void M_Spider_Attack_Web() bool M_Spider_Attack(int attack_type, entity targ) {SELFPARAM(); + .entity weaponentity = weaponentities[0]; switch(attack_type) { Weapon wep = WEP_SPIDER_ATTACK; case MONSTER_ATTACK_MELEE: { - wep.wr_think(wep, self, false, true); + wep.wr_think(wep, self, weaponentity, 2); return true; } case MONSTER_ATTACK_RANGED: { - wep.wr_think(wep, self, true, false); + wep.wr_think(wep, self, weaponentity, 1); return true; } } diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index ac9f32205d..b55c5a42d9 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -2,7 +2,7 @@ #define WYVERN_H #ifndef MENUQC -MODEL(MON_WYVERN, "models/monsters/wizard.mdl"); +MODEL(MON_WYVERN, M_Model("wizard.mdl")); #endif CLASS(Wyvern, Monster) @@ -28,7 +28,7 @@ CLASS(WyvernAttack, PortoLaunch) /* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(WyvernAttack, impulse, int, 9); /* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern"); -/* wepname */ ATTRIB(WyvernAttack, message, string, _("Wyvern attack")); +/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack")); ENDCLASS(WyvernAttack) REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack)); @@ -48,12 +48,13 @@ float autocvar_g_monster_wyvern_attack_fireball_speed; void M_Wyvern_Attack_Fireball_Explode(); void M_Wyvern_Attack_Fireball_Touch(); -METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, bool fire1, bool fire2)) { - if (fire1) - if (time > actor.attack_finished_single || weapon_prepareattack(thiswep, actor, false, 1.2)) { - if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); +SOUND(WyvernAttack_FIRE, W_Sound("electro_fire")); +METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity weaponentity, int fire)) { + if (fire & 1) + if (time > actor.attack_finished_single[0] || weapon_prepareattack(thiswep, actor, weaponentity, false, 1.2)) { + if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, SND(WyvernAttack_FIRE), CH_WEAPON_B, 0); if (IS_MONSTER(actor)) { - actor.attack_finished_single = time + 1.2; + actor.attack_finished_single[0] = time + 1.2; actor.anim_finished = time + 1.2; monster_makevectors(actor.enemy); } @@ -73,7 +74,7 @@ METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, bool fir missile.touch = M_Wyvern_Attack_Fireball_Touch; CSQCProjectile(missile, true, PROJECTILE_FIREMINE, true); - weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } } @@ -121,6 +122,7 @@ void M_Wyvern_Attack_Fireball_Touch() float M_Wyvern_Attack(float attack_type, entity targ) { SELFPARAM(); + .entity weaponentity = weaponentities[0]; switch(attack_type) { case MONSTER_ATTACK_MELEE: @@ -128,7 +130,7 @@ float M_Wyvern_Attack(float attack_type, entity targ) { w_shotdir = normalize((self.enemy.origin + '0 0 10') - self.origin); Weapon wep = WEP_WYVERN_ATTACK; - wep.wr_think(wep, self, true, false); + wep.wr_think(wep, self, weaponentity, 1); return true; } } diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index e68b7ff542..d63188b0fe 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -2,7 +2,7 @@ #define ZOMBIE_H #ifndef MENUQC -MODEL(MON_ZOMBIE, "models/monsters/zombie.dpm"); +MODEL(MON_ZOMBIE, M_Model("zombie.dpm")); #endif CLASS(Zombie, Monster) @@ -111,8 +111,8 @@ float M_Zombie_Defend_Block() {SELFPARAM(); self.armorvalue = 0.9; self.state = MONSTER_ATTACK_MELEE; // freeze monster - self.attack_finished_single = time + 2.1; - self.anim_finished = self.attack_finished_single; + self.attack_finished_single[0] = time + 2.1; + self.anim_finished = self.attack_finished_single[0]; setanim(self, self.anim_blockstart, false, true, true); Monster_Delay(1, 0, 2, M_Zombie_Defend_Block_End); diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index ac642fd3cb..a3610e17f7 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -36,7 +36,7 @@ void monster_dropitem() return; vector org = self.origin + ((self.mins + self.maxs) * 0.5); - entity e = spawn(); + entity e = new(droppedweapon); // use weapon handling to remove it on touch e.spawnfunc_checked = true; e.monster_loot = self.monster_loot; @@ -55,7 +55,6 @@ void monster_dropitem() setorigin(e, org); e.velocity = randomvec() * 175 + '0 0 325'; e.item_spawnshieldtime = time + 0.7; - e.classname = "droppedweapon"; // use weapon handling to remove it on touch SUB_SetFade(e, time + autocvar_g_monsters_drop_time, 1); setself(this); } @@ -347,7 +346,7 @@ void Monster_Sound(.string samplefield, float sound_delay, float delaytoo, float if(delaytoo) if(time < self.msound_delay) return; // too early - GlobalSound(self.(samplefield), chan, VOICETYPE_PLAYERSOUND); + _GlobalSound(self.(samplefield), chan, VOICETYPE_PLAYERSOUND, false); self.msound_delay = time + sound_delay; } @@ -364,9 +363,9 @@ float Monster_Attack_Melee(entity targ, float damg, vector anim, float er, float setanim(self, anim, false, true, false); if(self.animstate_endtime > time && (self.flags & FL_MONSTER)) - self.attack_finished_single = self.anim_finished = self.animstate_endtime; + self.attack_finished_single[0] = self.anim_finished = self.animstate_endtime; else - self.attack_finished_single = self.anim_finished = time + animtime; + self.attack_finished_single[0] = self.anim_finished = time + animtime; monster_makevectors(targ); @@ -386,7 +385,7 @@ float Monster_Attack_Leap_Check(vector vel) return false; // not on the ground if(self.health <= 0) return false; // called when dead? - if(time < self.attack_finished_single) + if(time < self.attack_finished_single[0]) return false; // still attacking vector old = self.velocity; @@ -408,9 +407,9 @@ bool Monster_Attack_Leap(vector anm, void() touchfunc, vector vel, float animtim setanim(self, anm, false, true, false); if(self.animstate_endtime > time && (self.flags & FL_MONSTER)) - self.attack_finished_single = self.anim_finished = self.animstate_endtime; + self.attack_finished_single[0] = self.anim_finished = self.animstate_endtime; else - self.attack_finished_single = self.anim_finished = time + animtime; + self.attack_finished_single[0] = self.anim_finished = time + animtime; if(self.flags & FL_MONSTER) self.state = MONSTER_ATTACK_RANGED; @@ -426,7 +425,7 @@ void Monster_Attack_Check(entity e, entity targ) { if((e == world || targ == world) || (!e.monster_attackfunc) - || (time < e.attack_finished_single) + || (time < e.attack_finished_single[0]) ) { return; } float targ_vlen = vlen(targ.origin - e.origin); @@ -845,7 +844,7 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed) self.touch = Monster_Touch; } - if(self.state && time >= self.attack_finished_single) + if(self.state && time >= self.attack_finished_single[0]) self.state = 0; // attack is over if(self.state != MONSTER_ATTACK_MELEE) // don't move if set @@ -911,12 +910,13 @@ void Monster_Move(float runspeed, float walkspeed, float stpspeed) void Monster_Remove(entity mon) { + .entity weaponentity = weaponentities[0]; if(!mon) { return; } if(!MUTATOR_CALLHOOK(MonsterRemove, mon)) Send_Effect(EFFECT_ITEM_PICKUP, mon.origin, '0 0 0', 1); - if(mon.weaponentity) { remove(mon.weaponentity); } + if(mon.(weaponentity)) { remove(mon.(weaponentity)); } if(mon.iceblock) { remove(mon.iceblock); } WaypointSprite_Kill(mon.sprite); remove(mon); @@ -966,7 +966,7 @@ void Monster_Reset() self.velocity = '0 0 0'; self.enemy = world; self.goalentity = world; - self.attack_finished_single = 0; + self.attack_finished_single[0] = 0; self.moveto = self.origin; } @@ -1028,7 +1028,7 @@ void Monster_Dead(entity attacker, float gibbed) self.touch = Monster_Touch; // reset incase monster was pouncing self.reset = func_null; self.state = 0; - self.attack_finished_single = 0; + self.attack_finished_single[0] = 0; self.effects = 0; if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM))) @@ -1176,7 +1176,7 @@ void Monster_Move_2D(float mspeed, float allow_jumpoff) movelib_move_simple_gravity(v_forward, mspeed, 1); if(time > self.pain_finished) - if(time > self.attack_finished_single) + if(time > self.attack_finished_single[0]) if(vlen(self.velocity) > 10) setanim(self, self.anim_walk, true, false, false); else @@ -1376,9 +1376,15 @@ bool Monster_Spawn(int mon_id) self.movetype = MOVETYPE_FLY; } - if(mon.spawnflags & MONSTER_SIZE_BROKEN) if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) - self.scale *= 1.3; + { + if(mon.spawnflags & MONSTER_SIZE_BROKEN) + self.scale *= 1.3; + + if(mon.spawnflags & MONSTER_SIZE_QUAKE) + if(autocvar_g_monsters_quake_resize) + self.scale *= 1.3; + } setsize(self, mon.mins * self.scale, mon.maxs * self.scale); diff --git a/qcsrc/common/movetypes/movetypes.qh b/qcsrc/common/movetypes/movetypes.qh index 67cd51e16b..362075da2d 100644 --- a/qcsrc/common/movetypes/movetypes.qh +++ b/qcsrc/common/movetypes/movetypes.qh @@ -2,9 +2,9 @@ #define MOVETYPES_H .float move_ltime; -.void(void)move_think; +.void()move_think; .float move_nextthink; -.void(void)move_blocked; +.void()move_blocked; .float move_movetype; .float move_time; @@ -15,7 +15,7 @@ .int move_flags; .int move_watertype; .int move_waterlevel; -.void(void)move_touch; +.void()move_touch; .void(float, float)contentstransition; .float move_bounce_factor; .float move_bounce_stopspeed; diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc index 6b19a0bc6e..bef00c649e 100644 --- a/qcsrc/common/mutators/all.inc +++ b/qcsrc/common/mutators/all.inc @@ -1,5 +1,36 @@ -#include "mutator/casings.qc" -#include "mutator/damagetext.qc" -#include "mutator/instagib/instagib.qc" +#include "mutator/waypoints/module.inc" + #include "mutator/itemstime.qc" -#include "mutator/waypoints/waypointsprites.qc" +#include "mutator/multijump/module.inc" +#include "mutator/nades/module.inc" +#include "mutator/superspec/module.inc" + +// completely self contained + +#include "mutator/bloodloss/module.inc" +#include "mutator/breakablehook/module.inc" +#include "mutator/buffs/module.inc" +#include "mutator/campcheck/module.inc" +#include "mutator/cloaked/module.inc" +#include "mutator/damagetext/module.inc" +#include "mutator/dodging/module.inc" +#include "mutator/hook/module.inc" +#include "mutator/instagib/module.inc" +#include "mutator/invincibleproj/module.inc" +#include "mutator/melee_only/module.inc" +#include "mutator/midair/module.inc" +#include "mutator/new_toys/module.inc" +#include "mutator/nix/module.inc" +#include "mutator/overkill/module.inc" +#include "mutator/physical_items/module.inc" +#include "mutator/pinata/module.inc" +#include "mutator/random_gravity/module.inc" +#include "mutator/rocketflying/module.inc" +#include "mutator/rocketminsta/module.inc" +#include "mutator/running_guns/module.inc" +#include "mutator/sandbox/module.inc" +#include "mutator/spawn_near_teammate/module.inc" +#include "mutator/touchexplode/module.inc" +#include "mutator/vampirehook/module.inc" +#include "mutator/vampire/module.inc" +#include "mutator/weaponarena_random/module.inc" diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh index ce98cd508e..7cfc297896 100644 --- a/qcsrc/common/mutators/base.qh +++ b/qcsrc/common/mutators/base.qh @@ -154,7 +154,8 @@ CLASS(Mutator, Object) } ENDCLASS(Mutator) -REGISTRY(Mutators, BITS(6)) +REGISTRY(Mutators, BITS(7)) +#define Mutators_from(i) _Mutators_from(i, NULL) Mutator loaded_mutators[Mutators_MAX]; bool Mutator_Add(Mutator mut) @@ -209,7 +210,7 @@ void Mutator_Remove(Mutator mut) bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \ } \ bool MUTATOR_##id##_check() { return dependence; } \ - REGISTER(RegisterMutators, MUTATOR, Mutators, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \ + REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \ { this.mutatorcheck = MUTATOR_##id##_check; } \ [[accumulate]] bool MUTATORFUNCTION_##id(int mode) diff --git a/qcsrc/common/mutators/events.qh b/qcsrc/common/mutators/events.qh index 7ba24fa7a4..82898237c5 100644 --- a/qcsrc/common/mutators/events.qh +++ b/qcsrc/common/mutators/events.qh @@ -15,7 +15,8 @@ string ret_string; _(x, string) \ /**/ -#define MUTATOR_NEWGLOBAL(x, type) type mutator_argv_##type##_##x; +#define MUTATOR_ARGV(x, type) MUTATOR_ARGV_##x##_##type +#define MUTATOR_NEWGLOBAL(x, type) type MUTATOR_ARGV(x, type); MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 0) MUTATOR_TYPES(MUTATOR_NEWGLOBAL, 1) @@ -47,4 +48,17 @@ MUTATOR_HOOKABLE(BuildMutatorsPrettyString, EV_BuildMutatorsPrettyString); /**/ MUTATOR_HOOKABLE(BuildGameplayTipsString, EV_BuildGameplayTipsString); +#define EV_IsFlying(i, o) \ + /**/ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(IsFlying, EV_IsFlying); + +#define EV_WP_Format(i, o) \ + /**/ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ i(string, MUTATOR_ARGV_0_string) \ + /**/ o(vector, MUTATOR_ARGV_0_vector) \ + /**/ o(string, MUTATOR_ARGV_0_string) \ + /**/ +MUTATOR_HOOKABLE(WP_Format, EV_WP_Format); + #endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc new file mode 100644 index 0000000000..ca37166690 --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc @@ -0,0 +1,45 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); + +.float bloodloss_timer; + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) +{SELFPARAM(); + if(IS_PLAYER(self)) + if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO) + { + self.BUTTON_CROUCH = true; + + if(time >= self.bloodloss_timer) + { + if(self.vehicle) + vehicles_exit(VHEF_RELEASE); + if(self.event_damage) + self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); + self.bloodloss_timer = time + 0.5 + random() * 0.5; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) +{SELFPARAM(); + if(self.health <= autocvar_g_bloodloss) + return true; + + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":bloodloss"); + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Blood loss"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/module.inc b/qcsrc/common/mutators/mutator/bloodloss/module.inc new file mode 100644 index 0000000000..d3f665a18b --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "bloodloss.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc new file mode 100644 index 0000000000..3ee077c723 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc @@ -0,0 +1,29 @@ +#ifdef IMPLEMENTATION +#include "../../../deathtypes/all.qh" +#include "../../../../server/g_hook.qh" + +REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); + +bool autocvar_g_breakablehook; // allow toggling mid match? +bool autocvar_g_breakablehook_owner; + +MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) +{ + if(frag_target.classname == "grapplinghook") + { + if((!autocvar_g_breakablehook) + || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) + ) { frag_damage = 0; } + + // hurt the owner of the hook + if(DIFF_TEAM(frag_attacker, frag_target.realowner)) + { + Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); + RemoveGrapplingHook(frag_target.realowner); + return false; // dead + } + } + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/module.inc b/qcsrc/common/mutators/mutator/breakablehook/module.inc new file mode 100644 index 0000000000..484eb4c563 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "breakablehook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/all.inc b/qcsrc/common/mutators/mutator/buffs/all.inc new file mode 100644 index 0000000000..25fa72236c --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/all.inc @@ -0,0 +1,118 @@ +REGISTER_BUFF(AMMO) { + this.m_prettyName = _("Ammo"); + this.m_name = "ammo"; + this.m_skin = 3; + this.m_color = '0.76 1 0.1'; +} +BUFF_SPAWNFUNCS(ammo, BUFF_AMMO) +BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_AMMO) + +REGISTER_BUFF(RESISTANCE) { + this.m_prettyName = _("Resistance"); + this.m_name = "resistance"; + this.m_skin = 0; + this.m_color = '0.36 1 0.07'; +} +BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE) +BUFF_SPAWNFUNC_Q3TA_COMPAT(resistance, BUFF_RESISTANCE) + +REGISTER_BUFF(SPEED) { + this.m_prettyName = _("Speed"); + this.m_name = "speed"; + this.m_skin = 9; + this.m_color = '0.1 1 0.84'; +} +BUFF_SPAWNFUNCS(speed, BUFF_SPEED) +BUFF_SPAWNFUNC_Q3TA_COMPAT(haste, BUFF_SPEED) +BUFF_SPAWNFUNC_Q3TA_COMPAT(scout, BUFF_SPEED) + +REGISTER_BUFF(MEDIC) { + this.m_prettyName = _("Medic"); + this.m_name = "medic"; + this.m_skin = 1; + this.m_color = '1 0.12 0'; +} +BUFF_SPAWNFUNCS(medic, BUFF_MEDIC) +BUFF_SPAWNFUNC_Q3TA_COMPAT(doubler, BUFF_MEDIC) +BUFF_SPAWNFUNC_Q3TA_COMPAT(medic, BUFF_MEDIC) + +REGISTER_BUFF(BASH) { + this.m_prettyName = _("Bash"); + this.m_name = "bash"; + this.m_skin = 5; + this.m_color = '1 0.39 0'; +} +BUFF_SPAWNFUNCS(bash, BUFF_BASH) + +REGISTER_BUFF(VAMPIRE) { + this.m_prettyName = _("Vampire"); + this.m_name = "vampire"; + this.m_skin = 2; + this.m_color = '1 0 0.24'; +} +BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE) + +REGISTER_BUFF(DISABILITY) { + this.m_prettyName = _("Disability"); + this.m_name = "disability"; + this.m_skin = 7; + this.m_color = '0.94 0.3 1'; +} +BUFF_SPAWNFUNCS(disability, BUFF_DISABILITY) + +REGISTER_BUFF(VENGEANCE) { + this.m_prettyName = _("Vengeance"); + this.m_name = "vengeance"; + this.m_skin = 15; + this.m_color = '1 0.23 0.61'; +} +BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE) + +REGISTER_BUFF(JUMP) { + this.m_prettyName = _("Jump"); + this.m_name = "jump"; + this.m_skin = 10; + this.m_color = '0.24 0.78 1'; +} +BUFF_SPAWNFUNCS(jump, BUFF_JUMP) + +REGISTER_BUFF(FLIGHT) { + this.m_prettyName = _("Flight"); + this.m_name = "flight"; + this.m_skin = 11; + this.m_color = '0.33 0.56 1'; +} +BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT) + +REGISTER_BUFF(INVISIBLE) { + this.m_prettyName = _("Invisible"); + this.m_name = "invisible"; + this.m_skin = 12; + this.m_color = '0.5 0.5 1'; +} +BUFF_SPAWNFUNCS(invisible, BUFF_INVISIBLE) +BUFF_SPAWNFUNC_Q3TA_COMPAT(invis, BUFF_INVISIBLE) + +REGISTER_BUFF(INFERNO) { + this.m_prettyName = _("Inferno"); + this.m_name = "inferno"; + this.m_skin = 16; + this.m_color = '1 0.62 0'; +} +BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO) + +REGISTER_BUFF(SWAPPER) { + this.m_prettyName = _("Swapper"); + this.m_name = "swapper"; + this.m_skin = 17; + this.m_color = '0.63 0.36 1'; +} +BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER) + +REGISTER_BUFF(MAGNET) { + this.m_prettyName = _("Magnet"); + this.m_name = "magnet"; + this.m_skin = 18; + this.m_color = '1 0.95 0.18'; +} +BUFF_SPAWNFUNCS(magnet, BUFF_MAGNET) diff --git a/qcsrc/common/mutators/mutator/buffs/all.qc b/qcsrc/common/mutators/mutator/buffs/all.qc new file mode 100644 index 0000000000..b056751624 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/all.qc @@ -0,0 +1 @@ +#include "all.qh" diff --git a/qcsrc/common/mutators/mutator/buffs/all.qh b/qcsrc/common/mutators/mutator/buffs/all.qh new file mode 100644 index 0000000000..94a00b9de7 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/all.qh @@ -0,0 +1,73 @@ +#ifndef BUFFS_ALL_H +#define BUFFS_ALL_H +// Welcome to the stuff behind the scenes +// Below, you will find the list of buffs +// Add new buffs here! +// Note: Buffs also need spawnfuncs, which are set below + +#include "../../../teams.qh" +#include "../../../util.qh" + +REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); +REGISTER_RADARICON(Buff, 1); + +REGISTRY(Buffs, BITS(4)) +#define Buffs_from(i) _Buffs_from(i, BUFF_Null) +REGISTER_REGISTRY(Buffs) +REGISTRY_CHECK(Buffs) + +#define REGISTER_BUFF(id) \ + REGISTER(Buffs, BUFF, id, m_id, NEW(Buff)); \ + REGISTER_INIT_POST(BUFF, id) { \ + this.netname = this.m_name; \ + this.m_itemid = BIT(this.m_id - 1); \ + this.m_sprite = strzone(strcat("buff-", this.m_name)); \ + } \ + REGISTER_INIT(BUFF, id) + +#include "../../../items/item/pickup.qh" +CLASS(Buff, Pickup) + /** bit index */ + ATTRIB(Buff, m_itemid, int, 0) + ATTRIB(Buff, m_name, string, "buff") + ATTRIB(Buff, m_color, vector, '1 1 1') + ATTRIB(Buff, m_prettyName, string, "Buff") + ATTRIB(Buff, m_skin, int, 0) + ATTRIB(Buff, m_sprite, string, "") + METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) { + returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name)); + } +#ifdef SVQC + METHOD(Buff, m_time, float(Buff this)) + { return cvar(strcat("g_buffs_", this.netname, "_time")); } +#endif +ENDCLASS(Buff) + +#ifdef SVQC + .int buffs; + void buff_Init(entity ent); + void buff_Init_Compat(entity ent, entity replacement); + #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ + self.buffs = b.m_itemid; \ + self.team = t; \ + buff_Init(self); \ + } + #define BUFF_SPAWNFUNCS(e, b) \ + BUFF_SPAWNFUNC(e, b, 0) \ + BUFF_SPAWNFUNC(e##_team1, b, NUM_TEAM_1) \ + BUFF_SPAWNFUNC(e##_team2, b, NUM_TEAM_2) \ + BUFF_SPAWNFUNC(e##_team3, b, NUM_TEAM_3) \ + BUFF_SPAWNFUNC(e##_team4, b, NUM_TEAM_4) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(self, r); } +#else + #define BUFF_SPAWNFUNC(e, b, t) + #define BUFF_SPAWNFUNCS(e, b) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) +#endif + +REGISTER_BUFF(Null); +BUFF_SPAWNFUNCS(random, BUFF_Null) + +#include "all.inc" + +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qc b/qcsrc/common/mutators/mutator/buffs/buffs.qc new file mode 100644 index 0000000000..08f1aa3b50 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qc @@ -0,0 +1,1050 @@ +#ifndef MUTATOR_BUFFS_H +#define MUTATOR_BUFFS_H + +#include "../instagib/module.inc" + +bool autocvar_g_buffs_effects; +float autocvar_g_buffs_waypoint_distance; +bool autocvar_g_buffs_randomize; +float autocvar_g_buffs_random_lifetime; +bool autocvar_g_buffs_random_location; +int autocvar_g_buffs_random_location_attempts; +int autocvar_g_buffs_spawn_count; +bool autocvar_g_buffs_replace_powerups; +float autocvar_g_buffs_cooldown_activate; +float autocvar_g_buffs_cooldown_respawn; +float autocvar_g_buffs_resistance_blockpercent; +float autocvar_g_buffs_medic_survive_chance; +float autocvar_g_buffs_medic_survive_health; +float autocvar_g_buffs_medic_rot; +float autocvar_g_buffs_medic_max; +float autocvar_g_buffs_medic_regen; +float autocvar_g_buffs_vengeance_damage_multiplier; +float autocvar_g_buffs_bash_force; +float autocvar_g_buffs_bash_force_self; +float autocvar_g_buffs_disability_slowtime; +float autocvar_g_buffs_disability_speed; +float autocvar_g_buffs_disability_rate; +float autocvar_g_buffs_disability_weaponspeed; +float autocvar_g_buffs_speed_speed; +float autocvar_g_buffs_speed_rate; +float autocvar_g_buffs_speed_weaponspeed; +float autocvar_g_buffs_speed_damage_take; +float autocvar_g_buffs_speed_regen; +float autocvar_g_buffs_vampire_damage_steal; +float autocvar_g_buffs_invisible_alpha; +float autocvar_g_buffs_flight_gravity; +float autocvar_g_buffs_jump_height; +float autocvar_g_buffs_inferno_burntime_factor; +float autocvar_g_buffs_inferno_burntime_min_time; +float autocvar_g_buffs_inferno_burntime_target_damage; +float autocvar_g_buffs_inferno_burntime_target_time; +float autocvar_g_buffs_inferno_damagemultiplier; +float autocvar_g_buffs_swapper_range; +float autocvar_g_buffs_magnet_range_item; + +// ammo +.float buff_ammo_prev_infitems; +.int buff_ammo_prev_clipload; +// invisible +.float buff_invisible_prev_alpha; +// flight +.float buff_flight_prev_gravity; +// disability +.float buff_disability_time; +.float buff_disability_effect_time; +// common buff variables +.float buff_effect_delay; + +// buff definitions +.float buff_active; +.float buff_activetime; +.float buff_activetime_updated; +.entity buff_waypoint; +.int oldbuffs; // for updating effects +.entity buff_model; // controls effects (TODO: make csqc) + +const vector BUFF_MIN = ('-16 -16 -20'); +const vector BUFF_MAX = ('16 16 20'); + +// client side options +.float cvar_cl_buffs_autoreplace; +#endif + +#ifdef IMPLEMENTATION + +#include "../../../triggers/target/music.qh" +#include "../../../gamemodes/all.qh" + +.float buff_time; +void buffs_DelayedInit(); + +REGISTER_MUTATOR(buffs, cvar("g_buffs")) +{ + MUTATOR_ONADD + { + addstat(STAT_BUFFS, AS_INT, buffs); + addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time); + + InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); + } +} + +entity buff_FirstFromFlags(int _buffs) +{ + if (flags) + { + FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); + } + return BUFF_Null; +} + +bool buffs_BuffModel_Customize() +{SELFPARAM(); + entity player, myowner; + bool same_team; + + player = WaypointSprite_getviewentity(other); + myowner = self.owner; + same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); + + if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) + return false; + + if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player)) + return false; + + if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) + { + // somewhat hide the model, but keep the glow + self.effects = 0; + self.alpha = -1; + } + else + { + self.effects = EF_FULLBRIGHT | EF_LOWPRECISION; + self.alpha = 1; + } + return true; +} + +void buffs_BuffModel_Spawn(entity player) +{ + player.buff_model = spawn(); + setmodel(player.buff_model, MDL_BUFF); + setsize(player.buff_model, '0 0 -40', '0 0 40'); + setattachment(player.buff_model, player, ""); + setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); + player.buff_model.owner = player; + player.buff_model.scale = 0.7; + player.buff_model.pflags = PFLAGS_FULLDYNAMIC; + player.buff_model.light_lev = 200; + player.buff_model.customizeentityforclient = buffs_BuffModel_Customize; +} + +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.m_color; +} + +void buff_Effect(entity player, string eff) +{SELFPARAM(); + if(!autocvar_g_buffs_effects) { return; } + + if(time >= self.buff_effect_delay) + { + Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + self.buff_effect_delay = time + 0.05; // prevent spam + } +} + +// buff item +float buff_Waypoint_visible_for_player(entity plr) +{SELFPARAM(); + if(!self.owner.buff_active && !self.owner.buff_activetime) + return false; + + if (plr.buffs) + { + return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs; + } + + return WaypointSprite_visible_for_player(plr); +} + +void buff_Waypoint_Spawn(entity e) +{ + entity buff = buff_FirstFromFlags(e.buffs); + entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff); + wp.wp_extra = buff.m_id; + WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); + e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; +} + +void buff_SetCooldown(float cd) +{SELFPARAM(); + cd = max(0, cd); + + if(!self.buff_waypoint) + buff_Waypoint_Spawn(self); + + WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd); + self.buff_activetime = cd; + self.buff_active = !cd; +} + +void buff_Respawn(entity ent) +{SELFPARAM(); + if(gameover) { return; } + + vector oldbufforigin = ent.origin; + + if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) + { + entity spot = SelectSpawnPoint(true); + setorigin(ent, spot.origin + '0 0 200'); + ent.angles = spot.angles; + } + + tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent); + + setorigin(ent, trace_endpos); // attempt to unstick + + ent.movetype = MOVETYPE_TOSS; + + makevectors(ent.angles); + ent.velocity = '0 0 200'; + ent.angles = '0 0 0'; + if(autocvar_g_buffs_random_lifetime > 0) + ent.lifetime = time + autocvar_g_buffs_random_lifetime; + + Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1); + + WaypointSprite_Ping(ent.buff_waypoint); + + sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) +} + +void buff_Touch() +{SELFPARAM(); + if(gameover) { return; } + + if(ITEM_TOUCH_NEEDKILL()) + { + buff_Respawn(self); + return; + } + + if((self.team && DIFF_TEAM(other, self)) + || (other.frozen) + || (other.vehicle) + || (!self.buff_active) + ) + { + // can't touch this + return; + } + + if(MUTATOR_CALLHOOK(BuffTouch, self, other)) + return; + + if(!IS_PLAYER(other)) + return; // incase mutator changed other + + if (other.buffs) + { + if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) + { + int buffid = buff_FirstFromFlags(other.buffs).m_id; + //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); + + other.buffs = 0; + //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else { return; } // do nothing + } + + self.owner = other; + self.buff_active = false; + self.lifetime = 0; + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); + + Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); + sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); + other.buffs |= (self.buffs); +} + +float buff_Available(entity buff) +{ + if (buff == BUFF_Null) + return false; + if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) + return false; + if (buff == BUFF_VAMPIRE && cvar("g_vampire")) + return false; + return cvar(strcat("g_buffs_", buff.m_name)); +} + +.int buff_seencount; + +void buff_NewType(entity ent, float cb) +{ + RandomSelection_Init(); + FOREACH(Buffs, buff_Available(it), LAMBDA( + it.buff_seencount += 1; + // if it's already been chosen, give it a lower priority + RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); + )); + ent.buffs = RandomSelection_chosen_float; +} + +void buff_Think() +{SELFPARAM(); + if(self.buffs != self.oldbuffs) + { + entity buff = buff_FirstFromFlags(self.buffs); + self.color = buff.m_color; + self.glowmod = buff_GlowColor(buff); + self.skin = buff.m_skin; + + setmodel(self, MDL_BUFF); + + if(self.buff_waypoint) + { + //WaypointSprite_Disown(self.buff_waypoint, 1); + WaypointSprite_Kill(self.buff_waypoint); + buff_Waypoint_Spawn(self); + if(self.buff_activetime) + WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime); + } + + self.oldbuffs = self.buffs; + } + + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + if(!self.buff_activetime_updated) + { + buff_SetCooldown(self.buff_activetime); + self.buff_activetime_updated = true; + } + + if(!self.buff_active && !self.buff_activetime) + if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs)) + { + buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime); + self.owner = world; + if(autocvar_g_buffs_randomize) + buff_NewType(self, self.buffs); + + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) + buff_Respawn(self); + } + + if(self.buff_activetime) + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + { + self.buff_activetime = max(0, self.buff_activetime - frametime); + + if(!self.buff_activetime) + { + self.buff_active = true; + sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); + Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); + } + } + + if(self.buff_active) + { + if(self.team && !self.buff_waypoint) + buff_Waypoint_Spawn(self); + + if(self.lifetime) + if(time >= self.lifetime) + buff_Respawn(self); + } + + self.nextthink = time; + //self.angles_y = time * 110.1; +} + +void buff_Waypoint_Reset() +{SELFPARAM(); + WaypointSprite_Kill(self.buff_waypoint); + + if(self.buff_activetime) { buff_Waypoint_Spawn(self); } +} + +void buff_Reset() +{SELFPARAM(); + if(autocvar_g_buffs_randomize) + buff_NewType(self, self.buffs); + self.owner = world; + buff_SetCooldown(autocvar_g_buffs_cooldown_activate); + buff_Waypoint_Reset(); + self.buff_activetime_updated = false; + + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) + buff_Respawn(self); +} + +float buff_Customize() +{SELFPARAM(); + entity player = WaypointSprite_getviewentity(other); + if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) + { + self.alpha = 0.3; + if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } + self.pflags = 0; + } + else + { + self.alpha = 1; + if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } + self.light_lev = 220 + 36 * sin(time); + self.pflags = PFLAGS_FULLDYNAMIC; + } + return true; +} + +void buff_Init(entity ent) +{SELFPARAM(); + if(!cvar("g_buffs")) { remove(ent); return; } + + if(!teamplay && ent.team) { ent.team = 0; } + + entity buff = buff_FirstFromFlags(self.buffs); + + setself(ent); + if(!self.buffs || buff_Available(buff)) + buff_NewType(self, 0); + + self.classname = "item_buff"; + self.solid = SOLID_TRIGGER; + self.flags = FL_ITEM; + self.think = buff_Think; + self.touch = buff_Touch; + self.reset = buff_Reset; + self.nextthink = time + 0.1; + self.gravity = 1; + self.movetype = MOVETYPE_TOSS; + self.scale = 1; + self.skin = buff.m_skin; + self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + self.customizeentityforclient = buff_Customize; + //self.gravity = 100; + self.color = buff.m_color; + self.glowmod = buff_GlowColor(self); + buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime); + self.buff_active = !self.buff_activetime; + self.pflags = PFLAGS_FULLDYNAMIC; + + if(self.spawnflags & 1) + self.noalign = true; + + if(self.noalign) + self.movetype = MOVETYPE_NONE; // reset by random location + + setmodel(self, MDL_BUFF); + setsize(self, BUFF_MIN, BUFF_MAX); + + if(cvar("g_buffs_random_location") || (self.spawnflags & 64)) + buff_Respawn(self); + + setself(this); +} + +void buff_Init_Compat(entity ent, entity replacement) +{ + if (ent.spawnflags & 2) + ent.team = NUM_TEAM_1; + else if (ent.spawnflags & 4) + ent.team = NUM_TEAM_2; + + ent.buffs = replacement.m_itemid; + + buff_Init(ent); +} + +void buff_SpawnReplacement(entity ent, entity old) +{ + setorigin(ent, old.origin); + ent.angles = old.angles; + ent.noalign = (old.noalign || (old.spawnflags & 1)); + + buff_Init(ent); +} + +void buff_Vengeance_DelayedDamage() +{SELFPARAM(); + if(self.enemy) + Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0'); + + remove(self); + return; +} + +float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) +{ + return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) +{ + if(frag_deathtype == DEATH_BUFF.m_id) { return false; } + + if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) + { + vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); + damage_take = v.x; + damage_save = v.y; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) +{ + if(frag_deathtype == DEATH_BUFF.m_id) { return false; } + + if(frag_target.buffs & BUFF_SPEED.m_itemid) + if(frag_target != frag_attacker) + frag_damage *= autocvar_g_buffs_speed_damage_take; + + if(frag_target.buffs & BUFF_MEDIC.m_itemid) + if((frag_target.health - frag_damage) <= 0) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(frag_attacker) + if(random() <= autocvar_g_buffs_medic_survive_chance) + frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); + + if(frag_target.buffs & BUFF_JUMP.m_itemid) + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) + if(frag_attacker) + if(frag_attacker != frag_target) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + { + entity dmgent = spawn(); + + dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; + dmgent.enemy = frag_attacker; + dmgent.owner = frag_target; + dmgent.think = buff_Vengeance_DelayedDamage; + dmgent.nextthink = time + 0.1; + } + + if(frag_target.buffs & BUFF_BASH.m_itemid) + if(frag_attacker != frag_target) + if(vlen(frag_force)) + frag_force = '0 0 0'; + + if(frag_attacker.buffs & BUFF_BASH.m_itemid) + if(vlen(frag_force)) + if(frag_attacker == frag_target) + frag_force *= autocvar_g_buffs_bash_force_self; + else + frag_force *= autocvar_g_buffs_bash_force; + + if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) + if(frag_target != frag_attacker) + frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; + + if(frag_attacker.buffs & BUFF_MEDIC.m_itemid) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) + if(SAME_TEAM(frag_attacker, frag_target)) + if(frag_attacker != frag_target) + { + frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage); + frag_damage = 0; + } + + if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) + if(frag_target != frag_attacker) { + float time = buff_Inferno_CalculateTime( + frag_damage, + 0, + autocvar_g_buffs_inferno_burntime_min_time, + autocvar_g_buffs_inferno_burntime_target_damage, + autocvar_g_buffs_inferno_burntime_target_time, + autocvar_g_buffs_inferno_burntime_factor + ); + Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id); + } + + // this... is ridiculous (TODO: fix!) + if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) + if(!frag_target.vehicle) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(frag_target.deadflag == DEAD_NO) + if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) + if(frag_attacker != frag_target) + if(!frag_target.frozen) + if(frag_target.takedamage) + if(DIFF_TEAM(frag_attacker, frag_target)) + { + frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); + if(frag_target.armorvalue) + frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn) +{SELFPARAM(); + self.buffs = 0; + // reset timers here to prevent them continuing after re-spawn + self.buff_disability_time = 0; + self.buff_disability_effect_time = 0; + return false; +} + +.float stat_sv_maxspeed; +.float stat_sv_airspeedlimit_nonqw; +.float stat_sv_jumpvelocity; + +MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + { + self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; + self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; + } + + if(time < self.buff_disability_time) + { + self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; + self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; + } + + if(self.buffs & BUFF_JUMP.m_itemid) + { + // automatically reset, no need to worry + self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerJump) +{SELFPARAM(); + if(self.buffs & BUFF_JUMP.m_itemid) + player_jumpheight = autocvar_g_buffs_jump_height; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, MonsterMove) +{SELFPARAM(); + if(time < self.buff_disability_time) + { + monster_speed_walk *= autocvar_g_buffs_disability_speed; + monster_speed_run *= autocvar_g_buffs_disability_speed; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDies) +{SELFPARAM(); + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + self.buffs = 0; + + if(self.buff_model) + { + remove(self.buff_model); + self.buff_model = world; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE || gameover) { return false; } + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + + self.buffs = 0; + sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if(self.buffs & BUFF_SWAPPER.m_itemid) + { + float best_distance = autocvar_g_buffs_swapper_range; + entity closest = world; + entity player; + FOR_EACH_PLAYER(player) + if(DIFF_TEAM(self, player)) + if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle) + if(vlen(self.origin - player.origin) <= best_distance) + { + best_distance = vlen(self.origin - player.origin); + closest = player; + } + + if(closest) + { + vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; + + my_org = self.origin; + my_vel = self.velocity; + my_ang = self.angles; + their_org = closest.origin; + their_vel = closest.velocity; + their_ang = closest.angles; + + Drop_Special_Items(closest); + + MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper + + setorigin(self, their_org); + setorigin(closest, my_org); + + closest.velocity = my_vel; + closest.angles = my_ang; + closest.fixangle = true; + closest.oldorigin = my_org; + closest.oldvelocity = my_vel; + self.velocity = their_vel; + self.angles = their_ang; + self.fixangle = true; + self.oldorigin = their_org; + self.oldvelocity = their_vel; + + // set pusher so self gets the kill if they fall into void + closest.pusher = self; + closest.pushltime = time + autocvar_g_maxpushtime; + closest.istypefrag = closest.BUTTON_CHAT; + + Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); + + sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + + // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam + self.buffs = 0; + return true; + } + } + return false; +} + +bool buffs_RemovePlayer(entity player) +{ + if(player.buff_model) + { + remove(player.buff_model); + player.buff_model = world; + } + + // also reset timers here to prevent them continuing after spectating + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; + + return false; +} +MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); } +MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); } + +MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) +{SELFPARAM(); + entity e = WaypointSprite_getviewentity(other); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other)) + if(DIFF_TEAM(self.owner, e)) + return true; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) +{SELFPARAM(); + if (self.classname == "item_flight" && cvar("g_buffs") && cvar("g_buffs_flight")) + { + buff_Init_Compat(self, BUFF_FLIGHT); + return true; + } + if(autocvar_g_buffs_replace_powerups) + switch(self.classname) + { + case "item_strength": + case "item_invincible": + { + entity e = spawn(); + buff_SpawnReplacement(e, self); + return true; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + weapon_rate *= autocvar_g_buffs_speed_rate; + + if(time < self.buff_disability_time) + weapon_rate *= autocvar_g_buffs_disability_rate; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + ret_float *= autocvar_g_buffs_speed_weaponspeed; + + if(time < self.buff_disability_time) + ret_float *= autocvar_g_buffs_disability_weaponspeed; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) +{SELFPARAM(); + if(gameover || self.deadflag != DEAD_NO) { return false; } + + if(time < self.buff_disability_time) + if(time >= self.buff_disability_effect_time) + { + Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1); + self.buff_disability_effect_time = time + 0.5; + } + + // handle buff lost status + // 1: notify everyone else + // 2: notify carrier as well + int buff_lost = 0; + + if(self.buff_time) + if(time >= self.buff_time) + buff_lost = 2; + + if(self.frozen) { buff_lost = 1; } + + if(buff_lost) + { + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + if(buff_lost >= 2) + { + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? + sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + self.buffs = 0; + } + } + + if(self.buffs & BUFF_MAGNET.m_itemid) + { + vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; + for(other = world; (other = findflags(other, flags, FL_ITEM)); ) + if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax)) + { + setself(other); + other = this; + if(self.touch) + self.touch(); + other = self; + setself(this); + } + } + + if(self.buffs & BUFF_AMMO.m_itemid) + if(self.clip_size) + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + + if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid)) + if(self.alpha != autocvar_g_buffs_invisible_alpha) + self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) + +#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid)) +#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid)) + + if(self.buffs != self.oldbuffs) + { + entity buff = buff_FirstFromFlags(self.buffs); + float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; + self.buff_time = (bufftime) ? time + bufftime : 0; + + BUFF_ONADD(BUFF_AMMO) + { + self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); + self.items |= IT_UNLIMITED_WEAPON_AMMO; + + if(self.clip_load) + self.buff_ammo_prev_clipload = self.clip_load; + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + } + + BUFF_ONREM(BUFF_AMMO) + { + if(self.buff_ammo_prev_infitems) + self.items |= IT_UNLIMITED_WEAPON_AMMO; + else + self.items &= ~IT_UNLIMITED_WEAPON_AMMO; + + if(self.buff_ammo_prev_clipload) + self.clip_load = self.buff_ammo_prev_clipload; + } + + BUFF_ONADD(BUFF_INVISIBLE) + { + if(time < self.strength_finished && g_instagib) + self.alpha = autocvar_g_instagib_invis_alpha; + else + self.alpha = self.buff_invisible_prev_alpha; + self.alpha = autocvar_g_buffs_invisible_alpha; + } + + BUFF_ONREM(BUFF_INVISIBLE) + self.alpha = self.buff_invisible_prev_alpha; + + BUFF_ONADD(BUFF_FLIGHT) + { + self.buff_flight_prev_gravity = self.gravity; + self.gravity = autocvar_g_buffs_flight_gravity; + } + + BUFF_ONREM(BUFF_FLIGHT) + self.gravity = self.buff_flight_prev_gravity; + + self.oldbuffs = self.buffs; + if(self.buffs) + { + if(!self.buff_model) + buffs_BuffModel_Spawn(self); + + self.buff_model.color = buff.m_color; + self.buff_model.glowmod = buff_GlowColor(self.buff_model); + self.buff_model.skin = buff.m_skin; + + self.effects |= EF_NOSHADOW; + } + else + { + remove(self.buff_model); + self.buff_model = world; + + self.effects &= ~(EF_NOSHADOW); + } + } + + if(self.buff_model) + { + self.buff_model.effects = self.effects; + self.buff_model.effects |= EF_LOWPRECISION; + self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance + + self.buff_model.alpha = self.alpha; + } + +#undef BUFF_ONADD +#undef BUFF_ONREM + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) +{SELFPARAM(); + self.buffs = other.buffs; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) +{ + vh_vehicle.buffs = vh_player.buffs; + vh_player.buffs = 0; + vh_vehicle.buff_time = vh_player.buff_time - time; + vh_player.buff_time = 0; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleExit) +{ + vh_player.buffs = vh_player.oldbuffs = vh_vehicle.buffs; + vh_vehicle.buffs = 0; + vh_player.buff_time = time + vh_vehicle.buff_time; + vh_vehicle.buff_time = 0; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) +{SELFPARAM(); + if(self.buffs & BUFF_MEDIC.m_itemid) + { + regen_mod_rot = autocvar_g_buffs_medic_rot; + regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max; + regen_mod_regen = autocvar_g_buffs_medic_regen; + } + + if(self.buffs & BUFF_SPEED.m_itemid) + regen_mod_regen = autocvar_g_buffs_speed_regen; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace"); + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Buffs"); + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Buffs"); + return false; +} + +void buffs_DelayedInit() +{ + if(autocvar_g_buffs_spawn_count > 0) + if(find(world, classname, "item_buff") == world) + { + float i; + for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) + { + entity e = spawn(); + e.spawnflags |= 64; // always randomize + e.velocity = randomvec() * 250; // this gets reset anyway if random location works + buff_Init(e); + } + } +} +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/module.inc b/qcsrc/common/mutators/mutator/buffs/module.inc new file mode 100644 index 0000000000..5f24f627e7 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/module.inc @@ -0,0 +1,46 @@ +#include "all.qc" +#ifdef SVQC +#include "buffs.qc" +#endif + +#ifdef IMPLEMENTATION + +string BUFF_NAME(int i) +{ + Buff b = Buffs_from(i); + return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName); +} + +#ifndef MENUQC +REGISTER_MUTATOR(buffs_flight, true); +MUTATOR_HOOKFUNCTION(buffs_flight, IsFlying) +{ + noref entity e = MUTATOR_ARGV(0, entity); + return BUFFS_STAT(e) & BUFF_FLIGHT.m_itemid; +} +#endif + +#ifdef CSQC +REGISTER_MUTATOR(cl_buffs, true); +MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) +{ + int allBuffs = getstati(STAT_BUFFS, 0, 24); + FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( + addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, getstatf(STAT_BUFF_TIME) - time, 99), 60); + )); +} +MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) +{ + entity this = MUTATOR_ARGV(0, entity); + string s = MUTATOR_ARGV(0, string); + if (s == WP_Buff.netname || s == RADARICON_Buff.netname) + { + Buff b = Buffs_from(this.wp_extra); + MUTATOR_ARGV(0, vector) = b.m_color; + MUTATOR_ARGV(0, string) = b.m_prettyName; + return true; + } +} + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc new file mode 100644 index 0000000000..0ba0bb64e1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc @@ -0,0 +1,90 @@ +#ifdef IMPLEMENTATION +float autocvar_g_campcheck_damage; +float autocvar_g_campcheck_distance; +float autocvar_g_campcheck_interval; + +REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); + +.float campcheck_nextcheck; +.float campcheck_traveled_distance; + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) +{SELFPARAM(); + Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK); + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) +{ + if(IS_PLAYER(frag_target)) + if(IS_PLAYER(frag_attacker)) + if(frag_attacker != frag_target) + { + frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; + frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) +{SELFPARAM(); + if(!gameover) + if(!warmup_stage) // don't consider it camping during warmup? + if(time >= game_starttime) + if(IS_PLAYER(self)) + if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them + if(self.deadflag == DEAD_NO) + if(!self.frozen) + if(!self.BUTTON_CHAT) + if(autocvar_g_campcheck_interval) + { + vector dist; + + // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) + dist = self.prevorigin - self.origin; + dist.z = 0; + self.campcheck_traveled_distance += fabs(vlen(dist)); + + if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + { + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + self.campcheck_traveled_distance = 0; + } + + if(time > self.campcheck_nextcheck) + { + if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK); + if(self.vehicle) + Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0'); + else + Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0'); + } + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; + self.campcheck_traveled_distance = 0; + } + + return false; + } + + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) +{SELFPARAM(); + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + self.campcheck_traveled_distance = 0; + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":CampCheck"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/module.inc b/qcsrc/common/mutators/mutator/campcheck/module.inc new file mode 100644 index 0000000000..9f4181f163 --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "campcheck.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/casings.qc b/qcsrc/common/mutators/mutator/casings.qc deleted file mode 100644 index fe945c9ea0..0000000000 --- a/qcsrc/common/mutators/mutator/casings.qc +++ /dev/null @@ -1,180 +0,0 @@ -#ifdef IMPLEMENTATION - -#include "../../util.qh" - -#ifdef CSQC -#include "../../movetypes/movetypes.qh" -#include "../../../client/rubble.qh" -#endif - -REGISTER_MUTATOR(casings, true); - -#ifdef SVQC -void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner) -{SELFPARAM(); - vector org = self.origin + self.view_ofs + self.weaponentity.spawnorigin.x * v_forward - self.weaponentity.spawnorigin.y * v_right + self.weaponentity.spawnorigin.z * v_up; - - if (!sound_allowed(MSG_BROADCAST, casingowner)) - casingtype |= 0x80; - - WriteByte(MSG_ALL, SVC_TEMPENTITY); - WriteMutator(MSG_ALL, casings); - WriteByte(MSG_ALL, casingtype); - WriteCoord(MSG_ALL, org.x); - WriteCoord(MSG_ALL, org.y); - WriteCoord(MSG_ALL, org.z); - WriteShort(MSG_ALL, compressShortVector(vel)); // actually compressed velocity - WriteByte(MSG_ALL, ang.x * 256 / 360); - WriteByte(MSG_ALL, ang.y * 256 / 360); - WriteByte(MSG_ALL, ang.z * 256 / 360); -} -#endif - -#ifdef CSQC -entityclass(Casing); -class(Casing) .float alpha; -class(Casing) .bool silent; -class(Casing) .int state; -class(Casing) .float cnt; - -void Casing_Delete() -{SELFPARAM(); - remove(self); -} - -void Casing_Draw(entity this) -{ - if (self.move_flags & FL_ONGROUND) - { - self.move_angles_x = 0; - self.move_angles_z = 0; - self.flags &= ~FL_ONGROUND; - } - - Movetype_Physics_MatchTicrate(autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy); - if (wasfreed(self)) - return; // deleted by touch function - - self.renderflags = 0; - self.alpha = bound(0, self.cnt - time, 1); - - if (self.alpha < ALPHA_MIN_VISIBLE) - { - Casing_Delete(); - self.drawmask = 0; - } -} - -SOUND(BRASS1, W_Sound("brass1")); -SOUND(BRASS2, W_Sound("brass2")); -SOUND(BRASS3, W_Sound("brass3")); -Sound SND_BRASS_RANDOM() { - return Sounds[SND_BRASS1.m_id + floor(prandom() * 3)]; -} -SOUND(CASINGS1, W_Sound("casings1")); -SOUND(CASINGS2, W_Sound("casings2")); -SOUND(CASINGS3, W_Sound("casings3")); -Sound SND_CASINGS_RANDOM() { - return Sounds[SND_CASINGS1.m_id + floor(prandom() * 3)]; -} - -void Casing_Touch() -{SELFPARAM(); - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { - Casing_Delete(); - return; - } - - if (!self.silent) - if (!trace_ent || trace_ent.solid == SOLID_BSP) - { - if (vlen(self.velocity) > 50) - { - if (time >= self.nextthink) - { - Sound s; - switch (self.state) - { - case 1: - s = SND_CASINGS_RANDOM(); - break; - default: - s = SND_BRASS_RANDOM(); - break; - } - - sound (self, CH_SHOTS, s, VOL_BASE, ATTEN_LARGE); - } - } - } - - self.nextthink = time + 0.2; -} - -void Casing_Damage(float thisdmg, int hittype, vector org, vector thisforce) -{SELFPARAM(); - if (thisforce.z < 0) - thisforce.z = 0; - self.move_velocity = self.move_velocity + thisforce + '0 0 100'; - self.move_flags &= ~FL_ONGROUND; -} - -MUTATOR_HOOKFUNCTION(casings, CSQC_Parse_TempEntity) -{ - if (MUTATOR_RETURNVALUE) return; - if (!ReadMutatorEquals(mutator_argv_int_0, casings)) return; - return = true; - - int _state = ReadByte(); - vector org; - org_x = ReadCoord(); - org_y = ReadCoord(); - org_z = ReadCoord(); - vector vel = decompressShortVector(ReadShort()); - vector ang; - ang_x = ReadByte() * 360 / 256; - ang_y = ReadByte() * 360 / 256; - ang_z = ReadByte() * 360 / 256; - - if (!autocvar_cl_casings) return; - - Casing casing = RubbleNew("casing"); - casing.silent = (_state & 0x80); - casing.state = (_state & 0x7F); - casing.origin = org; - setorigin(casing, casing.origin); - casing.velocity = vel; - casing.angles = ang; - casing.drawmask = MASK_NORMAL; - - casing.draw = Casing_Draw; - casing.move_origin = casing.origin; - casing.move_velocity = casing.velocity + 2 * prandomvec(); - casing.move_angles = casing.angles; - casing.move_avelocity = '0 250 0' + 100 * prandomvec(); - casing.move_movetype = MOVETYPE_BOUNCE; - casing.move_touch = Casing_Touch; - casing.move_time = time; - casing.event_damage = Casing_Damage; - casing.solid = SOLID_TRIGGER; - - switch (casing.state) - { - case 1: - setmodel(casing, MDL_CASING_SHELL); - casing.cnt = time + autocvar_cl_casings_shell_time; - break; - default: - setmodel(casing, MDL_CASING_BULLET); - casing.cnt = time + autocvar_cl_casings_bronze_time; - break; - } - - setsize(casing, '0 0 -1', '0 0 -1'); - - RubbleLimit("casing", autocvar_cl_casings_maxcount, Casing_Delete); -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/cloaked.qc new file mode 100644 index 0000000000..f8231f784e --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/cloaked.qc @@ -0,0 +1,19 @@ +#ifdef IMPLEMENTATION + +REGISTER_MUTATOR(cloaked, cvar("g_cloaked")); + +float autocvar_g_balance_cloaked_alpha; + +MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha) +{ + default_player_alpha = autocvar_g_balance_cloaked_alpha; + default_weapon_alpha = default_player_alpha; + return true; +} + +MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString) +{ + if (!g_cts) ret_string = strcat(ret_string, ", Cloaked"); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/module.inc b/qcsrc/common/mutators/mutator/cloaked/module.inc new file mode 100644 index 0000000000..3d3a042936 --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "cloaked.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext.qc deleted file mode 100644 index f6e9d5a471..0000000000 --- a/qcsrc/common/mutators/mutator/damagetext.qc +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef MUTATOR_DAMAGETEXT_H -#define MUTATOR_DAMAGETEXT_H - -#ifdef MENUQC -#include "../../../menu/xonotic/tab.qc" -#endif - -#endif - -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(damagetext, true); - -#if defined(CSQC) || defined(MENUQC) -AUTOCVAR_SAVE(cl_damagetext, bool, false, _("Draw damage dealt. 0: disabled, 1: enabled")); -AUTOCVAR_SAVE(cl_damagetext_format, string, "-%3$d", _("How to format the damage text. 1$ is health, 2$ is armor, 3$ is both")); -AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', _("Default damage text color")); -AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, _("Damage text uses weapon color")); -AUTOCVAR_SAVE(cl_damagetext_size, float, 8, _("Damage text font size")); -AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, _("Damage text initial alpha")); -AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, _("Damage text lifetime in seconds")); -AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', _("Damage text move direction")); -AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', _("Damage text offset")); -AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, _("Damage text spawned within this range is accumulated")); -#endif - -#ifdef CSQC -CLASS(DamageText, Object) - ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color) - ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size) - ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start) - ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime) - ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity) - ATTRIB(DamageText, m_group, int, 0) - ATTRIB(DamageText, m_damage, int, 0) - ATTRIB(DamageText, m_armordamage, int, 0) - ATTRIB(DamageText, m_deathtype, int, 0) - ATTRIB(DamageText, time_prev, float, time) - - void DamageText_draw2d(DamageText this) { - float dt = time - this.time_prev; - this.time_prev = time; - setorigin(this, this.origin + dt * this.velocity); - this.alpha -= dt * this.fade_rate; - if (this.alpha < 0) remove(this); - vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset; - if (pos.z >= 0 && this.m_size > 0) { - pos.z = 0; - vector rgb = this.m_color; - if (autocvar_cl_damagetext_color_per_weapon) { - Weapon w = DEATH_WEAPONOF(this.m_deathtype); - if (w != WEP_Null) rgb = w.wpcolor; - } - string s = sprintf(autocvar_cl_damagetext_format, this.m_damage, this.m_armordamage, this.m_damage + this.m_armordamage); - drawcolorcodedstring2(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL); - } - } - ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d) - - void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) { - this.m_damage = _health; - this.m_armordamage = _armor; - this.m_deathtype = _deathtype; - setorigin(this, _origin); - this.alpha = 1; - } - - CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) { - CONSTRUCT(DamageText); - this.m_group = _group; - DamageText_update(this, _origin, _health, _armor, _deathtype); - } -ENDCLASS(DamageText) -#endif - -#ifdef SVQC -AUTOCVAR(sv_damagetext, int, 2, _("<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players")); -#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 /* disabled */) -#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */) -#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2 /* players */) -#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3 /* all players */) -MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) { - if (SV_DAMAGETEXT_DISABLED()) return; - const entity attacker = mutator_argv_entity_0; - const entity hit = mutator_argv_entity_1; if (hit == attacker) return; - const int health = mutator_argv_int_0; - const int armor = mutator_argv_int_1; - const int deathtype = mutator_argv_int_2; - const vector location = hit.origin; - entity e; - FOR_EACH_REALCLIENT(e) if ( - (SV_DAMAGETEXT_ALL()) || - (SV_DAMAGETEXT_PLAYERS() && e == attacker) || - (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(e) && e.enemy == attacker) || - (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(e)) - ) { - msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteMutator(MSG_ONE, damagetext); - WriteShort(MSG_ONE, health); - WriteShort(MSG_ONE, armor); - WriteEntity(MSG_ONE, hit); - WriteCoord(MSG_ONE, location.x); - WriteCoord(MSG_ONE, location.y); - WriteCoord(MSG_ONE, location.z); - WriteInt24_t(MSG_ONE, deathtype); - } -} -#endif - -#ifdef CSQC -MUTATOR_HOOKFUNCTION(damagetext, CSQC_Parse_TempEntity) { - if (MUTATOR_RETURNVALUE) return false; - if (!ReadMutatorEquals(mutator_argv_int_0, damagetext)) return false; - int health = ReadShort(); - int armor = ReadShort(); - int group = ReadShort(); - vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord()); - int deathtype = ReadInt24_t(); - if (autocvar_cl_damagetext) { - if (autocvar_cl_damagetext_accumulate_range) { - for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) { - if (e.instanceOfDamageText && e.m_group == group) { - DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype); - return true; - } - } - } - NEW(DamageText, group, location, health, armor, deathtype); - } - return true; -} -#endif - -#ifdef MENUQC -CLASS(XonoticDamageTextSettings, XonoticTab) - #include "../../../menu/gamesettings.qh" - REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings)); - ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text")) - ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9) - ATTRIB(XonoticDamageTextSettings, rows, float, 13) - ATTRIB(XonoticDamageTextSettings, columns, float, 5) - INIT(XonoticDamageTextSettings) { this.configureDialog(this); } - METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); } - METHOD(XonoticDamageTextSettings, fill, void(entity this)) - { - this.gotoRC(this, 0, 1); this.setFirstColumn(this, this.currentColumn); - this.TD(this, 1, 3, makeXonoticCheckBox(0, "cl_damagetext", _("Draw damage numbers"))); - this.TR(this); - this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Font size:"))); - this.TD(this, 1, 2, makeXonoticSlider(0, 50, 1, "cl_damagetext_size")); - this.TR(this); - this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Accumulate range:"))); - this.TD(this, 1, 2, makeXonoticSlider(0, 500, 1, "cl_damagetext_accumulate_range")); - this.TR(this); - this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Lifetime:"))); - this.TD(this, 1, 2, makeXonoticSlider(0, 10, 1, "cl_damagetext_alpha_lifetime")); - this.TR(this); - this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Color:"))); - this.TD(this, 2, 2, makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color")); - } -ENDCLASS(XonoticDamageTextSettings) -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc new file mode 100644 index 0000000000..923ca3bcc7 --- /dev/null +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc @@ -0,0 +1,163 @@ +#ifndef MUTATOR_DAMAGETEXT_H +#define MUTATOR_DAMAGETEXT_H + +#ifdef MENUQC +#include "../../../../menu/xonotic/tab.qc" +#endif + +#endif + +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(damagetext, true); + +#if defined(CSQC) || defined(MENUQC) +AUTOCVAR_SAVE(cl_damagetext, bool, false, _("Draw damage dealt. 0: disabled, 1: enabled")); +AUTOCVAR_SAVE(cl_damagetext_format, string, "-%3$d", _("How to format the damage text. 1$ is health, 2$ is armor, 3$ is both")); +AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', _("Default damage text color")); +AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, _("Damage text uses weapon color")); +AUTOCVAR_SAVE(cl_damagetext_size, float, 8, _("Damage text font size")); +AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, _("Damage text initial alpha")); +AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, _("Damage text lifetime in seconds")); +AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', _("Damage text move direction")); +AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', _("Damage text offset")); +AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, _("Damage text spawned within this range is accumulated")); +#endif + +#ifdef CSQC +CLASS(DamageText, Object) + ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color) + ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size) + ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start) + ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime) + ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity) + ATTRIB(DamageText, m_group, int, 0) + ATTRIB(DamageText, m_damage, int, 0) + ATTRIB(DamageText, m_armordamage, int, 0) + ATTRIB(DamageText, m_deathtype, int, 0) + ATTRIB(DamageText, time_prev, float, time) + + void DamageText_draw2d(DamageText this) { + float dt = time - this.time_prev; + this.time_prev = time; + setorigin(this, this.origin + dt * this.velocity); + this.alpha -= dt * this.fade_rate; + if (this.alpha < 0) remove(this); + vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset; + if (pos.z >= 0 && this.m_size > 0) { + pos.z = 0; + vector rgb = this.m_color; + if (autocvar_cl_damagetext_color_per_weapon) { + Weapon w = DEATH_WEAPONOF(this.m_deathtype); + if (w != WEP_Null) rgb = w.wpcolor; + } + string s = sprintf(autocvar_cl_damagetext_format, this.m_damage, this.m_armordamage, this.m_damage + this.m_armordamage); + drawcolorcodedstring2(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL); + } + } + ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d) + + void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) { + this.m_damage = _health; + this.m_armordamage = _armor; + this.m_deathtype = _deathtype; + setorigin(this, _origin); + this.alpha = 1; + } + + CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) { + CONSTRUCT(DamageText); + this.m_group = _group; + DamageText_update(this, _origin, _health, _armor, _deathtype); + } +ENDCLASS(DamageText) +#endif + +REGISTER_NET_TEMP(damagetext) + +#ifdef SVQC +AUTOCVAR(sv_damagetext, int, 2, _("<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players")); +#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 /* disabled */) +#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */) +#define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2 /* players */) +#define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3 /* all players */) +MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) { + if (SV_DAMAGETEXT_DISABLED()) return; + const entity attacker = MUTATOR_ARGV(0, entity); + const entity hit = MUTATOR_ARGV(1, entity); if (hit == attacker) return; + const int health = MUTATOR_ARGV(0, int); + const int armor = MUTATOR_ARGV(1, int); + const int deathtype = MUTATOR_ARGV(2, int); + const vector location = hit.origin; + entity e; + FOR_EACH_REALCLIENT(e) if ( + (SV_DAMAGETEXT_ALL()) || + (SV_DAMAGETEXT_PLAYERS() && e == attacker) || + (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(e) && e.enemy == attacker) || + (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(e)) + ) { + msg_entity = e; + WriteHeader(MSG_ONE, damagetext); + WriteShort(MSG_ONE, health); + WriteShort(MSG_ONE, armor); + WriteEntity(MSG_ONE, hit); + WriteCoord(MSG_ONE, location.x); + WriteCoord(MSG_ONE, location.y); + WriteCoord(MSG_ONE, location.z); + WriteInt24_t(MSG_ONE, deathtype); + } +} +#endif + +#ifdef CSQC +NET_HANDLE(damagetext, bool isNew) +{ + int health = ReadShort(); + int armor = ReadShort(); + int group = ReadShort(); + vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord()); + int deathtype = ReadInt24_t(); + return = true; + if (autocvar_cl_damagetext) { + if (autocvar_cl_damagetext_accumulate_range) { + for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) { + if (e.instanceOfDamageText && e.m_group == group) { + DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype); + return; + } + } + } + NEW(DamageText, group, location, health, armor, deathtype); + } +} +#endif + +#ifdef MENUQC +CLASS(XonoticDamageTextSettings, XonoticTab) + #include "../../../../menu/gamesettings.qh" + REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings)); + ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text")) + ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9) + ATTRIB(XonoticDamageTextSettings, rows, float, 13) + ATTRIB(XonoticDamageTextSettings, columns, float, 5) + INIT(XonoticDamageTextSettings) { this.configureDialog(this); } + METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); } + METHOD(XonoticDamageTextSettings, fill, void(entity this)) + { + this.gotoRC(this, 0, 1); this.setFirstColumn(this, this.currentColumn); + this.TD(this, 1, 3, makeXonoticCheckBox(0, "cl_damagetext", _("Draw damage numbers"))); + this.TR(this); + this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Font size:"))); + this.TD(this, 1, 2, makeXonoticSlider(0, 50, 1, "cl_damagetext_size")); + this.TR(this); + this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Accumulate range:"))); + this.TD(this, 1, 2, makeXonoticSlider(0, 500, 1, "cl_damagetext_accumulate_range")); + this.TR(this); + this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Lifetime:"))); + this.TD(this, 1, 2, makeXonoticSlider(0, 10, 1, "cl_damagetext_alpha_lifetime")); + this.TR(this); + this.TD(this, 1, 1, makeXonoticTextLabel(0, _("Color:"))); + this.TD(this, 2, 2, makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color")); + } +ENDCLASS(XonoticDamageTextSettings) +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/module.inc b/qcsrc/common/mutators/mutator/damagetext/module.inc new file mode 100644 index 0000000000..8e70be7c6d --- /dev/null +++ b/qcsrc/common/mutators/mutator/damagetext/module.inc @@ -0,0 +1 @@ +#include "damagetext.qc" diff --git a/qcsrc/common/mutators/mutator/dodging/dodging.qc b/qcsrc/common/mutators/mutator/dodging/dodging.qc new file mode 100644 index 0000000000..f014ebbc57 --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/dodging.qc @@ -0,0 +1,334 @@ +#ifdef IMPLEMENTATION + +#ifdef CSQC + #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) + #define PHYS_DODGING getstati(STAT_DODGING) + #define PHYS_DODGING_DELAY getstatf(STAT_DODGING_DELAY) + #define PHYS_DODGING_TIMEOUT(s) getstatf(STAT_DODGING_TIMEOUT) + #define PHYS_DODGING_HORIZ_SPEED_FROZEN getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN) + #define PHYS_DODGING_FROZEN_NODOUBLETAP getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP) + #define PHYS_DODGING_HORIZ_SPEED getstatf(STAT_DODGING_HORIZ_SPEED) + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD getstatf(STAT_DODGING_HEIGHT_THRESHOLD) + #define PHYS_DODGING_DISTANCE_THRESHOLD getstatf(STAT_DODGING_DISTANCE_THRESHOLD) + #define PHYS_DODGING_RAMP_TIME getstatf(STAT_DODGING_RAMP_TIME) + #define PHYS_DODGING_UP_SPEED getstatf(STAT_DODGING_UP_SPEED) + #define PHYS_DODGING_WALL getstatf(STAT_DODGING_WALL) +#elif defined(SVQC) + #define PHYS_DODGING_FRAMETIME sys_frametime + #define PHYS_DODGING g_dodging + #define PHYS_DODGING_DELAY autocvar_sv_dodging_delay + #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout + #define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen + #define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap + #define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold + #define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold + #define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time + #define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed + #define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging + + float autocvar_sv_dodging_delay; + float autocvar_sv_dodging_height_threshold; + float autocvar_sv_dodging_horiz_speed; + float autocvar_sv_dodging_horiz_speed_frozen; + float autocvar_sv_dodging_ramp_time; + bool autocvar_sv_dodging_sound; + float autocvar_sv_dodging_up_speed; + float autocvar_sv_dodging_wall_distance_threshold; + bool autocvar_sv_dodging_wall_dodging; + bool autocvar_sv_dodging_frozen_doubletap; +#endif + +#ifdef SVQC + +float g_dodging; + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + +#include "../../../animdecide.qh" +#include "../../../physics.qh" + +.float cvar_cl_dodging_timeout; + +.float stat_dodging; +.float stat_dodging_delay; +.float stat_dodging_horiz_speed_frozen; +.float stat_dodging_frozen_nodoubletap; +.float stat_dodging_frozen; +.float stat_dodging_horiz_speed; +.float stat_dodging_height_threshold; +.float stat_dodging_distance_threshold; +.float stat_dodging_ramp_time; +.float stat_dodging_up_speed; +.float stat_dodging_wall; + +REGISTER_MUTATOR(dodging, cvar("g_dodging")) +{ + // this just turns on the cvar. + MUTATOR_ONADD + { + g_dodging = cvar("g_dodging"); + addstat(STAT_DODGING, AS_INT, stat_dodging); + addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay); + addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos) + addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap); + addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen); + addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen); + addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed); + addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold); + addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold); + addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time); + addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed); + addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall); + } + + // this just turns off the cvar. + MUTATOR_ONROLLBACK_OR_REMOVE + { + g_dodging = 0; + } + + return false; +} + +#endif + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + + +// these are used to store the last key press time for each of the keys.. +.float last_FORWARD_KEY_time; +.float last_BACKWARD_KEY_time; +.float last_LEFT_KEY_time; +.float last_RIGHT_KEY_time; + +// these store the movement direction at the time of the dodge action happening. +.vector dodging_direction; + +// this indicates the last time a dodge was executed. used to check if another one is allowed +// and to ramp up the dodge acceleration in the physics hook. +.float last_dodging_time; + +// This is the velocity gain to be added over the ramp time. +// It will decrease from frame to frame during dodging_action = 1 +// until it's 0. +.float dodging_velocity_gain; + +#ifdef CSQC +.int pressedkeys; + +#elif defined(SVQC) + +void dodging_UpdateStats() +{SELFPARAM(); + self.stat_dodging = PHYS_DODGING; + self.stat_dodging_delay = PHYS_DODGING_DELAY; + self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN; + self.stat_dodging_frozen = PHYS_DODGING_FROZEN; + self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP; + self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD; + self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD; + self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME; + self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED; + self.stat_dodging_wall = PHYS_DODGING_WALL; +} + +#endif + +// returns 1 if the player is close to a wall +bool check_close_to_wall(float threshold) +{SELFPARAM(); + if (PHYS_DODGING_WALL == 0) { return false; } + + #define X(OFFSET) \ + tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \ + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \ + return true; + X(1000*v_right); + X(-1000*v_right); + X(1000*v_forward); + X(-1000*v_forward); + #undef X + + return false; +} + +bool check_close_to_ground(float threshold) +{SELFPARAM(); + return IS_ONGROUND(self) ? true : false; +} + +float PM_dodging_checkpressedkeys() +{SELFPARAM(); + if(!PHYS_DODGING) + return false; + + float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN); + float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); + + // first check if the last dodge is far enough back in time so we can dodge again + if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY) + return false; + + makevectors(self.angles); + + if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1 + && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1) + return true; + + float tap_direction_x = 0; + float tap_direction_y = 0; + float dodge_detected = 0; + + #define X(COND,BTN,RESULT) \ + if (self.movement_##COND) \ + /* is this a state change? */ \ + if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \ + tap_direction_##RESULT; \ + if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \ + dodge_detected = 1; \ + self.last_##BTN##_KEY_time = time; \ + } + X(x < 0, BACKWARD, x--); + X(x > 0, FORWARD, x++); + X(y < 0, LEFT, y--); + X(y > 0, RIGHT, y++); + #undef X + + if (dodge_detected == 1) + { + self.last_dodging_time = time; + + self.dodging_action = 1; + self.dodging_single_action = 1; + + self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; + + self.dodging_direction_x = tap_direction_x; + self.dodging_direction_y = tap_direction_y; + + // normalize the dodging_direction vector.. (unlike UT99) XD + float length = self.dodging_direction_x * self.dodging_direction_x + + self.dodging_direction_y * self.dodging_direction_y; + length = sqrt(length); + + self.dodging_direction_x = self.dodging_direction_x * 1.0 / length; + self.dodging_direction_y = self.dodging_direction_y * 1.0 / length; + return true; + } + return false; +} + +void PM_dodging() +{SELFPARAM(); + if (!PHYS_DODGING) + return; + +#ifdef SVQC + dodging_UpdateStats(); +#endif + + if (PHYS_DEAD(self)) + return; + + // when swimming, no dodging allowed.. + if (self.waterlevel >= WATERLEVEL_SWIMMING) + { + self.dodging_action = 0; + self.dodging_direction_x = 0; + self.dodging_direction_y = 0; + return; + } + + // make sure v_up, v_right and v_forward are sane + makevectors(self.angles); + + // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code + // will be called ramp_time/frametime times = 2 times. so, we need to + // add 0.5 * the total speed each frame until the dodge action is done.. + float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; + + // if ramp time is smaller than frametime we get problems ;D + common_factor = min(common_factor, 1); + + float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; + float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed); + new_velocity_gain = max(0, new_velocity_gain); + + float velocity_difference = self.dodging_velocity_gain - new_velocity_gain; + + // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D + if (self.dodging_action == 1) + { + //disable jump key during dodge accel phase + if(self.movement_z > 0) { self.movement_z = 0; } + + self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right) + + ((self.dodging_direction_x * velocity_difference) * v_forward); + + self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference; + } + + // the up part of the dodge is a single shot action + if (self.dodging_single_action == 1) + { + UNSET_ONGROUND(self); + + self.velocity += PHYS_DODGING_UP_SPEED * v_up; + +#ifdef SVQC + if (autocvar_sv_dodging_sound) + PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); + + animdecide_setaction(self, ANIMACTION_JUMP, true); +#endif + + self.dodging_single_action = 0; + } + + // are we done with the dodging ramp yet? + if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) + { + // reset state so next dodge can be done correctly + self.dodging_action = 0; + self.dodging_direction_x = 0; + self.dodging_direction_y = 0; + } +} + +#ifdef SVQC + +MUTATOR_HOOKFUNCTION(dodging, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout"); + return false; +} + +MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) +{ + // print("dodging_PlayerPhysics\n"); + PM_dodging(); + + return false; +} + +MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) +{ + PM_dodging_checkpressedkeys(); + + return false; +} + +#endif + +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/module.inc b/qcsrc/common/mutators/mutator/dodging/module.inc new file mode 100644 index 0000000000..6308a2182c --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "dodging.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/hook/hook.qc b/qcsrc/common/mutators/mutator/hook/hook.qc new file mode 100644 index 0000000000..b298e7b2ea --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/hook.qc @@ -0,0 +1,42 @@ +#ifdef IMPLEMENTATION +AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); +#ifdef SVQC +REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { + MUTATOR_ONADD { + g_grappling_hook = true; + WEP_HOOK.ammo_factor = 0; + } + MUTATOR_ONROLLBACK_OR_REMOVE { + g_grappling_hook = false; + WEP_HOOK.ammo_factor = 1; + } +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":grappling_hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) +{ + ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); +} + +MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) +{ + SELFPARAM(); + self.offhand = OFFHAND_HOOK; +} + +MUTATOR_HOOKFUNCTION(hook, FilterItem) +{ + return self.weapon == WEP_HOOK.m_id; +} + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/hook/module.inc b/qcsrc/common/mutators/mutator/hook/module.inc new file mode 100644 index 0000000000..61600c47b1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "hook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/instagib.qc b/qcsrc/common/mutators/mutator/instagib/instagib.qc index 9d5caef000..1810734c64 100644 --- a/qcsrc/common/mutators/mutator/instagib/instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/instagib.qc @@ -3,13 +3,20 @@ #include "items.qc" +#ifdef SVQC +float autocvar_g_instagib_invis_alpha; +#endif + #endif #ifdef IMPLEMENTATION #ifdef SVQC +int autocvar_g_instagib_ammo_drop; +int autocvar_g_instagib_extralives; +float autocvar_g_instagib_speed_highspeed; + #include "../../../../server/cl_client.qh" -#include "../../../buffs/all.qh" #include "../../../items/all.qc" @@ -19,25 +26,25 @@ spawnfunc(item_minst_cells) { if (!g_instagib) { remove(self); return; } if (!self.ammo_cells) self.ammo_cells = autocvar_g_instagib_ammo_drop; - StartItemA(ITEM_VaporizerCells); + StartItem(this, ITEM_VaporizerCells); } void instagib_invisibility() {SELFPARAM(); self.strength_finished = autocvar_g_balance_powerup_strength_time; - StartItemA(ITEM_Invisibility); + StartItem(this, ITEM_Invisibility); } void instagib_extralife() {SELFPARAM(); self.max_health = 1; - StartItemA(ITEM_ExtraLife); + StartItem(this, ITEM_ExtraLife); } void instagib_speed() {SELFPARAM(); self.invincible_finished = autocvar_g_balance_powerup_invincible_time; - StartItemA(ITEM_Speed); + StartItem(this, ITEM_Speed); } .float instagib_nextthink; @@ -325,7 +332,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate) frag_mirrordamage = 0; } - if((frag_target.buffs & BUFF_INVISIBLE.m_itemid) || (frag_target.items & ITEM_Invisibility.m_itemid)) + if(frag_target.alpha && frag_target.alpha < 1) yoda = 1; return false; @@ -367,6 +374,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) e.noalign = self.noalign; e.cnt = self.cnt; e.team = self.team; + e.spawnfunc_checked = true; WITH(entity, self, e, spawnfunc_item_minst_cells(e)); return true; } diff --git a/qcsrc/common/mutators/mutator/instagib/items.qc b/qcsrc/common/mutators/mutator/instagib/items.qc index 2d804f758b..a069e535f4 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qc +++ b/qcsrc/common/mutators/mutator/instagib/items.qc @@ -9,13 +9,14 @@ GETTER(float, instagib_respawntimejitter_ammo) #ifndef MENUQC MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3")); +SOUND(VaporizerCells, "misc/itempickup"); #endif REGISTER_ITEM(VaporizerCells, Ammo) { #ifndef MENUQC this.m_model = MDL_VaporizerCells_ITEM; + this.m_sound = SND_VaporizerCells; #endif - this.m_sound = "misc/itempickup.wav"; this.m_name = "Vaporizer Ammo"; this.m_icon = "ammo_supercells"; #ifdef SVQC @@ -28,13 +29,14 @@ REGISTER_ITEM(VaporizerCells, Ammo) { #ifndef MENUQC MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3")); +SOUND(ExtraLife, "misc/megahealth"); #endif REGISTER_ITEM(ExtraLife, Powerup) { #ifndef MENUQC this.m_model = MDL_ExtraLife_ITEM; + this.m_sound = SND_ExtraLife; #endif - this.m_sound = "misc/megahealth.wav"; this.m_name = "Extra life"; this.m_icon = "item_mega_health"; this.m_color = '1 0 0'; @@ -45,13 +47,14 @@ REGISTER_ITEM(ExtraLife, Powerup) { #ifndef MENUQC MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); +SOUND(Invisibility, "misc/powerup"); #endif REGISTER_ITEM(Invisibility, Powerup) { #ifndef MENUQC this.m_model = MDL_Invisibility_ITEM; + this.m_sound = SND_Invisibility; #endif - this.m_sound = "misc/powerup.wav"; this.m_name = "Invisibility"; this.m_icon = "strength"; this.m_color = '0 0 1'; @@ -62,13 +65,14 @@ REGISTER_ITEM(Invisibility, Powerup) { #ifndef MENUQC MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); +SOUND(Speed, "misc/powerup_shield"); #endif REGISTER_ITEM(Speed, Powerup) { #ifndef MENUQC this.m_model = MDL_Speed_ITEM; + this.m_sound = SND_Speed; #endif - this.m_sound = "misc/powerup_shield.wav"; this.m_name = "Speed"; this.m_icon = "shield"; this.m_color = '1 0 1'; diff --git a/qcsrc/common/mutators/mutator/instagib/module.inc b/qcsrc/common/mutators/mutator/instagib/module.inc new file mode 100644 index 0000000000..7270067d35 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/module.inc @@ -0,0 +1 @@ +#include "instagib.qc" diff --git a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc new file mode 100644 index 0000000000..5a781a8812 --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc @@ -0,0 +1,25 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) +{ + if(other.health) + { + // disable health which in effect disables damage calculations + other.health = 0; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":InvincibleProjectiles"); + return 0; +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Invincible Projectiles"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/module.inc b/qcsrc/common/mutators/mutator/invincibleproj/module.inc new file mode 100644 index 0000000000..61d038350f --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "invincibleproj.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime.qc index 170862ad30..d6b256af3c 100644 --- a/qcsrc/common/mutators/mutator/itemstime.qc +++ b/qcsrc/common/mutators/mutator/itemstime.qc @@ -1,12 +1,13 @@ #ifdef IMPLEMENTATION REGISTER_MUTATOR(itemstime, true); +REGISTER_NET_TEMP(itemstime) + #ifdef SVQC void IT_Write(entity e, int i, float f) { if (!IS_REAL_CLIENT(e)) return; msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteMutator(MSG_ONE, itemstime); + WriteHeader(MSG_ONE, itemstime); WriteByte(MSG_ONE, i); WriteFloat(MSG_ONE, f); } @@ -15,13 +16,12 @@ void IT_Write(entity e, int i, float f) { #ifdef CSQC float ItemsTime_time[Items_MAX]; float ItemsTime_availableTime[Items_MAX]; -MUTATOR_HOOKFUNCTION(itemstime, CSQC_Parse_TempEntity) { - if (MUTATOR_RETURNVALUE) return false; - if (!ReadMutatorEquals(mutator_argv_int_0, itemstime)) return false; +NET_HANDLE(itemstime, bool isNew) +{ int i = ReadByte(); float f = ReadFloat(); + return = true; ItemsTime_time[i] = f; - return true; } #endif @@ -102,7 +102,10 @@ void Item_ItemsTime_SetTime(entity e, float t) return; GameItem item = e.itemdef; - it_times[item.m_id] = t; + if (item.instanceOfGameItem && !item.instanceOfWeaponPickup) + { + it_times[item.m_id] = t; + } } void Item_ItemsTime_SetTimesForAllPlayers() diff --git a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc new file mode 100644 index 0000000000..5b03f46ec1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc @@ -0,0 +1,40 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); + +MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) +{ + start_ammo_shells = warmup_start_ammo_shells = 0; + start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); + + return false; +} + +MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, FilterItem) +{SELFPARAM(); + switch (self.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_ArmorSmall.m_itemid: + return false; + } + + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":MeleeOnly"); + return false; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Melee Only Arena"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/module.inc b/qcsrc/common/mutators/mutator/melee_only/module.inc new file mode 100644 index 0000000000..c711556ccf --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "melee_only.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/midair/midair.qc b/qcsrc/common/mutators/mutator/midair/midair.qc new file mode 100644 index 0000000000..aaae006f52 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/midair.qc @@ -0,0 +1,50 @@ +#ifdef IMPLEMENTATION + +float autocvar_g_midair_shieldtime; + +REGISTER_MUTATOR(midair, cvar("g_midair")); + +.float midair_shieldtime; + +MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) +{SELFPARAM(); + if(IS_PLAYER(frag_attacker)) + if(IS_PLAYER(frag_target)) + if(time < self.midair_shieldtime) + frag_damage = false; + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) +{SELFPARAM(); + if(time >= game_starttime) + if(self.flags & FL_ONGROUND) + { + self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) +{SELFPARAM(); + if(IS_BOT_CLIENT(self)) + self.bot_moveskill = 0; // disable bunnyhopping + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":midair"); + return false; +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Midair"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/midair/module.inc b/qcsrc/common/mutators/mutator/midair/module.inc new file mode 100644 index 0000000000..10b1789159 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "midair.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/multijump/module.inc b/qcsrc/common/mutators/mutator/multijump/module.inc new file mode 100644 index 0000000000..3103320990 --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/module.inc @@ -0,0 +1,3 @@ +#ifndef MENUQC +#include "multijump.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qc b/qcsrc/common/mutators/mutator/multijump/multijump.qc new file mode 100644 index 0000000000..00e22af723 --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qc @@ -0,0 +1,187 @@ +#ifdef IMPLEMENTATION +#ifdef SVQC + #include "../../../../server/antilag.qh" +#endif +#include "../../../physics.qh" + +.int multijump_count; +.bool multijump_ready; +.bool cvar_cl_multijump; + +#ifdef CSQC + +#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP) +#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED) +#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD) +#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED) +#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING) + +#elif defined(SVQC) + +int autocvar_g_multijump; +float autocvar_g_multijump_add; +float autocvar_g_multijump_speed; +float autocvar_g_multijump_maxspeed; +float autocvar_g_multijump_dodging = 1; + +#define PHYS_MULTIJUMP autocvar_g_multijump +#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed +#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add +#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed +#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging + +.float stat_multijump; +.float stat_multijump_speed; +.float stat_multijump_add; +.float stat_multijump_maxspeed; +.float stat_multijump_dodging; + +void multijump_UpdateStats() +{SELFPARAM(); + self.stat_multijump = PHYS_MULTIJUMP; + self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED; + self.stat_multijump_add = PHYS_MULTIJUMP_ADD; + self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED; + self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING; +} + +void multijump_AddStats() +{ + addstat(STAT_MULTIJUMP, AS_INT, stat_multijump); + addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed); + addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add); + addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed); + addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging); +} + +#endif + +void PM_multijump() +{SELFPARAM(); + if(!PHYS_MULTIJUMP) { return; } + + if(IS_ONGROUND(self)) + { + self.multijump_count = 0; + } +} + +bool PM_multijump_checkjump() +{SELFPARAM(); + if(!PHYS_MULTIJUMP) { return false; } + +#ifdef SVQC + bool client_multijump = self.cvar_cl_multijump; +#elif defined(CSQC) + bool client_multijump = cvar("cl_multijump"); + + if(cvar("cl_multijump") > 1) + return false; // nope +#endif + + if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair + self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again + else + self.multijump_ready = false; + + int phys_multijump = PHYS_MULTIJUMP; + +#ifdef CSQC + phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0; +#endif + + if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED)) + { + if (PHYS_MULTIJUMP) + { + if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity + { + if (self.velocity_z < PHYS_JUMPVELOCITY) + { + player_multijump = true; + self.velocity_z = 0; + } + } + else + player_multijump = true; + + if(player_multijump) + { + if(PHYS_MULTIJUMP_DODGING) + if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys + { + float curspeed; + vector wishvel, wishdir; + +/*#ifdef SVQC + curspeed = max( + vlen(vec2(self.velocity)), // current xy speed + vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs + ); +#elif defined(CSQC)*/ + curspeed = vlen(vec2(self.velocity)); +//#endif + + makevectors(self.v_angle_y * '0 1 0'); + wishvel = v_forward * self.movement_x + v_right * self.movement_y; + wishdir = normalize(wishvel); + + self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump + self.velocity_y = wishdir_y * curspeed; + // keep velocity_z unchanged! + } + if (PHYS_MULTIJUMP > 0) + { + self.multijump_count += 1; + } + } + } + self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump + } + + return false; +} + +#ifdef SVQC +REGISTER_MUTATOR(multijump, cvar("g_multijump")) +{ + MUTATOR_ONADD + { + multijump_AddStats(); + } + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics) +{ + multijump_UpdateStats(); + PM_multijump(); + + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, PlayerJump) +{ + return PM_multijump_checkjump(); +} + +MUTATOR_HOOKFUNCTION(multijump, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump"); + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":multijump"); + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Multi jump"); + return false; +} + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/nades/effects.inc b/qcsrc/common/mutators/mutator/nades/effects.inc new file mode 100644 index 0000000000..cde0016f1b --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/effects.inc @@ -0,0 +1,47 @@ +EFFECT(0, NADE_EXPLODE_RED, "nade_red_explode") +EFFECT(0, NADE_EXPLODE_BLUE, "nade_blue_explode") +EFFECT(0, NADE_EXPLODE_YELLOW, "nade_yellow_explode") +EFFECT(0, NADE_EXPLODE_PINK, "nade_pink_explode") +EFFECT(0, NADE_EXPLODE_NEUTRAL, "nade_neutral_explode") +entity EFFECT_NADE_EXPLODE(int teamid) +{ + switch (teamid) { + case NUM_TEAM_1: return EFFECT_NADE_EXPLODE_RED; + case NUM_TEAM_2: return EFFECT_NADE_EXPLODE_BLUE; + case NUM_TEAM_3: return EFFECT_NADE_EXPLODE_YELLOW; + case NUM_TEAM_4: return EFFECT_NADE_EXPLODE_PINK; + default: return EFFECT_NADE_EXPLODE_NEUTRAL; + } +} + +EFFECT(1, NADE_TRAIL_RED, "nade_red") +EFFECT(1, NADE_TRAIL_BLUE, "nade_blue") +EFFECT(1, NADE_TRAIL_YELLOW, "nade_yellow") +EFFECT(1, NADE_TRAIL_PINK, "nade_pink") +EFFECT(1, NADE_TRAIL_NEUTRAL, "nade_neutral") +entity EFFECT_NADE_TRAIL(int teamid) +{ + switch (teamid) { + case NUM_TEAM_1: return EFFECT_NADE_TRAIL_RED; + case NUM_TEAM_2: return EFFECT_NADE_TRAIL_BLUE; + case NUM_TEAM_3: return EFFECT_NADE_TRAIL_YELLOW; + case NUM_TEAM_4: return EFFECT_NADE_TRAIL_PINK; + default: return EFFECT_NADE_TRAIL_NEUTRAL; + } +} + +EFFECT(1, NADE_TRAIL_BURN_RED, "nade_red_burn") +EFFECT(1, NADE_TRAIL_BURN_BLUE, "nade_blue_burn") +EFFECT(1, NADE_TRAIL_BURN_YELLOW, "nade_yellow_burn") +EFFECT(1, NADE_TRAIL_BURN_PINK, "nade_pink_burn") +EFFECT(1, NADE_TRAIL_BURN_NEUTRAL, "nade_neutral_burn") +entity EFFECT_NADE_TRAIL_BURN(int teamid) +{ + switch (teamid) { + case NUM_TEAM_1: return EFFECT_NADE_TRAIL_BURN_RED; + case NUM_TEAM_2: return EFFECT_NADE_TRAIL_BURN_BLUE; + case NUM_TEAM_3: return EFFECT_NADE_TRAIL_BURN_YELLOW; + case NUM_TEAM_4: return EFFECT_NADE_TRAIL_BURN_PINK; + default: return EFFECT_NADE_TRAIL_BURN_NEUTRAL; + } +} diff --git a/qcsrc/common/mutators/mutator/nades/module.inc b/qcsrc/common/mutators/mutator/nades/module.inc new file mode 100644 index 0000000000..e03900d6a5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/module.inc @@ -0,0 +1,4 @@ +#include "nades.qc" +#ifndef MENUQC +#include "net.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.inc b/qcsrc/common/mutators/mutator/nades/nades.inc new file mode 100644 index 0000000000..6d16fd1509 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/nades.inc @@ -0,0 +1,62 @@ +#ifndef MENUQC +#define NADE_PROJECTILE(i, projectile, trail) do { \ + this.m_projectile[i] = projectile; \ + this.m_trail[i] = trail; \ +} while (0) +#else +#define NADE_PROJECTILE(i, projectile, trail) +#endif + +REGISTER_NADE(NORMAL) { + this.m_color = '1 1 1'; + NADE_PROJECTILE(0, PROJECTILE_NADE, EFFECT_Null); + NADE_PROJECTILE(1, PROJECTILE_NADE_BURN, EFFECT_Null); +} + +REGISTER_NADE(NAPALM) { + this.m_color = '2 0.5 0'; + this.m_name = _("Napalm grenade"); + this.m_icon = "nade_napalm"; + NADE_PROJECTILE(0, PROJECTILE_NADE_NAPALM, EFFECT_TR_ROCKET); + NADE_PROJECTILE(1, PROJECTILE_NADE_NAPALM_BURN, EFFECT_SPIDERBOT_ROCKET_TRAIL); +} + +REGISTER_NADE(ICE) { + this.m_color = '0 0.5 2'; + this.m_name = _("Ice grenade"); + this.m_icon = "nade_ice"; + NADE_PROJECTILE(0, PROJECTILE_NADE_ICE, EFFECT_TR_NEXUIZPLASMA); + NADE_PROJECTILE(1, PROJECTILE_NADE_ICE_BURN, EFFECT_RACER_ROCKET_TRAIL); +} + +REGISTER_NADE(TRANSLOCATE) { + this.m_color = '1 0 1'; + this.m_name = _("Translocate grenade"); + this.m_icon = "nade_translocate"; + NADE_PROJECTILE(0, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA); + NADE_PROJECTILE(1, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA); +} + +REGISTER_NADE(SPAWN) { + this.m_color = '1 0.9 0'; + this.m_name = _("Spawn grenade"); + this.m_icon = "nade_spawn"; + NADE_PROJECTILE(0, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW); + NADE_PROJECTILE(1, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW); +} + +REGISTER_NADE(HEAL) { + this.m_color = '1 0 0'; + this.m_name = _("Heal grenade"); + this.m_icon = "nade_heal"; + NADE_PROJECTILE(0, PROJECTILE_NADE_HEAL, EFFECT_NADE_TRAIL_RED); + NADE_PROJECTILE(1, PROJECTILE_NADE_HEAL_BURN, EFFECT_NADE_TRAIL_BURN_RED); +} + +REGISTER_NADE(MONSTER) { + this.m_color = '0.25 0.75 0'; + this.m_name = _("Monster grenade"); + this.m_icon = "nade_monster"; + NADE_PROJECTILE(0, PROJECTILE_NADE_MONSTER, EFFECT_NADE_TRAIL_RED); + NADE_PROJECTILE(1, PROJECTILE_NADE_MONSTER_BURN, EFFECT_NADE_TRAIL_BURN_RED); +} diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc new file mode 100644 index 0000000000..ef021bbdcb --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -0,0 +1,1323 @@ +#include "nades.qh" + +#ifdef IMPLEMENTATION + +#ifndef MENUQC +entity Nade_TrailEffect(int proj, int nade_team) +{ + switch (proj) + { + case PROJECTILE_NADE: return EFFECT_NADE_TRAIL(nade_team); + case PROJECTILE_NADE_BURN: return EFFECT_NADE_TRAIL_BURN(nade_team); + } + + FOREACH(Nades, true, LAMBDA( + for (int j = 0; j < 2; j++) + { + if (it.m_projectile[j] == proj) + { + string trail = it.m_trail[j].eent_eff_name; + if (trail) return it.m_trail[j]; + break; + } + } + )); + + return EFFECT_Null; +} +#endif + +#ifdef CSQC +REGISTER_MUTATOR(cl_nades, true); +MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay) +{ + if (getstatf(STAT_HEALING_ORB) <= time) return false; + MUTATOR_ARGV(0, vector) = NADE_TYPE_HEAL.m_color; + MUTATOR_ARGV(0, float) = getstatf(STAT_HEALING_ORB_ALPHA); + return true; +} +MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile) +{ + if (self.cnt == PROJECTILE_NAPALM_FOUNTAIN) + { + self.modelindex = 0; + self.traileffect = EFFECT_FIREBALL.m_id; + return true; + } + if (Nade_FromProjectile(self.cnt) != NADE_TYPE_Null) + { + setmodel(self, MDL_PROJECTILE_NADE); + entity trail = Nade_TrailEffect(self.cnt, self.team); + if (trail.eent_eff_name) self.traileffect = trail.m_id; + return true; + } +} +MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile) +{ + if (self.cnt == PROJECTILE_NAPALM_FOUNTAIN) + { + loopsound(self, CH_SHOTS_SINGLE, SND(FIREBALL_FLY2), VOL_BASE, ATTEN_NORM); + self.mins = '-16 -16 -16'; + self.maxs = '16 16 16'; + } + + entity nade_type = Nade_FromProjectile(self.cnt); + if (nade_type == NADE_TYPE_Null) return; + self.mins = '-16 -16 -16'; + self.maxs = '16 16 16'; + self.colormod = nade_type.m_color; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.scale = 1.5; + self.avelocity = randomvec() * 720; + + if (nade_type == NADE_TYPE_TRANSLOCATE || nade_type == NADE_TYPE_SPAWN) + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + else + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; +} +bool Projectile_isnade(int p) +{ + return Nade_FromProjectile(p) != NADE_TYPE_Null; +} +void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time) +{ + float bonusNades = getstatf(STAT_NADE_BONUS); + float bonusProgress = getstatf(STAT_NADE_BONUS_SCORE); + float bonusType = getstati(STAT_NADE_BONUS_TYPE); + Nade def = Nades_from(bonusType); + vector nadeColor = def.m_color; + string nadeIcon = def.m_icon; + + vector iconPos, textPos; + + if(autocvar_hud_panel_ammo_iconalign) + { + iconPos = myPos + eX * 2 * mySize.y; + textPos = myPos; + } + else + { + iconPos = myPos; + textPos = myPos + eX * mySize.y; + } + + if(bonusNades > 0 || bonusProgress > 0) + { + DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor); + + if(autocvar_hud_panel_ammo_text) + drawstring_aspect(textPos, ftos(bonusNades), eX * (2/3) * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + if(draw_expanding) + drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL, expand_time); + + drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } +} +#endif + +#ifdef SVQC + +#include "../../../gamemodes/all.qh" +#include "../../../monsters/spawn.qh" +#include "../../../monsters/sv_monsters.qh" +#include "../../../../server/g_subs.qh" + +REGISTER_MUTATOR(nades, cvar("g_nades")) +{ + MUTATOR_ONADD + { + addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer); + addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades); + addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type); + addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score); + addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb); + addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha); + } + + return false; +} + +.float nade_time_primed; + +.entity nade_spawnloc; + +void nade_timer_think() +{SELFPARAM(); + self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10); + self.nextthink = time; + if(!self.owner || wasfreed(self.owner)) + remove(self); +} + +void nade_burn_spawn(entity _nade) +{ + CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[true], true); +} + +void nade_spawn(entity _nade) +{ + entity timer = new(nade_timer); + setmodel(timer, MDL_NADE_TIMER); + setattachment(timer, _nade, ""); + timer.colormap = _nade.colormap; + timer.glowmod = _nade.glowmod; + timer.think = nade_timer_think; + timer.nextthink = time; + timer.wait = _nade.wait; + timer.owner = _nade; + timer.skin = 10; + + _nade.effects |= EF_LOWPRECISION; + + CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[false], true); +} + +void napalm_damage(float dist, float damage, float edgedamage, float burntime) +{SELFPARAM(); + entity e; + float d; + vector p; + + if ( damage < 0 ) + return; + + RandomSelection_Init(); + for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain) + if(e.takedamage == DAMAGE_AIM) + if(self.realowner != e || autocvar_g_nades_napalm_selfdamage) + if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) + if(!e.frozen) + { + p = e.origin; + p.x += e.mins.x + random() * (e.maxs.x - e.mins.x); + p.y += e.mins.y + random() * (e.maxs.y - e.mins.y); + p.z += e.mins.z + random() * (e.maxs.z - e.mins.z); + d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); + if(d < dist) + { + e.fireball_impactvec = p; + RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); + } + } + if(RandomSelection_chosen_ent) + { + d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); + d = damage + (edgedamage - damage) * (d / dist); + Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); + //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec); + Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); + } +} + + +void napalm_ball_think() +{SELFPARAM(); + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time > self.pushltime) + { + remove(self); + return; + } + + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + { self.velocity_z = 200; } + } + + self.angles = vectoangles(self.velocity); + + napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage, + autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime); + + self.nextthink = time + 0.1; +} + + +void nade_napalm_ball() +{SELFPARAM(); + entity proj; + vector kick; + + spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM); + + proj = new(grenade); + proj.owner = self.owner; + proj.realowner = self.realowner; + proj.team = self.owner.team; + proj.bot_dodge = true; + proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; + proj.movetype = MOVETYPE_BOUNCE; + proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; + PROJECTILE_MAKETRIGGER(proj); + setmodel(proj, MDL_Null); + proj.scale = 1;//0.5; + setsize(proj, '-4 -4 -4', '4 4 4'); + setorigin(proj, self.origin); + proj.think = napalm_ball_think; + proj.nextthink = time; + proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale; + proj.effects = EF_LOWPRECISION | EF_FLAME; + + kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; + kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; + kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread; + proj.velocity = kick; + + proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime; + + proj.angles = vectoangles(proj.velocity); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; + + //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); +} + + +void napalm_fountain_think() +{SELFPARAM(); + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time >= self.ltime) + { + remove(self); + return; + } + + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + { self.velocity_z = 200; } + + UpdateCSQCProjectile(self); + } + + napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage, + autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime); + + self.nextthink = time + 0.1; + if(time >= self.nade_special_time) + { + self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay; + nade_napalm_ball(); + } +} + +void nade_napalm_boom() +{SELFPARAM(); + entity fountain; + int c; + for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++) + nade_napalm_ball(); + + + fountain = spawn(); + fountain.owner = self.owner; + fountain.realowner = self.realowner; + fountain.origin = self.origin; + setorigin(fountain, fountain.origin); + fountain.think = napalm_fountain_think; + fountain.nextthink = time; + fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; + fountain.pushltime = fountain.ltime; + fountain.team = self.team; + fountain.movetype = MOVETYPE_TOSS; + fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; + fountain.bot_dodge = true; + fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; + fountain.nade_special_time = time; + setsize(fountain, '-16 -16 -16', '16 16 16'); + CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true); +} + +void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time) +{ + frost_target.frozen_by = freezefield.realowner; + Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); + Freeze(frost_target, 1/freeze_time, 3, false); + + Drop_Special_Items(frost_target); +} + +void nade_ice_think() +{SELFPARAM(); + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time >= self.ltime) + { + if ( autocvar_g_nades_ice_explode ) + { + entity expef = EFFECT_NADE_EXPLODE(self.realowner.team); + Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1); + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + + RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); + } + remove(self); + return; + } + + + self.nextthink = time+0.1; + + // gaussian + float randomr; + randomr = random(); + randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius; + float randomw; + randomw = random()*M_PI*2; + vector randomp; + randomp.x = randomr*cos(randomw); + randomp.y = randomr*sin(randomw); + randomp.z = 1; + Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1); + + if(time >= self.nade_special_time) + { + self.nade_special_time = time+0.7; + + Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1); + Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1); + } + + + float current_freeze_time = self.ltime - time - 0.1; + + entity e; + for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain) + if(e != self) + if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner)) + if(e.takedamage && e.deadflag == DEAD_NO) + if(e.health > 0) + if(!e.revival_time || ((time - e.revival_time) >= 1.5)) + if(!e.frozen) + if(current_freeze_time > 0) + nade_ice_freeze(self, e, current_freeze_time); +} + +void nade_ice_boom() +{SELFPARAM(); + entity fountain; + fountain = spawn(); + fountain.owner = self.owner; + fountain.realowner = self.realowner; + fountain.origin = self.origin; + setorigin(fountain, fountain.origin); + fountain.think = nade_ice_think; + fountain.nextthink = time; + fountain.ltime = time + autocvar_g_nades_ice_freeze_time; + fountain.pushltime = fountain.wait = fountain.ltime; + fountain.team = self.team; + fountain.movetype = MOVETYPE_TOSS; + fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; + fountain.bot_dodge = false; + setsize(fountain, '-16 -16 -16', '16 16 16'); + fountain.nade_special_time = time+0.3; + fountain.angles = self.angles; + + if ( autocvar_g_nades_ice_explode ) + { + setmodel(fountain, MDL_PROJECTILE_GRENADE); + entity timer = new(nade_timer); + setmodel(timer, MDL_NADE_TIMER); + setattachment(timer, fountain, ""); + timer.colormap = self.colormap; + timer.glowmod = self.glowmod; + timer.think = nade_timer_think; + timer.nextthink = time; + timer.wait = fountain.ltime; + timer.owner = fountain; + timer.skin = 10; + } + else + setmodel(fountain, MDL_Null); +} + +void nade_translocate_boom() +{SELFPARAM(); + if(self.realowner.vehicle) + return; + + vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24); + tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner); + locout = trace_endpos; + + makevectors(self.realowner.angles); + + MUTATOR_CALLHOOK(PortalTeleport, self.realowner); + + TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); +} + +void nade_spawn_boom() +{SELFPARAM(); + entity spawnloc = spawn(); + setorigin(spawnloc, self.origin); + setsize(spawnloc, self.realowner.mins, self.realowner.maxs); + spawnloc.movetype = MOVETYPE_NONE; + spawnloc.solid = SOLID_NOT; + spawnloc.drawonlytoclient = self.realowner; + spawnloc.effects = EF_STARDUST; + spawnloc.cnt = autocvar_g_nades_spawn_count; + + if(self.realowner.nade_spawnloc) + { + remove(self.realowner.nade_spawnloc); + self.realowner.nade_spawnloc = world; + } + + self.realowner.nade_spawnloc = spawnloc; +} + +void nade_heal_think() +{SELFPARAM(); + if(time >= self.ltime) + { + remove(self); + return; + } + + self.nextthink = time; + + if(time >= self.nade_special_time) + { + self.nade_special_time = time+0.25; + self.nade_show_particles = 1; + } + else + self.nade_show_particles = 0; +} + +void nade_heal_touch() +{SELFPARAM(); + float maxhealth; + float health_factor; + if(IS_PLAYER(other) || IS_MONSTER(other)) + if(other.deadflag == DEAD_NO) + if(!other.frozen) + { + health_factor = autocvar_g_nades_heal_rate*frametime/2; + if ( other != self.realowner ) + { + if ( SAME_TEAM(other,self) ) + health_factor *= autocvar_g_nades_heal_friend; + else + health_factor *= autocvar_g_nades_heal_foe; + } + if ( health_factor > 0 ) + { + maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max; + if ( other.health < maxhealth ) + { + if ( self.nade_show_particles ) + Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); + other.health = min(other.health+health_factor, maxhealth); + } + other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + } + else if ( health_factor < 0 ) + { + Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0'); + } + + } + + if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) + { + entity show_red = (IS_VEHICLE(other)) ? other.owner : other; + show_red.stat_healing_orb = time+0.1; + show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime; + } +} + +void nade_heal_boom() +{SELFPARAM(); + entity healer; + healer = spawn(); + healer.owner = self.owner; + healer.realowner = self.realowner; + setorigin(healer, self.origin); + healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar + healer.ltime = time + healer.healer_lifetime; + healer.team = self.realowner.team; + healer.bot_dodge = false; + healer.solid = SOLID_TRIGGER; + healer.touch = nade_heal_touch; + + setmodel(healer, MDL_NADE_HEAL); + healer.healer_radius = autocvar_g_nades_nade_radius; + vector size = '1 1 1' * healer.healer_radius / 2; + setsize(healer,-size,size); + + Net_LinkEntity(healer, true, 0, healer_send); + + healer.think = nade_heal_think; + healer.nextthink = time; + healer.SendFlags |= 1; +} + +void nade_monster_boom() +{SELFPARAM(); + entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1); + + if(autocvar_g_nades_pokenade_monster_lifetime > 0) + e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; + e.monster_skill = MONSTER_SKILL_INSANE; +} + +void nade_boom() +{SELFPARAM(); + entity expef = NULL; + bool nade_blast = true; + + switch ( Nades_from(self.nade_type) ) + { + case NADE_TYPE_NAPALM: + nade_blast = autocvar_g_nades_napalm_blast; + expef = EFFECT_EXPLOSION_MEDIUM; + break; + case NADE_TYPE_ICE: + nade_blast = false; + expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact + break; + case NADE_TYPE_TRANSLOCATE: + nade_blast = false; + break; + case NADE_TYPE_MONSTER: + case NADE_TYPE_SPAWN: + nade_blast = false; + switch(self.realowner.team) + { + case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break; + case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break; + case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break; + case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break; + default: expef = EFFECT_SPAWN_NEUTRAL; break; + } + break; + case NADE_TYPE_HEAL: + nade_blast = false; + expef = EFFECT_SPAWN_RED; + break; + + default: + case NADE_TYPE_NORMAL: + expef = EFFECT_NADE_EXPLODE(self.realowner.team); + break; + } + + if(expef) + Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1); + + sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + + self.event_damage = func_null; // prevent somehow calling damage in the next call + + if(nade_blast) + { + RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); + } + + if(self.takedamage) + switch ( Nades_from(self.nade_type) ) + { + case NADE_TYPE_NAPALM: nade_napalm_boom(); break; + case NADE_TYPE_ICE: nade_ice_boom(); break; + case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break; + case NADE_TYPE_SPAWN: nade_spawn_boom(); break; + case NADE_TYPE_HEAL: nade_heal_boom(); break; + case NADE_TYPE_MONSTER: nade_monster_boom(); break; + } + + entity head; + for(head = world; (head = find(head, classname, "grapplinghook")); ) + if(head.aiment == self) + RemoveGrapplingHook(head.realowner); + + remove(self); +} + +void nade_touch() +{SELFPARAM(); + /*float is_weapclip = 0; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) + is_weapclip = 1;*/ + if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) + { + entity head; + for(head = world; (head = find(head, classname, "grapplinghook")); ) + if(head.aiment == self) + RemoveGrapplingHook(head.realowner); + remove(self); + return; + } + + PROJECTILE_TOUCH; + + //setsize(self, '-2 -2 -2', '2 2 2'); + //UpdateCSQCProjectile(self); + if(self.health == self.max_health) + { + spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM); + return; + } + + self.enemy = other; + nade_boom(); +} + +void nade_beep() +{SELFPARAM(); + sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); + self.think = nade_boom; + self.nextthink = max(self.wait, time); +} + +void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + self.takedamage = DAMAGE_NO; + nade_boom(); + return; + } + + if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id) + return; + + if (MUTATOR_CALLHOOK(Nade_Damage, DEATH_WEAPONOF(deathtype), force, damage)) {} + else if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) + { + force *= 1.5; + damage = 0; + } + else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY)) + { + force *= 0.5; // too much + damage = 0; + } + else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + { + force *= 6; + damage = self.max_health * 0.55; + } + else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + damage = self.max_health * 0.1; + else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO + { + if(deathtype & HITTYPE_SECONDARY) + { + damage = self.max_health * 0.1; + force *= 10; + } + else + damage = self.max_health * 1.15; + } + + self.velocity += force; + UpdateCSQCProjectile(self); + + if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker))) + return; + + if(self.health == self.max_health) + { + sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); + self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time); + self.think = nade_beep; + } + + self.health -= damage; + + if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) ) + self.realowner = attacker; + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, nade_boom); + else + nade_burn_spawn(self); +} + +void toss_nade(entity e, vector _velocity, float _time) +{SELFPARAM(); + if(e.nade == world) + return; + + entity _nade = e.nade; + e.nade = world; + + remove(e.fake_nade); + e.fake_nade = world; + + makevectors(e.v_angle); + + W_SetupShot(e, false, false, "", CH_WEAPON_A, 0); + + Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES); + + vector offset = (v_forward * autocvar_g_nades_throw_offset.x) + + (v_right * autocvar_g_nades_throw_offset.y) + + (v_up * autocvar_g_nades_throw_offset.z); + if(autocvar_g_nades_throw_offset == '0 0 0') + offset = '0 0 0'; + + setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1); + //setmodel(_nade, MDL_PROJECTILE_NADE); + //setattachment(_nade, world, ""); + PROJECTILE_MAKETRIGGER(_nade); + setsize(_nade, '-16 -16 -16', '16 16 16'); + _nade.movetype = MOVETYPE_BOUNCE; + + tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade); + if (trace_startsolid) + setorigin(_nade, e.origin); + + if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH) + _nade.velocity = '0 0 100'; + else if(autocvar_g_nades_nade_newton_style == 1) + _nade.velocity = e.velocity + _velocity; + else if(autocvar_g_nades_nade_newton_style == 2) + _nade.velocity = _velocity; + else + _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true); + + _nade.touch = nade_touch; + _nade.health = autocvar_g_nades_nade_health; + _nade.max_health = _nade.health; + _nade.takedamage = DAMAGE_AIM; + _nade.event_damage = nade_damage; + _nade.customizeentityforclient = func_null; + _nade.exteriormodeltoclient = world; + _nade.traileffectnum = 0; + _nade.teleportable = true; + _nade.pushable = true; + _nade.gravity = 1; + _nade.missile_flags = MIF_SPLASH | MIF_ARC; + _nade.damagedbycontents = true; + _nade.angles = vectoangles(_nade.velocity); + _nade.flags = FL_PROJECTILE; + _nade.projectiledeathtype = DEATH_NADE.m_id; + _nade.toss_time = time; + _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); + + if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id) + _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + else + _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + + nade_spawn(_nade); + + if(_time) + { + _nade.think = nade_boom; + _nade.nextthink = _time; + } + + e.nade_refire = time + autocvar_g_nades_nade_refire; + e.nade_timer = 0; +} + +void nades_GiveBonus(entity player, float score) +{ + if (autocvar_g_nades) + if (autocvar_g_nades_bonus) + if (IS_REAL_CLIENT(player)) + if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max) + if (player.frozen == 0) + if (player.deadflag == DEAD_NO) + { + if ( player.bonus_nade_score < 1 ) + player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max; + + if ( player.bonus_nade_score >= 1 ) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); + play2(player, SND(KH_ALARM)); + player.bonus_nades++; + player.bonus_nade_score -= 1; + } + } +} + +/** Remove all bonus nades from a player */ +void nades_RemoveBonus(entity player) +{ + player.bonus_nades = player.bonus_nade_score = 0; +} + +MUTATOR_HOOKFUNCTION(nades, PutClientInServer) +{ + nades_RemoveBonus(self); +} + +float nade_customize() +{SELFPARAM(); + //if(IS_SPEC(other)) { return false; } + if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner)) + { + // somewhat hide the model, but keep the glow + //self.effects = 0; + if(self.traileffectnum) + self.traileffectnum = 0; + self.alpha = -1; + } + else + { + //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + if(!self.traileffectnum) + self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(self.nade_type).m_projectile[false], self.team).eent_eff_name); + self.alpha = 1; + } + + return true; +} + +void nade_prime() +{SELFPARAM(); + if(autocvar_g_nades_bonus_only) + if(!self.bonus_nades) + return; // only allow bonus nades + + if(self.nade) + remove(self.nade); + + if(self.fake_nade) + remove(self.fake_nade); + + entity n = new(nade), fn = new(fake_nade); + + if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength) + n.nade_type = self.nade_type; + else if (self.bonus_nades >= 1) + { + n.nade_type = self.nade_type; + n.pokenade_type = self.pokenade_type; + self.bonus_nades -= 1; + } + else + { + n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type); + n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); + } + + n.nade_type = bound(1, n.nade_type, Nades_COUNT); + + setmodel(n, MDL_PROJECTILE_NADE); + //setattachment(n, self, "bip01 l hand"); + n.exteriormodeltoclient = self; + n.customizeentityforclient = nade_customize; + n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(n.nade_type).m_projectile[false], self.team).eent_eff_name); + n.colormod = Nades_from(n.nade_type).m_color; + n.realowner = self; + n.colormap = self.colormap; + n.glowmod = self.glowmod; + n.wait = time + autocvar_g_nades_nade_lifetime; + n.nade_time_primed = time; + n.think = nade_beep; + n.nextthink = max(n.wait - 3, time); + n.projectiledeathtype = DEATH_NADE.m_id; + + setmodel(fn, MDL_NADE_VIEW); + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + setattachment(fn, self.(weaponentity), ""); + fn.realowner = fn.owner = self; + fn.colormod = Nades_from(n.nade_type).m_color; + fn.colormap = self.colormap; + fn.glowmod = self.glowmod; + fn.think = SUB_Remove; + fn.nextthink = n.wait; + + self.nade = n; + self.fake_nade = fn; +} + +float CanThrowNade() +{SELFPARAM(); + if(self.vehicle) + return false; + + if(gameover) + return false; + + if(self.deadflag != DEAD_NO) + return false; + + if (!autocvar_g_nades) + return false; // allow turning them off mid match + + if(forbidWeaponUse(self)) + return false; + + if (!IS_PLAYER(self)) + return false; + + return true; +} + +.bool nade_altbutton; + +void nades_CheckThrow() +{SELFPARAM(); + if(!CanThrowNade()) + return; + + entity held_nade = self.nade; + if (!held_nade) + { + self.nade_altbutton = true; + if(time > self.nade_refire) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW); + nade_prime(); + self.nade_refire = time + autocvar_g_nades_nade_refire; + } + } + else + { + self.nade_altbutton = false; + if (time >= held_nade.nade_time_primed + 1) { + makevectors(self.v_angle); + float _force = time - held_nade.nade_time_primed; + _force /= autocvar_g_nades_nade_lifetime; + _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); + toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0); + } + } +} + +void nades_Clear(entity player) +{ + if(player.nade) + remove(player.nade); + if(player.fake_nade) + remove(player.fake_nade); + + player.nade = player.fake_nade = world; + player.nade_timer = 0; +} + +MUTATOR_HOOKFUNCTION(nades, VehicleEnter) +{ + if(vh_player.nade) + toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05)); + + return false; +} + +CLASS(NadeOffhand, OffhandWeapon) + METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) + { + entity held_nade = player.nade; + if (held_nade) + { + player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1); + // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed); + makevectors(player.angles); + held_nade.velocity = player.velocity; + setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); + held_nade.angles_y = player.angles.y; + + if (time + 0.1 >= held_nade.wait) + toss_nade(player, '0 0 0', time + 0.05); + } + + if (!CanThrowNade()) return; + if (!(time > player.nade_refire)) return; + if (key_pressed) { + if (!held_nade) { + nade_prime(); + held_nade = player.nade; + } + } else if (time >= held_nade.nade_time_primed + 1) { + if (held_nade) { + makevectors(player.v_angle); + float _force = time - held_nade.nade_time_primed; + _force /= autocvar_g_nades_nade_lifetime; + _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); + toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0); + } + } + } +ENDCLASS(NadeOffhand) +NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } + +MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) +{ + if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { + nades_CheckThrow(); + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) +{SELFPARAM(); + if (!IS_PLAYER(self)) { return false; } + + if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton); + + if(IS_PLAYER(self)) + { + if ( autocvar_g_nades_bonus && autocvar_g_nades ) + { + entity key; + float key_count = 0; + FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; } + + float time_score; + if(self.flagcarried || self.ballcarried) // this player is important + time_score = autocvar_g_nades_bonus_score_time_flagcarrier; + else + time_score = autocvar_g_nades_bonus_score_time; + + if(key_count) + time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding + + if(autocvar_g_nades_bonus_client_select) + { + self.nade_type = self.cvar_cl_nade_type; + self.pokenade_type = self.cvar_cl_pokenade_type; + } + else + { + self.nade_type = autocvar_g_nades_bonus_type; + self.pokenade_type = autocvar_g_nades_pokenade_monster_type; + } + + self.nade_type = bound(1, self.nade_type, Nades_COUNT); + + if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max) + nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max); + } + else + { + self.bonus_nades = self.bonus_nade_score = 0; + } + } + + float n = 0; + entity o = world; + if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout) + n = -1; + else + { + vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; + n = 0; + FOR_EACH_PLAYER(other) if(self != other) + { + if(other.deadflag == DEAD_NO) + if(other.frozen == 0) + if(SAME_TEAM(other, self)) + if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax)) + { + if(!o) + o = other; + if(self.frozen == 1) + other.reviving = true; + ++n; + } + } + } + + if(n && self.frozen == 3) // OK, there is at least one teammate reviving us + { + self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); + self.health = max(1, self.revive_progress * start_health); + + if(self.revive_progress >= 1) + { + Unfreeze(self); + + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); + Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname); + } + + FOR_EACH_PLAYER(other) if(other.reviving) + { + other.revive_progress = self.revive_progress; + other.reviving = false; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) +{SELFPARAM(); + if(autocvar_g_nades_spawn) + self.nade_refire = time + autocvar_g_spawnshieldtime; + else + self.nade_refire = time + autocvar_g_nades_nade_refire; + + if(autocvar_g_nades_bonus_client_select) + self.nade_type = self.cvar_cl_nade_type; + + self.nade_timer = 0; + + if (!self.offhand) self.offhand = OFFHAND_NADE; + + if(self.nade_spawnloc) + { + setorigin(self, self.nade_spawnloc.origin); + self.nade_spawnloc.cnt -= 1; + + if(self.nade_spawnloc.cnt <= 0) + { + remove(self.nade_spawnloc); + self.nade_spawnloc = world; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) +{ + if(frag_target.nade) + if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade) + toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05)); + + float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor); + + if(IS_PLAYER(frag_attacker)) + { + if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) + nades_RemoveBonus(frag_attacker); + else if(frag_target.flagcarried) + nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium); + else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1) + { + #define SPREE_ITEM(counta,countb,center,normal,gentle) \ + case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; } + switch(frag_attacker.killcount) + { + KILL_SPREE_LIST + default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break; + } + #undef SPREE_ITEM + } + else + nades_GiveBonus(frag_attacker, killcount_bonus); + } + + nades_RemoveBonus(frag_target); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate) +{ + if(frag_target.frozen) + if(autocvar_g_freezetag_revive_nade) + if(frag_attacker == frag_target) + if(frag_deathtype == DEATH_NADE.m_id) + if(time - frag_inflictor.toss_time <= 0.1) + { + Unfreeze(frag_target); + frag_target.health = autocvar_g_freezetag_revive_nade_health; + Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); + frag_damage = 0; + frag_force = '0 0 0'; + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname); + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, MonsterDies) +{SELFPARAM(); + if(IS_PLAYER(frag_attacker)) + if(DIFF_TEAM(frag_attacker, self)) + if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) + nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) +{ + if(frag_target.nade) + toss_nade(frag_target, '0 0 0', time + 0.05); + + return false; +} + +bool nades_RemovePlayer() +{SELFPARAM(); + nades_Clear(self); + nades_RemoveBonus(self); + return false; +} + +MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); } +MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); } +MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); } + +MUTATOR_HOOKFUNCTION(nades, SpectateCopy) +{SELFPARAM(); + self.nade_timer = other.nade_timer; + self.nade_type = other.nade_type; + self.pokenade_type = other.pokenade_type; + self.bonus_nades = other.bonus_nades; + self.bonus_nade_score = other.bonus_nade_score; + self.stat_healing_orb = other.stat_healing_orb; + self.stat_healing_orb_alpha = other.stat_healing_orb_alpha; + return false; +} + +MUTATOR_HOOKFUNCTION(nades, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type"); + GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type"); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Nades"); + return false; +} + +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Nades"); + return false; +} +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.qh b/qcsrc/common/mutators/mutator/nades/nades.qh new file mode 100644 index 0000000000..2e4829354a --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/nades.qh @@ -0,0 +1,103 @@ +#ifndef NADES_ALL_H +#define NADES_ALL_H + +#include "../../../teams.qh" + +// use slots 70-100 +const int PROJECTILE_NADE = 71; +const int PROJECTILE_NADE_BURN = 72; +const int PROJECTILE_NADE_NAPALM = 73; +const int PROJECTILE_NADE_NAPALM_BURN = 74; +const int PROJECTILE_NAPALM_FOUNTAIN = 75; +const int PROJECTILE_NADE_ICE = 76; +const int PROJECTILE_NADE_ICE_BURN = 77; +const int PROJECTILE_NADE_TRANSLOCATE = 78; +const int PROJECTILE_NADE_SPAWN = 79; +const int PROJECTILE_NADE_HEAL = 80; +const int PROJECTILE_NADE_HEAL_BURN = 81; +const int PROJECTILE_NADE_MONSTER = 82; +const int PROJECTILE_NADE_MONSTER_BURN = 83; + +REGISTRY(Nades, BITS(4)) +#define Nades_from(i) _Nades_from(i, NADE_TYPE_Null) +REGISTER_REGISTRY(Nades) +REGISTRY_CHECK(Nades) + +#define REGISTER_NADE(id) REGISTER(Nades, NADE_TYPE, id, m_id, NEW(Nade)) + +CLASS(Nade, Object) + ATTRIB(Nade, m_id, int, 0) + ATTRIB(Nade, m_color, vector, '0 0 0') + ATTRIB(Nade, m_name, string, _("Grenade")) + ATTRIB(Nade, m_icon, string, "nade_normal") + ATTRIBARRAY(Nade, m_projectile, int, 2) + ATTRIBARRAY(Nade, m_trail, entity, 2) + METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) { + returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon)); + } +ENDCLASS(Nade) + +REGISTER_NADE(Null); + +Nade Nade_FromProjectile(int proj) +{ + FOREACH(Nades, true, LAMBDA( + for (int j = 0; j < 2; j++) + { + if (it.m_projectile[j] == proj) return it; + } + )); + return NADE_TYPE_Null; +} + +#ifndef MENUQC +#include "effects.inc" +#endif + +#include "nades.inc" + +.float healer_lifetime; +.float healer_radius; + +#ifdef SVQC + +.entity nade; +.entity fake_nade; +.float nade_timer; +.float nade_refire; +.float bonus_nades; +.float nade_special_time; +.float bonus_nade_score; +.float nade_type; +.string pokenade_type; +.entity nade_damage_target; +.float cvar_cl_nade_type; +.string cvar_cl_pokenade_type; +.float toss_time; +.float stat_healing_orb; +.float stat_healing_orb_alpha; +.float nade_show_particles; + +bool healer_send(entity this, entity to, int sf); + +// Remove nades that are being thrown +void nades_Clear(entity player); + +// Give a bonus grenade to a player +void(entity player, float score) nades_GiveBonus; + +/** + * called to adjust nade damage and force on hit + */ +#define EV_Nade_Damage(i, o) \ + /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \ + /** force */ i(vector, MUTATOR_ARGV_0_vector) \ + /**/ o(vector, MUTATOR_ARGV_0_vector) \ + /** damage */ i(float, MUTATOR_ARGV_0_float) \ + /**/ o(float, MUTATOR_ARGV_0_float) \ + /**/ +MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); + +#endif + +#endif diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc new file mode 100644 index 0000000000..072f5072f5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -0,0 +1,84 @@ +#include "nades.qh" + +#ifdef IMPLEMENTATION + +#ifdef CSQC +.float ltime; +void healer_draw(entity this) +{ + float dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + self.alpha = (self.ltime - time) / self.healer_lifetime; + self.scale = min((1 - self.alpha)*self.healer_lifetime*4,1)*self.healer_radius; +} + +void healer_setup(entity e) +{ + setmodel(e, MDL_NADE_HEAL); + + setorigin(e, e.origin); + + float model_radius = e.maxs.x; + vector size = '1 1 1' * e.healer_radius / 2; + setsize(e,-size,size); + e.healer_radius = e.healer_radius/model_radius*0.6; + + e.draw = healer_draw; + e.health = 255; + e.movetype = MOVETYPE_NONE; + e.solid = SOLID_NOT; + e.drawmask = MASK_NORMAL; + e.scale = 0.01; + e.avelocity = e.move_avelocity = '7 0 11'; + e.colormod = '1 0 0'; + e.renderflags |= RF_ADDITIVE; +} +#endif + +REGISTER_NET_LINKED(Nade_Heal) + +#ifdef CSQC +NET_HANDLE(Nade_Heal, bool isNew) +{ + Net_Accept(Nade_Heal); + int sf = ReadByte(); + if (sf & 1) { + this.origin_x = ReadCoord(); + this.origin_y = ReadCoord(); + this.origin_z = ReadCoord(); + setorigin(this, this.origin); + this.healer_lifetime = ReadByte(); + this.healer_radius = ReadShort(); + this.ltime = time + ReadByte()/10.0; + // this.ltime = time + this.healer_lifetime; + healer_setup(this); + } + return true; +} +#endif + +#ifdef SVQC +bool healer_send(entity this, entity to, int sf) +{ + int channel = MSG_ENTITY; + WriteHeader(channel, Nade_Heal); + WriteByte(channel, sf); + if (sf & 1) { + WriteCoord(channel, this.origin.x); + WriteCoord(channel, this.origin.y); + WriteCoord(channel, this.origin.z); + + WriteByte(channel, this.healer_lifetime); + //WriteByte(MSG_ENTITY, this.ltime - time + 1); + WriteShort(channel, this.healer_radius); + // round time delta to a 1/10th of a second + WriteByte(channel, (this.ltime - time)*10.0+0.5); + } + return true; +} +#endif + +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/module.inc b/qcsrc/common/mutators/mutator/new_toys/module.inc new file mode 100644 index 0000000000..1217177c58 --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "new_toys.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc new file mode 100644 index 0000000000..78904ffae7 --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc @@ -0,0 +1,227 @@ +#ifdef IMPLEMENTATION +/* + +CORE laser vortex lg rl cry gl elec hagar fireb hook + vaporizer porto + tuba + +NEW rifle hlac minel seeker +IDEAS OPEN flak OPEN FUN FUN FUN FUN + + + +How this mutator works: + ======================= + +When a gun tries to spawn, this mutator is called. It will provide alternate +weaponreplace lists. + +Entity: + +{ +"classname" "weapon_vortex" +"new_toys" "rifle" +} +-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortext" +"new_toys" "vortex rifle" +} +-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortex" +"new_toys" "vortex" +} +-> This is always a Vortex. + +If the map specifies no "new_toys" argument + +There will be two default replacements selectable: "replace all" and "replace random". +In "replace all" mode, e.g. Vortex will have the default replacement "rifle". +In "replace random" mode, Vortex will have the default replacement "vortex rifle". + +This mutator's replacements run BEFORE regular weaponreplace! + +The New Toys guns do NOT get a spawn function, so they can only ever be spawned +when this mutator is active. + +Likewise, warmup, give all, give ALL and impulse 99 will not give them unless +this mutator is active. + +Outside this mutator, they still can be spawned by: +- setting their start weapon cvar to 1 +- give weaponname +- weaponreplace +- weaponarena (but all and most weapons arena again won't include them) + +This mutator performs the default replacements on the DEFAULTS of the +start weapon selection. + +These weapons appear in the menu's priority list, BUT get a suffix +"(Mutator weapon)". + +Picking up a "new toys" weapon will not play standard weapon pickup sound, but +roflsound "New toys, new toys!" sound. + +*/ + +bool nt_IsNewToy(int w); + +REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This cannot be added at runtime\n"); + + // mark the guns as ok to use by e.g. impulse 99 + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + if(nt_IsNewToy(i)) + get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + if(nt_IsNewToy(i)) + get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.string new_toys; + +float autocvar_g_new_toys_autoreplace; +bool autocvar_g_new_toys_use_pickupsound = true; +const float NT_AUTOREPLACE_NEVER = 0; +const float NT_AUTOREPLACE_ALWAYS = 1; +const float NT_AUTOREPLACE_RANDOM = 2; + +MUTATOR_HOOKFUNCTION(nt, SetModname) +{ + modname = "NewToys"; + return 0; +} + +bool nt_IsNewToy(int w) +{ + switch(w) + { + case WEP_SEEKER.m_id: + case WEP_MINE_LAYER.m_id: + case WEP_HLAC.m_id: + case WEP_RIFLE.m_id: + case WEP_SHOCKWAVE.m_id: + return true; + default: + return false; + } +} + +string nt_GetFullReplacement(string w) +{ + switch(w) + { + case "hagar": return "seeker"; + case "devastator": return "minelayer"; + case "machinegun": return "hlac"; + case "vortex": return "rifle"; + //case "shotgun": return "shockwave"; + default: return string_null; + } +} + +string nt_GetReplacement(string w, float m) +{ + if(m == NT_AUTOREPLACE_NEVER) + return w; + string s = nt_GetFullReplacement(w); + if (!s) + return w; + if(m == NT_AUTOREPLACE_RANDOM) + s = strcat(w, " ", s); + return s; +} + +MUTATOR_HOOKFUNCTION(nt, SetStartItems) +{ + // rearrange start_weapon_default + // apply those bits that are set by start_weapon_defaultmask + // same for warmup + + float i, j, k, n; + + WepSet newdefault; + WepSet warmup_newdefault; + + newdefault = '0 0 0'; + warmup_newdefault = '0 0 0'; + + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + entity e = get_weaponinfo(i); + if(!e.weapon) + continue; + + n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace)); + + for(j = 0; j < n; ++j) + for(k = WEP_FIRST; k <= WEP_LAST; ++k) + if(get_weaponinfo(k).netname == argv(j)) + { + if(start_weapons & WepSet_FromWeapon(i)) + newdefault |= WepSet_FromWeapon(k); + if(warmup_start_weapons & WepSet_FromWeapon(i)) + warmup_newdefault |= WepSet_FromWeapon(k); + } + } + + newdefault &= start_weapons_defaultmask; + start_weapons &= ~start_weapons_defaultmask; + start_weapons |= newdefault; + + warmup_newdefault &= warmup_start_weapons_defaultmask; + warmup_start_weapons &= ~warmup_start_weapons_defaultmask; + warmup_start_weapons |= warmup_newdefault; + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) +{SELFPARAM(); + // otherwise, we do replace + if(self.new_toys) + { + // map defined replacement: + ret_string = self.new_toys; + } + else + { + // auto replacement: + ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace); + } + + // apply regular weaponreplace + ret_string = W_Apply_Weaponreplace(ret_string); + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt, FilterItem) +{SELFPARAM(); + if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) { + self.item_pickupsound = string_null; + self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; + } + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/nix/module.inc b/qcsrc/common/mutators/mutator/nix/module.inc new file mode 100644 index 0000000000..fb4f9ec2bc --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "nix.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/nix/nix.qc b/qcsrc/common/mutators/mutator/nix/nix.qc new file mode 100644 index 0000000000..1a8089ca6d --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/nix.qc @@ -0,0 +1,291 @@ +#ifdef IMPLEMENTATION +int autocvar_g_balance_nix_ammo_cells; +int autocvar_g_balance_nix_ammo_plasma; +int autocvar_g_balance_nix_ammo_fuel; +int autocvar_g_balance_nix_ammo_nails; +int autocvar_g_balance_nix_ammo_rockets; +int autocvar_g_balance_nix_ammo_shells; +int autocvar_g_balance_nix_ammoincr_cells; +int autocvar_g_balance_nix_ammoincr_plasma; +int autocvar_g_balance_nix_ammoincr_fuel; +int autocvar_g_balance_nix_ammoincr_nails; +int autocvar_g_balance_nix_ammoincr_rockets; +int autocvar_g_balance_nix_ammoincr_shells; +float autocvar_g_balance_nix_incrtime; +float autocvar_g_balance_nix_roundtime; +bool autocvar_g_nix_with_healtharmor; +bool autocvar_g_nix_with_blaster; +bool autocvar_g_nix_with_powerups; +int autocvar_g_pickup_cells_max; +int autocvar_g_pickup_plasma_max; +int autocvar_g_pickup_fuel_max; +int autocvar_g_pickup_nails_max; +int autocvar_g_pickup_rockets_max; +int autocvar_g_pickup_shells_max; + +float g_nix_with_blaster; +// WEAPONTODO +int nix_weapon; +float nix_nextchange; +float nix_nextweapon; +.float nix_lastchange_id; +.float nix_lastinfotime; +.float nix_nextincr; + +bool NIX_CanChooseWeapon(int wpn); + +REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + g_nix_with_blaster = autocvar_g_nix_with_blaster; + + nix_nextchange = 0; + nix_nextweapon = 0; + + for (int i = WEP_FIRST; i <= WEP_LAST; ++i) + if (NIX_CanChooseWeapon(i)) { + Weapon w = get_weaponinfo(i); + w.wr_init(w); + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // as the PlayerSpawn hook will no longer run, NIX is turned off by this! + entity e; + FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) + { + e.ammo_cells = start_ammo_cells; + e.ammo_plasma = start_ammo_plasma; + e.ammo_shells = start_ammo_shells; + e.ammo_nails = start_ammo_nails; + e.ammo_rockets = start_ammo_rockets; + e.ammo_fuel = start_ammo_fuel; + e.weapons = start_weapons; + if(!client_hasweapon(e, e.weapon, true, false)) + e.switchweapon = w_getbestweapon(self); + } + } + + return 0; +} + +bool NIX_CanChooseWeapon(int wpn) +{ + entity e = get_weaponinfo(wpn); + if(!e.weapon) // skip dummies + return false; + if(g_weaponarena) + { + if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn))) + return false; + } + else + { + if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) + return false; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + return false; + if (!(e.spawnflags & WEP_FLAG_NORMAL)) + return false; + } + return true; +} +void NIX_ChooseNextWeapon() +{ + float j; + RandomSelection_Init(); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + if(NIX_CanChooseWeapon(j)) + RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon)); + nix_nextweapon = RandomSelection_chosen_float; +} + +void NIX_GiveCurrentWeapon() +{SELFPARAM(); + float dt; + + if(!nix_nextweapon) + NIX_ChooseNextWeapon(); + + dt = ceil(nix_nextchange - time); + + if(dt <= 0) + { + nix_weapon = nix_nextweapon; + nix_nextweapon = 0; + if (!nix_nextchange) // no round played yet? + nix_nextchange = time; // start the first round now! + else + nix_nextchange = time + autocvar_g_balance_nix_roundtime; + // Weapon w = get_weaponinfo(nix_weapon); + // w.wr_init(w); // forget it, too slow + } + + // get weapon info + entity e = get_weaponinfo(nix_weapon); + + if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round! + { + self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0; + + if(self.items & IT_UNLIMITED_WEAPON_AMMO) + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break; + case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break; + case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break; + } + } + else + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; + case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; + case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; + } + } + + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + if(dt >= 1 && dt <= 5) + self.nix_lastinfotime = -42; + else + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); + + Weapon w = get_weaponinfo(nix_weapon); + w.wr_resetplayer(w); + + // all weapons must be fully loaded when we spawn + if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + self.(weapon_load[nix_weapon]) = e.reloading_ammo; + + // vortex too + if(WEP_CVAR(vortex, charge)) + { + if(WEP_CVAR_SEC(vortex, chargepool)) + self.vortex_chargepool_ammo = 1; + self.vortex_charge = WEP_CVAR(vortex, charge_start); + } + + // set last change info + self.nix_lastchange_id = nix_nextchange; + } + if(self.nix_lastinfotime != dt) + { + self.nix_lastinfotime = dt; // initial value 0 should count as "not seen" + if(dt >= 1 && dt <= 5) + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); + } + + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr) + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; + case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; + case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; + case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; + case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; + case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; + } + + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + } + + self.weapons = '0 0 0'; + if(g_nix_with_blaster) + self.weapons |= WEPSET(BLASTER); + self.weapons |= WepSet_FromWeapon(nix_weapon); + + if(self.switchweapon != nix_weapon) + if(!client_hasweapon(self, self.switchweapon, true, false)) + if(client_hasweapon(self, nix_weapon, true, false)) + W_SwitchWeapon(nix_weapon); +} + +MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) +{ + return 1; // no throwing in NIX +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":NIX"); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", NIX"); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, FilterItem) +{SELFPARAM(); + switch (self.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_HealthMedium.m_itemid: + case ITEM_HealthLarge.m_itemid: + case ITEM_HealthMega.m_itemid: + case ITEM_ArmorSmall.m_itemid: + case ITEM_ArmorMedium.m_itemid: + case ITEM_ArmorLarge.m_itemid: + case ITEM_ArmorMega.m_itemid: + if (autocvar_g_nix_with_healtharmor) + return 0; + break; + case ITEM_Strength.m_itemid: + case ITEM_Shield.m_itemid: + if (autocvar_g_nix_with_powerups) + return 0; + break; + } + + return 1; // delete all other items +} + +MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) +{SELFPARAM(); + if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) + return 1; + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) +{SELFPARAM(); + if(!intermission_running) + if(self.deadflag == DEAD_NO) + if(IS_PLAYER(self)) + NIX_GiveCurrentWeapon(); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) +{SELFPARAM(); + self.nix_lastchange_id = -1; + NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning + self.items |= IT_UNLIMITED_SUPERWEAPONS; + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) +{ + modname = "NIX"; + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qc b/qcsrc/common/mutators/mutator/overkill/hmg.qc new file mode 100644 index 0000000000..7f2341fb0e --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/hmg.qc @@ -0,0 +1,178 @@ +#ifndef IMPLEMENTATION +CLASS(HeavyMachineGun, Weapon) +/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails) +/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3) +/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON); +/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); +/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); +#ifndef MENUQC +/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM); +#endif +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg"); +/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg"); +/* wepname */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun")); +ENDCLASS(HeavyMachineGun) +REGISTER_WEAPON(HMG, NEW(HeavyMachineGun)); + +#define HMG_SETTINGS(w_cvar,w_prop) HMG_SETTINGS_LIST(w_cvar, w_prop, HMG, hmg) +#define HMG_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, spread_min) \ + w_cvar(id, sn, NONE, spread_max) \ + w_cvar(id, sn, NONE, spread_add) \ + w_cvar(id, sn, NONE, solidpenetration) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, ammo) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +HMG_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#endif +#ifdef IMPLEMENTATION +#ifdef SVQC + +REGISTER_MUTATOR(hmg_nadesupport, true); +MUTATOR_HOOKFUNCTION(hmg_nadesupport, Nade_Damage) +{ + if (MUTATOR_ARGV(0, entity) != WEP_HMG) return; + return = true; + MUTATOR_ARGV(0, float) /* damage */ = self.max_health * 0.1; +} + +spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(this, WEP_HMG); } + +void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) +{ + if (!actor.BUTTON_ATCK) + { + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + Weapon w = get_weaponinfo(actor.weapon); + if(!w.wr_checkammo1(w)) + if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + W_DecreaseAmmo(WEP_HMG, self, WEP_CVAR(hmg, ammo)); + + W_SetupShot (actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage)); + + if(!autocvar_g_norecoil) + { + actor.punchangle_x = random () - 0.5; + actor.punchangle_y = random () - 0.5; + } + + float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.misc_bulletcounter), WEP_CVAR(hmg, spread_max)); + fireBullet(w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0); + + actor.misc_bulletcounter = actor.misc_bulletcounter + 1; + + Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(); + W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); + + if (autocvar_g_casings >= 2) // casing code + SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor); + + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto); +} + + METHOD(HeavyMachineGun, wr_aim, void(entity thiswep)) + { + if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) + self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); + else + self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); + } + METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) + { + if(WEP_CVAR(hmg, reload_ammo) && actor.clip_load < WEP_CVAR(hmg, ammo)) { // forced reload + Weapon w = get_weaponinfo(actor.weapon); + w.wr_reload(w); + } else + { + if (fire & 1) + if (weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) + { + actor.misc_bulletcounter = 0; + W_HeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire); + } + } + } + METHOD(HeavyMachineGun, wr_init, void(entity thiswep)) + { + HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + } + METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep)) + { + float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo); + + if(autocvar_g_balance_hmg_reload_ammo) + ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo); + + return ammo_amount; + } + METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep)) + { + float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo); + + if(autocvar_g_balance_hmg_reload_ammo) + ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo); + + return ammo_amount; + } + METHOD(HeavyMachineGun, wr_config, void(entity thiswep)) + { + HMG_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); + } + METHOD(HeavyMachineGun, wr_reload, void(entity thiswep)) + { + W_Reload(self, WEP_CVAR(hmg, ammo), SND(RELOAD)); + } + METHOD(HeavyMachineGun, wr_suicidemessage, int(entity thiswep)) + { + return WEAPON_THINKING_WITH_PORTALS; + } + METHOD(HeavyMachineGun, wr_killmessage, int(entity thiswep)) + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_HMG_MURDER_SNIPE; + else + return WEAPON_HMG_MURDER_SPRAY; + } + +#endif +#ifdef CSQC + + METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep)) + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1); + if(!w_issilent) + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); + } + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/module.inc b/qcsrc/common/mutators/mutator/overkill/module.inc new file mode 100644 index 0000000000..a8bde27b56 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/module.inc @@ -0,0 +1,6 @@ +#include "hmg.qc" +#include "rpc.qc" + +#ifdef SVQC + #include "overkill.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qc b/qcsrc/common/mutators/mutator/overkill/overkill.qc new file mode 100644 index 0000000000..c146f1c1a0 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/overkill.qc @@ -0,0 +1,385 @@ +#ifdef IMPLEMENTATION +bool autocvar_g_overkill_powerups_replace; +float autocvar_g_overkill_superguns_respawn_time; +bool autocvar_g_overkill_100h_anyway; +bool autocvar_g_overkill_100a_anyway; +bool autocvar_g_overkill_ammo_charge; +float autocvar_g_overkill_ammo_charge_notice; +float autocvar_g_overkill_ammo_charge_limit; + +.vector ok_deathloc; +.float ok_spawnsys_timer; +.float ok_lastwep; +.float ok_item; + +.float ok_notice_time; +.float ammo_charge[Weapons_MAX]; +.float ok_use_ammocharge; +.float ok_ammo_charge; + +.float ok_pauseregen_finished; + +void(entity ent, float wep) ok_DecreaseCharge; + +void ok_Initialize(); + +REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") +{ + MUTATOR_ONADD + { + ok_Initialize(); + } + + MUTATOR_ONREMOVE + { + WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo) +{ + entity actor = MUTATOR_ARGV(0, entity); + if (actor.ok_use_ammocharge) + { + ok_DecreaseCharge(actor, actor.weapon); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ok, W_Reload) +{ + entity actor = MUTATOR_ARGV(0, entity); + return actor.ok_use_ammocharge; +} + +void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float); +spawnfunc(weapon_hmg); +spawnfunc(weapon_rpc); + +void ok_DecreaseCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return; + + entity wepent = get_weaponinfo(wep); + + if(wepent.weapon == 0) + return; // dummy + + ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +void ok_IncreaseCharge(entity ent, int wep) +{ + entity wepent = get_weaponinfo(wep); + + if(wepent.weapon == 0) + return; // dummy + + if(ent.ok_use_ammocharge) + if(!ent.BUTTON_ATCK) // not while attacking? + ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); +} + +float ok_CheckWeaponCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return true; + + entity wepent = get_weaponinfo(wep); + + if(wepent.weapon == 0) + return 0; // dummy + + return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) +{ + if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) + if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + frag_damage = 0; + + if(frag_attacker != frag_target) + if(frag_target.health > 0) + if(frag_target.frozen == 0) + if(frag_target.deadflag == DEAD_NO) + { + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); + frag_force = '0 0 0'; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor) +{SELFPARAM(); + if(damage_take) + self.ok_pauseregen_finished = max(self.ok_pauseregen_finished, time + 2); + return false; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDies) +{SELFPARAM(); + entity targ = ((frag_attacker) ? frag_attacker : frag_target); + + if(IS_MONSTER(self)) + { + remove(other); // remove default item + other = world; + } + + setself(new(droppedweapon)); // hax + self.ok_item = true; + self.noalign = true; + self.pickup_anyway = true; + spawnfunc_item_armor_small(this); + self.movetype = MOVETYPE_TOSS; + self.gravity = 1; + self.reset = SUB_Remove; + setorigin(self, frag_target.origin + '0 0 32'); + self.velocity = '0 0 200' + normalize(targ.origin - self.origin) * 500; + SUB_SetFade(self, time + 5, 1); + setself(this); + + self.ok_lastwep = self.switchweapon; + + return false; +} +MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) { ok_PlayerDies(); } + +MUTATOR_HOOKFUNCTION(ok, PlayerRegen) +{SELFPARAM(); + // overkill's values are different, so use custom regen + if(!self.frozen) + { + self.armorvalue = CalcRotRegen(self.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, 1 * frametime * (time > self.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > self.pauserotarmor_finished), autocvar_g_balance_armor_limit); + self.health = CalcRotRegen(self.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > self.ok_pauseregen_finished), 200, 0, autocvar_g_balance_health_rotlinear, 1 * frametime * (time > self.pauserothealth_finished), autocvar_g_balance_health_limit); + + float minf, maxf, limitf; + + maxf = autocvar_g_balance_fuel_rotstable; + minf = autocvar_g_balance_fuel_regenstable; + limitf = autocvar_g_balance_fuel_limit; + + self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf); + } + return true; // return true anyway, as frozen uses no regen +} + +MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) +{SELFPARAM(); + if(intermission_running || gameover) + return false; + + if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || self.frozen) + return false; + + if(self.ok_lastwep) + { + self.switchweapon = self.ok_lastwep; + self.ok_lastwep = 0; + } + + ok_IncreaseCharge(self, self.weapon); + + if(self.BUTTON_ATCK2) + if(!forbidWeaponUse(self) || self.weapon_blocked) // allow if weapon is blocked + if(time >= self.jump_interval) + { + self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(); + makevectors(self.v_angle); + + int oldwep = self.weapon; + self.weapon = WEP_BLASTER.m_id; + W_Blaster_Attack( + self, + WEP_BLASTER.m_id | HITTYPE_SECONDARY, + WEP_CVAR_SEC(vaporizer, shotangle), + WEP_CVAR_SEC(vaporizer, damage), + WEP_CVAR_SEC(vaporizer, edgedamage), + WEP_CVAR_SEC(vaporizer, radius), + WEP_CVAR_SEC(vaporizer, force), + WEP_CVAR_SEC(vaporizer, speed), + WEP_CVAR_SEC(vaporizer, spread), + WEP_CVAR_SEC(vaporizer, delay), + WEP_CVAR_SEC(vaporizer, lifetime) + ); + self.weapon = oldwep; + } + + self.weapon_blocked = false; + + self.ok_ammo_charge = self.ammo_charge[self.weapon]; + + if(self.ok_use_ammocharge) + if(!ok_CheckWeaponCharge(self, self.weapon)) + { + if(autocvar_g_overkill_ammo_charge_notice && time > self.ok_notice_time && self.BUTTON_ATCK && IS_REAL_CLIENT(self) && self.weapon == self.switchweapon) + { + //Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE); + self.ok_notice_time = time + 2; + play2(self, SND(DRYFIRE)); + } + Weapon wpn = get_weaponinfo(self.weapon); + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if(self.(weaponentity).state != WS_CLEAR) + w_ready(wpn, self, weaponentity, (self.BUTTON_ATCK ? 1 : 0) | (self.BUTTON_ATCK2 ? 2 : 0)); + + self.weapon_blocked = true; + } + + self.BUTTON_ATCK2 = 0; + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) +{SELFPARAM(); + if(autocvar_g_overkill_ammo_charge) + { + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit; + + self.ok_use_ammocharge = 1; + self.ok_notice_time = time; + } + else + self.ok_use_ammocharge = 0; + + self.ok_pauseregen_finished = time + 2; + + return false; +} + +void _spawnfunc_weapon_hmg() { SELFPARAM(); spawnfunc_weapon_hmg(this); } +void _spawnfunc_weapon_rpc() { SELFPARAM(); spawnfunc_weapon_rpc(this); } + +MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) +{SELFPARAM(); + if(autocvar_g_powerups) + if(autocvar_g_overkill_powerups_replace) + { + if(self.classname == "item_strength") + { + entity wep = new(weapon_hmg); + setorigin(wep, self.origin); + setmodel(wep, MDL_OK_HMG); + wep.ok_item = true; + wep.noalign = self.noalign; + wep.cnt = self.cnt; + wep.team = self.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; + wep.pickup_anyway = true; + wep.think = _spawnfunc_weapon_hmg; + wep.nextthink = time + 0.1; + return true; + } + + if(self.classname == "item_invincible") + { + entity wep = new(weapon_rpc); + setorigin(wep, self.origin); + setmodel(wep, MDL_OK_RPC); + wep.ok_item = true; + wep.noalign = self.noalign; + wep.cnt = self.cnt; + wep.team = self.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; + wep.pickup_anyway = true; + wep.think = _spawnfunc_weapon_rpc; + wep.nextthink = time + 0.1; + return true; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, FilterItem) +{SELFPARAM(); + if(self.ok_item) + return false; + + switch(self.items) + { + case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway); + case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway); + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ok, SpectateCopy) +{SELFPARAM(); + self.ammo_charge[self.weapon] = other.ammo_charge[other.weapon]; + self.ok_use_ammocharge = other.ok_use_ammocharge; + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, SetStartItems) +{ + WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); + + if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } + if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } + + start_items |= IT_UNLIMITED_WEAPON_AMMO; + start_weapons = warmup_start_weapons = ok_start_items; + + return false; +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":OK"); + return false; +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Overkill"); + return false; +} + +MUTATOR_HOOKFUNCTION(ok, SetModname) +{ + modname = "Overkill"; + return true; +} + +void ok_SetCvars() +{ + // hack to force overkill playermodels + cvar_settemp("sv_defaultcharacter", "1"); + cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); + cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); + cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); +} + +void ok_Initialize() +{ + ok_SetCvars(); + + precache_all_playermodels("models/ok_player/*.dpm"); + + addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge); + addstat(STAT_OK_AMMO_CHARGEPOOL, AS_FLOAT, ok_ammo_charge); + + WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + WEP_SHOTGUN.mdl = "ok_shotgun"; + WEP_MACHINEGUN.mdl = "ok_mg"; + WEP_VORTEX.mdl = "ok_sniper"; +} +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qc b/qcsrc/common/mutators/mutator/overkill/rpc.qc new file mode 100644 index 0000000000..05aa9239d7 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/rpc.qc @@ -0,0 +1,231 @@ +#ifndef IMPLEMENTATION +CLASS(RocketPropelledChainsaw, Weapon) +/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets) +/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7) +/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON); +/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); +/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); +#ifndef MENUQC +/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); +#endif +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc"); +/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc"); +/* wepname */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw")); +ENDCLASS(RocketPropelledChainsaw) +REGISTER_WEAPON(RPC, NEW(RocketPropelledChainsaw)); + +#define RPC_SETTINGS(w_cvar,w_prop) RPC_SETTINGS_LIST(w_cvar, w_prop, RPC, rpc) +#define RPC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, ammo) \ + w_cvar(id, sn, NONE, animtime) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, damage2) \ + w_cvar(id, sn, NONE, damageforcescale) \ + w_cvar(id, sn, NONE, edgedamage) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, health) \ + w_cvar(id, sn, NONE, lifetime) \ + w_cvar(id, sn, NONE, radius) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, speed) \ + w_cvar(id, sn, NONE, speedaccel) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#endif +#ifdef IMPLEMENTATION +#ifdef SVQC +spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); } + +void W_RocketPropelledChainsaw_Explode() +{SELFPARAM(); + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + RadiusDamage (self, self.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), world, world, WEP_CVAR(rpc, force), self.projectiledeathtype, other); + + remove (self); +} + +void W_RocketPropelledChainsaw_Touch () +{SELFPARAM(); + if(WarpZone_Projectile_Touch()) + if(wasfreed(self)) + return; + + W_RocketPropelledChainsaw_Explode(); +} + +void W_RocketPropelledChainsaw_Damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if (self.health <= 0) + return; + + if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + + if (self.health <= 0) + W_PrepareExplosionByDamage(attacker, W_RocketPropelledChainsaw_Explode); +} + +void W_RocketPropelledChainsaw_Think() +{SELFPARAM(); + if(self.cnt <= time) + { + remove(self); + return; + } + + self.cnt = vlen(self.velocity); + self.wait = self.cnt * sys_frametime; + self.pos1 = normalize(self.velocity); + + tracebox(self.origin, self.mins, self.maxs, self.origin + self.pos1 * (2 * self.wait), MOVE_NORMAL, self); + if(IS_PLAYER(trace_ent)) + Damage (trace_ent, self, self.realowner, WEP_CVAR(rpc, damage2), self.projectiledeathtype, self.origin, normalize(self.origin - other.origin) * WEP_CVAR(rpc, force)); + + self.velocity = self.pos1 * (self.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime)); + + UpdateCSQCProjectile(self); + self.nextthink = time; +} + +void W_RocketPropelledChainsaw_Attack (Weapon thiswep) +{SELFPARAM(); + entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(self); + entity flash = spawn (); + + W_DecreaseAmmo(thiswep, self, WEP_CVAR(rpc, ammo)); + W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(rpc, damage)); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + PROJECTILE_MAKETRIGGER(missile); + + missile.owner = missile.realowner = self; + missile.bot_dodge = true; + missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = WEP_CVAR(rpc, damageforcescale); + missile.health = WEP_CVAR(rpc, health); + missile.event_damage = W_RocketPropelledChainsaw_Damage; + missile.damagedbycontents = true; + missile.movetype = MOVETYPE_FLY; + + missile.projectiledeathtype = WEP_RPC.m_id; + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point + W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0); + + missile.touch = W_RocketPropelledChainsaw_Touch; + + missile.think = W_RocketPropelledChainsaw_Think; + missile.cnt = time + WEP_CVAR(rpc, lifetime); + missile.nextthink = time; + missile.flags = FL_PROJECTILE; + + CSQCProjectile(missile, true, PROJECTILE_RPC, false); + + setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below + SUB_SetFade (flash, time, 0.1); + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(self, flash, '5 0 0'); + missile.pos1 = missile.velocity; + + MUTATOR_CALLHOOK(EditProjectile, self, missile); +} + + METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep)) + { + self.BUTTON_ATCK = bot_aim(WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false); + } + METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) + { + if(WEP_CVAR(rpc, reload_ammo) && actor.clip_load < WEP_CVAR(rpc, ammo)) { + Weapon w = get_weaponinfo(actor.weapon); + w.wr_reload(w); + } else + { + if (fire & 1) + { + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire))) + { + W_RocketPropelledChainsaw_Attack(thiswep); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready); + } + } + + if (fire & 2) + { + // to-do + } + } + } + METHOD(RocketPropelledChainsaw, wr_init, void(entity thiswep)) + { + RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); + } + METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep)) + { + float ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo); + ammo_amount += self.(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo); + return ammo_amount; + } + METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep)) + { + return false; + } + METHOD(RocketPropelledChainsaw, wr_config, void(entity thiswep)) + { + RPC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); + } + METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep)) + { + W_Reload(self, WEP_CVAR(rpc, ammo), SND(RELOAD)); + } + METHOD(RocketPropelledChainsaw, wr_suicidemessage, int(entity thiswep)) + { + if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_RPC_SUICIDE_SPLASH; + else + return WEAPON_RPC_SUICIDE_DIRECT; + } + METHOD(RocketPropelledChainsaw, wr_killmessage, int(entity thiswep)) + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_BLASTER_MURDER; + else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_RPC_MURDER_SPLASH; + else + return WEAPON_RPC_MURDER_DIRECT; + } + +#endif + +#ifdef CSQC + + METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep)) + { + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + } + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/module.inc b/qcsrc/common/mutators/mutator/physical_items/module.inc new file mode 100644 index 0000000000..7ed9b039be --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "physical_items.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc new file mode 100644 index 0000000000..6fbe77bce4 --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc @@ -0,0 +1,143 @@ +#ifdef IMPLEMENTATION +int autocvar_g_physical_items; +float autocvar_g_physical_items_damageforcescale; +float autocvar_g_physical_items_reset; + +REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) +{ + // check if we have a physics engine + MUTATOR_ONADD + { + if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) + { + LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); + return -1; + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.vector spawn_origin, spawn_angles; + +void physical_item_think() +{SELFPARAM(); + self.nextthink = time; + + self.alpha = self.owner.alpha; // apply fading and ghosting + + if(!self.cnt) // map item, not dropped + { + // copy ghost item properties + self.colormap = self.owner.colormap; + self.colormod = self.owner.colormod; + self.glowmod = self.owner.glowmod; + + // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there + if(autocvar_g_physical_items_reset) + { + if(self.owner.wait > time) // awaiting respawn + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + self.solid = SOLID_NOT; + self.alpha = -1; + self.movetype = MOVETYPE_NONE; + } + else + { + self.alpha = 1; + self.solid = SOLID_CORPSE; + self.movetype = MOVETYPE_PHYSICS; + } + } + } + + if(!self.owner.modelindex) + remove(self); // the real item is gone, remove this +} + +void physical_item_touch() +{SELFPARAM(); + if(!self.cnt) // not for dropped items + if (ITEM_TOUCH_NEEDKILL()) + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + } +} + +void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(!self.cnt) // not for dropped items + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + } +} + +MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) +{SELFPARAM(); + if(self.owner == world && autocvar_g_physical_items <= 1) + return false; + if (self.spawnflags & 1) // floating item + return false; + + // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. + // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. + entity wep; + wep = spawn(); + _setmodel(wep, self.model); + setsize(wep, self.mins, self.maxs); + setorigin(wep, self.origin); + wep.angles = self.angles; + wep.velocity = self.velocity; + + wep.owner = self; + wep.solid = SOLID_CORPSE; + wep.movetype = MOVETYPE_PHYSICS; + wep.takedamage = DAMAGE_AIM; + wep.effects |= EF_NOMODELFLAGS; // disable the spinning + wep.colormap = self.owner.colormap; + wep.glowmod = self.owner.glowmod; + wep.damageforcescale = autocvar_g_physical_items_damageforcescale; + wep.dphitcontentsmask = self.dphitcontentsmask; + wep.cnt = (self.owner != world); + + wep.think = physical_item_think; + wep.nextthink = time; + wep.touch = physical_item_touch; + wep.event_damage = physical_item_damage; + + if(!wep.cnt) + { + // fix the spawn origin + setorigin(wep, wep.origin + '0 0 1'); + entity oldself; + oldself = self; + WITH(entity, self, wep, builtin_droptofloor()); + } + + wep.spawn_origin = wep.origin; + wep.spawn_angles = self.angles; + + self.effects |= EF_NODRAW; // hide the original weapon + self.movetype = MOVETYPE_FOLLOW; + self.aiment = wep; // attach the original weapon + self.SendEntity = func_null; + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/module.inc b/qcsrc/common/mutators/mutator/pinata/module.inc new file mode 100644 index 0000000000..4e22966863 --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "pinata.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/pinata.qc b/qcsrc/common/mutators/mutator/pinata/pinata.qc new file mode 100644 index 0000000000..a806b2958a --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/pinata.qc @@ -0,0 +1,27 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); + +MUTATOR_HOOKFUNCTION(pinata, PlayerDies) +{SELFPARAM(); + for(int j = WEP_FIRST; j <= WEP_LAST; ++j) + if(self.weapons & WepSet_FromWeapon(j)) + if(self.switchweapon != j) + if(W_IsWeaponThrowable(j)) + W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325'); + + return true; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Pinata"); + return false; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Piñata"); + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/module.inc b/qcsrc/common/mutators/mutator/random_gravity/module.inc new file mode 100644 index 0000000000..91baa43102 --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "random_gravity.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc new file mode 100644 index 0000000000..80abfe343d --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc @@ -0,0 +1,56 @@ +#ifdef IMPLEMENTATION +// Random Gravity +// +// Mutator by Mario +// Inspired by Player 2 + +float autocvar_g_random_gravity_negative_chance; +float autocvar_g_random_gravity_min; +float autocvar_g_random_gravity_max; +float autocvar_g_random_gravity_positive; +float autocvar_g_random_gravity_negative; +float autocvar_g_random_gravity_delay; + +REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) +{ + MUTATOR_ONADD + { + cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end + } + + return false; +} + +float gravity_delay; + +MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) +{ + if(gameover || !cvar("g_random_gravity")) return false; + if(time < gravity_delay) return false; + if(time < game_starttime) return false; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; + + if(random() >= autocvar_g_random_gravity_negative_chance) + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); + else + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); + + gravity_delay = time + autocvar_g_random_gravity_delay; + + LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n"); + + return false; +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":RandomGravity"); + return 0; +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Random gravity"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/module.inc b/qcsrc/common/mutators/mutator/rocketflying/module.inc new file mode 100644 index 0000000000..7036bc49d6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "rocketflying.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc new file mode 100644 index 0000000000..f23d9918bd --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc @@ -0,0 +1,25 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); + +MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) +{ + if(other.classname == "rocket" || other.classname == "mine") + { + // kill detonate delay of rockets + other.spawnshieldtime = time; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":RocketFlying"); + return 0; +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Rocket Flying"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/module.inc b/qcsrc/common/mutators/mutator/rocketminsta/module.inc new file mode 100644 index 0000000000..b7d02a9f6b --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "rocketminsta.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc new file mode 100644 index 0000000000..6ddd6db94c --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc @@ -0,0 +1,35 @@ +#ifdef IMPLEMENTATION +#include "../../../deathtypes/all.qh" +#include "../../../../server/round_handler.qh" + +REGISTER_MUTATOR(rm, cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return false; } + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + if(frag_attacker == frag_target || frag_target.classname == "nade") + frag_damage = 0; + + if(autocvar_g_rm_laser) + if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + frag_damage = 0; + + return false; +} + +MUTATOR_HOOKFUNCTION(rm, PlayerDies) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return false; } + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + frag_damage = 1000; // always gib if it was a vaporizer death + + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/module.inc b/qcsrc/common/mutators/mutator/running_guns/module.inc new file mode 100644 index 0000000000..036b70ff62 --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "running_guns.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/running_guns.qc new file mode 100644 index 0000000000..cad4d5f2fe --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/running_guns.qc @@ -0,0 +1,14 @@ +#ifdef IMPLEMENTATION + +bool autocvar_g_running_guns; + +REGISTER_MUTATOR(running_guns, autocvar_g_running_guns); + +MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha) +{ + default_player_alpha = -1; + default_weapon_alpha = +1; + return true; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/module.inc b/qcsrc/common/mutators/mutator/sandbox/module.inc new file mode 100644 index 0000000000..0715d5b403 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/module.inc @@ -0,0 +1,4 @@ +#ifdef SVQC +#include "sandbox.qc" + +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc new file mode 100644 index 0000000000..e1decc8a06 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc @@ -0,0 +1,842 @@ +#ifdef IMPLEMENTATION +int autocvar_g_sandbox_info; +bool autocvar_g_sandbox_readonly; +string autocvar_g_sandbox_storage_name; +float autocvar_g_sandbox_storage_autosave; +bool autocvar_g_sandbox_storage_autoload; +float autocvar_g_sandbox_editor_flood; +int autocvar_g_sandbox_editor_maxobjects; +int autocvar_g_sandbox_editor_free; +float autocvar_g_sandbox_editor_distance_spawn; +float autocvar_g_sandbox_editor_distance_edit; +float autocvar_g_sandbox_object_scale_min; +float autocvar_g_sandbox_object_scale_max; +float autocvar_g_sandbox_object_material_velocity_min; +float autocvar_g_sandbox_object_material_velocity_factor; + +float autosave_time; +void sandbox_Database_Load(); + +REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) +{ + MUTATOR_ONADD + { + autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame + if(autocvar_g_sandbox_storage_autoload) + sandbox_Database_Load(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // nothing to remove + } + + return false; +} + +const float MAX_STORAGE_ATTACHMENTS = 16; +float object_count; +.float object_flood; +.entity object_attach; +.string material; + +.float touch_timer; +void sandbox_ObjectFunction_Touch() +{SELFPARAM(); + // apply material impact effects + + if(!self.material) + return; + if(self.touch_timer > time) + return; // don't execute each frame + self.touch_timer = time + 0.1; + + // make particle count and sound volume depend on impact speed + float intensity; + intensity = vlen(self.velocity) + vlen(other.velocity); + if(intensity) // avoid divisions by 0 + intensity /= 2; // average the two velocities + if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) + return; // impact not strong enough to do anything + // now offset intensity and apply it to the effects + intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity + intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); + + _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); + Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 +} + +void sandbox_ObjectFunction_Think() +{SELFPARAM(); + entity e; + + // decide if and how this object can be grabbed + if(autocvar_g_sandbox_readonly) + self.grab = 0; // no grabbing + else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp) + self.grab = 1; // owner only + else + self.grab = 3; // anyone + + // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). + // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, + // since if the owning player disconnects, the object's owner should also be reset. + FOR_EACH_REALPLAYER(e) // bots can't have objects + { + if(self.crypto_idfp == e.crypto_idfp) + { + self.realowner = e; + break; + } + self.realowner = world; + } + + self.nextthink = time; + + CSQCMODEL_AUTOUPDATE(self); +} + +.float old_solid, old_movetype; +entity sandbox_ObjectEdit_Get(float permissions) +{SELFPARAM(); + // Returns the traced entity if the player can edit it, and world if not. + // If permissions if false, the object is returned regardless of editing rights. + // Attached objects are SOLID_NOT and do not get traced. + + crosshair_trace_plusvisibletriggers(self); + if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit) + return world; // out of trace range + if(trace_ent.classname != "object") + return world; // entity is not an object + if(!permissions) + return trace_ent; // don't check permissions, anyone can edit this object + if(trace_ent.crypto_idfp == "") + return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it + if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)) + return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server + return world; +} + +void sandbox_ObjectEdit_Scale(entity e, float f) +{ + e.scale = f; + if(e.scale) + { + e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); + _setmodel(e, e.model); // reset mins and maxs based on mesh + setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size + } +} + +void sandbox_ObjectAttach_Remove(entity e); +void sandbox_ObjectAttach_Set(entity e, entity parent, string s) +{ + // attaches e to parent on string s + + // we can't attach to an attachment, for obvious reasons + sandbox_ObjectAttach_Remove(e); + + e.old_solid = e.solid; // persist solidity + e.old_movetype = e.movetype; // persist physics + e.movetype = MOVETYPE_FOLLOW; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_NO; + + setattachment(e, parent, s); + e.owner = parent; +} + +void sandbox_ObjectAttach_Remove(entity e) +{ + // detaches any object attached to e + + entity head; + for(head = world; (head = find(head, classname, "object")); ) + { + if(head.owner == e) + { + vector org; + org = gettaginfo(head, 0); + setattachment(head, world, ""); + head.owner = world; + + // objects change origin and angles when detached, so apply previous position + setorigin(head, org); + head.angles = e.angles; // don't allow detached objects to spin or roll + + head.solid = head.old_solid; // restore persisted solidity + head.movetype = head.old_movetype; // restore persisted physics + head.takedamage = DAMAGE_AIM; + } + } +} + +entity sandbox_ObjectSpawn(float database) +{SELFPARAM(); + // spawn a new object with default properties + + entity e = new(object); + e.takedamage = DAMAGE_AIM; + e.damageforcescale = 1; + e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly + e.movetype = MOVETYPE_TOSS; + e.frame = 0; + e.skin = 0; + e.material = string_null; + e.touch = sandbox_ObjectFunction_Touch; + e.think = sandbox_ObjectFunction_Think; + e.nextthink = time; + //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? + + if(!database) + { + // set the object's owner via player UID + // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone + if(self.crypto_idfp != "") + e.crypto_idfp = strzone(self.crypto_idfp); + else + print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); + + // set public object information + e.netname = strzone(self.netname); // name of the owner + e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time + + // set origin and direction based on player position and view angle + makevectors(self.v_angle); + WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self); + setorigin(e, trace_endpos); + e.angles_y = self.v_angle.y; + } + + WITH(entity, self, e, CSQCMODEL_AUTOINIT(e)); + + object_count += 1; + return e; +} + +void sandbox_ObjectRemove(entity e) +{ + sandbox_ObjectAttach_Remove(e); // detach child objects + + // if the object being removed has been selected for attachment by a player, unset it + entity head; + FOR_EACH_REALPLAYER(head) // bots can't have objects + { + if(head.object_attach == e) + head.object_attach = world; + } + + if(e.material) { strunzone(e.material); e.material = string_null; } + if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } + if(e.netname) { strunzone(e.netname); e.netname = string_null; } + if(e.message) { strunzone(e.message); e.message = string_null; } + if(e.message2) { strunzone(e.message2); e.message2 = string_null; } + remove(e); + e = world; + + object_count -= 1; +} + +string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global + +string sandbox_ObjectPort_Save(entity e, float database) +{ + // save object properties, and return them as a string + float i = 0; + string s; + entity head; + + for(head = world; (head = find(head, classname, "object")); ) + { + // the main object needs to be first in the array [0] with attached objects following + float slot, physics, solidity; + if(head == e) // this is the main object, place it first + { + slot = 0; + solidity = head.solid; // applied solidity is normal solidity for children + physics = head.movetype; // applied physics are normal physics for parents + } + else if(head.owner == e) // child object, list them in order + { + i += 1; // children start from 1 + slot = i; + solidity = head.old_solid; // persisted solidity is normal solidity for children + physics = head.old_movetype; // persisted physics are normal physics for children + gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below + } + else + continue; + + // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- + if(slot) + { + // properties stored only for child objects + if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + } + else + { + // properties stored only for parent objects + if(database) + { + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); + } + } + // properties stored for all objects + port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); + port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); + port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); + port_string[slot] = strcat(port_string[slot], ftos(physics), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); + if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + if(database) + { + // properties stored only for the database + if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); + } + } + + // now apply the array to a simple string, with the ; symbol separating objects + s = ""; + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + { + if(port_string[i]) + s = strcat(s, port_string[i], "; "); + port_string[i] = string_null; // fully clear the string + } + + return s; +} + +entity sandbox_ObjectPort_Load(string s, float database) +{ + // load object properties, and spawn a new object with them + float n, i; + entity e = world, parent = world; + + // separate objects between the ; symbols + n = tokenizebyseparator(s, "; "); + for(i = 0; i < n; ++i) + port_string[i] = argv(i); + + // now separate and apply the properties of each object + for(i = 0; i < n; ++i) + { + float argv_num; + string tagname = string_null; + argv_num = 0; + tokenize_console(port_string[i]); + e = sandbox_ObjectSpawn(database); + + // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- + if(i) + { + // properties stored only for child objects + if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; + } + else + { + // properties stored only for parent objects + if(database) + { + setorigin(e, stov(argv(argv_num))); ++argv_num; + e.angles = stov(argv(argv_num)); ++argv_num; + } + parent = e; // mark parent objects as such + } + // properties stored for all objects + _setmodel(e, argv(argv_num)); ++argv_num; + e.skin = stof(argv(argv_num)); ++argv_num; + e.alpha = stof(argv(argv_num)); ++argv_num; + e.colormod = stov(argv(argv_num)); ++argv_num; + e.glowmod = stov(argv(argv_num)); ++argv_num; + e.frame = stof(argv(argv_num)); ++argv_num; + sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; + e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; + e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num; + e.damageforcescale = stof(argv(argv_num)); ++argv_num; + if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; + if(database) + { + // properties stored only for the database + if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; + if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; + if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; + if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; + } + + // attach last + if(i) + sandbox_ObjectAttach_Set(e, parent, tagname); + } + + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + port_string[i] = string_null; // fully clear the string + + return e; +} + +void sandbox_Database_Save() +{ + // saves all objects to the database file + entity head; + string file_name; + float file_get; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); + fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); + + for(head = world; (head = find(head, classname, "object")); ) + { + // attached objects are persisted separately, ignore them here + if(head.owner != world) + continue; + + // use a line of text for each object, listing all properties + fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); + } + fclose(file_get); +} + +void sandbox_Database_Load() +{ + // loads all objects from the database file + string file_read, file_name; + float file_get, i; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); + } + else + { + for (;;) + { + file_read = fgets(file_get); + if(file_read == "") + break; + if(substring(file_read, 0, 2) == "//") + continue; + if(substring(file_read, 0, 1) == "#") + continue; + + entity e; + e = sandbox_ObjectPort_Load(file_read, true); + + if(e.material) + { + // since objects are being loaded for the first time, precache material sounds for each + for (i = 1; i <= 5; i++) // 5 sounds in total + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); + } + } + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); + } + fclose(file_get); +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + if(cmd_name == "g_sandbox") + { + if(autocvar_g_sandbox_readonly) + { + print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); + return true; + } + if(cmd_argc < 2) + { + print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); + return true; + } + + switch(argv(1)) + { + entity e; + float i; + string s; + + // ---------------- COMMAND: HELP ---------------- + case "help": + print_to(self, "You can use the following sandbox commands:"); + print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); + print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); + print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); + print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar"); + print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); + print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); + print_to(self, "^3get ^7- selects the object you are facing as the object to be attached"); + print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); + print_to(self, "^3remove ^7- detaches all objects from the object you are facing"); + print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); + print_to(self, "^3skin value ^7- changes the skin of the object"); + print_to(self, "^3alpha value ^7- sets object transparency"); + print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color"); + print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); + print_to(self, "^3frame value ^7- object animation frame, for self-animated models"); + print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); + print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); + print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); + print_to(self, "^3force value ^7- amount of force applied to objects that are shot"); + print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); + print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); + print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object"); + print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); + print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); + print_to(self, "^3attachments ^7- prints information about the object's attachments"); + print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); + return true; + + // ---------------- COMMAND: OBJECT, SPAWN ---------------- + case "object_spawn": + if(time < self.object_flood) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + self.object_flood = time + autocvar_g_sandbox_editor_flood; + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + if(cmd_argc < 3) + { + print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); + return true; + } + if (!(fexists(argv(2)))) + { + print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); + return true; + } + + e = sandbox_ObjectSpawn(false); + _setmodel(e, argv(2)); + + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); + return true; + + // ---------------- COMMAND: OBJECT, REMOVE ---------------- + case "object_remove": + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); + sandbox_ObjectRemove(e); + return true; + } + + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- + case "object_duplicate": + switch(argv(2)) + { + case "copy": + // copies customizable properties of the selected object to the clipboard cvar + e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? + if(e != world) + { + s = sandbox_ObjectPort_Save(e, false); + s = strreplace("\"", "\\\"", s); + stuffcmd(self, strcat("set ", argv(3), " \"", s, "\"")); + + print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard"); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); + return true; + + case "paste": + // spawns a new object using the properties in the player's clipboard cvar + if(time < self.object_flood) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + self.object_flood = time + autocvar_g_sandbox_editor_flood; + if(argv(3) == "") // no object in clipboard + { + print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); + return true; + } + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + e = sandbox_ObjectPort_Load(argv(3), false); + + print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully"); + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, ATTACH ---------------- + case "object_attach": + switch(argv(2)) + { + case "get": + // select e as the object as meant to be attached + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + self.object_attach = e; + print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment"); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); + return true; + case "set": + if(self.object_attach == world) + { + print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); + return true; + } + + // attaches the previously selected object to e + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + sandbox_ObjectAttach_Set(self.object_attach, e, argv(3)); + self.object_attach = world; // object was attached, no longer keep it scheduled for attachment + print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); + return true; + case "remove": + // removes e if it was attached + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + sandbox_ObjectAttach_Remove(e); + print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, EDIT ---------------- + case "object_edit": + if(argv(2) == "") + { + print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); + return true; + } + + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + switch(argv(2)) + { + case "skin": + e.skin = stof(argv(3)); + break; + case "alpha": + e.alpha = stof(argv(3)); + break; + case "color_main": + e.colormod = stov(argv(3)); + break; + case "color_glow": + e.glowmod = stov(argv(3)); + break; + case "frame": + e.frame = stof(argv(3)); + break; + case "scale": + sandbox_ObjectEdit_Scale(e, stof(argv(3))); + break; + case "solidity": + switch(argv(3)) + { + case "0": // non-solid + e.solid = SOLID_TRIGGER; + break; + case "1": // solid + e.solid = SOLID_BBOX; + break; + default: + break; + } + case "physics": + switch(argv(3)) + { + case "0": // static + e.movetype = MOVETYPE_NONE; + break; + case "1": // movable + e.movetype = MOVETYPE_TOSS; + break; + case "2": // physical + e.movetype = MOVETYPE_PHYSICS; + break; + default: + break; + } + break; + case "force": + e.damageforcescale = stof(argv(3)); + break; + case "material": + if(e.material) strunzone(e.material); + if(argv(3)) + { + for (i = 1; i <= 5; i++) // precache material sounds, 5 in total + precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); + e.material = strzone(argv(3)); + } + else + e.material = string_null; // no material + break; + default: + print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); + return true; + } + + // update last editing time + if(e.message2) strunzone(e.message2); + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); + + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, CLAIM ---------------- + case "object_claim": + // if the player can edit an object but is not its owner, this can be used to claim that object + if(self.crypto_idfp == "") + { + print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); + return true; + } + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + // update the owner's name + // Do this before checking if you're already the owner and skipping if such, so we + // also update the player's nickname if he changed it (but has the same player UID) + if(e.netname != self.netname) + { + if(e.netname) strunzone(e.netname); + e.netname = strzone(self.netname); + print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated"); + } + + if(e.crypto_idfp == self.crypto_idfp) + { + print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); + return true; + } + + if(e.crypto_idfp) strunzone(e.crypto_idfp); + e.crypto_idfp = strzone(self.crypto_idfp); + + print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, INFO ---------------- + case "object_info": + // prints public information about the object to the player + e = sandbox_ObjectEdit_Get(false); + if(e != world) + { + switch(argv(2)) + { + case "object": + print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); + return true; + case "mesh": + s = ""; + FOR_EACH_TAG(e) + s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); + print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); + return true; + case "attachments": + // this should show the same info as 'mesh' but for attachments + s = ""; + entity head; + i = 0; + for(head = world; (head = find(head, classname, "object")); ) + { + if(head.owner == e) + { + ++i; // start from 1 + gettaginfo(e, head.tag_index); + s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame)); + s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); + } + } + if(i) // object contains attachments + print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s)); + else + print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments"); + return true; + } + } + print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); + return true; + + // ---------------- COMMAND: DEFAULT ---------------- + default: + print_to(self, "Invalid command. For usage information, type 'sandbox help'"); + return true; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) +{ + if(!autocvar_g_sandbox_storage_autosave) + return false; + if(time < autosave_time) + return false; + autosave_time = time + autocvar_g_sandbox_storage_autosave; + + sandbox_Database_Save(); + + return true; +} +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc new file mode 100644 index 0000000000..f88a768a21 --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "spawn_near_teammate.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc new file mode 100644 index 0000000000..cd78b4082f --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc @@ -0,0 +1,175 @@ +#ifdef IMPLEMENTATION + +float autocvar_g_spawn_near_teammate_distance; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; +bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; + +REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay); + +.entity msnt_lookat; + +.float msnt_timer; +.vector msnt_deathloc; + +.float cvar_cl_spawn_near_teammate; + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) +{SELFPARAM(); + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) + return 0; + + entity p; + + spawn_spot.msnt_lookat = world; + + if(!teamplay) + return 0; + + RandomSelection_Init(); + FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag) + { + float l = vlen(spawn_spot.origin - p.origin); + if(l > autocvar_g_spawn_near_teammate_distance) + continue; + if(l < 48) + continue; + if(!checkpvs(spawn_spot.origin, p)) + continue; + RandomSelection_Add(p, 0, string_null, 1, 1); + } + + if(RandomSelection_chosen_ent) + { + spawn_spot.msnt_lookat = RandomSelection_chosen_ent; + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + } + else if(self.team == spawn_spot.team) + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate + + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) +{SELFPARAM(); + // Note: when entering this, fixangle is already set. + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) + self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; + + entity team_mate, best_mate = world; + vector best_spot = '0 0 0'; + float pc = 0, best_dist = 0, dist = 0; + FOR_EACH_PLAYER(team_mate) + { + if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) + if(team_mate.deadflag == DEAD_NO) + if(team_mate.msnt_timer < time) + if(SAME_TEAM(self, team_mate)) + if(time > team_mate.spawnshieldtime) // spawn shielding + if(team_mate.frozen == 0) + if(team_mate != self) + { + tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate); + if(trace_fraction != 1.0) + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + { + pc = pointcontents(trace_endpos + '0 0 1'); + if(pc == CONTENT_EMPTY) + { + if(vlen(team_mate.velocity) > 5) + fixedmakevectors(vectoangles(team_mate.velocity)); + else + fixedmakevectors(team_mate.angles); + + for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate + { + switch(pc) + { + case 0: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate); + break; + case 1: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate); + break; + case 2: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); + break; + case 3: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); + break; + case 4: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate); + break; + } + + if(trace_fraction == 1.0) + { + traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate); + if(trace_fraction != 1.0) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + { + dist = vlen(trace_endpos - self.msnt_deathloc); + if(dist < best_dist || best_dist == 0) + { + best_dist = dist; + best_spot = trace_endpos; + best_mate = team_mate; + } + } + else + { + setorigin(self, trace_endpos); + self.angles = team_mate.angles; + self.angles_z = 0; // never spawn tilted even if the spot says to + team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + return 0; + } + } + } + } + } + } + } + } + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + if(best_dist) + { + setorigin(self, best_spot); + self.angles = best_mate.angles; + self.angles_z = 0; // never spawn tilted even if the spot says to + best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + } + } + else if(spawn_spot.msnt_lookat) + { + self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin); + self.angles_x = -self.angles.x; + self.angles_z = 0; // never spawn tilted even if the spot says to + /* + sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); + sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n"); + sprint(self, "angles: ", vtos(self.angles), "\n"); + */ + } + + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) +{SELFPARAM(); + self.msnt_deathloc = self.origin; + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/module.inc b/qcsrc/common/mutators/mutator/superspec/module.inc new file mode 100644 index 0000000000..8e0a998c2d --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "superspec.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/superspec.qc b/qcsrc/common/mutators/mutator/superspec/superspec.qc new file mode 100644 index 0000000000..887b184234 --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/superspec.qc @@ -0,0 +1,480 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(superspec, cvar("g_superspectate")); + +#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" +#define _ISLOCAL ((edict_num(1) == self) ? true : false) + +const float ASF_STRENGTH = BIT(0); +const float ASF_SHIELD = BIT(1); +const float ASF_MEGA_AR = BIT(2); +const float ASF_MEGA_HP = BIT(3); +const float ASF_FLAG_GRAB = BIT(4); +const float ASF_OBSERVER_ONLY = BIT(5); +const float ASF_SHOWWHAT = BIT(6); +const float ASF_SSIM = BIT(7); +const float ASF_FOLLOWKILLER = BIT(8); +const float ASF_ALL = 0xFFFFFF; +.float autospec_flags; + +const float SSF_SILENT = 1; +const float SSF_VERBOSE = 2; +const float SSF_ITEMMSG = 4; +.float superspec_flags; + +.string superspec_itemfilter; //"classname1 classname2 ..." + +bool superspec_Spectate(entity _player) +{SELFPARAM(); + if(Spectate(_player) == 1) + self.classname = STR_SPECTATOR; + + return true; +} + +void superspec_save_client_conf() +{SELFPARAM(); + string fn = "superspec-local.options"; + float fh; + + if (!_ISLOCAL) + { + if(self.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_WRITE); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); + } + else + { + fputs(fh, _SSMAGIX); + fputs(fh, "\n"); + fputs(fh, ftos(self.autospec_flags)); + fputs(fh, "\n"); + fputs(fh, ftos(self.superspec_flags)); + fputs(fh, "\n"); + fputs(fh, self.superspec_itemfilter); + fputs(fh, "\n"); + fclose(fh); + } +} + +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) +{ + sprint(_to, strcat(_con_title, _msg)); + + if(_to.superspec_flags & SSF_SILENT) + return; + + if(_spamlevel > 1) + if (!(_to.superspec_flags & SSF_VERBOSE)) + return; + + centerprint(_to, strcat(_center_title, _msg)); +} + +float superspec_filteritem(entity _for, entity _item) +{ + float i; + + if(_for.superspec_itemfilter == "") + return true; + + if(_for.superspec_itemfilter == "") + return true; + + float l = tokenize_console(_for.superspec_itemfilter); + for(i = 0; i < l; ++i) + { + if(argv(i) == _item.classname) + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ItemTouch) +{SELFPARAM(); + entity _item = self; + + entity e; + FOR_EACH_CLIENT(e) if (IS_SPEC(e) || IS_OBSERVER(e)) + { + setself(e); + if(self.superspec_flags & SSF_ITEMMSG) + if(superspec_filteritem(self, _item)) + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1); + else + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1); + if((self.autospec_flags & ASF_SSIM) && self.enemy != other) + { + superspec_Spectate(other); + + setself(this); + return MUT_ITEMTOUCH_CONTINUE; + } + } + + if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) || + (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) || + (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) || + (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) || + (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team")) + { + + if((self.enemy != other) || IS_OBSERVER(self)) + { + if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self)) + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2); + } + else + { + if(self.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2); + + superspec_Spectate(other); + } + } + } + } + + setself(this); + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) +{SELFPARAM(); +#define OPTIONINFO(flag,var,test,text,long,short) \ + var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ + var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") + + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + + if(IS_PLAYER(self)) + return false; + + if(cmd_name == "superspec_itemfilter") + { + if(argv(1) == "help") + { + string _aspeco; + _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; + _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); + _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); + superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1); + } + else if(argv(1) == "clear") + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = ""; + } + else if(argv(1) == "show" || argv(1) == "") + { + if(self.superspec_itemfilter == "") + { + superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1); + return true; + } + float i; + float l = tokenize_console(self.superspec_itemfilter); + string _msg = ""; + for(i = 0; i < l; ++i) + _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); + //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); + + _msg = strcat(_msg,"\n"); + + superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1); + } + else + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = strzone(argv(1)); + } + + return true; + } + + if(cmd_name == "superspec") + { + string _aspeco; + + if(cmd_argc > 1) + { + float i, _bits = 0, _start = 1; + if(argv(1) == "help") + { + _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); + _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); + _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); + _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); + superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1); + return true; + } + + if(argv(1) == "clear") + { + self.superspec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.superspec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.superspec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; + if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; + if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; + } + } + } + + _aspeco = ""; + OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); + + superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1); + + return true; + } + +///////////////////// + + if(cmd_name == "autospec") + { + string _aspeco; + if(cmd_argc > 1) + { + if(argv(1) == "help") + { + _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); + _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); + _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); + _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); + _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); + _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); + _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); + _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); + _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); + _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); + superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1); + return true; + } + + float i, _bits = 0, _start = 1; + if(argv(1) == "clear") + { + self.autospec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.autospec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.autospec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; + if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; + if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; + if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; + if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; + if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; + if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; + if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; + if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; + if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; + } + } + } + + _aspeco = ""; + OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); + + superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1); + return true; + } + + if(cmd_name == "followpowerup") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time || _player.invincible_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active powerup\n", 1); + return true; + } + + if(cmd_name == "followstrength") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active Strength\n", 1); + return true; + } + + if(cmd_name == "followshield") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.invincible_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active Shield\n", 1); + return true; + } + + return false; +#undef OPTIONINFO +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":SS"); + return 0; +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Super Spectators"); + return 0; +} + +void superspec_hello() +{SELFPARAM(); + if(self.enemy.crypto_idfp == "") + Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); + + remove(self); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientConnect) +{SELFPARAM(); + if(!IS_REAL_CLIENT(self)) + return false; + + string fn = "superspec-local.options"; + float fh; + + self.superspec_flags = SSF_VERBOSE; + self.superspec_itemfilter = ""; + + entity _hello = spawn(); + _hello.enemy = self; + _hello.think = superspec_hello; + _hello.nextthink = time + 5; + + if (!_ISLOCAL) + { + if(self.crypto_idfp == "") + return false; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_READ); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); + } + else + { + string _magic = fgets(fh); + if(_magic != _SSMAGIX) + { + LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n"); + } + else + { + self.autospec_flags = stof(fgets(fh)); + self.superspec_flags = stof(fgets(fh)); + self.superspec_itemfilter = strzone(fgets(fh)); + } + fclose(fh); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, PlayerDies) +{SELFPARAM(); + entity e; + FOR_EACH_SPEC(e) + { + setself(e); + if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this) + { + if(self.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); + + superspec_Spectate(frag_attacker); + } + } + + setself(this); + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) +{ + superspec_save_client_conf(); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/module.inc b/qcsrc/common/mutators/mutator/touchexplode/module.inc new file mode 100644 index 0000000000..d3b0ea5af9 --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "touchexplode.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc new file mode 100644 index 0000000000..ec439489d0 --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc @@ -0,0 +1,48 @@ +#ifdef IMPLEMENTATION +float autocvar_g_touchexplode_radius; +float autocvar_g_touchexplode_damage; +float autocvar_g_touchexplode_edgedamage; +float autocvar_g_touchexplode_force; + +REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); + +.float touchexplode_time; + +void PlayerTouchExplode(entity p1, entity p2) +{SELFPARAM(); + vector org = (p1.origin + p2.origin) * 0.5; + org.z += (p1.mins.z + p2.mins.z) * 0.5; + + sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); + + entity e = spawn(); + setorigin(e, org); + RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world); + remove(e); +} + +MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) +{SELFPARAM(); + if(time > self.touchexplode_time) + if(!gameover) + if(!self.frozen) + if(IS_PLAYER(self)) + if(self.deadflag == DEAD_NO) + if (!IS_INDEPENDENT_PLAYER(self)) + FOR_EACH_PLAYER(other) if(self != other) + { + if(time > other.touchexplode_time) + if(!other.frozen) + if(other.deadflag == DEAD_NO) + if (!IS_INDEPENDENT_PLAYER(other)) + if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) + { + PlayerTouchExplode(self, other); + self.touchexplode_time = other.touchexplode_time = time + 0.2; + } + } + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/module.inc b/qcsrc/common/mutators/mutator/vampire/module.inc new file mode 100644 index 0000000000..864ea28b24 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "vampire.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/vampire.qc b/qcsrc/common/mutators/mutator/vampire/vampire.qc new file mode 100644 index 0000000000..315da7dc6f --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/vampire.qc @@ -0,0 +1,28 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) +{ + if(time >= frag_target.spawnshieldtime) + if(frag_target != frag_attacker) + if(frag_target.deadflag == DEAD_NO) + { + frag_attacker.health += bound(0, damage_take, frag_target.health); + frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Vampire"); + return 0; +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Vampire"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/module.inc b/qcsrc/common/mutators/mutator/vampirehook/module.inc new file mode 100644 index 0000000000..17ecf6005f --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "vampirehook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc new file mode 100644 index 0000000000..f669f6a962 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc @@ -0,0 +1,38 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(vh, cvar("g_vampirehook")); + +bool autocvar_g_vampirehook_teamheal; +float autocvar_g_vampirehook_damage; +float autocvar_g_vampirehook_damagerate; +float autocvar_g_vampirehook_health_steal; + +.float last_dmg; + +MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) +{SELFPARAM(); + entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment); + + if(IS_PLAYER(self.aiment)) + if(self.last_dmg < time) + if(!self.aiment.frozen) + if(time >= game_starttime) + if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal) + if(self.aiment.health > 0) + if(autocvar_g_vampirehook_damage) + { + self.last_dmg = time + autocvar_g_vampirehook_damagerate; + self.owner.damage_dealt += autocvar_g_vampirehook_damage; + Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0'); + if(SAME_TEAM(self.owner, self.aiment)) + self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + else + self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + + if(dmgent == self.owner) + dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + } + + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/all.inc b/qcsrc/common/mutators/mutator/waypoints/all.inc index 47fafe7ed5..c74715ea71 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.inc +++ b/qcsrc/common/mutators/mutator/waypoints/all.inc @@ -54,8 +54,6 @@ REGISTER_WAYPOINT(OnsCPAttack, _("Control point"), '1 0.5 0', 2); REGISTER_WAYPOINT(OnsGen, _("Generator"), '1 0.5 0', 1); REGISTER_WAYPOINT(OnsGenShielded, _("Generator"), '1 0.5 0', 1); -REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); - REGISTER_WAYPOINT(Weapon, _("Weapon"), '0 0 0', 1); REGISTER_WAYPOINT(Monster, _("Monster"), '1 0 0', 1); diff --git a/qcsrc/common/mutators/mutator/waypoints/all.qh b/qcsrc/common/mutators/mutator/waypoints/all.qh index f7fd03837d..9693d28090 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.qh +++ b/qcsrc/common/mutators/mutator/waypoints/all.qh @@ -3,10 +3,13 @@ #include "waypointsprites.qh" -REGISTRY(Waypoints, BIT(6)) -REGISTER_REGISTRY(RegisterWaypoints) +REGISTRY(Waypoints, BITS(6)) +#define Waypoints_from(i) _Waypoints_from(i, WP_Null) +REGISTER_REGISTRY(Waypoints) +REGISTRY_CHECK(Waypoints) + /** If you register a new waypoint, make sure to add it to all.inc */ -#define REGISTER_WAYPOINT_(id, init) REGISTER(RegisterWaypoints, WP, Waypoints, id, m_id, init) +#define REGISTER_WAYPOINT_(id, init) REGISTER(Waypoints, WP, id, m_id, init) CLASS(Waypoint, Object) ATTRIB(Waypoint, m_id, int, 0) @@ -26,9 +29,12 @@ ENDCLASS(Waypoint) #define REGISTER_WAYPOINT(id, text, color, blink) REGISTER_WAYPOINT_(id, NEW(Waypoint, #id, text, color, blink)) REGISTRY(RadarIcons, BITS(7)) -REGISTER_REGISTRY(RegisterRadarIcons) +#define RadarIcons_from(i) _RadarIcons_from(i, RADARICON_NONE) +REGISTER_REGISTRY(RadarIcons) +REGISTRY_CHECK(RadarIcons) + .int m_radaricon; -#define REGISTER_RADARICON(id, num) REGISTER(RegisterRadarIcons, RADARICON, RadarIcons, id, m_id, new(RadarIcon)) { this.m_radaricon = num; this.netname = #id; } +#define REGISTER_RADARICON(id, num) REGISTER(RadarIcons, RADARICON, id, m_id, new(RadarIcon)) { make_pure(this); this.m_radaricon = num; this.netname = #id; } REGISTER_WAYPOINT(Null, "", '0 0 0', 1); @@ -48,7 +54,6 @@ REGISTER_RADARICON(OBJECTIVE, 1); REGISTER_RADARICON(DOMPOINT, 1); REGISTER_RADARICON(TAGGED, 1); -REGISTER_RADARICON(Buff, 1); REGISTER_RADARICON(Item, 1); REGISTER_RADARICON(Vehicle, 1); REGISTER_RADARICON(Weapon, 1); diff --git a/qcsrc/common/mutators/mutator/waypoints/module.inc b/qcsrc/common/mutators/mutator/waypoints/module.inc new file mode 100644 index 0000000000..50bb5b4d6e --- /dev/null +++ b/qcsrc/common/mutators/mutator/waypoints/module.inc @@ -0,0 +1 @@ +#include "waypointsprites.qc" diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index f0b768c574..a11feab9ec 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -4,11 +4,13 @@ REGISTER_MUTATOR(waypointsprites, true); +REGISTER_NET_LINKED(waypointsprites) + #ifdef SVQC /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) { - WriteMutator(MSG_ENTITY, waypointsprites); + WriteHeader(MSG_ENTITY, waypointsprites); sendflags = sendflags & 0x7F; @@ -97,9 +99,7 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) #ifdef CSQC void Ent_WaypointSprite(); -MUTATOR_HOOKFUNCTION(waypointsprites, CSQC_Ent_Update) { - if (MUTATOR_RETURNVALUE) return false; - if (!ReadMutatorEquals(mutator_argv_int_0, waypointsprites)) return false; +NET_HANDLE(waypointsprites, bool isnew) { Ent_WaypointSprite(); return true; } @@ -224,7 +224,7 @@ float spritelookupblinkvalue(string s) if (get_weaponinfo(self.wp_extra).spawnflags & WEP_FLAG_SUPERWEAPON) return 2; } - if (s == WP_Item.netname) return Items[self.wp_extra].m_waypointblink; + if (s == WP_Item.netname) return Items_from(self.wp_extra).m_waypointblink; return 1; } @@ -232,18 +232,24 @@ float spritelookupblinkvalue(string s) vector spritelookupcolor(entity this, string s, vector def) { if (s == WP_Weapon.netname || s == RADARICON_Weapon.netname) return get_weaponinfo(this.wp_extra).wpcolor; - if (s == WP_Item.netname || s == RADARICON_Item.netname) return Items[this.wp_extra].m_color; - if (s == WP_Buff.netname || s == RADARICON_Buff.netname) return Buffs[this.wp_extra].m_color; + if (s == WP_Item.netname || s == RADARICON_Item.netname) return Items_from(this.wp_extra).m_color; + if (MUTATOR_CALLHOOK(WP_Format, this, s)) + { + return MUTATOR_ARGV(0, vector); + } return def; } string spritelookuptext(string s) {SELFPARAM(); if (s == WP_RaceStartFinish.netname) return (race_checkpointtime || race_mycheckpointtime) ? _("Finish") : _("Start"); - if (s == WP_Weapon.netname) return get_weaponinfo(self.wp_extra).message; - if (s == WP_Item.netname) return Items[self.wp_extra].m_waypoint; - if (s == WP_Buff.netname) return Buffs[self.wp_extra].m_prettyName; + if (s == WP_Weapon.netname) return get_weaponinfo(self.wp_extra).m_name; + if (s == WP_Item.netname) return Items_from(self.wp_extra).m_waypoint; if (s == WP_Monster.netname) return get_monsterinfo(self.wp_extra).monster_name; + if (MUTATOR_CALLHOOK(WP_Format, this, s)) + { + return MUTATOR_ARGV(0, string); + } // need to loop, as our netname could be one of three FOREACH(Waypoints, it.netname == s, LAMBDA( @@ -371,11 +377,13 @@ vector drawspritetext(vector o, float ang, float minwidth, vector rgb, float a, if (fabs(sa) > fabs(ca)) { algnx = (sa < 0); - algny = 0.5 - 0.5 * ca / fabs(sa); + float f = fabs(sa); + algny = 0.5 - 0.5 * (f ? (ca / f) : 0); } else { - algnx = 0.5 - 0.5 * sa / fabs(ca); + float f = fabs(ca); + algnx = 0.5 - 0.5 * (f ? (sa / f) : 0); algny = (ca < 0); } @@ -993,6 +1001,7 @@ entity WaypointSprite_Spawn( ) { entity wp = new(sprite_waypoint); + make_pure(wp); wp.teleport_time = time + _lifetime; wp.fade_time = _lifetime; wp.exteriormodeltoclient = ref; diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc new file mode 100644 index 0000000000..b7a5f66901 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "weaponarena_random.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc new file mode 100644 index 0000000000..5c82100778 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc @@ -0,0 +1,13 @@ +#ifdef IMPLEMENTATION +// WEAPONTODO: rename the cvars +REGISTER_MUTATOR(weaponarena_random, true); + +MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) { + SELFPARAM(); + if (!g_weaponarena_random) return; + if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER); + W_RandomWeapons(this, g_weaponarena_random); + if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER); +} + +#endif diff --git a/qcsrc/common/nades/all.inc b/qcsrc/common/nades/all.inc deleted file mode 100644 index 90326ede1f..0000000000 --- a/qcsrc/common/nades/all.inc +++ /dev/null @@ -1,58 +0,0 @@ -#define NADE_PROJECTILE(i, projectile, trail) do { \ - this.m_projectile[i] = projectile; \ - this.m_trail[i] = trail; \ -} while (0) - -REGISTER_NADE(NORMAL) { - this.m_color = '1 1 1'; - NADE_PROJECTILE(0, PROJECTILE_NADE, EFFECT_Null); - NADE_PROJECTILE(1, PROJECTILE_NADE_BURN, EFFECT_Null); -} - -REGISTER_NADE(NAPALM) { - this.m_color = '2 0.5 0'; - this.m_name = _("Napalm grenade"); - this.m_icon = "nade_napalm"; - NADE_PROJECTILE(0, PROJECTILE_NADE_NAPALM, EFFECT_TR_ROCKET); - NADE_PROJECTILE(1, PROJECTILE_NADE_NAPALM_BURN, EFFECT_SPIDERBOT_ROCKET_TRAIL); -} - -REGISTER_NADE(ICE) { - this.m_color = '0 0.5 2'; - this.m_name = _("Ice grenade"); - this.m_icon = "nade_ice"; - NADE_PROJECTILE(0, PROJECTILE_NADE_ICE, EFFECT_TR_NEXUIZPLASMA); - NADE_PROJECTILE(1, PROJECTILE_NADE_ICE_BURN, EFFECT_RACER_ROCKET_TRAIL); -} - -REGISTER_NADE(TRANSLOCATE) { - this.m_color = '1 0 1'; - this.m_name = _("Translocate grenade"); - this.m_icon = "nade_translocate"; - NADE_PROJECTILE(0, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA); - NADE_PROJECTILE(1, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA); -} - -REGISTER_NADE(SPAWN) { - this.m_color = '1 0.9 0'; - this.m_name = _("Spawn grenade"); - this.m_icon = "nade_spawn"; - NADE_PROJECTILE(0, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW); - NADE_PROJECTILE(1, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW); -} - -REGISTER_NADE(HEAL) { - this.m_color = '1 0 0'; - this.m_name = _("Heal grenade"); - this.m_icon = "nade_heal"; - NADE_PROJECTILE(0, PROJECTILE_NADE_HEAL, EFFECT_NADE_TRAIL_RED); - NADE_PROJECTILE(1, PROJECTILE_NADE_HEAL_BURN, EFFECT_NADE_TRAIL_BURN_RED); -} - -REGISTER_NADE(MONSTER) { - this.m_color = '0.25 0.75 0'; - this.m_name = _("Monster grenade"); - this.m_icon = "nade_monster"; - NADE_PROJECTILE(0, PROJECTILE_NADE_MONSTER, EFFECT_NADE_TRAIL_RED); - NADE_PROJECTILE(1, PROJECTILE_NADE_MONSTER_BURN, EFFECT_NADE_TRAIL_BURN_RED); -} diff --git a/qcsrc/common/nades/all.qc b/qcsrc/common/nades/all.qc deleted file mode 100644 index 30ad1782f2..0000000000 --- a/qcsrc/common/nades/all.qc +++ /dev/null @@ -1,90 +0,0 @@ -#if defined(CSQC) - #include "../../client/defs.qh" - #include "all.qh" - #include "../buffs/all.qh" - #include "../movetypes/movetypes.qh" - #include "../../client/main.qh" - #include "../../lib/csqcmodel/cl_model.qh" -#elif defined(MENUQC) -#elif defined(SVQC) - #include "../constants.qh" - #include "../../server/constants.qh" - #include "../turrets/sv_turrets.qh" -#endif - - -#ifdef CSQC -.float ltime; -void healer_draw(entity this) -{ - float dt = time - self.move_time; - self.move_time = time; - if(dt <= 0) - return; - - self.alpha = (self.ltime - time) / self.healer_lifetime; - self.scale = min((1 - self.alpha)*self.healer_lifetime*4,1)*self.healer_radius; -} - -void healer_setup(entity e) -{ - setmodel(e, MDL_NADE_HEAL); - - setorigin(e, e.origin); - - float model_radius = e.maxs.x; - vector size = '1 1 1' * e.healer_radius / 2; - setsize(e,-size,size); - e.healer_radius = e.healer_radius/model_radius*0.6; - - e.draw = healer_draw; - e.health = 255; - e.movetype = MOVETYPE_NONE; - e.solid = SOLID_NOT; - e.drawmask = MASK_NORMAL; - e.scale = 0.01; - e.avelocity = e.move_avelocity = '7 0 11'; - e.colormod = '1 0 0'; - e.renderflags |= RF_ADDITIVE; -} -#endif // CSQC - -REGISTER_NET_LINKED(Nade_Heal, bool isNew) -#ifdef CSQC -{ - Net_Accept(); - int sf = ReadByte(); - if (sf & 1) { - this.origin_x = ReadCoord(); - this.origin_y = ReadCoord(); - this.origin_z = ReadCoord(); - setorigin(this, this.origin); - this.healer_lifetime = ReadByte(); - this.healer_radius = ReadShort(); - this.ltime = time + ReadByte()/10.0; - // this.ltime = time + this.healer_lifetime; - healer_setup(this); - } -} -#endif - -#ifdef SVQC -bool healer_send(entity this, entity to, int sf) -{ - int channel = MSG_ENTITY; - WriteHeader(channel, Nade_Heal); - WriteByte(channel, sf); - if (sf & 1) { - WriteCoord(channel, this.origin.x); - WriteCoord(channel, this.origin.y); - WriteCoord(channel, this.origin.z); - - WriteByte(channel, this.healer_lifetime); - //WriteByte(MSG_ENTITY, this.ltime - time + 1); - WriteShort(channel, this.healer_radius); - // round time delta to a 1/10th of a second - WriteByte(channel, (this.ltime - time)*10.0+0.5); - } - return true; -} -#endif // SVQC diff --git a/qcsrc/common/nades/all.qh b/qcsrc/common/nades/all.qh deleted file mode 100644 index ad51a1e55e..0000000000 --- a/qcsrc/common/nades/all.qh +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef NADES_ALL_H -#define NADES_ALL_H - -#include "../teams.qh" - -.float healer_lifetime; -.float healer_radius; - -// use slots 70-100 -const int PROJECTILE_NADE = 71; -const int PROJECTILE_NADE_BURN = 72; -const int PROJECTILE_NADE_NAPALM = 73; -const int PROJECTILE_NADE_NAPALM_BURN = 74; -const int PROJECTILE_NAPALM_FOUNTAIN = 75; -const int PROJECTILE_NADE_ICE = 76; -const int PROJECTILE_NADE_ICE_BURN = 77; -const int PROJECTILE_NADE_TRANSLOCATE = 78; -const int PROJECTILE_NADE_SPAWN = 79; -const int PROJECTILE_NADE_HEAL = 80; -const int PROJECTILE_NADE_HEAL_BURN = 81; -const int PROJECTILE_NADE_MONSTER = 82; -const int PROJECTILE_NADE_MONSTER_BURN = 83; - -REGISTRY(Nades, BIT(3)) -REGISTER_REGISTRY(RegisterNades) -#define REGISTER_NADE(id) REGISTER(RegisterNades, NADE_TYPE, Nades, id, m_id, NEW(Nade)) - -CLASS(Nade, Object) - ATTRIB(Nade, m_id, int, 0) - ATTRIB(Nade, m_color, vector, '0 0 0') - ATTRIB(Nade, m_name, string, _("Grenade")) - ATTRIB(Nade, m_icon, string, "nade_normal") - ATTRIBARRAY(Nade, m_projectile, int, 2) - ATTRIBARRAY(Nade, m_trail, entity, 2) - METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) { - returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon)); - } -ENDCLASS(Nade) - -REGISTER_NADE(Null); - -#ifdef SVQC -bool healer_send(entity this, entity to, int sf); -#endif - -entity Nade_FromProjectile(float proj) -{ - FOREACH(Nades, true, LAMBDA( - for (int j = 0; j < 2; j++) - { - if (it.m_projectile[j] == proj) return it; - } - )); - return NADE_TYPE_Null; -} - -entity Nade_TrailEffect(int proj, int nade_team) -{ - switch (proj) - { - case PROJECTILE_NADE: return EFFECT_NADE_TRAIL(nade_team); - case PROJECTILE_NADE_BURN: return EFFECT_NADE_TRAIL_BURN(nade_team); - } - - FOREACH(Nades, true, LAMBDA( - for (int j = 0; j < 2; j++) - { - if (it.m_projectile[j] == proj) - { - string trail = it.m_trail[j].eent_eff_name; - if (trail) return it.m_trail[j]; - break; - } - } - )); - - return EFFECT_Null; -} - -#include "all.inc" - -#endif diff --git a/qcsrc/common/net_notice.qc b/qcsrc/common/net_notice.qc index f748f99038..63e318d2f7 100644 --- a/qcsrc/common/net_notice.qc +++ b/qcsrc/common/net_notice.qc @@ -1,5 +1,7 @@ #include "net_notice.qh" +REGISTER_NET_TEMP(TE_CSQC_SVNOTICE) + #ifdef SVQC void sv_notice_join_think() {SELFPARAM(); @@ -29,8 +31,7 @@ void sv_notice_join() void sv_notice_to(entity _to, string _notice, float _howlong, float _modal) { msg_entity = _to; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_SVNOTICE); + WriteHeader(MSG_ONE, TE_CSQC_SVNOTICE); WriteString(MSG_ONE, _notice); WriteLong(MSG_ONE, _howlong); WriteByte(MSG_ONE, _modal); @@ -47,13 +48,16 @@ void sv_notice_toall(string _notice, float _howlong, float _modal) #endif // SVQC #ifdef CSQC +NET_HANDLE(TE_CSQC_SVNOTICE, bool isNew) +{ + cl_notice_read(); + return true; +} void cl_notice_read() { - entity _notice; //float _done; //float _modal; - _notice = spawn(); - _notice.classname = "sv_notice"; + entity _notice = new(sv_notice); _notice.netname = strzone(ReadString()); _notice.alpha = ReadLong() + time; _notice.skin = ReadByte(); @@ -68,8 +72,8 @@ float cl_notice_run() _notes = findchain(classname, "sv_notice"); if(!_notes) return false; - #define M1 30 - #define M2 10 + const int M1 = 30; + const int M2 = 10; vector v1, v2 = '0 0 0', v3; v1 = '1 1 0' * M1; @@ -107,8 +111,6 @@ float cl_notice_run() } #undef OUT - #undef M1 - #undef M2 return m; } diff --git a/qcsrc/common/notifications.qc b/qcsrc/common/notifications.qc index 811e7007c6..94a46d94ae 100644 --- a/qcsrc/common/notifications.qc +++ b/qcsrc/common/notifications.qc @@ -1,4 +1,5 @@ #if defined(CSQC) + #include "../client/announcer.qh" #elif defined(MENUQC) #elif defined(SVQC) #include "constants.qh" @@ -233,7 +234,7 @@ void Destroy_Notification_Entity(entity notif) remove(notif); } -void Destroy_All_Notifications(void) +void Destroy_All_Notifications() { entity notif; int i; @@ -523,6 +524,7 @@ void Create_Notification_Entity( // Global Entity Setup // ===================== entity notif = spawn(); + make_pure(notif); switch(typeId) { case MSG_ANNCE: @@ -595,7 +597,7 @@ void Create_Notification_Entity( { if(notif.nent_enabled) { - precache_sound(sprintf("announcer/%s/%s.wav", autocvar_cl_announcer, snd)); + precache_sound(sprintf("announcer/%s/%s.wav", AnnouncerOption(), snd)); notif.nent_channel = channel; notif.nent_snd = strzone(snd); notif.nent_vol = vol; @@ -941,7 +943,7 @@ void Create_Notification_Entity( // used by MSG_CHOICE to build list of choices #ifdef SVQC -void Notification_GetCvars(void) +void Notification_GetCvars() { for(int i = 0; i <= NOTIF_CHOICE_COUNT; ++i) { @@ -1276,7 +1278,7 @@ void Local_Notification_sound( soundchannel, sprintf( "announcer/%s/%s.wav", - autocvar_cl_announcer, + AnnouncerOption(), soundfile ), soundvolume, @@ -1289,7 +1291,7 @@ void Local_Notification_sound( soundchannel, sprintf( "announcer/%s/%s.wav", - autocvar_cl_announcer, + AnnouncerOption(), soundfile ), soundvolume, @@ -1311,7 +1313,7 @@ void Local_Notification_sound( soundchannel, sprintf( "announcer/%s/%s.wav", - autocvar_cl_announcer, + AnnouncerOption(), soundfile ), soundvolume, @@ -1677,11 +1679,14 @@ void Local_Notification_WOVA( // Notification Networking // ========================= +REGISTER_NET_LINKED(ENT_CLIENT_NOTIFICATION) + #ifdef CSQC -void Read_Notification(float is_new) +NET_HANDLE(ENT_CLIENT_NOTIFICATION, bool is_new) { int net_type = ReadByte(); int net_name = ReadShort(); + return = true; entity notif; @@ -1775,7 +1780,7 @@ bool Net_Write_Notification(entity this, entity client, int sf) { if(Notification_ShouldSend(self.nent_broadcast, client, self.nent_client)) { - WriteByte(MSG_ENTITY, ENT_CLIENT_NOTIFICATION); + WriteHeader(MSG_ENTITY, ENT_CLIENT_NOTIFICATION); WriteByte(MSG_ENTITY, self.nent_net_type); WriteShort(MSG_ENTITY, self.nent_net_name); for(int i = 0; i < self.nent_stringcount; ++i) { WriteString(MSG_ENTITY, self.nent_strings[i]); } @@ -1841,8 +1846,8 @@ void Kill_Notification( if(killed_cpid != NO_CPID) { - net_notif = spawn(); - net_notif.classname = "net_kill_notification"; + net_notif = new(net_kill_notification); + make_pure(net_notif); net_notif.nent_broadcast = broadcast; net_notif.nent_client = client; net_notif.nent_net_type = MSG_CENTER_CPID; @@ -2092,9 +2097,9 @@ void Send_Notification( } else { - entity net_notif = spawn(); + entity net_notif = new(net_notification); + make_pure(net_notif); net_notif.owner = notif; - net_notif.classname = "net_notification"; net_notif.nent_broadcast = broadcast; net_notif.nent_client = client; net_notif.nent_net_type = net_type; diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index 6c6aa0f824..f3cd0ba494 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -50,7 +50,7 @@ const int NOTIF_ABORT = -1234; // allows Send_Notification to safely abort sen VARITEM(3, 4, XPD(s1, s2, s3, f1, f2, f3, f4)) \ VARITEM(4, 4, XPD(s1, s2, s3, s4, f1, f2, f3, f4)) -void Destroy_All_Notifications(void); +void Destroy_All_Notifications(); void Create_Notification_Entity( float var_default, float var_cvar, @@ -148,7 +148,6 @@ void Local_Notification_WOVA( float f1, float f2, float f3, float f4); #ifdef CSQC // CLIENT ONLY -void Read_Notification(float is_new); string prev_soundfile; float prev_soundtime; #endif @@ -203,7 +202,7 @@ float autocvar_notification_debug = false; #ifdef SVQC .float FRAG_VERBOSE; -void Notification_GetCvars(void); +void Notification_GetCvars(); float autocvar_notification_server_allows_location = 1; // 0 = no, 1 = yes #else float autocvar_notification_item_centerprinttime = 1.5; @@ -285,6 +284,8 @@ const float ARG_DC = 6; // unique result to durcnt/centerprint // todo possible idea.... declare how many floats/strings each arg needs, and then dynamically increment the input // this way, we don't need to have duplicates like i.e. s2loc and s3loc? +string BUFF_NAME(int i); + #define NOTIF_ARGUMENT_LIST \ ARG_CASE(ARG_CS_SV_HA, "s1", s1) \ ARG_CASE(ARG_CS_SV_HA, "s2", s2) \ @@ -318,8 +319,8 @@ const float ARG_DC = 6; // unique result to durcnt/centerprint ARG_CASE(ARG_CS_SV, "spree_end", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \ ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \ ARG_CASE(ARG_CS_SV, "item_wepname", WEP_NAME(f1)) \ - ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs[f1].m_color), Buffs[f1].m_prettyName)) \ - ARG_CASE(ARG_CS_SV, "f3buffname", sprintf("%s%s", rgb_to_hexcolor(Buffs[f3].m_color), Buffs[f3].m_prettyName)) \ + ARG_CASE(ARG_CS_SV, "item_buffname", BUFF_NAME(f1)) \ + ARG_CASE(ARG_CS_SV, "f3buffname", BUFF_NAME(f3)) \ ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \ ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \ ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \ @@ -574,11 +575,11 @@ float NOTIF_MULTI_COUNT; float NOTIF_CHOICE_COUNT; // notification limits -- INCREASE AS NECESSARY -const float NOTIF_ANNCE_MAX = 100; -const float NOTIF_INFO_MAX = 350; -const float NOTIF_CENTER_MAX = 250; -const float NOTIF_MULTI_MAX = 200; -const float NOTIF_CHOICE_MAX = 30; +const float NOTIF_ANNCE_MAX = 400; +const float NOTIF_INFO_MAX = 450; +const float NOTIF_CENTER_MAX = 350; +const float NOTIF_MULTI_MAX = 300; +const float NOTIF_CHOICE_MAX = 50; // notification entities entity msg_annce_notifs[NOTIF_ANNCE_MAX]; diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc index b3954d2eef..a49f79df83 100644 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@ -535,7 +535,7 @@ When you press the jump key returns true if handled ============= */ -bool PlayerJump (void) +bool PlayerJump () {SELFPARAM(); if (PHYS_FROZEN(self)) return true; // no jumping in freezetag when frozen @@ -1012,7 +1012,7 @@ float PM_check_specialcommand(float buttons) return false; } -void PM_check_nickspam(void) +void PM_check_nickspam() {SELFPARAM(); #ifdef SVQC if (time >= self.nickspamtime) @@ -1057,7 +1057,7 @@ void PM_check_punch() #endif } -void PM_check_spider(void) +void PM_check_spider() {SELFPARAM(); #ifdef SVQC if (time >= self.spider_slowness) @@ -1070,7 +1070,7 @@ void PM_check_spider(void) } // predict frozen movement, as frozen players CAN move in some cases -void PM_check_frozen(void) +void PM_check_frozen() {SELFPARAM(); if (!PHYS_FROZEN(self)) return; @@ -1115,16 +1115,16 @@ void PM_check_hitground() if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS)) { if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) - GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND); + GlobalSound(GS_FALL_METAL, CH_PLAYER, VOICETYPE_PLAYERSOUND); else - GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND); + GlobalSound(GS_FALL, CH_PLAYER, VOICETYPE_PLAYERSOUND); } } } #endif } -void PM_check_blocked(void) +void PM_check_blocked() {SELFPARAM(); #ifdef SVQC if (!self.player_blocked) @@ -1134,7 +1134,7 @@ void PM_check_blocked(void) #endif } -void PM_check_vortex(void) +void PM_check_vortex() {SELFPARAM(); #ifdef SVQC // WEAPONTODO @@ -1819,7 +1819,7 @@ void PM_Main() RaceCarPhysics(); #endif - else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY || (BUFFS_STAT(self) & BUFF_FLIGHT.m_itemid)) + else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY || self.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, self)) PM_fly(maxspeed_mod); else if (self.waterlevel >= WATERLEVEL_SWIMMING) @@ -1858,9 +1858,9 @@ void PM_Main() } #ifdef SVQC -void SV_PlayerPhysics(void) +void SV_PlayerPhysics() #elif defined(CSQC) -void CSQC_ClientMovement_PlayerMove_Frame(void) +void CSQC_ClientMovement_PlayerMove_Frame() #endif {SELFPARAM(); PM_Main(); diff --git a/qcsrc/common/physics.qh b/qcsrc/common/physics.qh index d1f82991cd..5ee8954d0c 100644 --- a/qcsrc/common/physics.qh +++ b/qcsrc/common/physics.qh @@ -33,6 +33,7 @@ bool IsFlying(entity a); void PM_multijump(); .float watertype; + .float waterlevel; .int items; .vector movement; diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 0fcbd4259a..7584e3353c 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -11,7 +11,7 @@ #endif #ifdef SVQC -void PlayerStats_Prematch(void) +void PlayerStats_Prematch() { //foobar } @@ -635,7 +635,7 @@ void PlayerStats_PlayerDetail_AddItem(string event, string data) LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB\n"); } -void PlayerStats_PlayerDetail(void) +void PlayerStats_PlayerDetail() { // http://stats.xonotic.org/player/me if((autocvar_g_playerstats_playerdetail_uri != "") && (crypto_getmyidstatus(0) > 0)) @@ -668,7 +668,7 @@ void PlayerStats_PlayerDetail(void) } } -void PlayerStats_PlayerDetail_CheckUpdate(void) +void PlayerStats_PlayerDetail_CheckUpdate() { // determine whether we should retrieve playerdetail information again float gamecount = cvar("cl_matchcount"); diff --git a/qcsrc/common/playerstats.qh b/qcsrc/common/playerstats.qh index e77cdbff59..ec309c4f4c 100644 --- a/qcsrc/common/playerstats.qh +++ b/qcsrc/common/playerstats.qh @@ -112,8 +112,8 @@ const float PS_D_STATUS_RECEIVED = 1; float PlayerStats_PlayerDetail_Status = PS_D_STATUS_IDLE; string autocvar_g_playerstats_playerdetail_uri = "http://stats.xonotic.org/player/me"; float autocvar_g_playerstats_playerdetail_autoupdatetime = 1800; // automatically update every 30 minutes anyway -void PlayerStats_PlayerDetail(void); -void PlayerStats_PlayerDetail_CheckUpdate(void); +void PlayerStats_PlayerDetail(); +void PlayerStats_PlayerDetail_CheckUpdate(); void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status); #endif #endif diff --git a/qcsrc/common/sounds/all.inc b/qcsrc/common/sounds/all.inc index 8a7dbbca37..69a95be0cc 100644 --- a/qcsrc/common/sounds/all.inc +++ b/qcsrc/common/sounds/all.inc @@ -36,7 +36,7 @@ SOUND(GRENADE_BOUNCE4, W_Sound("grenade_bounce4")); SOUND(GRENADE_BOUNCE5, W_Sound("grenade_bounce5")); SOUND(GRENADE_BOUNCE6, W_Sound("grenade_bounce6")); Sound SND_GRENADE_BOUNCE_RANDOM() { - return Sounds[SND_GRENADE_BOUNCE1.m_id + rint(random() * 5)]; + return Sounds_from(SND_GRENADE_BOUNCE1.m_id + rint(random() * 5)); } SOUND(GRENADE_FIRE, W_Sound("grenade_fire")); SOUND(GRENADE_IMPACT, W_Sound("grenade_impact")); @@ -48,7 +48,7 @@ SOUND(HAGEXP1, W_Sound("hagexp1")); SOUND(HAGEXP2, W_Sound("hagexp2")); SOUND(HAGEXP3, W_Sound("hagexp3")); Sound SND_HAGEXP_RANDOM() { - return Sounds[SND_HAGEXP1.m_id + rint(random() * 2)]; + return Sounds_from(SND_HAGEXP1.m_id + rint(random() * 2)); } SOUND(HOOKBOMB_FIRE, W_Sound("hookbomb_fire")); @@ -71,7 +71,7 @@ SOUND(NEXWHOOSH1, W_Sound("nexwhoosh1")); SOUND(NEXWHOOSH2, W_Sound("nexwhoosh2")); SOUND(NEXWHOOSH3, W_Sound("nexwhoosh3")); Sound SND_NEXWHOOSH_RANDOM() { - return Sounds[SND_NEXWHOOSH1.m_id + rint(random() * 2)]; + return Sounds_from(SND_NEXWHOOSH1.m_id + rint(random() * 2)); } SOUND(RELOAD, W_Sound("reload")); // until weapons have individual reload sounds, precache the reload sound here @@ -79,7 +79,7 @@ SOUND(RIC1, W_Sound("ric1")); SOUND(RIC2, W_Sound("ric2")); SOUND(RIC3, W_Sound("ric3")); Sound SND_RIC_RANDOM() { - return Sounds[SND_RIC1.m_id + rint(random() * 2)]; + return Sounds_from(SND_RIC1.m_id + rint(random() * 2)); } SOUND(ROCKET_DET, W_Sound("rocket_det")); @@ -106,11 +106,11 @@ SOUND(WEAPONPICKUP, W_Sound("weaponpickup")); SOUND(WEAPONPICKUP_NEW_TOYS, W_Sound("weaponpickup_new_toys")); SOUND(WEAPON_SWITCH, W_Sound("weapon_switch")); -SOUND(CTF_CAPTURE_NEUTRAL, "ctf/capture.ogg"); -SOUND(CTF_CAPTURE_RED, "ctf/red_capture.wav"); -SOUND(CTF_CAPTURE_BLUE, "ctf/blue_capture.wav"); -SOUND(CTF_CAPTURE_YELLOW, "ctf/yellow_capture.ogg"); -SOUND(CTF_CAPTURE_PINK, "ctf/pink_capture.ogg"); +SOUND(CTF_CAPTURE_NEUTRAL, "ctf/capture"); +SOUND(CTF_CAPTURE_RED, "ctf/red_capture"); +SOUND(CTF_CAPTURE_BLUE, "ctf/blue_capture"); +SOUND(CTF_CAPTURE_YELLOW, "ctf/yellow_capture"); +SOUND(CTF_CAPTURE_PINK, "ctf/pink_capture"); Sound SND_CTF_CAPTURE(int teamid) { switch (teamid) { case NUM_TEAM_1: return SND_CTF_CAPTURE_RED; @@ -121,11 +121,11 @@ Sound SND_CTF_CAPTURE(int teamid) { } } -SOUND(CTF_DROPPED_NEUTRAL, "ctf/neutral_dropped.wav"); -SOUND(CTF_DROPPED_RED, "ctf/red_dropped.wav"); -SOUND(CTF_DROPPED_BLUE, "ctf/blue_dropped.wav"); -SOUND(CTF_DROPPED_YELLOW, "ctf/yellow_dropped.wav"); -SOUND(CTF_DROPPED_PINK, "ctf/pink_dropped.wav"); +SOUND(CTF_DROPPED_NEUTRAL, "ctf/neutral_dropped"); +SOUND(CTF_DROPPED_RED, "ctf/red_dropped"); +SOUND(CTF_DROPPED_BLUE, "ctf/blue_dropped"); +SOUND(CTF_DROPPED_YELLOW, "ctf/yellow_dropped"); +SOUND(CTF_DROPPED_PINK, "ctf/pink_dropped"); Sound SND_CTF_DROPPED(int teamid) { switch (teamid) { case NUM_TEAM_1: return SND_CTF_DROPPED_RED; @@ -136,14 +136,14 @@ Sound SND_CTF_DROPPED(int teamid) { } } -SOUND(CTF_PASS, "ctf/pass.wav"); -SOUND(CTF_RESPAWN, "ctf/flag_respawn.wav"); +SOUND(CTF_PASS, "ctf/pass"); +SOUND(CTF_RESPAWN, "ctf/flag_respawn"); -SOUND(CTF_RETURNED_NEUTRAL, "ctf/return.wav"); -SOUND(CTF_RETURNED_RED, "ctf/red_returned.wav"); -SOUND(CTF_RETURNED_BLUE, "ctf/blue_returned.wav"); -SOUND(CTF_RETURNED_YELLOW, "ctf/yellow_returned.wav"); -SOUND(CTF_RETURNED_PINK, "ctf/pink_returned.wav"); +SOUND(CTF_RETURNED_NEUTRAL, "ctf/return"); +SOUND(CTF_RETURNED_RED, "ctf/red_returned"); +SOUND(CTF_RETURNED_BLUE, "ctf/blue_returned"); +SOUND(CTF_RETURNED_YELLOW, "ctf/yellow_returned"); +SOUND(CTF_RETURNED_PINK, "ctf/pink_returned"); Sound SND_CTF_RETURNED(int teamid) { switch (teamid) { case NUM_TEAM_1: return SND_CTF_RETURNED_RED; @@ -154,11 +154,11 @@ Sound SND_CTF_RETURNED(int teamid) { } } -SOUND(CTF_TAKEN_NEUTRAL, "ctf/neutral_taken.wav"); -SOUND(CTF_TAKEN_RED, "ctf/red_taken.wav"); -SOUND(CTF_TAKEN_BLUE, "ctf/blue_taken.wav"); -SOUND(CTF_TAKEN_YELLOW, "ctf/yellow_taken.wav"); -SOUND(CTF_TAKEN_PINK, "ctf/pink_taken.wav"); +SOUND(CTF_TAKEN_NEUTRAL, "ctf/neutral_taken"); +SOUND(CTF_TAKEN_RED, "ctf/red_taken"); +SOUND(CTF_TAKEN_BLUE, "ctf/blue_taken"); +SOUND(CTF_TAKEN_YELLOW, "ctf/yellow_taken"); +SOUND(CTF_TAKEN_PINK, "ctf/pink_taken"); Sound SND_CTF_TAKEN(int teamid) { switch (teamid) { case NUM_TEAM_1: return SND_CTF_TAKEN_RED; @@ -169,109 +169,109 @@ Sound SND_CTF_TAKEN(int teamid) { } } -SOUND(CTF_TOUCH, "ctf/touch.wav"); - -SOUND(DOM_CLAIM, "domination/claim.wav"); - -SOUND(KA_DROPPED, "keepaway/dropped.wav"); -SOUND(KA_PICKEDUP, "keepaway/pickedup.wav"); -SOUND(KA_RESPAWN, "keepaway/respawn.wav"); -SOUND(KA_TOUCH, "keepaway/touch.wav"); - -SOUND(KH_ALARM, "kh/alarm.wav"); -SOUND(KH_CAPTURE, "kh/capture.wav"); -SOUND(KH_COLLECT, "kh/collect.wav"); -SOUND(KH_DESTROY, "kh/destroy.wav"); -SOUND(KH_DROP, "kh/drop.wav"); - -SOUND(NB_BOUNCE, "nexball/bounce.ogg"); -SOUND(NB_DROP, "nexball/drop.ogg"); -SOUND(NB_SHOOT1, "nexball/shoot1.ogg"); -SOUND(NB_SHOOT2, "nexball/shoot2.ogg"); -SOUND(NB_STEAL, "nexball/steal.ogg"); - -SOUND(ONS_CONTROLPOINT_BUILD, "onslaught/controlpoint_build.ogg"); -SOUND(ONS_CONTROLPOINT_BUILT, "onslaught/controlpoint_built.ogg"); -SOUND(ONS_CONTROLPOINT_UNDERATTACK, "onslaught/controlpoint_underattack.ogg"); -SOUND(ONS_DAMAGEBLOCKEDBYSHIELD, "onslaught/damageblockedbyshield.wav"); -SOUND(ONS_ELECTRICITY_EXPLODE, "onslaught/electricity_explode.ogg"); -SOUND(ONS_GENERATOR_DECAY, "onslaught/generator_decay.ogg"); -SOUND(ONS_GENERATOR_UNDERATTACK, "onslaught/generator_underattack.ogg"); -SOUND(ONS_HIT1, "onslaught/ons_hit1.ogg"); -SOUND(ONS_HIT2, "onslaught/ons_hit2.ogg"); -SOUND(ONS_SPARK1, "onslaught/ons_spark1.ogg"); -SOUND(ONS_SPARK2, "onslaught/ons_spark2.ogg"); -SOUND(ONS_SHOCKWAVE, "onslaught/shockwave.ogg"); - -SOUND(PORTO_BOUNCE, "porto/bounce.ogg"); -SOUND(PORTO_CREATE, "porto/create.ogg"); -SOUND(PORTO_EXPIRE, "porto/expire.ogg"); -SOUND(PORTO_EXPLODE, "porto/explode.ogg"); -SOUND(PORTO_FIRE, "porto/fire.ogg"); -SOUND(PORTO_UNSUPPORTED, "porto/unsupported.ogg"); - -SOUND(TUR_PHASER, "turrets/phaser.ogg"); - -SOUND(VEH_ALARM, "vehicles/alarm.wav"); -SOUND(VEH_ALARM_SHIELD, "vehicles/alarm_shield.wav"); -SOUND(VEH_MISSILE_ALARM, "vehicles/missile_alarm.wav"); +SOUND(CTF_TOUCH, "ctf/touch"); + +SOUND(DOM_CLAIM, "domination/claim"); + +SOUND(KA_DROPPED, "keepaway/dropped"); +SOUND(KA_PICKEDUP, "keepaway/pickedup"); +SOUND(KA_RESPAWN, "keepaway/respawn"); +SOUND(KA_TOUCH, "keepaway/touch"); + +SOUND(KH_ALARM, "kh/alarm"); +SOUND(KH_CAPTURE, "kh/capture"); +SOUND(KH_COLLECT, "kh/collect"); +SOUND(KH_DESTROY, "kh/destroy"); +SOUND(KH_DROP, "kh/drop"); + +SOUND(NB_BOUNCE, "nexball/bounce"); +SOUND(NB_DROP, "nexball/drop"); +SOUND(NB_SHOOT1, "nexball/shoot1"); +SOUND(NB_SHOOT2, "nexball/shoot2"); +SOUND(NB_STEAL, "nexball/steal"); + +SOUND(ONS_CONTROLPOINT_BUILD, "onslaught/controlpoint_build"); +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_DECAY, "onslaught/generator_decay"); +SOUND(ONS_GENERATOR_UNDERATTACK, "onslaught/generator_underattack"); +SOUND(ONS_HIT1, "onslaught/ons_hit1"); +SOUND(ONS_HIT2, "onslaught/ons_hit2"); +SOUND(ONS_SPARK1, "onslaught/ons_spark1"); +SOUND(ONS_SPARK2, "onslaught/ons_spark2"); +SOUND(ONS_SHOCKWAVE, "onslaught/shockwave"); + +SOUND(PORTO_BOUNCE, "porto/bounce"); +SOUND(PORTO_CREATE, "porto/create"); +SOUND(PORTO_EXPIRE, "porto/expire"); +SOUND(PORTO_EXPLODE, "porto/explode"); +SOUND(PORTO_FIRE, "porto/fire"); +SOUND(PORTO_UNSUPPORTED, "porto/unsupported"); + +SOUND(TUR_PHASER, "turrets/phaser"); + +SOUND(VEH_ALARM, "vehicles/alarm"); +SOUND(VEH_ALARM_SHIELD, "vehicles/alarm_shield"); +SOUND(VEH_MISSILE_ALARM, "vehicles/missile_alarm"); SOUND(VEH_BUMBLEBEE_FIRE, W_Sound("flacexp3")); -SOUND(VEH_RACER_BOOST, "vehicles/racer_boost.wav"); -SOUND(VEH_RACER_IDLE, "vehicles/racer_idle.wav"); -SOUND(VEH_RACER_MOVE, "vehicles/racer_move.wav"); +SOUND(VEH_RACER_BOOST, "vehicles/racer_boost"); +SOUND(VEH_RACER_IDLE, "vehicles/racer_idle"); +SOUND(VEH_RACER_MOVE, "vehicles/racer_move"); -SOUND(VEH_RAPTOR_FLY, "vehicles/raptor_fly.wav"); -SOUND(VEH_RAPTOR_SPEED, "vehicles/raptor_speed.wav"); +SOUND(VEH_RAPTOR_FLY, "vehicles/raptor_fly"); +SOUND(VEH_RAPTOR_SPEED, "vehicles/raptor_speed"); -SOUND(VEH_SPIDERBOT_DIE, "vehicles/spiderbot_die.wav"); -SOUND(VEH_SPIDERBOT_IDLE, "vehicles/spiderbot_idle.wav"); -SOUND(VEH_SPIDERBOT_JUMP, "vehicles/spiderbot_jump.wav"); -SOUND(VEH_SPIDERBOT_LAND, "vehicles/spiderbot_land.wav"); -SOUND(VEH_SPIDERBOT_STRAFE, "vehicles/spiderbot_strafe.wav"); -SOUND(VEH_SPIDERBOT_WALK, "vehicles/spiderbot_walk.wav"); +SOUND(VEH_SPIDERBOT_DIE, "vehicles/spiderbot_die"); +SOUND(VEH_SPIDERBOT_IDLE, "vehicles/spiderbot_idle"); +SOUND(VEH_SPIDERBOT_JUMP, "vehicles/spiderbot_jump"); +SOUND(VEH_SPIDERBOT_LAND, "vehicles/spiderbot_land"); +SOUND(VEH_SPIDERBOT_STRAFE, "vehicles/spiderbot_strafe"); +SOUND(VEH_SPIDERBOT_WALK, "vehicles/spiderbot_walk"); -SOUND(NADE_BEEP, "overkill/grenadebip.ogg"); +SOUND(NADE_BEEP, "overkill/grenadebip"); -SOUND(BUFF_LOST, "relics/relic_effect.wav"); +SOUND(BUFF_LOST, "relics/relic_effect"); -SOUND(POWEROFF, "misc/poweroff.wav"); -SOUND(POWERUP, "misc/powerup.ogg"); -SOUND(SHIELD_RESPAWN, "misc/shield_respawn.wav"); -SOUND(STRENGTH_RESPAWN, "misc/strength_respawn.wav"); +SOUND(POWEROFF, "misc/poweroff"); +SOUND(POWERUP, "misc/powerup"); +SOUND(SHIELD_RESPAWN, "misc/shield_respawn"); +SOUND(STRENGTH_RESPAWN, "misc/strength_respawn"); -SOUND(ARMOR25, "misc/armor25.wav"); -SOUND(ARMORIMPACT, "misc/armorimpact.wav"); -SOUND(BODYIMPACT1, "misc/bodyimpact1.wav"); -SOUND(BODYIMPACT2, "misc/bodyimpact2.wav"); +SOUND(ARMOR25, "misc/armor25"); +SOUND(ARMORIMPACT, "misc/armorimpact"); +SOUND(BODYIMPACT1, "misc/bodyimpact1"); +SOUND(BODYIMPACT2, "misc/bodyimpact2"); -SOUND(ITEMPICKUP, "misc/itempickup.ogg"); -SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown.ogg"); -SOUND(ITEMRESPAWN, "misc/itemrespawn.ogg"); -SOUND(MEGAHEALTH, "misc/megahealth.ogg"); +SOUND(ITEMPICKUP, "misc/itempickup"); +SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown"); +SOUND(ITEMRESPAWN, "misc/itemrespawn"); +SOUND(MEGAHEALTH, "misc/megahealth"); -SOUND(LAVA, "player/lava.wav"); -SOUND(SLIME, "player/slime.wav"); +SOUND(LAVA, "player/lava"); +SOUND(SLIME, "player/slime"); -SOUND(GIB, "misc/gib.wav"); -SOUND(GIB_SPLAT01, "misc/gib_splat01.wav"); -SOUND(GIB_SPLAT02, "misc/gib_splat02.wav"); -SOUND(GIB_SPLAT03, "misc/gib_splat03.wav"); -SOUND(GIB_SPLAT04, "misc/gib_splat04.wav"); +SOUND(GIB, "misc/gib"); +SOUND(GIB_SPLAT01, "misc/gib_splat01"); +SOUND(GIB_SPLAT02, "misc/gib_splat02"); +SOUND(GIB_SPLAT03, "misc/gib_splat03"); +SOUND(GIB_SPLAT04, "misc/gib_splat04"); Sound SND_GIB_SPLAT_RANDOM() { - return Sounds[SND_GIB_SPLAT01.m_id + floor(prandom() * 4)]; + return Sounds_from(SND_GIB_SPLAT01.m_id + floor(prandom() * 4)); } -SOUND(HIT, "misc/hit.wav"); -SOUND(TYPEHIT, "misc/typehit.wav"); +SOUND(HIT, "misc/hit"); +SOUND(TYPEHIT, "misc/typehit"); -SOUND(SPAWN, "misc/spawn.ogg"); +SOUND(SPAWN, "misc/spawn"); -SOUND(TALK, "misc/talk.wav"); +SOUND(TALK, "misc/talk"); -SOUND(TELEPORT, "misc/teleport.ogg"); +SOUND(TELEPORT, "misc/teleport"); -SOUND(INVSHOT, "misc/invshot.wav"); +SOUND(INVSHOT, "misc/invshot"); -SOUND(JETPACK_FLY, "misc/jetpack_fly.ogg"); +SOUND(JETPACK_FLY, "misc/jetpack_fly"); diff --git a/qcsrc/common/sounds/all.qc b/qcsrc/common/sounds/all.qc new file mode 100644 index 0000000000..92c5ef059a --- /dev/null +++ b/qcsrc/common/sounds/all.qc @@ -0,0 +1,144 @@ +#ifdef SVQC + +bool autocvar_bot_sound_monopoly; + +.entity realowner; +bool sound_allowed(int to, entity e) +{ + for ( ; ; ) + { + if (e.classname == "body") e = e.enemy; + else if (e.realowner && e.realowner != e) e = e.realowner; + else if (e.owner && e.owner != e) e = e.owner; + else break; + } + // sounds to self may always pass + if (to == MSG_ONE && e == msg_entity) return true; + // sounds by players can be removed + if (autocvar_bot_sound_monopoly && IS_REAL_CLIENT(e)) return false; + // anything else may pass + return true; +} + +/** hack: string precache_sound(string s) = #19; */ +int precache_sound_index(string s) = #19; + +const int SVC_SOUND = 6; +const int SVC_STOPSOUND = 16; + +const int SND_VOLUME = BIT(0); +const int SND_ATTENUATION = BIT(1); +const int SND_LARGEENTITY = BIT(3); +const int SND_LARGESOUND = BIT(4); + +void soundtoat(int to, entity e, vector o, int chan, string samp, float vol, float attenu) +{ + if (!sound_allowed(to, e)) return; + int entno = etof(e); + int idx = precache_sound_index(samp); + attenu = floor(attenu * 64); + vol = floor(vol * 255); + int sflags = 0; + if (vol != 255) sflags |= SND_VOLUME; + if (attenu != 64) sflags |= SND_ATTENUATION; + if (entno >= 8192 || chan < 0 || chan > 7) sflags |= SND_LARGEENTITY; + if (idx >= 256) sflags |= SND_LARGESOUND; + WriteByte(to, SVC_SOUND); + WriteByte(to, sflags); + if (sflags & SND_VOLUME) WriteByte(to, vol); + if (sflags & SND_ATTENUATION) WriteByte(to, attenu); + if (sflags & SND_LARGEENTITY) + { + WriteShort(to, entno); + WriteByte(to, chan); + } + else + { + WriteShort(to, (entno << 3) | chan); + } + if (sflags & SND_LARGESOUND) WriteShort(to, idx); + else WriteByte(to, idx); + WriteCoord(to, o.x); + WriteCoord(to, o.y); + WriteCoord(to, o.z); +} + +void soundto(int _dest, entity e, int chan, string samp, float vol, float _atten) +{ + if (!sound_allowed(_dest, e)) return; + vector o = e.origin + 0.5 * (e.mins + e.maxs); + soundtoat(_dest, e, o, chan, samp, vol, _atten); +} +void soundat(entity e, vector o, int chan, string samp, float vol, float _atten) +{ + soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten); +} +void stopsoundto(int _dest, entity e, int chan) +{ + if (!sound_allowed(_dest, e)) return; + int entno = num_for_edict(e); + if (entno >= 8192 || chan < 0 || chan > 7) + { + int idx = precache_sound_index(SND(Null)); + int sflags = SND_LARGEENTITY; + if (idx >= 256) sflags |= SND_LARGESOUND; + WriteByte(_dest, SVC_SOUND); + WriteByte(_dest, sflags); + WriteShort(_dest, entno); + WriteByte(_dest, chan); + if (sflags & SND_LARGESOUND) WriteShort(_dest, idx); + else WriteByte(_dest, idx); + WriteCoord(_dest, e.origin.x); + WriteCoord(_dest, e.origin.y); + WriteCoord(_dest, e.origin.z); + } + else + { + WriteByte(_dest, SVC_STOPSOUND); + WriteShort(_dest, entno * 8 + chan); + } +} +void stopsound(entity e, int chan) +{ + if (!sound_allowed(MSG_BROADCAST, e)) return; + stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast + stopsoundto(MSG_ALL, e, chan); // in case of packet loss +} + +void play2(entity e, string filename) +{ + msg_entity = e; + soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE); +} + +.float spamtime; +/** use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame) */ +float spamsound(entity e, int chan, string samp, float vol, float _atten) +{ + if (!sound_allowed(MSG_BROADCAST, e)) return false; + if (time > e.spamtime) + { + e.spamtime = time; + _sound(e, chan, samp, vol, _atten); + return true; + } + return false; +} + +void play2team(float t, string filename) +{ + if (autocvar_bot_sound_monopoly) return; + entity head; + FOR_EACH_REALPLAYER(head) + { + if (head.team == t) play2(head, filename); + } +} + +void play2all(string samp) +{ + if (autocvar_bot_sound_monopoly) return; + _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE); +} + +#endif diff --git a/qcsrc/common/sounds/all.qh b/qcsrc/common/sounds/all.qh index d39c701d7f..8225db7368 100644 --- a/qcsrc/common/sounds/all.qh +++ b/qcsrc/common/sounds/all.qh @@ -4,22 +4,23 @@ #include "sound.qh" REGISTRY(Sounds, BITS(8)) -REGISTER_REGISTRY(RegisterSounds) +#define Sounds_from(i) _Sounds_from(i, SND_Null) +REGISTER_REGISTRY(Sounds) #define SOUND(name, path) \ string SND_##name##_get() { return path; } \ - REGISTER(RegisterSounds, SND, Sounds, name, m_id, NEW(Sound, SND_##name##_get)) + REGISTER(Sounds, SND, name, m_id, NEW(Sound, SND_##name##_get)) // Used in places where a string is required -#define SND(id) (SND_##id.sound_str()) +#define SND(id) Sound_fixpath(SND_##id) -STATIC_INIT(RegisterSounds_precache) { +PRECACHE(Sounds) { FOREACH(Sounds, true, LAMBDA({ it.sound_precache(it); })); } -SOUND(Null, "misc/null.wav"); +SOUND(Null, "misc/null"); #include "all.inc" - +#include "all.qc" #endif diff --git a/qcsrc/common/sounds/sound.qh b/qcsrc/common/sounds/sound.qh index bc393dda3d..36bdaa8330 100644 --- a/qcsrc/common/sounds/sound.qh +++ b/qcsrc/common/sounds/sound.qh @@ -1,36 +1,114 @@ #ifndef SOUND_H #define SOUND_H +const int CH_INFO = 0; +const int CH_TRIGGER = -3; +const int CH_WEAPON_A = -1; +const int CH_WEAPON_SINGLE = 1; +const int CH_VOICE = -2; +const int CH_BGM_SINGLE = 8; +const int CH_AMBIENT = -9; +const int CH_TRIGGER_SINGLE = 3; +const int CH_SHOTS = -4; +const int CH_SHOTS_SINGLE = 4; +const int CH_WEAPON_B = -1; +const int CH_PAIN = -6; +const int CH_PAIN_SINGLE = 6; +const int CH_PLAYER = -7; +const int CH_PLAYER_SINGLE = 7; +const int CH_TUBA_SINGLE = 5; + +const float ATTEN_NONE = 0; +const float ATTEN_MIN = 0.015625; +const float ATTEN_NORM = 0.5; +const float ATTEN_LARGE = 1; +const float ATTEN_IDLE = 2; +const float ATTEN_STATIC = 3; +const float ATTEN_MAX = 3.984375; + +const float VOL_BASE = 0.7; +const float VOL_BASEVOICE = 1.0; + // Play all sounds via sound7, for access to the extra channels. // Otherwise, channels 8 to 15 would be blocked for a weird QW feature. #ifdef SVQC - #define _sound(e, c, s, v, a) do { \ - entity __e = e; \ - if (!sound_allowed(MSG_BROADCAST, __e)) break; \ - sound7(__e, c, s, v, a, 0, 0); \ - } while (0) + #define _sound(e, c, s, v, a) \ + do \ + { \ + entity __e = e; \ + if (!sound_allowed(MSG_BROADCAST, __e)) break; \ + sound7(__e, c, s, v, a, 0, 0); \ + } \ + while (0) #else - #define _sound(e, c, s, v, a) sound7(e, c, s, v, a, 0, 0) + #define _sound(e, c, s, v, a) sound7(e, c, s, v, a, 0, 0) #endif -#define sound(e, c, s, v, a) _sound(e, c, s.sound_str(), v, a) +#define sound(e, c, s, v, a) _sound(e, c, Sound_fixpath(s), v, a) + +/** + * because sound7 didn't have origin + * + * @param e sound owner + * @param o sound origin + * @param chan sound channel + * @param samp sound filename + * @param vol sound volume + * @param atten sound attenuation + * @param speed + * @param sf + */ +#define sound8(e, o, chan, samp, vol, atten, speed, sf) \ + do \ + { \ + entity __e = e; \ + vector old_origin = __e.origin; \ + vector old_mins = __e.mins; \ + vector old_maxs = __e.maxs; \ + setorigin(__e, o); \ + setsize(__e, '0 0 0', '0 0 0'); \ + sound7(__e, chan, samp, vol, atten, speed, sf); \ + setorigin(__e, old_origin); \ + setsize(__e, old_mins, old_maxs); \ + } \ + while (0) CLASS(Sound, Object) - ATTRIB(Sound, m_id, int, 0) - ATTRIB(Sound, sound_str, string(), func_null) - CONSTRUCTOR(Sound, string() path) - { - CONSTRUCT(Sound); - this.sound_str = path; - } - METHOD(Sound, sound_precache, void(entity this)) { - string s = this.sound_str(); - if (s && s != "" && !fexists(strcat("sound/", s))) { - LOG_WARNINGF("Missing sound: \"%s\"\n", s); - return; - } - LOG_TRACEF("precache_sound(\"%s\")\n", s); - precache_sound(s); - } + ATTRIB(Sound, m_id, int, 0) + ATTRIB(Sound, sound_str, string(), func_null) + CONSTRUCTOR(Sound, string() path) + { + CONSTRUCT(Sound); + this.sound_str = path; + } + #define Sound_fixpath(this) _Sound_fixpath((this).sound_str()) + string _Sound_fixpath(string base) + { + if (base == "") return string_null; + #define extensions(x) \ + x(wav) \ + x(ogg) \ + x(flac) \ + /**/ + string full, relative; + #define tryext(ext) { if (fexists(full = strcat("sound/", relative = strcat(base, "." #ext)))) break; } + do + { + extensions(tryext); +#undef tryext +#undef extensions + LOG_WARNINGF("Missing sound: \"%s\"\n", full); + return string_null; + } + while (0); + return relative; + } + METHOD(Sound, sound_precache, void(entity this)) + { + string s = Sound_fixpath(this); + if (!s) return; + LOG_TRACEF("precache_sound(\"%s\")\n", s); + precache_sound(s); + } ENDCLASS(Sound) #endif diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 9bdcb5773a..cbf54fef29 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -52,225 +52,203 @@ const int STAT_VIEWZOOM = 21; // 29 empty? // 30 empty? // 31 empty? -const int STAT_KH_KEYS = 32; -const int STAT_CTF_STATE = 33; -// 34 empty? -const int STAT_WEAPONS = 35; -const int STAT_SWITCHWEAPON = 36; -const int STAT_GAMESTARTTIME = 37; -const int STAT_STRENGTH_FINISHED = 38; -const int STAT_INVINCIBLE_FINISHED = 39; -// 40 empty? -const int STAT_ARC_HEAT = 41; -const int STAT_PRESSED_KEYS = 42; -const int STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config -const int STAT_FUEL = 44; -const int STAT_NB_METERSTART = 45; -const int STAT_SHOTORG = 46; // compressShotOrigin -const int STAT_LEADLIMIT = 47; -const int STAT_WEAPON_CLIPLOAD = 48; -const int STAT_WEAPON_CLIPSIZE = 49; -const int STAT_VORTEX_CHARGE = 50; -const int STAT_LAST_PICKUP = 51; -const int STAT_HUD = 52; -const int STAT_VORTEX_CHARGEPOOL = 53; -const int STAT_HIT_TIME = 54; -const int STAT_DAMAGE_DEALT_TOTAL = 55; -const int STAT_TYPEHIT_TIME = 56; -const int STAT_LAYED_MINES = 57; -const int STAT_HAGAR_LOAD = 58; -const int STAT_SWITCHINGWEAPON = 59; -const int STAT_SUPERWEAPONS_FINISHED = 60; -const int STAT_VEHICLESTAT_HEALTH = 61; -const int STAT_VEHICLESTAT_SHIELD = 62; -const int STAT_VEHICLESTAT_ENERGY = 63; -const int STAT_VEHICLESTAT_AMMO1 = 64; -const int STAT_VEHICLESTAT_RELOAD1 = 65; -const int STAT_VEHICLESTAT_AMMO2 = 66; -const int STAT_VEHICLESTAT_RELOAD2 = 67; -const int STAT_VEHICLESTAT_W2MODE = 68; -const int STAT_NADE_TIMER = 69; -const int STAT_SECRETS_TOTAL = 70; -const int STAT_SECRETS_FOUND = 71; -const int STAT_RESPAWN_TIME = 72; -const int STAT_ROUNDSTARTTIME = 73; -const int STAT_WEAPONS2 = 74; -const int STAT_WEAPONS3 = 75; -const int STAT_MONSTERS_TOTAL = 76; -const int STAT_MONSTERS_KILLED = 77; -const int STAT_BUFFS = 78; -const int STAT_NADE_BONUS = 79; -const int STAT_NADE_BONUS_TYPE = 80; -const int STAT_NADE_BONUS_SCORE = 81; -const int STAT_HEALING_ORB = 82; -const int STAT_HEALING_ORB_ALPHA = 83; -const int STAT_PLASMA = 84; -const int STAT_OK_AMMO_CHARGE = 85; -const int STAT_OK_AMMO_CHARGEPOOL = 86; -const int STAT_FROZEN = 87; -const int STAT_REVIVE_PROGRESS = 88; -// 89 empty? -// 90 empty? -// 91 empty? -// 92 empty? -// 93 empty? -// 94 empty? -// 95 empty? -// 96 empty? -// 97 empty? -// 98 empty? -const int STAT_ROUNDLOST = 99; + +enum { + STAT_WEAPONS = 32, + STAT_WEAPONS2, + STAT_WEAPONS3, + + STAT_WEAPONSINMAP, + STAT_WEAPONSINMAP2, + STAT_WEAPONSINMAP3, + + STAT_PL_VIEW_OFS1, + STAT_PL_VIEW_OFS2, + STAT_PL_VIEW_OFS3, + + STAT_PL_CROUCH_VIEW_OFS1, + STAT_PL_CROUCH_VIEW_OFS2, + STAT_PL_CROUCH_VIEW_OFS3, + + STAT_PL_MIN1, + STAT_PL_MIN2, + STAT_PL_MIN3, + + STAT_PL_MAX1, + STAT_PL_MAX2, + STAT_PL_MAX3, + + STAT_PL_CROUCH_MIN1, + STAT_PL_CROUCH_MIN2, + STAT_PL_CROUCH_MIN3, + + STAT_PL_CROUCH_MAX1, + STAT_PL_CROUCH_MAX2, + STAT_PL_CROUCH_MAX3, + + STAT_LAST_VECTOR +}; + +const int REGISTERED_STATS = 6; + +REGISTER_STAT(KH_KEYS, int) +/** weapon requested to switch to; next WANTED weapon (for HUD) */ +REGISTER_STAT(SWITCHWEAPON, int) +REGISTER_STAT(GAMESTARTTIME, float) +REGISTER_STAT(STRENGTH_FINISHED, float) +REGISTER_STAT(INVINCIBLE_FINISHED, float) +/** arc heat in [0,1] */ +REGISTER_STAT(ARC_HEAT, float) + +enum { + STAT_FIRST_MAIN = (STAT_LAST_VECTOR - 1) + REGISTERED_STATS, + + STAT_PRESSED_KEYS, + /** this stat could later contain some other bits of info, like, more server-side particle config */ STAT_ALLOW_OLDVORTEXBEAM, + STAT_FUEL, + STAT_NB_METERSTART, + /** compressShotOrigin */ STAT_SHOTORG, + STAT_LEADLIMIT, + STAT_WEAPON_CLIPLOAD, + STAT_WEAPON_CLIPSIZE, + STAT_VORTEX_CHARGE, + STAT_LAST_PICKUP, + STAT_HUD, + STAT_VORTEX_CHARGEPOOL, + STAT_HIT_TIME, + STAT_DAMAGE_DEALT_TOTAL, + STAT_TYPEHIT_TIME, + STAT_LAYED_MINES, + STAT_HAGAR_LOAD, + STAT_SWITCHINGWEAPON, + STAT_SUPERWEAPONS_FINISHED, + STAT_VEHICLESTAT_HEALTH, + STAT_VEHICLESTAT_SHIELD, + STAT_VEHICLESTAT_ENERGY, + STAT_VEHICLESTAT_AMMO1, + STAT_VEHICLESTAT_RELOAD1, + STAT_VEHICLESTAT_AMMO2, + STAT_VEHICLESTAT_RELOAD2, + STAT_VEHICLESTAT_W2MODE, + STAT_NADE_TIMER, + STAT_SECRETS_TOTAL, + STAT_SECRETS_FOUND, + STAT_RESPAWN_TIME, + STAT_ROUNDSTARTTIME, + STAT_MONSTERS_TOTAL, + STAT_MONSTERS_KILLED, + STAT_BUFFS, + STAT_NADE_BONUS, + STAT_NADE_BONUS_TYPE, + STAT_NADE_BONUS_SCORE, + STAT_HEALING_ORB, + STAT_HEALING_ORB_ALPHA, + STAT_PLASMA, + STAT_OK_AMMO_CHARGE, + STAT_OK_AMMO_CHARGEPOOL, + STAT_FROZEN, + STAT_REVIVE_PROGRESS, + STAT_ROUNDLOST, + STAT_BUFF_TIME, + STAT_CTF_FLAGSTATUS, + STAT_MULTIJUMP_DODGING, + STAT_MULTIJUMP_MAXSPEED, + STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, + STAT_BUGRIGS_REVERSE_STOPPING, + STAT_BUGRIGS_REVERSE_SPINNING, + STAT_BUGRIGS_CAR_JUMPING, + STAT_BUGRIGS_FRICTION_AIR, + STAT_BUGRIGS_STEER, + STAT_BUGRIGS_SPEED_POW, + STAT_BUGRIGS_SPEED_REF, + STAT_BUGRIGS_ACCEL, + STAT_BUGRIGS_FRICTION_BRAKE, + STAT_BUGRIGS_AIR_STEERING, + STAT_BUGRIGS_FRICTION_FLOOR, + STAT_BUGRIGS_REVERSE_SPEEDING, + STAT_BUGRIGS_PLANAR_MOVEMENT, + STAT_BUGRIGS_ANGLE_SMOOTHING, + STAT_BUGRIGS, + STAT_GAMEPLAYFIX_STEPDOWN, + STAT_MOVEVARS_JUMPSTEP, + STAT_NOSTEP, + STAT_GAMEPLAYFIX_UNSTICKPLAYERS, + STAT_GAMEPLAYFIX_STEPMULTIPLETIMES, + STAT_GAMEPLAYFIX_DOWNTRACEONGROUND, + STAT_GAMEPLAYFIX_EASIERWATERJUMP, + STAT_MOVEVARS_FRICTION_SLICK, + STAT_MOVEVARS_FRICTION_ONLAND, + STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, + STAT_MOVEVARS_TRACK_CANJUMP, + STAT_DOUBLEJUMP, + STAT_MOVEVARS_CL_TRACK_CANJUMP, + STAT_MULTIJUMP_ADD, + STAT_MULTIJUMP_SPEED, + STAT_MULTIJUMP, + STAT_DODGING_TIMEOUT, + STAT_DODGING_WALL, + STAT_DODGING_UP_SPEED, + STAT_DODGING_RAMP_TIME, + STAT_DODGING_HEIGHT_THRESHOLD, + STAT_DODGING_DISTANCE_THRESHOLD, + STAT_DODGING_HORIZ_SPEED, + STAT_DODGING_DELAY, + STAT_DODGING_FROZEN_NO_DOUBLETAP, + STAT_DODGING_HORIZ_SPEED_FROZEN, + STAT_DODGING, + STAT_DODGING_FROZEN, + STAT_JETPACK_MAXSPEED_UP, + STAT_JETPACK_MAXSPEED_SIDE, + STAT_JETPACK_FUEL, + STAT_JETPACK_ANTIGRAVITY, + STAT_JETPACK_ACCEL_SIDE, + STAT_JETPACK_ACCEL_UP, + STAT_MOVEVARS_HIGHSPEED, + + STAT_LAST_MAIN +}; + +const int STAT_LAST = STAT_LAST_MAIN + 5; /* The following stats change depending on the gamemode, so can share the same ID */ -// IDs 100 to 104 reserved for gamemodes // freeze tag, clan arena, jailbreak -const int STAT_REDALIVE = 100; -const int STAT_BLUEALIVE = 101; -const int STAT_YELLOWALIVE = 102; -const int STAT_PINKALIVE = 103; +enum { + STAT_REDALIVE = STAT_LAST_MAIN, + STAT_BLUEALIVE, + STAT_YELLOWALIVE, + STAT_PINKALIVE, +}; // domination -const int STAT_DOM_TOTAL_PPS = 100; -const int STAT_DOM_PPS_RED = 101; -const int STAT_DOM_PPS_BLUE = 102; -const int STAT_DOM_PPS_YELLOW = 103; -const int STAT_DOM_PPS_PINK = 104; +enum { + STAT_DOM_TOTAL_PPS = STAT_LAST_MAIN, + STAT_DOM_PPS_RED, + STAT_DOM_PPS_BLUE, + STAT_DOM_PPS_YELLOW, + STAT_DOM_PPS_PINK, +}; // vip -const int STAT_VIP = 100; -const int STAT_VIP_RED = 101; -const int STAT_VIP_BLUE = 102; -const int STAT_VIP_YELLOW = 103; -const int STAT_VIP_PINK = 104; +enum { + STAT_VIP = STAT_LAST_MAIN, + STAT_VIP_RED, + STAT_VIP_BLUE, + STAT_VIP_YELLOW, + STAT_VIP_PINK, +}; // key hunt -const int STAT_KH_REDKEY_TEAM = 100; -const int STAT_KH_BLUEKEY_TEAM = 101; -const int STAT_KH_YELLOWKEY_TEAM = 102; -const int STAT_KH_PINKKEY_TEAM = 103; +enum { + STAT_KH_REDKEY_TEAM = STAT_LAST_MAIN, + STAT_KH_BLUEKEY_TEAM, + STAT_KH_YELLOWKEY_TEAM, + STAT_KH_PINKKEY_TEAM, +}; -/* Gamemode-specific stats end here */ +#define ASSERT_LESS(name, var, const) noref int name[(const - var + 1)]; +ASSERT_LESS(stat_limit, STAT_LAST, 220) -const int STAT_PL_VIEW_OFS1 = 105; -const int STAT_PL_VIEW_OFS2 = 106; -const int STAT_PL_VIEW_OFS3 = 107; -const int STAT_PL_MIN1 = 108; -const int STAT_PL_MIN2 = 109; -const int STAT_PL_MIN3 = 110; -const int STAT_PL_MAX1 = 111; -const int STAT_PL_MAX2 = 112; -const int STAT_PL_MAX3 = 113; -const int STAT_PL_CROUCH_MIN1 = 114; -const int STAT_PL_CROUCH_MIN2 = 115; -const int STAT_PL_CROUCH_MIN3 = 116; -const int STAT_PL_CROUCH_MAX1 = 117; -const int STAT_PL_CROUCH_MAX2 = 118; -const int STAT_PL_CROUCH_MAX3 = 119; -const int STAT_PL_CROUCH_VIEW_OFS1 = 117; -const int STAT_PL_CROUCH_VIEW_OFS2 = 118; -const int STAT_PL_CROUCH_VIEW_OFS3 = 119; -const int STAT_WEAPONSINMAP = 120; -const int STAT_WEAPONSINMAP2 = 121; -const int STAT_WEAPONSINMAP3 = 122; -const int STAT_BUFF_TIME = 123; -const int STAT_CTF_FLAGSTATUS = 124; -// 125 empty? -// 126 empty? -// 127 empty? -// 128 empty? -// 129 empty? -// 130 empty? -// 131 empty? -// 132 empty? -// 133 empty? -// 134 empty? -// 135 empty? -// 136 empty? -// 137 empty? -// 138 empty? -// 139 empty? -// 140 reserved -// 141 reserved -// 142 reserved -// 143 reserved -// 144 reserved -// 145 reserved -// 146 reserved -// 147 reserved -// 148 reserved -// 149 reserved -// 150 reserved -// 151 reserved -// 152 reserved -// 153 reserved -// 154 reserved -// 155 reserved -// 156 empty? -// 157 empty? -// 158 empty? -// 159 empty? -// 160 empty? -// 161 empty? -// 162 empty? -// 162 empty? -// 163 empty? -// 164 empty? -// 165 empty? -const int STAT_MULTIJUMP_DODGING = 166; -const int STAT_MULTIJUMP_MAXSPEED = 167; -const int STAT_GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND = 168; -const int STAT_BUGRIGS_REVERSE_STOPPING = 169; -const int STAT_BUGRIGS_REVERSE_SPINNING = 170; -const int STAT_BUGRIGS_CAR_JUMPING = 171; -const int STAT_BUGRIGS_FRICTION_AIR = 172; -const int STAT_BUGRIGS_STEER = 173; -const int STAT_BUGRIGS_SPEED_POW = 174; -const int STAT_BUGRIGS_SPEED_REF = 175; -const int STAT_BUGRIGS_ACCEL = 176; -const int STAT_BUGRIGS_FRICTION_BRAKE = 177; -const int STAT_BUGRIGS_AIR_STEERING = 178; -const int STAT_BUGRIGS_FRICTION_FLOOR = 179; -const int STAT_BUGRIGS_REVERSE_SPEEDING = 180; -const int STAT_BUGRIGS_PLANAR_MOVEMENT = 181; -const int STAT_BUGRIGS_ANGLE_SMOOTHING = 182; -const int STAT_BUGRIGS = 183; -const int STAT_GAMEPLAYFIX_STEPDOWN = 184; -const int STAT_MOVEVARS_JUMPSTEP = 185; -const int STAT_NOSTEP = 186; -const int STAT_GAMEPLAYFIX_UNSTICKPLAYERS = 187; -const int STAT_GAMEPLAYFIX_STEPMULTIPLETIMES = 188; -const int STAT_GAMEPLAYFIX_DOWNTRACEONGROUND = 189; -const int STAT_GAMEPLAYFIX_EASIERWATERJUMP = 190; -const int STAT_MOVEVARS_FRICTION_SLICK = 191; -const int STAT_MOVEVARS_FRICTION_ONLAND = 192; -const int STAT_MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS = 193; -const int STAT_MOVEVARS_TRACK_CANJUMP = 194; -// 195 empty? -const int STAT_DOUBLEJUMP = 196; -const int STAT_MOVEVARS_CL_TRACK_CANJUMP = 197; -const int STAT_MULTIJUMP_ADD = 198; -const int STAT_MULTIJUMP_SPEED = 199; -const int STAT_MULTIJUMP = 200; -const int STAT_DODGING_TIMEOUT = 201; -const int STAT_DODGING_WALL = 202; -const int STAT_DODGING_UP_SPEED = 203; -const int STAT_DODGING_RAMP_TIME = 204; -const int STAT_DODGING_HEIGHT_THRESHOLD = 205; -const int STAT_DODGING_DISTANCE_THRESHOLD = 206; -const int STAT_DODGING_HORIZ_SPEED = 207; -const int STAT_DODGING_DELAY = 208; -const int STAT_DODGING_FROZEN_NO_DOUBLETAP = 209; -const int STAT_DODGING_HORIZ_SPEED_FROZEN = 210; -const int STAT_DODGING = 211; -const int STAT_DODGING_FROZEN = 212; -const int STAT_JETPACK_MAXSPEED_UP = 213; -const int STAT_JETPACK_MAXSPEED_SIDE = 214; -const int STAT_JETPACK_FUEL = 215; -const int STAT_JETPACK_ANTIGRAVITY = 216; -const int STAT_JETPACK_ACCEL_SIDE = 217; -const int STAT_JETPACK_ACCEL_UP = 218; -const int STAT_MOVEVARS_HIGHSPEED = 219; const int STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220; const int STAT_MOVEVARS_AIRCONTROL_PENALTY = 221; const int STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222; diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc index 8c7000d933..909f5cd975 100644 --- a/qcsrc/common/triggers/func/bobbing.qc +++ b/qcsrc/common/triggers/func/bobbing.qc @@ -69,8 +69,7 @@ spawnfunc(func_bobbing) return; // wait for targets to spawn - controller = spawn(); - controller.classname = "func_bobbing_controller"; + controller = new(func_bobbing_controller); controller.owner = self; controller.nextthink = time + 1; controller.think = func_bobbing_controller_think; diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc index 4164f8341a..e03e23ea73 100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@ -211,7 +211,7 @@ void func_breakable_destroy() RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER.m_id, world); if(self.cnt) // TODO - pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count); + __pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count); if(self.respawntime) { diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc index 73f22b99b6..ac30dda43d 100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@ -1,3 +1,5 @@ +REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) + void conveyor_think() {SELFPARAM(); #ifdef CSQC @@ -72,7 +74,7 @@ void conveyor_reset() bool conveyor_send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR); + WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR); WriteByte(MSG_ENTITY, sf); if(sf & 1) @@ -160,8 +162,8 @@ void conveyor_init() self.move_time = time; } -void ent_conveyor() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew) +{ float sf = ReadByte(); if(sf & 1) @@ -195,5 +197,7 @@ void ent_conveyor() if(sf & 2) self.state = ReadByte(); + + return true; } #endif diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh deleted file mode 100644 index f272030256..0000000000 --- a/qcsrc/common/triggers/func/conveyor.qh +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef CSQC -void ent_conveyor(); -#endif diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc index 4ce3d261b0..6c82565566 100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@ -293,6 +293,7 @@ void door_damage(entity inflictor, entity attacker, float damage, int deathtype, } } +.float door_finished; /* ================ @@ -306,10 +307,10 @@ void door_touch() {SELFPARAM(); if (!IS_PLAYER(other)) return; - if (self.owner.attack_finished_single > time) + if (self.owner.door_finished > time) return; - self.owner.attack_finished_single = time + 2; + self.owner.door_finished = time + 2; #ifdef SVQC if (!(self.owner.dmg) && (self.owner.message != "")) @@ -437,14 +438,14 @@ void door_trigger_touch() #endif return; - if (time < self.attack_finished_single) + if (time < self.door_finished) return; // check if door is locked if (!door_check_keys(self, other)) return; - self.attack_finished_single = time + 1; + self.door_finished = time + 1; activator = other; @@ -457,8 +458,7 @@ void door_spawnfield(vector fmins, vector fmaxs) entity trigger; vector t1 = fmins, t2 = fmaxs; - trigger = spawn(); - trigger.classname = "doortriggerfield"; + trigger = new(doortriggerfield); trigger.movetype = MOVETYPE_NONE; trigger.solid = SOLID_TRIGGER; trigger.owner = self; @@ -599,6 +599,8 @@ void LinkDoors() door_spawnfield(cmins, cmaxs); } +REGISTER_NET_LINKED(ENT_CLIENT_DOOR) + #ifdef SVQC /*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE if two doors touch, they are assumed to be connected and operate as a unit. @@ -631,7 +633,7 @@ FIXME: only one sound set available at the time being float door_send(entity to, float sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_DOOR); + WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR); WriteByte(MSG_ENTITY, sf); if(sf & SF_TRIGGER_INIT) @@ -804,8 +806,8 @@ void door_draw(entity this) trigger_draw_generic(this); } -void ent_door() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) +{ float sf = ReadByte(); if(sf & SF_TRIGGER_INIT) @@ -874,6 +876,7 @@ void ent_door() self.pos2_y = ReadCoord(); self.pos2_z = ReadCoord(); } + return true; } #endif diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh index adfc060e80..f7ed28c5bb 100644 --- a/qcsrc/common/triggers/func/door.qh +++ b/qcsrc/common/triggers/func/door.qh @@ -12,8 +12,6 @@ const int SPAWNFLAGS_SILVER_KEY = 16; #ifdef CSQC // stuff for preload -void ent_door(); -// abused -.float attack_finished_single; +.float door_finished; #endif diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc index a0b70ecebe..4d6b9b48a1 100644 --- a/qcsrc/common/triggers/func/door_secret.qc +++ b/qcsrc/common/triggers/func/door_secret.qc @@ -133,11 +133,13 @@ void fd_secret_done() _sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTEN_NORM); } +.float door_finished; + void secret_blocked() {SELFPARAM(); - if (time < self.attack_finished_single) + if (time < self.door_finished) return; - self.attack_finished_single = time + 0.5; + self.door_finished = time + 0.5; //T_Damage (other, self, self, self.dmg, self.dmg, self.deathtype, DT_IMPACT, (self.absmin + self.absmax) * 0.5, '0 0 0', Obituary_Generic); } @@ -152,10 +154,10 @@ void secret_touch() {SELFPARAM(); if (!other.iscreature) return; - if (self.attack_finished_single > time) + if (self.door_finished > time) return; - self.attack_finished_single = time + 2; + self.door_finished = time + 2; if (self.message) { diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc index 2b3d07f6ea..31e3835914 100644 --- a/qcsrc/common/triggers/func/fourier.qc +++ b/qcsrc/common/triggers/func/fourier.qc @@ -73,8 +73,7 @@ spawnfunc(func_fourier) self.active = ACTIVE_ACTIVE; // wait for targets to spawn - controller = spawn(); - controller.classname = "func_fourier_controller"; + controller = new(func_fourier_controller); controller.owner = self; controller.nextthink = time + 1; controller.think = func_fourier_controller_think; diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh index 624c0b5c8e..628355cb79 100644 --- a/qcsrc/common/triggers/func/include.qh +++ b/qcsrc/common/triggers/func/include.qh @@ -1,12 +1,8 @@ #ifndef TRIGGERS_FUNC_INCLUDE_H #define TRIGGERS_FUNC_INCLUDE_H -#include "conveyor.qh" #include "door.qh" #include "ladder.qh" -#include "plat.qh" -#include "rainsnow.qh" -#include "pointparticles.qh" #include "train.qh" #endif diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc index c27dd2ce08..0ddf08eef0 100644 --- a/qcsrc/common/triggers/func/ladder.qc +++ b/qcsrc/common/triggers/func/ladder.qc @@ -1,3 +1,5 @@ +REGISTER_NET_LINKED(ENT_CLIENT_LADDER) + void func_ladder_touch() {SELFPARAM(); #ifdef SVQC @@ -20,7 +22,7 @@ void func_ladder_touch() #ifdef SVQC bool func_ladder_send(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER); WriteString(MSG_ENTITY, self.classname); WriteByte(MSG_ENTITY, self.skin); @@ -60,14 +62,17 @@ spawnfunc(func_water) #elif defined(CSQC) .float speed; -void ent_func_ladder() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_LADDER, bool isnew) +{ self.classname = strzone(ReadString()); self.skin = ReadByte(); self.speed = ReadByte(); self.model = strzone(ReadString()); trigger_common_read(false); + + return = true; + self.mins = self.maxs = '0 0 0'; self.solid = SOLID_TRIGGER; diff --git a/qcsrc/common/triggers/func/ladder.qh b/qcsrc/common/triggers/func/ladder.qh index 11eeeda26a..774e7cf408 100644 --- a/qcsrc/common/triggers/func/ladder.qh +++ b/qcsrc/common/triggers/func/ladder.qh @@ -1,6 +1,2 @@ .float ladder_time; .entity ladder_entity; - -#ifdef CSQC -void ent_func_ladder(); -#endif diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc index d8bab869e2..2d8aea35cf 100644 --- a/qcsrc/common/triggers/func/pendulum.qc +++ b/qcsrc/common/triggers/func/pendulum.qc @@ -62,8 +62,7 @@ spawnfunc(func_pendulum) self.cnt = self.angles_z; // wait for targets to spawn - controller = spawn(); - controller.classname = "func_pendulum_controller"; + controller = new(func_pendulum_controller); controller.owner = self; controller.nextthink = time + 1; controller.think = func_pendulum_controller_think; diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index fc62341a85..cb2cc3a49c 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -1,3 +1,5 @@ +REGISTER_NET_LINKED(ENT_CLIENT_PLAT) + #ifdef SVQC void plat_link(); @@ -9,7 +11,7 @@ void plat_delayedinit() float plat_send(entity to, float sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_PLAT); + WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT); WriteByte(MSG_ENTITY, sf); if(sf & SF_TRIGGER_INIT) @@ -133,8 +135,8 @@ void plat_draw(entity this) //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); } -void ent_plat() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) +{ float sf = ReadByte(); if(sf & SF_TRIGGER_INIT) @@ -197,5 +199,6 @@ void ent_plat() self.move_angles = self.angles; self.move_time = time; } + return true; } #endif diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh deleted file mode 100644 index e3562f4e8a..0000000000 --- a/qcsrc/common/triggers/func/plat.qh +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef CSQC -void ent_plat(); -#endif diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc index a4f31b2fd8..ef42c93eb0 100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@ -1,13 +1,11 @@ -#ifdef CSQC - #include "../../../client/particles.qh" -#endif +REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) #ifdef SVQC // NOTE: also contains func_sparks bool pointparticles_SendEntity(entity this, entity to, float fl) { - WriteByte(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); + WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); // optional features to save space fl = fl & 0x0F; @@ -183,6 +181,21 @@ spawnfunc(func_sparks) } #elif defined(CSQC) +.int dphitcontentsmask; + +entityclass(PointParticles); +class(PointParticles) .int cnt; // effect number +class(PointParticles) .vector velocity; // particle velocity +class(PointParticles) .float waterlevel; // direction jitter +class(PointParticles) .int count; // count multiplier +class(PointParticles) .int impulse; // density +class(PointParticles) .string noise; // sound +class(PointParticles) .float atten; +class(PointParticles) .float volume; +class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle +class(PointParticles) .vector movedir; // trace direction +class(PointParticles) .float glow_color; // palette index + void Draw_PointParticles(entity this) { float n, i, fail; @@ -221,11 +234,11 @@ void Draw_PointParticles(entity this) { traceline(p, p + normalize(self.movedir) * 4096, 0, world); p = trace_endpos; - pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count); + __pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count); } else { - pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count); + __pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count); } if(self.noise != "") { @@ -253,8 +266,8 @@ void Ent_PointParticles_Remove() self.bgmscript = string_null; } -void Ent_PointParticles() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) +{ float i; vector v; int f = ReadByte(); @@ -340,6 +353,8 @@ void Ent_PointParticles() BGMScript_InitEntity(self); } + return = true; + if(f & 2) { self.absolute = (self.impulse >= 0); diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh deleted file mode 100644 index f08ea48751..0000000000 --- a/qcsrc/common/triggers/func/pointparticles.qh +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef CSQC - -void Ent_PointParticles(); - -#endif diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc index 18c2c0a0fb..7ed9c558c3 100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@ -1,7 +1,9 @@ +REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) + #ifdef SVQC bool rainsnow_SendEntity(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_RAINSNOW); + WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW); WriteByte(MSG_ENTITY, self.state); WriteCoord(MSG_ENTITY, self.origin_x + self.mins_x); WriteCoord(MSG_ENTITY, self.origin_y + self.mins_y); @@ -100,8 +102,8 @@ void Draw_Snow(entity this) te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color); } -void Ent_RainOrSnow() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) +{ self.impulse = ReadByte(); // Rain, Snow, or Whatever self.origin_x = ReadCoord(); self.origin_y = ReadCoord(); @@ -113,6 +115,8 @@ void Ent_RainOrSnow() self.count = ReadShort() * 10; self.glow_color = ReadByte(); // color + return = true; + self.mins = -0.5 * self.maxs; self.maxs = 0.5 * self.maxs; self.origin = self.origin - self.mins; diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh deleted file mode 100644 index 5d8d92350a..0000000000 --- a/qcsrc/common/triggers/func/rainsnow.qh +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef CSQC -void Ent_RainOrSnow(); -#endif diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc index 65414548b2..0f827a60e3 100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@ -101,10 +101,12 @@ void train_next() _sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTEN_IDLE); } +REGISTER_NET_LINKED(ENT_CLIENT_TRAIN) + #ifdef SVQC float train_send(entity to, float sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_TRAIN); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN); WriteByte(MSG_ENTITY, sf); if(sf & SF_TRIGGER_INIT) @@ -245,8 +247,8 @@ void train_draw(entity this) Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); } -void ent_train() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) +{ float sf = ReadByte(); if(sf & SF_TRIGGER_INIT) @@ -321,6 +323,8 @@ void ent_train() { // TODO: make a reset function for trains } + + return true; } #endif diff --git a/qcsrc/common/triggers/func/train.qh b/qcsrc/common/triggers/func/train.qh index f69515f5a7..d42d8a8a0c 100644 --- a/qcsrc/common/triggers/func/train.qh +++ b/qcsrc/common/triggers/func/train.qh @@ -1,4 +1,3 @@ #ifdef CSQC .float dmgtime; -void ent_train(); #endif diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc index 57d3a0636f..5c20864774 100644 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ b/qcsrc/common/triggers/func/vectormamamam.qc @@ -91,8 +91,7 @@ void func_vectormamamam_findtarget() self.destvec = self.origin - func_vectormamamam_origin(self, 0); entity controller; - controller = spawn(); - controller.classname = "func_vectormamamam_controller"; + controller = new(func_vectormamamam_controller); controller.owner = self; controller.nextthink = time + 1; controller.think = func_vectormamamam_controller_think; diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh index 138d26786b..f69a72b0a2 100644 --- a/qcsrc/common/triggers/include.qh +++ b/qcsrc/common/triggers/include.qh @@ -13,9 +13,6 @@ // func #include "func/include.qh" -// misc -#include "misc/include.qh" - // target #include "target/include.qh" diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc index ccac77b6c9..d1bfe00de3 100644 --- a/qcsrc/common/triggers/misc/corner.qc +++ b/qcsrc/common/triggers/misc/corner.qc @@ -1,7 +1,9 @@ +REGISTER_NET_LINKED(ENT_CLIENT_CORNER) + #ifdef SVQC bool corner_send(entity to, int sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_CORNER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER); WriteString(MSG_ENTITY, self.platmovetype); @@ -57,8 +59,8 @@ void corner_remove() self.platmovetype = string_null; } -void ent_corner() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) +{ self.platmovetype = strzone(ReadString()); self.origin_x = ReadCoord(); @@ -75,6 +77,8 @@ void ent_corner() self.wait = ReadByte(); + return = true; + self.classname = "path_corner"; self.drawmask = MASK_NORMAL; self.entremove = corner_remove; diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh deleted file mode 100644 index 62b3aaed7e..0000000000 --- a/qcsrc/common/triggers/misc/corner.qh +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef CSQC -void ent_corner(); -#endif diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc index c05f8c376b..8965c62906 100644 --- a/qcsrc/common/triggers/misc/include.qc +++ b/qcsrc/common/triggers/misc/include.qc @@ -1,5 +1,3 @@ -#include "include.qh" - #include "corner.qc" #include "follow.qc" #include "laser.qc" diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh deleted file mode 100644 index 71b2205211..0000000000 --- a/qcsrc/common/triggers/misc/include.qh +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef TRIGGERS_MISC_INCLUDE_H -#define TRIGGERS_MISC_INCLUDE_H - -#include "corner.qh" -#include "laser.qh" - -#endif diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc index 06ca705435..8bfa35f40d 100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@ -1,5 +1,4 @@ #if defined(CSQC) - #include "../../buffs/all.qh" #include "../../../lib/csqcmodel/interpolate.qh" #include "../../../client/main.qh" #include "../../../lib/csqcmodel/cl_model.qh" @@ -7,6 +6,8 @@ #elif defined(SVQC) #endif +REGISTER_NET_LINKED(ENT_CLIENT_LASER) + #ifdef SVQC .float modelscale; void misc_laser_aim() @@ -124,7 +125,7 @@ void misc_laser_think() bool laser_SendEntity(entity this, entity to, float fl) { - WriteByte(MSG_ENTITY, ENT_CLIENT_LASER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER); fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser if(self.spawnflags & 2) fl |= 0x80; @@ -316,14 +317,14 @@ void Draw_Laser(entity this) if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) { if(self.cnt >= 0) - pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); + __pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); if(self.colormod != '0 0 0' && self.modelscale != 0) adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5); } } -void Ent_Laser() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_LASER, bool isnew) +{ InterpolateOrigin_Undo(); // 30 bytes, or 13 bytes for just moving @@ -379,6 +380,9 @@ void Ent_Laser() } if(f & 4) self.state = ReadByte(); + + return = true; + InterpolateOrigin_Note(); self.draw = Draw_Laser; } diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh deleted file mode 100644 index a77c4c58e1..0000000000 --- a/qcsrc/common/triggers/misc/laser.qh +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef CSQC -void Ent_Laser(); -#endif diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc index d9de97301b..a0f60bb32f 100644 --- a/qcsrc/common/triggers/subs.qc +++ b/qcsrc/common/triggers/subs.qc @@ -1,4 +1,4 @@ -void SUB_NullThink(void) { } +void SUB_NullThink() { } void() SUB_CalcMoveDone; void() SUB_CalcAngleMoveDone; @@ -12,7 +12,7 @@ Applies some friction to self ================== */ .float friction; -void SUB_Friction (void) +void SUB_Friction () {SELFPARAM(); self.SUB_NEXTTHINK = time; if(self.SUB_FLAGS & FL_ONGROUND) @@ -45,7 +45,7 @@ void SUB_VanishOrRemove (entity ent) } } -void SUB_SetFade_Think (void) +void SUB_SetFade_Think () {SELFPARAM(); if(self.alpha == 0) self.alpha = 1; @@ -80,7 +80,7 @@ calculate self.SUB_VELOCITY and self.SUB_NEXTTHINK to reach dest from self.SUB_ORIGIN traveling at speed =============== */ -void SUB_CalcMoveDone (void) +void SUB_CalcMoveDone () {SELFPARAM(); // After moving, set origin to exact final destination @@ -92,7 +92,7 @@ void SUB_CalcMoveDone (void) } .float platmovetype_turn; -void SUB_CalcMove_controller_think (void) +void SUB_CalcMove_controller_think () {SELFPARAM(); entity oldself; float traveltime; @@ -222,8 +222,7 @@ void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float return; } - controller = spawn(); - controller.classname = "SUB_CalcMove_controller"; + controller = new(SUB_CalcMove_controller); controller.owner = self; controller.platmovetype = self.platmovetype; controller.platmovetype_start = self.platmovetype_start; @@ -309,7 +308,7 @@ self.angles rotating The calling function should make sure self.SUB_THINK is valid =============== */ -void SUB_CalcAngleMoveDone (void) +void SUB_CalcAngleMoveDone () {SELFPARAM(); // After rotating, set angle to exact final angle self.angles = self.finalangle; diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc index ebecb18e31..283b8afb47 100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@ -6,6 +6,9 @@ #include "../../../server/defs.qh" #endif +REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC) +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC) + #ifdef SVQC // values: @@ -19,8 +22,7 @@ // when targetname is not set, THIS ONE is default void target_music_sendto(float to, float is) {SELFPARAM(); - WriteByte(to, SVC_TEMPENTITY); - WriteByte(to, TE_CSQC_TARGET_MUSIC); + WriteHeader(to, TE_CSQC_TARGET_MUSIC); WriteShort(to, num_for_edict(self)); WriteByte(to, self.volume * 255.0 * is); WriteByte(to, self.fade_time * 16.0); @@ -88,7 +90,7 @@ void TargetMusic_RestoreGame() // when triggered, it is disabled/enabled for everyone bool trigger_music_SendEntity(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); sf &= ~0x80; if(self.cnt) sf |= 0x80; @@ -165,7 +167,7 @@ void TargetMusic_Advance() best = music_target; if(music_trigger) best = music_trigger; - for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise) + for(e = world; (e = findfloat(e, enttype, NET_ENT_CLIENT_TRIGGER_MUSIC.m_id)); ) if(e.noise) { vol0 = e.lastvol; if(getsoundtime(e, CH_BGM_SINGLE) < 0) @@ -206,6 +208,12 @@ void TargetMusic_Advance() bgmtime = gettime(GETTIME_CDTRACK); } +NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew) +{ + Net_TargetMusic(); + return true; +} + void Net_TargetMusic() { int id = ReadShort(); @@ -216,7 +224,7 @@ void Net_TargetMusic() string noi = ReadString(); entity e; - for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) + for(e = world; (e = findfloat(e, enttype, NET_ENT_CLIENT_TRIGGER_MUSIC.m_id)); ) { if(e.count == id) break; @@ -224,7 +232,7 @@ void Net_TargetMusic() if(!e) { e = spawn(); - e.enttype = ENT_CLIENT_TRIGGER_MUSIC; + e.enttype = NET_ENT_CLIENT_TRIGGER_MUSIC.m_id; e.count = id; } if(e.noise != noi) @@ -281,8 +289,8 @@ void Ent_TriggerMusic_Remove() self.noise = string_null; } -void Ent_ReadTriggerMusic() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) +{ int f = ReadByte(); if(f & 4) { @@ -335,6 +343,7 @@ void Ent_ReadTriggerMusic() self.cnt = 1; self.think = Ent_TriggerMusic_Think; self.nextthink = time; + return true; } #endif diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh index d46c460554..1884879764 100644 --- a/qcsrc/common/triggers/target/music.qh +++ b/qcsrc/common/triggers/target/music.qh @@ -22,8 +22,6 @@ void Ent_TriggerMusic_Think(); void Ent_TriggerMusic_Remove(); -void Ent_ReadTriggerMusic(); - #elif defined(SVQC) void target_music_kill(); #endif diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc index 92575384f7..1f0c00e5e8 100644 --- a/qcsrc/common/triggers/teleporters.qc +++ b/qcsrc/common/triggers/teleporters.qc @@ -177,7 +177,7 @@ entity Simple_TeleportPlayer(entity teleporter, entity player) return e; } -void teleport_findtarget (void) +void teleport_findtarget () {SELFPARAM(); entity e; float n; diff --git a/qcsrc/common/triggers/teleporters.qh b/qcsrc/common/triggers/teleporters.qh index bdd24ddeff..01e738e43d 100644 --- a/qcsrc/common/triggers/teleporters.qh +++ b/qcsrc/common/triggers/teleporters.qh @@ -53,9 +53,9 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle entity Simple_TeleportPlayer(entity teleporter, entity player); -void Teleport_Touch (void); +void Teleport_Touch (); -void teleport_findtarget (void); +void teleport_findtarget (); entity Teleport_Find(vector mi, vector ma); diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc index c40fed3a83..77c491c356 100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@ -113,6 +113,8 @@ void trigger_impulse_touch3() #endif } +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE) + /*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? -------- KEYS -------- target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed. @@ -133,7 +135,7 @@ in directional and sperical mode. For damper/accelerator mode this is not nesses #ifdef SVQC bool trigger_impulse_send(entity to, int sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); WriteCoord(MSG_ENTITY, self.radius); WriteCoord(MSG_ENTITY, self.strength); @@ -180,15 +182,15 @@ spawnfunc(trigger_impulse) trigger_impulse_link(); } #elif defined(CSQC) -void ent_trigger_impulse() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew) +{ self.radius = ReadCoord(); self.strength = ReadCoord(); self.falloff = ReadByte(); self.active = ReadByte(); trigger_common_read(true); - + return = true; self.classname = "trigger_impulse"; self.solid = SOLID_TRIGGER; diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh index a4c248dcad..67d6361fbe 100644 --- a/qcsrc/common/triggers/trigger/impulse.qh +++ b/qcsrc/common/triggers/trigger/impulse.qh @@ -7,8 +7,4 @@ .float strength; .float lastpushtime; -#ifdef CSQC -void ent_trigger_impulse(); -#endif - #endif diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc index 7e25751bba..0b3be0056d 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@ -13,6 +13,9 @@ void trigger_push_use() } #endif +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) +REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) + /* trigger_push_calculatevelocity @@ -317,7 +320,7 @@ void trigger_push_findtarget() #ifdef SVQC float trigger_push_send(entity to, float sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); WriteByte(MSG_ENTITY, sf); if(sf & 1) @@ -388,7 +391,7 @@ spawnfunc(trigger_push) bool target_push_send(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); WriteByte(MSG_ENTITY, self.cnt); WriteString(MSG_ENTITY, self.targetname); @@ -401,20 +404,20 @@ bool target_push_send(entity this, entity to, float sf) void target_push_link() {SELFPARAM(); - Net_LinkEntity(self, false, 0, target_push_send); - self.SendFlags |= 1; // update + //Net_LinkEntity(self, false, 0, target_push_send); + //self.SendFlags |= 1; // update } spawnfunc(target_push) { target_push_link(); } spawnfunc(info_notnull) { target_push_link(); } -spawnfunc(target_position) { target_push_link(); } +spawnfunc(target_position) { make_pure(this); target_push_link(); } #endif #ifdef CSQC -void ent_trigger_push() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) +{ float sf = ReadByte(); if(sf & 1) @@ -441,6 +444,7 @@ void ent_trigger_push() self.team = ReadByte(); self.active = ReadByte(); } + return true; } void target_push_remove() @@ -454,14 +458,17 @@ void target_push_remove() self.targetname = string_null; } -void ent_target_push() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew) +{ self.classname = "push_target"; self.cnt = ReadByte(); self.targetname = strzone(ReadString()); self.origin_x = ReadCoord(); self.origin_y = ReadCoord(); self.origin_z = ReadCoord(); + + return = true; + setorigin(self, self.origin); self.drawmask = MASK_NORMAL; diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh index efce0df62d..0c41808496 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ b/qcsrc/common/triggers/trigger/jumppads.qh @@ -19,12 +19,6 @@ void() SUB_UseTargets; void trigger_push_use(); #endif -#ifdef CSQC -void ent_trigger_push(); - -void ent_target_push(); -#endif - /* trigger_push_calculatevelocity diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc index 365c6f5a90..98b121d6ee 100644 --- a/qcsrc/common/triggers/trigger/keylock.qc +++ b/qcsrc/common/triggers/trigger/keylock.qc @@ -94,10 +94,12 @@ void trigger_keylock_touch() } +REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK) + #ifdef SVQC bool trigger_keylock_send(entity to, int sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_KEYLOCK); + WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK); WriteInt24_t(MSG_ENTITY, self.itemkeys); WriteByte(MSG_ENTITY, self.height); @@ -146,7 +148,7 @@ spawnfunc(trigger_keylock) if(self.sounds == 1) self.noise = "misc/secret.wav"; else if(self.sounds == 2) - self.noise = SND(TALK); + self.noise = strzone(SND(TALK)); else //if (self.sounds == 3) { self.noise = "misc/trigger1.wav"; } @@ -195,13 +197,15 @@ void keylock_remove() self.targetname = string_null; } -void ent_keylock() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew) +{ self.itemkeys = ReadInt24_t(); self.height = ReadByte(); trigger_common_read(true); + return = true; + self.classname = "trigger_keylock"; self.drawmask = MASK_NORMAL; self.draw = trigger_draw_generic; diff --git a/qcsrc/common/triggers/trigger/keylock.qh b/qcsrc/common/triggers/trigger/keylock.qh index bbec0182f3..a59cb3f695 100644 --- a/qcsrc/common/triggers/trigger/keylock.qh +++ b/qcsrc/common/triggers/trigger/keylock.qh @@ -1,5 +1,4 @@ #ifdef CSQC -void ent_keylock(); bool item_keys_usekey(entity l, entity p) { int valid = (l.itemkeys & p.itemkeys); diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc index bc5821c809..91bfc99834 100644 --- a/qcsrc/common/triggers/trigger/multi.qc +++ b/qcsrc/common/triggers/trigger/multi.qc @@ -153,7 +153,7 @@ spawnfunc(trigger_multiple) } else if (self.sounds == 2) { - self.noise = SND(TALK); + self.noise = strzone(SND(TALK)); } else if (self.sounds == 3) { diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc index a906fcd313..ae29a75342 100644 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ b/qcsrc/common/triggers/trigger/swamp.qc @@ -22,7 +22,7 @@ #ifdef SVQC spawnfunc(trigger_swamp); #endif -void swamp_touch(void); +void swamp_touch(); void swampslug_think(); @@ -36,7 +36,7 @@ void swampslug_think(); * * I do it this way becuz there is no "untouch" event. */ -void swampslug_think(void) +void swampslug_think() {SELFPARAM(); //Slowly kill the slug self.health = self.health - 1; @@ -60,7 +60,7 @@ void swampslug_think(void) self.nextthink = time + self.swamp_interval; } -void swamp_touch(void) +void swamp_touch() {SELFPARAM(); // If whatever thats touching the swamp is not a player // or if its a dead player, just dont care abt it. @@ -92,10 +92,12 @@ void swamp_touch(void) other.swampslug.health = 2; } +REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) + #ifdef SVQC float swamp_send(entity to, float sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_LADDER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP); WriteByte(MSG_ENTITY, self.dmg); // can probably get away with using a single byte here WriteByte(MSG_ENTITY, self.swamp_slowdown); @@ -134,14 +136,16 @@ spawnfunc(trigger_swamp) #elif defined(CSQC) -void ent_swamp() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew) +{ self.dmg = ReadByte(); self.swamp_slowdown = ReadByte(); self.swamp_interval = ReadByte(); trigger_common_read(false); + return = true; + self.classname = "trigger_swamp"; self.solid = SOLID_TRIGGER; self.draw = trigger_draw_generic; diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh index 86b14315b0..7ae50bac42 100644 --- a/qcsrc/common/triggers/trigger/swamp.qh +++ b/qcsrc/common/triggers/trigger/swamp.qh @@ -8,8 +8,4 @@ .float in_swamp; // bool .entity swampslug; // Uses this to release from swamp ("untouch" fix) -#ifdef CSQC -void ent_swamp(); -#endif - #endif diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc index 95bd7fbbd4..6522669200 100644 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ b/qcsrc/common/triggers/trigger/teleport.qc @@ -8,7 +8,7 @@ void trigger_teleport_use() #endif } -void Teleport_Touch (void) +void Teleport_Touch () {SELFPARAM(); string s; diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc index c21fe6b548..af67533a83 100644 --- a/qcsrc/common/triggers/trigger/viewloc.qc +++ b/qcsrc/common/triggers/trigger/viewloc.qc @@ -5,6 +5,9 @@ #include "../../../server/defs.qh" #endif +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC) +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER) + #ifdef SVQC void viewloc_think() @@ -40,7 +43,7 @@ void viewloc_think() bool trigger_viewloc_send(entity this, entity to, int sf) { // CSQC doesn't need to know our origin (yet), as we're only available for referencing - WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); WriteEntity(MSG_ENTITY, self.enemy); WriteEntity(MSG_ENTITY, self.goalentity); @@ -90,7 +93,7 @@ spawnfunc(trigger_viewlocation) bool viewloc_send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_VIEWLOC); + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC); WriteByte(MSG_ENTITY, self.cnt); @@ -137,8 +140,8 @@ void trigger_viewloc_updatelink() self.goalentity = findfloat(world, entnum, self.count); } -void ent_viewloc_trigger() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew) +{ float point1 = ReadShort(); float point2 = ReadShort(); @@ -148,6 +151,9 @@ void ent_viewloc_trigger() self.origin_x = ReadCoord(); self.origin_y = ReadCoord(); self.origin_z = ReadCoord(); + + return = true; + setorigin(self, self.origin); self.cnt = point1; @@ -160,8 +166,8 @@ void ent_viewloc_trigger() self.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive } -void ent_viewloc() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) +{ self.cnt = ReadByte(); self.origin_x = ReadCoord(); @@ -173,6 +179,8 @@ void ent_viewloc() self.movedir_y = ReadCoord(); self.movedir_z = ReadCoord(); + return = true; + self.classname = ((self.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start"); self.drawmask = MASK_NORMAL; // don't cull it } diff --git a/qcsrc/common/triggers/trigger/viewloc.qh b/qcsrc/common/triggers/trigger/viewloc.qh index 6708a8e47f..881197a3b8 100644 --- a/qcsrc/common/triggers/trigger/viewloc.qh +++ b/qcsrc/common/triggers/trigger/viewloc.qh @@ -7,9 +7,6 @@ .entity goalentity; .entity enemy; .vector movedir; - -void ent_viewloc(); -void ent_viewloc_trigger(); #endif #endif diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc index b97d2b1d9d..41180dd2cc 100644 --- a/qcsrc/common/triggers/triggers.qc +++ b/qcsrc/common/triggers/triggers.qc @@ -148,8 +148,7 @@ void SUB_UseTargets() if (self.delay) { // create a temp object to fire at a later time - t = spawn(); - t.classname = "DelayedUse"; + t = new(DelayedUse); t.nextthink = time + self.delay; t.think = DelayThink; t.enemy = activator; diff --git a/qcsrc/common/turrets/all.qc b/qcsrc/common/turrets/all.qc index d996778e65..bbd0d6811a 100644 --- a/qcsrc/common/turrets/all.qc +++ b/qcsrc/common/turrets/all.qc @@ -1,13 +1,11 @@ #include "all.qh" +REGISTER_NET_LINKED(ENT_CLIENT_TURRET) + #ifdef SVQC #include "sv_turrets.qh" #endif -#ifdef CSQC -#include "cl_turrets.qh" -#endif - #define IMPLEMENTATION #include "all.inc" #undef IMPLEMENTATION diff --git a/qcsrc/common/turrets/all.qh b/qcsrc/common/turrets/all.qh index 3f9eba4f37..fc79400434 100644 --- a/qcsrc/common/turrets/all.qh +++ b/qcsrc/common/turrets/all.qh @@ -6,8 +6,11 @@ #include "turret.qh" -REGISTRY(Turrets, BIT(5)) -REGISTER_REGISTRY(RegisterTurrets) +REGISTRY(Turrets, BITS(5)) +#define Turrets_from(i) _Turrets_from(i, TUR_Null) +#define get_turretinfo(i) Turrets_from(i) +REGISTER_REGISTRY(Turrets) +REGISTRY_CHECK(Turrets) GENERIC_COMMAND(dumpturrets, "Dump all turrets into turrets_dump.txt") @@ -67,19 +70,10 @@ GENERIC_COMMAND(dumpturrets, "Dump all turrets into turrets_dump.txt") const int TUR_FIRST = 1; #define TUR_LAST (Turrets_COUNT - 1) -#define REGISTER_TURRET(id, inst) REGISTER(RegisterTurrets, TUR, Turrets, id, m_id, inst) +#define REGISTER_TURRET(id, inst) REGISTER(Turrets, TUR, id, m_id, inst) REGISTER_TURRET(Null, NEW(Turret)); -Turret get_turretinfo(int id) -{ - if (id >= TUR_FIRST && id <= TUR_LAST) { - Turret t = Turrets[id]; - if (t) return t; - } - return TUR_Null; -} - #include "all.inc" #endif diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index 582113cf80..07378804f6 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -45,11 +45,11 @@ void turret_draw(entity this) if(self.health < 85) if(dt < 0.01) - pointparticles(particleeffectnum(EFFECT_SMOKE_LARGE), (self.origin + (randomvec() * 80)), '0 0 0', 1); + pointparticles(EFFECT_SMOKE_LARGE, (self.origin + (randomvec() * 80)), '0 0 0', 1); if(self.health < 32) if(dt < 0.015) - pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), (self.origin + (randomvec() * 80)), '0 0 0', 1); + pointparticles(EFFECT_SMOKE_SMALL, (self.origin + (randomvec() * 80)), '0 0 0', 1); } @@ -273,7 +273,7 @@ void turret_gibboom() float i; sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1); + pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1); for (i = 1; i < 5; i = i + 1) turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false); @@ -287,7 +287,7 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo if(trace_startsolid) return world; - gib = spawn(); + gib = new(turret_gib); setorigin(gib, _from); _setmodel(gib, _model); gib.colormod = _cmod; @@ -311,7 +311,6 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo gib.move_avelocity = prandomvec() * 32; gib.move_time = time; gib.damageforcescale = 1; - gib.classname = "turret_gib"; return gib; } @@ -319,7 +318,7 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo void turret_die() {SELFPARAM(); sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1); + pointparticles(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1); if (!autocvar_cl_nogibs) { // Base @@ -355,8 +354,8 @@ void turret_die() setmodel(self.tur_head, MDL_Null); } -void ent_turret() -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) +{ float sf; sf = ReadByte(); @@ -442,4 +441,5 @@ void ent_turret() self.health = _tmp; } //self.enemy.health = self.health / 255; + return true; } diff --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh deleted file mode 100644 index 0f8ff94851..0000000000 --- a/qcsrc/common/turrets/cl_turrets.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CL_TURRETS_H -#define CL_TURRETS_H - -void ent_turret(); - -#endif diff --git a/qcsrc/common/turrets/config.qc b/qcsrc/common/turrets/config.qc index c2a609b15c..34cf50eacc 100644 --- a/qcsrc/common/turrets/config.qc +++ b/qcsrc/common/turrets/config.qc @@ -25,7 +25,7 @@ float T_Config_Queue_Compare(float root, float child, entity pass) return 0; } -void Dump_Turret_Settings(void) +void Dump_Turret_Settings() { float x, totalsettings = 0; FOREACH(Turrets, it != TUR_Null, LAMBDA({ diff --git a/qcsrc/common/turrets/config.qh b/qcsrc/common/turrets/config.qh index 4d35ee3b38..bb2a81b847 100644 --- a/qcsrc/common/turrets/config.qh +++ b/qcsrc/common/turrets/config.qh @@ -3,7 +3,7 @@ #ifdef SVQC -void Dump_Turret_Settings(void); +void Dump_Turret_Settings(); float tur_config_file; float tur_config_alsoprint; diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index ec7ce3711e..9ab9a32118 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -13,7 +13,7 @@ vector turret_aim_generic() if(self.aim_flags & TFL_AIM_SIMPLE) return real_origin(self.enemy); - mintime = max(self.attack_finished_single - time,0) + sys_frametime; + mintime = max(self.attack_finished_single[0] - time,0) + sys_frametime; // Baseline pre_pos = real_origin(self.enemy); @@ -302,7 +302,7 @@ void turrets_setframe(float _frame, float client_only) bool turret_send(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TURRET); WriteByte(MSG_ENTITY, sf); if(sf & TNSF_SETUP) { @@ -913,7 +913,7 @@ float turret_firecheck() // Ready? if (self.firecheck_flags & TFL_FIRECHECK_REFIRE) - if (self.attack_finished_single > time) return 0; + if (self.attack_finished_single[0] > time) return 0; // Special case: volly fire turret that has to fire a full volly if a shot was fired. if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) @@ -996,7 +996,7 @@ void turret_fire() Turret info = get_turretinfo(self.m_id); info.tr_attack(info); - self.attack_finished_single = time + self.shot_refire; + self.attack_finished_single[0] = time + self.shot_refire; self.ammo -= self.shot_dmg; self.volly_counter = self.volly_counter - 1; @@ -1008,7 +1008,7 @@ void turret_fire() self.enemy = world; if (self.shot_volly > 1) - self.attack_finished_single = time + self.shot_volly_refire; + self.attack_finished_single[0] = time + self.shot_volly_refire; } #ifdef TURRET_DEBUG @@ -1267,8 +1267,7 @@ float turret_initialize(Turret tur) entity e = find(world, classname, "turret_manager"); if(!e) { - e = spawn(); - e.classname = "turret_manager"; + e = new(turret_manager); e.think = turrets_manager_think; e.nextthink = time + 2; } @@ -1353,13 +1352,13 @@ float turret_initialize(Turret tur) self.nextthink = time + 1; self.nextthink += turret_count * sys_frametime; - self.tur_head = spawn(); + self.tur_head = new(turret_head); _setmodel(self.tur_head, tur.head_model); setsize(self.tur_head, '0 0 0', '0 0 0'); setorigin(self.tur_head, '0 0 0'); setattachment(self.tur_head, self, "tag_head"); - self.tur_head.netname = self.tur_head.classname = "turret_head"; + self.tur_head.netname = self.tur_head.classname; self.tur_head.team = self.team; self.tur_head.owner = self; self.tur_head.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/turrets/turret.qh b/qcsrc/common/turrets/turret.qh index 8862f47d77..dcb641404a 100644 --- a/qcsrc/common/turrets/turret.qh +++ b/qcsrc/common/turrets/turret.qh @@ -43,11 +43,14 @@ CLASS(Turret, Object) } ATTRIB(Turret, m_weapon, Weapon, WEP_Null) +#ifdef SVQC /** (SERVER) called when turret attacks */ METHOD(Turret, tr_attack, void(Turret this)) { Weapon w = this.m_weapon; - w.wr_think(w, self, true, false); + .entity weaponentity = weaponentities[0]; + w.wr_think(w, self, weaponentity, 1); } +#endif /** (ALL) */ METHOD(Turret, tr_config, void(Turret this)) { // TODO diff --git a/qcsrc/common/turrets/turret/ewheel_weapon.qc b/qcsrc/common/turrets/turret/ewheel_weapon.qc index 34e5a59868..e7ce705002 100644 --- a/qcsrc/common/turrets/turret/ewheel_weapon.qc +++ b/qcsrc/common/turrets/turret/ewheel_weapon.qc @@ -5,7 +5,7 @@ CLASS(EWheelAttack, PortoLaunch) /* flags */ ATTRIB(EWheelAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(EWheelAttack, impulse, int, 5); /* refname */ ATTRIB(EWheelAttack, netname, string, "turret_ewheel"); -/* wepname */ ATTRIB(EWheelAttack, message, string, _("eWheel")); +/* wepname */ ATTRIB(EWheelAttack, m_name, string, _("eWheel")); ENDCLASS(EWheelAttack) REGISTER_WEAPON(EWHEEL, NEW(EWheelAttack)); @@ -16,17 +16,18 @@ REGISTER_WEAPON(EWHEEL, NEW(EWheelAttack)); #ifdef SVQC void turret_initparams(entity); -METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(EWheelAttack_FIRE, W_Sound("electro_fire")); +METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(EWheelAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } turret_do_updates(actor); diff --git a/qcsrc/common/turrets/turret/flac_weapon.qc b/qcsrc/common/turrets/turret/flac_weapon.qc index 834f255dae..d1b560bfb2 100644 --- a/qcsrc/common/turrets/turret/flac_weapon.qc +++ b/qcsrc/common/turrets/turret/flac_weapon.qc @@ -5,7 +5,7 @@ CLASS(FlacAttack, PortoLaunch) /* flags */ ATTRIB(FlacAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(FlacAttack, impulse, int, 5); /* refname */ ATTRIB(FlacAttack, netname, string, "turret_flac"); -/* wepname */ ATTRIB(FlacAttack, message, string, _("FLAC")); +/* wepname */ ATTRIB(FlacAttack, m_name, string, _("FLAC")); ENDCLASS(FlacAttack) REGISTER_WEAPON(FLAC, NEW(FlacAttack)); @@ -16,18 +16,19 @@ REGISTER_WEAPON(FLAC, NEW(FlacAttack)); #ifdef SVQC void turret_flac_projectile_think_explode(); -METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(FlacAttack_FIRE, W_Sound("electro_fire")); +METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(FlacAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; actor.tur_impacttime = 10; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } turret_tag_fire_update(); diff --git a/qcsrc/common/turrets/turret/fusionreactor.qc b/qcsrc/common/turrets/turret/fusionreactor.qc index 1077d5c635..3f51c06c32 100644 --- a/qcsrc/common/turrets/turret/fusionreactor.qc +++ b/qcsrc/common/turrets/turret/fusionreactor.qc @@ -19,7 +19,7 @@ REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor)); #ifdef SVQC bool turret_fusionreactor_firecheck() {SELFPARAM(); - if (self.attack_finished_single > time) + if (self.attack_finished_single[0] > time) return false; if (self.enemy.deadflag != DEAD_NO) diff --git a/qcsrc/common/turrets/turret/hellion_weapon.qc b/qcsrc/common/turrets/turret/hellion_weapon.qc index 2d754e877d..77aeb7c7e1 100644 --- a/qcsrc/common/turrets/turret/hellion_weapon.qc +++ b/qcsrc/common/turrets/turret/hellion_weapon.qc @@ -5,7 +5,7 @@ CLASS(HellionAttack, PortoLaunch) /* flags */ ATTRIB(HellionAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(HellionAttack, impulse, int, 9); /* refname */ ATTRIB(HellionAttack, netname, string, "turret_hellion"); -/* wepname */ ATTRIB(HellionAttack, message, string, _("Hellion")); +/* wepname */ ATTRIB(HellionAttack, m_name, string, _("Hellion")); ENDCLASS(HellionAttack) REGISTER_WEAPON(HELLION, NEW(HellionAttack)); @@ -19,18 +19,19 @@ float autocvar_g_turrets_unit_hellion_shot_speed_gain; float autocvar_g_turrets_unit_hellion_shot_speed_max; void turret_hellion_missile_think(); -METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(HellionAttack_FIRE, W_Sound("electro_fire")); +METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(HellionAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; actor.shot_radius = 500; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } if (!isPlayer) { if (actor.tur_head.frame != 0) diff --git a/qcsrc/common/turrets/turret/hk_weapon.qc b/qcsrc/common/turrets/turret/hk_weapon.qc index 98bff57575..b083c6830e 100644 --- a/qcsrc/common/turrets/turret/hk_weapon.qc +++ b/qcsrc/common/turrets/turret/hk_weapon.qc @@ -5,7 +5,7 @@ CLASS(HunterKillerAttack, PortoLaunch) /* flags */ ATTRIB(HunterKillerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(HunterKillerAttack, impulse, int, 9); /* refname */ ATTRIB(HunterKillerAttack, netname, string, "turret_hk"); -/* wepname */ ATTRIB(HunterKillerAttack, message, string, _("Hunter-Killer")); +/* wepname */ ATTRIB(HunterKillerAttack, m_name, string, _("Hunter-Killer")); ENDCLASS(HunterKillerAttack) REGISTER_WEAPON(HK, NEW(HunterKillerAttack)); @@ -23,18 +23,19 @@ float autocvar_g_turrets_unit_hk_shot_speed_max; float autocvar_g_turrets_unit_hk_shot_speed_turnrate; void turret_hk_missile_think(); -METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) +SOUND(HunterKillerAttack_FIRE, W_Sound("electro_fire")); +METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(HunterKillerAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_HK.m_id, PROJECTILE_ROCKET, FALSE, FALSE); te_explosion (missile.origin); diff --git a/qcsrc/common/turrets/turret/machinegun_weapon.qc b/qcsrc/common/turrets/turret/machinegun_weapon.qc index a20bdb22cf..7bd8b40868 100644 --- a/qcsrc/common/turrets/turret/machinegun_weapon.qc +++ b/qcsrc/common/turrets/turret/machinegun_weapon.qc @@ -5,7 +5,7 @@ CLASS(MachineGunTurretAttack, PortoLaunch) /* flags */ ATTRIB(MachineGunTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(MachineGunTurretAttack, impulse, int, 9); /* refname */ ATTRIB(MachineGunTurretAttack, netname, string, "turret_machinegun"); -/* wepname */ ATTRIB(MachineGunTurretAttack, message, string, _("Machinegun")); +/* wepname */ ATTRIB(MachineGunTurretAttack, m_name, string, _("Machinegun")); ENDCLASS(MachineGunTurretAttack) REGISTER_WEAPON(TUR_MACHINEGUN, NEW(MachineGunTurretAttack)); @@ -16,19 +16,19 @@ REGISTER_WEAPON(TUR_MACHINEGUN, NEW(MachineGunTurretAttack)); #ifdef SVQC void W_MachineGun_MuzzleFlash(); - -METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) +SOUND(MachineGunTurretAttack_FIRE, W_Sound("electro_fire")); +METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(MachineGunTurretAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, 0); W_MachineGun_MuzzleFlash(); diff --git a/qcsrc/common/turrets/turret/mlrs_weapon.qc b/qcsrc/common/turrets/turret/mlrs_weapon.qc index 0593359136..abd7b0fc4e 100644 --- a/qcsrc/common/turrets/turret/mlrs_weapon.qc +++ b/qcsrc/common/turrets/turret/mlrs_weapon.qc @@ -5,7 +5,7 @@ CLASS(MLRSTurretAttack, PortoLaunch) /* flags */ ATTRIB(MLRSTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(MLRSTurretAttack, impulse, int, 9); /* refname */ ATTRIB(MLRSTurretAttack, netname, string, "turret_mlrs"); -/* wepname */ ATTRIB(MLRSTurretAttack, message, string, _("MLRS")); +/* wepname */ ATTRIB(MLRSTurretAttack, m_name, string, _("MLRS")); ENDCLASS(MLRSTurretAttack) REGISTER_WEAPON(TUR_MLRS, NEW(MLRSTurretAttack)); @@ -14,20 +14,20 @@ REGISTER_WEAPON(TUR_MLRS, NEW(MLRSTurretAttack)); #ifdef IMPLEMENTATION #ifdef SVQC - -METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) +SOUND(MLRSTurretAttack_FIRE, W_Sound("electro_fire")); +METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR(machinegun, sustained_refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(MLRSTurretAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; actor.shot_radius = 500; - weapon_thinkf(actor, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } turret_tag_fire_update(); entity missile = turret_projectile(SND(ROCKET_FIRE), 6, 10, DEATH_TURRET_MLRS.m_id, PROJECTILE_ROCKET, TRUE, TRUE); diff --git a/qcsrc/common/turrets/turret/phaser_weapon.qc b/qcsrc/common/turrets/turret/phaser_weapon.qc index c4529ae267..087ade8d47 100644 --- a/qcsrc/common/turrets/turret/phaser_weapon.qc +++ b/qcsrc/common/turrets/turret/phaser_weapon.qc @@ -5,7 +5,7 @@ CLASS(PhaserTurretAttack, PortoLaunch) /* flags */ ATTRIB(PhaserTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(PhaserTurretAttack, impulse, int, 9); /* refname */ ATTRIB(PhaserTurretAttack, netname, string, "turret_phaser"); -/* wepname */ ATTRIB(PhaserTurretAttack, message, string, _("Phaser")); +/* wepname */ ATTRIB(PhaserTurretAttack, m_name, string, _("Phaser")); ENDCLASS(PhaserTurretAttack) REGISTER_WEAPON(PHASER, NEW(PhaserTurretAttack)); @@ -17,20 +17,20 @@ REGISTER_WEAPON(PHASER, NEW(PhaserTurretAttack)); void beam_think(); .int fireflag; - -METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) +SOUND(PhaserTurretAttack_FIRE, W_Sound("electro_fire")); +METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(PhaserTurretAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; actor.shot_speed = 1; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } entity beam = spawn(); beam.ticrate = 0.1; //autocvar_sys_ticrate; @@ -51,8 +51,8 @@ METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, bool fir sound (beam, CH_SHOTS_SINGLE, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM); actor.fireflag = 1; - beam.attack_finished_single = actor.attack_finished_single; - actor.attack_finished_single = time; // + autocvar_sys_ticrate; + beam.attack_finished_single[0] = actor.attack_finished_single[0]; + actor.attack_finished_single[0] = time; // + autocvar_sys_ticrate; setattachment(beam,actor.tur_head, "tag_fire"); @@ -67,7 +67,7 @@ void beam_think() {SELFPARAM(); if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) { - self.owner.attack_finished_single = time + self.owner.shot_refire; + self.owner.attack_finished_single[0] = time + self.owner.shot_refire; self.owner.fireflag = 2; self.owner.tur_head.frame = 10; sound (self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); @@ -86,7 +86,7 @@ void beam_think() self.nextthink = time + self.ticrate; - self.owner.attack_finished_single = time + frametime; + self.owner.attack_finished_single[0] = time + frametime; setself(self.owner); FireImoBeam ( self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * self.target_range, diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc index 318f4e8ae6..7ed255911b 100644 --- a/qcsrc/common/turrets/turret/plasma.qc +++ b/qcsrc/common/turrets/turret/plasma.qc @@ -41,7 +41,7 @@ METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this)) } else { - super.tr_attack(this); + SUPER(PlasmaTurret).tr_attack(this); } if (self.tur_head.frame == 0) self.tur_head.frame = 1; diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc index 387ae44a59..cb78547a26 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qc +++ b/qcsrc/common/turrets/turret/plasma_dual.qc @@ -3,7 +3,7 @@ CLASS(PlasmaDualAttack, PlasmaAttack) /* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); -/* wepname */ ATTRIB(PlasmaDualAttack, message, string, _("Dual plasma")); +/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); ENDCLASS(PlasmaDualAttack) REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack)); @@ -41,7 +41,7 @@ METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret this)) vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); WarpZone_TrailParticles(world, particleeffectnum(EFFECT_VAPORIZER(self.team)), self.tur_shotorg, v); } else { - super.vtblbase.tr_attack(this); + SUPER(PlasmaTurret).tr_attack(this); } self.tur_head.frame += 1; } diff --git a/qcsrc/common/turrets/turret/plasma_weapon.qc b/qcsrc/common/turrets/turret/plasma_weapon.qc index 2141fe64f2..65e182be38 100644 --- a/qcsrc/common/turrets/turret/plasma_weapon.qc +++ b/qcsrc/common/turrets/turret/plasma_weapon.qc @@ -5,7 +5,7 @@ CLASS(PlasmaAttack, PortoLaunch) /* flags */ ATTRIB(PlasmaAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(PlasmaAttack, impulse, int, 5); /* refname */ ATTRIB(PlasmaAttack, netname, string, "turret_plasma"); -/* wepname */ ATTRIB(PlasmaAttack, message, string, _("Plasma")); +/* wepname */ ATTRIB(PlasmaAttack, m_name, string, _("Plasma")); ENDCLASS(PlasmaAttack) REGISTER_WEAPON(PLASMA, NEW(PlasmaAttack)); @@ -14,18 +14,18 @@ REGISTER_WEAPON(PLASMA, NEW(PlasmaAttack)); #ifdef IMPLEMENTATION #ifdef SVQC - -METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(PlasmaAttack_FIRE, W_Sound("electro_fire")); +METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(PlasmaAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } entity missile = turret_projectile(SND(HAGAR_FIRE), 1, 0, DEATH_TURRET_PLASMA.m_id, PROJECTILE_ELECTRO_BEAM, true, true); missile.missile_flags = MIF_SPLASH; diff --git a/qcsrc/common/turrets/turret/tesla.qc b/qcsrc/common/turrets/turret/tesla.qc index 3ae039c3cc..16a9e423d3 100644 --- a/qcsrc/common/turrets/turret/tesla.qc +++ b/qcsrc/common/turrets/turret/tesla.qc @@ -42,7 +42,7 @@ METHOD(TeslaCoil, tr_think, void(TeslaCoil thistur)) { self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg); - if(self.attack_finished_single > time) + if(self.attack_finished_single[0] > time) return; float f; diff --git a/qcsrc/common/turrets/turret/tesla_weapon.qc b/qcsrc/common/turrets/turret/tesla_weapon.qc index 4391472f73..7e9108cb42 100644 --- a/qcsrc/common/turrets/turret/tesla_weapon.qc +++ b/qcsrc/common/turrets/turret/tesla_weapon.qc @@ -5,7 +5,7 @@ CLASS(TeslaCoilTurretAttack, PortoLaunch) /* flags */ ATTRIB(TeslaCoilTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(TeslaCoilTurretAttack, impulse, int, 9); /* refname */ ATTRIB(TeslaCoilTurretAttack, netname, string, "turret_tesla"); -/* wepname */ ATTRIB(TeslaCoilTurretAttack, message, string, _("Tesla Coil")); +/* wepname */ ATTRIB(TeslaCoilTurretAttack, m_name, string, _("Tesla Coil")); ENDCLASS(TeslaCoilTurretAttack) REGISTER_WEAPON(TESLA, NEW(TeslaCoilTurretAttack)); @@ -16,17 +16,18 @@ REGISTER_WEAPON(TESLA, NEW(TeslaCoilTurretAttack)); #ifdef SVQC entity toast(entity from, float range, float damage); -METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(TeslaCoilTurretAttack_FIRE, W_Sound("electro_fire")); +METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(TeslaCoilTurretAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } float d = actor.shot_dmg; @@ -43,7 +44,7 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, bool actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK; - actor.attack_finished_single = time + actor.shot_refire; + actor.attack_finished_single[0] = time + actor.shot_refire; for (int i = 0; i < 10; ++i) { d *= 0.75; r *= 0.85; diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc index cbdeb5e8e7..eac40d2209 100644 --- a/qcsrc/common/turrets/turret/walker.qc +++ b/qcsrc/common/turrets/turret/walker.qc @@ -233,13 +233,12 @@ void walker_fire_rocket(vector org) te_explosion (org); - rocket = spawn (); + rocket = new(walker_rocket); setorigin(rocket, org); sound (self, CH_WEAPON_A, SND_HAGAR_FIRE, VOL_BASE, ATTEN_NORM); setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - rocket.classname = "walker_rocket"; rocket.owner = self; rocket.bot_dodge = true; rocket.bot_dodgerating = 50; @@ -429,7 +428,7 @@ spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER)) remove(self); } self.animflag = ANIM_MELEE; } } - else if (self.tur_head.attack_finished_single < time) + else if (self.tur_head.attack_finished_single[0] < time) { if(self.tur_head.shot_volly) { @@ -437,9 +436,9 @@ spawnfunc(turret_walker) { if(!turret_initialize(TUR_WALKER)) remove(self); } self.tur_head.shot_volly = self.tur_head.shot_volly -1; if(self.tur_head.shot_volly == 0) - self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire); + self.tur_head.attack_finished_single[0] = time + (autocvar_g_turrets_unit_walker_rocket_refire); else - self.tur_head.attack_finished_single = time + 0.2; + self.tur_head.attack_finished_single[0] = time + 0.2; if(self.tur_head.shot_volly > 1) walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01"))); diff --git a/qcsrc/common/turrets/turret/walker_weapon.qc b/qcsrc/common/turrets/turret/walker_weapon.qc index 4da45df7fd..a3150d820d 100644 --- a/qcsrc/common/turrets/turret/walker_weapon.qc +++ b/qcsrc/common/turrets/turret/walker_weapon.qc @@ -5,7 +5,7 @@ CLASS(WalkerTurretAttack, PortoLaunch) /* flags */ ATTRIB(WalkerTurretAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(WalkerTurretAttack, impulse, int, 5); /* refname */ ATTRIB(WalkerTurretAttack, netname, string, "turret_walker"); -/* wepname */ ATTRIB(WalkerTurretAttack, message, string, _("Walker")); +/* wepname */ ATTRIB(WalkerTurretAttack, m_name, string, _("Walker")); ENDCLASS(WalkerTurretAttack) REGISTER_WEAPON(WALKER, NEW(WalkerTurretAttack)); @@ -15,17 +15,18 @@ REGISTER_WEAPON(WALKER, NEW(WalkerTurretAttack)); #ifdef SVQC -METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +SOUND(WalkerTurretAttack_FIRE, W_Sound("electro_fire")); +METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); - if (fire1) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) { + if (fire & 1) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, W_Sound("electro_fire"), CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, v_forward, false, 0, SND(WalkerTurretAttack_FIRE), CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } sound (actor, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM); fireBullet (actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_WALK_GUN.m_id, 0); diff --git a/qcsrc/common/turrets/util.qc b/qcsrc/common/turrets/util.qc index 53c5fb74df..b0667c89af 100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@ -224,10 +224,7 @@ void marker_think() void mark_error(vector where,float lifetime) { - entity err; - - err = spawn(); - err.classname = "error_marker"; + entity err = new(error_marker); setmodel(err, MDL_MARKER); setorigin(err,where); err.movetype = MOVETYPE_NONE; @@ -240,10 +237,7 @@ void mark_error(vector where,float lifetime) void mark_info(vector where,float lifetime) { - entity err; - - err = spawn(); - err.classname = "info_marker"; + entity err = spawn(info_marker); setmodel(err, MDL_MARKER); setorigin(err,where); err.movetype = MOVETYPE_NONE; @@ -256,10 +250,7 @@ void mark_info(vector where,float lifetime) entity mark_misc(vector where,float lifetime) { - entity err; - - err = spawn(); - err.classname = "mark_misc"; + entity err = spawn(mark_misc); setmodel(err, MDL_MARKER); setorigin(err,where); err.movetype = MOVETYPE_NONE; diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 7ac910f998..3e380ca696 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -163,205 +163,6 @@ void depthfirst(entity start, .entity up, .entity downleft, .entity right, void( } } -// converts a number to a string with the indicated number of decimals -// works for up to 10 decimals! -string ftos_decimals(float number, float decimals) -{ - // inhibit stupid negative zero - if(number == 0) - number = 0; - // we have sprintf... - return sprintf("%.*f", decimals, number); -} - -// Databases (hash tables) -const float DB_BUCKETS = 8192; -void db_save(float db, string pFilename) -{ - float fh, i, n; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - { - LOG_INFO(strcat("^1Can't write DB to ", pFilename)); - return; - } - n = buf_getsize(db); - fputs(fh, strcat(ftos(DB_BUCKETS), "\n")); - for(i = 0; i < n; ++i) - fputs(fh, strcat(bufstr_get(db, i), "\n")); - fclose(fh); -} - -int db_create() -{ - return buf_create(); -} - -int db_load(string pFilename) -{ - float db, fh, i, j, n; - string l; - db = buf_create(); - if(db < 0) - return -1; - fh = fopen(pFilename, FILE_READ); - if(fh < 0) - return db; - l = fgets(fh); - if(stof(l) == DB_BUCKETS) - { - i = 0; - while((l = fgets(fh))) - { - if(l != "") - bufstr_set(db, i, l); - ++i; - } - } - else - { - // different count of buckets, or a dump? - // need to reorganize the database then (SLOW) - // - // note: we also parse the first line (l) in case the DB file is - // missing the bucket count - do - { - n = tokenizebyseparator(l, "\\"); - for(j = 2; j < n; j += 2) - db_put(db, argv(j-1), uri_unescape(argv(j))); - } - while((l = fgets(fh))); - } - fclose(fh); - return db; -} - -void db_dump(float db, string pFilename) -{ - float fh, i, j, n, m; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - error(strcat("Can't dump DB to ", pFilename)); - n = buf_getsize(db); - fputs(fh, "0\n"); - for(i = 0; i < n; ++i) - { - m = tokenizebyseparator(bufstr_get(db, i), "\\"); - for(j = 2; j < m; j += 2) - fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n")); - } - fclose(fh); -} - -void db_close(float db) -{ - buf_del(db); -} - -string db_get(float db, string pKey) -{ - float h; - h = crc16(false, pKey) % DB_BUCKETS; - return uri_unescape(infoget(bufstr_get(db, h), pKey)); -} - -void db_put(float db, string pKey, string pValue) -{ - float h; - h = crc16(false, pKey) % DB_BUCKETS; - bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue))); -} - -void db_test() -{ - float db, i; - LOG_INFO("LOAD...\n"); - db = db_load("foo.db"); - LOG_INFO("LOADED. FILL...\n"); - for(i = 0; i < DB_BUCKETS; ++i) - db_put(db, ftos(random()), "X"); - LOG_INFO("FILLED. SAVE...\n"); - db_save(db, "foo.db"); - LOG_INFO("SAVED. CLOSE...\n"); - db_close(db); - LOG_INFO("CLOSED.\n"); -} - -// Multiline text file buffers -int buf_load(string pFilename) -{ - float buf, fh, i; - string l; - buf = buf_create(); - if(buf < 0) - return -1; - fh = fopen(pFilename, FILE_READ); - if(fh < 0) - { - buf_del(buf); - return -1; - } - i = 0; - while((l = fgets(fh))) - { - bufstr_set(buf, i, l); - ++i; - } - fclose(fh); - return buf; -} - -void buf_save(float buf, string pFilename) -{ - float fh, i, n; - fh = fopen(pFilename, FILE_WRITE); - if(fh < 0) - error(strcat("Can't write buf to ", pFilename)); - n = buf_getsize(buf); - for(i = 0; i < n; ++i) - fputs(fh, strcat(bufstr_get(buf, i), "\n")); - fclose(fh); -} - -string format_time(float seconds) -{ - float days, hours, minutes; - seconds = floor(seconds + 0.5); - days = floor(seconds / 864000); - seconds -= days * 864000; - hours = floor(seconds / 36000); - seconds -= hours * 36000; - minutes = floor(seconds / 600); - seconds -= minutes * 600; - if (days > 0) - return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds); - else - return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds); -} - -string mmsss(float tenths) -{ - float minutes; - string s; - tenths = floor(tenths + 0.5); - minutes = floor(tenths / 600); - tenths -= minutes * 600; - s = ftos(1000 + tenths); - return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1)); -} - -string mmssss(float hundredths) -{ - float minutes; - string s; - hundredths = floor(hundredths + 0.5); - minutes = floor(hundredths / 6000); - hundredths -= minutes * 6000; - s = ftos(10000 + hundredths); - return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2)); -} - string ScoreString(int pFlags, float pValue) { string valstr; @@ -624,23 +425,6 @@ string swapInPriorityList(string order, float i, float j) return order; } -float cvar_value_issafe(string s) -{ - if(strstrofs(s, "\"", 0) >= 0) - return 0; - if(strstrofs(s, "\\", 0) >= 0) - return 0; - if(strstrofs(s, ";", 0) >= 0) - return 0; - if(strstrofs(s, "$", 0) >= 0) - return 0; - if(strstrofs(s, "\r", 0) >= 0) - return 0; - if(strstrofs(s, "\n", 0) >= 0) - return 0; - return 1; -} - #ifndef MENUQC void get_mi_min_max(float mode) { @@ -803,8 +587,8 @@ float cvar_settemp(string tmp_cvar, string tmp_value) if(created_saved_value != -1) { // creating a new entity to keep track of this cvar - e = spawn(); - e.classname = "saved_cvar_value"; + e = new(saved_cvar_value); + make_pure(e); e.netname = strzone(tmp_cvar); e.message = strzone(cvar_string(tmp_cvar)); created_saved_value = 1; @@ -835,150 +619,6 @@ float cvar_settemp_restore() return i; } -float rgb_mi_ma_to_hue(vector rgb, float mi, float ma) -{ - if(mi == ma) - return 0; - else if(ma == rgb.x) - { - if(rgb.y >= rgb.z) - return (rgb.y - rgb.z) / (ma - mi); - else - return (rgb.y - rgb.z) / (ma - mi) + 6; - } - else if(ma == rgb.y) - return (rgb.z - rgb.x) / (ma - mi) + 2; - else // if(ma == rgb_z) - return (rgb.x - rgb.y) / (ma - mi) + 4; -} - -vector hue_mi_ma_to_rgb(float hue, float mi, float ma) -{ - vector rgb; - - hue -= 6 * floor(hue / 6); - - //else if(ma == rgb_x) - // hue = 60 * (rgb_y - rgb_z) / (ma - mi); - if(hue <= 1) - { - rgb.x = ma; - rgb.y = hue * (ma - mi) + mi; - rgb.z = mi; - } - //else if(ma == rgb_y) - // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120; - else if(hue <= 2) - { - rgb.x = (2 - hue) * (ma - mi) + mi; - rgb.y = ma; - rgb.z = mi; - } - else if(hue <= 3) - { - rgb.x = mi; - rgb.y = ma; - rgb.z = (hue - 2) * (ma - mi) + mi; - } - //else // if(ma == rgb_z) - // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240; - else if(hue <= 4) - { - rgb.x = mi; - rgb.y = (4 - hue) * (ma - mi) + mi; - rgb.z = ma; - } - else if(hue <= 5) - { - rgb.x = (hue - 4) * (ma - mi) + mi; - rgb.y = mi; - rgb.z = ma; - } - //else if(ma == rgb_x) - // hue = 60 * (rgb_y - rgb_z) / (ma - mi); - else // if(hue <= 6) - { - rgb.x = ma; - rgb.y = mi; - rgb.z = (6 - hue) * (ma - mi) + mi; - } - - return rgb; -} - -vector rgb_to_hsv(vector rgb) -{ - float mi, ma; - vector hsv; - - mi = min(rgb.x, rgb.y, rgb.z); - ma = max(rgb.x, rgb.y, rgb.z); - - hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma); - hsv.z = ma; - - if(ma == 0) - hsv.y = 0; - else - hsv.y = 1 - mi/ma; - - return hsv; -} - -vector hsv_to_rgb(vector hsv) -{ - return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z); -} - -vector rgb_to_hsl(vector rgb) -{ - float mi, ma; - vector hsl; - - mi = min(rgb.x, rgb.y, rgb.z); - ma = max(rgb.x, rgb.y, rgb.z); - - hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma); - - hsl.z = 0.5 * (mi + ma); - if(mi == ma) - hsl.y = 0; - else if(hsl.z <= 0.5) - hsl.y = (ma - mi) / (2*hsl.z); - else // if(hsl_z > 0.5) - hsl.y = (ma - mi) / (2 - 2*hsl.z); - - return hsl; -} - -vector hsl_to_rgb(vector hsl) -{ - float mi, ma, maminusmi; - - if(hsl.z <= 0.5) - maminusmi = hsl.y * 2 * hsl.z; - else - maminusmi = hsl.y * (2 - 2 * hsl.z); - - // hsl_z = 0.5 * mi + 0.5 * ma - // maminusmi = - mi + ma - mi = hsl.z - 0.5 * maminusmi; - ma = hsl.z + 0.5 * maminusmi; - - return hue_mi_ma_to_rgb(hsl.x, mi, ma); -} - -string rgb_to_hexcolor(vector rgb) -{ - return - strcat( - "^x", - DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)), - DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)), - DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5)) - ); -} - float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w) { // STOP. @@ -1297,60 +937,6 @@ float isGametypeInFilter(float gt, float tp, float ts, string pattern) return 1; } -vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0 -{ - vector v; - float D; - v = '0 0 0'; - if(a == 0) - { - if(b != 0) - { - v.x = v.y = -c / b; - v.z = 1; - } - else - { - if(c == 0) - { - // actually, every number solves the equation! - v.z = 1; - } - } - } - else - { - D = b*b - 4*a*c; - if(D >= 0) - { - D = sqrt(D); - if(a > 0) // put the smaller solution first - { - v.x = ((-b)-D) / (2*a); - v.y = ((-b)+D) / (2*a); - } - else - { - v.x = (-b+D) / (2*a); - v.y = (-b-D) / (2*a); - } - v.z = 1; - } - else - { - // complex solutions! - D = sqrt(-D); - v.x = -b / (2*a); - if(a > 0) - v.y = D / (2*a); - else - v.y = -D / (2*a); - v.z = 0; - } - } - return v; -} - vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style) { vector ret; @@ -1535,27 +1121,6 @@ string getcurrentmod() return argv(n - 1); } -// from the GNU Scientific Library -float gsl_ran_gaussian_lastvalue; -float gsl_ran_gaussian_lastvalue_set; -float gsl_ran_gaussian(float sigma) -{ - float a, b; - if(gsl_ran_gaussian_lastvalue_set) - { - gsl_ran_gaussian_lastvalue_set = 0; - return sigma * gsl_ran_gaussian_lastvalue; - } - else - { - a = random() * 2 * M_PI; - b = sqrt(-2 * log(random())); - gsl_ran_gaussian_lastvalue = cos(a) * b; - gsl_ran_gaussian_lastvalue_set = 1; - return sigma * sin(a) * b; - } -} - float matchacl(string acl, string str) { string t, s; @@ -1713,57 +1278,6 @@ float get_model_parameters(string m, float sk) return 1; } -float vercmp_recursive(string v1, string v2) -{ - float dot1, dot2; - string s1, s2; - float r; - - dot1 = strstrofs(v1, ".", 0); - dot2 = strstrofs(v2, ".", 0); - if(dot1 == -1) - s1 = v1; - else - s1 = substring(v1, 0, dot1); - if(dot2 == -1) - s2 = v2; - else - s2 = substring(v2, 0, dot2); - - r = stof(s1) - stof(s2); - if(r != 0) - return r; - - r = strcasecmp(s1, s2); - if(r != 0) - return r; - - if(dot1 == -1) - if(dot2 == -1) - return 0; - else - return -1; - else - if(dot2 == -1) - return 1; - else - return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999)); -} - -float vercmp(string v1, string v2) -{ - if(strcasecmp(v1, v2) == 0) // early out check - return 0; - - // "git" beats all - if(v1 == "git") - return 1; - if(v2 == "git") - return -1; - - return vercmp_recursive(v1, v2); -} - // x-encoding (encoding as zero length invisible string) const string XENCODE_2 = "xX"; const string XENCODE_22 = "0123456789abcdefABCDEF"; @@ -1807,16 +1321,6 @@ string strlimitedlen(string input, string truncation, float strip_colors, float return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation); }*/ -#ifdef CSQC -entity ReadCSQCEntity() -{ - int f = ReadShort(); - if(f == 0) - return world; - return findfloat(world, entnum, f); -} -#endif - float shutdown_running; #ifdef SVQC void SV_Shutdown() @@ -1840,44 +1344,6 @@ void m_shutdown() cvar_settemp_restore(); // this must be done LAST, but in any case } -const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; -#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) -#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) -// this will use the value: -// 128 -// accuracy near zero is APPROXPASTTIME_MAX/(256*255) -// accuracy at x is 1/derivative, i.e. -// APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536 -#ifdef SVQC -void WriteApproxPastTime(float dst, float t) -{ - float dt = time - t; - - // warning: this is approximate; do not resend when you don't have to! - // be careful with sendflags here! - // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75 - - // map to range... - dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt)); - - // round... - dt = rint(bound(0, dt, 255)); - - WriteByte(dst, dt); -} -#endif -#ifdef CSQC -float ReadApproxPastTime() -{ - float dt = ReadByte(); - - // map from range...PPROXPASTTIME_MAX / 256 - dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt)); - - return servertime - dt; -} -#endif - #ifndef MENUQC .float skeleton_bones_index; void Skeleton_SetBones(entity e) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 8bedccf0ae..f0d180dad1 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -101,11 +101,6 @@ float almost_in_bounds(float a, float b, float c); float power2of(float e); float log2of(float x); -const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef"; -#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0)) -#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10) -#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1)) - vector rgb_to_hsl(vector rgb); vector hsl_to_rgb(vector hsl); vector rgb_to_hsv(vector rgb); @@ -174,11 +169,7 @@ float float2range01(float f); float gsl_ran_gaussian(float sigma); -string car(string s); // returns first word -string cdr(string s); // returns all but first word float matchacl(string acl, string str); // matches str against ACL acl (with entries +foo*, +foo, +*foo, +*foo*, and same with - for forbidding) -float startsWith(string haystack, string needle); -float startsWithNocase(string haystack, string needle); string get_model_datafilename(string mod, float skn, string fil); // skin -1 will return wildcard, mod string_null will also put wildcard there string get_model_parameters_modelname; @@ -215,10 +206,6 @@ const float XENCODE_LEN = 5; string xencode(float f); float xdecode(string s); -#ifdef CSQC -entity ReadCSQCEntity(); -#endif - #ifndef MENUQC string strtolower(string s); #endif @@ -282,11 +269,6 @@ string CCR(string input); #define normal_or_gentle(normal, gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal) #endif -// allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example) -#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname -#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement) -#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0 - vector vec3(float x, float y, float z); #ifndef MENUQC diff --git a/qcsrc/common/vehicles/all.qc b/qcsrc/common/vehicles/all.qc index 72d72d29d1..c7b8458223 100644 --- a/qcsrc/common/vehicles/all.qc +++ b/qcsrc/common/vehicles/all.qc @@ -23,6 +23,8 @@ STATIC_INIT(vehicles_common_initialize) } #endif +REGISTER_NET_LINKED(ENT_CLIENT_AUXILIARYXHAIR) + #if defined(SVQC) #include "sv_vehicles.qc" #elif defined(CSQC) diff --git a/qcsrc/common/vehicles/all.qh b/qcsrc/common/vehicles/all.qh index a44eb2209e..3867cf1edd 100644 --- a/qcsrc/common/vehicles/all.qh +++ b/qcsrc/common/vehicles/all.qh @@ -3,13 +3,17 @@ #include "vehicle.qh" -REGISTRY(Vehicles, BIT(3)) -REGISTER_REGISTRY(RegisterVehicles) +REGISTRY(Vehicles, BITS(3)) +#define Vehicles_from(i) _Vehicles_from(i, VEH_Null) +#define get_vehicleinfo(i) Vehicles_from(i) +REGISTER_REGISTRY(Vehicles) +REGISTRY_CHECK(Vehicles) + const int VEH_FIRST = 1; #define VEH_LAST (Vehicles_COUNT - 1) /** If you register a new vehicle, make sure to add it to all.inc */ -#define REGISTER_VEHICLE(id, inst) REGISTER(RegisterVehicles, VEH, Vehicles, id, vehicleid, inst) +#define REGISTER_VEHICLE(id, inst) REGISTER(Vehicles, VEH, id, vehicleid, inst) #if defined(SVQC) #include "sv_vehicles.qh" @@ -19,15 +23,6 @@ const int VEH_FIRST = 1; REGISTER_VEHICLE(Null, NEW(Vehicle)); -Vehicle get_vehicleinfo(int id) -{ - if (id >= VEH_FIRST && id <= VEH_LAST) { - Vehicle v = Vehicles[id]; - if (v) return v; - } - return VEH_Null; -} - #include "all.inc" #endif diff --git a/qcsrc/common/vehicles/cl_vehicles.qc b/qcsrc/common/vehicles/cl_vehicles.qc index b24097c033..be615772a9 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@ -45,7 +45,7 @@ void AuxiliaryXhair_Draw2D(entity this) self.draw2d = func_null; } -void Net_AuXair2(bool bIsNew) +NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew) { int axh_id = bound(0, ReadByte(), MAX_AXH); entity axh = AuxiliaryXhair[axh_id]; @@ -70,11 +70,13 @@ void Net_AuXair2(bool bIsNew) axh.colormod_z = ReadByte() / 255; axh.cnt = time; axh.draw2d = AuxiliaryXhair_Draw2D; + return true; } -void Net_VehicleSetup() -{SELFPARAM(); +NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew) +{ int hud_id = ReadByte(); + return = true; // hud_id == 0 means we exited a vehicle, so stop alarm sound/s if(hud_id == 0) diff --git a/qcsrc/common/vehicles/cl_vehicles.qh b/qcsrc/common/vehicles/cl_vehicles.qh index 87f0751af8..fbaf88c6f9 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qh +++ b/qcsrc/common/vehicles/cl_vehicles.qh @@ -4,10 +4,6 @@ vector vehicleHud_Size; vector vehicleHud_Pos; -void Net_AuXair2(float bIsNew); - -void Net_VehicleSetup(); - void RaptorCBShellfragDraw(entity this); void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang); diff --git a/qcsrc/common/vehicles/sv_vehicles.qc b/qcsrc/common/vehicles/sv_vehicles.qc index a00cdcfa34..7f0537eeb5 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qc +++ b/qcsrc/common/vehicles/sv_vehicles.qc @@ -40,7 +40,7 @@ bool vehicle_send(entity to, int sf) bool SendAuxiliaryXhair(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR); + WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR); WriteByte(MSG_ENTITY, self.cnt); @@ -87,8 +87,7 @@ void CSQCVehicleSetup(entity own, int vehicle_id) msg_entity = own; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_VEHICLESETUP); + WriteHeader(MSG_ONE, TE_CSQC_VEHICLESETUP); WriteByte(MSG_ONE, vehicle_id); } @@ -455,10 +454,8 @@ void vehicles_reset_colors() void vehicles_clearreturn(entity veh) { - entity ret; // Remove "return helper", if any. - ret = findchain(classname, "vehicle_return"); - while(ret) + for (entity ret = findchain(classname, "vehicle_return"); ret; ret = ret.chain) { if(ret.wp00 == veh) { @@ -471,7 +468,6 @@ void vehicles_clearreturn(entity veh) return; } - ret = ret.chain; } } @@ -548,8 +544,7 @@ void vehicles_setreturn(entity veh) vehicles_clearreturn(veh); - ret = spawn(); - ret.classname = "vehicle_return"; + ret = new(vehicle_return); ret.wp00 = veh; ret.team = veh.team; ret.think = vehicles_showwp; @@ -634,7 +629,7 @@ void vehicles_painframe() float _ftmp; _ftmp = self.owner.vehicle_health / 50; self.pain_frame = time + 0.1 + (random() * 0.5 * _ftmp); - pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), (self.origin + (randomvec() * 80)), '0 0 0', 1); + pointparticles(EFFECT_SMOKE_SMALL, (self.origin + (randomvec() * 80)), '0 0 0', 1); if(self.vehicle_flags & VHF_DMGSHAKE) self.velocity += randomvec() * 30; @@ -1234,9 +1229,9 @@ bool vehicle_initialize(entity veh, bool nodrop) self.vehicle_flags |= VHF_ISVEHICLE; - self.vehicle_viewport = spawn(); - self.vehicle_hudmodel = spawn(); - self.tur_head = spawn(); + self.vehicle_viewport = new(vehicle_viewport); + self.vehicle_hudmodel = new(vehicle_hudmodel); + self.tur_head = new(tur_head); self.tur_head.owner = self; self.takedamage = DAMAGE_NO; self.bot_attack = true; diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 2eee4dcf5a..244b4e2f61 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -174,13 +174,13 @@ float bumblebee_gunner_frame() if(!forbidWeaponUse(gunner)) if(gunner.BUTTON_ATCK) - if(time > gun.attack_finished_single) + if(time > gun.attack_finished_single[0]) if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost) { gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost; bumblebee_fire_cannon(gun, "fire", gunner); gun.delay = time; - gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire; + gun.attack_finished_single[0] = time + autocvar_g_vehicle_bumblebee_cannon_refire; } VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee); @@ -859,9 +859,9 @@ spawnfunc(vehicle_bumblebee) self.vehicle_shieldent.alpha = -1; self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW; - self.gun1 = spawn(); - self.gun2 = spawn(); - self.gun3 = spawn(); + self.gun1 = new(vehicle_playerslot); + self.gun2 = new(vehicle_playerslot); + self.gun3 = new(bumblebee_raygun); self.vehicle_flags |= VHF_MULTISLOT; @@ -869,8 +869,6 @@ spawnfunc(vehicle_bumblebee) self.gun2.owner = self; self.gun3.owner = self; - self.gun1.classname = self.gun2.classname = "vehicle_playerslot"; - setmodel(self.gun1, MDL_VEH_BUMBLEBEE_CANNON_RIGHT); setmodel(self.gun2, MDL_VEH_BUMBLEBEE_CANNON_LEFT); setmodel(self.gun3, MDL_VEH_BUMBLEBEE_CANNON_CENTER); diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qh b/qcsrc/common/vehicles/vehicle/bumblebee.qh index 7c387e4486..b043038921 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qh @@ -2,7 +2,6 @@ #define BUMBLEBEE_H #ifdef CSQC -void bumble_raygun_read(bool bIsNew); void CSQC_BUMBLE_GUN_HUD(); #endif diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc index 722c6852e7..8ff604579c 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc @@ -7,6 +7,8 @@ #ifdef IMPLEMENTATION +REGISTER_NET_LINKED(ENT_CLIENT_BUMBLE_RAYGUN) + #ifdef SVQC float autocvar_g_vehicle_bumblebee_cannon_cost; @@ -30,7 +32,7 @@ void bumblebee_fire_cannon(entity _gun, string _tagname, entity _owner) bool bumble_raygun_send(entity this, entity to, float sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN); + WriteHeader(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN); WriteByte(MSG_ENTITY, sf); if(sf & BRG_SETUP) @@ -63,8 +65,8 @@ bool bumble_raygun_send(entity this, entity to, float sf) void bumble_raygun_draw(entity this); -void bumble_raygun_read(bool bIsNew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew) +{ int sf = ReadByte(); if(sf & BRG_SETUP) @@ -99,8 +101,10 @@ void bumble_raygun_read(bool bIsNew) self.move_origin_y = ReadCoord(); self.move_origin_z = ReadCoord(); } + return true; } +.float bumble_raygun_nextdraw; void bumble_raygun_draw(entity this) { float _len; @@ -110,11 +114,11 @@ void bumble_raygun_draw(entity this) _len = vlen(self.origin - self.move_origin); _dir = normalize(self.move_origin - self.origin); - if(self.total_damages < time) + if(self.bumble_raygun_nextdraw < time) { - boxparticles(particleeffectnum(Effects[self.traileffect]), self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA); + boxparticles(particleeffectnum(Effects_from(self.traileffect)), self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA); boxparticles(self.lip, self, self.move_origin, self.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA); - self.total_damages = time + 0.1; + self.bumble_raygun_nextdraw = time + 0.1; } float i, df, sz, al; diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 8c45b4aa9d..38e343f65e 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -153,11 +153,11 @@ void racer_align4point(float _delta) self.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); } -void racer_fire_rocket_aim(string tagname, entity trg) +void racer_fire_rocket_aim(entity player, string tagname, entity trg) { - SELFPARAM(); - vector v = gettaginfo(self, gettagindex(self, tagname)); - racer_fire_rocket(v, v_forward, trg); + entity racer = player.vehicle; + vector v = gettaginfo(racer, gettagindex(racer, tagname)); + racer_fire_rocket(player, v, v_forward, trg); } float racer_frame() @@ -257,7 +257,7 @@ float racer_frame() { #ifdef SVQC if(time - racer.wait > 0.2) - pointparticles(particleeffectnum(EFFECT_RACER_BOOSTER), self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1); + pointparticles(EFFECT_RACER_BOOSTER, self.origin - v_forward * 32, v_forward * vlen(self.velocity), 1); #endif racer.wait = time; @@ -278,7 +278,7 @@ float racer_frame() { traceline(racer.origin, racer.origin - '0 0 256', MOVE_NORMAL, self); if(trace_fraction != 1.0) - pointparticles(particleeffectnum(EFFECT_SMOKE_SMALL), trace_endpos, '0 0 0', 1); + pointparticles(EFFECT_SMOKE_SMALL, trace_endpos, '0 0 0', 1); racer.invincible_finished = time + 0.1 + (random() * 0.1); } @@ -321,7 +321,8 @@ float racer_frame() // Fix z-aim (for chase mode) crosshair_trace(player); w_shotdir.z = normalize(trace_endpos - org).z * 0.5; - wep1.wr_think(wep1, self, true, false); + .entity weaponentity = weaponentities[0]; + wep1.wr_think(wep1, self, weaponentity, 1); } if(autocvar_g_vehicle_racer_rocket_locktarget) @@ -350,12 +351,12 @@ float racer_frame() if(racer.misc_bulletcounter == 1) { - racer_fire_rocket_aim("tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + racer_fire_rocket_aim(player, "tag_rocket_r", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); player.vehicle_ammo2 = 50; } else if(racer.misc_bulletcounter == 2) { - racer_fire_rocket_aim("tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); + racer_fire_rocket_aim(player, "tag_rocket_l", (racer.lock_strength == 1 && racer.lock_target) ? racer.lock_target : world); racer.lock_strength = 0; racer.lock_target = world; racer.misc_bulletcounter = 0; diff --git a/qcsrc/common/vehicles/vehicle/racer_weapon.qc b/qcsrc/common/vehicles/vehicle/racer_weapon.qc index 6a2339f1e3..21f4313c63 100644 --- a/qcsrc/common/vehicles/vehicle/racer_weapon.qc +++ b/qcsrc/common/vehicles/vehicle/racer_weapon.qc @@ -7,14 +7,14 @@ CLASS(RacerAttack, PortoLaunch) /* flags */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(RacerAttack, impulse, int, 3); /* refname */ ATTRIB(RacerAttack, netname, string, "racercannon"); -/* wepname */ ATTRIB(RacerAttack, message, string, _("Racer cannon")); +/* wepname */ ATTRIB(RacerAttack, m_name, string, _("Racer cannon")); ENDCLASS(RacerAttack) REGISTER_WEAPON(RACER, NEW(RacerAttack)); // TODO: move into implementation #ifdef SVQC float autocvar_g_vehicle_racer_rocket_refire; -void racer_fire_rocket(vector org, vector dir, entity trg); +void racer_fire_rocket(entity player, vector org, vector dir, entity trg); #endif #endif @@ -41,14 +41,14 @@ float autocvar_g_vehicle_racer_rocket_turnrate; float autocvar_g_vehicle_racer_rocket_climbspeed; float autocvar_g_vehicle_racer_rocket_locked_maxangle; -void racer_fire_rocket(vector org, vector dir, entity trg); -METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) +void racer_fire_rocket(entity player, vector org, vector dir, entity trg); +METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; - if (fire1) - if (weapon_prepareattack(thiswep, player, false, autocvar_g_vehicle_racer_cannon_refire)) { + if (fire & 1) + if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) { if (veh) { veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost; veh.wait = time; @@ -61,13 +61,13 @@ METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, bool fire1, boo autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0, DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player); bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed; - weapon_thinkf(player, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready); } - if (fire2) - if (!isPlayer || weapon_prepareattack(thiswep, actor, false, 0.2)) { + if (fire & 2) + if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { if (isPlayer) W_SetupShot_Dir(actor, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0); - racer_fire_rocket(w_shotorg, w_shotdir, NULL); - weapon_thinkf(actor, WFRAME_FIRE2, 0, w_ready); + racer_fire_rocket(player, w_shotorg, w_shotdir, NULL); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready); } } @@ -83,12 +83,12 @@ METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep)) void racer_rocket_tracker(); void racer_rocket_groundhugger(); -void racer_fire_rocket(vector org, vector dir, entity trg) +void racer_fire_rocket(entity player, vector org, vector dir, entity trg) {SELFPARAM(); entity rocket = vehicles_projectile(EFFECT_RACER_ROCKETLAUNCH.eent_eff_name, SND(ROCKET_FIRE), org, dir * autocvar_g_vehicle_racer_rocket_speed, autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3, - DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, self.owner); + DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player); rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime; rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate; diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index 58a189a850..866a69eb2e 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -392,7 +392,8 @@ float raptor_frame() if(player.BUTTON_ATCK) if (wep1.wr_checkammo1(wep1)) { - wep1.wr_think(wep1, self, true, false); + .entity weaponentity = weaponentities[0]; + wep1.wr_think(wep1, self, weaponentity, 1); } if(self.vehicle_flags & VHF_SHIELDREGEN) @@ -411,7 +412,8 @@ float raptor_frame() if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire) if(player.BUTTON_ATCK2) { - wep2a.wr_think(wep2a, self, false, true); + .entity weaponentity = weaponentities[1]; + wep2a.wr_think(wep2a, self, weaponentity, 2); raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire; raptor.lip = time; } @@ -422,7 +424,8 @@ float raptor_frame() if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire) if(player.BUTTON_ATCK2) { - wep2b.wr_think(wep2b, self, false, true); + .entity weaponentity = weaponentities[1]; + wep2b.wr_think(wep2b, self, weaponentity, 2); raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire; raptor.lip = time; } @@ -671,10 +674,10 @@ spawnfunc(vehicle_raptor) self.frame = 0; - self.bomb1 = spawn(); - self.bomb2 = spawn(); - self.gun1 = spawn(); - self.gun2 = spawn(); + self.bomb1 = new(raptor_bomb); + self.bomb2 = new(raptor_bomb); + self.gun1 = new(raptor_gun); + self.gun2 = new(raptor_gun); setmodel(self.bomb1, MDL_VEH_RAPTOR_CB_FOLDED); setmodel(self.bomb2, MDL_VEH_RAPTOR_CB_FOLDED); @@ -705,7 +708,7 @@ spawnfunc(vehicle_raptor) self.angles = self.bomb1.angles; self.bomb1.angles = '0 0 0'; - spinner = spawn(); + spinner = new(raptor_spinner); spinner.owner = self; setmodel(spinner, MDL_VEH_RAPTOR_PROP); setattachment(spinner, self, "engine_left"); @@ -713,7 +716,7 @@ spawnfunc(vehicle_raptor) spinner.avelocity = '0 90 0'; self.bomb1.gun1 = spinner; - spinner = spawn(); + spinner = new(raptor_spinner); spinner.owner = self; setmodel(spinner, MDL_VEH_RAPTOR_PROP); setattachment(spinner, self, "engine_right"); diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc index 69d981beb3..6783a92cc9 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc @@ -7,7 +7,7 @@ CLASS(RaptorCannon, PortoLaunch) /* flags */ ATTRIB(RaptorCannon, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(RaptorCannon, impulse, int, 3); /* refname */ ATTRIB(RaptorCannon, netname, string, "raptorcannon"); -/* wepname */ ATTRIB(RaptorCannon, message, string, _("Raptor cannon")); +/* wepname */ ATTRIB(RaptorCannon, m_name, string, _("Raptor cannon")); ENDCLASS(RaptorCannon) REGISTER_WEAPON(RAPTOR, NEW(RaptorCannon)); @@ -15,7 +15,7 @@ CLASS(RaptorBomb, PortoLaunch) /* flags */ ATTRIB(RaptorBomb, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(RaptorBomb, impulse, int, 3); /* refname */ ATTRIB(RaptorBomb, netname, string, "raptorbomb"); -/* wepname */ ATTRIB(RaptorBomb, message, string, _("Raptor bomb")); +/* wepname */ ATTRIB(RaptorBomb, m_name, string, _("Raptor bomb")); ENDCLASS(RaptorBomb) REGISTER_WEAPON(RAPTOR_BOMB, NEW(RaptorBomb)); @@ -23,7 +23,7 @@ CLASS(RaptorFlare, PortoLaunch) /* flags */ ATTRIB(RaptorFlare, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* impulse */ ATTRIB(RaptorFlare, impulse, int, 3); /* refname */ ATTRIB(RaptorFlare, netname, string, "raptorflare"); -/* wepname */ ATTRIB(RaptorFlare, message, string, _("Raptor flare")); +/* wepname */ ATTRIB(RaptorFlare, m_name, string, _("Raptor flare")); ENDCLASS(RaptorFlare) REGISTER_WEAPON(RAPTOR_FLARE, NEW(RaptorFlare)); @@ -51,14 +51,14 @@ float autocvar_g_vehicle_raptor_bomblet_radius; float autocvar_g_vehicle_raptor_bomblet_force; float autocvar_g_vehicle_raptor_bomblet_explode_delay; -METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; // 1 [wait] 1 [wait] 2 [wait] 2 [wait] [wait] float t = autocvar_g_vehicle_raptor_cannon_refire * (1 + veh.misc_bulletcounter == 4); - if (fire1) - if (weapon_prepareattack(thiswep, player, false, t)) { + if (fire & 1) + if (weapon_prepareattack(thiswep, player, weaponentity, false, t)) { if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0); vector org = w_shotorg; vector dir = w_shotdir; @@ -74,7 +74,7 @@ METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, bool fire1, bo org, normalize(dir + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed, autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force, 0, DEATH_VH_RAPT_CANNON.m_id, PROJECTILE_RAPTORCANNON, 0, true, true, player); - weapon_thinkf(player, WFRAME_FIRE1, 0, w_ready); + weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready); } } METHOD(RaptorCannon, wr_checkammo1, bool(RacerAttack thiswep)) { @@ -88,15 +88,15 @@ METHOD(RaptorCannon, wr_checkammo1, bool(RacerAttack thiswep)) { float autocvar_g_vehicle_raptor_bombs_refire; void raptor_bombdrop(); -METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; - if (fire2) - if (!isPlayer || weapon_prepareattack(thiswep, player, true, autocvar_g_vehicle_raptor_bombs_refire)) { + if (fire & 2) + if (!isPlayer || weapon_prepareattack(thiswep, player, weaponentity, true, autocvar_g_vehicle_raptor_bombs_refire)) { if (veh) setself(veh); raptor_bombdrop(); - weapon_thinkf(player, WFRAME_FIRE2, 0, w_ready); + weapon_thinkf(player, weaponentity, WFRAME_FIRE2, 0, w_ready); } } @@ -109,12 +109,12 @@ void raptor_flare_think(); void raptor_flare_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); void raptor_flare_touch(); -METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) { +METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; - if (fire2) - if (!isPlayer || weapon_prepareattack(thiswep, player, true, autocvar_g_vehicle_raptor_flare_refire)) { + if (fire & 2) + if (!isPlayer || weapon_prepareattack(thiswep, player, weaponentity, true, autocvar_g_vehicle_raptor_flare_refire)) { for(int i = 0; i < 3; ++i) { entity _flare = spawn(); setmodel(_flare, MDL_VEH_RAPTOR_FLARE); @@ -134,7 +134,7 @@ METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, bool fire1, boo _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime; _flare.touch = raptor_flare_touch; } - weapon_thinkf(player, WFRAME_FIRE2, 0, w_ready); + weapon_thinkf(player, weaponentity, WFRAME_FIRE2, 0, w_ready); } } diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index 019f5889e0..8cb2487811 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -276,7 +276,7 @@ float spiderbot_frame() if(player.BUTTON_ATCK) { spider.cnt = time; - if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single <= time) + if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single[0] <= time) { entity gun; vector v; @@ -295,12 +295,12 @@ float spiderbot_frame() sound (gun, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM); //trailparticles(self, _particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos); - pointparticles(particleeffectnum(EFFECT_SPIDERBOT_MINIGUN_MUZZLEFLASH), v, v_forward * 2500, 1); + pointparticles(EFFECT_SPIDERBOT_MINIGUN_MUZZLEFLASH, v, v_forward * 2500, 1); setself(spider); spider.vehicle_ammo1 -= autocvar_g_vehicle_spiderbot_minigun_ammo_cost; - spider.tur_head.attack_finished_single = time + autocvar_g_vehicle_spiderbot_minigun_refire; + spider.tur_head.attack_finished_single[0] = time + autocvar_g_vehicle_spiderbot_minigun_refire; player.vehicle_ammo1 = (spider.vehicle_ammo1 / autocvar_g_vehicle_spiderbot_minigun_ammo_max) * 100; spider.gun1.angles_z += 45; spider.gun2.angles_z -= 45; @@ -332,7 +332,7 @@ float spiderbot_frame() if(spider.gun2.cnt <= time) player.vehicle_reload2 = 100; else - player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single) * 100; + player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single[0]) * 100; setorigin(player, spider.origin + '0 0 1' * spider.maxs_z); player.velocity = spider.velocity; diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc index 421a15a910..bccd8b0ae2 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc @@ -271,11 +271,11 @@ void spiderbot_rocket_do() self.tur_head.frame += 1; if (self.tur_head.frame == 9) - self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_reload; + self.attack_finished_single[0] = autocvar_g_vehicle_spiderbot_rocket_reload; else - self.attack_finished_single = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire); + self.attack_finished_single[0] = ((self.vehicle_weapon2mode == SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire); - self.gun2.cnt = time + self.attack_finished_single; + self.gun2.cnt = time + self.attack_finished_single[0]; } #endif diff --git a/qcsrc/common/weapons/all.inc b/qcsrc/common/weapons/all.inc index fc89a0cd20..99c7225ac1 100644 --- a/qcsrc/common/weapons/all.inc +++ b/qcsrc/common/weapons/all.inc @@ -25,5 +25,3 @@ #include "weapon/seeker.qc" #include "weapon/shockwave.qc" #include "weapon/arc.qc" -#include "weapon/hmg.qc" -#include "weapon/rpc.qc" diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index ffbc0712da..452438add9 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -11,7 +11,6 @@ #include "../../lib/warpzone/common.qh" #include "../../lib/warpzone/client.qh" #include "../util.qh" - #include "../buffs/all.qh" #include "../../client/autocvars.qh" #include "../deathtypes/all.qh" #include "../../lib/csqcmodel/interpolate.qh" @@ -28,7 +27,6 @@ #include "../stats.qh" #include "../teams.qh" #include "../util.qh" - #include "../buffs/all.qh" #include "../monsters/all.qh" #include "config.qh" #include "../../server/weapons/csqcprojectile.qh" @@ -291,16 +289,7 @@ int GetAmmoStat(.int ammotype) string W_Sound(string w_snd) { - #define extensions(X) X(wav) X(ogg) - #define tryext(ext) { if (fexists(strcat("sound/", output = strcat("weapons/", w_snd, "."#ext)))) break; } - string output; - do { - extensions(tryext); - #undef tryext - #undef extensions - output = strcat("weapons/", w_snd); - } while (0); - + string output = strcat("weapons/", w_snd); #ifdef SVQC MUTATOR_CALLHOOK(WeaponSound, w_snd, output); return weapon_sound_output; diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh index e3f6c7e7cc..93df313c05 100644 --- a/qcsrc/common/weapons/all.qh +++ b/qcsrc/common/weapons/all.qh @@ -2,6 +2,7 @@ #define WEAPONS_ALL_H #include "../command/all.qh" +#include "../stats.qh" #include "config.qh" // weapon sets @@ -34,8 +35,10 @@ WepSet ReadWepSet(); #endif REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72. -REGISTER_REGISTRY(RegisterWeapons) -entity get_weaponinfo(int id); +#define Weapons_from(i) _Weapons_from(i, WEP_Null) +#define get_weaponinfo(i) Weapons_from(i) +REGISTER_REGISTRY(Weapons) +STATIC_INIT(WeaponPickup) { FOREACH(Weapons, true, LAMBDA(it.m_pickup = NEW(WeaponPickup, it))); } GENERIC_COMMAND(dumpweapons, "Dump all weapons into weapons_dump.txt") // WEAPONTODO: make this work with other progs than just server @@ -93,7 +96,7 @@ GENERIC_COMMAND(dumpweapons, "Dump all weapons into weapons_dump.txt") // WEAPON #define REGISTER_WEAPON(id, inst) \ /* WepSet WEPSET_##id; */ \ - REGISTER(RegisterWeapons, WEP, Weapons, id, m_id, inst) + REGISTER(Weapons, WEP, id, m_id, inst) // create cvars for weapon settings #define WEP_ADD_CVAR_NONE(wepname,name) [[last]] float autocvar_g_balance_##wepname##_##name; @@ -130,23 +133,15 @@ REGISTER_WEAPON(Null, NEW(Weapon)); #include "all.inc" -entity get_weaponinfo(int id) -{ - if (id >= WEP_FIRST && id <= WEP_LAST) { - Weapon w = Weapons[id]; - if (w) return w; - } - return WEP_Null; -} - // TODO: remove after 0.8.2. Retains impulse number compatibility because 0.8.1 clients don't reload the weapons.cfg -#define WEP_HARDCODED_IMPULSES 22 +#define WEP_HARDCODED_IMPULSES 20 // TODO: invert after 0.8.2. Will require moving 'best weapon' impulses #define WEP_IMPULSE_BEGIN 230 #define WEP_IMPULSE_END bound(WEP_IMPULSE_BEGIN, WEP_IMPULSE_BEGIN + (Weapons_COUNT - 1) - 1, 253) -REGISTRY_SORT(Weapons, netname, WEP_HARDCODED_IMPULSES + 1) +REGISTRY_SORT(Weapons, WEP_HARDCODED_IMPULSES + 1) +REGISTRY_CHECK(Weapons) STATIC_INIT(register_weapons_done) { @@ -168,7 +163,7 @@ STATIC_INIT(register_weapons_done) #endif weaponorder_byid = ""; for (int i = Weapons_MAX - 1; i >= 1; --i) - if (Weapons[i]) + if (Weapons_from(i)) weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i)); weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1)); } diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc index 7d1b4e3d26..3ee4a684a2 100644 --- a/qcsrc/common/weapons/config.qc +++ b/qcsrc/common/weapons/config.qc @@ -22,7 +22,7 @@ float W_Config_Queue_Compare(int root, int child, entity pass) return strcmp(wep_config_queue[root], wep_config_queue[child]); } -void Dump_Weapon_Settings(void) +void Dump_Weapon_Settings() { int i, x, totalsettings = 0; for(i = WEP_FIRST; i <= WEP_LAST; ++i) diff --git a/qcsrc/common/weapons/config.qh b/qcsrc/common/weapons/config.qh index c50c1e5440..b232bbcda3 100644 --- a/qcsrc/common/weapons/config.qh +++ b/qcsrc/common/weapons/config.qh @@ -6,7 +6,7 @@ // Balance Config Generator // ========================== -void Dump_Weapon_Settings(void); +void Dump_Weapon_Settings(); int wep_config_file; bool wep_config_alsoprint; diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index a4eb7b71d0..3a878a461d 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -1,5 +1,21 @@ #ifndef WEAPON_H #define WEAPON_H +#include "../items/item/pickup.qh" + +const int MAX_WEAPONSLOTS = 2; +.entity weaponentities[MAX_WEAPONSLOTS]; + +int weaponslot(.entity weaponentity) +{ + for (int i = 0; i < MAX_WEAPONSLOTS; ++i) + { + if (weaponentities[i] == weaponentity) + { + return i; + } + } + return 0; +} .int ammo_shells; .int ammo_nails; @@ -42,12 +58,14 @@ CLASS(Weapon, Object) /** M: refname : reference name name */ ATTRIB(Weapon, netname, string, ""); /** M: wepname : human readable name */ - ATTRIB(Weapon, message, string, "AOL CD Thrower"); + ATTRIB(Weapon, m_name, string, "AOL CD Thrower"); + + ATTRIB(Weapon, m_pickup, entity, NULL); /** (SERVER) setup weapon data */ METHOD(Weapon, wr_setup, void(Weapon this)) {} /** (SERVER) logic to run every frame */ - METHOD(Weapon, wr_think, void(Weapon this, entity actor, bool fire1, bool fire2)) {} + METHOD(Weapon, wr_think, void(Weapon this, entity actor, .entity weaponentity, int fire)) {} /** (SERVER) checks ammo for weapon primary */ METHOD(Weapon, wr_checkammo1, bool(Weapon this)) {return false;} /** (SERVER) checks ammo for weapon second */ @@ -83,10 +101,45 @@ CLASS(Weapon, Object) METHOD(Weapon, wr_pickup, void(Weapon this)) {} METHOD(Weapon, display, void(entity this, void(string name, string icon) returns)) { - returns(this.message, this.model2 ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.model2) : string_null); + returns(this.m_name, this.model2 ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.model2) : string_null); } ENDCLASS(Weapon) +#include "../items/all.qh" +CLASS(WeaponPickup, Pickup) + ATTRIB(WeaponPickup, m_weapon, Weapon, NULL) + ATTRIB(WeaponPickup, m_name, string, string_null) +#ifndef MENUQC + ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP) +#endif +#ifdef SVQC + ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON) + float weapon_pickupevalfunc(entity player, entity item); + ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc) +#endif + CONSTRUCTOR(WeaponPickup, Weapon w) { + CONSTRUCT(WeaponPickup); + this.m_weapon = w; + this.m_name = w.m_name; +#ifndef MENUQC + this.m_model = w.m_model; +#endif +#ifdef SVQC + this.m_botvalue = w.bot_pickupbasevalue; +#endif + } +#ifdef SVQC + METHOD(WeaponPickup, giveTo, bool(entity this, entity item, entity player)) + { + bool b = Item_GiveTo(item, player); + if (b) { + LOG_TRACEF("entity %i picked up %s\n", player, this.m_name); + } + return b; + } +#endif +ENDCLASS(WeaponPickup) + CLASS(OffhandWeapon, Object) METHOD(OffhandWeapon, offhand_think, void(OffhandWeapon this, entity player, bool key_pressed)) {} ENDCLASS(OffhandWeapon) @@ -140,6 +193,6 @@ string W_Model(string w_mdl); // other useful macros #define WEP_AMMO(wpn) (WEP_##wpn.ammo_field) // only used inside weapon files/with direct name, don't duplicate prefix -#define WEP_NAME(wpn) ((get_weaponinfo(wpn)).message) +#define WEP_NAME(wpn) ((get_weaponinfo(wpn)).m_name) #endif diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index b05132cb16..b873e62388 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -13,7 +13,7 @@ CLASS(Arc, Weapon) /* crosshair */ ATTRIB(Arc, w_crosshair_size, float, 0.7); /* wepimg */ ATTRIB(Arc, model2, string, "weaponarc"); /* refname */ ATTRIB(Arc, netname, string, "arc"); -/* wepname */ ATTRIB(Arc, message, string, _("Arc")); +/* wepname */ ATTRIB(Arc, m_name, string, _("Arc")); ENDCLASS(Arc) REGISTER_WEAPON(ARC, NEW(Arc)); @@ -92,19 +92,18 @@ ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) .float beam_heat; // (beam) amount of heat produced .float arc_overheat; // (dropped arc/player) time during which it's too hot .float arc_cooldown; // (dropped arc/player) cooling speed -.float arc_heat_percent; // (player) arc heat in [0,1] (stat) +.float arc_heat_percent = _STAT(ARC_HEAT); .float arc_smoke_sound; #endif #ifdef CSQC -void Ent_ReadArcBeam(float isnew); .vector beam_color; .float beam_alpha; .float beam_thickness; -.float beam_traileffect; -.float beam_hiteffect; +.entity beam_traileffect; +.entity beam_hiteffect; .float beam_hitlight[4]; // 0: radius, 123: rgb -.float beam_muzzleeffect; +.entity beam_muzzleeffect; .float beam_muzzlelight[4]; // 0: radius, 123: rgb .string beam_image; @@ -128,11 +127,11 @@ vector Draw_ArcBeam_callback_last_bottom; // NOTE: in same coordinate system as #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_arc) { weapon_defaultspawnfunc(WEP_ARC.m_id); } +spawnfunc(weapon_arc) { weapon_defaultspawnfunc(this, WEP_ARC); } bool W_Arc_Beam_Send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM); + WriteHeader(MSG_ENTITY, ENT_CLIENT_ARC_BEAM); // Truncate information when this beam is displayed to the owner client // - The owner client has no use for beam start position or directions, @@ -214,7 +213,7 @@ void Arc_Player_SetHeat(entity player) //dprint("Heat: ",ftos(player.arc_heat_percent*100),"%\n"); } -void W_Arc_Beam_Think(void) +void W_Arc_Beam_Think() {SELFPARAM(); if(self != self.owner.arc_beam) { @@ -267,7 +266,7 @@ void W_Arc_Beam_Think(void) if ( WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max) ) { - Send_Effect_("arc_overheat", + Send_Effect(EFFECT_ARC_OVERHEAT, self.beam_start, self.beam_wantdir, 1 ); sound(self, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM); } @@ -584,8 +583,7 @@ void W_Arc_Beam(float burst) if(time - self.beam_prev > 1) sound(self, CH_WEAPON_A, SND_ARC_FIRE, VOL_BASE, ATTN_NORM); - entity beam = self.arc_beam = spawn(); - beam.classname = "W_Arc_Beam"; + entity beam = self.arc_beam = new(W_Arc_Beam); beam.solid = SOLID_NOT; beam.think = W_Arc_Beam_Think; beam.owner = self; @@ -607,10 +605,10 @@ void Arc_Smoke() if ( self.arc_overheat > time ) { if ( random() < self.arc_heat_percent ) - Send_Effect_("arc_smoke", smoke_origin, '0 0 0', 1 ); + Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 ); if ( self.BUTTON_ATCK || self.BUTTON_ATCK2 ) { - Send_Effect_("arc_overheat_fire", smoke_origin, w_shotdir, 1 ); + Send_Effect(EFFECT_ARC_OVERHEAT_FIRE, smoke_origin, w_shotdir, 1 ); if ( !self.arc_smoke_sound ) { self.arc_smoke_sound = 1; @@ -623,7 +621,7 @@ void Arc_Smoke() { if ( random() < (self.arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) / ( WEP_CVAR(arc, overheat_max)-WEP_CVAR(arc, overheat_min) ) ) - Send_Effect_("arc_smoke", smoke_origin, '0 0 0', 1 ); + Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 ); } if ( self.arc_smoke_sound && ( self.arc_overheat <= time || @@ -656,34 +654,34 @@ void Arc_Smoke() ); } } - METHOD(Arc, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { Arc_Player_SetHeat(actor); Arc_Smoke(); if (time >= actor.arc_overheat) - if (fire1 || fire2 || actor.arc_beam.beam_bursting) + if ((fire & 1) || (fire & 2) || actor.arc_beam.beam_bursting) { if(actor.arc_BUTTON_ATCK_prev) { #if 0 if(actor.animstate_startframe == actor.anim_shoot.x && actor.animstate_numframes == actor.anim_shoot.y) - weapon_thinkf(actor, WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready); else #endif - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); } if((!actor.arc_beam) || wasfreed(actor.arc_beam)) { - if(weapon_prepareattack(thiswep, actor, fire2, 0)) + if(weapon_prepareattack(thiswep, actor, weaponentity, boolean(fire & 2), 0)) { - W_Arc_Beam(fire2); + W_Arc_Beam(boolean(fire & 2)); if(!actor.arc_BUTTON_ATCK_prev) { - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); actor.arc_BUTTON_ATCK_prev = true; } } @@ -695,18 +693,19 @@ void Arc_Smoke() if(actor.arc_BUTTON_ATCK_prev) { sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); - ATTACK_FINISHED(actor) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(); } actor.arc_BUTTON_ATCK_prev = false; #if 0 - if(fire2) - if(weapon_prepareattack(thiswep, actor, true, autocvar_g_balance_arc_secondary_refire)) + if(fire & 2) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_arc_secondary_refire)) { W_Arc_Attack2(); actor.arc_count = autocvar_g_balance_arc_secondary_count; - weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack); actor.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor(); } #endif @@ -839,7 +838,7 @@ void Draw_ArcBeam_callback(vector start, vector hit, vector end) Draw_ArcBeam_callback_last_bottom = WarpZone_UnTransformOrigin(WarpZone_trace_transform, bottom); } -void Reset_ArcBeam(void) +void Reset_ArcBeam() { entity e; for (e = world; (e = findfloat(e, beam_usevieworigin, 1)); ) { @@ -1143,14 +1142,14 @@ void Draw_ArcBeam(entity this) Draw_ArcBeam_callback_last_bottom = '0 0 0'; } -void Remove_ArcBeam(void) +void Remove_ArcBeam() {SELFPARAM(); remove(self.beam_muzzleentity); sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); } -void Ent_ReadArcBeam(float isnew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) +{ int sf = ReadByte(); entity flash; @@ -1258,18 +1257,18 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 8; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1283,19 +1282,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 8; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; // particleeffectnum(EFFECT_GRENADE_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; // (EFFECT_GRENADE_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1309,19 +1308,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 8; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL_IMPACT); + self.beam_traileffect = (EFFECT_ARC_BEAM_HEAL); + self.beam_hiteffect = (EFFECT_ARC_BEAM_HEAL_IMPACT); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1335,19 +1334,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 8; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 20; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 0; self.beam_hitlight[3] = 0; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 50; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 0; self.beam_muzzlelight[3] = 0; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1361,19 +1360,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 14; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1387,19 +1386,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 14; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1413,19 +1412,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 14; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_BEAM_HEAL_IMPACT2); + self.beam_traileffect = (EFFECT_ARC_BEAM_HEAL); + self.beam_hiteffect = (EFFECT_ARC_BEAM_HEAL_IMPACT2); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1439,19 +1438,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = '1 1 1'; self.beam_alpha = 0.5; self.beam_thickness = 14; - self.beam_traileffect = particleeffectnum(EFFECT_ARC_BEAM); - self.beam_hiteffect = particleeffectnum(EFFECT_ARC_LIGHTNING); + self.beam_traileffect = (EFFECT_ARC_BEAM); + self.beam_hiteffect = (EFFECT_ARC_LIGHTNING); self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1467,19 +1466,19 @@ void Ent_ReadArcBeam(float isnew) self.beam_color = randomvec(); self.beam_alpha = 1; self.beam_thickness = 8; - self.beam_traileffect = false; - self.beam_hiteffect = false; + self.beam_traileffect = NULL; + self.beam_hiteffect = NULL; self.beam_hitlight[0] = 0; self.beam_hitlight[1] = 1; self.beam_hitlight[2] = 1; self.beam_hitlight[3] = 1; - self.beam_muzzleeffect = -1; //particleeffectnum(EFFECT_VORTEX_MUZZLEFLASH); + self.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); self.beam_muzzlelight[0] = 0; self.beam_muzzlelight[1] = 1; self.beam_muzzlelight[2] = 1; self.beam_muzzlelight[3] = 1; self.beam_image = "particles/lgbeam"; - if(self.beam_muzzleeffect >= 0) + if(self.beam_muzzleeffect) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = self.beam_alpha; @@ -1495,6 +1494,7 @@ void Ent_ReadArcBeam(float isnew) { InterpolateOrigin_Note(); } + return true; } #endif diff --git a/qcsrc/common/weapons/weapon/blaster.qc b/qcsrc/common/weapons/weapon/blaster.qc index d7792fac4a..055293e930 100644 --- a/qcsrc/common/weapons/weapon/blaster.qc +++ b/qcsrc/common/weapons/weapon/blaster.qc @@ -13,7 +13,7 @@ CLASS(Blaster, Weapon) /* crosshair */ ATTRIB(Blaster, w_crosshair_size, float, 0.5); /* wepimg */ ATTRIB(Blaster, model2, string, "weaponlaser"); /* refname */ ATTRIB(Blaster, netname, string, "blaster"); -/* wepname */ ATTRIB(Blaster, message, string, _("Blaster")); +/* wepname */ ATTRIB(Blaster, m_name, string, _("Blaster")); ENDCLASS(Blaster) REGISTER_WEAPON(BLASTER, NEW(Blaster)); @@ -50,10 +50,10 @@ BLASTER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(WEP_BLASTER.m_id); } +spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); } spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); } -void W_Blaster_Touch(void) +void W_Blaster_Touch() {SELFPARAM(); PROJECTILE_TOUCH; @@ -75,7 +75,7 @@ void W_Blaster_Touch(void) remove(self); } -void W_Blaster_Think(void) +void W_Blaster_Think() {SELFPARAM(); self.movetype = MOVETYPE_FLY; self.think = SUB_Remove; @@ -101,9 +101,8 @@ void W_Blaster_Attack( W_SetupShot_Dir(actor, s_forward, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_B, atk_damage); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - entity missile = spawn(); + entity missile = new(blasterbolt); missile.owner = missile.realowner = actor; - missile.classname = "blasterbolt"; missile.bot_dodge = true; missile.bot_dodgerating = atk_damage; PROJECTILE_MAKETRIGGER(missile); @@ -161,11 +160,11 @@ void W_Blaster_Attack( { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), false); } } - METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponentity, int fire)) { - if(fire1) + if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(blaster, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(blaster, refire))) { W_Blaster_Attack( actor, @@ -180,10 +179,10 @@ void W_Blaster_Attack( WEP_CVAR_PRI(blaster, delay), WEP_CVAR_PRI(blaster, lifetime) ); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready); } } - else if(fire2) + else if(fire & 2) { switch(WEP_CVAR(blaster, secondary)) { @@ -196,7 +195,7 @@ void W_Blaster_Attack( case 1: // normal projectile secondary { - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(blaster, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(blaster, refire))) { W_Blaster_Attack( actor, @@ -211,7 +210,7 @@ void W_Blaster_Attack( WEP_CVAR_SEC(blaster, delay), WEP_CVAR_SEC(blaster, lifetime) ); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready); } break; @@ -262,7 +261,7 @@ void W_Blaster_Attack( { vector org2; org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); } } diff --git a/qcsrc/common/weapons/weapon/crylink.qc b/qcsrc/common/weapons/weapon/crylink.qc index 75d8146de4..252cc56dc8 100644 --- a/qcsrc/common/weapons/weapon/crylink.qc +++ b/qcsrc/common/weapons/weapon/crylink.qc @@ -13,7 +13,7 @@ CLASS(Crylink, Weapon) /* crosshair */ ATTRIB(Crylink, w_crosshair_size, float, 0.5); /* wepimg */ ATTRIB(Crylink, model2, string, "weaponcrylink"); /* refname */ ATTRIB(Crylink, netname, string, "crylink"); -/* wepname */ ATTRIB(Crylink, message, string, _("Crylink")); +/* wepname */ ATTRIB(Crylink, m_name, string, _("Crylink")); ENDCLASS(Crylink) REGISTER_WEAPON(CRYLINK, NEW(Crylink)); @@ -66,7 +66,7 @@ CRYLINK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(WEP_CRYLINK.m_id); } +spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(this, WEP_CRYLINK); } void W_Crylink_CheckLinks(entity e) { @@ -108,7 +108,7 @@ void W_Crylink_Dequeue(entity e) W_Crylink_Dequeue_Raw(e.realowner, e.queueprev, e, e.queuenext); } -void W_Crylink_Reset(void) +void W_Crylink_Reset() {SELFPARAM(); W_Crylink_Dequeue(self); remove(self); @@ -225,7 +225,7 @@ vector W_Crylink_LinkJoin(entity e, float jspeed) return targ_origin; } -void W_Crylink_LinkJoinEffect_Think(void) +void W_Crylink_LinkJoinEffect_Think() {SELFPARAM(); // is there at least 2 projectiles very close? entity e, p; @@ -290,7 +290,7 @@ float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) } // NO bounce protection, as bounces are limited! -void W_Crylink_Touch(void) +void W_Crylink_Touch() {SELFPARAM(); float finalhit; float f; @@ -335,7 +335,7 @@ void W_Crylink_Touch(void) // CSQCProjectile(proj, true, PROJECTILE_CRYLINK, true); } -void W_Crylink_Fadethink(void) +void W_Crylink_Fadethink() {SELFPARAM(); W_Crylink_Dequeue(self); remove(self); @@ -366,10 +366,9 @@ void W_Crylink_Attack(Weapon thiswep) proj = prevproj = firstproj = world; for(counter = 0; counter < shots; ++counter) { - proj = spawn(); + proj = new(spike); proj.reset = W_Crylink_Reset; proj.realowner = proj.owner = self; - proj.classname = "spike"; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_PRI(crylink, damage); if(shots == 1) { @@ -475,10 +474,9 @@ void W_Crylink_Attack2(Weapon thiswep) proj = prevproj = firstproj = world; for(counter = 0; counter < shots; ++counter) { - proj = spawn(); + proj = new(spike); proj.reset = W_Crylink_Reset; proj.realowner = proj.owner = self; - proj.classname = "spike"; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage); if(shots == 1) { @@ -574,34 +572,34 @@ void W_Crylink_Attack2(Weapon thiswep) else self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), false); } - METHOD(Crylink, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_crylink_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); } - if(fire1) + if(fire & 1) { if(actor.crylink_waitrelease != 1) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(crylink, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire))) { W_Crylink_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready); } } - if(fire2 && autocvar_g_balance_crylink_secondary) + if((fire & 2) && autocvar_g_balance_crylink_secondary) { if(actor.crylink_waitrelease != 2) - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(crylink, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire))) { W_Crylink_Attack2(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready); } } - if((actor.crylink_waitrelease == 1 && !fire1) || (actor.crylink_waitrelease == 2 && !fire2)) + if((actor.crylink_waitrelease == 1 && !(fire & 1)) || (actor.crylink_waitrelease == 2 && !(fire & 2))) { if(!actor.crylink_lastgroup || time > actor.crylink_lastgroup.teleport_time) { @@ -614,9 +612,8 @@ void W_Crylink_Attack2(Weapon thiswep) pos = W_Crylink_LinkJoin(actor.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed)); - linkjoineffect = spawn(); + linkjoineffect = new(linkjoineffect); linkjoineffect.think = W_Crylink_LinkJoinEffect_Think; - linkjoineffect.classname = "linkjoineffect"; linkjoineffect.nextthink = time + w_crylink_linkjoin_time; linkjoineffect.owner = actor; setorigin(linkjoineffect, pos); @@ -683,13 +680,13 @@ void W_Crylink_Attack2(Weapon thiswep) org2 = w_org + w_backoff * 2; if(w_deathtype & HITTYPE_SECONDARY) { - pointparticles(particleeffectnum(EFFECT_CRYLINK_IMPACT2), org2, '0 0 0', 1); + pointparticles(EFFECT_CRYLINK_IMPACT2, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_CRYLINK_IMPACT2, VOL_BASE, ATTN_NORM); } else { - pointparticles(particleeffectnum(EFFECT_CRYLINK_IMPACT), org2, '0 0 0', 1); + pointparticles(EFFECT_CRYLINK_IMPACT, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_CRYLINK_IMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index b9cc0358bb..d6ffd50d68 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -13,7 +13,7 @@ CLASS(Devastator, Weapon) /* crosshair */ ATTRIB(Devastator, w_crosshair_size, float, 0.7); /* wepimg */ ATTRIB(Devastator, model2, string, "weaponrocketlauncher"); /* refname */ ATTRIB(Devastator, netname, string, "devastator"); -/* wepname */ ATTRIB(Devastator, message, string, _("Devastator")); +/* wepname */ ATTRIB(Devastator, m_name, string, _("Devastator")); ENDCLASS(Devastator) REGISTER_WEAPON(DEVASTATOR, NEW(Devastator)); @@ -64,12 +64,12 @@ DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(WEP_DEVASTATOR.m_id); } +spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(this, WEP_DEVASTATOR); } spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); } .entity lastrocket; -void W_Devastator_Unregister(void) +void W_Devastator_Unregister() {SELFPARAM(); if(self.realowner && self.realowner.lastrocket == self) { @@ -78,7 +78,7 @@ void W_Devastator_Unregister(void) } } -void W_Devastator_Explode(void) +void W_Devastator_Explode() {SELFPARAM(); W_Devastator_Unregister(); @@ -111,14 +111,15 @@ void W_Devastator_Explode(void) if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { self.realowner.cnt = WEP_DEVASTATOR.m_id; - ATTACK_FINISHED(self.realowner) = time; + int slot = 0; // TODO: unhardcode + ATTACK_FINISHED(self.realowner, slot) = time; self.realowner.switchweapon = w_getbestweapon(self.realowner); } } remove(self); } -void W_Devastator_DoRemoteExplode(void) +void W_Devastator_DoRemoteExplode(.entity weaponentity) {SELFPARAM(); W_Devastator_Unregister(); @@ -190,14 +191,15 @@ void W_Devastator_DoRemoteExplode(void) if(!(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)) { self.realowner.cnt = WEP_DEVASTATOR.m_id; - ATTACK_FINISHED(self.realowner) = time; + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(self.realowner, slot) = time; self.realowner.switchweapon = w_getbestweapon(self.realowner); } } remove(self); } -void W_Devastator_RemoteExplode(void) +void W_Devastator_RemoteExplode(.entity weaponentity) {SELFPARAM(); if(self.realowner.deadflag == DEAD_NO) if(self.realowner.lastrocket) @@ -207,7 +209,7 @@ void W_Devastator_RemoteExplode(void) : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(devastator, remote_radius)) // safety device ) { - W_Devastator_DoRemoteExplode(); + W_Devastator_DoRemoteExplode(weaponentity); } } } @@ -245,7 +247,7 @@ vector W_Devastator_SteerTo(vector thisdir, vector goaldir, float maxturn_cos) // normalize(thisdir + goaldir) // normalize(0) -void W_Devastator_Think(void) +void W_Devastator_Think() {SELFPARAM(); vector desireddir, olddir, newdir, desiredorigin, goal; float velspeed, f; @@ -304,15 +306,16 @@ void W_Devastator_Think(void) } } + .entity weaponentity = weaponentities[0]; // TODO: unhardcode if(self.rl_detonate_later) - W_Devastator_RemoteExplode(); + W_Devastator_RemoteExplode(weaponentity); } if(self.csqcprojectile_clientanimate == 0) UpdateCSQCProjectile(self); } -void W_Devastator_Touch(void) +void W_Devastator_Touch() {SELFPARAM(); if(WarpZone_Projectile_Touch()) { @@ -520,26 +523,26 @@ void W_Devastator_Attack(Weapon thiswep) } } #endif - METHOD(Devastator, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); } else { - if(fire1) + if(fire & 1) { if(actor.rl_release || WEP_CVAR(devastator, guidestop)) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(devastator, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) { W_Devastator_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); actor.rl_release = 0; } } else actor.rl_release = 1; - if(fire2) + if(fire & 2) if(actor.switchweapon == WEP_DEVASTATOR.m_id) { entity rock; @@ -569,7 +572,7 @@ void W_Devastator_Attack(Weapon thiswep) { #if 0 // don't switch while guiding a missile - if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR.m_id) + if(ATTACK_FINISHED(self, slot) <= time || self.weapon != WEP_DEVASTATOR.m_id) { ammo_amount = false; if(WEP_CVAR(devastator, reload_ammo)) @@ -637,7 +640,7 @@ void W_Devastator_Attack(Weapon thiswep) { vector org2; org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/electro.qc b/qcsrc/common/weapons/weapon/electro.qc index 7321d38f2a..0f48bb2591 100644 --- a/qcsrc/common/weapons/weapon/electro.qc +++ b/qcsrc/common/weapons/weapon/electro.qc @@ -13,7 +13,7 @@ CLASS(Electro, Weapon) /* crosshair */ ATTRIB(Electro, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(Electro, model2, string, "weaponelectro"); /* refname */ ATTRIB(Electro, netname, string, "electro"); -/* wepname */ ATTRIB(Electro, message, string, _("Electro")); +/* wepname */ ATTRIB(Electro, m_name, string, _("Electro")); ENDCLASS(Electro) REGISTER_WEAPON(ELECTRO, NEW(Electro)); @@ -64,12 +64,12 @@ REGISTER_WEAPON(ELECTRO, NEW(Electro)); ELECTRO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) .float electro_count; .float electro_secondarytime; -void W_Electro_ExplodeCombo(void); +void W_Electro_ExplodeCombo(); #endif #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_electro) { weapon_defaultspawnfunc(WEP_ELECTRO.m_id); } +spawnfunc(weapon_electro) { weapon_defaultspawnfunc(this, WEP_ELECTRO); } void W_Electro_TriggerCombo(vector org, float rad, entity own) { @@ -118,7 +118,7 @@ void W_Electro_TriggerCombo(vector org, float rad, entity own) } } -void W_Electro_ExplodeCombo(void) +void W_Electro_ExplodeCombo() {SELFPARAM(); W_Electro_TriggerCombo(self.origin, WEP_CVAR(electro, combo_comboradius), self.realowner); @@ -140,7 +140,7 @@ void W_Electro_ExplodeCombo(void) remove(self); } -void W_Electro_Explode(void) +void W_Electro_Explode() {SELFPARAM(); if(other.takedamage == DAMAGE_AIM) if(IS_PLAYER(other)) @@ -187,13 +187,13 @@ void W_Electro_Explode(void) remove(self); } -void W_Electro_TouchExplode(void) +void W_Electro_TouchExplode() { PROJECTILE_TOUCH; W_Electro_Explode(); } -void W_Electro_Bolt_Think(void) +void W_Electro_Bolt_Think() {SELFPARAM(); if(time >= self.ltime) { @@ -264,8 +264,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep) Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - proj = spawn(); - proj.classname = "electro_bolt"; + proj = new(electro_bolt); proj.owner = proj.realowner = self; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_PRI(electro, damage); @@ -290,7 +289,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep) MUTATOR_CALLHOOK(EditProjectile, self, proj); } -void W_Electro_Orb_Touch(void) +void W_Electro_Orb_Touch() {SELFPARAM(); PROJECTILE_TOUCH; if(other.takedamage == DAMAGE_AIM) @@ -364,8 +363,7 @@ void W_Electro_Attack_Orb(Weapon thiswep) Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - entity proj = spawn(); - proj.classname = "electro_orb"; + entity proj = new(electro_orb); proj.owner = proj.realowner = self; proj.use = W_Electro_Explode; proj.think = adaptor_think2use_hittype_splash; @@ -406,19 +404,19 @@ void W_Electro_Attack_Orb(Weapon thiswep) MUTATOR_CALLHOOK(EditProjectile, self, proj); } -void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, int fire) {SELFPARAM(); if(self.electro_count > 1) if(self.BUTTON_ATCK2) - if(weapon_prepareattack(thiswep, actor, true, -1)) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1)) { W_Electro_Attack_Orb(WEP_ELECTRO); self.electro_count -= 1; - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); return; } // WEAPONTODO: when the player releases the button, cut down the length of refire2? - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); } .float bot_secondary_electromooth; @@ -451,7 +449,7 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2) } } } - METHOD(Electro, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO { @@ -469,22 +467,22 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2) } } - if(fire1) + if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(electro, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { W_Electro_Attack_Bolt(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } } - else if(fire2) + else if(fire & 2) { if(time >= actor.electro_secondarytime) - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(electro, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(electro, refire))) { W_Electro_Attack_Orb(thiswep); actor.electro_count = WEP_CVAR_SEC(electro, count); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); actor.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor(); } } @@ -557,7 +555,7 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2) org2 = w_org + w_backoff * 6; if(w_deathtype & HITTYPE_SECONDARY) { - pointparticles(particleeffectnum(EFFECT_ELECTRO_BALLEXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_ELECTRO_BALLEXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM); } @@ -566,13 +564,13 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, bool fire1, bool fire2) if(w_deathtype & HITTYPE_BOUNCE) { // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls - pointparticles(particleeffectnum(EFFECT_ELECTRO_COMBO), org2, '0 0 0', 1); + pointparticles(EFFECT_ELECTRO_COMBO, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_ELECTRO_IMPACT_COMBO, VOL_BASE, ATTEN_NORM); } else { - pointparticles(particleeffectnum(EFFECT_ELECTRO_IMPACT), org2, '0 0 0', 1); + pointparticles(EFFECT_ELECTRO_IMPACT, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM); } diff --git a/qcsrc/common/weapons/weapon/fireball.qc b/qcsrc/common/weapons/weapon/fireball.qc index b0761b2c2a..7b96971e92 100644 --- a/qcsrc/common/weapons/weapon/fireball.qc +++ b/qcsrc/common/weapons/weapon/fireball.qc @@ -13,7 +13,7 @@ CLASS(Fireball, Weapon) /* crosshair */ //ATTRIB(Fireball, w_crosshair_size, float, 0.65); /* wepimg */ ATTRIB(Fireball, model2, string, "weaponfireball"); /* refname */ ATTRIB(Fireball, netname, string, "fireball"); -/* wepname */ ATTRIB(Fireball, message, string, _("Fireball")); +/* wepname */ ATTRIB(Fireball, m_name, string, _("Fireball")); ENDCLASS(Fireball) REGISTER_WEAPON(FIREBALL, NEW(Fireball)); @@ -57,9 +57,9 @@ FIREBALL_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(WEP_FIREBALL.m_id); } +spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); } -void W_Fireball_Explode(void) +void W_Fireball_Explode() {SELFPARAM(); entity e; float dist; @@ -109,7 +109,7 @@ void W_Fireball_Explode(void) remove(self); } -void W_Fireball_TouchExplode(void) +void W_Fireball_TouchExplode() { PROJECTILE_TOUCH; W_Fireball_Explode(); @@ -149,7 +149,7 @@ void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, } } -void W_Fireball_Think(void) +void W_Fireball_Think() {SELFPARAM(); if(time > self.pushltime) { @@ -180,7 +180,7 @@ void W_Fireball_Damage(entity inflictor, entity attacker, float damage, int deat } } -void W_Fireball_Attack1(void) +void W_Fireball_Attack1() {SELFPARAM(); entity proj; @@ -188,8 +188,7 @@ void W_Fireball_Attack1(void) Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - proj = spawn(); - proj.classname = "plasma_prim"; + proj = new(plasma_prim); proj.owner = proj.realowner = self; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage); @@ -226,38 +225,38 @@ void W_Fireball_AttackEffect(float i, vector f_diff) Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); } -void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_Fireball_Attack1(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready); } -void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_Fireball_AttackEffect(0, '+1.25 +3.75 0'); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4); } -void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_Fireball_AttackEffect(0, '-1.25 +3.75 0'); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3); } -void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_Fireball_AttackEffect(1, '+1.25 -3.75 0'); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2); } -void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, .entity weaponentity, int fire) {SELFPARAM(); W_Fireball_AttackEffect(0, '-1.25 -3.75 0'); sound(self, CH_WEAPON_SINGLE, SND_FIREBALL_PREFIRE2, VOL_BASE, ATTEN_NORM); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1); } -void W_Fireball_Firemine_Think(void) +void W_Fireball_Firemine_Think() {SELFPARAM(); if(time > self.pushltime) { @@ -283,7 +282,7 @@ void W_Fireball_Firemine_Think(void) self.nextthink = time + 0.1; } -void W_Fireball_Firemine_Touch(void) +void W_Fireball_Firemine_Touch() {SELFPARAM(); PROJECTILE_TOUCH; if(other.takedamage == DAMAGE_AIM) @@ -295,7 +294,7 @@ void W_Fireball_Firemine_Touch(void) self.projectiledeathtype |= HITTYPE_BOUNCE; } -void W_Fireball_Attack2(void) +void W_Fireball_Attack2() {SELFPARAM(); entity proj; vector f_diff; @@ -324,9 +323,8 @@ void W_Fireball_Attack2(void) Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - proj = spawn(); + proj = new(grenade); proj.owner = proj.realowner = self; - proj.classname = "grenade"; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage); proj.movetype = MOVETYPE_BOUNCE; @@ -371,23 +369,23 @@ void W_Fireball_Attack2(void) } } } - METHOD(Fireball, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Fireball, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - if(fire1) + if(fire & 1) { if(time >= actor.fireball_primarytime) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(fireball, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(fireball, refire))) { - W_Fireball_Attack1_Frame0(thiswep, actor, fire1, fire2); + W_Fireball_Attack1_Frame0(thiswep, actor, weaponentity, fire); actor.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(); } } - else if(fire2) + else if(fire & 2) { - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(fireball, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(fireball, refire))) { W_Fireball_Attack2(); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready); } } } @@ -443,7 +441,7 @@ void W_Fireball_Attack2(void) else { org2 = w_org + w_backoff * 16; - pointparticles(particleeffectnum(EFFECT_FIREBALL_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_FIREBALL_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_FIREBALL_IMPACT2, VOL_BASE, ATTEN_NORM * 0.25); // long range boom } diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index 37f6875b55..859b54c959 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -13,7 +13,7 @@ CLASS(Hagar, Weapon) /* crosshair */ ATTRIB(Hagar, w_crosshair_size, float, 0.8); /* wepimg */ ATTRIB(Hagar, model2, string, "weaponhagar"); /* refname */ ATTRIB(Hagar, netname, string, "hagar"); -/* wepname */ ATTRIB(Hagar, message, string, _("Hagar")); +/* wepname */ ATTRIB(Hagar, m_name, string, _("Hagar")); ENDCLASS(Hagar) REGISTER_WEAPON(HAGAR, NEW(Hagar)); @@ -58,11 +58,11 @@ HAGAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(WEP_HAGAR.m_id); } +spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); } // NO bounce protection, as bounces are limited! -void W_Hagar_Explode(void) +void W_Hagar_Explode() {SELFPARAM(); self.event_damage = func_null; RadiusDamage(self, self.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), world, world, WEP_CVAR_PRI(hagar, force), self.projectiledeathtype, other); @@ -70,7 +70,7 @@ void W_Hagar_Explode(void) remove(self); } -void W_Hagar_Explode2(void) +void W_Hagar_Explode2() {SELFPARAM(); self.event_damage = func_null; RadiusDamage(self, self.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), world, world, WEP_CVAR_SEC(hagar, force), self.projectiledeathtype, other); @@ -102,13 +102,13 @@ void W_Hagar_Damage(entity inflictor, entity attacker, float damage, int deathty W_PrepareExplosionByDamage(attacker, self.think); } -void W_Hagar_Touch(void) +void W_Hagar_Touch() {SELFPARAM(); PROJECTILE_TOUCH; self.use(); } -void W_Hagar_Touch2(void) +void W_Hagar_Touch2() {SELFPARAM(); PROJECTILE_TOUCH; @@ -133,9 +133,8 @@ void W_Hagar_Attack(Weapon thiswep) Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - missile = spawn(); + missile = new(missile); missile.owner = missile.realowner = self; - missile.classname = "missile"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage); @@ -176,9 +175,8 @@ void W_Hagar_Attack2(Weapon thiswep) Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - missile = spawn(); + missile = new(missile); missile.owner = missile.realowner = self; - missile.classname = "missile"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); @@ -211,7 +209,7 @@ void W_Hagar_Attack2(Weapon thiswep) } .float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning; -void W_Hagar_Attack2_Load_Release(void) +void W_Hagar_Attack2_Load_Release(.entity weaponentity) {SELFPARAM(); // time to release the rockets we've loaded @@ -223,7 +221,7 @@ void W_Hagar_Attack2_Load_Release(void) if(!self.hagar_load) return; - weapon_prepareattack_do(self, true, WEP_CVAR_SEC(hagar, refire)); + weapon_prepareattack_do(self, weaponentity, true, WEP_CVAR_SEC(hagar, refire)); W_SetupShot(self, false, 2, SND(HAGAR_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -236,9 +234,8 @@ void W_Hagar_Attack2_Load_Release(void) missile = world; for(counter = 0; counter < shots; ++counter) { - missile = spawn(); + missile = new(missile); missile.owner = missile.realowner = self; - missile.classname = "missile"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); @@ -286,12 +283,12 @@ void W_Hagar_Attack2_Load_Release(void) MUTATOR_CALLHOOK(EditProjectile, self, missile); } - weapon_thinkf(self, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready); + weapon_thinkf(self, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready); self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor(); self.hagar_load = 0; } -void W_Hagar_Attack2_Load(Weapon thiswep) +void W_Hagar_Attack2_Load(Weapon thiswep, .entity weaponentity) {SELFPARAM(); // loadable hagar secondary attack, must always run each frame @@ -318,7 +315,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep) if(self.hagar_load) { // if we pressed primary fire while loading, unload all rockets and abort - self.weaponentity.state = WS_READY; + self.(weaponentity).state = WS_READY; W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo self.hagar_load = 0; sound(self, CH_WEAPON_A, SND_HAGAR_BEEP, VOL_BASE, ATTN_NORM); @@ -338,7 +335,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep) if(!self.hagar_loadblock && self.hagar_loadstep < time) { W_DecreaseAmmo(thiswep, self, WEP_CVAR_SEC(hagar, ammo)); - self.weaponentity.state = WS_INUSE; + self.(weaponentity).state = WS_INUSE; self.hagar_load += 1; sound(self, CH_WEAPON_B, SND_HAGAR_LOAD, VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most @@ -379,8 +376,8 @@ void W_Hagar_Attack2_Load(Weapon thiswep) // release if player let go of button or if they've held it in too long if(!self.BUTTON_ATCK2 || (stopped && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)) { - self.weaponentity.state = WS_READY; - W_Hagar_Attack2_Load_Release(); + self.(weaponentity).state = WS_READY; + W_Hagar_Attack2_Load_Release(weaponentity); } } else @@ -407,30 +404,30 @@ void W_Hagar_Attack2_Load(Weapon thiswep) else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), false); } - METHOD(Hagar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { float loadable_secondary; loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary)); if(loadable_secondary) - W_Hagar_Attack2_Load(thiswep); // must always run each frame + W_Hagar_Attack2_Load(thiswep, weaponentity); // must always run each frame if(autocvar_g_balance_hagar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); - } else if(fire1 && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset + } else if((fire & 1) && !actor.hagar_load && !actor.hagar_loadblock) // not while secondary is loaded or awaiting reset { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(hagar, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hagar, refire))) { W_Hagar_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready); } } - else if(fire2 && !loadable_secondary && WEP_CVAR(hagar, secondary)) + else if((fire & 2) && !loadable_secondary && WEP_CVAR(hagar, secondary)) { - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hagar, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire))) { W_Hagar_Attack2(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready); } } } @@ -439,8 +436,9 @@ void W_Hagar_Attack2_Load(Weapon thiswep) // we lost the weapon and want to prepare switching away if(self.hagar_load) { - self.weaponentity.state = WS_READY; - W_Hagar_Attack2_Load_Release(); + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + self.(weaponentity).state = WS_READY; + W_Hagar_Attack2_Load_Release(weaponentity); } } METHOD(Hagar, wr_init, void(entity thiswep)) @@ -479,9 +477,10 @@ void W_Hagar_Attack2_Load(Weapon thiswep) } METHOD(Hagar, wr_playerdeath, void(entity thiswep)) { + .entity weaponentity = weaponentities[0]; // TODO: unhardcode // if we have any rockets loaded when we die, release them if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath)) - W_Hagar_Attack2_Load_Release(); + W_Hagar_Attack2_Load_Release(weaponentity); } METHOD(Hagar, wr_reload, void(entity thiswep)) { @@ -507,7 +506,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep) { vector org2; org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) { if(w_random<0.15) diff --git a/qcsrc/common/weapons/weapon/hlac.qc b/qcsrc/common/weapons/weapon/hlac.qc index 616939d1e8..9c4d8b2a66 100644 --- a/qcsrc/common/weapons/weapon/hlac.qc +++ b/qcsrc/common/weapons/weapon/hlac.qc @@ -13,7 +13,7 @@ CLASS(HLAC, Weapon) /* crosshair */ ATTRIB(HLAC, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(HLAC, model2, string, "weaponhlac"); /* refname */ ATTRIB(HLAC, netname, string, "hlac"); -/* wepname */ ATTRIB(HLAC, message, string, _("Heavy Laser Assault Cannon")); +/* wepname */ ATTRIB(HLAC, m_name, string, _("Heavy Laser Assault Cannon")); ENDCLASS(HLAC) REGISTER_WEAPON(HLAC, NEW(HLAC)); @@ -50,9 +50,9 @@ HLAC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(WEP_HLAC.m_id); } +spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); } -void W_HLAC_Touch(void) +void W_HLAC_Touch() {SELFPARAM(); float isprimary; @@ -87,9 +87,8 @@ void W_HLAC_Attack(Weapon thiswep) self.punchangle_y = random() - 0.5; } - missile = spawn(); + missile = new(hlacbolt); missile.owner = missile.realowner = self; - missile.classname = "hlacbolt"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage); @@ -116,7 +115,7 @@ void W_HLAC_Attack(Weapon thiswep) MUTATOR_CALLHOOK(EditProjectile, self, missile); } -void W_HLAC_Attack2(void) +void W_HLAC_Attack2() {SELFPARAM(); entity missile; float spread; @@ -130,9 +129,8 @@ void W_HLAC_Attack2(void) W_SetupShot(self, false, 3, SND(LASERGUN_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - missile = spawn(); + missile = new(hlacbolt); missile.owner = missile.realowner = self; - missile.classname = "hlacbolt"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage); @@ -161,11 +159,11 @@ void W_HLAC_Attack2(void) } // weapon frames -void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire) { if(actor.weapon != actor.switchweapon) // abort immediately if switching { - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } @@ -176,18 +174,19 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2) if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } - ATTACK_FINISHED(actor) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(); + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(); W_HLAC_Attack(WEP_HLAC); actor.misc_bulletcounter = actor.misc_bulletcounter + 1; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); } else { - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready); } } @@ -211,27 +210,27 @@ void W_HLAC_Attack2_Frame(Weapon thiswep) { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), false); } - METHOD(HLAC, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_hlac_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); - } else if(fire1) + } else if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(hlac, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hlac, refire))) { actor.misc_bulletcounter = 0; W_HLAC_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); } } - else if(fire2 && WEP_CVAR(hlac, secondary)) + else if((fire & 2) && WEP_CVAR(hlac, secondary)) { - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hlac, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hlac, refire))) { W_HLAC_Attack2_Frame(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready); } } } @@ -275,7 +274,7 @@ void W_HLAC_Attack2_Frame(Weapon thiswep) { vector org2; org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent) sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/hmg.qc b/qcsrc/common/weapons/weapon/hmg.qc deleted file mode 100644 index 6707a7c32e..0000000000 --- a/qcsrc/common/weapons/weapon/hmg.qc +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef IMPLEMENTATION -CLASS(HeavyMachineGun, Weapon) -/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails) -/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3) -/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON); -/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); -#ifndef MENUQC -/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM); -#endif -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg"); -/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg"); -/* wepname */ ATTRIB(HeavyMachineGun, message, string, _("Heavy Machine Gun")); -ENDCLASS(HeavyMachineGun) -REGISTER_WEAPON(HMG, NEW(HeavyMachineGun)); - -#define HMG_SETTINGS(w_cvar,w_prop) HMG_SETTINGS_LIST(w_cvar, w_prop, HMG, hmg) -#define HMG_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ - w_cvar(id, sn, NONE, spread_min) \ - w_cvar(id, sn, NONE, spread_max) \ - w_cvar(id, sn, NONE, spread_add) \ - w_cvar(id, sn, NONE, solidpenetration) \ - w_cvar(id, sn, NONE, damage) \ - w_cvar(id, sn, NONE, force) \ - w_cvar(id, sn, NONE, refire) \ - w_cvar(id, sn, NONE, ammo) \ - w_prop(id, sn, float, reloading_ammo, reload_ammo) \ - w_prop(id, sn, float, reloading_time, reload_time) \ - w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ - w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ - w_prop(id, sn, string, weaponreplace, weaponreplace) \ - w_prop(id, sn, float, weaponstart, weaponstart) \ - w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ - w_prop(id, sn, float, weaponthrowable, weaponthrowable) - -#ifdef SVQC -HMG_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) -#endif -#endif -#ifdef IMPLEMENTATION -#ifdef SVQC - -spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(WEP_HMG.m_id); } - -void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2) -{ - if (!actor.BUTTON_ATCK) - { - w_ready(thiswep, actor, fire1, fire2); - return; - } - - Weapon w = get_weaponinfo(actor.weapon); - if(!w.wr_checkammo1(w)) - if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) - { - W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); - return; - } - - W_DecreaseAmmo(WEP_HMG, self, WEP_CVAR(hmg, ammo)); - - W_SetupShot (actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(hmg, damage)); - - if(!autocvar_g_norecoil) - { - actor.punchangle_x = random () - 0.5; - actor.punchangle_y = random () - 0.5; - } - - float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.misc_bulletcounter), WEP_CVAR(hmg, spread_max)); - fireBullet(w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0); - - actor.misc_bulletcounter = actor.misc_bulletcounter + 1; - - Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - - W_MachineGun_MuzzleFlash(); - W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); - - if (autocvar_g_casings >= 2) // casing code - SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor); - - ATTACK_FINISHED(actor) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto); -} - - METHOD(HeavyMachineGun, wr_aim, void(entity thiswep)) - { - if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); - else - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); - } - METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) - { - if(WEP_CVAR(hmg, reload_ammo) && actor.clip_load < WEP_CVAR(hmg, ammo)) { // forced reload - Weapon w = get_weaponinfo(actor.weapon); - w.wr_reload(w); - } else - { - if (fire1) - if (weapon_prepareattack(thiswep, actor, false, 0)) - { - actor.misc_bulletcounter = 0; - W_HeavyMachineGun_Attack_Auto(thiswep, actor, fire1, fire2); - } - } - } - METHOD(HeavyMachineGun, wr_init, void(entity thiswep)) - { - HMG_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); - } - METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep)) - { - float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo); - - if(autocvar_g_balance_hmg_reload_ammo) - ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo); - - return ammo_amount; - } - METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep)) - { - float ammo_amount = self.ammo_nails >= WEP_CVAR(hmg, ammo); - - if(autocvar_g_balance_hmg_reload_ammo) - ammo_amount += self.(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo); - - return ammo_amount; - } - METHOD(HeavyMachineGun, wr_config, void(entity thiswep)) - { - HMG_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); - } - METHOD(HeavyMachineGun, wr_reload, void(entity thiswep)) - { - W_Reload(self, WEP_CVAR(hmg, ammo), SND(RELOAD)); - } - METHOD(HeavyMachineGun, wr_suicidemessage, int(entity thiswep)) - { - return WEAPON_THINKING_WITH_PORTALS; - } - METHOD(HeavyMachineGun, wr_killmessage, int(entity thiswep)) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_HMG_MURDER_SNIPE; - else - return WEAPON_HMG_MURDER_SPRAY; - } - -#endif -#ifdef CSQC - - METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep)) - { - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), org2, w_backoff * 1000, 1); - if(!w_issilent) - if(w_random < 0.05) - sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_NORM); - else if(w_random < 0.1) - sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTEN_NORM); - else if(w_random < 0.2) - sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM); - } - -#endif -#endif diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index c158d1c328..4e3722f138 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -13,17 +13,20 @@ CLASS(Hook, Weapon) /* crosshair */ ATTRIB(Hook, w_crosshair_size, float, 0.5); /* wepimg */ ATTRIB(Hook, model2, string, "weaponhook"); /* refname */ ATTRIB(Hook, netname, string, "hook"); -/* wepname */ ATTRIB(Hook, message, string, _("Grappling Hook")); +/* wepname */ ATTRIB(Hook, m_name, string, _("Grappling Hook")); ATTRIB(Hook, ammo_factor, float, 1) ENDCLASS(Hook) REGISTER_WEAPON(HOOK, NEW(Hook)); CLASS(OffhandHook, OffhandWeapon) +#ifdef SVQC METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity actor, bool key_pressed)) { Weapon wep = WEP_HOOK; - wep.wr_think(wep, actor, key_pressed, false); + .entity weaponentity = weaponentities[1]; + wep.wr_think(wep, actor, weaponentity, key_pressed ? 1 : 0); } +#endif ENDCLASS(OffhandHook) OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); } @@ -71,9 +74,9 @@ HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_hook) { weapon_defaultspawnfunc(WEP_HOOK.m_id); } +spawnfunc(weapon_hook) { weapon_defaultspawnfunc(this, WEP_HOOK); } -void W_Hook_ExplodeThink(void) +void W_Hook_ExplodeThink() {SELFPARAM(); float dt, dmg_remaining_next, f; @@ -93,7 +96,7 @@ void W_Hook_ExplodeThink(void) remove(self); } -void W_Hook_Explode2(void) +void W_Hook_Explode2() {SELFPARAM(); self.event_damage = func_null; self.touch = func_null; @@ -126,7 +129,7 @@ void W_Hook_Damage(entity inflictor, entity attacker, float damage, int deathtyp W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2); } -void W_Hook_Touch2(void) +void W_Hook_Touch2() {SELFPARAM(); PROJECTILE_TOUCH; self.use(); @@ -174,30 +177,30 @@ void W_Hook_Attack2(Weapon thiswep, entity actor) MUTATOR_CALLHOOK(EditProjectile, actor, gren); } - METHOD(Hook, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - if (fire1) { + if (fire & 1) { if(!actor.hook) if(!(actor.hook_state & HOOK_WAITING_FOR_RELEASE)) if(time > actor.hook_refire) - if(weapon_prepareattack(thiswep, actor, false, -1)) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, -1)) { W_DecreaseAmmo(thiswep, actor, thiswep.ammo_factor * WEP_CVAR_PRI(hook, ammo)); actor.hook_state |= HOOK_FIRING; actor.hook_state |= HOOK_WAITING_FOR_RELEASE; - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready); } } else { actor.hook_state |= HOOK_REMOVING; actor.hook_state &= ~HOOK_WAITING_FOR_RELEASE; } - if(fire2) + if(fire & 2) { - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(hook, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hook, refire))) { W_Hook_Attack2(thiswep, actor); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready); } } @@ -313,7 +316,7 @@ void W_Hook_Attack2(Weapon thiswep, entity actor) { vector org2; org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum(EFFECT_HOOK_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_HOOK_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_HOOKBOMB_IMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index 90a4471f17..4679ef38f7 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -13,7 +13,7 @@ CLASS(MachineGun, Weapon) /* crosshair */ ATTRIB(MachineGun, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(MachineGun, model2, string, "weaponuzi"); /* refname */ ATTRIB(MachineGun, netname, string, "machinegun"); -/* wepname */ ATTRIB(MachineGun, message, string, _("MachineGun")); +/* wepname */ ATTRIB(MachineGun, m_name, string, _("MachineGun")); ENDCLASS(MachineGun) REGISTER_WEAPON(MACHINEGUN, NEW(MachineGun)); @@ -62,14 +62,14 @@ spawnfunc(weapon_machinegun) if(autocvar_sv_q3acompat_machineshotgunswap) if(self.classname != "droppedweapon") { - weapon_defaultspawnfunc(WEP_SHOCKWAVE.m_id); + weapon_defaultspawnfunc(this, WEP_SHOCKWAVE); return; } - weapon_defaultspawnfunc(WEP_MACHINEGUN.m_id); + weapon_defaultspawnfunc(this, WEP_MACHINEGUN); } spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); } -void W_MachineGun_MuzzleFlash_Think(void) +void W_MachineGun_MuzzleFlash_Think() {SELFPARAM(); self.frame = self.frame + 2; self.scale = self.scale * 0.5; @@ -86,7 +86,7 @@ void W_MachineGun_MuzzleFlash_Think(void) } -void W_MachineGun_MuzzleFlash(void) +void W_MachineGun_MuzzleFlash() {SELFPARAM(); if(self.muzzle_flash == world) self.muzzle_flash = spawn(); @@ -104,7 +104,7 @@ void W_MachineGun_MuzzleFlash(void) self.muzzle_flash.owner = self.muzzle_flash.realowner = self; } -void W_MachineGun_Attack(Weapon thiswep, int deathtype) +void W_MachineGun_Attack(Weapon thiswep, int deathtype, .entity weaponentity) {SELFPARAM(); W_SetupShot(self, true, 0, SND(UZI_FIRE), CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); if(!autocvar_g_norecoil) @@ -112,9 +112,9 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype) self.punchangle_x = random() - 0.5; self.punchangle_y = random() - 0.5; } - + int slot = weaponslot(weaponentity); // this attack_finished just enforces a cooldown at the end of a burst - ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); + ATTACK_FINISHED(self, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); if(self.misc_bulletcounter == 1) fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, 0); @@ -137,11 +137,11 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype) } // weapon frames -void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire) { if(actor.weapon != actor.switchweapon) // abort immediately if switching { - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } if(actor.BUTTON_ATCK) @@ -151,25 +151,25 @@ void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, bool fire1, bool fi if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } actor.misc_bulletcounter = actor.misc_bulletcounter + 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); + W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); } else - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready); } -void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) { float machinegun_spread; - if(!fire1) + if(!(fire & 1)) { - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } @@ -178,7 +178,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fir if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } @@ -204,11 +204,12 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, bool fire1, bool fir if(autocvar_g_casings >= 2) // casing code SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor); - ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto); + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto); } -void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_SetupShot(actor, true, 0, SND(UZI_FIRE), CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); if(!autocvar_g_norecoil) @@ -230,12 +231,13 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fi actor.misc_bulletcounter = actor.misc_bulletcounter + 1; if(actor.misc_bulletcounter == 0) { - ATTACK_FINISHED(actor) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready); + int slot = weaponslot(weaponentity); + ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready); } else { - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst); } } @@ -247,7 +249,7 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fi else self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, false); } - METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(machinegun, reload_ammo) && actor.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); @@ -255,48 +257,48 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fi } else if(WEP_CVAR(machinegun, mode) == 1) { - if(fire1) - if(weapon_prepareattack(thiswep, actor, false, 0)) + if(fire & 1) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) { actor.misc_bulletcounter = 0; - W_MachineGun_Attack_Auto(thiswep, actor, fire1, fire2); + W_MachineGun_Attack_Auto(thiswep, actor, weaponentity, fire); } - if(fire2) - if(weapon_prepareattack(thiswep, actor, true, 0)) + if(fire & 2) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0)) { Weapon w = get_weaponinfo(actor.weapon); if(!w.wr_checkammo2(w)) if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, burst_ammo)); actor.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1; - W_MachineGun_Attack_Burst(thiswep, actor, fire1, fire2); + W_MachineGun_Attack_Burst(thiswep, actor, weaponentity, fire); } } else { - if(fire1) - if(weapon_prepareattack(thiswep, actor, false, 0)) + if(fire & 1) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) { actor.misc_bulletcounter = 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id); // sets attack_finished - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); + W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, weaponentity); // sets attack_finished + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); } - if(fire2 && WEP_CVAR(machinegun, first)) - if(weapon_prepareattack(thiswep, actor, true, 0)) + if((fire & 2) && WEP_CVAR(machinegun, first)) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0)) { actor.misc_bulletcounter = 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY); // sets attack_finished - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready); + W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY, weaponentity); // sets attack_finished + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready); } } } @@ -365,14 +367,9 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, bool fire1, bool fi { vector org2; org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum(EFFECT_MACHINEGUN_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent) - if(w_random < 0.05) - sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTN_NORM); - else if(w_random < 0.1) - sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTN_NORM); - else if(w_random < 0.2) - sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM); + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM); } #endif diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index 548ede9660..1365e61b99 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -13,7 +13,7 @@ CLASS(MineLayer, Weapon) /* crosshair */ ATTRIB(MineLayer, w_crosshair_size, float, 0.9); /* wepimg */ ATTRIB(MineLayer, model2, string, "weaponminelayer"); /* refname */ ATTRIB(MineLayer, netname, string, "minelayer"); -/* wepname */ ATTRIB(MineLayer, message, string, _("Mine Layer")); +/* wepname */ ATTRIB(MineLayer, m_name, string, _("Mine Layer")); ENDCLASS(MineLayer) REGISTER_WEAPON(MINE_LAYER, NEW(MineLayer)); @@ -51,7 +51,7 @@ REGISTER_WEAPON(MINE_LAYER, NEW(MineLayer)); #ifdef SVQC MINELAYER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) -void W_MineLayer_Think(void); +void W_MineLayer_Think(); .float minelayer_detonate, mine_explodeanyway; .float mine_time; .vector mine_orientation; @@ -59,7 +59,7 @@ void W_MineLayer_Think(void); #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(WEP_MINE_LAYER.m_id); } +spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(this, WEP_MINE_LAYER); } void W_MineLayer_Stick(entity to) {SELFPARAM(); @@ -67,8 +67,7 @@ void W_MineLayer_Stick(entity to) // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile - entity newmine; - newmine = spawn(); + entity newmine = spawn(); newmine.classname = self.classname; newmine.bot_dodge = self.bot_dodge; @@ -108,7 +107,7 @@ void W_MineLayer_Stick(entity to) SetMovetypeFollow(self, to); } -void W_MineLayer_Explode(void) +void W_MineLayer_Explode() {SELFPARAM(); if(other.takedamage == DAMAGE_AIM) if(IS_PLAYER(other)) @@ -129,7 +128,8 @@ void W_MineLayer_Explode(void) if(!w.wr_checkammo1(w)) { self.cnt = WEP_MINE_LAYER.m_id; - ATTACK_FINISHED(self) = time; + int slot = 0; // TODO: unhardcode + ATTACK_FINISHED(self, slot) = time; self.switchweapon = w_getbestweapon(self); } setself(this); @@ -138,7 +138,7 @@ void W_MineLayer_Explode(void) remove(self); } -void W_MineLayer_DoRemoteExplode(void) +void W_MineLayer_DoRemoteExplode() {SELFPARAM(); self.event_damage = func_null; self.takedamage = DAMAGE_NO; @@ -155,7 +155,8 @@ void W_MineLayer_DoRemoteExplode(void) if(!w.wr_checkammo1(w)) { self.cnt = WEP_MINE_LAYER.m_id; - ATTACK_FINISHED(self) = time; + int slot = 0; // TODO: unhardcode + ATTACK_FINISHED(self, slot) = time; self.switchweapon = w_getbestweapon(self); } setself(this); @@ -164,7 +165,7 @@ void W_MineLayer_DoRemoteExplode(void) remove(self); } -void W_MineLayer_RemoteExplode(void) +void W_MineLayer_RemoteExplode() {SELFPARAM(); if(self.realowner.deadflag == DEAD_NO) if((self.spawnshieldtime >= 0) @@ -176,7 +177,7 @@ void W_MineLayer_RemoteExplode(void) } } -void W_MineLayer_ProximityExplode(void) +void W_MineLayer_ProximityExplode() {SELFPARAM(); // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0) @@ -205,7 +206,7 @@ int W_MineLayer_Count(entity e) return minecount; } -void W_MineLayer_Think(void) +void W_MineLayer_Think() {SELFPARAM(); entity head; @@ -268,7 +269,7 @@ void W_MineLayer_Think(void) W_MineLayer_RemoteExplode(); } -void W_MineLayer_Touch(void) +void W_MineLayer_Touch() {SELFPARAM(); if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW) return; // we're already a stuck mine, why do we get called? TODO does this even happen? @@ -499,7 +500,7 @@ float W_MineLayer_PlacedMines(float detonate) if(self.BUTTON_ATCK2 == true) self.BUTTON_ATCK = false; } } - METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_minelayer_reload_ammo && actor.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload { @@ -509,16 +510,16 @@ float W_MineLayer_PlacedMines(float detonate) w.wr_reload(w); } } - else if(fire1) + else if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(minelayer, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(minelayer, refire))) { W_MineLayer_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready); } } - if(fire2) + if(fire & 2) { if(W_MineLayer_PlacedMines(true)) sound(actor, CH_WEAPON_B, SND_MINE_DET, VOL_BASE, ATTN_NORM); @@ -530,8 +531,9 @@ float W_MineLayer_PlacedMines(float detonate) } METHOD(MineLayer, wr_checkammo1, bool(entity thiswep)) { + int slot = 0; // TODO: unhardcode // don't switch while placing a mine - if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER.m_id) + if(ATTACK_FINISHED(self, slot) <= time || self.weapon != WEP_MINE_LAYER.m_id) { float ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo); ammo_amount += self.(weapon_load[WEP_MINE_LAYER.m_id]) >= WEP_CVAR(minelayer, ammo); @@ -574,7 +576,7 @@ float W_MineLayer_PlacedMines(float detonate) { vector org2; org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_MINE_EXP, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index c199574581..36a3c0c9e6 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -13,7 +13,7 @@ CLASS(Mortar, Weapon) /* crosshair */ ATTRIB(Mortar, w_crosshair_size, float, 0.7); /* wepimg */ ATTRIB(Mortar, model2, string, "weapongrenadelauncher"); /* refname */ ATTRIB(Mortar, netname, string, "mortar"); -/* wepname */ ATTRIB(Mortar, message, string, _("Mortar")); +/* wepname */ ATTRIB(Mortar, m_name, string, _("Mortar")); ENDCLASS(Mortar) REGISTER_WEAPON(MORTAR, NEW(Mortar)); @@ -58,10 +58,10 @@ MORTAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(WEP_MORTAR.m_id); } +spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); } spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); } -void W_Mortar_Grenade_Explode(void) +void W_Mortar_Grenade_Explode() {SELFPARAM(); if(other.takedamage == DAMAGE_AIM) if(IS_PLAYER(other)) @@ -81,7 +81,7 @@ void W_Mortar_Grenade_Explode(void) remove(self); } -void W_Mortar_Grenade_Explode2(void) +void W_Mortar_Grenade_Explode2() {SELFPARAM(); if(other.takedamage == DAMAGE_AIM) if(IS_PLAYER(other)) @@ -116,7 +116,7 @@ void W_Mortar_Grenade_Damage(entity inflictor, entity attacker, float damage, in W_PrepareExplosionByDamage(attacker, self.use); } -void W_Mortar_Grenade_Think1(void) +void W_Mortar_Grenade_Think1() {SELFPARAM(); self.nextthink = time; if(time > self.cnt) @@ -130,7 +130,7 @@ void W_Mortar_Grenade_Think1(void) W_Mortar_Grenade_Explode(); } -void W_Mortar_Grenade_Touch1(void) +void W_Mortar_Grenade_Touch1() {SELFPARAM(); PROJECTILE_TOUCH; if(other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile @@ -162,7 +162,7 @@ void W_Mortar_Grenade_Touch1(void) } } -void W_Mortar_Grenade_Touch2(void) +void W_Mortar_Grenade_Touch2() {SELFPARAM(); PROJECTILE_TOUCH; if(other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile @@ -209,9 +209,8 @@ void W_Mortar_Attack(Weapon thiswep) Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - gren = spawn(); + gren = new(grenade); gren.owner = gren.realowner = self; - gren.classname = "grenade"; gren.bot_dodge = true; gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage); gren.movetype = MOVETYPE_BOUNCE; @@ -258,9 +257,8 @@ void W_Mortar_Attack2(Weapon thiswep) Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - gren = spawn(); + gren = new(grenade); gren.owner = gren.realowner = self; - gren.classname = "grenade"; gren.bot_dodge = true; gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage); gren.movetype = MOVETYPE_BOUNCE; @@ -334,20 +332,20 @@ void W_Mortar_Attack2(Weapon thiswep) wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime)))); wepinfo_ter_dps = 0; */ - METHOD(Mortar, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_mortar_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); - } else if(fire1) + } else if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(mortar, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(mortar, refire))) { W_Mortar_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready); } } - else if(fire2) + else if(fire & 2) { if(WEP_CVAR_SEC(mortar, remote_detonateprimary)) { @@ -364,10 +362,10 @@ void W_Mortar_Attack2(Weapon thiswep) if(nadefound) sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM); } - else if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(mortar, refire))) + else if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(mortar, refire))) { W_Mortar_Attack2(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready); } } } @@ -417,7 +415,7 @@ void W_Mortar_Attack2(Weapon thiswep) { vector org2; org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum(EFFECT_GRENADE_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_GRENADE_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_GRENADE_IMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index 790ab53caf..b8c54c4165 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -13,7 +13,7 @@ CLASS(PortoLaunch, Weapon) /* crosshair */ ATTRIB(PortoLaunch, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(PortoLaunch, model2, string, "weaponporto"); /* refname */ ATTRIB(PortoLaunch, netname, string, "porto"); -/* wepname */ ATTRIB(PortoLaunch, message, string, _("Port-O-Launch")); +/* wepname */ ATTRIB(PortoLaunch, m_name, string, _("Port-O-Launch")); ENDCLASS(PortoLaunch) REGISTER_WEAPON(PORTO, NEW(PortoLaunch)); @@ -44,7 +44,7 @@ PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #ifdef SVQC #include "../../triggers/trigger/jumppads.qh" -spawnfunc(weapon_porto) { weapon_defaultspawnfunc(WEP_PORTO.m_id); } +spawnfunc(weapon_porto) { weapon_defaultspawnfunc(this, WEP_PORTO); } REGISTER_MUTATOR(porto_ticker, true); MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) { @@ -53,7 +53,7 @@ MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) { e.porto_forbidden = max(0, e.porto_forbidden - 1); } -void W_Porto_Success(void) +void W_Porto_Success() {SELFPARAM(); if(self.realowner == world) { @@ -109,7 +109,7 @@ void W_Porto_Remove(entity p) } } -void W_Porto_Think(void) +void W_Porto_Think() {SELFPARAM(); trace_plane_normal = '0 0 0'; if(self.realowner.playerid != self.playerid) @@ -118,7 +118,7 @@ void W_Porto_Think(void) W_Porto_Fail(0); } -void W_Porto_Touch(void) +void W_Porto_Touch() {SELFPARAM(); vector norm; @@ -251,11 +251,10 @@ void W_Porto_Attack(float type) //Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - gren = spawn(); + gren = new(porto); gren.cnt = type; gren.owner = gren.realowner = self; gren.playerid = self.playerid; - gren.classname = "porto"; gren.bot_dodge = true; gren.bot_dodgerating = 200; gren.movetype = MOVETYPE_BOUNCEMISSILE; @@ -306,33 +305,33 @@ void W_Porto_Attack(float type) { PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); } - METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(porto, secondary)) { - if(fire1) + if(fire & 1) if(!actor.porto_current) if(!actor.porto_forbidden) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(porto, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire))) { W_Porto_Attack(0); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } - if(fire2) + if(fire & 2) if(!actor.porto_current) if(!actor.porto_forbidden) - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(porto, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(porto, refire))) { W_Porto_Attack(1); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready); } } else { if(actor.porto_v_angle_held) { - if(!fire2) + if(!(fire & 2)) { actor.porto_v_angle_held = 0; @@ -341,7 +340,7 @@ void W_Porto_Attack(float type) } else { - if(fire2) + if(fire & 2) { actor.porto_v_angle = actor.v_angle; actor.porto_v_angle_held = 1; @@ -352,13 +351,13 @@ void W_Porto_Attack(float type) if(actor.porto_v_angle_held) makevectors(actor.porto_v_angle); // override the previously set angles - if(fire1) + if(fire & 1) if(!actor.porto_current) if(!actor.porto_forbidden) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(porto, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire))) { W_Porto_Attack(-1); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } } } diff --git a/qcsrc/common/weapons/weapon/rifle.qc b/qcsrc/common/weapons/weapon/rifle.qc index 49b3931566..e7cd2606c1 100644 --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@ -13,7 +13,7 @@ CLASS(Rifle, Weapon) /* crosshair */ ATTRIB(Rifle, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(Rifle, model2, string, "weaponrifle"); /* refname */ ATTRIB(Rifle, netname, string, "rifle"); -/* wepname */ ATTRIB(Rifle, message, string, _("Rifle")); +/* wepname */ ATTRIB(Rifle, m_name, string, _("Rifle")); ENDCLASS(Rifle) REGISTER_WEAPON(RIFLE, NEW(Rifle)); @@ -49,7 +49,7 @@ RIFLE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(WEP_RIFLE.m_id); } +spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); } spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); } spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); } @@ -76,46 +76,44 @@ void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pFor SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self); } -void W_Rifle_Attack(void) +void W_Rifle_Attack() { W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND(CAMPINGRIFLE_FIRE)); } -void W_Rifle_Attack2(void) +void W_Rifle_Attack2() { W_Rifle_FireBullet(WEP_RIFLE, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND(CAMPINGRIFLE_FIRE2)); } -.void(void) rifle_bullethail_attackfunc; +.void() rifle_bullethail_attackfunc; .float rifle_bullethail_frame; .float rifle_bullethail_animtime; .float rifle_bullethail_refire; -void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponentity, int fire) { float r, sw, af; sw = actor.switchweapon; // make it not detect weapon changes as reason to abort firing - af = ATTACK_FINISHED(actor); + int slot = weaponslot(weaponentity); + af = ATTACK_FINISHED(actor, slot); actor.switchweapon = actor.weapon; - ATTACK_FINISHED(actor) = time; - LOG_INFO(ftos(actor.WEP_AMMO(RIFLE)), "\n"); - r = weapon_prepareattack(thiswep, actor, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire); + ATTACK_FINISHED(actor, slot) = time; + r = weapon_prepareattack(thiswep, actor, weaponentity, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire); if(actor.switchweapon == actor.weapon) actor.switchweapon = sw; if(r) { actor.rifle_bullethail_attackfunc(); - weapon_thinkf(actor, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); - LOG_INFO("thinkf set\n"); + weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); } else { - ATTACK_FINISHED(actor) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time - LOG_INFO("out of ammo... ", ftos(actor.weaponentity.state), "\n"); + ATTACK_FINISHED(actor, slot) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time } } -void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire) +void W_Rifle_BulletHail(.entity weaponentity, float mode, void() AttackFunc, float fr, float animtime, float refire) {SELFPARAM(); // if we get here, we have at least one bullet to fire AttackFunc(); @@ -126,12 +124,12 @@ void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animt self.rifle_bullethail_frame = fr; self.rifle_bullethail_animtime = animtime; self.rifle_bullethail_refire = refire; - weapon_thinkf(self, fr, animtime, W_Rifle_BulletHail_Continue); + weapon_thinkf(self, weaponentity, fr, animtime, W_Rifle_BulletHail_Continue); } else { // just one shot - weapon_thinkf(self, fr, animtime, w_ready); + weapon_thinkf(self, weaponentity, fr, animtime, w_ready); } } @@ -160,7 +158,7 @@ void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animt } } } - METHOD(Rifle, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_rifle_reload_ammo && actor.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); @@ -168,15 +166,15 @@ void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animt } else { actor.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), actor.rifle_accumulator, time); - if(fire1) - if(weapon_prepareattack_check(thiswep, actor, false, WEP_CVAR_PRI(rifle, refire))) + if(fire & 1) + if(weapon_prepareattack_check(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(rifle, refire))) if(time >= actor.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost)) { - weapon_prepareattack_do(actor, false, WEP_CVAR_PRI(rifle, refire)); - W_Rifle_BulletHail(WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + weapon_prepareattack_do(actor, weaponentity, false, WEP_CVAR_PRI(rifle, refire)); + W_Rifle_BulletHail(weaponentity, WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); actor.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost); } - if(fire2) + if(fire & 2) { if(WEP_CVAR(rifle, secondary)) { @@ -185,11 +183,11 @@ void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animt w.wr_reload(w); } else { - if(weapon_prepareattack_check(thiswep, actor, true, WEP_CVAR_SEC(rifle, refire))) + if(weapon_prepareattack_check(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(rifle, refire))) if(time >= actor.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost)) { - weapon_prepareattack_do(actor, true, WEP_CVAR_SEC(rifle, refire)); - W_Rifle_BulletHail(WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(rifle, refire)); + W_Rifle_BulletHail(weaponentity, WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); actor.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost); } } @@ -254,15 +252,10 @@ void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animt { vector org2; org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum(EFFECT_RIFLE_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_RIFLE_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent) { - if(w_random < 0.2) - sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTN_NORM); - else if(w_random < 0.4) - sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTN_NORM); - else if(w_random < 0.5) - sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTN_NORM); + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM); } } METHOD(Rifle, wr_init, void(entity thiswep)) diff --git a/qcsrc/common/weapons/weapon/rpc.qc b/qcsrc/common/weapons/weapon/rpc.qc deleted file mode 100644 index 71ddaa6b58..0000000000 --- a/qcsrc/common/weapons/weapon/rpc.qc +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef IMPLEMENTATION -CLASS(RocketPropelledChainsaw, Weapon) -/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7) -/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON); -/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); -#ifndef MENUQC -/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); -#endif -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc"); -/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc"); -/* wepname */ ATTRIB(RocketPropelledChainsaw, message, string, _("Rocket Propelled Chainsaw")); -ENDCLASS(RocketPropelledChainsaw) -REGISTER_WEAPON(RPC, NEW(RocketPropelledChainsaw)); - -#define RPC_SETTINGS(w_cvar,w_prop) RPC_SETTINGS_LIST(w_cvar, w_prop, RPC, rpc) -#define RPC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ - w_cvar(id, sn, NONE, ammo) \ - w_cvar(id, sn, NONE, animtime) \ - w_cvar(id, sn, NONE, damage) \ - w_cvar(id, sn, NONE, damage2) \ - w_cvar(id, sn, NONE, damageforcescale) \ - w_cvar(id, sn, NONE, edgedamage) \ - w_cvar(id, sn, NONE, force) \ - w_cvar(id, sn, NONE, health) \ - w_cvar(id, sn, NONE, lifetime) \ - w_cvar(id, sn, NONE, radius) \ - w_cvar(id, sn, NONE, refire) \ - w_cvar(id, sn, NONE, speed) \ - w_cvar(id, sn, NONE, speedaccel) \ - w_prop(id, sn, float, reloading_ammo, reload_ammo) \ - w_prop(id, sn, float, reloading_time, reload_time) \ - w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ - w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ - w_prop(id, sn, string, weaponreplace, weaponreplace) \ - w_prop(id, sn, float, weaponstart, weaponstart) \ - w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ - w_prop(id, sn, float, weaponthrowable, weaponthrowable) - -#ifdef SVQC -RPC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) -#endif -#endif -#ifdef IMPLEMENTATION -#ifdef SVQC -spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(WEP_RPC.m_id); } - -void W_RocketPropelledChainsaw_Explode() -{SELFPARAM(); - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - RadiusDamage (self, self.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), world, world, WEP_CVAR(rpc, force), self.projectiledeathtype, other); - - remove (self); -} - -void W_RocketPropelledChainsaw_Touch (void) -{SELFPARAM(); - if(WarpZone_Projectile_Touch()) - if(wasfreed(self)) - return; - - W_RocketPropelledChainsaw_Explode(); -} - -void W_RocketPropelledChainsaw_Damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if (self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, W_RocketPropelledChainsaw_Explode); -} - -void W_RocketPropelledChainsaw_Think() -{SELFPARAM(); - if(self.cnt <= time) - { - remove(self); - return; - } - - self.cnt = vlen(self.velocity); - self.wait = self.cnt * sys_frametime; - self.pos1 = normalize(self.velocity); - - tracebox(self.origin, self.mins, self.maxs, self.origin + self.pos1 * (2 * self.wait), MOVE_NORMAL, self); - if(IS_PLAYER(trace_ent)) - Damage (trace_ent, self, self.realowner, WEP_CVAR(rpc, damage2), self.projectiledeathtype, self.origin, normalize(self.origin - other.origin) * WEP_CVAR(rpc, force)); - - self.velocity = self.pos1 * (self.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime)); - - UpdateCSQCProjectile(self); - self.nextthink = time; -} - -void W_RocketPropelledChainsaw_Attack (Weapon thiswep) -{SELFPARAM(); - entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(self); - entity flash = spawn (); - - W_DecreaseAmmo(thiswep, self, WEP_CVAR(rpc, ammo)); - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', false, 5, SND(ROCKET_FIRE), CH_WEAPON_A, WEP_CVAR(rpc, damage)); - Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - PROJECTILE_MAKETRIGGER(missile); - - missile.owner = missile.realowner = self; - missile.bot_dodge = true; - missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2; - - missile.takedamage = DAMAGE_YES; - missile.damageforcescale = WEP_CVAR(rpc, damageforcescale); - missile.health = WEP_CVAR(rpc, health); - missile.event_damage = W_RocketPropelledChainsaw_Damage; - missile.damagedbycontents = true; - missile.movetype = MOVETYPE_FLY; - - missile.projectiledeathtype = WEP_RPC.m_id; - setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - - setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point - W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0); - - missile.touch = W_RocketPropelledChainsaw_Touch; - - missile.think = W_RocketPropelledChainsaw_Think; - missile.cnt = time + WEP_CVAR(rpc, lifetime); - missile.nextthink = time; - missile.flags = FL_PROJECTILE; - - CSQCProjectile(missile, true, PROJECTILE_RPC, false); - - setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below - SUB_SetFade (flash, time, 0.1); - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(self, flash, '5 0 0'); - missile.pos1 = missile.velocity; - - MUTATOR_CALLHOOK(EditProjectile, self, missile); -} - - METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep)) - { - self.BUTTON_ATCK = bot_aim(WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false); - } - METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) - { - if(WEP_CVAR(rpc, reload_ammo) && actor.clip_load < WEP_CVAR(rpc, ammo)) { - Weapon w = get_weaponinfo(actor.weapon); - w.wr_reload(w); - } else - { - if (fire1) - { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(rpc, refire))) - { - W_RocketPropelledChainsaw_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready); - } - } - - if (fire2) - { - // to-do - } - } - } - METHOD(RocketPropelledChainsaw, wr_init, void(entity thiswep)) - { - RPC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP); - } - METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep)) - { - float ammo_amount = self.WEP_AMMO(RPC) >= WEP_CVAR(rpc, ammo); - ammo_amount += self.(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo); - return ammo_amount; - } - METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep)) - { - return false; - } - METHOD(RocketPropelledChainsaw, wr_config, void(entity thiswep)) - { - RPC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS); - } - METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep)) - { - W_Reload(self, WEP_CVAR(rpc, ammo), SND(RELOAD)); - } - METHOD(RocketPropelledChainsaw, wr_suicidemessage, int(entity thiswep)) - { - if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_RPC_SUICIDE_SPLASH; - else - return WEAPON_RPC_SUICIDE_DIRECT; - } - METHOD(RocketPropelledChainsaw, wr_killmessage, int(entity thiswep)) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_BLASTER_MURDER; - else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_RPC_MURDER_SPLASH; - else - return WEAPON_RPC_MURDER_DIRECT; - } - -#endif - -#ifdef CSQC - - METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep)) - { - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - } - -#endif -#endif diff --git a/qcsrc/common/weapons/weapon/seeker.qc b/qcsrc/common/weapons/weapon/seeker.qc index 645bd9eb9b..faf20a4fb9 100644 --- a/qcsrc/common/weapons/weapon/seeker.qc +++ b/qcsrc/common/weapons/weapon/seeker.qc @@ -13,7 +13,7 @@ CLASS(Seeker, Weapon) /* crosshair */ ATTRIB(Seeker, w_crosshair_size, float, 0.8); /* wepimg */ ATTRIB(Seeker, model2, string, "weaponseeker"); /* refname */ ATTRIB(Seeker, netname, string, "seeker"); -/* wepname */ ATTRIB(Seeker, message, string, _("T.A.G. Seeker")); +/* wepname */ ATTRIB(Seeker, m_name, string, _("T.A.G. Seeker")); ENDCLASS(Seeker) REGISTER_WEAPON(SEEKER, NEW(Seeker)); @@ -86,12 +86,12 @@ SEEKER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(WEP_SEEKER.m_id); } +spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); } // ============================ // Begin: Missile functions, these are general functions to be manipulated by other code // ============================ -void W_Seeker_Missile_Explode(void) +void W_Seeker_Missile_Explode() {SELFPARAM(); self.event_damage = func_null; RadiusDamage(self, self.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), world, world, WEP_CVAR(seeker, missile_force), self.projectiledeathtype, other); @@ -99,14 +99,14 @@ void W_Seeker_Missile_Explode(void) remove(self); } -void W_Seeker_Missile_Touch(void) +void W_Seeker_Missile_Touch() { PROJECTILE_TOUCH; W_Seeker_Missile_Explode(); } -void W_Seeker_Missile_Think(void) +void W_Seeker_Missile_Think() {SELFPARAM(); entity e; vector desireddir, olddir, newdir, eorg; @@ -221,7 +221,7 @@ void W_Seeker_Missile_Damage(entity inflictor, entity attacker, float damage, in } /* -void W_Seeker_Missile_Animate(void) +void W_Seeker_Missile_Animate() { self.frame = self.frame +1; self.nextthink = time + 0.05; @@ -258,9 +258,8 @@ void W_Seeker_Fire_Missile(Weapon thiswep, vector f_diff, entity m_target) //self.detornator = false; - missile = spawn(); + missile = new(seeker_missile); missile.owner = missile.realowner = self; - missile.classname = "seeker_missile"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage); @@ -302,7 +301,7 @@ void W_Seeker_Fire_Missile(Weapon thiswep, vector f_diff, entity m_target) // ============================ // Begin: FLAC, close range attack meant for defeating rockets which are coming at you. // ============================ -void W_Seeker_Flac_Explode(void) +void W_Seeker_Flac_Explode() {SELFPARAM(); self.event_damage = func_null; @@ -311,7 +310,7 @@ void W_Seeker_Flac_Explode(void) remove(self); } -void W_Seeker_Flac_Touch(void) +void W_Seeker_Flac_Touch() { PROJECTILE_TOUCH; @@ -348,9 +347,8 @@ void W_Seeker_Fire_Flac(Weapon thiswep) Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - missile = spawn(); + missile = new(missile); missile.owner = missile.realowner = self; - missile.classname = "missile"; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage); missile.touch = W_Seeker_Flac_Explode; @@ -390,7 +388,7 @@ entity W_Seeker_Tagged_Info(entity isowner, entity istarget) return world; } -void W_Seeker_Attack(void) +void W_Seeker_Attack() {SELFPARAM(); entity tracker, closest_target; @@ -413,7 +411,7 @@ void W_Seeker_Attack(void) W_Seeker_Fire_Missile(WEP_SEEKER, '0 0 0', closest_target); } -void W_Seeker_Vollycontroller_Think(void) // TODO: Merge this with W_Seeker_Attack +void W_Seeker_Vollycontroller_Think() // TODO: Merge this with W_Seeker_Attack {SELFPARAM(); float c; entity oldenemy; @@ -454,7 +452,7 @@ void W_Seeker_Vollycontroller_Think(void) // TODO: Merge this with W_Seeker_Atta setself(this); } -void W_Seeker_Tracker_Think(void) +void W_Seeker_Tracker_Think() {SELFPARAM(); // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up if((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER.m_id) @@ -475,7 +473,7 @@ void W_Seeker_Tracker_Think(void) // ============================ // Begin: Tag projectile // ============================ -void W_Seeker_Tag_Explode(void) +void W_Seeker_Tag_Explode() {SELFPARAM(); //if(other==self.realowner) // return; @@ -493,7 +491,7 @@ void W_Seeker_Tag_Damage(entity inflictor, entity attacker, float damage, int de W_Seeker_Tag_Explode(); } -void W_Seeker_Tag_Touch(void) +void W_Seeker_Tag_Touch() {SELFPARAM(); vector dir; vector org2; @@ -524,9 +522,8 @@ void W_Seeker_Tag_Touch(void) else { //sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n")); - e = spawn(); + e = new(tag_tracker); e.cnt = WEP_CVAR(seeker, missile_count); - e.classname = "tag_tracker"; e.owner = self.owner; e.realowner = self.realowner; @@ -563,9 +560,8 @@ void W_Seeker_Fire_Tag(Weapon thiswep) W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', false, 2, SND(TAG_FIRE), CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); - missile = spawn(); + missile = new(seeker_tag); missile.owner = missile.realowner = self; - missile.classname = "seeker_tag"; missile.bot_dodge = true; missile.bot_dodgerating = 50; missile.touch = W_Seeker_Tag_Touch; @@ -608,47 +604,47 @@ void W_Seeker_Fire_Tag(Weapon thiswep) else self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), false); } - METHOD(Seeker, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(autocvar_g_balance_seeker_reload_ammo && actor.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) { // forced reload Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); - } else if(fire1) + } else if(fire & 1) { if(WEP_CVAR(seeker, type) == 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, missile_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, missile_refire))) { W_Seeker_Attack(); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready); } } else { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, tag_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire))) { W_Seeker_Fire_Tag(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); } } } - else if(fire2) + else if(fire & 2) { if(WEP_CVAR(seeker, type) == 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, tag_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire))) { W_Seeker_Fire_Tag(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); } } else { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(seeker, flac_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, flac_refire))) { W_Seeker_Fire_Flac(thiswep); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready); } } } @@ -723,7 +719,7 @@ void W_Seeker_Fire_Tag(Weapon thiswep) } else { - pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) { if(w_random<0.15) @@ -737,7 +733,7 @@ void W_Seeker_Fire_Tag(Weapon thiswep) } else { - pointparticles(particleeffectnum(EFFECT_HAGAR_EXPLODE), org2, '0 0 0', 1); + pointparticles(EFFECT_HAGAR_EXPLODE, org2, '0 0 0', 1); if(!w_issilent) { if(w_random<0.15) diff --git a/qcsrc/common/weapons/weapon/shockwave.qc b/qcsrc/common/weapons/weapon/shockwave.qc index cc4daf0486..b795cb7e9f 100644 --- a/qcsrc/common/weapons/weapon/shockwave.qc +++ b/qcsrc/common/weapons/weapon/shockwave.qc @@ -13,7 +13,7 @@ CLASS(Shockwave, Weapon) /* crosshair */ ATTRIB(Shockwave, w_crosshair_size, float, 0.7); /* wepimg */ ATTRIB(Shockwave, model2, string, "weaponshotgun"); /* refname */ ATTRIB(Shockwave, netname, string, "shockwave"); -/* wepname */ ATTRIB(Shockwave, message, string, _("Shockwave")); +/* wepname */ ATTRIB(Shockwave, m_name, string, _("Shockwave")); ENDCLASS(Shockwave) REGISTER_WEAPON(SHOCKWAVE, NEW(Shockwave)); @@ -73,7 +73,7 @@ REGISTER_WEAPON(SHOCKWAVE, NEW(Shockwave)); SHOCKWAVE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef CSQC -void Net_ReadShockwaveParticle(void); +void Net_ReadShockwaveParticle(); .vector sw_shotorg; .vector sw_shotdir; .float sw_distance; @@ -83,6 +83,9 @@ void Net_ReadShockwaveParticle(void); #endif #endif #ifdef IMPLEMENTATION + +REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE) + #ifdef SVQC spawnfunc(weapon_shockwave) { @@ -90,10 +93,10 @@ spawnfunc(weapon_shockwave) if(autocvar_sv_q3acompat_machineshotgunswap) if(self.classname != "droppedweapon") { - weapon_defaultspawnfunc(WEP_MACHINEGUN.m_id); + weapon_defaultspawnfunc(this, WEP_MACHINEGUN); return; } - weapon_defaultspawnfunc(WEP_SHOCKWAVE.m_id); + weapon_defaultspawnfunc(this, WEP_SHOCKWAVE); } const float MAX_SHOCKWAVE_HITS = 10; @@ -107,7 +110,7 @@ float shockwave_hit_damage[MAX_SHOCKWAVE_HITS]; vector shockwave_hit_force[MAX_SHOCKWAVE_HITS]; // MELEE ATTACK MODE -void W_Shockwave_Melee_Think(void) +void W_Shockwave_Melee_Think() {SELFPARAM(); // declarations float i, f, swing, swing_factor, swing_damage, meleetime, is_player; @@ -229,13 +232,13 @@ void W_Shockwave_Melee_Think(void) } } -void W_Shockwave_Melee(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Shockwave_Melee(Weapon thiswep, entity actor, .entity weaponentity, int fire) { sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTN_NORM); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready); - entity meleetemp; - meleetemp = spawn(); + entity meleetemp = new(meleetemp); + make_pure(meleetemp); meleetemp.owner = meleetemp.realowner = actor; meleetemp.think = W_Shockwave_Melee_Think; meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor(); @@ -335,10 +338,9 @@ float W_Shockwave_Attack_CheckHit( return true; } -void W_Shockwave_Send(void) +void W_Shockwave_Send() {SELFPARAM(); - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE); + WriteHeader(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE); WriteCoord(MSG_BROADCAST, w_shotorg.x); WriteCoord(MSG_BROADCAST, w_shotorg.y); WriteCoord(MSG_BROADCAST, w_shotorg.z); @@ -351,7 +353,7 @@ void W_Shockwave_Send(void) WriteByte(MSG_BROADCAST, num_for_edict(self)); } -void W_Shockwave_Attack(void) +void W_Shockwave_Attack() {SELFPARAM(); // declarations float multiplier, multiplier_from_accuracy, multiplier_from_distance; @@ -675,28 +677,28 @@ void W_Shockwave_Attack(void) else { self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); } } - METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - if(fire1) + if(fire & 1) { if(time >= actor.shockwave_blasttime) // handle refire separately so the secondary can be fired straight after a primary { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(shockwave, blast_animtime))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(shockwave, blast_animtime))) { W_Shockwave_Attack(); actor.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready); } } } - else if(fire2) + else if(fire & 2) { //if(actor.clip_load >= 0) // we are not currently reloading if(!actor.crouch) // no crouchmelee please - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR(shockwave, melee_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(shockwave, melee_refire))) { // attempt forcing playback of the anim by switching to another anim (that we never play) here... - weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shockwave_Melee); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, W_Shockwave_Melee); } } } @@ -833,7 +835,13 @@ void Draw_Shockwave(entity this) } } -void Net_ReadShockwaveParticle(void) +NET_HANDLE(TE_CSQC_SHOCKWAVEPARTICLE, bool isNew) +{ + Net_ReadShockwaveParticle(); + return true; +} + +void Net_ReadShockwaveParticle() { entity shockwave; shockwave = spawn(); @@ -856,7 +864,7 @@ void Net_ReadShockwaveParticle(void) // handled by Net_ReadShockwaveParticle //vector org2; //org2 = w_org + w_backoff * 2; - //pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1); + //pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1); } #endif diff --git a/qcsrc/common/weapons/weapon/shotgun.qc b/qcsrc/common/weapons/weapon/shotgun.qc index a5cb397737..e7ad8b0420 100644 --- a/qcsrc/common/weapons/weapon/shotgun.qc +++ b/qcsrc/common/weapons/weapon/shotgun.qc @@ -13,7 +13,7 @@ CLASS(Shotgun, Weapon) /* crosshair */ ATTRIB(Shotgun, w_crosshair_size, float, 0.65); /* wepimg */ ATTRIB(Shotgun, model2, string, "weaponshotgun"); /* refname */ ATTRIB(Shotgun, netname, string, "shotgun"); -/* wepname */ ATTRIB(Shotgun, message, string, _("Shotgun")); +/* wepname */ ATTRIB(Shotgun, m_name, string, _("Shotgun")); ENDCLASS(Shotgun) REGISTER_WEAPON(SHOTGUN, NEW(Shotgun)); @@ -54,7 +54,7 @@ SHOTGUN_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(WEP_SHOTGUN.m_id); } +spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); } void W_Shotgun_Attack(Weapon thiswep, float isprimary) {SELFPARAM(); @@ -85,7 +85,7 @@ void W_Shotgun_Attack(Weapon thiswep, float isprimary) .float swing_prev; .entity swing_alreadyhit; -void W_Shotgun_Melee_Think(void) +void W_Shotgun_Melee_Think() {SELFPARAM(); // declarations float i, f, swing, swing_factor, swing_damage, meleetime, is_player; @@ -181,13 +181,13 @@ void W_Shotgun_Melee_Think(void) } } -void W_Shotgun_Attack2(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Shotgun_Attack2(Weapon thiswep, entity actor, .entity weaponentity, int fire) { sound(actor, CH_WEAPON_A, SND_SHOTGUN_MELEE, VOL_BASE, ATTEN_NORM); - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready); - entity meleetemp; - meleetemp = spawn(); + entity meleetemp = new(meleetemp); + make_pure(meleetemp); meleetemp.realowner = actor; meleetemp.think = W_Shotgun_Melee_Think; meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor(); @@ -195,34 +195,34 @@ void W_Shotgun_Attack2(Weapon thiswep, entity actor, bool fire1, bool fire2) } // alternate secondary weapon frames -void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire) { Weapon w = get_weaponinfo(actor.weapon); if (!w.wr_checkammo2(w)) if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound W_Shotgun_Attack(WEP_SHOTGUN, true); // actually is secondary, but we trick the last shot into playing full reload sound - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready); } -void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire) { Weapon w = get_weaponinfo(actor.weapon); if (!w.wr_checkammo2(w)) if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); - w_ready(thiswep, actor, fire1, fire2); + w_ready(thiswep, actor, weaponentity, fire); return; } W_Shotgun_Attack(WEP_SHOTGUN, false); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2); } .float shotgun_primarytime; @@ -234,7 +234,7 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fir else self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, false); } - METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(shotgun, reload_ammo) && actor.clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload { @@ -246,27 +246,27 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fir } else { - if(fire1) + if(fire & 1) { if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(shotgun, animtime))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime))) { W_Shotgun_Attack(thiswep, true); actor.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready); } } } - else if(fire2 && WEP_CVAR(shotgun, secondary) == 2) + else if((fire & 2) && WEP_CVAR(shotgun, secondary) == 2) { if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_SEC(shotgun, alt_animtime))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime))) { W_Shotgun_Attack(thiswep, false); actor.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1); } } } @@ -274,11 +274,11 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fir if(actor.clip_load >= 0) // we are not currently reloading if(!actor.crouch) // no crouchmelee please if(WEP_CVAR(shotgun, secondary) == 1) - if((fire1 && actor.WEP_AMMO(SHOTGUN) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || fire2) - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR_SEC(shotgun, refire))) + if(((fire & 1) && actor.WEP_AMMO(SHOTGUN) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2)) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire))) { // attempt forcing playback of the anim by switching to another anim (that we never play) here... - weapon_thinkf(actor, WFRAME_FIRE1, 0, W_Shotgun_Attack2); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, W_Shotgun_Attack2); } } METHOD(Shotgun, wr_init, void(entity thiswep)) @@ -339,15 +339,11 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, bool fire1, bool fir METHOD(Shotgun, wr_impacteffect, void(entity thiswep)) { vector org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum(EFFECT_SHOTGUN_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_SHOTGUN_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent && time - self.prevric > 0.25) { - if(w_random < 0.0165) - sound(self, CH_SHOTS, SND_RIC1, VOL_BASE, ATTEN_NORM); - else if(w_random < 0.033) - sound(self, CH_SHOTS, SND_RIC2, VOL_BASE, ATTEN_NORM); - else if(w_random < 0.05) - sound(self, CH_SHOTS, SND_RIC3, VOL_BASE, ATTEN_NORM); + if(w_random < 0.05) + sound(self, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); self.prevric = time; } } diff --git a/qcsrc/common/weapons/weapon/tuba.qc b/qcsrc/common/weapons/weapon/tuba.qc index 66ea8f7b51..308d43fc12 100644 --- a/qcsrc/common/weapons/weapon/tuba.qc +++ b/qcsrc/common/weapons/weapon/tuba.qc @@ -14,7 +14,7 @@ CLASS(Tuba, Weapon) /* wepimg */ ATTRIB(Tuba, model2, string, "weapontuba"); /* refname */ ATTRIB(Tuba, netname, string, "tuba"); /* xgettext:no-c-format */ -/* wepname */ ATTRIB(Tuba, message, string, _("@!#%'n Tuba")); +/* wepname */ ATTRIB(Tuba, m_name, string, _("@!#%'n Tuba")); ENDCLASS(Tuba) REGISTER_WEAPON(TUBA, NEW(Tuba)); @@ -56,7 +56,7 @@ float W_Tuba_MarkClientOnlyFieldsAsUsed() { #endif #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(WEP_TUBA.m_id); } +spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(this, WEP_TUBA); } bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo) { @@ -149,7 +149,7 @@ bool W_Tuba_HasPlayed(entity pl, string melody, int instrument, bool ignorepitch return true; } -void W_Tuba_NoteOff(void) +void W_Tuba_NoteOff() {SELFPARAM(); // we have a note: // on: self.spawnshieldtime @@ -261,7 +261,7 @@ bool W_Tuba_NoteSendEntity(entity this, entity to, int sf) if(!sound_allowed(MSG_ONE, self.realowner)) return false; - WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TUBANOTE); WriteByte(MSG_ENTITY, sf); if(sf & 1) { @@ -281,7 +281,7 @@ bool W_Tuba_NoteSendEntity(entity this, entity to, int sf) return true; } -void W_Tuba_NoteThink(void) +void W_Tuba_NoteThink() {SELFPARAM(); float dist_mult; float vol0, vol1; @@ -379,25 +379,25 @@ void W_Tuba_NoteOn(float hittype) self.BUTTON_ATCK2 = 1; } } - METHOD(Tuba, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Tuba, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - if(fire1) - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR(tuba, refire))) + if(fire & 1) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(tuba, refire))) { W_Tuba_NoteOn(0); //weapon_thinkf(actor, WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready); - weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); } - if(fire2) - if(weapon_prepareattack(thiswep, actor, true, WEP_CVAR(tuba, refire))) + if(fire & 2) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(tuba, refire))) { W_Tuba_NoteOn(HITTYPE_SECONDARY); //weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready); - weapon_thinkf(actor, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); } if(actor.tuba_note) { - if(!fire1 && !fire2) + if(!(fire & 1) && !(fire & 2)) { WITH(entity, self, actor.tuba_note, W_Tuba_NoteOff()); } @@ -414,8 +414,9 @@ void W_Tuba_NoteOn(float hittype) } METHOD(Tuba, wr_reload, void(entity thiswep)) { + .entity weaponentity = weaponentities[0]; // TODO: unhardcode // switch to alternate instruments :) - if(self.weaponentity.state == WS_READY) + if(self.(weaponentity).state == WS_READY) { switch(self.tuba_instrument) { @@ -434,8 +435,8 @@ void W_Tuba_NoteOn(float hittype) } W_SetupShot(self, false, 0, "", 0, 0); Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1); - self.weaponentity.state = WS_INUSE; - weapon_thinkf(self, WFRAME_RELOAD, 0.5, w_ready); + self.(weaponentity).state = WS_INUSE; + weapon_thinkf(self, weaponentity, WFRAME_RELOAD, 0.5, w_ready); } } METHOD(Tuba, wr_checkammo1, bool(entity thiswep)) diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index f7aceee20f..dc449d35d0 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -13,7 +13,7 @@ CLASS(Vaporizer, Weapon) /* crosshair */ ATTRIB(Vaporizer, w_crosshair_size, float, 0.6); /* wepimg */ ATTRIB(Vaporizer, model2, string, "weaponminstanex"); /* refname */ ATTRIB(Vaporizer, netname, string, "vaporizer"); -/* wepname */ ATTRIB(Vaporizer, message, string, _("Vaporizer")); +/* wepname */ ATTRIB(Vaporizer, m_name, string, _("Vaporizer")); ENDCLASS(Vaporizer) REGISTER_WEAPON(VAPORIZER, NEW(Vaporizer)); @@ -56,8 +56,101 @@ VAPORIZER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #endif #ifdef IMPLEMENTATION + +REGISTER_NET_TEMP(TE_CSQC_VAPORBEAMPARTICLE) + +#if defined(SVQC) +void SendCSQCVaporizerBeamParticle(entity player, int hit) { + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + WriteHeader(MSG_BROADCAST, TE_CSQC_VAPORBEAMPARTICLE); + WriteCoord(MSG_BROADCAST, w_shotorg.x); + WriteCoord(MSG_BROADCAST, w_shotorg.y); + WriteCoord(MSG_BROADCAST, w_shotorg.z); + WriteCoord(MSG_BROADCAST, v.x); + WriteCoord(MSG_BROADCAST, v.y); + WriteCoord(MSG_BROADCAST, v.z); + WriteByte(MSG_BROADCAST, hit); + WriteShort(MSG_BROADCAST, num_for_edict(player)); + WriteByte(MSG_BROADCAST, player.team); +} +#elif defined(CSQC) +bool autocvar_cl_vaporizerbeam_particle = false; +float autocvar_cl_vaporizerbeam_lifetime = 0.8; +float autocvar_cl_vaporizerbeam_colorboost = 0.7; + +string Draw_VaporizerBeam_trace_callback_tex; +float Draw_VaporizerBeam_trace_callback_rnd; +vector Draw_VaporizerBeam_trace_callback_rgb; +float Draw_VaporizerBeam_trace_callback_a; +void Draw_VaporizerBeam_trace_callback(vector start, vector hit, vector end) +{ + float i; + vector vorg; + vorg = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin); + for(i = 0; i < Draw_VaporizerBeam_trace_callback_a; ++i) + Draw_CylindricLine(hit, start, 8, Draw_VaporizerBeam_trace_callback_tex, 0.25, Draw_VaporizerBeam_trace_callback_rnd, Draw_VaporizerBeam_trace_callback_rgb, min(1, Draw_VaporizerBeam_trace_callback_a - i), DRAWFLAG_NORMAL, vorg); + Draw_VaporizerBeam_trace_callback_rnd += 0.25 * vlen(hit - start) / 8; +} + +.vector vorg1, vorg2; +.float spawn_time; +void VaporizerBeam_Draw(entity this) +{ + //draw either the old v2.3 beam or the new beam + particles_alphamin = particles_alphamax = particles_fade = 1; + + string tex = "particles/lgbeam"; + if(this.cnt) + tex = "particles/gauntletbeam"; + vector rgb = getcsqcplayercolor(this.sv_entnum); + rgb *= (1 + autocvar_cl_vaporizerbeam_colorboost); + + float fail = (self.nextthink - time); + + Draw_VaporizerBeam_trace_callback_tex = tex; + Draw_VaporizerBeam_trace_callback_rnd = 0; + Draw_VaporizerBeam_trace_callback_rgb = rgb; + Draw_VaporizerBeam_trace_callback_a = bound(0, fail, 1); + WarpZone_TraceBox_ThroughZone(this.vorg1, '0 0 0', '0 0 0', this.vorg2, MOVE_NOTHING, world, world, Draw_VaporizerBeam_trace_callback); + Draw_VaporizerBeam_trace_callback_tex = string_null; + + /*if(!MUTATOR_CALLHOOK(Particles_VaporizerBeam, this.vorg1, this.vorg2)) + if(autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) + WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), this.vorg1, this.vorg2, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); + else + WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), this.vorg1, this.vorg2, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);*/ +} + +NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew) +{ + Net_Accept(vortex_beam); + this.think = SUB_Remove; + this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10); + this.draw = VaporizerBeam_Draw; + this.drawmask = MASK_NORMAL; + + this.vorg1_x = ReadCoord(); this.vorg1_y = ReadCoord(); this.vorg1_z = ReadCoord(); + this.vorg2_x = ReadCoord(); this.vorg2_y = ReadCoord(); this.vorg2_z = ReadCoord(); + this.cnt = ReadByte(); + this.sv_entnum = ReadShort(); + this.team = ReadByte() - 1; + + if(autocvar_cl_vaporizerbeam_particle) + { + WarpZone_TrailParticles(world, particleeffectnum(((this.cnt) ? EFFECT_VAPORIZER_HIT(this.team) : EFFECT_VAPORIZER(this.team))), this.vorg1, this.vorg2); + this.draw = func_null; + this.drawmask = MASK_NORMAL; + remove(this); + } + + pointparticles(EFFECT_VORTEX_MUZZLEFLASH, this.vorg1, normalize(this.vorg2 - this.vorg1) * 1000, 1); + return true; +} +#endif + #ifdef SVQC -spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(WEP_VAPORIZER.m_id); } +spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(this, WEP_VAPORIZER); } spawnfunc(weapon_minstanex) { spawnfunc_weapon_vaporizer(this); } void W_RocketMinsta_Explosion(vector loc) @@ -85,6 +178,9 @@ void W_Vaporizer_Attack(Weapon thiswep) damage_goodhits = 0; FireRailgunBullet(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, vaporizer_damage, 800, 0, 0, 0, 0, WEP_VAPORIZER.m_id); + // do this now, as goodhits is disabled below + SendCSQCVaporizerBeamParticle(self, damage_goodhits); + if(yoda && flying) Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); if(damage_goodhits && self.vaporizer_lasthit) @@ -95,12 +191,6 @@ void W_Vaporizer_Attack(Weapon thiswep) self.vaporizer_lasthit = damage_goodhits; - Send_Effect(EFFECT_VORTEX_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - - // teamcolor / hit beam effect - vector v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); - Send_Effect((damage_goodhits ? EFFECT_VAPORIZER_HIT(self.team) : EFFECT_VAPORIZER(self.team)), w_shotorg, v, 1); - if(autocvar_g_rm) if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) W_RocketMinsta_Explosion(trace_endpos); @@ -108,7 +198,7 @@ void W_Vaporizer_Attack(Weapon thiswep) W_DecreaseAmmo(thiswep, self, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo))); } -void W_RocketMinsta_Laser_Explode (void) +void W_RocketMinsta_Laser_Explode () {SELFPARAM(); if(other.takedamage == DAMAGE_AIM) if(IS_PLAYER(other)) @@ -123,7 +213,7 @@ void W_RocketMinsta_Laser_Explode (void) remove(self); } -void W_RocketMinsta_Laser_Touch (void) +void W_RocketMinsta_Laser_Touch () {SELFPARAM(); PROJECTILE_TOUCH; //W_RocketMinsta_Laser_Explode (); @@ -131,7 +221,7 @@ void W_RocketMinsta_Laser_Touch (void) remove(self); } -void W_RocketMinsta_Attack2(void) +void W_RocketMinsta_Attack2() {SELFPARAM(); makevectors(self.v_angle); @@ -150,8 +240,7 @@ void W_RocketMinsta_Attack2(void) while(counter < total) { - proj = spawn (); - proj.classname = "plasma_prim"; + proj = new(plasma_prim); proj.owner = proj.realowner = self; proj.bot_dodge = true; proj.bot_dodgerating = autocvar_g_rm_laser_damage; @@ -186,7 +275,7 @@ void W_RocketMinsta_Attack2(void) } } -void W_RocketMinsta_Attack3 (void) +void W_RocketMinsta_Attack3 () {SELFPARAM(); makevectors(self.v_angle); @@ -203,8 +292,7 @@ void W_RocketMinsta_Attack3 (void) while(counter < total) { - proj = spawn (); - proj.classname = "plasma_prim"; + proj = new(plasma_prim); proj.owner = proj.realowner = self; proj.bot_dodge = true; proj.bot_dodgerating = autocvar_g_rm_laser_damage; @@ -244,7 +332,7 @@ void W_RocketMinsta_Attack3 (void) else self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), false); // WEAPONTODO: replace with proper vaporizer cvars } - METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); // if the laser uses load, we also consider its ammo for reloading @@ -255,15 +343,15 @@ void W_RocketMinsta_Attack3 (void) Weapon w = get_weaponinfo(actor.weapon); w.wr_reload(w); } - if(fire1 && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor)) + if((fire & 1) && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor)) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(vaporizer, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire))) { W_Vaporizer_Attack(thiswep); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); } } - if(fire2 || (fire1 && !actor.ammo_cells && autocvar_g_rm)) + if((fire & 2) || ((fire & 1) && !actor.ammo_cells && autocvar_g_rm)) { if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2) { @@ -314,7 +402,7 @@ void W_RocketMinsta_Attack3 (void) actor.weapon = oldwep; // now do normal refire - weapon_thinkf(actor, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready); } } else @@ -381,12 +469,12 @@ void W_RocketMinsta_Attack3 (void) vector org2 = w_org + w_backoff * 6; if(w_deathtype & HITTYPE_SECONDARY) { - pointparticles(particleeffectnum(EFFECT_BLASTER_IMPACT), org2, w_backoff * 1000, 1); + pointparticles(EFFECT_BLASTER_IMPACT, org2, w_backoff * 1000, 1); if(!w_issilent) { sound(self, CH_SHOTS, SND_LASERIMPACT, VOL_BASE, ATTN_NORM); } } else { - pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1); + pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1); if(!w_issilent) { sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM); } } } diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index 5aca18183e..8a90679b81 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -13,7 +13,7 @@ CLASS(Vortex, Weapon) /* crosshair */ ATTRIB(Vortex, w_crosshair_size, float, 0.65); /* wepimg */ ATTRIB(Vortex, model2, string, "weaponnex"); /* refname */ ATTRIB(Vortex, netname, string, "vortex"); -/* wepname */ ATTRIB(Vortex, message, string, _("Vortex")); +/* wepname */ ATTRIB(Vortex, m_name, string, _("Vortex")); ENDCLASS(Vortex) REGISTER_WEAPON(VORTEX, NEW(Vortex)); @@ -60,15 +60,14 @@ VORTEX_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) #endif #endif #ifdef IMPLEMENTATION -#ifdef SVQC -spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(WEP_VORTEX.m_id); } -spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); } +REGISTER_NET_TEMP(TE_CSQC_VORTEXBEAMPARTICLE) + +#if defined(SVQC) void SendCSQCVortexBeamParticle(float charge) { vector v; v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_VORTEXBEAMPARTICLE); + WriteHeader(MSG_BROADCAST, TE_CSQC_VORTEXBEAMPARTICLE); WriteCoord(MSG_BROADCAST, w_shotorg.x); WriteCoord(MSG_BROADCAST, w_shotorg.y); WriteCoord(MSG_BROADCAST, w_shotorg.z); @@ -77,6 +76,33 @@ void SendCSQCVortexBeamParticle(float charge) { WriteCoord(MSG_BROADCAST, v.z); WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255)); } +#elif defined(CSQC) +NET_HANDLE(TE_CSQC_VORTEXBEAMPARTICLE, bool isNew) +{ + vector shotorg, endpos; + float charge; + shotorg.x = ReadCoord(); shotorg.y = ReadCoord(); shotorg.z = ReadCoord(); + endpos.x = ReadCoord(); endpos.y = ReadCoord(); endpos.z = ReadCoord(); + charge = ReadByte() / 255.0; + + pointparticles(EFFECT_VORTEX_MUZZLEFLASH, shotorg, normalize(endpos - shotorg) * 1000, 1); + + //draw either the old v2.3 beam or the new beam + charge = sqrt(charge); // divide evenly among trail spacing and alpha + particles_alphamin = particles_alphamax = particles_fade = charge; + + if(!MUTATOR_CALLHOOK(Particles_VortexBeam, shotorg, endpos)) + if(autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) + WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM_OLD), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); + else + WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum(EFFECT_VORTEX_BEAM), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); + return true; +} +#endif + +#ifdef SVQC +spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(this, WEP_VORTEX); } +spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); } void W_Vortex_Attack(Weapon thiswep, float issecondary) {SELFPARAM(); @@ -143,7 +169,7 @@ void W_Vortex_Attack(Weapon thiswep, float issecondary) self.BUTTON_ATCK2 = true; } } - METHOD(Vortex, wr_think, void(entity thiswep, entity actor, bool fire1, bool fire2)) + METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(vortex, charge) && actor.vortex_charge < WEP_CVAR(vortex, charge_limit)) actor.vortex_charge = min(1, actor.vortex_charge + WEP_CVAR(vortex, charge_rate) * frametime / W_TICSPERFRAME); @@ -161,15 +187,15 @@ void W_Vortex_Attack(Weapon thiswep, float issecondary) w.wr_reload(w); } else { - if(fire1) + if(fire & 1) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_PRI(vortex, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vortex, refire))) { W_Vortex_Attack(thiswep, 0); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready); } } - if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (actor.BUTTON_ZOOM | actor.BUTTON_ZOOMSCRIPT) : fire2) + if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (actor.BUTTON_ZOOM | actor.BUTTON_ZOOMSCRIPT) : (fire & 2)) { if(WEP_CVAR(vortex, charge)) { @@ -196,7 +222,7 @@ void W_Vortex_Attack(Weapon thiswep, float issecondary) else if(WEP_CVAR_SEC(vortex, ammo)) { - if(fire2) // only eat ammo when the button is pressed + if(fire & 2) // only eat ammo when the button is pressed { dt = min(dt, (1 - actor.vortex_charge) / WEP_CVAR(vortex, charge_rate)); if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) @@ -235,10 +261,10 @@ void W_Vortex_Attack(Weapon thiswep, float issecondary) } else if(WEP_CVAR(vortex, secondary)) { - if(weapon_prepareattack(thiswep, actor, false, WEP_CVAR_SEC(vortex, refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(vortex, refire))) { W_Vortex_Attack(thiswep, 1); - weapon_thinkf(actor, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready); } } } @@ -306,7 +332,7 @@ float autocvar_g_balance_vortex_secondary = 0; // WEAPONTODO METHOD(Vortex, wr_impacteffect, void(entity thiswep)) { vector org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum(EFFECT_VORTEX_IMPACT), org2, '0 0 0', 1); + pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1); if(!w_issilent) sound(self, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM); } diff --git a/qcsrc/dpdefs/csprogsdefs.qh b/qcsrc/dpdefs/csprogsdefs.qh index 6ad27242aa..b9bcec58e6 100644 --- a/qcsrc/dpdefs/csprogsdefs.qh +++ b/qcsrc/dpdefs/csprogsdefs.qh @@ -10,6 +10,8 @@ #define spawn _spawn #define particleeffectnum _particleeffectnum +#define trailparticles __trailparticles +#define pointparticles __pointparticles #define setmodel _setmodel #include "upstream/csprogsdefs.qc" @@ -21,10 +23,10 @@ #undef spawn #undef particleeffectnum +#undef trailparticles +#undef pointparticles #undef setmodel #pragma noref 0 -#define ReadFloat() ReadCoord() - #endif diff --git a/qcsrc/dpdefs/dpextensions.qh b/qcsrc/dpdefs/dpextensions.qh index 7b437082f1..ff9214844b 100644 --- a/qcsrc/dpdefs/dpextensions.qh +++ b/qcsrc/dpdefs/dpextensions.qh @@ -4,10 +4,14 @@ #pragma noref 1 #define particleeffectnum __particleeffectnum +#define trailparticles __trailparticles +#define pointparticles __pointparticles #include "upstream/dpextensions.qc" #undef particleeffectnum +#undef trailparticles +#undef pointparticles int(entity ent, string tagname) _gettagindex = #451; #define gettagindex _gettagindex diff --git a/qcsrc/dpdefs/progsdefs.qh b/qcsrc/dpdefs/progsdefs.qh index 5a93a2444c..535c3b6f7c 100644 --- a/qcsrc/dpdefs/progsdefs.qh +++ b/qcsrc/dpdefs/progsdefs.qh @@ -23,6 +23,4 @@ #pragma noref 0 -#define WriteFloat(to, f) WriteCoord(to, f) - #endif diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 9bc9ca2aef..ceb59b780a 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -1,28 +1,28 @@ #ifndef NOCOMPAT - #define COMPAT_NO_MOD_IS_XONOTIC + #define COMPAT_NO_MOD_IS_XONOTIC #endif #include "compiler.qh" #ifndef QCC_SUPPORT_INT - #define int float + #define int float #endif #ifndef QCC_SUPPORT_BOOL - #define bool float + #define bool float #endif #if defined(CSQC) - #include "../dpdefs/csprogsdefs.qh" - #include "../dpdefs/keycodes.qh" + #include "../dpdefs/csprogsdefs.qh" + #include "../dpdefs/keycodes.qh" #elif defined(SVQC) - #include "../server/sys-pre.qh" - #include "../dpdefs/progsdefs.qh" - #include "../dpdefs/dpextensions.qh" - #include "../server/sys-post.qh" + #include "../server/sys-pre.qh" + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../server/sys-post.qh" #elif defined(MENUQC) - #include "../dpdefs/menudefs.qh" - #include "../dpdefs/keycodes.qh" + #include "../dpdefs/menudefs.qh" + #include "../dpdefs/keycodes.qh" #endif #include "warpzone/mathlib.qc" @@ -43,6 +43,7 @@ #include "lazy.qh" #include "linkedlist.qh" #include "log.qh" +#include "map.qc" #include "math.qh" #include "misc.qh" #include "net.qh" @@ -54,12 +55,14 @@ #include "progname.qh" #include "random.qc" #include "registry.qh" +#include "registry_net.qh" #include "replicate.qh" #include "self.qh" #include "sortlist.qc" #include "sort.qh" #include "spawnfunc.qh" #include "static.qh" +#include "stats.qh" #include "string.qh" #include "struct.qh" #include "test.qc" diff --git a/qcsrc/lib/accumulate.qh b/qcsrc/lib/accumulate.qh index 7e1a1edc0f..e3d17b9f26 100644 --- a/qcsrc/lib/accumulate.qh +++ b/qcsrc/lib/accumulate.qh @@ -2,51 +2,59 @@ #define ACCUMULATE_H #ifdef QCC_SUPPORT_ACCUMULATE -# define ACCUMULATE_FUNCTION(func, otherfunc) \ - [[accumulate]] void func() { otherfunc(); } -# define CALL_ACCUMULATED_FUNCTION(func) \ - func() + #define ACCUMULATE_FUNCTION(func, otherfunc) \ + [[accumulate]] void func() \ + { \ + otherfunc(); \ + } + #define CALL_ACCUMULATED_FUNCTION(func) \ + func() #else -#ifdef HAVE_YO_DAWG_CPP + #ifdef HAVE_YO_DAWG_CPP // TODO make ascii art pic of xzibit // YO DAWG! // I HERD YO LIEK MACROS // SO I PUT A MACRO DEFINITION IN YO MACRO DEFINITION // SO YO CAN EXPAND MACROS WHILE YO EXPAND MACROS -# define ACCUMULATE_FUNCTION(func,otherfunc) \ - #ifdef func \ - void __merge__##otherfunc() { func(); otherfunc(); } \ - #undef func \ - #define func __merge__##otherfunc \ - #else \ - #define func otherfunc \ - #endif -# define CALL_ACCUMULATED_FUNCTION(func) \ - func() -#else -# define ACCUMULATE_FUNCTION(func,otherfunc) \ - .float _ACCUMULATE_##func##__##otherfunc; -void ACCUMULATE_call(string func) -{ - float i; - float n = numentityfields(); - string funcprefix = strcat("_ACCUMULATE_", func, "__"); - float funcprefixlen = strlen(funcprefix); - for(i = 0; i < n; ++i) - { - string name = entityfieldname(i); - if(substring(name, 0, funcprefixlen) == funcprefix) - callfunction(substring(name, funcprefixlen, -1)); - } -} -# define CALL_ACCUMULATED_FUNCTION(func) \ - ACCUMULATE_call(#func) -#endif + #define ACCUMULATE_FUNCTION(func, otherfunc) \ + #ifdef func \ + void __merge__##otherfunc() \ + { \ + func(); otherfunc(); \ + } \ + #undef func \ + #define func __merge__##otherfunc \ + #else \ + #define func otherfunc \ + #endif + #define CALL_ACCUMULATED_FUNCTION(func) \ + func() + #else + #define ACCUMULATE_FUNCTION(func, otherfunc) \ + .float _ACCUMULATE_##func##__##otherfunc; + void ACCUMULATE_call(string func) + { + float i; + float n = numentityfields(); + string funcprefix = strcat("_ACCUMULATE_", func, "__"); + float funcprefixlen = strlen(funcprefix); + for (i = 0; i < n; ++i) + { + string name = entityfieldname(i); + if (substring(name, 0, funcprefixlen) == funcprefix) callfunction(substring(name, funcprefixlen, -1)); + } + } + #define CALL_ACCUMULATED_FUNCTION(func) \ + ACCUMULATE_call( #func) + #endif #endif // used for simplifying ACCUMULATE_FUNCTIONs -#define SET_FIRST_OR_LAST(input,first,count) if(!input) { input = (first + count); } -#define SET_FIELD_COUNT(field,first,count) if(!field) { field = (first + count); ++count; } -#define CHECK_MAX_COUNT(name,max,count,type) if(count > max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); } +#define SET_FIRST_OR_LAST(input, first, count) \ + if (!input) { input = (first + count); } +#define SET_FIELD_COUNT(field, first, count) \ + if (!field) { field = (first + count); ++count; } +#define CHECK_MAX_COUNT(name, max, count, type) \ + if (count > max) { error(strcat("Maximum ", type, " hit: ", #name, ": ", ftos(count), ".\n")); } #endif diff --git a/qcsrc/lib/bits.qh b/qcsrc/lib/bits.qh index 86b5df5970..001984a632 100644 --- a/qcsrc/lib/bits.qh +++ b/qcsrc/lib/bits.qh @@ -1,22 +1,125 @@ #ifndef BITS_H #define BITS_H +#include "log.qh" #define BIT(n) (1 << (n)) #define BITS(n) (BIT(n) - 1) #ifndef BRANCHLESS_BITSET - #define BITSET(var, mask, flag) (flag ? (var) | (mask) : (var) &~ (mask)) + #define BITSET(var, mask, flag) (flag ? (var) | (mask) : (var) & ~(mask)) #else - #define BITSET(var, mask, flag) ((var) ^ (-(flag) ^ (var)) & (mask)) + #define BITSET(var, mask, flag) ((var) ^ (-(flag) ^ (var)) & (mask)) #endif int lowestbit(int f) { - f &= ~(f << 1); - f &= ~(f << 2); - f &= ~(f << 4); - f &= ~(f << 8); - f &= ~(f << 16); - return f; + f &= ~(f << 1); + f &= ~(f << 2); + f &= ~(f << 4); + f &= ~(f << 8); + f &= ~(f << 16); + return f; +} + +int randombit(int bits) +{ + if (!(bits & (bits - 1))) // this ONLY holds for powers of two! + return bits; + + int r = random(); + int b = 0; + int n = 0; + + for (int f = 1; f <= bits; f *= 2) + { + if (bits & f) + { + ++n; + r *= n; + if (r <= 1) b = f; + else r = (r - 1) / (n - 1); + } + } + return b; +} + +int randombits(int bits, int k, bool error_return) +{ + int r = 0; + while (k > 0 && bits != r) + { + r += randombit(bits - r); + --k; + } + if (error_return) + if (k > 0) return -1; + // all + return r; +} + +void randombit_test(int bits, int iter) +{ + while (iter > 0) + { + LOG_INFO(ftos(randombit(bits)), "\n"); + --iter; + } +} + +enum { + OP_SET, + OP_MIN, + OP_MAX, + OP_PLUS, + OP_MINUS +}; + +bool GiveBit(entity e, .int fld, int bit, int op, int val) +{ + int v0 = (e.(fld) & bit); + switch (op) + { + case OP_SET: + if (val > 0) e.(fld) |= bit; + else e.(fld) &= ~bit; + break; + case OP_MIN: + case OP_PLUS: + if (val > 0) e.(fld) |= bit; + break; + case OP_MAX: + if (val <= 0) e.(fld) &= ~bit; + break; + case OP_MINUS: + if (val > 0) e.(fld) &= ~bit; + break; + } + int v1 = (e.(fld) & bit); + return v0 != v1; +} + +bool GiveValue(entity e, .int fld, int op, int val) +{ + int v0 = e.(fld); + switch (op) + { + case OP_SET: + e.(fld) = val; + break; + case OP_MIN: + e.(fld) = max(e.(fld), val); // min 100 cells = at least 100 cells + break; + case OP_MAX: + e.(fld) = min(e.(fld), val); + break; + case OP_PLUS: + e.(fld) += val; + break; + case OP_MINUS: + e.(fld) -= val; + break; + } + int v1 = e.(fld); + return v0 != v1; } #endif diff --git a/qcsrc/lib/bool.qh b/qcsrc/lib/bool.qh index 7a719af409..5510c171cf 100644 --- a/qcsrc/lib/bool.qh +++ b/qcsrc/lib/bool.qh @@ -2,36 +2,34 @@ #define BOOL_H #ifndef QCC_SUPPORT_BOOL - // Boolean Constants - const int true = 1; - const int false = 0; + // Boolean Constants + const int true = 1; + const int false = 0; #endif // Transitional aliases -[[deprecated("use true")]] [[alias("true")]] const bool TRUE; -[[deprecated("use false")]] [[alias("false")]] const bool FALSE; +[[deprecated("use true")]][[alias("true")]] const bool TRUE; +[[deprecated("use false")]][[alias("false")]] const bool FALSE; + +#define boolean(value) ((value) != 0) // get true/false value of a string with multiple different inputs float InterpretBoolean(string input) { - switch (strtolower(input)) - { - case "yes": - case "true": - case "on": - return true; - - case "no": - case "false": - case "off": - return false; + switch (strtolower(input)) + { + case "yes": + case "true": + case "on": + return true; - default: return stof(input); - } -} + case "no": + case "false": + case "off": + return false; -float boolean(float value) { // if value is 0 return false (0), otherwise return true (1) - return (value == 0) ? false : true; + default: return boolean(stof(input)); + } } #endif diff --git a/qcsrc/lib/color.qh b/qcsrc/lib/color.qh index d21dab84ea..442d8ee89e 100644 --- a/qcsrc/lib/color.qh +++ b/qcsrc/lib/color.qh @@ -1,38 +1,180 @@ #ifndef COLOR_H #define COLOR_H +#include "string.qh" + #define colormapPaletteColor(c, isPants) colormapPaletteColor_(c, isPants, time) vector colormapPaletteColor_(int c, bool isPants, float t) { - switch (c) { - case 0: return '1.000000 1.000000 1.000000'; - case 1: return '1.000000 0.333333 0.000000'; - case 2: return '0.000000 1.000000 0.501961'; - case 3: return '0.000000 1.000000 0.000000'; - case 4: return '1.000000 0.000000 0.000000'; - case 5: return '0.000000 0.666667 1.000000'; - case 6: return '0.000000 1.000000 1.000000'; - case 7: return '0.501961 1.000000 0.000000'; - case 8: return '0.501961 0.000000 1.000000'; - case 9: return '1.000000 0.000000 1.000000'; - case 10: return '1.000000 0.000000 0.501961'; - case 11: return '0.000000 0.000000 1.000000'; - case 12: return '1.000000 1.000000 0.000000'; - case 13: return '0.000000 0.333333 1.000000'; - case 14: return '1.000000 0.666667 0.000000'; - case 15: - if (isPants) - return - '1 0 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 0.0000000000)) - + '0 1 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 2.0943951024)) - + '0 0 1' * (0.502 + 0.498 * sin(t / 2.7182818285 + 4.1887902048)); - else - return - '1 0 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 5.2359877560)) - + '0 1 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 3.1415926536)) - + '0 0 1' * (0.502 + 0.498 * sin(t / 3.1415926536 + 1.0471975512)); - default: return '0.000 0.000 0.000'; - } + switch (c) + { + case 0: return '1.000000 1.000000 1.000000'; + case 1: return '1.000000 0.333333 0.000000'; + case 2: return '0.000000 1.000000 0.501961'; + case 3: return '0.000000 1.000000 0.000000'; + case 4: return '1.000000 0.000000 0.000000'; + case 5: return '0.000000 0.666667 1.000000'; + case 6: return '0.000000 1.000000 1.000000'; + case 7: return '0.501961 1.000000 0.000000'; + case 8: return '0.501961 0.000000 1.000000'; + case 9: return '1.000000 0.000000 1.000000'; + case 10: return '1.000000 0.000000 0.501961'; + case 11: return '0.000000 0.000000 1.000000'; + case 12: return '1.000000 1.000000 0.000000'; + case 13: return '0.000000 0.333333 1.000000'; + case 14: return '1.000000 0.666667 0.000000'; + case 15: + if (isPants) + return '1 0 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 0.0000000000)) + + '0 1 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 2.0943951024)) + + '0 0 1' * (0.502 + 0.498 * sin(t / 2.7182818285 + 4.1887902048)); + else + return '1 0 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 5.2359877560)) + + '0 1 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 3.1415926536)) + + '0 0 1' * (0.502 + 0.498 * sin(t / 3.1415926536 + 1.0471975512)); + default: return '0.000 0.000 0.000'; + } +} + +float rgb_mi_ma_to_hue(vector rgb, float mi, float ma) +{ + if (mi == ma) + { + return 0; + } + else if (ma == rgb.x) + { + if (rgb.y >= rgb.z) return (rgb.y - rgb.z) / (ma - mi); + else return (rgb.y - rgb.z) / (ma - mi) + 6; + } + else if (ma == rgb.y) + { + return (rgb.z - rgb.x) / (ma - mi) + 2; + } + else // if(ma == rgb_z) + { + return (rgb.x - rgb.y) / (ma - mi) + 4; + } +} + +vector hue_mi_ma_to_rgb(float hue, float mi, float ma) +{ + vector rgb; + + hue -= 6 * floor(hue / 6); + + // else if(ma == rgb_x) + // hue = 60 * (rgb_y - rgb_z) / (ma - mi); + if (hue <= 1) + { + rgb.x = ma; + rgb.y = hue * (ma - mi) + mi; + rgb.z = mi; + } + // else if(ma == rgb_y) + // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120; + else if (hue <= 2) + { + rgb.x = (2 - hue) * (ma - mi) + mi; + rgb.y = ma; + rgb.z = mi; + } + else if (hue <= 3) + { + rgb.x = mi; + rgb.y = ma; + rgb.z = (hue - 2) * (ma - mi) + mi; + } + // else // if(ma == rgb_z) + // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240; + else if (hue <= 4) + { + rgb.x = mi; + rgb.y = (4 - hue) * (ma - mi) + mi; + rgb.z = ma; + } + else if (hue <= 5) + { + rgb.x = (hue - 4) * (ma - mi) + mi; + rgb.y = mi; + rgb.z = ma; + } + // else if(ma == rgb_x) + // hue = 60 * (rgb_y - rgb_z) / (ma - mi); + else // if(hue <= 6) + { + rgb.x = ma; + rgb.y = mi; + rgb.z = (6 - hue) * (ma - mi) + mi; + } + + return rgb; +} + +vector rgb_to_hsv(vector rgb) +{ + float mi, ma; + vector hsv; + + mi = min(rgb.x, rgb.y, rgb.z); + ma = max(rgb.x, rgb.y, rgb.z); + + hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma); + hsv.z = ma; + + if (ma == 0) hsv.y = 0; + else hsv.y = 1 - mi / ma; + + return hsv; +} + +vector hsv_to_rgb(vector hsv) +{ + return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z); +} + +vector rgb_to_hsl(vector rgb) +{ + float mi, ma; + vector hsl; + + mi = min(rgb.x, rgb.y, rgb.z); + ma = max(rgb.x, rgb.y, rgb.z); + + hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma); + + hsl.z = 0.5 * (mi + ma); + if (mi == ma) hsl.y = 0; + else if (hsl.z <= 0.5) hsl.y = (ma - mi) / (2 * hsl.z); + else // if(hsl_z > 0.5) + hsl.y = (ma - mi) / (2 - 2 * hsl.z); + + return hsl; +} + +vector hsl_to_rgb(vector hsl) +{ + float mi, ma, maminusmi; + + if (hsl.z <= 0.5) maminusmi = hsl.y * 2 * hsl.z; + else maminusmi = hsl.y * (2 - 2 * hsl.z); + + // hsl_z = 0.5 * mi + 0.5 * ma + // maminusmi = - mi + ma + mi = hsl.z - 0.5 * maminusmi; + ma = hsl.z + 0.5 * maminusmi; + + return hue_mi_ma_to_rgb(hsl.x, mi, ma); +} + +string rgb_to_hexcolor(vector rgb) +{ + return strcat( + "^x", + DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)), + DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)), + DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5)) + ); } #endif diff --git a/qcsrc/lib/compiler.qh b/qcsrc/lib/compiler.qh index 69aad5c54a..d389248b0e 100644 --- a/qcsrc/lib/compiler.qh +++ b/qcsrc/lib/compiler.qh @@ -2,15 +2,15 @@ #define COMPILER_H #ifndef QCC_SUPPORT_ACCUMULATE - #ifdef GMQCC - #define QCC_SUPPORT_ACCUMULATE - #endif + #ifdef GMQCC + #define QCC_SUPPORT_ACCUMULATE + #endif #endif #ifndef QCC_SUPPORT_NIL - #ifdef GMQCC - #define QCC_SUPPORT_NIL - #endif + #ifdef GMQCC + #define QCC_SUPPORT_NIL + #endif #endif #endif diff --git a/qcsrc/lib/counting.qh b/qcsrc/lib/counting.qh index 81103a51ca..24ce56d70c 100644 --- a/qcsrc/lib/counting.qh +++ b/qcsrc/lib/counting.qh @@ -7,54 +7,60 @@ // Time processing and counting functions/macros // =============================================== -#define count_years_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s years")), ftos_decimals(time, decs)) -#define count_years(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d years")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d year")), /* first */ \ - ZCTX(_("CI_SEC^%d years")), /* year */ \ - ZCTX(_("CI_THI^%d years")), /* third */ \ - ZCTX(_("CI_MUL^%d years"))) /* multi */ - -#define count_weeks_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s weeks")), ftos_decimals(time, decs)) -#define count_weeks(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d weeks")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d week")), /* first */ \ - ZCTX(_("CI_SEC^%d weeks")), /* week */ \ - ZCTX(_("CI_THI^%d weeks")), /* third */ \ - ZCTX(_("CI_MUL^%d weeks"))) /* multi */ - -#define count_days_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s days")), ftos_decimals(time, decs)) -#define count_days(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d days")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d day")), /* first */ \ - ZCTX(_("CI_SEC^%d days")), /* day */ \ - ZCTX(_("CI_THI^%d days")), /* third */ \ - ZCTX(_("CI_MUL^%d days"))) /* multi */ - -#define count_hours_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s hours")), ftos_decimals(time, decs)) -#define count_hours(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d hours")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d hour")), /* first */ \ - ZCTX(_("CI_SEC^%d hours")), /* hour */ \ - ZCTX(_("CI_THI^%d hours")), /* third */ \ - ZCTX(_("CI_MUL^%d hours"))) /* multi */ - - -#define count_minutes_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s minutes")), ftos_decimals(time, decs)) -#define count_minutes(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d minutes")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d minute")), /* first */ \ - ZCTX(_("CI_SEC^%d minutes")), /* minute */ \ - ZCTX(_("CI_THI^%d minutes")), /* third */ \ - ZCTX(_("CI_MUL^%d minutes"))) /* multi */ - -#define count_seconds_decs(time,decs) sprintf(ZCTX(_("CI_DEC^%s seconds")), ftos_decimals(time, decs)) -#define count_seconds(time) count_fill(time, \ - ZCTX(_("CI_ZER^%d seconds")), /* zeroth */ \ - ZCTX(_("CI_FIR^%d second")), /* first */ \ - ZCTX(_("CI_SEC^%d seconds")), /* second */ \ - ZCTX(_("CI_THI^%d seconds")), /* third */ \ - ZCTX(_("CI_MUL^%d seconds"))) /* multi */ +#define count_years_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s years")), ftos_decimals(time, decs)) +#define count_years(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d years")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d year")), /* first */ \ + ZCTX(_("CI_SEC^%d years")), /* year */ \ + ZCTX(_("CI_THI^%d years")), /* third */ \ + ZCTX(_("CI_MUL^%d years"))) /* multi */ + +#define count_weeks_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s weeks")), ftos_decimals(time, decs)) +#define count_weeks(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d weeks")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d week")), /* first */ \ + ZCTX(_("CI_SEC^%d weeks")), /* week */ \ + ZCTX(_("CI_THI^%d weeks")), /* third */ \ + ZCTX(_("CI_MUL^%d weeks"))) /* multi */ + +#define count_days_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s days")), ftos_decimals(time, decs)) +#define count_days(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d days")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d day")), /* first */ \ + ZCTX(_("CI_SEC^%d days")), /* day */ \ + ZCTX(_("CI_THI^%d days")), /* third */ \ + ZCTX(_("CI_MUL^%d days"))) /* multi */ + +#define count_hours_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s hours")), ftos_decimals(time, decs)) +#define count_hours(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d hours")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d hour")), /* first */ \ + ZCTX(_("CI_SEC^%d hours")), /* hour */ \ + ZCTX(_("CI_THI^%d hours")), /* third */ \ + ZCTX(_("CI_MUL^%d hours"))) /* multi */ + + +#define count_minutes_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s minutes")), ftos_decimals(time, decs)) +#define count_minutes(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d minutes")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d minute")), /* first */ \ + ZCTX(_("CI_SEC^%d minutes")), /* minute */ \ + ZCTX(_("CI_THI^%d minutes")), /* third */ \ + ZCTX(_("CI_MUL^%d minutes"))) /* multi */ + +#define count_seconds_decs(time, decs) sprintf(ZCTX(_("CI_DEC^%s seconds")), ftos_decimals(time, decs)) +#define count_seconds(time) \ + count_fill(time, \ + ZCTX(_("CI_ZER^%d seconds")), /* zeroth */ \ + ZCTX(_("CI_FIR^%d second")), /* first */ \ + ZCTX(_("CI_SEC^%d seconds")), /* second */ \ + ZCTX(_("CI_THI^%d seconds")), /* third */ \ + ZCTX(_("CI_MUL^%d seconds"))) /* multi */ string count_ordinal(int interval) { @@ -65,10 +71,10 @@ string count_ordinal(int interval) // Basically, it just allows you to represent a number or count in different ways // depending on the number... like, with count_ordinal you can provide integers // and retrieve 1st, 2nd, 3rd, nth ordinal numbers in a clean and simple way. - if(floor((interval % 100)/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block + if (floor((interval % 100) / 10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block { // otherwise, check normally for 1st,2nd,3rd insertions - switch(interval % 10) + switch (interval % 10) { case 1: return sprintf(_("%dst"), interval); case 2: return sprintf(_("%dnd"), interval); @@ -95,15 +101,14 @@ string count_fill(float interval, string zeroth, string first, string second, st // 3 seconds // etc... minutes, hours, days, etc. - switch(floor(interval)) + switch (floor(interval)) { case 0: return sprintf(zeroth, interval); case 1: { - if(interval == 1) // EXACTLY value of 1 + if (interval == 1) // EXACTLY value of 1 return sprintf(first, interval); - else - return sprintf(multi, interval); + else return sprintf(multi, interval); } case 2: return sprintf(second, interval); case 3: return sprintf(third, interval); @@ -119,26 +124,26 @@ string process_time(float outputtype, float seconds) tmp_seconds = floor(seconds); - if(tmp_seconds) + if (tmp_seconds) { tmp_minutes = floor(tmp_seconds / 60); - if(tmp_minutes) + if (tmp_minutes) { tmp_seconds -= (tmp_minutes * 60); tmp_hours = floor(tmp_minutes / 60); - if(tmp_hours) + if (tmp_hours) { tmp_minutes -= (tmp_hours * 60); tmp_days = floor(tmp_hours / 24); - if(tmp_days) + if (tmp_days) { tmp_hours -= (tmp_days * 24); tmp_weeks = floor(tmp_days / 7); - if(tmp_weeks) + if (tmp_weeks) { tmp_days -= (tmp_weeks * 7); tmp_years = floor(tmp_weeks / 52); @@ -148,7 +153,7 @@ string process_time(float outputtype, float seconds) } } - switch(outputtype) + switch (outputtype) { case 1: return sprintf("%02d:%02d:%02d", tmp_hours, tmp_minutes, tmp_seconds); case 2: @@ -157,7 +162,7 @@ string process_time(float outputtype, float seconds) output = count_seconds(tmp_seconds); - if(tmp_minutes) + if (tmp_minutes) { output = sprintf( "%s%s", @@ -165,7 +170,7 @@ string process_time(float outputtype, float seconds) ((output != "") ? sprintf(", %s", output) : "")); } - if(tmp_hours) + if (tmp_hours) { output = sprintf( "%s%s", @@ -173,7 +178,7 @@ string process_time(float outputtype, float seconds) ((output != "") ? sprintf(", %s", output) : "")); } - if(tmp_days) + if (tmp_days) { output = sprintf( "%s%s", @@ -181,7 +186,7 @@ string process_time(float outputtype, float seconds) ((output != "") ? sprintf(", %s", output) : "")); } - if(tmp_weeks) + if (tmp_weeks) { output = sprintf( "%s%s", @@ -189,7 +194,7 @@ string process_time(float outputtype, float seconds) ((output != "") ? sprintf(", %s", output) : "")); } - if(tmp_years) + if (tmp_years) { output = sprintf( "%s%s", @@ -205,9 +210,9 @@ string process_time(float outputtype, float seconds) output = count_hours(tmp_hours); - if(tmp_weeks) { tmp_days += (tmp_weeks * 7); } - if(tmp_years) { tmp_days += (tmp_years * 365); } - if(tmp_days) + if (tmp_weeks) tmp_days += (tmp_weeks * 7); + if (tmp_years) tmp_days += (tmp_years * 365); + if (tmp_days) { output = sprintf( "%s%s", diff --git a/qcsrc/lib/csqcmodel/cl_model.qc b/qcsrc/lib/csqcmodel/cl_model.qc index 8d3d4ab930..a656fb7908 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qc +++ b/qcsrc/lib/csqcmodel/cl_model.qc @@ -209,8 +209,8 @@ void CSQCModel_Draw() self.csqcmodel_teleported = 0; } -void CSQCModel_Read(bool isnew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_MODEL, bool isnew) +{ int sf = ReadInt24_t(); // some nice flags for CSQCMODEL_IF and the hooks @@ -279,6 +279,7 @@ void CSQCModel_Read(bool isnew) // draw it self.drawmask = MASK_NORMAL; self.predraw = CSQCModel_Draw; + return true; } entity CSQCModel_server2csqc(float pl) diff --git a/qcsrc/lib/csqcmodel/cl_model.qh b/qcsrc/lib/csqcmodel/cl_model.qh index 55401e4b4d..179350f950 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qh +++ b/qcsrc/lib/csqcmodel/cl_model.qh @@ -24,8 +24,6 @@ #include "common.qh" -void CSQCModel_Read(bool isnew); - #define CSQCMODEL_IF(cond) #define CSQCMODEL_ENDIF #define CSQCMODEL_PROPERTY(flag,t,r,w,f) \ diff --git a/qcsrc/lib/csqcmodel/cl_player.qc b/qcsrc/lib/csqcmodel/cl_player.qc index c39ee87970..155ad7f922 100644 --- a/qcsrc/lib/csqcmodel/cl_player.qc +++ b/qcsrc/lib/csqcmodel/cl_player.qc @@ -146,7 +146,7 @@ void PM_Movement_Move() #endif } -void CSQCPlayer_Physics(void) +void CSQCPlayer_Physics() { switch(autocvar_cl_movement) { diff --git a/qcsrc/lib/csqcmodel/common.qh b/qcsrc/lib/csqcmodel/common.qh index e922453afe..6da742a04e 100644 --- a/qcsrc/lib/csqcmodel/common.qh +++ b/qcsrc/lib/csqcmodel/common.qh @@ -61,7 +61,7 @@ const int CSQCMODEL_PROPERTY_ORIGIN = BIT(20); const int CSQCMODEL_PROPERTY_YAW = BIT(19); const int CSQCMODEL_PROPERTY_PITCHROLL = BIT(18); const int CSQCMODEL_PROPERTY_FRAME2 = BIT(17); -const int CSQCMODEL_PROPERTY_LERPFRAC = BIT(BIT(4)); +const int CSQCMODEL_PROPERTY_LERPFRAC = BIT(16); const int CSQCMODEL_PROPERTY_SIZE = BIT(15); #define ALLPROPERTIES_COMMON \ diff --git a/qcsrc/lib/csqcmodel/sv_model.qc b/qcsrc/lib/csqcmodel/sv_model.qc index 8fcdcfbee6..cadb77467c 100644 --- a/qcsrc/lib/csqcmodel/sv_model.qc +++ b/qcsrc/lib/csqcmodel/sv_model.qc @@ -41,7 +41,7 @@ bool CSQCModel_Send(entity to, int sf) unused_float = islocalplayer; unused_float = isnolocalplayer; - WriteByte(MSG_ENTITY, ENT_CLIENT_MODEL); + WriteHeader(MSG_ENTITY, ENT_CLIENT_MODEL); WriteInt24_t(MSG_ENTITY, sf); #define CSQCMODEL_IF(cond) if(cond) { @@ -61,7 +61,7 @@ bool CSQCModel_Send(entity to, int sf) return true; } -#ifdef CSQCPLAYER_FORCE_UPDATES +#if CSQCPLAYER_FORCE_UPDATES .float csqcmodel_nextforcedupdate; #endif void CSQCModel_CheckUpdate(entity e) @@ -75,7 +75,7 @@ void CSQCModel_CheckUpdate(entity e) unused_float = islocalplayer; unused_float = isnolocalplayer; -#ifdef CSQCPLAYER_FORCE_UPDATES +#if CSQCPLAYER_FORCE_UPDATES if(isplayer && time > e.csqcmodel_nextforcedupdate) { e.SendFlags |= CSQCMODEL_PROPERTY_ORIGIN; diff --git a/qcsrc/lib/cvar.qh b/qcsrc/lib/cvar.qh index 5ee3222bc6..d79ae11544 100644 --- a/qcsrc/lib/cvar.qh +++ b/qcsrc/lib/cvar.qh @@ -5,7 +5,18 @@ #include "progname.qh" #include "static.qh" -void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) { } +void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) {} + +bool cvar_value_issafe(string s) +{ + if (strstrofs(s, "\"", 0) >= 0) return false; + if (strstrofs(s, "\\", 0) >= 0) return false; + if (strstrofs(s, ";", 0) >= 0) return false; + if (strstrofs(s, "$", 0) >= 0) return false; + if (strstrofs(s, "\r", 0) >= 0) return false; + if (strstrofs(s, "\n", 0) >= 0) return false; + return true; +} /** escape the string to make it safe for consoles */ string MakeConsoleSafe(string input) @@ -17,34 +28,38 @@ string MakeConsoleSafe(string input) return input; } -void cvar_describe(string name, string desc) { - localcmd(sprintf("\nset %1$s \"$%1$s\" \"%2$s\"\n", name, MakeConsoleSafe(desc))); +void cvar_describe(string name, string desc) +{ + localcmd(sprintf("\nset %1$s \"$%1$s\" \"%2$s\"\n", name, MakeConsoleSafe(desc))); } -void cvar_archive(string name) { - localcmd(sprintf("\nseta %1$s \"$%1$s\"\n", name)); +void cvar_archive(string name) +{ + localcmd(sprintf("\nseta %1$s \"$%1$s\"\n", name)); } void RegisterCvars_Set(string name, string def, string desc, bool archive, string file) { - cvar_describe(name, desc); - if (archive) cvar_archive(name); + cvar_describe(name, desc); + if (archive) cvar_archive(name); } int RegisterCvars_Save_fd; void RegisterCvars_Save(string name, string def, string desc, bool archive, string file) { - if (!archive) return; - fputs(RegisterCvars_Save_fd, sprintf("seta %s \"%s\"\n", name, def)); + if (!archive) return; + fputs(RegisterCvars_Save_fd, sprintf("seta %s \"%s\"\n", name, def)); } -STATIC_INIT_LATE(Cvars) { - RegisterCvars(RegisterCvars_Set); - RegisterCvars_Save_fd = fopen(sprintf("default%s.cfg", PROGNAME), FILE_WRITE); - if (RegisterCvars_Save_fd >= 0) { - RegisterCvars(RegisterCvars_Save); - fclose(RegisterCvars_Save_fd); - } +STATIC_INIT_LATE(Cvars) +{ + RegisterCvars(RegisterCvars_Set); + RegisterCvars_Save_fd = fopen(sprintf("default%s.cfg", PROGNAME), FILE_WRITE); + if (RegisterCvars_Save_fd >= 0) + { + RegisterCvars(RegisterCvars_Save); + fclose(RegisterCvars_Save_fd); + } } const noref bool default_bool = false; @@ -53,19 +68,22 @@ const noref float default_float = 0; const noref string default_string = ""; const noref vector default_vector = '0 0 0'; -#define repr_cvar_bool(x) ((x) ? "1" : "0") -#define repr_cvar_int(x) (ftos(x)) -#define repr_cvar_float(x) (ftos(x)) +#define repr_cvar_bool(x) ((x) ? "1" : "0") +#define repr_cvar_int(x) (ftos(x)) +#define repr_cvar_float(x) (ftos(x)) #define repr_cvar_string(x) (x) #define repr_cvar_vector(x) (sprintf("%v", x)) #define __AUTOCVAR(file, archive, var, type, desc, default) \ - [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) { f(#var, repr_cvar_##type(default), desc, archive, file); } \ - type autocvar_##var = default + [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) \ + { \ + f( #var, repr_cvar_##type(default), desc, archive, file); \ + } \ + type autocvar_##var = default #define AUTOCVAR_5(file, archive, var, type, desc) \ - __AUTOCVAR(file, archive, var, type, desc, default_##type) + __AUTOCVAR(file, archive, var, type, desc, default_##type) #define AUTOCVAR_6(file, archive, var, type, default, desc) \ - __AUTOCVAR(file, archive, var, type, desc, default) + __AUTOCVAR(file, archive, var, type, desc, default) #define _AUTOCVAR(...) EVAL(OVERLOAD(AUTOCVAR, __FILE__, __VA_ARGS__)) #define AUTOCVAR_SAVE(...) _AUTOCVAR(true, __VA_ARGS__) #define AUTOCVAR(...) _AUTOCVAR(false, __VA_ARGS__) diff --git a/qcsrc/lib/defer.qh b/qcsrc/lib/defer.qh index 6c49566345..749e4a665e 100644 --- a/qcsrc/lib/defer.qh +++ b/qcsrc/lib/defer.qh @@ -1,14 +1,14 @@ -#ifndef MENUQC #ifndef DEFER_H #define DEFER_H +#ifndef MENUQC -#include "oo.qh" -#include "self.qh" + #include "oo.qh" + #include "self.qh" -entityclass(Defer); -class(Defer) .entity owner; -class(Defer) .void() think; -class(Defer) .float nextthink; + entityclass(Defer); + class(Defer).entity owner; + class(Defer).void() think; + class(Defer).float nextthink; /* ================== @@ -17,32 +17,35 @@ SUB_Remove Remove self ================== */ -void SUB_Remove() -{SELFPARAM(); - remove (self); -} - -void defer_think() -{SELFPARAM(); - self.think = SUB_Remove; - self.nextthink = time; - WITH(entity, self, self.owner, self.use()); -} + void SUB_Remove() + { + SELFPARAM(); + remove(self); + } + + void defer_think() + { + SELFPARAM(); + self.think = SUB_Remove; + self.nextthink = time; + WITH(entity, self, self.owner, self.use()); + } /* Execute func() after time + fdelay. self when func is executed = self when defer is called */ -void defer(float fdelay, void() func) -{SELFPARAM(); - entity e; - - e = spawn(); - e.owner = self; - e.use = func; - e.think = defer_think; - e.nextthink = time + fdelay; -} + void defer(float fdelay, void() func) + { + SELFPARAM(); + + entity e = new(deferred); + make_pure(e); + e.owner = this; + e.use = func; + e.think = defer_think; + e.nextthink = time + fdelay; + } #endif #endif diff --git a/qcsrc/lib/draw.qh b/qcsrc/lib/draw.qh index 90abcb2d3b..40900b8772 100644 --- a/qcsrc/lib/draw.qh +++ b/qcsrc/lib/draw.qh @@ -1,38 +1,38 @@ #ifdef CSQC #ifndef DRAW_H -#define DRAW_H + #define DRAW_H -#include "i18n.qh" -#include "vector.qh" + #include "i18n.qh" + #include "vector.qh" -#include "../client/defs.qh" + #include "../client/defs.qh" -void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg) -{ - // I want to draw a quad... - // from and to are MIDPOINTS. + void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg) + { + // I want to draw a quad... + // from and to are MIDPOINTS. - vector axis, thickdir, A, B, C, D; - float length_tex; + vector axis, thickdir, A, B, C, D; + float length_tex; - axis = normalize(to - from); - length_tex = aspect * vlen(to - from) / thickness; + axis = normalize(to - from); + length_tex = aspect * vlen(to - from) / thickness; - // direction is perpendicular to the view normal, and perpendicular to the axis - thickdir = normalize(cross(axis, vieworg - from)); + // direction is perpendicular to the view normal, and perpendicular to the axis + thickdir = normalize(cross(axis, vieworg - from)); - A = from - thickdir * (thickness / 2); - B = from + thickdir * (thickness / 2); - C = to + thickdir * (thickness / 2); - D = to - thickdir * (thickness / 2); + A = from - thickdir * (thickness / 2); + B = from + thickdir * (thickness / 2); + C = to + thickdir * (thickness / 2); + D = to - thickdir * (thickness / 2); - R_BeginPolygon(texture, drawflag); - R_PolygonVertex(A, '0 0 0' + shift * '1 0 0', rgb, theAlpha); - R_PolygonVertex(B, '0 1 0' + shift * '1 0 0', rgb, theAlpha); - R_PolygonVertex(C, '0 1 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha); - R_PolygonVertex(D, '0 0 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha); - R_EndPolygon(); -} + R_BeginPolygon(texture, drawflag); + R_PolygonVertex(A, '0 0 0' + shift * '1 0 0', rgb, theAlpha); + R_PolygonVertex(B, '0 1 0' + shift * '1 0 0', rgb, theAlpha); + R_PolygonVertex(C, '0 1 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha); + R_PolygonVertex(D, '0 0 0' + (shift + length_tex) * '1 0 0', rgb, theAlpha); + R_EndPolygon(); + } // a border picture is a texture containing nine parts: // 1/4 width: left part @@ -42,92 +42,92 @@ void Draw_CylindricLine(vector from, vector to, float thickness, string texture, // 1/4 height: top part // 1/2 height: middle part (stretched) // 1/4 height: bottom part -void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize) -{ - if (theBorderSize.x < 0 && theBorderSize.y < 0) // draw whole image as it is - { - drawpic(theOrigin, pic, theSize, theColor, theAlpha, 0); - return; - } - if (theBorderSize.x == 0 && theBorderSize.y == 0) // no border + void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize) { - // draw only the central part - drawsubpic(theOrigin, theSize, pic, '0.25 0.25 0', '0.5 0.5 0', theColor, theAlpha, 0); - return; - } - - vector dX, dY; - vector width, height; - vector bW, bH; - //pic = draw_UseSkinFor(pic); - width = eX * theSize.x; - height = eY * theSize.y; - if(theSize.x <= theBorderSize.x * 2) - { - // not wide enough... draw just left and right then - bW = eX * (0.25 * theSize.x / (theBorderSize.x * 2)); - if(theSize.y <= theBorderSize.y * 2) + if (theBorderSize.x < 0 && theBorderSize.y < 0) // draw whole image as it is { - // not high enough... draw just corners - bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2)); - drawsubpic(theOrigin, width * 0.5 + height * 0.5, pic, '0 0 0', bW + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + width * 0.5, width * 0.5 + height * 0.5, pic, eX - bW, bW + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + height * 0.5, width * 0.5 + height * 0.5, pic, eY - bH, bW + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0); + drawpic(theOrigin, pic, theSize, theColor, theAlpha, 0); + return; } - else + if (theBorderSize.x == 0 && theBorderSize.y == 0) // no border { - dY = theBorderSize.x * eY; - drawsubpic(theOrigin, width * 0.5 + dY, pic, '0 0 0', '0 0.25 0' + bW, theColor, theAlpha, 0); - drawsubpic(theOrigin + width * 0.5, width * 0.5 + dY, pic, '0 0 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0); - drawsubpic(theOrigin + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0', '0 0.5 0' + bW, theColor, theAlpha, 0); - drawsubpic(theOrigin + width * 0.5 + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5 0' + bW, theColor, theAlpha, 0); - drawsubpic(theOrigin + height - dY, width * 0.5 + dY, pic, '0 0.75 0', '0 0.25 0' + bW, theColor, theAlpha, 0); - drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5 + dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0); + // draw only the central part + drawsubpic(theOrigin, theSize, pic, '0.25 0.25 0', '0.5 0.5 0', theColor, theAlpha, 0); + return; } - } - else - { - if(theSize.y <= theBorderSize.y * 2) + + vector dX, dY; + vector width, height; + vector bW, bH; + // pic = draw_UseSkinFor(pic); + width = eX * theSize.x; + height = eY * theSize.y; + if (theSize.x <= theBorderSize.x * 2) { - // not high enough... draw just top and bottom then - bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2)); - dX = theBorderSize.x * eX; - drawsubpic(theOrigin, dX + height * 0.5, pic, '0 0 0', '0.25 0 0' + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + dX, width - 2 * dX + height * 0.5, pic, '0.25 0 0', '0.5 0 0' + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + width - dX, dX + height * 0.5, pic, '0.75 0 0', '0.25 0 0' + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + height * 0.5, dX + height * 0.5, pic, '0 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + dX + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5 0 0' + bH, theColor, theAlpha, 0); - drawsubpic(theOrigin + width - dX + height * 0.5, dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0); + // not wide enough... draw just left and right then + bW = eX * (0.25 * theSize.x / (theBorderSize.x * 2)); + if (theSize.y <= theBorderSize.y * 2) + { + // not high enough... draw just corners + bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2)); + drawsubpic(theOrigin, width * 0.5 + height * 0.5, pic, '0 0 0', bW + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + width * 0.5, width * 0.5 + height * 0.5, pic, eX - bW, bW + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + height * 0.5, width * 0.5 + height * 0.5, pic, eY - bH, bW + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + theSize * 0.5, width * 0.5 + height * 0.5, pic, eX + eY - bW - bH, bW + bH, theColor, theAlpha, 0); + } + else + { + dY = theBorderSize.x * eY; + drawsubpic(theOrigin, width * 0.5 + dY, pic, '0 0 0', '0 0.25 0' + bW, theColor, theAlpha, 0); + drawsubpic(theOrigin + width * 0.5, width * 0.5 + dY, pic, '0 0 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0); + drawsubpic(theOrigin + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0', '0 0.5 0' + bW, theColor, theAlpha, 0); + drawsubpic(theOrigin + width * 0.5 + dY, width * 0.5 + height - 2 * dY, pic, '0 0.25 0' + eX - bW, '0 0.5 0' + bW, theColor, theAlpha, 0); + drawsubpic(theOrigin + height - dY, width * 0.5 + dY, pic, '0 0.75 0', '0 0.25 0' + bW, theColor, theAlpha, 0); + drawsubpic(theOrigin + width * 0.5 + height - dY, width * 0.5 + dY, pic, '0 0.75 0' + eX - bW, '0 0.25 0' + bW, theColor, theAlpha, 0); + } } else { - dX = theBorderSize.x * eX; - dY = theBorderSize.x * eY; - drawsubpic(theOrigin, dX + dY, pic, '0 0 0', '0.25 0.25 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + dX, width - 2 * dX + dY, pic, '0.25 0 0', '0.5 0.25 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + width - dX, dX + dY, pic, '0.75 0 0', '0.25 0.25 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + dY, dX + height - 2 * dY, pic, '0 0.25 0', '0.25 0.5 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + dY + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5 0.5 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + dY + width - dX, dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + height - dY, dX + dY, pic, '0 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + height - dY + dX, width - 2 * dX + dY, pic, '0.25 0.75 0', '0.5 0.25 0', theColor, theAlpha, 0); - drawsubpic(theOrigin + height - dY + width - dX, dX + dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0); + if (theSize.y <= theBorderSize.y * 2) + { + // not high enough... draw just top and bottom then + bH = eY * (0.25 * theSize.y / (theBorderSize.y * 2)); + dX = theBorderSize.x * eX; + drawsubpic(theOrigin, dX + height * 0.5, pic, '0 0 0', '0.25 0 0' + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + dX, width - 2 * dX + height * 0.5, pic, '0.25 0 0', '0.5 0 0' + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + width - dX, dX + height * 0.5, pic, '0.75 0 0', '0.25 0 0' + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + height * 0.5, dX + height * 0.5, pic, '0 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + dX + height * 0.5, width - 2 * dX + height * 0.5, pic, '0.25 0 0' + eY - bH, '0.5 0 0' + bH, theColor, theAlpha, 0); + drawsubpic(theOrigin + width - dX + height * 0.5, dX + height * 0.5, pic, '0.75 0 0' + eY - bH, '0.25 0 0' + bH, theColor, theAlpha, 0); + } + else + { + dX = theBorderSize.x * eX; + dY = theBorderSize.x * eY; + drawsubpic(theOrigin, dX + dY, pic, '0 0 0', '0.25 0.25 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + dX, width - 2 * dX + dY, pic, '0.25 0 0', '0.5 0.25 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + width - dX, dX + dY, pic, '0.75 0 0', '0.25 0.25 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + dY, dX + height - 2 * dY, pic, '0 0.25 0', '0.25 0.5 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + dY + dX, width - 2 * dX + height - 2 * dY, pic, '0.25 0.25 0', '0.5 0.5 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + dY + width - dX, dX + height - 2 * dY, pic, '0.75 0.25 0', '0.25 0.5 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + height - dY, dX + dY, pic, '0 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + height - dY + dX, width - 2 * dX + dY, pic, '0.25 0.75 0', '0.5 0.25 0', theColor, theAlpha, 0); + drawsubpic(theOrigin + height - dY + width - dX, dX + dY, pic, '0.75 0.75 0', '0.25 0.25 0', theColor, theAlpha, 0); + } } } -} -void drawstringright(vector position, string text, vector theScale, vector rgb, float theAlpha, int flag) -{ - position.x -= 2 / 3 * strlen(text) * theScale.x; - drawstring(position, text, theScale, rgb, theAlpha, flag); -} + void drawstringright(vector position, string text, vector theScale, vector rgb, float theAlpha, int flag) + { + position.x -= 2 / 3 * strlen(text) * theScale.x; + drawstring(position, text, theScale, rgb, theAlpha, flag); + } -void drawstringcenter(vector position, string text, vector theScale, vector rgb, float theAlpha, int flag) -{ - position.x = 0.5 * (vid_conwidth - 0.6025 * strlen(text) * theScale.x); - drawstring(position, text, theScale, rgb, theAlpha, flag); -} + void drawstringcenter(vector position, string text, vector theScale, vector rgb, float theAlpha, int flag) + { + position.x = 0.5 * (vid_conwidth - 0.6025 * strlen(text) * theScale.x); + drawstring(position, text, theScale, rgb, theAlpha, flag); + } #endif #endif diff --git a/qcsrc/lib/file.qh b/qcsrc/lib/file.qh index f732bfe2c1..87d1460250 100644 --- a/qcsrc/lib/file.qh +++ b/qcsrc/lib/file.qh @@ -3,11 +3,10 @@ bool fexists(string f) { - int fh = fopen(f, FILE_READ); - if (fh < 0) - return false; - fclose(fh); - return true; + int fh = fopen(f, FILE_READ); + if (fh < 0) return false; + fclose(fh); + return true; } #endif diff --git a/qcsrc/lib/functional.qh b/qcsrc/lib/functional.qh index 2bbe7d59df..237492d900 100644 --- a/qcsrc/lib/functional.qh +++ b/qcsrc/lib/functional.qh @@ -3,24 +3,24 @@ #define MAP(f, ...) EVAL(OVERLOAD(MAP, f, __VA_ARGS__)) #define MAP_2(f, it) f(it) -#define MAP_3(f, it, ...) f(it)MAP_2(f, __VA_ARGS__) -#define MAP_4(f, it, ...) f(it)MAP_3(f, __VA_ARGS__) -#define MAP_5(f, it, ...) f(it)MAP_4(f, __VA_ARGS__) -#define MAP_6(f, it, ...) f(it)MAP_5(f, __VA_ARGS__) -#define MAP_7(f, it, ...) f(it)MAP_6(f, __VA_ARGS__) -#define MAP_8(f, it, ...) f(it)MAP_7(f, __VA_ARGS__) -#define MAP_9(f, it, ...) f(it)MAP_8(f, __VA_ARGS__) -#define MAP_10(f, it, ...) f(it)MAP_9(f, __VA_ARGS__) -#define MAP_11(f, it, ...) f(it)MAP_10(f, __VA_ARGS__) -#define MAP_12(f, it, ...) f(it)MAP_11(f, __VA_ARGS__) -#define MAP_13(f, it, ...) f(it)MAP_12(f, __VA_ARGS__) -#define MAP_14(f, it, ...) f(it)MAP_13(f, __VA_ARGS__) -#define MAP_15(f, it, ...) f(it)MAP_14(f, __VA_ARGS__) -#define MAP_16(f, it, ...) f(it)MAP_15(f, __VA_ARGS__) -#define MAP_17(f, it, ...) f(it)MAP_16(f, __VA_ARGS__) -#define MAP_18(f, it, ...) f(it)MAP_17(f, __VA_ARGS__) -#define MAP_19(f, it, ...) f(it)MAP_18(f, __VA_ARGS__) -#define MAP_20(f, it, ...) f(it)MAP_19(f, __VA_ARGS__) +#define MAP_3(f, it, ...) f(it) MAP_2(f, __VA_ARGS__) +#define MAP_4(f, it, ...) f(it) MAP_3(f, __VA_ARGS__) +#define MAP_5(f, it, ...) f(it) MAP_4(f, __VA_ARGS__) +#define MAP_6(f, it, ...) f(it) MAP_5(f, __VA_ARGS__) +#define MAP_7(f, it, ...) f(it) MAP_6(f, __VA_ARGS__) +#define MAP_8(f, it, ...) f(it) MAP_7(f, __VA_ARGS__) +#define MAP_9(f, it, ...) f(it) MAP_8(f, __VA_ARGS__) +#define MAP_10(f, it, ...) f(it) MAP_9(f, __VA_ARGS__) +#define MAP_11(f, it, ...) f(it) MAP_10(f, __VA_ARGS__) +#define MAP_12(f, it, ...) f(it) MAP_11(f, __VA_ARGS__) +#define MAP_13(f, it, ...) f(it) MAP_12(f, __VA_ARGS__) +#define MAP_14(f, it, ...) f(it) MAP_13(f, __VA_ARGS__) +#define MAP_15(f, it, ...) f(it) MAP_14(f, __VA_ARGS__) +#define MAP_16(f, it, ...) f(it) MAP_15(f, __VA_ARGS__) +#define MAP_17(f, it, ...) f(it) MAP_16(f, __VA_ARGS__) +#define MAP_18(f, it, ...) f(it) MAP_17(f, __VA_ARGS__) +#define MAP_19(f, it, ...) f(it) MAP_18(f, __VA_ARGS__) +#define MAP_20(f, it, ...) f(it) MAP_19(f, __VA_ARGS__) #define IDENTITY(it) it @@ -29,15 +29,15 @@ #define APPLY(f, ...) f(__VA_ARGS__) #ifdef SVQC - #define SV(f, ...) f(__VA_ARGS__) + #define SV(f, ...) f(__VA_ARGS__) #else - #define SV(f, ...) + #define SV(f, ...) #endif #ifdef CSQC - #define CL(f, ...) f(__VA_ARGS__) + #define CL(f, ...) f(__VA_ARGS__) #else - #define CL(f, ...) + #define CL(f, ...) #endif #define IF(cond, f, ...) cond(f, __VA_ARGS__) diff --git a/qcsrc/lib/i18n.qh b/qcsrc/lib/i18n.qh index 87c41cda5d..3773e16d1d 100644 --- a/qcsrc/lib/i18n.qh +++ b/qcsrc/lib/i18n.qh @@ -8,25 +8,23 @@ string prvm_language; string language_filename(string s) { - string fn = prvm_language; - if (fn == "" || fn == "dump") - return s; - fn = strcat(s, ".", fn); - int fh = fopen(fn, FILE_READ); - if (fh >= 0) - { - fclose(fh); - return fn; - } - return s; + string fn = prvm_language; + if (fn == "" || fn == "dump") return s; + fn = strcat(s, ".", fn); + int fh = fopen(fn, FILE_READ); + if (fh >= 0) + { + fclose(fh); + return fn; + } + return s; } string CTX(string s) { - int p = strstrofs(s, "^", 0); - if (p < 0) - return s; - return substring(s, p + 1, -1); + int p = strstrofs(s, "^", 0); + if (p < 0) return s; + return substring(s, p + 1, -1); } #define ZCTX(s) strzone(CTX(s)) diff --git a/qcsrc/lib/int.qh b/qcsrc/lib/int.qh index d9ea61f0c8..9bd129b9e1 100644 --- a/qcsrc/lib/int.qh +++ b/qcsrc/lib/int.qh @@ -2,13 +2,13 @@ #define INT_H #ifndef QCC_SUPPORT_INT - #define stoi(s) stof(s) - #define stob(s) stof(s) - #define itos(i) ftos(i) + #define stoi(s) stof(s) + #define stob(s) stof(s) + #define itos(i) ftos(i) #else - #define stoi(s) ((int) stof(s)) - #define stob(s) ((bool) stof(s)) - #define itos(i) ftos(i) + #define stoi(s) ((int) stof(s)) + #define stob(s) ((bool) stof(s)) + #define itos(i) ftos(i) #endif #endif diff --git a/qcsrc/lib/iter.qh b/qcsrc/lib/iter.qh index 53b3d66299..f3214a2486 100644 --- a/qcsrc/lib/iter.qh +++ b/qcsrc/lib/iter.qh @@ -1,19 +1,40 @@ #ifndef ITER_H #define ITER_H -#define FOREACH_ARRAY(arr, start, end, cond, body) do { \ - for (int i = start; i < end; ++i) { \ - const noref entity it = arr[i]; \ - if (cond) { body } \ - } \ -} while(0) +#define FOREACH_ARRAY(arr, start, end, cond, body) \ + do \ + { \ + for (int i = start; i < end; ++i) \ + { \ + const noref entity it = arr[i]; \ + if (cond) { body } \ + } \ + } \ + while (0) -#define FOREACH_LIST(list, next, cond, body) do { \ - noref int i = 0; \ - for (entity it = list##_first; it; (it = it.next, ++i)) { \ - if (cond) { body } \ - } \ -} while(0) +#define FOREACH_LIST(list, next, cond, body) \ + do \ + { \ + int i = 0; \ + for (entity it = list##_first; it; (it = it.next, ++i)) \ + { \ + if (cond) { body } \ + } \ + } \ + while (0) + +#define FOREACH_WORD(words, cond, body) \ + do \ + { \ + string _words = words; \ + int i = 0; \ + for (string _it; (_it = car(_words)); (_words = cdr(_words), ++i)) \ + { \ + const noref string it = _it; \ + if (cond) { body } \ + } \ + } \ + while (0) #define FOREACH(list, cond, body) FOREACH_LIST(list, enemy, cond, body) diff --git a/qcsrc/lib/lazy.qh b/qcsrc/lib/lazy.qh index f632b38c32..5e0329bd20 100644 --- a/qcsrc/lib/lazy.qh +++ b/qcsrc/lib/lazy.qh @@ -4,15 +4,19 @@ #include "oo.qh" CLASS(Lazy, Object) - ATTRIB(Lazy, m_get, entity(), func_null); - CONSTRUCTOR(Lazy, entity() _compute) { this.m_get = _compute; } + ATTRIB(Lazy, m_get, entity(), func_null); + CONSTRUCTOR(Lazy, entity() _compute) + { + this.m_get = _compute; + } ENDCLASS(Lazy) #define LAZY(id) __lazy_##id -#define LAZY_NEW(id, compute) entity LAZY(id)() { \ - static bool done; \ - static entity it; \ - if (!done) { it = compute; done = true; } \ - return it; \ -} +#define LAZY_NEW(id, compute) \ + entity LAZY(id)() { \ + static bool done; \ + static entity it; \ + if (!done) { it = compute; done = true; } \ + return it; \ + } #endif diff --git a/qcsrc/lib/linkedlist.qh b/qcsrc/lib/linkedlist.qh index be57bb5bf9..2d07527c87 100644 --- a/qcsrc/lib/linkedlist.qh +++ b/qcsrc/lib/linkedlist.qh @@ -2,14 +2,14 @@ #define LINKEDLIST_H CLASS(LinkedListNode, Object) - ATTRIB(LinkedListNode, ll_data, entity, NULL) - ATTRIB(LinkedListNode, ll_prev, LinkedListNode, NULL) - ATTRIB(LinkedListNode, ll_next, LinkedListNode, NULL) + ATTRIB(LinkedListNode, ll_data, entity, NULL) + ATTRIB(LinkedListNode, ll_prev, LinkedListNode, NULL) + ATTRIB(LinkedListNode, ll_next, LinkedListNode, NULL) ENDCLASS(LinkedListNode) CLASS(LinkedList, Object) - ATTRIB(LinkedList, ll_head, LinkedListNode, NULL); - ATTRIB(LinkedList, ll_tail, LinkedListNode, NULL); + ATTRIB(LinkedList, ll_head, LinkedListNode, NULL); + ATTRIB(LinkedList, ll_tail, LinkedListNode, NULL); ENDCLASS(LinkedList) #define LL_NEW() NEW(LinkedList) @@ -17,41 +17,39 @@ ENDCLASS(LinkedList) /** * Push to tail */ -entity LL_PUSH(LinkedList this, entity e) { - LinkedListNode n = NEW(LinkedListNode); - n.ll_data = e; - n.ll_prev = this.ll_tail; - LinkedListNode tail = this.ll_tail; - if (tail) { - tail.ll_next = n; - } else { - this.ll_head = this.ll_tail = n; - } - return e; +entity LL_PUSH(LinkedList this, entity e) +{ + LinkedListNode n = NEW(LinkedListNode); + n.ll_data = e; + LinkedListNode tail = n.ll_prev = this.ll_tail; + this.ll_tail = (tail) ? tail.ll_next = n : this.ll_head = n; + return e; } /** * Pop from tail */ -entity LL_POP(LinkedList this) { - if (!this.ll_tail) return NULL; - LinkedListNode n = this.ll_tail; - entity e = n.ll_data; - LinkedListNode prev = n.ll_prev; - if (prev) { - prev.ll_next = NULL; - } else { - this.ll_head = this.ll_tail = NULL; - } - return e; +entity LL_POP(LinkedList this) +{ + if (!this.ll_tail) return NULL; + LinkedListNode n = this.ll_tail; + entity e = n.ll_data; + LinkedListNode prev = n.ll_prev; + if (prev) prev.ll_next = NULL; + else this.ll_head = this.ll_tail = NULL; + return e; } -#define LL_EACH(list, cond, body) do { \ - noref int i = 0; \ - for (entity _it = list.ll_head; _it; (_it = _it.ll_next, ++i)) { \ - noref entity it = _it.ll_data; \ - if (cond) { body } \ - } \ -} while(0) +#define LL_EACH(list, cond, body) \ + do \ + { \ + noref int i = 0; \ + for (entity _it = list.ll_head; _it; (_it = _it.ll_next, ++i)) \ + { \ + noref entity it = _it.ll_data; \ + if (cond) { body } \ + } \ + } \ + while (0) #endif diff --git a/qcsrc/lib/log.qh b/qcsrc/lib/log.qh index 3d186e3894..e24bd8f05d 100644 --- a/qcsrc/lib/log.qh +++ b/qcsrc/lib/log.qh @@ -1,64 +1,94 @@ #ifndef LOG_H #define LOG_H -#define _printferr(...) error(sprintf(__VA_ARGS__)) -#define _printfbt(...) backtrace(sprintf(__VA_ARGS__)) -#define printf(...) print(sprintf(__VA_ARGS__)) -#define dprintf(...) dprint(sprintf(__VA_ARGS__)) -#define _dprintf2(...) do { if (autocvar_developer > 1) dprintf(__VA_ARGS__); } while (0) +#define _printferr(...) error(sprintf(__VA_ARGS__)) +#define _printfbt(...) backtrace(sprintf(__VA_ARGS__)) +#define printf(...) print(sprintf(__VA_ARGS__)) +#define dprintf(...) dprint(sprintf(__VA_ARGS__)) +#define _dprintf2(...) \ + do \ + { \ + if (autocvar_developer > 1) dprintf(__VA_ARGS__); \ + } \ + while (0) -#define assert(expr, ...) do { if (!(expr)) LOG_WARNINGF(__VA_ARGS__); } while (0) +#define assert(expr, ...) \ + do \ + { \ + if (!(expr)) LOG_WARNINGF(__VA_ARGS__); \ + } \ + while (0) -#define _LOG(f, level, s) f("[::"level"] ["__FILE__":%s:%.0f] %s", __FUNC__, __LINE__, s) +#define _LOG(f, level, s) f("[::"level "] ["__FILE__ ":%s:%.0f] %s", __FUNC__, __LINE__, s) -#define LOG_FATAL(...) _LOG_FATAL(strcat("", __VA_ARGS__)) -#define LOG_FATALF(...) _LOG_FATAL(sprintf(__VA_ARGS__)) -#define _LOG_FATAL(s) _LOG(_printferr, "FATAL", s) +#define LOG_FATAL(...) _LOG_FATAL(strcat("", __VA_ARGS__)) +#define LOG_FATALF(...) _LOG_FATAL(sprintf(__VA_ARGS__)) +#define _LOG_FATAL(s) _LOG(_printferr, "FATAL", s) -#define LOG_SEVERE(...) _LOG_SEVERE(strcat("", __VA_ARGS__)) -#define LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__)) -#define _LOG_SEVERE(s) _LOG(_printfbt, "SEVERE", s) +#define LOG_SEVERE(...) _LOG_SEVERE(strcat("", __VA_ARGS__)) +#define LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__)) +#define _LOG_SEVERE(s) _LOG(_printfbt, "SEVERE", s) -#define LOG_WARNING(...) _LOG_WARNING(strcat("", __VA_ARGS__)) -#define LOG_WARNINGF(...) _LOG_WARNING(sprintf(__VA_ARGS__)) -#define _LOG_WARNING(s) _LOG(printf, "WARNING", s) +#define LOG_WARNING(...) _LOG_WARNING(strcat("", __VA_ARGS__)) +#define LOG_WARNINGF(...) _LOG_WARNING(sprintf(__VA_ARGS__)) +#define _LOG_WARNING(s) _LOG(printf, "WARNING", s) -#define LOG_INFO(...) do { if (autocvar_developer) _LOG_INFO(strcat("", __VA_ARGS__)); else print (__VA_ARGS__); } while (0) -#define LOG_INFOF(...) do { if (autocvar_developer) _LOG_INFO(sprintf(__VA_ARGS__)); else printf(__VA_ARGS__); } while (0) -#define _LOG_INFO(s) _LOG(printf, "INFO", s) +#define LOG_INFO(...) \ + do \ + { \ + if (autocvar_developer) _LOG_INFO(strcat("", __VA_ARGS__)); \ + else print(__VA_ARGS__); \ + } \ + while (0) +#define LOG_INFOF(...) \ + do \ + { \ + if (autocvar_developer) _LOG_INFO(sprintf(__VA_ARGS__)); \ + else printf(__VA_ARGS__); \ + } \ + while (0) +#define _LOG_INFO(s) _LOG(printf, "INFO", s) -#define LOG_TRACE(...) _LOG_TRACE(strcat("", __VA_ARGS__)) -#define LOG_TRACEF(...) _LOG_TRACE(sprintf(__VA_ARGS__)) -#define _LOG_TRACE(s) _LOG(dprintf, "TRACE", s) +#define LOG_TRACE(...) _LOG_TRACE(strcat("", __VA_ARGS__)) +#define LOG_TRACEF(...) _LOG_TRACE(sprintf(__VA_ARGS__)) +#define _LOG_TRACE(s) _LOG(dprintf, "TRACE", s) -#define LOG_DEBUG(...) _LOG_DEBUG(strcat("", __VA_ARGS__)) -#define LOG_DEBUGF(...) _LOG_DEBUG(sprintf(__VA_ARGS__)) -#define _LOG_DEBUG(s) _LOG(_dprintf2, "DEBUG", s) +#define LOG_DEBUG(...) _LOG_DEBUG(strcat("", __VA_ARGS__)) +#define LOG_DEBUGF(...) _LOG_DEBUG(sprintf(__VA_ARGS__)) +#define _LOG_DEBUG(s) _LOG(_dprintf2, "DEBUG", s) // TODO: this sucks, lets find a better way to do backtraces? #ifdef SVQC -void builtin_remove(entity); -#define _backtrace() builtin_remove(NULL) + void builtin_remove(entity); + #define _backtrace() builtin_remove(NULL) #else -void remove(entity); -#define _backtrace() remove(NULL) + void remove(entity); + #define _backtrace() remove(NULL) #endif noref int autocvar_developer; noref bool autocvar_prvm_backtraceforwarnings; -#define backtrace(msg) do { \ - int dev = autocvar_developer; \ - bool war = autocvar_prvm_backtraceforwarnings; \ - cvar_set("developer", "1"); \ - cvar_set("prvm_backtraceforwarnings", "1"); \ - print("\n--- CUT HERE ---\n", msg, "\n"); \ - _backtrace(); \ - print("\n--- CUT UNTIL HERE ---\n"); \ - cvar_set("developer", ftos(dev)); \ - cvar_set("prvm_backtraceforwarnings", ftos(war)); \ -} while (0) +#define backtrace(msg) \ + do \ + { \ + int dev = autocvar_developer; \ + bool war = autocvar_prvm_backtraceforwarnings; \ + cvar_set("developer", "1"); \ + cvar_set("prvm_backtraceforwarnings", "1"); \ + print("\n--- CUT HERE ---\n", msg, "\n"); \ + _backtrace(); \ + print("\n--- CUT UNTIL HERE ---\n"); \ + cvar_set("developer", ftos(dev)); \ + cvar_set("prvm_backtraceforwarnings", ftos(war)); \ + } \ + while (0) -#define ASSERT(expr) do { if (!(expr)) LOG_FATAL("assertion failed: " #expr "\n"); } while (0) +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) LOG_FATAL("assertion failed: " #expr "\n"); \ + } \ + while (0) #endif diff --git a/qcsrc/lib/map.qc b/qcsrc/lib/map.qc new file mode 100644 index 0000000000..d71c2639fb --- /dev/null +++ b/qcsrc/lib/map.qc @@ -0,0 +1,107 @@ +#ifndef MAP_H +#define MAP_H + +// Databases (hash tables) +const float DB_BUCKETS = 8192; +void db_save(float db, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) + { + LOG_INFO(strcat("^1Can't write DB to ", pFilename)); + return; + } + int n = buf_getsize(db); + fputs(fh, strcat(ftos(DB_BUCKETS), "\n")); + for (int i = 0; i < n; ++i) + fputs(fh, strcat(bufstr_get(db, i), "\n")); + fclose(fh); +} + +int db_create() +{ + return buf_create(); +} + +void db_put(float db, string pKey, string pValue); + +int db_load(string pFilename) +{ + int db = buf_create(); + if (db < 0) return -1; + int fh = fopen(pFilename, FILE_READ); + if (fh < 0) return db; + string l = fgets(fh); + if (stof(l) == DB_BUCKETS) + { + for (int i = 0; (l = fgets(fh)); ++i) + { + if (l != "") bufstr_set(db, i, l); + } + } + else + { + // different count of buckets, or a dump? + // need to reorganize the database then (SLOW) + // + // note: we also parse the first line (l) in case the DB file is + // missing the bucket count + do + { + int n = tokenizebyseparator(l, "\\"); + for (int j = 2; j < n; j += 2) + db_put(db, argv(j - 1), uri_unescape(argv(j))); + } + while ((l = fgets(fh))); + } + fclose(fh); + return db; +} + +void db_dump(float db, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) error(strcat("Can't dump DB to ", pFilename)); + int n = buf_getsize(db); + fputs(fh, "0\n"); + for (int i = 0; i < n; ++i) + { + int m = tokenizebyseparator(bufstr_get(db, i), "\\"); + for (int j = 2; j < m; j += 2) + fputs(fh, strcat("\\", argv(j - 1), "\\", argv(j), "\n")); + } + fclose(fh); +} + +void db_close(float db) +{ + buf_del(db); +} + +string db_get(float db, string pKey) +{ + int h = crc16(false, pKey) % DB_BUCKETS; + return uri_unescape(infoget(bufstr_get(db, h), pKey)); +} + +void db_put(float db, string pKey, string pValue) +{ + int h = crc16(false, pKey) % DB_BUCKETS; + bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue))); +} + +void db_test() +{ + LOG_INFO("LOAD...\n"); + int db = db_load("foo.db"); + LOG_INFO("LOADED. FILL...\n"); + for (int i = 0; i < DB_BUCKETS; ++i) + db_put(db, ftos(random()), "X"); + LOG_INFO("FILLED. SAVE...\n"); + db_save(db, "foo.db"); + LOG_INFO("SAVED. CLOSE...\n"); + db_close(db); + LOG_INFO("CLOSED.\n"); +} + +#endif diff --git a/qcsrc/lib/math.qh b/qcsrc/lib/math.qh index 1a707a435c..a32e9b47c7 100644 --- a/qcsrc/lib/math.qh +++ b/qcsrc/lib/math.qh @@ -3,40 +3,25 @@ void mean_accumulate(entity e, .float a, .float c, float mean, float value, float weight) { - if (weight == 0) - return; - if (mean == 0) - e.(a) *= pow(value, weight); - else - e.(a) += pow(value, mean) * weight; - e.(c) += weight; + if (weight == 0) return; + if (mean == 0) e.(a) *= pow(value, weight); + else e.(a) += pow(value, mean) * weight; + e.(c) += weight; } float mean_evaluate(entity e, .float a, .float c, float mean) { - if (e.(c) == 0) - return 0; - if (mean == 0) - return pow(e.(a), 1.0 / e.(c)); - else - return pow(e.(a) / e.(c), 1.0 / mean); + if (e.(c) == 0) return 0; + if (mean == 0) return pow(e.(a), 1.0 / e.(c)); + else return pow(e.(a) / e.(c), 1.0 / mean); } -#define MEAN_ACCUMULATE(prefix,v,w) mean_accumulate(self,prefix##_accumulator,prefix##_count,prefix##_mean,v,w) -#define MEAN_EVALUATE(prefix) mean_evaluate(self,prefix##_accumulator,prefix##_count,prefix##_mean) -#define MEAN_DECLARE(prefix,m) float prefix##_mean = m; .float prefix##_count, prefix##_accumulator +#define MEAN_ACCUMULATE(prefix, v, w) mean_accumulate(self, prefix##_accumulator, prefix##_count, prefix##_mean, v, w) +#define MEAN_EVALUATE(prefix) mean_evaluate(self, prefix##_accumulator, prefix##_count, prefix##_mean) +#define MEAN_DECLARE(prefix, m) float prefix##_mean = m; .float prefix##_count, prefix##_accumulator -/* -================== -crandom - -Returns a random number between -1.0 and 1.0 -================== -*/ -float crandom() -{ - return 2 * (random() - 0.5); -} +/** Returns a random number between -1.0 and 1.0 */ +#define crandom() (2 * (random() - 0.5)) /* @@ -48,236 +33,271 @@ Angc used for animations float angc(float a1, float a2) { - while (a1 > 180) a1 -= 360; - while (a1 < -179) a1 += 360; - while (a2 > 180) a2 -= 360; - while (a2 < -179) a2 += 360; - float a = a1 - a2; - while (a > 180) a -= 360; - while (a < -179) a += 360; - return a; + while (a1 > 180) + a1 -= 360; + while (a1 < -179) + a1 += 360; + while (a2 > 180) + a2 -= 360; + while (a2 < -179) + a2 += 360; + float a = a1 - a2; + while (a > 180) + a -= 360; + while (a < -179) + a += 360; + return a; } -float fsnap(float val,float fsize) +float fsnap(float val, float fsize) { - return rint(val / fsize) * fsize; + return rint(val / fsize) * fsize; } -vector vsnap(vector point,float fsize) +vector vsnap(vector point, float fsize) { - vector vret; + vector vret; - vret.x = rint(point.x / fsize) * fsize; - vret.y = rint(point.y / fsize) * fsize; - vret.z = ceil(point.z / fsize) * fsize; + vret.x = rint(point.x / fsize) * fsize; + vret.y = rint(point.y / fsize) * fsize; + vret.z = ceil(point.z / fsize) * fsize; - return vret; + return vret; } vector lerpv(float t0, vector v0, float t1, vector v1, float t) { - return v0 + (v1 - v0) * ((t - t0) / (t1 - t0)); + return v0 + (v1 - v0) * ((t - t0) / (t1 - t0)); } vector bezier_quadratic_getpoint(vector a, vector b, vector c, float t) { - return - (c - 2 * b + a) * (t * t) + - (b - a) * (2 * t) + - a; + return (c - 2 * b + a) * (t * t) + + (b - a) * (2 * t) + + a; } vector bezier_quadratic_getderivative(vector a, vector b, vector c, float t) { - return - (c - 2 * b + a) * (2 * t) + - (b - a) * 2; + return (c - 2 * b + a) * (2 * t) + + (b - a) * 2; } float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x) { - return - ((( startspeedfactor + endspeedfactor - 2 - ) * x - 2 * startspeedfactor - endspeedfactor + 3 - ) * x + startspeedfactor - ) * x; + return (((startspeedfactor + endspeedfactor - 2 + ) * x - 2 * startspeedfactor - endspeedfactor + 3 + ) * x + startspeedfactor + ) * x; } bool cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor) { - if (startspeedfactor < 0 || endspeedfactor < 0) - return false; - - /* - // if this is the case, the possible zeros of the first derivative are outside - // 0..1 - We can calculate this condition as condition - if(se <= 3) - return true; - */ - - // better, see below: - if (startspeedfactor <= 3 && endspeedfactor <= 3) - return true; - - // if this is the case, the first derivative has no zeros at all - float se = startspeedfactor + endspeedfactor; - float s_e = startspeedfactor - endspeedfactor; - if (3 * (se - 4) * (se - 4) + s_e * s_e <= 12) // an ellipse - return true; - - // Now let s <= 3, s <= 3, s+e >= 3 (triangle) then we get se <= 6 (top right corner). - // we also get s_e <= 6 - se - // 3 * (se - 4)^2 + (6 - se)^2 - // is quadratic, has value 12 at 3 and 6, and value < 12 in between. - // Therefore, above "better" check works! - - return false; - - // known good cases: - // (0, [0..3]) - // (0.5, [0..3.8]) - // (1, [0..4]) - // (1.5, [0..3.9]) - // (2, [0..3.7]) - // (2.5, [0..3.4]) - // (3, [0..3]) - // (3.5, [0.2..2.3]) - // (4, 1) - - /* - On another note: - inflection point is always at (2s + e - 3) / (3s + 3e - 6). - - s + e - 2 == 0: no inflection - - s + e > 2: - 0 < inflection < 1 if: - 0 < 2s + e - 3 < 3s + 3e - 6 - 2s + e > 3 and 2e + s > 3 - - s + e < 2: - 0 < inflection < 1 if: - 0 > 2s + e - 3 > 3s + 3e - 6 - 2s + e < 3 and 2e + s < 3 - - Therefore: there is an inflection point iff: - e outside (3 - s)/2 .. 3 - s*2 - - in other words, if (s,e) in triangle (1,1)(0,3)(0,1.5) or in triangle (1,1)(3,0)(1.5,0) - */ + if (startspeedfactor < 0 || endspeedfactor < 0) return false; + + /* + // if this is the case, the possible zeros of the first derivative are outside + // 0..1 + We can calculate this condition as condition + if(se <= 3) + return true; + */ + + // better, see below: + if (startspeedfactor <= 3 && endspeedfactor <= 3) return true; + + // if this is the case, the first derivative has no zeros at all + float se = startspeedfactor + endspeedfactor; + float s_e = startspeedfactor - endspeedfactor; + if (3 * (se - 4) * (se - 4) + s_e * s_e <= 12) // an ellipse + return true; + + // Now let s <= 3, s <= 3, s+e >= 3 (triangle) then we get se <= 6 (top right corner). + // we also get s_e <= 6 - se + // 3 * (se - 4)^2 + (6 - se)^2 + // is quadratic, has value 12 at 3 and 6, and value < 12 in between. + // Therefore, above "better" check works! + + return false; + + // known good cases: + // (0, [0..3]) + // (0.5, [0..3.8]) + // (1, [0..4]) + // (1.5, [0..3.9]) + // (2, [0..3.7]) + // (2.5, [0..3.4]) + // (3, [0..3]) + // (3.5, [0.2..2.3]) + // (4, 1) + + /* + On another note: + inflection point is always at (2s + e - 3) / (3s + 3e - 6). + + s + e - 2 == 0: no inflection + + s + e > 2: + 0 < inflection < 1 if: + 0 < 2s + e - 3 < 3s + 3e - 6 + 2s + e > 3 and 2e + s > 3 + + s + e < 2: + 0 < inflection < 1 if: + 0 > 2s + e - 3 > 3s + 3e - 6 + 2s + e < 3 and 2e + s < 3 + + Therefore: there is an inflection point iff: + e outside (3 - s)/2 .. 3 - s*2 + + in other words, if (s,e) in triangle (1,1)(0,3)(0,1.5) or in triangle (1,1)(3,0)(1.5,0) + */ } /** continuous function mapping all reals into -1..1 */ float float2range11(float f) { - return f / (fabs(f) + 1); + return f / (fabs(f) + 1); } /** continuous function mapping all reals into 0..1 */ float float2range01(float f) { - return 0.5 + 0.5 * float2range11(f); + return 0.5 + 0.5 * float2range11(f); } float median(float a, float b, float c) { - return (a < c) ? bound(a, b, c) : bound(c, b, a); + return (a < c) ? bound(a, b, c) : bound(c, b, a); } float almost_equals(float a, float b) { - float eps = (max(a, -a) + max(b, -b)) * 0.001; - return a - b < eps && b - a < eps; + float eps = (max(a, -a) + max(b, -b)) * 0.001; + return a - b < eps && b - a < eps; } float almost_in_bounds(float a, float b, float c) { - float eps = (max(a, -a) + max(c, -c)) * 0.001; - if (a > c) - eps = -eps; - return b == median(a - eps, b, c + eps); + float eps = (max(a, -a) + max(c, -c)) * 0.001; + if (a > c) eps = -eps; + return b == median(a - eps, b, c + eps); +} + +float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d) +{ + if (halflifedist > 0) return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist); + else if (halflifedist < 0) return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist); + else return 1; } float power2of(float e) { - return pow(2, e); + return pow(2, e); } float log2of(float x) { - // NOTE: generated code - if (x > 2048) - if (x > 131072) - if (x > 1048576) - if (x > 4194304) - return 23; - else - if (x > 2097152) - return 22; - else - return 21; - else - if (x > 524288) - return 20; - else - if (x > 262144) - return 19; - else - return 18; - else - if (x > 16384) - if (x > 65536) - return 17; - else - if (x > 32768) - return 16; - else - return 15; - else - if (x > 8192) - return 14; - else - if (x > 4096) - return 13; - else - return 12; - else - if (x > 32) - if (x > 256) - if (x > 1024) - return 11; - else - if (x > 512) - return 10; - else - return 9; - else - if (x > 128) - return 8; - else - if (x > 64) - return 7; - else - return 6; - else - if (x > 4) - if (x > 16) - return 5; - else - if (x > 8) - return 4; - else - return 3; - else - if (x > 2) - return 2; - else - if (x > 1) - return 1; - else - return 0; + // NOTE: generated code + if (x > 2048) + if (x > 131072) + if (x > 1048576) + if (x > 4194304) return 23; + else + if (x > 2097152) return 22; + else return 21; + else + if (x > 524288) return 20; + else + if (x > 262144) return 19; + else return 18; + else + if (x > 16384) + if (x > 65536) return 17; + else + if (x > 32768) return 16; + else return 15; + else + if (x > 8192) return 14; + else + if (x > 4096) return 13; + else return 12; + else + if (x > 32) + if (x > 256) + if (x > 1024) return 11; + else + if (x > 512) return 10; + else return 9; + else + if (x > 128) return 8; + else + if (x > 64) return 7; + else return 6; + else + if (x > 4) + if (x > 16) return 5; + else + if (x > 8) return 4; + else return 3; + else + if (x > 2) return 2; + else + if (x > 1) return 1; + else return 0; } +/** ax^2 + bx + c = 0 */ +vector solve_quadratic(float a, float b, float c) +{ + vector v; + float D; + v = '0 0 0'; + if (a == 0) + { + if (b != 0) + { + v.x = v.y = -c / b; + v.z = 1; + } + else + { + if (c == 0) + { + // actually, every number solves the equation! + v.z = 1; + } + } + } + else + { + D = b * b - 4 * a * c; + if (D >= 0) + { + D = sqrt(D); + if (a > 0) // put the smaller solution first + { + v.x = ((-b) - D) / (2 * a); + v.y = ((-b) + D) / (2 * a); + } + else + { + v.x = (-b + D) / (2 * a); + v.y = (-b - D) / (2 * a); + } + v.z = 1; + } + else + { + // complex solutions! + D = sqrt(-D); + v.x = -b / (2 * a); + if (a > 0) v.y = D / (2 * a); + else v.y = -D / (2 * a); + v.z = 0; + } + } + return v; +} #endif diff --git a/qcsrc/lib/misc.qh b/qcsrc/lib/misc.qh index 8a6e117a77..e0ac0a4780 100644 --- a/qcsrc/lib/misc.qh +++ b/qcsrc/lib/misc.qh @@ -2,38 +2,55 @@ #define MISC_H #ifdef GMQCC - #define EVAL(...) __VA_ARGS__ + #define EVAL(...) __VA_ARGS__ - #define OVERLOAD_(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) - #define OVERLOAD(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) + #define OVERLOAD_(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) + #define OVERLOAD(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) #else - #define EMPTY() - #define DEFER(id) id EMPTY() - - #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) - #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) - #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) - #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) - #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) - #define EVAL5(...) __VA_ARGS__ - - #define OVERLOAD___(F,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,n,...) F##_##n - #define OVERLOAD__(F, ...) OVERLOAD___(F,##__VA_ARGS__,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) - #define OVERLOAD_(...) DEFER(OVERLOAD__(__VA_ARGS__)) - #define OVERLOAD(F, ...) OVERLOAD_(F,##__VA_ARGS__)(__VA_ARGS__) + #define EMPTY() + #define DEFER(id) id EMPTY() + + #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) + #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) + #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) + #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) + #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) + #define EVAL5(...) __VA_ARGS__ + + #define OVERLOAD___(F, _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, n, ...) F##_##n + #define OVERLOAD__(F, ...) OVERLOAD___(F,##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + #define OVERLOAD_(...) DEFER(OVERLOAD__(__VA_ARGS__)) + #define OVERLOAD(F, ...) OVERLOAD_(F,##__VA_ARGS__)(__VA_ARGS__) #endif -#define GET(name) name##get -#define GETTER(type, name) type GET(name)() { return name; } +#if defined(CSQC) + #define etof(e) num_for_edict(e) + #define ftoe(i) entitybyindex(i) +#elif defined(SVQC) + #define etof(e) num_for_edict(e) + #define ftoe(i) edict_num(i) +#elif defined(MENUQC) + // already defined +#endif -#define LAMBDA(...) { __VA_ARGS__ ; } +#undef etof +// avoid bounds checks +#define etof(e) stof(sprintf("%i", e)) -// Can't wrap with do-while as block may contain continue or break -#define WITH(type, name, value, block) { \ - type __with_save = (name); \ - name = (value); \ - LAMBDA(block) \ - name = __with_save; \ -} do { } while (0) +#define GET(name) name##get +#define GETTER(type, name) type GET(name)() { return name; } +#define PROPERTY(type, name) type name; GETTER(type, name) +#define LAMBDA(...) { __VA_ARGS__; } + +// With block may not contain continue or break +#define WITH(type, name, value, block) \ + do \ + { \ + type __with_save = (name); \ + name = (value); \ + LAMBDA(block) \ + name = __with_save; \ + } \ + while (0) #endif diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 834de760f5..3daa1cede5 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -2,52 +2,53 @@ #define NET_H #ifdef SVQC -.int Version; // deprecated, use SendFlags -.int SendFlags; -.bool(entity to, int sendflags) SendEntity; -.bool(entity this, entity to, int sendflags) SendEntity3; + .int Version; // deprecated, use SendFlags + .int SendFlags; + .bool(entity to, int sendflags) SendEntity; + .bool(entity this, entity to, int sendflags) SendEntity3; -bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); } + bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); } -void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc) -{ - if (e.classname == "") e.classname = "net_linked"; + void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc) + { + if (e.classname == "") e.classname = "net_linked"; - if (e.model == "" || self.modelindex == 0) { - vector mi = e.mins; - vector ma = e.maxs; - _setmodel(e, "null"); - setsize(e, mi, ma); - } + if (e.model == "" || self.modelindex == 0) + { + vector mi = e.mins; + vector ma = e.maxs; + _setmodel(e, "null"); + setsize(e, mi, ma); + } - e.SendEntity = SendEntity_self; - e.SendEntity3 = sendfunc; - e.SendFlags = 0xFFFFFF; + e.SendEntity = SendEntity_self; + e.SendEntity3 = sendfunc; + e.SendFlags = 0xFFFFFF; - if (!docull) e.effects |= EF_NODEPTHTEST; + if (!docull) e.effects |= EF_NODEPTHTEST; - if (dt) { - e.nextthink = time + dt; - e.think = SUB_Remove; - } -} + if (dt) + { + e.nextthink = time + dt; + e.think = SUB_Remove; + } + } -.void() uncustomizeentityforclient; -.float uncustomizeentityforclient_set; + .void() uncustomizeentityforclient; + .float uncustomizeentityforclient_set; -void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer) -{ - e.customizeentityforclient = customizer; - e.uncustomizeentityforclient = uncustomizer; - e.uncustomizeentityforclient_set = !!uncustomizer; -} + void SetCustomizer(entity e, float() customizer, void() uncustomizer) + { + e.customizeentityforclient = customizer; + e.uncustomizeentityforclient = uncustomizer; + e.uncustomizeentityforclient_set = !!uncustomizer; + } -void UncustomizeEntitiesRun() -{ - for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) { - WITH(entity, self, e, e.uncustomizeentityforclient()); - } -} + void UncustomizeEntitiesRun() + { + for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); ) + WITH(entity, self, e, e.uncustomizeentityforclient()); + } #endif @@ -56,111 +57,211 @@ void UncustomizeEntitiesRun() .string netname; .int m_id; -.void(entity this, bool isNew) m_read; +.bool(entity this, bool isNew) m_read; #ifdef CSQC - #define Net_Accept() do { if (!this) this = spawn(); } while (0) - #define Net_Reject() do { if (this) remove(this); } while (0) + #define Net_Accept(classname) \ + do \ + { \ + if (!this) this = new(classname); \ + } \ + while (0) + #define Net_Reject() \ + do \ + { \ + if (this) remove(this); \ + } \ + while (0) + #define NET_HANDLE(id, param) \ + bool Net_Handle_##id(entity this, param) #else - #define WriteHeader(to, id) do { \ - if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \ - WriteByte(to, NET_##id.m_id); \ - } while (0) + #define WriteHeader(to, id) \ + do \ + { \ + if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \ + WriteByte(to, NET_##id.m_id); \ + } \ + while (0) #endif #ifdef CSQC - #define REGISTER_NET_LINKED(id, param) \ - void Ent_Read##id(entity this, param) { this = self; } \ - REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) { \ - this.netname = #id; \ - this.m_read = Ent_Read##id; \ - } \ - [[accumulate]] void Ent_Read##id(entity this, param) + #define REGISTER_NET_LINKED(id) \ + [[accumulate]] NET_HANDLE(id, bool) \ + { \ + this = self; \ + this.sourceLocFile = __FILE__; \ + this.sourceLocLine = __LINE__; \ + } \ + REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \ + { \ + make_pure(this); \ + this.netname = #id; \ + this.m_read = Net_Handle_##id; \ + } #else - #define REGISTER_NET_LINKED(id, param) \ - const bool NET_##id##_istemp = false; \ - REGISTER(RegisterLinkedEntities, NET, LinkedEntities, id, m_id, spawn()) { \ - this.netname = #id; \ - } + #define REGISTER_NET_LINKED(id) \ + const bool NET_##id##_istemp = false; \ + REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \ + { \ + make_pure(this); \ + this.netname = #id; \ + } #endif -REGISTRY(LinkedEntities, BIT(0)) -REGISTER_REGISTRY(RegisterLinkedEntities) -REGISTRY_SORT(LinkedEntities, netname, 0) -STATIC_INIT(RegisterLinkedEntities_renumber) { - for (int i = 0; i < LinkedEntities_COUNT; ++i) { - LinkedEntities[i].m_id = 100 + i; - } +REGISTRY(LinkedEntities, BITS(8) - 1) +#define LinkedEntities_from(i) _LinkedEntities_from(i, NULL) +REGISTER_REGISTRY(LinkedEntities) +REGISTRY_SORT(LinkedEntities, 0) +REGISTRY_CHECK(LinkedEntities) +STATIC_INIT(RegisterLinkedEntities_renumber) +{ + for (int i = 0; i < LinkedEntities_COUNT; ++i) + LinkedEntities_from(i).m_id = 1 + i; } #ifdef CSQC - #define REGISTER_NET_TEMP(id, param) \ - void Net_Read##id(entity this, param); \ - REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) { \ - this.netname = #id; \ - this.m_read = Net_Read##id; \ - } \ - void Net_Read##id(entity this, param) + #define REGISTER_NET_TEMP(id) \ + NET_HANDLE(id, bool); \ + REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \ + { \ + make_pure(this); \ + this.netname = #id; \ + this.m_read = Net_Handle_##id; \ + } #else - #define REGISTER_NET_TEMP(id, param) \ - const bool NET_##id##_istemp = true; \ - REGISTER(RegisterTempEntities, NET, TempEntities, id, m_id, spawn()) { \ - this.netname = #id; \ - } + #define REGISTER_NET_TEMP(id) \ + const bool NET_##id##_istemp = true; \ + REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \ + { \ + make_pure(this); \ + this.netname = #id; \ + } #endif -REGISTRY(TempEntities, BIT(0)) -REGISTER_REGISTRY(RegisterTempEntities) -REGISTRY_SORT(TempEntities, netname, 0) -STATIC_INIT(RegisterTempEntities_renumber) { - for (int i = 0; i < TempEntities_COUNT; ++i) { - TempEntities[i].m_id = 115 + i; - } +REGISTRY(TempEntities, BITS(8) - 80) +#define TempEntities_from(i) _TempEntities_from(i, NULL) +REGISTER_REGISTRY(TempEntities) +REGISTRY_SORT(TempEntities, 0) +REGISTRY_CHECK(TempEntities) +STATIC_INIT(RegisterTempEntities_renumber) +{ + for (int i = 0; i < TempEntities_COUNT; ++i) + TempEntities_from(i).m_id = 80 + i; } #ifndef MENUQC -#ifdef CSQC -int ReadInt24_t() -{ - int v = ReadShort() << 8; // note: this is signed - v += ReadByte(); // note: this is unsigned - return v; -} -vector ReadInt48_t() -{ - vector v; - v.x = ReadInt24_t(); - v.y = ReadInt24_t(); - v.z = 0; - return v; -} -vector ReadInt72_t() -{ - vector v; - v.x = ReadInt24_t(); - v.y = ReadInt24_t(); - v.z = ReadInt24_t(); - return v; -} -#else -void WriteInt24_t(float dst, float val) -{ - float v; - WriteShort(dst, (v = floor(val >> 8))); - WriteByte(dst, val - (v << 8)); // 0..255 -} -void WriteInt48_t(float dst, vector val) -{ - WriteInt24_t(dst, val.x); - WriteInt24_t(dst, val.y); -} -void WriteInt72_t(float dst, vector val) -{ - WriteInt24_t(dst, val.x); - WriteInt24_t(dst, val.y); - WriteInt24_t(dst, val.z); -} -#endif + const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; + #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) + + #ifdef CSQC + entity ReadCSQCEntity() + { + int f = ReadShort(); + if (f == 0) return world; + return findfloat(world, entnum, f); + } + int ReadInt24_t() + { + int v = ReadShort() << 8; // note: this is signed + v += ReadByte(); // note: this is unsigned + return v; + } + vector ReadInt48_t() + { + vector v; + v.x = ReadInt24_t(); + v.y = ReadInt24_t(); + v.z = 0; + return v; + } + vector ReadInt72_t() + { + vector v; + v.x = ReadInt24_t(); + v.y = ReadInt24_t(); + v.z = ReadInt24_t(); + return v; + } + + #define ReadFloat() ReadCoord() + vector ReadVector() { vector v; v.x = ReadFloat(); v_y = ReadFloat(); v.z = ReadFloat(); return v; } + vector ReadVector2D() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = 0; return v; } + + float ReadApproxPastTime() + { + float dt = ReadByte(); + + // map from range...PPROXPASTTIME_MAX / 256 + dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt)); + + return servertime - dt; + } + + #else + const int MSG_ENTITY = 5; + + void WriteInt24_t(float dst, float val) + { + float v; + WriteShort(dst, (v = floor(val >> 8))); + WriteByte(dst, val - (v << 8)); // 0..255 + } + void WriteInt48_t(float dst, vector val) + { + WriteInt24_t(dst, val.x); + WriteInt24_t(dst, val.y); + } + void WriteInt72_t(float dst, vector val) + { + WriteInt24_t(dst, val.x); + WriteInt24_t(dst, val.y); + WriteInt24_t(dst, val.z); + } + + #define WriteFloat(to, f) WriteCoord(to, f) + #define WriteVector(to, v) do { WriteFloat(to, v.x); WriteFloat(to, v.y); WriteFloat(to, v.z); } while (0) + #define WriteVector2D(to, v) do { WriteFloat(to, v.x); WriteFloat(to, v.y); } while (0) + + // this will use the value: + // 128 + // accuracy near zero is APPROXPASTTIME_MAX/(256*255) + // accuracy at x is 1/derivative, i.e. + // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536 + void WriteApproxPastTime(float dst, float t) + { + float dt = time - t; + + // warning: this is approximate; do not resend when you don't have to! + // be careful with sendflags here! + // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75 + + // map to range... + dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt)); + + // round... + dt = rint(bound(0, dt, 255)); + + WriteByte(dst, dt); + } + + // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example) + #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname, statement) \ + entity varname; varname = msg_entity; \ + FOR_EACH_REALCLIENT(msg_entity) \ + if (msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \ + statement msg_entity = varname + #define WRITESPECTATABLE_MSG_ONE(statement) \ + do \ + { \ + WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement); \ + } \ + while (0) + #define WRITESPECTATABLE(msg, statement) \ + if (msg == MSG_ONE) WRITESPECTATABLE_MSG_ONE(statement); \ + else \ + statement float WRITESPECTATABLE_workaround = 0 + #endif #endif #endif diff --git a/qcsrc/lib/nil.qh b/qcsrc/lib/nil.qh index 87e3aea5e5..8067cf1ad4 100644 --- a/qcsrc/lib/nil.qh +++ b/qcsrc/lib/nil.qh @@ -2,12 +2,12 @@ #define NIL_H #ifdef QCC_SUPPORT_NIL -#define func_null nil -#define string_null nil + #define func_null nil + #define string_null nil #else // the NULL function -var void func_null(void); -string string_null; + var void func_null(); + string string_null; #endif #endif diff --git a/qcsrc/lib/noise.qc b/qcsrc/lib/noise.qc index 5bd6ad650b..2987f78e64 100644 --- a/qcsrc/lib/noise.qc +++ b/qcsrc/lib/noise.qc @@ -1,14 +1,14 @@ // noises "usually" start in the range -1..1 entityclass(Noise); -class(Noise) .float noise_baccum; -class(Noise) .float noise_paccum; -class(Noise) .float noise_paccum2; -class(Noise) .float noise_paccum3; -class(Noise) .float noise_bstate; +class(Noise).float noise_baccum; +class(Noise).float noise_paccum; +class(Noise).float noise_paccum2; +class(Noise).float noise_paccum3; +class(Noise).float noise_bstate; float Noise_Brown(entity e, float dt) { - e.noise_baccum += random() * sqrt(dt); // same stddev for all dt + e.noise_baccum += random() * sqrt(dt); // same stddev for all dt return e.noise_baccum; } float Noise_Pink(entity e, float dt) @@ -16,12 +16,9 @@ float Noise_Pink(entity e, float dt) float f; f = dt * 60; // http://home.earthlink.net/~ltrammell/tech/pinkalg.htm - if(random() > pow(0.3190, f)) - e.noise_paccum = 0.34848 * (2 * random() - 1); - if(random() > pow(0.7756, f)) - e.noise_paccum2 = 0.28768 * (2 * random() - 1); - if(random() > pow(0.9613, f)) - e.noise_paccum3 = 0.43488 * (2 * random() - 1); + if (random() > pow(0.3190, f)) e.noise_paccum = 0.34848 * (2 * random() - 1); + if (random() > pow(0.7756, f)) e.noise_paccum2 = 0.28768 * (2 * random() - 1); + if (random() > pow(0.9613, f)) e.noise_paccum3 = 0.43488 * (2 * random() - 1); return e.noise_paccum + e.noise_paccum2 + e.noise_paccum3; } float Noise_White(entity e, float dt) @@ -31,7 +28,6 @@ float Noise_White(entity e, float dt) /** +1 or -1 */ float Noise_Burst(entity e, float dt, float p) { - if(random() > pow(p, dt)) - e.noise_bstate = !e.noise_bstate; + if (random() > pow(p, dt)) e.noise_bstate = !e.noise_bstate; return 2 * e.noise_bstate - 1; } diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index 0615282c3e..a0d6f35db3 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -3,148 +3,200 @@ #include "misc.qh" #include "nil.qh" +#include "static.qh" #ifdef MENUQC - #define NULL (null_entity) + #define NULL (null_entity) #else - #define NULL (world) + #define NULL (world) #endif +.vector origin; +.bool pure_data; +#define make_pure(e) \ + do \ + { \ + (e).pure_data = true; \ + } \ + while (0) +#define is_pure(e) ((e).pure_data) + .string classname; /** Location entity was spawned from in source */ .string sourceLocFile; .int sourceLocLine; entity _spawn(); -entity __spawn(string _classname, string _sourceFile, int _sourceLine) { - entity this = _spawn(); - this.classname = _classname; - this.sourceLocFile = _sourceFile; - this.sourceLocLine = _sourceLine; - return this; +entity __spawn(string _classname, string _sourceFile, int _sourceLine, bool pure) +{ + entity this = _spawn(); + this.classname = _classname; + this.sourceLocFile = _sourceFile; + this.sourceLocLine = _sourceLine; + if (pure) make_pure(this); + return this; } - #define entityclass(...) EVAL(OVERLOAD(entityclass, __VA_ARGS__)) #define entityclass_1(name) entityclass_2(name, Object) #ifndef QCC_SUPPORT_ENTITYCLASS - #define entityclass_2(name, base) typedef entity name - #define class(name) - #define new(class) __spawn(#class, __FILE__, __LINE__) + #define entityclass_2(name, base) typedef entity name + #define class(name) + #define new(class) __spawn( #class, __FILE__, __LINE__, false) #else - #define entityclass_2(name, base) entityclass name : base {} - #define class(name) [[class(name)]] - #define new(class) ((class) __spawn(#class, __FILE__, __LINE__)) + #define entityclass_2(name, base) entityclass name : base {} + #define class(name) [[class(name)]] + #define new(class) ((class) __spawn( #class, __FILE__, __LINE__, false)) +#endif +#define spawn() __spawn("entity", __FILE__, __LINE__, false) + +entity _clearentity_ent; +STATIC_INIT(clearentity) +{ + _clearentity_ent = new(clearentity); +} +void clearentity(entity e) +{ +#ifdef CSQC + int n = e.entnum; +#endif + copyentity(_clearentity_ent, e); +#ifdef CSQC + e.entnum = n; #endif -#define spawn() new(entity) +} // Classes have a `spawn##cname(entity)` constructor // The parameter is used across [[accumulate]] functions // Macros to hide this implementation detail: #ifdef GMQCC -#define NEW(cname, ...) \ - OVERLOAD(spawn##cname, new(cname), ##__VA_ARGS__) + #define NEW(cname, ...) \ + OVERLOAD(spawn##cname, new(cname),##__VA_ARGS__) -#define CONSTRUCT(cname, ...) \ - OVERLOAD(spawn##cname, this, ##__VA_ARGS__) + #define CONSTRUCT(cname, ...) \ + OVERLOAD(spawn##cname, this,##__VA_ARGS__) #else -#define NEW_(cname, ...) \ - OVERLOAD_(spawn##cname, __VA_ARGS__) -#define NEW(cname, ...) \ - NEW_(cname, new(cname), ##__VA_ARGS__)(new(cname), ##__VA_ARGS__) - -#define CONSTRUCT_(cname, ...) \ - OVERLOAD_(spawn##cname, __VA_ARGS__) -#define CONSTRUCT(cname, ...) \ - CONSTRUCT_(cname, this, ##__VA_ARGS__)(this, ##__VA_ARGS__) + #define NEW_(cname, ...) \ + OVERLOAD_(spawn##cname, __VA_ARGS__) + #define NEW(cname, ...) \ + NEW_(cname, new(cname),##__VA_ARGS__)(new(cname),##__VA_ARGS__) + + #define CONSTRUCT_(cname, ...) \ + OVERLOAD_(spawn##cname, __VA_ARGS__) + #define CONSTRUCT(cname, ...) \ + CONSTRUCT_(cname, this,##__VA_ARGS__)(this,##__VA_ARGS__) #endif #define CONSTRUCTOR(cname, ...) \ - cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) { return = this; } \ - [[accumulate]] cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) + cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) \ + { \ + return = this; \ + } \ + [[accumulate]] cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) .string vtblname; .entity vtblbase; -void RegisterClasses() { } -STATIC_INIT(RegisterClasses) { RegisterClasses(); } +void RegisterClasses() {} +STATIC_INIT(RegisterClasses) +{ + RegisterClasses(); +} #define VTBL(cname, base) \ - INIT_STATIC(cname); \ - entity cname##_vtbl; \ - void cname##_vtbl_init() { \ - cname e = new(vtbl); \ - spawn##cname##_static(e); \ - e.vtblname = #cname; \ - /* Top level objects refer to themselves */ \ - e.vtblbase = base##_vtbl ? base##_vtbl : e; \ - cname##_vtbl = e; \ - } \ - ACCUMULATE_FUNCTION(RegisterClasses, cname##_vtbl_init) + INIT_STATIC(cname); \ + entity cname##_vtbl; \ + void cname##_vtbl_init() \ + { \ + cname e = new(vtbl); \ + make_pure(e); \ + spawn##cname##_static(e); \ + e.vtblname = #cname; \ + /* Top level objects refer to themselves */ \ + e.vtblbase = base##_vtbl ? base##_vtbl : e; \ + cname##_vtbl = e; \ + } \ + ACCUMULATE_FUNCTION(RegisterClasses, cname##_vtbl_init) #define INIT_STATIC(cname) [[accumulate]] void spawn##cname##_static(cname this) #define INIT(cname) [[accumulate]] cname spawn##cname##_1(cname this) #define CLASS(cname, base) \ - entityclass(cname, base); \ - class(cname) .bool instanceOf##cname; \ - VTBL(cname, base) \ - INIT_STATIC(cname) { \ - if (cname##_vtbl) { \ - copyentity(cname##_vtbl, this); \ - return; \ - } \ - spawn##base##_static(this); \ - this.instanceOf##cname = true; \ - } \ - INIT(cname) { \ - /* Only statically initialize the current class, it contains everything it inherits */ \ - if (cname##_vtbl.vtblname == this.classname) { \ - spawn##cname##_static(this); \ - this.classname = #cname; \ - this.vtblname = string_null; \ - this.vtblbase = cname##_vtbl; \ - } \ - spawn##base##_1(this); \ - } + entityclass(cname, base); \ + class(cname).bool instanceOf##cname; \ + VTBL(cname, base) \ + INIT_STATIC(cname) \ + { \ + if (cname##_vtbl) \ + { \ + copyentity(cname##_vtbl, this); \ + return; \ + } \ + spawn##base##_static(this); \ + this.instanceOf##cname = true; \ + } \ + INIT(cname) \ + { \ + /* Only statically initialize the current class, it contains everything it inherits */ \ + if (cname##_vtbl.vtblname == this.classname) \ + { \ + spawn##cname##_static(this); \ + this.classname = #cname; \ + this.vtblname = string_null; \ + this.vtblbase = cname##_vtbl; \ + } \ + spawn##base##_1(this); \ + } #define METHOD(cname, name, prototype) \ - class(cname) .prototype name; \ - prototype cname##_##name; \ - INIT_STATIC(cname) { this.name = cname##_##name; } \ - prototype cname##_##name + class(cname).prototype name; \ + prototype cname##_##name; \ + INIT_STATIC(cname) \ + { \ + this.name = cname##_##name; \ + } \ + prototype cname##_##name #define ATTRIB(cname, name, type, val) \ - class(cname) .type name; \ - INIT(cname) { this.name = val; } + class(cname).type name; \ + INIT(cname) \ + { \ + this.name = val; \ + } #define ATTRIBARRAY(cname, name, type, cnt) \ - class(cname) .type name[cnt]; + class(cname).type name[cnt]; #define ENDCLASS(cname) \ - [[last]] INIT(cname) { return this; } + INIT(cname) \ + { \ + return this; \ + } #define SUPER(cname) (cname##_vtbl.vtblbase) -#define super (this.vtblbase.vtblbase) #define spawn_static(this) #define spawn_1(this) #define _vtbl NULL CLASS(Object, ); - METHOD(Object, describe, string(entity this)) { - string s = _("No description"); - if (cvar("developer")) { - for (int i = 0, n = numentityfields(); i < n; ++i) { - string value = getentityfieldstring(i, this); - if (value != "") s = sprintf("%s\n%s = %s", s, entityfieldname(i), value); - } - } - return s; - } - METHOD(Object, display, void(entity this, void(string name, string icon) returns)) { - returns(sprintf("entity %i", this), "nopreview_map"); - } + METHOD(Object, describe, string(entity this)) + { + string s = _("No description"); + if (cvar("developer")) + { + for (int i = 0, n = numentityfields(); i < n; ++i) + { + string value = getentityfieldstring(i, this); + if (value != "") s = sprintf("%s\n%s = %s", s, entityfieldname(i), value); + } + } + return s; + } + METHOD(Object, display, void(entity this, void(string name, string icon) returns)) + { + returns(sprintf("entity %i", this), "nopreview_map"); + } ENDCLASS(Object) #undef spawn_static #undef spawn_1 diff --git a/qcsrc/lib/p2mathlib.qc b/qcsrc/lib/p2mathlib.qc index 85c5396d7e..ac04034dda 100644 --- a/qcsrc/lib/p2mathlib.qc +++ b/qcsrc/lib/p2mathlib.qc @@ -17,82 +17,87 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -vector vec_bias(vector v, float f){ +vector vec_bias(vector v, float f) +{ vector c; c_x = v_x + f; c_y = v_y + f; c_z = v_z + f; return c; } -vector vec_to_min (vector a, vector b) { +vector vec_to_min(vector a, vector b) +{ vector c; - c_x = min (a_x, b_x); - c_y = min (a_y, b_y); - c_z = min (a_z, b_z); + c_x = min(a_x, b_x); + c_y = min(a_y, b_y); + c_z = min(a_z, b_z); return c; } -vector vec_to_max (vector a, vector b) { +vector vec_to_max(vector a, vector b) +{ vector c; - c_x = max (a_x, b_x); - c_y = max (a_y, b_y); - c_z = max (a_z, b_z); + c_x = max(a_x, b_x); + c_y = max(a_y, b_y); + c_z = max(a_z, b_z); return c; } // there may already be a function for bounding a vector in this manner, however my very quick search did not reveal one -- Player_2 -vector vec_bounds_in (vector point, vector a, vector b) { +vector vec_bounds_in(vector point, vector a, vector b) +{ vector c, d, e; - d = vec_to_min(a,b); - e = vec_to_max(a,b); + d = vec_to_min(a, b); + e = vec_to_max(a, b); c = vec_to_max(point, d); c = vec_to_min(c, e); return c; - } -vector vec_bounds_out (vector point, vector a, vector b) { +vector vec_bounds_out(vector point, vector a, vector b) +{ vector c, d, e; - d = vec_to_max(a,b); - e = vec_to_min(a,b); + d = vec_to_max(a, b); + e = vec_to_min(a, b); c = vec_to_max(point, d); c = vec_to_min(c, e); return c; - } -float angle_snap_f (float f, float increment){ - +float angle_snap_f(float f, float increment) +{ float i; - for (i = 0; i <= 360; ){ - if (f <= i - increment) - return i - increment; + for (i = 0; i <= 360; ) + { + if (f <= i - increment) return i - increment; i = i + increment; } return 0; } -vector angle_snap_vec (vector v, float increment) { +vector angle_snap_vec(vector v, float increment) +{ vector c; - c_x = angle_snap_f (v_x, increment); - c_y = angle_snap_f (v_y, increment); - c_z = angle_snap_f (v_z, increment); + c_x = angle_snap_f(v_x, increment); + c_y = angle_snap_f(v_y, increment); + c_z = angle_snap_f(v_z, increment); return c; } -vector aim_vec (vector origin, vector target) { +vector aim_vec(vector origin, vector target) +{ vector v; - //we float around x and y, but rotate around z + // we float around x and y, but rotate around z v_x = target_x - origin_x; v_y = target_y - origin_y; v_z = origin_z - target_z; - //get the angles actual + // get the angles actual return vectoangles(normalize(v)); } diff --git a/qcsrc/lib/p2mathlib.qh b/qcsrc/lib/p2mathlib.qh index c92f26a912..b1886ae126 100644 --- a/qcsrc/lib/p2mathlib.qh +++ b/qcsrc/lib/p2mathlib.qh @@ -20,14 +20,14 @@ vector vec_bias(vector v, float f); -vector vec_to_min (vector a, vector b); -vector vec_to_max (vector a, vector b); +vector vec_to_min(vector a, vector b); +vector vec_to_max(vector a, vector b); // there may already be a function for bounding a vector in this manner, however my very quick search did not reveal one -- Player_2 -vector vec_bounds_in (vector point, vector a, vector b); -vector vec_bounds_out (vector point, vector a, vector b); +vector vec_bounds_in(vector point, vector a, vector b); +vector vec_bounds_out(vector point, vector a, vector b); -float angle_snap_f (float f, float increment); -vector angle_snap_vec (vector v, float increment); +float angle_snap_f(float f, float increment); +vector angle_snap_vec(vector v, float increment); -vector aim_vec (vector origin, vector target); +vector aim_vec(vector origin, vector target); diff --git a/qcsrc/lib/player.qh b/qcsrc/lib/player.qh index f89a3b07a8..6a8ce038c4 100644 --- a/qcsrc/lib/player.qh +++ b/qcsrc/lib/player.qh @@ -1,34 +1,30 @@ #ifdef CSQC #ifndef PLAYER_H -#define PLAYER_H + #define PLAYER_H -#include "string.qh" + #include "string.qh" -#include "../client/main.qh" -#include "../common/teams.qh" + #include "../client/main.qh" + #include "../common/teams.qh" -int GetPlayerColorForce(int i) -{ - if(!teamplay) - return 0; - else - return stof(getplayerkeyvalue(i, "colors")) & 15; -} + int GetPlayerColorForce(int i) + { + if (!teamplay) return 0; + else return stof(getplayerkeyvalue(i, "colors")) & 15; + } -int GetPlayerColor(int i) -{ - if(!playerslots[i].gotscores) // unconnected - return NUM_SPECTATOR; - else if(stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR) - return NUM_SPECTATOR; - else - return GetPlayerColorForce(i); -} + int GetPlayerColor(int i) + { + if (!playerslots[i].gotscores) // unconnected + return NUM_SPECTATOR; + else if (stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR) return NUM_SPECTATOR; + else return GetPlayerColorForce(i); + } -string GetPlayerName(int i) -{ - return ColorTranslateRGB(getplayerkeyvalue(i, "name")); -} + string GetPlayerName(int i) + { + return ColorTranslateRGB(getplayerkeyvalue(i, "name")); + } #endif #endif diff --git a/qcsrc/lib/progname.qh b/qcsrc/lib/progname.qh index ed112a5c2b..f0729460e5 100644 --- a/qcsrc/lib/progname.qh +++ b/qcsrc/lib/progname.qh @@ -2,13 +2,13 @@ #define PROGNAME_H #if defined(MENUQC) - #define PROGNAME "MENUQC" + #define PROGNAME "MENUQC" #elif defined(SVQC) - #define PROGNAME "SVQC" + #define PROGNAME "SVQC" #elif defined(CSQC) - #define PROGNAME "CSQC" + #define PROGNAME "CSQC" #else - #error "Unable to detect PROGNAME" + #error "Unable to detect PROGNAME" #endif #endif diff --git a/qcsrc/lib/random.qc b/qcsrc/lib/random.qc index 149323b8fa..aff961c55d 100644 --- a/qcsrc/lib/random.qc +++ b/qcsrc/lib/random.qc @@ -2,80 +2,134 @@ void RandomSelection_Init() { - RandomSelection_totalweight = 0; - RandomSelection_chosen_ent = NULL; - RandomSelection_chosen_float = 0; - RandomSelection_chosen_string = string_null; - RandomSelection_best_priority = -1; + RandomSelection_totalweight = 0; + RandomSelection_chosen_ent = NULL; + RandomSelection_chosen_float = 0; + RandomSelection_chosen_string = string_null; + RandomSelection_best_priority = -1; } void RandomSelection_Add(entity e, float f, string s, float weight, float priority) { - if (priority > RandomSelection_best_priority) - { - RandomSelection_best_priority = priority; - RandomSelection_chosen_ent = e; - RandomSelection_chosen_float = f; - RandomSelection_chosen_string = s; - RandomSelection_totalweight = weight; - } - else if (priority == RandomSelection_best_priority) - { - RandomSelection_totalweight += weight; - if (random() * RandomSelection_totalweight <= weight) - { - RandomSelection_chosen_ent = e; - RandomSelection_chosen_float = f; - RandomSelection_chosen_string = s; - } - } + if (priority > RandomSelection_best_priority) + { + RandomSelection_best_priority = priority; + RandomSelection_chosen_ent = e; + RandomSelection_chosen_float = f; + RandomSelection_chosen_string = s; + RandomSelection_totalweight = weight; + } + else if (priority == RandomSelection_best_priority) + { + RandomSelection_totalweight += weight; + if (random() * RandomSelection_totalweight <= weight) + { + RandomSelection_chosen_ent = e; + RandomSelection_chosen_float = f; + RandomSelection_chosen_string = s; + } + } } +float DistributeEvenly_amount; +float DistributeEvenly_totalweight; -// prandom - PREDICTABLE random number generator (not seeded yet) - -#ifdef USE_PRANDOM -float prandom_seed; -float prandom() +void DistributeEvenly_Init(float amount, float totalweight) { - float c; - c = crc16(false, strcat(ftos(prandom_seed), ftos(prandom_seed + M_PI))); - prandom_seed = c; - -#ifdef USE_PRANDOM_DEBUG - LOG_TRACE("RANDOM -> ", ftos(c), "\n"); -#endif - - return c / 65536; // in [0..1[ + if (DistributeEvenly_amount) + { + LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for "); + LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n"); + } + if (totalweight == 0) DistributeEvenly_amount = 0; + else DistributeEvenly_amount = amount; + DistributeEvenly_totalweight = totalweight; } -vector prandomvec() +float DistributeEvenly_Get(float weight) { - vector v; - - do - { - v.x = prandom(); - v.y = prandom(); - v.z = prandom(); - } - while(v * v > 1); - - return v; + float f; + if (weight <= 0) return 0; + f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight); + DistributeEvenly_totalweight -= weight; + DistributeEvenly_amount -= f; + return f; } -void psrandom(float seed) +float DistributeEvenly_GetRandomized(float weight) { - prandom_seed = seed; -#ifdef USE_PRANDOM_DEBUG - LOG_TRACE("SRANDOM ", ftos(seed), "\n"); -#endif + float f; + if (weight <= 0) return 0; + f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight); + DistributeEvenly_totalweight -= weight; + DistributeEvenly_amount -= f; + return f; } -#ifdef USE_PRANDOM_DEBUG -void prandom_debug() +// from the GNU Scientific Library +float gsl_ran_gaussian_lastvalue; +float gsl_ran_gaussian_lastvalue_set; +float gsl_ran_gaussian(float sigma) { - LOG_TRACE("Current random seed = ", ftos(prandom_seed), "\n"); + if (gsl_ran_gaussian_lastvalue_set) + { + gsl_ran_gaussian_lastvalue_set = 0; + return sigma * gsl_ran_gaussian_lastvalue; + } + else + { + float a = random() * 2 * M_PI; + float b = sqrt(-2 * log(random())); + gsl_ran_gaussian_lastvalue = cos(a) * b; + gsl_ran_gaussian_lastvalue_set = 1; + return sigma * sin(a) * b; + } } -#endif + +// prandom - PREDICTABLE random number generator (not seeded yet) + +#ifdef USE_PRANDOM + float prandom_seed; + float prandom() + { + float c; + c = crc16(false, strcat(ftos(prandom_seed), ftos(prandom_seed + M_PI))); + prandom_seed = c; + + #ifdef USE_PRANDOM_DEBUG + LOG_TRACE("RANDOM -> ", ftos(c), "\n"); + #endif + + return c / 65536; // in [0..1[ + } + + vector prandomvec() + { + vector v; + + do + { + v.x = prandom(); + v.y = prandom(); + v.z = prandom(); + } + while (v * v > 1); + + return v; + } + + void psrandom(float seed) + { + prandom_seed = seed; + #ifdef USE_PRANDOM_DEBUG + LOG_TRACE("SRANDOM ", ftos(seed), "\n"); + #endif + } + + #ifdef USE_PRANDOM_DEBUG + void prandom_debug() + { + LOG_TRACE("Current random seed = ", ftos(prandom_seed), "\n"); + } + #endif #endif diff --git a/qcsrc/lib/random.qh b/qcsrc/lib/random.qh index 551cf216d9..5b7fb0d3cc 100644 --- a/qcsrc/lib/random.qh +++ b/qcsrc/lib/random.qh @@ -15,19 +15,19 @@ void RandomSelection_Add(entity e, float f, string s, float weight, float priori #define USE_PRANDOM #ifdef USE_PRANDOM -float prandom(); -vector prandomvec(); + float prandom(); + vector prandomvec(); -void psrandom(float seed); -#ifdef USE_PRANDOM_DEBUG -void prandom_debug(); + void psrandom(float seed); + #ifdef USE_PRANDOM_DEBUG + void prandom_debug(); + #else + #define prandom_debug() + #endif #else -#define prandom_debug() -#endif -#else -#define prandom random -#define prandomvec randomvec -#define psrandom(x) -#define prandom_debug() + #define prandom random + #define prandomvec randomvec + #define psrandom(x) + #define prandom_debug() #endif #endif diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index 4c39a9dec6..52ab233774 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -3,86 +3,146 @@ #include "oo.qh" -#define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this) -#define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this) - +/** + * Declare a new registry. + * + * Don't forget to call `REGISTER_REGISTRY`: + * REGISTER_REGISTRY(Foos) + */ #define REGISTRY(id, max) \ - void Register##id() {} \ - const int id##_MAX = max; \ - noref entity id[id##_MAX], id##_first, id##_last; \ - int id##_COUNT; + void Register##id() {} \ + const int id##_MAX = max; \ + noref entity _##id[id##_MAX], id##_first, id##_last; \ + int id##_COUNT; \ + entity _##id##_from(int i, entity null) { if (i >= 0 && i < id##_COUNT) { entity e = _##id[i]; if (e) return e; } return null; } + +REGISTRY(Registries, BITS(8)) + +/** registered item identifier */ +.string registered_id; /** - * Register a new entity with a global constructor. + * Register a new entity with a registry. * Must be followed by a semicolon or a function body with a `this` parameter. * Wrapper macros may perform actions after user initialization like so: * #define REGISTER_FOO(id) \ - * REGISTER(RegisterFoos, FOO, FOOS, id, m_id, NEW(Foo)); \ + * REGISTER(Foos, FOO, id, m_id, NEW(Foo)); \ * REGISTER_INIT_POST(FOO, id) { \ * print("Registering foo #", this.m_id + 1, "\n"); \ * } \ * REGISTER_INIT(FOO, id) * - * Don't forget to forward declare `initfunc` and call `REGISTER_REGISTRY`: - * void RegisterFoos(); - * REGISTER_REGISTRY(RegisterFoos) * - * @param initfunc The global constructor to accumulate into + * @param registry The registry to add each entity to. * @param ns Short for namespace, prefix for each global (ns##_##id) - * @param array The array to add each entity to. Also requires `array##_first` and `array##_last` to be defined * @param id The identifier of the current entity being registered - * @param fld The field to store the current count into + * @param fld The field to store the locally unique unique entity id * @param inst An expression to create a new instance, invoked for every registration */ -#define REGISTER(initfunc, ns, array, id, fld, inst) \ - entity ns##_##id; \ - REGISTER_INIT(ns, id) { } \ - REGISTER_INIT_POST(ns, id) { } \ - void Register_##ns##_##id() { \ - if (array##_COUNT >= array##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(array##_MAX)); \ - entity this = inst; \ - ns##_##id = this; \ - this.fld = array##_COUNT; \ - array[array##_COUNT++] = this; \ - if (!array##_first) array##_first = this; \ - if ( array##_last) array##_last.REGISTRY_NEXT = this; \ - array##_last = this; \ - Register_##ns##_##id##_init(this); \ - Register_##ns##_##id##_init_post(this); \ - } \ - ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \ - REGISTER_INIT(ns, id) +#define REGISTER(registry, ns, id, fld, inst) \ + entity ns##_##id; \ + REGISTER_INIT(ns, id) {} \ + REGISTER_INIT_POST(ns, id) {} \ + void Register_##ns##_##id() \ + { \ + if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(registry##_MAX)); \ + entity this = ns##_##id = inst; \ + this.registered_id = #id; \ + this.fld = registry##_COUNT; \ + _##registry[registry##_COUNT] = this; \ + ++registry##_COUNT; \ + if (!registry##_first) registry##_first = this; \ + if (registry##_last) registry##_last.REGISTRY_NEXT = this; \ + registry##_last = this; \ + Register_##ns##_##id##_init(this); \ + Register_##ns##_##id##_init_post(this); \ + } \ + ACCUMULATE_FUNCTION(Register##registry, Register_##ns##_##id) \ + REGISTER_INIT(ns, id) + +#define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this) +#define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this) /** internal next pointer */ #define REGISTRY_NEXT enemy .entity REGISTRY_NEXT; -#define REGISTRY_SORT(id, field, skip) \ - void _REGISTRY_SWAP_##id(int i, int j, entity pass) { \ - i += skip; j += skip; \ - \ - entity a = id[i], b = id[j]; \ - id[i] = b; \ - id[j] = a; \ - \ - entity a_next = a.REGISTRY_NEXT, b_next = b.REGISTRY_NEXT; \ - a.REGISTRY_NEXT = b_next; \ - b.REGISTRY_NEXT = a_next; \ - \ - if (i == 0) id##_first = b; \ - else id[i - 1].REGISTRY_NEXT = b; \ - \ - if (j == 0) id##_first = a; \ - else id[j - 1].REGISTRY_NEXT = a; \ - } \ - float _REGISTRY_CMP_##id(int i, int j, entity pass) { \ - i += skip; j += skip; \ - string a = id[i].field; \ - string b = id[j].field; \ - return strcasecmp(a, b); \ - } \ - STATIC_INIT(Registry_sort_##id) { \ - heapsort(id##_COUNT - (skip), _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \ - } +#define REGISTRY_SORT(id, skip) \ + void _REGISTRY_SWAP_##id(int i, int j, entity pass) \ + { \ + i += skip; j += skip; \ + \ + entity a = _##id[i], b = _##id[j]; \ + _##id[i] = b; \ + _##id[j] = a; \ + \ + entity a_next = a.REGISTRY_NEXT, b_next = b.REGISTRY_NEXT; \ + a.REGISTRY_NEXT = b_next; \ + b.REGISTRY_NEXT = a_next; \ + \ + if (i == 0) id##_first = b; \ + else _##id[i - 1].REGISTRY_NEXT = b; \ + \ + if (j == 0) id##_first = a; \ + else _##id[j - 1].REGISTRY_NEXT = a; \ + } \ + int _REGISTRY_CMP_##id(int i, int j, entity pass) \ + { \ + i += skip; j += skip; \ + string a = _##id[i].registered_id; \ + string b = _##id[j].registered_id; \ + return strcmp(a, b); \ + } \ + STATIC_INIT(Registry_sort_##id) \ + { \ + heapsort(id##_COUNT - (skip), _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \ + } + +#define REGISTRY_HASH(id) Registry_hash_##id + +[[accumulate]] void Registry_check(string r, string server) { } +[[accumulate]] void Registry_send_all() { } + +#ifdef SVQC +void Registry_send(string id, string hash); +#else +#define Registry_send(id, hash) +#endif + +#define REGISTRY_CHECK(id) \ + string REGISTRY_HASH(id); \ + STATIC_INIT(Registry_check_##id) \ + { \ + string algo = "SHA256"; \ + string join = ":"; \ + string s = ""; \ + FOREACH(id, true, LAMBDA(s = strcat(s, join, it.registered_id))); \ + s = substring(s, strlen(join), -1); \ + string h = REGISTRY_HASH(id) = strzone(digest_hex(algo, s)); \ + LOG_TRACEF(#id ": %s\n[%s]\n", h, s); \ + } \ + void Registry_check(string r, string sv) \ + { \ + if (r == #id) \ + { \ + string cl = REGISTRY_HASH(id); \ + if (cl != sv) \ + { \ + LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s\n", r, cl, sv); \ + } \ + } \ + } \ + void Registry_send_all() { Registry_send(#id, REGISTRY_HASH(id)); } \ + +#define REGISTER_REGISTRY(...) EVAL(OVERLOAD(REGISTER_REGISTRY, __VA_ARGS__)) +#define REGISTER_REGISTRY_1(id) REGISTER_REGISTRY_2(id, #id) +#define REGISTER_REGISTRY_2(id, str) \ + ACCUMULATE_FUNCTION(__static_init, Register##id) \ + CLASS(id##Registry, Object) \ + ATTRIB(id##Registry, m_name, string, str) \ + ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first) \ + ENDCLASS(id##Registry) \ + REGISTER(Registries, REGISTRY, id, m_id, NEW(id##Registry)); + #endif diff --git a/qcsrc/lib/registry_net.qh b/qcsrc/lib/registry_net.qh new file mode 100644 index 0000000000..14e4dbc3cc --- /dev/null +++ b/qcsrc/lib/registry_net.qh @@ -0,0 +1,28 @@ +#ifndef REGISTRY_NET_H +#define REGISTRY_NET_H + +#include "net.qh" + +REGISTER_NET_TEMP(registry) + +#ifdef CSQC +NET_HANDLE(registry, bool isnew) +{ + string k = ReadString(); + string v = ReadString(); + Registry_check(k, v); + return true; +} +#endif + +#ifdef SVQC +void Registry_send(string id, string hash) +{ + int channel = MSG_ONE; + WriteHeader(channel, registry); + WriteString(channel, id); + WriteString(channel, hash); +} +#endif + +#endif diff --git a/qcsrc/lib/replicate.qh b/qcsrc/lib/replicate.qh index 6ff77139f8..b74a881f35 100644 --- a/qcsrc/lib/replicate.qh +++ b/qcsrc/lib/replicate.qh @@ -2,48 +2,55 @@ #define REPLICATE_H #ifndef MENUQC -#define REPLICATE(...) EVAL(OVERLOAD(REPLICATE, __VA_ARGS__)) + #define REPLICATE(...) EVAL(OVERLOAD(REPLICATE, __VA_ARGS__)) -[[accumulate]] void ReplicateVars(entity this, string thisname, int i) { } + [[accumulate]] void ReplicateVars(entity this, string thisname, int i) {} -#define REPLICATE_3(fld, type, var) REPLICATE_4(fld, type, var, ) -#define REPLICATE_4(fld, type, var, func) REPLICATE_##type(fld, var, func) -#define REPLICATE_string(fld, var, func) REPLICATE_7(fld, string, var, , \ - { if (field) strunzone(field); field = strzone(it); }, \ - { if (field) strunzone(field); field = string_null; }, \ - { \ - /* also initialize to the default value of func when requesting cvars */ \ - string s = func(field); \ - if (s != field) { \ - strunzone(field); \ - field = strzone(s); \ - } \ - }) -#define REPLICATE_float(fld, var, func) REPLICATE_7(fld, float, var, func, { field = stof(it); }, , ) -#define REPLICATE_bool(fld, var, func) REPLICATE_7(fld, bool, var, func, { field = boolean(stoi(it)); }, , ) -#define REPLICATE_int(fld, var, func) REPLICATE_7(fld, int, var, func, { field = stoi(it); }, , ) + #define REPLICATE_3(fld, type, var) REPLICATE_4(fld, type, var, ) + #define REPLICATE_4(fld, type, var, func) REPLICATE_##type(fld, var, func) + #define REPLICATE_string(fld, var, func) \ + REPLICATE_7(fld, string, var, , \ + { if (field) strunzone(field); field = strzone(it); }, \ + { if (field) strunzone(field); field = string_null; }, \ + { \ + /* also initialize to the default value of func when requesting cvars */ \ + string s = func(field); \ + if (s != field) \ + { \ + strunzone(field); \ + field = strzone(s); \ + } \ + }) + #define REPLICATE_float(fld, var, func) REPLICATE_7(fld, float, var, func, { field = stof(it); }, , ) + #define REPLICATE_bool(fld, var, func) REPLICATE_7(fld, bool, var, func, { field = boolean(stoi(it)); }, , ) + #define REPLICATE_int(fld, var, func) REPLICATE_7(fld, int, var, func, { field = stoi(it); }, , ) -#if defined(SVQC) - #define REPLICATE_7(fld, type, var, func, create, destroy, after) \ - void ReplicateVars(entity this, string thisname, int i) { \ - type field = this.fld; \ - if (i < 0) { destroy } \ - else { \ - string it = func(argv(i + 1)); \ - bool current = thisname == var; \ - if (i > 0) { \ - if (current) { create } \ - } else { \ - stuffcmd(this, "cl_cmd sendcvar " var "\n"); \ - } \ - if (current) { after } \ - } \ - this.fld = field; \ - } -#elif defined(CSQC) - // TODO - #define REPLICATE_7(fld, type, var, func, create, destroy, after) -#endif + #if defined(SVQC) + #define REPLICATE_7(fld, type, var, func, create, destroy, after) \ + void ReplicateVars(entity this, string thisname, int i) \ + { \ + type field = this.fld; \ + if (i < 0) { destroy } \ + else \ + { \ + string it = func(argv(i + 1)); \ + bool current = thisname == var; \ + if (i > 0) \ + { \ + if (current) { create } \ + } \ + else \ + { \ + stuffcmd(this, "cl_cmd sendcvar " var "\n"); \ + } \ + if (current) { after } \ + } \ + this.fld = field; \ + } + #elif defined(CSQC) + // TODO + #define REPLICATE_7(fld, type, var, func, create, destroy, after) + #endif #endif #endif diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index eac0c644a6..743214ab5f 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -7,31 +7,31 @@ // Step 1: auto oldself #if 1 -#define SELFPARAM() noref entity this = __self -#define setself(s) (__self = s) -#define self __self + #define SELFPARAM() noref entity this = __self + #define setself(s) (__self = s) + #define self __self #endif // Step 2: check SELFPARAM() is present for functions that use self #if 0 -#define SELFPARAM() [[alias("__self")]] noref entity this = __self -#define setself(s) (__self = s) -#define self this + #define SELFPARAM() [[alias("__self")]] noref entity this = __self + #define setself(s) (__self = s) + #define self this #endif // Step 3: const self #if 0 -#define SELFPARAM() noref const entity this = __self -entity setself(entity e) { return self = e; } -entity getself() { return self; } -#define self getself() + #define SELFPARAM() noref const entity this = __self + entity setself(entity e) { return self = e; } + entity getself() { return self; } + #define self getself() #endif // Step 4: enable when possible // TODO: Remove SELFPARAM in favor of a parameter #if 0 -#define SELFPARAM() noref const entity this = __self -#define self this + #define SELFPARAM() noref const entity this = __self + #define self this #endif #endif diff --git a/qcsrc/lib/sort.qh b/qcsrc/lib/sort.qh index 0d3177a98a..748bd2b274 100644 --- a/qcsrc/lib/sort.qh +++ b/qcsrc/lib/sort.qh @@ -2,74 +2,62 @@ #define SORT_H /** is only ever called for i1 < i2 */ -typedef void(float i1, float i2, entity pass) swapfunc_t; +typedef void (int i1, int i2, entity pass) swapfunc_t; /** <0 for <, ==0 for ==, >0 for > (like strcmp) */ -typedef float(float i1, float i2, entity pass) comparefunc_t; +typedef int (int i1, int i2, entity pass) comparefunc_t; -void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass) +void heapsort(int n, swapfunc_t swap, comparefunc_t cmp, entity pass) { - int root, child; + #define heapify(_count) \ + do \ + { \ + for (int start = floor(((_count) - 2) / 2); start >= 0; --start) \ + { \ + siftdown(start, (_count) - 1); \ + } \ + } \ + while (0) - // heapify - int start = floor((n - 2) / 2); - while (start >= 0) { - // siftdown(start, n - 1); - root = start; - while (root * 2 + 1 <= n - 1) { - child = root * 2 + 1; - if (child < n - 1 && cmp(child, child + 1, pass) < 0) { - child += 1; - } - if (cmp(root, child, pass) < 0) { - swap(root, child, pass); - root = child; - } else { - break; - } - } - // end of siftdown - --start; - } + #define siftdown(_start, _end) \ + do \ + { \ + for (int root = (_start); root * 2 + 1 <= (_end); ) \ + { \ + int child = root * 2 + 1; \ + if (child < (_end) && cmp(child, child + 1, pass) < 0) child += 1; \ + if (cmp(root, child, pass) >= 0) break; \ + swap(root, child, pass); \ + root = child; \ + } \ + } \ + while (0) - // extract - int end = n - 1; - while (end > 0) { - swap(0, end, pass); - end -= 1; - // siftdown(0, end); - root = 0; - while (root * 2 + 1 <= end) { - child = root * 2 + 1; - if (child < end && cmp(child, child+1, pass) < 0) { - child += 1; - } - if (cmp(root, child, pass) < 0) { - swap(root, child, pass); - root = child; - } else { - break; - } - } - // end of siftdown - } + heapify(n); + int end = n - 1; + while (end > 0) + { + swap(0, end, pass); + end -= 1; + siftdown(0, end); + } } void shuffle(float n, swapfunc_t swap, entity pass) { - for (int i = 1; i < n; ++i) { - // swap i-th item at a random position from 0 to i - // proof for even distribution: - // n = 1: obvious - // n -> n+1: - // item n+1 gets at any position with chance 1/(n+1) - // all others will get their 1/n chance reduced by factor n/(n+1) - // to be on place n+1, their chance will be 1/(n+1) - // 1/n * n/(n+1) = 1/(n+1) - // q.e.d. - int j = floor(random() * (i + 1)); - if (j != i) - swap(j, i, pass); - } + for (int i = 1; i < n; ++i) + { + // swap i-th item at a random position from 0 to i + // proof for even distribution: + // n = 1: obvious + // n -> n+1: + // item n+1 gets at any position with chance 1/(n+1) + // all others will get their 1/n chance reduced by factor n/(n+1) + // to be on place n+1, their chance will be 1/(n+1) + // 1/n * n/(n+1) = 1/(n+1) + // q.e.d. + int j = floor(random() * (i + 1)); + if (j != i) swap(j, i, pass); + } } #endif diff --git a/qcsrc/lib/sortlist.qc b/qcsrc/lib/sortlist.qc index fcaf6be50d..fecc79bdff 100644 --- a/qcsrc/lib/sortlist.qc +++ b/qcsrc/lib/sortlist.qc @@ -2,8 +2,8 @@ entity Sort_Spawn() { - entity sort; - sort = spawn(); + entity sort = new(sortlist); + make_pure(sort); sort.sort_next = NULL; sort.chain = sort; return sort; @@ -11,111 +11,111 @@ entity Sort_Spawn() /* entity Sort_New(float(entity,entity) cmp) { - entity sort; - sort = spawn(); - sort.sort_cmp = cmp; - sort.sort_next = world; - sort.chain = sort; - return sort; + entity sort; + sort = spawn(); + sort.sort_cmp = cmp; + sort.sort_next = world; + sort.chain = sort; + return sort; } void Sort_Remove(entity sort) { - entity next; - while(sort.sort_next) - { - next = sort.sort_next; - remove(sort); - sort = next; - } - remove(sort); + entity next; + while(sort.sort_next) + { + next = sort.sort_next; + remove(sort); + sort = next; + } + remove(sort); } void Sort_Add(entity sort, entity ent) { - entity next, parent; - parent = sort; - next = sort.sort_next; - while(next) - { - if(!sort.sort_cmp(next, ent)) - break; - parent = next; - next = next.sort_next; - } - ent.sort_next = next; - ent.sort_prev = parent; - parent.sort_next = ent; - if(next) - next.sort_prev = ent; + entity next, parent; + parent = sort; + next = sort.sort_next; + while(next) + { + if(!sort.sort_cmp(next, ent)) + break; + parent = next; + next = next.sort_next; + } + ent.sort_next = next; + ent.sort_prev = parent; + parent.sort_next = ent; + if(next) + next.sort_prev = ent; } void Sort_Reset(entity sort) { - sort.chain = sort; + sort.chain = sort; } float Sort_HasNext(entity sort) { - return (sort.chain.sort_next != world); + return (sort.chain.sort_next != world); } entity Sort_Next(entity sort) { - entity next; - next = sort.chain.sort_next; - if(!next) { - next = spawn(); - sort.chain.sort_next = next; - next.sort_prev = sort.chain; - next.sort_next = world; - } - sort.chain = next; - return next; + entity next; + next = sort.chain.sort_next; + if(!next) { + next = spawn(); + sort.chain.sort_next = next; + next.sort_prev = sort.chain; + next.sort_next = world; + } + sort.chain = next; + return next; } void Sort_Finish(entity sort) { - entity next; - next = sort.chain; - if(!next) - return; + entity next; + next = sort.chain; + if(!next) + return; - while(next.sort_next) - { - sort = next.sort_next; - next.sort_next = sort.sort_next; - remove(sort); - } + while(next.sort_next) + { + sort = next.sort_next; + next.sort_next = sort.sort_next; + remove(sort); + } } entity Sort_Get(entity sort, float i) { - for (; sort.sort_next && i > 0; --i) - sort = sort.sort_next; - return sort; + for (; sort.sort_next && i > 0; --i) + sort = sort.sort_next; + return sort; } */ /* void Sort_Erase(entity ent) { - ent.sort_prev.sort_next = ent.sort_next; - if(ent.sort_next) - ent.sort_next.sort_prev = ent.sort_prev; - remove(ent); + ent.sort_prev.sort_next = ent.sort_next; + if(ent.sort_next) + ent.sort_next.sort_prev = ent.sort_prev; + remove(ent); } void Sort_RemoveOld(entity sort) { - entity tmp; - for(tmp = sort.sort_next; tmp; tmp = tmp.sort_next) - { - if(tmp.frame < time) - { - tmp = tmp.sort_prev; - Sort_Erase(tmp.sort_next); - } - } + entity tmp; + for(tmp = sort.sort_next; tmp; tmp = tmp.sort_next) + { + if(tmp.frame < time) + { + tmp = tmp.sort_prev; + Sort_Erase(tmp.sort_next); + } + } } */ diff --git a/qcsrc/lib/sortlist.qh b/qcsrc/lib/sortlist.qh index d4a1f183e1..eb8103f8fd 100644 --- a/qcsrc/lib/sortlist.qh +++ b/qcsrc/lib/sortlist.qh @@ -2,8 +2,8 @@ #define SORTLIST_H entityclass(Sort); -//.float(entity,entity) sort_cmp; -class(Sort) .entity chain, sort_next, sort_prev; +// .float(entity,entity) sort_cmp; +class(Sort).entity chain, sort_next, sort_prev; entity Sort_Spawn(); @@ -12,12 +12,12 @@ entity Sort_Spawn(); * @param a FIRST entity * @param b entity after a */ -#define SORT_SWAP(a,b) \ - b.sort_prev = a.sort_prev; \ - a.sort_next = b.sort_next; \ - if(b.sort_next) b.sort_next.sort_prev = a; \ - if(a.sort_prev) a.sort_prev.sort_next = b; \ - a.sort_prev = b; \ +#define SORT_SWAP(a, b) \ + b.sort_prev = a.sort_prev; \ + a.sort_next = b.sort_next; \ + if (b.sort_next) b.sort_next.sort_prev = a; \ + if (a.sort_prev) a.sort_prev.sort_next = b; \ + a.sort_prev = b; \ b.sort_next = a #endif diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index 1ab059cdf9..8a17854cb9 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -7,161 +7,175 @@ noref bool require_spawnfunc_prefix; // Optional type checking; increases compile time too much to be enabled by default #if 0 -bool entityfieldassignablefromeditor(int i) { - switch (entityfieldtype(i)) { - case FIELD_STRING: - case FIELD_FLOAT: - case FIELD_VECTOR: - return true; - } - return false; -} + bool entityfieldassignablefromeditor(int i) + { + switch (entityfieldtype(i)) + { + case FIELD_STRING: + case FIELD_FLOAT: + case FIELD_VECTOR: + return true; + } + return false; + } -#define _spawnfunc_checktypes(fld) if (fieldname == #fld) \ - if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted\n", fieldname); + #define _spawnfunc_checktypes(fld) \ + if (fieldname == #fld) \ + if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted\n", fieldname); #else -#define _spawnfunc_checktypes(fld) + #define _spawnfunc_checktypes(fld) #endif -#define _spawnfunc_check(fld) if (fieldname == #fld) \ - continue; + #define _spawnfunc_check(fld) \ + if (fieldname == #fld) continue; -#define spawnfunc_1(id, whitelist) spawnfunc_2(id, whitelist) -#define spawnfunc_2(id, whitelist) void spawnfunc_##id(entity this) { \ - this = self; \ - if (!this.spawnfunc_checked) { \ - for (int i = 0, n = numentityfields(); i < n; ++i) { \ - string value = getentityfieldstring(i, this); \ - string fieldname = entityfieldname(i); \ - whitelist(_spawnfunc_checktypes) \ - if (value == "") continue; \ - if (fieldname == "") continue; \ - FIELDS_COMMON(_spawnfunc_check) \ - whitelist(_spawnfunc_check) \ - LOG_WARNINGF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #id, fieldname, value); \ - } \ - this.spawnfunc_checked = true; \ - } \ -} \ -[[accumulate]] void spawnfunc_##id(entity this) + #define spawnfunc_1(id, whitelist) spawnfunc_2(id, whitelist) + #define spawnfunc_2(id, whitelist) \ + void spawnfunc_##id(entity this) \ + { \ + this = self; \ + if (!this.sourceLocFile) \ + { \ + this.sourceLocFile = __FILE__; \ + this.sourceLocLine = __LINE__; \ + } \ + if (!this.spawnfunc_checked) \ + { \ + for (int i = 0, n = numentityfields(); i < n; ++i) \ + { \ + string value = getentityfieldstring(i, this); \ + string fieldname = entityfieldname(i); \ + whitelist(_spawnfunc_checktypes) \ + if (value == "") continue; \ + if (fieldname == "") continue; \ + FIELDS_COMMON(_spawnfunc_check) \ + whitelist(_spawnfunc_check) \ + LOG_WARNINGF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #id, fieldname, value); \ + } \ + this.spawnfunc_checked = true; \ + } \ + } \ + [[accumulate]] void spawnfunc_##id(entity this) -#define FIELD_SCALAR(fld, n) \ - fld(n) -#define FIELD_VEC(fld, n) \ - fld(n) \ - fld(n##_x) \ - fld(n##_y) \ - fld(n##_z) + #define FIELD_SCALAR(fld, n) \ + fld(n) + #define FIELD_VEC(fld, n) \ + fld(n) \ + fld(n##_x) \ + fld(n##_y) \ + fld(n##_z) -#define FIELDS_NONE(fld) -#define FIELDS_ALL(fld) if (false) + #define FIELDS_NONE(fld) + #define FIELDS_ALL(fld) if (false) -#define FIELDS_COMMON(fld) \ - FIELD_SCALAR(fld, classname) \ - FIELD_SCALAR(fld, spawnfunc_checked) \ - /**/ + #define FIELDS_COMMON(fld) \ + FIELD_SCALAR(fld, classname) \ + FIELD_SCALAR(fld, spawnfunc_checked) \ + /**/ -#define FIELDS_UNION(fld) \ - FIELD_SCALAR(fld, Version) \ - FIELD_SCALAR(fld, ammo_cells) \ - FIELD_SCALAR(fld, ammo_nails) \ - FIELD_SCALAR(fld, ammo_rockets) \ - FIELD_SCALAR(fld, armorvalue) \ - FIELD_SCALAR(fld, atten) \ - FIELD_SCALAR(fld, bgmscriptdecay) \ - FIELD_SCALAR(fld, bgmscriptsustain) \ - FIELD_SCALAR(fld, bgmscript) \ - FIELD_SCALAR(fld, button0) \ - FIELD_SCALAR(fld, cnt) \ - FIELD_SCALAR(fld, colormap) \ - FIELD_SCALAR(fld, count) \ - FIELD_SCALAR(fld, curvetarget) \ - FIELD_SCALAR(fld, cvarfilter) \ - FIELD_SCALAR(fld, debrisdamageforcescale) \ - FIELD_SCALAR(fld, debrisfadetime) \ - FIELD_SCALAR(fld, debristimejitter) \ - FIELD_SCALAR(fld, debristime) \ - FIELD_SCALAR(fld, debris) \ - FIELD_SCALAR(fld, delay) \ - FIELD_SCALAR(fld, dmgtime) \ - FIELD_SCALAR(fld, dmg) \ - FIELD_SCALAR(fld, dmg_edge) \ - FIELD_SCALAR(fld, dmg_force) \ - FIELD_SCALAR(fld, dmg_radius) \ - FIELD_SCALAR(fld, effects) \ - FIELD_SCALAR(fld, flags) \ - FIELD_SCALAR(fld, fog) \ - FIELD_SCALAR(fld, frags) \ - FIELD_SCALAR(fld, frame) \ - FIELD_SCALAR(fld, gametypefilter) \ - FIELD_SCALAR(fld, geomtype) \ - FIELD_SCALAR(fld, gravity) \ - FIELD_SCALAR(fld, health) \ - FIELD_SCALAR(fld, height) \ - FIELD_SCALAR(fld, impulse) \ - 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, ltime) \ - FIELD_SCALAR(fld, mdl) \ - FIELD_SCALAR(fld, message2) \ - FIELD_SCALAR(fld, message) \ - FIELD_SCALAR(fld, modelindex) \ - FIELD_SCALAR(fld, modelscale) \ - FIELD_SCALAR(fld, model) \ - FIELD_SCALAR(fld, monster_moveflags) \ - FIELD_SCALAR(fld, movetype) \ - FIELD_SCALAR(fld, netname) \ - FIELD_SCALAR(fld, nextthink) \ - FIELD_SCALAR(fld, noalign) \ - FIELD_SCALAR(fld, noise1) \ - FIELD_SCALAR(fld, noise2) \ - FIELD_SCALAR(fld, noise) \ - FIELD_SCALAR(fld, phase) \ - FIELD_SCALAR(fld, platmovetype) \ - FIELD_SCALAR(fld, race_place) \ - FIELD_SCALAR(fld, radius) \ - FIELD_SCALAR(fld, respawntimejitter) \ - FIELD_SCALAR(fld, respawntime) \ - FIELD_SCALAR(fld, restriction) \ - FIELD_SCALAR(fld, scale) \ - FIELD_SCALAR(fld, skin) \ - FIELD_SCALAR(fld, solid) \ - FIELD_SCALAR(fld, sound1) \ - FIELD_SCALAR(fld, sounds) \ - FIELD_SCALAR(fld, spawnflags) \ - FIELD_SCALAR(fld, speed) \ - FIELD_SCALAR(fld, strength) \ - FIELD_SCALAR(fld, target2) \ - FIELD_SCALAR(fld, target3) \ - FIELD_SCALAR(fld, target4) \ - FIELD_SCALAR(fld, targetname) \ - FIELD_SCALAR(fld, target) \ - FIELD_SCALAR(fld, target_random) \ - FIELD_SCALAR(fld, target_range) \ - FIELD_SCALAR(fld, team) \ - FIELD_SCALAR(fld, turret_scale_health) \ - FIELD_SCALAR(fld, turret_scale_range) \ - FIELD_SCALAR(fld, turret_scale_respawn) \ - FIELD_SCALAR(fld, volume) \ - FIELD_SCALAR(fld, wait) \ - FIELD_SCALAR(fld, warpzone_fadeend) \ - FIELD_SCALAR(fld, warpzone_fadestart) \ - FIELD_SCALAR(fld, weapon) \ - FIELD_VEC(fld, absmax) \ - FIELD_VEC(fld, absmin) \ - FIELD_VEC(fld, angles) \ - FIELD_VEC(fld, avelocity) \ - FIELD_VEC(fld, maxs) \ - FIELD_VEC(fld, maxs) \ - FIELD_VEC(fld, mins) \ - FIELD_VEC(fld, modelscale_vec) \ - FIELD_VEC(fld, origin) \ - FIELD_VEC(fld, velocity) \ - /**/ + #define FIELDS_UNION(fld) \ + FIELD_SCALAR(fld, sourceLocFile) \ + FIELD_SCALAR(fld, sourceLocLine) \ + FIELD_SCALAR(fld, Version) \ + FIELD_SCALAR(fld, ammo_cells) \ + FIELD_SCALAR(fld, ammo_nails) \ + FIELD_SCALAR(fld, ammo_rockets) \ + FIELD_SCALAR(fld, armorvalue) \ + FIELD_SCALAR(fld, atten) \ + FIELD_SCALAR(fld, bgmscriptdecay) \ + FIELD_SCALAR(fld, bgmscriptsustain) \ + FIELD_SCALAR(fld, bgmscript) \ + FIELD_SCALAR(fld, button0) \ + FIELD_SCALAR(fld, cnt) \ + FIELD_SCALAR(fld, colormap) \ + FIELD_SCALAR(fld, count) \ + FIELD_SCALAR(fld, curvetarget) \ + FIELD_SCALAR(fld, cvarfilter) \ + FIELD_SCALAR(fld, debrisdamageforcescale) \ + FIELD_SCALAR(fld, debrisfadetime) \ + FIELD_SCALAR(fld, debristimejitter) \ + FIELD_SCALAR(fld, debristime) \ + FIELD_SCALAR(fld, debris) \ + FIELD_SCALAR(fld, delay) \ + FIELD_SCALAR(fld, dmgtime) \ + FIELD_SCALAR(fld, dmg) \ + FIELD_SCALAR(fld, dmg_edge) \ + FIELD_SCALAR(fld, dmg_force) \ + FIELD_SCALAR(fld, dmg_radius) \ + FIELD_SCALAR(fld, effects) \ + FIELD_SCALAR(fld, flags) \ + FIELD_SCALAR(fld, fog) \ + FIELD_SCALAR(fld, frags) \ + FIELD_SCALAR(fld, frame) \ + FIELD_SCALAR(fld, gametypefilter) \ + FIELD_SCALAR(fld, geomtype) \ + FIELD_SCALAR(fld, gravity) \ + FIELD_SCALAR(fld, health) \ + FIELD_SCALAR(fld, height) \ + FIELD_SCALAR(fld, impulse) \ + 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, ltime) \ + FIELD_SCALAR(fld, mdl) \ + FIELD_SCALAR(fld, message2) \ + FIELD_SCALAR(fld, message) \ + FIELD_SCALAR(fld, modelindex) \ + FIELD_SCALAR(fld, modelscale) \ + FIELD_SCALAR(fld, model) \ + FIELD_SCALAR(fld, monster_moveflags) \ + FIELD_SCALAR(fld, movetype) \ + FIELD_SCALAR(fld, netname) \ + FIELD_SCALAR(fld, nextthink) \ + FIELD_SCALAR(fld, noalign) \ + FIELD_SCALAR(fld, noise1) \ + FIELD_SCALAR(fld, noise2) \ + FIELD_SCALAR(fld, noise) \ + FIELD_SCALAR(fld, phase) \ + FIELD_SCALAR(fld, platmovetype) \ + FIELD_SCALAR(fld, race_place) \ + FIELD_SCALAR(fld, radius) \ + FIELD_SCALAR(fld, respawntimejitter) \ + FIELD_SCALAR(fld, respawntime) \ + FIELD_SCALAR(fld, restriction) \ + FIELD_SCALAR(fld, scale) \ + FIELD_SCALAR(fld, skin) \ + FIELD_SCALAR(fld, solid) \ + FIELD_SCALAR(fld, sound1) \ + FIELD_SCALAR(fld, sounds) \ + FIELD_SCALAR(fld, spawnflags) \ + FIELD_SCALAR(fld, speed) \ + FIELD_SCALAR(fld, strength) \ + FIELD_SCALAR(fld, target2) \ + FIELD_SCALAR(fld, target3) \ + FIELD_SCALAR(fld, target4) \ + FIELD_SCALAR(fld, targetname) \ + FIELD_SCALAR(fld, target) \ + FIELD_SCALAR(fld, target_random) \ + FIELD_SCALAR(fld, target_range) \ + FIELD_SCALAR(fld, team) \ + FIELD_SCALAR(fld, turret_scale_health) \ + FIELD_SCALAR(fld, turret_scale_range) \ + FIELD_SCALAR(fld, turret_scale_respawn) \ + FIELD_SCALAR(fld, volume) \ + FIELD_SCALAR(fld, wait) \ + FIELD_SCALAR(fld, warpzone_fadeend) \ + FIELD_SCALAR(fld, warpzone_fadestart) \ + FIELD_SCALAR(fld, weapon) \ + FIELD_VEC(fld, absmax) \ + FIELD_VEC(fld, absmin) \ + FIELD_VEC(fld, angles) \ + FIELD_VEC(fld, avelocity) \ + FIELD_VEC(fld, maxs) \ + FIELD_VEC(fld, maxs) \ + FIELD_VEC(fld, mins) \ + FIELD_VEC(fld, modelscale_vec) \ + FIELD_VEC(fld, origin) \ + FIELD_VEC(fld, velocity) \ + /**/ -#define spawnfunc(...) EVAL(OVERLOAD(spawnfunc, __VA_ARGS__, FIELDS_UNION)) + #define spawnfunc(...) EVAL(OVERLOAD(spawnfunc, __VA_ARGS__, FIELDS_UNION)) #endif diff --git a/qcsrc/lib/static.qh b/qcsrc/lib/static.qh index f7bcdcc296..70eafd1b7d 100644 --- a/qcsrc/lib/static.qh +++ b/qcsrc/lib/static.qh @@ -1,19 +1,20 @@ #ifndef STATIC_H #define STATIC_H -void __static_init() { } +void __static_init() {} #define static_init() CALL_ACCUMULATED_FUNCTION(__static_init) -void __static_init_late() { } +void __static_init_late() {} #define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_late) - -#define REGISTER_REGISTRY(func) ACCUMULATE_FUNCTION(__static_init, func) +void __static_init_precache() {} +#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_precache) #define _STATIC_INIT(where, func) \ - void _static_##func(); \ - ACCUMULATE_FUNCTION(where, _static_##func) \ - void _static_##func() + void _static_##func(); \ + ACCUMULATE_FUNCTION(where, _static_##func) \ + void _static_##func() -#define STATIC_INIT(func) _STATIC_INIT(__static_init, func) -#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late) +#define STATIC_INIT(func) _STATIC_INIT(__static_init, func) +#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late) +#define PRECACHE(func) _STATIC_INIT(__static_init_precache, func##_precache) #endif diff --git a/qcsrc/lib/stats.qh b/qcsrc/lib/stats.qh new file mode 100644 index 0000000000..7dd2706fd2 --- /dev/null +++ b/qcsrc/lib/stats.qh @@ -0,0 +1,72 @@ +#ifndef LIB_STATS_H +#define LIB_STATS_H + +#include "registry.qh" +#include "sort.qh" + +.int m_id; + +#if defined(CSQC) + /** Get all stats and store them as globals, access with `STAT(ID)` */ + void stats_get() {} + #define STAT(...) EVAL(OVERLOAD(STAT, __VA_ARGS__)) + #define STAT_1(id) STAT_2(id, NULL) + #define STAT_2(id, cl) (0, _STAT(id)) + + #define getstat_int(id) getstati(id, 0, 24) + #define getstat_bool(id) boolean(getstati(id)) + #define getstat_float(id) getstatf(id) + + #define _STAT(id) g_stat_##id + #define REGISTER_STAT(id, type) \ + type _STAT(id); \ + REGISTER(Stats, STAT, id, m_id, new(stat)) \ + { \ + make_pure(this); \ + } \ + [[accumulate]] void stats_get() \ + { \ + _STAT(id) = getstat_##type(STAT_##id.m_id); \ + } +#elif defined(SVQC) + /** Add all registered stats, access with `STAT(ID, player)` or `.type stat = _STAT(ID); player.stat` */ + void stats_add() {} + #define STAT(id, cl) (cl._STAT(id)) + + #define addstat_int(id, fld) addstat(id, AS_INT, fld) + #define addstat_bool(id, fld) addstat(id, AS_INT, fld) + #define addstat_float(id, fld) addstat(id, AS_FLOAT, fld) + const int AS_STRING = 1; + const int AS_INT = 2; + const int AS_FLOAT = 8; + + #define _STAT(id) stat_##id + #define REGISTER_STAT(id, type) \ + .type _STAT(id); \ + REGISTER(Stats, STAT, id, m_id, new(stat)) \ + { \ + make_pure(this); \ + } \ + [[accumulate]] void stats_add() \ + { \ + addstat_##type(STAT_##id.m_id, _STAT(id)); \ + } +#else + #define REGISTER_STAT(id, type) +#endif + +const int STATS_ENGINE_RESERVE = 32 + (8 * 3); // Not sure how to handle vector stats yet, reserve them too + +REGISTRY(Stats, 220 - STATS_ENGINE_RESERVE) +REGISTER_REGISTRY(Stats) +REGISTRY_SORT(Stats, 0) +REGISTRY_CHECK(Stats) +STATIC_INIT(RegisterStats_renumber) +{ + FOREACH(Stats, true, LAMBDA(it.m_id = STATS_ENGINE_RESERVE + i)); +} +#ifdef SVQC +STATIC_INIT(stats_add) { stats_add(); } +#endif + +#endif diff --git a/qcsrc/lib/string.qh b/qcsrc/lib/string.qh index ede2edf1f4..6f61155800 100644 --- a/qcsrc/lib/string.qh +++ b/qcsrc/lib/string.qh @@ -1,33 +1,66 @@ #ifndef STRING_H #define STRING_H +#include "nil.qh" +#include "sort.qh" +#include "oo.qh" + #ifndef SVQC -float stringwidth_colors(string s, vector theSize) + float stringwidth_colors(string s, vector theSize) + { + return stringwidth(s, true, theSize); + } + + float stringwidth_nocolors(string s, vector theSize) + { + return stringwidth(s, false, theSize); + } +#endif + +// TODO: macro +string seconds_tostring(float sec) { - return stringwidth(s, true, theSize); + float minutes = floor(sec / 60); + sec -= minutes * 60; + return sprintf("%d:%02d", minutes, sec); } -float stringwidth_nocolors(string s, vector theSize) +string format_time(float seconds) { - return stringwidth(s, false, theSize); + seconds = floor(seconds + 0.5); + float days = floor(seconds / 864000); + seconds -= days * 864000; + float hours = floor(seconds / 36000); + seconds -= hours * 36000; + float minutes = floor(seconds / 600); + seconds -= minutes * 600; + if (days > 0) return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds); + else return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds); } -#endif -// Timer (#5) -// -// TODO: macro -string seconds_tostring(float sec) +string mmsss(float tenths) +{ + tenths = floor(tenths + 0.5); + float minutes = floor(tenths / 600); + tenths -= minutes * 600; + string s = ftos(1000 + tenths); + return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1)); +} + +string mmssss(float hundredths) { - float minutes = floor(sec / 60); - sec -= minutes * 60; - return sprintf("%d:%02d", minutes, sec); + hundredths = floor(hundredths + 0.5); + float minutes = floor(hundredths / 6000); + hundredths -= minutes * 6000; + string s = ftos(10000 + hundredths); + return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2)); } int ColorTranslateMode; string ColorTranslateRGB(string s) { - return (ColorTranslateMode & 1) ? strdecolorize(s) : s; + return (ColorTranslateMode & 1) ? strdecolorize(s) : s; } // color code replace, place inside of sprintf and parse the string... defaults described as constants @@ -37,155 +70,251 @@ string autocvar_hud_colorset_foreground_2 = "3"; // F2 - Yellow // secondary pri string autocvar_hud_colorset_foreground_3 = "4"; // F3 - Blue // tertiary priority or relatively inconsequential text string autocvar_hud_colorset_foreground_4 = "1"; // F4 - Red // notice/attention grabbing texting // "kill" colors -string autocvar_hud_colorset_kill_1 = "1"; // K1 - Red // "bad" or "dangerous" text (death messages against you, kill notifications, etc) -string autocvar_hud_colorset_kill_2 = "3"; // K2 - Yellow // similar to above, but less important... OR, a highlight out of above message type -string autocvar_hud_colorset_kill_3 = "4"; // K3 - Blue // "good" or "beneficial" text (you fragging someone, etc) +string autocvar_hud_colorset_kill_1 = "1"; // K1 - Red // "bad" or "dangerous" text (death messages against you, kill notifications, etc) +string autocvar_hud_colorset_kill_2 = "3"; // K2 - Yellow // similar to above, but less important... OR, a highlight out of above message type +string autocvar_hud_colorset_kill_3 = "4"; // K3 - Blue // "good" or "beneficial" text (you fragging someone, etc) // background color -string autocvar_hud_colorset_background = "7"; // BG - White // neutral/unimportant text +string autocvar_hud_colorset_background = "7"; // BG - White // neutral/unimportant text /** color code replace, place inside of sprintf and parse the string */ string CCR(string input) { - // See the autocvar declarations in util.qh for default values + // See the autocvar declarations in util.qh for default values - // foreground/normal colors - input = strreplace("^F1", strcat("^", autocvar_hud_colorset_foreground_1), input); - input = strreplace("^F2", strcat("^", autocvar_hud_colorset_foreground_2), input); - input = strreplace("^F3", strcat("^", autocvar_hud_colorset_foreground_3), input); - input = strreplace("^F4", strcat("^", autocvar_hud_colorset_foreground_4), input); + // foreground/normal colors + input = strreplace("^F1", strcat("^", autocvar_hud_colorset_foreground_1), input); + input = strreplace("^F2", strcat("^", autocvar_hud_colorset_foreground_2), input); + input = strreplace("^F3", strcat("^", autocvar_hud_colorset_foreground_3), input); + input = strreplace("^F4", strcat("^", autocvar_hud_colorset_foreground_4), input); - // "kill" colors - input = strreplace("^K1", strcat("^", autocvar_hud_colorset_kill_1), input); - input = strreplace("^K2", strcat("^", autocvar_hud_colorset_kill_2), input); - input = strreplace("^K3", strcat("^", autocvar_hud_colorset_kill_3), input); + // "kill" colors + input = strreplace("^K1", strcat("^", autocvar_hud_colorset_kill_1), input); + input = strreplace("^K2", strcat("^", autocvar_hud_colorset_kill_2), input); + input = strreplace("^K3", strcat("^", autocvar_hud_colorset_kill_3), input); - // background colors - input = strreplace("^BG", strcat("^", autocvar_hud_colorset_background), input); - input = strreplace("^N", "^7", input); // "none"-- reset to white... - return input; + // background colors + input = strreplace("^BG", strcat("^", autocvar_hud_colorset_background), input); + input = strreplace("^N", "^7", input); // "none"-- reset to white... + return input; } -bool startsWith(string haystack, string needle) -{ - return substring(haystack, 0, strlen(needle)) == needle; -} +#define startsWith(haystack, needle) (strstrofs(haystack, needle, 0) == 0) bool startsWithNocase(string haystack, string needle) { - return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0; + return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0; } /** unzone the string, and return it as tempstring. Safe to be called on string_null */ string fstrunzone(string s) { - if (!s) return s; - string sc = strcat(s, ""); - strunzone(s); - return sc; + if (!s) return s; + string sc = strcat(s, ""); + strunzone(s); + return sc; } +/** returns first word */ string car(string s) { - int o = strstrofs(s, " ", 0); - if (o < 0) return s; - return substring(s, 0, o); + int o = strstrofs(s, " ", 0); + if (o < 0) return s; + return substring(s, 0, o); } +/** returns all but first word */ string cdr(string s) { - int o = strstrofs(s, " ", 0); - if (o < 0) return string_null; - return substring(s, o + 1, strlen(s) - (o + 1)); + int o = strstrofs(s, " ", 0); + if (o < 0) return string_null; + return substring(s, o + 1, strlen(s) - (o + 1)); } string substring_range(string s, float b, float e) { - return substring(s, b, e - b); + return substring(s, b, e - b); } string swapwords(string str, float i, float j) { - float n; - string s1, s2, s3, s4, s5; - float si, ei, sj, ej, s0, en; - n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle" - si = argv_start_index(i); - sj = argv_start_index(j); - ei = argv_end_index(i); - ej = argv_end_index(j); - s0 = argv_start_index(0); - en = argv_end_index(n-1); - s1 = substring_range(str, s0, si); - s2 = substring_range(str, si, ei); - s3 = substring_range(str, ei, sj); - s4 = substring_range(str, sj, ej); - s5 = substring_range(str, ej, en); - return strcat(s1, s4, s3, s2, s5); + float n; + string s1, s2, s3, s4, s5; + float si, ei, sj, ej, s0, en; + n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle" + si = argv_start_index(i); + sj = argv_start_index(j); + ei = argv_end_index(i); + ej = argv_end_index(j); + s0 = argv_start_index(0); + en = argv_end_index(n - 1); + s1 = substring_range(str, s0, si); + s2 = substring_range(str, si, ei); + s3 = substring_range(str, ei, sj); + s4 = substring_range(str, sj, ej); + s5 = substring_range(str, ej, en); + return strcat(s1, s4, s3, s2, s5); } string _shufflewords_str; void _shufflewords_swapfunc(float i, float j, entity pass) { - _shufflewords_str = swapwords(_shufflewords_str, i, j); + _shufflewords_str = swapwords(_shufflewords_str, i, j); } string shufflewords(string str) { - _shufflewords_str = str; - int n = tokenizebyseparator(str, " "); - shuffle(n, _shufflewords_swapfunc, NULL); - str = _shufflewords_str; - _shufflewords_str = string_null; - return str; + _shufflewords_str = str; + int n = tokenizebyseparator(str, " "); + shuffle(n, _shufflewords_swapfunc, NULL); + str = _shufflewords_str; + _shufflewords_str = string_null; + return str; } string unescape(string in) { - in = strzone(in); // but it doesn't seem to be necessary in my tests at least - - int len = strlen(in); - string str = ""; - for (int i = 0; i < len; ++i) { - string s = substring(in, i, 1); - if (s == "\\") { - s = substring(in, i + 1, 1); - if (s == "n") - str = strcat(str, "\n"); - else if (s == "\\") - str = strcat(str, "\\"); - else - str = strcat(str, substring(in, i, 2)); - ++i; - continue; - } - str = strcat(str, s); - } - strunzone(in); - return str; + in = strzone(in); // but it doesn't seem to be necessary in my tests at least + + int len = strlen(in); + string str = ""; + for (int i = 0; i < len; ++i) + { + string s = substring(in, i, 1); + if (s == "\\") + { + s = substring(in, i + 1, 1); + if (s == "n") str = strcat(str, "\n"); + else if (s == "\\") str = strcat(str, "\\"); + else str = strcat(str, substring(in, i, 2)); + ++i; + continue; + } + str = strcat(str, s); + } + strunzone(in); + return str; } string strwords(string s, int w) { - int endpos = 0; - for (; w && endpos >= 0; --w) endpos = strstrofs(s, " ", endpos + 1); - if (endpos < 0) return s; - return substring(s, 0, endpos); + int endpos = 0; + for ( ; w && endpos >= 0; --w) + endpos = strstrofs(s, " ", endpos + 1); + if (endpos < 0) return s; + return substring(s, 0, endpos); } -bool strhasword(string s, string w) +#define strhasword(s, w) (strstrofs(strcat(" ", s, " "), strcat(" ", w, " "), 0) >= 0) + +int u8_strsize(string s) { - return strstrofs(strcat(" ", s, " "), strcat(" ", w, " "), 0) >= 0; + int l = 0; + for (int i = 0, c; (c = str2chr(s, i)) > 0; ++i, ++l) + { + l += (c >= 0x80); + l += (c >= 0x800); + l += (c >= 0x10000); + } + return l; } -int u8_strsize(string s) +bool isInvisibleString(string s) +{ + s = strdecolorize(s); + bool utf8 = cvar("utf8_enable"); + for (int i = 0, n = strlen(s); i < n; ++i) + { + int c = str2chr(s, i); + switch (c) + { + case 0: + case 32: // space + break; + case 192: // charmap space + if (!utf8) break; + return false; + case 160: // space in unicode fonts + case 0xE000 + 192: // utf8 charmap space + if (utf8) break; + default: + return false; + } + } + return true; +} + +// Multiline text file buffers + +int buf_load(string pFilename) +{ + int buf = buf_create(); + if (buf < 0) return -1; + int fh = fopen(pFilename, FILE_READ); + if (fh < 0) + { + buf_del(buf); + return -1; + } + string l; + for (int i = 0; (l = fgets(fh)); ++i) + bufstr_set(buf, i, l); + fclose(fh); + return buf; +} + +void buf_save(float buf, string pFilename) +{ + int fh = fopen(pFilename, FILE_WRITE); + if (fh < 0) error(strcat("Can't write buf to ", pFilename)); + int n = buf_getsize(buf); + for (int i = 0; i < n; ++i) + fputs(fh, strcat(bufstr_get(buf, i), "\n")); + fclose(fh); +} + +/** + * converts a number to a string with the indicated number of decimals + * works for up to 10 decimals! + */ +string ftos_decimals(float number, int decimals) +{ + // inhibit stupid negative zero + if (number == 0) number = 0; + // we have sprintf... + return sprintf("%.*f", decimals, number); +} + +int vercmp_recursive(string v1, string v2) +{ + int dot1 = strstrofs(v1, ".", 0); + int dot2 = strstrofs(v2, ".", 0); + string s1 = (dot1 == -1) ? v1 : substring(v1, 0, dot1); + string s2 = (dot2 == -1) ? v2 : substring(v2, 0, dot2); + + float r; + r = stof(s1) - stof(s2); + if (r != 0) return r; + + r = strcasecmp(s1, s2); + if (r != 0) return r; + + if (dot1 == -1) return (dot2 == -1) ? 0 : -1; + else return (dot2 == -1) ? 1 : vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999)); +} + +int vercmp(string v1, string v2) { - int l = 0; - for (int i = 0, c; (c = str2chr(s, i)) > 0; ++i, ++l) - { - l += (c >= 0x80); - l += (c >= 0x800); - l += (c >= 0x10000); - } - return l; + if (strcasecmp(v1, v2) == 0) return 0; // early out check + + // "git" beats all + if (v1 == "git") return 1; + if (v2 == "git") return -1; + + return vercmp_recursive(v1, v2); } +const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef"; +#define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0)) +#define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10) +#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1)) + #endif diff --git a/qcsrc/lib/struct.qh b/qcsrc/lib/struct.qh index 507c417f57..bce19f4fd6 100644 --- a/qcsrc/lib/struct.qh +++ b/qcsrc/lib/struct.qh @@ -2,34 +2,34 @@ #define STRUCT_H #ifndef QCC_SUPPORT_STRUCT - #define _STRUCT_DECLARE(x, id, type, END) noref type x ##_## id ; - #define STRUCT_DECLARE(id, s) s(_STRUCT_DECLARE, id) + #define _STRUCT_DECLARE(x, id, type, END) noref type x##_##id; + #define STRUCT_DECLARE(id, s) s(_STRUCT_DECLARE, id) - #define _STRUCT_PARAM_(x, id, type) type x ##_## id , - #define _STRUCT_PARAM_END(x, id, type) type x ##_## id - #define _STRUCT_PARAM(x, id, type, isend) _STRUCT_PARAM_##isend(x, id, type) - #define STRUCT_PARAM(id, s) s(_STRUCT_PARAM, id) + #define _STRUCT_PARAM_(x, id, type) type x##_##id, + #define _STRUCT_PARAM_END(x, id, type) type x##_##id + #define _STRUCT_PARAM(x, id, type, isend) _STRUCT_PARAM_##isend(x, id, type) + #define STRUCT_PARAM(id, s) s(_STRUCT_PARAM, id) - #define _STRUCT_PASS_(x, id, type) x ##_## id , - #define _STRUCT_PASS_END(x, id, type) x ##_## id - #define _STRUCT_PASS(x, id, type, END) _STRUCT_PASS_##END(x, id, type) - #define STRUCT_PASS(id, s) s(_STRUCT_PASS, id) + #define _STRUCT_PASS_(x, id, type) x##_##id, + #define _STRUCT_PASS_END(x, id, type) x##_##id + #define _STRUCT_PASS(x, id, type, END) _STRUCT_PASS_##END(x, id, type) + #define STRUCT_PASS(id, s) s(_STRUCT_PASS, id) - #define _STRUCT_STORE_DST(_, it) it - #define _STRUCT_STORE_SRC(it, _) it - #define _CONCAT3_(a, b, c) a ## b ## c - #define _CONCAT3(a, b, c) _CONCAT3_(a, b, c) - #define _STRUCT_STORE(x, id, type, END) _CONCAT3(_STRUCT_STORE_DST x, _, id) = _CONCAT3(_STRUCT_STORE_SRC x, _, id); - #define STRUCT_STORE(from, to, s) s(_STRUCT_STORE, (from, to)) + #define _STRUCT_STORE_DST(_, it) it + #define _STRUCT_STORE_SRC(it, _) it + #define _CONCAT3_(a, b, c) a##b##c + #define _CONCAT3(a, b, c) _CONCAT3_(a, b, c) + #define _STRUCT_STORE(x, id, type, END) _CONCAT3(_STRUCT_STORE_DST x, _, id) = _CONCAT3(_STRUCT_STORE_SRC x, _, id); + #define STRUCT_STORE(from, to, s) s(_STRUCT_STORE, (from, to)) - #define STRUCT(id, ...) + #define STRUCT(id, ...) #else - #define STRUCT_DECLARE(id, type) type id; - #define STRUCT_PARAM(id, type) type id - #define STRUCT_PASS(id, type) id - #define STRUCT_STORE(from, to, s) to = from - #define _STRUCT_MEMBER(my, id, type, END) type id; - #define STRUCT(id, s) struct STRUCT_##id { s(_STRUCT_MEMBER, ) }; + #define STRUCT_DECLARE(id, type) type id; + #define STRUCT_PARAM(id, type) type id + #define STRUCT_PASS(id, type) id + #define STRUCT_STORE(from, to, s) to = from + #define _STRUCT_MEMBER(my, id, type, END) type id; + #define STRUCT(id, s) struct STRUCT_##id { s(_STRUCT_MEMBER, ) }; #endif #endif diff --git a/qcsrc/lib/test.qc b/qcsrc/lib/test.qc index 23907fe5f1..3929bf1c38 100644 --- a/qcsrc/lib/test.qc +++ b/qcsrc/lib/test.qc @@ -6,7 +6,7 @@ float TEST_ok; void TEST_Fail(string cond) { LOG_INFOF("Assertion failed: ", cond); - //backtrace(); + // backtrace(); ++TEST_failed; } @@ -19,14 +19,13 @@ float TEST_RunAll() { int f = 0; float n = numentityfields(); - for(int i = 0; i < n; ++i) + for (int i = 0; i < n; ++i) { string name = entityfieldname(i); - if(substring(name, 0, 6) == "_TEST_") - if(!TEST_Run(substring(name, 6, -1))) - ++f; + if (substring(name, 0, 6) == "_TEST_") + if (!TEST_Run(substring(name, 6, -1))) ++f; } - if(f) + if (f) { LOG_INFOF("%d tests failed\n", f); return 1; @@ -43,12 +42,12 @@ float TEST_Run(string s) LOG_INFOF("%s: testing...\n", s); TEST_failed = TEST_ok = 0; callfunction(strcat("_TEST_", s)); - if(TEST_failed > 0) + if (TEST_failed > 0) { LOG_INFOF("%s: %d items failed.\n", s, TEST_failed); return 0; } - else if(!TEST_ok) + else if (!TEST_ok) { LOG_INFOF("%s: did not complete.\n", s); return 0; diff --git a/qcsrc/lib/test.qh b/qcsrc/lib/test.qh index edc2f5bf37..6cddb12e50 100644 --- a/qcsrc/lib/test.qh +++ b/qcsrc/lib/test.qh @@ -1,7 +1,12 @@ #ifndef TEST_H #define TEST_H -#define TEST_Check(cond) do { if(!(cond)) TEST_Fail(#cond); } while(0) +#define TEST_Check(cond) \ + do \ + { \ + if (!(cond)) TEST_Fail( #cond); \ + } \ + while (0) void TEST_OK(); void TEST_Fail(string cond); diff --git a/qcsrc/lib/urllib.qc b/qcsrc/lib/urllib.qc index 9648b2080f..0a48ef6cd3 100644 --- a/qcsrc/lib/urllib.qc +++ b/qcsrc/lib/urllib.qc @@ -24,16 +24,13 @@ int autocvar__urllib_nextslot; float url_URI_Get_Callback(int id, float status, string data) { - if(id < MIN_URL_ID) - return 0; + if (id < MIN_URL_ID) return 0; id -= MIN_URL_ID; - if(id >= NUM_URL_ID) - return 0; + if (id >= NUM_URL_ID) return 0; entity e; e = url_fromid[id]; - if(!e) - return 0; - if(e.url_rbuf >= 0 || e.url_wbuf >= 0) + if (!e) return 0; + if (e.url_rbuf >= 0 || e.url_wbuf >= 0) { LOG_INFOF("WARNING: handle %d (%s) has already received data?!?\n", id + NUM_URL_ID, e.url_url); return 0; @@ -43,16 +40,15 @@ float url_URI_Get_Callback(int id, float status, string data) url_fromid[id] = NULL; // if we get here, we MUST have both buffers cleared - if(e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL) - error("url_URI_Get_Callback: not a request waiting for data"); + if (e.url_rbuf != -1 || e.url_wbuf != -1 || e.url_fh != URL_FH_CURL) error("url_URI_Get_Callback: not a request waiting for data"); - if(status == 0) + if (status == 0) { // WE GOT DATA! float n, i; n = tokenizebyseparator(data, "\n"); e.url_rbuf = buf_create(); - if(e.url_rbuf < 0) + if (e.url_rbuf < 0) { LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); @@ -61,7 +57,7 @@ float url_URI_Get_Callback(int id, float status, string data) return 1; } e.url_rbufpos = 0; - if(e.url_rbuf < 0) + if (e.url_rbuf < 0) { LOG_INFO("url_URI_Get_Callback: out of memory in buf_create\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); @@ -69,7 +65,7 @@ float url_URI_Get_Callback(int id, float status, string data) remove(e); return 1; } - for(i = 0; i < n; ++i) + for (i = 0; i < n; ++i) bufstr_set(e.url_rbuf, i, argv(i)); e.url_ready(e, e.url_ready_pass, URL_READY_CANREAD); return 1; @@ -88,9 +84,9 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) { entity e; int i; - if(strstrofs(url, "://", 0) >= 0) + if (strstrofs(url, "://", 0) >= 0) { - switch(mode) + switch (mode) { case FILE_WRITE: case FILE_APPEND: @@ -98,12 +94,12 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) // attempts to close will result in a reading handle // create a writing end that does nothing yet - e = spawn(); - e.classname = "url_single_fopen_file"; + e = new(url_single_fopen_file); + make_pure(e); e.url_url = strzone(url); e.url_fh = URL_FH_CURL; e.url_wbuf = buf_create(); - if(e.url_wbuf < 0) + if (e.url_wbuf < 0) { LOG_INFO("url_single_fopen: out of memory in buf_create\n"); rdy(e, pass, URL_READY_ERROR); @@ -122,15 +118,13 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) // read data only // get slot for HTTP request - for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i) - if(url_fromid[i] == NULL) - break; - if(i >= NUM_URL_ID) + for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i) + if (url_fromid[i] == NULL) break; + if (i >= NUM_URL_ID) { - for(i = 0; i < autocvar__urllib_nextslot; ++i) - if(url_fromid[i] == NULL) - break; - if(i >= autocvar__urllib_nextslot) + for (i = 0; i < autocvar__urllib_nextslot; ++i) + if (url_fromid[i] == NULL) break; + if (i >= autocvar__urllib_nextslot) { LOG_INFO("url_single_fopen: too many concurrent requests\n"); rdy(NULL, pass, URL_READY_ERROR); @@ -139,7 +133,7 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) } // GET the data - if(!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0)) + if (!crypto_uri_postbuf(url, i + MIN_URL_ID, string_null, string_null, -1, 0)) { LOG_INFO("url_single_fopen: failure in crypto_uri_postbuf\n"); rdy(NULL, pass, URL_READY_ERROR); @@ -149,8 +143,8 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) // Make a dummy handle object (no buffers at // all). Wait for data to come from the // server, then call the callback - e = spawn(); - e.classname = "url_single_fopen_file"; + e = new(url_single_fopen_file); + make_pure(e); e.url_url = strzone(url); e.url_fh = URL_FH_CURL; e.url_rbuf = -1; @@ -165,14 +159,14 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) break; } } - else if(url == "-") + else if (url == "-") { - switch(mode) + switch (mode) { case FILE_WRITE: case FILE_APPEND: - e = spawn(); - e.classname = "url_single_fopen_stdout"; + e = new(url_single_fopen_stdout); + make_pure(e); e.url_fh = URL_FH_STDOUT; e.url_ready = rdy; e.url_ready_pass = pass; @@ -188,22 +182,20 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) { float fh; fh = fopen(url, mode); - if(fh < 0) + if (fh < 0) { rdy(NULL, pass, URL_READY_ERROR); return; } else { - e = spawn(); - e.classname = "url_single_fopen_file"; + e = new(url_single_fopen_file); + make_pure(e); e.url_fh = fh; e.url_ready = rdy; e.url_ready_pass = pass; - if(mode == FILE_READ) - rdy(e, pass, URL_READY_CANREAD); - else - rdy(e, pass, URL_READY_CANWRITE); + if (mode == FILE_READ) rdy(e, pass, URL_READY_CANREAD); + else rdy(e, pass, URL_READY_CANWRITE); } } } @@ -213,30 +205,28 @@ void url_fclose(entity e) { int i; - if(e.url_fh == URL_FH_CURL) + if (e.url_fh == URL_FH_CURL) { - if(e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request) - if(e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request) - error("url_fclose: not closable in current state"); + if (e.url_rbuf == -1 || e.url_wbuf != -1) // not(post GET/POST request) + if (e.url_rbuf != -1 || e.url_wbuf == -1) // not(pre POST request) + error("url_fclose: not closable in current state"); // closing an URL! - if(e.url_wbuf >= 0) + if (e.url_wbuf >= 0) { // we are closing the write end (HTTP POST request) // get slot for HTTP request - for(i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i) - if(url_fromid[i] == NULL) - break; - if(i >= NUM_URL_ID) + for (i = autocvar__urllib_nextslot; i < NUM_URL_ID; ++i) + if (url_fromid[i] == NULL) break; + if (i >= NUM_URL_ID) { - for(i = 0; i < autocvar__urllib_nextslot; ++i) - if(url_fromid[i] == NULL) - break; - if(i >= autocvar__urllib_nextslot) + for (i = 0; i < autocvar__urllib_nextslot; ++i) + if (url_fromid[i] == NULL) break; + if (i >= autocvar__urllib_nextslot) { LOG_INFO("url_fclose: too many concurrent requests\n"); - e.url_ready(e,e.url_ready_pass, URL_READY_ERROR); + e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); buf_del(e.url_wbuf); strunzone(e.url_url); remove(e); @@ -245,7 +235,7 @@ void url_fclose(entity e) } // POST the data - if(!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0)) + if (!crypto_uri_postbuf(e.url_url, i + MIN_URL_ID, "text/plain", "", e.url_wbuf, 0)) { LOG_INFO("url_fclose: failure in crypto_uri_postbuf\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); @@ -275,16 +265,16 @@ void url_fclose(entity e) remove(e); } } - else if(e.url_fh == URL_FH_STDOUT) + else if (e.url_fh == URL_FH_STDOUT) { - e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle + e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle remove(e); } else { // file fclose(e.url_fh); - e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle + e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle remove(e); } } @@ -292,17 +282,16 @@ void url_fclose(entity e) // with \n (blame FRIK_FILE) string url_fgets(entity e) { - if(e.url_fh == URL_FH_CURL) + if (e.url_fh == URL_FH_CURL) { - if(e.url_rbuf == -1) - error("url_fgets: not readable in current state"); + if (e.url_rbuf == -1) error("url_fgets: not readable in current state"); // curl string s; s = bufstr_get(e.url_rbuf, e.url_rbufpos); e.url_rbufpos += 1; return s; } - else if(e.url_fh == URL_FH_STDOUT) + else if (e.url_fh == URL_FH_STDOUT) { // stdout return string_null; @@ -317,15 +306,14 @@ string url_fgets(entity e) // without \n (blame FRIK_FILE) void url_fputs(entity e, string s) { - if(e.url_fh == URL_FH_CURL) + if (e.url_fh == URL_FH_CURL) { - if(e.url_wbuf == -1) - error("url_fputs: not writable in current state"); + if (e.url_wbuf == -1) error("url_fputs: not writable in current state"); // curl bufstr_set(e.url_wbuf, e.url_wbufpos, s); e.url_wbufpos += 1; } - else if(e.url_fh == URL_FH_STDOUT) + else if (e.url_fh == URL_FH_STDOUT) { // stdout LOG_INFO(s); @@ -341,9 +329,9 @@ void url_fputs(entity e, string s) void url_multi_ready(entity fh, entity me, float status) { float n; - if(status == URL_READY_ERROR || status < 0) + if (status == URL_READY_ERROR || status < 0) { - if(status == -422) // Unprocessable Entity + if (status == -422) // Unprocessable Entity { LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing\n"); me.url_ready(fh, me.url_ready_pass, status); @@ -353,7 +341,7 @@ void url_multi_ready(entity fh, entity me, float status) } me.url_attempt += 1; n = tokenize_console(me.url_url); - if(n <= me.url_attempt) + if (n <= me.url_attempt) { me.url_ready(fh, me.url_ready_pass, status); strunzone(me.url_url); @@ -369,16 +357,15 @@ void url_multi_fopen(string url, int mode, url_ready_func rdy, entity pass) { float n; n = tokenize_console(url); - if(n <= 0) + if (n <= 0) { LOG_INFO("url_multi_fopen: need at least one URL\n"); rdy(NULL, pass, URL_READY_ERROR); return; } - entity me; - me = spawn(); - me.classname = "url_multi"; + entity me = new(url_multi); + make_pure(me); me.url_url = strzone(url); me.url_attempt = 0; me.url_mode = mode; diff --git a/qcsrc/lib/urllib.qh b/qcsrc/lib/urllib.qh index e4b8a8f273..918504ff8b 100644 --- a/qcsrc/lib/urllib.qh +++ b/qcsrc/lib/urllib.qh @@ -16,7 +16,7 @@ const float URL_READY_CLOSED = 0; const float URL_READY_CANWRITE = 1; const float URL_READY_CANREAD = 2; // errors: -1, or negative HTTP status code -typedef void(entity handle, entity pass, float status) url_ready_func; +typedef void (entity handle, entity pass, float status) url_ready_func; void url_single_fopen(string url, float mode, url_ready_func rdy, entity pass); void url_fclose(entity e); diff --git a/qcsrc/lib/vector.qh b/qcsrc/lib/vector.qh index b57e27aeb9..0cda013c0e 100644 --- a/qcsrc/lib/vector.qh +++ b/qcsrc/lib/vector.qh @@ -1,92 +1,122 @@ #ifndef VECTOR_H #define VECTOR_H +#define cross(a, b) ((a) >< (b)) +/* +vector cross(vector a, vector b) +{ + return + '1 0 0' * (a.y * b.z - a.z * b.y) + + '0 1 0' * (a.z * b.x - a.x * b.z) + + '0 0 1' * (a.x * b.y - a.y * b.x); +} +*/ + const vector eX = '1 0 0'; const vector eY = '0 1 0'; const vector eZ = '0 0 1'; vector randompos(vector m1, vector m2) { - vector v; - m2 = m2 - m1; - v_x = m2_x * random() + m1_x; - v_y = m2_y * random() + m1_y; - v_z = m2_z * random() + m1_z; - return v; + vector v; + m2 = m2 - m1; + v_x = m2_x * random() + m1_x; + v_y = m2_y * random() + m1_y; + v_z = m2_z * random() + m1_z; + return v; } float vlen2d(vector v) { - return sqrt(v.x * v.x + v.y * v.y); + return sqrt(v.x * v.x + v.y * v.y); } float vlen_maxnorm2d(vector v) { - return max(v.x, v.y, -v.x, -v.y); + return max(v.x, v.y, -v.x, -v.y); } float vlen_minnorm2d(vector v) { - return min(max(v.x, -v.x), max(v.y, -v.y)); + return min(max(v.x, -v.x), max(v.y, -v.y)); } float dist_point_line(vector p, vector l0, vector ldir) { - ldir = normalize(ldir); + ldir = normalize(ldir); - // remove the component in line direction - p = p - (p * ldir) * ldir; + // remove the component in line direction + p = p - (p * ldir) * ldir; - // vlen of the remaining vector - return vlen(p); + // vlen of the remaining vector + return vlen(p); } /** requires that m2>m1 in all coordinates, and that m4>m3 */ -float boxesoverlap(vector m1, vector m2, vector m3, vector m4) {return m2_x >= m3_x && m1_x <= m4_x && m2_y >= m3_y && m1_y <= m4_y && m2_z >= m3_z && m1_z <= m4_z;} +float boxesoverlap(vector m1, vector m2, vector m3, vector m4) { return m2_x >= m3_x && m1_x <= m4_x && m2_y >= m3_y && m1_y <= m4_y && m2_z >= m3_z && m1_z <= m4_z; } /** requires the same as boxesoverlap, but is a stronger condition */ -float boxinsidebox(vector smins, vector smaxs, vector bmins, vector bmaxs) {return smins.x >= bmins.x && smaxs.x <= bmaxs.x && smins.y >= bmins.y && smaxs.y <= bmaxs.y && smins.z >= bmins.z && smaxs.z <= bmaxs.z;} +float boxinsidebox(vector smins, vector smaxs, vector bmins, vector bmaxs) { return smins.x >= bmins.x && smaxs.x <= bmaxs.x && smins.y >= bmins.y && smaxs.y <= bmaxs.y && smins.z >= bmins.z && smaxs.z <= bmaxs.z; } vector vec2(vector v) { - v.z = 0; - return v; + v.z = 0; + return v; } vector vec3(float x, float y, float z) { - vector v; v.x = x; v.y = y; v.z = z; - return v; + vector v; + v.x = x; + v.y = y; + v.z = z; + return v; } -#ifndef MENUQC -vector get_corner_position(entity box, int corner) +vector rotate(vector v, float a) { - switch (corner) { - case 1: return vec3(box.absmin.x, box.absmin.y, box.absmin.z); - case 2: return vec3(box.absmax.x, box.absmin.y, box.absmin.z); - case 3: return vec3(box.absmin.x, box.absmax.y, box.absmin.z); - case 4: return vec3(box.absmin.x, box.absmin.y, box.absmax.z); - case 5: return vec3(box.absmax.x, box.absmax.y, box.absmin.z); - case 6: return vec3(box.absmin.x, box.absmax.y, box.absmax.z); - case 7: return vec3(box.absmax.x, box.absmin.y, box.absmax.z); - case 8: return vec3(box.absmax.x, box.absmax.y, box.absmax.z); - default: return '0 0 0'; - } + float a_sin = sin(a), a_cos = cos(a); + vector r = '0 0 0'; + r.x = v.x * a_cos + v.y * a_sin; + r.y = -1 * v.x * a_sin + v.y * a_cos; + return r; } -vector NearestPointOnBox(entity box, vector org) +vector yinvert(vector v) { - vector m1 = box.mins + box.origin; - vector m2 = box.maxs + box.origin; - - vector ret; - ret.x = bound(m1.x, org.x, m2.x); - ret.y = bound(m1.y, org.y, m2.y); - ret.z = bound(m1.z, org.z, m2.z); - return ret; + v.y = 1 - v.y; + return v; } + +#ifndef MENUQC + vector get_corner_position(entity box, int corner) + { + switch (corner) + { + case 1: return vec3(box.absmin.x, box.absmin.y, box.absmin.z); + case 2: return vec3(box.absmax.x, box.absmin.y, box.absmin.z); + case 3: return vec3(box.absmin.x, box.absmax.y, box.absmin.z); + case 4: return vec3(box.absmin.x, box.absmin.y, box.absmax.z); + case 5: return vec3(box.absmax.x, box.absmax.y, box.absmin.z); + case 6: return vec3(box.absmin.x, box.absmax.y, box.absmax.z); + case 7: return vec3(box.absmax.x, box.absmin.y, box.absmax.z); + case 8: return vec3(box.absmax.x, box.absmax.y, box.absmax.z); + default: return '0 0 0'; + } + } + + vector NearestPointOnBox(entity box, vector org) + { + vector m1 = box.mins + box.origin; + vector m2 = box.maxs + box.origin; + + vector ret; + ret.x = bound(m1.x, org.x, m2.x); + ret.y = bound(m1.y, org.y, m2.y); + ret.z = bound(m1.z, org.z, m2.z); + return ret; + } #endif #endif diff --git a/qcsrc/lib/warpzone/client.qc b/qcsrc/lib/warpzone/client.qc index aba39c2cb7..dd2b5cc9a1 100644 --- a/qcsrc/lib/warpzone/client.qc +++ b/qcsrc/lib/warpzone/client.qc @@ -25,13 +25,12 @@ void WarpZone_Fade_PreDraw() self.drawmask = MASK_NORMAL; } -void WarpZone_Read(float isnew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) +{ warpzone_warpzones_exist = 1; if (!self.enemy) { - self.enemy = spawn(); - self.enemy.classname = "warpzone_from"; + self.enemy = new(warpzone_from); } self.classname = "trigger_warpzone"; @@ -88,10 +87,11 @@ void WarpZone_Read(float isnew) // how to draw // engine currently wants this self.predraw = WarpZone_Fade_PreDraw; + return true; } -void WarpZone_Camera_Read(float isnew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_WARPZONE_CAMERA, bool isnew) +{ warpzone_cameras_exist = 1; self.classname = "func_warpzone_camera"; @@ -144,18 +144,19 @@ void WarpZone_Camera_Read(float isnew) // how to draw // engine currently wants this self.predraw = WarpZone_Fade_PreDraw; + return true; } void CL_RotateMoves(vector ang) = #638; -void WarpZone_Teleported_Read(float isnew) -{SELFPARAM(); - vector v; +NET_HANDLE(ENT_CLIENT_WARPZONE_TELEPORTED, bool isnew) +{ self.classname = "warpzone_teleported"; + vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); - if(!isnew) - return; + return = true; + if (!isnew) return; self.warpzone_transform = v; setproperty(VF_CL_VIEWANGLES, WarpZone_TransformVAngles(self, getpropertyvec(VF_CL_VIEWANGLES))); if(checkextension("DP_CSQC_ROTATEMOVES")) @@ -279,10 +280,6 @@ void WarpZone_FixView() setproperty(VF_ORIGIN, org + o); } -void WarpZone_Init() -{ -} - void WarpZone_Shutdown() { WarpZone_View_Outside(); diff --git a/qcsrc/lib/warpzone/client.qh b/qcsrc/lib/warpzone/client.qh index 016ac58822..8f3e643b5c 100644 --- a/qcsrc/lib/warpzone/client.qh +++ b/qcsrc/lib/warpzone/client.qh @@ -1,14 +1,9 @@ #ifndef LIB_WARPZONE_CLIENT_H #define LIB_WARPZONE_CLIENT_H -void WarpZone_Read(float bIsNewEntity); -void WarpZone_Camera_Read(float bIsNewEntity); -void WarpZone_Teleported_Read(float bIsNewEntity); - void WarpZone_FixPMove(); void WarpZone_FixView(); -void WarpZone_Init(); void WarpZone_Shutdown(); vector warpzone_save_view_origin; diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index 6fe901bd87..50339a7303 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -186,8 +186,8 @@ void WarpZone_Trace_InitTransform() { if(!WarpZone_trace_transform) { - WarpZone_trace_transform = spawn(); - WarpZone_trace_transform.classname = "warpzone_trace_transform"; + WarpZone_trace_transform = new(warpzone_trace_transform); + make_pure(WarpZone_trace_transform); } WarpZone_Accumulator_Clear(WarpZone_trace_transform); } @@ -457,7 +457,7 @@ entity WarpZone_TrailParticles_trace_callback_own; float WarpZone_TrailParticles_trace_callback_eff; void WarpZone_TrailParticles_trace_callback(vector from, vector endpos, vector to) { - trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos); + __trailparticles(WarpZone_TrailParticles_trace_callback_own, WarpZone_TrailParticles_trace_callback_eff, from, endpos); } void WarpZone_TrailParticles(entity own, float eff, vector org, vector end) @@ -572,37 +572,20 @@ vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org) bool WarpZoneLib_BadEntity(entity e) { - string myclassname = e.classname; - if (e.instanceOfObject) return true; - switch(myclassname) + string s = e.classname; + if (is_pure(e)) return true; + switch (s) { - case "deathtype": - case "weaponentity": - case "exteriorweaponentity": - case "csqc_score_team": - case "pingplreport": - case "ent_client_scoreinfo": - case "saved_cvar_value": - case "accuracy": case "entcs_sender": case "entcs_receiver": - case "clientinit": - case "sprite_waypoint": - case "waypoint": - case "gibsplash": - //case "net_linked": // actually some real entities are linked without classname, fail + // case "net_linked": // actually some real entities are linked without classname, fail case "": return true; } - if(startsWith(myclassname, "msg_")) - return true; - - if(startsWith(myclassname, "target_")) - return true; + if (startsWith(s, "target_")) return true; - if(startsWith(myclassname, "info_")) - return true; + if (startsWith(s, "info_")) return true; return false; } @@ -703,8 +686,7 @@ void WarpZone_RefSys_CheckCreate(entity me) { if(me.WarpZone_refsys.owner != me) { - me.WarpZone_refsys = spawn(); - me.WarpZone_refsys.classname = "warpzone_refsys"; + me.WarpZone_refsys = new(warpzone_refsys); me.WarpZone_refsys.owner = me; me.WarpZone_refsys.think = WarpZone_RefSys_GC; me.WarpZone_refsys.nextthink = time + 1; diff --git a/qcsrc/lib/warpzone/mathlib.qc b/qcsrc/lib/warpzone/mathlib.qc index 92b7ee14dd..ac3b65ff1a 100644 --- a/qcsrc/lib/warpzone/mathlib.qc +++ b/qcsrc/lib/warpzone/mathlib.qc @@ -290,11 +290,3 @@ int isunordered(float x, float y) { return !(x < y || x == y || x > y); } - -vector cross(vector a, vector b) -{ - return - '1 0 0' * (a.y * b.z - a.z * b.y) - + '0 1 0' * (a.z * b.x - a.x * b.z) - + '0 0 1' * (a.x * b.y - a.y * b.x); -} diff --git a/qcsrc/lib/warpzone/mathlib.qh b/qcsrc/lib/warpzone/mathlib.qh index 9acece2ab2..c3de3838dd 100644 --- a/qcsrc/lib/warpzone/mathlib.qh +++ b/qcsrc/lib/warpzone/mathlib.qh @@ -115,7 +115,4 @@ const float M_2_SQRTPI = 1.12837916709551257390; /* 2/sqrt(pi) */ const float M_SQRT2 = 1.41421356237309504880; /* sqrt(2) */ const float M_SQRT1_2 = 0.70710678118654752440; /* 1/sqrt(2) */ -// Non- stuff follows here. -vector cross(vector a, vector b); - #endif diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index e21e4cab49..27640770a1 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -53,7 +53,7 @@ void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector bool WarpZone_Teleported_Send(entity to, int sf) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED); + WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE_TELEPORTED); WriteCoord(MSG_ENTITY, self.angles.x); WriteCoord(MSG_ENTITY, self.angles.y); WriteCoord(MSG_ENTITY, self.angles.z); @@ -137,7 +137,7 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) // instead of fixangle, send the transform to the client for smoother operation player.fixangle = false; - entity ts = spawn(); + entity ts = new(warpzone_teleported); setmodel(ts, MDL_Null); ts.SendEntity = WarpZone_Teleported_Send; ts.SendFlags = 0xFFFFFF; @@ -147,7 +147,6 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) ts.owner = player; ts.enemy = wz; ts.effects = EF_NODEPTHTEST; - ts.classname = "warpzone_teleported"; ts.angles = wz.warpzone_transform; } #endif @@ -155,7 +154,7 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) return 1; } -void WarpZone_Touch (void) +void WarpZone_Touch () {SELFPARAM(); if(other.classname == "trigger_warpzone") return; @@ -217,7 +216,7 @@ void WarpZone_Touch (void) bool WarpZone_Send(entity to, int sendflags) {SELFPARAM(); - WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE); // we must send this flag for clientside to match properly too int f = 0; @@ -272,7 +271,7 @@ bool WarpZone_Send(entity to, int sendflags) bool WarpZone_Camera_Send(entity to, int sendflags) {SELFPARAM(); int f = 0; - WriteByte(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA); + WriteHeader(MSG_ENTITY, ENT_CLIENT_WARPZONE_CAMERA); if(self.warpzone_fadestart) BITSET_ASSIGN(f, 2); @@ -486,7 +485,7 @@ void WarpZonePosition_InitStep_FindTarget() self.enemy.aiment = self; } -void WarpZoneCamera_Think(void) +void WarpZoneCamera_Think() {SELFPARAM(); if(self.warpzone_save_origin != self.origin || self.warpzone_save_angles != self.angles @@ -885,7 +884,7 @@ spawnfunc(target_warpzone_reconnect) spawnfunc_trigger_warpzone_reconnect(this); // both names make sense here :( } -void WarpZone_PlayerPhysics_FixVAngle(void) +void WarpZone_PlayerPhysics_FixVAngle() {SELFPARAM(); #ifndef WARPZONE_DONT_FIX_VANGLE if(IS_REAL_CLIENT(self)) diff --git a/qcsrc/lib/warpzone/server.qh b/qcsrc/lib/warpzone/server.qh index 943f0322a6..b96b7b3361 100644 --- a/qcsrc/lib/warpzone/server.qh +++ b/qcsrc/lib/warpzone/server.qh @@ -12,7 +12,7 @@ float WarpZone_Projectile_Touch_ImpactFilter_Callback(); //const float ENT_CLIENT_WARPZONE; //const float ENT_CLIENT_WARPZONE_CAMERA; -void WarpZone_PlayerPhysics_FixVAngle(void); +void WarpZone_PlayerPhysics_FixVAngle(); -void WarpZone_PostInitialize_Callback(void); +void WarpZone_PostInitialize_Callback(); #endif diff --git a/qcsrc/menu/anim/animation.qc b/qcsrc/menu/anim/animation.qc index d24220fefa..09bf77217f 100644 --- a/qcsrc/menu/anim/animation.qc +++ b/qcsrc/menu/anim/animation.qc @@ -1,124 +1,117 @@ #ifndef ANIM_ANIMATION_H -#define ANIM_ANIMATION_H -void setterDummy(entity, float); -CLASS(Animation, Object) - METHOD(Animation, configureAnimation, void(entity, entity, void(entity, float), float, float, float, float)); - METHOD(Animation, update, void(entity, float, float, float)); - METHOD(Animation, setTimeStartEnd, void(entity, float, float)); - METHOD(Animation, setTimeStartDuration, void(entity, float, float)); - METHOD(Animation, setValueStartEnd, void(entity, float, float)); - METHOD(Animation, setValueStartDelta, void(entity, float, float)); - METHOD(Animation, setObjectSetter, void(entity, entity, void(entity, float))); - METHOD(Animation, tick, void(entity, float)); - METHOD(Animation, calcValue, float(entity, float, float, float, float)); - METHOD(Animation, isStopped, float(entity)); - METHOD(Animation, stopAnim, void(entity)); - METHOD(Animation, resumeAnim, void(entity)); - METHOD(Animation, isFinished, float(entity)); - METHOD(Animation, finishAnim, void(entity)); - ATTRIB(Animation, object, entity, NULL) - ATTRIB(Animation, setter, void(entity, float), setterDummy) - ATTRIB(Animation, value, float, 0) - ATTRIB(Animation, startTime, float, 0) - ATTRIB(Animation, duration, float, 0) - ATTRIB(Animation, startValue, float, 0) - ATTRIB(Animation, delta, float, 0) - ATTRIB(Animation, stopped, float, false) - ATTRIB(Animation, finished, float, false) -ENDCLASS(Animation) + #define ANIM_ANIMATION_H + CLASS(Animation, Object) + METHOD(Animation, configureAnimation, void(entity, entity, void(entity, float), float, float, float, float)); + METHOD(Animation, update, void(entity, float, float, float)); + METHOD(Animation, setTimeStartEnd, void(entity, float, float)); + METHOD(Animation, setTimeStartDuration, void(entity, float, float)); + METHOD(Animation, setValueStartEnd, void(entity, float, float)); + METHOD(Animation, setValueStartDelta, void(entity, float, float)); + METHOD(Animation, setObjectSetter, void(entity, entity, void(entity, float))); + METHOD(Animation, tick, void(entity, float)); + METHOD(Animation, calcValue, float(entity, float, float, float, float)); + METHOD(Animation, isStopped, float(entity)); + METHOD(Animation, stopAnim, void(entity)); + METHOD(Animation, resumeAnim, void(entity)); + METHOD(Animation, isFinished, float(entity)); + METHOD(Animation, finishAnim, void(entity)); + ATTRIB(Animation, object, entity, NULL) + void setterDummy(entity, float) {} + ATTRIB(Animation, setter, void(entity, float), setterDummy) + ATTRIB(Animation, value, float, 0) + ATTRIB(Animation, startTime, float, 0) + ATTRIB(Animation, duration, float, 0) + ATTRIB(Animation, startValue, float, 0) + ATTRIB(Animation, delta, float, 0) + ATTRIB(Animation, stopped, float, false) + ATTRIB(Animation, finished, float, false) + ENDCLASS(Animation) #endif #ifdef IMPLEMENTATION -void Animation_configureAnimation(entity me, entity obj, void(entity, float) objSetter, float animStartTime, float animDuration, float animStartValue, float animEndValue) -{ - me.setObjectSetter(me, obj, objSetter); - me.setTimeStartDuration(me, animStartTime, animDuration); - me.setValueStartEnd(me, animStartValue, animEndValue); -} - -void Animation_update(entity me, float animDuration, float animStartValue, float animEndValue) -{ - me.setTimeStartDuration(me, time, animDuration); - me.setValueStartEnd(me, animStartValue, animEndValue); -} - -void Animation_setTimeStartEnd(entity me, float s, float e) -{ - me.startTime = s; - me.duration = e - s; -} - -void Animation_setTimeStartDuration(entity me, float s, float d) -{ - me.startTime = s; - me.duration = d; -} - -void Animation_setValueStartEnd(entity me, float s, float e) -{ - me.startValue = s; - me.delta = e - s; -} - -void Animation_setValueStartDelta(entity me, float s, float d) -{ - me.startValue = s; - me.delta = d; -} - -void Animation_setObjectSetter(entity me, entity o, void(entity, float) s) -{ - me.object = o; - me.setter = s; -} - -void Animation_tick(entity me, float tickTime) -{ - if (me.isStopped(me) || me.isFinished(me) || (tickTime < me.startTime)) - return; - - if (tickTime >= (me.startTime + me.duration)) - me.finishAnim(me); - else - me.value = me.calcValue(me, (tickTime - me.startTime), me.duration, me.startValue, me.delta); - - me.setter(me.object, me.value); -} - -float Animation_calcValue(entity me, float tickTime, float animDuration, float animStartValue, float animDelta) -{ - return animStartValue; -} - -float Animation_isStopped(entity me) -{ - return me.stopped; -} - -void Animation_stopAnim(entity me) -{ - me.stopped = true; -} - -void Animation_resumeAnim(entity me) -{ - me.stopped = false; -} - -float Animation_isFinished(entity me) -{ - return me.finished; -} - -void Animation_finishAnim(entity me) -{ - me.value = me.delta + me.startValue; - me.finished = true; - me.setter(me.object, me.value); -} - -void setterDummy(entity obj, float objValue) -{ -} + METHOD(Animation, configureAnimation, void(entity this, entity obj, void(entity, float) objSetter, float animStartTime, float animDuration, float animStartValue, float animEndValue)) + { + this.setObjectSetter(this, obj, objSetter); + this.setTimeStartDuration(this, animStartTime, animDuration); + this.setValueStartEnd(this, animStartValue, animEndValue); + } + + METHOD(Animation, update, void(entity this, float animDuration, float animStartValue, float animEndValue)) + { + this.setTimeStartDuration(this, time, animDuration); + this.setValueStartEnd(this, animStartValue, animEndValue); + } + + METHOD(Animation, setTimeStartEnd, void(entity this, float s, float e)) + { + this.startTime = s; + this.duration = e - s; + } + + METHOD(Animation, setTimeStartDuration, void(entity this, float s, float d)) + { + this.startTime = s; + this.duration = d; + } + + METHOD(Animation, setValueStartEnd, void(entity this, float s, float e)) + { + this.startValue = s; + this.delta = e - s; + } + + METHOD(Animation, setValueStartDelta, void(entity this, float s, float d)) + { + this.startValue = s; + this.delta = d; + } + + METHOD(Animation, setObjectSetter, void(entity this, entity o, void(entity, float) s)) + { + this.object = o; + this.setter = s; + } + + METHOD(Animation, tick, void(entity this, float tickTime)) + { + if (this.isStopped(this) || this.isFinished(this) || (tickTime < this.startTime)) return; + + if (tickTime >= (this.startTime + this.duration)) this.finishAnim(this); + else this.value = this.calcValue(this, (tickTime - this.startTime), this.duration, this.startValue, this.delta); + + this.setter(this.object, this.value); + } + + METHOD(Animation, calcValue, float(entity this, float tickTime, float animDuration, float animStartValue, float animDelta)) + { + return animStartValue; + } + + METHOD(Animation, isStopped, bool(entity this)) + { + return this.stopped; + } + + METHOD(Animation, stopAnim, void(entity this)) + { + this.stopped = true; + } + + METHOD(Animation, resumeAnim, void(entity this)) + { + this.stopped = false; + } + + METHOD(Animation, isFinished, bool(entity this)) + { + return this.finished; + } + + METHOD(Animation, finishAnim, void(entity this)) + { + this.value = this.delta + this.startValue; + this.finished = true; + this.setter(this.object, this.value); + } #endif diff --git a/qcsrc/menu/anim/animhost.qc b/qcsrc/menu/anim/animhost.qc index 55ca901dc5..7826a47b79 100644 --- a/qcsrc/menu/anim/animhost.qc +++ b/qcsrc/menu/anim/animhost.qc @@ -1,177 +1,143 @@ #include "../menu.qh" #ifndef ANIM_ANIMHOST_H -#define ANIM_ANIMHOST_H -CLASS(AnimHost, Object) - METHOD(AnimHost, addAnim, void(entity, entity)); - METHOD(AnimHost, removeAnim, void(entity, entity)); - METHOD(AnimHost, removeAllAnim, void(entity)); - METHOD(AnimHost, removeObjAnim, void(entity, entity)); - METHOD(AnimHost, stopAllAnim, void(entity)); - METHOD(AnimHost, stopObjAnim, void(entity, entity)); - METHOD(AnimHost, resumeAllAnim, void(entity)); - METHOD(AnimHost, resumeObjAnim, void(entity, entity)); - METHOD(AnimHost, finishAllAnim, void(entity)); - METHOD(AnimHost, finishObjAnim, void(entity, entity)); - METHOD(AnimHost, tickAll, void(entity)); - ATTRIB(AnimHost, firstChild, entity, NULL) - ATTRIB(AnimHost, lastChild, entity, NULL) -ENDCLASS(AnimHost) -.entity nextSibling; -.entity prevSibling; + #define ANIM_ANIMHOST_H + CLASS(AnimHost, Object) + METHOD(AnimHost, addAnim, void(entity, entity)); + METHOD(AnimHost, removeAnim, void(entity, entity)); + METHOD(AnimHost, removeAllAnim, void(entity)); + METHOD(AnimHost, removeObjAnim, void(entity, entity)); + METHOD(AnimHost, stopAllAnim, void(entity)); + METHOD(AnimHost, stopObjAnim, void(entity, entity)); + METHOD(AnimHost, resumeAllAnim, void(entity)); + METHOD(AnimHost, resumeObjAnim, void(entity, entity)); + METHOD(AnimHost, finishAllAnim, void(entity)); + METHOD(AnimHost, finishObjAnim, void(entity, entity)); + METHOD(AnimHost, tickAll, void(entity)); + ATTRIB(AnimHost, firstChild, entity, NULL) + ATTRIB(AnimHost, lastChild, entity, NULL) + ENDCLASS(AnimHost) + .entity nextSibling; + .entity prevSibling; #endif #ifdef IMPLEMENTATION -void AnimHost_addAnim(entity me, entity other) -{ - if(other.parent) - error("Can't add already added anim!"); - - if(other.isFinished(other)) - error("Can't add finished anim!"); - - other.parent = me; - - entity l; - l = me.lastChild; - - if(l) - l.nextSibling = other; - else - me.firstChild = other; - - other.prevSibling = l; - other.nextSibling = NULL; - me.lastChild = other; -} - -void AnimHost_removeAnim(entity me, entity other) -{ - if(other.parent != me) - error("Can't remove from wrong AnimHost!"); - - other.parent = NULL; - - entity n, p; - n = other.nextSibling; - p = other.prevSibling; - - if(p) - p.nextSibling = n; - else - me.firstChild = n; - - if(n) - n.prevSibling = p; - else - me.lastChild = p; - remove(other); -} - -void AnimHost_removeAllAnim(entity me) -{ - entity e, tmp; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, addAnim, void(entity this, entity other)) { - tmp = e; - e = tmp.prevSibling; - me.removeAnim(me, tmp); + if (other.parent) error("Can't add already added anim!"); + + if (other.isFinished(other)) error("Can't add finished anim!"); + + other.parent = this; + + entity l = this.lastChild; + + if (l) l.nextSibling = other; + else this.firstChild = other; + + other.prevSibling = l; + other.nextSibling = NULL; + this.lastChild = other; + } + + METHOD(AnimHost, removeAnim, void(entity this, entity other)) + { + if (other.parent != this) error("Can't remove from wrong AnimHost!"); + + other.parent = NULL; + + entity n = other.nextSibling; + entity p = other.prevSibling; + + if (p) p.nextSibling = n; + else this.firstChild = n; + + if (n) n.prevSibling = p; + else this.lastChild = p; + remove(other); } -} -void AnimHost_removeObjAnim(entity me, entity obj) -{ - entity e, tmp; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, removeAllAnim, void(entity this)) { - if (e.object == obj) + for (entity e = this.firstChild; e; e = e.nextSibling) { - tmp = e; + entity tmp = e; e = tmp.prevSibling; - me.removeAnim(me, tmp); + this.removeAnim(this, tmp); } } -} -void AnimHost_stopAllAnim(entity me) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, removeObjAnim, void(entity this, entity obj)) { - e.stopAnim(e); + for (entity e = this.firstChild; e; e = e.nextSibling) + { + if (e.object == obj) + { + entity tmp = e; + e = tmp.prevSibling; + this.removeAnim(this, tmp); + } + } } -} -void AnimHost_stopObjAnim(entity me, entity obj) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, stopAllAnim, void(entity this)) { - if (e.object == obj) - { + for (entity e = this.firstChild; e; e = e.nextSibling) e.stopAnim(e); - } } -} -void AnimHost_resumeAllAnim(entity me) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, stopObjAnim, void(entity this, entity obj)) { - e.resumeAnim(e); + for (entity e = this.firstChild; e; e = e.nextSibling) + if (e.object == obj) e.stopAnim(e); } -} -void AnimHost_resumeObjAnim(entity me, entity obj) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, resumeAllAnim, void(entity this)) { - if (e.object == obj) - { + for (entity e = this.firstChild; e; e = e.nextSibling) e.resumeAnim(e); - } } -} -void AnimHost_finishAllAnim(entity me) -{ - entity e, tmp; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, resumeObjAnim, void(entity this, entity obj)) { - tmp = e; - e = tmp.prevSibling; - tmp.finishAnim(tmp); + for (entity e = this.firstChild; e; e = e.nextSibling) + if (e.object == obj) e.resumeAnim(e); } -} -void AnimHost_finishObjAnim(entity me, entity obj) -{ - entity e, tmp; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, finishAllAnim, void(entity this)) { - if (e.object == obj) + for (entity e = this.firstChild; e; e = e.nextSibling) { - tmp = e; + entity tmp = e; e = tmp.prevSibling; tmp.finishAnim(tmp); } } -} -void AnimHost_tickAll(entity me) -{ - entity e, tmp; - for(e = me.firstChild; e; e = e.nextSibling) + METHOD(AnimHost, finishObjAnim, void(entity this, entity obj)) { - e.tick(e, time); - if (e.isFinished(e)) + for (entity e = this.firstChild; e; e = e.nextSibling) { - tmp = e; - e = tmp.prevSibling; - me.removeAnim(me, tmp); + if (e.object == obj) + { + entity tmp = e; + e = tmp.prevSibling; + tmp.finishAnim(tmp); + } + } + } + + METHOD(AnimHost, tickAll, void(entity this)) + { + for (entity e = this.firstChild; e; e = e.nextSibling) + { + e.tick(e, time); + if (e.isFinished(e)) + { + entity tmp = e; + e = tmp.prevSibling; + this.removeAnim(this, tmp); + } } } -} #endif diff --git a/qcsrc/menu/anim/easing.qc b/qcsrc/menu/anim/easing.qc index f1962719f8..080f390ba9 100644 --- a/qcsrc/menu/anim/easing.qc +++ b/qcsrc/menu/anim/easing.qc @@ -1,74 +1,66 @@ #ifndef ANIM_EASING_H -#define ANIM_EASING_H -#include "animation.qc" -entity makeHostedEasing(entity, void(entity, float), float(float, float, float, float), float, float, float); -entity makeEasing(entity, void(entity, float), float(float, float, float, float), float, float, float, float); -float easingLinear(float, float, float, float); -float easingQuadIn(float, float, float, float); -float easingQuadOut(float, float, float, float); -float easingQuadInOut(float, float, float, float); -CLASS(Easing, Animation) - METHOD(Easing, calcValue, float(entity, float, float, float, float)); - METHOD(Easing, setMath, void(entity, float(float, float, float, float))); - ATTRIB(Easing, math, float(float, float, float, float), easingLinear) -ENDCLASS(Easing) + #define ANIM_EASING_H + #include "animation.qc" + entity makeHostedEasing(entity, void(entity, float), float(float, float, float, float), float, float, float); + entity makeEasing(entity, void(entity, float), float(float, float, float, float), float, float, float, float); + float easingLinear(float, float, float, float); + float easingQuadIn(float, float, float, float); + float easingQuadOut(float, float, float, float); + float easingQuadInOut(float, float, float, float); + CLASS(Easing, Animation) + METHOD(Easing, calcValue, float(entity, float, float, float, float)); + METHOD(Easing, setMath, void(entity, float(float, float, float, float))); + ATTRIB(Easing, math, float(float, float, float, float), easingLinear) + ENDCLASS(Easing) #endif #ifdef IMPLEMENTATION -entity makeHostedEasing(entity obj, void(entity, float) objSetter, float(float, float, float, float) func, float animDuration, float animStartValue, float animEnd) -{ - entity me; - me = makeEasing(obj, objSetter, func, time, animDuration, animStartValue, animEnd); - anim.addAnim(anim, me); - return me; -} - -entity makeEasing(entity obj, void(entity, float) objSetter, float(float, float, float, float) func, float animStartTime, float animDuration, float animStartValue, float animEnd) -{ - entity me; - me = NEW(Easing); - me.configureAnimation(me, obj, objSetter, animStartTime, animDuration, animStartValue, animEnd); - me.setMath(me, func); - return me; -} + entity makeHostedEasing(entity obj, void(entity, float) objSetter, float(float, float, float, float) func, float animDuration, float animStartValue, float animEnd) + { + entity this = makeEasing(obj, objSetter, func, time, animDuration, animStartValue, animEnd); + anim.addAnim(anim, this); + return this; + } -float Easing_calcValue(entity me, float tickTime, float animDuration, float animStart, float animDelta) -{ - return me.math(tickTime, animDuration, animStart, animDelta); -} + entity makeEasing(entity obj, void(entity, float) objSetter, float(float, float, float, float) func, float animStartTime, float animDuration, float animStartValue, float animEnd) + { + entity this = NEW(Easing); + this.configureAnimation(this, obj, objSetter, animStartTime, animDuration, animStartValue, animEnd); + this.setMath(this, func); + return this; + } -void Easing_setMath(entity me, float(float, float, float, float) func) -{ - me.math = func; -} + METHOD(Easing, calcValue, float(entity this, float tickTime, float animDuration, float animStart, float animDelta)) + { + return this.math(tickTime, animDuration, animStart, animDelta); + } -float easingLinear(float tickTime, float animDuration, float animStart, float animDelta) -{ - return (animDelta * (tickTime / animDuration)) + animStart; -} + METHOD(Easing, setMath, void(entity this, float(float, float, float, float) func)) + { + this.math = func; + } -float easingQuadIn(float tickTime, float animDuration, float animStart, float animDelta) -{ - float frac = tickTime / animDuration; - return (animDelta * frac * frac) + animStart; -} + float easingLinear(float tickTime, float animDuration, float animStart, float animDelta) + { + return (animDelta * (tickTime / animDuration)) + animStart; + } -float easingQuadOut(float tickTime, float animDuration, float animStart, float animDelta) -{ - float frac = tickTime / animDuration; - return (-animDelta * frac * (frac - 2)) + animStart; -} + float easingQuadIn(float tickTime, float animDuration, float animStart, float animDelta) + { + float frac = tickTime / animDuration; + return (animDelta * frac * frac) + animStart; + } -float easingQuadInOut(float tickTime, float animDuration, float animStart, float animDelta) -{ - if (tickTime < (animDuration / 2)) + float easingQuadOut(float tickTime, float animDuration, float animStart, float animDelta) { - return easingQuadIn(tickTime, (animDuration / 2), animStart, (animDelta / 2)); + float frac = tickTime / animDuration; + return (-animDelta * frac * (frac - 2)) + animStart; } - else + + float easingQuadInOut(float tickTime, float animDuration, float animStart, float animDelta) { - return easingQuadOut((tickTime - (animDuration / 2)), (animDuration / 2), (animStart + (animDelta / 2)), (animDelta / 2)); + if (tickTime < (animDuration / 2)) return easingQuadIn(tickTime, (animDuration / 2), animStart, (animDelta / 2)); + else return easingQuadOut((tickTime - (animDuration / 2)), (animDuration / 2), (animStart + (animDelta / 2)), (animDelta / 2)); } -} #endif diff --git a/qcsrc/menu/anim/keyframe.qc b/qcsrc/menu/anim/keyframe.qc index d83a2cbd49..eec5c03aec 100644 --- a/qcsrc/menu/anim/keyframe.qc +++ b/qcsrc/menu/anim/keyframe.qc @@ -1,110 +1,100 @@ #ifndef ANIM_KEYFRAME_H -#define ANIM_KEYFRAME_H -#include "animation.qc" -CLASS(Keyframe, Animation) - METHOD(Keyframe, addEasing, entity(entity, float, float, float(float, float, float, float))); - METHOD(Keyframe, addAnim, void(entity, entity)); - METHOD(Keyframe, calcValue, float(entity, float, float, float, float)); - ATTRIB(Keyframe, currentChild, entity, NULL) - ATTRIB(Keyframe, firstChild, entity, NULL) - ATTRIB(Keyframe, lastChild, entity, NULL) -ENDCLASS(Keyframe) -entity makeHostedKeyframe(entity, void(entity, float), float, float, float); -entity makeKeyframe(entity, void(entity, float), float, float, float); -float getNewChildStart(entity); -float getNewChildDuration(entity, float); -float getNewChildValue(entity); + #define ANIM_KEYFRAME_H + #include "animation.qc" + CLASS(Keyframe, Animation) + METHOD(Keyframe, addEasing, entity(entity, float, float, float(float, float, float, float))); + METHOD(Keyframe, addAnim, void(entity, entity)); + METHOD(Keyframe, calcValue, float(entity, float, float, float, float)); + ATTRIB(Keyframe, currentChild, entity, NULL) + ATTRIB(Keyframe, firstChild, entity, NULL) + ATTRIB(Keyframe, lastChild, entity, NULL) + ENDCLASS(Keyframe) + entity makeHostedKeyframe(entity, void(entity, float), float, float, float); + entity makeKeyframe(entity, void(entity, float), float, float, float); + float getNewChildStart(entity); + float getNewChildDuration(entity, float); + float getNewChildValue(entity); #endif #ifdef IMPLEMENTATION -entity makeHostedKeyframe(entity obj, void(entity, float) objSetter, float animDuration, float animStart, float animEnd) -{ - entity me; - me = makeKeyframe(obj, objSetter, animDuration, animStart, animEnd); - anim.addAnim(anim, me); - return me; -} + entity makeHostedKeyframe(entity obj, void(entity, float) objSetter, float animDuration, float animStart, float animEnd) + { + entity this = makeKeyframe(obj, objSetter, animDuration, animStart, animEnd); + anim.addAnim(anim, this); + return this; + } -entity makeKeyframe(entity obj, void(entity, float) objSetter, float animDuration, float animStart, float animEnd) -{ - entity me; - me = NEW(Keyframe); - me.configureAnimation(me, obj, objSetter, time, animDuration, animStart, animEnd); - return me; -} + entity makeKeyframe(entity obj, void(entity, float) objSetter, float animDuration, float animStart, float animEnd) + { + entity this = NEW(Keyframe); + this.configureAnimation(this, obj, objSetter, time, animDuration, animStart, animEnd); + return this; + } -entity Keyframe_addEasing(entity me, float animDurationTime, float animEnd, float(float, float, float, float) func) -{ - entity other; - other = makeEasing(me.object, me.setter, func, getNewChildStart(me), getNewChildDuration(me, animDurationTime), getNewChildValue(me), animEnd); - me.addAnim(me, other); - return other; -} + METHOD(Keyframe, addEasing, entity(entity this, float animDurationTime, float animEnd, float(float, float, float, float) func)) + { + entity other = makeEasing(this.object, this.setter, func, getNewChildStart(this), getNewChildDuration(this, animDurationTime), getNewChildValue(this), animEnd); + this.addAnim(this, other); + return other; + } + + float getNewChildStart(entity this) + { + if (this.lastChild) return this.lastChild.startTime + this.lastChild.duration; + else return 0; + } -float getNewChildStart(entity me) -{ - if (me.lastChild) - return (me.lastChild.startTime + me.lastChild.duration); - else - return 0; -} + float getNewChildDuration(entity this, float durationTime) + { + float maxDura = this.duration; + if (this.lastChild) maxDura = maxDura - (this.lastChild.startTime + this.lastChild.duration); + float dura = durationTime; + if (0 >= dura || dura > maxDura) dura = maxDura; + return dura; + } -float getNewChildDuration(entity me, float durationTime) -{ - float dura, maxDura; - maxDura = me.duration; - if (me.lastChild) maxDura = maxDura - (me.lastChild.startTime + me.lastChild.duration); - dura = durationTime; - if (0 >= dura || dura > maxDura) dura = maxDura; - return dura; -} + float getNewChildValue(entity this) + { + if (this.lastChild) return this.lastChild.startValue + this.lastChild.delta; + else return this.startValue; + } -float getNewChildValue(entity me) -{ - if (me.lastChild) - return (me.lastChild.startValue + me.lastChild.delta); - else - return me.startValue; -} + METHOD(Keyframe, addAnim, void(entity this, entity other)) + { + if (other.parent) error("Can't add already added anim!"); -void Keyframe_addAnim(entity me, entity other) -{ - if(other.parent) - error("Can't add already added anim!"); + if (other.isFinished(other)) error("Can't add finished anim!"); - if(other.isFinished(other)) - error("Can't add finished anim!"); + other.parent = this; - other.parent = me; + entity l = this.lastChild; - entity l; - l = me.lastChild; + if (l) + { + l.nextSibling = other; + } + else + { + this.currentChild = other; + this.firstChild = other; + } - if(l) - l.nextSibling = other; - else - { - me.currentChild = other; - me.firstChild = other; + other.prevSibling = l; + other.nextSibling = NULL; + this.lastChild = other; } - other.prevSibling = l; - other.nextSibling = NULL; - me.lastChild = other; -} + METHOD(Keyframe, calcValue, float(entity this, float tickTime, float animDuration, float animStartValue, float animDelta)) + { + if (this.currentChild) + if (this.currentChild.isFinished(this.currentChild)) this.currentChild = this.currentChild.nextSibling; -float Keyframe_calcValue(entity me, float tickTime, float animDuration, float animStartValue, float animDelta) -{ - if (me.currentChild) - if (me.currentChild.isFinished(me.currentChild)) - me.currentChild = me.currentChild.nextSibling; + if (this.currentChild) + { + this.currentChild.tick(this.currentChild, tickTime); + return this.currentChild.value; + } - if (me.currentChild) - { - me.currentChild.tick(me.currentChild, tickTime); - return me.currentChild.value; + return animStartValue + animDelta; } - - return animStartValue + animDelta; -} #endif diff --git a/qcsrc/menu/classes.qc b/qcsrc/menu/classes.qc new file mode 100644 index 0000000000..9454a3113d --- /dev/null +++ b/qcsrc/menu/classes.qc @@ -0,0 +1,9 @@ +#ifndef CLASSES_H +#define CLASSES_H + +#include "classes.inc" +#define IMPLEMENTATION +#include "classes.inc" +#undef IMPLEMENTATION + +#endif diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index 179dc1949b..5a5fe26a68 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -1,7 +1,9 @@ #include "menu_cmd.qh" #include "../menu.qh" -#include "../oo/classes.qc" +#include "../classes.qc" + +#include "../mutators/events.qh" #include "../../common/command/generic.qh" @@ -12,22 +14,22 @@ void _dumptree_open(entity pass, entity me) { string s; s = me.toString(me); - if(s == "") - s = me.classname; - else - s = strcat(me.classname, ": ", s); + if (s == "") s = me.classname; + else s = strcat(me.classname, ": ", s); LOG_INFO(_dumptree_space, etos(me), " (", s, ")"); - if(me.firstChild) + if (me.firstChild) { LOG_INFO(" {\n"); _dumptree_space = strcat(_dumptree_space, " "); } else + { LOG_INFO("\n"); + } } void _dumptree_close(entity pass, entity me) { - if(me.firstChild) + if (me.firstChild) { _dumptree_space = substring(_dumptree_space, 0, strlen(_dumptree_space) - 2); LOG_INFO(_dumptree_space, "}\n"); @@ -38,10 +40,10 @@ float updateConwidths(float width, float height, float pixelheight); void GameCommand(string theCommand) { - float argc; - argc = tokenize_console(theCommand); + int argc = tokenize_console(theCommand); + string ss = strtolower(argv(0)); - if(argv(0) == "help" || argc == 0) + if (argv(0) == "help" || argc == 0) { LOG_INFO(_("Usage: menu_cmd command..., where possible commands are:\n")); LOG_INFO(_(" sync - reloads all cvars on the current menu page\n")); @@ -53,82 +55,81 @@ void GameCommand(string theCommand) return; } - if(GenericCommand(theCommand)) - return; + if (GenericCommand(theCommand)) return; - if(argv(0) == "sync") + if (argv(0) == "sync") { m_sync(); return; } - if(argv(0) == "update_conwidths_before_vid_restart") + if (argv(0) == "update_conwidths_before_vid_restart") { updateConwidths(cvar("vid_width"), cvar("vid_height"), cvar("vid_pixelheight")); return; } - if(argv(0) == "directmenu" || argv(0) == "directpanelhudmenu") + if (argv(0) == "directmenu" || argv(0) == "directpanelhudmenu") { string filter = string_null; - if(argv(0) == "directpanelhudmenu") - filter = strzone("HUD"); + if (argv(0) == "directpanelhudmenu") filter = strzone("HUD"); - if(argc == 1) + if (argc == 1) { LOG_INFO(_("Available options:\n")); float i; entity e; string s; - for(i = 0, e = NULL; (e = nextent(e)); ) - if(e.classname != "vtbl" && e.name != "") + for (i = 0, e = NULL; (e = nextent(e)); ) + if (e.classname != "vtbl" && e.name != "") { s = e.name; - if(filter) + if (filter) { - if(substring(s, 0, strlen(filter)) != filter) - continue; + if (substring(s, 0, strlen(filter)) != filter) continue; s = substring(s, strlen(filter), strlen(s) - strlen(filter)); } - LOG_INFO(strcat(" ", s ,"\n")); + LOG_INFO(strcat(" ", s, "\n")); ++i; } } - else if(argc == 2 && !isdemo()) // don't allow this command in demos + else if (argc == 2 && !isdemo()) // don't allow this command in demos { m_play_click_sound(MENU_SOUND_OPEN); m_goto(strcat(filter, argv(1))); // switch to a menu item } - if(filter) - strunzone(filter); + if (filter) strunzone(filter); return; } - if(argv(0) == "skinselect") + if (argv(0) == "skinselect") { m_goto("skinselector"); return; } - if(argv(0) == "languageselect") + if (argv(0) == "languageselect") { m_goto("languageselector"); return; } - if(argv(0) == "videosettings") + if (argv(0) == "videosettings") { m_goto("videosettings"); return; } - if(argv(0) == "dumptree") + if (argv(0) == "dumptree") { _dumptree_space = ""; depthfirst(main, parent, firstChild, nextSibling, _dumptree_open, _dumptree_close, NULL); return; } + if(MUTATOR_CALLHOOK(Menu_ConsoleCommand, ss, argc, theCommand)) // handled by a mutator + return; + LOG_INFO(_("Invalid command. For a list of supported commands, try menu_cmd help.\n")); } diff --git a/qcsrc/menu/draw.qc b/qcsrc/menu/draw.qc index 7edc6eff50..b2ce50382a 100644 --- a/qcsrc/menu/draw.qc +++ b/qcsrc/menu/draw.qc @@ -27,16 +27,6 @@ void draw_reset(float cw, float ch, float ox, float oy) draw_endBoldFont(); } -void draw_beginBoldFont() -{ - drawfont = FONT_USER+3; -} - -void draw_endBoldFont() -{ - drawfont = FONT_USER+0; -} - vector globalToBox(vector v, vector theOrigin, vector theScale) { v -= theOrigin; diff --git a/qcsrc/menu/draw.qh b/qcsrc/menu/draw.qh index 873ccb6df3..bb12e29c81 100644 --- a/qcsrc/menu/draw.qh +++ b/qcsrc/menu/draw.qh @@ -10,8 +10,8 @@ vector draw_scale; float draw_alpha; void draw_reset(float cw, float ch, float ox, float oy); -void draw_beginBoldFont(); -void draw_endBoldFont(); +#define draw_beginBoldFont() do { drawfont = FONT_USER + 3; } while (0) +#define draw_endBoldFont() do { drawfont = FONT_USER + 0; } while (0) void draw_setMousePointer(string pic, vector theSize, vector theOffset); void draw_drawMousePointer(vector where); diff --git a/qcsrc/menu/gamesettings.qh b/qcsrc/menu/gamesettings.qh index cf0916ce9c..0db9f77f2e 100644 --- a/qcsrc/menu/gamesettings.qh +++ b/qcsrc/menu/gamesettings.qh @@ -4,11 +4,12 @@ #include "xonotic/tab.qc" -REGISTRY(Settings, BIT(3)) -REGISTER_REGISTRY(RegisterSettings) +REGISTRY(Settings, BITS(3)) +#define Settings_from(i) _Settings_from(i, NULL) +REGISTER_REGISTRY(Settings) #define REGISTER_SETTINGS(id, impl) \ LAZY_NEW(id, impl) \ - REGISTER(RegisterSettings, MENU, Settings, id, m_id, NEW(Lazy, LAZY(id))) + REGISTER(Settings, MENU, id, m_id, NEW(Lazy, LAZY(id))) #endif #endif diff --git a/qcsrc/menu/item.qc b/qcsrc/menu/item.qc index fa6161a977..12132affb4 100644 --- a/qcsrc/menu/item.qc +++ b/qcsrc/menu/item.qc @@ -2,21 +2,21 @@ #define ITEM_H #include "skin.qh" CLASS(Item, Object) - METHOD(Item, draw, void(entity)); - METHOD(Item, keyDown, float(entity, float, float, float)); - METHOD(Item, keyUp, float(entity, float, float, float)); - METHOD(Item, mouseMove, float(entity, vector)); - METHOD(Item, mousePress, float(entity, vector)); - METHOD(Item, mouseDrag, float(entity, vector)); - METHOD(Item, mouseRelease, float(entity, vector)); - METHOD(Item, focusEnter, void(entity)); - METHOD(Item, focusLeave, void(entity)); - METHOD(Item, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Item, relinquishFocus, void(entity)); - METHOD(Item, showNotify, void(entity)); - METHOD(Item, hideNotify, void(entity)); - METHOD(Item, toString, string(entity)); - METHOD(Item, destroy, void(entity)); + METHOD(Item, draw, void(Item)); + METHOD(Item, keyDown, float(Item, float, float, float)); + METHOD(Item, keyUp, float(Item, float, float, float)); + METHOD(Item, mouseMove, float(Item, vector)); + METHOD(Item, mousePress, float(Item, vector)); + METHOD(Item, mouseDrag, float(Item, vector)); + METHOD(Item, mouseRelease, float(Item, vector)); + METHOD(Item, focusEnter, void(Item)); + METHOD(Item, focusLeave, void(Item)); + METHOD(Item, resizeNotify, void(Item, vector, vector, vector, vector)); + METHOD(Item, relinquishFocus, void(Item)); + METHOD(Item, showNotify, void(Item)); + METHOD(Item, hideNotify, void(Item)); + METHOD(Item, toString, string(Item)); + METHOD(Item, destroy, void(Item)); ATTRIB(Item, focused, float, 0) ATTRIB(Item, focusable, float, 0) ATTRIB(Item, allowFocusSound, float, 0) @@ -29,111 +29,102 @@ ENDCLASS(Item) #endif #ifdef IMPLEMENTATION -void Item_destroy(entity me) -{ - // free memory associated with me -} - -void Item_relinquishFocus(entity me) -{ - if(me.parent) - if(me.parent.instanceOfContainer) - me.parent.setFocus(me.parent, NULL); -} - -void Item_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - me.origin = absOrigin; - me.size = absSize; -} - -float autocvar_menu_showboxes; -void Item_draw(entity me) -{ - if(autocvar_menu_showboxes) + METHOD(Item, destroy, void(Item this)) { + // free memory associated with this + } + + METHOD(Item, relinquishFocus, void(Item this)) + { + entity par = this.parent; + if (!par) return; + if (par.instanceOfContainer) par.setFocus(par, NULL); + } + + METHOD(Item, resizeNotify, void(Item this, vector relOrigin, vector relSize, vector absOrigin, vector absSize)) + { + this.origin = absOrigin; + this.size = absSize; + } + + int autocvar_menu_showboxes; + METHOD(Item, draw, void(Item this)) + { + if (!autocvar_menu_showboxes) return; vector rgb = '1 0 1'; float a = fabs(autocvar_menu_showboxes); // don't draw containers and border images - if(me.instanceOfContainer || me.instanceOfBorderImage) + if (this.instanceOfContainer || this.instanceOfBorderImage) { rgb = '0 0 0'; a = 0; } -#if 0 - // hack to detect multi drawing - float r = random() * 3; - if(r >= 2) - rgb = '1 0 0'; - else if(r >= 1) - rgb = '0 1 0'; - else - rgb = '0 0 1'; -#endif - if(autocvar_menu_showboxes < 0) + #if 0 + // hack to detect multi drawing + float r = random() * 3; + if (r >= 2) rgb = '1 0 0'; + else if (r >= 1) rgb = '0 1 0'; + else rgb = '0 0 1'; + #endif + if (autocvar_menu_showboxes < 0) { draw_Fill('0 0 0', '0.5 0.5 0', rgb, a); draw_Fill('0.5 0.5 0', '0.5 0.5 0', rgb, a); } - if(autocvar_menu_showboxes > 0) + else if (autocvar_menu_showboxes > 0) { draw_Fill('0 0 0', '1 1 0', rgb, a); } } -} - -void Item_showNotify(entity me) -{ -} - -void Item_hideNotify(entity me) -{ -} - -float Item_keyDown(entity me, float scan, float ascii, float shift) -{ - return 0; // unhandled -} - -float Item_keyUp(entity me, float scan, float ascii, float shift) -{ - return 0; // unhandled -} - -float Item_mouseMove(entity me, vector pos) -{ - return 0; // unhandled -} - -float Item_mousePress(entity me, vector pos) -{ - return 0; // unhandled -} - -float Item_mouseDrag(entity me, vector pos) -{ - return 0; // unhandled -} - -float Item_mouseRelease(entity me, vector pos) -{ - return 0; // unhandled -} - -void Item_focusEnter(entity me) -{ - if(me.allowFocusSound) - m_play_focus_sound(); -} - -void Item_focusLeave(entity me) -{ -} - -string Item_toString(entity me) -{ - return string_null; -} + + METHOD(Item, showNotify, void(Item this)) + {} + + METHOD(Item, hideNotify, void(Item this)) + {} + + METHOD(Item, keyDown, float(Item this, float scan, float ascii, float shift)) + { + return 0; // unhandled + } + + METHOD(Item, keyUp, float(Item this, float scan, float ascii, float shift)) + { + return 0; // unhandled + } + + METHOD(Item, mouseMove, float(Item this, vector pos)) + { + return 0; // unhandled + } + + METHOD(Item, mousePress, float(Item this, vector pos)) + { + return 0; // unhandled + } + + METHOD(Item, mouseDrag, float(Item this, vector pos)) + { + return 0; // unhandled + } + + METHOD(Item, mouseRelease, float(Item this, vector pos)) + { + return 0; // unhandled + } + + METHOD(Item, focusEnter, void(Item this)) + { + if (this.allowFocusSound) m_play_focus_sound(); + } + + METHOD(Item, focusLeave, void(Item this)) + {} + + METHOD(Item, toString, string(Item this)) + { + return string_null; + } #endif diff --git a/qcsrc/menu/item/borderimage.qc b/qcsrc/menu/item/borderimage.qc index bcbd408edd..4acf33d861 100644 --- a/qcsrc/menu/item/borderimage.qc +++ b/qcsrc/menu/item/borderimage.qc @@ -1,112 +1,111 @@ #ifndef ITEM_BORDERIMAGE_H -#define ITEM_BORDERIMAGE_H -#include "label.qc" -CLASS(BorderImage, Label) - METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float)); - METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(BorderImage, recalcPositionWithText, void(entity, string)); - ATTRIB(BorderImage, isBold, float, 1) - METHOD(BorderImage, draw, void(entity)); - ATTRIB(BorderImage, src, string, string_null) - ATTRIB(BorderImage, borderHeight, float, 0) - ATTRIB(BorderImage, borderVec, vector, '0 0 0') - ATTRIB(BorderImage, color, vector, '1 1 1') - ATTRIB(BorderImage, closeButton, entity, NULL) - ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0') - ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0') - ATTRIB(BorderImage, isNexposeeTitleBar, float, 0) - ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0) - ATTRIB(BorderImage, zoomedOutTitleBar, float, 0) - ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0') - ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0') - ATTRIB(BorderImage, saveRelSize, vector, '0 0 0') -ENDCLASS(BorderImage) + #define ITEM_BORDERIMAGE_H + #include "label.qc" + CLASS(BorderImage, Label) + METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float)); + METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(BorderImage, recalcPositionWithText, void(entity, string)); + ATTRIB(BorderImage, isBold, float, 1) + METHOD(BorderImage, draw, void(entity)); + ATTRIB(BorderImage, src, string, string_null) + ATTRIB(BorderImage, borderHeight, float, 0) + ATTRIB(BorderImage, borderVec, vector, '0 0 0') + ATTRIB(BorderImage, color, vector, '1 1 1') + ATTRIB(BorderImage, closeButton, entity, NULL) + ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0') + ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0') + ATTRIB(BorderImage, isNexposeeTitleBar, float, 0) + ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0) + ATTRIB(BorderImage, zoomedOutTitleBar, float, 0) + ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0') + ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0') + ATTRIB(BorderImage, saveRelSize, vector, '0 0 0') + ENDCLASS(BorderImage) #endif #ifdef IMPLEMENTATION -void BorderImage_recalcPositionWithText(entity me, string t) -{ - if(me.isNexposeeTitleBar) + void BorderImage_recalcPositionWithText(entity me, string t) { - vector scrs; - scrs = eX * conwidth + eY * conheight; - me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_smallOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_smallSize, scrs)); + if (me.isNexposeeTitleBar) + { + vector scrs; + scrs = eX * conwidth + eY * conheight; + me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_smallOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_smallSize, scrs)); + SUPER(BorderImage).recalcPositionWithText(me, t); + me.realOrigin_y = me.realFontSize.y * me.zoomedOutTitleBarPosition; + me.realOrigin_Nexposeed = me.realOrigin; + me.realFontSize_Nexposeed = me.realFontSize; + me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_initialOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_initialSize, scrs)); + } SUPER(BorderImage).recalcPositionWithText(me, t); - me.realOrigin_y = me.realFontSize.y * me.zoomedOutTitleBarPosition; - me.realOrigin_Nexposeed = me.realOrigin; - me.realFontSize_Nexposeed = me.realFontSize; - me.resizeNotify(me, me.saveRelOrigin, me.saveRelSize, boxToGlobal(me.parent.Nexposee_initialOrigin, '0 0 0', scrs), boxToGlobalSize(me.parent.Nexposee_initialSize, scrs)); } - SUPER(BorderImage).recalcPositionWithText(me, t); -} -void BorderImage_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - me.isNexposeeTitleBar = 0; - if(me.zoomedOutTitleBar) - if(me.parent.parent.instanceOfNexposee) - if(me.parent.instanceOfDialog) - if(me == me.parent.frame) - me.isNexposeeTitleBar = 1; - me.saveRelOrigin = relOrigin; - me.saveRelSize = relSize; - SUPER(BorderImage).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.borderVec_x = me.borderHeight / absSize.x; - me.borderVec_y = me.borderHeight / absSize.y; - me.realOrigin_y = 0.5 * (me.borderVec.y - me.realFontSize.y); - if(me.closeButton) + void BorderImage_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { - // move the close button to the right place - me.closeButton.Container_origin = '1 0 0' * (1 - me.borderVec.x); - me.closeButton.Container_size = me.borderVec; - me.closeButton.color = me.color; - me.closeButton.colorC = me.color; - me.closeButton.colorF = me.color; + me.isNexposeeTitleBar = 0; + if (me.zoomedOutTitleBar) + if (me.parent.parent.instanceOfNexposee) + if (me.parent.instanceOfDialog) + if (me == me.parent.frame) me.isNexposeeTitleBar = 1; + me.saveRelOrigin = relOrigin; + me.saveRelSize = relSize; + SUPER(BorderImage).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + me.borderVec_x = me.borderHeight / absSize.x; + me.borderVec_y = me.borderHeight / absSize.y; + me.realOrigin_y = 0.5 * (me.borderVec.y - me.realFontSize.y); + if (me.closeButton) + { + // move the close button to the right place + me.closeButton.Container_origin = '1 0 0' * (1 - me.borderVec.x); + me.closeButton.Container_size = me.borderVec; + me.closeButton.color = me.color; + me.closeButton.colorC = me.color; + me.closeButton.colorF = me.color; + } } -} -void BorderImage_configureBorderImage(entity me, string theTitle, float sz, vector theColor, string path, float theBorderHeight) -{ - me.configureLabel(me, theTitle, sz, 0.5); - me.src = path; - me.color = theColor; - me.borderHeight = theBorderHeight; -} -void BorderImage_draw(entity me) -{ - if(me.src) - draw_BorderPicture('0 0 0', me.src, '1 1 0', me.color, 1, me.borderVec); - - if(me.fontSize > 0) + void BorderImage_configureBorderImage(entity me, string theTitle, float sz, vector theColor, string path, float theBorderHeight) + { + me.configureLabel(me, theTitle, sz, 0.5); + me.src = path; + me.color = theColor; + me.borderHeight = theBorderHeight; + } + void BorderImage_draw(entity me) { - if(me.recalcPos) - me.recalcPositionWithText(me, me.text); + if (me.src) draw_BorderPicture('0 0 0', me.src, '1 1 0', me.color, 1, me.borderVec); - if(me.isNexposeeTitleBar) + if (me.fontSize > 0) { - vector ro, rf, df; + if (me.recalcPos) me.recalcPositionWithText(me, me.text); - // me.parent.Nexposee_animationFactor 0 (small) or 1 (full) - // default values are for 1 - ro = me.realOrigin; - rf = me.realFontSize; - df = draw_fontscale; - me.realOrigin = ro * me.parent.Nexposee_animationFactor + me.realOrigin_Nexposeed * (1 - me.parent.Nexposee_animationFactor); - me.realFontSize = rf * me.parent.Nexposee_animationFactor + me.realFontSize_Nexposeed * (1 - me.parent.Nexposee_animationFactor); - draw_fontscale = globalToBoxSize(boxToGlobalSize(df, me.realFontSize), rf); + if (me.isNexposeeTitleBar) + { + vector ro, rf, df; - SUPER(BorderImage).draw(me); + // me.parent.Nexposee_animationFactor 0 (small) or 1 (full) + // default values are for 1 + ro = me.realOrigin; + rf = me.realFontSize; + df = draw_fontscale; + me.realOrigin = ro * me.parent.Nexposee_animationFactor + me.realOrigin_Nexposeed * (1 - me.parent.Nexposee_animationFactor); + me.realFontSize = rf * me.parent.Nexposee_animationFactor + me.realFontSize_Nexposeed * (1 - me.parent.Nexposee_animationFactor); + draw_fontscale = globalToBoxSize(boxToGlobalSize(df, me.realFontSize), rf); + + SUPER(BorderImage).draw(me); - // me.Nexposee_animationState 0 (small) or 1 (full) - // default values are for 1 - me.realOrigin = ro; - me.realFontSize = rf; - draw_fontscale = df; + // me.Nexposee_animationState 0 (small) or 1 (full) + // default values are for 1 + me.realOrigin = ro; + me.realFontSize = rf; + draw_fontscale = df; + } + else + { + SUPER(BorderImage).draw(me); + } } else + { SUPER(BorderImage).draw(me); + } } - else - { - SUPER(BorderImage).draw(me); - } -} #endif diff --git a/qcsrc/menu/item/button.qc b/qcsrc/menu/item/button.qc index 934e8cd342..a9112b88ff 100644 --- a/qcsrc/menu/item/button.qc +++ b/qcsrc/menu/item/button.qc @@ -1,180 +1,164 @@ #ifndef ITEM_BUTTON_H -#define ITEM_BUTTON_H -#include "label.qc" -CLASS(Button, Label) - METHOD(Button, configureButton, void(entity, string, float, string)); - METHOD(Button, draw, void(entity)); - METHOD(Button, showNotify, void(entity)); - METHOD(Button, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Button, keyDown, float(entity, float, float, float)); - METHOD(Button, mousePress, float(entity, vector)); - METHOD(Button, mouseDrag, float(entity, vector)); - METHOD(Button, mouseRelease, float(entity, vector)); - METHOD(Button, playClickSound, void(entity)); - ATTRIB(Button, onClick, void(entity, entity), func_null) - ATTRIB(Button, onClickEntity, entity, NULL) - ATTRIB(Button, src, string, string_null) - ATTRIB(Button, srcSuffix, string, string_null) - ATTRIB(Button, src2, string, string_null) // is centered, same aspect, and stretched to label size - ATTRIB(Button, src2scale, float, 1) - ATTRIB(Button, srcMulti, float, 1) // 0: button square left, text right; 1: button stretched, text over it - ATTRIB(Button, buttonLeftOfText, float, 0) - ATTRIB(Button, focusable, float, 1) - ATTRIB(Button, allowFocusSound, float, 1) - ATTRIB(Button, pressed, float, 0) - ATTRIB(Button, clickTime, float, 0) - ATTRIB(Button, disabled, float, 0) - ATTRIB(Button, disabledAlpha, float, 0.3) - ATTRIB(Button, forcePressed, float, 0) - ATTRIB(Button, color, vector, '1 1 1') - ATTRIB(Button, colorC, vector, '1 1 1') - ATTRIB(Button, colorF, vector, '1 1 1') - ATTRIB(Button, colorD, vector, '1 1 1') - ATTRIB(Button, color2, vector, '1 1 1') - ATTRIB(Button, alpha2, float, 1) + #define ITEM_BUTTON_H + #include "label.qc" + CLASS(Button, Label) + METHOD(Button, configureButton, void(entity, string, float, string)); + METHOD(Button, draw, void(entity)); + METHOD(Button, showNotify, void(entity)); + METHOD(Button, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Button, keyDown, float(entity, float, float, float)); + METHOD(Button, mousePress, float(entity, vector)); + METHOD(Button, mouseDrag, float(entity, vector)); + METHOD(Button, mouseRelease, float(entity, vector)); + METHOD(Button, playClickSound, void(entity)); + ATTRIB(Button, onClick, void(entity, entity), func_null) + ATTRIB(Button, onClickEntity, entity, NULL) + ATTRIB(Button, src, string, string_null) + ATTRIB(Button, srcSuffix, string, string_null) + ATTRIB(Button, src2, string, string_null) // is centered, same aspect, and stretched to label size + ATTRIB(Button, src2scale, float, 1) + ATTRIB(Button, srcMulti, float, 1) // 0: button square left, text right; 1: button stretched, text over it + ATTRIB(Button, buttonLeftOfText, float, 0) + ATTRIB(Button, focusable, float, 1) + ATTRIB(Button, allowFocusSound, float, 1) + ATTRIB(Button, pressed, float, 0) + ATTRIB(Button, clickTime, float, 0) + ATTRIB(Button, disabled, float, 0) + ATTRIB(Button, disabledAlpha, float, 0.3) + ATTRIB(Button, forcePressed, float, 0) + ATTRIB(Button, color, vector, '1 1 1') + ATTRIB(Button, colorC, vector, '1 1 1') + ATTRIB(Button, colorF, vector, '1 1 1') + ATTRIB(Button, colorD, vector, '1 1 1') + ATTRIB(Button, color2, vector, '1 1 1') + ATTRIB(Button, alpha2, float, 1) - ATTRIB(Button, origin, vector, '0 0 0') - ATTRIB(Button, size, vector, '0 0 0') -ENDCLASS(Button) + ATTRIB(Button, origin, vector, '0 0 0') + ATTRIB(Button, size, vector, '0 0 0') + ENDCLASS(Button) #endif #ifdef IMPLEMENTATION -void Button_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - if(me.srcMulti) - me.keepspaceLeft = 0; - else - me.keepspaceLeft = min(0.8, absSize.x == 0 ? 0 : (absSize.y / absSize.x)); - SUPER(Button).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); -} -void Button_configureButton(entity me, string txt, float sz, string gfx) -{ - SUPER(Button).configureLabel(me, txt, sz, me.srcMulti ? 0.5 : 0); - me.src = gfx; -} -float Button_keyDown(entity me, float key, float ascii, float shift) -{ - if(key == K_ENTER || key == K_SPACE || key == K_KP_ENTER) + void Button_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { - me.playClickSound(me); - me.clickTime = 0.1; // delayed for effect - return 1; + if (me.srcMulti) me.keepspaceLeft = 0; + else me.keepspaceLeft = min(0.8, absSize.x == 0 ? 0 : (absSize.y / absSize.x)); + SUPER(Button).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + } + void Button_configureButton(entity me, string txt, float sz, string gfx) + { + SUPER(Button).configureLabel(me, txt, sz, me.srcMulti ? 0.5 : 0); + me.src = gfx; } - return 0; -} -float Button_mouseDrag(entity me, vector pos) -{ - me.pressed = 1; - if(pos.x < 0) me.pressed = 0; - if(pos.y < 0) me.pressed = 0; - if(pos.x >= 1) me.pressed = 0; - if(pos.y >= 1) me.pressed = 0; - return 1; -} -float Button_mousePress(entity me, vector pos) -{ - me.mouseDrag(me, pos); // verify coordinates - return 1; -} -float Button_mouseRelease(entity me, vector pos) -{ - me.mouseDrag(me, pos); // verify coordinates - if(me.pressed) + float Button_keyDown(entity me, float key, float ascii, float shift) { - if (!me.disabled) + if (key == K_ENTER || key == K_SPACE || key == K_KP_ENTER) { me.playClickSound(me); - if(me.onClick) - me.onClick(me, me.onClickEntity); + me.clickTime = 0.1; // delayed for effect + return 1; } - me.pressed = 0; + return 0; } - return 1; -} -void Button_showNotify(entity me) -{ - me.focusable = !me.disabled; -} -void Button_draw(entity me) -{ - vector bOrigin, bSize; - float save; - - me.focusable = !me.disabled; - - save = draw_alpha; - if(me.disabled) - draw_alpha *= me.disabledAlpha; - - if(me.src) + float Button_mouseDrag(entity me, vector pos) + { + me.pressed = 1; + if (pos.x < 0) me.pressed = 0; + if (pos.y < 0) me.pressed = 0; + if (pos.x >= 1) me.pressed = 0; + if (pos.y >= 1) me.pressed = 0; + return 1; + } + float Button_mousePress(entity me, vector pos) { - if(me.srcMulti) + me.mouseDrag(me, pos); // verify coordinates + return 1; + } + float Button_mouseRelease(entity me, vector pos) + { + me.mouseDrag(me, pos); // verify coordinates + if (me.pressed) { - bOrigin = '0 0 0'; - bSize = '1 1 0'; - if(me.disabled) - draw_ButtonPicture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1); - else if(me.forcePressed || me.pressed || me.clickTime > 0) - draw_ButtonPicture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1); - else if(me.focused) - draw_ButtonPicture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1); - else - draw_ButtonPicture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1); + if (!me.disabled) + { + me.playClickSound(me); + if (me.onClick) me.onClick(me, me.onClickEntity); + } + me.pressed = 0; } - else + return 1; + } + void Button_showNotify(entity me) + { + me.focusable = !me.disabled; + } + void Button_draw(entity me) + { + vector bOrigin, bSize; + float save; + + me.focusable = !me.disabled; + + save = draw_alpha; + if (me.disabled) draw_alpha *= me.disabledAlpha; + + if (me.src) { - if(me.realFontSize_y == 0) + if (me.srcMulti) { bOrigin = '0 0 0'; bSize = '1 1 0'; + if (me.disabled) draw_ButtonPicture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1); + else if (me.forcePressed || me.pressed || me.clickTime > 0) draw_ButtonPicture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1); + else if (me.focused) draw_ButtonPicture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1); + else draw_ButtonPicture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1); } else { - bOrigin = eY * (0.5 * (1 - me.realFontSize.y)) + eX * (0.5 * (me.keepspaceLeft - me.realFontSize.x)); - bSize = me.realFontSize; + if (me.realFontSize_y == 0) + { + bOrigin = '0 0 0'; + bSize = '1 1 0'; + } + else + { + bOrigin = eY * (0.5 * (1 - me.realFontSize.y)) + eX * (0.5 * (me.keepspaceLeft - me.realFontSize.x)); + bSize = me.realFontSize; + } + if (me.disabled) draw_Picture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1); + else if (me.forcePressed || me.pressed || me.clickTime > 0) draw_Picture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1); + else if (me.focused) draw_Picture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1); + else draw_Picture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1); } - if(me.disabled) - draw_Picture(bOrigin, strcat(me.src, "_d", me.srcSuffix), bSize, me.colorD, 1); - else if(me.forcePressed || me.pressed || me.clickTime > 0) - draw_Picture(bOrigin, strcat(me.src, "_c", me.srcSuffix), bSize, me.colorC, 1); - else if(me.focused) - draw_Picture(bOrigin, strcat(me.src, "_f", me.srcSuffix), bSize, me.colorF, 1); - else - draw_Picture(bOrigin, strcat(me.src, "_n", me.srcSuffix), bSize, me.color, 1); } - } - if(me.src2) - { - bOrigin = me.keepspaceLeft * eX; - bSize = eY + eX * (1 - me.keepspaceLeft); + if (me.src2) + { + bOrigin = me.keepspaceLeft * eX; + bSize = eY + eX * (1 - me.keepspaceLeft); - bOrigin += bSize * (0.5 - 0.5 * me.src2scale); - bSize = bSize * me.src2scale; + bOrigin += bSize * (0.5 - 0.5 * me.src2scale); + bSize = bSize * me.src2scale; - draw_Picture(bOrigin, me.src2, bSize, me.color2, me.alpha2); - } + draw_Picture(bOrigin, me.src2, bSize, me.color2, me.alpha2); + } + + draw_alpha = save; - draw_alpha = save; + if (me.clickTime > 0 && me.clickTime <= frametime) + { + // keyboard click timer expired? Fire the event then. + if (!me.disabled) + if (me.onClick) me.onClick(me, me.onClickEntity); + } + me.clickTime -= frametime; - if(me.clickTime > 0 && me.clickTime <= frametime) + SUPER(Button).draw(me); + } + void Dialog_Close(entity button, entity me); + void Button_playClickSound(entity me) { - // keyboard click timer expired? Fire the event then. - if (!me.disabled) - if(me.onClick) - me.onClick(me, me.onClickEntity); + if (me.onClick == DialogOpenButton_Click) m_play_click_sound(MENU_SOUND_OPEN); + else if (me.onClick == Dialog_Close) m_play_click_sound(MENU_SOUND_CLOSE); + else m_play_click_sound(MENU_SOUND_EXECUTE); } - me.clickTime -= frametime; - - SUPER(Button).draw(me); -} -void Dialog_Close(entity button, entity me); -void Button_playClickSound(entity me) -{ - if(me.onClick == DialogOpenButton_Click) - m_play_click_sound(MENU_SOUND_OPEN); - else if(me.onClick == Dialog_Close) - m_play_click_sound(MENU_SOUND_CLOSE); - else - m_play_click_sound(MENU_SOUND_EXECUTE); -} #endif diff --git a/qcsrc/menu/item/checkbox.qc b/qcsrc/menu/item/checkbox.qc index cda07c518b..17bc50103e 100644 --- a/qcsrc/menu/item/checkbox.qc +++ b/qcsrc/menu/item/checkbox.qc @@ -1,55 +1,57 @@ #ifndef ITEM_CHECKBOX_H -#define ITEM_CHECKBOX_H -#include "button.qc" -void CheckBox_Click(entity me, entity other); -CLASS(CheckBox, Button) - METHOD(CheckBox, configureCheckBox, void(entity, string, float, string)); - METHOD(CheckBox, draw, void(entity)); - METHOD(CheckBox, playClickSound, void(entity)); - METHOD(CheckBox, toString, string(entity)); - METHOD(CheckBox, setChecked, void(entity, float)); - ATTRIB(CheckBox, useDownAsChecked, float, 0) - ATTRIB(CheckBox, checked, float, 0) - ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click) - ATTRIB(CheckBox, srcMulti, float, 0) - ATTRIB(CheckBox, disabled, float, 0) -ENDCLASS(CheckBox) + #define ITEM_CHECKBOX_H + #include "button.qc" + void CheckBox_Click(entity me, entity other); + CLASS(CheckBox, Button) + METHOD(CheckBox, configureCheckBox, void(entity, string, float, string)); + METHOD(CheckBox, draw, void(entity)); + METHOD(CheckBox, playClickSound, void(entity)); + METHOD(CheckBox, toString, string(entity)); + METHOD(CheckBox, setChecked, void(entity, float)); + ATTRIB(CheckBox, useDownAsChecked, float, 0) + ATTRIB(CheckBox, checked, float, 0) + ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click) + ATTRIB(CheckBox, srcMulti, float, 0) + ATTRIB(CheckBox, disabled, float, 0) + ENDCLASS(CheckBox) #endif #ifdef IMPLEMENTATION -void CheckBox_setChecked(entity me, float val) -{ - me.checked = val; -} -void CheckBox_Click(entity me, entity other) -{ - me.setChecked(me, !me.checked); -} -string CheckBox_toString(entity me) -{ - return strcat(SUPER(CheckBox).toString(me), ", ", me.checked ? "checked" : "unchecked"); -} -void CheckBox_configureCheckBox(entity me, string txt, float sz, string gfx) -{ - me.configureButton(me, txt, sz, gfx); - me.align = 0; -} -void CheckBox_draw(entity me) -{ - float s; - s = me.pressed; - if(me.useDownAsChecked) + void CheckBox_setChecked(entity me, float val) { - me.srcSuffix = string_null; - me.forcePressed = me.checked; + me.checked = val; + } + void CheckBox_Click(entity me, entity other) + { + me.setChecked(me, !me.checked); + } + string CheckBox_toString(entity me) + { + return strcat(SUPER(CheckBox).toString(me), ", ", me.checked ? "checked" : "unchecked"); + } + void CheckBox_configureCheckBox(entity me, string txt, float sz, string gfx) + { + me.configureButton(me, txt, sz, gfx); + me.align = 0; + } + void CheckBox_draw(entity me) + { + float s; + s = me.pressed; + if (me.useDownAsChecked) + { + me.srcSuffix = string_null; + me.forcePressed = me.checked; + } + else + { + me.srcSuffix = (me.checked ? "1" : "0"); + } + me.pressed = s; + SUPER(CheckBox).draw(me); + } + void CheckBox_playClickSound(entity me) + { + m_play_click_sound(MENU_SOUND_SELECT); } - else - me.srcSuffix = (me.checked ? "1" : "0"); - me.pressed = s; - SUPER(CheckBox).draw(me); -} -void CheckBox_playClickSound(entity me) -{ - m_play_click_sound(MENU_SOUND_SELECT); -} #endif diff --git a/qcsrc/menu/item/container.qc b/qcsrc/menu/item/container.qc index cacb0124a2..23857dcead 100644 --- a/qcsrc/menu/item/container.qc +++ b/qcsrc/menu/item/container.qc @@ -1,461 +1,426 @@ #ifndef ITEM_CONTAINER_H -#define ITEM_CONTAINER_H -#include "../item.qc" -CLASS(Container, Item) - METHOD(Container, draw, void(entity)); - METHOD(Container, keyUp, float(entity, float, float, float)); - METHOD(Container, keyDown, float(entity, float, float, float)); - METHOD(Container, mouseMove, float(entity, vector)); - METHOD(Container, mousePress, float(entity, vector)); - METHOD(Container, mouseDrag, float(entity, vector)); - METHOD(Container, mouseRelease, float(entity, vector)); - METHOD(Container, focusLeave, void(entity)); - METHOD(Container, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Container, resizeNotifyLie, void(entity, vector, vector, vector, vector, .vector, .vector, .vector)); - METHOD(Container, addItem, void(entity, entity, vector, vector, float)); - METHOD(Container, addItemCentered, void(entity, entity, vector, float)); - METHOD(Container, addItemRightCentered, void(entity, entity, vector, float)); - METHOD(Container, moveItemAfter, void(entity, entity, entity)); - METHOD(Container, removeItem, void(entity, entity)); - METHOD(Container, setFocus, void(entity, entity)); - METHOD(Container, saveFocus, void(entity)); - METHOD(Container, setAlphaOf, void(entity, entity, float)); - METHOD(Container, itemFromPoint, entity(entity, vector)); - METHOD(Container, showNotify, void(entity)); - METHOD(Container, hideNotify, void(entity)); - METHOD(Container, preferredFocusedGrandChild, entity(entity)); - ATTRIB(Container, focusable, float, 0) - ATTRIB(Container, firstChild, entity, NULL) - ATTRIB(Container, lastChild, entity, NULL) - ATTRIB(Container, focusedChild, entity, NULL) - ATTRIB(Container, savedFocus, entity, NULL) - ATTRIB(Container, shown, float, 0) - - METHOD(Container, enterSubitem, void(entity, entity)); - METHOD(Container, enterLieSubitem, void(entity, vector, vector, vector, float)); - METHOD(Container, leaveSubitem, void(entity)); -ENDCLASS(Container) -.entity nextSibling; -.entity prevSibling; -.float resized; -.vector Container_origin; -.vector Container_size; -.vector Container_fontscale; -.float Container_alpha; -.vector Container_save_shift; -.vector Container_save_scale; -.vector Container_save_fontscale; -.float Container_save_alpha; + #define ITEM_CONTAINER_H + #include "../item.qc" + CLASS(Container, Item) + METHOD(Container, draw, void(entity)); + METHOD(Container, keyUp, float(entity, float, float, float)); + METHOD(Container, keyDown, float(entity, float, float, float)); + METHOD(Container, mouseMove, float(entity, vector)); + METHOD(Container, mousePress, float(entity, vector)); + METHOD(Container, mouseDrag, float(entity, vector)); + METHOD(Container, mouseRelease, float(entity, vector)); + METHOD(Container, focusLeave, void(entity)); + METHOD(Container, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Container, resizeNotifyLie, void(entity, vector, vector, vector, vector, .vector, .vector, .vector)); + METHOD(Container, addItem, void(entity, entity, vector, vector, float)); + METHOD(Container, addItemCentered, void(entity, entity, vector, float)); + METHOD(Container, addItemRightCentered, void(entity, entity, vector, float)); + METHOD(Container, moveItemAfter, void(entity, entity, entity)); + METHOD(Container, removeItem, void(entity, entity)); + METHOD(Container, setFocus, void(entity, entity)); + METHOD(Container, saveFocus, void(entity)); + METHOD(Container, setAlphaOf, void(entity, entity, float)); + METHOD(Container, itemFromPoint, entity(entity, vector)); + METHOD(Container, showNotify, void(entity)); + METHOD(Container, hideNotify, void(entity)); + METHOD(Container, preferredFocusedGrandChild, entity(entity)); + ATTRIB(Container, focusable, float, 0) + ATTRIB(Container, firstChild, entity, NULL) + ATTRIB(Container, lastChild, entity, NULL) + ATTRIB(Container, focusedChild, entity, NULL) + ATTRIB(Container, savedFocus, entity, NULL) + ATTRIB(Container, shown, float, 0) + + METHOD(Container, enterSubitem, void(entity, entity)); + METHOD(Container, enterLieSubitem, void(entity, vector, vector, vector, float)); + METHOD(Container, leaveSubitem, void(entity)); + ENDCLASS(Container) + .entity nextSibling; + .entity prevSibling; + .float resized; + .vector Container_origin; + .vector Container_size; + .vector Container_fontscale; + .float Container_alpha; + .vector Container_save_shift; + .vector Container_save_scale; + .vector Container_save_fontscale; + .float Container_save_alpha; #endif #ifdef IMPLEMENTATION -void Container_enterSubitem(entity me, entity sub) -{ - me.enterLieSubitem(me, sub.Container_origin, sub.Container_size, sub.Container_fontscale, sub.Container_alpha); -} - -void Container_enterLieSubitem(entity me, vector o, vector s, vector f, float a) -{ - me.Container_save_shift = draw_shift; - me.Container_save_scale = draw_scale; - me.Container_save_alpha = draw_alpha; - me.Container_save_fontscale = draw_fontscale; - - draw_shift = boxToGlobal(o, draw_shift, draw_scale); - draw_scale = boxToGlobalSize(s, draw_scale); - if(f != '0 0 0') - draw_fontscale = boxToGlobalSize(f, draw_fontscale); - draw_alpha *= a; -} - -void Container_leaveSubitem(entity me) -{ - draw_shift = me.Container_save_shift; - draw_scale = me.Container_save_scale; - draw_alpha = me.Container_save_alpha; - draw_fontscale = me.Container_save_fontscale; -} - -void Container_showNotify(entity me) -{ - entity e; - if(me.shown) - return; - me.shown = 1; - for(e = me.firstChild; e; e = e.nextSibling) - if(e.Container_alpha > 0) - e.showNotify(e); -} - -void Container_hideNotify(entity me) -{ - entity e; - if (!me.shown) - return; - me.shown = 0; - for(e = me.firstChild; e; e = e.nextSibling) - if(e.Container_alpha > 0) - e.hideNotify(e); -} - -void Container_setAlphaOf(entity me, entity other, float theAlpha) -{ - if(theAlpha <= 0) + void Container_enterSubitem(entity me, entity sub) { - if(other.Container_alpha > 0) - other.hideNotify(other); + me.enterLieSubitem(me, sub.Container_origin, sub.Container_size, sub.Container_fontscale, sub.Container_alpha); } - else // value > 0 + + void Container_enterLieSubitem(entity me, vector o, vector s, vector f, float a) { - if(other.Container_alpha <= 0) - other.showNotify(other); + me.Container_save_shift = draw_shift; + me.Container_save_scale = draw_scale; + me.Container_save_alpha = draw_alpha; + me.Container_save_fontscale = draw_fontscale; + + draw_shift = boxToGlobal(o, draw_shift, draw_scale); + draw_scale = boxToGlobalSize(s, draw_scale); + if (f != '0 0 0') draw_fontscale = boxToGlobalSize(f, draw_fontscale); + draw_alpha *= a; } - other.Container_alpha = theAlpha; -} - -void Container_resizeNotifyLie(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize, .vector originField, .vector sizeField, .vector fontScaleField) -{ - entity e; - vector o, s; - float d; - for(e = me.firstChild; e; e = e.nextSibling) + + void Container_leaveSubitem(entity me) { - o = e.(originField); - s = e.(sizeField); - me.enterLieSubitem(me, o, s, e.(fontScaleField), e.Container_alpha); - e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize)); - me.leaveSubitem(me); + draw_shift = me.Container_save_shift; + draw_scale = me.Container_save_scale; + draw_alpha = me.Container_save_alpha; + draw_fontscale = me.Container_save_fontscale; } - do + + void Container_showNotify(entity me) { - d = 0; - for(e = me.firstChild; e; e = e.nextSibling) - if(e.resized) - { - e.resized = 0; - d = 1; - o = e.(originField); - s = e.(sizeField); - me.enterLieSubitem(me, o, s, e.(fontScaleField), e.Container_alpha); - e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize)); - me.leaveSubitem(me); - } + entity e; + if (me.shown) return; + me.shown = 1; + for (e = me.firstChild; e; e = e.nextSibling) + if (e.Container_alpha > 0) e.showNotify(e); + } + + void Container_hideNotify(entity me) + { + entity e; + if (!me.shown) return; + me.shown = 0; + for (e = me.firstChild; e; e = e.nextSibling) + if (e.Container_alpha > 0) e.hideNotify(e); + } + + void Container_setAlphaOf(entity me, entity other, float theAlpha) + { + if (theAlpha <= 0) + { + if (other.Container_alpha > 0) other.hideNotify(other); + } + else // value > 0 + { + if (other.Container_alpha <= 0) other.showNotify(other); + } + other.Container_alpha = theAlpha; } - while(d); - SUPER(Container).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); -} - -void Container_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Container_origin, Container_size, Container_fontscale); -} - -entity Container_itemFromPoint(entity me, vector pos) -{ - entity e; - vector o, s; - for(e = me.lastChild; e; e = e.prevSibling) + + void Container_resizeNotifyLie(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize, .vector originField, .vector sizeField, .vector fontScaleField) { - o = e.Container_origin; - s = e.Container_size; - if(pos.x < o.x) continue; - if(pos.y < o.y) continue; - if(pos.x >= o.x + s.x) continue; - if(pos.y >= o.y + s.y) continue; - return e; + entity e; + vector o, s; + float d; + for (e = me.firstChild; e; e = e.nextSibling) + { + o = e.(originField); + s = e.(sizeField); + me.enterLieSubitem(me, o, s, e.(fontScaleField), e.Container_alpha); + e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize)); + me.leaveSubitem(me); + } + do + { + d = 0; + for (e = me.firstChild; e; e = e.nextSibling) + if (e.resized) + { + e.resized = 0; + d = 1; + o = e.(originField); + s = e.(sizeField); + me.enterLieSubitem(me, o, s, e.(fontScaleField), e.Container_alpha); + e.resizeNotify(e, o, s, boxToGlobal(o, absOrigin, absSize), boxToGlobalSize(s, absSize)); + me.leaveSubitem(me); + } + } + while (d); + SUPER(Container).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); } - return NULL; -} -void Container_draw(entity me) -{ - entity e; + void Container_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Container_origin, Container_size, Container_fontscale); + } - me.focusable = 0; - for(e = me.firstChild; e; e = e.nextSibling) + entity Container_itemFromPoint(entity me, vector pos) { - if(e.focusable) - me.focusable += 1; - if(e.Container_alpha < 0.003) // can't change color values anyway - continue; - me.enterSubitem(me, e); - e.draw(e); - me.leaveSubitem(me); + entity e; + vector o, s; + for (e = me.lastChild; e; e = e.prevSibling) + { + o = e.Container_origin; + s = e.Container_size; + if (pos.x < o.x) continue; + if (pos.y < o.y) continue; + if (pos.x >= o.x + s.x) continue; + if (pos.y >= o.y + s.y) continue; + return e; + } + return NULL; } - SUPER(Container).draw(me); -} + void Container_draw(entity me) + { + entity e; + + me.focusable = 0; + for (e = me.firstChild; e; e = e.nextSibling) + { + if (e.focusable) me.focusable += 1; + if (e.Container_alpha < 0.003) // can't change color values anyway + continue; + me.enterSubitem(me, e); + e.draw(e); + me.leaveSubitem(me); + } -void Container_focusLeave(entity me) -{ - me.setFocus(me, NULL); -} + SUPER(Container).draw(me); + } -float Container_keyUp(entity me, float scan, float ascii, float shift) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + void Container_focusLeave(entity me) { - me.enterSubitem(me, f); - r = f.keyUp(f, scan, ascii, shift); - me.leaveSubitem(me); - return r; + me.setFocus(me, NULL); } - return 0; -} - -float Container_keyDown(entity me, float scan, float ascii, float shift) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + + float Container_keyUp(entity me, float scan, float ascii, float shift) { - me.enterSubitem(me, f); - r = f.keyDown(f, scan, ascii, shift); - me.leaveSubitem(me); - return r; + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.keyUp(f, scan, ascii, shift); + me.leaveSubitem(me); + return r; + } + return 0; } - return 0; -} - -float Container_mouseMove(entity me, vector pos) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + + float Container_keyDown(entity me, float scan, float ascii, float shift) { - me.enterSubitem(me, f); - r = f.mouseMove(f, globalToBox(pos, f.Container_origin, f.Container_size)); - me.leaveSubitem(me); - return r; + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.keyDown(f, scan, ascii, shift); + me.leaveSubitem(me); + return r; + } + return 0; } - return 0; -} -float Container_mousePress(entity me, vector pos) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + + float Container_mouseMove(entity me, vector pos) { - me.enterSubitem(me, f); - r = f.mousePress(f, globalToBox(pos, f.Container_origin, f.Container_size)); - me.leaveSubitem(me); - return r; + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.mouseMove(f, globalToBox(pos, f.Container_origin, f.Container_size)); + me.leaveSubitem(me); + return r; + } + return 0; } - return 0; -} -float Container_mouseDrag(entity me, vector pos) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + float Container_mousePress(entity me, vector pos) { - me.enterSubitem(me, f); - r = f.mouseDrag(f, globalToBox(pos, f.Container_origin, f.Container_size)); - me.leaveSubitem(me); - return r; + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.mousePress(f, globalToBox(pos, f.Container_origin, f.Container_size)); + me.leaveSubitem(me); + return r; + } + return 0; } - return 0; -} -float Container_mouseRelease(entity me, vector pos) -{ - entity f; - float r; - f = me.focusedChild; - if(f) + float Container_mouseDrag(entity me, vector pos) { - me.enterSubitem(me, f); - r = f.mouseRelease(f, globalToBox(pos, f.Container_origin, f.Container_size)); - me.leaveSubitem(me); - return r; + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.mouseDrag(f, globalToBox(pos, f.Container_origin, f.Container_size)); + me.leaveSubitem(me); + return r; + } + return 0; + } + float Container_mouseRelease(entity me, vector pos) + { + entity f; + float r; + f = me.focusedChild; + if (f) + { + me.enterSubitem(me, f); + r = f.mouseRelease(f, globalToBox(pos, f.Container_origin, f.Container_size)); + me.leaveSubitem(me); + return r; + } + return 0; } - return 0; -} - -void Container_addItemCentered(entity me, entity other, vector theSize, float theAlpha) -{ - me.addItem(me, other, '0.5 0.5 0' - 0.5 * theSize, theSize, theAlpha); -} - -void Container_addItemRightCentered(entity me, entity other, vector theSize, float theAlpha) -{ - me.addItem(me, other, '1 0.5 0' - 0.5 * theSize, theSize, theAlpha); -} - -void Container_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) -{ - if(other.parent) - error("Can't add already added item!"); - if(other.focusable) - me.focusable += 1; + void Container_addItemCentered(entity me, entity other, vector theSize, float theAlpha) + { + me.addItem(me, other, '0.5 0.5 0' - 0.5 * theSize, theSize, theAlpha); + } - if(theSize.x > 1) + void Container_addItemRightCentered(entity me, entity other, vector theSize, float theAlpha) { - theOrigin.x -= 0.5 * (theSize.x - 1); - theSize.x = 1; + me.addItem(me, other, '1 0.5 0' - 0.5 * theSize, theSize, theAlpha); } - if(theSize.y > 1) + + void Container_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) { - theOrigin.y -= 0.5 * (theSize.y - 1); - theSize.y = 1; + if (other.parent) error("Can't add already added item!"); + + if (other.focusable) me.focusable += 1; + + if (theSize.x > 1) + { + theOrigin.x -= 0.5 * (theSize.x - 1); + theSize.x = 1; + } + if (theSize.y > 1) + { + theOrigin.y -= 0.5 * (theSize.y - 1); + theSize.y = 1; + } + theOrigin.x = bound(0, theOrigin.x, 1 - theSize.x); + theOrigin.y = bound(0, theOrigin.y, 1 - theSize.y); + + other.parent = me; + other.Container_origin = theOrigin; + other.Container_size = theSize; + me.setAlphaOf(me, other, theAlpha); + + entity l; + l = me.lastChild; + + if (l) l.nextSibling = other; + else me.firstChild = other; + + other.prevSibling = l; + other.nextSibling = NULL; + me.lastChild = other; } - theOrigin.x = bound(0, theOrigin.x, 1 - theSize.x); - theOrigin.y = bound(0, theOrigin.y, 1 - theSize.y); - - other.parent = me; - other.Container_origin = theOrigin; - other.Container_size = theSize; - me.setAlphaOf(me, other, theAlpha); - - entity l; - l = me.lastChild; - - if(l) - l.nextSibling = other; - else - me.firstChild = other; - - other.prevSibling = l; - other.nextSibling = NULL; - me.lastChild = other; -} - -void Container_removeItem(entity me, entity other) -{ - if(other.parent != me) - error("Can't remove from wrong container!"); - - if(other.focusable) - me.focusable -= 1; - - other.parent = NULL; - - entity n, p; - n = other.nextSibling; - p = other.prevSibling; - - if(p) - p.nextSibling = n; - else - me.firstChild = n; - - if(n) - n.prevSibling = p; - else - me.lastChild = p; -} - -void Container_setFocus(entity me, entity other) -{ - if(me.focusedChild == other) - return; - - if(me.focusedChild) + + void Container_removeItem(entity me, entity other) { - me.focusedChild.focused = 0; - me.focusedChild.focusLeave(me.focusedChild); - me.focusedChild = NULL; + if (other.parent != me) error("Can't remove from wrong container!"); + + if (other.focusable) me.focusable -= 1; + + other.parent = NULL; + + entity n, p; + n = other.nextSibling; + p = other.prevSibling; + + if (p) p.nextSibling = n; + else me.firstChild = n; + + if (n) n.prevSibling = p; + else me.lastChild = p; } - if(other) + void Container_setFocus(entity me, entity other) { - if(!me.focused) - error("Trying to set focus in a non-focused control!"); + if (me.focusedChild == other) return; - if(me.savedFocus) + if (me.focusedChild) { - me.focusedChild = me.savedFocus; - me.savedFocus = NULL; - me.focusedChild.focused = 1; - me.focusedChild.focusEnter(me.focusedChild); - - if(me.focusedChild.instanceOfContainer) - me.focusedChild.setFocus(me.focusedChild, me.focusedChild.savedFocus); + me.focusedChild.focused = 0; + me.focusedChild.focusLeave(me.focusedChild); + me.focusedChild = NULL; } - else + + if (other) { - me.focusedChild = other; - me.focusedChild.focused = 1; - me.focusedChild.focusEnter(me.focusedChild); + if (!me.focused) error("Trying to set focus in a non-focused control!"); + + if (me.savedFocus) + { + me.focusedChild = me.savedFocus; + me.savedFocus = NULL; + me.focusedChild.focused = 1; + me.focusedChild.focusEnter(me.focusedChild); + + if (me.focusedChild.instanceOfContainer) me.focusedChild.setFocus(me.focusedChild, me.focusedChild.savedFocus); + } + else + { + me.focusedChild = other; + me.focusedChild.focused = 1; + me.focusedChild.focusEnter(me.focusedChild); + } } } -} - -void Container_saveFocus(entity me) -{ - me.savedFocus = me.focusedChild; - - if(me.focusedChild.instanceOfContainer) - me.focusedChild.saveFocus(me.focusedChild); -} - -void Container_moveItemAfter(entity me, entity other, entity dest) -{ - // first: remove other from the chain - entity n, p; - - if(other.parent != me) - error("Can't move in wrong container!"); - - n = other.nextSibling; - p = other.prevSibling; - - if(p) - p.nextSibling = n; - else - me.firstChild = n; - - if(n) - n.prevSibling = p; - else - me.lastChild = p; - - // now other got removed. Insert it behind dest now. - other.prevSibling = dest; - if(dest) - other.nextSibling = dest.nextSibling; - else - other.nextSibling = me.firstChild; - - if(dest) - dest.nextSibling = other; - else - me.firstChild = other; - - if(other.nextSibling) - other.nextSibling.prevSibling = other; - else - me.lastChild = other; -} -entity Container_preferredFocusedGrandChild(entity me) -{ - entity e, e2; - entity best; + void Container_saveFocus(entity me) + { + me.savedFocus = me.focusedChild; + + if (me.focusedChild.instanceOfContainer) me.focusedChild.saveFocus(me.focusedChild); + } + + void Container_moveItemAfter(entity me, entity other, entity dest) + { + // first: remove other from the chain + entity n, p; + + if (other.parent != me) error("Can't move in wrong container!"); - best = NULL; + n = other.nextSibling; + p = other.prevSibling; - for(e = me.firstChild; e; e = e.nextSibling) + if (p) p.nextSibling = n; + else me.firstChild = n; + + if (n) n.prevSibling = p; + else me.lastChild = p; + + // now other got removed. Insert it behind dest now. + other.prevSibling = dest; + if (dest) other.nextSibling = dest.nextSibling; + else other.nextSibling = me.firstChild; + + if (dest) dest.nextSibling = other; + else me.firstChild = other; + + if (other.nextSibling) other.nextSibling.prevSibling = other; + else me.lastChild = other; + } + + entity Container_preferredFocusedGrandChild(entity me) { - if(e.instanceOfContainer) + entity e, e2; + entity best; + + best = NULL; + + for (e = me.firstChild; e; e = e.nextSibling) { - e2 = e.preferredFocusedGrandChild(e); - if(e2) - if(!best || best.preferredFocusPriority < e2.preferredFocusPriority) - best = e2; + if (e.instanceOfContainer) + { + e2 = e.preferredFocusedGrandChild(e); + if (e2) + if (!best || best.preferredFocusPriority < e2.preferredFocusPriority) best = e2; + } + if (e) + if (!best || best.preferredFocusPriority < e.preferredFocusPriority) best = e; } - if(e) - if(!best || best.preferredFocusPriority < e.preferredFocusPriority) - best = e; - } - return best; -} + return best; + } #endif diff --git a/qcsrc/menu/item/dialog.qc b/qcsrc/menu/item/dialog.qc index f02be5b53f..fc14d9222b 100644 --- a/qcsrc/menu/item/dialog.qc +++ b/qcsrc/menu/item/dialog.qc @@ -14,184 +14,177 @@ // a subclass may help with using this as a tab #ifndef ITEM_DIALOG_H -#define ITEM_DIALOG_H -#include "inputcontainer.qc" -CLASS(Dialog, InputContainer) - METHOD(Dialog, configureDialog, void(entity)); // no runtime configuration, all parameters are given in the code! - METHOD(Dialog, fill, void(entity)); // to be overridden by user to fill the dialog with controls - METHOD(Dialog, keyDown, float(entity, float, float, float)); - METHOD(Dialog, close, void(entity)); - METHOD(Dialog, addItemSimple, void(entity, float, float, float, float, entity, vector)); - - METHOD(Dialog, TD, void(entity, float, float, entity)); - METHOD(Dialog, TDNoMargin, void(entity, float, float, entity, vector)); - METHOD(Dialog, TDempty, void(entity, float)); - METHOD(Dialog, setFirstColumn, void(entity, float)); - METHOD(Dialog, TR, void(entity)); - METHOD(Dialog, gotoRC, void(entity, float, float)); - - ATTRIB(Dialog, isTabRoot, float, 1) - ATTRIB(Dialog, closeButton, entity, NULL) - ATTRIB(Dialog, intendedHeight, float, 0) - ATTRIB(Dialog, itemOrigin, vector, '0 0 0') - ATTRIB(Dialog, itemSize, vector, '0 0 0') - ATTRIB(Dialog, itemSpacing, vector, '0 0 0') - ATTRIB(Dialog, currentRow, float, 0) - ATTRIB(Dialog, currentColumn, float, 0) - ATTRIB(Dialog, firstColumn, float, 0) - - // to be customized - ATTRIB(Dialog, closable, float, 1) - ATTRIB(Dialog, title, string, "Form1") // ;) - ATTRIB(Dialog, color, vector, '1 0.5 1') - ATTRIB(Dialog, intendedWidth, float, 0) - ATTRIB(Dialog, rows, float, 3) - ATTRIB(Dialog, columns, float, 2) - - ATTRIB(Dialog, marginTop, float, 0) // pixels - ATTRIB(Dialog, marginBottom, float, 0) // pixels - ATTRIB(Dialog, marginLeft, float, 0) // pixels - ATTRIB(Dialog, marginRight, float, 0) // pixels - ATTRIB(Dialog, columnSpacing, float, 0) // pixels - ATTRIB(Dialog, rowSpacing, float, 0) // pixels - ATTRIB(Dialog, rowHeight, float, 0) // pixels - ATTRIB(Dialog, titleHeight, float, 0) // pixels - ATTRIB(Dialog, titleFontSize, float, 0) // pixels; if 0, title causes no margin - ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0) - ATTRIB(Dialog, zoomedOutTitleBar, float, 0) - - ATTRIB(Dialog, requiresConnection, float, 0) // set to true if the dialog requires a connection to be opened - - ATTRIB(Dialog, backgroundImage, string, string_null) - ATTRIB(Dialog, borderLines, float, 1) - ATTRIB(Dialog, closeButtonImage, string, string_null) - - ATTRIB(Dialog, frame, entity, NULL) -ENDCLASS(Dialog) + #define ITEM_DIALOG_H + #include "inputcontainer.qc" + CLASS(Dialog, InputContainer) + METHOD(Dialog, configureDialog, void(entity)); // no runtime configuration, all parameters are given in the code! + METHOD(Dialog, fill, void(entity)); // to be overridden by user to fill the dialog with controls + METHOD(Dialog, keyDown, float(entity, float, float, float)); + METHOD(Dialog, close, void(entity)); + METHOD(Dialog, addItemSimple, void(entity, float, float, float, float, entity, vector)); + + METHOD(Dialog, TD, void(entity, float, float, entity)); + METHOD(Dialog, TDNoMargin, void(entity, float, float, entity, vector)); + METHOD(Dialog, TDempty, void(entity, float)); + METHOD(Dialog, setFirstColumn, void(entity, float)); + METHOD(Dialog, TR, void(entity)); + METHOD(Dialog, gotoRC, void(entity, float, float)); + + ATTRIB(Dialog, isTabRoot, float, 1) + ATTRIB(Dialog, closeButton, entity, NULL) + ATTRIB(Dialog, intendedHeight, float, 0) + ATTRIB(Dialog, itemOrigin, vector, '0 0 0') + ATTRIB(Dialog, itemSize, vector, '0 0 0') + ATTRIB(Dialog, itemSpacing, vector, '0 0 0') + ATTRIB(Dialog, currentRow, float, 0) + ATTRIB(Dialog, currentColumn, float, 0) + ATTRIB(Dialog, firstColumn, float, 0) + + // to be customized + ATTRIB(Dialog, closable, float, 1) + ATTRIB(Dialog, title, string, "Form1") // ;) + ATTRIB(Dialog, color, vector, '1 0.5 1') + ATTRIB(Dialog, intendedWidth, float, 0) + ATTRIB(Dialog, rows, float, 3) + ATTRIB(Dialog, columns, float, 2) + + ATTRIB(Dialog, marginTop, float, 0) // pixels + ATTRIB(Dialog, marginBottom, float, 0) // pixels + ATTRIB(Dialog, marginLeft, float, 0) // pixels + ATTRIB(Dialog, marginRight, float, 0) // pixels + ATTRIB(Dialog, columnSpacing, float, 0) // pixels + ATTRIB(Dialog, rowSpacing, float, 0) // pixels + ATTRIB(Dialog, rowHeight, float, 0) // pixels + ATTRIB(Dialog, titleHeight, float, 0) // pixels + ATTRIB(Dialog, titleFontSize, float, 0) // pixels; if 0, title causes no margin + ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0) + ATTRIB(Dialog, zoomedOutTitleBar, float, 0) + + ATTRIB(Dialog, requiresConnection, float, 0) // set to true if the dialog requires a connection to be opened + + ATTRIB(Dialog, backgroundImage, string, string_null) + ATTRIB(Dialog, borderLines, float, 1) + ATTRIB(Dialog, closeButtonImage, string, string_null) + + ATTRIB(Dialog, frame, entity, NULL) + ENDCLASS(Dialog) #endif #ifdef IMPLEMENTATION -void Dialog_Close(entity button, entity me) -{ - me.close(me); -} - -void Dialog_fill(entity me) -{ -} - -void Dialog_addItemSimple(entity me, float row, float col, float rowspan, float colspan, entity e, vector v) -{ - vector o, s; - o = me.itemOrigin + eX * ( col * me.itemSpacing.x) + eY * ( row * me.itemSpacing.y); - s = me.itemSize + eX * ((colspan - 1) * me.itemSpacing.x) + eY * ((rowspan - 1) * me.itemSpacing.y); - o.x -= 0.5 * (me.itemSpacing.x - me.itemSize.x) * v.x; - s.x += (me.itemSpacing.x - me.itemSize.x) * v.x; - o.y -= 0.5 * (me.itemSpacing.y - me.itemSize.y) * v.y; - s.y += (me.itemSpacing.y - me.itemSize.y) * v.y; - me.addItem(me, e, o, s, 1); -} - -void Dialog_gotoRC(entity me, float row, float col) -{ - me.currentRow = row; - me.currentColumn = col; -} - -void Dialog_TR(entity me) -{ - me.currentRow += 1; - me.currentColumn = me.firstColumn; -} - -void Dialog_TD(entity me, float rowspan, float colspan, entity e) -{ - me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, '0 0 0'); - me.currentColumn += colspan; -} - -void Dialog_TDNoMargin(entity me, float rowspan, float colspan, entity e, vector v) -{ - me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, v); - me.currentColumn += colspan; -} - -void Dialog_setFirstColumn(entity me, float col) -{ - me.firstColumn = col; -} - -void Dialog_TDempty(entity me, float colspan) -{ - me.currentColumn += colspan; -} - -void Dialog_configureDialog(entity me) -{ - float absWidth, absHeight; - - if(me.isTabRoot) + void Dialog_Close(entity button, entity me) { - me.frame = NEW(BorderImage); - me.frame.configureBorderImage(me.frame, me.title, me.titleFontSize, me.color, me.backgroundImage, me.borderLines * me.titleHeight); - me.frame.zoomedOutTitleBarPosition = me.zoomedOutTitleBarPosition; - me.frame.zoomedOutTitleBar = me.zoomedOutTitleBar; - me.frame.alpha = me.alpha; - me.addItem(me, me.frame, '0 0 0', '1 1 0', 1); + me.close(me); } - if (!me.titleFontSize) - me.titleHeight = 0; // no title bar - - absWidth = me.intendedWidth * conwidth; - absHeight = me.borderLines * me.titleHeight + me.marginTop + me.rows * me.rowHeight + (me.rows - 1) * me.rowSpacing + me.marginBottom; - me.itemOrigin = eX * (me.marginLeft / absWidth) - + eY * ((me.borderLines * me.titleHeight + me.marginTop) / absHeight); - me.itemSize = eX * ((1 - (me.marginLeft + me.marginRight + me.columnSpacing * (me.columns - 1)) / absWidth) / me.columns) - + eY * (me.rowHeight / absHeight); - me.itemSpacing = me.itemSize - + eX * (me.columnSpacing / absWidth) - + eY * (me.rowSpacing / absHeight); - me.intendedHeight = absHeight / conheight; - me.currentRow = -1; - me.currentColumn = -1; - - me.fill(me); - - if(me.isTabRoot && me.closable && me.borderLines > 0) + void Dialog_fill(entity me) + {} + + void Dialog_addItemSimple(entity me, float row, float col, float rowspan, float colspan, entity e, vector v) + { + vector o, s; + o = me.itemOrigin + eX * (col * me.itemSpacing.x) + eY * (row * me.itemSpacing.y); + s = me.itemSize + eX * ((colspan - 1) * me.itemSpacing.x) + eY * ((rowspan - 1) * me.itemSpacing.y); + o.x -= 0.5 * (me.itemSpacing.x - me.itemSize.x) * v.x; + s.x += (me.itemSpacing.x - me.itemSize.x) * v.x; + o.y -= 0.5 * (me.itemSpacing.y - me.itemSize.y) * v.y; + s.y += (me.itemSpacing.y - me.itemSize.y) * v.y; + me.addItem(me, e, o, s, 1); + } + + void Dialog_gotoRC(entity me, float row, float col) { - entity closebutton; - closebutton = me.closeButton = me.frame.closeButton = NEW(Button); - closebutton.configureButton(closebutton, "", 0, me.closeButtonImage); - closebutton.onClick = Dialog_Close; closebutton.onClickEntity = me; - closebutton.srcMulti = 0; - me.addItem(me, closebutton, '0 0 0', '1 1 0', 1); // put it as LAST + me.currentRow = row; + me.currentColumn = col; } -} -void Dialog_close(entity me) -{ - if(me.parent.instanceOfNexposee) + void Dialog_TR(entity me) { - ExposeeCloseButton_Click(me, me.parent); + me.currentRow += 1; + me.currentColumn = me.firstColumn; } - else if(me.parent.instanceOfModalController) + + void Dialog_TD(entity me, float rowspan, float colspan, entity e) + { + me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, '0 0 0'); + me.currentColumn += colspan; + } + + void Dialog_TDNoMargin(entity me, float rowspan, float colspan, entity e, vector v) + { + me.addItemSimple(me, me.currentRow, me.currentColumn, rowspan, colspan, e, v); + me.currentColumn += colspan; + } + + void Dialog_setFirstColumn(entity me, float col) + { + me.firstColumn = col; + } + + void Dialog_TDempty(entity me, float colspan) + { + me.currentColumn += colspan; + } + + void Dialog_configureDialog(entity me) + { + float absWidth, absHeight; + + if (me.isTabRoot) + { + me.frame = NEW(BorderImage); + me.frame.configureBorderImage(me.frame, me.title, me.titleFontSize, me.color, me.backgroundImage, me.borderLines * me.titleHeight); + me.frame.zoomedOutTitleBarPosition = me.zoomedOutTitleBarPosition; + me.frame.zoomedOutTitleBar = me.zoomedOutTitleBar; + me.frame.alpha = me.alpha; + me.addItem(me, me.frame, '0 0 0', '1 1 0', 1); + } + + if (!me.titleFontSize) me.titleHeight = 0; // no title bar + + absWidth = me.intendedWidth * conwidth; + absHeight = me.borderLines * me.titleHeight + me.marginTop + me.rows * me.rowHeight + (me.rows - 1) * me.rowSpacing + me.marginBottom; + me.itemOrigin = eX * (me.marginLeft / absWidth) + + eY * ((me.borderLines * me.titleHeight + me.marginTop) / absHeight); + me.itemSize = eX * ((1 - (me.marginLeft + me.marginRight + me.columnSpacing * (me.columns - 1)) / absWidth) / me.columns) + + eY * (me.rowHeight / absHeight); + me.itemSpacing = me.itemSize + + eX * (me.columnSpacing / absWidth) + + eY * (me.rowSpacing / absHeight); + me.intendedHeight = absHeight / conheight; + me.currentRow = -1; + me.currentColumn = -1; + + me.fill(me); + + if (me.isTabRoot && me.closable && me.borderLines > 0) + { + entity closebutton; + closebutton = me.closeButton = me.frame.closeButton = NEW(Button); + closebutton.configureButton(closebutton, "", 0, me.closeButtonImage); + closebutton.onClick = Dialog_Close; + closebutton.onClickEntity = me; + closebutton.srcMulti = 0; + me.addItem(me, closebutton, '0 0 0', '1 1 0', 1); // put it as LAST + } + } + + void Dialog_close(entity me) { - DialogCloseButton_Click(me, me); + if (me.parent.instanceOfNexposee) ExposeeCloseButton_Click(me, me.parent); + else if (me.parent.instanceOfModalController) DialogCloseButton_Click(me, me); } -} -float Dialog_keyDown(entity me, float key, float ascii, float shift) -{ - if(me.closable) + float Dialog_keyDown(entity me, float key, float ascii, float shift) { - if(key == K_ESCAPE) + if (me.closable) { - m_play_click_sound(MENU_SOUND_CLOSE); - me.close(me); - return 1; + if (key == K_ESCAPE) + { + m_play_click_sound(MENU_SOUND_CLOSE); + me.close(me); + return 1; + } } + return SUPER(Dialog).keyDown(me, key, ascii, shift); } - return SUPER(Dialog).keyDown(me, key, ascii, shift); -} #endif diff --git a/qcsrc/menu/item/image.qc b/qcsrc/menu/item/image.qc index baa5d61573..2a28c78b1d 100644 --- a/qcsrc/menu/item/image.qc +++ b/qcsrc/menu/item/image.qc @@ -1,233 +1,224 @@ #ifndef ITEM_IMAGE_H -#define ITEM_IMAGE_H -#include "../item.qc" -CLASS(Image, Item) - METHOD(Image, configureImage, void(entity, string)); - METHOD(Image, draw, void(entity)); - METHOD(Image, toString, string(entity)); - METHOD(Image, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Image, updateAspect, void(entity)); - METHOD(Image, initZoom, void(entity)); - METHOD(Image, setZoom, void(entity, float, float)); - METHOD(Image, drag_setStartPos, float(entity, vector)); - METHOD(Image, drag, float(entity, vector)); - ATTRIB(Image, src, string, string_null) - ATTRIB(Image, color, vector, '1 1 1') - ATTRIB(Image, forcedAspect, float, 0) // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size - ATTRIB(Image, zoomBox, float, 0) // used by forcedAspect -2 when the image is larger than the containing box - ATTRIB(Image, zoomFactor, float, 1) - ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0') - ATTRIB(Image, zoomSnapToTheBox, float, 1) // snap the zoomed in image to the box borders when zooming/dragging it - ATTRIB(Image, zoomTime, float, 0) - ATTRIB(Image, zoomLimitedByTheBox, float, 0) // forbids zoom if image would be larger than the containing box - ATTRIB(Image, zoomMax, float, 0) - ATTRIB(Image, start_zoomOffset, vector, '0 0 0') - ATTRIB(Image, start_coords, vector, '0 0 0') - ATTRIB(Image, imgOrigin, vector, '0 0 0') - ATTRIB(Image, imgSize, vector, '0 0 0') -ENDCLASS(Image) + #define ITEM_IMAGE_H + #include "../item.qc" + CLASS(Image, Item) + METHOD(Image, configureImage, void(entity, string)); + METHOD(Image, draw, void(entity)); + METHOD(Image, toString, string(entity)); + METHOD(Image, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Image, updateAspect, void(entity)); + METHOD(Image, initZoom, void(entity)); + METHOD(Image, setZoom, void(entity, float, float)); + METHOD(Image, drag_setStartPos, float(entity, vector)); + METHOD(Image, drag, float(entity, vector)); + ATTRIB(Image, src, string, string_null) + ATTRIB(Image, color, vector, '1 1 1') + ATTRIB(Image, forcedAspect, float, 0) // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size + ATTRIB(Image, zoomBox, float, 0) // used by forcedAspect -2 when the image is larger than the containing box + ATTRIB(Image, zoomFactor, float, 1) + ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0') + ATTRIB(Image, zoomSnapToTheBox, float, 1) // snap the zoomed in image to the box borders when zooming/dragging it + ATTRIB(Image, zoomTime, float, 0) + ATTRIB(Image, zoomLimitedByTheBox, float, 0) // forbids zoom if image would be larger than the containing box + ATTRIB(Image, zoomMax, float, 0) + ATTRIB(Image, start_zoomOffset, vector, '0 0 0') + ATTRIB(Image, start_coords, vector, '0 0 0') + ATTRIB(Image, imgOrigin, vector, '0 0 0') + ATTRIB(Image, imgSize, vector, '0 0 0') + ENDCLASS(Image) #endif #ifdef IMPLEMENTATION -string Image_toString(entity me) -{ - return me.src; -} -void Image_configureImage(entity me, string path) -{ - me.src = path; -} -void Image_initZoom(entity me) -{ - me.zoomOffset = '0.5 0.5 0'; - me.zoomFactor = 1; - if (me.forcedAspect == -2) - me.zoomBox = -1; // calculate zoomBox at the first updateAspect call - if (me.zoomLimitedByTheBox) - me.zoomMax = -1; // calculate zoomMax at the first updateAspect call -} + string Image_toString(entity me) + { + return me.src; + } + void Image_configureImage(entity me, string path) + { + me.src = path; + } + void Image_initZoom(entity me) + { + me.zoomOffset = '0.5 0.5 0'; + me.zoomFactor = 1; + if (me.forcedAspect == -2) me.zoomBox = -1; // calculate zoomBox at the first updateAspect call + if (me.zoomLimitedByTheBox) me.zoomMax = -1; // calculate zoomMax at the first updateAspect call + } -void Image_draw(entity me) -{ - if(me.imgSize.x > 1 || me.imgSize.y > 1) - draw_SetClip(); - draw_Picture(me.imgOrigin, me.src, me.imgSize, me.color, 1); - if(me.imgSize.x > 1 || me.imgSize.y > 1) - draw_ClearClip(); - SUPER(Image).draw(me); -} -void Image_updateAspect(entity me) -{ - float asp = 0; - if(me.size.x <= 0 || me.size.y <= 0) - return; - if(me.forcedAspect == 0) + void Image_draw(entity me) { - me.imgOrigin = '0 0 0'; - me.imgSize = '1 1 0'; + if (me.imgSize.x > 1 || me.imgSize.y > 1) draw_SetClip(); + draw_Picture(me.imgOrigin, me.src, me.imgSize, me.color, 1); + if (me.imgSize.x > 1 || me.imgSize.y > 1) draw_ClearClip(); + SUPER(Image).draw(me); } - else + void Image_updateAspect(entity me) { - vector sz = '0 0 0'; - if(me.forcedAspect < 0) + float asp = 0; + if (me.size.x <= 0 || me.size.y <= 0) return; + if (me.forcedAspect == 0) { - if (me.src != "") - sz = draw_PictureSize(me.src); - if(sz.x <= 0 || sz.y <= 0) - { - // image is broken or doesn't exist, set the size for the placeholder image - sz.x = me.size.x; - sz.y = me.size.y; - } - asp = sz.x / sz.y; + me.imgOrigin = '0 0 0'; + me.imgSize = '1 1 0'; } else - asp = me.forcedAspect; - - if(me.forcedAspect <= -2) { - me.imgSize_x = sz.x / me.size.x; - me.imgSize_y = sz.y / me.size.y; - if(me.zoomBox < 0 && (me.imgSize.x > 1 || me.imgSize.y > 1)) + vector sz = '0 0 0'; + if (me.forcedAspect < 0) { - // image larger than the containing box, zoom it out to fit into the box - if(me.size.x > asp * me.size.y) - me.zoomBox = (me.size.y * asp / me.size.x) / me.imgSize.x; - else - me.zoomBox = (me.size.x / (asp * me.size.y)) / me.imgSize.y; - me.zoomFactor = me.zoomBox; + if (me.src != "") sz = draw_PictureSize(me.src); + if (sz.x <= 0 || sz.y <= 0) + { + // image is broken or doesn't exist, set the size for the placeholder image + sz.x = me.size.x; + sz.y = me.size.y; + } + asp = sz.x / sz.y; } - } - else - { - if(me.size.x > asp * me.size.y) + else { - // x too large, so center x-wise - me.imgSize = eY + eX * (me.size.y * asp / me.size.x); + asp = me.forcedAspect; + } + + if (me.forcedAspect <= -2) + { + me.imgSize_x = sz.x / me.size.x; + me.imgSize_y = sz.y / me.size.y; + if (me.zoomBox < 0 && (me.imgSize.x > 1 || me.imgSize.y > 1)) + { + // image larger than the containing box, zoom it out to fit into the box + if (me.size.x > asp * me.size.y) me.zoomBox = (me.size.y * asp / me.size.x) / me.imgSize.x; + else me.zoomBox = (me.size.x / (asp * me.size.y)) / me.imgSize.y; + me.zoomFactor = me.zoomBox; + } } else { - // y too large, so center y-wise - me.imgSize = eX + eY * (me.size.x / (asp * me.size.y)); + if (me.size.x > asp * me.size.y) + { + // x too large, so center x-wise + me.imgSize = eY + eX * (me.size.y * asp / me.size.x); + } + else + { + // y too large, so center y-wise + me.imgSize = eX + eY * (me.size.x / (asp * me.size.y)); + } } } - } - if (me.zoomMax < 0) - { - if(me.zoomBox > 0) - me.zoomMax = me.zoomBox; - else + if (me.zoomMax < 0) { - if(me.size.x > asp * me.size.y) - me.zoomMax = (me.size.y * asp / me.size.x) / me.imgSize.x; + if (me.zoomBox > 0) + { + me.zoomMax = me.zoomBox; + } else - me.zoomMax = (me.size.x / (asp * me.size.y)) / me.imgSize.y; + { + if (me.size.x > asp * me.size.y) me.zoomMax = (me.size.y * asp / me.size.x) / me.imgSize.x; + else me.zoomMax = (me.size.x / (asp * me.size.y)) / me.imgSize.y; + } } - } - if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) - me.zoomFactor = me.zoomMax; - if (me.zoomFactor) - me.imgSize = me.imgSize * me.zoomFactor; + if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) me.zoomFactor = me.zoomMax; + if (me.zoomFactor) me.imgSize = me.imgSize * me.zoomFactor; - if(me.imgSize.x > 1 || me.imgSize.y > 1) - { - if(me.zoomSnapToTheBox) + if (me.imgSize.x > 1 || me.imgSize.y > 1) { - if(me.imgSize.x > 1) - me.zoomOffset_x = bound(0.5/me.imgSize.x, me.zoomOffset.x, 1 - 0.5/me.imgSize.x); - else - me.zoomOffset_x = bound(1 - 0.5/me.imgSize.x, me.zoomOffset.x, 0.5/me.imgSize.x); + if (me.zoomSnapToTheBox) + { + if (me.imgSize.x > 1) me.zoomOffset_x = bound(0.5 / me.imgSize.x, me.zoomOffset.x, 1 - 0.5 / me.imgSize.x); + else me.zoomOffset_x = bound(1 - 0.5 / me.imgSize.x, me.zoomOffset.x, 0.5 / me.imgSize.x); - if(me.imgSize.y > 1) - me.zoomOffset_y = bound(0.5/me.imgSize.y, me.zoomOffset.y, 1 - 0.5/me.imgSize.y); + if (me.imgSize.y > 1) me.zoomOffset_y = bound(0.5 / me.imgSize.y, me.zoomOffset.y, 1 - 0.5 / me.imgSize.y); + else me.zoomOffset_y = bound(1 - 0.5 / me.imgSize.y, me.zoomOffset.y, 0.5 / me.imgSize.y); + } else - me.zoomOffset_y = bound(1 - 0.5/me.imgSize.y, me.zoomOffset.y, 0.5/me.imgSize.y); + { + me.zoomOffset_x = bound(0, me.zoomOffset.x, 1); + me.zoomOffset_y = bound(0, me.zoomOffset.y, 1); + } } else { - me.zoomOffset_x = bound(0, me.zoomOffset.x, 1); - me.zoomOffset_y = bound(0, me.zoomOffset.y, 1); + me.zoomOffset = '0.5 0.5 0'; } - } - else - me.zoomOffset = '0.5 0.5 0'; - me.imgOrigin_x = 0.5 - me.zoomOffset.x * me.imgSize.x; - me.imgOrigin_y = 0.5 - me.zoomOffset.y * me.imgSize.y; -} -float Image_drag_setStartPos(entity me, vector coords) -{ - //if(me.imgSize_x > 1 || me.imgSize_y > 1) // check disabled: mousewheel zoom may start from a non-zoomed-in image - { - me.start_zoomOffset = me.zoomOffset; - me.start_coords = coords; - } - return 1; -} -float Image_drag(entity me, vector coords) -{ - if(me.imgSize.x > 1 || me.imgSize.y > 1) - { - me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - coords.x) / me.imgSize.x; - me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - coords.y) / me.imgSize.y; - me.updateAspect(me); + me.imgOrigin_x = 0.5 - me.zoomOffset.x * me.imgSize.x; + me.imgOrigin_y = 0.5 - me.zoomOffset.y * me.imgSize.y; } - return 1; -} -void Image_setZoom(entity me, float z, float atMousePosition) -{ - float prev_zoomFactor; - prev_zoomFactor = me.zoomFactor; - if (z < 0) // multiply by the current zoomFactor (but can also snap to real dimensions or to box) + float Image_drag_setStartPos(entity me, vector coords) { - me.zoomFactor *= -z; - float realSize_in_the_middle, boxSize_in_the_middle; - realSize_in_the_middle = ((prev_zoomFactor - 1) * (me.zoomFactor - 1) < 0); - boxSize_in_the_middle = (me.zoomBox > 0 && (prev_zoomFactor - me.zoomBox) * (me.zoomFactor - me.zoomBox) < 0); - if (realSize_in_the_middle && boxSize_in_the_middle) + // if(me.imgSize_x > 1 || me.imgSize_y > 1) // check disabled: mousewheel zoom may start from a non-zoomed-in image { - // snap to real dimensions or to box - if (prev_zoomFactor < me.zoomFactor) - me.zoomFactor = min(1, me.zoomBox); - else - me.zoomFactor = max(1, me.zoomBox); + me.start_zoomOffset = me.zoomOffset; + me.start_coords = coords; } - else if (realSize_in_the_middle) - me.zoomFactor = 1; // snap to real dimensions - else if (boxSize_in_the_middle) - me.zoomFactor = me.zoomBox; // snap to box + return 1; } - else if (z == 0) // reset (no zoom) + float Image_drag(entity me, vector coords) { - if (me.zoomBox > 0) - me.zoomFactor = me.zoomBox; - else - me.zoomFactor = 1; + if (me.imgSize.x > 1 || me.imgSize.y > 1) + { + me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - coords.x) / me.imgSize.x; + me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - coords.y) / me.imgSize.y; + me.updateAspect(me); + } + return 1; } - else // directly set - me.zoomFactor = z; - me.zoomFactor = bound(1/16, me.zoomFactor, 16); - if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) - me.zoomFactor = me.zoomMax; - if (prev_zoomFactor != me.zoomFactor) + void Image_setZoom(entity me, float z, float atMousePosition) { - me.zoomTime = time; - if (atMousePosition) + float prev_zoomFactor; + prev_zoomFactor = me.zoomFactor; + if (z < 0) // multiply by the current zoomFactor (but can also snap to real dimensions or to box) + { + me.zoomFactor *= -z; + float realSize_in_the_middle, boxSize_in_the_middle; + realSize_in_the_middle = ((prev_zoomFactor - 1) * (me.zoomFactor - 1) < 0); + boxSize_in_the_middle = (me.zoomBox > 0 && (prev_zoomFactor - me.zoomBox) * (me.zoomFactor - me.zoomBox) < 0); + if (realSize_in_the_middle && boxSize_in_the_middle) + { + // snap to real dimensions or to box + if (prev_zoomFactor < me.zoomFactor) me.zoomFactor = min(1, me.zoomBox); + else me.zoomFactor = max(1, me.zoomBox); + } + else if (realSize_in_the_middle) + { + me.zoomFactor = 1; // snap to real dimensions + } + else if (boxSize_in_the_middle) + { + me.zoomFactor = me.zoomBox; // snap to box + } + } + else if (z == 0) // reset (no zoom) + { + if (me.zoomBox > 0) me.zoomFactor = me.zoomBox; + else me.zoomFactor = 1; + } + else // directly set { - me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - 0.5) / me.imgSize.x; - me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - 0.5) / me.imgSize.y; - // updateAspect will reset zoomOffset to '0.5 0.5 0' if - // with this zoomFactor the image will not be zoomed in - // (updateAspect will check the new values of imgSize). + me.zoomFactor = z; } + me.zoomFactor = bound(1 / 16, me.zoomFactor, 16); + if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) me.zoomFactor = me.zoomMax; + if (prev_zoomFactor != me.zoomFactor) + { + me.zoomTime = time; + if (atMousePosition) + { + me.zoomOffset_x = me.start_zoomOffset.x + (me.start_coords.x - 0.5) / me.imgSize.x; + me.zoomOffset_y = me.start_zoomOffset.y + (me.start_coords.y - 0.5) / me.imgSize.y; + // updateAspect will reset zoomOffset to '0.5 0.5 0' if + // with this zoomFactor the image will not be zoomed in + // (updateAspect will check the new values of imgSize). + } + } + me.updateAspect(me); + } + void Image_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + SUPER(Image).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + me.updateAspect(me); } - me.updateAspect(me); -} -void Image_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(Image).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.updateAspect(me); -} #endif diff --git a/qcsrc/menu/item/inputbox.qc b/qcsrc/menu/item/inputbox.qc index 35ffe44d8b..89adfaae93 100644 --- a/qcsrc/menu/item/inputbox.qc +++ b/qcsrc/menu/item/inputbox.qc @@ -1,392 +1,401 @@ #ifndef ITEM_INPUTBOX_H -#define ITEM_INPUTBOX_H -#include "label.qc" -CLASS(InputBox, Label) - METHOD(InputBox, configureInputBox, void(entity, string, float, float, string)); - METHOD(InputBox, draw, void(entity)); - METHOD(InputBox, setText, void(entity, string)); - METHOD(InputBox, enterText, void(entity, string)); - METHOD(InputBox, keyDown, float(entity, float, float, float)); - METHOD(InputBox, mouseMove, float(entity, vector)); - METHOD(InputBox, mouseRelease, float(entity, vector)); - METHOD(InputBox, mousePress, float(entity, vector)); - METHOD(InputBox, mouseDrag, float(entity, vector)); - METHOD(InputBox, showNotify, void(entity)); - METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector)); - - ATTRIB(InputBox, src, string, string_null) - - ATTRIB(InputBox, cursorPos, float, 0) // characters - ATTRIB(InputBox, scrollPos, float, 0) // widths - - ATTRIB(InputBox, focusable, float, 1) - ATTRIB(InputBox, allowFocusSound, float, 1) - ATTRIB(InputBox, disabled, float, 0) - ATTRIB(InputBox, lastChangeTime, float, 0) - ATTRIB(InputBox, dragScrollTimer, float, 0) - ATTRIB(InputBox, dragScrollPos, vector, '0 0 0') - ATTRIB(InputBox, pressed, float, 0) - ATTRIB(InputBox, editColorCodes, float, 1) - ATTRIB(InputBox, forbiddenCharacters, string, "") - ATTRIB(InputBox, color, vector, '1 1 1') - ATTRIB(InputBox, colorF, vector, '1 1 1') - ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars - - ATTRIB(InputBox, enableClearButton, float, 1) - ATTRIB(InputBox, clearButton, entity, NULL) - ATTRIB(InputBox, cb_width, float, 0) - ATTRIB(InputBox, cb_pressed, float, 0) - ATTRIB(InputBox, cb_focused, float, 0) - ATTRIB(InputBox, cb_color, vector, '1 1 1') - ATTRIB(InputBox, cb_colorF, vector, '1 1 1') - ATTRIB(InputBox, cb_colorC, vector, '1 1 1') -ENDCLASS(InputBox) + #define ITEM_INPUTBOX_H + #include "label.qc" + CLASS(InputBox, Label) + METHOD(InputBox, configureInputBox, void(entity, string, float, float, string)); + METHOD(InputBox, draw, void(entity)); + METHOD(InputBox, setText, void(entity, string)); + METHOD(InputBox, enterText, void(entity, string)); + METHOD(InputBox, keyDown, float(entity, float, float, float)); + METHOD(InputBox, mouseMove, float(entity, vector)); + METHOD(InputBox, mouseRelease, float(entity, vector)); + METHOD(InputBox, mousePress, float(entity, vector)); + METHOD(InputBox, mouseDrag, float(entity, vector)); + METHOD(InputBox, showNotify, void(entity)); + METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector)); + + ATTRIB(InputBox, src, string, string_null) + + ATTRIB(InputBox, cursorPos, float, 0) // characters + ATTRIB(InputBox, scrollPos, float, 0) // widths + + ATTRIB(InputBox, focusable, float, 1) + ATTRIB(InputBox, allowFocusSound, float, 1) + ATTRIB(InputBox, disabled, float, 0) + ATTRIB(InputBox, lastChangeTime, float, 0) + ATTRIB(InputBox, dragScrollTimer, float, 0) + ATTRIB(InputBox, dragScrollPos, vector, '0 0 0') + ATTRIB(InputBox, pressed, float, 0) + ATTRIB(InputBox, editColorCodes, float, 1) + ATTRIB(InputBox, forbiddenCharacters, string, "") + ATTRIB(InputBox, color, vector, '1 1 1') + ATTRIB(InputBox, colorF, vector, '1 1 1') + ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars + + ATTRIB(InputBox, enableClearButton, float, 1) + ATTRIB(InputBox, clearButton, entity, NULL) + ATTRIB(InputBox, cb_width, float, 0) + ATTRIB(InputBox, cb_pressed, float, 0) + ATTRIB(InputBox, cb_focused, float, 0) + ATTRIB(InputBox, cb_color, vector, '1 1 1') + ATTRIB(InputBox, cb_colorF, vector, '1 1 1') + ATTRIB(InputBox, cb_colorC, vector, '1 1 1') + ENDCLASS(InputBox) #endif #ifdef IMPLEMENTATION -void InputBox_configureInputBox(entity me, string theText, float theCursorPos, float theFontSize, string gfx) -{ - SUPER(InputBox).configureLabel(me, theText, theFontSize, 0.0); - me.src = gfx; - me.cursorPos = theCursorPos; -} -void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - if (me.enableClearButton) + void InputBox_configureInputBox(entity me, string theText, float theCursorPos, float theFontSize, string gfx) { - me.cb_width = absSize.y / absSize.x; - me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_width; // bound to range -1, 0 - me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_width; + SUPER(InputBox).configureLabel(me, theText, theFontSize, 0.0); + me.src = gfx; + me.cursorPos = theCursorPos; } -} - -void InputBox_setText(entity me, string txt) -{ - if(me.text) - strunzone(me.text); - SUPER(InputBox).setText(me, strzone(txt)); -} - -float over_ClearButton(entity me, vector pos) -{ - if (pos.x >= 1 + me.cb_offset - me.cb_width) - if (pos.x < 1 + me.cb_offset) - if (pos.y >= 0) - if (pos.y < 1) - return 1; - return 0; -} - -float InputBox_mouseMove(entity me, vector pos) -{ - if (me.enableClearButton) + void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { - if (over_ClearButton(me, pos)) + SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + if (me.enableClearButton) { - me.cb_focused = 1; - return 1; + me.cb_width = absSize.y / absSize.x; + me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_width; // bound to range -1, 0 + me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_width; } - me.cb_focused = 0; } - return 1; -} -float InputBox_mouseDrag(entity me, vector pos) -{ - float p; - if(me.pressed) + void InputBox_setText(entity me, string txt) { - me.dragScrollPos = pos; - p = me.scrollPos + pos.x - me.keepspaceLeft; - me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize); - me.lastChangeTime = time; + if (me.text) strunzone(me.text); + SUPER(InputBox).setText(me, strzone(txt)); } - else if (me.enableClearButton) + + float over_ClearButton(entity me, vector pos) { - if (over_ClearButton(me, pos)) - { - me.cb_pressed = 1; - return 1; - } + if (pos.x >= 1 + me.cb_offset - me.cb_width) + if (pos.x < 1 + me.cb_offset) + if (pos.y >= 0) + if (pos.y < 1) return 1; + return 0; } - me.cb_pressed = 0; - return 1; -} - -float InputBox_mousePress(entity me, vector pos) -{ - if (me.enableClearButton) - if (over_ClearButton(me, pos)) + + float InputBox_mouseMove(entity me, vector pos) { - me.cb_pressed = 1; + if (me.enableClearButton) + { + if (over_ClearButton(me, pos)) + { + me.cb_focused = 1; + return 1; + } + me.cb_focused = 0; + } return 1; } - me.dragScrollTimer = time; - me.pressed = 1; - return InputBox_mouseDrag(me, pos); -} - -float InputBox_mouseRelease(entity me, vector pos) -{ - if(me.cb_pressed) - if (over_ClearButton(me, pos)) + + float InputBox_mouseDrag(entity me, vector pos) { - m_play_click_sound(MENU_SOUND_CLEAR); - me.setText(me, ""); + float p; + if (me.pressed) + { + me.dragScrollPos = pos; + p = me.scrollPos + pos.x - me.keepspaceLeft; + me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize); + me.lastChangeTime = time; + } + else if (me.enableClearButton) + { + if (over_ClearButton(me, pos)) + { + me.cb_pressed = 1; + return 1; + } + } me.cb_pressed = 0; return 1; } - float r = InputBox_mouseDrag(me, pos); - //reset cb_pressed after mouseDrag, mouseDrag could set cb_pressed in this case: - //mouse press out of the clear button, drag and then mouse release over the clear button - me.cb_pressed = 0; - me.pressed = 0; - return r; -} - -void InputBox_enterText(entity me, string ch) -{ - float i; - for(i = 0; i < strlen(ch); ++i) - if(strstrofs(me.forbiddenCharacters, substring(ch, i, 1), 0) > -1) - return; - if(me.maxLength > 0) + + float InputBox_mousePress(entity me, vector pos) { - if(strlen(ch) + strlen(me.text) > me.maxLength) - return; + if (me.enableClearButton) + if (over_ClearButton(me, pos)) + { + me.cb_pressed = 1; + return 1; + } + me.dragScrollTimer = time; + me.pressed = 1; + return InputBox_mouseDrag(me, pos); } - else if(me.maxLength < 0) + + float InputBox_mouseRelease(entity me, vector pos) { - if(u8_strsize(ch) + u8_strsize(me.text) > -me.maxLength) - return; + if (me.cb_pressed) + if (over_ClearButton(me, pos)) + { + m_play_click_sound(MENU_SOUND_CLEAR); + me.setText(me, ""); + me.cb_pressed = 0; + return 1; + } + float r = InputBox_mouseDrag(me, pos); + // reset cb_pressed after mouseDrag, mouseDrag could set cb_pressed in this case: + // mouse press out of the clear button, drag and then mouse release over the clear button + me.cb_pressed = 0; + me.pressed = 0; + return r; } - me.setText(me, strcat(substring(me.text, 0, me.cursorPos), ch, substring(me.text, me.cursorPos, strlen(me.text) - me.cursorPos))); - me.cursorPos += strlen(ch); -} - -float InputBox_keyDown(entity me, float key, float ascii, float shift) -{ - me.lastChangeTime = time; - me.dragScrollTimer = time; - if(ascii >= 32 && ascii != 127) + + void InputBox_enterText(entity me, string ch) { - me.enterText(me, chr(ascii)); - return 1; + float i; + for (i = 0; i < strlen(ch); ++i) + if (strstrofs(me.forbiddenCharacters, substring(ch, i, 1), 0) > -1) return; + if (me.maxLength > 0) + { + if (strlen(ch) + strlen(me.text) > me.maxLength) return; + } + else if (me.maxLength < 0) + { + if (u8_strsize(ch) + u8_strsize(me.text) > -me.maxLength) return; + } + me.setText(me, strcat(substring(me.text, 0, me.cursorPos), ch, substring(me.text, me.cursorPos, strlen(me.text) - me.cursorPos))); + me.cursorPos += strlen(ch); } - switch(key) + + float InputBox_keyDown(entity me, float key, float ascii, float shift) { - case K_KP_LEFTARROW: - case K_LEFTARROW: - me.cursorPos -= 1; - return 1; - case K_KP_RIGHTARROW: - case K_RIGHTARROW: - me.cursorPos += 1; - return 1; - case K_KP_HOME: - case K_HOME: - me.cursorPos = 0; - return 1; - case K_KP_END: - case K_END: - me.cursorPos = strlen(me.text); + me.lastChangeTime = time; + me.dragScrollTimer = time; + if (ascii >= 32 && ascii != 127) + { + me.enterText(me, chr(ascii)); return 1; - case K_BACKSPACE: - if(me.cursorPos > 0) - { + } + switch (key) + { + case K_KP_LEFTARROW: + case K_LEFTARROW: me.cursorPos -= 1; - me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1))); - } - return 1; - case K_KP_DEL: - case K_DEL: - if(shift & S_CTRL) - { - m_play_click_sound(MENU_SOUND_CLEAR); - me.setText(me, ""); - } - else - me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1))); - return 1; + return 1; + case K_KP_RIGHTARROW: + case K_RIGHTARROW: + me.cursorPos += 1; + return 1; + case K_KP_HOME: + case K_HOME: + me.cursorPos = 0; + return 1; + case K_KP_END: + case K_END: + me.cursorPos = strlen(me.text); + return 1; + case K_BACKSPACE: + if (me.cursorPos > 0) + { + me.cursorPos -= 1; + me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1))); + } + return 1; + case K_KP_DEL: + case K_DEL: + if (shift & S_CTRL) + { + m_play_click_sound(MENU_SOUND_CLEAR); + me.setText(me, ""); + } + else + { + me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1))); + } + return 1; + } + return 0; } - return 0; -} -void InputBox_draw(entity me) -{ - string CURSOR = "_"; - float cursorPosInWidths, totalSizeInWidths; + void InputBox_draw(entity me) + { + string CURSOR = "_"; + float cursorPosInWidths, totalSizeInWidths; - if(me.pressed) - me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event + if (me.pressed) me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event - if(me.recalcPos) - me.recalcPositionWithText(me, me.text); + if (me.recalcPos) me.recalcPositionWithText(me, me.text); - me.focusable = !me.disabled; - if(me.disabled) - draw_alpha *= me.disabledAlpha; + me.focusable = !me.disabled; + if (me.disabled) draw_alpha *= me.disabledAlpha; - if(me.src) - { - if(me.focused && !me.disabled) - draw_ButtonPicture('0 0 0', strcat(me.src, "_f"), '1 1 0', me.colorF, 1); - else - draw_ButtonPicture('0 0 0', strcat(me.src, "_n"), '1 1 0', me.color, 1); - } + if (me.src) + { + if (me.focused && !me.disabled) draw_ButtonPicture('0 0 0', strcat(me.src, "_f"), '1 1 0', me.colorF, 1); + else draw_ButtonPicture('0 0 0', strcat(me.src, "_n"), '1 1 0', me.color, 1); + } - me.cursorPos = bound(0, me.cursorPos, strlen(me.text)); - cursorPosInWidths = draw_TextWidth(substring(me.text, 0, me.cursorPos), 0, me.realFontSize); - totalSizeInWidths = draw_TextWidth(strcat(me.text, CURSOR), 0, me.realFontSize); + me.cursorPos = bound(0, me.cursorPos, strlen(me.text)); + cursorPosInWidths = draw_TextWidth(substring(me.text, 0, me.cursorPos), 0, me.realFontSize); + totalSizeInWidths = draw_TextWidth(strcat(me.text, CURSOR), 0, me.realFontSize); - if(me.dragScrollTimer < time) - { - float save; - save = me.scrollPos; - me.scrollPos = bound(cursorPosInWidths - (0.875 - me.keepspaceLeft - me.keepspaceRight), me.scrollPos, cursorPosInWidths - 0.125); - if(me.scrollPos != save) - me.dragScrollTimer = time + 0.2; - } - me.scrollPos = min(me.scrollPos, totalSizeInWidths - (1 - me.keepspaceRight - me.keepspaceLeft)); - me.scrollPos = max(0, me.scrollPos); + if (me.dragScrollTimer < time) + { + float save; + save = me.scrollPos; + me.scrollPos = bound(cursorPosInWidths - (0.875 - me.keepspaceLeft - me.keepspaceRight), me.scrollPos, cursorPosInWidths - 0.125); + if (me.scrollPos != save) me.dragScrollTimer = time + 0.2; + } + me.scrollPos = min(me.scrollPos, totalSizeInWidths - (1 - me.keepspaceRight - me.keepspaceLeft)); + me.scrollPos = max(0, me.scrollPos); - draw_SetClipRect(eX * me.keepspaceLeft, eX * (1 - me.keepspaceLeft - me.keepspaceRight) + eY); - if(me.editColorCodes) - { - string ch, ch2; - float i, n; - vector theColor; - float theAlpha; //float theVariableAlpha; - vector p; - vector theTempColor; - float component; - - p = me.realOrigin - eX * me.scrollPos; - theColor = '1 1 1'; - theAlpha = 1; //theVariableAlpha = 1; // changes when ^ax found - - n = strlen(me.text); - for(i = 0; i < n; ++i) + draw_SetClipRect(eX * me.keepspaceLeft, eX * (1 - me.keepspaceLeft - me.keepspaceRight) + eY); + if (me.editColorCodes) { - ch = substring(me.text, i, 1); - if(ch == "^") + string ch, ch2; + float i, n; + vector theColor; + float theAlpha; // float theVariableAlpha; + vector p; + vector theTempColor; + float component; + + p = me.realOrigin - eX * me.scrollPos; + theColor = '1 1 1'; + theAlpha = 1; // theVariableAlpha = 1; // changes when ^ax found + + n = strlen(me.text); + for (i = 0; i < n; ++i) { - float w; - ch2 = substring(me.text, i+1, 1); - w = draw_TextWidth(strcat(ch, ch2), 0, me.realFontSize); - if(ch2 == "^") - { - draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); - draw_Text(p + eX * 0.25 * w, "^", me.realFontSize, theColor, theAlpha, 0); - } - else if(ch2 == "0" || stof(ch2)) // digit? + ch = substring(me.text, i, 1); + if (ch == "^") { - switch(stof(ch2)) + float w; + ch2 = substring(me.text, i + 1, 1); + w = draw_TextWidth(strcat(ch, ch2), 0, me.realFontSize); + if (ch2 == "^") { - case 0: theColor = '0 0 0'; theAlpha = 1; break; - case 1: theColor = '1 0 0'; theAlpha = 1; break; - case 2: theColor = '0 1 0'; theAlpha = 1; break; - case 3: theColor = '1 1 0'; theAlpha = 1; break; - case 4: theColor = '0 0 1'; theAlpha = 1; break; - case 5: theColor = '0 1 1'; theAlpha = 1; break; - case 6: theColor = '1 0 1'; theAlpha = 1; break; - case 7: theColor = '1 1 1'; theAlpha = 1; break; - case 8: theColor = '1 1 1'; theAlpha = 0.5; break; - case 9: theColor = '0.5 0.5 0.5'; theAlpha = 1; break; + draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); + draw_Text(p + eX * 0.25 * w, "^", me.realFontSize, theColor, theAlpha, 0); } - draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); - draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0); - } - else if(ch2 == "x") // ^x found - { - theColor = '1 1 1'; - - component = HEXDIGIT_TO_DEC(substring(me.text, i+2, 1)); - if (component >= 0) // ^xr found + else if (ch2 == "0" || stof(ch2)) // digit? + { + switch (stof(ch2)) + { + case 0: theColor = '0 0 0'; + theAlpha = 1; + break; + case 1: theColor = '1 0 0'; + theAlpha = 1; + break; + case 2: theColor = '0 1 0'; + theAlpha = 1; + break; + case 3: theColor = '1 1 0'; + theAlpha = 1; + break; + case 4: theColor = '0 0 1'; + theAlpha = 1; + break; + case 5: theColor = '0 1 1'; + theAlpha = 1; + break; + case 6: theColor = '1 0 1'; + theAlpha = 1; + break; + case 7: theColor = '1 1 1'; + theAlpha = 1; + break; + case 8: theColor = '1 1 1'; + theAlpha = 0.5; + break; + case 9: theColor = '0.5 0.5 0.5'; + theAlpha = 1; + break; + } + draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); + draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0); + } + else if (ch2 == "x") // ^x found { - theTempColor.x = component/15; + theColor = '1 1 1'; - component = HEXDIGIT_TO_DEC(substring(me.text, i+3, 1)); - if (component >= 0) // ^xrg found + component = HEXDIGIT_TO_DEC(substring(me.text, i + 2, 1)); + if (component >= 0) // ^xr found { - theTempColor.y = component/15; + theTempColor.x = component / 15; - component = HEXDIGIT_TO_DEC(substring(me.text, i+4, 1)); - if (component >= 0) // ^xrgb found + component = HEXDIGIT_TO_DEC(substring(me.text, i + 3, 1)); + if (component >= 0) // ^xrg found { - theTempColor.z = component/15; - theColor = theTempColor; - w = draw_TextWidth(substring(me.text, i, 5), 0, me.realFontSize); - - draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); - draw_Text(p, substring(me.text, i, 5), me.realFontSize, theColor, 1, 0); // theVariableAlpha instead of 1 using alpha tags ^ax - i += 3; + theTempColor.y = component / 15; + + component = HEXDIGIT_TO_DEC(substring(me.text, i + 4, 1)); + if (component >= 0) // ^xrgb found + { + theTempColor.z = component / 15; + theColor = theTempColor; + w = draw_TextWidth(substring(me.text, i, 5), 0, me.realFontSize); + + draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); + draw_Text(p, substring(me.text, i, 5), me.realFontSize, theColor, 1, 0); // theVariableAlpha instead of 1 using alpha tags ^ax + i += 3; + } + else + { + // blue missing + w = draw_TextWidth(substring(me.text, i, 4), 0, me.realFontSize); + draw_Fill(p, eX * w + eY * me.realFontSize.y, eZ, 0.5); + draw_Text(p, substring(me.text, i, 4), me.realFontSize, '1 1 1', theAlpha, 0); + i += 2; + } } else { - // blue missing - w = draw_TextWidth(substring(me.text, i, 4), 0, me.realFontSize); - draw_Fill(p, eX * w + eY * me.realFontSize.y, eZ, 0.5); - draw_Text(p, substring(me.text, i, 4), me.realFontSize, '1 1 1', theAlpha, 0); - i += 2; + // green missing + w = draw_TextWidth(substring(me.text, i, 3), 0, me.realFontSize); + draw_Fill(p, eX * w + eY * me.realFontSize.y, eY, 0.5); + draw_Text(p, substring(me.text, i, 3), me.realFontSize, '1 1 1', theAlpha, 0); + i += 1; } } else { - // green missing - w = draw_TextWidth(substring(me.text, i, 3), 0, me.realFontSize); - draw_Fill(p, eX * w + eY * me.realFontSize.y, eY, 0.5); - draw_Text(p, substring(me.text, i, 3), me.realFontSize, '1 1 1', theAlpha, 0); - i += 1; + // red missing + // w = draw_TextWidth(substring(me.text, i, 2), 0) * me.realFontSize_x; + draw_Fill(p, eX * w + eY * me.realFontSize.y, eX, 0.5); + draw_Text(p, substring(me.text, i, 2), me.realFontSize, '1 1 1', theAlpha, 0); } } else { - // red missing - //w = draw_TextWidth(substring(me.text, i, 2), 0) * me.realFontSize_x; - draw_Fill(p, eX * w + eY * me.realFontSize.y, eX, 0.5); - draw_Text(p, substring(me.text, i, 2), me.realFontSize, '1 1 1', theAlpha, 0); + draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); + draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0); } + p += w * eX; + ++i; + continue; } - else - { - draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5); - draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0); - } - p += w * eX; - ++i; - continue; + draw_Text(p, ch, me.realFontSize, theColor, theAlpha, 0); // TODO theVariableAlpha + p += eX * draw_TextWidth(ch, 0, me.realFontSize); } - draw_Text(p, ch, me.realFontSize, theColor, theAlpha, 0); // TODO theVariableAlpha - p += eX * draw_TextWidth(ch, 0, me.realFontSize); } - } - else - draw_Text(me.realOrigin - eX * me.scrollPos, me.text, me.realFontSize, '1 1 1', 1, 0); + else + { + draw_Text(me.realOrigin - eX * me.scrollPos, me.text, me.realFontSize, '1 1 1', 1, 0); + } - if(!me.focused || (time - me.lastChangeTime) < floor(time - me.lastChangeTime) + 0.5) - draw_Text(me.realOrigin + eX * (cursorPosInWidths - me.scrollPos), CURSOR, me.realFontSize, '1 1 1', 1, 0); + if (!me.focused || (time - me.lastChangeTime) < floor(time - me.lastChangeTime) + 0.5) draw_Text(me.realOrigin + eX * (cursorPosInWidths - me.scrollPos), CURSOR, me.realFontSize, '1 1 1', 1, 0); - draw_ClearClip(); + draw_ClearClip(); - if (me.enableClearButton) - if (me.text != "") - { - if(me.focused && me.cb_pressed) - draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_c"), eX * me.cb_width + eY, me.cb_colorC, 1); - else if(me.focused && me.cb_focused) - draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_f"), eX * me.cb_width + eY, me.cb_colorF, 1); - else - draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_n"), eX * me.cb_width + eY, me.cb_color, 1); - } + if (me.enableClearButton) + if (me.text != "") + { + if (me.focused && me.cb_pressed) draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_c"), eX * me.cb_width + eY, me.cb_colorC, 1); + else if (me.focused && me.cb_focused) draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_f"), eX * me.cb_width + eY, me.cb_colorF, 1); + else draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_n"), eX * me.cb_width + eY, me.cb_color, 1); + } - // skipping SUPER(InputBox).draw(me); - Item_draw(me); -} + // skipping SUPER(InputBox).draw(me); + Item_draw(me); + } -void InputBox_showNotify(entity me) -{ - me.focusable = !me.disabled; -} + void InputBox_showNotify(entity me) + { + me.focusable = !me.disabled; + } #endif diff --git a/qcsrc/menu/item/inputcontainer.qc b/qcsrc/menu/item/inputcontainer.qc index 8e7f24f73d..45f60854f9 100644 --- a/qcsrc/menu/item/inputcontainer.qc +++ b/qcsrc/menu/item/inputcontainer.qc @@ -1,163 +1,152 @@ #ifndef ITEM_INPUTCONTAINER_H -#define ITEM_INPUTCONTAINER_H -#include "container.qc" -CLASS(InputContainer, Container) - METHOD(InputContainer, keyDown, float(entity, float, float, float)); - METHOD(InputContainer, mouseMove, float(entity, vector)); - METHOD(InputContainer, mousePress, float(entity, vector)); - METHOD(InputContainer, mouseRelease, float(entity, vector)); - METHOD(InputContainer, mouseDrag, float(entity, vector)); - METHOD(InputContainer, focusLeave, void(entity)); - METHOD(InputContainer, resizeNotify, void(entity, vector, vector, vector, vector)); + #define ITEM_INPUTCONTAINER_H + #include "container.qc" + CLASS(InputContainer, Container) + METHOD(InputContainer, keyDown, float(entity, float, float, float)); + METHOD(InputContainer, mouseMove, float(entity, vector)); + METHOD(InputContainer, mousePress, float(entity, vector)); + METHOD(InputContainer, mouseRelease, float(entity, vector)); + METHOD(InputContainer, mouseDrag, float(entity, vector)); + METHOD(InputContainer, focusLeave, void(entity)); + METHOD(InputContainer, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(InputContainer, _changeFocusXY, bool(entity this, vector pos)); - ATTRIB(InputContainer, mouseFocusedChild, entity, NULL) - ATTRIB(InputContainer, isTabRoot, float, 0) -ENDCLASS(InputContainer) + METHOD(InputContainer, _changeFocusXY, bool(entity this, vector pos)); + ATTRIB(InputContainer, mouseFocusedChild, entity, NULL) + ATTRIB(InputContainer, isTabRoot, float, 0) + ENDCLASS(InputContainer) #endif #ifdef IMPLEMENTATION -void InputContainer_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(InputContainer).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - /* - if(me.parent.instanceOfInputContainer) - me.isTabRoot = 0; - else - me.isTabRoot = 1; - */ -} + void InputContainer_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + SUPER(InputContainer).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + /* + if(me.parent.instanceOfInputContainer) + me.isTabRoot = 0; + else + me.isTabRoot = 1; + */ + } -void InputContainer_focusLeave(entity me) -{ - SUPER(InputContainer).focusLeave(me); - me.mouseFocusedChild = NULL; -} + void InputContainer_focusLeave(entity me) + { + SUPER(InputContainer).focusLeave(me); + me.mouseFocusedChild = NULL; + } -float InputContainer_keyDown(entity me, float scan, float ascii, float shift) -{ - entity f, ff; - if(SUPER(InputContainer).keyDown(me, scan, ascii, shift)) - return 1; - if(scan == K_ESCAPE) + float InputContainer_keyDown(entity me, float scan, float ascii, float shift) { - f = me.focusedChild; - if(f) + entity f, ff; + if (SUPER(InputContainer).keyDown(me, scan, ascii, shift)) return 1; + if (scan == K_ESCAPE) { - me.setFocus(me, NULL); - return 1; + f = me.focusedChild; + if (f) + { + me.setFocus(me, NULL); + return 1; + } + return 0; } - return 0; - } - if(scan == K_TAB) - { - f = me.focusedChild; - if(shift & S_SHIFT) + if (scan == K_TAB) { - if(f) + f = me.focusedChild; + if (shift & S_SHIFT) { - for(ff = f.prevSibling; ff; ff = ff.prevSibling) + if (f) { - if (!ff.focusable) - continue; - me.setFocus(me, ff); - return 1; + for (ff = f.prevSibling; ff; ff = ff.prevSibling) + { + if (!ff.focusable) continue; + me.setFocus(me, ff); + return 1; + } } - } - if(!f || me.isTabRoot) - { - for(ff = me.lastChild; ff; ff = ff.prevSibling) + if (!f || me.isTabRoot) { - if (!ff.focusable) - continue; - me.setFocus(me, ff); - return 1; + for (ff = me.lastChild; ff; ff = ff.prevSibling) + { + if (!ff.focusable) continue; + me.setFocus(me, ff); + return 1; + } + return 0; // AIIIIEEEEE! } - return 0; // AIIIIEEEEE! } - } - else - { - if(f) + else { - for(ff = f.nextSibling; ff; ff = ff.nextSibling) + if (f) { - if (!ff.focusable) - continue; - me.setFocus(me, ff); - return 1; + for (ff = f.nextSibling; ff; ff = ff.nextSibling) + { + if (!ff.focusable) continue; + me.setFocus(me, ff); + return 1; + } } - } - if(!f || me.isTabRoot) - { - for(ff = me.firstChild; ff; ff = ff.nextSibling) + if (!f || me.isTabRoot) { - if (!ff.focusable) - continue; - me.setFocus(me, ff); - return 1; + for (ff = me.firstChild; ff; ff = ff.nextSibling) + { + if (!ff.focusable) continue; + me.setFocus(me, ff); + return 1; + } + return 0; // AIIIIEEEEE! } - return 0; // AIIIIEEEEE! } } + return 0; } - return 0; -} -bool InputContainer__changeFocusXY(entity this, vector pos) -{ - entity e = this.itemFromPoint(this, pos); - if (e && !e.focusable) e = NULL; - entity prev = this.mouseFocusedChild; - this.mouseFocusedChild = e; - if (!e) return false; // keep focus when hovering over non-focusable elements - if (e != prev) { - this.setFocus(this, e); - if (e.instanceOfInputContainer) { - e.focusedChild = NULL; - e._changeFocusXY(e, globalToBox(pos, e.Container_origin, e.Container_size)); + bool InputContainer__changeFocusXY(entity this, vector pos) + { + entity e = this.itemFromPoint(this, pos); + if (e && !e.focusable) e = NULL; + entity prev = this.mouseFocusedChild; + this.mouseFocusedChild = e; + if (!e) return false; // keep focus when hovering over non-focusable elements + if (e != prev) + { + this.setFocus(this, e); + if (e.instanceOfInputContainer) + { + e.focusedChild = NULL; + e._changeFocusXY(e, globalToBox(pos, e.Container_origin, e.Container_size)); + } } + return true; // have focus } - return true; // have focus -} -float InputContainer_mouseDrag(entity me, vector pos) -{ - if(SUPER(InputContainer).mouseDrag(me, pos)) - return 1; - if(pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) - return 1; - return 0; -} -float InputContainer_mouseMove(entity me, vector pos) -{ - if(me.mouseFocusedChild != me.focusedChild) // if the keyboard moved the focus away - me.mouseFocusedChild = NULL; // force focusing - if(me._changeFocusXY(me, pos)) - if(SUPER(InputContainer).mouseMove(me, pos)) - return 1; - if(pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) - return 1; - return 0; -} -float InputContainer_mousePress(entity me, vector pos) -{ - me.mouseFocusedChild = NULL; // force focusing - if(me._changeFocusXY(me, pos)) - if(SUPER(InputContainer).mousePress(me, pos)) - return 1; - if(pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) - return 1; - return 0; -} -float InputContainer_mouseRelease(entity me, vector pos) -{ - SUPER(InputContainer).mouseRelease(me, pos); // return value? - if(me.focused) // am I still eligible for this? (UGLY HACK, but a mouse event could have changed focus away) - if(me._changeFocusXY(me, pos)) - return 1; - if(pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) - return 1; - return 0; -} + float InputContainer_mouseDrag(entity me, vector pos) + { + if (SUPER(InputContainer).mouseDrag(me, pos)) return 1; + if (pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) return 1; + return 0; + } + float InputContainer_mouseMove(entity me, vector pos) + { + if (me.mouseFocusedChild != me.focusedChild) // if the keyboard moved the focus away + me.mouseFocusedChild = NULL; // force focusing + if (me._changeFocusXY(me, pos)) + if (SUPER(InputContainer).mouseMove(me, pos)) return 1; + if (pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) return 1; + return 0; + } + float InputContainer_mousePress(entity me, vector pos) + { + me.mouseFocusedChild = NULL; // force focusing + if (me._changeFocusXY(me, pos)) + if (SUPER(InputContainer).mousePress(me, pos)) return 1; + if (pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) return 1; + return 0; + } + float InputContainer_mouseRelease(entity me, vector pos) + { + SUPER(InputContainer).mouseRelease(me, pos); // return value? + if (me.focused) // am I still eligible for this? (UGLY HACK, but a mouse event could have changed focus away) + if (me._changeFocusXY(me, pos)) return 1; + if (pos.x >= 0 && pos.y >= 0 && pos.x < 1 && pos.y < 1) return 1; + return 0; + } #endif diff --git a/qcsrc/menu/item/label.qc b/qcsrc/menu/item/label.qc index ea170777ea..6d9b9cda1f 100644 --- a/qcsrc/menu/item/label.qc +++ b/qcsrc/menu/item/label.qc @@ -1,183 +1,87 @@ #ifndef ITEM_LABEL_H -#define ITEM_LABEL_H -#include "../item.qc" -CLASS(Label, Item) - METHOD(Label, configureLabel, void(entity, string, float, float)); - METHOD(Label, draw, void(entity)); - METHOD(Label, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Label, setText, void(entity, string)); - METHOD(Label, toString, string(entity)); - METHOD(Label, recalcPositionWithText, void(entity, string)); - ATTRIB(Label, isBold, float, 0) - ATTRIB(Label, text, string, string_null) - ATTRIB(Label, currentText, string, string_null) - ATTRIB(Label, fontSize, float, 8) - ATTRIB(Label, align, float, 0.5) - ATTRIB(Label, allowCut, float, 0) - ATTRIB(Label, allowColors, float, 0) - ATTRIB(Label, keepspaceLeft, float, 0) // for use by subclasses (radiobuttons for example) - ATTRIB(Label, keepspaceRight, float, 0) - ATTRIB(Label, marginLeft, float, 0) // alternate way to specify keepspace* (in characters from the font) - ATTRIB(Label, marginRight, float, 0) - ATTRIB(Label, realFontSize, vector, '0 0 0') - ATTRIB(Label, realOrigin, vector, '0 0 0') - ATTRIB(Label, alpha, float, 0.7) - ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT) - ATTRIB(Label, disabled, float, 0) - ATTRIB(Label, disabledAlpha, float, 0.3) - ATTRIB(Label, textEntity, entity, NULL) - ATTRIB(Label, allowWrap, float, 0) - ATTRIB(Label, recalcPos, float, 0) - ATTRIB(Label, condenseFactor, float, 1) - ATTRIB(Label, overrideRealOrigin, vector, '0 0 0') - ATTRIB(Label, overrideCondenseFactor, float, 0) -ENDCLASS(Label) + #define ITEM_LABEL_H + #include "../item.qc" + CLASS(Label, Item) + METHOD(Label, configureLabel, void(entity, string, float, float)); + METHOD(Label, draw, void(entity)); + METHOD(Label, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Label, setText, void(entity, string)); + METHOD(Label, toString, string(entity)); + METHOD(Label, recalcPositionWithText, void(entity, string)); + ATTRIB(Label, isBold, float, 0) + ATTRIB(Label, text, string, string_null) + ATTRIB(Label, currentText, string, string_null) + ATTRIB(Label, fontSize, float, 8) + ATTRIB(Label, align, float, 0.5) + ATTRIB(Label, allowCut, float, 0) + ATTRIB(Label, allowColors, float, 0) + ATTRIB(Label, keepspaceLeft, float, 0) // for use by subclasses (radiobuttons for example) + ATTRIB(Label, keepspaceRight, float, 0) + ATTRIB(Label, marginLeft, float, 0) // alternate way to specify keepspace* (in characters from the font) + ATTRIB(Label, marginRight, float, 0) + ATTRIB(Label, realFontSize, vector, '0 0 0') + ATTRIB(Label, realOrigin, vector, '0 0 0') + ATTRIB(Label, alpha, float, 0.7) + ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT) + ATTRIB(Label, disabled, float, 0) + ATTRIB(Label, disabledAlpha, float, 0.3) + ATTRIB(Label, textEntity, entity, NULL) + ATTRIB(Label, allowWrap, float, 0) + ATTRIB(Label, recalcPos, float, 0) + ATTRIB(Label, condenseFactor, float, 1) + ATTRIB(Label, overrideRealOrigin, vector, '0 0 0') + ATTRIB(Label, overrideCondenseFactor, float, 0) + ENDCLASS(Label) #endif #ifdef IMPLEMENTATION -string Label_toString(entity me) -{ - return me.text; -} -void Label_setText(entity me, string txt) -{ - me.text = txt; - if(txt != me.currentText) + string Label_toString(entity me) { - if(me.currentText) - strunzone(me.currentText); - me.currentText = strzone(txt); - me.recalcPos = 1; + return me.text; } -} -void Label_recalcPositionWithText(entity me, string t) -{ - float spaceAvail; - spaceAvail = 1 - me.keepspaceLeft - me.keepspaceRight; - - if(me.isBold) - draw_beginBoldFont(); - - float spaceUsed; - spaceUsed = draw_TextWidth(t, me.allowColors, me.realFontSize); - - if(spaceUsed <= spaceAvail) + void Label_setText(entity me, string txt) { - if(!me.overrideRealOrigin_x) - me.realOrigin_x = me.align * (spaceAvail - spaceUsed) + me.keepspaceLeft; - if(!me.overrideCondenseFactor) - me.condenseFactor = 1; - } - else if(me.allowCut || me.allowWrap) - { - if(!me.overrideRealOrigin_x) - me.realOrigin_x = me.keepspaceLeft; - if(!me.overrideCondenseFactor) - me.condenseFactor = 1; - } - else - { - if(!me.overrideRealOrigin_x) - me.realOrigin_x = me.keepspaceLeft; - if(!me.overrideCondenseFactor) - me.condenseFactor = spaceAvail / spaceUsed; - LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f\n", t, me.condenseFactor); + me.text = txt; + if (txt != me.currentText) + { + if (me.currentText) strunzone(me.currentText); + me.currentText = strzone(txt); + me.recalcPos = 1; + } } - - if(!me.overrideRealOrigin_y) + void Label_recalcPositionWithText(entity me, string t) { - float lines; - vector dfs; - vector fs; + float spaceAvail; + spaceAvail = 1 - me.keepspaceLeft - me.keepspaceRight; - // set up variables to draw in condensed size, but use hinting for original size - fs = me.realFontSize; - fs.x *= me.condenseFactor; + if (me.isBold) draw_beginBoldFont(); - dfs = draw_fontscale; - draw_fontscale.x *= me.condenseFactor; + float spaceUsed; + spaceUsed = draw_TextWidth(t, me.allowColors, me.realFontSize); - if(me.allowCut) // FIXME allowCut incompatible with align != 0 - lines = 1; - else if(me.allowWrap) // FIXME allowWrap incompatible with align != 0 + if (spaceUsed <= spaceAvail) { - getWrappedLine_remaining = me.text; - lines = 0; - while(getWrappedLine_remaining) - { - if (me.allowColors) - getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); - else - getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); - ++lines; - } + if (!me.overrideRealOrigin_x) me.realOrigin_x = me.align * (spaceAvail - spaceUsed) + me.keepspaceLeft; + if (!me.overrideCondenseFactor) me.condenseFactor = 1; + } + else if (me.allowCut || me.allowWrap) + { + if (!me.overrideRealOrigin_x) me.realOrigin_x = me.keepspaceLeft; + if (!me.overrideCondenseFactor) me.condenseFactor = 1; } else - lines = 1; - - draw_fontscale = dfs; - - me.realOrigin_y = 0.5 * (1 - lines * me.realFontSize.y); - } - - if(me.isBold) - draw_endBoldFont(); - - me.recalcPos = 0; -} -void Label_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(Label).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - - // absSize_y is height of label - me.realFontSize_y = absSize.y == 0 ? 0 : (me.fontSize / absSize.y); - me.realFontSize_x = absSize.x == 0 ? 0 : (me.fontSize / absSize.x); - if(me.marginLeft) - me.keepspaceLeft = me.marginLeft * me.realFontSize.x; - if(me.marginRight) - me.keepspaceRight = me.marginRight * me.realFontSize.x; - - me.recalcPos = 1; -} -void Label_configureLabel(entity me, string txt, float sz, float algn) -{ - me.fontSize = sz; - me.align = algn; - me.setText(me, txt); -} -void Label_draw(entity me) -{ - string t; - vector o; - if(me.disabled) - draw_alpha *= me.disabledAlpha; - - if(me.textEntity) - { - t = me.textEntity.toString(me.textEntity); - if(t != me.currentText) { - if(me.currentText) - strunzone(me.currentText); - me.currentText = strzone(t); - me.recalcPos = 1; + if (!me.overrideRealOrigin_x) me.realOrigin_x = me.keepspaceLeft; + if (!me.overrideCondenseFactor) me.condenseFactor = spaceAvail / spaceUsed; + LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f\n", t, me.condenseFactor); } - } - else - t = me.text; - if(me.recalcPos) - me.recalcPositionWithText(me, t); - - if(me.fontSize) - if(t) + if (!me.overrideRealOrigin_y) { + float lines; vector dfs; vector fs; - if(me.isBold) - draw_beginBoldFont(); - // set up variables to draw in condensed size, but use hinting for original size fs = me.realFontSize; fs.x *= me.condenseFactor; @@ -185,31 +89,117 @@ void Label_draw(entity me) dfs = draw_fontscale; draw_fontscale.x *= me.condenseFactor; - if(me.allowCut) // FIXME allowCut incompatible with align != 0 - draw_Text(me.realOrigin, draw_TextShortenToWidth(t, (1 - me.keepspaceLeft - me.keepspaceRight), me.allowColors, fs), fs, me.colorL, me.alpha, me.allowColors); - else if(me.allowWrap) // FIXME allowWrap incompatible with align != 0 + if (me.allowCut) // FIXME allowCut incompatible with align != 0 { - getWrappedLine_remaining = t; - o = me.realOrigin; - while(getWrappedLine_remaining) + lines = 1; + } + else if (me.allowWrap) // FIXME allowWrap incompatible with align != 0 + { + getWrappedLine_remaining = me.text; + lines = 0; + while (getWrappedLine_remaining) { - if (me.allowColors) - t = getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); - else - t = getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); - draw_Text(o, t, fs, me.colorL, me.alpha, me.allowColors); - o.y += me.realFontSize.y; + if (me.allowColors) getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); + else getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); + ++lines; } } else - draw_Text(me.realOrigin, t, fs, me.colorL, me.alpha, me.allowColors); + { + lines = 1; + } draw_fontscale = dfs; - if(me.isBold) - draw_endBoldFont(); + me.realOrigin_y = 0.5 * (1 - lines * me.realFontSize.y); } - SUPER(Label).draw(me); -} + if (me.isBold) draw_endBoldFont(); + + me.recalcPos = 0; + } + void Label_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + SUPER(Label).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + + // absSize_y is height of label + me.realFontSize_y = absSize.y == 0 ? 0 : (me.fontSize / absSize.y); + me.realFontSize_x = absSize.x == 0 ? 0 : (me.fontSize / absSize.x); + if (me.marginLeft) me.keepspaceLeft = me.marginLeft * me.realFontSize.x; + if (me.marginRight) me.keepspaceRight = me.marginRight * me.realFontSize.x; + + me.recalcPos = 1; + } + void Label_configureLabel(entity me, string txt, float sz, float algn) + { + me.fontSize = sz; + me.align = algn; + me.setText(me, txt); + } + void Label_draw(entity me) + { + string t; + vector o; + if (me.disabled) draw_alpha *= me.disabledAlpha; + + if (me.textEntity) + { + t = me.textEntity.toString(me.textEntity); + if (t != me.currentText) + { + if (me.currentText) strunzone(me.currentText); + me.currentText = strzone(t); + me.recalcPos = 1; + } + } + else + { + t = me.text; + } + + if (me.recalcPos) me.recalcPositionWithText(me, t); + + if (me.fontSize) + if (t) + { + vector dfs; + vector fs; + + if (me.isBold) draw_beginBoldFont(); + + // set up variables to draw in condensed size, but use hinting for original size + fs = me.realFontSize; + fs.x *= me.condenseFactor; + + dfs = draw_fontscale; + draw_fontscale.x *= me.condenseFactor; + + if (me.allowCut) // FIXME allowCut incompatible with align != 0 + { + draw_Text(me.realOrigin, draw_TextShortenToWidth(t, (1 - me.keepspaceLeft - me.keepspaceRight), me.allowColors, fs), fs, me.colorL, me.alpha, me.allowColors); + } + else if (me.allowWrap) // FIXME allowWrap incompatible with align != 0 + { + getWrappedLine_remaining = t; + o = me.realOrigin; + while (getWrappedLine_remaining) + { + if (me.allowColors) t = getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithColors); + else t = getWrappedLine((1 - me.keepspaceLeft - me.keepspaceRight), fs, draw_TextWidth_WithoutColors); + draw_Text(o, t, fs, me.colorL, me.alpha, me.allowColors); + o.y += me.realFontSize.y; + } + } + else + { + draw_Text(me.realOrigin, t, fs, me.colorL, me.alpha, me.allowColors); + } + + draw_fontscale = dfs; + + if (me.isBold) draw_endBoldFont(); + } + + SUPER(Label).draw(me); + } #endif diff --git a/qcsrc/menu/item/listbox.qc b/qcsrc/menu/item/listbox.qc index 9210e14b38..7f303fda7a 100644 --- a/qcsrc/menu/item/listbox.qc +++ b/qcsrc/menu/item/listbox.qc @@ -1,512 +1,505 @@ #ifndef ITEM_LISTBOX_H -#define ITEM_LISTBOX_H -#include "../item.qc" -CLASS(ListBox, Item) - METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(ListBox, configureListBox, void(entity, float, float)); - METHOD(ListBox, draw, void(entity)); - METHOD(ListBox, keyDown, float(entity, float, float, float)); - METHOD(ListBox, mouseMove, float(entity, vector)); - METHOD(ListBox, mousePress, float(entity, vector)); - METHOD(ListBox, mouseDrag, float(entity, vector)); - METHOD(ListBox, mouseRelease, float(entity, vector)); - METHOD(ListBox, focusLeave, void(entity)); - ATTRIB(ListBox, focusable, float, 1) - ATTRIB(ListBox, focusedItem, int, -1) - ATTRIB(ListBox, focusedItemAlpha, float, 0.3) - METHOD(ListBox, setFocusedItem, void(entity, int)); - ATTRIB(ListBox, mouseMoveOffset, float, -1) // let know where the cursor is when the list scrolls without moving the cursor - ATTRIB(ListBox, allowFocusSound, float, 1) - ATTRIB(ListBox, selectedItem, int, 0) - ATTRIB(ListBox, size, vector, '0 0 0') - ATTRIB(ListBox, origin, vector, '0 0 0') - ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed - ATTRIB(ListBox, scrollPosTarget, float, 0) - METHOD(ListBox, isScrolling, bool(entity)); - ATTRIB(ListBox, needScrollToItem, float, -1) - METHOD(ListBox, scrollToItem, void(entity, int)); - ATTRIB(ListBox, previousValue, float, 0) - ATTRIB(ListBox, pressed, float, 0) // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released - ATTRIB(ListBox, pressOffset, float, 0) + #define ITEM_LISTBOX_H + #include "../item.qc" + CLASS(ListBox, Item) + METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(ListBox, configureListBox, void(entity, float, float)); + METHOD(ListBox, draw, void(entity)); + METHOD(ListBox, keyDown, float(entity, float, float, float)); + METHOD(ListBox, mouseMove, float(entity, vector)); + METHOD(ListBox, mousePress, float(entity, vector)); + METHOD(ListBox, mouseDrag, float(entity, vector)); + METHOD(ListBox, mouseRelease, float(entity, vector)); + METHOD(ListBox, focusLeave, void(entity)); + ATTRIB(ListBox, focusable, float, 1) + ATTRIB(ListBox, focusedItem, int, -1) + ATTRIB(ListBox, focusedItemAlpha, float, 0.3) + METHOD(ListBox, setFocusedItem, void(entity, int)); + ATTRIB(ListBox, mouseMoveOffset, float, -1) // let know where the cursor is when the list scrolls without moving the cursor + ATTRIB(ListBox, allowFocusSound, float, 1) + ATTRIB(ListBox, selectedItem, int, 0) + ATTRIB(ListBox, size, vector, '0 0 0') + ATTRIB(ListBox, origin, vector, '0 0 0') + ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed + ATTRIB(ListBox, scrollPosTarget, float, 0) + METHOD(ListBox, isScrolling, bool(entity)); + ATTRIB(ListBox, needScrollToItem, float, -1) + METHOD(ListBox, scrollToItem, void(entity, int)); + ATTRIB(ListBox, previousValue, float, 0) + ATTRIB(ListBox, pressed, float, 0) // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released + ATTRIB(ListBox, pressOffset, float, 0) - METHOD(ListBox, updateControlTopBottom, void(entity)); - ATTRIB(ListBox, controlTop, float, 0) - ATTRIB(ListBox, controlBottom, float, 0) - ATTRIB(ListBox, controlWidth, float, 0) - ATTRIB(ListBox, dragScrollPos, vector, '0 0 0') - ATTRIB(ListBox, selectionDoesntMatter, bool, false) // improves scrolling by keys for lists that don't need to show an active selection + METHOD(ListBox, updateControlTopBottom, void(entity)); + ATTRIB(ListBox, controlTop, float, 0) + ATTRIB(ListBox, controlBottom, float, 0) + ATTRIB(ListBox, controlWidth, float, 0) + ATTRIB(ListBox, dragScrollPos, vector, '0 0 0') + ATTRIB(ListBox, selectionDoesntMatter, bool, false) // improves scrolling by keys for lists that don't need to show an active selection - ATTRIB(ListBox, src, string, string_null) // scrollbar - ATTRIB(ListBox, color, vector, '1 1 1') - ATTRIB(ListBox, color2, vector, '1 1 1') - ATTRIB(ListBox, colorC, vector, '1 1 1') - ATTRIB(ListBox, colorF, vector, '1 1 1') - ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance - ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels - ATTRIB(ListBox, nItems, float, 42) - ATTRIB(ListBox, itemHeight, float, 0) - ATTRIB(ListBox, colorBG, vector, '0 0 0') - ATTRIB(ListBox, alphaBG, float, 0) + ATTRIB(ListBox, src, string, string_null) // scrollbar + ATTRIB(ListBox, color, vector, '1 1 1') + ATTRIB(ListBox, color2, vector, '1 1 1') + ATTRIB(ListBox, colorC, vector, '1 1 1') + ATTRIB(ListBox, colorF, vector, '1 1 1') + ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance + ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels + ATTRIB(ListBox, nItems, float, 42) + ATTRIB(ListBox, itemHeight, float, 0) + ATTRIB(ListBox, colorBG, vector, '0 0 0') + ATTRIB(ListBox, alphaBG, float, 0) - ATTRIB(ListBox, lastClickedItem, float, -1) - ATTRIB(ListBox, lastClickedTime, float, 0) + ATTRIB(ListBox, lastClickedItem, float, -1) + ATTRIB(ListBox, lastClickedTime, float, 0) - METHOD(ListBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused - METHOD(ListBox, clickListBoxItem, void(entity, float, vector)); // item number, relative clickpos - METHOD(ListBox, doubleClickListBoxItem, void(entity, float, vector)); // item number, relative clickpos - METHOD(ListBox, setSelected, void(entity, float)); - METHOD(ListBox, focusedItemChangeNotify, void(entity)); + METHOD(ListBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused + METHOD(ListBox, clickListBoxItem, void(entity, float, vector)); // item number, relative clickpos + METHOD(ListBox, doubleClickListBoxItem, void(entity, float, vector)); // item number, relative clickpos + METHOD(ListBox, setSelected, void(entity, float)); + METHOD(ListBox, focusedItemChangeNotify, void(entity)); - METHOD(ListBox, getLastFullyVisibleItemAtScrollPos, float(entity, float)); - METHOD(ListBox, getFirstFullyVisibleItemAtScrollPos, float(entity, float)); + METHOD(ListBox, getLastFullyVisibleItemAtScrollPos, float(entity, float)); + METHOD(ListBox, getFirstFullyVisibleItemAtScrollPos, float(entity, float)); - // NOTE: override these four methods if you want variable sized list items - METHOD(ListBox, getTotalHeight, float(entity)); - METHOD(ListBox, getItemAtPos, float(entity, float)); - METHOD(ListBox, getItemStart, float(entity, float)); - METHOD(ListBox, getItemHeight, float(entity, float)); - // NOTE: if getItemAt* are overridden, it may make sense to cache the - // start and height of the last item returned by getItemAtPos and fast - // track returning their properties for getItemStart and getItemHeight. - // The "hot" code path calls getItemAtPos first, then will query - // getItemStart and getItemHeight on it soon. - // When overriding, the following consistency rules must hold: - // getTotalHeight() == SUM(getItemHeight(i), i, 0, me.nItems-1) - // getItemStart(i+1) == getItemStart(i) + getItemHeight(i) - // for 0 <= i < me.nItems-1 - // getItemStart(0) == 0 - // getItemStart(getItemAtPos(p)) <= p - // if p >= 0 - // getItemAtPos(p) == 0 - // if p < 0 - // getItemStart(getItemAtPos(p)) + getItemHeight(getItemAtPos(p)) > p - // if p < getTotalHeigt() - // getItemAtPos(p) == me.nItems - 1 - // if p >= getTotalHeight() -ENDCLASS(ListBox) + // NOTE: override these four methods if you want variable sized list items + METHOD(ListBox, getTotalHeight, float(entity)); + METHOD(ListBox, getItemAtPos, float(entity, float)); + METHOD(ListBox, getItemStart, float(entity, float)); + METHOD(ListBox, getItemHeight, float(entity, float)); + // NOTE: if getItemAt* are overridden, it may make sense to cache the + // start and height of the last item returned by getItemAtPos and fast + // track returning their properties for getItemStart and getItemHeight. + // The "hot" code path calls getItemAtPos first, then will query + // getItemStart and getItemHeight on it soon. + // When overriding, the following consistency rules must hold: + // getTotalHeight() == SUM(getItemHeight(i), i, 0, me.nItems-1) + // getItemStart(i+1) == getItemStart(i) + getItemHeight(i) + // for 0 <= i < me.nItems-1 + // getItemStart(0) == 0 + // getItemStart(getItemAtPos(p)) <= p + // if p >= 0 + // getItemAtPos(p) == 0 + // if p < 0 + // getItemStart(getItemAtPos(p)) + getItemHeight(getItemAtPos(p)) > p + // if p < getTotalHeigt() + // getItemAtPos(p) == me.nItems - 1 + // if p >= getTotalHeight() + ENDCLASS(ListBox) #endif #ifdef IMPLEMENTATION -bool ListBox_isScrolling(entity me) -{ - return (me.scrollPos != me.scrollPosTarget); -} - -void ListBox_scrollToItem(entity me, int i) -{ - // scroll doesn't work properly until itemHeight is set to the correct value - // at the first resizeNotify call - if(me.itemHeight == 1) // initial temporary value of itemHeight is 1 + bool ListBox_isScrolling(entity me) { - me.needScrollToItem = i; - return; + return me.scrollPos != me.scrollPosTarget; } - i = bound(0, i, me.nItems - 1); + void ListBox_scrollToItem(entity me, int i) + { + // scroll doesn't work properly until itemHeight is set to the correct value + // at the first resizeNotify call + if (me.itemHeight == 1) // initial temporary value of itemHeight is 1 + { + me.needScrollToItem = i; + return; + } + + i = bound(0, i, me.nItems - 1); + + // scroll the list to make sure the selected item is visible + // (even if the selected item doesn't change). + if (i < me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos)) + { + // above visible area + me.scrollPosTarget = me.getItemStart(me, i); + } + else if (i > me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos)) + { + // below visible area + if (i == me.nItems - 1) me.scrollPosTarget = me.getTotalHeight(me) - 1; + else me.scrollPosTarget = me.getItemStart(me, i + 1) - 1; + } + } - // scroll the list to make sure the selected item is visible - // (even if the selected item doesn't change). - if(i < me.getFirstFullyVisibleItemAtScrollPos(me, me.scrollPos)) + void ListBox_setSelected(entity me, float i) { - // above visible area - me.scrollPosTarget = me.getItemStart(me, i); + i = bound(0, i, me.nItems - 1); + me.scrollToItem(me, i); + me.selectedItem = i; } - else if(i > me.getLastFullyVisibleItemAtScrollPos(me, me.scrollPos)) + void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { - // below visible area - if(i == me.nItems - 1) - me.scrollPosTarget = me.getTotalHeight(me) - 1; - else - me.scrollPosTarget = me.getItemStart(me, i + 1) - 1; + SUPER(ListBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + me.controlWidth = me.scrollbarWidth / absSize.x; + } + void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemHeight) + { + me.scrollbarWidth = theScrollbarWidth; + me.itemHeight = theItemHeight; } -} - -void ListBox_setSelected(entity me, float i) -{ - i = bound(0, i, me.nItems - 1); - me.scrollToItem(me, i); - me.selectedItem = i; -} -void ListBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(ListBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.controlWidth = me.scrollbarWidth / absSize.x; -} -void ListBox_configureListBox(entity me, float theScrollbarWidth, float theItemHeight) -{ - me.scrollbarWidth = theScrollbarWidth; - me.itemHeight = theItemHeight; -} -float ListBox_getTotalHeight(entity me) -{ - return me.nItems * me.itemHeight; -} -float ListBox_getItemAtPos(entity me, float pos) -{ - return floor(pos / me.itemHeight); -} -float ListBox_getItemStart(entity me, float i) -{ - return me.itemHeight * i; -} -float ListBox_getItemHeight(entity me, float i) -{ - return me.itemHeight; -} + float ListBox_getTotalHeight(entity me) + { + return me.nItems * me.itemHeight; + } + float ListBox_getItemAtPos(entity me, float pos) + { + return floor(pos / me.itemHeight); + } + float ListBox_getItemStart(entity me, float i) + { + return me.itemHeight * i; + } + float ListBox_getItemHeight(entity me, float i) + { + return me.itemHeight; + } -float ListBox_getLastFullyVisibleItemAtScrollPos(entity me, float pos) -{ - return me.getItemAtPos(me, pos + 0.999) - 1; -} -float ListBox_getFirstFullyVisibleItemAtScrollPos(entity me, float pos) -{ - return me.getItemAtPos(me, pos + 0.001) + 1; -} -float ListBox_keyDown(entity me, float key, float ascii, float shift) -{ - if(key == K_MWHEELUP) + float ListBox_getLastFullyVisibleItemAtScrollPos(entity me, float pos) { - me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0); + return me.getItemAtPos(me, pos + 0.999) - 1; } - else if(key == K_MWHEELDOWN) + float ListBox_getFirstFullyVisibleItemAtScrollPos(entity me, float pos) { - me.scrollPosTarget = min(me.scrollPosTarget + 0.5, max(0, me.getTotalHeight(me) - 1)); + return me.getItemAtPos(me, pos + 0.001) + 1; } - else if(key == K_PGUP || key == K_KP_PGUP) + float ListBox_keyDown(entity me, float key, float ascii, float shift) { - if(me.selectionDoesntMatter) + if (key == K_MWHEELUP) { me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0); - return 1; } + else if (key == K_MWHEELDOWN) + { + me.scrollPosTarget = min(me.scrollPosTarget + 0.5, max(0, me.getTotalHeight(me) - 1)); + } + else if (key == K_PGUP || key == K_KP_PGUP) + { + if (me.selectionDoesntMatter) + { + me.scrollPosTarget = max(me.scrollPosTarget - 0.5, 0); + return 1; + } - float i = me.selectedItem; - float a = me.getItemHeight(me, i); - for (;;) + float i = me.selectedItem; + float a = me.getItemHeight(me, i); + for ( ; ; ) + { + --i; + if (i < 0) break; + a += me.getItemHeight(me, i); + if (a >= 1) break; + } + me.setSelected(me, i + 1); + } + else if (key == K_PGDN || key == K_KP_PGDN) { - --i; - if (i < 0) - break; - a += me.getItemHeight(me, i); - if (a >= 1) - break; + if (me.selectionDoesntMatter) + { + me.scrollPosTarget = min(me.scrollPosTarget + 0.5, me.nItems * me.itemHeight - 1); + return 1; + } + + float i = me.selectedItem; + float a = me.getItemHeight(me, i); + for ( ; ; ) + { + ++i; + if (i >= me.nItems) break; + a += me.getItemHeight(me, i); + if (a >= 1) break; + } + me.setSelected(me, i - 1); } - me.setSelected(me, i + 1); - } - else if(key == K_PGDN || key == K_KP_PGDN) - { - if(me.selectionDoesntMatter) + else if (key == K_UPARROW || key == K_KP_UPARROW) { - me.scrollPosTarget = min(me.scrollPosTarget + 0.5, me.nItems * me.itemHeight - 1); - return 1; + if (me.selectionDoesntMatter) + { + me.scrollPosTarget = max(me.scrollPosTarget - me.itemHeight, 0); + return 1; + } + + me.setSelected(me, me.selectedItem - 1); } + else if (key == K_DOWNARROW || key == K_KP_DOWNARROW) + { + if (me.selectionDoesntMatter) + { + me.scrollPosTarget = min(me.scrollPosTarget + me.itemHeight, me.nItems * me.itemHeight - 1); + return 1; + } - float i = me.selectedItem; - float a = me.getItemHeight(me, i); - for (;;) + me.setSelected(me, me.selectedItem + 1); + } + else if (key == K_HOME || key == K_KP_HOME) { - ++i; - if (i >= me.nItems) - break; - a += me.getItemHeight(me, i); - if (a >= 1) - break; + me.setSelected(me, 0); } - me.setSelected(me, i - 1); - } - else if(key == K_UPARROW || key == K_KP_UPARROW) - { - if(me.selectionDoesntMatter) + else if (key == K_END || key == K_KP_END) { - me.scrollPosTarget = max(me.scrollPosTarget - me.itemHeight, 0); - return 1; + me.setSelected(me, me.nItems - 1); } - - me.setSelected(me, me.selectedItem - 1); - } - else if(key == K_DOWNARROW || key == K_KP_DOWNARROW) - { - if(me.selectionDoesntMatter) + else { - me.scrollPosTarget = min(me.scrollPosTarget + me.itemHeight, me.nItems * me.itemHeight - 1); - return 1; + return 0; } - - me.setSelected(me, me.selectedItem + 1); + return 1; } - else if(key == K_HOME || key == K_KP_HOME) - me.setSelected(me, 0); - else if(key == K_END || key == K_KP_END) - me.setSelected(me, me.nItems - 1); - else - return 0; - return 1; -} -float ListBox_mouseMove(entity me, vector pos) -{ - me.mouseMoveOffset = -1; - if(pos_x < 0) return 0; - if(pos_y < 0) return 0; - if(pos_x >= 1) return 0; - if(pos_y >= 1) return 0; - if(pos_x < 1 - me.controlWidth) - me.mouseMoveOffset = pos.y; - else + float ListBox_mouseMove(entity me, vector pos) { - me.setFocusedItem(me, -1); me.mouseMoveOffset = -1; - } - return 1; -} -float ListBox_mouseDrag(entity me, vector pos) -{ - float hit; - me.updateControlTopBottom(me); - me.dragScrollPos = pos; - if(me.pressed == 1) - { - hit = 1; - if(pos.x < 1 - me.controlWidth - me.tolerance.y * me.controlWidth) hit = 0; - if(pos.y < 0 - me.tolerance.x) hit = 0; - if(pos.x >= 1 + me.tolerance.y * me.controlWidth) hit = 0; - if(pos.y >= 1 + me.tolerance.x) hit = 0; - if(hit) + if (pos_x < 0) return 0; + if (pos_y < 0) return 0; + if (pos_x >= 1) return 0; + if (pos_y >= 1) return 0; + if (pos_x < 1 - me.controlWidth) { - // calculate new pos to v - float d; - d = (pos.y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.getTotalHeight(me) - 1); - me.scrollPosTarget = me.previousValue + d; + me.mouseMoveOffset = pos.y; } else - me.scrollPosTarget = me.previousValue; - me.scrollPosTarget = min(me.scrollPosTarget, me.getTotalHeight(me) - 1); - me.scrollPosTarget = max(me.scrollPosTarget, 0); - } - else if(me.pressed == 2) - { - me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); - me.setFocusedItem(me, me.selectedItem); - me.mouseMoveOffset = -1; + { + me.setFocusedItem(me, -1); + me.mouseMoveOffset = -1; + } + return 1; } - return 1; -} -float ListBox_mousePress(entity me, vector pos) -{ - if(pos.x < 0) return 0; - if(pos.y < 0) return 0; - if(pos.x >= 1) return 0; - if(pos.y >= 1) return 0; - me.dragScrollPos = pos; - me.updateControlTopBottom(me); - if(pos.x >= 1 - me.controlWidth) + float ListBox_mouseDrag(entity me, vector pos) { - // if hit, set me.pressed, otherwise scroll by one page - if(pos.y < me.controlTop) + float hit; + me.updateControlTopBottom(me); + me.dragScrollPos = pos; + if (me.pressed == 1) + { + hit = 1; + if (pos.x < 1 - me.controlWidth - me.tolerance.y * me.controlWidth) hit = 0; + if (pos.y < 0 - me.tolerance.x) hit = 0; + if (pos.x >= 1 + me.tolerance.y * me.controlWidth) hit = 0; + if (pos.y >= 1 + me.tolerance.x) hit = 0; + if (hit) + { + // calculate new pos to v + float d; + d = (pos.y - me.pressOffset) / (1 - (me.controlBottom - me.controlTop)) * (me.getTotalHeight(me) - 1); + me.scrollPosTarget = me.previousValue + d; + } + else + { + me.scrollPosTarget = me.previousValue; + } + me.scrollPosTarget = min(me.scrollPosTarget, me.getTotalHeight(me) - 1); + me.scrollPosTarget = max(me.scrollPosTarget, 0); + } + else if (me.pressed == 2) { - // page up - me.scrollPosTarget = max(me.scrollPosTarget - 1, 0); + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); + me.setFocusedItem(me, me.selectedItem); + me.mouseMoveOffset = -1; } - else if(pos.y > me.controlBottom) + return 1; + } + float ListBox_mousePress(entity me, vector pos) + { + if (pos.x < 0) return 0; + if (pos.y < 0) return 0; + if (pos.x >= 1) return 0; + if (pos.y >= 1) return 0; + me.dragScrollPos = pos; + me.updateControlTopBottom(me); + if (pos.x >= 1 - me.controlWidth) { - // page down - me.scrollPosTarget = min(me.scrollPosTarget + 1, me.getTotalHeight(me) - 1); + // if hit, set me.pressed, otherwise scroll by one page + if (pos.y < me.controlTop) + { + // page up + me.scrollPosTarget = max(me.scrollPosTarget - 1, 0); + } + else if (pos.y > me.controlBottom) + { + // page down + me.scrollPosTarget = min(me.scrollPosTarget + 1, me.getTotalHeight(me) - 1); + } + else + { + me.pressed = 1; + me.pressOffset = pos.y; + me.previousValue = me.scrollPos; + } } else { - me.pressed = 1; - me.pressOffset = pos.y; - me.previousValue = me.scrollPos; + // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item. + me.pressed = 2; + // an item has been clicked. Select it, ... + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); + me.setFocusedItem(me, me.selectedItem); } + return 1; } - else - { - // continue doing that while dragging (even when dragging outside). When releasing, forward the click to the then selected item. - me.pressed = 2; - // an item has been clicked. Select it, ... - me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); - me.setFocusedItem(me, me.selectedItem); - } - return 1; -} -void ListBox_setFocusedItem(entity me, int item) -{ - float focusedItem_save = me.focusedItem; - me.focusedItem = (item < me.nItems) ? item : -1; - if(focusedItem_save != me.focusedItem) + void ListBox_setFocusedItem(entity me, int item) { - me.focusedItemChangeNotify(me); - if(me.focusedItem >= 0) - me.focusedItemAlpha = SKINALPHA_LISTBOX_FOCUSED; - } -} -float ListBox_mouseRelease(entity me, vector pos) -{ - if(me.pressed == 1) - { - // slider dragging mode - // in that case, nothing happens on releasing + float focusedItem_save = me.focusedItem; + me.focusedItem = (item < me.nItems) ? item : -1; + if (focusedItem_save != me.focusedItem) + { + me.focusedItemChangeNotify(me); + if (me.focusedItem >= 0) me.focusedItemAlpha = SKINALPHA_LISTBOX_FOCUSED; + } } - else if(me.pressed == 2) + float ListBox_mouseRelease(entity me, vector pos) { - me.pressed = 3; // do that here, so setSelected can know the mouse has been released - // item dragging mode - // select current one one last time... - me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); - me.setFocusedItem(me, me.selectedItem); - // and give it a nice click event - if(me.nItems > 0) + if (me.pressed == 1) + { + // slider dragging mode + // in that case, nothing happens on releasing + } + else if (me.pressed == 2) { - vector where = globalToBox(pos, eY * (me.getItemStart(me, me.selectedItem) - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, me.selectedItem)); + me.pressed = 3; // do that here, so setSelected can know the mouse has been released + // item dragging mode + // select current one one last time... + me.setSelected(me, me.getItemAtPos(me, me.scrollPos + pos.y)); + me.setFocusedItem(me, me.selectedItem); + // and give it a nice click event + if (me.nItems > 0) + { + vector where = globalToBox(pos, eY * (me.getItemStart(me, me.selectedItem) - me.scrollPos), eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, me.selectedItem)); - if((me.selectedItem == me.lastClickedItem) && (time < me.lastClickedTime + 0.3)) - me.doubleClickListBoxItem(me, me.selectedItem, where); - else - me.clickListBoxItem(me, me.selectedItem, where); + if ((me.selectedItem == me.lastClickedItem) && (time < me.lastClickedTime + 0.3)) me.doubleClickListBoxItem(me, me.selectedItem, where); + else me.clickListBoxItem(me, me.selectedItem, where); - me.lastClickedItem = me.selectedItem; - me.lastClickedTime = time; + me.lastClickedItem = me.selectedItem; + me.lastClickedTime = time; + } } + me.pressed = 0; + return 1; } - me.pressed = 0; - return 1; -} -void ListBox_focusLeave(entity me) -{ - // Reset the var pressed in case listbox loses focus - // by a mouse click on an item of the list - // for example showing a dialog on right click - me.pressed = 0; - me.setFocusedItem(me, -1); - me.mouseMoveOffset = -1; -} -void ListBox_updateControlTopBottom(entity me) -{ - float f; - // scrollPos is in 0..1 and indicates where the "page" currently shown starts. - if(me.getTotalHeight(me) <= 1) + void ListBox_focusLeave(entity me) { - // we don't need no stinkin' scrollbar, we don't need no view control... - me.controlTop = 0; - me.controlBottom = 1; - me.scrollPos = 0; + // Reset the var pressed in case listbox loses focus + // by a mouse click on an item of the list + // for example showing a dialog on right click + me.pressed = 0; + me.setFocusedItem(me, -1); + me.mouseMoveOffset = -1; } - else + void ListBox_updateControlTopBottom(entity me) { - // if scroll pos is below end of list, fix it - me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1); - // if scroll pos is above beginning of list, fix it - me.scrollPos = max(me.scrollPos, 0); - // now that we know where the list is scrolled to, find out where to draw the control - me.controlTop = max(0, me.scrollPos / me.getTotalHeight(me)); - me.controlBottom = min((me.scrollPos + 1) / me.getTotalHeight(me), 1); - - float minfactor; - minfactor = 2 * me.controlWidth / me.size.y * me.size.x; - f = me.controlBottom - me.controlTop; - if(f < minfactor) // FIXME good default? + float f; + // scrollPos is in 0..1 and indicates where the "page" currently shown starts. + if (me.getTotalHeight(me) <= 1) { - // f * X + 1 * (1-X) = minfactor - // (f - 1) * X + 1 = minfactor - // (f - 1) * X = minfactor - 1 - // X = (minfactor - 1) / (f - 1) - f = (minfactor - 1) / (f - 1); - me.controlTop = me.controlTop * f + 0 * (1 - f); - me.controlBottom = me.controlBottom * f + 1 * (1 - f); + // we don't need no stinkin' scrollbar, we don't need no view control... + me.controlTop = 0; + me.controlBottom = 1; + me.scrollPos = 0; + } + else + { + // if scroll pos is below end of list, fix it + me.scrollPos = min(me.scrollPos, me.getTotalHeight(me) - 1); + // if scroll pos is above beginning of list, fix it + me.scrollPos = max(me.scrollPos, 0); + // now that we know where the list is scrolled to, find out where to draw the control + me.controlTop = max(0, me.scrollPos / me.getTotalHeight(me)); + me.controlBottom = min((me.scrollPos + 1) / me.getTotalHeight(me), 1); + + float minfactor; + minfactor = 2 * me.controlWidth / me.size.y * me.size.x; + f = me.controlBottom - me.controlTop; + if (f < minfactor) // FIXME good default? + { + // f * X + 1 * (1-X) = minfactor + // (f - 1) * X + 1 = minfactor + // (f - 1) * X = minfactor - 1 + // X = (minfactor - 1) / (f - 1) + f = (minfactor - 1) / (f - 1); + me.controlTop = me.controlTop * f + 0 * (1 - f); + me.controlBottom = me.controlBottom * f + 1 * (1 - f); + } } } -} -AUTOCVAR(menu_scroll_averaging_time, float, 0.16, "smooth scroll averaging time"); + AUTOCVAR(menu_scroll_averaging_time, float, 0.16, "smooth scroll averaging time"); // scroll faster while dragging the scrollbar -AUTOCVAR(menu_scroll_averaging_time_pressed, float, 0.06, "smooth scroll averaging time when dragging the scrollbar"); -void ListBox_draw(entity me) -{ - float i; - vector absSize, fillSize = '0 0 0'; - vector oldshift, oldscale; + AUTOCVAR(menu_scroll_averaging_time_pressed, float, 0.06, "smooth scroll averaging time when dragging the scrollbar"); + void ListBox_draw(entity me) + { + float i; + vector absSize, fillSize = '0 0 0'; + vector oldshift, oldscale; - // we can't do this in mouseMove as the list can scroll without moving the cursor - if(me.mouseMoveOffset != -1) - me.setFocusedItem(me, me.getItemAtPos(me, me.scrollPos + me.mouseMoveOffset)); + // we can't do this in mouseMove as the list can scroll without moving the cursor + if (me.mouseMoveOffset != -1) me.setFocusedItem(me, me.getItemAtPos(me, me.scrollPos + me.mouseMoveOffset)); - if(me.needScrollToItem >= 0) - { - me.scrollToItem(me, me.needScrollToItem); - me.needScrollToItem = -1; - } - if(me.scrollPos != me.scrollPosTarget) - { - float averaging_time = (me.pressed == 1) - ? autocvar_menu_scroll_averaging_time_pressed - : autocvar_menu_scroll_averaging_time; - // this formula works with whatever framerate - float f = averaging_time ? exp(-frametime / averaging_time) : 0; - me.scrollPos = me.scrollPos * f + me.scrollPosTarget * (1 - f); - if(fabs(me.scrollPos - me.scrollPosTarget) < 0.001) - me.scrollPos = me.scrollPosTarget; - } + if (me.needScrollToItem >= 0) + { + me.scrollToItem(me, me.needScrollToItem); + me.needScrollToItem = -1; + } + if (me.scrollPos != me.scrollPosTarget) + { + float averaging_time = (me.pressed == 1) + ? autocvar_menu_scroll_averaging_time_pressed + : autocvar_menu_scroll_averaging_time; + // this formula works with whatever framerate + float f = averaging_time ? exp(-frametime / averaging_time) : 0; + me.scrollPos = me.scrollPos * f + me.scrollPosTarget * (1 - f); + if (fabs(me.scrollPos - me.scrollPosTarget) < 0.001) me.scrollPos = me.scrollPosTarget; + } - if(me.pressed == 2) - me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event - me.updateControlTopBottom(me); - fillSize.x = (1 - me.controlWidth); - if(me.alphaBG) - draw_Fill('0 0 0', '0 1 0' + fillSize, me.colorBG, me.alphaBG); - if(me.controlWidth) - { - draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1); - if(me.getTotalHeight(me) > 1) + if (me.pressed == 2) me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event + me.updateControlTopBottom(me); + fillSize.x = (1 - me.controlWidth); + if (me.alphaBG) draw_Fill('0 0 0', '0 1 0' + fillSize, me.colorBG, me.alphaBG); + if (me.controlWidth) { - vector o, s; - o = eX * (1 - me.controlWidth) + eY * me.controlTop; - s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop); - if(me.pressed == 1) - draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1); - else if(me.focused) - draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1); - else - draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1); + draw_VertButtonPicture(eX * (1 - me.controlWidth), strcat(me.src, "_s"), eX * me.controlWidth + eY, me.color2, 1); + if (me.getTotalHeight(me) > 1) + { + vector o, s; + o = eX * (1 - me.controlWidth) + eY * me.controlTop; + s = eX * me.controlWidth + eY * (me.controlBottom - me.controlTop); + if (me.pressed == 1) draw_VertButtonPicture(o, strcat(me.src, "_c"), s, me.colorC, 1); + else if (me.focused) draw_VertButtonPicture(o, strcat(me.src, "_f"), s, me.colorF, 1); + else draw_VertButtonPicture(o, strcat(me.src, "_n"), s, me.color, 1); + } } - } - draw_SetClip(); - oldshift = draw_shift; - oldscale = draw_scale; + draw_SetClip(); + oldshift = draw_shift; + oldscale = draw_scale; - float y; - i = me.getItemAtPos(me, me.scrollPos); - y = me.getItemStart(me, i) - me.scrollPos; - for (; i < me.nItems && y < 1; ++i) - { - draw_shift = boxToGlobal(eY * y, oldshift, oldscale); - vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i); - absSize = boxToGlobalSize(relSize, me.size); - draw_scale = boxToGlobalSize(relSize, oldscale); - me.drawListBoxItem(me, i, absSize, (me.selectedItem == i), (me.focusedItem == i)); - y += relSize.y; - } - draw_ClearClip(); + float y; + i = me.getItemAtPos(me, me.scrollPos); + y = me.getItemStart(me, i) - me.scrollPos; + for ( ; i < me.nItems && y < 1; ++i) + { + draw_shift = boxToGlobal(eY * y, oldshift, oldscale); + vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i); + absSize = boxToGlobalSize(relSize, me.size); + draw_scale = boxToGlobalSize(relSize, oldscale); + me.drawListBoxItem(me, i, absSize, (me.selectedItem == i), (me.focusedItem == i)); + y += relSize.y; + } + draw_ClearClip(); - draw_shift = oldshift; - draw_scale = oldscale; - SUPER(ListBox).draw(me); -} + draw_shift = oldshift; + draw_scale = oldscale; + SUPER(ListBox).draw(me); + } -void ListBox_focusedItemChangeNotify(entity me) -{ -} + void ListBox_focusedItemChangeNotify(entity me) + {} -void ListBox_clickListBoxItem(entity me, float i, vector where) -{ - // template method -} + void ListBox_clickListBoxItem(entity me, float i, vector where) + { + // template method + } -void ListBox_doubleClickListBoxItem(entity me, float i, vector where) -{ - // template method -} + void ListBox_doubleClickListBoxItem(entity me, float i, vector where) + { + // template method + } -void ListBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused) -{ - draw_Text('0 0 0', sprintf(_("Item %d"), i), eX * (8 / absSize.x) + eY * (8 / absSize.y), (isSelected ? '0 1 0' : '1 1 1'), 1, 0); -} + void ListBox_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused) + { + draw_Text('0 0 0', sprintf(_("Item %d"), i), eX * (8 / absSize.x) + eY * (8 / absSize.y), (isSelected ? '0 1 0' : '1 1 1'), 1, 0); + } #endif diff --git a/qcsrc/menu/item/modalcontroller.qc b/qcsrc/menu/item/modalcontroller.qc index 046d8e1942..ed02a0cef3 100644 --- a/qcsrc/menu/item/modalcontroller.qc +++ b/qcsrc/menu/item/modalcontroller.qc @@ -1,31 +1,31 @@ #ifndef ITEM_MODALCONTROLLER_H -#define ITEM_MODALCONTROLLER_H -#include "container.qc" -CLASS(ModalController, Container) - METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(ModalController, draw, void(entity)); - METHOD(ModalController, showChild, void(entity, entity, vector, vector, float)); - METHOD(ModalController, hideChild, void(entity, entity, float)); - METHOD(ModalController, hideAll, void(entity, float)); - METHOD(ModalController, addItem, void(entity, entity, vector, vector, float)); - METHOD(ModalController, addTab, void(entity, entity, entity)); + #define ITEM_MODALCONTROLLER_H + #include "container.qc" + CLASS(ModalController, Container) + METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(ModalController, draw, void(entity)); + METHOD(ModalController, showChild, void(entity, entity, vector, vector, float)); + METHOD(ModalController, hideChild, void(entity, entity, float)); + METHOD(ModalController, hideAll, void(entity, float)); + METHOD(ModalController, addItem, void(entity, entity, vector, vector, float)); + METHOD(ModalController, addTab, void(entity, entity, entity)); - METHOD(ModalController, initializeDialog, void(entity, entity)); + METHOD(ModalController, initializeDialog, void(entity, entity)); - METHOD(ModalController, switchState, void(entity, entity, float, float)); - ATTRIB(ModalController, origin, vector, '0 0 0') - ATTRIB(ModalController, size, vector, '0 0 0') - ATTRIB(ModalController, previousButton, entity, NULL) - ATTRIB(ModalController, fadedAlpha, float, 0.3) -ENDCLASS(ModalController) + METHOD(ModalController, switchState, void(entity, entity, float, float)); + ATTRIB(ModalController, origin, vector, '0 0 0') + ATTRIB(ModalController, size, vector, '0 0 0') + ATTRIB(ModalController, previousButton, entity, NULL) + ATTRIB(ModalController, fadedAlpha, float, 0.3) + ENDCLASS(ModalController) -.entity tabSelectingButton; -.vector origin; -.vector size; -void TabButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate -void DialogOpenButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate -void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize); -void DialogCloseButton_Click(entity button, entity tab); // assumes a button has set the above fields to the tab to close + .entity tabSelectingButton; + .vector origin; + .vector size; + void TabButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate + void DialogOpenButton_Click(entity button, entity tab); // assumes a button has set the above fields to its own absolute origin, its size, and the tab to activate + void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize); + void DialogCloseButton_Click(entity button, entity tab); // assumes a button has set the above fields to the tab to close #endif #ifdef IMPLEMENTATION @@ -51,246 +51,238 @@ void DialogCloseButton_Click(entity button, entity tab); // assumes a button has // - to initialize: me.hideAll(me, 1); me.showChild(me, me.firstChild, '0 0 0', '0 0 0', 1); // - to show a tab: me.hideChild(me, currentTab, 0); me.showChild(me, newTab, buttonAbsOrigin, buttonAbsSize, 0); -.vector ModalController_initialSize; -.vector ModalController_initialOrigin; -.vector ModalController_initialFontScale; -.float ModalController_initialAlpha; -.vector ModalController_buttonSize; -.vector ModalController_buttonOrigin; -.float ModalController_state; -.float ModalController_factor; -.entity ModalController_controllingButton; + .vector ModalController_initialSize; + .vector ModalController_initialOrigin; + .vector ModalController_initialFontScale; + .float ModalController_initialAlpha; + .vector ModalController_buttonSize; + .vector ModalController_buttonOrigin; + .float ModalController_state; + .float ModalController_factor; + .entity ModalController_controllingButton; -void ModalController_initializeDialog(entity me, entity root) -{ - me.hideAll(me, 1); - me.showChild(me, root, '0 0 0', '0 0 0', 1); // someone else animates for us -} - -void TabButton_Click(entity button, entity tab) -{ - if(tab.ModalController_state == 1) - return; - tab.parent.hideAll(tab.parent, 0); - button.forcePressed = 1; - tab.ModalController_controllingButton = button; - tab.parent.showChild(tab.parent, tab, button.origin, button.size, 0); -} - -void DialogOpenButton_Click(entity button, entity tab) -{ - DialogOpenButton_Click_withCoords(button, tab, button.origin, button.size); -} + void ModalController_initializeDialog(entity me, entity root) + { + me.hideAll(me, 1); + me.showChild(me, root, '0 0 0', '0 0 0', 1); // someone else animates for us + } -void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize) -{ - if(tab.ModalController_state) - return; - if(button) + void TabButton_Click(entity button, entity tab) + { + if (tab.ModalController_state == 1) return; + tab.parent.hideAll(tab.parent, 0); button.forcePressed = 1; - if(tab.parent.focusedChild) - tab.parent.focusedChild.saveFocus(tab.parent.focusedChild); - tab.ModalController_controllingButton = button; - tab.parent.showChild(tab.parent, tab, theOrigin, theSize, 0); -} + tab.ModalController_controllingButton = button; + tab.parent.showChild(tab.parent, tab, button.origin, button.size, 0); + } -void DialogCloseButton_Click(entity button, entity tab) -{ - tab.parent.hideChild(tab.parent, tab, 0); -} + void DialogOpenButton_Click(entity button, entity tab) + { + DialogOpenButton_Click_withCoords(button, tab, button.origin, button.size); + } -void ModalController_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, ModalController_initialOrigin, ModalController_initialSize, ModalController_initialFontScale); -} + void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize) + { + if (tab.ModalController_state) return; + if (button) button.forcePressed = 1; + if (tab.parent.focusedChild) tab.parent.focusedChild.saveFocus(tab.parent.focusedChild); + tab.ModalController_controllingButton = button; + tab.parent.showChild(tab.parent, tab, theOrigin, theSize, 0); + } -void ModalController_switchState(entity me, entity other, float state, float skipAnimation) -{ - float previousState; - previousState = other.ModalController_state; - if(state == previousState && !skipAnimation) - return; - other.ModalController_state = state; - switch(state) + void DialogCloseButton_Click(entity button, entity tab) { - case 0: - other.ModalController_factor = 1 - other.Container_alpha / other.ModalController_initialAlpha; - // fading out - break; - case 1: - other.ModalController_factor = other.Container_alpha / other.ModalController_initialAlpha; - if(previousState == 0 && !skipAnimation) - { - other.Container_origin = other.ModalController_buttonOrigin; - other.Container_size = other.ModalController_buttonSize; - } - // zooming in - break; - case 2: - other.ModalController_factor = bound(0, (1 - other.Container_alpha / other.ModalController_initialAlpha) / me.fadedAlpha, 1); - // fading out halfway - break; + tab.parent.hideChild(tab.parent, tab, 0); } - if(skipAnimation) - other.ModalController_factor = 1; -} -void ModalController_draw(entity me) -{ - entity e; - entity front; - float animating; - float f; // animation factor - float df; // animation step size - float prevFactor, targetFactor; - vector targetOrigin, targetSize; float targetAlpha; - vector fs; - animating = 0; + void ModalController_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, ModalController_initialOrigin, ModalController_initialSize, ModalController_initialFontScale); + } - front = NULL; - for(e = me.firstChild; e; e = e.nextSibling) - if(e.ModalController_state) + void ModalController_switchState(entity me, entity other, float state, float skipAnimation) + { + float previousState; + previousState = other.ModalController_state; + if (state == previousState && !skipAnimation) return; + other.ModalController_state = state; + switch (state) { - if(front) - me.switchState(me, front, 2, 0); - front = e; + case 0: + other.ModalController_factor = 1 - other.Container_alpha / other.ModalController_initialAlpha; + // fading out + break; + case 1: + other.ModalController_factor = other.Container_alpha / other.ModalController_initialAlpha; + if (previousState == 0 && !skipAnimation) + { + other.Container_origin = other.ModalController_buttonOrigin; + other.Container_size = other.ModalController_buttonSize; + } + // zooming in + break; + case 2: + other.ModalController_factor = bound(0, (1 - other.Container_alpha / other.ModalController_initialAlpha) / me.fadedAlpha, 1); + // fading out halfway + break; } - if(front) - me.switchState(me, front, 1, 0); - - df = frametime * 3; // animation speed + if (skipAnimation) other.ModalController_factor = 1; + } - for(e = me.firstChild; e; e = e.nextSibling) + void ModalController_draw(entity me) { - if(e.ModalController_state == 2) - { - // fading out partially - targetOrigin = e.Container_origin; // stay as is - targetSize = e.Container_size; // stay as is - targetAlpha = me.fadedAlpha * e.ModalController_initialAlpha; - } - else if(e.ModalController_state == 1) - { - // zooming in - targetOrigin = e.ModalController_initialOrigin; - targetSize = e.ModalController_initialSize; - targetAlpha = e.ModalController_initialAlpha; - } - else - { - // fading out - targetOrigin = e.Container_origin; // stay as is - targetSize = e.Container_size; // stay as is - targetAlpha = 0; - } + entity e; + entity front; + float animating; + float f; // animation factor + float df; // animation step size + float prevFactor, targetFactor; + vector targetOrigin, targetSize; + float targetAlpha; + vector fs; + animating = 0; - f = (e.ModalController_factor = min(1, e.ModalController_factor + df)); - if(f == 1) - { - prevFactor = 0; - targetFactor = 1; - e.Container_origin = targetOrigin; - e.Container_size = targetSize; - me.setAlphaOf(me, e, targetAlpha); - } - else + front = NULL; + for (e = me.firstChild; e; e = e.nextSibling) + if (e.ModalController_state) + { + if (front) me.switchState(me, front, 2, 0); + front = e; + } + if (front) me.switchState(me, front, 1, 0); + + df = frametime * 3; // animation speed + + for (e = me.firstChild; e; e = e.nextSibling) { - prevFactor = (1 - f) / (1 - f + df); - if(!e.ModalController_state) // optimize code and avoid precision errors - me.setAlphaOf(me, e, e.Container_alpha * prevFactor); + if (e.ModalController_state == 2) + { + // fading out partially + targetOrigin = e.Container_origin; // stay as is + targetSize = e.Container_size; // stay as is + targetAlpha = me.fadedAlpha * e.ModalController_initialAlpha; + } + else if (e.ModalController_state == 1) + { + // zooming in + targetOrigin = e.ModalController_initialOrigin; + targetSize = e.ModalController_initialSize; + targetAlpha = e.ModalController_initialAlpha; + } else { - animating = 1; - targetFactor = df / (1 - f + df); + // fading out + targetOrigin = e.Container_origin; // stay as is + targetSize = e.Container_size; // stay as is + targetAlpha = 0; + } - if(e.ModalController_state == 1) + f = (e.ModalController_factor = min(1, e.ModalController_factor + df)); + if (f == 1) + { + prevFactor = 0; + targetFactor = 1; + e.Container_origin = targetOrigin; + e.Container_size = targetSize; + me.setAlphaOf(me, e, targetAlpha); + } + else + { + prevFactor = (1 - f) / (1 - f + df); + if (!e.ModalController_state) // optimize code and avoid precision errors + { + me.setAlphaOf(me, e, e.Container_alpha * prevFactor); + } + else { - e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor; - e.Container_size = e.Container_size * prevFactor + targetSize * targetFactor; + animating = 1; + targetFactor = df / (1 - f + df); + + if (e.ModalController_state == 1) + { + e.Container_origin = e.Container_origin * prevFactor + targetOrigin * targetFactor; + e.Container_size = e.Container_size * prevFactor + targetSize * targetFactor; + } + me.setAlphaOf(me, e, e.Container_alpha * prevFactor + targetAlpha * targetFactor); } - me.setAlphaOf(me, e, e.Container_alpha * prevFactor + targetAlpha * targetFactor); + } + // assume: o == to * f_prev + X * (1 - f_prev) + // make: o' = to * f + X * (1 - f) + // --> + // X == (o - to * f_prev) / (1 - f_prev) + // o' = to * f + (o - to * f_prev) / (1 - f_prev) * (1 - f) + // --> (maxima) + // o' = (to * (f - f_prev) + o * (1 - f)) / (1 - f_prev) + + if (e.ModalController_state == 1) + { + fs = globalToBoxSize(e.Container_size, e.ModalController_initialSize); + e.Container_fontscale_x = fs.x * e.ModalController_initialFontScale.x; + e.Container_fontscale_y = fs.y * e.ModalController_initialFontScale.y; } } - // assume: o == to * f_prev + X * (1 - f_prev) - // make: o' = to * f + X * (1 - f) - // --> - // X == (o - to * f_prev) / (1 - f_prev) - // o' = to * f + (o - to * f_prev) / (1 - f_prev) * (1 - f) - // --> (maxima) - // o' = (to * (f - f_prev) + o * (1 - f)) / (1 - f_prev) - if(e.ModalController_state == 1) + if (animating || !me.focused) me.setFocus(me, NULL); + else me.setFocus(me, front); + SUPER(ModalController).draw(me); + } + + void ModalController_addTab(entity me, entity other, entity tabButton) + { + me.addItem(me, other, '0 0 0', '1 1 1', 1); + tabButton.onClick = TabButton_Click; + tabButton.onClickEntity = other; + other.tabSelectingButton = tabButton; + if (other == me.firstChild) { - fs = globalToBoxSize(e.Container_size, e.ModalController_initialSize); - e.Container_fontscale_x = fs.x * e.ModalController_initialFontScale.x; - e.Container_fontscale_y = fs.y * e.ModalController_initialFontScale.y; + tabButton.forcePressed = 1; + other.ModalController_controllingButton = tabButton; + me.showChild(me, other, '0 0 0', '0 0 0', 1); } } - if(animating || !me.focused) - me.setFocus(me, NULL); - else - me.setFocus(me, front); - SUPER(ModalController).draw(me); -} - -void ModalController_addTab(entity me, entity other, entity tabButton) -{ - me.addItem(me, other, '0 0 0', '1 1 1', 1); - tabButton.onClick = TabButton_Click; - tabButton.onClickEntity = other; - other.tabSelectingButton = tabButton; - if(other == me.firstChild) + void ModalController_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) { - tabButton.forcePressed = 1; - other.ModalController_controllingButton = tabButton; - me.showChild(me, other, '0 0 0', '0 0 0', 1); + SUPER(ModalController).addItem(me, other, theOrigin, theSize, (other == me.firstChild) ? theAlpha : 0); + other.ModalController_initialFontScale = other.Container_fontscale; + other.ModalController_initialSize = other.Container_size; + other.ModalController_initialOrigin = other.Container_origin; + other.ModalController_initialAlpha = theAlpha; // hope Container never modifies this + if (other.ModalController_initialFontScale == '0 0 0') other.ModalController_initialFontScale = '1 1 0'; } -} -void ModalController_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) -{ - SUPER(ModalController).addItem(me, other, theOrigin, theSize, (other == me.firstChild) ? theAlpha : 0); - other.ModalController_initialFontScale = other.Container_fontscale; - other.ModalController_initialSize = other.Container_size; - other.ModalController_initialOrigin = other.Container_origin; - other.ModalController_initialAlpha = theAlpha; // hope Container never modifies this - if(other.ModalController_initialFontScale == '0 0 0') - other.ModalController_initialFontScale = '1 1 0'; -} - -void ModalController_showChild(entity me, entity other, vector theOrigin, vector theSize, float skipAnimation) -{ - if(other.ModalController_state == 0 || skipAnimation) + void ModalController_showChild(entity me, entity other, vector theOrigin, vector theSize, float skipAnimation) { - me.setFocus(me, NULL); - if(!skipAnimation) + if (other.ModalController_state == 0 || skipAnimation) { - other.ModalController_buttonOrigin = globalToBox(theOrigin, me.origin, me.size); - other.ModalController_buttonSize = globalToBoxSize(theSize, me.size); - } - me.switchState(me, other, 1, skipAnimation); - } // zoom in from button (factor increases) -} + me.setFocus(me, NULL); + if (!skipAnimation) + { + other.ModalController_buttonOrigin = globalToBox(theOrigin, me.origin, me.size); + other.ModalController_buttonSize = globalToBoxSize(theSize, me.size); + } + me.switchState(me, other, 1, skipAnimation); + } // zoom in from button (factor increases) + } -void ModalController_hideAll(entity me, float skipAnimation) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) - me.hideChild(me, e, skipAnimation); -} + void ModalController_hideAll(entity me, float skipAnimation) + { + entity e; + for (e = me.firstChild; e; e = e.nextSibling) + me.hideChild(me, e, skipAnimation); + } -void ModalController_hideChild(entity me, entity other, float skipAnimation) -{ - if(other.ModalController_state || skipAnimation) + void ModalController_hideChild(entity me, entity other, float skipAnimation) { - me.setFocus(me, NULL); - me.switchState(me, other, 0, skipAnimation); - if(other.ModalController_controllingButton) + if (other.ModalController_state || skipAnimation) { - other.ModalController_controllingButton.forcePressed = 0; - other.ModalController_controllingButton = NULL; - } - } // just alpha fade out (factor increases and decreases alpha) -} + me.setFocus(me, NULL); + me.switchState(me, other, 0, skipAnimation); + if (other.ModalController_controllingButton) + { + other.ModalController_controllingButton.forcePressed = 0; + other.ModalController_controllingButton = NULL; + } + } // just alpha fade out (factor increases and decreases alpha) + } #endif diff --git a/qcsrc/menu/item/nexposee.qc b/qcsrc/menu/item/nexposee.qc index 06616e68f5..e35909493c 100644 --- a/qcsrc/menu/item/nexposee.qc +++ b/qcsrc/menu/item/nexposee.qc @@ -1,30 +1,30 @@ #ifndef ITEM_NEXPOSEE_H -#define ITEM_NEXPOSEE_H -#include "container.qc" -CLASS(Nexposee, Container) - METHOD(Nexposee, draw, void(entity)); - METHOD(Nexposee, keyDown, float(entity, float, float, float)); - METHOD(Nexposee, keyUp, float(entity, float, float, float)); - METHOD(Nexposee, mousePress, float(entity, vector)); - METHOD(Nexposee, mouseMove, float(entity, vector)); - METHOD(Nexposee, mouseRelease, float(entity, vector)); - METHOD(Nexposee, mouseDrag, float(entity, vector)); - METHOD(Nexposee, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Nexposee, focusEnter, void(entity)); - METHOD(Nexposee, close, void(entity)); - - ATTRIB(Nexposee, animationState, float, -1) - ATTRIB(Nexposee, animationFactor, float, 0) - ATTRIB(Nexposee, selectedChild, entity, NULL) - ATTRIB(Nexposee, mouseFocusedChild, entity, NULL) - METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float)); - METHOD(Nexposee, calc, void(entity)); - METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float)); - ATTRIB(Nexposee, mousePosition, vector, '0 0 0') - METHOD(Nexposee, pullNexposee, void(entity, entity, vector)); -ENDCLASS(Nexposee) - -void ExposeeCloseButton_Click(entity button, entity other); // un-exposees the current state + #define ITEM_NEXPOSEE_H + #include "container.qc" + CLASS(Nexposee, Container) + METHOD(Nexposee, draw, void(entity)); + METHOD(Nexposee, keyDown, float(entity, float, float, float)); + METHOD(Nexposee, keyUp, float(entity, float, float, float)); + METHOD(Nexposee, mousePress, float(entity, vector)); + METHOD(Nexposee, mouseMove, float(entity, vector)); + METHOD(Nexposee, mouseRelease, float(entity, vector)); + METHOD(Nexposee, mouseDrag, float(entity, vector)); + METHOD(Nexposee, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Nexposee, focusEnter, void(entity)); + METHOD(Nexposee, close, void(entity)); + + ATTRIB(Nexposee, animationState, float, -1) + ATTRIB(Nexposee, animationFactor, float, 0) + ATTRIB(Nexposee, selectedChild, entity, NULL) + ATTRIB(Nexposee, mouseFocusedChild, entity, NULL) + METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float)); + METHOD(Nexposee, calc, void(entity)); + METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float)); + ATTRIB(Nexposee, mousePosition, vector, '0 0 0') + METHOD(Nexposee, pullNexposee, void(entity, entity, vector)); + ENDCLASS(Nexposee) + + void ExposeeCloseButton_Click(entity button, entity other); // un-exposees the current state // animation states: // 0 = thumbnails seen @@ -32,337 +32,315 @@ void ExposeeCloseButton_Click(entity button, entity other); // un-exposees the c // 2 = zoomed in // 3 = zooming out // animation factor: 0 = minimum theSize, 1 = maximum theSize -.vector Nexposee_initialSize; -.vector Nexposee_initialFontScale; -.vector Nexposee_initialOrigin; -.float Nexposee_initialAlpha; - -.vector Nexposee_smallSize; -.vector Nexposee_smallOrigin; -.float Nexposee_smallAlpha; -.float Nexposee_mediumAlpha; -.vector Nexposee_scaleCenter; -.vector Nexposee_align; -.float Nexposee_animationFactor; + .vector Nexposee_initialSize; + .vector Nexposee_initialFontScale; + .vector Nexposee_initialOrigin; + .float Nexposee_initialAlpha; + + .vector Nexposee_smallSize; + .vector Nexposee_smallOrigin; + .float Nexposee_smallAlpha; + .float Nexposee_mediumAlpha; + .vector Nexposee_scaleCenter; + .vector Nexposee_align; + .float Nexposee_animationFactor; #endif #ifdef IMPLEMENTATION -void Nexposee_close(entity me) -{ - // user must override this -} - -void ExposeeCloseButton_Click(entity button, entity other) -{ - other.selectedChild = other.focusedChild; - other.setFocus(other, NULL); - other.animationState = 3; -} - -void Nexposee_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - me.calc(me); - me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Nexposee_initialOrigin, Nexposee_initialSize, Nexposee_initialFontScale); -} - -void Nexposee_Calc_Scale(entity me, float scale) -{ - entity e; - for(e = me.firstChild; e; e = e.nextSibling) + void Nexposee_close(entity me) { - e.Nexposee_smallOrigin = (e.Nexposee_initialOrigin - e.Nexposee_scaleCenter) * scale + e.Nexposee_scaleCenter; - e.Nexposee_smallSize = e.Nexposee_initialSize * scale; - if(e.Nexposee_align.x > 0) - e.Nexposee_smallOrigin_x = 1 - e.Nexposee_align.x * scale; - if(e.Nexposee_align.x < 0) - e.Nexposee_smallOrigin_x = -e.Nexposee_smallSize.x + e.Nexposee_align.x * scale; - if(e.Nexposee_align.y > 0) - e.Nexposee_smallOrigin_y = 1 - e.Nexposee_align.y * scale; - if(e.Nexposee_align.y < 0) - e.Nexposee_smallOrigin_y = -e.Nexposee_smallSize.y + e.Nexposee_align.y * scale; + // user must override this } -} - -void Nexposee_calc(entity me) -{ - /* - * patented by Apple - * can't put that here ;) - */ - float scale; - entity e, e2; - vector emins, emaxs, e2mins, e2maxs; - - for(scale = 0.7;; scale *= 0.99) + + void ExposeeCloseButton_Click(entity button, entity other) { - Nexposee_Calc_Scale(me, scale); + other.selectedChild = other.focusedChild; + other.setFocus(other, NULL); + other.animationState = 3; + } - for(e = me.firstChild; e; e = e.nextSibling) + void Nexposee_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) + { + me.calc(me); + me.resizeNotifyLie(me, relOrigin, relSize, absOrigin, absSize, Nexposee_initialOrigin, Nexposee_initialSize, Nexposee_initialFontScale); + } + + void Nexposee_Calc_Scale(entity me, float scale) + { + entity e; + for (e = me.firstChild; e; e = e.nextSibling) { - emins = e.Nexposee_smallOrigin; - emaxs = emins + e.Nexposee_smallSize; - for(e2 = e.nextSibling; e2; e2 = e2.nextSibling) - { - e2mins = e2.Nexposee_smallOrigin; - e2maxs = e2mins + e2.Nexposee_smallSize; - - // two intervals [amins, amaxs] and [bmins, bmaxs] overlap if: - // amins < bmins < amaxs < bmaxs - // for which suffices - // bmins < amaxs - // amins < bmaxs - if((e2mins.x - emaxs.x) * (emins.x - e2maxs.x) > 0) // x overlap - if((e2mins.y - emaxs.y) * (emins.y - e2maxs.y) > 0) // y overlap - { - goto have_overlap; - } - } + e.Nexposee_smallOrigin = (e.Nexposee_initialOrigin - e.Nexposee_scaleCenter) * scale + e.Nexposee_scaleCenter; + e.Nexposee_smallSize = e.Nexposee_initialSize * scale; + if (e.Nexposee_align.x > 0) e.Nexposee_smallOrigin_x = 1 - e.Nexposee_align.x * scale; + if (e.Nexposee_align.x < 0) e.Nexposee_smallOrigin_x = -e.Nexposee_smallSize.x + e.Nexposee_align.x * scale; + if (e.Nexposee_align.y > 0) e.Nexposee_smallOrigin_y = 1 - e.Nexposee_align.y * scale; + if (e.Nexposee_align.y < 0) e.Nexposee_smallOrigin_y = -e.Nexposee_smallSize.y + e.Nexposee_align.y * scale; } - - break; -:have_overlap } - scale *= 0.95; + void Nexposee_calc(entity me) + { + /* + * patented by Apple + * can't put that here ;) + */ + float scale; + entity e, e2; + vector emins, emaxs, e2mins, e2maxs; + + for (scale = 0.7; ; scale *= 0.99) + { + Nexposee_Calc_Scale(me, scale); - Nexposee_Calc_Scale(me, scale); -} + for (e = me.firstChild; e; e = e.nextSibling) + { + emins = e.Nexposee_smallOrigin; + emaxs = emins + e.Nexposee_smallSize; + for (e2 = e.nextSibling; e2; e2 = e2.nextSibling) + { + e2mins = e2.Nexposee_smallOrigin; + e2maxs = e2mins + e2.Nexposee_smallSize; + + // two intervals [amins, amaxs] and [bmins, bmaxs] overlap if: + // amins < bmins < amaxs < bmaxs + // for which suffices + // bmins < amaxs + // amins < bmaxs + if ((e2mins.x - emaxs.x) * (emins.x - e2maxs.x) > 0) // x overlap + if ((e2mins.y - emaxs.y) * (emins.y - e2maxs.y) > 0) // y overlap + goto have_overlap; + } + } + + break; + : have_overlap + } -void Nexposee_setNexposee(entity me, entity other, vector scalecenter, float a0, float a1) -{ - other.Nexposee_scaleCenter = scalecenter; - other.Nexposee_smallAlpha = a0; - me.setAlphaOf(me, other, a0); - other.Nexposee_mediumAlpha = a1; -} + scale *= 0.95; -void Nexposee_draw(entity me) -{ - float a; - float a0; - entity e; - float f; - vector fs; + Nexposee_Calc_Scale(me, scale); + } - if(me.animationState == -1) + void Nexposee_setNexposee(entity me, entity other, vector scalecenter, float a0, float a1) { - me.animationState = 0; + other.Nexposee_scaleCenter = scalecenter; + other.Nexposee_smallAlpha = a0; + me.setAlphaOf(me, other, a0); + other.Nexposee_mediumAlpha = a1; } - f = min(1, frametime * 5); - switch(me.animationState) + void Nexposee_draw(entity me) { - case 0: - me.animationFactor = 0; - break; - case 1: - me.animationFactor += f; - if(me.animationFactor >= 1) - { + float a; + float a0; + entity e; + float f; + vector fs; + + if (me.animationState == -1) me.animationState = 0; + + f = min(1, frametime * 5); + switch (me.animationState) + { + case 0: + me.animationFactor = 0; + break; + case 1: + me.animationFactor += f; + if (me.animationFactor >= 1) + { + me.animationFactor = 1; + me.animationState = 2; + SUPER(Nexposee).setFocus(me, me.selectedChild); + } + break; + case 2: me.animationFactor = 1; - me.animationState = 2; - SUPER(Nexposee).setFocus(me, me.selectedChild); + break; + case 3: + me.animationFactor -= f; + me.mouseFocusedChild = me.itemFromPoint(me, me.mousePosition); + if (me.animationFactor <= 0) + { + me.animationFactor = 0; + me.animationState = 0; + me.selectedChild = me.mouseFocusedChild; + } + break; + } + + f = min(1, frametime * 10); + for (e = me.firstChild; e; e = e.nextSibling) + { + if (e == me.selectedChild) + { + e.Container_origin = e.Nexposee_smallOrigin * (1 - me.animationFactor) + e.Nexposee_initialOrigin * me.animationFactor; + e.Container_size = e.Nexposee_smallSize * (1 - me.animationFactor) + e.Nexposee_initialSize * me.animationFactor; + e.Nexposee_animationFactor = me.animationFactor; + a0 = e.Nexposee_mediumAlpha; + if (me.animationState == 3) + if (e != me.mouseFocusedChild) a0 = e.Nexposee_smallAlpha; + a = a0 * (1 - me.animationFactor) + me.animationFactor; } - break; - case 2: - me.animationFactor = 1; - break; - case 3: - me.animationFactor -= f; - me.mouseFocusedChild = me.itemFromPoint(me, me.mousePosition); - if(me.animationFactor <= 0) + else { - me.animationFactor = 0; - me.animationState = 0; - me.selectedChild = me.mouseFocusedChild; + // minimum theSize counts + e.Container_origin = e.Nexposee_smallOrigin; + e.Container_size = e.Nexposee_smallSize; + e.Nexposee_animationFactor = 0; + a = e.Nexposee_smallAlpha * (1 - me.animationFactor); } - break; + me.setAlphaOf(me, e, e.Container_alpha * (1 - f) + a * f); + + fs = globalToBoxSize(e.Container_size, e.Nexposee_initialSize); + e.Container_fontscale_x = fs.x * e.Nexposee_initialFontScale.x; + e.Container_fontscale_y = fs.y * e.Nexposee_initialFontScale.y; + } + + SUPER(Nexposee).draw(me); } - f = min(1, frametime * 10); - for(e = me.firstChild; e; e = e.nextSibling) + float Nexposee_mousePress(entity me, vector pos) { - if(e == me.selectedChild) + if (me.animationState == 0) { - e.Container_origin = e.Nexposee_smallOrigin * (1 - me.animationFactor) + e.Nexposee_initialOrigin * me.animationFactor; - e.Container_size = e.Nexposee_smallSize * (1 - me.animationFactor) + e.Nexposee_initialSize * me.animationFactor; - e.Nexposee_animationFactor = me.animationFactor; - a0 = e.Nexposee_mediumAlpha; - if(me.animationState == 3) - if(e != me.mouseFocusedChild) - a0 = e.Nexposee_smallAlpha; - a = a0 * (1 - me.animationFactor) + me.animationFactor; + me.mouseFocusedChild = NULL; + Nexposee_mouseMove(me, pos); + if (me.mouseFocusedChild) + { + m_play_click_sound(MENU_SOUND_OPEN); + me.animationState = 1; + SUPER(Nexposee).setFocus(me, NULL); + } + else + { + me.close(me); + } + return 1; } - else + else if (me.animationState == 2) { - // minimum theSize counts - e.Container_origin = e.Nexposee_smallOrigin; - e.Container_size = e.Nexposee_smallSize; - e.Nexposee_animationFactor = 0; - a = e.Nexposee_smallAlpha * (1 - me.animationFactor); + if (!(SUPER(Nexposee).mousePress(me, pos))) + { + m_play_click_sound(MENU_SOUND_CLOSE); + me.animationState = 3; + SUPER(Nexposee).setFocus(me, NULL); + } + return 1; } - me.setAlphaOf(me, e, e.Container_alpha * (1 - f) + a * f); - - fs = globalToBoxSize(e.Container_size, e.Nexposee_initialSize); - e.Container_fontscale_x = fs.x * e.Nexposee_initialFontScale.x; - e.Container_fontscale_y = fs.y * e.Nexposee_initialFontScale.y; + return 0; } - SUPER(Nexposee).draw(me); -} + float Nexposee_mouseRelease(entity me, vector pos) + { + if (me.animationState == 2) return SUPER(Nexposee).mouseRelease(me, pos); + return 0; + } -float Nexposee_mousePress(entity me, vector pos) -{ - if(me.animationState == 0) + float Nexposee_mouseDrag(entity me, vector pos) { - me.mouseFocusedChild = NULL; - Nexposee_mouseMove(me, pos); - if(me.mouseFocusedChild) - { - m_play_click_sound(MENU_SOUND_OPEN); - me.animationState = 1; - SUPER(Nexposee).setFocus(me, NULL); - } - else - me.close(me); - return 1; + if (me.animationState == 2) return SUPER(Nexposee).mouseDrag(me, pos); + return 0; } - else if(me.animationState == 2) + + float Nexposee_mouseMove(entity me, vector pos) { - if (!(SUPER(Nexposee).mousePress(me, pos))) + entity e; + me.mousePosition = pos; + e = me.mouseFocusedChild; + me.mouseFocusedChild = me.itemFromPoint(me, pos); + if (me.animationState == 2) return SUPER(Nexposee).mouseMove(me, pos); + if (me.animationState == 0) { - m_play_click_sound(MENU_SOUND_CLOSE); - me.animationState = 3; - SUPER(Nexposee).setFocus(me, NULL); + if (me.mouseFocusedChild) + if (me.mouseFocusedChild != e || me.mouseFocusedChild != me.selectedChild) me.selectedChild = me.mouseFocusedChild; + return 1; } - return 1; + return 0; } - return 0; -} - -float Nexposee_mouseRelease(entity me, vector pos) -{ - if(me.animationState == 2) - return SUPER(Nexposee).mouseRelease(me, pos); - return 0; -} - -float Nexposee_mouseDrag(entity me, vector pos) -{ - if(me.animationState == 2) - return SUPER(Nexposee).mouseDrag(me, pos); - return 0; -} - -float Nexposee_mouseMove(entity me, vector pos) -{ - entity e; - me.mousePosition = pos; - e = me.mouseFocusedChild; - me.mouseFocusedChild = me.itemFromPoint(me, pos); - if(me.animationState == 2) - return SUPER(Nexposee).mouseMove(me, pos); - if(me.animationState == 0) + + float Nexposee_keyUp(entity me, float scan, float ascii, float shift) { - if(me.mouseFocusedChild) - if(me.mouseFocusedChild != e || me.mouseFocusedChild != me.selectedChild) - me.selectedChild = me.mouseFocusedChild; - return 1; + if (me.animationState == 2) return SUPER(Nexposee).keyUp(me, scan, ascii, shift); + return 0; } - return 0; -} - -float Nexposee_keyUp(entity me, float scan, float ascii, float shift) -{ - if(me.animationState == 2) - return SUPER(Nexposee).keyUp(me, scan, ascii, shift); - return 0; -} - -float Nexposee_keyDown(entity me, float scan, float ascii, float shift) -{ - float nexposeeKey = 0; - if(me.animationState == 2) - if(SUPER(Nexposee).keyDown(me, scan, ascii, shift)) - return 1; - if(scan == K_TAB) + + float Nexposee_keyDown(entity me, float scan, float ascii, float shift) { - if(me.animationState == 0) + float nexposeeKey = 0; + if (me.animationState == 2) + if (SUPER(Nexposee).keyDown(me, scan, ascii, shift)) return 1; + if (scan == K_TAB) { - if(shift & S_SHIFT) - { - if(me.selectedChild) - me.selectedChild = me.selectedChild.prevSibling; - if (!me.selectedChild) - me.selectedChild = me.lastChild; - } - else + if (me.animationState == 0) { - if(me.selectedChild) - me.selectedChild = me.selectedChild.nextSibling; - if (!me.selectedChild) - me.selectedChild = me.firstChild; + if (shift & S_SHIFT) + { + if (me.selectedChild) me.selectedChild = me.selectedChild.prevSibling; + if (!me.selectedChild) me.selectedChild = me.lastChild; + } + else + { + if (me.selectedChild) me.selectedChild = me.selectedChild.nextSibling; + if (!me.selectedChild) me.selectedChild = me.firstChild; + } } } - } - switch(me.animationState) - { - default: - case 0: - case 3: - nexposeeKey = ((scan == K_SPACE) || (scan == K_ENTER) || (scan == K_KP_ENTER)); - break; - case 1: - case 2: - nexposeeKey = (scan == K_ESCAPE); - break; - } - if(nexposeeKey) - { - switch(me.animationState) + switch (me.animationState) { default: case 0: case 3: - m_play_click_sound(MENU_SOUND_OPEN); - me.animationState = 1; + nexposeeKey = ((scan == K_SPACE) || (scan == K_ENTER) || (scan == K_KP_ENTER)); break; case 1: case 2: - m_play_click_sound(MENU_SOUND_CLOSE); - me.animationState = 3; + nexposeeKey = (scan == K_ESCAPE); break; } - if(me.focusedChild) - me.selectedChild = me.focusedChild; - if (!me.selectedChild) - me.animationState = 0; - SUPER(Nexposee).setFocus(me, NULL); - return 1; + if (nexposeeKey) + { + switch (me.animationState) + { + default: + case 0: + case 3: + m_play_click_sound(MENU_SOUND_OPEN); + me.animationState = 1; + break; + case 1: + case 2: + m_play_click_sound(MENU_SOUND_CLOSE); + me.animationState = 3; + break; + } + if (me.focusedChild) me.selectedChild = me.focusedChild; + if (!me.selectedChild) me.animationState = 0; + SUPER(Nexposee).setFocus(me, NULL); + return 1; + } + return 0; + } + + void Nexposee_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) + { + SUPER(Nexposee).addItem(me, other, theOrigin, theSize, theAlpha); + other.Nexposee_initialFontScale = other.Container_fontscale; + other.Nexposee_initialSize = other.Container_size; + other.Nexposee_initialOrigin = other.Container_origin; + other.Nexposee_initialAlpha = other.Container_alpha; + if (other.Nexposee_initialFontScale == '0 0 0') other.Nexposee_initialFontScale = '1 1 0'; + } + + void Nexposee_focusEnter(entity me) + { + if (me.animationState == 2) SUPER(Nexposee).setFocus(me, me.selectedChild); + } + + void Nexposee_pullNexposee(entity me, entity other, vector theAlign) + { + other.Nexposee_align = theAlign; } - return 0; -} - -void Nexposee_addItem(entity me, entity other, vector theOrigin, vector theSize, float theAlpha) -{ - SUPER(Nexposee).addItem(me, other, theOrigin, theSize, theAlpha); - other.Nexposee_initialFontScale = other.Container_fontscale; - other.Nexposee_initialSize = other.Container_size; - other.Nexposee_initialOrigin = other.Container_origin; - other.Nexposee_initialAlpha = other.Container_alpha; - if(other.Nexposee_initialFontScale == '0 0 0') - other.Nexposee_initialFontScale = '1 1 0'; -} - -void Nexposee_focusEnter(entity me) -{ - if(me.animationState == 2) - SUPER(Nexposee).setFocus(me, me.selectedChild); -} - -void Nexposee_pullNexposee(entity me, entity other, vector theAlign) -{ - other.Nexposee_align = theAlign; -} #endif diff --git a/qcsrc/menu/item/radiobutton.qc b/qcsrc/menu/item/radiobutton.qc index 4dfadd2d56..dac17b1415 100644 --- a/qcsrc/menu/item/radiobutton.qc +++ b/qcsrc/menu/item/radiobutton.qc @@ -1,39 +1,37 @@ #ifndef ITEM_RADIOBUTTON_H -#define ITEM_RADIOBUTTON_H -#include "checkbox.qc" -void RadioButton_Click(entity me, entity other); -CLASS(RadioButton, CheckBox) - METHOD(RadioButton, configureRadioButton, void(entity, string, float, string, float, float)); - ATTRIB(RadioButton, checked, float, 0) - ATTRIB(RadioButton, group, float, 0) - ATTRIB(RadioButton, allowDeselect, float, 0) - ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click) -ENDCLASS(RadioButton) + #define ITEM_RADIOBUTTON_H + #include "checkbox.qc" + void RadioButton_Click(entity me, entity other); + CLASS(RadioButton, CheckBox) + METHOD(RadioButton, configureRadioButton, void(entity, string, float, string, float, float)); + ATTRIB(RadioButton, checked, float, 0) + ATTRIB(RadioButton, group, float, 0) + ATTRIB(RadioButton, allowDeselect, float, 0) + ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click) + ENDCLASS(RadioButton) #endif #ifdef IMPLEMENTATION -void RadioButton_configureRadioButton(entity me, string txt, float sz, string gfx, float theGroup, float doAllowDeselect) -{ - me.configureCheckBox(me, txt, sz, gfx); - me.align = 0; - me.group = theGroup; - me.allowDeselect = doAllowDeselect; -} -void RadioButton_Click(entity me, entity other) -{ - if(me.checked) + void RadioButton_configureRadioButton(entity me, string txt, float sz, string gfx, float theGroup, float doAllowDeselect) { - if(me.allowDeselect) - me.setChecked(me, 0); + me.configureCheckBox(me, txt, sz, gfx); + me.align = 0; + me.group = theGroup; + me.allowDeselect = doAllowDeselect; } - else + void RadioButton_Click(entity me, entity other) { - entity e; - for(e = me.parent.firstChild; e; e = e.nextSibling) - if(e != me) - if(e.group == me.group) - e.setChecked(e, 0); - me.setChecked(me, 1); + if (me.checked) + { + if (me.allowDeselect) me.setChecked(me, 0); + } + else + { + entity e; + for (e = me.parent.firstChild; e; e = e.nextSibling) + if (e != me) + if (e.group == me.group) e.setChecked(e, 0); + me.setChecked(me, 1); + } } -} #endif diff --git a/qcsrc/menu/item/slider.qc b/qcsrc/menu/item/slider.qc index 3bde6565a6..f065e34b18 100644 --- a/qcsrc/menu/item/slider.qc +++ b/qcsrc/menu/item/slider.qc @@ -1,322 +1,299 @@ // Note: // to use this, you FIRST call configureSliderVisuals, then configureSliderValues #ifndef ITEM_SLIDER_H -#define ITEM_SLIDER_H -#include "label.qc" -CLASS(Slider, Label) - METHOD(Slider, resizeNotify, void(entity, vector, vector, vector, vector)); - METHOD(Slider, configureSliderVisuals, void(entity, float, float, float, string)); - METHOD(Slider, configureSliderValues, void(entity, float, float, float, float, float, float)); - METHOD(Slider, draw, void(entity)); - METHOD(Slider, keyDown, float(entity, float, float, float)); - METHOD(Slider, keyUp, float(entity, float, float, float)); - METHOD(Slider, mousePress, float(entity, vector)); - METHOD(Slider, mouseDrag, float(entity, vector)); - METHOD(Slider, mouseRelease, float(entity, vector)); - METHOD(Slider, valueToText, string(entity, float)); - METHOD(Slider, toString, string(entity)); - METHOD(Slider, setValue_allowAnim, void(entity, float, bool)); - METHOD(Slider, setValue_noAnim, void(entity, float)); - METHOD(Slider, setValue, void(entity, float)); - METHOD(Slider, setSliderValue, void(entity, float)); - METHOD(Slider, showNotify, void(entity)); - ATTRIB(Slider, src, string, string_null) - ATTRIB(Slider, focusable, float, 1) - ATTRIB(Slider, allowFocusSound, float, 1) - ATTRIB(Slider, value, float, 0) - ATTRIB(Slider, animated, float, 1) - ATTRIB(Slider, sliderValue, float, 0) - ATTRIB(Slider, sliderAnim, entity, NULL) - ATTRIB(Slider, valueMin, float, 0) - ATTRIB(Slider, valueMax, float, 0) - ATTRIB(Slider, valueStep, float, 0) - ATTRIB(Slider, valueDigits, float, 0) - ATTRIB(Slider, valueKeyStep, float, 0) - ATTRIB(Slider, valuePageStep, float, 0) - ATTRIB(Slider, valueDisplayMultiplier, float, 1.0) - ATTRIB(Slider, textSpace, float, 0) - ATTRIB(Slider, controlWidth, float, 0) - ATTRIB(Slider, pressed, float, 0) - ATTRIB(Slider, pressOffset, float, 0) - ATTRIB(Slider, previousValue, float, 0) - ATTRIB(Slider, tolerance, vector, '0 0 0') - ATTRIB(Slider, disabled, float, 0) - ATTRIB(Slider, color, vector, '1 1 1') - ATTRIB(Slider, color2, vector, '1 1 1') - ATTRIB(Slider, colorD, vector, '1 1 1') - ATTRIB(Slider, colorC, vector, '1 1 1') - ATTRIB(Slider, colorF, vector, '1 1 1') - ATTRIB(Slider, disabledAlpha, float, 0.3) -ENDCLASS(Slider) + #define ITEM_SLIDER_H + #include "label.qc" + CLASS(Slider, Label) + METHOD(Slider, resizeNotify, void(entity, vector, vector, vector, vector)); + METHOD(Slider, configureSliderVisuals, void(entity, float, float, float, string)); + METHOD(Slider, configureSliderValues, void(entity, float, float, float, float, float, float)); + METHOD(Slider, draw, void(entity)); + METHOD(Slider, keyDown, float(entity, float, float, float)); + METHOD(Slider, keyUp, float(entity, float, float, float)); + METHOD(Slider, mousePress, float(entity, vector)); + METHOD(Slider, mouseDrag, float(entity, vector)); + METHOD(Slider, mouseRelease, float(entity, vector)); + METHOD(Slider, valueToText, string(entity, float)); + METHOD(Slider, toString, string(entity)); + METHOD(Slider, setValue_allowAnim, void(entity, float, bool)); + METHOD(Slider, setValue_noAnim, void(entity, float)); + METHOD(Slider, setValue, void(entity, float)); + METHOD(Slider, setSliderValue, void(entity, float)); + METHOD(Slider, showNotify, void(entity)); + ATTRIB(Slider, src, string, string_null) + ATTRIB(Slider, focusable, float, 1) + ATTRIB(Slider, allowFocusSound, float, 1) + ATTRIB(Slider, value, float, 0) + ATTRIB(Slider, animated, float, 1) + ATTRIB(Slider, sliderValue, float, 0) + ATTRIB(Slider, sliderAnim, entity, NULL) + ATTRIB(Slider, valueMin, float, 0) + ATTRIB(Slider, valueMax, float, 0) + ATTRIB(Slider, valueStep, float, 0) + ATTRIB(Slider, valueDigits, float, 0) + ATTRIB(Slider, valueKeyStep, float, 0) + ATTRIB(Slider, valuePageStep, float, 0) + ATTRIB(Slider, valueDisplayMultiplier, float, 1.0) + ATTRIB(Slider, textSpace, float, 0) + ATTRIB(Slider, controlWidth, float, 0) + ATTRIB(Slider, pressed, float, 0) + ATTRIB(Slider, pressOffset, float, 0) + ATTRIB(Slider, previousValue, float, 0) + ATTRIB(Slider, tolerance, vector, '0 0 0') + ATTRIB(Slider, disabled, float, 0) + ATTRIB(Slider, color, vector, '1 1 1') + ATTRIB(Slider, color2, vector, '1 1 1') + ATTRIB(Slider, colorD, vector, '1 1 1') + ATTRIB(Slider, colorC, vector, '1 1 1') + ATTRIB(Slider, colorF, vector, '1 1 1') + ATTRIB(Slider, disabledAlpha, float, 0.3) + ENDCLASS(Slider) #endif #ifdef IMPLEMENTATION -void Slider_setValue_allowAnim(entity me, float val, bool allowAnim) -{ - if(allowAnim && me.animated) { - float t = 0.5; - if(!me.sliderAnim) - me.sliderAnim = makeHostedEasing(me, Slider_setSliderValue, easingQuadOut, t, me.sliderValue, val); + void Slider_setValue_allowAnim(entity me, float val, bool allowAnim) + { + if (allowAnim && me.animated) + { + float t = 0.5; + if (!me.sliderAnim) me.sliderAnim = makeHostedEasing(me, Slider_setSliderValue, easingQuadOut, t, me.sliderValue, val); + else me.sliderAnim.update(me.sliderAnim, t, me.sliderValue, val); + } else - me.sliderAnim.update(me.sliderAnim, t, me.sliderValue, val); - } else { - me.setSliderValue(me, val); + { + me.setSliderValue(me, val); + } + me.value = val; } - me.value = val; -} -void Slider_setValue_noAnim(entity me, float val) -{ - Slider_setValue_allowAnim(me, val, false); -} -void Slider_setValue(entity me, float val) -{ - Slider_setValue_allowAnim(me, val, true); -} -void Slider_setSliderValue(entity me, float val) -{ - me.sliderValue = val; -} -string Slider_toString(entity me) -{ - return sprintf("%d (%s)", me.value, me.valueToText(me, me.value)); -} -void Slider_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) -{ - SUPER(Slider).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.controlWidth = absSize.x == 0 ? 0 : (absSize.y / absSize.x); -} -string Slider_valueToText(entity me, float val) -{ - if(almost_in_bounds(me.valueMin, val, me.valueMax)) - return ftos_decimals(val * me.valueDisplayMultiplier, me.valueDigits); - return ""; -} -void Slider_configureSliderVisuals(entity me, float sz, float theAlign, float theTextSpace, string gfx) -{ - SUPER(Slider).configureLabel(me, string_null, sz, theAlign); - me.textSpace = theTextSpace; - me.keepspaceLeft = (theTextSpace == 0) ? 0 : (1 - theTextSpace); - me.src = gfx; -} -void Slider_configureSliderValues(entity me, float theValueMin, float theValue, float theValueMax, float theValueStep, float theValueKeyStep, float theValuePageStep) -{ - me.value = theValue; - me.sliderValue = theValue; - me.valueStep = theValueStep; - me.valueMin = theValueMin; - me.valueMax = theValueMax; - me.valueKeyStep = theValueKeyStep; - me.valuePageStep = theValuePageStep; - me.valueDigits = 3; - if(fabs(floor(me.valueStep * 100 + 0.5) - (me.valueStep * 100)) < 0.01) // about a whole number of 100ths - me.valueDigits = 2; - if(fabs(floor(me.valueStep * 10 + 0.5) - (me.valueStep * 10)) < 0.01) // about a whole number of 10ths - me.valueDigits = 1; - if(fabs(floor(me.valueStep * 1 + 0.5) - (me.valueStep * 1)) < 0.01) // about a whole number - me.valueDigits = 0; -} -float Slider_keyDown(entity me, float key, float ascii, float shift) -{ - float inRange; - if(me.disabled) - return 0; - inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax)); - if(key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_MWHEELDOWN) + void Slider_setValue_noAnim(entity me, float val) { - if(inRange) - me.setValue(me, median(me.valueMin, me.value - me.valueKeyStep, me.valueMax)); - else - me.setValue(me, me.valueMax); - return 1; + Slider_setValue_allowAnim(me, val, false); } - if(key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_MWHEELUP) + void Slider_setValue(entity me, float val) { - if(inRange) - me.setValue(me, median(me.valueMin, me.value + me.valueKeyStep, me.valueMax)); - else - me.setValue(me, me.valueMin); - return 1; + Slider_setValue_allowAnim(me, val, true); } - if(key == K_PGDN || key == K_KP_PGDN) + void Slider_setSliderValue(entity me, float val) { - if(inRange) - me.setValue(me, median(me.valueMin, me.value - me.valuePageStep, me.valueMax)); - else - me.setValue(me, me.valueMax); - return 1; + me.sliderValue = val; } - if(key == K_PGUP || key == K_KP_PGUP) + string Slider_toString(entity me) { - if(inRange) - me.setValue(me, median(me.valueMin, me.value + me.valuePageStep, me.valueMax)); - else - me.setValue(me, me.valueMin); - return 1; + return sprintf("%d (%s)", me.value, me.valueToText(me, me.value)); } - if(key == K_HOME || key == K_KP_HOME) + void Slider_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { - me.setValue(me, me.valueMin); - return 1; + SUPER(Slider).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); + me.controlWidth = absSize.x == 0 ? 0 : (absSize.y / absSize.x); } - if(key == K_END || key == K_KP_END) + string Slider_valueToText(entity me, float val) { - me.setValue(me, me.valueMax); - return 1; + if (almost_in_bounds(me.valueMin, val, me.valueMax)) return ftos_decimals(val * me.valueDisplayMultiplier, me.valueDigits); + return ""; } - // TODO more keys (NOTE also add them to Slider_keyUp) - return 0; -} -float Slider_keyUp(entity me, float key, float ascii, float shift) -{ - if(me.disabled) - return 0; - switch(key) + void Slider_configureSliderVisuals(entity me, float sz, float theAlign, float theTextSpace, string gfx) { - case K_LEFTARROW: - case K_KP_LEFTARROW: - case K_RIGHTARROW: - case K_KP_RIGHTARROW: - case K_PGUP: - case K_KP_PGUP: - case K_PGDN: - case K_KP_PGDN: - case K_HOME: - case K_KP_HOME: - case K_END: - case K_KP_END: - m_play_click_sound(MENU_SOUND_SLIDE); + SUPER(Slider).configureLabel(me, string_null, sz, theAlign); + me.textSpace = theTextSpace; + me.keepspaceLeft = (theTextSpace == 0) ? 0 : (1 - theTextSpace); + me.src = gfx; } - return 0; -} -float Slider_mouseDrag(entity me, vector pos) -{ - float hit; - float v; - if(me.disabled) - return 0; - - if(me.pressed) + void Slider_configureSliderValues(entity me, float theValueMin, float theValue, float theValueMax, float theValueStep, float theValueKeyStep, float theValuePageStep) { - hit = 1; - if(pos.x < 0 - me.tolerance.x) hit = 0; - if(pos.y < 0 - me.tolerance.y) hit = 0; - if(pos.x >= 1 - me.textSpace + me.tolerance.x) hit = 0; - if(pos.y >= 1 + me.tolerance.y) hit = 0; - if(hit) + me.value = theValue; + me.sliderValue = theValue; + me.valueStep = theValueStep; + me.valueMin = theValueMin; + me.valueMax = theValueMax; + me.valueKeyStep = theValueKeyStep; + me.valuePageStep = theValuePageStep; + me.valueDigits = 3; + if (fabs(floor(me.valueStep * 100 + 0.5) - (me.valueStep * 100)) < 0.01) // about a whole number of 100ths + me.valueDigits = 2; + if (fabs(floor(me.valueStep * 10 + 0.5) - (me.valueStep * 10)) < 0.01) // about a whole number of 10ths + me.valueDigits = 1; + if (fabs(floor(me.valueStep * 1 + 0.5) - (me.valueStep * 1)) < 0.01) // about a whole number + me.valueDigits = 0; + } + float Slider_keyDown(entity me, float key, float ascii, float shift) + { + float inRange; + if (me.disabled) return 0; + inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax)); + if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_MWHEELDOWN) { - // handle dragging - me.pressed = 2; - - v = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin; - if(me.valueStep) - v = floor(0.5 + v / me.valueStep) * me.valueStep; - me.setValue_noAnim(me, v); + if (inRange) me.setValue(me, median(me.valueMin, me.value - me.valueKeyStep, me.valueMax)); + else me.setValue(me, me.valueMax); + return 1; } - else - me.setValue(me, me.previousValue); - } - - return 1; -} -float Slider_mousePress(entity me, vector pos) -{ - float controlCenter; - if(me.disabled) + if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_MWHEELUP) + { + if (inRange) me.setValue(me, median(me.valueMin, me.value + me.valueKeyStep, me.valueMax)); + else me.setValue(me, me.valueMin); + return 1; + } + if (key == K_PGDN || key == K_KP_PGDN) + { + if (inRange) me.setValue(me, median(me.valueMin, me.value - me.valuePageStep, me.valueMax)); + else me.setValue(me, me.valueMax); + return 1; + } + if (key == K_PGUP || key == K_KP_PGUP) + { + if (inRange) me.setValue(me, median(me.valueMin, me.value + me.valuePageStep, me.valueMax)); + else me.setValue(me, me.valueMin); + return 1; + } + if (key == K_HOME || key == K_KP_HOME) + { + me.setValue(me, me.valueMin); + return 1; + } + if (key == K_END || key == K_KP_END) + { + me.setValue(me, me.valueMax); + return 1; + } + // TODO more keys (NOTE also add them to Slider_keyUp) return 0; - if(pos.x < 0) return 0; - if(pos.y < 0) return 0; - if(pos.x >= 1 - me.textSpace) return 0; - if(pos.y >= 1) return 0; - controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth; - if(fabs(pos.x - controlCenter) <= 0.5 * me.controlWidth) - { - me.pressed = 1; - me.pressOffset = pos.x - controlCenter; - me.previousValue = me.value; - //me.mouseDrag(me, pos); } - else + float Slider_keyUp(entity me, float key, float ascii, float shift) { - float clickValue, pageValue, inRange; - clickValue = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin; - inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax)); - if(pos.x < controlCenter) + if (me.disabled) return 0; + switch (key) { - pageValue = me.value - me.valuePageStep; - if(me.valueStep) - clickValue = floor(clickValue / me.valueStep) * me.valueStep; - pageValue = max(pageValue, clickValue); - if(inRange) - me.setValue(me, median(me.valueMin, pageValue, me.valueMax)); - else - me.setValue(me, me.valueMax); + case K_LEFTARROW: + case K_KP_LEFTARROW: + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + case K_PGUP: + case K_KP_PGUP: + case K_PGDN: + case K_KP_PGDN: + case K_HOME: + case K_KP_HOME: + case K_END: + case K_KP_END: + m_play_click_sound(MENU_SOUND_SLIDE); } - else + return 0; + } + float Slider_mouseDrag(entity me, vector pos) + { + float hit; + float v; + if (me.disabled) return 0; + + if (me.pressed) { - pageValue = me.value + me.valuePageStep; - if(me.valueStep) - clickValue = ceil(clickValue / me.valueStep) * me.valueStep; - pageValue = min(pageValue, clickValue); - if(inRange) - me.setValue(me, median(me.valueMin, pageValue, me.valueMax)); + hit = 1; + if (pos.x < 0 - me.tolerance.x) hit = 0; + if (pos.y < 0 - me.tolerance.y) hit = 0; + if (pos.x >= 1 - me.textSpace + me.tolerance.x) hit = 0; + if (pos.y >= 1 + me.tolerance.y) hit = 0; + if (hit) + { + // handle dragging + me.pressed = 2; + + v = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin; + if (me.valueStep) v = floor(0.5 + v / me.valueStep) * me.valueStep; + me.setValue_noAnim(me, v); + } else - me.setValue(me, me.valueMax); + { + me.setValue(me, me.previousValue); + } } - if(pageValue == clickValue) + + return 1; + } + float Slider_mousePress(entity me, vector pos) + { + float controlCenter; + if (me.disabled) return 0; + if (pos.x < 0) return 0; + if (pos.y < 0) return 0; + if (pos.x >= 1 - me.textSpace) return 0; + if (pos.y >= 1) return 0; + controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth; + if (fabs(pos.x - controlCenter) <= 0.5 * me.controlWidth) { - controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth; me.pressed = 1; me.pressOffset = pos.x - controlCenter; me.previousValue = me.value; - //me.mouseDrag(me, pos); + // me.mouseDrag(me, pos); } + else + { + float clickValue, pageValue, inRange; + clickValue = median(0, (pos.x - me.pressOffset - 0.5 * me.controlWidth) / (1 - me.textSpace - me.controlWidth), 1) * (me.valueMax - me.valueMin) + me.valueMin; + inRange = (almost_in_bounds(me.valueMin, me.value, me.valueMax)); + if (pos.x < controlCenter) + { + pageValue = me.value - me.valuePageStep; + if (me.valueStep) clickValue = floor(clickValue / me.valueStep) * me.valueStep; + pageValue = max(pageValue, clickValue); + if (inRange) me.setValue(me, median(me.valueMin, pageValue, me.valueMax)); + else me.setValue(me, me.valueMax); + } + else + { + pageValue = me.value + me.valuePageStep; + if (me.valueStep) clickValue = ceil(clickValue / me.valueStep) * me.valueStep; + pageValue = min(pageValue, clickValue); + if (inRange) me.setValue(me, median(me.valueMin, pageValue, me.valueMax)); + else me.setValue(me, me.valueMax); + } + if (pageValue == clickValue) + { + controlCenter = (me.value - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth) + 0.5 * me.controlWidth; + me.pressed = 1; + me.pressOffset = pos.x - controlCenter; + me.previousValue = me.value; + // me.mouseDrag(me, pos); + } + } + return 1; } - return 1; -} -float Slider_mouseRelease(entity me, vector pos) -{ - me.pressed = 0; - if(me.disabled) - return 0; - m_play_click_sound(MENU_SOUND_SLIDE); - return 1; -} -void Slider_showNotify(entity me) -{ - me.focusable = !me.disabled; -} -void Slider_draw(entity me) -{ - float controlLeft; - float save; - me.focusable = !me.disabled; - save = draw_alpha; - if(me.disabled) - draw_alpha *= me.disabledAlpha; - draw_ButtonPicture('0 0 0', strcat(me.src, "_s"), eX * (1 - me.textSpace) + eY, me.color2, 1); - if(almost_in_bounds(me.valueMin, me.sliderValue, me.valueMax)) + float Slider_mouseRelease(entity me, vector pos) { - controlLeft = (me.sliderValue - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth); - if(me.disabled) - draw_Picture(eX * controlLeft, strcat(me.src, "_d"), eX * me.controlWidth + eY, me.colorD, 1); - else if(me.pressed) - draw_Picture(eX * controlLeft, strcat(me.src, "_c"), eX * me.controlWidth + eY, me.colorC, 1); - else if(me.focused) - draw_Picture(eX * controlLeft, strcat(me.src, "_f"), eX * me.controlWidth + eY, me.colorF, 1); - else - draw_Picture(eX * controlLeft, strcat(me.src, "_n"), eX * me.controlWidth + eY, me.color, 1); + me.pressed = 0; + if (me.disabled) return 0; + m_play_click_sound(MENU_SOUND_SLIDE); + return 1; } - - if(me.sliderAnim) - if(me.sliderAnim.isFinished(me.sliderAnim)) + void Slider_showNotify(entity me) { - anim.removeObjAnim(anim, me); - me.sliderAnim = NULL; + me.focusable = !me.disabled; } + void Slider_draw(entity me) + { + float controlLeft; + float save; + me.focusable = !me.disabled; + save = draw_alpha; + if (me.disabled) draw_alpha *= me.disabledAlpha; + draw_ButtonPicture('0 0 0', strcat(me.src, "_s"), eX * (1 - me.textSpace) + eY, me.color2, 1); + if (almost_in_bounds(me.valueMin, me.sliderValue, me.valueMax)) + { + controlLeft = (me.sliderValue - me.valueMin) / (me.valueMax - me.valueMin) * (1 - me.textSpace - me.controlWidth); + if (me.disabled) draw_Picture(eX * controlLeft, strcat(me.src, "_d"), eX * me.controlWidth + eY, me.colorD, 1); + else if (me.pressed) draw_Picture(eX * controlLeft, strcat(me.src, "_c"), eX * me.controlWidth + eY, me.colorC, 1); + else if (me.focused) draw_Picture(eX * controlLeft, strcat(me.src, "_f"), eX * me.controlWidth + eY, me.colorF, 1); + else draw_Picture(eX * controlLeft, strcat(me.src, "_n"), eX * me.controlWidth + eY, me.color, 1); + } - me.setText(me, me.valueToText(me, me.value)); - draw_alpha = save; - SUPER(Slider).draw(me); - me.text = string_null; // TEMPSTRING! -} + if (me.sliderAnim) + if (me.sliderAnim.isFinished(me.sliderAnim)) + { + anim.removeObjAnim(anim, me); + me.sliderAnim = NULL; + } + + me.setText(me, me.valueToText(me, me.value)); + draw_alpha = save; + SUPER(Slider).draw(me); + me.text = string_null; // TEMPSTRING! + } #endif diff --git a/qcsrc/menu/item/tab.qc b/qcsrc/menu/item/tab.qc index fbbf776687..7564f02cf8 100644 --- a/qcsrc/menu/item/tab.qc +++ b/qcsrc/menu/item/tab.qc @@ -1,29 +1,29 @@ #ifndef ITEM_TAB_H -#define ITEM_TAB_H -#include "dialog.qc" -CLASS(Tab, Dialog) - ATTRIB(Tab, isTabRoot, float, 0) - ATTRIB(Tab, closable, float, 0) - ATTRIB(Tab, rootDialog, float, 0) - ATTRIB(Tab, title, string, string_null) - ATTRIB(Tab, titleFontSize, float, 0) // pixels + #define ITEM_TAB_H + #include "dialog.qc" + CLASS(Tab, Dialog) + ATTRIB(Tab, isTabRoot, float, 0) + ATTRIB(Tab, closable, float, 0) + ATTRIB(Tab, rootDialog, float, 0) + ATTRIB(Tab, title, string, string_null) + ATTRIB(Tab, titleFontSize, float, 0) // pixels - // still to be customized - ATTRIB(Tab, intendedWidth, float, 0) - ATTRIB(Tab, rows, float, 3) - ATTRIB(Tab, columns, float, 2) + // still to be customized + ATTRIB(Tab, intendedWidth, float, 0) + ATTRIB(Tab, rows, float, 3) + ATTRIB(Tab, columns, float, 2) - ATTRIB(Tab, marginTop, float, 0) // pixels - ATTRIB(Tab, marginBottom, float, 0) // pixels - ATTRIB(Tab, marginLeft, float, 0) // pixels - ATTRIB(Tab, marginRight, float, 0) // pixels - ATTRIB(Tab, columnSpacing, float, 0) // pixels - ATTRIB(Tab, rowSpacing, float, 0) // pixels - ATTRIB(Tab, rowHeight, float, 0) // pixels - ATTRIB(Tab, titleHeight, float, 0) // pixels + ATTRIB(Tab, marginTop, float, 0) // pixels + ATTRIB(Tab, marginBottom, float, 0) // pixels + ATTRIB(Tab, marginLeft, float, 0) // pixels + ATTRIB(Tab, marginRight, float, 0) // pixels + ATTRIB(Tab, columnSpacing, float, 0) // pixels + ATTRIB(Tab, rowSpacing, float, 0) // pixels + ATTRIB(Tab, rowHeight, float, 0) // pixels + ATTRIB(Tab, titleHeight, float, 0) // pixels - ATTRIB(Tab, backgroundImage, string, string_null) -ENDCLASS(Tab) + ATTRIB(Tab, backgroundImage, string, string_null) + ENDCLASS(Tab) #endif #ifdef IMPLEMENTATION diff --git a/qcsrc/menu/item/textslider.qc b/qcsrc/menu/item/textslider.qc index 30b80c196d..4c23ff9a09 100644 --- a/qcsrc/menu/item/textslider.qc +++ b/qcsrc/menu/item/textslider.qc @@ -1,90 +1,86 @@ // Note: // to use this, you FIRST call configureSliderVisuals, then multiple times addValue, then configureTextSlider #ifndef ITEM_TEXTSLIDER_H -#define ITEM_TEXTSLIDER_H -#include "slider.qc" -CLASS(TextSlider, Slider) - METHOD(TextSlider, valueToText, string(entity, float)); - METHOD(TextSlider, valueToIdentifier, string(entity, float)); - METHOD(TextSlider, setValueFromIdentifier_allowAnim, void(entity, string, bool)); - METHOD(TextSlider, setValueFromIdentifier_noAnim, void(entity, string)); - METHOD(TextSlider, setValueFromIdentifier, void(entity, string)); - METHOD(TextSlider, getIdentifier, string(entity)); - METHOD(TextSlider, clearValues, void(entity)); - METHOD(TextSlider, addValue, void(entity, string, string)); - METHOD(TextSlider, insertValue, void(entity, float, string, string)); - METHOD(TextSlider, configureTextSliderValues, void(entity, string)); - ATTRIBARRAY(TextSlider, valueStrings, string, 256) - ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256) - ATTRIB(TextSlider, nValues, int, 0) -ENDCLASS(TextSlider) + #define ITEM_TEXTSLIDER_H + #include "slider.qc" + CLASS(TextSlider, Slider) + METHOD(TextSlider, valueToText, string(entity, float)); + METHOD(TextSlider, valueToIdentifier, string(entity, float)); + METHOD(TextSlider, setValueFromIdentifier_allowAnim, void(entity, string, bool)); + METHOD(TextSlider, setValueFromIdentifier_noAnim, void(entity, string)); + METHOD(TextSlider, setValueFromIdentifier, void(entity, string)); + METHOD(TextSlider, getIdentifier, string(entity)); + METHOD(TextSlider, clearValues, void(entity)); + METHOD(TextSlider, addValue, void(entity, string, string)); + METHOD(TextSlider, insertValue, void(entity, float, string, string)); + METHOD(TextSlider, configureTextSliderValues, void(entity, string)); + ATTRIBARRAY(TextSlider, valueStrings, string, 256) + ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256) + ATTRIB(TextSlider, nValues, int, 0) + ENDCLASS(TextSlider) #endif #ifdef IMPLEMENTATION -string TextSlider_valueToIdentifier(entity me, int val) -{ - if(val >= me.nValues) - return "custom"; - if(val < 0) - return "custom"; - return me.(valueIdentifiers[val]); -} -string TextSlider_valueToText(entity me, int val) -{ - if(val >= me.nValues) - return _("Custom"); - if(val < 0) - return _("Custom"); - return me.(valueStrings[val]); -} -void TextSlider_setValueFromIdentifier_allowAnim(entity me, string id, bool allowAnim) -{ - int i; - for(i = 0; i < me.nValues; ++i) - if(me.valueToIdentifier(me, i) == id) + string TextSlider_valueToIdentifier(entity me, int val) + { + if (val >= me.nValues) return "custom"; + if (val < 0) return "custom"; + return me.(valueIdentifiers[val]); + } + string TextSlider_valueToText(entity me, int val) + { + if (val >= me.nValues) return _("Custom"); + if (val < 0) return _("Custom"); + return me.(valueStrings[val]); + } + void TextSlider_setValueFromIdentifier_allowAnim(entity me, string id, bool allowAnim) + { + int i; + for (i = 0; i < me.nValues; ++i) + if (me.valueToIdentifier(me, i) == id) + { + SUPER(TextSlider).setValue_allowAnim(me, i, allowAnim); + return; + } + SUPER(TextSlider).setValue_allowAnim(me, -1, allowAnim); + } + void TextSlider_setValueFromIdentifier_noAnim(entity me, string id) + { + TextSlider_setValueFromIdentifier_allowAnim(me, id, false); + } + void TextSlider_setValueFromIdentifier(entity me, string id) + { + TextSlider_setValueFromIdentifier_allowAnim(me, id, true); + } + string TextSlider_getIdentifier(entity me) + { + return me.valueToIdentifier(me, me.value); + } + void TextSlider_clearValues(entity me) + { + me.nValues = 0; + } + void TextSlider_addValue(entity me, string theString, string theIdentifier) + { + me.(valueStrings[me.nValues]) = theString; + me.(valueIdentifiers[me.nValues]) = theIdentifier; + me.nValues += 1; + } + void TextSlider_insertValue(entity me, int pos, string theString, string theIdentifier) + { + int i; + for (i = me.nValues; i > pos; --i) { - SUPER(TextSlider).setValue_allowAnim(me, i, allowAnim); - return; + me.(valueStrings[i]) = me.(valueStrings[i - 1]); + me.(valueIdentifiers[i]) = me.(valueIdentifiers[i - 1]); } - SUPER(TextSlider).setValue_allowAnim(me, -1, allowAnim); -} -void TextSlider_setValueFromIdentifier_noAnim(entity me, string id) -{ - TextSlider_setValueFromIdentifier_allowAnim(me, id, false); -} -void TextSlider_setValueFromIdentifier(entity me, string id) -{ - TextSlider_setValueFromIdentifier_allowAnim(me, id, true); -} -string TextSlider_getIdentifier(entity me) -{ - return me.valueToIdentifier(me, me.value); -} -void TextSlider_clearValues(entity me) -{ - me.nValues = 0; -} -void TextSlider_addValue(entity me, string theString, string theIdentifier) -{ - me.(valueStrings[me.nValues]) = theString; - me.(valueIdentifiers[me.nValues]) = theIdentifier; - me.nValues += 1; -} -void TextSlider_insertValue(entity me, int pos, string theString, string theIdentifier) -{ - int i; - for (i = me.nValues; i > pos; --i) + me.(valueStrings[pos]) = theString; + me.(valueIdentifiers[pos]) = theIdentifier; + me.nValues += 1; + } + void TextSlider_configureTextSliderValues(entity me, string theDefault) { - me.(valueStrings[i]) = me.(valueStrings[i-1]); - me.(valueIdentifiers[i]) = me.(valueIdentifiers[i-1]); + me.configureSliderValues(me, 0, 0, me.nValues - 1, 1, 1, 1); + me.setValueFromIdentifier_noAnim(me, theDefault); } - me.(valueStrings[pos]) = theString; - me.(valueIdentifiers[pos]) = theIdentifier; - me.nValues += 1; -} -void TextSlider_configureTextSliderValues(entity me, string theDefault) -{ - me.configureSliderValues(me, 0, 0, me.nValues - 1, 1, 1, 1); - me.setValueFromIdentifier_noAnim(me, theDefault); -} #endif diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 13d8fcd49e..899de0ec97 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -1,5 +1,5 @@ #include "menu.qh" -#include "oo/classes.qc" +#include "classes.qc" #include "xonotic/util.qh" #include "../common/items/all.qh" @@ -7,31 +7,25 @@ #include "../common/mapinfo.qh" #include "../common/mutators/base.qh" -/////////////////////////////////////////////// -// Menu Source File -/////////////////////// -// This file belongs to dpmod/darkplaces -// AK contains all menu functions (especially the required ones) -/////////////////////////////////////////////// - -float mouseButtonsPressed; +int mouseButtonsPressed; vector menuMousePos; int menuShiftState; float menuPrevTime; float menuAlpha; float menuLogoAlpha; float prevMenuAlpha; -float menuInitialized; -float menuNotTheFirstFrame; -float menuMouseMode; +bool menuInitialized; +bool menuNotTheFirstFrame; +int menuMouseMode; -float conwidth_s, conheight_s, vidwidth_s, vidheight_s, vidpixelheight_s, - realconwidth, realconheight; +float conwidth_s, conheight_s; +float vidwidth_s, vidheight_s, vidpixelheight_s; +float realconwidth, realconheight; void m_sync() { updateCompression(); - vidwidth_s = vidheight_s = vidpixelheight_s = 0; // Force updateConwidths on next draw. + vidwidth_s = vidheight_s = vidpixelheight_s = 0; // Force updateConwidths on next draw loadAllCvars(main); } @@ -39,43 +33,37 @@ void m_sync() void m_gamestatus() { gamestatus = 0; - if(isserver()) - gamestatus = gamestatus | GAME_ISSERVER; - if(clientstate() == CS_CONNECTED || isdemo()) - gamestatus = gamestatus | GAME_CONNECTED; - if(cvar("developer")) - gamestatus = gamestatus | GAME_DEVELOPER; + if (isserver()) gamestatus |= GAME_ISSERVER; + if (clientstate() == CS_CONNECTED || isdemo()) gamestatus |= GAME_CONNECTED; + if (cvar("developer")) gamestatus |= GAME_DEVELOPER; } void m_init() { - float restarting = 0; + bool restarting = false; cvar_set("_menu_alpha", "0"); prvm_language = cvar_string("prvm_language"); - if(prvm_language == "") + if (prvm_language == "") { prvm_language = "en"; cvar_set("prvm_language", prvm_language); localcmd("\nmenu_restart\n"); - restarting = 1; + restarting = true; } prvm_language = strzone(prvm_language); cvar_set("_menu_prvm_language", prvm_language); #ifdef WATERMARK - LOG_TRACEF("^4MQC Build information: ^1%s\n", WATERMARK); + LOG_INFOF("^4MQC Build information: ^1%s\n", WATERMARK); #endif // list all game dirs (TEST) - if(cvar("developer")) + if (cvar("developer")) { - float i; - string s; - for(i = 0; ; ++i) + for (int i = 0; ; ++i) { - s = getgamedirinfo(i, GETGAMEDIRINFO_NAME); - if (!s) - break; + string s = getgamedirinfo(i, GETGAMEDIRINFO_NAME); + if (!s) break; LOG_TRACE(s, ": ", getgamedirinfo(i, GETGAMEDIRINFO_DESCRIPTION)); } } @@ -83,27 +71,25 @@ void m_init() // needs to be done so early because of the constants they create static_init(); static_init_late(); + static_init_precache(); RegisterSLCategories(); float ddsload = cvar("r_texture_dds_load"); float texcomp = cvar("gl_texturecompression"); updateCompression(); - if(ddsload != cvar("r_texture_dds_load") || texcomp != cvar("gl_texturecompression")) - localcmd("\nr_restart\n"); + if (ddsload != cvar("r_texture_dds_load") || texcomp != cvar("gl_texturecompression")) localcmd("\nr_restart\n"); - if(!restarting) + if (!restarting) { - if(cvar("_menu_initialized")) // always show menu after menu_restart + if (cvar("_menu_initialized")) // always show menu after menu_restart m_display(); - else - m_hide(); + else m_hide(); cvar_set("_menu_initialized", "1"); } - } -const float MENU_ASPECT = 1.25; // 1280x1024 +const float MENU_ASPECT = 1280 / 1024; void draw_reset_cropped() { @@ -118,8 +104,7 @@ void UpdateConWidthHeight(float w, float h, float p) { if (w != vidwidth_s || h != vidheight_s || p != vidpixelheight_s) { - if (updateConwidths(w, h, p)) - localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg"))); + if (updateConwidths(w, h, p)) localcmd(sprintf("\nexec %s\n", cvar_string("menu_font_cfg"))); vidwidth_s = w; vidheight_s = h; vidpixelheight_s = p; @@ -128,7 +113,7 @@ void UpdateConWidthHeight(float w, float h, float p) conheight_s = conheight; realconwidth = cvar("vid_conwidth"); realconheight = cvar("vid_conheight"); - if(realconwidth / realconheight > MENU_ASPECT) + if (realconwidth / realconheight > MENU_ASPECT) { // widescreen conwidth = realconheight * MENU_ASPECT; @@ -140,9 +125,9 @@ void UpdateConWidthHeight(float w, float h, float p) conwidth = realconwidth; conheight = realconwidth / MENU_ASPECT; } - if(main) + if (main) { - if(conwidth_s != conwidth || conheight_s != conheight) + if (conwidth_s != conwidth || conheight_s != conheight) { draw_reset_cropped(); main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight); @@ -150,64 +135,53 @@ void UpdateConWidthHeight(float w, float h, float p) } else { - vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame + vidwidth_s = vidheight_s = vidpixelheight_s = 0; // retry next frame } } string m_goto_buffer; void m_init_delayed() { - float fh, glob, n, i; - string s; - draw_reset_cropped(); - menuInitialized = 0; - if(!preMenuInit()) - return; - menuInitialized = 1; + menuInitialized = false; + if (!preMenuInit()) return; + menuInitialized = true; - fh = -1; - if(cvar_string("menu_skin") != "") + int fh = -1; + if (cvar_string("menu_skin") != "") { draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin")); fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ); } - if(fh < 0) - if(cvar_defstring("menu_skin") != "") + if (fh < 0 && cvar_defstring("menu_skin") != "") { cvar_set("menu_skin", cvar_defstring("menu_skin")); draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin")); fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ); } - if(fh < 0) + if (fh < 0) { draw_currentSkin = "gfx/menu/default"; fh = fopen(language_filename(strcat(draw_currentSkin, "/skinvalues.txt")), FILE_READ); } - if(fh < 0) - { - error("cannot load any menu skin\n"); - } + if (fh < 0) error("cannot load any menu skin\n"); draw_currentSkin = strzone(draw_currentSkin); - while((s = fgets(fh))) + for (string s; (s = fgets(fh)); ) { // these two are handled by skinlist.qc - if(substring(s, 0, 6) == "title ") - continue; - if(substring(s, 0, 7) == "author ") - continue; - n = tokenize_console(s); - if(n >= 2) - Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); + if (substring(s, 0, 6) == "title ") continue; + if (substring(s, 0, 7) == "author ") continue; + int n = tokenize_console(s); + if (n < 2) continue; + Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); } fclose(fh); - glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true); - if(glob >= 0) + int glob = search_begin(strcat(draw_currentSkin, "/*.tga"), true, true); + if (glob >= 0) { - n = search_getsize(glob); - for(i = 0; i < n; ++i) + for (int i = 0, n = search_getsize(glob); i < n; ++i) precache_pic(search_getfilename(glob, i)); search_end(glob); } @@ -215,117 +189,110 @@ void m_init_delayed() draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR); anim = NEW(AnimHost); - main = NEW(MainWindow); main.configureMainWindow(main); + main = NEW(MainWindow); + main.configureMainWindow(main); main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight); - main.focused = 1; + main.focused = true; menuShiftState = 0; menuMousePos = '0.5 0.5 0'; m_sync(); - if(m_goto_buffer) + if (m_goto_buffer) { m_goto(m_goto_buffer); strunzone(m_goto_buffer); m_goto_buffer = string_null; } - if(Menu_Active) - m_display(); // delayed menu display + if (Menu_Active) m_display(); // delayed menu display } -void m_keyup (float key, float ascii) +void m_keyup(float key, float ascii) { - if(!menuInitialized) - return; - if(!Menu_Active) - return; + if (!menuInitialized) return; + if (!Menu_Active) return; draw_reset_cropped(); main.keyUp(main, key, ascii, menuShiftState); - if(key >= K_MOUSE1 && key <= K_MOUSE3) + if (key >= K_MOUSE1 && key <= K_MOUSE3) { --mouseButtonsPressed; - if(!mouseButtonsPressed) - main.mouseRelease(main, menuMousePos); - if(mouseButtonsPressed < 0) + if (!mouseButtonsPressed) main.mouseRelease(main, menuMousePos); + if (mouseButtonsPressed < 0) { mouseButtonsPressed = 0; LOG_TRACE("Warning: released an already released button\n"); } } - if(key == K_ALT) menuShiftState -= (menuShiftState & S_ALT); - if(key == K_CTRL) menuShiftState -= (menuShiftState & S_CTRL); - if(key == K_SHIFT) menuShiftState -= (menuShiftState & S_SHIFT); + if (key == K_ALT) menuShiftState &= ~S_ALT; + if (key == K_CTRL) menuShiftState &= ~S_CTRL; + if (key == K_SHIFT) menuShiftState &= ~S_SHIFT; } void m_keydown(float key, float ascii) { - if(!menuInitialized) - return; - if(!Menu_Active) - return; + if (!menuInitialized) return; + if (!Menu_Active) return; - if(menuMouseMode) - if(key >= K_MOUSE1 && key <= K_MOUSE3) + if (menuMouseMode && key >= K_MOUSE1 && key <= K_MOUSE3) { // detect a click outside of the game window vector p = getmousepos(); - if(p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight) + if (p.x < 0 || p.x > realconwidth || p.y < 0 || p.y > realconheight) { ++mouseButtonsPressed; return; } } - if(keyGrabber) + if (keyGrabber) { - entity e; - e = keyGrabber; + entity e = keyGrabber; keyGrabber = NULL; e.keyGrabbed(e, key, ascii); } else { draw_reset_cropped(); - if(key >= K_MOUSE1 && key <= K_MOUSE3) - if(!mouseButtonsPressed) - main.mousePress(main, menuMousePos); - if(!main.keyDown(main, key, ascii, menuShiftState)) - if(key == K_ESCAPE) - if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only - m_hide(); // disable menu on unhandled ESC + if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3) main.mousePress(main, menuMousePos); + if (!main.keyDown(main, key, ascii, menuShiftState)) + { + // disable menu on unhandled ESC + if (key == K_ESCAPE) + if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only + m_hide(); + } } - if(key >= K_MOUSE1 && key <= K_MOUSE3) + if (key >= K_MOUSE1 && key <= K_MOUSE3) { ++mouseButtonsPressed; - if(mouseButtonsPressed > 10) + if (mouseButtonsPressed > 10) { mouseButtonsPressed = 10; LOG_TRACE("Warning: pressed an already pressed button\n"); } } - if(key == K_ALT) menuShiftState |= S_ALT; - if(key == K_CTRL) menuShiftState |= S_CTRL; - if(key == K_SHIFT) menuShiftState |= S_SHIFT; + if (key == K_ALT) menuShiftState |= S_ALT; + if (key == K_CTRL) menuShiftState |= S_CTRL; + if (key == K_SHIFT) menuShiftState |= S_SHIFT; } -const float SCALEMODE_CROP = 0; -const float SCALEMODE_LETTERBOX = 1; -const float SCALEMODE_WIDTH = 2; -const float SCALEMODE_HEIGHT = 3; -const float SCALEMODE_STRETCH = 4; +enum { + SCALEMODE_CROP, + SCALEMODE_LETTERBOX, + SCALEMODE_WIDTH, + SCALEMODE_HEIGHT, + SCALEMODE_STRETCH, +}; void draw_Picture_Aligned(vector algn, float scalemode, string img, float a) { - vector sz, org, isz, isz_w, isz_h; - float width_is_larger; - - sz = draw_PictureSize(img); - width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x); - isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y)); - isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x)); - - switch(scalemode) + vector sz = draw_PictureSize(img); + bool width_is_larger = (sz.x * draw_scale.y >= sz.y * draw_scale.x); + vector isz_w = '1 0 0' + '0 1 0' * ((sz.y / sz.x) * (draw_scale.x / draw_scale.y)); + vector isz_h = '0 1 0' + '1 0 0' * ((sz.x / sz.y) * (draw_scale.y / draw_scale.x)); + vector isz; + switch (scalemode) { default: case SCALEMODE_CROP: @@ -344,170 +311,174 @@ void draw_Picture_Aligned(vector algn, float scalemode, string img, float a) isz = '1 1 0'; break; } - - org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y)); + vector org = eX * (algn.x * (1 - isz.x)) + eY * (algn.y * (1 - isz.y)); draw_Picture(org, img, isz, '1 1 1', a); } void drawBackground(string img, float a, string algn, float force1) { - if(main.mainNexposee.ModalController_state == 0) - return; - - vector v; - float i, l; - string c; - float scalemode; - - v.z = 0; - - scalemode = SCALEMODE_CROP; - - l = 0; - for(i = 0; i < strlen(algn); ++i) + if (main.mainNexposee.ModalController_state == 0) return; + vector v = '0 0 0'; + int scalemode = SCALEMODE_CROP; + for (int i = 0, l = 0; i < strlen(algn); ++i) { - c = substring(algn, i, 1); - switch(c) + string c = substring(algn, i, 1); + switch (c) { - case "c": scalemode = SCALEMODE_CROP; goto nopic; - case "l": scalemode = SCALEMODE_LETTERBOX; goto nopic; - case "h": scalemode = SCALEMODE_HEIGHT; goto nopic; - case "w": scalemode = SCALEMODE_WIDTH; goto nopic; - case "s": scalemode = SCALEMODE_STRETCH; goto nopic; - case "1": case "4": case "7": v.x = 0.0; break; - case "2": case "5": case "8": v.x = 0.5; break; - case "3": case "6": case "9": v.x = 1.0; break; - default: v.x = random(); break; + case "c": + scalemode = SCALEMODE_CROP; + goto nopic; + case "l": + scalemode = SCALEMODE_LETTERBOX; + goto nopic; + case "h": + scalemode = SCALEMODE_HEIGHT; + goto nopic; + case "w": + scalemode = SCALEMODE_WIDTH; + goto nopic; + case "s": + scalemode = SCALEMODE_STRETCH; + goto nopic; + case "1": case "4": case "7": + v.x = 0.0; + break; + case "2": case "5": case "8": + v.x = 0.5; + break; + case "3": case "6": case "9": + v.x = 1.0; + break; + default: + v.x = random(); + break; } - switch(c) + switch (c) { - case "7": case "8": case "9": v.y = 0.0; break; - case "4": case "5": case "6": v.y = 0.5; break; - case "1": case "2": case "3": v.y = 1.0; break; - default: v.y = random(); break; + case "7": case "8": case "9": + v.y = 0.0; + break; + case "4": case "5": case "6": + v.y = 0.5; + break; + case "1": case "2": case "3": + v.y = 1.0; + break; + default: + v.y = random(); + break; } - if(l == 0) + if (l == 0) + { draw_Picture_Aligned(v, scalemode, img, a); - else if(force1) + } + else if (force1) + { // force all secondary layers to use alpha 1. Prevents ugly issues // with overlap. It's a flag because it cannot be used for the // ingame background - draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l+1)), 1); + draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), 1); + } else - draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l+1)), a); + { + draw_Picture_Aligned(v, scalemode, strcat(img, "_l", ftos(l + 1)), a); + } ++l; -:nopic + : nopic } } -float menu_tooltips; -float menu_tooltips_old; +int menu_tooltips; +int menu_tooltips_old; vector menuTooltipAveragedMousePos; entity menuTooltipItem; vector menuTooltipOrigin; vector menuTooltipSize; float menuTooltipAlpha; string menuTooltipText; -float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out -float m_testmousetooltipbox(vector pos) +int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out +bool m_testmousetooltipbox(vector pos) { - if(pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x) - if(pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y) - return false; - return true; + return !( + (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x) + && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y) + ); } -float m_testtooltipbox(vector tooltippos) +bool m_testtooltipbox(vector tooltippos) { - if(tooltippos.x < 0) - return false; - if(tooltippos.y < 0) - return false; - if(tooltippos.x + menuTooltipSize.x > 1) - return false; - if(tooltippos.y + menuTooltipSize.y > 1) - return false; + if (tooltippos.x < 0) return false; + if (tooltippos.y < 0) return false; + if (tooltippos.x + menuTooltipSize.x > 1) return false; + if (tooltippos.y + menuTooltipSize.y > 1) return false; menuTooltipOrigin = tooltippos; return true; } -float m_allocatetooltipbox(vector pos) +bool m_allocatetooltipbox(vector pos) { - vector avoidplus, avoidminus; - vector v; - + vector avoidplus; avoidplus.x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth; avoidplus.y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight; avoidplus.z = 0; + vector avoidminus; avoidminus.x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x * SKINSIZE_CURSOR_x) / conwidth + menuTooltipSize.x; avoidminus.y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y * SKINSIZE_CURSOR_y) / conheight + menuTooltipSize.y; avoidminus.z = 0; // bottom right - v = pos + avoidplus; - if(m_testtooltipbox(v)) - return true; + vector v = pos + avoidplus; + if (m_testtooltipbox(v)) return true; // bottom center v.x = pos.x - menuTooltipSize.x * 0.5; - if(m_testtooltipbox(v)) - return true; + if (m_testtooltipbox(v)) return true; // bottom left v.x = pos.x - avoidminus.x; - if(m_testtooltipbox(v)) - return true; + if (m_testtooltipbox(v)) return true; // top left v.y = pos.y - avoidminus.y; - if(m_testtooltipbox(v)) - return true; + if (m_testtooltipbox(v)) return true; // top center v.x = pos.x - menuTooltipSize.x * 0.5; - if(m_testtooltipbox(v)) - return true; + if (m_testtooltipbox(v)) return true; // top right v.x = pos.x + avoidplus.x; - if(m_testtooltipbox(v)) - return true; + if (m_testtooltipbox(v)) return true; return false; } entity m_findtooltipitem(entity root, vector pos) { - entity it; - entity best; - - best = NULL; - it = root; - - while(it.instanceOfContainer) + entity best = NULL; + for (entity it = root; it.instanceOfContainer; ) { - while(it.instanceOfNexposee && it.focusedChild) + while (it.instanceOfNexposee && it.focusedChild) { it = it.focusedChild; pos = globalToBox(pos, it.Container_origin, it.Container_size); } - if(it.instanceOfNexposee) + if (it.instanceOfNexposee) { it = it.itemFromPoint(it, pos); - if(it.tooltip) - best = it; - else if(menu_tooltips == 2 && (it.cvarName || it.onClickCommand)) - best = it; + if (it.tooltip) best = it; + else if (menu_tooltips == 2 && (it.cvarName || it.onClickCommand)) best = it; it = NULL; } - else if(it.instanceOfModalController) + else if (it.instanceOfModalController) + { it = it.focusedChild; + } else + { it = it.itemFromPoint(it, pos); - if(!it) - break; - if(it.tooltip) - best = it; - else if(menu_tooltips == 2 && (it.cvarName || it.onClickCommand)) - best = it; + } + if (!it) break; + if (it.tooltip) best = it; + else if (menu_tooltips == 2 && (it.cvarName || it.onClickCommand)) best = it; pos = globalToBox(pos, it.Container_origin, it.Container_size); } @@ -520,74 +491,72 @@ string gettooltip() string s; if (menuTooltipItem.cvarName) { - if (getCvarsMulti(menuTooltipItem)) - s = strcat("[", menuTooltipItem.cvarName, " ", getCvarsMulti(menuTooltipItem), "]"); - else - s = strcat("[", menuTooltipItem.cvarName, "]"); + if (getCvarsMulti(menuTooltipItem)) s = + strcat("[", menuTooltipItem.cvarName, " ", getCvarsMulti(menuTooltipItem), "]"); + else s = strcat("[", menuTooltipItem.cvarName, "]"); } else if (menuTooltipItem.onClickCommand) + { s = strcat("<", menuTooltipItem.onClickCommand, ">"); + } else + { return menuTooltipItem.tooltip; - if (menuTooltipItem.tooltip) - return strcat(menuTooltipItem.tooltip, " ", s); + } + if (menuTooltipItem.tooltip) return strcat(menuTooltipItem.tooltip, " ", s); return s; } return menuTooltipItem.tooltip; } -string prev_tooltip; void m_tooltip(vector pos) { - float f, i, w; + static string prev_tooltip; entity it; - vector fontsize, p; - string s; - menu_tooltips = cvar("menu_tooltips"); if (!menu_tooltips) { // don't return immediately, fade out the active tooltip first - if (menuTooltipItem == NULL) - return; + if (menuTooltipItem == NULL) return; it = NULL; menu_tooltips_old = menu_tooltips; } else { - f = bound(0, frametime * 2, 1); + float f = bound(0, frametime * 2, 1); menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f; f = vlen(pos - menuTooltipAveragedMousePos); - if(f < 0.01) + if (f < 0.01) { it = m_findtooltipitem(main, pos); - if(it.instanceOfListBox && it.isScrolling(it)) - it = world; + if (it.instanceOfListBox && it.isScrolling(it)) it = world; - if(it && prev_tooltip != it.tooltip) + if (it && prev_tooltip != it.tooltip) { // fade out if tooltip of a certain item has changed menuTooltipState = 3; - if(prev_tooltip) - strunzone(prev_tooltip); + if (prev_tooltip) strunzone(prev_tooltip); prev_tooltip = strzone(it.tooltip); } - else if(menuTooltipItem && !m_testmousetooltipbox(pos)) - menuTooltipState = 3; // fade out if mouse touches it - + else if (menuTooltipItem && !m_testmousetooltipbox(pos)) + { + menuTooltipState = 3; // fade out if mouse touches it + } } else + { it = NULL; + } } - fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight); + vector fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight); // float menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fading out - if(it != menuTooltipItem) + if (it != menuTooltipItem) { - switch(menuTooltipState) + switch (menuTooltipState) { case 0: - if(menuTooltipItem) + if (menuTooltipItem) { // another item: fade out first menuTooltipState = 2; @@ -598,22 +567,18 @@ void m_tooltip(vector pos) menuTooltipState = 1; menuTooltipItem = it; - menuTooltipOrigin.x = -1; // unallocated + menuTooltipOrigin.x = -1; // unallocated - if (menuTooltipText) - strunzone(menuTooltipText); + if (menuTooltipText) strunzone(menuTooltipText); menuTooltipText = strzone(gettooltip()); - i = 0; - w = 0; - getWrappedLine_remaining = menuTooltipText; - while(getWrappedLine_remaining) + int i = 0; + float w = 0; + for (getWrappedLine_remaining = menuTooltipText; getWrappedLine_remaining; ++i) { - s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors); - ++i; - f = draw_TextWidth(s, false, fontsize); - if(f > w) - w = f; + string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors); + float f = draw_TextWidth(s, false, fontsize); + if (f > w) w = f; } menuTooltipSize.x = w + 2 * (SKINMARGIN_TOOLTIP_x / conwidth); menuTooltipSize.y = i * fontsize.y + 2 * (SKINMARGIN_TOOLTIP_y / conheight); @@ -629,20 +594,21 @@ void m_tooltip(vector pos) break; } } - else if(menuTooltipState == 2) // re-fade in? + else if (menuTooltipState == 2) // re-fade in? + { menuTooltipState = 1; + } - switch(menuTooltipState) + switch (menuTooltipState) { - case 1: // fade in + case 1: // fade in menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1); - if(menuTooltipAlpha == 1) - menuTooltipState = 0; + if (menuTooltipAlpha == 1) menuTooltipState = 0; break; - case 2: // fade out - case 3: // forced fade out + case 2: // fade out + case 3: // forced fade out menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1); - if(menuTooltipAlpha == 0) + if (menuTooltipAlpha == 0) { menuTooltipState = 0; menuTooltipItem = NULL; @@ -650,7 +616,7 @@ void m_tooltip(vector pos) break; } - if(menuTooltipItem == NULL) + if (menuTooltipItem == NULL) { if (menuTooltipText) { @@ -661,31 +627,29 @@ void m_tooltip(vector pos) } else { - if(menu_tooltips != menu_tooltips_old) + if (menu_tooltips != menu_tooltips_old) { - if (menu_tooltips != 0 && menu_tooltips_old != 0) - menuTooltipItem = NULL; // reload tooltip next frame + if (menu_tooltips != 0 && menu_tooltips_old != 0) menuTooltipItem = NULL; // reload tooltip next frame menu_tooltips_old = menu_tooltips; } - else if(menuTooltipOrigin.x < 0) // unallocated? + else if (menuTooltipOrigin.x < 0) // unallocated? + { m_allocatetooltipbox(pos); - - if(menuTooltipOrigin.x >= 0) + } + if (menuTooltipOrigin.x >= 0) { // draw the tooltip! - p = SKINBORDER_TOOLTIP; + vector p = SKINBORDER_TOOLTIP; p.x *= 1 / conwidth; p.y *= 1 / conheight; draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p); p = menuTooltipOrigin; p.x += SKINMARGIN_TOOLTIP_x / conwidth; p.y += SKINMARGIN_TOOLTIP_y / conheight; - getWrappedLine_remaining = menuTooltipText; - while(getWrappedLine_remaining) + for (getWrappedLine_remaining = menuTooltipText; getWrappedLine_remaining; p.y += fontsize.y) { - s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors); + string s = getWrappedLine(SKINWIDTH_TOOLTIP, fontsize, draw_TextWidth_WithoutColors); draw_Text(p, s, fontsize, SKINCOLOR_TOOLTIP, SKINALPHA_TOOLTIP * menuTooltipAlpha, false); - p.y += fontsize.y; } } } @@ -693,94 +657,90 @@ void m_tooltip(vector pos) void m_draw(float width, float height) { - float t; - float realFrametime; - m_gamestatus(); execute_next_frame(); menuMouseMode = cvar("menu_mouse_absolute"); - if (anim) - anim.tickAll(anim); + if (anim) anim.tickAll(anim); UpdateConWidthHeight(width, height, cvar("vid_pixelheight")); - if(!menuInitialized) + if (!menuInitialized) { // TODO draw an info image about this situation m_init_delayed(); return; } - if(!menuNotTheFirstFrame) + if (!menuNotTheFirstFrame) { - menuNotTheFirstFrame = 1; - if(Menu_Active) - if(!cvar("menu_video_played")) - { - localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n"); - menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME - } + menuNotTheFirstFrame = true; + if (Menu_Active && !cvar("menu_video_played")) + { + localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n"); + menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME + } // ALWAYS set this cvar; if we start but menu is not active, this means we want no background music! localcmd("set menu_video_played 1\n"); } - t = gettime(); - realFrametime = frametime = min(0.2, t - menuPrevTime); + float t = gettime(); + float realFrametime = frametime = min(0.2, t - menuPrevTime); menuPrevTime = t; time += frametime; t = cvar("menu_slowmo"); - if(t) + if (t) { frametime *= t; realFrametime *= t; } else + { t = 1; + } - if(Menu_Active) + if (Menu_Active) { - if(getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED)) - setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU); - else - m_hide(); + if (getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) + && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED)) + setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU); + else m_hide(); } - if(cvar("cl_capturevideo")) - frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly + if (cvar("cl_capturevideo")) frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly prevMenuAlpha = menuAlpha; - if(Menu_Active) + if (Menu_Active) { - if(menuAlpha == 0 && menuLogoAlpha < 2) + if (menuAlpha == 0 && menuLogoAlpha < 2) { - menuLogoAlpha = menuLogoAlpha + frametime * 2; + menuLogoAlpha += 2 * frametime; } else { - menuAlpha = min(1, menuAlpha + frametime * 5); + menuAlpha = min(1, menuAlpha + 5 * frametime); menuLogoAlpha = 2; } } else { - menuAlpha = max(0, menuAlpha - frametime * 5); + menuAlpha = max(0, menuAlpha - 5 * frametime); menuLogoAlpha = 2; } draw_reset_cropped(); - if(!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER))) + if (!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER))) { - if(menuLogoAlpha > 0) + if (menuLogoAlpha > 0) { draw_reset_full(); draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1); drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, true); draw_reset_cropped(); - if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0) + if (menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0) { draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1); draw_drawMousePointer(menuMousePos); @@ -788,27 +748,26 @@ void m_draw(float width, float height) } } } - else if(SKINALPHA_BACKGROUND_INGAME) + else if (SKINALPHA_BACKGROUND_INGAME) { - if(menuAlpha > 0) + if (menuAlpha > 0) { draw_reset_full(); - drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, false); + drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, + SKINALIGN_BACKGROUND_INGAME, false); draw_reset_cropped(); } } - if(menuAlpha != prevMenuAlpha) - cvar_set("_menu_alpha", ftos(menuAlpha)); + if (menuAlpha != prevMenuAlpha) cvar_set("_menu_alpha", ftos(menuAlpha)); draw_reset_cropped(); preMenuDraw(); draw_reset_cropped(); - if(menuAlpha <= 0) + if (menuAlpha <= 0) { - if(prevMenuAlpha > 0) - main.initializeDialog(main, main.firstChild); + if (prevMenuAlpha > 0) main.initializeDialog(main, main.firstChild); draw_reset_cropped(); postMenuDraw(); return; @@ -816,44 +775,34 @@ void m_draw(float width, float height) draw_alpha *= menuAlpha; - if(!Menu_Active) + if (!Menu_Active) { // do not update mouse position // it prevents mouse jumping to '0 0 0' when menu is fading out } - else if(menuMouseMode) + else if (menuMouseMode) { - vector newMouse; - newMouse = globalToBox(getmousepos(), draw_shift, draw_scale); - if(newMouse != '0 0 0') - if(newMouse != menuMousePos) - { - menuMousePos = newMouse; - if(mouseButtonsPressed) - main.mouseDrag(main, menuMousePos); - else - main.mouseMove(main, menuMousePos); - } + vector newMouse = globalToBox(getmousepos(), draw_shift, draw_scale); + if (newMouse != '0 0 0' && newMouse != menuMousePos) + { + menuMousePos = newMouse; + if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos); + else main.mouseMove(main, menuMousePos); + } } - else + else if (frametime > 0) { - if(frametime > 0) + vector dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo + if (dMouse != '0 0 0') { - vector dMouse, minpos, maxpos; - dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo - if(dMouse != '0 0 0') - { - minpos = globalToBox('0 0 0', draw_shift, draw_scale); - maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale); - dMouse = globalToBoxSize(dMouse, draw_scale); - menuMousePos += dMouse * cvar("menu_mouse_speed"); - menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x); - menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y); - if(mouseButtonsPressed) - main.mouseDrag(main, menuMousePos); - else - main.mouseMove(main, menuMousePos); - } + vector minpos = globalToBox('0 0 0', draw_shift, draw_scale); + vector maxpos = globalToBox(eX * (realconwidth - 1) + eY * (realconheight - 1), draw_shift, draw_scale); + dMouse = globalToBoxSize(dMouse, draw_scale); + menuMousePos += dMouse * cvar("menu_mouse_speed"); + menuMousePos.x = bound(minpos.x, menuMousePos.x, maxpos.x); + menuMousePos.y = bound(minpos.y, menuMousePos.y, maxpos.y); + if (mouseButtonsPressed) main.mouseDrag(main, menuMousePos); + else main.mouseMove(main, menuMousePos); } } main.draw(main); @@ -876,11 +825,9 @@ void m_display() setkeydest(KEY_MENU); setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU)); - if(!menuInitialized) - return; + if (!menuInitialized) return; - if(mouseButtonsPressed) - main.mouseRelease(main, menuMousePos); + if (mouseButtonsPressed) main.mouseRelease(main, menuMousePos); mouseButtonsPressed = 0; main.focusEnter(main); @@ -893,112 +840,94 @@ void m_hide() setkeydest(KEY_GAME); setmousetarget(MT_CLIENT); - if(!menuInitialized) - return; + if (!menuInitialized) return; main.focusLeave(main); main.hideNotify(main); } -void m_toggle(float mode) +void m_toggle(int mode) { - if(Menu_Active) + if (Menu_Active) { - if (mode == 1) - return; + if (mode == 1) return; m_hide(); } else { - if (mode == 0) - return; + if (mode == 0) return; m_display(); } } void Shutdown() { - entity e; - m_hide(); - for(e = NULL; (e = nextent(e)) != NULL; ) + for (entity e = NULL; (e = nextent(e)); ) { - if(e.classname != "vtbl") - if(e.destroy) - e.destroy(e); + if (e.classname == "vtbl") continue; + if (e.destroy) e.destroy(e); } } void m_focus_item_chain(entity outermost, entity innermost) { - if(innermost.parent != outermost) - m_focus_item_chain(outermost, innermost.parent); + if (innermost.parent != outermost) m_focus_item_chain(outermost, innermost.parent); innermost.parent.setFocus(innermost.parent, innermost); } void m_activate_window(entity wnd) { - entity par; - par = wnd.parent; - if(par) - m_activate_window(par); + entity par = wnd.parent; + if (par) m_activate_window(par); - if(par.instanceOfModalController) + if (par.instanceOfModalController) { - if(wnd.tabSelectingButton) + if (wnd.tabSelectingButton) // tabs TabButton_Click(wnd.tabSelectingButton, wnd); else // root par.initializeDialog(par, wnd); } - else if(par.instanceOfNexposee) + else if (par.instanceOfNexposee) { // nexposee (sorry for violating abstraction here) par.selectedChild = wnd; par.animationState = 1; Container_setFocus(par, NULL); } - else if(par.instanceOfContainer) + else if (par.instanceOfContainer) { // other containers - if(par.focused) - par.setFocus(par, wnd); + if (par.focused) par.setFocus(par, wnd); } } void m_setpointerfocus(entity wnd) { - if(wnd.instanceOfContainer) - { - entity focus = wnd.preferredFocusedGrandChild(wnd); - if(focus) - { - menuMousePos = focus.origin + 0.5 * focus.size; - menuMousePos.x *= 1 / conwidth; - menuMousePos.y *= 1 / conheight; - entity par = wnd.parent; - if(par.focused) - par.setFocus(par, wnd); - if(wnd.focused) - m_focus_item_chain(wnd, focus); - } - } + if (!wnd.instanceOfContainer) return; + entity focus = wnd.preferredFocusedGrandChild(wnd); + if (!focus) return; + menuMousePos = focus.origin + 0.5 * focus.size; + menuMousePos.x *= 1 / conwidth; + menuMousePos.y *= 1 / conheight; + entity par = wnd.parent; + if (par.focused) par.setFocus(par, wnd); + if (wnd.focused) m_focus_item_chain(wnd, focus); } void m_goto(string itemname) { - entity e; - if(!menuInitialized) + if (!menuInitialized) { - if(m_goto_buffer) - strunzone(m_goto_buffer); + if (m_goto_buffer) strunzone(m_goto_buffer); m_goto_buffer = strzone(itemname); return; } - if(itemname == "") // this can be called by GameCommand + if (itemname == "") // this can be called by GameCommand { - if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) + if (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) { m_hide(); } @@ -1010,11 +939,11 @@ void m_goto(string itemname) } else { - for(e = NULL; (e = find(e, name, itemname)); ) - if(e.classname != "vtbl") - break; + entity e; + for (e = NULL; (e = find(e, name, itemname)); ) + if (e.classname != "vtbl") break; - if((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)))) + if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)))) { m_hide(); m_activate_window(e); @@ -1024,19 +953,17 @@ void m_goto(string itemname) } } -float menuLastFocusSoundTime; void m_play_focus_sound() { - if(cvar("menu_sounds") > 1) - if(time - menuLastFocusSoundTime > 0.25) - { - localsound(MENU_SOUND_FOCUS); - menuLastFocusSoundTime = time; - } + static float menuLastFocusSoundTime; + if (cvar("menu_sounds") < 2) return; + if (time - menuLastFocusSoundTime <= 0.25) return; + localsound(MENU_SOUND_FOCUS); + menuLastFocusSoundTime = time; } void m_play_click_sound(string soundfile) { - if(cvar("menu_sounds")) - localsound(soundfile); + if (!cvar("menu_sounds")) return; + localsound(soundfile); } diff --git a/qcsrc/menu/menu.qh b/qcsrc/menu/menu.qh index 6d45ca82f4..a4cdbc5f51 100644 --- a/qcsrc/menu/menu.qh +++ b/qcsrc/menu/menu.qh @@ -9,15 +9,11 @@ #include "../common/constants.qh" #include "../common/util.qh" -// constants +const int GAME_ISSERVER = BIT(0); +const int GAME_CONNECTED = BIT(1); +const int GAME_DEVELOPER = BIT(2); -const int GAME_ISSERVER = 1; -const int GAME_CONNECTED = 2; -const int GAME_DEVELOPER = 4; - -// prototypes - -float Menu_Active; +bool Menu_Active; int gamestatus; const int S_SHIFT = 1; @@ -35,20 +31,22 @@ void m_goto(string name); .string name; entity keyGrabber; -.void(entity me, float key, float ascii) keyGrabbed; +.void(entity this, float key, float ascii) keyGrabbed; -float conwidth, conheight; // "virtual" conwidth/height values for other stuff to assume for scaling +// "virtual" conwidth/height values for other stuff to assume for scaling +float conwidth, conheight; -float preMenuInit(); // you have to define this for pre-menu initialization. Return 0 if initialization needs to be retried a frame later, 1 if it succeeded. -void preMenuDraw(); // this is run before the menu is drawn. You may put some stuff there that has to be done every frame. -void postMenuDraw(); // this is run just after the menu is drawn (or not). Useful to draw something over everything else. +/** you have to define this for pre-menu initialization. Return 0 if initialization needs to be retried a frame later, 1 if it succeeded. */ +float preMenuInit(); +/** this is run before the menu is drawn. You may put some stuff there that has to be done every frame. */ +void preMenuDraw(); +/** this is run just after the menu is drawn (or not). Useful to draw something over everything else. */ +void postMenuDraw(); void m_sync(); void draw_reset_cropped(); -// sounds - const string MENU_SOUND_CLEAR = "sound/menu/clear.wav"; const string MENU_SOUND_CLOSE = "sound/menu/close.wav"; const string MENU_SOUND_EXECUTE = "sound/menu/execute.wav"; diff --git a/qcsrc/menu/mutators/events.qh b/qcsrc/menu/mutators/events.qh new file mode 100644 index 0000000000..a3dc720e8f --- /dev/null +++ b/qcsrc/menu/mutators/events.qh @@ -0,0 +1,37 @@ +#ifndef MENU_MUTATORS_EVENTS_H +#define MENU_MUTATORS_EVENTS_H + +#include "../../common/mutators/base.qh" + +// globals + +string cmd_name; +int cmd_argc; +string cmd_string; + +/** + * Called when a menu command is parsed + * NOTE: hooks MUST start with if (MUTATOR_RETURNVALUE) return false; + * NOTE: return true if you handled the command, return false to continue handling + * NOTE: THESE HOOKS MUST NEVER EVER CALL tokenize() + * // example: + * MUTATOR_HOOKFUNCTION(foo, Menu_ConsoleCommand) { + * if (MUTATOR_RETURNVALUE) return false; // command was already handled + * if (cmd_name == "echocvar" && cmd_argc >= 2) { + * print(cvar_string(argv(1)), "\n"); + * return true; + * } + * if (cmd_name == "echostring" && cmd_argc >= 2) { + * print(substring(cmd_string, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), "\n"); + * return true; + * } + * return false; + * } + */ +#define EV_Menu_ConsoleCommand(i, o) \ + /** command name */ i(string, cmd_name) \ + /** also, argv() can be used */ i(int, cmd_argc) \ + /** whole command, use only if you really have to */ i(string, cmd_string) \ + /**/ +MUTATOR_HOOKABLE(Menu_ConsoleCommand, EV_Menu_ConsoleCommand); +#endif diff --git a/qcsrc/menu/oo/classes.qc b/qcsrc/menu/oo/classes.qc deleted file mode 100644 index 16a830da04..0000000000 --- a/qcsrc/menu/oo/classes.qc +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CLASSES_H -#define CLASSES_H - -#include "../classes.inc" -#define IMPLEMENTATION -#include "../classes.inc" -#undef IMPLEMENTATION - -#endif diff --git a/qcsrc/menu/progs.inc b/qcsrc/menu/progs.inc index ae221de272..5282a01888 100644 --- a/qcsrc/menu/progs.inc +++ b/qcsrc/menu/progs.inc @@ -2,7 +2,7 @@ #define world NULL -#include "oo/classes.qc" +#include "classes.qc" #include "draw.qc" #include "menu.qc" diff --git a/qcsrc/menu/sys-post.qh b/qcsrc/menu/sys-post.qh deleted file mode 100644 index 2b4120e8b0..0000000000 --- a/qcsrc/menu/sys-post.qh +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef SYS_POST_H -#define SYS_POST_H - -#pragma noref 0 -#endif diff --git a/qcsrc/menu/sys-pre.qh b/qcsrc/menu/sys-pre.qh deleted file mode 100644 index 333d5c6bc9..0000000000 --- a/qcsrc/menu/sys-pre.qh +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef SYS_PRE_H -#define SYS_PRE_H - -#pragma noref 1 -#endif diff --git a/qcsrc/menu/xonotic/crosshairpicker.qc b/qcsrc/menu/xonotic/crosshairpicker.qc index 0ac826a440..63da9cd92a 100644 --- a/qcsrc/menu/xonotic/crosshairpicker.qc +++ b/qcsrc/menu/xonotic/crosshairpicker.qc @@ -60,18 +60,17 @@ bool XonoticCrosshairPicker_cellIsValid(entity me, vector cell) void XonoticCrosshairPicker_cellDraw(entity me, vector cell, vector cellPos) { - vector sz; - string cross = strcat("/gfx/crosshair", crosshairpicker_cellToCrosshair(me, cell)); - sz = draw_PictureSize(cross); + string s = strcat("/gfx/crosshair", crosshairpicker_cellToCrosshair(me, cell)); + vector sz = draw_PictureSize(s); sz = globalToBoxSize(sz, me.size); float ar = sz.x / sz.y; sz.x = me.realCellSize.x; sz.y = sz.x / ar; - sz = sz * 0.95; + sz *= 0.95; vector crosshairPos = cellPos + 0.5 * me.realCellSize; - draw_Picture(crosshairPos - 0.5 * sz, cross, sz, SKINCOLOR_CROSSHAIRPICKER_CROSSHAIR, SKINALPHA_CROSSHAIRPICKER_CROSSHAIR); + draw_Picture(crosshairPos - 0.5 * sz, s, sz, SKINCOLOR_CROSSHAIRPICKER_CROSSHAIR, SKINALPHA_CROSSHAIRPICKER_CROSSHAIR); if(cvar("crosshair_dot")) draw_Picture(crosshairPos - 0.5 * sz * cvar("crosshair_dot_size"), "/gfx/crosshairdot", sz * cvar("crosshair_dot_size"), SKINCOLOR_CROSSHAIRPICKER_CROSSHAIR, SKINALPHA_CROSSHAIRPICKER_CROSSHAIR); diff --git a/qcsrc/menu/xonotic/datasource.qc b/qcsrc/menu/xonotic/datasource.qc index 57ce0c80c3..3b8be8d27b 100644 --- a/qcsrc/menu/xonotic/datasource.qc +++ b/qcsrc/menu/xonotic/datasource.qc @@ -57,13 +57,13 @@ CLASS(CvarStringSource, StringSource) { string s = this.CvarStringSource_cvar; this.StringSource_str = s ? cvar_string(s) : string_null; - return super.getEntry(this, i, returns); + return SUPER(CvarStringSource).getEntry(this, i, returns); } METHOD(CvarStringSource, reload, int(entity this, string filter)) { string s = this.CvarStringSource_cvar; this.StringSource_str = s ? cvar_string(s) : string_null; - return super.reload(this, filter); + return SUPER(CvarStringSource).reload(this, filter); } ENDCLASS(CvarStringSource) #endif diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index 408cb950b4..2f3cd47b02 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -54,7 +54,7 @@ string WeaponArenaString() { e = get_weaponinfo(j); if(argv(i) == e.netname) - s = strcat(s, " & ", e.message); + s = strcat(s, " & ", e.m_name); } } s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3)); @@ -259,7 +259,7 @@ void XonoticMutatorsDialog_fill(entity me) if((j & 1) == 0) me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.message))); + me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); setDependentWeird(e, checkCompatibility_weaponarena_weapon); ++j; } diff --git a/qcsrc/menu/xonotic/dialog_settings_game.qc b/qcsrc/menu/xonotic/dialog_settings_game.qc index 3c1503b297..6f68b332d3 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game.qc +++ b/qcsrc/menu/xonotic/dialog_settings_game.qc @@ -7,14 +7,14 @@ CLASS(SettingSource, DataSource) METHOD(SettingSource, getEntry, entity(entity this, int i, void(string name, string icon) returns)) { - Lazy l = Settings[i]; + Lazy l = Settings_from(i); entity it = l.m_get(); if (returns) returns(it.title, string_null); return it; } METHOD(SettingSource, getEntryTooltip, entity(entity this, int i, void(string theTooltip) returns)) { - Lazy l = Settings[i]; + Lazy l = Settings_from(i); entity it = l.m_get(); if (returns) returns(it.tooltip); return it; @@ -94,7 +94,7 @@ CLASS(XonoticRegisteredSettingsList, XonoticListBox) } METHOD(XonoticRegisteredSettingsList, resizeNotify, void(entity this, vector relOrigin, vector relSize, vector absOrigin, vector absSize)) { - super.resizeNotify(this, relOrigin, relSize, absOrigin, absSize); + SUPER(XonoticRegisteredSettingsList).resizeNotify(this, relOrigin, relSize, absOrigin, absSize); this.itemAbsSize = '0 0 0'; this.realFontSize_y = this.fontSize / (this.itemAbsSize_y = (absSize.y * this.itemHeight)); @@ -103,7 +103,7 @@ CLASS(XonoticRegisteredSettingsList, XonoticListBox) } METHOD(XonoticRegisteredSettingsList, setSelected, void(entity this, int i)) { - super.setSelected(this, i); + SUPER(XonoticRegisteredSettingsList).setSelected(this, i); this.onChange(this, this.onChangeEntity); } CONSTRUCTOR(XonoticRegisteredSettingsList, DataSource _source) { @@ -147,7 +147,7 @@ CLASS(XonoticGameSettingsTab, XonoticTab) topics.onChangeEntity = this; int - col = 0, width = 1.5; + col = 0, width = 1; this.gotoRC(this, 0, col); this.TD(this, this.rows, width, topics); diff --git a/qcsrc/menu/xonotic/gametypelist.qc b/qcsrc/menu/xonotic/gametypelist.qc index 5522e13c7f..f04a0d23a6 100644 --- a/qcsrc/menu/xonotic/gametypelist.qc +++ b/qcsrc/menu/xonotic/gametypelist.qc @@ -25,7 +25,7 @@ entity makeXonoticGametypeList(); #ifdef IMPLEMENTATION -entity makeXonoticGametypeList(void) +entity makeXonoticGametypeList() { entity me; me = NEW(XonoticGametypeList); diff --git a/qcsrc/menu/xonotic/languagelist.qc b/qcsrc/menu/xonotic/languagelist.qc index 0b7281dbff..66dbb83512 100644 --- a/qcsrc/menu/xonotic/languagelist.qc +++ b/qcsrc/menu/xonotic/languagelist.qc @@ -184,15 +184,10 @@ void XonoticLanguageList_getLanguages(entity me) continue; bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_ID, argv(0)); bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME, argv(1)); - float k = strstrofs(argv(2), "(", 0); - if(k > 0) - if(substring(argv(2), strlen(argv(2)) - 1, 1) == ")") - { - string percent = substring(argv(2), k + 1, -2); - if(percent != "100%") - bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_PERCENTAGE, percent); - } - bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME_LOCALIZED, (k < 0) ? argv(2) : substring(argv(2), 0, k - 1)); + bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_NAME_LOCALIZED, argv(2)); + string percent = argv(3); + if(percent && percent != "100%") + bufstr_set(buf, i * LANGPARM_COUNT + LANGPARM_PERCENTAGE, percent); ++i; } fclose(fh); diff --git a/qcsrc/menu/xonotic/serverlist.qc b/qcsrc/menu/xonotic/serverlist.qc index 2cee6162ff..3819036ad4 100644 --- a/qcsrc/menu/xonotic/serverlist.qc +++ b/qcsrc/menu/xonotic/serverlist.qc @@ -173,9 +173,7 @@ void RegisterSLCategories() #define SLIST_CATEGORY(name,enoverride,dioverride,str) \ SET_FIELD_COUNT(name, CATEGORY_FIRST, category_ent_count) \ CHECK_MAX_COUNT(name, MAX_CATEGORIES, category_ent_count, "SLIST_CATEGORY") \ - cat = spawn(); \ - categories[name - 1] = cat; \ - cat.classname = "slist_category"; \ + cat = categories[name - 1] = new(slist_category); \ cat.cat_name = strzone(#name); \ cat.cat_enoverride_string = strzone(SLIST_CATEGORY_AUTOCVAR(name)); \ cat.cat_dioverride_string = strzone(dioverride); \ diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 146075ecae..4260480022 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -277,6 +277,7 @@ string _Nex_ExtResponseSystem_UpdateToURL; string _Nex_ExtResponseSystem_Packs; float _Nex_ExtResponseSystem_PacksStep; +/** engine callback */ void URI_Get_Callback(float id, float status, string data) { if(url_URI_Get_Callback(id, status, data)) diff --git a/qcsrc/menu/xonotic/util.qh b/qcsrc/menu/xonotic/util.qh index 9e989a4662..79ce7c5eef 100644 --- a/qcsrc/menu/xonotic/util.qh +++ b/qcsrc/menu/xonotic/util.qh @@ -28,8 +28,6 @@ float updateCompression(); void UpdateNotification_URI_Get_Callback(float id, float status, string data); -void URI_Get_Callback(float id, float status, string data); - // game type list box stuff (does not NEED to contain all game types, other // types stay available via console) int GameType_GetID(int cnt); diff --git a/qcsrc/menu/xonotic/weaponslist.qc b/qcsrc/menu/xonotic/weaponslist.qc index 1f2a4e19e6..c1189dd9ba 100644 --- a/qcsrc/menu/xonotic/weaponslist.qc +++ b/qcsrc/menu/xonotic/weaponslist.qc @@ -88,7 +88,7 @@ string XonoticWeaponsList_toString(entity me) for(i = 0; i < n; ++i) { e = get_weaponinfo(stof(argv(i))); - s = strcat(s, e.message, ", "); + s = strcat(s, e.m_name, ", "); } return substring(s, 0, strlen(s) - 2); } @@ -103,7 +103,7 @@ void XonoticWeaponsList_drawListBoxItem(entity me, int i, vector absSize, bool i draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, me.focusedItemAlpha); } e = get_weaponinfo(stof(argv(i))); - string msg = e.message; + string msg = e.m_name; if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) msg = strcat(msg, "*"); diff --git a/qcsrc/server/_all.qh b/qcsrc/server/_all.qh index eeb06e7a69..cbda56873a 100644 --- a/qcsrc/server/_all.qh +++ b/qcsrc/server/_all.qh @@ -1,14 +1,43 @@ #ifndef SERVER_ALL_H #define SERVER_ALL_H -#include "autocvars.qh" -#include "constants.qh" -#include "defs.qh" -#include "miscfunctions.qh" +int maxclients; + +const string STR_PLAYER = "player"; +const string STR_SPECTATOR = "spectator"; +const string STR_OBSERVER = "observer"; + +#define IS_PLAYER(v) ((v).classname == STR_PLAYER) +#define IS_SPEC(v) ((v).classname == STR_SPECTATOR) +#define IS_OBSERVER(v) ((v).classname == STR_OBSERVER) + +#define IS_CLIENT(v) (v.flags & FL_CLIENT) +#define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT) +#define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) +#define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) + +#define IS_MONSTER(v) (v.flags & FL_MONSTER) +#define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE) +#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET) +#define FOR_EACH_CLIENTSLOT(v) for (v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) +#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if (IS_CLIENT(v)) +#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if (IS_REAL_CLIENT(v)) + +#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if (IS_PLAYER(v)) +#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (IS_SPEC(v)) +#define FOR_EACH_OBSERVER(v) FOR_EACH_CLIENT(v) if (IS_OBSERVER(v)) +#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if (IS_PLAYER(v)) + +#define FOR_EACH_MONSTER(v) for (v = world; (v = findflags(v, flags, FL_MONSTER)) != world; ) #include "../common/effects/all.qh" #include "../common/models/all.qh" #include "../common/sounds/all.qh" +#include "autocvars.qh" +#include "constants.qh" +#include "defs.qh" +#include "miscfunctions.qh" + #endif diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 7a60823152..6cd7e2395a 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -55,7 +55,6 @@ bool autocvar_bot_navigation_ignoreplayers; bool autocvar_bot_nofire; #define autocvar_bot_number cvar("bot_number") #define autocvar_bot_prefix cvar_string("bot_prefix") -bool autocvar_bot_sound_monopoly; #define autocvar_bot_suffix cvar_string("bot_suffix") bool autocvar_bot_usemodelnames; int autocvar_bot_vs_human; @@ -81,7 +80,6 @@ float autocvar_g_balance_armor_rot; float autocvar_g_balance_armor_rotlinear; int autocvar_g_balance_armor_rotstable; int autocvar_g_balance_armor_start; -float autocvar_g_balance_cloaked_alpha; float autocvar_g_balance_contents_damagerate; float autocvar_g_balance_contents_drowndelay; int autocvar_g_balance_contents_playerdamage_drowning; @@ -121,20 +119,6 @@ float autocvar_g_balance_health_rotlinear; float autocvar_g_balance_health_rotstable; float autocvar_g_balance_kill_delay; float autocvar_g_balance_kill_antispam; -int autocvar_g_balance_nix_ammo_cells; -int autocvar_g_balance_nix_ammo_plasma; -int autocvar_g_balance_nix_ammo_fuel; -int autocvar_g_balance_nix_ammo_nails; -int autocvar_g_balance_nix_ammo_rockets; -int autocvar_g_balance_nix_ammo_shells; -int autocvar_g_balance_nix_ammoincr_cells; -int autocvar_g_balance_nix_ammoincr_plasma; -int autocvar_g_balance_nix_ammoincr_fuel; -int autocvar_g_balance_nix_ammoincr_nails; -int autocvar_g_balance_nix_ammoincr_rockets; -int autocvar_g_balance_nix_ammoincr_shells; -float autocvar_g_balance_nix_incrtime; -float autocvar_g_balance_nix_roundtime; float autocvar_g_balance_pause_armor_rot; float autocvar_g_balance_pause_armor_rot_spawn; float autocvar_g_balance_pause_fuel_regen; @@ -234,12 +218,7 @@ int autocvar_g_maxplayers; float autocvar_g_maxplayers_spectator_blocktime; float autocvar_g_maxpushtime; float autocvar_g_maxspeed; -float autocvar_g_midair_shieldtime; #define autocvar_g_instagib cvar("g_instagib") -int autocvar_g_instagib_ammo_drop; -int autocvar_g_instagib_extralives; -float autocvar_g_instagib_speed_highspeed; -float autocvar_g_instagib_invis_alpha; bool autocvar_g_instagib_damagedbycontents = true; bool autocvar_g_instagib_blaster_keepdamage = false; bool autocvar_g_instagib_blaster_keepforce = false; @@ -247,31 +226,17 @@ bool autocvar_g_instagib_blaster_keepforce = false; #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual") float autocvar_g_movement_highspeed = 1; -int autocvar_g_multijump; -float autocvar_g_multijump_add; -float autocvar_g_multijump_speed; -float autocvar_g_multijump_maxspeed; -float autocvar_g_multijump_dodging = 1; string autocvar_g_mutatormsg; //float autocvar_g_nick_flood_penalty; int autocvar_g_nick_flood_penalty_red; int autocvar_g_nick_flood_penalty_yellow; //float autocvar_g_nick_flood_timeout; -bool autocvar_g_nix_with_healtharmor; -bool autocvar_g_nix_with_blaster; -bool autocvar_g_nix_with_powerups; bool autocvar_g_nodepthtestitems; bool autocvar_g_nodepthtestplayers; bool autocvar_g_norecoil; float autocvar_g_items_mindist; float autocvar_g_items_maxdist; -int autocvar_g_pickup_cells_max; -int autocvar_g_pickup_plasma_max; -int autocvar_g_pickup_fuel_max; int autocvar_g_pickup_items; -int autocvar_g_pickup_nails_max; -int autocvar_g_pickup_rockets_max; -int autocvar_g_pickup_shells_max; float autocvar_g_player_alpha; float autocvar_g_player_brightness; bool autocvar_g_playerclip_collisions; @@ -292,7 +257,6 @@ bool autocvar_g_respawn_ghosts; float autocvar_g_respawn_ghosts_maxtime; float autocvar_g_respawn_ghosts_speed; int autocvar_g_respawn_waves; -bool autocvar_g_running_guns; bool autocvar_g_shootfromcenter; bool autocvar_g_shootfromeye; string autocvar_g_shootfromfixedorigin; @@ -382,17 +346,7 @@ string autocvar_sv_defaultplayermodel_pink; string autocvar_sv_defaultplayermodel_red; string autocvar_sv_defaultplayermodel_yellow; int autocvar_sv_defaultplayerskin; -float autocvar_sv_dodging_delay; -float autocvar_sv_dodging_height_threshold; -float autocvar_sv_dodging_horiz_speed; -float autocvar_sv_dodging_horiz_speed_frozen; -float autocvar_sv_dodging_ramp_time; -bool autocvar_sv_dodging_sound; -float autocvar_sv_dodging_up_speed; -float autocvar_sv_dodging_wall_distance_threshold; -bool autocvar_sv_dodging_wall_dodging; bool autocvar_sv_dodging_frozen; -bool autocvar_sv_dodging_frozen_doubletap; bool autocvar_sv_doublejump; bool autocvar_sv_eventlog; bool autocvar_sv_eventlog_console; @@ -485,25 +439,8 @@ bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag; float autocvar_g_trueaim_minrange; bool autocvar_g_debug_defaultsounds; float autocvar_g_grab_range; -int autocvar_g_sandbox_info; -bool autocvar_g_sandbox_readonly; -string autocvar_g_sandbox_storage_name; -float autocvar_g_sandbox_storage_autosave; -bool autocvar_g_sandbox_storage_autoload; -float autocvar_g_sandbox_editor_flood; -int autocvar_g_sandbox_editor_maxobjects; -int autocvar_g_sandbox_editor_free; -float autocvar_g_sandbox_editor_distance_spawn; -float autocvar_g_sandbox_editor_distance_edit; -float autocvar_g_sandbox_object_scale_min; -float autocvar_g_sandbox_object_scale_max; -float autocvar_g_sandbox_object_material_velocity_min; -float autocvar_g_sandbox_object_material_velocity_factor; int autocvar_g_max_info_autoscreenshot; bool autocvar_physics_ode; -int autocvar_g_physical_items; -float autocvar_g_physical_items_damageforcescale; -float autocvar_g_physical_items_reset; float autocvar_g_monsters; bool autocvar_g_monsters_edit; bool autocvar_g_monsters_sounds; @@ -523,23 +460,14 @@ float autocvar_g_monsters_miniboss_chance; float autocvar_g_monsters_miniboss_healthboost; float autocvar_g_monsters_drop_time; float autocvar_g_monsters_spawnshieldtime; +bool autocvar_g_monsters_quake_resize = true; bool autocvar_g_monsters_teams; float autocvar_g_monsters_respawn_delay; bool autocvar_g_monsters_respawn; float autocvar_g_monsters_armor_blockpercent; float autocvar_g_monsters_healthbars; float autocvar_g_monsters_lineofsight; -float autocvar_g_touchexplode_radius; -float autocvar_g_touchexplode_damage; -float autocvar_g_touchexplode_edgedamage; -float autocvar_g_touchexplode_force; #define autocvar_g_bloodloss cvar("g_bloodloss") -float autocvar_g_random_gravity_negative_chance; -float autocvar_g_random_gravity_min; -float autocvar_g_random_gravity_max; -float autocvar_g_random_gravity_positive; -float autocvar_g_random_gravity_negative; -float autocvar_g_random_gravity_delay; bool autocvar_g_nades; bool autocvar_g_nades_override_dropweapon = true; vector autocvar_g_nades_throw_offset; @@ -595,67 +523,12 @@ float autocvar_g_nades_heal_friend; float autocvar_g_nades_heal_foe; string autocvar_g_nades_pokenade_monster_type; float autocvar_g_nades_pokenade_monster_lifetime; -float autocvar_g_campcheck_damage; -float autocvar_g_campcheck_distance; -float autocvar_g_campcheck_interval; bool autocvar_g_jump_grunt; -bool autocvar_g_overkill_powerups_replace; -float autocvar_g_overkill_superguns_respawn_time; -bool autocvar_g_overkill_100h_anyway; -bool autocvar_g_overkill_100a_anyway; -bool autocvar_g_overkill_ammo_charge; -float autocvar_g_overkill_ammo_charge_notice; -float autocvar_g_overkill_ammo_charge_limit; -float autocvar_g_spawn_near_teammate_distance; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; -bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; bool autocvar_g_physics_clientselect; string autocvar_g_physics_clientselect_options; string autocvar_g_physics_clientselect_default; -bool autocvar_g_buffs_effects; -float autocvar_g_buffs_waypoint_distance; -bool autocvar_g_buffs_randomize; -float autocvar_g_buffs_random_lifetime; -bool autocvar_g_buffs_random_location; -int autocvar_g_buffs_random_location_attempts; -int autocvar_g_buffs_spawn_count; -bool autocvar_g_buffs_replace_powerups; -float autocvar_g_buffs_cooldown_activate; -float autocvar_g_buffs_cooldown_respawn; -float autocvar_g_buffs_resistance_blockpercent; -float autocvar_g_buffs_medic_survive_chance; -float autocvar_g_buffs_medic_survive_health; -float autocvar_g_buffs_medic_rot; -float autocvar_g_buffs_medic_max; -float autocvar_g_buffs_medic_regen; -float autocvar_g_buffs_vengeance_damage_multiplier; -float autocvar_g_buffs_bash_force; -float autocvar_g_buffs_bash_force_self; -float autocvar_g_buffs_disability_slowtime; -float autocvar_g_buffs_disability_speed; -float autocvar_g_buffs_disability_rate; -float autocvar_g_buffs_disability_weaponspeed; -float autocvar_g_buffs_speed_speed; -float autocvar_g_buffs_speed_rate; -float autocvar_g_buffs_speed_weaponspeed; -float autocvar_g_buffs_speed_damage_take; -float autocvar_g_buffs_speed_regen; -float autocvar_g_buffs_vampire_damage_steal; -float autocvar_g_buffs_invisible_alpha; -float autocvar_g_buffs_flight_gravity; -float autocvar_g_buffs_jump_height; bool autocvar_sv_minigames; bool autocvar_sv_minigames_observer; -float autocvar_g_buffs_inferno_burntime_factor; -float autocvar_g_buffs_inferno_burntime_min_time; -float autocvar_g_buffs_inferno_burntime_target_damage; -float autocvar_g_buffs_inferno_burntime_target_time; -float autocvar_g_buffs_inferno_damagemultiplier; -float autocvar_g_buffs_swapper_range; -float autocvar_g_buffs_magnet_range_item; float autocvar_sv_player_scale; float autocvar_g_rm; float autocvar_g_rm_damage; diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc index 50e1d456f5..9541540298 100644 --- a/qcsrc/server/bot/aim.qc +++ b/qcsrc/server/bot/aim.qc @@ -19,7 +19,7 @@ float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, f if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER return false; // could never hit it if (!tracetossent) - tracetossent = spawn(); + tracetossent = new(tracetossent); tracetossent.owner = ignore; setsize(tracetossent, m1, m2); savesolid = targ.solid; @@ -38,7 +38,7 @@ float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, f } if (!tracetossfaketarget) - tracetossfaketarget = spawn(); + tracetossfaketarget = new(tracetossfaketarget); tracetossfaketarget.solid = savesolid; tracetossfaketarget.movetype = targ.movetype; _setmodel(tracetossfaketarget, targ.model); // no low precision diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc index c607de4f48..b125564716 100644 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@ -556,7 +556,7 @@ void autoskill(float factor) head.totalfrags_lastcheck = head.totalfrags; } -void bot_calculate_stepheightvec(void) +void bot_calculate_stepheightvec() { stepheightvec = autocvar_sv_stepheight * '0 0 1'; jumpstepheightvec = stepheightvec + diff --git a/qcsrc/server/bot/bot.qh b/qcsrc/server/bot/bot.qh index 2ae70728a0..12cd763e1f 100644 --- a/qcsrc/server/bot/bot.qh +++ b/qcsrc/server/bot/bot.qh @@ -114,5 +114,5 @@ void bot_serverframe(); void() havocbot_setupbot; -void bot_calculate_stepheightvec(void); +void bot_calculate_stepheightvec(); #endif diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index ced6463c2e..696de21dca 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -1045,7 +1045,7 @@ void havocbot_chooseweapon() // Should it do a weapon combo? float af, ct, combo_time, combo; - af = ATTACK_FINISHED(self); + af = ATTACK_FINISHED(self, 0); ct = autocvar_bot_ai_weapon_combo_threshold; // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos diff --git a/qcsrc/server/bot/scripting.qc b/qcsrc/server/bot/scripting.qc index 76cda90d90..ea3a74fd54 100644 --- a/qcsrc/server/bot/scripting.qc +++ b/qcsrc/server/bot/scripting.qc @@ -1059,10 +1059,11 @@ float bot_cmd_sound() .entity tuba_note; float bot_cmd_debug_assert_canfire() {SELFPARAM(); - float f; - f = bot_cmd.bot_cmd_parm_float; + float f = bot_cmd.bot_cmd_parm_float; - if(self.weaponentity.state != WS_READY) + int slot = 0; + .entity weaponentity = weaponentities[slot]; + if(self.(weaponentity).state != WS_READY) { if(f) { @@ -1070,12 +1071,12 @@ float bot_cmd_debug_assert_canfire() LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by weaponentity state\n"); } } - else if(ATTACK_FINISHED(self) > time) + else if(ATTACK_FINISHED(self, slot) > time) { if(f) { self.colormod = '8 0 8'; - LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self) - time), " seconds left)\n"); + LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(self, slot) - time), " seconds left)\n"); } } else if(self.tuba_note) @@ -1091,7 +1092,7 @@ float bot_cmd_debug_assert_canfire() if(!f) { self.colormod = '8 8 0'; - LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self) - time), " seconds left\n"); + LOG_INFO("Bot ", self.netname, " using ", self.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(self, slot) - time), " seconds left\n"); } } @@ -1118,9 +1119,8 @@ void bot_setcurrentcommand() if(!self.bot_cmd_current) { - self.bot_cmd_current = spawn(); - self.bot_cmd_current.classname = "bot_cmd"; - self.bot_cmd_current.is_bot_cmd = 1; + self.bot_cmd_current = new(bot_cmd); + self.bot_cmd_current.is_bot_cmd = true; } bot_cmd = self.bot_cmd_current; diff --git a/qcsrc/server/bot/waypoints.qc b/qcsrc/server/bot/waypoints.qc index 156ad79fc2..fe767b8c93 100644 --- a/qcsrc/server/bot/waypoints.qc +++ b/qcsrc/server/bot/waypoints.qc @@ -27,9 +27,9 @@ entity waypoint_spawn(vector m1, vector m2, float f) w = find(w, classname, "waypoint"); } - w = spawn(); + w = new(waypoint); + make_pure(w); w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - w.classname = "waypoint"; w.wpflags = f; setorigin(w, (m1 + m2) * 0.5); setsize(w, m1 - w.origin, m2 - w.origin); diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 8f34a7798b..5d6fba14fd 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -150,8 +150,7 @@ float CheatImpulse(float i) // shared with regular waypoint init, so this is not a cheat by itself if(!self.personal) { - self.personal = spawn(); - self.personal.classname = "personal_wp"; + self.personal = new(personal_wp); } self.personal.origin = self.origin; self.personal.v_angle = self.v_angle; @@ -338,7 +337,7 @@ float CheatCommand(float argc) effectnum = _particleeffectnum(argv(1)); W_SetupShot(self, false, false, "", CH_WEAPON_A, 0); traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self); - trailparticles(self, effectnum, w_shotorg, trace_endpos); + __trailparticles(self, effectnum, w_shotorg, trace_endpos); DID_CHEAT(); break; } @@ -402,8 +401,7 @@ float CheatCommand(float argc) break; case "dragbox_spawn": { IS_CHEAT(0, argc, 0); - entity e = spawn(); - e.classname = "dragbox_box"; + entity e = new(dragbox_box); e.think = DragBox_Think; e.nextthink = time; e.solid = -1; // black @@ -413,8 +411,7 @@ float CheatCommand(float argc) else e.cnt = max(0, drag_lastcnt); - e.aiment = spawn(); - e.aiment.classname = "dragbox_corner_1"; + e.aiment = new(dragbox_corner_1); e.aiment.owner = e; setmodel(e.aiment, MDL_MARKER); e.aiment.skin = 0; @@ -427,8 +424,7 @@ float CheatCommand(float argc) setorigin(e.aiment, trace_endpos); } - e.enemy = spawn(); - e.enemy.classname = "dragbox_corner_2"; + e.enemy = new(dragbox_corner_2); e.enemy.owner = e; setmodel(e.enemy, MDL_MARKER); e.enemy.skin = 1; @@ -442,13 +438,11 @@ float CheatCommand(float argc) else setorigin(e.enemy, e.aiment.origin + 32 * end); - e.killindicator = spawn(); - e.killindicator.classname = "drag_digit"; + e.killindicator = new(drag_digit); e.killindicator.owner = e; setattachment(e.killindicator, e, ""); setorigin(e.killindicator, '0 0 -8'); - e.killindicator.killindicator = spawn(); - e.killindicator.killindicator.classname = "drag_digit"; + e.killindicator.killindicator = new(drag_digit); e.killindicator.killindicator.owner = e; setattachment(e.killindicator.killindicator, e, ""); setorigin(e.killindicator.killindicator, '0 0 8'); @@ -457,8 +451,7 @@ float CheatCommand(float argc) } case "dragpoint_spawn": { IS_CHEAT(0, argc, 0); - entity e = spawn(); - e.classname = "dragpoint"; + entity e = new(dragpoint); e.think = DragBox_Think; e.nextthink = time; e.solid = 0; // nothing special @@ -478,13 +471,11 @@ float CheatCommand(float argc) move_out_of_solid(e); } - e.killindicator = spawn(); - e.killindicator.classname = "drag_digit"; + e.killindicator = new(drag_digit); e.killindicator.owner = e; setattachment(e.killindicator, e, ""); setorigin(e.killindicator, '0 0 40'); - e.killindicator.killindicator = spawn(); - e.killindicator.killindicator.classname = "drag_digit"; + e.killindicator.killindicator = new(drag_digit); e.killindicator.killindicator.owner = e; setattachment(e.killindicator.killindicator, e, ""); setorigin(e.killindicator.killindicator, '0 0 56'); @@ -713,9 +704,8 @@ float CheatCommand(float argc) break; case "teleporttotarget": IS_CHEAT(0, argc, 0); - setself(spawn()); + setself(new(cheattriggerteleport)); setorigin(self, self.origin); - self.classname = "cheattriggerteleport"; self.target = argv(1); teleport_findtarget(); if(!wasfreed(self)) @@ -1025,8 +1015,9 @@ void Drag_Update(entity dragger) draggee.ltime = max(servertime + serverframetime, draggee.ltime); // fixes func_train breakage vector vecs = '0 0 0'; - if(dragger.weaponentity.movedir_x > 0) - vecs = dragger.weaponentity.movedir; + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if(dragger.(weaponentity).movedir.x > 0) + vecs = dragger.(weaponentity).movedir; vector dv = v_right * -vecs_y + v_up * vecs_z; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index eb3dfc44e1..24a63d5aab 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -50,8 +50,7 @@ void send_CSQC_teamnagger() { - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); + WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); } bool ClientData_Send(entity this, entity to, int sf) @@ -79,7 +78,7 @@ bool ClientData_Send(entity this, entity to, int sf) if(e.porto_v_angle_held) sf |= 8; // angles held - WriteByte(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); + WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); WriteByte(MSG_ENTITY, sf); if(sf & 2) @@ -96,9 +95,10 @@ bool ClientData_Send(entity this, entity to, int sf) void ClientData_Attach() {SELFPARAM(); - Net_LinkEntity(self.clientdata = spawn(), false, 0, ClientData_Send); - self.clientdata.drawonlytoclient = self; - self.clientdata.owner = self; + Net_LinkEntity(this.clientdata = new(clientdata), false, 0, ClientData_Send); + make_pure(this.clientdata); + self.clientdata.drawonlytoclient = this; + self.clientdata.owner = this; } void ClientData_Detach() @@ -175,7 +175,7 @@ void setplayermodel(entity e, string modelname) precache_model(modelname); _setmodel(e, modelname); player_setupanimsformodel(); - UpdatePlayerSounds(); + UpdatePlayerSounds(e); } /* @@ -304,7 +304,10 @@ void PutObserverInServer() self.weaponname = ""; self.switchingweapon = 0; self.weaponmodel = ""; - self.weaponentity = world; + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + self.weaponentities[slot] = NULL; + } self.exteriorweaponentity = world; self.killcount = FRAGS_SPECTATOR; self.velocity = '0 0 0'; @@ -316,6 +319,15 @@ void PutObserverInServer() self.event_damage = func_null; } +int player_getspecies(entity this) +{ + get_model_parameters(this.model, this.skin); + int s = get_model_parameters_species; + get_model_parameters(string_null, 0); + if (s < 0) return SPECIES_HUMAN; + return s; +} + .float model_randomizer; void FixPlayermodel(entity player) { @@ -392,8 +404,8 @@ void FixPlayermodel(entity player) if(chmdl || oldskin != player.skin) // model or skin has changed { - player.species = player_getspecies(); // update species - UpdatePlayerSounds(); // update skin sounds + player.species = player_getspecies(player); // update species + UpdatePlayerSounds(player); // update skin sounds } if(!teamplay) @@ -532,7 +544,8 @@ void PutClientInServer() this.revival_time = 0; this.air_finished = time + 12; - entity spawnevent = spawn(); + entity spawnevent = new(spawnevent); + make_pure(spawnevent); spawnevent.owner = this; Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send); @@ -568,7 +581,10 @@ void PutClientInServer() this.killcount = 0; } - CL_SpawnWeaponentity(this); + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + CL_SpawnWeaponentity(this, weaponentities[slot]); + } this.alpha = default_player_alpha; this.colormod = '1 1 1' * autocvar_g_player_brightness; this.exteriorweaponentity.alpha = default_weapon_alpha; @@ -623,34 +639,40 @@ void PutClientInServer() // changes and just have a console command to update this? bool ClientInit_SendEntity(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_INIT); - WriteByte(MSG_ENTITY, g_nexball_meter_period * 32); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[0])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3])); + WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); + return = true; + msg_entity = to; + Registry_send_all(); + int channel = MSG_ONE; + WriteHeader(channel, ENT_CLIENT_INIT); + WriteByte(channel, g_nexball_meter_period * 32); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3])); if(sv_foginterval && world.fog != "") - WriteString(MSG_ENTITY, world.fog); + WriteString(channel, world.fog); else - WriteString(MSG_ENTITY, ""); - WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent - WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO - WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop - WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor - WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop - WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO - WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO - WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not - WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO - WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO - WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange); - WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO - return true; + WriteString(channel, ""); + WriteByte(channel, self.count * 255.0); // g_balance_armor_blockpercent + WriteCoord(channel, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO + WriteCoord(channel, self.bouncestop); // g_balance_mortar_bouncestop + WriteCoord(channel, self.ebouncefactor); // g_balance_mortar_bouncefactor + WriteCoord(channel, self.ebouncestop); // g_balance_mortar_bouncestop + WriteByte(channel, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO + WriteByte(channel, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO + WriteByte(channel, serverflags); // client has to know if it should zoom or not + WriteByte(channel, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO + WriteByte(channel, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO + WriteCoord(channel, autocvar_g_trueaim_minrange); + WriteByte(channel, WEP_CVAR(porto, secondary)); // WEAPONTODO + + MUTATOR_CALLHOOK(Ent_Init); } void ClientInit_CheckUpdate() @@ -685,8 +707,9 @@ void ClientInit_CheckUpdate() void ClientInit_Spawn() {SELFPARAM(); - entity e = spawn(); - e.classname = "clientinit"; + + entity e = new(clientinit); + make_pure(e); e.think = ClientInit_CheckUpdate; Net_LinkEntity(e, false, 0, ClientInit_SendEntity); @@ -698,7 +721,7 @@ void ClientInit_Spawn() SetNewParms ============= */ -void SetNewParms (void) +void SetNewParms () { // initialize parms for a new player parm1 = -(86400 * 366); @@ -711,7 +734,7 @@ void SetNewParms (void) SetChangeParms ============= */ -void SetChangeParms (void) +void SetChangeParms () {SELFPARAM(); // save parms for level change parm1 = self.parm_idlesince - time; @@ -724,7 +747,7 @@ void SetChangeParms (void) DecodeLevelParms ============= */ -void DecodeLevelParms (void) +void DecodeLevelParms () {SELFPARAM(); // load parms self.parm_idlesince = parm1; @@ -927,7 +950,7 @@ void ClientKill_TeamChange (float targetteam) // 0 = don't change, -1 = auto, -2 } -void ClientKill (void) +void ClientKill () {SELFPARAM(); if(gameover) return; if(self.player_blocked) return; @@ -975,7 +998,7 @@ ClientPreConnect Called once (not at each match start) when a client begins a connection to the server ============= */ -void ClientPreConnect (void) +void ClientPreConnect () {SELFPARAM(); if(autocvar_sv_eventlog) { @@ -995,8 +1018,8 @@ ClientConnect Called when a client connects to the server ============= */ -void DecodeLevelParms (void); -void ClientConnect (void) +void DecodeLevelParms (); +void ClientConnect () {SELFPARAM(); float t; @@ -1214,7 +1237,7 @@ Called when a client disconnects from the server */ .entity chatbubbleentity; void ReadyCount(); -void ClientDisconnect (void) +void ClientDisconnect () {SELFPARAM(); if(self.vehicle) vehicles_exit(VHEF_RELEASE); @@ -1284,7 +1307,7 @@ void ClientDisconnect (void) if(self.weaponorder_byimpulse) strunzone(self.weaponorder_byimpulse); - ClearPlayerSounds(); + ClearPlayerSounds(self); if(self.personal) remove(self.personal); @@ -1330,7 +1353,7 @@ void UpdateChatBubble() // spawn a chatbubble entity if needed if (!self.chatbubbleentity) { - self.chatbubbleentity = spawn(); + self.chatbubbleentity = new(chatbubbleentity); self.chatbubbleentity.owner = self; self.chatbubbleentity.exteriormodeltoclient = self; self.chatbubbleentity.think = ChatBubbleThink; @@ -1362,7 +1385,7 @@ void UpdateChatBubble() else self.colormod = '1 1 1'; }*/ -void respawn(void) +void respawn() {SELFPARAM(); if(self.alpha >= 0 && autocvar_g_respawn_ghosts) { @@ -1391,7 +1414,7 @@ void play_countdown(float finished, string samp) _sound (self, CH_INFO, samp, VOL_BASE, ATTEN_NORM); } -void player_powerups (void) +void player_powerups () {SELFPARAM(); // add a way to see what the items were BEFORE all of these checks for the mutator hook int items_prev = self.items; @@ -1554,7 +1577,7 @@ float CalcRotRegen(float current, float regenstable, float regenfactor, float re return current; } -void player_regen (void) +void player_regen () {SELFPARAM(); float max_mod, regen_mod, rot_mod, limit_mod; max_mod = regen_mod = rot_mod = limit_mod = 1; @@ -1625,19 +1648,20 @@ void SetZoomState(float z) } void GetPressedKeys() -{SELFPARAM(); +{ + SELFPARAM(); MUTATOR_CALLHOOK(GetPressedKeys); - #define X(var,bit,flag) (flag ? var |= bit : var &= ~bit) - X(self.pressedkeys, KEY_FORWARD, self.movement_x > 0); - X(self.pressedkeys, KEY_BACKWARD, self.movement_x < 0); - X(self.pressedkeys, KEY_RIGHT, self.movement_y > 0); - X(self.pressedkeys, KEY_LEFT, self.movement_y < 0); - - X(self.pressedkeys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(self)); - X(self.pressedkeys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(self)); - X(self.pressedkeys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(self)); - X(self.pressedkeys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(self)); - #undef X + int keys = this.pressedkeys; + keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); + keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); + keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); + keys = BITSET(keys, KEY_LEFT, this.movement.y < 0); + + keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); + keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); + keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); + keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); + this.pressedkeys = keys; } /* @@ -1780,10 +1804,9 @@ bool SpectateNext() {SELFPARAM(); other = find(self.enemy, classname, "player"); - bool mutator_returnvalue = MUTATOR_CALLHOOK(SpectateNext, self, other); - other = spec_player; - - if(!mutator_returnvalue && !other) + if (MUTATOR_CALLHOOK(SpectateNext, self, other)) + other = spec_player; + else if (!other) other = find(other, classname, "player"); if(other) { SetSpectatee(self, other); } @@ -1804,13 +1827,14 @@ bool SpectatePrev() while(other && other != self.enemy) other = other.chain; - int mutator_returnvalue = MUTATOR_CALLHOOK(SpectatePrev, self, other, first); - other = spec_player; - - switch(mutator_returnvalue) + switch (MUTATOR_CALLHOOK(SpectatePrev, self, other, first)) { - case MUT_SPECPREV_FOUND: break; - case MUT_SPECPREV_RETURN: return true; + case MUT_SPECPREV_FOUND: + other = spec_player; + break; + case MUT_SPECPREV_RETURN: + other = spec_player; + return true; case MUT_SPECPREV_CONTINUE: default: { @@ -1861,7 +1885,6 @@ void LeaveSpectatorMode() if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0) { self.classname = STR_PLAYER; - nades_RemoveBonus(self); if(autocvar_g_campaign || autocvar_g_balance_teams) { JoinBestTeam(self, false, true); } @@ -1982,7 +2005,7 @@ void PrintWelcomeMessage() { if(self.BUTTON_INFO) // BUTTON_INFO hides initial MOTD self.motd_actived_time = -2; // wait until BUTTON_INFO gets released - else if(self.motd_actived_time == -2 || IS_PLAYER(self)) + else if(self.motd_actived_time == -2 || IS_PLAYER(self) || IS_SPEC(self)) { // instanctly hide MOTD self.motd_actived_time = 0; @@ -2129,32 +2152,6 @@ void PlayerUseKey() MUTATOR_CALLHOOK(PlayerUseKey); } -float isInvisibleString(string s) -{ - float i, n, c; - s = strdecolorize(s); - for((i = 0), (n = strlen(s)); i < n; ++i) - { - c = str2chr(s, i); - switch(c) - { - case 0: - case 32: // space - break; - case 192: // charmap space - if (!autocvar_utf8_enable) - break; - return false; - case 160: // space in unicode fonts - case 0xE000 + 192: // utf8 charmap space - if (autocvar_utf8_enable) - break; - default: - return false; - } - } - return true; -} /* ============= @@ -2167,7 +2164,7 @@ Called every frame for each client before the physics are run void() nexball_setstatus; .float last_vehiclecheck; .int items_added; -void PlayerPreThink (void) +void PlayerPreThink () {SELFPARAM(); WarpZone_PlayerPhysics_FixVAngle(); @@ -2427,7 +2424,8 @@ void PlayerPreThink (void) // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY // It cannot be predicted by the engine! - if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink) + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if((self.weapon == WEP_SHOCKWAVE.m_id || self.weapon == WEP_SHOTGUN.m_id) && self.(weaponentity).wframe == WFRAME_FIRE2 && time < self.(weaponentity).weapon_nextthink) do_crouch = 0; if (do_crouch) @@ -2555,7 +2553,7 @@ Called every frame for each client after the physics are run ============= */ .float idlekick_lasttimeleft; -void PlayerPostThink (void) +void PlayerPostThink () {SELFPARAM(); if(sv_maxidle > 0 && frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). if(IS_REAL_CLIENT(self)) diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc index 8b36e17531..58f1a9bd37 100644 --- a/qcsrc/server/cl_impulse.qc +++ b/qcsrc/server/cl_impulse.qc @@ -54,7 +54,7 @@ * 230 to 253: individual weapons (up to 24) */ -void ImpulseCommands (void) +void ImpulseCommands () {SELFPARAM(); int imp; vector org; @@ -145,8 +145,8 @@ void ImpulseCommands (void) break; } } - else - self.impulse = imp; // retry in next frame + //else + //self.impulse = imp; // retry in next frame } else if(imp == 21) { @@ -162,16 +162,16 @@ void ImpulseCommands (void) m = (imp - (210 + i)); // <0 for prev, =0 for best, >0 for next W_CycleWeapon(self.(cvar_cl_weaponpriorities[i]), m); } - else - self.impulse = imp; // retry in next frame + //else + //self.impulse = imp; // retry in next frame } else if(imp >= WEP_IMPULSE_BEGIN && imp <= WEP_IMPULSE_END) { if(!self.vehicle) if(self.deadflag == DEAD_NO) W_SwitchWeapon (imp - WEP_IMPULSE_BEGIN + WEP_FIRST); - else - self.impulse = imp; // retry in next frame + //else + //self.impulse = imp; // retry in next frame } // deploy waypoints else if (imp >= 30 && imp <= 49) diff --git a/qcsrc/server/cl_impulse.qh b/qcsrc/server/cl_impulse.qh index ced8119469..3a5ed9ecdf 100644 --- a/qcsrc/server/cl_impulse.qh +++ b/qcsrc/server/cl_impulse.qh @@ -36,5 +36,5 @@ * 230 to 253: individual weapons (up to 24) */ -void ImpulseCommands (void); +void ImpulseCommands (); #endif diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 01b9ab0d34..b49488b055 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -4,7 +4,6 @@ #include "cheats.qh" #include "g_damage.qh" #include "g_subs.qh" -#include "g_violence.qh" #include "miscfunctions.qh" #include "portals.qh" #include "teamplay.qh" @@ -34,7 +33,7 @@ void Drop_Special_Items(entity player) MUTATOR_CALLHOOK(DropSpecialItems, player); } -void CopyBody_Think(void) +void CopyBody_Think() {SELFPARAM(); if(self.CopyBody_nextthink && time > self.CopyBody_nextthink) { @@ -52,7 +51,7 @@ void CopyBody(float keepvelocity) {SELFPARAM(); if (self.effects & EF_NODRAW) return; - setself(spawn()); + setself(new(body)); self.enemy = this; self.lip = this.lip; self.colormap = this.colormap; @@ -62,7 +61,6 @@ void CopyBody(float keepvelocity) self.angles = this.angles; self.v_angle = this.v_angle; self.avelocity = this.avelocity; - self.classname = "body"; self.damageforcescale = this.damageforcescale; self.effects = this.effects; self.glowmod = this.glowmod; @@ -125,17 +123,6 @@ void CopyBody(float keepvelocity) setself(this); } -float player_getspecies() -{SELFPARAM(); - float s; - get_model_parameters(self.model, self.skin); - s = get_model_parameters_species; - get_model_parameters(string_null, 0); - if(s < 0) - return SPECIES_HUMAN; - return s; -} - void player_setupanimsformodel() {SELFPARAM(); // load animation info @@ -143,7 +130,7 @@ void player_setupanimsformodel() animdecide_setstate(self, 0, false); } -void player_anim (void) +void player_anim () {SELFPARAM(); int deadbits = (self.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); if(self.deadflag) { @@ -168,11 +155,14 @@ void player_anim (void) animdecide_setstate(self, animbits, false); animdecide_setimplicitstate(self, (self.flags & FL_ONGROUND)); - if (self.weaponentity) + .entity weaponentity = weaponentities[0]; // TODO: unhardcode { - updateanim(self.weaponentity); - if (!self.weaponentity.animstate_override) - setanim(self.weaponentity, self.weaponentity.anim_idle, true, false, false); + if (self.(weaponentity)) + { + updateanim(self.(weaponentity)); + if (!self.(weaponentity).animstate_override) + setanim(self.(weaponentity), self.(weaponentity).anim_idle, true, false, false); + } } } @@ -479,9 +469,18 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtyp self.dmg_take = self.dmg_take + take;//max(take - 10, 0); self.dmg_inflictor = inflictor; - float abot, vbot; - abot = (IS_BOT_CLIENT(attacker)); - vbot = (IS_BOT_CLIENT(self)); + if (self != attacker) { + float realdmg = damage - excess; + if (IS_PLAYER(attacker)) { + PlayerScore_Add(attacker, SP_DMG, realdmg); + } + if (IS_PLAYER(self)) { + PlayerScore_Add(self, SP_DMGTAKEN, realdmg); + } + } + + bool abot = (IS_BOT_CLIENT(attacker)); + bool vbot = (IS_BOT_CLIENT(self)); valid_damage_for_weaponstats = 0; Weapon awep = WEP_Null; @@ -643,17 +642,33 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtyp { Weapon w = get_weaponinfo(j); w.wr_resetplayer(w); - ATTACK_FINISHED_FOR(self, j) = 0; + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + ATTACK_FINISHED_FOR(self, j, slot) = 0; + } } } } -float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol) -// message "": do not say, just test flood control -// return value: -// 1 = accept -// 0 = reject -// -1 = fake accept +void MoveToTeam(entity client, int team_colour, int type) +{ + int lockteams_backup = lockteams; // backup any team lock + lockteams = 0; // disable locked teams + TeamchangeFrags(client); // move the players frags + SetPlayerColors(client, team_colour - 1); // set the players colour + Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player + lockteams = lockteams_backup; // restore the team lock + LogTeamchange(client.playerid, client.team, type); +} + +/** + * message "": do not say, just test flood control + * return value: + * 1 = accept + * 0 = reject + * -1 = fake accept + */ +int Say(entity source, float teamsay, entity privatesay, string msgin, bool floodcontrol) { string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, colorprefix; float flood; @@ -945,347 +960,281 @@ float Say(entity source, float teamsay, entity privatesay, string msgin, float f return ret; } -float GetVoiceMessageVoiceType(string type) +int GetVoiceMessageVoiceType(string type) { - if(type == "taunt") - return VOICETYPE_TAUNT; - if(type == "teamshoot") - return VOICETYPE_LASTATTACKER; + if (type == "taunt") return VOICETYPE_TAUNT; + if (type == "teamshoot") return VOICETYPE_LASTATTACKER; return VOICETYPE_TEAMRADIO; } .string GetVoiceMessageSampleField(string type) { - GetPlayerSoundSampleField_notFound = 0; - switch(type) + GetPlayerSoundSampleField_notFound = false; + switch (type) { -#define _VOICEMSG(m) case #m: return playersound_##m; - ALLVOICEMSGS -#undef _VOICEMSG +#define X(m) case #m: return playersound_##m; + ALLVOICEMSGS(X) +#undef X } - GetPlayerSoundSampleField_notFound = 1; + GetPlayerSoundSampleField_notFound = true; return playersound_taunt; } .string GetPlayerSoundSampleField(string type) { - GetPlayerSoundSampleField_notFound = 0; - switch(type) + GetPlayerSoundSampleField_notFound = false; + switch (type) { -#define _VOICEMSG(m) case #m: return playersound_##m; - ALLPLAYERSOUNDS -#undef _VOICEMSG +#define X(m) case #m: return playersound_##m; + ALLPLAYERSOUNDS(X) +#undef X } - GetPlayerSoundSampleField_notFound = 1; + GetPlayerSoundSampleField_notFound = true; return playersound_taunt; } -void PrecacheGlobalSound(string samplestring) +void PrecacheGlobalSound(string sample) { - float n, i; - tokenize_console(samplestring); - n = stof(argv(1)); - if(n > 0) + int n; { - for(i = 1; i <= n; ++i) - precache_sound(strcat(argv(0), ftos(i), ".wav")); + string s = cdr(sample); + if (s) n = stof(s); + else n = 0; + } + sample = car(sample); + if (n > 0) + { + for (int i = 1; i <= n; ++i) + precache_sound(sprintf("%s%d.wav", sample, i)); } else { - precache_sound(strcat(argv(0), ".wav")); + precache_sound(sprintf("%s.wav", sample)); } } +string allvoicesamples; + void PrecachePlayerSounds(string f) { int fh = fopen(f, FILE_READ); if (fh < 0) + { + LOG_WARNINGF("Player sound file not found: %s\n", f); return; + } for (string s; (s = fgets(fh)); ) { int n = tokenize_console(s); if (n != 3) { - if (n != 0) LOG_TRACEF("Invalid sound info line: %s\n", s); + if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); continue; } - PrecacheGlobalSound(strcat(argv(1), " ", argv(2))); + string file = argv(1); + string variants = argv(2); + PrecacheGlobalSound(strcat(file, " ", variants)); } fclose(fh); if (!allvoicesamples) { -#define _VOICEMSG(m) allvoicesamples = strcat(allvoicesamples, " ", #m); - ALLVOICEMSGS -#undef _VOICEMSG - allvoicesamples = strzone(substring(allvoicesamples, 1, strlen(allvoicesamples) - 1)); +#define X(m) allvoicesamples = strcat(allvoicesamples, " ", #m); + ALLVOICEMSGS(X) +#undef X + allvoicesamples = strzone(substring(allvoicesamples, 1, -1)); } } -void ClearPlayerSounds() -{SELFPARAM(); -#define _VOICEMSG(m) if(self.playersound_##m) { strunzone(self.playersound_##m); self.playersound_##m = string_null; } - ALLPLAYERSOUNDS - ALLVOICEMSGS -#undef _VOICEMSG +void ClearPlayerSounds(entity this) +{ +#define X(m) if (this.playersound_##m) { strunzone(this.playersound_##m); this.playersound_##m = string_null; } + ALLPLAYERSOUNDS(X) + ALLVOICEMSGS(X) +#undef X } -float LoadPlayerSounds(string f, float first) -{SELFPARAM(); - float fh; - string s; - var .string field; - fh = fopen(f, FILE_READ); - if(fh < 0) +bool LoadPlayerSounds(string f, bool strict) +{ + SELFPARAM(); + int fh = fopen(f, FILE_READ); + if (fh < 0) { - LOG_TRACE("Player sound file not found: ", f, "\n"); - return 0; + if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f); + return false; } - while((s = fgets(fh))) + for (string s; (s = fgets(fh)); ) { - if(tokenize_console(s) != 3) + int n = tokenize_console(s); + if (n != 3) + { + if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); continue; - field = GetPlayerSoundSampleField(argv(0)); - if(GetPlayerSoundSampleField_notFound) - field = GetVoiceMessageSampleField(argv(0)); - if(GetPlayerSoundSampleField_notFound) + } + string key = argv(0); + var .string field = GetPlayerSoundSampleField(key); + if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key); + if (GetPlayerSoundSampleField_notFound) + { + LOG_TRACEF("Invalid sound info field: %s\n", key); continue; - if (self.(field)) - strunzone(self.(field)); - self.(field) = strzone(strcat(argv(1), " ", argv(2))); + } + string file = argv(1); + string variants = argv(2); + if (self.(field)) strunzone(self.(field)); + self.(field) = strzone(strcat(file, " ", variants)); } fclose(fh); - return 1; -} - -void UpdatePlayerSounds() -{SELFPARAM(); - if(self.modelindex == self.modelindex_for_playersound) - if(self.skin == self.skin_for_playersound) - return; - self.modelindex_for_playersound = self.modelindex; - self.skin_for_playersound = self.skin; - ClearPlayerSounds(); - LoadPlayerSounds("sound/player/default.sounds", 1); - if(!autocvar_g_debug_defaultsounds) - if(!LoadPlayerSounds(get_model_datafilename(self.model, self.skin, "sounds"), 0)) - LoadPlayerSounds(get_model_datafilename(self.model, 0, "sounds"), 0); + return true; } -void FakeGlobalSound(string sample, float chan, float voicetype) -{SELFPARAM(); - float n; - float tauntrand; - - if(sample == "") - return; +.int modelindex_for_playersound; +.int skin_for_playersound; - tokenize_console(sample); - n = stof(argv(1)); - if(n > 0) - sample = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization - else - sample = strcat(argv(0), ".wav"); // randomization +void UpdatePlayerSounds(entity this) +{ + if (this.modelindex == this.modelindex_for_playersound && this.skin == this.skin_for_playersound) return; + this.modelindex_for_playersound = this.modelindex; + this.skin_for_playersound = this.skin; + ClearPlayerSounds(this); + LoadPlayerSounds("sound/player/default.sounds", true); + if (autocvar_g_debug_defaultsounds) return; + if (!LoadPlayerSounds(get_model_datafilename(this.model, this.skin, "sounds"), false)) + LoadPlayerSounds(get_model_datafilename(this.model, 0, "sounds"), true); +} - switch(voicetype) +void _GlobalSound(string sample, int chan, int voicetype, bool fake) +{ + SELFPARAM(); + if (sample == "") return; + int n; + { + string s = cdr(sample); + if (s) n = stof(s); + else n = 0; + } + sample = car(sample); + if (n > 0) sample = sprintf("%s%d.wav", sample, floor(random() * n + 1)); // randomization + else sample = sprintf("%s.wav", sample); + switch (voicetype) { case VOICETYPE_LASTATTACKER_ONLY: - break; case VOICETYPE_LASTATTACKER: - if(self.pusher) + { + if (!fake) { - msg_entity = self; - if(IS_REAL_CLIENT(msg_entity)) - soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NONE); + if (!this.pusher) break; + msg_entity = this.pusher; + if (IS_REAL_CLIENT(msg_entity)) + { + float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; + soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); + } } + if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break; + msg_entity = this; + if (IS_REAL_CLIENT(msg_entity)) soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NONE); break; + } case VOICETYPE_TEAMRADIO: - msg_entity = self; - if(msg_entity.cvar_cl_voice_directional == 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN); + { + #define X() \ + do \ + { \ + float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \ + soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ + } \ + while (0) + + if (fake) { msg_entity = this; X(); } else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); - break; - case VOICETYPE_AUTOTAUNT: - if(!sv_autotaunt) - break; - if(!sv_taunt) - break; - if(autocvar_sv_gentle) - break; - tauntrand = random(); - msg_entity = self; - if (tauntrand < msg_entity.cvar_cl_autotaunt) { - if (msg_entity.cvar_cl_voice_directional >= 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX)); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); + FOR_EACH_REALCLIENT(msg_entity) + { + if (!teamplay || msg_entity.team == this.team) X(); + } } + #undef X break; + } + case VOICETYPE_AUTOTAUNT: case VOICETYPE_TAUNT: - if(IS_PLAYER(self)) - if(self.deadflag == DEAD_NO) - animdecide_setaction(self, ANIMACTION_TAUNT, true); - if(!sv_taunt) - break; - if(autocvar_sv_gentle) - break; - msg_entity = self; - if (msg_entity.cvar_cl_voice_directional >= 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX)); + { + if (voicetype == VOICETYPE_AUTOTAUNT) if (!sv_autotaunt) { break; }else {} + else if (IS_PLAYER(this) && this.deadflag == DEAD_NO) animdecide_setaction(this, ANIMACTION_TAUNT, true); + if (!sv_taunt) break; + if (autocvar_sv_gentle) break; + float tauntrand = 0; + if (voicetype == VOICETYPE_AUTOTAUNT) tauntrand = random(); + #define X() \ + do \ + { \ + if (voicetype != VOICETYPE_AUTOTAUNT || tauntrand < msg_entity.cvar_cl_autotaunt) \ + { \ + float atten = (msg_entity.cvar_cl_voice_directional >= 1) \ + ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX) \ + : ATTEN_NONE; \ + soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ + } \ + } \ + while (0) + if (fake) + { + msg_entity = this; + X(); + } else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); - break; - case VOICETYPE_PLAYERSOUND: - msg_entity = self; - soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NORM); - break; - default: - backtrace("Invalid voice type!"); - break; - } -} - -void GlobalSound(string sample, float chan, float voicetype) -{SELFPARAM(); - float n; - float tauntrand; - - if(sample == "") - return; - - tokenize_console(sample); - n = stof(argv(1)); - if(n > 0) - sample = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization - else - sample = strcat(argv(0), ".wav"); // randomization - - switch(voicetype) - { - case VOICETYPE_LASTATTACKER_ONLY: - if(self.pusher) { - msg_entity = self.pusher; - if(IS_REAL_CLIENT(msg_entity)) + FOR_EACH_REALCLIENT(msg_entity) { - if(msg_entity.cvar_cl_voice_directional == 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); + X(); } } + #undef X break; - case VOICETYPE_LASTATTACKER: - if(self.pusher) + } + case VOICETYPE_PLAYERSOUND: + { + if (fake) { - msg_entity = self.pusher; - if(IS_REAL_CLIENT(msg_entity)) - { - if(msg_entity.cvar_cl_voice_directional == 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); - } - msg_entity = self; - if(IS_REAL_CLIENT(msg_entity)) - soundto(MSG_ONE, self, chan, sample, VOL_BASE, ATTEN_NONE); + msg_entity = this; + soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NORM); } - break; - case VOICETYPE_TEAMRADIO: - FOR_EACH_REALCLIENT(msg_entity) - if(!teamplay || msg_entity.team == self.team) - { - if(msg_entity.cvar_cl_voice_directional == 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_MIN); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); - } - break; - case VOICETYPE_AUTOTAUNT: - if(!sv_autotaunt) - break; - if(!sv_taunt) - break; - if(autocvar_sv_gentle) - break; - tauntrand = random(); - FOR_EACH_REALCLIENT(msg_entity) - if (tauntrand < msg_entity.cvar_cl_autotaunt) - { - if (msg_entity.cvar_cl_voice_directional >= 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX)); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); - } - break; - case VOICETYPE_TAUNT: - if(IS_PLAYER(self)) - if(self.deadflag == DEAD_NO) - animdecide_setaction(self, ANIMACTION_TAUNT, true); - if(!sv_taunt) - break; - if(autocvar_sv_gentle) - break; - FOR_EACH_REALCLIENT(msg_entity) + else { - if (msg_entity.cvar_cl_voice_directional >= 1) - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, ATTEN_MAX)); - else - soundto(MSG_ONE, self, chan, sample, VOL_BASEVOICE, ATTEN_NONE); + _sound(this, chan, sample, VOL_BASE, ATTEN_NORM); } break; - case VOICETYPE_PLAYERSOUND: - _sound(self, chan, sample, VOL_BASE, ATTEN_NORM); - break; + } default: + { backtrace("Invalid voice type!"); break; + } } } -void PlayerSound(.string samplefield, float chan, float voicetype) -{SELFPARAM(); - GlobalSound(self.(samplefield), chan, voicetype); +void PlayerSound(.string samplefield, int chan, float voicetype) +{ + SELFPARAM(); + _GlobalSound(this.(samplefield), chan, voicetype, false); } void VoiceMessage(string type, string msg) -{SELFPARAM(); - float voicetype, ownteam; - float flood; +{ + SELFPARAM(); var .string sample = GetVoiceMessageSampleField(type); - - if(GetPlayerSoundSampleField_notFound) + if (GetPlayerSoundSampleField_notFound) { - sprint(self, strcat("Invalid voice. Use one of: ", allvoicesamples, "\n")); + sprint(this, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples)); return; } - - voicetype = GetVoiceMessageVoiceType(type); - ownteam = (voicetype == VOICETYPE_TEAMRADIO); - - flood = Say(self, ownteam, world, msg, 1); - - if (IS_SPEC(self) || IS_OBSERVER(self) || flood < 0) - FakeGlobalSound(self.(sample), CH_VOICE, voicetype); - else if (flood > 0) - GlobalSound(self.(sample), CH_VOICE, voicetype); -} - -void MoveToTeam(entity client, float team_colour, float type) -{ - float lockteams_backup; - - lockteams_backup = lockteams; // backup any team lock - - lockteams = 0; // disable locked teams - - TeamchangeFrags(client); // move the players frags - SetPlayerColors(client, team_colour - 1); // set the players colour - Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player - - lockteams = lockteams_backup; // restore the team lock - - LogTeamchange(client.playerid, client.team, type); + int voicetype = GetVoiceMessageVoiceType(type); + bool ownteam = (voicetype == VOICETYPE_TEAMRADIO); + int flood = Say(this, ownteam, world, msg, true); + bool fake; + if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true; + else if (flood > 0) fake = false; + else return; + _GlobalSound(this.(sample), CH_VOICE, voicetype, fake); } diff --git a/qcsrc/server/cl_player.qh b/qcsrc/server/cl_player.qh index 27d1bd0dda..76c49d852f 100644 --- a/qcsrc/server/cl_player.qh +++ b/qcsrc/server/cl_player.qh @@ -6,17 +6,15 @@ .float istypefrag; .float CopyBody_nextthink; -.void(void) CopyBody_think; -void CopyBody_Think(void); +.void() CopyBody_think; +void CopyBody_Think(); void CopyBody(float keepvelocity); -float player_getspecies(); - void player_setupanimsformodel(); -void player_anim (void); +void player_anim(); -void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); +void PlayerCorpseDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); // g__str: // If 0, default is used. @@ -25,51 +23,102 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, int de // For consistency, negative values there are mapped to zero too. #define GAMETYPE_DEFAULTED_SETTING(str) \ ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ - (gametype_setting_tmp < 0) ? 0 : \ - (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) : \ - gametype_setting_tmp) - + (gametype_setting_tmp < 0) ? 0 \ + : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \ + : gametype_setting_tmp) void calculate_player_respawn_time(); void ClientKill_Now_TeamChange(); -void PlayerDamage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); - -.float muted; // to be used by prvm_edictset server playernumber muted 1 -float Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); -// message "": do not say, just test flood control -// return value: -// 1 = accept -// 0 = reject -// -1 = fake accept +void MoveToTeam(entity client, float team_colour, float type); +void PlayerDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +/** to be used by `prvm_edictset server playernumber muted 1` */ +.float muted; +int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); + +// player sounds, voice messages +// TODO implemented fall and falling +#define ALLPLAYERSOUNDS(X) \ + X(death) \ + X(drown) \ + X(fall) \ + X(falling) \ + X(gasp) \ + X(jump) \ + X(pain100) \ + X(pain25) \ + X(pain50) \ + X(pain75) + +#define ALLVOICEMSGS(X) \ + X(attack) \ + X(attackinfive) \ + X(coverme) \ + X(defend) \ + X(freelance) \ + X(incoming) \ + X(meet) \ + X(needhelp) \ + X(seenflag) \ + X(taunt) \ + X(teamshoot) + +// reserved sound names for the future (some models lack sounds for them): +// _VOICEMSG(flagcarriertakingdamage) +// _VOICEMSG(getflag) +// reserved sound names for the future (ALL models lack sounds for them): +// _VOICEMSG(affirmative) +// _VOICEMSG(attacking) +// _VOICEMSG(defending) +// _VOICEMSG(roaming) +// _VOICEMSG(onmyway) +// _VOICEMSG(droppedflag) +// _VOICEMSG(negative) +// _VOICEMSG(seenenemy) + +#define X(m) .string playersound_##m; +ALLPLAYERSOUNDS(X) +ALLVOICEMSGS(X) +#undef X + +bool GetPlayerSoundSampleField_notFound; float GetVoiceMessageVoiceType(string type); - -string allvoicesamples; .string GetVoiceMessageSampleField(string type); - .string GetPlayerSoundSampleField(string type); - void PrecacheGlobalSound(string samplestring); - void PrecachePlayerSounds(string f); - -void ClearPlayerSounds(); - -float LoadPlayerSounds(string f, float first); - -.int modelindex_for_playersound; -.int skin_for_playersound; -void UpdatePlayerSounds(); - -void FakeGlobalSound(string sample, float chan, float voicetype); - -void GlobalSound(string sample, float chan, float voicetype); - +void ClearPlayerSounds(entity this); +float LoadPlayerSounds(string f, bool strict); +void UpdatePlayerSounds(entity this); +#define FakeGlobalSound(sample, chan, voicetype) _GlobalSound(sample, chan, voicetype, true) +void _GlobalSound(string sample, float chan, float voicetype, bool fake); +#define GlobalSound(def, chan, voicetype) _GlobalSound((def).m_globalsoundstr, chan, voicetype, false) void PlayerSound(.string samplefield, float chan, float voicetype); - void VoiceMessage(string type, string msg); -void MoveToTeam(entity client, float team_colour, float type); +.string m_globalsoundstr; +REGISTRY(GlobalSounds, BITS(8) - 1) +#define GlobalSounds_from(i) _GlobalSounds_from(i, NULL) +#define REGISTER_GLOBALSOUND(id, str) \ + REGISTER(GlobalSounds, GS, id, m_id, new(GlobalSound)) \ + { \ + make_pure(this); \ + this.m_globalsoundstr = str; \ + } +REGISTER_REGISTRY(GlobalSounds) +REGISTRY_SORT(GlobalSounds, 0) +REGISTRY_CHECK(GlobalSounds) +PRECACHE(GlobalSounds) +{ + FOREACH(GlobalSounds, true, LAMBDA(PrecacheGlobalSound(it.m_globalsoundstr))); +} + +REGISTER_GLOBALSOUND(STEP, "misc/footstep0 6") +REGISTER_GLOBALSOUND(STEP_METAL, "misc/metalfootstep0 6") +REGISTER_GLOBALSOUND(FALL, "misc/hitground 4") +REGISTER_GLOBALSOUND(FALL_METAL, "misc/metalhitground 4") + #endif diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index f325dcd72c..30d6291f64 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -15,11 +15,11 @@ void BanCommand_ban(float request, float argc, string command) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) + if (argc >= 2) { string ip = argv(1); float reason_arg, bantime; @@ -51,7 +51,7 @@ void BanCommand_ban(float request, float argc, string command) void BanCommand_banlist(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -72,18 +72,18 @@ void BanCommand_banlist(float request) void BanCommand_kickban(float request, float argc, string command) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) + if (argc >= 2) { entity client = GetIndexedEntity(argc, 1); float accepted = VerifyKickableEntity(client); float reason_arg, bantime, masksize; string reason; - if(accepted > 0) + if (accepted > 0) { reason_arg = next_token; @@ -117,18 +117,18 @@ void BanCommand_kickban(float request, float argc, string command) } } -void BanCommand_mute(float request, float argc, string command) // TODO: Add a sort of mute-"ban" which allows players to be muted based on IP/cryptokey +void BanCommand_mute(float request, float argc, string command) // TODO: Add a sort of mute-"ban" which allows players to be muted based on IP/cryptokey { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) + if (argc >= 2) { entity client = GetFilteredEntity(argv(1)); float accepted = VerifyClientEntity(client, true, false); - if(accepted > 0) + if (accepted > 0) { client.muted = true; return; @@ -154,39 +154,32 @@ void BanCommand_mute(float request, float argc, string command) // TODO: Add a s void BanCommand_unban(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { float tmp_number = -1; string tmp_string; - if(substring(argv(1), 0, 1) == "#") + if (substring(argv(1), 0, 1) == "#") { tmp_string = substring(argv(1), 1, -1); - if(tmp_string != "") // is it all one token? like #1 - { + if (tmp_string != "") // is it all one token? like #1 tmp_number = stof(tmp_string); - } - else if(argc > 2) // no, it's two tokens? # 1 - { + else if (argc > 2) // no, it's two tokens? # 1 tmp_number = stof(argv(2)); - } - else - tmp_number = -1; + else tmp_number = -1; } - else // maybe it's ONLY a number? + else // maybe it's ONLY a number? { tmp_number = stof(argv(1)); - if((tmp_number == 0) && (argv(1) != "0")) - { tmp_number = -1; } - } + if ((tmp_number == 0) && (argv(1) != "0")) tmp_number = -1; } - if(tmp_number >= 0) + if (tmp_number >= 0) { Ban_Delete(tmp_number); return; @@ -207,16 +200,16 @@ void BanCommand_unban(float request, float argc) void BanCommand_unmute(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) + if (argc >= 2) { entity client = GetFilteredEntity(argv(1)); float accepted = VerifyClientEntity(client, true, false); - if(accepted > 0) + if (accepted > 0) { client.muted = false; return; @@ -244,22 +237,22 @@ void BanCommand_unmute(float request, float argc) ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void BanCommand_(float request) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; - } - - default: - case CMD_REQUEST_USAGE: - { - print("\nUsage:^3 sv_cmd \n"); - print(" No arguments required.\n"); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 sv_cmd \n"); + print(" No arguments required.\n"); + return; + } + } } */ @@ -269,7 +262,7 @@ void BanCommand_(float request) // ================================== // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define BAN_COMMANDS(request,arguments,command) \ +#define BAN_COMMANDS(request, arguments, command) \ BAN_COMMAND("ban", BanCommand_ban(request, arguments, command), "Ban an IP address or a range of addresses (like 1.2.3)") \ BAN_COMMAND("banlist", BanCommand_banlist(request), "List all existing bans") \ BAN_COMMAND("kickban", BanCommand_kickban(request, arguments, command), "Disconnect a client and ban it at the same time") \ @@ -280,46 +273,42 @@ void BanCommand_(float request) void BanCommand_macro_help() { - #define BAN_COMMAND(name,function,description) \ - { if(strtolower(description) != "") { LOG_INFO(" ^2", name, "^7: ", description, "\n"); } } + #define BAN_COMMAND(name, function, description) \ + { if (strtolower(description) != "") { LOG_INFO(" ^2", name, "^7: ", description, "\n"); } } BAN_COMMANDS(0, 0, ""); - #undef BAN_COMMAND - - return; +#undef BAN_COMMAND } float BanCommand_macro_command(float argc, string command) { - #define BAN_COMMAND(name,function,description) \ - { if(name == strtolower(argv(0))) { function; return true; } } + #define BAN_COMMAND(name, function, description) \ + { if (name == strtolower(argv(0))) { function; return true; } } BAN_COMMANDS(CMD_REQUEST_COMMAND, argc, command); - #undef BAN_COMMAND +#undef BAN_COMMAND return false; } float BanCommand_macro_usage(float argc) { - #define BAN_COMMAND(name,function,description) \ - { if(name == strtolower(argv(1))) { function; return true; } } + #define BAN_COMMAND(name, function, description) \ + { if (name == strtolower(argv(1))) { function; return true; } } BAN_COMMANDS(CMD_REQUEST_USAGE, argc, ""); - #undef BAN_COMMAND +#undef BAN_COMMAND return false; } void BanCommand_macro_write_aliases(float fh) { - #define BAN_COMMAND(name,function,description) \ - { if(strtolower(description) != "") { CMD_Write_Alias("qc_cmd_sv", name, description); } } + #define BAN_COMMAND(name, function, description) \ + { if (strtolower(description) != "") { CMD_Write_Alias("qc_cmd_sv", name, description); } } BAN_COMMANDS(0, 0, ""); - #undef BAN_COMMAND - - return; +#undef BAN_COMMAND } float BanCommand(string command) @@ -331,10 +320,8 @@ float BanCommand(string command) // argv: 0 - 1 - 2 - 3 // cmd vote - master - login - password - if(BanCommand_macro_command(argc, command)) // continue as usual and scan for normal commands - { - return true; // handled by one of the above GenericCommand_* functions - } + if (BanCommand_macro_command(argc, command)) // continue as usual and scan for normal commands + return true; // handled by one of the above GenericCommand_* functions return false; } diff --git a/qcsrc/server/command/banning.qh b/qcsrc/server/command/banning.qh index 17eca95c73..a330ff1b0b 100644 --- a/qcsrc/server/command/banning.qh +++ b/qcsrc/server/command/banning.qh @@ -6,8 +6,8 @@ // Last updated: December 29th, 2011 // ===================================== -#define GET_BAN_ARG(v,d) if(argc > reason_arg) { if((v = stof(argv(reason_arg))) != 0) ++reason_arg; else v = d; } else { v = d; } -#define GET_BAN_REASON(v,d) if(argc > reason_arg) v = substring(command, argv_start_index(reason_arg), strlen(command) - argv_start_index(reason_arg)); else v = d; +#define GET_BAN_ARG(v, d) if (argc > reason_arg) { if ((v = stof(argv(reason_arg))) != 0) ++reason_arg; else v = d; } else { v = d; } +#define GET_BAN_REASON(v, d) if (argc > reason_arg) v = substring(command, argv_start_index(reason_arg), strlen(command) - argv_start_index(reason_arg)); else v = d; void Ban_KickBanClient(entity client, float bantime, float masksize, string reason); void Ban_View(); diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 69517738b7..ac21bc62dc 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -35,7 +35,7 @@ #include "../../lib/warpzone/common.qh" -void ClientKill_TeamChange (float targetteam); // 0 = don't change, -1 = auto, -2 = spec +void ClientKill_TeamChange(float targetteam); // 0 = don't change, -1 = auto, -2 = spec // ========================================================= // Server side networked commands code, reworked by Samual @@ -43,13 +43,14 @@ void ClientKill_TeamChange (float targetteam); // 0 = don't change, -1 = auto, - // ========================================================= float SV_ParseClientCommand_floodcheck() -{SELFPARAM(); - if (!timeout_status) // not while paused +{ + SELFPARAM(); + if (!timeout_status) // not while paused { - if(time <= (self.cmd_floodtime + autocvar_sv_clientcommand_antispam_time)) + if (time <= (self.cmd_floodtime + autocvar_sv_clientcommand_antispam_time)) { self.cmd_floodcount += 1; - if(self.cmd_floodcount > autocvar_sv_clientcommand_antispam_count) { return false; } // too much spam, halt + if (self.cmd_floodcount > autocvar_sv_clientcommand_antispam_count) return false; // too much spam, halt } else { @@ -57,7 +58,7 @@ float SV_ParseClientCommand_floodcheck() self.cmd_floodcount = 1; } } - return true; // continue, as we're not flooding yet + return true; // continue, as we're not flooding yet } @@ -66,12 +67,13 @@ float SV_ParseClientCommand_floodcheck() // ======================= void ClientCommand_autoswitch(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { self.autoswitch = InterpretBoolean(argv(1)); sprint(self, strcat("^1autoswitch is currently turned ", (self.autoswitch ? "on" : "off"), ".\n")); @@ -90,30 +92,31 @@ void ClientCommand_autoswitch(float request, float argc) } } -void ClientCommand_clientversion(float request, float argc) // internal command, used only by code -{SELFPARAM(); - switch(request) +void ClientCommand_clientversion(float request, float argc) // internal command, used only by code +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { - if(IS_CLIENT(self)) + if (IS_CLIENT(self)) { self.version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1))); - if(self.version < autocvar_gameversion_min || self.version > autocvar_gameversion_max) + if (self.version < autocvar_gameversion_min || self.version > autocvar_gameversion_max) { self.version_mismatch = 1; - ClientKill_TeamChange(-2); // observe + ClientKill_TeamChange(-2); // observe } - else if(autocvar_g_campaign || autocvar_g_balance_teams) + else if (autocvar_g_campaign || autocvar_g_balance_teams) { - //JoinBestTeam(self, false, true); + // JoinBestTeam(self, false, true); } - else if(teamplay && !autocvar_sv_spectate && !(self.team_forced > 0)) + else if (teamplay && !autocvar_sv_spectate && !(self.team_forced > 0)) { - self.classname = STR_OBSERVER; // really? + self.classname = STR_OBSERVER; // really? stuffcmd(self, "menu_showteamselect\n"); } } @@ -133,16 +136,16 @@ void ClientCommand_clientversion(float request, float argc) // internal command, } } -void ClientCommand_mv_getpicture(float request, float argc) // internal command, used only by code -{SELFPARAM(); - switch(request) +void ClientCommand_mv_getpicture(float request, float argc) // internal command, used only by code +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { - if(intermission_running) - MapVote_SendPicture(stof(argv(1))); + if (intermission_running) MapVote_SendPicture(stof(argv(1))); return; } @@ -160,21 +163,20 @@ void ClientCommand_mv_getpicture(float request, float argc) // internal command, } void ClientCommand_join(float request) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(IS_CLIENT(self)) + if (IS_CLIENT(self)) { - if(!IS_PLAYER(self) && !lockteams && !gameover) + if (!IS_PLAYER(self) && !lockteams && !gameover) { - if(self.caplayer) - return; - if(nJoinAllowed(self)) + if (self.caplayer) return; + if (nJoinAllowed(self)) { - if(autocvar_g_campaign) { campaign_bots_may_start = 1; } - + if (autocvar_g_campaign) campaign_bots_may_start = 1; self.classname = STR_PLAYER; PlayerScore_Clear(self); Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN); @@ -183,12 +185,12 @@ void ClientCommand_join(float request) } else { - //player may not join because of g_maxplayers is set + // player may not join because of g_maxplayers is set Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_JOIN_PREVENT); } } } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -202,26 +204,27 @@ void ClientCommand_join(float request) } void ClientCommand_physics(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { string command = strtolower(argv(1)); - if(!autocvar_g_physics_clientselect) + if (!autocvar_g_physics_clientselect) { sprint(self, "Client physics selection is currently disabled.\n"); return; } - if(command == "list" || command == "help") + if (command == "list" || command == "help") { sprint(self, strcat("Available physics sets: \n\n", autocvar_g_physics_clientselect_options, " default\n")); return; } - if(Physics_Valid(command) || command == "default") + if (Physics_Valid(command) || command == "default") { stuffcmd(self, strcat("\nseta cl_physics ", command, "\nsendcvar cl_physics\n")); sprint(self, strcat("^2Physics set successfully changed to ^3", command, "\n")); @@ -241,21 +244,22 @@ void ClientCommand_physics(float request, float argc) } } -void ClientCommand_ready(float request) // todo: anti-spam for toggling readyness -{SELFPARAM(); - switch(request) +void ClientCommand_ready(float request) // todo: anti-spam for toggling readyness +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(IS_CLIENT(self)) + if (IS_CLIENT(self)) { - if(warmup_stage || autocvar_sv_ready_restart || g_race_qualifying == 2) + if (warmup_stage || autocvar_sv_ready_restart || g_race_qualifying == 2) { - if(!readyrestart_happened || autocvar_sv_ready_restart_repeatable) + if (!readyrestart_happened || autocvar_sv_ready_restart_repeatable) { - if(time < game_starttime) // game is already restarting + if (time < game_starttime) // game is already restarting return; - if (self.ready) // toggle + if (self.ready) // toggle { self.ready = false; bprint(self.netname, "^2 is ^1NOT^2 ready\n"); @@ -267,14 +271,15 @@ void ClientCommand_ready(float request) // todo: anti-spam for toggling readynes } // cannot reset the game while a timeout is active! - if (!timeout_status) - ReadyCount(); - } else { + if (!timeout_status) ReadyCount(); + } + else + { sprint(self, "^1Game has already been restarted\n"); } } } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -288,13 +293,14 @@ void ClientCommand_ready(float request) // todo: anti-spam for toggling readynes } void ClientCommand_say(float request, float argc, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) { Say(self, false, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); } - return; // never fall through to usage + if (argc >= 2) Say(self, false, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); + return; // never fall through to usage } default: @@ -308,13 +314,14 @@ void ClientCommand_say(float request, float argc, string command) } void ClientCommand_say_team(float request, float argc, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) { Say(self, true, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); } - return; // never fall through to usage + if (argc >= 2) Say(self, true, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); + return; // never fall through to usage } default: @@ -328,45 +335,58 @@ void ClientCommand_say_team(float request, float argc, string command) } void ClientCommand_selectteam(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { - if(IS_CLIENT(self)) + if (IS_CLIENT(self)) { - if(teamplay) - if(self.team_forced <= 0) + if (teamplay) + { + if (self.team_forced <= 0) + { if (!lockteams) { float selection; - switch(argv(1)) + switch (argv(1)) { - case "red": selection = NUM_TEAM_1; break; - case "blue": selection = NUM_TEAM_2; break; - case "yellow": selection = NUM_TEAM_3; break; - case "pink": selection = NUM_TEAM_4; break; - case "auto": selection = (-1); break; - - default: selection = 0; break; + case "red": selection = NUM_TEAM_1; + break; + case "blue": selection = NUM_TEAM_2; + break; + case "yellow": selection = NUM_TEAM_3; + break; + case "pink": selection = NUM_TEAM_4; + break; + case "auto": selection = (-1); + break; + + default: selection = 0; + break; } - if(selection) + if (selection) { - if(self.team == selection && self.deadflag == DEAD_NO) + if (self.team == selection && self.deadflag == DEAD_NO) + { sprint(self, "^7You already are on that team.\n"); - else if(self.wasplayer && autocvar_g_changeteam_banned) + } + else if (self.wasplayer && autocvar_g_changeteam_banned) + { sprint(self, "^1You cannot change team, forbidden by the server.\n"); + } else { - if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance) + if (autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance) { CheckAllowedTeams(self); GetTeamCounts(self); - if(!TeamSmallerEqThanTeam(Team_TeamToNumber(selection), Team_TeamToNumber(self.team), self)) + if (!TeamSmallerEqThanTeam(Team_TeamToNumber(selection), Team_TeamToNumber(self.team), self)) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM); return; @@ -377,11 +397,19 @@ void ClientCommand_selectteam(float request, float argc) } } else + { sprint(self, "^7The game has already begun, you must wait until the next map to be able to join a team.\n"); + } + } else + { sprint(self, "^7selectteam can not be used as your team is forced\n"); + } + } else + { sprint(self, "^7selectteam can only be used in teamgames\n"); + } } return; } @@ -400,12 +428,13 @@ void ClientCommand_selectteam(float request, float argc) } void ClientCommand_selfstuff(float request, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { stuffcmd(self, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); return; @@ -424,17 +453,18 @@ void ClientCommand_selfstuff(float request, string command) } void ClientCommand_sentcvar(float request, float argc, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { - //float tokens; + // float tokens; string s; - if(argc == 2) // undefined cvar: use the default value on the server then + if (argc == 2) // undefined cvar: use the default value on the server then { s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\""); tokenize_console(s); @@ -458,22 +488,21 @@ void ClientCommand_sentcvar(float request, float argc, string command) } void ClientCommand_spectate(float request) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(IS_CLIENT(self)) + if (IS_CLIENT(self)) { int mutator_returnvalue = MUTATOR_CALLHOOK(ClientCommand_Spectate, self); - if(mutator_returnvalue == MUT_SPECCMD_RETURN) - return; + if (mutator_returnvalue == MUT_SPECCMD_RETURN) return; - if((IS_PLAYER(self) || mutator_returnvalue == MUT_SPECCMD_FORCE) && autocvar_sv_spectate == 1) - ClientKill_TeamChange(-2); // observe + if ((IS_PLAYER(self) || mutator_returnvalue == MUT_SPECCMD_FORCE) && autocvar_sv_spectate == 1) ClientKill_TeamChange(-2); // observe } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -487,12 +516,13 @@ void ClientCommand_spectate(float request) } void ClientCommand_suggestmap(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { sprint(self, strcat(MapVote_Suggest(argv(1)), "\n")); return; @@ -511,19 +541,20 @@ void ClientCommand_suggestmap(float request, float argc) } void ClientCommand_tell(float request, float argc, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 3) + if (argc >= 3) { entity tell_to = GetIndexedEntity(argc, 1); float tell_accepted = VerifyClientEntity(tell_to, true, false); - if(tell_accepted > 0) // the target is a real client + if (tell_accepted > 0) // the target is a real client { - if(tell_to != self) // and we're allowed to send to them :D + if (tell_to != self) // and we're allowed to send to them :D { // workaround for argv indexes indexing ascii chars instead of utf8 chars // In this case when the player name contains utf8 chars @@ -541,7 +572,7 @@ void ClientCommand_tell(float request, float argc, string command) } else { print_to(self, "You can't ^2tell^7 a message to yourself."); return; } } - else if(argv(1) == "#0") + else if (argv(1) == "#0") { trigger_magicear_processmessage_forallears(self, -1, world, substring(command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token))); return; @@ -562,17 +593,16 @@ void ClientCommand_tell(float request, float argc, string command) } void ClientCommand_voice(float request, float argc, string command) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { - if(argc >= 3) - VoiceMessage(argv(1), substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2))); - else - VoiceMessage(argv(1), ""); + if (argc >= 3) VoiceMessage(argv(1), substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2))); + else VoiceMessage(argv(1), ""); return; } @@ -594,22 +624,22 @@ void ClientCommand_voice(float request, float argc, string command) ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void ClientCommand_(float request) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; // never fall through to usage - } - - default: - case CMD_REQUEST_USAGE: - { - sprint(self, "\nUsage:^3 cmd \n"); - sprint(self, " No arguments required.\n"); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; // never fall through to usage + } + + default: + case CMD_REQUEST_USAGE: + { + sprint(self, "\nUsage:^3 cmd \n"); + sprint(self, " No arguments required.\n"); + return; + } + } } */ @@ -619,7 +649,7 @@ void ClientCommand_(float request) // ===================================== // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define CLIENT_COMMANDS(request,arguments,command) \ +#define CLIENT_COMMANDS(request, arguments, command) \ CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(request, arguments), "Whether or not to switch automatically when getting a better weapon") \ CLIENT_COMMAND("clientversion", ClientCommand_clientversion(request, arguments), "Release version of the game") \ CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(request, arguments), "Retrieve mapshot picture from the server") \ @@ -639,47 +669,44 @@ void ClientCommand_(float request) /* nothing */ void ClientCommand_macro_help() -{SELFPARAM(); - #define CLIENT_COMMAND(name,function,description) \ +{ + SELFPARAM(); + #define CLIENT_COMMAND(name, function, description) \ { sprint(self, " ^2", name, "^7: ", description, "\n"); } CLIENT_COMMANDS(0, 0, ""); - #undef CLIENT_COMMAND - - return; +#undef CLIENT_COMMAND } float ClientCommand_macro_command(float argc, string command) { - #define CLIENT_COMMAND(name,function,description) \ - { if(name == strtolower(argv(0))) { function; return true; } } + #define CLIENT_COMMAND(name, function, description) \ + { if (name == strtolower(argv(0))) { function; return true; } } CLIENT_COMMANDS(CMD_REQUEST_COMMAND, argc, command); - #undef CLIENT_COMMAND +#undef CLIENT_COMMAND return false; } float ClientCommand_macro_usage(float argc) { - #define CLIENT_COMMAND(name,function,description) \ - { if(name == strtolower(argv(1))) { function; return true; } } + #define CLIENT_COMMAND(name, function, description) \ + { if (name == strtolower(argv(1))) { function; return true; } } CLIENT_COMMANDS(CMD_REQUEST_USAGE, argc, ""); - #undef CLIENT_COMMAND +#undef CLIENT_COMMAND return false; } void ClientCommand_macro_write_aliases(float fh) { - #define CLIENT_COMMAND(name,function,description) \ + #define CLIENT_COMMAND(name, function, description) \ { CMD_Write_Alias("qc_cmd_cmd", name, description); } CLIENT_COMMANDS(0, 0, ""); - #undef CLIENT_COMMAND - - return; +#undef CLIENT_COMMAND } // ====================================== @@ -688,19 +715,18 @@ void ClientCommand_macro_write_aliases(float fh) // If this function exists, server game code parses clientcommand before the engine code gets it. void SV_ParseClientCommand(string command) -{SELFPARAM(); +{ + SELFPARAM(); // If invalid UTF-8, don't even parse it string command2 = ""; float len = strlen(command); float i; for (i = 0; i < len; ++i) command2 = strcat(command2, chr2str(str2chr(command, i))); - if (command != command2) - return; + if (command != command2) return; // if we're banned, don't even parse the command - if(Ban_MaybeEnforceBanOnce(self)) - return; + if (Ban_MaybeEnforceBanOnce(self)) return; float argc = tokenize_console(command); @@ -710,28 +736,26 @@ void SV_ParseClientCommand(string command) // cmd vote - master - login - password // for floodcheck - switch(strtolower(argv(0))) + switch (strtolower(argv(0))) { // exempt commands which are not subject to floodcheck - case "begin": break; // handled by engine in host_cmd.c - case "download": break; // handled by engine in cl_parse.c - case "mv_getpicture": break; // handled by server in this file - case "pause": break; // handled by engine in host_cmd.c - case "prespawn": break; // handled by engine in host_cmd.c - case "sentcvar": break; // handled by server in this file - case "spawn": break; // handled by engine in host_cmd.c + case "begin": break; // handled by engine in host_cmd.c + case "download": break; // handled by engine in cl_parse.c + case "mv_getpicture": break; // handled by server in this file + case "pause": break; // handled by engine in host_cmd.c + case "prespawn": break; // handled by engine in host_cmd.c + case "sentcvar": break; // handled by server in this file + case "spawn": break; // handled by engine in host_cmd.c default: - if(SV_ParseClientCommand_floodcheck()) - break; // "true": continue, as we're not flooding yet - else - return; // "false": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n"); + if (SV_ParseClientCommand_floodcheck()) break; // "true": continue, as we're not flooding yet + else return; // "false": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n"); } /* NOTE: should this be disabled? It can be spammy perhaps, but hopefully it's okay for now */ - if(argv(0) == "help") + if (argv(0) == "help") { - if(argc == 1) + if (argc == 1) { sprint(self, "\nClient networked commands:\n"); ClientCommand_macro_help(); @@ -743,31 +767,33 @@ void SV_ParseClientCommand(string command) sprint(self, "For help about a specific command, type cmd help COMMAND\n"); return; } - else if(CommonCommand_macro_usage(argc, self)) // Instead of trying to call a command, we're going to see detailed information about it + else if (CommonCommand_macro_usage(argc, self)) // Instead of trying to call a command, we're going to see detailed information about it { return; } - else if(ClientCommand_macro_usage(argc)) // same, but for normal commands now + else if (ClientCommand_macro_usage(argc)) // same, but for normal commands now { return; } } - else if(MUTATOR_CALLHOOK(SV_ParseClientCommand, strtolower(argv(0)), argc, command)) + else if (MUTATOR_CALLHOOK(SV_ParseClientCommand, strtolower(argv(0)), argc, command)) { - return; // handled by a mutator + return; // handled by a mutator } - else if(CheatCommand(argc)) + else if (CheatCommand(argc)) { - return; // handled by server/cheats.qc + return; // handled by server/cheats.qc } - else if(CommonCommand_macro_command(argc, self, command)) + else if (CommonCommand_macro_command(argc, self, command)) { - return; // handled by server/command/common.qc + return; // handled by server/command/common.qc } - else if(ClientCommand_macro_command(argc, command)) // continue as usual and scan for normal commands + else if (ClientCommand_macro_command(argc, command)) // continue as usual and scan for normal commands { - return; // handled by one of the above ClientCommand_* functions + return; // handled by one of the above ClientCommand_* functions } else + { clientcommand(self, command); + } } diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index 8d80458723..69f0222cb7 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -16,38 +16,30 @@ // select the proper prefix for usage and other messages string GetCommandPrefix(entity caller) { - if(caller) - return "cmd"; - else - return "sv_cmd"; + if (caller) return "cmd"; + else return "sv_cmd"; } // if client return player nickname, or if server return admin nickname string GetCallerName(entity caller) { - if(caller) - return caller.netname; - else - return admin_name(); //((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : autocvar_hostname); + if (caller) return caller.netname; + else return admin_name(); // ((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : autocvar_hostname); } // verify that the client provided is acceptable for kicking float VerifyKickableEntity(entity client) { - if (!IS_REAL_CLIENT(client)) - return CLIENT_NOT_REAL; + if (!IS_REAL_CLIENT(client)) return CLIENT_NOT_REAL; return CLIENT_ACCEPTABLE; } // verify that the client provided is acceptable for use float VerifyClientEntity(entity client, float must_be_real, float must_be_bots) { - if (!IS_CLIENT(client)) - return CLIENT_DOESNT_EXIST; - else if(must_be_real && !IS_REAL_CLIENT(client)) - return CLIENT_NOT_REAL; - else if(must_be_bots && !IS_BOT_CLIENT(client)) - return CLIENT_NOT_BOT; + if (!IS_CLIENT(client)) return CLIENT_DOESNT_EXIST; + else if (must_be_real && !IS_REAL_CLIENT(client)) return CLIENT_NOT_REAL; + else if (must_be_bots && !IS_BOT_CLIENT(client)) return CLIENT_NOT_BOT; return CLIENT_ACCEPTABLE; } @@ -55,22 +47,28 @@ float VerifyClientEntity(entity client, float must_be_real, float must_be_bots) // if the client is not acceptable, return a string to be used for error messages string GetClientErrorString_color(float clienterror, string original_input, string col) { - switch(clienterror) + switch (clienterror) { - case CLIENT_DOESNT_EXIST: { return strcat(col, "Client '", original_input, col, "' doesn't exist"); } - case CLIENT_NOT_REAL: { return strcat(col, "Client '", original_input, col, "' is not real"); } - case CLIENT_NOT_BOT: { return strcat(col, "Client '", original_input, col, "' is not a bot"); } - default: { return "Incorrect usage of GetClientErrorString"; } + case CLIENT_DOESNT_EXIST: + { return strcat(col, "Client '", original_input, col, "' doesn't exist"); + } + case CLIENT_NOT_REAL: + { return strcat(col, "Client '", original_input, col, "' is not real"); + } + case CLIENT_NOT_BOT: + { return strcat(col, "Client '", original_input, col, "' is not a bot"); + } + default: + { return "Incorrect usage of GetClientErrorString"; + } } } // is this entity number even in the possible range of entities? float VerifyClientNumber(float tmp_number) { - if((tmp_number < 1) || (tmp_number > maxclients)) - return false; - else - return true; + if ((tmp_number < 1) || (tmp_number > maxclients)) return false; + else return true; } entity GetIndexedEntity(float argc, float start_index) @@ -83,47 +81,48 @@ entity GetIndexedEntity(float argc, float start_index) index = start_index; selection = world; - if(argc > start_index) + if (argc > start_index) { - if(substring(argv(index), 0, 1) == "#") + if (substring(argv(index), 0, 1) == "#") { tmp_string = substring(argv(index), 1, -1); ++index; - if(tmp_string != "") // is it all one token? like #1 + if (tmp_string != "") // is it all one token? like #1 { tmp_number = stof(tmp_string); } - else if(argc > index) // no, it's two tokens? # 1 + else if (argc > index) // no, it's two tokens? # 1 { tmp_number = stof(argv(index)); ++index; } else + { tmp_number = 0; + } } - else // maybe it's ONLY a number? + else // maybe it's ONLY a number? { tmp_number = stof(argv(index)); ++index; } - if(VerifyClientNumber(tmp_number)) + if (VerifyClientNumber(tmp_number)) { - selection = edict_num(tmp_number); // yes, it was a number + selection = edict_num(tmp_number); // yes, it was a number } - else // no, maybe it's a name? + else // no, maybe it's a name? { FOR_EACH_CLIENT(tmp_player) - if (strdecolorize(tmp_player.netname) == strdecolorize(argv(start_index))) - selection = tmp_player; + if (strdecolorize(tmp_player.netname) == strdecolorize(argv(start_index))) selection = tmp_player; index = (start_index + 1); } } next_token = index; - //print(strcat("start_index: ", ftos(start_index), ", next_token: ", ftos(next_token), ", edict: ", ftos(num_for_edict(selection)), ".\n")); + // print(strcat("start_index: ", ftos(start_index), ", next_token: ", ftos(next_token), ", edict: ", ftos(num_for_edict(selection)), ".\n")); return selection; } @@ -133,12 +132,10 @@ entity GetFilteredEntity(string input) entity tmp_player, selection; float tmp_number; - if(substring(input, 0, 1) == "#") - tmp_number = stof(substring(input, 1, -1)); - else - tmp_number = stof(input); + if (substring(input, 0, 1) == "#") tmp_number = stof(substring(input, 1, -1)); + else tmp_number = stof(input); - if(VerifyClientNumber(tmp_number)) + if (VerifyClientNumber(tmp_number)) { selection = edict_num(tmp_number); } @@ -146,8 +143,7 @@ entity GetFilteredEntity(string input) { selection = world; FOR_EACH_CLIENT(tmp_player) - if (strdecolorize(tmp_player.netname) == strdecolorize(input)) - selection = tmp_player; + if (strdecolorize(tmp_player.netname) == strdecolorize(input)) selection = tmp_player; } return selection; @@ -167,10 +163,8 @@ float GetFilteredNumber(string input) // switch between sprint and print depending on whether the receiver is the server or a player void print_to(entity to, string input) { - if(to) - sprint(to, strcat(input, "\n")); - else - LOG_INFO(input, "\n"); + if (to) sprint(to, strcat(input, "\n")); + else LOG_INFO(input, "\n"); } // ========================================== @@ -179,7 +173,8 @@ void print_to(entity to, string input) // used by CommonCommand_timeout() and CommonCommand_timein() to handle game pausing and messaging and such. void timeout_handler_reset() -{SELFPARAM(); +{ + SELFPARAM(); timeout_caller = world; timeout_time = 0; timeout_leadtime = 0; @@ -188,24 +183,25 @@ void timeout_handler_reset() } void timeout_handler_think() -{SELFPARAM(); +{ + SELFPARAM(); entity tmp_player; - switch(timeout_status) + switch (timeout_status) { case TIMEOUT_ACTIVE: { - if(timeout_time > 0) // countdown is still going + if (timeout_time > 0) // countdown is still going { Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TIMEOUT_ENDING, timeout_time); - if(timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only seconds are left + if (timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only seconds are left Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_PREPARE); - self.nextthink = time + TIMEOUT_SLOWMO_VALUE; // think again in one second - timeout_time -= 1; // decrease the time counter + self.nextthink = time + TIMEOUT_SLOWMO_VALUE; // think again in one second + timeout_time -= 1; // decrease the time counter } - else // time to end the timeout + else // time to end the timeout { timeout_status = TIMEOUT_INACTIVE; @@ -214,7 +210,7 @@ void timeout_handler_think() // unlock the view for players so they can move around again FOR_EACH_REALPLAYER(tmp_player) - tmp_player.fixangle = false; + tmp_player.fixangle = false; timeout_handler_reset(); } @@ -224,14 +220,14 @@ void timeout_handler_think() case TIMEOUT_LEADTIME: { - if(timeout_leadtime > 0) // countdown is still going + if (timeout_leadtime > 0) // countdown is still going { Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_TIMEOUT_BEGINNING, timeout_leadtime); self.nextthink = time + 1; // think again in one second - timeout_leadtime -= 1; // decrease the time counter + timeout_leadtime -= 1; // decrease the time counter } - else // time to begin the timeout + else // time to begin the timeout { timeout_status = TIMEOUT_ACTIVE; @@ -240,15 +236,15 @@ void timeout_handler_think() // reset all the flood variables FOR_EACH_CLIENT(tmp_player) - tmp_player.nickspamcount = tmp_player.nickspamtime = tmp_player.floodcontrol_chat = - tmp_player.floodcontrol_chatteam = tmp_player.floodcontrol_chattell = - tmp_player.floodcontrol_voice = tmp_player.floodcontrol_voiceteam = 0; + tmp_player.nickspamcount = tmp_player.nickspamtime = tmp_player.floodcontrol_chat = + tmp_player.floodcontrol_chatteam = tmp_player.floodcontrol_chattell = + tmp_player.floodcontrol_voice = tmp_player.floodcontrol_voiceteam = 0; // copy .v_angle to .lastV_angle for every player in order to fix their view during pause (see PlayerPreThink) FOR_EACH_REALPLAYER(tmp_player) - tmp_player.lastV_angle = tmp_player.v_angle; + tmp_player.lastV_angle = tmp_player.v_angle; - self.nextthink = time; // think again next frame to handle it under TIMEOUT_ACTIVE code + self.nextthink = time; // think again next frame to handle it under TIMEOUT_ACTIVE code } return; @@ -265,19 +261,18 @@ void timeout_handler_think() } - // =================================================== // Common commands used in both sv_cmd.qc and cmd.qc // =================================================== void CommonCommand_cvar_changes(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, cvar_changes); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -293,12 +288,12 @@ void CommonCommand_cvar_changes(float request, entity caller) void CommonCommand_cvar_purechanges(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, cvar_purechanges); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -313,15 +308,16 @@ void CommonCommand_cvar_purechanges(float request, entity caller) } void CommonCommand_editmob(int request, entity caller, int argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(autocvar_g_campaign) { print_to(caller, "Monster editing is disabled in singleplayer"); return; } + if (autocvar_g_campaign) { print_to(caller, "Monster editing is disabled in singleplayer"); return; } // no checks for g_monsters here, as it may be toggled mid match which existing monsters - if(caller) + if (caller) { makevectors(self.v_angle); WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self); @@ -331,80 +327,83 @@ void CommonCommand_editmob(int request, entity caller, int argc) bool is_visible = IS_MONSTER(mon); string argument = argv(2); - switch(argv(1)) + switch (argv(1)) { case "name": { - if(!caller) { print_to(caller, "Only players can edit monsters"); return; } - if(!argument) { break; } // escape to usage - if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } - if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } - if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if (!caller) { print_to(caller, "Only players can edit monsters"); return; } + if (!argument) break; // escape to usage + if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } string mon_oldname = mon.monster_name; mon.monster_name = argument; - if(mon.sprite) { WaypointSprite_UpdateSprites(mon.sprite, WP_Monster, WP_Null, WP_Null); } + if (mon.sprite) WaypointSprite_UpdateSprites(mon.sprite, WP_Monster, WP_Null, WP_Null); print_to(caller, sprintf("Your pet '%s' is now known as '%s'", mon_oldname, mon.monster_name)); return; } case "spawn": { - if(!caller) { print_to(caller, "Only players can spawn monsters"); return; } - if(!argv(2)) { break; } // escape to usage + if (!caller) { print_to(caller, "Only players can spawn monsters"); return; } + if (!argv(2)) break; // escape to usage int moveflag, tmp_moncount = 0; string arg_lower = strtolower(argument); - moveflag = (argv(3)) ? stof(argv(3)) : 1; // follow owner if not defined + moveflag = (argv(3)) ? stof(argv(3)) : 1; // follow owner if not defined ret_string = "Monster spawning is currently disabled by a mutator"; - if(arg_lower == "list") { print_to(caller, monsterlist_reply); return; } + if (arg_lower == "list") { print_to(caller, monsterlist_reply); return; } - FOR_EACH_MONSTER(mon) { if(mon.realowner == caller) ++tmp_moncount; } + FOR_EACH_MONSTER(mon) + { + if (mon.realowner == caller) ++tmp_moncount; + } - if(!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; } - if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; } - if(!IS_PLAYER(caller)) { print_to(caller, "You must be playing to spawn a monster"); return; } - if(MUTATOR_CALLHOOK(AllowMobSpawning)) { print_to(caller, ret_string); return; } - if(caller.vehicle) { print_to(caller, "You can't spawn monsters while driving a vehicle"); return; } - if(caller.frozen) { print_to(caller, "You can't spawn monsters while frozen"); return; } - if(caller.deadflag != DEAD_NO) { print_to(caller, "You can't spawn monsters while dead"); return; } - if(tmp_moncount >= autocvar_g_monsters_max) { print_to(caller, "The maximum monster count has been reached"); return; } - if(tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; } + if (!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; } + if (autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; } + if (!IS_PLAYER(caller)) { print_to(caller, "You must be playing to spawn a monster"); return; } + if (MUTATOR_CALLHOOK(AllowMobSpawning)) { print_to(caller, ret_string); return; } + if (caller.vehicle) { print_to(caller, "You can't spawn monsters while driving a vehicle"); return; } + if (caller.frozen) { print_to(caller, "You can't spawn monsters while frozen"); return; } + if (caller.deadflag != DEAD_NO) { print_to(caller, "You can't spawn monsters while dead"); return; } + if (tmp_moncount >= autocvar_g_monsters_max) { print_to(caller, "The maximum monster count has been reached"); return; } + if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; } bool found = false; - for(int i = MON_FIRST; i <= MON_LAST; ++i) + for (int i = MON_FIRST; i <= MON_LAST; ++i) { mon = get_monsterinfo(i); - if(mon.netname == arg_lower) { found = true; break; } + if (mon.netname == arg_lower) { found = true; break; } } - if(!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; } + if (!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; } totalspawned += 1; - WarpZone_TraceBox (CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller); + WarpZone_TraceBox(CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller); mon = spawnmonster(arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag); print_to(caller, strcat("Spawned ", mon.monster_name)); return; } case "kill": { - if(!caller) { print_to(caller, "Only players can kill monsters"); return; } - if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } - if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if (!caller) { print_to(caller, "Only players can kill monsters"); return; } + if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } - Damage (mon, world, world, mon.health + mon.max_health + 200, DEATH_KILL.m_id, mon.origin, '0 0 0'); + Damage(mon, world, world, mon.health + mon.max_health + 200, DEATH_KILL.m_id, mon.origin, '0 0 0'); print_to(caller, strcat("Your pet '", mon.monster_name, "' has been brutally mutilated")); return; } case "skin": { - if(!caller) { print_to(caller, "Only players can edit monsters"); return; } - if(!argument) { break; } // escape to usage - if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } - if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } - if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } - if(mon.monsterid == MON_MAGE.monsterid) { print_to(caller, "Mage skins can't be changed"); return; } // TODO + if (!caller) { print_to(caller, "Only players can edit monsters"); return; } + if (!argument) break; // escape to usage + if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if (mon.monsterid == MON_MAGE.monsterid) { print_to(caller, "Mage skins can't be changed"); return; } // TODO mon.skin = stof(argument); print_to(caller, strcat("Monster skin successfully changed to ", ftos(mon.skin))); @@ -412,11 +411,11 @@ void CommonCommand_editmob(int request, entity caller, int argc) } case "movetarget": { - if(!caller) { print_to(caller, "Only players can edit monsters"); return; } - if(!argument) { break; } // escape to usage - if(!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } - if(!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } - if(mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } + if (!caller) { print_to(caller, "Only players can edit monsters"); return; } + if (!argument) break; // escape to usage + if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; } + if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } + if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } mon.monster_moveflags = stof(argument); print_to(caller, strcat("Monster move target successfully changed to ", ftos(mon.monster_moveflags))); @@ -424,13 +423,17 @@ void CommonCommand_editmob(int request, entity caller, int argc) } case "butcher": { - if(caller) { print_to(caller, "This command is not available to players"); return; } - if(MUTATOR_CALLHOOK(AllowMobButcher)) { LOG_INFO(ret_string, "\n"); return; } + if (caller) { print_to(caller, "This command is not available to players"); return; } + if (MUTATOR_CALLHOOK(AllowMobButcher)) { LOG_INFO(ret_string, "\n"); return; } int tmp_remcount = 0; entity tmp_entity; - FOR_EACH_MONSTER(tmp_entity) { Monster_Remove(tmp_entity); ++tmp_remcount; } + FOR_EACH_MONSTER(tmp_entity) + { + Monster_Remove(tmp_entity); + ++tmp_remcount; + } monsters_total = monsters_killed = totalspawned = 0; @@ -455,18 +458,16 @@ void CommonCommand_editmob(int request, entity caller, int argc) void CommonCommand_info(float request, entity caller, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { string command = builtin_cvar_string(strcat("sv_info_", argv(1))); - if(command) - wordwrap_sprint(command, 1000); - else - print_to(caller, "ERROR: unsupported info command"); + if (command) wordwrap_sprint(command, 1000); + else print_to(caller, "ERROR: unsupported info command"); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -481,12 +482,12 @@ void CommonCommand_info(float request, entity caller, float argc) void CommonCommand_ladder(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, ladder_reply); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -501,12 +502,12 @@ void CommonCommand_ladder(float request, entity caller) void CommonCommand_lsmaps(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, lsmaps_reply); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -521,12 +522,12 @@ void CommonCommand_lsmaps(float request, entity caller) void CommonCommand_printmaplist(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, maplist_reply); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -541,12 +542,12 @@ void CommonCommand_printmaplist(float request, entity caller) void CommonCommand_rankings(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { print_to(caller, rankings_reply); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -561,15 +562,14 @@ void CommonCommand_rankings(float request, entity caller) void CommonCommand_records(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - for(int i = 0; i < 10; ++i) - if(records_reply[i] != "") - print_to(caller, records_reply[i]); + for (int i = 0; i < 10; ++i) + if (records_reply[i] != "") print_to(caller, records_reply[i]); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -584,12 +584,12 @@ void CommonCommand_records(float request, entity caller) void CommonCommand_teamstatus(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { Score_NicePrint(caller); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -604,7 +604,7 @@ void CommonCommand_teamstatus(float request, entity caller) void CommonCommand_time(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -630,24 +630,27 @@ void CommonCommand_time(float request, entity caller) void CommonCommand_timein(float request, entity caller) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(!caller || autocvar_sv_timeout) + if (!caller || autocvar_sv_timeout) { if (!timeout_status) { print_to(caller, "^7Error: There is no active timeout called."); } - else if(caller && (caller != timeout_caller)) { print_to(caller, "^7Error: You are not allowed to stop the active timeout."); } + else if (caller && (caller != timeout_caller)) + { + print_to(caller, "^7Error: You are not allowed to stop the active timeout."); + } - else // everything should be okay, continue aborting timeout + else // everything should be okay, continue aborting timeout { - switch(timeout_status) + switch (timeout_status) { case TIMEOUT_LEADTIME: { timeout_status = TIMEOUT_INACTIVE; timeout_time = 0; - timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately + timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately bprint(strcat("^7The timeout was aborted by ", GetCallerName(caller), " !\n")); return; } @@ -655,18 +658,19 @@ void CommonCommand_timein(float request, entity caller) case TIMEOUT_ACTIVE: { timeout_time = autocvar_sv_timeout_resumetime; - timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately + timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately bprint(strcat("^1Attention: ^7", GetCallerName(caller), " resumed the game! Prepare for battle!\n")); return; } - default: LOG_TRACE("timeout status was inactive, but this code was executed anyway?"); return; + default: LOG_TRACE("timeout status was inactive, but this code was executed anyway?"); + return; } } } else { print_to(caller, "^1Timeins are not allowed to be called, enable them with sv_timeout 1.\n"); } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -679,28 +683,45 @@ void CommonCommand_timein(float request, entity caller) } } -void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE. +void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE. { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(!caller || autocvar_sv_timeout) + if (!caller || autocvar_sv_timeout) { float last_possible_timeout = ((autocvar_timelimit * 60) - autocvar_sv_timeout_leadtime - 1); - if(timeout_status) { print_to(caller, "^7Error: A timeout is already active."); } - else if(vote_called) { print_to(caller, "^7Error: You can not call a timeout while a vote is active."); } - else if(warmup_stage && !g_warmup_allow_timeout) { print_to(caller, "^7Error: You can not call a timeout in warmup-stage."); } - else if(time < game_starttime) { print_to(caller, "^7Error: You can not call a timeout while the map is being restarted."); } - else if(caller && (caller.allowed_timeouts < 1)) { print_to(caller, "^7Error: You already used all your timeout calls for this map."); } - else if(caller && !IS_PLAYER(caller)) { print_to(caller, "^7Error: You must be a player to call a timeout."); } - else if((autocvar_timelimit) && (last_possible_timeout < time - game_starttime)) { print_to(caller, "^7Error: It is too late to call a timeout now!"); } - - else // everything should be okay, proceed with starting the timeout + if (timeout_status) { print_to(caller, "^7Error: A timeout is already active."); } + else if (vote_called) { - if(caller) { caller.allowed_timeouts -= 1; } + print_to(caller, "^7Error: You can not call a timeout while a vote is active."); + } + else if (warmup_stage && !g_warmup_allow_timeout) + { + print_to(caller, "^7Error: You can not call a timeout in warmup-stage."); + } + else if (time < game_starttime) + { + print_to(caller, "^7Error: You can not call a timeout while the map is being restarted."); + } + else if (caller && (caller.allowed_timeouts < 1)) + { + print_to(caller, "^7Error: You already used all your timeout calls for this map."); + } + else if (caller && !IS_PLAYER(caller)) + { + print_to(caller, "^7Error: You must be a player to call a timeout."); + } + else if ((autocvar_timelimit) && (last_possible_timeout < time - game_starttime)) + { + print_to(caller, "^7Error: It is too late to call a timeout now!"); + } + else // everything should be okay, proceed with starting the timeout + { + if (caller) caller.allowed_timeouts -= 1; // write a bprint who started the timeout (and how many they have left) bprint(GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(caller.allowed_timeouts), " timeout(s) left)") : ""), "!\n"); @@ -711,14 +732,14 @@ void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAN timeout_handler = spawn(); timeout_handler.think = timeout_handler_think; - timeout_handler.nextthink = time; // always let the entity think asap + timeout_handler.nextthink = time; // always let the entity think asap Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_TIMEOUT); } } else { print_to(caller, "^1Timeouts are not allowed to be called, enable them with sv_timeout 1.\n"); } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -733,7 +754,7 @@ void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAN void CommonCommand_who(float request, entity caller, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -753,12 +774,12 @@ void CommonCommand_who(float request, entity caller, float argc) { is_bot = (IS_BOT_CLIENT(tmp_player)); - if(is_bot) + if (is_bot) { tmp_netaddress = "null/botclient"; tmp_crypto_idfp = "null/botclient"; } - else if(privacy) + else if (privacy) { tmp_netaddress = "hidden"; tmp_crypto_idfp = "hidden"; @@ -783,7 +804,7 @@ void CommonCommand_who(float request, entity caller, float argc) print_to(caller, strcat("Finished listing ", ftos(total_listed_players), " client(s) out of ", ftos(maxclients), " slots.")); - return; // never fall through to usage + return; // never fall through to usage } default: @@ -800,21 +821,21 @@ void CommonCommand_who(float request, entity caller, float argc) ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void CommonCommand_(float request, entity caller) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; // never fall through to usage - } - - default: - case CMD_REQUEST_USAGE: - { - print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " ")); - print_to(caller, " No arguments required."); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; // never fall through to usage + } + + default: + case CMD_REQUEST_USAGE: + { + print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " ")); + print_to(caller, " No arguments required."); + return; + } + } } */ diff --git a/qcsrc/server/command/common.qh b/qcsrc/server/command/common.qh index 9a011fb851..2a1a2b42fd 100644 --- a/qcsrc/server/command/common.qh +++ b/qcsrc/server/command/common.qh @@ -27,15 +27,15 @@ const float TIMEOUT_ACTIVE = 2; const float TIMEOUT_SLOWMO_VALUE = 0.0001; // global timeout information declarations -entity timeout_caller; // contains the entity of the player who started the last timeout -entity timeout_handler; // responsible for centerprinting the timeout countdowns and playing sounds -float sys_frametime; // gets initialised in worldspawn, saves the value from autocvar_sys_ticrate -float orig_slowmo; // contains the value of autocvar_slowmo so that, after timeout finished, it isn't set to slowmo 1 necessarily -float timeout_time; // contains the time in seconds that the active timeout has left -float timeout_leadtime; // contains the number of seconds left of the leadtime (before the timeout starts) -float timeout_status; // (values: 0, 1, 2) contains whether a timeout is not active (0), was called but still at leadtime (1) or is active (2) +entity timeout_caller; // contains the entity of the player who started the last timeout +entity timeout_handler; // responsible for centerprinting the timeout countdowns and playing sounds +float sys_frametime; // gets initialised in worldspawn, saves the value from autocvar_sys_ticrate +float orig_slowmo; // contains the value of autocvar_slowmo so that, after timeout finished, it isn't set to slowmo 1 necessarily +float timeout_time; // contains the time in seconds that the active timeout has left +float timeout_leadtime; // contains the number of seconds left of the leadtime (before the timeout starts) +float timeout_status; // (values: 0, 1, 2) contains whether a timeout is not active (0), was called but still at leadtime (1) or is active (2) .float allowed_timeouts; // contains the number of allowed timeouts for each player -.vector lastV_angle; //used when pausing the game in order to force the player to keep his old view angle fixed +.vector lastV_angle; // used when pausing the game in order to force the player to keep his old view angle fixed // allow functions to be used in other code like g_world.qc and teamplay.qc void timeout_handler_think(); @@ -60,7 +60,7 @@ float VerifyClientEntity(entity client, float must_be_real, float must_be_bots); // if the client is not acceptable, return a string to be used for error messages string GetClientErrorString_color(float clienterror, string original_input, string col); -#define GetClientErrorString(clienterror,original_input) GetClientErrorString_color(clienterror,original_input,"^7") +#define GetClientErrorString(clienterror, original_input) GetClientErrorString_color(clienterror, original_input, "^7") // is this entity number even in the possible range of entities? float VerifyClientNumber(float tmp_number); @@ -123,7 +123,7 @@ void CommonCommand_who(float request, entity caller, float argc); // ================================== // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define COMMON_COMMANDS(request,caller,arguments,command) \ +#define COMMON_COMMANDS(request, caller, arguments, command) \ COMMON_COMMAND("cvar_changes", CommonCommand_cvar_changes(request, caller), "Prints a list of all changed server cvars") \ COMMON_COMMAND("cvar_purechanges", CommonCommand_cvar_purechanges(request, caller), "Prints a list of all changed gameplay cvars") \ COMMON_COMMAND("editmob", CommonCommand_editmob(request, caller, arguments), "Modifies a monster or all monsters") \ @@ -143,46 +143,42 @@ void CommonCommand_who(float request, entity caller, float argc); void CommonCommand_macro_help(entity caller) { - #define COMMON_COMMAND(name,function,description) \ + #define COMMON_COMMAND(name, function, description) \ { print_to(caller, strcat(" ^2", name, "^7: ", description)); } COMMON_COMMANDS(0, caller, 0, ""); - #undef COMMON_COMMAND - - return; +#undef COMMON_COMMAND } float CommonCommand_macro_command(float argc, entity caller, string command) { - #define COMMON_COMMAND(name,function,description) \ - { if(name == strtolower(argv(0))) { function; return true; } } + #define COMMON_COMMAND(name, function, description) \ + { if (name == strtolower(argv(0))) { function; return true; } } COMMON_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, command); - #undef COMMON_COMMAND +#undef COMMON_COMMAND return false; } float CommonCommand_macro_usage(float argc, entity caller) { - #define COMMON_COMMAND(name,function,description) \ - { if(name == strtolower(argv(1))) { function; return true; } } + #define COMMON_COMMAND(name, function, description) \ + { if (name == strtolower(argv(1))) { function; return true; } } COMMON_COMMANDS(CMD_REQUEST_USAGE, caller, argc, ""); - #undef COMMON_COMMAND +#undef COMMON_COMMAND return false; } void CommonCommand_macro_write_aliases(float fh) { - #define COMMON_COMMAND(name,function,description) \ + #define COMMON_COMMAND(name, function, description) \ { CMD_Write_Alias("qc_cmd_svcmd", name, description); } COMMON_COMMANDS(0, world, 0, ""); - #undef COMMON_COMMAND - - return; +#undef COMMON_COMMAND } diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index fd312bff61..d2233029d8 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -21,7 +21,7 @@ // See common.qc for their proper commands -string getrecords(int page) // 50 records per page +string getrecords(int page) // 50 records per page { string s = ""; @@ -30,10 +30,8 @@ string getrecords(int page) // 50 records per page MapInfo_ClearTemps(); - if(s == "" && page == 0) - return "No records are available on this server.\n"; - else - return s; + if (s == "" && page == 0) return "No records are available on this server.\n"; + else return s; } string getrankings() @@ -48,8 +46,7 @@ string getrankings() { t = race_readTime(map, i); - if (t == 0) - continue; + if (t == 0) continue; n = race_readName(map, i); p = count_ordinal(i); @@ -58,10 +55,8 @@ string getrankings() MapInfo_ClearTemps(); - if (s == "") - return strcat("No records are available for the map: ", map, "\n"); - else - return strcat("Records for ", map, ":\n", s); + if (s == "") return strcat("No records are available for the map: ", map, "\n"); + else return strcat("Records for ", map, ":\n", s); } string getladder() @@ -71,23 +66,21 @@ string getladder() rr = (g_cts) ? CTS_RECORD : RACE_RECORD; - for(k = 0; k < MapInfo_count; ++k) + for (k = 0; k < MapInfo_count; ++k) { - if(MapInfo_Get_ByID(k)) + if (MapInfo_Get_ByID(k)) { - for(i = 0; i <= LADDER_CNT; ++i) // i = 0 because it is the speed award + for (i = 0; i <= LADDER_CNT; ++i) // i = 0 because it is the speed award { - if(i == 0) // speed award + if (i == 0) // speed award { - if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0) - continue; + if (stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0) continue; myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp")); } - else // normal record, if it exists (else break) + else // normal record, if it exists (else break) { - if(race_readTime(MapInfo_Map_bspname, i) == 0) - continue; + if (race_readTime(MapInfo_Map_bspname, i) == 0) continue; myuid = race_readUID(MapInfo_Map_bspname, i); } @@ -101,43 +94,38 @@ string getladder() temp_s = db_get(TemporaryDB, strcat("ladder", myuid)); - if(temp_s == "") + if (temp_s == "") { db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid); ++uidcnt; - for(j = 0; j <= LADDER_CNT + 1; ++j) + for (j = 0; j <= LADDER_CNT + 1; ++j) { - if(j != LADDER_CNT + 1) - temp_s = strcat(temp_s, "0 "); - else - temp_s = strcat(temp_s, "0"); + if (j != LADDER_CNT + 1) temp_s = strcat(temp_s, "0 "); + else temp_s = strcat(temp_s, "0"); } } tokenize_console(temp_s); s = ""; - if(i == 0) // speed award + if (i == 0) // speed award { - for(j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string + for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string { - if(j == 0) // speed award - s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write - else - s = strcat(s, " ", argv(j)); // just copy over everything else + if (j == 0) // speed award + s = strcat(s, ftos(stof(argv(j)) + 1)); // add 1 to speed rec count and write + else s = strcat(s, " ", argv(j)); // just copy over everything else } } - else // record + else // record { - for(j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string + for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string { - if(j == 0) - s = strcat(s, argv(j)); // speed award, dont prefix with " " - else if(j == i) // wanted rec! - s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j) - else - s = strcat(s, " ", argv(j)); // just copy over everything else + if (j == 0) s = strcat(s, argv(j)); // speed award, dont prefix with " " + else if (j == i) // wanted rec! + s = strcat(s, " ", ftos(stof(argv(j)) + 1)); // update argv(j) + else s = strcat(s, " ", argv(j)); // just copy over everything else } } @@ -150,33 +138,31 @@ string getladder() // 5th place = floor(100 / 5) = 20 points // ... etc - if(i == 0) - s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points - else - s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points + if (i == 0) s = strcat(s, " ", ftos(stof(argv(LADDER_CNT + 1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points + else s = strcat(s, " ", ftos(stof(argv(LADDER_CNT + 1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points db_put(TemporaryDB, strcat("ladder", myuid), s); } } } - for(i = 0; i <= uidcnt; ++i) // for each known uid + for (i = 0; i <= uidcnt; ++i) // for each known uid { thisuid = db_get(TemporaryDB, strcat("uid", ftos(i))); temp_s = db_get(TemporaryDB, strcat("ladder", thisuid)); tokenize_console(temp_s); - thiscnt = stof(argv(LADDER_CNT+1)); + thiscnt = stof(argv(LADDER_CNT + 1)); - if(thiscnt > top_scores[LADDER_SIZE-1]) + if (thiscnt > top_scores[LADDER_SIZE - 1]) { - for(j = 0; j < LADDER_SIZE; ++j) // for each place in ladder + for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder { - if(thiscnt > top_scores[j]) + if (thiscnt > top_scores[j]) { - for(k = LADDER_SIZE-1; k >= j; --k) + for (k = LADDER_SIZE - 1; k >= j; --k) { - top_uids[k] = top_uids[k-1]; - top_scores[k] = top_scores[k-1]; + top_uids[k] = top_uids[k - 1]; + top_scores[k] = top_scores[k - 1]; } top_uids[j] = thisuid; @@ -192,51 +178,47 @@ string getladder() s = strcat(s, "Pos ^3|"); s = strcat(s, " ^7Total ^3|"); - for(i = 1; i <= LADDER_CNT; ++i) - { s = strcat(s, " ^7", count_ordinal(i), " ^3|"); } - + for (i = 1; i <= LADDER_CNT; ++i) + s = strcat(s, " ^7", count_ordinal(i), " ^3|"); s = strcat(s, " ^7Speed awards ^3| ^7Name"); s = strcat(s, "\n^3----+--------"); - for(i = 1; i <= min(9, LADDER_CNT); ++i) - { s = strcat(s, "+-----"); } - - #if LADDER_CNT > 9 - for(i = 1; i <= LADDER_CNT - 9; ++i) - { s = strcat(s, "+------"); } - #endif + for (i = 1; i <= min(9, LADDER_CNT); ++i) + s = strcat(s, "+-----"); +#if LADDER_CNT > 9 + for (i = 1; i <= LADDER_CNT - 9; ++i) + s = strcat(s, "+------"); +#endif s = strcat(s, "+--------------+--------------------\n"); - for(i = 0; i < LADDER_SIZE; ++i) + for (i = 0; i < LADDER_SIZE; ++i) { temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i])); tokenize_console(temp_s); - if(argv(LADDER_CNT+1) == "") // total is 0, skip + if (argv(LADDER_CNT + 1) == "") // total is 0, skip continue; - s = strcat(s, strpad(4, count_ordinal(i+1)), "^3| ^7"); // pos - s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total + s = strcat(s, strpad(4, count_ordinal(i + 1)), "^3| ^7"); // pos + s = strcat(s, strpad(7, argv(LADDER_CNT + 1)), "^3| ^7"); // total - for(j = 1; j <= min(9, LADDER_CNT); ++j) - { s = strcat(s, strpad(4, argv(j)), "^3| ^7"); } // 1st, 2nd, 3rd etc cnt + for (j = 1; j <= min(9, LADDER_CNT); ++j) + s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt - #if LADDER_CNT > 9 - for(j = 10; j <= LADDER_CNT; ++j) - { s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); } // 1st, 2nd, 3rd etc cnt - #endif +#if LADDER_CNT > 9 + for (j = 10; j <= LADDER_CNT; ++j) + s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt +#endif - s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt - s = strcat(s, uid2name(top_uids[i]), "\n"); // name + s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt + s = strcat(s, uid2name(top_uids[i]), "\n"); // name } MapInfo_ClearTemps(); - if(s == "") - return "No ladder on this server!\n"; - else - return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s); + if (s == "") return "No ladder on this server!\n"; + else return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s); } string getmaplist() @@ -245,12 +227,11 @@ string getmaplist() int i, argc; argc = tokenize_console(autocvar_g_maplist); - for(i = 0; i < argc; ++i) + for (i = 0; i < argc; ++i) { - if(MapInfo_CheckMap(argv(i))) + if (MapInfo_CheckMap(argv(i))) { - if(i % 2) { col = "^2"; } - else { col = "^3"; } + if (i % 2) col = "^2"; else col = "^3"; maplist = sprintf("%s%s%s ", maplist, col, argv(i)); } } @@ -265,25 +246,23 @@ string getlsmaps() string lsmaps = "", col; float i, newmaps = 0; - for(i = 0; i < MapInfo_count; ++i) + for (i = 0; i < MapInfo_count; ++i) { - if((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags())) + if ((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags())) { // todo: Check by play count of maps for other game types? - if( - (g_race && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")))) - || - (g_cts && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")))) - ) + if ( + (g_race && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")))) + || + (g_cts && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")))) + ) { newmaps = true; - if(i % 2) { col = "^4*"; } - else { col = "^5*"; } + if (i % 2) col = "^4*"; else col = "^5*"; } else { - if(i % 2) { col = "^2"; } - else { col = "^3"; } + if (i % 2) col = "^2"; else col = "^3"; } lsmaps = sprintf("%s%s%s ", lsmaps, col, MapInfo_Map_bspname); @@ -298,10 +277,9 @@ string getmonsterlist() { string monsterlist = "", col; - for(int i = MON_FIRST; i <= MON_LAST; ++i) + for (int i = MON_FIRST; i <= MON_LAST; ++i) { - if(i % 2) { col = "^2"; } - else { col = "^3"; } + if (i % 2) col = "^2"; else col = "^3"; monsterlist = sprintf("%s%s%s ", monsterlist, col, (get_monsterinfo(i)).netname); } diff --git a/qcsrc/server/command/getreplies.qh b/qcsrc/server/command/getreplies.qh index 0e419fdea3..a0f9b21ef8 100644 --- a/qcsrc/server/command/getreplies.qh +++ b/qcsrc/server/command/getreplies.qh @@ -8,17 +8,17 @@ // ladder bullshit todo const int LADDER_FIRSTPOINT = 100; -#define LADDER_CNT 10 // position X still gives LADDER_FIRSTPOINT/X points -const int LADDER_SIZE = 30; // ladder shows the top X players +#define LADDER_CNT 10 // position X still gives LADDER_FIRSTPOINT/X points +const int LADDER_SIZE = 30; // ladder shows the top X players string top_uids[LADDER_SIZE]; float top_scores[LADDER_SIZE]; // allow functions to be used in other code like g_world.qc and race.qc string getrecords(float page); -string getrankings(void); -string getladder(void); -string getmaplist(void); -string getlsmaps(void); -string getmonsterlist(void); +string getrankings(); +string getladder(); +string getmaplist(); +string getlsmaps(); +string getmonsterlist(); #endif diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index 60e6bde62d..7475c5a546 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -25,14 +25,14 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b) float n, m; n = m = 0; - while(vlen(c - b) > 1) + while (vlen(c - b) > 1) { ++m; tracebox(c, mi, ma, b, MOVE_WORLDONLY, world); ++n; - if(!trace_startsolid) + if (!trace_startsolid) { black += vlen(trace_endpos - c); c = trace_endpos; @@ -44,8 +44,7 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b) c = trace_endpos; } - if(n > 200) - LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations\n"); + if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations\n"); return white / (black + white); } @@ -74,17 +73,13 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, ma = '1 0 0' * w + '0 1 0' * h + dz; o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; - if(x < world.absmin.x - w) - return 0; - if(y < world.absmin.y - h) - return 0; - if(x > world.absmax.x) - return 0; - if(y > world.absmax.y) - return 0; + if (x < world.absmin.x - w) return 0; + if (y < world.absmin.y - h) return 0; + if (x > world.absmax.x) return 0; + if (y > world.absmax.y) return 0; r = 0; - for(i = 0; i < q; ++i) + for (i = 0; i < q; ++i) { vector v1, v2; v1 = v2 = o + dz * i + mi; @@ -95,8 +90,7 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, v2_y += random() * (ma.y - mi.y); v2_z += random() * (ma.z - mi.z); traceline(v1, v2, MOVE_WORLDONLY, world); - if(trace_startsolid || trace_fraction < 1) - ++r; + if (trace_startsolid || trace_fraction < 1) ++r; } return r / q; } @@ -114,21 +108,16 @@ float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, floa ma = '1 0 0' * w + '0 1 0' * h + dz; o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; - if(x < world.absmin.x - w) - return 0; - if(y < world.absmin.y - h) - return 0; - if(x > world.absmax.x) - return 0; - if(y > world.absmax.y) - return 0; + if (x < world.absmin.x - w) return 0; + if (y < world.absmin.y - h) return 0; + if (x > world.absmax.x) return 0; + if (y > world.absmax.y) return 0; r = 0; - for(i = 0; i < q; ++i) + for (i = 0; i < q; ++i) { tracebox(o + dz * i, mi, ma, o + dz * i, MOVE_WORLDONLY, world); - if(trace_startsolid) - ++r; + if (trace_startsolid) ++r; } return r / q; } @@ -136,7 +125,7 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo { vector a, b, mi, ma; - q *= 4; // choose q so it matches the regular algorithm in speed + q *= 4; // choose q so it matches the regular algorithm in speed q = 256 * q - 1; // 256q-1 is the ideal sample count to map equal amount of sample values to one pixel value @@ -149,15 +138,14 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo float c, i; c = 0; - for(i = 0; i < q; ++i) + for (i = 0; i < q; ++i) { vector v; v.x = a.x + random() * b.x; v.y = a.y + random() * b.y; v.z = a.z + random() * b.z; traceline(v, v, MOVE_WORLDONLY, world); - if(trace_startsolid) - ++c; + if (trace_startsolid) ++c; } return c / q; @@ -168,22 +156,17 @@ void sharpen_set(int x, float v) } float sharpen_getpixel(int x, int y) { - if(x < 0) - return 0; - if(x >= RADAR_WIDTH_MAX) - return 0; - if(y < 0) - return 0; - if(y > 2) - return 0; + if (x < 0) return 0; + if (x >= RADAR_WIDTH_MAX) return 0; + if (y < 0) return 0; + if (y > 2) return 0; return sharpen_buffer[x + y * RADAR_WIDTH_MAX]; } float sharpen_get(float x, float a) { float sum = sharpen_getpixel(x, 1); - if(a == 0) - return sum; - sum *= (8 + 1/a); + if (a == 0) return sum; + sum *= (8 + 1 / a); sum -= sharpen_getpixel(x - 1, 0); sum -= sharpen_getpixel(x - 1, 1); sum -= sharpen_getpixel(x - 1, 2); @@ -196,7 +179,7 @@ float sharpen_get(float x, float a) } void sharpen_shift(int w) { - for(int i = 0; i < w; ++i) + for (int i = 0; i < w; ++i) { sharpen_buffer[i] = sharpen_buffer[i + RADAR_WIDTH_MAX]; sharpen_buffer[i + RADAR_WIDTH_MAX] = sharpen_buffer[i + 2 * RADAR_WIDTH_MAX]; @@ -205,7 +188,7 @@ void sharpen_shift(int w) } void sharpen_init(int w) { - for(int i = 0; i < w; ++i) + for (int i = 0; i < w; ++i) { sharpen_buffer[i] = 0; sharpen_buffer[i + RADAR_WIDTH_MAX] = 0; @@ -214,11 +197,11 @@ void sharpen_init(int w) } void RadarMap_Next() { - if(radarmapper.count & 4) + if (radarmapper.count & 4) { localcmd("quit\n"); } - else if(radarmapper.count & 2) + else if (radarmapper.count & 2) { localcmd(strcat("defer 1 \"sv_cmd radarmap --flags ", ftos(radarmapper.count), strcat(" --res ", ftos(radarmapper.size.x), " ", ftos(radarmapper.size.y), " --sharpen ", ftos(radarmapper.ltime), " --qual ", ftos(radarmapper.size.z)), "\"\n")); GotoNextMap(0); @@ -227,7 +210,8 @@ void RadarMap_Next() radarmapper = world; } void RadarMap_Think() -{SELFPARAM(); +{ + SELFPARAM(); // rough map entity // cnt: current line // size: pixel width/height @@ -237,7 +221,7 @@ void RadarMap_Think() float i, x, l; string si; - if(self.frame == 0) + if (self.frame == 0) { // initialize get_mi_min_max_texcoords(1); @@ -247,22 +231,16 @@ void RadarMap_Think() self.maxs_z = mi_max.z - mi_min.z; LOG_INFO("Picture mins/maxs: ", ftos(self.maxs.x), " and ", ftos(self.maxs.y), " should match\n"); self.netname = strzone(strcat("gfx/", mi_shortname, "_radar.xpm")); - if(!(self.count & 1)) + if (!(self.count & 1)) { self.cnt = fopen(self.netname, FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.tga"), FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.png"), FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.jpg"), FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.tga"), FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.png"), FILE_READ); - if(self.cnt < 0) - self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.jpg"), FILE_READ); - if(self.cnt >= 0) + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.tga"), FILE_READ); + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.png"), FILE_READ); + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_radar.jpg"), FILE_READ); + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.tga"), FILE_READ); + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.png"), FILE_READ); + if (self.cnt < 0) self.cnt = fopen(strcat("gfx/", mi_shortname, "_mini.jpg"), FILE_READ); + if (self.cnt >= 0) { fclose(self.cnt); @@ -272,7 +250,7 @@ void RadarMap_Think() } } self.cnt = fopen(self.netname, FILE_WRITE); - if(self.cnt < 0) + if (self.cnt < 0) { LOG_INFO("Error writing ", self.netname, "\n"); remove(self); @@ -284,47 +262,47 @@ void RadarMap_Think() fputs(self.cnt, "static char *RadarMap[] = {\n"); fputs(self.cnt, "/* columns rows colors chars-per-pixel */\n"); fputs(self.cnt, strcat("\"", ftos(self.size.x), " ", ftos(self.size.y), " 256 2\",\n")); - for(i = 0; i < 256; ++i) + for (i = 0; i < 256; ++i) { - si = substring(doublehex, i*2, 2); + si = substring(doublehex, i * 2, 2); fputs(self.cnt, strcat("\"", si, " c #", si, si, si, "\",\n")); } self.frame += 1; self.nextthink = time; sharpen_init(self.size.x); } - else if(self.frame <= self.size.y) + else if (self.frame <= self.size.y) { // fill the sharpen buffer with this line sharpen_shift(self.size.x); i = self.count & 24; - switch(i) + switch (i) { case 0: default: - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = RadarMapAtPoint_Block(self.mins.x + x * self.maxs.x, self.mins.y + (self.size.y - self.frame) * self.maxs.y, self.maxs.x, self.maxs.y, self.mins.z, self.maxs.z, self.size.z); sharpen_set(x, l); } break; case 8: - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = RadarMapAtPoint_Trace(self.mins.x + x * self.maxs.x, self.mins.y + (self.size.y - self.frame) * self.maxs.y, self.maxs.x, self.maxs.y, self.mins.z, self.maxs.z, self.size.z); sharpen_set(x, l); } break; case 16: - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = RadarMapAtPoint_Sample(self.mins.x + x * self.maxs.x, self.mins.y + (self.size.y - self.frame) * self.maxs.y, self.maxs.x, self.maxs.y, self.mins.z, self.maxs.z, self.size.z); sharpen_set(x, l); } break; case 24: - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = RadarMapAtPoint_LineBlock(self.mins.x + x * self.maxs.x, self.mins.y + (self.size.y - self.frame) * self.maxs.y, self.maxs.x, self.maxs.y, self.mins.z, self.maxs.z, self.size.z); sharpen_set(x, l); @@ -333,17 +311,19 @@ void RadarMap_Think() } // do we have enough lines? - if(self.frame >= 2) + if (self.frame >= 2) { // write a pixel line fputs(self.cnt, "\""); - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = sharpen_get(x, self.ltime); fputs(self.cnt, substring(doublehex, 2 * floor(l * 256.0), 2)); } - if(self.frame == self.size.y) + if (self.frame == self.size.y) + { fputs(self.cnt, "\"\n"); + } else { fputs(self.cnt, "\",\n"); @@ -352,18 +332,20 @@ void RadarMap_Think() } // is this the last line? then write back the missing line - if(self.frame == self.size.y) + if (self.frame == self.size.y) { sharpen_shift(self.size.x); // write a pixel line fputs(self.cnt, "\""); - for(x = 0; x < self.size.x; ++x) + for (x = 0; x < self.size.x; ++x) { l = sharpen_get(x, self.ltime); fputs(self.cnt, substring(doublehex, 2 * floor(l * 256.0), 2)); } - if(self.frame == self.size.y) + if (self.frame == self.size.y) + { fputs(self.cnt, "\"\n"); + } else { fputs(self.cnt, "\",\n"); @@ -388,32 +370,72 @@ float RadarMap_Make(float argc) { float i; - if(!radarmapper) + if (!radarmapper) { - radarmapper = spawn(); - radarmapper.classname = "radarmapper"; + radarmapper = new(radarmapper); radarmapper.think = RadarMap_Think; radarmapper.nextthink = time; - radarmapper.count = 8; // default to the --trace method, as it is faster now + radarmapper.count = 8; // default to the --trace method, as it is faster now radarmapper.ltime = 1; radarmapper.size = '512 512 1'; - for(i = 1; i < argc; ++i) + for (i = 1; i < argc; ++i) { - switch(argv(i)) + switch (argv(i)) { - case "--force": { radarmapper.count |= 1; break; } - case "--loop": { radarmapper.count |= 2; break; } - case "--quit": { radarmapper.count |= 4; break; } - case "--block": { radarmapper.count &= ~24; break; } - case "--trace": { radarmapper.count &= ~24; radarmapper.count |= 8; break; } - case "--sample": { radarmapper.count &= ~24; radarmapper.count |= 16; break; } - case "--lineblock": { radarmapper.count |= 24; break; } - case "--flags": { ++i; radarmapper.count = stof(argv(i)); break; } // for the recursive call - case "--sharpen": { ++i; radarmapper.ltime = stof(argv(i)); break; } // for the recursive call - case "--res": // minor alias - case "--resolution": { ++i; radarmapper.size_x = stof(argv(i)); ++i; radarmapper.size_y = stof(argv(i)); break; } - case "--qual": // minor alias - case "--quality": { ++i; radarmapper.size_z = stof(argv(i)); break; } + case "--force": + { radarmapper.count |= 1; + break; + } + case "--loop": + { radarmapper.count |= 2; + break; + } + case "--quit": + { radarmapper.count |= 4; + break; + } + case "--block": + { radarmapper.count &= ~24; + break; + } + case "--trace": + { radarmapper.count &= ~24; + radarmapper.count |= 8; + break; + } + case "--sample": + { radarmapper.count &= ~24; + radarmapper.count |= 16; + break; + } + case "--lineblock": + { radarmapper.count |= 24; + break; + } + case "--flags": + { ++i; + radarmapper.count = stof(argv(i)); + break; + } // for the recursive call + case "--sharpen": + { ++i; + radarmapper.ltime = stof(argv(i)); + break; + } // for the recursive call + case "--res": // minor alias + case "--resolution": + { ++i; + radarmapper.size_x = stof(argv(i)); + ++i; + radarmapper.size_y = stof(argv(i)); + break; + } + case "--qual": // minor alias + case "--quality": + { ++i; + radarmapper.size_z = stof(argv(i)); + break; + } default: i = argc; @@ -423,10 +445,10 @@ float RadarMap_Make(float argc) } } - if(radarmapper) // after doing the arguments, see if we successfully went forward. + if (radarmapper) // after doing the arguments, see if we successfully went forward. { LOG_INFO("Radarmap entity spawned.\n"); - return true; // if so, don't print usage. + return true; // if so, don't print usage. } } diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index cde2d6c452..eec862f364 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -31,7 +31,7 @@ #include "../../common/monsters/sv_monsters.qh" -void PutObserverInServer (void); +void PutObserverInServer(); // ===================================================== // Server side game commands code, reworked by Samual @@ -40,8 +40,9 @@ void PutObserverInServer (void); // used by GameCommand_make_mapinfo() void make_mapinfo_Think() -{SELFPARAM(); - if(MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) +{ + SELFPARAM(); + if (MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) { LOG_INFO("Done rebuiling mapinfos.\n"); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); @@ -61,35 +62,29 @@ void changematchtime(float delta, float mi, float ma) float update; float lim; - if(delta == 0) - return; - if(autocvar_timelimit < 0) - return; + if (delta == 0) return; + if (autocvar_timelimit < 0) return; - if(mi <= 10) - mi = 10; // at least ten sec in the future + if (mi <= 10) mi = 10; // at least ten sec in the future cur = time - game_starttime; - if(cur > 0) - mi += cur; // from current time! + if (cur > 0) mi += cur; // from current time! lim = autocvar_timelimit * 60; - if(delta > 0) + if (delta > 0) { - if(lim == 0) - return; // cannot increase any further - else if(lim < ma) - update = min(ma, lim + delta); - else // already above maximum: FAIL + if (lim == 0) return; // cannot increase any further + else if (lim < ma) update = min(ma, lim + delta); + else // already above maximum: FAIL return; } else { - if(lim == 0) // infinite: try reducing to max, if we are allowed to + if (lim == 0) // infinite: try reducing to max, if we are allowed to update = max(mi, ma); - else if(lim > mi) // above minimum: decrease + else if (lim > mi) // above minimum: decrease update = max(mi, lim + delta); - else // already below minimum: FAIL + else // already below minimum: FAIL return; } @@ -103,7 +98,7 @@ void changematchtime(float delta, float mi, float ma) void GameCommand_adminmsg(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -118,24 +113,25 @@ void GameCommand_adminmsg(float request, float argc) string successful, t; successful = string_null; - if((targets) && (admin_message)) + if ((targets) && (admin_message)) { - for (;targets;) + for ( ; targets; ) { - t = car(targets); targets = cdr(targets); + t = car(targets); + targets = cdr(targets); // Check to see if the player is a valid target client = GetFilteredEntity(t); accepted = VerifyClientEntity(client, true, false); - if(accepted <= 0) + if (accepted <= 0) { LOG_INFO("adminmsg: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n")); continue; } // send the centerprint/console print or infomessage - if(infobartime) + if (infobartime) { stuffcmd(client, sprintf("\ninfobar %f \"%s\"\n", infobartime, MakeConsoleSafe(admin_message))); } @@ -150,10 +146,8 @@ void GameCommand_adminmsg(float request, float argc) continue; } - if(successful) - bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n"); - else - LOG_INFO("No players given (", original_targets, ") could receive the message.\n"); + if (successful) bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n"); + else LOG_INFO("No players given (", original_targets, ") could receive the message.\n"); return; } @@ -176,7 +170,7 @@ void GameCommand_adminmsg(float request, float argc) void GameCommand_allready(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -195,8 +189,9 @@ void GameCommand_allready(float request) } void GameCommand_allspec(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { @@ -206,13 +201,12 @@ void GameCommand_allspec(float request, float argc) FOR_EACH_REALPLAYER(client) { - if (client.caplayer) - client.caplayer = 0; + if (client.caplayer) client.caplayer = 0; WITH(entity, self, client, PutObserverInServer()); ++i; } - if(i) { bprint(strcat("Successfully forced all (", ftos(i), ") players to spectate", (reason ? strcat(" for reason: '", reason, "'") : ""), ".\n")); } - else { LOG_INFO("No players found to spectate.\n"); } + if (i) bprint(strcat("Successfully forced all (", ftos(i), ") players to spectate", (reason ? strcat(" for reason: '", reason, "'") : ""), ".\n")); + else LOG_INFO("No players found to spectate.\n"); return; } @@ -228,15 +222,16 @@ void GameCommand_allspec(float request, float argc) } void GameCommand_anticheat(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { entity client = GetIndexedEntity(argc, 1); float accepted = VerifyClientEntity(client, false, false); - if(accepted > 0) + if (accepted > 0) { WITH(entity, self, client, anticheat_report()); return; @@ -260,7 +255,7 @@ void GameCommand_anticheat(float request, float argc) void GameCommand_bbox(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -271,70 +266,58 @@ void GameCommand_bbox(float request) LOG_INFO("Solid bounding box size:"); tracebox('1 0 0' * world.absmin.x, - '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z, - '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z, - '1 0 0' * world.absmax.x, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmin.x)); - else - LOG_INFO(" ", ftos(trace_endpos.x)); + '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z, + '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z, + '1 0 0' * world.absmax.x, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmin.x)); + else LOG_INFO(" ", ftos(trace_endpos.x)); tracebox('0 1 0' * world.absmin.y, - '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z, - '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z, - '0 1 0' * world.absmax.y, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmin.y)); - else - LOG_INFO(" ", ftos(trace_endpos.y)); + '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z, + '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z, + '0 1 0' * world.absmax.y, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmin.y)); + else LOG_INFO(" ", ftos(trace_endpos.y)); tracebox('0 0 1' * world.absmin.z, - '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y, - '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y, - '0 0 1' * world.absmax.z, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmin.z)); - else - LOG_INFO(" ", ftos(trace_endpos.z)); + '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y, + '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y, + '0 0 1' * world.absmax.z, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmin.z)); + else LOG_INFO(" ", ftos(trace_endpos.z)); tracebox('1 0 0' * world.absmax.x, - '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z, - '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z, - '1 0 0' * world.absmin.x, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmax.x)); - else - LOG_INFO(" ", ftos(trace_endpos.x)); + '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z, + '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z, + '1 0 0' * world.absmin.x, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmax.x)); + else LOG_INFO(" ", ftos(trace_endpos.x)); tracebox('0 1 0' * world.absmax.y, - '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z, - '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z, - '0 1 0' * world.absmin.y, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmax.y)); - else - LOG_INFO(" ", ftos(trace_endpos.y)); + '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z, + '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z, + '0 1 0' * world.absmin.y, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmax.y)); + else LOG_INFO(" ", ftos(trace_endpos.y)); tracebox('0 0 1' * world.absmax.z, - '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y, - '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y, - '0 0 1' * world.absmin.z, - MOVE_WORLDONLY, - world); - if(trace_startsolid) - LOG_INFO(" ", ftos(world.absmax.z)); - else - LOG_INFO(" ", ftos(trace_endpos.z)); + '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y, + '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y, + '0 0 1' * world.absmin.z, + MOVE_WORLDONLY, + world); + if (trace_startsolid) LOG_INFO(" ", ftos(world.absmax.z)); + else LOG_INFO(" ", ftos(trace_endpos.z)); LOG_INFO("\n"); return; @@ -353,72 +336,70 @@ void GameCommand_bbox(float request) void GameCommand_bot_cmd(float request, float argc, string command) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity bot; - if(argv(1) == "reset") + if (argv(1) == "reset") { bot_resetqueues(); return; } - else if(argv(1) == "setbots") + else if (argv(1) == "setbots") { cvar_settemp("bot_vs_human", "0"); cvar_settemp("minplayers", "0"); cvar_settemp("bot_number", "0"); bot_fixcount(); cvar_settemp("bot_number", argv(2)); - if(!bot_fixcount()) - LOG_INFO("Sorry, could not set requested bot count\n"); + if (!bot_fixcount()) LOG_INFO("Sorry, could not set requested bot count\n"); return; } - else if(argv(1) == "load" && argc == 3) + else if (argv(1) == "load" && argc == 3) { float fh, i; string s; fh = fopen(argv(2), FILE_READ); - if(fh < 0) + if (fh < 0) { LOG_INFO("cannot open the file\n"); return; } i = 0; - while((s = fgets(fh))) + while ((s = fgets(fh))) { argc = tokenize_console(s); - if(argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd") + if (argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd") { - if(argv(2) == "reset") + if (argv(2) == "reset") { bot_resetqueues(); } - else if(argv(2) == "setbots") + else if (argv(2) == "setbots") { cvar_settemp("bot_vs_human", "0"); cvar_settemp("minplayers", "0"); cvar_settemp("bot_number", "0"); bot_fixcount(); cvar_settemp("bot_number", argv(3)); - if(!bot_fixcount()) - LOG_INFO("Sorry, could not set requested bot count\n"); + if (!bot_fixcount()) LOG_INFO("Sorry, could not set requested bot count\n"); } else { // let's start at token 2 so we can skip sv_cmd bot_cmd bot = find_bot_by_number(stof(argv(2))); - if(bot == world) - bot = find_bot_by_name(argv(2)); - if(bot) - bot_queuecommand(bot, substring(s, argv_start_index(3), -1)); + if (bot == world) bot = find_bot_by_name(argv(2)); + if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1)); } } else + { localcmd(strcat(s, "\n")); + } ++i; } @@ -426,27 +407,26 @@ void GameCommand_bot_cmd(float request, float argc, string command) fclose(fh); return; } - else if(argv(1) == "help") + else if (argv(1) == "help") { - if(argv(2)) - bot_cmdhelp(argv(2)); - else - bot_list_commands(); + if (argv(2)) bot_cmdhelp(argv(2)); + else bot_list_commands(); return; } - else if(argc >= 3) // this comes last + else if (argc >= 3) // this comes last { bot = find_bot_by_number(stof(argv(1))); - if(bot == world) - bot = find_bot_by_name(argv(1)); - if(bot) + if (bot == world) bot = find_bot_by_name(argv(1)); + if (bot) { LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n")); bot_queuecommand(bot, substring(command, argv_start_index(2), -1)); return; } else - LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1),"' - Did you mistype the command?\n")); // don't return so that usage is shown + { + LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n")); // don't return so that usage is shown + } } } @@ -466,7 +446,7 @@ void GameCommand_bot_cmd(float request, float argc, string command) void GameCommand_cointoss(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -490,25 +470,25 @@ void GameCommand_cointoss(float request, float argc) void GameCommand_database(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc == 3) + if (argc == 3) { - if(argv(1) == "save") + if (argv(1) == "save") { db_save(ServerProgsDB, argv(2)); LOG_INFO(strcat("Copied serverprogs database to '", argv(2), "' in the data directory.\n")); return; } - else if(argv(1) == "dump") + else if (argv(1) == "dump") { db_dump(ServerProgsDB, argv(2)); - LOG_INFO("DB dumped.\n"); // wtf does this do? + LOG_INFO("DB dumped.\n"); // wtf does this do? return; } - else if(argv(1) == "load") + else if (argv(1) == "load") { db_close(ServerProgsDB); ServerProgsDB = db_load(argv(2)); @@ -533,19 +513,19 @@ void GameCommand_database(float request, float argc) void GameCommand_defer_clear(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity client; float accepted; - if(argc >= 2) + if (argc >= 2) { client = GetIndexedEntity(argc, 1); accepted = VerifyClientEntity(client, true, false); - if(accepted > 0) + if (accepted > 0) { stuffcmd(client, "defer clear\n"); LOG_INFO("defer clear stuffed to ", client.netname, "\n"); @@ -570,7 +550,7 @@ void GameCommand_defer_clear(float request, float argc) void GameCommand_defer_clear_all(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -584,7 +564,7 @@ void GameCommand_defer_clear_all(float request) GameCommand_defer_clear(CMD_REQUEST_COMMAND, argc); ++i; } - if(i) { LOG_INFO(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found? + if (i) LOG_INFO(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); // should a message be added if no players were found? return; } @@ -601,16 +581,14 @@ void GameCommand_defer_clear_all(float request) void GameCommand_delrec(float request, float argc) // perhaps merge later with records and printstats and such? { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { - if(argv(2)) - race_deleteTime(argv(2), stof(argv(1))); - else - race_deleteTime(GetMapname(), stof(argv(1))); + if (argv(2)) race_deleteTime(argv(2), stof(argv(1))); + else race_deleteTime(GetMapname(), stof(argv(1))); return; } } @@ -630,7 +608,7 @@ void GameCommand_delrec(float request, float argc) // perhaps merge later with void GameCommand_effectindexdump(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -639,54 +617,89 @@ void GameCommand_effectindexdump(float request) d = db_create(); LOG_INFO("begin of effects list\n"); - db_put(d, "TE_GUNSHOT", "1"); LOG_INFO("effect TE_GUNSHOT is ", ftos(_particleeffectnum("TE_GUNSHOT")), "\n"); - db_put(d, "TE_GUNSHOTQUAD", "1"); LOG_INFO("effect TE_GUNSHOTQUAD is ", ftos(_particleeffectnum("TE_GUNSHOTQUAD")), "\n"); - db_put(d, "TE_SPIKE", "1"); LOG_INFO("effect TE_SPIKE is ", ftos(_particleeffectnum("TE_SPIKE")), "\n"); - db_put(d, "TE_SPIKEQUAD", "1"); LOG_INFO("effect TE_SPIKEQUAD is ", ftos(_particleeffectnum("TE_SPIKEQUAD")), "\n"); - db_put(d, "TE_SUPERSPIKE", "1"); LOG_INFO("effect TE_SUPERSPIKE is ", ftos(_particleeffectnum("TE_SUPERSPIKE")), "\n"); - db_put(d, "TE_SUPERSPIKEQUAD", "1"); LOG_INFO("effect TE_SUPERSPIKEQUAD is ", ftos(_particleeffectnum("TE_SUPERSPIKEQUAD")), "\n"); - db_put(d, "TE_WIZSPIKE", "1"); LOG_INFO("effect TE_WIZSPIKE is ", ftos(_particleeffectnum("TE_WIZSPIKE")), "\n"); - db_put(d, "TE_KNIGHTSPIKE", "1"); LOG_INFO("effect TE_KNIGHTSPIKE is ", ftos(_particleeffectnum("TE_KNIGHTSPIKE")), "\n"); - db_put(d, "TE_EXPLOSION", "1"); LOG_INFO("effect TE_EXPLOSION is ", ftos(_particleeffectnum("TE_EXPLOSION")), "\n"); - db_put(d, "TE_EXPLOSIONQUAD", "1"); LOG_INFO("effect TE_EXPLOSIONQUAD is ", ftos(_particleeffectnum("TE_EXPLOSIONQUAD")), "\n"); - db_put(d, "TE_TAREXPLOSION", "1"); LOG_INFO("effect TE_TAREXPLOSION is ", ftos(_particleeffectnum("TE_TAREXPLOSION")), "\n"); - db_put(d, "TE_TELEPORT", "1"); LOG_INFO("effect TE_TELEPORT is ", ftos(_particleeffectnum("TE_TELEPORT")), "\n"); - db_put(d, "TE_LAVASPLASH", "1"); LOG_INFO("effect TE_LAVASPLASH is ", ftos(_particleeffectnum("TE_LAVASPLASH")), "\n"); - db_put(d, "TE_SMALLFLASH", "1"); LOG_INFO("effect TE_SMALLFLASH is ", ftos(_particleeffectnum("TE_SMALLFLASH")), "\n"); - db_put(d, "TE_FLAMEJET", "1"); LOG_INFO("effect TE_FLAMEJET is ", ftos(_particleeffectnum("TE_FLAMEJET")), "\n"); - db_put(d, "EF_FLAME", "1"); LOG_INFO("effect EF_FLAME is ", ftos(_particleeffectnum("EF_FLAME")), "\n"); - db_put(d, "TE_BLOOD", "1"); LOG_INFO("effect TE_BLOOD is ", ftos(_particleeffectnum("TE_BLOOD")), "\n"); - db_put(d, "TE_SPARK", "1"); LOG_INFO("effect TE_SPARK is ", ftos(_particleeffectnum("TE_SPARK")), "\n"); - db_put(d, "TE_PLASMABURN", "1"); LOG_INFO("effect TE_PLASMABURN is ", ftos(_particleeffectnum("TE_PLASMABURN")), "\n"); - db_put(d, "TE_TEI_G3", "1"); LOG_INFO("effect TE_TEI_G3 is ", ftos(_particleeffectnum("TE_TEI_G3")), "\n"); - db_put(d, "TE_TEI_SMOKE", "1"); LOG_INFO("effect TE_TEI_SMOKE is ", ftos(_particleeffectnum("TE_TEI_SMOKE")), "\n"); - db_put(d, "TE_TEI_BIGEXPLOSION", "1"); LOG_INFO("effect TE_TEI_BIGEXPLOSION is ", ftos(_particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n"); - db_put(d, "TE_TEI_PLASMAHIT", "1"); LOG_INFO("effect TE_TEI_PLASMAHIT is ", ftos(_particleeffectnum("TE_TEI_PLASMAHIT")), "\n"); - db_put(d, "EF_STARDUST", "1"); LOG_INFO("effect EF_STARDUST is ", ftos(_particleeffectnum("EF_STARDUST")), "\n"); - db_put(d, "TR_ROCKET", "1"); LOG_INFO("effect TR_ROCKET is ", ftos(_particleeffectnum("TR_ROCKET")), "\n"); - db_put(d, "TR_GRENADE", "1"); LOG_INFO("effect TR_GRENADE is ", ftos(_particleeffectnum("TR_GRENADE")), "\n"); - db_put(d, "TR_BLOOD", "1"); LOG_INFO("effect TR_BLOOD is ", ftos(_particleeffectnum("TR_BLOOD")), "\n"); - db_put(d, "TR_WIZSPIKE", "1"); LOG_INFO("effect TR_WIZSPIKE is ", ftos(_particleeffectnum("TR_WIZSPIKE")), "\n"); - db_put(d, "TR_SLIGHTBLOOD", "1"); LOG_INFO("effect TR_SLIGHTBLOOD is ", ftos(_particleeffectnum("TR_SLIGHTBLOOD")), "\n"); - db_put(d, "TR_KNIGHTSPIKE", "1"); LOG_INFO("effect TR_KNIGHTSPIKE is ", ftos(_particleeffectnum("TR_KNIGHTSPIKE")), "\n"); - db_put(d, "TR_VORESPIKE", "1"); LOG_INFO("effect TR_VORESPIKE is ", ftos(_particleeffectnum("TR_VORESPIKE")), "\n"); - db_put(d, "TR_NEHAHRASMOKE", "1"); LOG_INFO("effect TR_NEHAHRASMOKE is ", ftos(_particleeffectnum("TR_NEHAHRASMOKE")), "\n"); - db_put(d, "TR_NEXUIZPLASMA", "1"); LOG_INFO("effect TR_NEXUIZPLASMA is ", ftos(_particleeffectnum("TR_NEXUIZPLASMA")), "\n"); - db_put(d, "TR_GLOWTRAIL", "1"); LOG_INFO("effect TR_GLOWTRAIL is ", ftos(_particleeffectnum("TR_GLOWTRAIL")), "\n"); - db_put(d, "TR_SEEKER", "1"); LOG_INFO("effect TR_SEEKER is ", ftos(_particleeffectnum("TR_SEEKER")), "\n"); - db_put(d, "SVC_PARTICLE", "1"); LOG_INFO("effect SVC_PARTICLE is ", ftos(_particleeffectnum("SVC_PARTICLE")), "\n"); + db_put(d, "TE_GUNSHOT", "1"); + LOG_INFO("effect TE_GUNSHOT is ", ftos(_particleeffectnum("TE_GUNSHOT")), "\n"); + db_put(d, "TE_GUNSHOTQUAD", "1"); + LOG_INFO("effect TE_GUNSHOTQUAD is ", ftos(_particleeffectnum("TE_GUNSHOTQUAD")), "\n"); + db_put(d, "TE_SPIKE", "1"); + LOG_INFO("effect TE_SPIKE is ", ftos(_particleeffectnum("TE_SPIKE")), "\n"); + db_put(d, "TE_SPIKEQUAD", "1"); + LOG_INFO("effect TE_SPIKEQUAD is ", ftos(_particleeffectnum("TE_SPIKEQUAD")), "\n"); + db_put(d, "TE_SUPERSPIKE", "1"); + LOG_INFO("effect TE_SUPERSPIKE is ", ftos(_particleeffectnum("TE_SUPERSPIKE")), "\n"); + db_put(d, "TE_SUPERSPIKEQUAD", "1"); + LOG_INFO("effect TE_SUPERSPIKEQUAD is ", ftos(_particleeffectnum("TE_SUPERSPIKEQUAD")), "\n"); + db_put(d, "TE_WIZSPIKE", "1"); + LOG_INFO("effect TE_WIZSPIKE is ", ftos(_particleeffectnum("TE_WIZSPIKE")), "\n"); + db_put(d, "TE_KNIGHTSPIKE", "1"); + LOG_INFO("effect TE_KNIGHTSPIKE is ", ftos(_particleeffectnum("TE_KNIGHTSPIKE")), "\n"); + db_put(d, "TE_EXPLOSION", "1"); + LOG_INFO("effect TE_EXPLOSION is ", ftos(_particleeffectnum("TE_EXPLOSION")), "\n"); + db_put(d, "TE_EXPLOSIONQUAD", "1"); + LOG_INFO("effect TE_EXPLOSIONQUAD is ", ftos(_particleeffectnum("TE_EXPLOSIONQUAD")), "\n"); + db_put(d, "TE_TAREXPLOSION", "1"); + LOG_INFO("effect TE_TAREXPLOSION is ", ftos(_particleeffectnum("TE_TAREXPLOSION")), "\n"); + db_put(d, "TE_TELEPORT", "1"); + LOG_INFO("effect TE_TELEPORT is ", ftos(_particleeffectnum("TE_TELEPORT")), "\n"); + db_put(d, "TE_LAVASPLASH", "1"); + LOG_INFO("effect TE_LAVASPLASH is ", ftos(_particleeffectnum("TE_LAVASPLASH")), "\n"); + db_put(d, "TE_SMALLFLASH", "1"); + LOG_INFO("effect TE_SMALLFLASH is ", ftos(_particleeffectnum("TE_SMALLFLASH")), "\n"); + db_put(d, "TE_FLAMEJET", "1"); + LOG_INFO("effect TE_FLAMEJET is ", ftos(_particleeffectnum("TE_FLAMEJET")), "\n"); + db_put(d, "EF_FLAME", "1"); + LOG_INFO("effect EF_FLAME is ", ftos(_particleeffectnum("EF_FLAME")), "\n"); + db_put(d, "TE_BLOOD", "1"); + LOG_INFO("effect TE_BLOOD is ", ftos(_particleeffectnum("TE_BLOOD")), "\n"); + db_put(d, "TE_SPARK", "1"); + LOG_INFO("effect TE_SPARK is ", ftos(_particleeffectnum("TE_SPARK")), "\n"); + db_put(d, "TE_PLASMABURN", "1"); + LOG_INFO("effect TE_PLASMABURN is ", ftos(_particleeffectnum("TE_PLASMABURN")), "\n"); + db_put(d, "TE_TEI_G3", "1"); + LOG_INFO("effect TE_TEI_G3 is ", ftos(_particleeffectnum("TE_TEI_G3")), "\n"); + db_put(d, "TE_TEI_SMOKE", "1"); + LOG_INFO("effect TE_TEI_SMOKE is ", ftos(_particleeffectnum("TE_TEI_SMOKE")), "\n"); + db_put(d, "TE_TEI_BIGEXPLOSION", "1"); + LOG_INFO("effect TE_TEI_BIGEXPLOSION is ", ftos(_particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n"); + db_put(d, "TE_TEI_PLASMAHIT", "1"); + LOG_INFO("effect TE_TEI_PLASMAHIT is ", ftos(_particleeffectnum("TE_TEI_PLASMAHIT")), "\n"); + db_put(d, "EF_STARDUST", "1"); + LOG_INFO("effect EF_STARDUST is ", ftos(_particleeffectnum("EF_STARDUST")), "\n"); + db_put(d, "TR_ROCKET", "1"); + LOG_INFO("effect TR_ROCKET is ", ftos(_particleeffectnum("TR_ROCKET")), "\n"); + db_put(d, "TR_GRENADE", "1"); + LOG_INFO("effect TR_GRENADE is ", ftos(_particleeffectnum("TR_GRENADE")), "\n"); + db_put(d, "TR_BLOOD", "1"); + LOG_INFO("effect TR_BLOOD is ", ftos(_particleeffectnum("TR_BLOOD")), "\n"); + db_put(d, "TR_WIZSPIKE", "1"); + LOG_INFO("effect TR_WIZSPIKE is ", ftos(_particleeffectnum("TR_WIZSPIKE")), "\n"); + db_put(d, "TR_SLIGHTBLOOD", "1"); + LOG_INFO("effect TR_SLIGHTBLOOD is ", ftos(_particleeffectnum("TR_SLIGHTBLOOD")), "\n"); + db_put(d, "TR_KNIGHTSPIKE", "1"); + LOG_INFO("effect TR_KNIGHTSPIKE is ", ftos(_particleeffectnum("TR_KNIGHTSPIKE")), "\n"); + db_put(d, "TR_VORESPIKE", "1"); + LOG_INFO("effect TR_VORESPIKE is ", ftos(_particleeffectnum("TR_VORESPIKE")), "\n"); + db_put(d, "TR_NEHAHRASMOKE", "1"); + LOG_INFO("effect TR_NEHAHRASMOKE is ", ftos(_particleeffectnum("TR_NEHAHRASMOKE")), "\n"); + db_put(d, "TR_NEXUIZPLASMA", "1"); + LOG_INFO("effect TR_NEXUIZPLASMA is ", ftos(_particleeffectnum("TR_NEXUIZPLASMA")), "\n"); + db_put(d, "TR_GLOWTRAIL", "1"); + LOG_INFO("effect TR_GLOWTRAIL is ", ftos(_particleeffectnum("TR_GLOWTRAIL")), "\n"); + db_put(d, "TR_SEEKER", "1"); + LOG_INFO("effect TR_SEEKER is ", ftos(_particleeffectnum("TR_SEEKER")), "\n"); + db_put(d, "SVC_PARTICLE", "1"); + LOG_INFO("effect SVC_PARTICLE is ", ftos(_particleeffectnum("SVC_PARTICLE")), "\n"); fh = fopen("effectinfo.txt", FILE_READ); - while((s = fgets(fh))) + while ((s = fgets(fh))) { tokenize_console(s); - if(argv(0) == "effect") + if (argv(0) == "effect") { - if(db_get(d, argv(1)) != "1") + if (db_get(d, argv(1)) != "1") { int i = _particleeffectnum(argv(1)); - if(i >= 0) - LOG_INFO("effect ", argv(1), " is ", ftos(i), "\n"); + if (i >= 0) LOG_INFO("effect ", argv(1), " is ", ftos(i), "\n"); db_put(d, argv(1), "1"); } } @@ -709,7 +722,7 @@ void GameCommand_effectindexdump(float request) void GameCommand_extendmatchtime(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -730,13 +743,13 @@ void GameCommand_extendmatchtime(float request) void GameCommand_find(float request, float argc) // is this even needed? We have prvm_edicts command and such ANYWAY { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity client; - for(client = world; (client = find(client, classname, argv(1))); ) + for (client = world; (client = find(client, classname, argv(1))); ) LOG_INFO(etos(client), "\n"); return; @@ -755,23 +768,23 @@ void GameCommand_find(float request, float argc) // is this even needed? We hav void GameCommand_gametype(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1) != "") + if (argv(1) != "") { string s = argv(1); float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype(); - if(t) + if (t) { MapInfo_SwitchGameType(t); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); - if(MapInfo_count > 0) + if (MapInfo_count > 0) { // update lsmaps in case the gametype changed, this way people can easily list maps for it - if(lsmaps_reply != "") { strunzone(lsmaps_reply); } + if (lsmaps_reply != "") strunzone(lsmaps_reply); lsmaps_reply = strzone(getlsmaps()); bprint("Game type successfully switched to ", s, "\n"); } @@ -783,7 +796,9 @@ void GameCommand_gametype(float request, float argc) } } else + { bprint("Game type switch to ", s, " failed: this type does not exist!\n"); + } return; } @@ -803,7 +818,7 @@ void GameCommand_gametype(float request, float argc) void GameCommand_gettaginfo(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -811,22 +826,23 @@ void GameCommand_gettaginfo(float request, float argc) float i; vector v; - if(argc >= 4) + if (argc >= 4) { tmp_entity = spawn(); - if(argv(1) == "w") - _setmodel(tmp_entity, (nextent(world)).weaponentity.model); + if (argv(1) == "w") + { + .entity weaponentity = weaponentities[0]; + _setmodel(tmp_entity, (nextent(world)).(weaponentity).model); + } else { precache_model(argv(1)); _setmodel(tmp_entity, argv(1)); } tmp_entity.frame = stof(argv(2)); - if(substring(argv(3), 0, 1) == "#") - i = stof(substring(argv(3), 1, -1)); - else - i = gettagindex(tmp_entity, argv(3)); - if(i) + if (substring(argv(3), 0, 1) == "#") i = stof(substring(argv(3), 1, -1)); + else i = gettagindex(tmp_entity, argv(3)); + if (i) { v = gettaginfo(tmp_entity, i); LOG_INFO("model ", tmp_entity.model, " frame ", ftos(tmp_entity.frame), " tag ", gettaginfo_name); @@ -836,14 +852,16 @@ void GameCommand_gettaginfo(float request, float argc) LOG_INFO(" forward = ", ftos(gettaginfo_forward.x), " ", ftos(gettaginfo_forward.y), " ", ftos(gettaginfo_forward.z), "\n"); LOG_INFO(" right = ", ftos(gettaginfo_right.x), " ", ftos(gettaginfo_right.y), " ", ftos(gettaginfo_right.z), "\n"); LOG_INFO(" up = ", ftos(gettaginfo_up.x), " ", ftos(gettaginfo_up.y), " ", ftos(gettaginfo_up.z), "\n"); - if(argc >= 6) + if (argc >= 6) { v.y = -v.y; localcmd(strcat(argv(4), vtos(v), argv(5), "\n")); } } else + { LOG_INFO("bone not found\n"); + } remove(tmp_entity); return; @@ -863,17 +881,20 @@ void GameCommand_gettaginfo(float request, float argc) void GameCommand_animbench(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity tmp_entity; - if(argc >= 4) + if (argc >= 4) { tmp_entity = spawn(); - if(argv(1) == "w") - _setmodel(tmp_entity, (nextent(world)).weaponentity.model); + if (argv(1) == "w") + { + .entity weaponentity = weaponentities[0]; + _setmodel(tmp_entity, (nextent(world)).(weaponentity).model); + } else { precache_model(argv(1)); @@ -886,7 +907,7 @@ void GameCommand_animbench(float request, float argc) float t2 = 0; float n = 0; - while(t1 + t2 < 1) + while (t1 + t2 < 1) { tmp_entity.frame = f1; t0 = gettime(GETTIME_HIRES); @@ -919,11 +940,11 @@ void GameCommand_animbench(float request, float argc) void GameCommand_gotomap(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(1)) + if (argv(1)) { LOG_INFO(GotoMap(argv(1)), "\n"); return; @@ -944,11 +965,11 @@ void GameCommand_gotomap(float request, float argc) void GameCommand_lockteams(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(teamplay) + if (teamplay) { lockteams = 1; bprint("^1The teams are now locked.\n"); @@ -973,14 +994,13 @@ void GameCommand_lockteams(float request) void GameCommand_make_mapinfo(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { entity tmp_entity; - tmp_entity = spawn(); - tmp_entity.classname = "make_mapinfo"; + tmp_entity = new(make_mapinfo); tmp_entity.think = make_mapinfo_Think; tmp_entity.nextthink = time; MapInfo_Enumerate(); @@ -999,8 +1019,9 @@ void GameCommand_make_mapinfo(float request) } void GameCommand_moveplayer(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { @@ -1015,29 +1036,29 @@ void GameCommand_moveplayer(float request, float argc) successful = string_null; // lets see if the target(s) even actually exist. - if((targets) && (destination)) + if ((targets) && (destination)) { - for (;targets;) + for ( ; targets; ) { - t = car(targets); targets = cdr(targets); + t = car(targets); + targets = cdr(targets); // Check to see if the player is a valid target client = GetFilteredEntity(t); accepted = VerifyClientEntity(client, false, false); - if(accepted <= 0) + if (accepted <= 0) { LOG_INFO("moveplayer: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : ".\n")); continue; } // Where are we putting this player? - if(destination == "spec" || destination == "spectator") + if (destination == "spec" || destination == "spectator") { - if(!IS_SPEC(client) && !IS_OBSERVER(client)) + if (!IS_SPEC(client) && !IS_OBSERVER(client)) { - if (client.caplayer) - client.caplayer = 0; + if (client.caplayer) client.caplayer = 0; WITH(entity, self, client, PutObserverInServer()); successful = strcat(successful, (successful ? ", " : ""), client.netname); @@ -1050,9 +1071,9 @@ void GameCommand_moveplayer(float request, float argc) } else { - if(!IS_SPEC(client) && !IS_OBSERVER(client)) + if (!IS_SPEC(client) && !IS_OBSERVER(client)) { - if(teamplay) + if (teamplay) { // set up float team_id; @@ -1061,13 +1082,13 @@ void GameCommand_moveplayer(float request, float argc) // find the team to move the player to team_id = Team_ColorToTeam(destination); - if(team_id == client.team) // already on the destination team + if (team_id == client.team) // already on the destination team { // keep the forcing undone LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", client.netname, ") is already on the ", Team_ColoredFullName(client.team), (targets ? "^7, skipping to next player.\n" : "^7.\n")); continue; } - else if(team_id == 0) // auto team + else if (team_id == 0) // auto team { CheckAllowedTeams(client); team_id = Team_NumberToTeam(FindSmallestTeam(client, false)); @@ -1079,14 +1100,15 @@ void GameCommand_moveplayer(float request, float argc) client.team_forced = save; // Check to see if the destination team is even available - switch(team_id) + switch (team_id) { - case NUM_TEAM_1: if(c1 == -1) { LOG_INFO("Sorry, can't move player to red team if it doesn't exist.\n"); return; } break; - case NUM_TEAM_2: if(c2 == -1) { LOG_INFO("Sorry, can't move player to blue team if it doesn't exist.\n"); return; } break; - case NUM_TEAM_3: if(c3 == -1) { LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist.\n"); return; } break; - case NUM_TEAM_4: if(c4 == -1) { LOG_INFO("Sorry, can't move player to pink team if it doesn't exist.\n"); return; } break; + case NUM_TEAM_1: if (c1 == -1) { LOG_INFO("Sorry, can't move player to red team if it doesn't exist.\n"); return; } break; + case NUM_TEAM_2: if (c2 == -1) { LOG_INFO("Sorry, can't move player to blue team if it doesn't exist.\n"); return; } break; + case NUM_TEAM_3: if (c3 == -1) { LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist.\n"); return; } break; + case NUM_TEAM_4: if (c4 == -1) { LOG_INFO("Sorry, can't move player to pink team if it doesn't exist.\n"); return; } break; - default: LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist.\n"); return; + default: LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist.\n"); + return; } // If so, lets continue and finally move the player @@ -1104,18 +1126,16 @@ void GameCommand_moveplayer(float request, float argc) } else { - LOG_INFO("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P + LOG_INFO("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P return; } } } - if(successful) - bprint("Successfully moved players ", successful, " to destination ", destination, ".\n"); - else - LOG_INFO("No players given (", original_targets, ") are able to move.\n"); + if (successful) bprint("Successfully moved players ", successful, " to destination ", destination, ".\n"); + else LOG_INFO("No players given (", original_targets, ") are able to move.\n"); - return; // still correct parameters so return to avoid usage print + return; // still correct parameters so return to avoid usage print } } @@ -1137,19 +1157,21 @@ void GameCommand_moveplayer(float request, float argc) void GameCommand_nospectators(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { blockSpectators = 1; entity plr; - FOR_EACH_REALCLIENT(plr) //give every spectator seconds time to become a player + FOR_EACH_REALCLIENT(plr) // give every spectator seconds time to become a player { - if(IS_SPEC(plr) || IS_OBSERVER(plr)) - if(!plr.caplayer) + if (IS_SPEC(plr) || IS_OBSERVER(plr)) { - plr.spectatortime = time; - Send_Notification(NOTIF_ONE_ONLY, plr, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + if (!plr.caplayer) + { + plr.spectatortime = time; + Send_Notification(NOTIF_ONE_ONLY, plr, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + } } } bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n")); @@ -1167,24 +1189,25 @@ void GameCommand_nospectators(float request) } void GameCommand_playerdemo(float request, float argc) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(argv(2) && argv(3)) + if (argv(2) && argv(3)) { entity client; float i, n, accepted; - switch(argv(1)) + switch (argv(1)) { case "read": { client = GetIndexedEntity(argc, 2); accepted = VerifyClientEntity(client, false, true); - if(accepted <= 0) + if (accepted <= 0) { LOG_INFO("playerdemo: read: ", GetClientErrorString(accepted, argv(2)), ".\n"); return; @@ -1199,7 +1222,7 @@ void GameCommand_playerdemo(float request, float argc) client = GetIndexedEntity(argc, 2); accepted = VerifyClientEntity(client, false, false); - if(accepted <= 0) + if (accepted <= 0) { LOG_INFO("playerdemo: write: ", GetClientErrorString(accepted, argv(2)), ".\n"); return; @@ -1215,9 +1238,9 @@ void GameCommand_playerdemo(float request, float argc) cvar_set("bot_number", ftos(n)); localcmd("wait; wait; wait\n"); - for(i = 0; i < n; ++i) { localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", argv(2), ftos(i+1), "\n"); } - - localcmd("sv_cmd playerdemo write 1 ", ftos(n+1), "\n"); + for (i = 0; i < n; ++i) + localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); + localcmd("sv_cmd playerdemo write 1 ", ftos(n + 1), "\n"); return; } @@ -1227,7 +1250,8 @@ void GameCommand_playerdemo(float request, float argc) cvar_set("bot_number", ftos(n)); localcmd("wait; wait; wait\n"); - for(i = 0; i < n; ++i) { localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", argv(2), ftos(i+1), "\n"); } + for (i = 0; i < n; ++i) + localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); return; } } @@ -1247,7 +1271,7 @@ void GameCommand_playerdemo(float request, float argc) void GameCommand_printstats(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -1268,12 +1292,11 @@ void GameCommand_printstats(float request) void GameCommand_radarmap(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(RadarMap_Make(argc)) - return; + if (RadarMap_Make(argc)) return; } default: @@ -1291,11 +1314,11 @@ void GameCommand_radarmap(float request, float argc) void GameCommand_reducematchtime(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - changematchtime(autocvar_timelimit_decrement *-60, autocvar_timelimit_min * 60, autocvar_timelimit_max * 60); + changematchtime(autocvar_timelimit_decrement * -60, autocvar_timelimit_min * 60, autocvar_timelimit_max * 60); return; } @@ -1312,11 +1335,11 @@ void GameCommand_reducematchtime(float request) void GameCommand_setbots(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(argc >= 2) + if (argc >= 2) { cvar_settemp("minplayers", "0"); cvar_settemp("bot_number", argv(1)); @@ -1338,12 +1361,13 @@ void GameCommand_setbots(float request, float argc) } void GameCommand_shuffleteams(float request) -{SELFPARAM(); - switch(request) +{ + SELFPARAM(); + switch (request) { case CMD_REQUEST_COMMAND: { - if(teamplay) + if (teamplay) { entity tmp_player; int i; @@ -1353,29 +1377,29 @@ void GameCommand_shuffleteams(float request) t_players = 0; t_teams = 0; FOR_EACH_CLIENT(tmp_player) - if(IS_PLAYER(tmp_player) || tmp_player.caplayer) + if (IS_PLAYER(tmp_player) || tmp_player.caplayer) { CheckAllowedTeams(tmp_player); - if(c1 >= 0) t_teams = max(1, t_teams); - if(c2 >= 0) t_teams = max(2, t_teams); - if(c3 >= 0) t_teams = max(3, t_teams); - if(c4 >= 0) t_teams = max(4, t_teams); + if (c1 >= 0) t_teams = max(1, t_teams); + if (c2 >= 0) t_teams = max(2, t_teams); + if (c3 >= 0) t_teams = max(3, t_teams); + if (c4 >= 0) t_teams = max(4, t_teams); ++t_players; } // build a list of the players in a random order FOR_EACH_CLIENT(tmp_player) - if(IS_PLAYER(tmp_player) || tmp_player.caplayer) + if (IS_PLAYER(tmp_player) || tmp_player.caplayer) { - for (;;) + for ( ; ; ) { i = bound(1, floor(random() * maxclients) + 1, maxclients); - if(shuffleteams_players[i]) + if (shuffleteams_players[i]) { - continue; // a player is already assigned to this slot + continue; // a player is already assigned to this slot } else { @@ -1399,21 +1423,18 @@ void GameCommand_shuffleteams(float request) { if (!(shuffleteams_teams[i] >= x)) { - if (!(shuffleteams_players[z])) - continue; // not a player, move on to next random slot + if (!(shuffleteams_players[z])) continue; // not a player, move on to next random slot - if(VerifyClientNumber(shuffleteams_players[z])) - setself(edict_num(shuffleteams_players[z])); + if (VerifyClientNumber(shuffleteams_players[z])) setself(edict_num(shuffleteams_players[z])); - if(self.team != team_color) - MoveToTeam(self, team_color, 6); + if (self.team != team_color) MoveToTeam(self, team_color, 6); shuffleteams_players[z] = 0; shuffleteams_teams[i] = shuffleteams_teams[i] + 1; } else { - break; // move on to next team + break; // move on to next team } } } @@ -1421,10 +1442,10 @@ void GameCommand_shuffleteams(float request) bprint("Successfully shuffled the players around randomly.\n"); // clear the buffers now - for (i=0; i 0) + if (argv(2)) { - stuffcmd(client, strcat("\n", argv(next_token), "\n")); - LOG_INFO(strcat("Command: \"", argv(next_token), "\" sent to ", GetCallerName(client), " (", argv(1) ,").\n")); + entity client = GetIndexedEntity(argc, 1); + float accepted = VerifyClientEntity(client, true, false); + + if (accepted > 0) + { + stuffcmd(client, strcat("\n", argv(next_token), "\n")); + LOG_INFO(strcat("Command: \"", argv(next_token), "\" sent to ", GetCallerName(client), " (", argv(1), ").\n")); + } + else + { + LOG_INFO("stuffto: ", GetClientErrorString(accepted, argv(1)), ".\n"); + } + + return; } - else - LOG_INFO("stuffto: ", GetClientErrorString(accepted, argv(1)), ".\n"); + } + default: + LOG_INFO("Incorrect parameters for ^2stuffto^7\n"); + case CMD_REQUEST_USAGE: + { + LOG_INFO("\nUsage:^3 sv_cmd stuffto client \"command\"\n"); + LOG_INFO(" 'client' is the entity number or name of the player,\n"); + LOG_INFO(" and 'command' is the command to be sent to that player.\n"); return; } } - - default: - LOG_INFO("Incorrect parameters for ^2stuffto^7\n"); - case CMD_REQUEST_USAGE: +#else + if (request) { - LOG_INFO("\nUsage:^3 sv_cmd stuffto client \"command\"\n"); - LOG_INFO(" 'client' is the entity number or name of the player,\n"); - LOG_INFO(" and 'command' is the command to be sent to that player.\n"); + LOG_INFO("stuffto command is not enabled on this server.\n"); return; } - } - #else - if(request) - { - LOG_INFO("stuffto command is not enabled on this server.\n"); - return; - } - #endif +#endif } void GameCommand_trace(float request, float argc) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { @@ -1504,14 +1527,14 @@ void GameCommand_trace(float request, float argc) vector org, delta, start, end, p, q, q0, pos, vv, dv; float i, f, safe, unsafe, dq, dqf; - switch(argv(1)) + switch (argv(1)) { case "debug": { float hitcount = 0; LOG_INFO("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.\n"); float worst_endpos_bug = 0; - for (;;) + for ( ; ; ) { org = world.mins; delta = world.maxs - world.mins; @@ -1528,32 +1551,30 @@ void GameCommand_trace(float request, float argc) end = stov(vtos(end)); tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world); - if(!trace_startsolid && trace_fraction < 1) + if (!trace_startsolid && trace_fraction < 1) { p = trace_endpos; tracebox(p, PL_MIN, PL_MAX, p, MOVE_NOMONSTERS, world); - if(trace_startsolid) + if (trace_startsolid) { - rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid + rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world); // how much do we need to back off? safe = 1; unsafe = 0; - for (;;) + for ( ; ; ) { pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5); tracebox(pos, PL_MIN, PL_MAX, pos, MOVE_NOMONSTERS, world); - if(trace_startsolid) + if (trace_startsolid) { - if((safe + unsafe) * 0.5 == unsafe) - break; + if ((safe + unsafe) * 0.5 == unsafe) break; unsafe = (safe + unsafe) * 0.5; } else { - if((safe + unsafe) * 0.5 == safe) - break; + if ((safe + unsafe) * 0.5 == safe) break; safe = (safe + unsafe) * 0.5; } } @@ -1562,38 +1583,31 @@ void GameCommand_trace(float request, float argc) LOG_INFO("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n"); tracebox(p, PL_MIN + '0.1 0.1 0.1', PL_MAX - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, world); - if(trace_startsolid) - LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); - else - LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); - if (++hitcount >= 10) - break; + if (trace_startsolid) LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); + else LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); + if (++hitcount >= 10) break; } else { q0 = p; dq = 0; dqf = 1; - for (;;) + for ( ; ; ) { q = p + normalize(end - p) * (dq + dqf); - if(q == q0) - break; + if (q == q0) break; tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world); - if(trace_startsolid) - error("THIS ONE cannot happen"); - if(trace_fraction > 0) - dq += dqf * trace_fraction; + if (trace_startsolid) error("THIS ONE cannot happen"); + if (trace_fraction > 0) dq += dqf * trace_fraction; dqf *= 0.5; q0 = q; } - if(dq > worst_endpos_bug) + if (dq > worst_endpos_bug) { worst_endpos_bug = dq; LOG_INFO("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); LOG_INFO("could go ", ftos(dq), " units further to ", vtos(q), "\n"); - if (++hitcount >= 10) - break; + if (++hitcount >= 10) break; } } } @@ -1606,26 +1620,26 @@ void GameCommand_trace(float request, float argc) e = nextent(world); tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e); vv = trace_endpos; - if(trace_fraction == 1) + if (trace_fraction == 1) { LOG_INFO("not above ground, aborting\n"); return; } f = 0; - for(i = 0; i < 100000; ++i) + for (i = 0; i < 100000; ++i) { dv = randomvec(); - if(dv.z > 0) - dv = -1 * dv; + if (dv.z > 0) dv = -1 * dv; tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e); - if(trace_startsolid) - LOG_INFO("bug 1\n"); - if(trace_fraction == 1) - if(dv.z < f) + if (trace_startsolid) LOG_INFO("bug 1\n"); + if (trace_fraction == 1) { - LOG_INFO("bug 2: ", ftos(dv.x), " ", ftos(dv.y), " ", ftos(dv.z)); - LOG_INFO(" (", ftos(asin(dv.z / vlen(dv)) * 180 / M_PI), " degrees)\n"); - f = dv.z; + if (dv.z < f) + { + LOG_INFO("bug 2: ", ftos(dv.x), " ", ftos(dv.y), " ", ftos(dv.z)); + LOG_INFO(" (", ftos(asin(dv.z / vlen(dv)) * 180 / M_PI), " degrees)\n"); + f = dv.z; + } } } LOG_INFO("highest possible dist: ", ftos(f), "\n"); @@ -1634,31 +1648,29 @@ void GameCommand_trace(float request, float argc) case "walk": { - if(argc == 4) + if (argc == 4) { e = nextent(world); - if(tracewalk(e, stov(argv(2)), e.mins, e.maxs, stov(argv(3)), MOVE_NORMAL)) - LOG_INFO("can walk\n"); - else - LOG_INFO("cannot walk\n"); + if (tracewalk(e, stov(argv(2)), e.mins, e.maxs, stov(argv(3)), MOVE_NORMAL)) LOG_INFO("can walk\n"); + else LOG_INFO("cannot walk\n"); return; } } case "showline": { - if(argc == 4) + if (argc == 4) { vv = stov(argv(2)); dv = stov(argv(3)); traceline(vv, dv, MOVE_NORMAL, world); - trailparticles(world, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos); - trailparticles(world, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv); + __trailparticles(world, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos); + __trailparticles(world, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv); return; } } - // no default case, just go straight to invalid + // no default case, just go straight to invalid } } @@ -1676,11 +1688,11 @@ void GameCommand_trace(float request, float argc) void GameCommand_unlockteams(float request) { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(teamplay) + if (teamplay) { lockteams = 0; bprint("^1The teams are now unlocked.\n"); @@ -1709,9 +1721,9 @@ void GameCommand_warp(float request, float argc) { case CMD_REQUEST_COMMAND: { - if(autocvar_g_campaign) + if (autocvar_g_campaign) { - if(argc >= 2) + if (argc >= 2) { CampaignLevelWarp(stof(argv(1))); LOG_INFO("Successfully warped to campaign level ", stof(argv(1)), ".\n"); @@ -1723,7 +1735,9 @@ void GameCommand_warp(float request, float argc) } } else + { LOG_INFO("Not in campaign, can't level warp\n"); + } return; } @@ -1742,22 +1756,22 @@ void GameCommand_warp(float request, float argc) ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void GameCommand_(float request) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; - } - - default: - case CMD_REQUEST_USAGE: - { - print("\nUsage:^3 sv_cmd \n"); - print(" No arguments required.\n"); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print("\nUsage:^3 sv_cmd \n"); + print(" No arguments required.\n"); + return; + } + } } */ @@ -1767,7 +1781,7 @@ void GameCommand_(float request) // ================================== // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define SERVER_COMMANDS(request,arguments,command) \ +#define SERVER_COMMANDS(request, arguments, command) \ SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \ SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \ SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \ @@ -1804,46 +1818,42 @@ void GameCommand_(float request) void GameCommand_macro_help() { - #define SERVER_COMMAND(name,function,description) \ + #define SERVER_COMMAND(name, function, description) \ { LOG_INFO(" ^2", name, "^7: ", description, "\n"); } SERVER_COMMANDS(0, 0, ""); - #undef SERVER_COMMAND - - return; +#undef SERVER_COMMAND } float GameCommand_macro_command(float argc, string command) { - #define SERVER_COMMAND(name,function,description) \ - { if(name == strtolower(argv(0))) { function; return true; } } + #define SERVER_COMMAND(name, function, description) \ + { if (name == strtolower(argv(0))) { function; return true; } } SERVER_COMMANDS(CMD_REQUEST_COMMAND, argc, command); - #undef SERVER_COMMAND +#undef SERVER_COMMAND return false; } float GameCommand_macro_usage(float argc) { - #define SERVER_COMMAND(name,function,description) \ - { if(name == strtolower(argv(1))) { function; return true; } } + #define SERVER_COMMAND(name, function, description) \ + { if (name == strtolower(argv(1))) { function; return true; } } SERVER_COMMANDS(CMD_REQUEST_USAGE, argc, ""); - #undef SERVER_COMMAND +#undef SERVER_COMMAND return false; } void GameCommand_macro_write_aliases(float fh) { - #define SERVER_COMMAND(name,function,description) \ + #define SERVER_COMMAND(name, function, description) \ { CMD_Write_Alias("qc_cmd_sv", name, description); } SERVER_COMMANDS(0, 0, ""); - #undef SERVER_COMMAND - - return; +#undef SERVER_COMMAND } @@ -1861,9 +1871,9 @@ void GameCommand(string command) // argv: 0 - 1 - 2 - 3 // cmd vote - master - login - password - if(strtolower(argv(0)) == "help") + if (strtolower(argv(0)) == "help") { - if(argc == 1) + if (argc == 1) { LOG_INFO("\nServer console commands:\n"); GameCommand_macro_help(); @@ -1882,46 +1892,44 @@ void GameCommand(string command) return; } - else if(BanCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it + else if (BanCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it { return; } - else if(CommonCommand_macro_usage(argc, world)) // same here, but for common commands instead + else if (CommonCommand_macro_usage(argc, world)) // same here, but for common commands instead { return; } - else if(GenericCommand_macro_usage(argc)) // same here, but for generic commands instead + else if (GenericCommand_macro_usage(argc)) // same here, but for generic commands instead { return; } - else if(GameCommand_macro_usage(argc)) // finally try for normal commands too + else if (GameCommand_macro_usage(argc)) // finally try for normal commands too { return; } } - else if(MUTATOR_CALLHOOK(SV_ParseServerCommand, strtolower(argv(0)), argc, command)) + else if (MUTATOR_CALLHOOK(SV_ParseServerCommand, strtolower(argv(0)), argc, command)) { - return; // handled by a mutator + return; // handled by a mutator } - else if(BanCommand(command)) + else if (BanCommand(command)) { - return; // handled by server/command/ipban.qc + return; // handled by server/command/ipban.qc } - else if(CommonCommand_macro_command(argc, world, command)) + else if (CommonCommand_macro_command(argc, world, command)) { - return; // handled by server/command/common.qc + return; // handled by server/command/common.qc } - else if(GenericCommand(command)) + else if (GenericCommand(command)) { - return; // handled by common/command/generic.qc + return; // handled by common/command/generic.qc } - else if(GameCommand_macro_command(argc, command)) // continue as usual and scan for normal commands + else if (GameCommand_macro_command(argc, command)) // continue as usual and scan for normal commands { - return; // handled by one of the above GameCommand_* functions + return; // handled by one of the above GameCommand_* functions } // nothing above caught the command, must be invalid LOG_INFO(((command != "") ? strcat("Unknown server command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try sv_cmd help.\n"); - - return; } diff --git a/qcsrc/server/command/sv_cmd.qh b/qcsrc/server/command/sv_cmd.qh index 0cc520c2e4..8fa66dac47 100644 --- a/qcsrc/server/command/sv_cmd.qh +++ b/qcsrc/server/command/sv_cmd.qh @@ -13,7 +13,7 @@ void race_deleteTime(string map, float pos); const float SHUFFLETEAMS_MAX_PLAYERS = 255; const float SHUFFLETEAMS_MAX_TEAMS = 4; float shuffleteams_players[SHUFFLETEAMS_MAX_PLAYERS]; // maximum of 255 player slots -float shuffleteams_teams[SHUFFLETEAMS_MAX_TEAMS]; // maximum of 4 teams +float shuffleteams_teams[SHUFFLETEAMS_MAX_TEAMS]; // maximum of 4 teams // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void GameCommand_macro_write_aliases(float fh); diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index da9be11ff6..81d5136f96 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -27,7 +27,7 @@ bool Nagger_SendEntity(entity this, entity to, float sendflags) { int nags, i, f, b; entity e; - WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER); + WriteHeader(MSG_ENTITY, ENT_CLIENT_NAGGER); // bits: // 1 = ready @@ -40,33 +40,28 @@ bool Nagger_SendEntity(entity this, entity to, float sendflags) // 128 = vote string nags = 0; - if(readycount) + if (readycount) { nags |= BIT(0); - if(to.ready == 0) - nags |= BIT(1); + if (to.ready == 0) nags |= BIT(1); } - if(vote_called) + if (vote_called) { nags |= BIT(2); - if(to.vote_selection == 0) - nags |= BIT(3); + if (to.vote_selection == 0) nags |= BIT(3); } - if(warmup_stage) - nags |= BIT(4); + if (warmup_stage) nags |= BIT(4); - if(sendflags & BIT(6)) - nags |= BIT(6); + if (sendflags & BIT(6)) nags |= BIT(6); - if(sendflags & BIT(7)) - nags |= BIT(7); + if (sendflags & BIT(7)) nags |= BIT(7); - if(!(nags & 4)) // no vote called? send no string + if (!(nags & 4)) // no vote called? send no string nags &= ~(BIT(6) | BIT(7)); WriteByte(MSG_ENTITY, nags); - if(nags & BIT(6)) + if (nags & BIT(6)) { WriteByte(MSG_ENTITY, vote_accept_count); WriteByte(MSG_ENTITY, vote_reject_count); @@ -74,16 +69,14 @@ bool Nagger_SendEntity(entity this, entity to, float sendflags) WriteChar(MSG_ENTITY, to.vote_selection); } - if(nags & BIT(7)) - WriteString(MSG_ENTITY, vote_called_display); + if (nags & BIT(7)) WriteString(MSG_ENTITY, vote_called_display); - if(nags & 1) + if (nags & 1) { - for(i = 1; i <= maxclients; i += 8) + for (i = 1; i <= maxclients; i += 8) { - for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e)) - if(!IS_REAL_CLIENT(e) || e.ready) - f |= b; + for (f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e)) + if (!IS_REAL_CLIENT(e) || e.ready) f |= b; WriteByte(MSG_ENTITY, f); } } @@ -93,32 +86,29 @@ bool Nagger_SendEntity(entity this, entity to, float sendflags) void Nagger_Init() { - Net_LinkEntity(nagger = spawn(), false, 0, Nagger_SendEntity); + Net_LinkEntity(nagger = new(nagger), false, 0, Nagger_SendEntity); + make_pure(nagger); } void Nagger_VoteChanged() { - if(nagger) - nagger.SendFlags |= BIT(7); + if (nagger) nagger.SendFlags |= BIT(7); } void Nagger_VoteCountChanged() { - if(nagger) - nagger.SendFlags |= BIT(6); + if (nagger) nagger.SendFlags |= BIT(6); } void Nagger_ReadyCounted() { - if(nagger) - nagger.SendFlags |= BIT(0); + if (nagger) nagger.SendFlags |= BIT(0); } // If the vote_caller is still here, return their name, otherwise vote_caller_name string OriginalCallerName() { - if (IS_REAL_CLIENT(vote_caller)) - return vote_caller.netname; + if (IS_REAL_CLIENT(vote_caller)) return vote_caller.netname; return vote_caller_name; } @@ -130,9 +120,12 @@ void VoteReset() { entity tmp_player; - FOR_EACH_CLIENT(tmp_player) { tmp_player.vote_selection = 0; } + FOR_EACH_CLIENT(tmp_player) + { + tmp_player.vote_selection = 0; + } - if(vote_called) + if (vote_called) { strunzone(vote_called_command); strunzone(vote_called_display); @@ -156,11 +149,9 @@ void VoteReset() void VoteStop(entity stopper) { bprint("\{1}^2* ^3", GetCallerName(stopper), "^2 stopped ^3", OriginalCallerName(), "^2's vote\n"); - if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid))); } - + if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vstop:", ftos(stopper.playerid))); // Don't force them to wait for next vote, this way they can e.g. correct their vote. - if((vote_caller) && (stopper == vote_caller)) { vote_caller.vote_waittime = time + autocvar_sv_vote_stop; } - + if ((vote_caller) && (stopper == vote_caller)) vote_caller.vote_waittime = time + autocvar_sv_vote_stop; VoteReset(); } @@ -168,12 +159,10 @@ void VoteAccept() { bprint("\{1}^2* ^3", OriginalCallerName(), "^2's vote for ^1", vote_called_display, "^2 was accepted\n"); - if((vote_called == VOTE_MASTER) && vote_caller) - vote_caller.vote_master = 1; - else - localcmd(strcat(vote_called_command, "\n")); + if ((vote_called == VOTE_MASTER) && vote_caller) vote_caller.vote_master = 1; + else localcmd(strcat(vote_called_command, "\n")); - if(vote_caller) { vote_caller.vote_waittime = 0; } // people like your votes, you don't need to wait to vote again + if (vote_caller) vote_caller.vote_waittime = 0; // people like your votes, you don't need to wait to vote again VoteReset(); Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_ACCEPT); @@ -202,7 +191,7 @@ void VoteSpam(float notvoters, float mincount, string result) strcat(", ^1", ftos(vote_abstain_count), "^2 didn't care"), strcat(", ^1", ftos(notvoters), strcat("^2 didn't ", ((mincount >= 0) ? "" : "have to "), "vote\n")))); - if(autocvar_sv_eventlog) + if (autocvar_sv_eventlog) { GameLogEcho(strcat( strcat(":vote:v", result, ":", ftos(vote_accept_count)), @@ -219,8 +208,8 @@ void VoteCount(float first_count) vote_accept_count = vote_reject_count = vote_abstain_count = 0; float spectators_allowed = ((autocvar_sv_vote_nospectators != 2) - || ((autocvar_sv_vote_nospectators == 1) && (warmup_stage || gameover)) - || (autocvar_sv_vote_nospectators == 0)); + || ((autocvar_sv_vote_nospectators == 1) && (warmup_stage || gameover)) + || (autocvar_sv_vote_nospectators == 0)); float vote_player_count = 0, notvoters = 0; float vote_real_player_count = 0, vote_real_accept_count = 0; @@ -236,28 +225,36 @@ void VoteCount(float first_count) FOR_EACH_REALCLIENT(tmp_player) { ++vote_player_count; - if(IS_PLAYER(tmp_player)) { ++vote_real_player_count; } - - switch(tmp_player.vote_selection) + if (IS_PLAYER(tmp_player)) ++vote_real_player_count; + switch (tmp_player.vote_selection) { - case VOTE_SELECT_REJECT: { ++vote_reject_count; { if(IS_PLAYER(tmp_player)) ++vote_real_reject_count; } break; } - case VOTE_SELECT_ACCEPT: { ++vote_accept_count; { if(IS_PLAYER(tmp_player)) ++vote_real_accept_count; } break; } - case VOTE_SELECT_ABSTAIN: { ++vote_abstain_count; { if(IS_PLAYER(tmp_player)) ++vote_real_abstain_count; } break; } + case VOTE_SELECT_REJECT: + { ++vote_reject_count; + { if (IS_PLAYER(tmp_player)) ++vote_real_reject_count; } break; + } + case VOTE_SELECT_ACCEPT: + { ++vote_accept_count; + { if (IS_PLAYER(tmp_player)) ++vote_real_accept_count; } break; + } + case VOTE_SELECT_ABSTAIN: + { ++vote_abstain_count; + { if (IS_PLAYER(tmp_player)) ++vote_real_abstain_count; } break; + } default: break; } } // Check to see if there are enough players on the server to allow master voting... otherwise, vote master could be used for evil. - if((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count) + if ((vote_called == VOTE_MASTER) && autocvar_sv_vote_master_playerlimit > vote_player_count) { - if(vote_caller) { vote_caller.vote_waittime = 0; } + if (vote_caller) vote_caller.vote_waittime = 0; print_to(vote_caller, "^1There are not enough players on this server to allow you to become vote master."); VoteReset(); return; } // if spectators aren't allowed to vote and there are players in a match, then only count the players in the vote and ignore spectators. - if(!spectators_allowed && (vote_real_player_count > 0)) + if (!spectators_allowed && (vote_real_player_count > 0)) { vote_accept_count = vote_real_accept_count; vote_reject_count = vote_real_reject_count; @@ -277,43 +274,43 @@ void VoteCount(float first_count) vote_needed_of_voted = floor((vote_accept_count + vote_reject_count) * vote_factor_of_voted) + 1; // are there any players at all on the server? it could be an admin vote - if(vote_player_count == 0 && first_count) + if (vote_player_count == 0 && first_count) { - VoteSpam(0, -1, "yes"); // no players at all, just accept it + VoteSpam(0, -1, "yes"); // no players at all, just accept it VoteAccept(); return; } // since there ARE players, finally calculate the result of the vote - if(vote_accept_count >= vote_needed_overall) + if (vote_accept_count >= vote_needed_overall) { - VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote + VoteSpam(notvoters, -1, "yes"); // there is enough acceptions to pass the vote VoteAccept(); return; } - if(vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall) + if (vote_reject_count > vote_player_count - vote_abstain_count - vote_needed_overall) { - VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote + VoteSpam(notvoters, -1, "no"); // there is enough rejections to deny the vote VoteReject(); return; } // there is not enough votes in either direction, now lets just calculate what the voters have said - if(time > vote_endtime) + if (time > vote_endtime) { final_needed_votes = vote_needed_overall; - if(autocvar_sv_vote_majority_factor_of_voted) + if (autocvar_sv_vote_majority_factor_of_voted) { - if(vote_accept_count >= vote_needed_of_voted) + if (vote_accept_count >= vote_needed_of_voted) { VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "yes"); VoteAccept(); return; } - if(vote_accept_count + vote_reject_count > 0) + if (vote_accept_count + vote_reject_count > 0) { VoteSpam(notvoters, min(vote_needed_overall, vote_needed_of_voted), "no"); VoteReject(); @@ -331,13 +328,11 @@ void VoteCount(float first_count) void VoteThink() { - if(vote_endtime > 0) // a vote was called - if(time > vote_endtime) // time is up + if (vote_endtime > 0) // a vote was called { - VoteCount(false); + if (time > vote_endtime) // time is up + VoteCount(false); } - - return; } @@ -347,39 +342,39 @@ void VoteThink() // Resets the state of all clients, items, weapons, waypoints, ... of the map. void reset_map(float dorespawn) -{SELFPARAM(); +{ + SELFPARAM(); - if(time <= game_starttime && round_handler_IsActive()) + if (time <= game_starttime && round_handler_IsActive()) round_handler_Reset(game_starttime); MUTATOR_CALLHOOK(reset_map_global); - for(entity e = world; (e = nextent(e)); ) + for (entity e = world; (e = nextent(e)); ) { setself(e); - if(IS_NOT_A_CLIENT(self)) + if (IS_NOT_A_CLIENT(self)) { - if(self.reset) + if (self.reset) { self.reset(); continue; } - if(self.team_saved) - self.team = self.team_saved; + if (self.team_saved) self.team = self.team_saved; - if(self.flags & FL_PROJECTILE) // remove any projectiles left + if (self.flags & FL_PROJECTILE) // remove any projectiles left remove(self); } } // Waypoints and assault start come LAST - for(entity e = world; (e = nextent(e)); ) + for (entity e = world; (e = nextent(e)); ) { setself(e); - if(IS_NOT_A_CLIENT(self)) + if (IS_NOT_A_CLIENT(self)) { - if(self.reset2) + if (self.reset2) { self.reset2(); continue; @@ -389,51 +384,52 @@ void reset_map(float dorespawn) entity e; FOR_EACH_PLAYER(e) - if(e.frozen) - { - WITH(entity, self, e, Unfreeze(self)); - } + if (e.frozen) WITH(entity, self, e, Unfreeze(self)); // Moving the player reset code here since the player-reset depends // on spawnpoint entities which have to be reset first --blub - if(dorespawn) - if(!MUTATOR_CALLHOOK(reset_map_players)) - FOR_EACH_CLIENT(e) // reset all players + if (dorespawn) { - setself(e); - /* - only reset players if a restart countdown is active - this can either be due to cvar sv_ready_restart_after_countdown having set - restart_mapalreadyrestarted to 1 after the countdown ended or when - sv_ready_restart_after_countdown is not used and countdown is still running - */ - if (restart_mapalreadyrestarted || (time < game_starttime)) + if (!MUTATOR_CALLHOOK(reset_map_players)) { - //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players - if (IS_PLAYER(self)) { - //PlayerScore_Clear(self); - self.killcount = 0; - //stop the player from moving so that he stands still once he gets respawned - self.velocity = '0 0 0'; - self.avelocity = '0 0 0'; - self.movement = '0 0 0'; - PutClientInServer(); + FOR_EACH_CLIENT(e) // reset all players + { + setself(e); + /* + only reset players if a restart countdown is active + this can either be due to cvar sv_ready_restart_after_countdown having set + restart_mapalreadyrestarted to 1 after the countdown ended or when + sv_ready_restart_after_countdown is not used and countdown is still running + */ + if (restart_mapalreadyrestarted || (time < game_starttime)) + { + // NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players + if (IS_PLAYER(self)) + { + // PlayerScore_Clear(self); + self.killcount = 0; + // stop the player from moving so that he stands still once he gets respawned + self.velocity = '0 0 0'; + self.avelocity = '0 0 0'; + self.movement = '0 0 0'; + PutClientInServer(); + } + } } + + setself(this); } } - - setself(this); } // Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set) void ReadyRestart_think() -{SELFPARAM(); +{ + SELFPARAM(); restart_mapalreadyrestarted = 1; reset_map(true); Score_ClearAll(); remove(self); - - return; } // Forces a restart of the game without actually reloading the map // this is a mess... @@ -446,8 +442,7 @@ void ReadyRestart_force() VoteReset(); // clear overtime, we have to decrease timelimit to its original value again. - if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) { cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime))); } - + if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) cvar_set("timelimit", ftos(autocvar_timelimit - (checkrules_overtimesadded * autocvar_timelimit_overtime))); checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0; readyrestart_happened = 1; @@ -464,22 +459,25 @@ void ReadyRestart_force() restart_mapalreadyrestarted = 0; // reset this var, needed when cvar sv_ready_restart_repeatable is in use // disable the warmup global for the server - warmup_stage = 0; // once the game is restarted the game is in match stage + warmup_stage = 0; // once the game is restarted the game is in match stage // reset the .ready status of all players (also spectators) - FOR_EACH_REALCLIENT(tmp_player) { tmp_player.ready = 0; } + FOR_EACH_REALCLIENT(tmp_player) + { + tmp_player.ready = 0; + } readycount = 0; - Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client + Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client // lock teams with lockonrestart - if(autocvar_teamplay_lockonrestart && teamplay) + if (autocvar_teamplay_lockonrestart && teamplay) { lockteams = 1; bprint("^1The teams are now locked.\n"); } - //initiate the restart-countdown-announcer entity - if(autocvar_sv_ready_restart_after_countdown) + // initiate the restart-countdown-announcer entity + if (autocvar_sv_ready_restart_after_countdown) { restart_timer = spawn(); restart_timer.think = ReadyRestart_think; @@ -487,29 +485,26 @@ void ReadyRestart_force() } // after a restart every players number of allowed timeouts gets reset, too - if(autocvar_sv_timeout) { FOR_EACH_REALPLAYER(tmp_player) { tmp_player.allowed_timeouts = autocvar_sv_timeout_number; } } - - //reset map immediately if this cvar is not set - if (!autocvar_sv_ready_restart_after_countdown) { reset_map(true); } - - if(autocvar_sv_eventlog) { GameLogEcho(":restart"); } -} + if (autocvar_sv_timeout) + { + FOR_EACH_REALPLAYER(tmp_player) + { + tmp_player.allowed_timeouts = autocvar_sv_timeout_number; + } + // reset map immediately if this cvar is not set + if (!autocvar_sv_ready_restart_after_countdown) reset_map(true); } + if (autocvar_sv_eventlog) GameLogEcho(":restart"); } void ReadyRestart() { // no assault support yet... - if(g_assault | gameover | intermission_running | race_completing) - localcmd("restart\n"); - else - localcmd("\nsv_hook_gamerestart\n"); + if (g_assault | gameover | intermission_running | race_completing) localcmd("restart\n"); + else localcmd("\nsv_hook_gamerestart\n"); // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off! // Otherwise scores could be manipulated during the countdown. - if (!autocvar_sv_ready_restart_after_countdown) { Score_ClearAll(); } - + if (!autocvar_sv_ready_restart_after_countdown) Score_ClearAll(); ReadyRestart_force(); - - return; } // Count the players who are ready and determine whether or not to restart the match @@ -521,11 +516,10 @@ void ReadyCount() FOR_EACH_REALCLIENT(tmp_player) { - if(IS_PLAYER(tmp_player) || tmp_player.caplayer == 1) + if (IS_PLAYER(tmp_player) || tmp_player.caplayer == 1) { ++t_players; - if(tmp_player.ready) { ++t_ready; } - } + if (tmp_player.ready) ++t_ready; } } readycount = t_ready; @@ -535,12 +529,7 @@ void ReadyCount() ready_needed_factor = bound(0.5, cvar("g_warmup_majority_factor"), 0.999); ready_needed_count = floor(t_players * ready_needed_factor) + 1; - if(readycount >= ready_needed_count) - { - ReadyRestart(); - } - - return; + if (readycount >= ready_needed_count) ReadyRestart(); } @@ -552,12 +541,9 @@ float Votecommand_check_assignment(entity caller, float assignment) { float from_server = (!caller); - if((assignment == VC_ASGNMNT_BOTH) - || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY) - || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) - { - return true; - } + if ((assignment == VC_ASGNMNT_BOTH) + || ((!from_server && assignment == VC_ASGNMNT_CLIENTONLY) + || (from_server && assignment == VC_ASGNMNT_SERVERONLY))) return true; return false; } @@ -566,21 +552,18 @@ string VoteCommand_extractcommand(string input, float startpos, float argc) { string output; - if((argc - 1) < startpos) - output = ""; - else - output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos)); + if ((argc - 1) < startpos) output = ""; + else output = substring(input, argv_start_index(startpos), argv_end_index(-1) - argv_start_index(startpos)); return output; } float VoteCommand_checknasty(string vote_command) { - if((strstrofs(vote_command, ";", 0) >= 0) - || (strstrofs(vote_command, "\n", 0) >= 0) - || (strstrofs(vote_command, "\r", 0) >= 0) - || (strstrofs(vote_command, "$", 0) >= 0)) - return false; + if ((strstrofs(vote_command, ";", 0) >= 0) + || (strstrofs(vote_command, "\n", 0) >= 0) + || (strstrofs(vote_command, "\r", 0) >= 0) + || (strstrofs(vote_command, "$", 0) >= 0)) return false; return true; } @@ -589,8 +572,7 @@ float VoteCommand_checkinlist(string vote_command, string list) { string l = strcat(" ", list, " "); - if(strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) - return true; + if (strstrofs(l, strcat(" ", vote_command, " "), 0) >= 0) return true; return false; } @@ -605,16 +587,16 @@ string ValidateMap(string validated_map, entity caller) return string_null; } - if(!autocvar_sv_vote_override_mostrecent && caller) + if (!autocvar_sv_vote_override_mostrecent && caller) { - if(Map_IsRecent(validated_map)) + if (Map_IsRecent(validated_map)) { print_to(caller, "This server does not allow for recent maps to be played again. Please be patient for some rounds."); return string_null; } } - if(!MapInfo_CheckMap(validated_map)) + if (!MapInfo_CheckMap(validated_map)) { print_to(caller, strcat("^1Invalid mapname, \"^3", validated_map, "^1\" does not support the current game mode.")); return string_null; @@ -627,14 +609,13 @@ float VoteCommand_checkargs(float startpos, float argc) { float p, q, check, minargs; string cvarname = strcat("sv_vote_command_restriction_", argv(startpos)); - string cmdrestriction = cvar_string(cvarname); // note: this warns on undefined cvar. We want that. + string cmdrestriction = cvar_string(cvarname); // note: this warns on undefined cvar. We want that. string charlist, arg; float checkmate; - if(cmdrestriction == "") - return true; + if (cmdrestriction == "") return true; - ++startpos; // skip command name + ++startpos; // skip command name // check minimum arg count @@ -643,49 +624,45 @@ float VoteCommand_checkargs(float startpos, float argc) // ... minargs = stof(cmdrestriction); - if(argc - startpos < minargs) - return false; + if (argc - startpos < minargs) return false; - p = strstrofs(cmdrestriction, ";", 0); // find first semicolon + p = strstrofs(cmdrestriction, ";", 0); // find first semicolon - for (;;) + for ( ; ; ) { // we know that at any time, startpos <= argc - minargs // so this means: argc-minargs >= startpos >= argc, thus // argc-minargs >= argc, thus minargs <= 0, thus all minargs // have been seen already - if(startpos >= argc) // all args checked? GOOD + if (startpos >= argc) // all args checked? GOOD break; - if(p < 0) // no more args? FAIL + if (p < 0) // no more args? FAIL { // exception: exactly minargs left, this one included - if(argc - startpos == minargs) - break; + if (argc - startpos == minargs) break; // otherwise fail return false; } // cut to next semicolon - q = strstrofs(cmdrestriction, ";", p+1); // find next semicolon - if(q < 0) - charlist = substring(cmdrestriction, p+1, -1); - else - charlist = substring(cmdrestriction, p+1, q - (p+1)); + q = strstrofs(cmdrestriction, ";", p + 1); // find next semicolon + if (q < 0) charlist = substring(cmdrestriction, p + 1, -1); + else charlist = substring(cmdrestriction, p + 1, q - (p + 1)); // in case we ever want to allow semicolons in VoteCommand_checknasty // charlist = strreplace("^^", ";", charlist); - if(charlist != "") + if (charlist != "") { // verify the arg only contains allowed chars arg = argv(startpos); checkmate = strlen(arg); - for(check = 0; check < checkmate; ++check) - if(strstrofs(charlist, substring(arg, check, 1), 0) < 0) - return false; // not allowed character + for (check = 0; check < checkmate; ++check) + if (strstrofs(charlist, substring(arg, check, 1), 0) < 0) return false; + // not allowed character // all characters are allowed. FINE. } @@ -704,40 +681,35 @@ float VoteCommand_parse(entity caller, string vote_command, string vote_list, fl first_command = argv(startpos); /*printf("VoteCommand_parse(): Command: '%s', Length: %f.\n", - substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos)), - strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) + substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos)), + strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) );*/ - if( - (autocvar_sv_vote_limit > 0) - && - (strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) > autocvar_sv_vote_limit) - ) - return false; + if ( + (autocvar_sv_vote_limit > 0) + && + (strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) > autocvar_sv_vote_limit) + ) return false; - if (!VoteCommand_checkinlist(first_command, vote_list)) - return false; + if (!VoteCommand_checkinlist(first_command, vote_list)) return false; - if (!VoteCommand_checkargs(startpos, argc)) - return false; + if (!VoteCommand_checkargs(startpos, argc)) return false; - switch(first_command) // now go through and parse the proper commands to adjust as needed. + switch (first_command) // now go through and parse the proper commands to adjust as needed. { case "kick": - case "kickban": // catch all kick/kickban commands + case "kickban": // catch all kick/kickban commands { entity victim = GetIndexedEntity(argc, (startpos + 1)); float accepted = VerifyClientEntity(victim, true, false); - if(accepted > 0) + if (accepted > 0) { string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), strlen(vote_command) - argv_start_index(next_token)) : "No reason provided"); string command_arguments; - if(first_command == "kickban") - command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~"); - else - command_arguments = reason; + if (first_command == "kickban") command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~"); + else command_arguments = reason; vote_parsed_command = strcat(first_command, " # ", ftos(num_for_edict(victim)), " ", command_arguments); vote_parsed_display = strcat("^1", vote_command, " (^7", victim.netname, "^1): ", reason); @@ -749,10 +721,10 @@ float VoteCommand_parse(entity caller, string vote_command, string vote_list, fl case "map": case "chmap": - case "gotomap": // re-direct all map selection commands to gotomap + case "gotomap": // re-direct all map selection commands to gotomap { vote_command = ValidateMap(argv(startpos + 1), caller); - if (!vote_command) { return false; } + if (!vote_command) return false; vote_parsed_command = strcat("gotomap ", vote_command); vote_parsed_display = strzone(strcat("^1", vote_parsed_command)); @@ -776,22 +748,24 @@ float VoteCommand_parse(entity caller, string vote_command, string vote_list, fl // Command Sub-Functions // ======================= -void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY +void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { if (!vote_called) { print_to(caller, "^1No vote called."); } - else if(caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } + else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) + { + print_to(caller, "^1You have already voted."); + } - else // everything went okay, continue changing vote + else // everything went okay, continue changing vote { print_to(caller, "^1You abstained from your vote."); caller.vote_selection = VOTE_SELECT_ABSTAIN; msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(false); } - } + if (!autocvar_sv_vote_singlecount) VoteCount(false); } return; } @@ -806,54 +780,81 @@ void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY } } -void VoteCommand_call(float request, entity caller, float argc, string vote_command) // BOTH +void VoteCommand_call(float request, entity caller, float argc, string vote_command) // BOTH { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { float spectators_allowed = ((autocvar_sv_vote_nospectators != 2) - || ((autocvar_sv_vote_nospectators == 1) && warmup_stage) - || (autocvar_sv_vote_nospectators == 0)); + || ((autocvar_sv_vote_nospectators == 1) && warmup_stage) + || (autocvar_sv_vote_nospectators == 0)); float tmp_playercount = 0; entity tmp_player; vote_command = VoteCommand_extractcommand(vote_command, 2, argc); - if(!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); } - else if(!autocvar_sv_vote_gamestart && time < game_starttime) { print_to(caller, "^1Vote calling is not allowed before the match has started."); } - else if(vote_called) { print_to(caller, "^1There is already a vote called."); } - else if(!spectators_allowed && (caller && !IS_PLAYER(caller))) { print_to(caller, "^1Only players can call a vote."); } - else if(caller && !IS_CLIENT(caller)) { print_to(caller, "^1Only connected clients can vote."); } - else if(timeout_status) { print_to(caller, "^1You can not call a vote while a timeout is active."); } - else if(caller && (time < caller.vote_waittime)) { print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote.")); } - else if (!VoteCommand_checknasty(vote_command)) { print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); } - else if (!VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) { print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info."); } - - else // everything went okay, continue with calling the vote + if (!autocvar_sv_vote_call && caller) { print_to(caller, "^1Vote calling is not allowed."); } + else if (!autocvar_sv_vote_gamestart && time < game_starttime) { - vote_caller = caller; // remember who called the vote + print_to(caller, "^1Vote calling is not allowed before the match has started."); + } + else if (vote_called) + { + print_to(caller, "^1There is already a vote called."); + } + else if (!spectators_allowed && (caller && !IS_PLAYER(caller))) + { + print_to(caller, "^1Only players can call a vote."); + } + else if (caller && !IS_CLIENT(caller)) + { + print_to(caller, "^1Only connected clients can vote."); + } + else if (timeout_status) + { + print_to(caller, "^1You can not call a vote while a timeout is active."); + } + else if (caller && (time < caller.vote_waittime)) + { + print_to(caller, strcat("^1You have to wait ^2", ftos(ceil(caller.vote_waittime - time)), "^1 seconds before you can again call a vote.")); + } + else if (!VoteCommand_checknasty(vote_command)) + { + print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); + } + else if (!VoteCommand_parse(caller, vote_command, autocvar_sv_vote_commands, 2, argc)) + { + print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info."); + } + + else // everything went okay, continue with calling the vote + { + vote_caller = caller; // remember who called the vote vote_caller_name = strzone(GetCallerName(vote_caller)); vote_called = VOTE_NORMAL; vote_called_command = strzone(vote_parsed_command); vote_called_display = strzone(vote_parsed_display); vote_endtime = time + autocvar_sv_vote_timeout; - if(caller) + if (caller) { caller.vote_selection = VOTE_SELECT_ACCEPT; caller.vote_waittime = time + autocvar_sv_vote_wait; msg_entity = caller; } - FOR_EACH_REALCLIENT(tmp_player) { ++tmp_playercount; } - if(tmp_playercount > 1) { Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_CALL); } // don't announce a "vote now" sound if player is alone + FOR_EACH_REALCLIENT(tmp_player) + { + ++tmp_playercount; + } + if (tmp_playercount > 1) Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_VOTE_CALL); // don't announce a "vote now" sound if player is alone bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n"); - if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); } + if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); Nagger_VoteChanged(); - VoteCount(true); // needed if you are the only one + VoteCount(true); // needed if you are the only one } return; @@ -871,64 +872,83 @@ void VoteCommand_call(float request, entity caller, float argc, string vote_comm } } -void VoteCommand_master(float request, entity caller, float argc, string vote_command) // CLIENT ONLY +void VoteCommand_master(float request, entity caller, float argc, string vote_command) // CLIENT ONLY { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(autocvar_sv_vote_master) + if (autocvar_sv_vote_master) { - switch(strtolower(argv(2))) + switch (strtolower(argv(2))) { case "do": { vote_command = VoteCommand_extractcommand(vote_command, 3, argc); if (!caller.vote_master) { print_to(caller, "^1You do not have vote master privelages."); } - else if (!VoteCommand_checknasty(vote_command)) { print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); } - else if (!VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) { print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info."); } + else if (!VoteCommand_checknasty(vote_command)) + { + print_to(caller, "^1Syntax error in command, see 'vhelp' for more info."); + } + else if (!VoteCommand_parse(caller, vote_command, strcat(autocvar_sv_vote_commands, " ", autocvar_sv_vote_master_commands), 3, argc)) + { + print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info."); + } - else // everything went okay, proceed with command + else // everything went okay, proceed with command { localcmd(strcat(vote_parsed_command, "\n")); print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server.")); bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n"); - if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display)); } - } + if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display)); } return; } case "login": { - if(autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); } - else if(caller.vote_master) { print_to(caller, "^1You are already logged in as vote master."); } - else if(autocvar_sv_vote_master_password != argv(3)) { print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller))); } + if (autocvar_sv_vote_master_password == "") { print_to(caller, "^1Login to vote master is not allowed."); } + else if (caller.vote_master) + { + print_to(caller, "^1You are already logged in as vote master."); + } + else if (autocvar_sv_vote_master_password != argv(3)) + { + print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller))); + } - else // everything went okay, proceed with giving this player master privilages + else // everything went okay, proceed with giving this player master privilages { caller.vote_master = true; print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller))); bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n"); - if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid))); } - } + if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid))); } return; } - default: // calling a vote for master + default: // calling a vote for master { float spectators_allowed = ((autocvar_sv_vote_nospectators != 2) - || ((autocvar_sv_vote_nospectators == 1) && warmup_stage) - || (autocvar_sv_vote_nospectators == 0)); + || ((autocvar_sv_vote_nospectators == 1) && warmup_stage) + || (autocvar_sv_vote_nospectators == 0)); if (!autocvar_sv_vote_master_callable) { print_to(caller, "^1Vote to become vote master is not allowed."); } - else if(vote_called) { print_to(caller, "^1There is already a vote called."); } - else if(!spectators_allowed && (caller && !IS_PLAYER(caller))) { print_to(caller, "^1Only players can call a vote."); } - else if(timeout_status) { print_to(caller, "^1You can not call a vote while a timeout is active."); } + else if (vote_called) + { + print_to(caller, "^1There is already a vote called."); + } + else if (!spectators_allowed && (caller && !IS_PLAYER(caller))) + { + print_to(caller, "^1Only players can call a vote."); + } + else if (timeout_status) + { + print_to(caller, "^1You can not call a vote while a timeout is active."); + } - else // everything went okay, continue with creating vote + else // everything went okay, continue with creating vote { vote_caller = caller; vote_caller_name = strzone(GetCallerName(vote_caller)); @@ -941,9 +961,9 @@ void VoteCommand_master(float request, entity caller, float argc, string vote_co caller.vote_waittime = time + autocvar_sv_vote_wait; bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n"); - if(autocvar_sv_eventlog) { GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); } + if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display)); Nagger_VoteChanged(); - VoteCount(true); // needed if you are the only one + VoteCount(true); // needed if you are the only one } return; @@ -966,23 +986,28 @@ void VoteCommand_master(float request, entity caller, float argc, string vote_co } } -void VoteCommand_no(float request, entity caller) // CLIENT ONLY +void VoteCommand_no(float request, entity caller) // CLIENT ONLY { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { if (!vote_called) { print_to(caller, "^1No vote called."); } - else if(caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } - else if(((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote) { VoteStop(caller); } + else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) + { + print_to(caller, "^1You have already voted."); + } + else if (((caller == vote_caller) || caller.vote_master) && autocvar_sv_vote_no_stops_vote) + { + VoteStop(caller); + } - else // everything went okay, continue changing vote + else // everything went okay, continue changing vote { print_to(caller, "^1You rejected the vote."); caller.vote_selection = VOTE_SELECT_REJECT; msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(false); } - } + if (!autocvar_sv_vote_singlecount) VoteCount(false); } return; } @@ -997,16 +1022,14 @@ void VoteCommand_no(float request, entity caller) // CLIENT ONLY } } -void VoteCommand_status(float request, entity caller) // BOTH +void VoteCommand_status(float request, entity caller) // BOTH { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(vote_called) - print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7.")); - else - print_to(caller, "^1No vote called."); + if (vote_called) print_to(caller, strcat("^7Vote for ", vote_called_display, "^7 called by ^7", OriginalCallerName(), "^7.")); + else print_to(caller, "^1No vote called."); return; } @@ -1021,16 +1044,15 @@ void VoteCommand_status(float request, entity caller) // BOTH } } -void VoteCommand_stop(float request, entity caller) // BOTH +void VoteCommand_stop(float request, entity caller) // BOTH { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if (!vote_called) { print_to(caller, "^1No vote called."); } - else if((caller == vote_caller) || !caller || caller.vote_master) { VoteStop(caller); } - else { print_to(caller, "^1You are not allowed to stop that vote."); } - + if (!vote_called) print_to(caller, "^1No vote called."); + else if ((caller == vote_caller) || !caller || caller.vote_master) VoteStop(caller); + else print_to(caller, "^1You are not allowed to stop that vote."); return; } @@ -1044,22 +1066,24 @@ void VoteCommand_stop(float request, entity caller) // BOTH } } -void VoteCommand_yes(float request, entity caller) // CLIENT ONLY +void VoteCommand_yes(float request, entity caller) // CLIENT ONLY { - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { if (!vote_called) { print_to(caller, "^1No vote called."); } - else if(caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) { print_to(caller, "^1You have already voted."); } + else if (caller.vote_selection != VOTE_SELECT_NULL && !autocvar_sv_vote_change) + { + print_to(caller, "^1You have already voted."); + } - else // everything went okay, continue changing vote + else // everything went okay, continue changing vote { print_to(caller, "^1You accepted the vote."); caller.vote_selection = VOTE_SELECT_ACCEPT; msg_entity = caller; - if(!autocvar_sv_vote_singlecount) { VoteCount(false); } - } + if (!autocvar_sv_vote_singlecount) VoteCount(false); } return; } @@ -1078,22 +1102,22 @@ void VoteCommand_yes(float request, entity caller) // CLIENT ONLY ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! void VoteCommand_(float request) { - switch(request) - { - case CMD_REQUEST_COMMAND: - { - - return; - } - - default: - case CMD_REQUEST_USAGE: - { - print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote "); - print_to(caller, " No arguments required."); - return; - } - } + switch(request) + { + case CMD_REQUEST_COMMAND: + { + + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " vote "); + print_to(caller, " No arguments required."); + return; + } + } } */ @@ -1103,7 +1127,7 @@ void VoteCommand_(float request) // ================================ // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) -#define VOTE_COMMANDS(request,caller,arguments,command) \ +#define VOTE_COMMANDS(request, caller, arguments, command) \ VOTE_COMMAND("abstain", VoteCommand_abstain(request, caller), "Abstain your vote in current vote", VC_ASGNMNT_CLIENTONLY) \ VOTE_COMMAND("call", VoteCommand_call(request, caller, arguments, command), "Create a new vote for players to decide on", VC_ASGNMNT_BOTH) \ VOTE_COMMAND("help", VoteCommand_macro_help(caller, arguments), "Shows this information", VC_ASGNMNT_BOTH) \ @@ -1118,38 +1142,36 @@ void VoteCommand_macro_help(entity caller, float argc) { string command_origin = GetCommandPrefix(caller); - if(argc == 2 || argv(2) == "help") // help display listing all commands + if (argc == 2 || argv(2) == "help") // help display listing all commands { print_to(caller, "\nVoting commands:\n"); - #define VOTE_COMMAND(name,function,description,assignment) \ - { if(Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } } + #define VOTE_COMMAND(name, function, description, assignment) \ + { if (Votecommand_check_assignment(caller, assignment)) { print_to(caller, strcat(" ^2", name, "^7: ", description)); } } VOTE_COMMANDS(0, caller, 0, ""); - #undef VOTE_COMMAND +#undef VOTE_COMMAND print_to(caller, strcat("\nUsage:^3 ", command_origin, " vote COMMAND...^7, where possible commands are listed above.\n")); print_to(caller, strcat("For help about a specific command, type ", command_origin, " vote help COMMAND")); print_to(caller, strcat("\n^7You can call a vote for or execute these commands: ^3", autocvar_sv_vote_commands, "^7 and maybe further ^3arguments^7")); } - else // usage for individual command + else // usage for individual command { - #define VOTE_COMMAND(name,function,description,assignment) \ - { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(2))) { function; return; } } } + #define VOTE_COMMAND(name, function, description, assignment) \ + { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(2))) { function; return; } } } VOTE_COMMANDS(CMD_REQUEST_USAGE, caller, argc, ""); - #undef VOTE_COMMAND +#undef VOTE_COMMAND } - - return; } float VoteCommand_macro_command(entity caller, float argc, string vote_command) { - #define VOTE_COMMAND(name,function,description,assignment) \ - { if(Votecommand_check_assignment(caller, assignment)) { if(name == strtolower(argv(1))) { function; return true; } } } + #define VOTE_COMMAND(name, function, description, assignment) \ + { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } } VOTE_COMMANDS(CMD_REQUEST_COMMAND, caller, argc, vote_command); - #undef VOTE_COMMAND +#undef VOTE_COMMAND return false; } @@ -1166,12 +1188,11 @@ void VoteCommand(float request, entity caller, float argc, string vote_command) // argv: 0 - 1 - 2 - 3 // cmd vote - master - login - password - switch(request) + switch (request) { case CMD_REQUEST_COMMAND: { - if(VoteCommand_macro_command(caller, argc, vote_command)) - return; + if (VoteCommand_macro_command(caller, argc, vote_command)) return; } default: diff --git a/qcsrc/server/command/vote.qh b/qcsrc/server/command/vote.qh index f80785e2a1..98b000e75d 100644 --- a/qcsrc/server/command/vote.qh +++ b/qcsrc/server/command/vote.qh @@ -23,17 +23,17 @@ const float VOTE_NORMAL = 1; const float VOTE_MASTER = 2; // global vote information declarations -entity vote_caller; // original caller of the current vote -string vote_caller_name; // name of the vote caller -float vote_called; // stores status of current vote (See VOTE_*) -float vote_endtime; // time when the vote is finished -float vote_accept_count; // total amount of players who accept the vote (counted by VoteCount() function) -float vote_reject_count; // same as above, but rejected -float vote_abstain_count; // same as above, but abstained -float vote_needed_overall; // total amount of players NEEDED for a vote to pass (based on sv_vote_majority_factor) -.float vote_master; // flag for if the player has vote master privelages -.float vote_waittime; // flag for how long the player must wait before they can vote again -.float vote_selection; // flag for which vote selection the player has made (See VOTE_SELECT_*) +entity vote_caller; // original caller of the current vote +string vote_caller_name; // name of the vote caller +float vote_called; // stores status of current vote (See VOTE_*) +float vote_endtime; // time when the vote is finished +float vote_accept_count; // total amount of players who accept the vote (counted by VoteCount() function) +float vote_reject_count; // same as above, but rejected +float vote_abstain_count; // same as above, but abstained +float vote_needed_overall; // total amount of players NEEDED for a vote to pass (based on sv_vote_majority_factor) +.float vote_master; // flag for if the player has vote master privelages +.float vote_waittime; // flag for how long the player must wait before they can vote again +.float vote_selection; // flag for which vote selection the player has made (See VOTE_SELECT_*) string vote_called_command; // command sent by client string vote_called_display; // visual string of command sent by client string vote_parsed_command; // command which is fixed after being parsed @@ -47,10 +47,10 @@ void VoteCommand(float request, entity caller, float argc, string vote_command); // warmup and nagger stuff const float RESTART_COUNTDOWN = 10; entity nagger; -float readycount; // amount of players who are ready -float readyrestart_happened; // keeps track of whether a restart has already happened +float readycount; // amount of players who are ready +float readyrestart_happened; // keeps track of whether a restart has already happened float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed -.float ready; // flag for if a player is ready +.float ready; // flag for if a player is ready void reset_map(float dorespawn); void ReadyCount(); void ReadyRestart_force(); diff --git a/qcsrc/server/constants.qh b/qcsrc/server/constants.qh index 6de55189f1..c1ea1c6c61 100644 --- a/qcsrc/server/constants.qh +++ b/qcsrc/server/constants.qh @@ -4,13 +4,11 @@ const int FL_WEAPON = BIT(13); const int FL_POWERUP = BIT(14); const int FL_PROJECTILE = BIT(15); -const int FL_TOSSED = BIT(BIT(4)); +const int FL_TOSSED = BIT(16); const int FL_NO_WEAPON_STAY = BIT(17); const int FL_SPAWNING = BIT(18); const int FL_PICKUPITEMS = BIT(19); -const int SVC_SOUND = 6; -const int SVC_STOPSOUND = 16; const int SVC_SETVIEW = 5; const int RESPAWN_FORCE = 1; @@ -18,8 +16,6 @@ const int RESPAWN_SILENT = 2; #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT) -const int MSG_ENTITY = 5; // csqc - const int NUM_PLAYERSKINS_TEAMPLAY = 3; const int ASSAULT_VALUE_INACTIVE = 1000; diff --git a/qcsrc/server/controlpoint.qc b/qcsrc/server/controlpoint.qc deleted file mode 100644 index ceeb4f185b..0000000000 --- a/qcsrc/server/controlpoint.qc +++ /dev/null @@ -1,42 +0,0 @@ -#include "controlpoint.qh" - -#include "command/common.qh" - -.bool iscaptured; - -bool cpicon_send(entity this, entity to, int sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON); - WriteByte(MSG_ENTITY, sf); - if(sf & CPSF_SETUP) - { - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - - WriteByte(MSG_ENTITY, self.health); - WriteByte(MSG_ENTITY, self.max_health); - WriteByte(MSG_ENTITY, self.count); - WriteByte(MSG_ENTITY, self.team); - WriteByte(MSG_ENTITY, self.owner.iscaptured); - } - - if(sf & CPSF_STATUS) - { - WriteByte(MSG_ENTITY, self.team); - - if(self.health <= 0) - WriteByte(MSG_ENTITY, 0); - else - WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); - } - - return true; -} - -void onslaught_controlpoint_icon_link(entity e, void() spawnproc) -{ - Net_LinkEntity(e, true, 0, cpicon_send); - e.think = spawnproc; - e.nextthink = time * sys_frametime; -} diff --git a/qcsrc/server/controlpoint.qh b/qcsrc/server/controlpoint.qh deleted file mode 100644 index d76f0ea069..0000000000 --- a/qcsrc/server/controlpoint.qh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CONTROLPOINT_H -#define CONTROLPOINT_H - -const vector CPICON_MIN = '-32 -32 -9'; -const vector CPICON_MAX = '32 32 25'; - -const int CPSF_STATUS = 4; -const int CPSF_SETUP = 8; - -#endif diff --git a/qcsrc/server/csqceffects.qc b/qcsrc/server/csqceffects.qc deleted file mode 100644 index a26fa97aa8..0000000000 --- a/qcsrc/server/csqceffects.qc +++ /dev/null @@ -1,19 +0,0 @@ -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include "../common/constants.qh" -#endif - -void te_csqc_lightningarc(vector from,vector to) -{ - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_ARC); - - WriteCoord(MSG_BROADCAST, from.x); - WriteCoord(MSG_BROADCAST, from.y); - WriteCoord(MSG_BROADCAST, from.z); - WriteCoord(MSG_BROADCAST, to.x); - WriteCoord(MSG_BROADCAST, to.y); - WriteCoord(MSG_BROADCAST, to.z); -} - diff --git a/qcsrc/server/csqceffects.qh b/qcsrc/server/csqceffects.qh deleted file mode 100644 index 9fd0c386ef..0000000000 --- a/qcsrc/server/csqceffects.qh +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef CSQCEFFECTS_H -#define CSQCEFFECTS_H -void te_csqc_lightningarc(vector from,vector to); -#endif diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 4f79001493..094eb45319 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -2,8 +2,9 @@ #define SERVER_DEFS_H #include "../common/weapons/all.qh" +#include "../common/stats.qh" -#define INDEPENDENT_ATTACK_FINISHED +#define INDEPENDENT_ATTACK_FINISHED 1 #define BUTTON_ATCK button0 #define BUTTON_JUMP button2 @@ -20,25 +21,25 @@ // Globals -float g_cloaked, g_footsteps, g_grappling_hook, g_instagib; +float g_footsteps, g_grappling_hook, g_instagib; float g_warmup_limit; float g_warmup_allguns; float g_warmup_allow_timeout; float warmup_stage; -float g_pickup_respawntime_weapon; -float g_pickup_respawntime_superweapon; -float g_pickup_respawntime_ammo; -float g_pickup_respawntime_short; -float g_pickup_respawntime_medium; -float g_pickup_respawntime_long; -float g_pickup_respawntime_powerup; -float g_pickup_respawntimejitter_weapon; -float g_pickup_respawntimejitter_superweapon; -float g_pickup_respawntimejitter_ammo; -float g_pickup_respawntimejitter_short; -float g_pickup_respawntimejitter_medium; -float g_pickup_respawntimejitter_long; -float g_pickup_respawntimejitter_powerup; +PROPERTY(float, g_pickup_respawntime_weapon) +PROPERTY(float, g_pickup_respawntime_superweapon) +PROPERTY(float, g_pickup_respawntime_ammo) +PROPERTY(float, g_pickup_respawntime_short) +PROPERTY(float, g_pickup_respawntime_medium) +PROPERTY(float, g_pickup_respawntime_long) +PROPERTY(float, g_pickup_respawntime_powerup) +PROPERTY(float, g_pickup_respawntimejitter_weapon) +PROPERTY(float, g_pickup_respawntimejitter_superweapon) +PROPERTY(float, g_pickup_respawntimejitter_ammo) +PROPERTY(float, g_pickup_respawntimejitter_short) +PROPERTY(float, g_pickup_respawntimejitter_medium) +PROPERTY(float, g_pickup_respawntimejitter_long) +PROPERTY(float, g_pickup_respawntimejitter_powerup) float g_jetpack; float sv_clones; @@ -53,8 +54,6 @@ void UpdateFrags(entity player, float f); float team1_score, team2_score, team3_score, team4_score; -float maxclients; - // flag set on worldspawn so that the code knows if it is dedicated or not float server_is_dedicated; @@ -76,8 +75,8 @@ float server_is_dedicated; .float pain_frame; //" .float crouch; // Crouching or not? -.float strength_finished; -.float invincible_finished; +.float strength_finished = _STAT(STRENGTH_FINISHED); +.float invincible_finished = _STAT(INVINCIBLE_FINISHED); .float superweapons_finished; .float cnt; // used in too many places @@ -148,26 +147,26 @@ const float MAX_DAMAGEEXTRARADIUS = 16; // string overrides entity .string item_pickupsound; .entity item_pickupsound_ent; +.entity item_model_ent; // definitions for weaponsystem // more WEAPONTODO: move these to their proper files -.entity weaponentity; .entity exteriorweaponentity; .vector weaponentity_glowmod; //.int weapon; // current weapon -.int switchweapon; // weapon requested to switch to +.int switchweapon = _STAT(SWITCHWEAPON); .int switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible) .string weaponname; // name of .weapon // WEAPONTODO .float autoswitch; float client_hasweapon(entity cl, float wpn, float andammo, float complain); -void w_clear(Weapon thiswep, entity actor, bool fire1, bool fire2); -void w_ready(Weapon thiswep, entity actor, bool fire1, bool fire2); +void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire); +void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire); // VorteX: standalone think for weapons, so normal think on weaponentity can be reserved by weaponflashes (which needs update even player dies) .float weapon_nextthink; -.void(Weapon thiswep, entity actor, bool fire1, bool fire2) weapon_think; +.void(Weapon thiswep, entity actor, .entity weaponentity, int fire) weapon_think; // weapon states (self.weaponentity.state) @@ -180,7 +179,7 @@ const int WS_READY = 4; // idle frame // there is 2 weapon tics that can run in one server frame const int W_TICSPERFRAME = 2; -void weapon_defaultspawnfunc(float wpn); +void weapon_defaultspawnfunc(entity this, Weapon e); float gameover; float intermission_running; @@ -261,14 +260,14 @@ WepSet weaponsInMap; float bot_waypoints_for_items; -.float attack_finished_for[Weapons_MAX]; -.float attack_finished_single; -#ifdef INDEPENDENT_ATTACK_FINISHED -#define ATTACK_FINISHED_FOR(ent,w) ((ent).(attack_finished_for[(w) - WEP_FIRST])) +.float attack_finished_for[Weapons_MAX * MAX_WEAPONSLOTS]; +.float attack_finished_single[MAX_WEAPONSLOTS]; +#if INDEPENDENT_ATTACK_FINISHED +#define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).(attack_finished_for[((w) - WEP_FIRST) * MAX_WEAPONSLOTS + (slot)])) #else -#define ATTACK_FINISHED_FOR(ent,w) ((ent).attack_finished_single) +#define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot]) #endif -#define ATTACK_FINISHED(ent) ATTACK_FINISHED_FOR(ent,(ent).weapon) +#define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, (ent).weapon, slot) // assault game mode: Which team is attacking in this round? float assault_attacker_team; @@ -302,57 +301,6 @@ float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) float next_pingtime; -// player sounds, voice messages -// TODO implemented fall and falling -#define ALLPLAYERSOUNDS \ - _VOICEMSG(death) \ - _VOICEMSG(drown) \ - _VOICEMSG(fall) \ - _VOICEMSG(falling) \ - _VOICEMSG(gasp) \ - _VOICEMSG(jump) \ - _VOICEMSG(pain100) \ - _VOICEMSG(pain25) \ - _VOICEMSG(pain50) \ - _VOICEMSG(pain75) - -#define ALLVOICEMSGS \ - _VOICEMSG(attack) \ - _VOICEMSG(attackinfive) \ - _VOICEMSG(coverme) \ - _VOICEMSG(defend) \ - _VOICEMSG(freelance) \ - _VOICEMSG(incoming) \ - _VOICEMSG(meet) \ - _VOICEMSG(needhelp) \ - _VOICEMSG(seenflag) \ - _VOICEMSG(taunt) \ - _VOICEMSG(teamshoot) - -#define _VOICEMSG(m) .string playersound_##m; -ALLPLAYERSOUNDS -ALLVOICEMSGS -#undef _VOICEMSG - -// reserved sound names for the future (some models lack sounds for them): -// _VOICEMSG(flagcarriertakingdamage) \ -// _VOICEMSG(getflag) \ -// reserved sound names for the future (ALL models lack sounds for them): -// _VOICEMSG(affirmative) \ -// _VOICEMSG(attacking) \ -// _VOICEMSG(defending) \ -// _VOICEMSG(roaming) \ -// _VOICEMSG(onmyway) \ -// _VOICEMSG(droppedflag) \ -// _VOICEMSG(negative) \ -// _VOICEMSG(seenenemy) \ -// /**/ - -string globalsound_fall; -string globalsound_metalfall; -string globalsound_step; -string globalsound_metalstep; - const float VOICETYPE_PLAYERSOUND = 10; const float VOICETYPE_TEAMRADIO = 11; const float VOICETYPE_LASTATTACKER = 12; @@ -360,17 +308,6 @@ const float VOICETYPE_LASTATTACKER_ONLY = 13; const float VOICETYPE_AUTOTAUNT = 14; const float VOICETYPE_TAUNT = 15; -void PrecachePlayerSounds(string f); -void PrecacheGlobalSound(string samplestring); -void UpdatePlayerSounds(); -void ClearPlayerSounds(); -void PlayerSound(.string samplefield, float channel, float voicetype); -void GlobalSound(string samplestring, float channel, float voicetype); -void FakeGlobalSound(string samplestring, float channel, float voicetype); -void VoiceMessage(string type, string message); -float GetPlayerSoundSampleField_notFound; -.string GetVoiceMessageSampleField(string type); - // autotaunt system .float cvar_cl_autotaunt; .float cvar_cl_voice_directional; @@ -396,7 +333,7 @@ float cvar_purechanges_count; float game_starttime; //point in time when the countdown to game start is over float round_starttime; //point in time when the countdown to round start is over -.float stat_game_starttime; +.float stat_game_starttime = _STAT(GAMESTARTTIME); .float stat_round_starttime; void W_Porto_Remove (entity p); diff --git a/qcsrc/server/ent_cs.qc b/qcsrc/server/ent_cs.qc index cf275c0f89..9c98e7d99f 100644 --- a/qcsrc/server/ent_cs.qc +++ b/qcsrc/server/ent_cs.qc @@ -19,7 +19,7 @@ float entcs_customize() bool entcs_send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_ENTCS); + WriteHeader(MSG_ENTITY, ENT_CLIENT_ENTCS); WriteByte(MSG_ENTITY, sf); if(sf & BIT(0)) WriteByte(MSG_ENTITY, num_for_edict(self.owner) - 1); @@ -68,6 +68,7 @@ void entcs_think() entity attach_entcs(entity e) { entity ent = e.entcs = new(entcs_sender); + make_pure(ent); ent.owner = e; ent.think = entcs_think; ent.nextthink = time; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 802eabd7fd..147c03ca26 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -12,7 +12,6 @@ #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" -#include "../common/buffs/all.qh" #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/notifications.qh" @@ -24,45 +23,6 @@ #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/common.qh" -bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO); - WriteShort(MSG_ENTITY, self.projectiledeathtype); - WriteCoord(MSG_ENTITY, floor(self.origin.x)); - WriteCoord(MSG_ENTITY, floor(self.origin.y)); - WriteCoord(MSG_ENTITY, floor(self.origin.z)); - WriteByte(MSG_ENTITY, bound(1, self.dmg, 255)); - WriteByte(MSG_ENTITY, bound(0, self.dmg_radius, 255)); - WriteByte(MSG_ENTITY, bound(1, self.dmg_edge, 255)); - WriteShort(MSG_ENTITY, self.oldorigin.x); - WriteByte(MSG_ENTITY, self.species); - return true; -} - -void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, int deathtype, float bloodtype, entity dmgowner) -{ - // TODO maybe call this from non-edgedamage too? - // TODO maybe make the client do the particle effects for the weapons and the impact sounds using this info? - - entity e; - - if(!sound_allowed(MSG_BROADCAST, dmgowner)) - deathtype |= 0x8000; - - e = spawn(); - setorigin(e, org); - e.projectiledeathtype = deathtype; - e.dmg = coredamage; - e.dmg_edge = edgedamage; - e.dmg_radius = rad; - e.dmg_force = vlen(force); - e.velocity = force; - e.oldorigin_x = compressShortVector(e.velocity); - e.species = bloodtype; - - Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity); -} - void UpdateFrags(entity player, float f) { PlayerTeamScore_AddScore(player, f); @@ -114,8 +74,7 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype) { if(!GiveFrags_randomweapons) { - GiveFrags_randomweapons = spawn(); - GiveFrags_randomweapons.classname = "GiveFrags_randomweapons"; + GiveFrags_randomweapons = new(GiveFrags_randomweapons); } if(warmup_stage) @@ -204,7 +163,7 @@ void Obituary_SpecialDeath( { if(DEATH_ISSPECIAL(deathtype)) { - entity deathent = Deathtypes[deathtype - DT_FIRST]; + entity deathent = Deathtypes_from(deathtype - DT_FIRST); if (!deathent) { backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n"); return; } if(murder) @@ -302,6 +261,8 @@ float Obituary_WeaponDeath( return false; } +.int buffs; // TODO: remove + void Obituary(entity attacker, entity inflictor, entity targ, int deathtype) { // Sanity check @@ -563,10 +524,8 @@ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypo targ.revive_speed = freeze_time; self.bot_attack = false; - entity ice, head; - ice = spawn(); + entity ice = new(ice); ice.owner = targ; - ice.classname = "ice"; ice.scale = targ.scale; ice.think = Ice_Think; ice.nextthink = time; @@ -582,6 +541,7 @@ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypo RemoveGrapplingHook(targ); + entity head; FOR_EACH_PLAYER(head) if(head.hook.aiment == targ) RemoveGrapplingHook(head); @@ -889,9 +849,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d vector farce = damage_explosion_calcpush(self.damageforcescale * force, self.velocity, autocvar_g_balance_damagepush_speedfactor); if(self.movetype == MOVETYPE_PHYSICS) { - entity farcent; - farcent = spawn(); - farcent.classname = "farce"; + entity farcent = new(farce); farcent.enemy = self; farcent.movedir = farce * 10; if(self.mass) @@ -1140,8 +1098,7 @@ float Fire_AddDamage(entity e, entity o, float d, float t, float dt) if(!e.fire_burner) { // print("adding a fire burner to ", e.classname, "\n"); - e.fire_burner = spawn(); - e.fire_burner.classname = "fireburner"; + e.fire_burner = new(fireburner); e.fire_burner.think = fireburner_think; e.fire_burner.nextthink = time; e.fire_burner.owner = e; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index f95115a2ca..99bd819495 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -80,7 +80,7 @@ void RemoveGrapplingHook(entity pl) //pl.disableclientprediction = false; } -void GrapplingHookReset(void) +void GrapplingHookReset() {SELFPARAM(); if(self.realowner.hook == self) RemoveGrapplingHook(self.owner); @@ -106,7 +106,7 @@ void GrapplingHook_Stop() .vector hook_start, hook_end; bool GrapplingHookSend(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_HOOK); + WriteHeader(MSG_ENTITY, ENT_CLIENT_HOOK); sf = sf & 0x7F; if(sound_allowed(MSG_BROADCAST, self.realowner)) sf |= 0x80; @@ -299,7 +299,7 @@ void GrapplingHookThink() } } -void GrapplingHookTouch (void) +void GrapplingHookTouch () {SELFPARAM(); if(other.movetype == MOVETYPE_FOLLOW) return; @@ -339,7 +339,7 @@ void GrapplingHook_Damage (entity inflictor, entity attacker, float damage, int } } -void FireGrapplingHook (void) +void FireGrapplingHook () {SELFPARAM(); entity missile; vector org; diff --git a/qcsrc/server/g_models.qc b/qcsrc/server/g_models.qc index f9f4083e34..46ebe5ed89 100644 --- a/qcsrc/server/g_models.qc +++ b/qcsrc/server/g_models.qc @@ -15,7 +15,7 @@ class(BGMScript) .float bgmscriptrelease; .float modelscale; -void g_model_setcolormaptoactivator (void) +void g_model_setcolormaptoactivator () {SELFPARAM(); if(teamplay) { @@ -29,13 +29,13 @@ void g_model_setcolormaptoactivator (void) self.colormap |= BIT(10); // RENDER_COLORMAPPED } -void g_clientmodel_setcolormaptoactivator (void) +void g_clientmodel_setcolormaptoactivator () {SELFPARAM(); g_model_setcolormaptoactivator(); self.SendFlags |= (BIT(3) | BIT(0)); } -void g_clientmodel_use(void) +void g_clientmodel_use() {SELFPARAM(); if (self.antiwall_flag == 1) { @@ -90,23 +90,23 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf) if(self.lodmodelindex1) sf |= 0x80; - WriteByte(MSG_ENTITY, ENT_CLIENT_WALL); + WriteHeader(MSG_ENTITY, ENT_CLIENT_WALL); WriteByte(MSG_ENTITY, sf); - if(sf & 1) + if(sf & BIT(0)) { if(sf & 0x40) WriteShort(MSG_ENTITY, self.colormap); } - if(sf & 2) + if(sf & BIT(1)) { WriteCoord(MSG_ENTITY, self.origin.x); WriteCoord(MSG_ENTITY, self.origin.y); WriteCoord(MSG_ENTITY, self.origin.z); } - if(sf & 4) + if(sf & BIT(2)) { if(sf & 0x10) { @@ -116,7 +116,7 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf) } } - if(sf & 8) + if(sf & BIT(3)) { if(sf & 0x80) { diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index 41bac7a43f..f2e3da8986 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -62,7 +62,7 @@ main unused but required by the engine ================== */ -void main (void) +void main () { } diff --git a/qcsrc/server/g_subs.qh b/qcsrc/server/g_subs.qh index c9d1264c07..a50885a7fe 100644 --- a/qcsrc/server/g_subs.qh +++ b/qcsrc/server/g_subs.qh @@ -1,7 +1,7 @@ #ifndef G_SUBS_H #define G_SUBS_H -void SUB_NullThink(void); +void SUB_NullThink(); void() SUB_CalcMoveDone; void() SUB_CalcAngleMoveDone; @@ -21,7 +21,7 @@ SUB_Remove Remove self ================== */ -void SUB_Remove (void); +void SUB_Remove (); /* ================== @@ -31,7 +31,7 @@ Applies some friction to self ================== */ .float friction; -void SUB_Friction (void); +void SUB_Friction (); /* ================== @@ -42,7 +42,7 @@ Makes client invisible or removes non-client */ void SUB_VanishOrRemove (entity ent); -void SUB_SetFade_Think (void); +void SUB_SetFade_Think (); /* ================== @@ -61,10 +61,10 @@ calculate self.velocity and self.nextthink to reach dest from self.origin traveling at speed =============== */ -void SUB_CalcMoveDone (void); +void SUB_CalcMoveDone (); .float platmovetype_turn; -void SUB_CalcMove_controller_think (void); +void SUB_CalcMove_controller_think (); void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest); @@ -86,7 +86,7 @@ self.angles rotating The calling function should make sure self.think is valid =============== */ -void SUB_CalcAngleMoveDone (void); +void SUB_CalcAngleMoveDone (); // FIXME: I fixed this function only for rotation around the main axes void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func); @@ -100,7 +100,7 @@ main unused but required by the engine ================== */ -void main (void); +void main (); // Misc @@ -135,15 +135,6 @@ Ripped from DPMod */ vector findbetterlocation (vector org, float mindist); -/* -================== -crandom - -Returns a random number between -1.0 and 1.0 -================== -*/ -float crandom (void); - /* ================== Angc used for animations diff --git a/qcsrc/server/g_violence.qc b/qcsrc/server/g_violence.qc deleted file mode 100644 index b016acdd62..0000000000 --- a/qcsrc/server/g_violence.qc +++ /dev/null @@ -1,51 +0,0 @@ -#include "g_violence.qh" - -.int state; - -bool Violence_GibSplash_SendEntity(entity this, entity to, int sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_GIBSPLASH); - WriteByte(MSG_ENTITY, self.state); // actually type - WriteByte(MSG_ENTITY, bound(1, self.cnt * 16, 255)); // gibbage amount multiplier - WriteShort(MSG_ENTITY, floor(self.origin.x / 4)); // not using a coord here, as gibs don't need this accuracy - WriteShort(MSG_ENTITY, floor(self.origin.y / 4)); // not using a coord here, as gibs don't need this accuracy - WriteShort(MSG_ENTITY, floor(self.origin.z / 4)); // not using a coord here, as gibs don't need this accuracy - WriteShort(MSG_ENTITY, self.oldorigin.x); // acrually compressed velocity - return true; -} - -// TODO maybe convert this to a TE? -void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker) -{SELFPARAM(); - if(g_cts) // no gibs in CTS - return; - - entity e; - - e = spawn(); - e.classname = "gibsplash"; - e.cnt = amount; - e.state = type; // should stay smaller than 15 - if(!sound_allowed(MSG_BROADCAST, gibowner) || !sound_allowed(MSG_BROADCAST, attacker)) - e.state |= 0x40; // "silence" bit - e.state |= 8 * self.species; // gib type, ranges from 0 to 15 - - // if this is a copied dead body, send the num of its player instead - // TODO: remove this field, read from model txt files - if(self.classname == "body") - e.team = num_for_edict(self.enemy); - else - e.team = num_for_edict(self); - - setorigin(e, org); - e.velocity = dir; - - e.oldorigin_x = compressShortVector(e.velocity); - - Net_LinkEntity(e, false, 0.2, Violence_GibSplash_SendEntity); -} - -void Violence_GibSplash(entity source, float type, float amount, entity attacker) -{ - Violence_GibSplash_At(source.origin + source.view_ofs, source.velocity, type, amount, source, attacker); -} diff --git a/qcsrc/server/g_violence.qh b/qcsrc/server/g_violence.qh deleted file mode 100644 index 6a33ac4418..0000000000 --- a/qcsrc/server/g_violence.qh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef G_VIOLENCE_H -#define G_VIOLENCE_H - -bool Violence_GibSplash_SendEntity(entity this, entity to, int sf); - -// TODO maybe convert this to a TE? -void Violence_GibSplash_At(vector org, vector dir, float type, float amount, entity gibowner, entity attacker); - -void Violence_GibSplash(entity source, float type, float amount, entity attacker); -#endif diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index c706a1bdd5..85f1ce163b 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -18,7 +18,6 @@ #include "scores.qh" #include "teamplay.qh" #include "weapons/weaponstats.qh" -#include "../common/buffs/all.qh" #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/mapinfo.qh" @@ -54,8 +53,7 @@ void PingPLReport_Think() e = edict_num(self.cnt + 1); if(IS_REAL_CLIENT(e)) { - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_PINGPLREPORT); + WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT); WriteByte(MSG_BROADCAST, self.cnt); WriteShort(MSG_BROADCAST, max(1, e.ping)); WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255)); @@ -72,8 +70,7 @@ void PingPLReport_Think() } else { - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_PINGPLREPORT); + WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT); WriteByte(MSG_BROADCAST, self.cnt); WriteShort(MSG_BROADCAST, 0); WriteByte(MSG_BROADCAST, 0); @@ -83,8 +80,8 @@ void PingPLReport_Think() } void PingPLReport_Spawn() { - pingplreport = spawn(); - pingplreport.classname = "pingplreport"; + pingplreport = new(pingplreport); + make_pure(pingplreport); pingplreport.think = PingPLReport_Think; pingplreport.nextthink = time; } @@ -98,17 +95,7 @@ void ShuffleMaplist(); void SetDefaultAlpha() { - if(autocvar_g_running_guns) - { - default_player_alpha = -1; - default_weapon_alpha = +1; - } - else if(g_cloaked) - { - default_player_alpha = autocvar_g_balance_cloaked_alpha; - default_weapon_alpha = default_player_alpha; - } - else + if (!MUTATOR_CALLHOOK(SetDefaultAlpha)) { default_player_alpha = autocvar_g_player_alpha; if(default_player_alpha == 0) @@ -515,7 +502,7 @@ void detect_maptype() entity randomseed; bool RandomSeed_Send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_RANDOMSEED); + WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED); WriteShort(MSG_ENTITY, self.cnt); return true; } @@ -528,7 +515,8 @@ void RandomSeed_Think() } void RandomSeed_Spawn() {SELFPARAM(); - randomseed = spawn(); + randomseed = new(randomseed); + make_pure(randomseed); randomseed.think = RandomSeed_Think; Net_LinkEntity(randomseed, false, 0, RandomSeed_Send); @@ -546,19 +534,18 @@ spawnfunc(__init_dedicated_server) remove = remove_unsafely; - entity e; - e = spawn(); + entity e = spawn(); e.think = GotoFirstMap; e.nextthink = time; // this is usually 1 at this point - e = spawn(); - e.classname = "info_player_deathmatch"; // safeguard against player joining + e = new(info_player_deathmatch); // safeguard against player joining self.classname = "worldspawn"; // safeguard against various stuff ;) // needs to be done so early because of the constants they create static_init(); static_init_late(); + static_init_precache(); MapInfo_Enumerate(); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); @@ -658,6 +645,7 @@ spawnfunc(worldspawn) InitGameplayMode(); static_init_late(); + static_init_precache(); readlevelcvars(); GrappleHookInit(); @@ -768,15 +756,11 @@ spawnfunc(worldspawn) WepSet_AddStat(); WepSet_AddStat_InMap(); - addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon); addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon); - addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime); addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime); addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam); Nagger_Init(); - addstat(STAT_STRENGTH_FINISHED, AS_FLOAT, strength_finished); - addstat(STAT_INVINCIBLE_FINISHED, AS_FLOAT, invincible_finished); addstat(STAT_SUPERWEAPONS_FINISHED, AS_FLOAT, superweapons_finished); addstat(STAT_PRESSED_KEYS, AS_FLOAT, pressedkeys); addstat(STAT_FUEL, AS_INT, ammo_fuel); @@ -796,8 +780,6 @@ spawnfunc(worldspawn) addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load); - addstat(STAT_ARC_HEAT, AS_FLOAT, arc_heat_percent); - // freeze attacks addstat(STAT_FROZEN, AS_INT, frozen); addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress); @@ -1476,7 +1458,6 @@ void DumpStats(float final) void FixIntermissionClient(entity e) { - string s; if(!e.autoscreenshot) // initial call { e.autoscreenshot = time + 0.8; // used for autoscreenshot @@ -1485,18 +1466,24 @@ void FixIntermissionClient(entity e) e.solid = SOLID_NOT; e.movetype = MOVETYPE_NONE; e.takedamage = DAMAGE_NO; - if(e.weaponentity) + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { - e.weaponentity.effects = EF_NODRAW; - if (e.weaponentity.weaponentity) - e.weaponentity.weaponentity.effects = EF_NODRAW; + .entity weaponentity = weaponentities[slot]; + if(e.(weaponentity)) + { + e.(weaponentity).effects = EF_NODRAW; + if (e.(weaponentity).(weaponentity)) + e.(weaponentity).(weaponentity).effects = EF_NODRAW; + } } if(IS_REAL_CLIENT(e)) { stuffcmd(e, "\nscr_printspeed 1000000\n"); - s = autocvar_sv_intermission_cdtrack; - if(s != "") - stuffcmd(e, strcat("\ncd loop ", s, "\n")); + string list = autocvar_sv_intermission_cdtrack; + for(string it; (it = car(list)); list = cdr(list)) + RandomSelection_Add(world, 0, it, 1, 1); + if(RandomSelection_chosen_string && RandomSelection_chosen_string != "") + stuffcmd(e, strcat("\ncd loop ", RandomSelection_chosen_string, "\n")); msg_entity = e; WriteByte(MSG_ONE, SVC_INTERMISSION); } @@ -1657,7 +1644,7 @@ void AddWinners(.float field, float value) } // clear the .winning flags -void ClearWinners(void) +void ClearWinners() { entity head; FOR_EACH_PLAYER(head) diff --git a/qcsrc/server/generator.qc b/qcsrc/server/generator.qc deleted file mode 100644 index 2a1c0b2f86..0000000000 --- a/qcsrc/server/generator.qc +++ /dev/null @@ -1,37 +0,0 @@ -#include "generator.qh" - -bool generator_send(entity this, entity to, int sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_GENERATOR); - WriteByte(MSG_ENTITY, sf); - if(sf & GSF_SETUP) - { - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - - WriteByte(MSG_ENTITY, self.health); - WriteByte(MSG_ENTITY, self.max_health); - WriteByte(MSG_ENTITY, self.count); - WriteByte(MSG_ENTITY, self.team); - } - - if(sf & GSF_STATUS) - { - WriteByte(MSG_ENTITY, self.team); - - if(self.health <= 0) - WriteByte(MSG_ENTITY, 0); - else - WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); - } - - return true; -} - -void generator_link(void() spawnproc) -{SELFPARAM(); - Net_LinkEntity(self, true, 0, generator_send); - self.think = spawnproc; - self.nextthink = time; -} diff --git a/qcsrc/server/generator.qh b/qcsrc/server/generator.qh deleted file mode 100644 index 003c2b1d68..0000000000 --- a/qcsrc/server/generator.qh +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef GENERATOR_H -#define GENERATOR_H -const vector GENERATOR_MIN = '-52 -52 -14'; -const vector GENERATOR_MAX = '52 52 75'; - -const int GSF_STATUS = 4; -const int GSF_SETUP = 8; - -bool generator_send(entity this, entity to, int sf); -#endif diff --git a/qcsrc/server/ipban.qc b/qcsrc/server/ipban.qc index a60970d21a..7463fefbea 100644 --- a/qcsrc/server/ipban.qc +++ b/qcsrc/server/ipban.qc @@ -318,9 +318,7 @@ void Ban_LoadBans() } } - entity e; - e = spawn(); - e.classname = "bansyncer"; + entity e = new(bansyncer); e.think = OnlineBanList_Think; e.nextthink = time + 1; } diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index 5ab3c8df22..d1e1f32e42 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -239,7 +239,7 @@ spawnfunc(item_key) self.message = strzone(strcat("You've picked up the ", self.netname, "!")); if (self.noise == "") - self.noise = SND(ITEMPICKUP); + self.noise = strzone(SND(ITEMPICKUP)); // save the name for later item_keys_names[lowestbit(self.itemkeys)] = self.netname; diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc index 5a9f7e0765..ed17d86e8e 100644 --- a/qcsrc/server/mapvoting.qc +++ b/qcsrc/server/mapvoting.qc @@ -264,8 +264,7 @@ void MapVote_Init() void MapVote_SendPicture(float id) {SELFPARAM(); msg_entity = self; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_PICTURE); + WriteHeader(MSG_ONE, TE_CSQC_PICTURE); WriteByte(MSG_ONE, id); WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072); } @@ -349,7 +348,7 @@ bool MapVote_SendEntity(entity this, entity to, int sf) if(sf & 1) sf &= ~2; // if we send 1, we don't need to also send 2 - WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_MAPVOTE); WriteByte(MSG_ENTITY, sf); if(sf & 1) diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 3a4680ebb9..d12a903778 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -52,7 +52,7 @@ void WarpZone_crosshair_trace(entity pl) } -string admin_name(void) +string admin_name() { if(autocvar_sv_adminnick != "") return autocvar_sv_adminnick; @@ -60,40 +60,6 @@ string admin_name(void) return "SERVER ADMIN"; } -void DistributeEvenly_Init(float amount, float totalweight) -{ - if (DistributeEvenly_amount) - { - LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for "); - LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n"); - } - if (totalweight == 0) - DistributeEvenly_amount = 0; - else - DistributeEvenly_amount = amount; - DistributeEvenly_totalweight = totalweight; -} -float DistributeEvenly_Get(float weight) -{ - float f; - if (weight <= 0) - return 0; - f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight); - DistributeEvenly_totalweight -= weight; - DistributeEvenly_amount -= f; - return f; -} -float DistributeEvenly_GetRandomized(float weight) -{ - float f; - if (weight <= 0) - return 0; - f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight); - DistributeEvenly_totalweight -= weight; - DistributeEvenly_amount -= f; - return f; -} - void GameLogEcho(string s) { @@ -296,7 +262,7 @@ string formatmessage(string msg) case "l": replacement = NearestLocation(self.origin); break; case "y": replacement = NearestLocation(cursor); break; case "d": replacement = NearestLocation(self.death_origin); break; - case "w": replacement = WEP_NAME((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon); break; + case "w": replacement = WEP_NAME(((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon)); break; case "W": replacement = ammoitems; break; case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break; case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break; @@ -446,11 +412,6 @@ void GetCvars(float f) GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag"); GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional"); GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation"); - GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share"); - GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive"); - - self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share); - self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive); GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign"); GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name"); @@ -601,7 +562,7 @@ void readplayerstartcvars() if (e.netname == s) { g_weaponarena_weapons |= WepSet_FromWeapon(j); - g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & "); + g_weaponarena_list = strcat(g_weaponarena_list, e.m_name, " & "); break; } } @@ -742,183 +703,6 @@ void readplayerstartcvars() warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel); } -float sound_allowed(float destin, entity e) -{ - // sounds from world may always pass - for (;;) - { - if (e.classname == "body") - e = e.enemy; - else if (e.realowner && e.realowner != e) - e = e.realowner; - else if (e.owner && e.owner != e) - e = e.owner; - else - break; - } - // sounds to self may always pass - if (destin == MSG_ONE) - if (e == msg_entity) - return true; - // sounds by players can be removed - if (autocvar_bot_sound_monopoly) - if (IS_REAL_CLIENT(e)) - return false; - // anything else may pass - return true; -} - -void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu) -{ - float entno, idx; - - if (!sound_allowed(_dest, e)) - return; - - entno = num_for_edict(e); - idx = precache_sound_index(samp); - - int sflags; - sflags = 0; - - attenu = floor(attenu * 64); - vol = floor(vol * 255); - - if (vol != 255) - sflags |= SND_VOLUME; - if (attenu != 64) - sflags |= SND_ATTENUATION; - if (entno >= 8192 || chan < 0 || chan > 7) - sflags |= SND_LARGEENTITY; - if (idx >= 256) - sflags |= SND_LARGESOUND; - - WriteByte(_dest, SVC_SOUND); - WriteByte(_dest, sflags); - if (sflags & SND_VOLUME) - WriteByte(_dest, vol); - if (sflags & SND_ATTENUATION) - WriteByte(_dest, attenu); - if (sflags & SND_LARGEENTITY) - { - WriteShort(_dest, entno); - WriteByte(_dest, chan); - } - else - { - WriteShort(_dest, entno * 8 + chan); - } - if (sflags & SND_LARGESOUND) - WriteShort(_dest, idx); - else - WriteByte(_dest, idx); - - WriteCoord(_dest, o.x); - WriteCoord(_dest, o.y); - WriteCoord(_dest, o.z); -} -void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten) -{ - vector o; - - if (!sound_allowed(_dest, e)) - return; - - o = e.origin + 0.5 * (e.mins + e.maxs); - soundtoat(_dest, e, o, chan, samp, vol, _atten); -} -void soundat(entity e, vector o, float chan, string samp, float vol, float _atten) -{ - soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten); -} -void stopsoundto(float _dest, entity e, float chan) -{ - float entno; - - if (!sound_allowed(_dest, e)) - return; - - entno = num_for_edict(e); - - if (entno >= 8192 || chan < 0 || chan > 7) - { - float idx, sflags; - idx = precache_sound_index(SND(Null)); - sflags = SND_LARGEENTITY; - if (idx >= 256) - sflags |= SND_LARGESOUND; - WriteByte(_dest, SVC_SOUND); - WriteByte(_dest, sflags); - WriteShort(_dest, entno); - WriteByte(_dest, chan); - if (sflags & SND_LARGESOUND) - WriteShort(_dest, idx); - else - WriteByte(_dest, idx); - WriteCoord(_dest, e.origin.x); - WriteCoord(_dest, e.origin.y); - WriteCoord(_dest, e.origin.z); - } - else - { - WriteByte(_dest, SVC_STOPSOUND); - WriteShort(_dest, entno * 8 + chan); - } -} -void stopsound(entity e, float chan) -{ - if (!sound_allowed(MSG_BROADCAST, e)) - return; - - stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast - stopsoundto(MSG_ALL, e, chan); // in case of packet loss -} - -void play2(entity e, string filename) -{ - //stuffcmd(e, strcat("play2 ", filename, "\n")); - msg_entity = e; - soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE); -} - -// use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame) -.float spamtime; -float spamsound(entity e, float chan, string samp, float vol, float _atten) -{ - if (!sound_allowed(MSG_BROADCAST, e)) - return false; - - if (time > e.spamtime) - { - e.spamtime = time; - _sound(e, chan, samp, vol, _atten); - return true; - } - return false; -} - -void play2team(float t, string filename) -{ - entity head; - - if (autocvar_bot_sound_monopoly) - return; - - FOR_EACH_REALPLAYER(head) - { - if (head.team == t) - play2(head, filename); - } -} - -void play2all(string samp) -{ - if (autocvar_bot_sound_monopoly) - return; - - _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE); -} - void PrecachePlayerSounds(string f); void precache_playermodel(string m) { @@ -1003,16 +787,6 @@ void precache() precache_playermodels(autocvar_sv_defaultplayermodel); } - if (g_footsteps) - { - PrecacheGlobalSound((globalsound_step = "misc/footstep0 6")); - PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6")); - } - - // gore and miscellaneous sounds - PrecacheGlobalSound((globalsound_fall = "misc/hitground 4")); - PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4")); - #if 0 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks). @@ -1087,7 +861,7 @@ void remove_safely(entity e) builtin_remove(e); } -void InitializeEntity(entity e, void(void) func, float order) +void InitializeEntity(entity e, void() func, float order) { entity prev, cur; @@ -1163,7 +937,7 @@ bool EliminatedPlayers_SendEntity(entity this, entity to, float sendflags) { float i, f, b; entity e; - WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS); + WriteHeader(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS); WriteByte(MSG_ENTITY, sendflags); if(sendflags & 1) @@ -1346,7 +1120,7 @@ float WarpZone_Projectile_Touch_ImpactFilter_Callback() return false; } - +/** engine callback */ void URI_Get_Callback(float id, float status, string data) { if(url_URI_Get_Callback(id, status, data)) @@ -1603,151 +1377,6 @@ vector gettaginfo_relative(entity e, float tag) return gettaginfo(gettaginfo_relative_ent, tag); } -.float scale2; - -bool modeleffect_SendEntity(entity this, entity to, int sf) -{ - float f; - WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT); - - f = 0; - if(self.velocity != '0 0 0') - f |= 1; - if(self.angles != '0 0 0') - f |= 2; - if(self.avelocity != '0 0 0') - f |= 4; - - WriteByte(MSG_ENTITY, f); - WriteShort(MSG_ENTITY, self.modelindex); - WriteByte(MSG_ENTITY, self.skin); - WriteByte(MSG_ENTITY, self.frame); - WriteCoord(MSG_ENTITY, self.origin.x); - WriteCoord(MSG_ENTITY, self.origin.y); - WriteCoord(MSG_ENTITY, self.origin.z); - if(f & 1) - { - WriteCoord(MSG_ENTITY, self.velocity.x); - WriteCoord(MSG_ENTITY, self.velocity.y); - WriteCoord(MSG_ENTITY, self.velocity.z); - } - if(f & 2) - { - WriteCoord(MSG_ENTITY, self.angles.x); - WriteCoord(MSG_ENTITY, self.angles.y); - WriteCoord(MSG_ENTITY, self.angles.z); - } - if(f & 4) - { - WriteCoord(MSG_ENTITY, self.avelocity.x); - WriteCoord(MSG_ENTITY, self.avelocity.y); - WriteCoord(MSG_ENTITY, self.avelocity.z); - } - WriteShort(MSG_ENTITY, self.scale * 256.0); - WriteShort(MSG_ENTITY, self.scale2 * 256.0); - WriteByte(MSG_ENTITY, self.teleport_time * 100.0); - WriteByte(MSG_ENTITY, self.fade_time * 100.0); - WriteByte(MSG_ENTITY, self.alpha * 255.0); - - return true; -} - -void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2) -{ - entity e; - float sz; - e = spawn(); - e.classname = "modeleffect"; - _setmodel(e, m); - e.frame = f; - setorigin(e, o); - e.velocity = v; - e.angles = ang; - e.avelocity = angv; - e.alpha = a; - e.teleport_time = t1; - e.fade_time = t2; - e.skin = s; - if(s0 >= 0) - e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z); - else - e.scale = -s0; - if(s2 >= 0) - e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z); - else - e.scale2 = -s2; - sz = max(e.scale, e.scale2); - setsize(e, e.mins * sz, e.maxs * sz); - Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity); -} - -void shockwave_spawn(string m, vector org, float sz, float t1, float t2) -{ - return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2); -} - -float randombit(float bits) -{ - if(!(bits & (bits-1))) // this ONLY holds for powers of two! - return bits; - - float n, f, b, r; - - r = random(); - b = 0; - n = 0; - - for(f = 1; f <= bits; f *= 2) - { - if(bits & f) - { - ++n; - r *= n; - if(r <= 1) - b = f; - else - r = (r - 1) / (n - 1); - } - } - - return b; -} - -float randombits(float bits, float k, float error_return) -{ - float r; - r = 0; - while(k > 0 && bits != r) - { - r += randombit(bits - r); - --k; - } - if(error_return) - if(k > 0) - return -1; // all - return r; -} - -void randombit_test(float bits, float iter) -{ - while(iter > 0) - { - LOG_INFO(ftos(randombit(bits)), "\n"); - --iter; - } -} - -float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d) -{ - if(halflifedist > 0) - return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist); - else if(halflifedist < 0) - return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist); - else - return 1; -} - - .string aiment_classname; .float aiment_deadflag; void SetMovetypeFollow(entity ent, entity e) diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index 3ca3ea6730..4193ff29ad 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -33,19 +33,12 @@ float cvar_normal(string n) entity eliminatedPlayers; void EliminatedPlayers_Init(float(entity) isEliminated_func); -string admin_name(void); +string admin_name(); void write_recordmarker(entity pl, float tstart, float dt); void play2all(string samp); -void DistributeEvenly_Init(float amount, float totalweight); -float DistributeEvenly_Get(float weight); - -void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2); - -void shockwave_spawn(string m, vector org, float sz, float t1, float t2); - void play2team(float t, string filename); void GetCvars_handleFloat(string thisname, float f, .float field, string name); @@ -62,10 +55,7 @@ void InitializeEntitiesRun(); void stopsoundto(float _dest, entity e, float chan); void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten); -float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d); -float DistributeEvenly_amount; -float DistributeEvenly_totalweight; void objerror(string s); void droptofloor(); void() SUB_Remove; @@ -134,32 +124,6 @@ void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomo #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return -const string STR_PLAYER = "player"; -const string STR_SPECTATOR = "spectator"; -const string STR_OBSERVER = "observer"; - -#define IS_PLAYER(v) ((v).classname == STR_PLAYER) -#define IS_SPEC(v) ((v).classname == STR_SPECTATOR) -#define IS_OBSERVER(v) ((v).classname == STR_OBSERVER) -#define IS_CLIENT(v) (v.flags & FL_CLIENT) -#define IS_BOT_CLIENT(v) (clienttype(v) == CLIENTTYPE_BOT) -#define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) -#define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) - -#define IS_MONSTER(v) (v.flags & FL_MONSTER) -#define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE) -#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET) - -#define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) -#define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v)) -#define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v)) - -#define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(IS_PLAYER(v)) -#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if (!IS_PLAYER(v)) // Samual: shouldn't this be IS_SPEC(v)? and rather create a separate macro to include observers too -#define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(IS_PLAYER(v)) - -#define FOR_EACH_MONSTER(v) for(v = world; (v = findflags(v, flags, FL_MONSTER)) != world; ) - #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) // copies a string to a tempstring (so one can strunzone it) @@ -297,7 +261,7 @@ float sv_autotaunt; float sv_taunt; string GetGametype(); // g_world.qc -void readlevelcvars(void) +void readlevelcvars() { if(cvar("sv_allow_fullbright")) serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; @@ -322,7 +286,6 @@ void readlevelcvars(void) sv_clones = cvar("sv_clones"); sv_foginterval = cvar("sv_foginterval"); - g_cloaked = cvar("g_cloaked"); g_footsteps = cvar("g_footsteps"); g_jetpack = cvar("g_jetpack"); sv_maxidle = cvar("sv_maxidle"); @@ -419,17 +382,6 @@ void readlevelcvars(void) //#NO AUTOCVARS END - -// Sound functions -//string precache_sound (string s) = #19; -// hack -float precache_sound_index (string s) = #19; - -const float SND_VOLUME = BIT(0); -const float SND_ATTENUATION = BIT(1); -const float SND_LARGEENTITY = BIT(3); -const float SND_LARGESOUND = BIT(4); - const float INITPRIO_FIRST = 0; const float INITPRIO_GAMETYPE = 0; const float INITPRIO_GAMETYPE_FALLBACK = 1; @@ -439,7 +391,7 @@ const float INITPRIO_SETLOCATION = 90; const float INITPRIO_LINKDOORS = 91; const float INITPRIO_LAST = 99; -.void(void) initialize_entity; +.void() initialize_entity; .float initialize_entity_order; .entity initialize_entity_next; entity initialize_entity_first; @@ -449,7 +401,7 @@ entity initialize_entity_first; float sound_allowed(float dest, entity e); -void InitializeEntity(entity e, void(void) func, float order); -void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer); +void InitializeEntity(entity e, void() func, float order); +void SetCustomizer(entity e, float() customizer, void() uncustomizer); #endif diff --git a/qcsrc/server/mutators/all.inc b/qcsrc/server/mutators/all.inc index d1cd583b15..1a80b44094 100644 --- a/qcsrc/server/mutators/all.inc +++ b/qcsrc/server/mutators/all.inc @@ -9,34 +9,5 @@ #include "mutator/gamemode_keepaway.qc" #include "mutator/gamemode_keyhunt.qc" #include "mutator/gamemode_lms.qc" -#include "mutator/gamemode_onslaught.qc" #include "mutator/gamemode_race.qc" #include "mutator/gamemode_tdm.qc" - -#include "mutator/mutator_bloodloss.qc" -#include "mutator/mutator_breakablehook.qc" -#include "mutator/mutator_buffs.qc" -#include "mutator/mutator_campcheck.qc" -#include "mutator/mutator_dodging.qc" -#include "mutator/mutator_hook.qc" -#include "mutator/mutator_invincibleproj.qc" -#include "mutator/mutator_melee_only.qc" -#include "mutator/mutator_midair.qc" -#include "mutator/mutator_multijump.qc" -#include "mutator/mutator_nades.qc" -#include "mutator/mutator_new_toys.qc" -#include "mutator/mutator_nix.qc" -#include "mutator/mutator_overkill.qc" -#include "mutator/mutator_physical_items.qc" -#include "mutator/mutator_pinata.qc" -#include "mutator/mutator_random_gravity.qc" -#include "mutator/mutator_rocketflying.qc" -#include "mutator/mutator_rocketminsta.qc" -#include "mutator/mutator_spawn_near_teammate.qc" -#include "mutator/mutator_superspec.qc" -#include "mutator/mutator_touchexplode.qc" -#include "mutator/mutator_vampirehook.qc" -#include "mutator/mutator_vampire.qc" -#include "mutator/mutator_weaponarena_random.qc" - -#include "mutator/sandbox.qc" diff --git a/qcsrc/server/mutators/all.qc b/qcsrc/server/mutators/all.qc index 542763fd28..3520953ba5 100644 --- a/qcsrc/server/mutators/all.qc +++ b/qcsrc/server/mutators/all.qc @@ -9,8 +9,6 @@ #include "../../common/stats.qh" #include "../../common/teams.qh" #include "../../common/util.qh" - #include "../../common/nades/all.qh" - #include "../../common/buffs/all.qh" #include "../../common/command/markup.qh" #include "../../common/command/rpn.qh" #include "../../common/command/generic.qh" diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 2d9e75b94e..4795aab28d 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -153,6 +153,9 @@ MUTATOR_HOOKABLE(ForbidThrowCurrentWeapon, EV_NO_ARGS); /** returns true if dropping the current weapon shall not be allowed at any time including death */ MUTATOR_HOOKABLE(ForbidDropCurrentWeapon, EV_NO_ARGS); +/** */ +MUTATOR_HOOKABLE(SetDefaultAlpha, EV_NO_ARGS); + /** allows changing attack rate */ #define EV_WeaponRateFactor(i, o) \ /**/ i(float, weapon_rate) \ @@ -182,7 +185,10 @@ MUTATOR_HOOKABLE(CustomizeWaypoint, EV_CustomizeWaypoint); * checks if the current item may be spawned (self.items and self.weapons may be read and written to, as well as the ammo_ fields) * return error to request removal */ -MUTATOR_HOOKABLE(FilterItem, EV_NO_ARGS); +#define EV_FilterItem(i, o) \ + /** the current item */ i(entity, __self) \ + /**/ +MUTATOR_HOOKABLE(FilterItem, EV_FilterItem); /** return error to request removal */ #define EV_TurretSpawn(i, o) \ @@ -334,15 +340,31 @@ MUTATOR_HOOKABLE(PlayerDamage_Calculate, EV_PlayerDamage_Calculate); * Called when a player is damaged */ #define EV_PlayerDamaged(i, o) \ - /** attacker */ i(entity, mutator_argv_entity_0) \ - /** target */ i(entity, mutator_argv_entity_1) \ - /** health */ i(int, mutator_argv_int_0) \ - /** armor */ i(int, mutator_argv_int_1) \ - /** location */ i(vector, mutator_argv_vector_0) \ - /** deathtype */ i(int, mutator_argv_int_2) \ + /** attacker */ i(entity, MUTATOR_ARGV_0_entity) \ + /** target */ i(entity, MUTATOR_ARGV_1_entity) \ + /** health */ i(int, MUTATOR_ARGV_0_int) \ + /** armor */ i(int, MUTATOR_ARGV_1_int) \ + /** location */ i(vector, MUTATOR_ARGV_0_vector) \ + /** deathtype */ i(int, MUTATOR_ARGV_2_int) \ /**/ MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged); +/** + * Called by W_DecreaseAmmo + */ +#define EV_W_DecreaseAmmo(i, o) \ + /** actor */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo); + +/** + * Called by W_Reload + */ +#define EV_W_Reload(i, o) \ + /** actor */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(W_Reload, EV_W_Reload); + /** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */ #define EV_PlayerPowerups(i, o) \ /**/ i(entity, __self) \ @@ -811,4 +833,23 @@ MUTATOR_HOOKABLE(TurretValidateTarget, EV_TurretValidateTarget); /**/ i(entity, __self) \ /**/ MUTATOR_HOOKABLE(TurretThink, EV_TurretThink); + +MUTATOR_HOOKABLE(Ent_Init, EV_NO_ARGS); + +/** */ +#define EV_PrepareExplosionByDamage(i, o) \ + /**/ i(entity, __self) \ + /**/ i(entity, frag_attacker) \ + /**/ +MUTATOR_HOOKABLE(PrepareExplosionByDamage, EV_PrepareExplosionByDamage); + +/** called when a monster model is about to be set, allows custom paths etc. */ +#define EV_MonsterModel(i, o) \ + /**/ i(string, monster_model) \ + /**/ i(string, monster_model_output) \ + /**/ o(string, monster_model_output) \ + /**/ +string monster_model; +string monster_model_output; +MUTATOR_HOOKABLE(MonsterModel, EV_MonsterModel); #endif diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qc b/qcsrc/server/mutators/mutator/gamemode_ca.qc index b9621cdada..725569bfff 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ca.qc @@ -9,17 +9,17 @@ void ca_Initialize(); REGISTER_MUTATOR(ca, false) { - ActivateTeamplay(); - SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1); - - if (autocvar_g_ca_team_spawns) - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); ca_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, -1, -1); + + if (autocvar_g_ca_team_spawns) + have_team_spawns = -1; // request team spawns } MUTATOR_ONREMOVE diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qc b/qcsrc/server/mutators/mutator/gamemode_ctf.qc index 73e992c95b..d16b168778 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qc @@ -6,15 +6,15 @@ void ctf_Initialize(); REGISTER_MUTATOR(ctf, false) { - ActivateTeamplay(); - SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1); - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); ctf_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_capturelimit_override, autocvar_captureleadlimit_override, -1, -1); + have_team_spawns = -1; // request team spawns } MUTATOR_ONROLLBACK_OR_REMOVE @@ -47,9 +47,14 @@ const int SP_CTF_DROPS = 7; const int SP_CTF_FCKILLS = 8; const int SP_CTF_RETURNS = 9; +CLASS(Flag, Pickup) + ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13') + ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13') +ENDCLASS(Flag) +Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } +void ctf_FlagTouch() { SELFPARAM(); ITEM_HANDLE(Pickup, CTF_FLAG, this, other); } + // flag constants // for most of these, there is just one question to be asked: WHYYYYY? -#define FLAG_MIN (PL_MIN_CONST + '0 0 -13') -#define FLAG_MAX (PL_MAX_CONST + '0 0 -13') const float FLAG_SCALE = 0.6; @@ -125,6 +130,8 @@ const int RETURN_DAMAGE = 3; const int RETURN_SPEEDRUN = 4; const int RETURN_NEEDKILL = 5; +void ctf_Handle_Throw(entity player, entity receiver, float droptype); + // flag properties #define ctf_spawnorigin dropped_origin bool ctf_stalemate; // indicates that a stalemate is active @@ -467,13 +474,12 @@ void ctf_CaptureShield_Touch() void ctf_CaptureShield_Spawn(entity flag) {SELFPARAM(); - entity shield = spawn(); + entity shield = new(ctf_captureshield); shield.enemy = self; shield.team = self.team; shield.touch = ctf_CaptureShield_Touch; shield.customizeentityforclient = ctf_CaptureShield_Customize; - shield.classname = "ctf_captureshield"; shield.effects = EF_ADDITIVE; shield.movetype = MOVETYPE_NOCLIP; shield.solid = SOLID_TRIGGER; @@ -672,6 +678,10 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype) ctf_CaptureShield_Update(player, 0); // shield player from picking up flag } +void shockwave_spawn(string m, vector org, float sz, float t1, float t2) +{ + return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2); +} // ============== // Event Handlers @@ -930,7 +940,7 @@ bool ctf_Stalemate_Customize() return true; } -void ctf_CheckStalemate(void) +void ctf_CheckStalemate() { // declarations int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0; @@ -1033,11 +1043,11 @@ void ctf_FlagThink() ctf_CaptureShield_Update(tmp_entity, 1); // release shield only // sanity checks - if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished + if(self.mins != CTF_FLAG.m_mins || self.maxs != CTF_FLAG.m_maxs) { // reset the flag boundaries in case it got squished LOG_TRACE("wtf the flag got squashed?\n"); - tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self); + tracebox(self.origin, CTF_FLAG.m_mins, CTF_FLAG.m_maxs, self.origin, MOVE_NOMONSTERS, self); if(!trace_startsolid || self.noalign) // can we resize it without getting stuck? - setsize(self, FLAG_MIN, FLAG_MAX); } + setsize(self, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); } switch(self.ctf_status) // reset flag angles in case warpzones adjust it { @@ -1168,26 +1178,27 @@ void ctf_FlagThink() } } -void ctf_FlagTouch() -{SELFPARAM(); +METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) +{ + return = false; if(gameover) { return; } if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; } - entity toucher = other, tmp_entity; - bool is_not_monster = (!IS_MONSTER(toucher)), num_perteam = 0; + bool is_not_monster = (!IS_MONSTER(toucher)); // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces if(ITEM_TOUCH_NEEDKILL()) { if(!autocvar_g_ctf_flag_return_damage_delay) { - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + flag.health = 0; + ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); } - if(!self.ctf_flagdamaged) { return; } + if(!flag.ctf_flagdamaged) { return; } } - FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; } + int num_perteam = 0; + entity tmp_entity; FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; } // special touch behaviors if(toucher.frozen) { return; } @@ -1205,40 +1216,40 @@ void ctf_FlagTouch() } else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world { - if(time > self.wait) // if we haven't in a while, play a sound/effect + if(time > flag.wait) // if we haven't in a while, play a sound/effect { - Send_Effect_(self.toucheffect, self.origin, '0 0 0', 1); - _sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTEN_NORM); - self.wait = time + FLAG_TOUCHRATE; + Send_Effect_(flag.toucheffect, flag.origin, '0 0 0', 1); + _sound(flag, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); + flag.wait = time + FLAG_TOUCHRATE; } return; } else if(toucher.deadflag != DEAD_NO) { return; } - switch(self.ctf_status) + switch(flag.ctf_status) { case FLAG_BASE: { if(ctf_oneflag) { - if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) - ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base - else if(!self.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the neutral flag + if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base + else if(!flag.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the neutral flag } - else if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster) - ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base - else if(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag + else if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, flag) && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base + else if(CTF_DIFFTEAM(toucher, flag) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the enemies flag break; } case FLAG_DROPPED: { - if(CTF_SAMETEAM(toucher, self) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && self.team) // automatically return if there's only 1 player on the team - ctf_Handle_Return(self, toucher); // toucher just returned his own flag - else if(is_not_monster && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) - ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag + if(CTF_SAMETEAM(toucher, flag) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && flag.team) // automatically return if there's only 1 player on the team + ctf_Handle_Return(flag, toucher); // toucher just returned his own flag + else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) + ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag break; } @@ -1250,12 +1261,12 @@ void ctf_FlagTouch() case FLAG_PASSING: { - if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender)) + if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != flag.pass_sender)) { - if(DIFF_TEAM(toucher, self.pass_sender)) - ctf_Handle_Return(self, toucher); + if(DIFF_TEAM(toucher, flag.pass_sender)) + ctf_Handle_Return(flag, toucher); else - ctf_Handle_Retrieve(self, toucher); + ctf_Handle_Retrieve(flag, toucher); } break; } @@ -1323,7 +1334,7 @@ void ctf_Reset() ctf_RespawnFlag(self); } -void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup() +void ctf_DelayedFlagSetup() // called after a flag is placed on a map by ctf_FlagSetup() {SELFPARAM(); // bot waypoints waypoint_spawnforitem_force(self, self.origin); @@ -1349,12 +1360,6 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf ctf_CaptureShield_Spawn(self); } -void set_flag_string(entity flag, .string field, string value, string teamname) -{ - if(flag.(field) == "") - flag.(field) = strzone(sprintf(value,teamname)); -} - void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc {SELFPARAM(); // declarations @@ -1395,20 +1400,20 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e if(!flag.scale) { flag.scale = FLAG_SCALE; } if(flag.skin == 0) { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); } if(flag.model == "") { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); } - set_flag_string(flag, toucheffect, "%sflag_touch", teamname); - set_flag_string(flag, passeffect, "%s_pass", teamname); - set_flag_string(flag, capeffect, "%s_cap", teamname); + if (flag.toucheffect == "") { flag.toucheffect = EFFECT_FLAG_TOUCH(teamnumber).eent_eff_name; } + if (flag.passeffect == "") { flag.passeffect = EFFECT_PASS(teamnumber).eent_eff_name; } + if (flag.capeffect == "") { flag.capeffect = EFFECT_CAP(teamnumber).eent_eff_name; } // sounds - flag.snd_flag_taken = SND(CTF_TAKEN(teamnumber)); - flag.snd_flag_returned = SND(CTF_RETURNED(teamnumber)); - flag.snd_flag_capture = SND(CTF_CAPTURE(teamnumber)); - flag.snd_flag_dropped = SND(CTF_DROPPED(teamnumber)); - if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = SND(CTF_RESPAWN); // if there is ever a team-based sound for this, update the code to match. + flag.snd_flag_taken = strzone(SND(CTF_TAKEN(teamnumber))); + flag.snd_flag_returned = strzone(SND(CTF_RETURNED(teamnumber))); + flag.snd_flag_capture = strzone(SND(CTF_CAPTURE(teamnumber))); + flag.snd_flag_dropped = strzone(SND(CTF_DROPPED(teamnumber))); + if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = strzone(SND(CTF_RESPAWN)); // if there is ever a team-based sound for this, update the code to match. precache_sound(flag.snd_flag_respawn); - if (flag.snd_flag_touch == "") flag.snd_flag_touch = SND(CTF_TOUCH); // again has no team-based sound + if (flag.snd_flag_touch == "") flag.snd_flag_touch = strzone(SND(CTF_TOUCH)); // again has no team-based sound precache_sound(flag.snd_flag_touch); - if (flag.snd_flag_pass == "") flag.snd_flag_pass = SND(CTF_PASS); // same story here + if (flag.snd_flag_pass == "") flag.snd_flag_pass = strzone(SND(CTF_PASS)); // same story here precache_sound(flag.snd_flag_pass); // precache @@ -1416,7 +1421,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e // appearence _setmodel(flag, flag.model); // precision set below - setsize(flag, FLAG_MIN, FLAG_MAX); + setsize(flag, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET)); if(autocvar_g_ctf_flag_glowtrails) diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qc b/qcsrc/server/mutators/mutator/gamemode_cts.qc index 982da6ea3e..c256d1b974 100644 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qc +++ b/qcsrc/server/mutators/mutator/gamemode_cts.qc @@ -7,15 +7,15 @@ void cts_Initialize(); REGISTER_MUTATOR(cts, false) { - g_race_qualifying = true; - independent_players = 1; - SetLimits(0, 0, 0, -1); - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); cts_Initialize(); + + g_race_qualifying = true; + independent_players = 1; + SetLimits(0, 0, 0, -1); } MUTATOR_ONROLLBACK_OR_REMOVE @@ -380,13 +380,6 @@ MUTATOR_HOOKFUNCTION(cts, ForbidPlayerScore_Clear) return true; // in CTS, you don't lose score by observing } -MUTATOR_HOOKFUNCTION(cts, SetModname) -{ - g_cloaked = 1; // always enable cloak in CTS - - return false; -} - MUTATOR_HOOKFUNCTION(cts, GetRecords) { for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc index 5ebd7c6b6e..d05bc18bd2 100644 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc +++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc @@ -18,7 +18,7 @@ REGISTER_MUTATOR(dm, false) MUTATOR_ONREMOVE { - print("This is a game type and it cannot be removed at runtime."); + error("This is a game type and it cannot be removed at runtime."); return -1; } diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc index 5195177dfc..8c5d5159d5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc @@ -10,19 +10,19 @@ void dom_Initialize(); REGISTER_MUTATOR(dom, false) { - int fraglimit_override = autocvar_g_domination_point_limit; - if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) - fraglimit_override = autocvar_g_domination_roundbased_point_limit; - - ActivateTeamplay(); - SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1); - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); dom_Initialize(); + + int fraglimit_override = autocvar_g_domination_point_limit; + if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) + fraglimit_override = autocvar_g_domination_roundbased_point_limit; + + ActivateTeamplay(); + SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, -1, -1); + have_team_spawns = -1; // request team spawns } MUTATOR_ONREMOVE diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc index c392c4be58..29ae006684 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc @@ -8,17 +8,17 @@ void freezetag_Initialize(); REGISTER_MUTATOR(ft, false) { - ActivateTeamplay(); - SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1); - - if (autocvar_g_freezetag_team_spawns) - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); freezetag_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, -1, -1); + + if (autocvar_g_freezetag_team_spawns) + have_team_spawns = -1; // request team spawns } MUTATOR_ONROLLBACK_OR_REMOVE @@ -44,15 +44,16 @@ const float ICE_MIN_ALPHA = 0.1; float freezetag_teams; .float reviving; // temp var -#endif +float autocvar_g_freezetag_revive_extra_size; +float autocvar_g_freezetag_revive_speed; +bool autocvar_g_freezetag_revive_nade; +float autocvar_g_freezetag_revive_nade_health; + +#endif #ifdef IMPLEMENTATION float autocvar_g_freezetag_frozen_maxtime; -bool autocvar_g_freezetag_revive_nade; -float autocvar_g_freezetag_revive_nade_health; -float autocvar_g_freezetag_revive_extra_size; -float autocvar_g_freezetag_revive_speed; float autocvar_g_freezetag_revive_clearspeed; float autocvar_g_freezetag_round_timelimit; int autocvar_g_freezetag_teams; diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qc b/qcsrc/server/mutators/mutator/gamemode_invasion.qc index f52afb9e2f..1fa986483b 100644 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qc @@ -9,14 +9,6 @@ void invasion_Initialize(); REGISTER_MUTATOR(inv, false) { - SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1); - if (autocvar_g_invasion_teams >= 2) - { - ActivateTeamplay(); - if (autocvar_g_invasion_team_spawns) - have_team_spawns = -1; // request team spawns - } - MUTATOR_ONADD { if (time > 1) // game loads at time 1 @@ -25,6 +17,14 @@ REGISTER_MUTATOR(inv, false) invasion_Initialize(); cvar_settemp("g_monsters", "1"); + + SetLimits(autocvar_g_invasion_point_limit, -1, -1, -1); + if (autocvar_g_invasion_teams >= 2) + { + ActivateTeamplay(); + if (autocvar_g_invasion_team_spawns) + have_team_spawns = -1; // request team spawns + } } MUTATOR_ONROLLBACK_OR_REMOVE diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc index 8961e6bf00..ea14243709 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc @@ -462,13 +462,11 @@ MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) void ka_SpawnBall() // loads various values for the ball, runs only once at start of match { - entity e; - e = spawn(); + entity e = new(keepawayball); e.model = "models/orbs/orbblue.md3"; precache_model(e.model); _setmodel(e, e.model); setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off - e.classname = "keepawayball"; e.damageforcescale = autocvar_g_keepawayball_damageforcescale; e.takedamage = DAMAGE_YES; e.solid = SOLID_TRIGGER; diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc index 5982cf2085..8d4240e29a 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@ -8,16 +8,16 @@ void kh_Initialize(); REGISTER_MUTATOR(kh, false) { - ActivateTeamplay(); - SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1); - if (autocvar_g_keyhunt_team_spawns) - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); kh_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1); + if (autocvar_g_keyhunt_team_spawns) + have_team_spawns = -1; // request team spawns } MUTATOR_ONROLLBACK_OR_REMOVE @@ -45,7 +45,7 @@ float kh_tracking_enabled; .entity kh_next; float kh_Key_AllOwnedByWhichTeam(); -typedef void(void) kh_Think_t; +typedef void() kh_Think_t; void kh_StartRound(); void kh_Controller_SetThink(float t, kh_Think_t func); @@ -102,7 +102,7 @@ float kh_no_radar_circles; // bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self // bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self // bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self -.float kh_state; +.float kh_state = _STAT(KH_KEYS); .float siren_time; // time delay the siren //.float stuff_time; // time delay to stuffcmd a cvar @@ -800,17 +800,16 @@ void key_reset() } const string STR_ITEM_KH_KEY = "item_kh_key"; -void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected +void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every time a new flag is created, ie after all the keys have been collected { - entity key; - key = spawn(); + entity key = spawn(); key.count = i; key.classname = STR_ITEM_KH_KEY; key.touch = kh_Key_Touch; key.think = kh_Key_Think; key.nextthink = time; key.items = IT_KEY1 | IT_KEY2; - key.cnt = angle; + key.cnt = _angle; key.angles = '0 360 0' * random(); key.event_damage = kh_Key_Damage; key.takedamage = DAMAGE_YES; @@ -1096,8 +1095,6 @@ void kh_Initialize() // sets up th KH environment kh_controller.model = ""; kh_controller.modelindex = 0; - addstat(STAT_KH_KEYS, AS_INT, kh_state); - kh_ScoreRules(kh_teams); } diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qc b/qcsrc/server/mutators/mutator/gamemode_lms.qc index f2ee672dad..9fa7f47397 100644 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qc +++ b/qcsrc/server/mutators/mutator/gamemode_lms.qc @@ -6,13 +6,13 @@ void lms_Initialize(); REGISTER_MUTATOR(lms, false) { - SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1); - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); lms_Initialize(); + + SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1); } MUTATOR_ONROLLBACK_OR_REMOVE diff --git a/qcsrc/server/mutators/mutator/gamemode_onslaught.qc b/qcsrc/server/mutators/mutator/gamemode_onslaught.qc deleted file mode 100644 index 51ddcadf91..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_onslaught.qc +++ /dev/null @@ -1,2326 +0,0 @@ -#ifndef GAMEMODE_ONSLAUGHT_H -#define GAMEMODE_ONSLAUGHT_H - -float autocvar_g_onslaught_point_limit; -void ons_Initialize(); - -REGISTER_MUTATOR(ons, false) -{ - ActivateTeamplay(); - SetLimits(autocvar_g_onslaught_point_limit, -1, -1, -1); - have_team_spawns = -1; // request team spawns - - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ons_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ons_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return false; -} - -#ifdef SVQC - -.entity ons_toucher; // player who touched the control point - -// control point / generator constants -const float ONS_CP_THINKRATE = 0.2; -const float GEN_THINKRATE = 1; -#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) -const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); -const vector CPICON_OFFSET = ('0 0 96'); - -// list of generators on the map -entity ons_worldgeneratorlist; -.entity ons_worldgeneratornext; -.entity ons_stalegeneratornext; - -// list of control points on the map -entity ons_worldcplist; -.entity ons_worldcpnext; -.entity ons_stalecpnext; - -// list of links on the map -entity ons_worldlinklist; -.entity ons_worldlinknext; -.entity ons_stalelinknext; - -// definitions -.entity sprite; -.string target2; -.int iscaptured; -.int islinked; -.int isshielded; -.float lasthealth; -.int lastteam; -.int lastshielded; -.int lastcaptured; - -.bool waslinked; - -bool ons_stalemate; - -.float teleport_antispam; - -.bool ons_roundlost; - -// waypoint sprites -.entity bot_basewaypoint; // generator waypointsprite - -.bool isgenneighbor[17]; -.bool iscpneighbor[17]; -float ons_notification_time[17]; - -.float ons_overtime_damagedelay; - -.vector ons_deathloc; - -.entity ons_spawn_by; - -// declarations for functions used outside gamemode_onslaught.qc -void ons_Generator_UpdateSprite(entity e); -void ons_ControlPoint_UpdateSprite(entity e); -bool ons_ControlPoint_Attackable(entity cp, int teamnumber); - -// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet -float ons_captureshield_force; // push force of the shield - -// bot player logic -const int HAVOCBOT_ONS_ROLE_NONE = 0; -const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; -const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; -const int HAVOCBOT_ONS_ROLE_OFFENSE = 8; - -.entity havocbot_ons_target; - -.int havocbot_role_flags; -.float havocbot_attack_time; - -void havocbot_role_ons_defense(); -void havocbot_role_ons_offense(); -void havocbot_role_ons_assistant(); - -void havocbot_ons_reset_role(entity bot); -void havocbot_goalrating_items(float ratingscale, vector org, float sradius); -void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius); - -// score rule declarations -const int ST_ONS_CAPS = 1; -const int SP_ONS_CAPS = 4; -const int SP_ONS_TAKES = 6; - -#endif -#endif - -#ifdef IMPLEMENTATION - -#include "../../controlpoint.qh" -#include "../../generator.qh" - -bool g_onslaught; - -float autocvar_g_onslaught_debug; -float autocvar_g_onslaught_teleport_wait; -bool autocvar_g_onslaught_spawn_at_controlpoints; -bool autocvar_g_onslaught_spawn_at_generator; -float autocvar_g_onslaught_cp_proxydecap; -float autocvar_g_onslaught_cp_proxydecap_distance = 512; -float autocvar_g_onslaught_cp_proxydecap_dps = 100; -float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; -float autocvar_g_onslaught_spawn_at_controlpoints_random; -float autocvar_g_onslaught_spawn_at_generator_chance; -float autocvar_g_onslaught_spawn_at_generator_random; -float autocvar_g_onslaught_cp_buildhealth; -float autocvar_g_onslaught_cp_buildtime; -float autocvar_g_onslaught_cp_health; -float autocvar_g_onslaught_cp_regen; -float autocvar_g_onslaught_gen_health; -float autocvar_g_onslaught_shield_force = 100; -float autocvar_g_onslaught_allow_vehicle_touch; -float autocvar_g_onslaught_round_timelimit; -float autocvar_g_onslaught_warmup; -float autocvar_g_onslaught_teleport_radius; -float autocvar_g_onslaught_spawn_choose; -float autocvar_g_onslaught_click_radius; - -void FixSize(entity e); - -// ======================= -// CaptureShield Functions -// ======================= - -bool ons_CaptureShield_Customize() -{SELFPARAM(); - entity e = WaypointSprite_getviewentity(other); - - if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, e.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return false; } - if(SAME_TEAM(self, e)) { return false; } - - return true; -} - -void ons_CaptureShield_Touch() -{SELFPARAM(); - if(!self.enemy.isshielded && (ons_ControlPoint_Attackable(self.enemy, other.team) > 0 || self.enemy.classname != "onslaught_controlpoint")) { return; } - if(!IS_PLAYER(other)) { return; } - if(SAME_TEAM(other, self)) { return; } - - vector mymid = (self.absmin + self.absmax) * 0.5; - vector othermid = (other.absmin + other.absmax) * 0.5; - - Damage(other, self, self, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force); - - if(IS_REAL_CLIENT(other)) - { - play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - - if(self.enemy.classname == "onslaught_generator") - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); - else - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); - } -} - -void ons_CaptureShield_Reset() -{SELFPARAM(); - self.colormap = self.enemy.colormap; - self.team = self.enemy.team; -} - -void ons_CaptureShield_Spawn(entity generator, bool is_generator) -{ - entity shield = spawn(); - - shield.enemy = generator; - shield.team = generator.team; - shield.colormap = generator.colormap; - shield.reset = ons_CaptureShield_Reset; - shield.touch = ons_CaptureShield_Touch; - shield.customizeentityforclient = ons_CaptureShield_Customize; - shield.classname = "ons_captureshield"; - shield.effects = EF_ADDITIVE; - shield.movetype = MOVETYPE_NOCLIP; - shield.solid = SOLID_TRIGGER; - shield.avelocity = '7 0 11'; - shield.scale = 1; - shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); - - precache_model(shield.model); - setorigin(shield, generator.origin); - _setmodel(shield, shield.model); - setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); -} - - -// ========== -// Junk Pile -// ========== - -void ons_debug(string input) -{ - switch(autocvar_g_onslaught_debug) - { - case 1: LOG_TRACE(input); break; - case 2: LOG_INFO(input); break; - } -} - -void setmodel_fixsize(entity e, Model m) -{ - setmodel(e, m); - FixSize(e); -} - -void onslaught_updatelinks() -{ - entity l; - // first check if the game has ended - ons_debug("--- updatelinks ---\n"); - // mark generators as being shielded and networked - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.iscaptured) - ons_debug(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n")); - else - ons_debug(strcat(etos(l), " (generator) is destroyed\n")); - l.islinked = l.iscaptured; - l.isshielded = l.iscaptured; - l.sprite.SendFlags |= 16; - } - // mark points as shielded and not networked - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.islinked = false; - l.isshielded = true; - int i; - for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } - ons_debug(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n")); - l.sprite.SendFlags |= 16; - } - // flow power outward from the generators through the network - bool stop = false; - while (!stop) - { - stop = true; - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - // if both points are captured by the same team, and only one of - // them is powered, mark the other one as powered as well - if (l.enemy.iscaptured && l.goalentity.iscaptured) - if (l.enemy.islinked != l.goalentity.islinked) - if(SAME_TEAM(l.enemy, l.goalentity)) - { - if (!l.goalentity.islinked) - { - stop = false; - l.goalentity.islinked = true; - ons_debug(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n")); - } - else if (!l.enemy.islinked) - { - stop = false; - l.enemy.islinked = true; - ons_debug(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n")); - } - } - } - } - // now that we know which points are powered we can mark their neighbors - // as unshielded if team differs - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - if (l.goalentity.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n")); - l.enemy.isshielded = false; - } - if(l.goalentity.classname == "onslaught_generator") - l.enemy.isgenneighbor[l.goalentity.team] = true; - else - l.enemy.iscpneighbor[l.goalentity.team] = true; - } - if (l.enemy.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - ons_debug(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n")); - l.goalentity.isshielded = false; - } - if(l.enemy.classname == "onslaught_generator") - l.goalentity.isgenneighbor[l.enemy.team] = true; - else - l.goalentity.iscpneighbor[l.enemy.team] = true; - } - } - // now update the generators - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.isshielded) - { - ons_debug(strcat(etos(l), " (generator) is shielded\n")); - l.takedamage = DAMAGE_NO; - l.bot_attack = false; - } - else - { - ons_debug(strcat(etos(l), " (generator) is not shielded\n")); - l.takedamage = DAMAGE_AIM; - l.bot_attack = true; - } - - ons_Generator_UpdateSprite(l); - } - // now update the takedamage and alpha variables on control point icons - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - if (l.isshielded) - { - ons_debug(strcat(etos(l), " (point) is shielded\n")); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_NO; - l.goalentity.bot_attack = false; - } - } - else - { - ons_debug(strcat(etos(l), " (point) is not shielded\n")); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_AIM; - l.goalentity.bot_attack = true; - } - } - ons_ControlPoint_UpdateSprite(l); - } - l = findchain(classname, "ons_captureshield"); - while(l) - { - l.team = l.enemy.team; - l.colormap = l.enemy.colormap; - l = l.chain; - } -} - - -// =================== -// Main Link Functions -// =================== - -bool ons_Link_Send(entity this, entity to, int sendflags) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & 1) - { - WriteCoord(MSG_ENTITY, self.goalentity.origin_x); - WriteCoord(MSG_ENTITY, self.goalentity.origin_y); - WriteCoord(MSG_ENTITY, self.goalentity.origin_z); - } - if(sendflags & 2) - { - WriteCoord(MSG_ENTITY, self.enemy.origin_x); - WriteCoord(MSG_ENTITY, self.enemy.origin_y); - WriteCoord(MSG_ENTITY, self.enemy.origin_z); - } - if(sendflags & 4) - { - WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16 - } - return true; -} - -void ons_Link_CheckUpdate() -{SELFPARAM(); - // TODO check if the two sides have moved (currently they won't move anyway) - float cc = 0, cc1 = 0, cc2 = 0; - - if(self.goalentity.islinked || self.goalentity.iscaptured) { cc1 = (self.goalentity.team - 1) * 0x01; } - if(self.enemy.islinked || self.enemy.iscaptured) { cc2 = (self.enemy.team - 1) * 0x10; } - - cc = cc1 + cc2; - - if(cc != self.clientcolors) - { - self.clientcolors = cc; - self.SendFlags |= 4; - } - - self.nextthink = time; -} - -void ons_DelayedLinkSetup() -{SELFPARAM(); - self.goalentity = find(world, targetname, self.target); - self.enemy = find(world, targetname, self.target2); - if(!self.goalentity) { objerror("can not find target\n"); } - if(!self.enemy) { objerror("can not find target2\n"); } - - ons_debug(strcat(etos(self.goalentity), " linked with ", etos(self.enemy), "\n")); - self.SendFlags |= 3; - self.think = ons_Link_CheckUpdate; - self.nextthink = time; -} - - -// ============================= -// Main Control Point Functions -// ============================= - -int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) -{ - if(cp.isgenneighbor[teamnumber]) { return 2; } - if(cp.iscpneighbor[teamnumber]) { return 1; } - - return 0; -} - -int ons_ControlPoint_Attackable(entity cp, int teamnumber) - // -2: SAME TEAM, attackable by enemy! - // -1: SAME TEAM! - // 0: off limits - // 1: attack it - // 2: touch it - // 3: attack it (HIGH PRIO) - // 4: touch it (HIGH PRIO) -{ - int a; - - if(cp.isshielded) - { - return 0; - } - else if(cp.goalentity) - { - // if there's already an icon built, nothing happens - if(cp.team == teamnumber) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a) // attackable by enemy? - return -2; // EMERGENCY! - return -1; - } - // we know it can be linked, so no need to check - // but... - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a == 2) // near our generator? - return 3; // EMERGENCY! - return 1; - } - else - { - // free point - if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t - if(a == 2) - return 4; // GET THIS ONE NOW! - else - return 2; // TOUCH ME - } - } - return 0; -} - -void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(damage <= 0) { return; } - - if (self.owner.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - self.pain_finished = time + 1; - attacker.typehitsound += 1; // play both sounds (shield is way too quiet) - } - - return; - } - - if(IS_PLAYER(attacker)) - if(time - ons_notification_time[self.team] > 10) - { - play2team(self.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); - ons_notification_time[self.team] = time; - } - - self.health = self.health - damage; - if(self.owner.iscaptured) - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - else - WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE)); - self.pain_finished = time + 1; - // particles on every hit - pointparticles(particleeffectnum(EFFECT_SPARKS), hitloc, force*-1, 1); - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); - else - sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); - - if (self.health < 0) - { - sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum(EFFECT_ROCKET_EXPLODE), self.origin, '0 0 0', 1); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname); - - PlayerScore_Add(attacker, SP_ONS_TAKES, 1); - PlayerScore_Add(attacker, SP_SCORE, 10); - - self.owner.goalentity = world; - self.owner.islinked = false; - self.owner.iscaptured = false; - self.owner.team = 0; - self.owner.colormap = 1024; - - WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0); - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - setself(self.owner); - activator = self; - SUB_UseTargets (); - setself(this); - - self.owner.waslinked = self.owner.islinked; - if(self.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel_fixsize(self.owner, MDL_ONS_CP_PAD1); - //setsize(self, '-32 -32 0', '32 32 8'); - - remove(self); - } - - self.SendFlags |= CPSF_STATUS; -} - -void ons_ControlPoint_Icon_Think() -{SELFPARAM(); - self.nextthink = time + ONS_CP_THINKRATE; - - if(autocvar_g_onslaught_cp_proxydecap) - { - int _enemy_count = 0; - int _friendly_count = 0; - float _dist; - entity _player; - - FOR_EACH_PLAYER(_player) - { - if(!_player.deadflag) - { - _dist = vlen(_player.origin - self.origin); - if(_dist < autocvar_g_onslaught_cp_proxydecap_distance) - { - if(SAME_TEAM(_player, self)) - ++_friendly_count; - else - ++_enemy_count; - } - } - } - - _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - - self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health); - self.SendFlags |= CPSF_STATUS; - if(self.health <= 0) - { - ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0'); - return; - } - } - - if (time > self.pain_finished + 5) - { - if(self.health < self.max_health) - { - self.health = self.health + self.count; - if (self.health >= self.max_health) - self.health = self.max_health; - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - } - } - - if(self.owner.islinked != self.owner.waslinked) - { - // unteam the spawnpoint if needed - int t = self.owner.team; - if(!self.owner.islinked) - self.owner.team = 0; - - setself(self.owner); - activator = self; - SUB_UseTargets (); - setself(this); - - self.owner.team = t; - - self.owner.waslinked = self.owner.islinked; - } - - // damaged fx - if(random() < 0.6 - self.health / self.max_health) - { - Send_Effect(EFFECT_ELECTRIC_SPARKS, self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); - - if(random() > 0.8) - sound(self, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); - else if (random() > 0.5) - sound(self, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); - } -} - -void ons_ControlPoint_Icon_BuildThink() -{SELFPARAM(); - int a; - - self.nextthink = time + ONS_CP_THINKRATE; - - // only do this if there is power - a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team); - if(!a) - return; - - self.health = self.health + self.count; - - self.SendFlags |= CPSF_STATUS; - - if (self.health >= self.max_health) - { - self.health = self.max_health; - self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on - self.think = ons_ControlPoint_Icon_Think; - sound(self, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); - self.owner.iscaptured = true; - self.solid = SOLID_BBOX; - - Send_Effect(EFFECT_CAP(self.owner.team), self.owner.origin, '0 0 0', 1); - - WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.owner.sprite, self.health); - - if(IS_PLAYER(self.owner.ons_toucher)) - { - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message); - Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message); - Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message); - PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1); - PlayerTeamScore_AddScore(self.owner.ons_toucher, 10); - } - - self.owner.ons_toucher = world; - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - setself(self.owner); - activator = self; - SUB_UseTargets (); - setself(this); - - self.SendFlags |= CPSF_SETUP; - } - if(self.owner.model != MDL_ONS_CP_PAD2.model_str()) - setmodel_fixsize(self.owner, MDL_ONS_CP_PAD2); - - if(random() < 0.9 - self.health / self.max_health) - Send_Effect(EFFECT_RAGE, self.origin + 10 * randomvec(), '0 0 -1', 1); -} - -void onslaught_controlpoint_icon_link(entity e, void() spawnproc); - -void ons_ControlPoint_Icon_Spawn(entity cp, entity player) -{ - entity e = spawn(); - - setsize(e, CPICON_MIN, CPICON_MAX); - setorigin(e, cp.origin + CPICON_OFFSET); - - e.classname = "onslaught_controlpoint_icon"; - e.owner = cp; - e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_AIM; - e.bot_attack = true; - e.event_damage = ons_ControlPoint_Icon_Damage; - e.team = player.team; - e.colormap = 1024 + (e.team - 1) * 17; - e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build - - sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); - - cp.goalentity = e; - cp.team = e.team; - cp.colormap = e.colormap; - - Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); - - WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); - WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); - cp.sprite.SendFlags |= 16; - - onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); -} - -entity ons_ControlPoint_Waypoint(entity e) -{ - if(e.team) - { - int a = ons_ControlPoint_Attackable(e, e.team); - - if(a == -2) { return WP_OnsCPDefend; } // defend now - if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch - if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack - } - else - return WP_OnsCP; - - return WP_Null; -} - -void ons_ControlPoint_UpdateSprite(entity e) -{ - entity s1 = ons_ControlPoint_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - bool sh; - sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); - - if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) - { - if(e.iscaptured) // don't mess up build bars! - { - if(sh) - { - WaypointSprite_UpdateMaxHealth(e.sprite, 0); - } - else - { - WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); - } - } - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - - e.lastteam = e.team + 2; - e.lastshielded = sh; - e.lastcaptured = e.iscaptured; - } -} - -void ons_ControlPoint_Touch() -{SELFPARAM(); - entity toucher = other; - int attackable; - - if(IS_VEHICLE(toucher) && toucher.owner) - if(autocvar_g_onslaught_allow_vehicle_touch) - toucher = toucher.owner; - else - return; - - if(!IS_PLAYER(toucher)) { return; } - if(toucher.frozen) { return; } - if(toucher.deadflag != DEAD_NO) { return; } - - if ( SAME_TEAM(self,toucher) ) - if ( self.iscaptured ) - { - if(time <= toucher.teleport_antispam) - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); - else - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); - } - - attackable = ons_ControlPoint_Attackable(self, toucher.team); - if(attackable != 2 && attackable != 4) - return; - // we've verified that this player has a legitimate claim to this point, - // so start building the captured point icon (which only captures this - // point if it successfully builds without being destroyed first) - ons_ControlPoint_Icon_Spawn(self, toucher); - - self.ons_toucher = toucher; - - onslaught_updatelinks(); -} - -void ons_ControlPoint_Think() -{SELFPARAM(); - self.nextthink = time + ONS_CP_THINKRATE; - CSQCMODEL_AUTOUPDATE(self); -} - -void ons_ControlPoint_Reset() -{SELFPARAM(); - if(self.goalentity) - remove(self.goalentity); - - self.goalentity = world; - self.team = 0; - self.colormap = 1024; - self.iscaptured = false; - self.islinked = false; - self.isshielded = true; - self.think = ons_ControlPoint_Think; - self.ons_toucher = world; - self.nextthink = time + ONS_CP_THINKRATE; - setmodel_fixsize(self, MDL_ONS_CP_PAD1); - - WaypointSprite_UpdateMaxHealth(self.sprite, 0); - WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); - - activator = self; - SUB_UseTargets(); // to reset the structures, playerspawns etc. - - CSQCMODEL_AUTOUPDATE(self); -} - -void ons_DelayedControlPoint_Setup(void) -{SELFPARAM(); - onslaught_updatelinks(); - - // captureshield setup - ons_CaptureShield_Spawn(self, false); - - CSQCMODEL_AUTOINIT(self); -} - -void ons_ControlPoint_Setup(entity cp) -{SELFPARAM(); - // declarations - setself(cp); // for later usage with droptofloor() - - // main setup - cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist - ons_worldcplist = cp; - - cp.netname = "Control point"; - cp.team = 0; - cp.solid = SOLID_BBOX; - cp.movetype = MOVETYPE_NONE; - cp.touch = ons_ControlPoint_Touch; - cp.think = ons_ControlPoint_Think; - cp.nextthink = time + ONS_CP_THINKRATE; - cp.reset = ons_ControlPoint_Reset; - cp.colormap = 1024; - cp.iscaptured = false; - cp.islinked = false; - cp.isshielded = true; - - if(cp.message == "") { cp.message = "a"; } - - // appearence - setmodel_fixsize(cp, MDL_ONS_CP_PAD1); - - // control point placement - if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location - { - cp.noalign = true; - cp.movetype = MOVETYPE_NONE; - } - else // drop to floor, automatically find a platform and set that as spawn origin - { - setorigin(cp, cp.origin + '0 0 20'); - cp.noalign = false; - setself(cp); - droptofloor(); - cp.movetype = MOVETYPE_TOSS; - } - - // waypointsprites - WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE); - WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); - - InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); -} - - -// ========================= -// Main Generator Functions -// ========================= - -entity ons_Generator_Waypoint(entity e) -{ - if (e.isshielded) - return WP_OnsGenShielded; - return WP_OnsGen; -} - -void ons_Generator_UpdateSprite(entity e) -{ - entity s1 = ons_Generator_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } -} - -void ons_GeneratorDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(damage <= 0) { return; } - if(warmup_stage || gameover) { return; } - if(!round_handler_IsRoundStarted()) { return; } - - if (attacker != self) - { - if (self.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > self.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - attacker.typehitsound += 1; - self.pain_finished = time + 1; - } - return; - } - if (time > self.pain_finished) - { - self.pain_finished = time + 10; - entity head; - FOR_EACH_REALPLAYER(head) if(SAME_TEAM(head, self)) { Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK); } - play2team(self.team, SND(ONS_GENERATOR_UNDERATTACK)); - } - } - self.health = self.health - damage; - WaypointSprite_UpdateHealth(self.sprite, self.health); - // choose an animation frame based on health - self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1); - // see if the generator is still functional, or dying - if (self.health > 0) - { - self.lasthealth = self.health; - } - else - { - if (attacker == self) - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME_)); - else - { - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_GENDESTROYED_)); - PlayerScore_Add(attacker, SP_SCORE, 100); - } - self.iscaptured = false; - self.islinked = false; - self.isshielded = false; - self.takedamage = DAMAGE_NO; // can't be hurt anymore - self.event_damage = func_null; // won't do anything if hurt - self.count = 0; // reset counter - self.think = func_null; - self.nextthink = 0; - //self.think(); // do the first explosion now - - WaypointSprite_UpdateMaxHealth(self.sprite, 0); - WaypointSprite_Ping(self.sprite); - //WaypointSprite_Kill(self.sprite); // can't do this yet, code too poor - - onslaught_updatelinks(); - } - - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(self, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - } - else - { - // particles on every hit - Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); - - //sound on every hit - if (random() < 0.5) - sound(self, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); - else - sound(self, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); - } - - self.SendFlags |= GSF_STATUS; -} - -void ons_GeneratorThink() -{SELFPARAM(); - entity e; - self.nextthink = time + GEN_THINKRATE; - if (!gameover) - { - if(!self.isshielded && self.wait < time) - { - self.wait = time + 5; - FOR_EACH_REALPLAYER(e) - { - if(SAME_TEAM(e, self)) - { - Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); - soundto(MSG_ONE, e, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? - } - else - Send_Notification(NOTIF_ONE, e, MSG_CENTER, APP_TEAM_NUM_4(self.team, CENTER_ONS_NOTSHIELDED_)); - } - } - } -} - -void ons_GeneratorReset() -{SELFPARAM(); - self.team = self.team_saved; - self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health; - self.takedamage = DAMAGE_AIM; - self.bot_attack = true; - self.iscaptured = true; - self.islinked = true; - self.isshielded = true; - self.event_damage = ons_GeneratorDamage; - self.think = ons_GeneratorThink; - self.nextthink = time + GEN_THINKRATE; - - Net_LinkEntity(self, false, 0, generator_send); - - self.SendFlags = GSF_SETUP; // just incase - self.SendFlags |= GSF_STATUS; - - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); - WaypointSprite_UpdateRule(self.sprite,self.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); -} - -void ons_DelayedGeneratorSetup() -{SELFPARAM(); - // bot waypoints - waypoint_spawnforitem_force(self, self.origin); - self.nearestwaypointtimeout = 0; // activate waypointing again - self.bot_basewaypoint = self.nearestwaypoint; - - // captureshield setup - ons_CaptureShield_Spawn(self, true); - - onslaught_updatelinks(); - - Net_LinkEntity(self, false, 0, generator_send); -} - - -void onslaught_generator_touch() -{SELFPARAM(); - if ( IS_PLAYER(other) ) - if ( SAME_TEAM(self,other) ) - if ( self.iscaptured ) - { - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT); - } -} - -void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc -{SELFPARAM(); - // declarations - int teamnumber = gen.team; - setself(gen); // for later usage with droptofloor() - - // main setup - gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist - ons_worldgeneratorlist = gen; - - gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); - gen.classname = "onslaught_generator"; - gen.solid = SOLID_BBOX; - gen.team_saved = teamnumber; - gen.movetype = MOVETYPE_NONE; - gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; - gen.takedamage = DAMAGE_AIM; - gen.bot_attack = true; - gen.event_damage = ons_GeneratorDamage; - gen.reset = ons_GeneratorReset; - gen.think = ons_GeneratorThink; - gen.nextthink = time + GEN_THINKRATE; - gen.iscaptured = true; - gen.islinked = true; - gen.isshielded = true; - gen.touch = onslaught_generator_touch; - - // appearence - // model handled by CSQC - setsize(gen, GENERATOR_MIN, GENERATOR_MAX); - setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); - gen.colormap = 1024 + (teamnumber - 1) * 17; - - // generator placement - setself(gen); - droptofloor(); - - // waypointsprites - WaypointSprite_SpawnFixed(WP_Null, self.origin + CPGEN_WAYPOINT_OFFSET, self, sprite, RADARICON_NONE); - WaypointSprite_UpdateRule(self.sprite, self.team, SPRITERULE_TEAMPLAY); - WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health); - WaypointSprite_UpdateHealth(self.sprite, self.health); - - InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); -} - - -// =============== -// Round Handler -// =============== - -int total_generators; -void Onslaught_count_generators() -{ - entity e; - total_generators = redowned = blueowned = yellowowned = pinkowned = 0; - for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) - { - ++total_generators; - redowned += (e.team == NUM_TEAM_1 && e.health > 0); - blueowned += (e.team == NUM_TEAM_2 && e.health > 0); - yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); - pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); - } -} - -int Onslaught_GetWinnerTeam() -{ - int winner_team = 0; - if(redowned > 0) - winner_team = NUM_TEAM_1; - if(blueowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) - return winner_team; - return -1; // no generators left? -} - -#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) -#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) -bool Onslaught_CheckWinner() -{ - entity e; - - if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) - { - ons_stalemate = true; - - if (!wpforenemy_announced) - { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); - sound(world, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); - - wpforenemy_announced = true; - } - - entity tmp_entity; // temporary entity - float d; - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) - { - // tmp_entity.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - d = 1; - for(e = ons_worldcplist; e; e = e.ons_worldcpnext) - { - if(DIFF_TEAM(e, tmp_entity)) - if(e.islinked) - d = d + 1; - } - - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * tmp_entity.max_health; - else - d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - - Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); - - tmp_entity.sprite.SendFlags |= 16; - - tmp_entity.ons_overtime_damagedelay = time + 1; - } - } - else { wpforenemy_announced = false; ons_stalemate = false; } - - Onslaught_count_generators(); - - if(ONS_OWNED_GENERATORS_OK()) - return 0; - - int winner_team = Onslaught_GetWinnerTeam(); - - if(winner_team > 0) - { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_)); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_)); - TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); - } - else if(winner_team == -1) - { - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED); - } - - ons_stalemate = false; - - play2all(SND(CTF_CAPTURE(winner_team))); - - round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); - - FOR_EACH_PLAYER(e) - { - e.ons_roundlost = true; - e.player_blocked = true; - - nades_Clear(e); - } - - return 1; -} - -bool Onslaught_CheckPlayers() -{ - return 1; -} - -void Onslaught_RoundStart() -{ - entity tmp_entity; - FOR_EACH_PLAYER(tmp_entity) { tmp_entity.player_blocked = false; } - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - tmp_entity.sprite.SendFlags |= 16; - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - tmp_entity.sprite.SendFlags |= 16; -} - - -// ================ -// Bot player logic -// ================ - -// NOTE: LEGACY CODE, needs to be re-written! - -void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius) -{SELFPARAM(); - entity head; - float t, c; - int i; - bool needarmor = false, needweapons = false; - - // Needs armor/health? - if(self.health<100) - needarmor = true; - - // Needs weapons? - c = 0; - for(i = WEP_FIRST; i <= WEP_LAST ; ++i) - { - // Find weapon - if(self.weapons & WepSet_FromWeapon(i)) - if(++c>=4) - break; - } - - if(c<4) - needweapons = true; - - if(!needweapons && !needarmor) - return; - - ons_debug(strcat(self.netname, " needs weapons ", ftos(needweapons) , "\n")); - ons_debug(strcat(self.netname, " needs armor ", ftos(needarmor) , "\n")); - - // See what is around - head = findchainfloat(bot_pickup, true); - while (head) - { - // gather health and armor only - if (head.solid) - if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) - if (vlen(head.origin - org) < sradius) - { - t = head.bot_pickupevalfunc(self, head); - if (t > 0) - navigation_routerating(head, t * ratingscale, 500); - } - head = head.chain; - } -} - -void havocbot_role_ons_setrole(entity bot, int role) -{ - ons_debug(strcat(bot.netname," switched to ")); - switch(role) - { - case HAVOCBOT_ONS_ROLE_DEFENSE: - ons_debug("defense"); - bot.havocbot_role = havocbot_role_ons_defense; - bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_ONS_ROLE_ASSISTANT: - ons_debug("assistant"); - bot.havocbot_role = havocbot_role_ons_assistant; - bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_ONS_ROLE_OFFENSE: - ons_debug("offense"); - bot.havocbot_role = havocbot_role_ons_offense; - bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; - bot.havocbot_role_timeout = 0; - break; - } - ons_debug("\n"); -} - -int havocbot_ons_teamcount(entity bot, int role) -{SELFPARAM(); - int c = 0; - entity head; - - FOR_EACH_PLAYER(head) - if(SAME_TEAM(head, self)) - if(head.havocbot_role_flags & role) - ++c; - - return c; -} - -void havocbot_goalrating_ons_controlpoints_attack(float ratingscale) -{SELFPARAM(); - entity cp, cp1, cp2, best, pl, wp; - float radius, bestvalue; - int c; - bool found; - - // Filter control points - for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) - { - cp2.wpcost = c = 0; - cp2.wpconsidered = false; - - if(cp2.isshielded) - continue; - - // Ignore owned controlpoints - if(!(cp2.isgenneighbor[self.team] || cp2.iscpneighbor[self.team])) - continue; - - // Count team mates interested in this control point - // (easier and cleaner than keeping counters per cp and teams) - FOR_EACH_PLAYER(pl) - if(SAME_TEAM(pl, self)) - if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) - if(pl.havocbot_ons_target==cp2) - ++c; - - // NOTE: probably decrease the cost of attackable control points - cp2.wpcost = c; - cp2.wpconsidered = true; - } - - // We'll consider only the best case - bestvalue = 99999999999; - cp = world; - for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) - { - if (!cp1.wpconsidered) - continue; - - if(cp1.wpcost self.havocbot_role_timeout) - { - havocbot_ons_reset_role(self); - return; - } - - if(self.havocbot_attack_time>time) - return; - - if (self.bot_strategytime < time) - { - navigation_goalrating_start(); - havocbot_goalrating_enemyplayers(20000, self.origin, 650); - if(!havocbot_goalrating_ons_generator_attack(20000)) - havocbot_goalrating_ons_controlpoints_attack(20000); - havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000); - navigation_goalrating_end(); - - self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - } -} - -void havocbot_role_ons_assistant() -{SELFPARAM(); - havocbot_ons_reset_role(self); -} - -void havocbot_role_ons_defense() -{SELFPARAM(); - havocbot_ons_reset_role(self); -} - -void havocbot_ons_reset_role(entity bot) -{SELFPARAM(); - entity head; - int c = 0; - - if(self.deadflag != DEAD_NO) - return; - - bot.havocbot_ons_target = world; - - // TODO: Defend control points or generator if necessary - - // if there is only me on the team switch to offense - c = 0; - FOR_EACH_PLAYER(head) - if(SAME_TEAM(head, self)) - ++c; - - if(c==1) - { - havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); - return; - } - - havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE); -} - - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - */ -entity ons_Nearest_ControlPoint(vector pos, float max_dist) -{SELFPARAM(); - entity tmp_entity, closest_target = world; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, self)) - if(tmp_entity.iscaptured) - if(max_dist <= 0 || vlen(tmp_entity.origin - pos) <= max_dist) - if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, self)) - if(max_dist <= 0 || vlen(tmp_entity.origin - pos) < max_dist) - if(vlen(tmp_entity.origin - pos) <= vlen(closest_target.origin - pos) || closest_target == world) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } - - return closest_target; -} - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - * This function only check distances on the XY plane, disregarding Z - */ -entity ons_Nearest_ControlPoint_2D(vector pos, float max_dist) -{SELFPARAM(); - entity tmp_entity, closest_target = world; - vector delta; - float smallest_distance = 0, distance; - - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) - { - delta = tmp_entity.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(tmp_entity, self)) - if(tmp_entity.iscaptured) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == world || distance <= smallest_distance ) - { - closest_target = tmp_entity; - smallest_distance = distance; - } - - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - delta = tmp_entity.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(tmp_entity, self)) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == world || distance <= smallest_distance ) - { - closest_target = tmp_entity; - smallest_distance = distance; - } - - tmp_entity = tmp_entity.chain; - } - - return closest_target; -} -/** - * find the number of control points and generators in the same team as self - */ -int ons_Count_SelfControlPoints() -{SELFPARAM(); - entity tmp_entity; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - int n = 0; - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, self)) - if(tmp_entity.iscaptured) - n++; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, self)) - n++; - tmp_entity = tmp_entity.chain; - } - return n; -} - -/** - * Teleport player to a random position near tele_target - * if tele_effects is true, teleport sound+particles are created - * return false on failure - */ -bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) -{ - if ( !tele_target ) - return false; - - int i; - vector loc; - float theta; - // narrow the range for each iteration to increase chances that a spawnpoint - // can be found even if there's little room around the control point - float iteration_scale = 1; - for(i = 0; i < 16; ++i) - { - iteration_scale -= i / 16; - theta = random() * 2 * M_PI; - loc_y = sin(theta); - loc_x = cos(theta); - loc_z = 0; - loc *= random() * range * iteration_scale; - - loc += tele_target.origin + '0 0 128' * iteration_scale; - - tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the world - if(trace_fraction == 1.0 && !trace_startsolid) - { - if ( tele_effects ) - { - Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); - sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); - } - setorigin(player, loc); - player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); - makevectors(player.angles); - player.fixangle = true; - player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; - - if ( tele_effects ) - Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); - return true; - } - } - } - - return false; -} - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ons, reset_map_global) -{SELFPARAM(); - entity e; - FOR_EACH_PLAYER(e) - { - e.ons_roundlost = false; - e.ons_deathloc = '0 0 0'; - WITH(entity, self, e, PutClientInServer()); - } - return false; -} - -MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) -{SELFPARAM(); - self.ons_deathloc = '0 0 0'; - return false; -} - -MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) -{SELFPARAM(); - self.ons_deathloc = '0 0 0'; - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) -{SELFPARAM(); - if(!round_handler_IsRoundStarted()) - { - self.player_blocked = true; - return false; - } - - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if(ons_stalemate) { Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( self.ons_spawn_by ) - if ( ons_Teleport(self,self.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) - { - self.ons_spawn_by = world; - return false; - } - - if(autocvar_g_onslaught_spawn_at_controlpoints) - if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; - entity tmp_entity, closest_target = world; - vector spawn_loc = self.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - { - if(SAME_TEAM(tmp_entity, self)) - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) - closest_target = tmp_entity; - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 96' * iteration_scale; - loc += ('0 1 0' * random()) * 128 * iteration_scale; - tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(self, loc); - self.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - if(autocvar_g_onslaught_spawn_at_generator) - if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_generator_random; - entity tmp_entity, closest_target = world; - vector spawn_loc = self.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - { - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else - { - if(SAME_TEAM(tmp_entity, self)) - if(vlen(tmp_entity.origin - spawn_loc) <= vlen(closest_target.origin - spawn_loc) || closest_target == world) - closest_target = tmp_entity; - } - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 128' * iteration_scale; - loc += ('0 1 0' * random()) * 256 * iteration_scale; - tracebox(loc, PL_MIN, PL_MAX, loc, MOVE_NORMAL, self); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the world - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(self, loc); - self.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerDies) -{SELFPARAM(); - frag_target.ons_deathloc = frag_target.origin; - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( ons_Count_SelfControlPoints() > 1 ) - stuffcmd(self, "qc_cmd_cl hud clickradar\n"); - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, MonsterMove) -{SELFPARAM(); - entity e = find(world, targetname, self.target); - if (e != world) - self.team = e.team; - - return false; -} - -void ons_MonsterSpawn_Delayed() -{SELFPARAM(); - entity e, own = self.owner; - - if(!own) { remove(self); return; } - - if(own.targetname) - { - e = find(world, target, own.targetname); - if(e != world) - { - own.team = e.team; - - activator = e; - own.use(); - } - } - - remove(self); -} - -MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) -{SELFPARAM(); - entity e = spawn(); - e.owner = self; - InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); - - return false; -} - -void ons_TurretSpawn_Delayed() -{SELFPARAM(); - entity e, own = self.owner; - - if(!own) { remove(self); return; } - - if(own.targetname) - { - e = find(world, target, own.targetname); - if(e != world) - { - own.team = e.team; - own.active = ACTIVE_NOT; - - activator = e; - own.use(); - } - } - - remove(self); -} - -MUTATOR_HOOKFUNCTION(ons, TurretSpawn) -{SELFPARAM(); - entity e = spawn(); - e.owner = self; - InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) -{SELFPARAM(); - havocbot_ons_reset_role(self); - return true; -} - -MUTATOR_HOOKFUNCTION(ons, GetTeamCount) -{ - // onslaught is special - for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - { - switch(tmp_entity.team) - { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; - } - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ons, SpectateCopy) -{SELFPARAM(); - self.ons_roundlost = other.ons_roundlost; // make spectators see it too - return false; -} - -MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - - if ( cmd_name == "ons_spawn" ) - { - vector pos = self.origin; - if(cmd_argc > 1) - pos_x = stof(argv(1)); - if(cmd_argc > 2) - pos_y = stof(argv(2)); - if(cmd_argc > 3) - pos_z = stof(argv(3)); - - if ( IS_PLAYER(self) ) - { - if ( !self.frozen ) - { - entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); - - if ( !source_point && self.health > 0 ) - { - sprint(self, "\nYou need to be next to a control point\n"); - return 1; - } - - - entity closest_target = ons_Nearest_ControlPoint_2D(pos, autocvar_g_onslaught_click_radius); - - if ( closest_target == world ) - { - sprint(self, "\nNo control point found\n"); - return 1; - } - - if ( self.health <= 0 ) - { - self.ons_spawn_by = closest_target; - self.respawn_flags = self.respawn_flags | RESPAWN_FORCE; - } - else - { - if ( source_point == closest_target ) - { - sprint(self, "\nTeleporting to the same point\n"); - return 1; - } - - if ( !ons_Teleport(self,closest_target,autocvar_g_onslaught_teleport_radius,true) ) - sprint(self, "\nUnable to teleport there\n"); - } - - return 1; - } - - sprint(self, "\nNo teleportation for you\n"); - } - - return 1; - } - return 0; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE || gameover) { return false; } - - if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle) - { - entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius); - if ( source_point ) - { - stuffcmd(self, "qc_cmd_cl hud clickradar\n"); - return true; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayHitsound) -{ - return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) - || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); -} - -MUTATOR_HOOKFUNCTION(ons, SendWaypoint) -{ - if(wp_sendflags & 16) - { - if(self.owner.classname == "onslaught_controlpoint") - { - entity wp_owner = self.owner; - entity e = WaypointSprite_getviewentity(wp_sendto); - if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } - if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } - } - if(self.owner.classname == "onslaught_generator") - { - entity wp_owner = self.owner; - if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } - if(wp_owner.health <= 0) { wp_flag |= 2; } - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) -{ - if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! - { - ret_float = -3; - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, TurretThink) -{ - // ONS uses somewhat backwards linking. - if(self.target) - { - entity e = find(world, targetname, self.target); - if (e != world) - self.team = e.team; - } - - if(self.team != self.tur_head.team) - turret_respawn(); - - return false; -} - - -// ========== -// Spawnfuncs -// ========== - -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) - Link between control points. - - This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. - -keys: -"target" - first control point. -"target2" - second control point. - */ -spawnfunc(onslaught_link) -{ - if(!g_onslaught) { remove(self); return; } - - if (self.target == "" || self.target2 == "") - objerror("target and target2 must be set\n"); - - self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist - ons_worldlinklist = self; - - InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); - Net_LinkEntity(self, false, 0, ons_Link_Send); -} - -/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) - Control point. Be sure to give this enough clearance so that the shootable part has room to exist - - This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. - -keys: -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. -"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. -"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) - */ - -spawnfunc(onslaught_controlpoint) -{ - if(!g_onslaught) { remove(self); return; } - - ons_ControlPoint_Setup(self); -} - -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. - -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -spawnfunc(onslaught_generator) -{ - if(!g_onslaught) { remove(self); return; } - if(!self.team) { objerror("team must be set"); } - - ons_GeneratorSetup(self); -} - -// scoreboard setup -void ons_ScoreRules() -{ - CheckAllowedTeams(world); - ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true); - ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); - ScoreRules_basics_end(); -} - -void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up -{ - ons_ScoreRules(); - - round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); - round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); -} - -void ons_Initialize() -{ - g_onslaught = true; - ons_captureshield_force = autocvar_g_onslaught_shield_force; - - addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost); - - InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE); -} - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qc b/qcsrc/server/mutators/mutator/gamemode_race.qc index 229242f8b3..4fbafe0353 100644 --- a/qcsrc/server/mutators/mutator/gamemode_race.qc +++ b/qcsrc/server/mutators/mutator/gamemode_race.qc @@ -6,13 +6,13 @@ void race_Initialize(); REGISTER_MUTATOR(rc, false) { - rc_SetLimits(); - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); race_Initialize(); + + rc_SetLimits(); } MUTATOR_ONROLLBACK_OR_REMOVE diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qc b/qcsrc/server/mutators/mutator/gamemode_tdm.qc index 49ec0acc9f..42415a495e 100644 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qc +++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qc @@ -8,16 +8,16 @@ void tdm_DelayedInit(); REGISTER_MUTATOR(tdm, false) { - ActivateTeamplay(); - SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1); - if (autocvar_g_tdm_team_spawns) - have_team_spawns = -1; // request team spawns - MUTATOR_ONADD { if (time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); InitializeEntity(world, tdm_DelayedInit, INITPRIO_GAMETYPE); + + ActivateTeamplay(); + SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, -1, -1); + if (autocvar_g_tdm_team_spawns) + have_team_spawns = -1; // request team spawns } MUTATOR_ONROLLBACK_OR_REMOVE diff --git a/qcsrc/server/mutators/mutator/mutator_bloodloss.qc b/qcsrc/server/mutators/mutator/mutator_bloodloss.qc deleted file mode 100644 index ca37166690..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_bloodloss.qc +++ /dev/null @@ -1,45 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); - -.float bloodloss_timer; - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) -{SELFPARAM(); - if(IS_PLAYER(self)) - if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO) - { - self.BUTTON_CROUCH = true; - - if(time >= self.bloodloss_timer) - { - if(self.vehicle) - vehicles_exit(VHEF_RELEASE); - if(self.event_damage) - self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); - self.bloodloss_timer = time + 0.5 + random() * 0.5; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) -{SELFPARAM(); - if(self.health <= autocvar_g_bloodloss) - return true; - - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":bloodloss"); - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Blood loss"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_breakablehook.qc b/qcsrc/server/mutators/mutator/mutator_breakablehook.qc deleted file mode 100644 index cb9463b867..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_breakablehook.qc +++ /dev/null @@ -1,29 +0,0 @@ -#ifdef IMPLEMENTATION -#include "../../../common/deathtypes/all.qh" -#include "../../g_hook.qh" - -REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); - -bool autocvar_g_breakablehook; // allow toggling mid match? -bool autocvar_g_breakablehook_owner; - -MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) -{ - if(frag_target.classname == "grapplinghook") - { - if((!autocvar_g_breakablehook) - || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) - ) { frag_damage = 0; } - - // hurt the owner of the hook - if(DIFF_TEAM(frag_attacker, frag_target.realowner)) - { - Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); - RemoveGrapplingHook(frag_target.realowner); - return false; // dead - } - } - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_buffs.qc b/qcsrc/server/mutators/mutator/mutator_buffs.qc deleted file mode 100644 index 4f0758fa35..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_buffs.qc +++ /dev/null @@ -1,1000 +0,0 @@ -#ifndef MUTATOR_BUFFS_H -#define MUTATOR_BUFFS_H - -// ammo -.float buff_ammo_prev_infitems; -.int buff_ammo_prev_clipload; -// invisible -.float buff_invisible_prev_alpha; -// flight -.float buff_flight_prev_gravity; -// disability -.float buff_disability_time; -.float buff_disability_effect_time; -// common buff variables -.float buff_effect_delay; - -// buff definitions -.float buff_active; -.float buff_activetime; -.float buff_activetime_updated; -.entity buff_waypoint; -.int oldbuffs; // for updating effects -.entity buff_model; // controls effects (TODO: make csqc) - -const vector BUFF_MIN = ('-16 -16 -20'); -const vector BUFF_MAX = ('16 16 20'); - -// client side options -.float cvar_cl_buffs_autoreplace; -#endif - -#ifdef IMPLEMENTATION - -#include "../../../common/triggers/target/music.qh" -#include "../../../common/gamemodes/all.qh" -#include "../../../common/buffs/all.qh" - -.float buff_time; -void buffs_DelayedInit(); - -REGISTER_MUTATOR(buffs, cvar("g_buffs")) -{ - MUTATOR_ONADD - { - addstat(STAT_BUFFS, AS_INT, buffs); - addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time); - - InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); - } -} - -entity buff_FirstFromFlags(int _buffs) -{ - if (flags) - { - FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); - } - return BUFF_Null; -} - -bool buffs_BuffModel_Customize() -{SELFPARAM(); - entity player, myowner; - bool same_team; - - player = WaypointSprite_getviewentity(other); - myowner = self.owner; - same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); - - if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) - return false; - - if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player)) - return false; - - if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) - { - // somewhat hide the model, but keep the glow - self.effects = 0; - self.alpha = -1; - } - else - { - self.effects = EF_FULLBRIGHT | EF_LOWPRECISION; - self.alpha = 1; - } - return true; -} - -void buffs_BuffModel_Spawn(entity player) -{ - player.buff_model = spawn(); - setmodel(player.buff_model, MDL_BUFF); - setsize(player.buff_model, '0 0 -40', '0 0 40'); - setattachment(player.buff_model, player, ""); - setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); - player.buff_model.owner = player; - player.buff_model.scale = 0.7; - player.buff_model.pflags = PFLAGS_FULLDYNAMIC; - player.buff_model.light_lev = 200; - player.buff_model.customizeentityforclient = buffs_BuffModel_Customize; -} - -vector buff_GlowColor(entity buff) -{ - //if(buff.team) { return Team_ColorRGB(buff.team); } - return buff.m_color; -} - -void buff_Effect(entity player, string eff) -{SELFPARAM(); - if(!autocvar_g_buffs_effects) { return; } - - if(time >= self.buff_effect_delay) - { - Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - self.buff_effect_delay = time + 0.05; // prevent spam - } -} - -// buff item -float buff_Waypoint_visible_for_player(entity plr) -{SELFPARAM(); - if(!self.owner.buff_active && !self.owner.buff_activetime) - return false; - - if (plr.buffs) - { - return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs; - } - - return WaypointSprite_visible_for_player(plr); -} - -void buff_Waypoint_Spawn(entity e) -{ - entity buff = buff_FirstFromFlags(e.buffs); - entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff); - wp.wp_extra = buff.m_id; - WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); - e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; -} - -void buff_SetCooldown(float cd) -{SELFPARAM(); - cd = max(0, cd); - - if(!self.buff_waypoint) - buff_Waypoint_Spawn(self); - - WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd); - self.buff_activetime = cd; - self.buff_active = !cd; -} - -void buff_Respawn(entity ent) -{SELFPARAM(); - if(gameover) { return; } - - vector oldbufforigin = ent.origin; - - if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) - { - entity spot = SelectSpawnPoint(true); - setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300))); - ent.angles = spot.angles; - } - - tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent); - - setorigin(ent, trace_endpos); // attempt to unstick - - ent.movetype = MOVETYPE_TOSS; - - makevectors(ent.angles); - ent.velocity = '0 0 200'; - ent.angles = '0 0 0'; - if(autocvar_g_buffs_random_lifetime > 0) - ent.lifetime = time + autocvar_g_buffs_random_lifetime; - - Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1); - - WaypointSprite_Ping(ent.buff_waypoint); - - sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) -} - -void buff_Touch() -{SELFPARAM(); - if(gameover) { return; } - - if(ITEM_TOUCH_NEEDKILL()) - { - buff_Respawn(self); - return; - } - - if((self.team && DIFF_TEAM(other, self)) - || (other.frozen) - || (other.vehicle) - || (!self.buff_active) - ) - { - // can't touch this - return; - } - - if(MUTATOR_CALLHOOK(BuffTouch, self, other)) - return; - - if(!IS_PLAYER(other)) - return; // incase mutator changed other - - if (other.buffs) - { - if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) - { - int buffid = buff_FirstFromFlags(other.buffs).m_id; - //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); - - other.buffs = 0; - //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - else { return; } // do nothing - } - - self.owner = other; - self.buff_active = false; - self.lifetime = 0; - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); - - Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); - sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); - other.buffs |= (self.buffs); -} - -float buff_Available(entity buff) -{ - if (buff == BUFF_Null) - return false; - if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) - return false; - if (buff == BUFF_VAMPIRE && cvar("g_vampire")) - return false; - return cvar(strcat("g_buffs_", buff.m_name)); -} - -.int buff_seencount; - -void buff_NewType(entity ent, float cb) -{ - RandomSelection_Init(); - FOREACH(Buffs, buff_Available(it), LAMBDA( - it.buff_seencount += 1; - // if it's already been chosen, give it a lower priority - RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); - )); - ent.buffs = RandomSelection_chosen_float; -} - -void buff_Think() -{SELFPARAM(); - if(self.buffs != self.oldbuffs) - { - entity buff = buff_FirstFromFlags(self.buffs); - self.color = buff.m_color; - self.glowmod = buff_GlowColor(buff); - self.skin = buff.m_skin; - - setmodel(self, MDL_BUFF); - - if(self.buff_waypoint) - { - //WaypointSprite_Disown(self.buff_waypoint, 1); - WaypointSprite_Kill(self.buff_waypoint); - buff_Waypoint_Spawn(self); - if(self.buff_activetime) - WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime); - } - - self.oldbuffs = self.buffs; - } - - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - if(!self.buff_activetime_updated) - { - buff_SetCooldown(self.buff_activetime); - self.buff_activetime_updated = true; - } - - if(!self.buff_active && !self.buff_activetime) - if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs)) - { - buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime); - self.owner = world; - if(autocvar_g_buffs_randomize) - buff_NewType(self, self.buffs); - - if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) - buff_Respawn(self); - } - - if(self.buff_activetime) - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - { - self.buff_activetime = max(0, self.buff_activetime - frametime); - - if(!self.buff_activetime) - { - self.buff_active = true; - sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); - Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); - } - } - - if(self.buff_active) - { - if(self.team && !self.buff_waypoint) - buff_Waypoint_Spawn(self); - - if(self.lifetime) - if(time >= self.lifetime) - buff_Respawn(self); - } - - self.nextthink = time; - //self.angles_y = time * 110.1; -} - -void buff_Waypoint_Reset() -{SELFPARAM(); - WaypointSprite_Kill(self.buff_waypoint); - - if(self.buff_activetime) { buff_Waypoint_Spawn(self); } -} - -void buff_Reset() -{SELFPARAM(); - if(autocvar_g_buffs_randomize) - buff_NewType(self, self.buffs); - self.owner = world; - buff_SetCooldown(autocvar_g_buffs_cooldown_activate); - buff_Waypoint_Reset(); - self.buff_activetime_updated = false; - - if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) - buff_Respawn(self); -} - -float buff_Customize() -{SELFPARAM(); - entity player = WaypointSprite_getviewentity(other); - if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) - { - self.alpha = 0.3; - if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } - self.pflags = 0; - } - else - { - self.alpha = 1; - if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } - self.light_lev = 220 + 36 * sin(time); - self.pflags = PFLAGS_FULLDYNAMIC; - } - return true; -} - -void buff_Init(entity ent) -{SELFPARAM(); - if(!cvar("g_buffs")) { remove(ent); return; } - - if(!teamplay && ent.team) { ent.team = 0; } - - entity buff = buff_FirstFromFlags(self.buffs); - - setself(ent); - if(!self.buffs || buff_Available(buff)) - buff_NewType(self, 0); - - self.classname = "item_buff"; - self.solid = SOLID_TRIGGER; - self.flags = FL_ITEM; - self.think = buff_Think; - self.touch = buff_Touch; - self.reset = buff_Reset; - self.nextthink = time + 0.1; - self.gravity = 1; - self.movetype = MOVETYPE_TOSS; - self.scale = 1; - self.skin = buff.m_skin; - self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - self.customizeentityforclient = buff_Customize; - //self.gravity = 100; - self.color = buff.m_color; - self.glowmod = buff_GlowColor(self); - buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime); - self.buff_active = !self.buff_activetime; - self.pflags = PFLAGS_FULLDYNAMIC; - - if(self.spawnflags & 1) - self.noalign = true; - - if(self.noalign) - self.movetype = MOVETYPE_NONE; // reset by random location - - setmodel(self, MDL_BUFF); - setsize(self, BUFF_MIN, BUFF_MAX); - - if(cvar("g_buffs_random_location") || (self.spawnflags & 64)) - buff_Respawn(self); - - setself(this); -} - -void buff_Init_Compat(entity ent, entity replacement) -{ - if (ent.spawnflags & 2) - ent.team = NUM_TEAM_1; - else if (ent.spawnflags & 4) - ent.team = NUM_TEAM_2; - - ent.buffs = replacement.m_itemid; - - buff_Init(ent); -} - -void buff_SpawnReplacement(entity ent, entity old) -{ - setorigin(ent, old.origin); - ent.angles = old.angles; - ent.noalign = (old.noalign || (old.spawnflags & 1)); - - buff_Init(ent); -} - -void buff_Vengeance_DelayedDamage() -{SELFPARAM(); - if(self.enemy) - Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0'); - - remove(self); - return; -} - -float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) -{ - return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) -{ - if(frag_deathtype == DEATH_BUFF.m_id) { return false; } - - if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) - { - vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); - damage_take = v.x; - damage_save = v.y; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) -{ - if(frag_deathtype == DEATH_BUFF.m_id) { return false; } - - if(frag_target.buffs & BUFF_SPEED.m_itemid) - if(frag_target != frag_attacker) - frag_damage *= autocvar_g_buffs_speed_damage_take; - - if(frag_target.buffs & BUFF_MEDIC.m_itemid) - if((frag_target.health - frag_damage) <= 0) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(frag_attacker) - if(random() <= autocvar_g_buffs_medic_survive_chance) - frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); - - if(frag_target.buffs & BUFF_JUMP.m_itemid) - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; - - if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) - if(frag_attacker) - if(frag_attacker != frag_target) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - { - entity dmgent = spawn(); - - dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; - dmgent.enemy = frag_attacker; - dmgent.owner = frag_target; - dmgent.think = buff_Vengeance_DelayedDamage; - dmgent.nextthink = time + 0.1; - } - - if(frag_target.buffs & BUFF_BASH.m_itemid) - if(frag_attacker != frag_target) - if(vlen(frag_force)) - frag_force = '0 0 0'; - - if(frag_attacker.buffs & BUFF_BASH.m_itemid) - if(vlen(frag_force)) - if(frag_attacker == frag_target) - frag_force *= autocvar_g_buffs_bash_force_self; - else - frag_force *= autocvar_g_buffs_bash_force; - - if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) - if(frag_target != frag_attacker) - frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; - - if(frag_attacker.buffs & BUFF_MEDIC.m_itemid) - if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) - if(SAME_TEAM(frag_attacker, frag_target)) - if(frag_attacker != frag_target) - { - frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage); - frag_damage = 0; - } - - if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) - if(frag_target != frag_attacker) { - float time = buff_Inferno_CalculateTime( - frag_damage, - 0, - autocvar_g_buffs_inferno_burntime_min_time, - autocvar_g_buffs_inferno_burntime_target_damage, - autocvar_g_buffs_inferno_burntime_target_time, - autocvar_g_buffs_inferno_burntime_factor - ); - Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id); - } - - // this... is ridiculous (TODO: fix!) - if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) - if(!frag_target.vehicle) - if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(frag_target.deadflag == DEAD_NO) - if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) - if(frag_attacker != frag_target) - if(!frag_target.frozen) - if(frag_target.takedamage) - if(DIFF_TEAM(frag_attacker, frag_target)) - { - frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); - if(frag_target.armorvalue) - frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn) -{SELFPARAM(); - self.buffs = 0; - // reset timers here to prevent them continuing after re-spawn - self.buff_disability_time = 0; - self.buff_disability_effect_time = 0; - return false; -} - -.float stat_sv_maxspeed; -.float stat_sv_airspeedlimit_nonqw; -.float stat_sv_jumpvelocity; - -MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - { - self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; - self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; - } - - if(time < self.buff_disability_time) - { - self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; - self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; - } - - if(self.buffs & BUFF_JUMP.m_itemid) - { - // automatically reset, no need to worry - self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerJump) -{SELFPARAM(); - if(self.buffs & BUFF_JUMP.m_itemid) - player_jumpheight = autocvar_g_buffs_jump_height; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, MonsterMove) -{SELFPARAM(); - if(time < self.buff_disability_time) - { - monster_speed_walk *= autocvar_g_buffs_disability_speed; - monster_speed_run *= autocvar_g_buffs_disability_speed; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDies) -{SELFPARAM(); - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - self.buffs = 0; - - if(self.buff_model) - { - remove(self.buff_model); - self.buff_model = world; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE || gameover) { return false; } - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - - self.buffs = 0; - sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE || gameover) { return false; } - - if(self.buffs & BUFF_SWAPPER.m_itemid) - { - float best_distance = autocvar_g_buffs_swapper_range; - entity closest = world; - entity player; - FOR_EACH_PLAYER(player) - if(DIFF_TEAM(self, player)) - if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle) - if(vlen(self.origin - player.origin) <= best_distance) - { - best_distance = vlen(self.origin - player.origin); - closest = player; - } - - if(closest) - { - vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; - - my_org = self.origin; - my_vel = self.velocity; - my_ang = self.angles; - their_org = closest.origin; - their_vel = closest.velocity; - their_ang = closest.angles; - - Drop_Special_Items(closest); - - MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper - - setorigin(self, their_org); - setorigin(closest, my_org); - - closest.velocity = my_vel; - closest.angles = my_ang; - closest.fixangle = true; - closest.oldorigin = my_org; - closest.oldvelocity = my_vel; - self.velocity = their_vel; - self.angles = their_ang; - self.fixangle = true; - self.oldorigin = their_org; - self.oldvelocity = their_vel; - - // set pusher so self gets the kill if they fall into void - closest.pusher = self; - closest.pushltime = time + autocvar_g_maxpushtime; - closest.istypefrag = closest.BUTTON_CHAT; - - Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); - - sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - - // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam - self.buffs = 0; - return true; - } - } - return false; -} - -bool buffs_RemovePlayer(entity player) -{ - if(player.buff_model) - { - remove(player.buff_model); - player.buff_model = world; - } - - // also reset timers here to prevent them continuing after spectating - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; - - return false; -} -MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); } -MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); } - -MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) -{SELFPARAM(); - entity e = WaypointSprite_getviewentity(other); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other)) - if(DIFF_TEAM(self.owner, e)) - return true; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) -{SELFPARAM(); - if(autocvar_g_buffs_replace_powerups) - switch(self.classname) - { - case "item_strength": - case "item_invincible": - { - entity e = spawn(); - buff_SpawnReplacement(e, self); - return true; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - weapon_rate *= autocvar_g_buffs_speed_rate; - - if(time < self.buff_disability_time) - weapon_rate *= autocvar_g_buffs_disability_rate; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - ret_float *= autocvar_g_buffs_speed_weaponspeed; - - if(time < self.buff_disability_time) - ret_float *= autocvar_g_buffs_disability_weaponspeed; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) -{SELFPARAM(); - if(gameover || self.deadflag != DEAD_NO) { return false; } - - if(time < self.buff_disability_time) - if(time >= self.buff_disability_effect_time) - { - Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1); - self.buff_disability_effect_time = time + 0.5; - } - - // handle buff lost status - // 1: notify everyone else - // 2: notify carrier as well - int buff_lost = 0; - - if(self.buff_time) - if(time >= self.buff_time) - buff_lost = 2; - - if(self.frozen) { buff_lost = 1; } - - if(buff_lost) - { - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - if(buff_lost >= 2) - { - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? - sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - self.buffs = 0; - } - } - - if(self.buffs & BUFF_MAGNET.m_itemid) - { - vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; - for(other = world; (other = findflags(other, flags, FL_ITEM)); ) - if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax)) - { - setself(other); - other = this; - if(self.touch) - self.touch(); - other = self; - setself(this); - } - } - - if(self.buffs & BUFF_AMMO.m_itemid) - if(self.clip_size) - self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; - - if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid)) - if(self.alpha != autocvar_g_buffs_invisible_alpha) - self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) - -#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid)) -#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid)) - - if(self.buffs != self.oldbuffs) - { - entity buff = buff_FirstFromFlags(self.buffs); - float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; - self.buff_time = (bufftime) ? time + bufftime : 0; - - BUFF_ONADD(BUFF_AMMO) - { - self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); - self.items |= IT_UNLIMITED_WEAPON_AMMO; - - if(self.clip_load) - self.buff_ammo_prev_clipload = self.clip_load; - self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; - } - - BUFF_ONREM(BUFF_AMMO) - { - if(self.buff_ammo_prev_infitems) - self.items |= IT_UNLIMITED_WEAPON_AMMO; - else - self.items &= ~IT_UNLIMITED_WEAPON_AMMO; - - if(self.buff_ammo_prev_clipload) - self.clip_load = self.buff_ammo_prev_clipload; - } - - BUFF_ONADD(BUFF_INVISIBLE) - { - if(time < self.strength_finished && g_instagib) - self.alpha = autocvar_g_instagib_invis_alpha; - else - self.alpha = self.buff_invisible_prev_alpha; - self.alpha = autocvar_g_buffs_invisible_alpha; - } - - BUFF_ONREM(BUFF_INVISIBLE) - self.alpha = self.buff_invisible_prev_alpha; - - BUFF_ONADD(BUFF_FLIGHT) - { - self.buff_flight_prev_gravity = self.gravity; - self.gravity = autocvar_g_buffs_flight_gravity; - } - - BUFF_ONREM(BUFF_FLIGHT) - self.gravity = self.buff_flight_prev_gravity; - - self.oldbuffs = self.buffs; - if(self.buffs) - { - if(!self.buff_model) - buffs_BuffModel_Spawn(self); - - self.buff_model.color = buff.m_color; - self.buff_model.glowmod = buff_GlowColor(self.buff_model); - self.buff_model.skin = buff.m_skin; - - self.effects |= EF_NOSHADOW; - } - else - { - remove(self.buff_model); - self.buff_model = world; - - self.effects &= ~(EF_NOSHADOW); - } - } - - if(self.buff_model) - { - self.buff_model.effects = self.effects; - self.buff_model.effects |= EF_LOWPRECISION; - self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance - - self.buff_model.alpha = self.alpha; - } - -#undef BUFF_ONADD -#undef BUFF_ONREM - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) -{SELFPARAM(); - self.buffs = other.buffs; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) -{ - vh_vehicle.buffs = vh_player.buffs; - vh_player.buffs = 0; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleExit) -{ - vh_player.buffs = vh_vehicle.buffs; - vh_vehicle.buffs = 0; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) -{SELFPARAM(); - if(self.buffs & BUFF_MEDIC.m_itemid) - { - regen_mod_rot = autocvar_g_buffs_medic_rot; - regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max; - regen_mod_regen = autocvar_g_buffs_medic_regen; - } - - if(self.buffs & BUFF_SPEED.m_itemid) - regen_mod_regen = autocvar_g_buffs_speed_regen; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace"); - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Buffs"); - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Buffs"); - return false; -} - -void buffs_DelayedInit() -{ - if(autocvar_g_buffs_spawn_count > 0) - if(find(world, classname, "item_buff") == world) - { - float i; - for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) - { - entity e = spawn(); - e.spawnflags |= 64; // always randomize - e.velocity = randomvec() * 250; // this gets reset anyway if random location works - buff_Init(e); - } - } -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_campcheck.qc b/qcsrc/server/mutators/mutator/mutator_campcheck.qc deleted file mode 100644 index be5bfceb81..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_campcheck.qc +++ /dev/null @@ -1,86 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); - -.float campcheck_nextcheck; -.float campcheck_traveled_distance; - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) -{SELFPARAM(); - Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK); - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) -{ - if(IS_PLAYER(frag_target)) - if(IS_PLAYER(frag_attacker)) - if(frag_attacker != frag_target) - { - frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; - frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) -{SELFPARAM(); - if(!gameover) - if(!warmup_stage) // don't consider it camping during warmup? - if(time >= game_starttime) - if(IS_PLAYER(self)) - if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them - if(self.deadflag == DEAD_NO) - if(!self.frozen) - if(!self.BUTTON_CHAT) - if(autocvar_g_campcheck_interval) - { - vector dist; - - // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) - dist = self.prevorigin - self.origin; - dist.z = 0; - self.campcheck_traveled_distance += fabs(vlen(dist)); - - if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - { - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - self.campcheck_traveled_distance = 0; - } - - if(time > self.campcheck_nextcheck) - { - if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance) - { - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK); - if(self.vehicle) - Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0'); - else - Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0'); - } - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; - self.campcheck_traveled_distance = 0; - } - - return false; - } - - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) -{SELFPARAM(); - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - self.campcheck_traveled_distance = 0; - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":CampCheck"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_dodging.qc b/qcsrc/server/mutators/mutator/mutator_dodging.qc deleted file mode 100644 index 85b9fea619..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_dodging.qc +++ /dev/null @@ -1,323 +0,0 @@ -#ifdef IMPLEMENTATION - -#ifdef CSQC - #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) - #define PHYS_DODGING getstati(STAT_DODGING) - #define PHYS_DODGING_DELAY getstatf(STAT_DODGING_DELAY) - #define PHYS_DODGING_TIMEOUT(s) getstatf(STAT_DODGING_TIMEOUT) - #define PHYS_DODGING_HORIZ_SPEED_FROZEN getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN) - #define PHYS_DODGING_FROZEN_NODOUBLETAP getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP) - #define PHYS_DODGING_HORIZ_SPEED getstatf(STAT_DODGING_HORIZ_SPEED) - #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys - #define PHYS_DODGING_HEIGHT_THRESHOLD getstatf(STAT_DODGING_HEIGHT_THRESHOLD) - #define PHYS_DODGING_DISTANCE_THRESHOLD getstatf(STAT_DODGING_DISTANCE_THRESHOLD) - #define PHYS_DODGING_RAMP_TIME getstatf(STAT_DODGING_RAMP_TIME) - #define PHYS_DODGING_UP_SPEED getstatf(STAT_DODGING_UP_SPEED) - #define PHYS_DODGING_WALL getstatf(STAT_DODGING_WALL) -#elif defined(SVQC) - #define PHYS_DODGING_FRAMETIME sys_frametime - #define PHYS_DODGING g_dodging - #define PHYS_DODGING_DELAY autocvar_sv_dodging_delay - #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout - #define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen - #define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap - #define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed - #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys - #define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold - #define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold - #define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time - #define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed - #define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging -#endif - -#ifdef SVQC - -float g_dodging; - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - -#include "../../../common/animdecide.qh" -#include "../../../common/physics.qh" - -.float cvar_cl_dodging_timeout; - -.float stat_dodging; -.float stat_dodging_delay; -.float stat_dodging_horiz_speed_frozen; -.float stat_dodging_frozen_nodoubletap; -.float stat_dodging_frozen; -.float stat_dodging_horiz_speed; -.float stat_dodging_height_threshold; -.float stat_dodging_distance_threshold; -.float stat_dodging_ramp_time; -.float stat_dodging_up_speed; -.float stat_dodging_wall; - -REGISTER_MUTATOR(dodging, cvar("g_dodging")) -{ - // this just turns on the cvar. - MUTATOR_ONADD - { - g_dodging = cvar("g_dodging"); - addstat(STAT_DODGING, AS_INT, stat_dodging); - addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay); - addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos) - addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap); - addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen); - addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen); - addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed); - addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold); - addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold); - addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time); - addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed); - addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall); - } - - // this just turns off the cvar. - MUTATOR_ONROLLBACK_OR_REMOVE - { - g_dodging = 0; - } - - return false; -} - -#endif - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - - -// these are used to store the last key press time for each of the keys.. -.float last_FORWARD_KEY_time; -.float last_BACKWARD_KEY_time; -.float last_LEFT_KEY_time; -.float last_RIGHT_KEY_time; - -// these store the movement direction at the time of the dodge action happening. -.vector dodging_direction; - -// this indicates the last time a dodge was executed. used to check if another one is allowed -// and to ramp up the dodge acceleration in the physics hook. -.float last_dodging_time; - -// This is the velocity gain to be added over the ramp time. -// It will decrease from frame to frame during dodging_action = 1 -// until it's 0. -.float dodging_velocity_gain; - -#ifdef CSQC -.int pressedkeys; - -#elif defined(SVQC) - -void dodging_UpdateStats() -{SELFPARAM(); - self.stat_dodging = PHYS_DODGING; - self.stat_dodging_delay = PHYS_DODGING_DELAY; - self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN; - self.stat_dodging_frozen = PHYS_DODGING_FROZEN; - self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP; - self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD; - self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD; - self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME; - self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED; - self.stat_dodging_wall = PHYS_DODGING_WALL; -} - -#endif - -// returns 1 if the player is close to a wall -bool check_close_to_wall(float threshold) -{SELFPARAM(); - if (PHYS_DODGING_WALL == 0) { return false; } - - #define X(OFFSET) \ - tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \ - if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \ - return true; - X(1000*v_right); - X(-1000*v_right); - X(1000*v_forward); - X(-1000*v_forward); - #undef X - - return false; -} - -bool check_close_to_ground(float threshold) -{SELFPARAM(); - return IS_ONGROUND(self) ? true : false; -} - -float PM_dodging_checkpressedkeys() -{SELFPARAM(); - if(!PHYS_DODGING) - return false; - - float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN); - float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); - - // first check if the last dodge is far enough back in time so we can dodge again - if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY) - return false; - - makevectors(self.angles); - - if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1 - && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1) - return true; - - float tap_direction_x = 0; - float tap_direction_y = 0; - float dodge_detected = 0; - - #define X(COND,BTN,RESULT) \ - if (self.movement_##COND) \ - /* is this a state change? */ \ - if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \ - tap_direction_##RESULT; \ - if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \ - dodge_detected = 1; \ - self.last_##BTN##_KEY_time = time; \ - } - X(x < 0, BACKWARD, x--); - X(x > 0, FORWARD, x++); - X(y < 0, LEFT, y--); - X(y > 0, RIGHT, y++); - #undef X - - if (dodge_detected == 1) - { - self.last_dodging_time = time; - - self.dodging_action = 1; - self.dodging_single_action = 1; - - self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; - - self.dodging_direction_x = tap_direction_x; - self.dodging_direction_y = tap_direction_y; - - // normalize the dodging_direction vector.. (unlike UT99) XD - float length = self.dodging_direction_x * self.dodging_direction_x - + self.dodging_direction_y * self.dodging_direction_y; - length = sqrt(length); - - self.dodging_direction_x = self.dodging_direction_x * 1.0 / length; - self.dodging_direction_y = self.dodging_direction_y * 1.0 / length; - return true; - } - return false; -} - -void PM_dodging() -{SELFPARAM(); - if (!PHYS_DODGING) - return; - -#ifdef SVQC - dodging_UpdateStats(); -#endif - - if (PHYS_DEAD(self)) - return; - - // when swimming, no dodging allowed.. - if (self.waterlevel >= WATERLEVEL_SWIMMING) - { - self.dodging_action = 0; - self.dodging_direction_x = 0; - self.dodging_direction_y = 0; - return; - } - - // make sure v_up, v_right and v_forward are sane - makevectors(self.angles); - - // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code - // will be called ramp_time/frametime times = 2 times. so, we need to - // add 0.5 * the total speed each frame until the dodge action is done.. - float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; - - // if ramp time is smaller than frametime we get problems ;D - common_factor = min(common_factor, 1); - - float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; - float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed); - new_velocity_gain = max(0, new_velocity_gain); - - float velocity_difference = self.dodging_velocity_gain - new_velocity_gain; - - // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D - if (self.dodging_action == 1) - { - //disable jump key during dodge accel phase - if(self.movement_z > 0) { self.movement_z = 0; } - - self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right) - + ((self.dodging_direction_x * velocity_difference) * v_forward); - - self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference; - } - - // the up part of the dodge is a single shot action - if (self.dodging_single_action == 1) - { - UNSET_ONGROUND(self); - - self.velocity += PHYS_DODGING_UP_SPEED * v_up; - -#ifdef SVQC - if (autocvar_sv_dodging_sound) - PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); - - animdecide_setaction(self, ANIMACTION_JUMP, true); -#endif - - self.dodging_single_action = 0; - } - - // are we done with the dodging ramp yet? - if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) - { - // reset state so next dodge can be done correctly - self.dodging_action = 0; - self.dodging_direction_x = 0; - self.dodging_direction_y = 0; - } -} - -#ifdef SVQC - -MUTATOR_HOOKFUNCTION(dodging, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout"); - return false; -} - -MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) -{ - // print("dodging_PlayerPhysics\n"); - PM_dodging(); - - return false; -} - -MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) -{ - PM_dodging_checkpressedkeys(); - - return false; -} - -#endif - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_hook.qc b/qcsrc/server/mutators/mutator/mutator_hook.qc deleted file mode 100644 index b298e7b2ea..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_hook.qc +++ /dev/null @@ -1,42 +0,0 @@ -#ifdef IMPLEMENTATION -AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); -#ifdef SVQC -REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { - MUTATOR_ONADD { - g_grappling_hook = true; - WEP_HOOK.ammo_factor = 0; - } - MUTATOR_ONROLLBACK_OR_REMOVE { - g_grappling_hook = false; - WEP_HOOK.ammo_factor = 1; - } -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":grappling_hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) -{ - ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); -} - -MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) -{ - SELFPARAM(); - self.offhand = OFFHAND_HOOK; -} - -MUTATOR_HOOKFUNCTION(hook, FilterItem) -{ - return self.weapon == WEP_HOOK.m_id; -} - -#endif -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc b/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc deleted file mode 100644 index 5a781a8812..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) -{ - if(other.health) - { - // disable health which in effect disables damage calculations - other.health = 0; - } - return 0; -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":InvincibleProjectiles"); - return 0; -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Invincible Projectiles"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_melee_only.qc b/qcsrc/server/mutators/mutator/mutator_melee_only.qc deleted file mode 100644 index 5b03f46ec1..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_melee_only.qc +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); - -MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) -{ - start_ammo_shells = warmup_start_ammo_shells = 0; - start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); - - return false; -} - -MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, FilterItem) -{SELFPARAM(); - switch (self.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_ArmorSmall.m_itemid: - return false; - } - - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":MeleeOnly"); - return false; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Melee Only Arena"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_midair.qc b/qcsrc/server/mutators/mutator/mutator_midair.qc deleted file mode 100644 index bf34283344..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_midair.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(midair, cvar("g_midair")); - -.float midair_shieldtime; - -MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) -{SELFPARAM(); - if(IS_PLAYER(frag_attacker)) - if(IS_PLAYER(frag_target)) - if(time < self.midair_shieldtime) - frag_damage = false; - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) -{SELFPARAM(); - if(time >= game_starttime) - if(self.flags & FL_ONGROUND) - { - self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); - self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) -{SELFPARAM(); - if(IS_BOT_CLIENT(self)) - self.bot_moveskill = 0; // disable bunnyhopping - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":midair"); - return false; -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Midair"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_multijump.qc b/qcsrc/server/mutators/mutator/mutator_multijump.qc deleted file mode 100644 index f01a801aa7..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_multijump.qc +++ /dev/null @@ -1,182 +0,0 @@ -#ifdef IMPLEMENTATION -#ifdef SVQC - #include "../../antilag.qh" -#endif -#include "../../../common/physics.qh" - -.int multijump_count; -.bool multijump_ready; -.bool cvar_cl_multijump; - -#ifdef CSQC - -#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP) -#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED) -#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD) -#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED) -#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING) - -#elif defined(SVQC) - -#define PHYS_MULTIJUMP autocvar_g_multijump -#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed -#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add -#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed -#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging - - -.float stat_multijump; -.float stat_multijump_speed; -.float stat_multijump_add; -.float stat_multijump_maxspeed; -.float stat_multijump_dodging; - -void multijump_UpdateStats() -{SELFPARAM(); - self.stat_multijump = PHYS_MULTIJUMP; - self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED; - self.stat_multijump_add = PHYS_MULTIJUMP_ADD; - self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED; - self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING; -} - -void multijump_AddStats() -{ - addstat(STAT_MULTIJUMP, AS_INT, stat_multijump); - addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed); - addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add); - addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed); - addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging); -} - -#endif - -void PM_multijump() -{SELFPARAM(); - if(!PHYS_MULTIJUMP) { return; } - - if(IS_ONGROUND(self)) - { - self.multijump_count = 0; - } -} - -bool PM_multijump_checkjump() -{SELFPARAM(); - if(!PHYS_MULTIJUMP) { return false; } - -#ifdef SVQC - bool client_multijump = self.cvar_cl_multijump; -#elif defined(CSQC) - bool client_multijump = cvar("cl_multijump"); - - if(cvar("cl_multijump") > 1) - return false; // nope -#endif - - if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair - self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again - else - self.multijump_ready = false; - - int phys_multijump = PHYS_MULTIJUMP; - -#ifdef CSQC - phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0; -#endif - - if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED)) - { - if (PHYS_MULTIJUMP) - { - if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity - { - if (self.velocity_z < PHYS_JUMPVELOCITY) - { - player_multijump = true; - self.velocity_z = 0; - } - } - else - player_multijump = true; - - if(player_multijump) - { - if(PHYS_MULTIJUMP_DODGING) - if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys - { - float curspeed; - vector wishvel, wishdir; - -/*#ifdef SVQC - curspeed = max( - vlen(vec2(self.velocity)), // current xy speed - vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs - ); -#elif defined(CSQC)*/ - curspeed = vlen(vec2(self.velocity)); -//#endif - - makevectors(self.v_angle_y * '0 1 0'); - wishvel = v_forward * self.movement_x + v_right * self.movement_y; - wishdir = normalize(wishvel); - - self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump - self.velocity_y = wishdir_y * curspeed; - // keep velocity_z unchanged! - } - if (PHYS_MULTIJUMP > 0) - { - self.multijump_count += 1; - } - } - } - self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump - } - - return false; -} - -#ifdef SVQC -REGISTER_MUTATOR(multijump, cvar("g_multijump")) -{ - MUTATOR_ONADD - { - multijump_AddStats(); - } - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics) -{ - multijump_UpdateStats(); - PM_multijump(); - - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, PlayerJump) -{ - return PM_multijump_checkjump(); -} - -MUTATOR_HOOKFUNCTION(multijump, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump"); - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":multijump"); - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Multi jump"); - return false; -} - -#endif -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_nades.qc b/qcsrc/server/mutators/mutator/mutator_nades.qc deleted file mode 100644 index d9fd1c8b6f..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_nades.qc +++ /dev/null @@ -1,1233 +0,0 @@ -#ifndef MUTATOR_NADES_H -#define MUTATOR_NADES_H - -.entity nade; -.entity fake_nade; -.float nade_timer; -.float nade_refire; -.float bonus_nades; -.float nade_special_time; -.float bonus_nade_score; -.float nade_type; -.string pokenade_type; -.entity nade_damage_target; -.float cvar_cl_nade_type; -.string cvar_cl_pokenade_type; -.float toss_time; -.float stat_healing_orb; -.float stat_healing_orb_alpha; -.float nade_show_particles; - -// Remove nades that are being thrown -void(entity player) nades_Clear; - -// Give a bonus grenade to a player -void(entity player, float score) nades_GiveBonus; -// Remove all bonus nades from a player -void(entity player) nades_RemoveBonus; - -#endif -#ifdef IMPLEMENTATION - -#include "../../../common/nades/all.qh" -#include "../../../common/gamemodes/all.qh" -#include "../../../common/monsters/spawn.qh" -#include "../../../common/monsters/sv_monsters.qh" -#include "../../g_subs.qh" - -REGISTER_MUTATOR(nades, cvar("g_nades")) -{ - MUTATOR_ONADD - { - addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer); - addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades); - addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type); - addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score); - addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb); - addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha); - } - - return false; -} - -.float nade_time_primed; - -.entity nade_spawnloc; - -void nade_timer_think() -{SELFPARAM(); - self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10); - self.nextthink = time; - if(!self.owner || wasfreed(self.owner)) - remove(self); -} - -void nade_burn_spawn(entity _nade) -{ - CSQCProjectile(_nade, true, Nades[_nade.nade_type].m_projectile[true], true); -} - -void nade_spawn(entity _nade) -{ - entity timer = spawn(); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, _nade, ""); - timer.classname = "nade_timer"; - timer.colormap = _nade.colormap; - timer.glowmod = _nade.glowmod; - timer.think = nade_timer_think; - timer.nextthink = time; - timer.wait = _nade.wait; - timer.owner = _nade; - timer.skin = 10; - - _nade.effects |= EF_LOWPRECISION; - - CSQCProjectile(_nade, true, Nades[_nade.nade_type].m_projectile[false], true); -} - -void napalm_damage(float dist, float damage, float edgedamage, float burntime) -{SELFPARAM(); - entity e; - float d; - vector p; - - if ( damage < 0 ) - return; - - RandomSelection_Init(); - for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain) - if(e.takedamage == DAMAGE_AIM) - if(self.realowner != e || autocvar_g_nades_napalm_selfdamage) - if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) - if(!e.frozen) - { - p = e.origin; - p.x += e.mins.x + random() * (e.maxs.x - e.mins.x); - p.y += e.mins.y + random() * (e.maxs.y - e.mins.y); - p.z += e.mins.z + random() * (e.maxs.z - e.mins.z); - d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); - if(d < dist) - { - e.fireball_impactvec = p; - RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); - } - } - if(RandomSelection_chosen_ent) - { - d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); - d = damage + (edgedamage - damage) * (d / dist); - Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); - //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec); - Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); - } -} - - -void napalm_ball_think() -{SELFPARAM(); - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time > self.pushltime) - { - remove(self); - return; - } - - vector midpoint = ((self.absmin + self.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - self.velocity = self.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { self.velocity_z = 200; } - } - - self.angles = vectoangles(self.velocity); - - napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage, - autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime); - - self.nextthink = time + 0.1; -} - - -void nade_napalm_ball() -{SELFPARAM(); - entity proj; - vector kick; - - spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM); - - proj = spawn (); - proj.owner = self.owner; - proj.realowner = self.realowner; - proj.team = self.owner.team; - proj.classname = "grenade"; - proj.bot_dodge = true; - proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; - proj.movetype = MOVETYPE_BOUNCE; - proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - PROJECTILE_MAKETRIGGER(proj); - setmodel(proj, MDL_Null); - proj.scale = 1;//0.5; - setsize(proj, '-4 -4 -4', '4 4 4'); - setorigin(proj, self.origin); - proj.think = napalm_ball_think; - proj.nextthink = time; - proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale; - proj.effects = EF_LOWPRECISION | EF_FLAME; - - kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread; - proj.velocity = kick; - - proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime; - - proj.angles = vectoangles(proj.velocity); - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; - - //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); -} - - -void napalm_fountain_think() -{SELFPARAM(); - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time >= self.ltime) - { - remove(self); - return; - } - - vector midpoint = ((self.absmin + self.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - self.velocity = self.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { self.velocity_z = 200; } - - UpdateCSQCProjectile(self); - } - - napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage, - autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime); - - self.nextthink = time + 0.1; - if(time >= self.nade_special_time) - { - self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay; - nade_napalm_ball(); - } -} - -void nade_napalm_boom() -{SELFPARAM(); - entity fountain; - int c; - for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++) - nade_napalm_ball(); - - - fountain = spawn(); - fountain.owner = self.owner; - fountain.realowner = self.realowner; - fountain.origin = self.origin; - setorigin(fountain, fountain.origin); - fountain.think = napalm_fountain_think; - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; - fountain.pushltime = fountain.ltime; - fountain.team = self.team; - fountain.movetype = MOVETYPE_TOSS; - fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - fountain.bot_dodge = true; - fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; - fountain.nade_special_time = time; - setsize(fountain, '-16 -16 -16', '16 16 16'); - CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true); -} - -void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time) -{ - frost_target.frozen_by = freezefield.realowner; - Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); - Freeze(frost_target, 1/freeze_time, 3, false); - - Drop_Special_Items(frost_target); -} - -void nade_ice_think() -{SELFPARAM(); - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time >= self.ltime) - { - if ( autocvar_g_nades_ice_explode ) - { - entity expef = EFFECT_NADE_EXPLODE(self.realowner.team); - Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1); - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); - Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); - } - remove(self); - return; - } - - - self.nextthink = time+0.1; - - // gaussian - float randomr; - randomr = random(); - randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius; - float randomw; - randomw = random()*M_PI*2; - vector randomp; - randomp.x = randomr*cos(randomw); - randomp.y = randomr*sin(randomw); - randomp.z = 1; - Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1); - - if(time >= self.nade_special_time) - { - self.nade_special_time = time+0.7; - - Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1); - Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1); - } - - - float current_freeze_time = self.ltime - time - 0.1; - - entity e; - for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain) - if(e != self) - if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner)) - if(e.takedamage && e.deadflag == DEAD_NO) - if(e.health > 0) - if(!e.revival_time || ((time - e.revival_time) >= 1.5)) - if(!e.frozen) - if(current_freeze_time > 0) - nade_ice_freeze(self, e, current_freeze_time); -} - -void nade_ice_boom() -{SELFPARAM(); - entity fountain; - fountain = spawn(); - fountain.owner = self.owner; - fountain.realowner = self.realowner; - fountain.origin = self.origin; - setorigin(fountain, fountain.origin); - fountain.think = nade_ice_think; - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_ice_freeze_time; - fountain.pushltime = fountain.wait = fountain.ltime; - fountain.team = self.team; - fountain.movetype = MOVETYPE_TOSS; - fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; - fountain.bot_dodge = false; - setsize(fountain, '-16 -16 -16', '16 16 16'); - fountain.nade_special_time = time+0.3; - fountain.angles = self.angles; - - if ( autocvar_g_nades_ice_explode ) - { - setmodel(fountain, MDL_PROJECTILE_GRENADE); - entity timer = spawn(); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, fountain, ""); - timer.classname = "nade_timer"; - timer.colormap = self.colormap; - timer.glowmod = self.glowmod; - timer.think = nade_timer_think; - timer.nextthink = time; - timer.wait = fountain.ltime; - timer.owner = fountain; - timer.skin = 10; - } - else - setmodel(fountain, MDL_Null); -} - -void nade_translocate_boom() -{SELFPARAM(); - if(self.realowner.vehicle) - return; - - vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24); - tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner); - locout = trace_endpos; - - makevectors(self.realowner.angles); - - MUTATOR_CALLHOOK(PortalTeleport, self.realowner); - - TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); -} - -void nade_spawn_boom() -{SELFPARAM(); - entity spawnloc = spawn(); - setorigin(spawnloc, self.origin); - setsize(spawnloc, self.realowner.mins, self.realowner.maxs); - spawnloc.movetype = MOVETYPE_NONE; - spawnloc.solid = SOLID_NOT; - spawnloc.drawonlytoclient = self.realowner; - spawnloc.effects = EF_STARDUST; - spawnloc.cnt = autocvar_g_nades_spawn_count; - - if(self.realowner.nade_spawnloc) - { - remove(self.realowner.nade_spawnloc); - self.realowner.nade_spawnloc = world; - } - - self.realowner.nade_spawnloc = spawnloc; -} - -void nade_heal_think() -{SELFPARAM(); - if(time >= self.ltime) - { - remove(self); - return; - } - - self.nextthink = time; - - if(time >= self.nade_special_time) - { - self.nade_special_time = time+0.25; - self.nade_show_particles = 1; - } - else - self.nade_show_particles = 0; -} - -void nade_heal_touch() -{SELFPARAM(); - float maxhealth; - float health_factor; - if(IS_PLAYER(other) || IS_MONSTER(other)) - if(other.deadflag == DEAD_NO) - if(!other.frozen) - { - health_factor = autocvar_g_nades_heal_rate*frametime/2; - if ( other != self.realowner ) - { - if ( SAME_TEAM(other,self) ) - health_factor *= autocvar_g_nades_heal_friend; - else - health_factor *= autocvar_g_nades_heal_foe; - } - if ( health_factor > 0 ) - { - maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max; - if ( other.health < maxhealth ) - { - if ( self.nade_show_particles ) - Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); - other.health = min(other.health+health_factor, maxhealth); - } - other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - } - else if ( health_factor < 0 ) - { - Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0'); - } - - } - - if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) - { - entity show_red = (IS_VEHICLE(other)) ? other.owner : other; - show_red.stat_healing_orb = time+0.1; - show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime; - } -} - -void nade_heal_boom() -{SELFPARAM(); - entity healer; - healer = spawn(); - healer.owner = self.owner; - healer.realowner = self.realowner; - setorigin(healer, self.origin); - healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar - healer.ltime = time + healer.healer_lifetime; - healer.team = self.realowner.team; - healer.bot_dodge = false; - healer.solid = SOLID_TRIGGER; - healer.touch = nade_heal_touch; - - setmodel(healer, MDL_NADE_HEAL); - healer.healer_radius = autocvar_g_nades_nade_radius; - vector size = '1 1 1' * healer.healer_radius / 2; - setsize(healer,-size,size); - - Net_LinkEntity(healer, true, 0, healer_send); - - healer.think = nade_heal_think; - healer.nextthink = time; - healer.SendFlags |= 1; -} - -void nade_monster_boom() -{SELFPARAM(); - entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1); - - if(autocvar_g_nades_pokenade_monster_lifetime > 0) - e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; - e.monster_skill = MONSTER_SKILL_INSANE; -} - -void nade_boom() -{SELFPARAM(); - entity expef = NULL; - bool nade_blast = true; - - switch ( Nades[self.nade_type] ) - { - case NADE_TYPE_NAPALM: - nade_blast = autocvar_g_nades_napalm_blast; - expef = EFFECT_EXPLOSION_MEDIUM; - break; - case NADE_TYPE_ICE: - nade_blast = false; - expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact - break; - case NADE_TYPE_TRANSLOCATE: - nade_blast = false; - break; - case NADE_TYPE_MONSTER: - case NADE_TYPE_SPAWN: - nade_blast = false; - switch(self.realowner.team) - { - case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break; - case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break; - case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break; - case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break; - default: expef = EFFECT_SPAWN_NEUTRAL; break; - } - break; - case NADE_TYPE_HEAL: - nade_blast = false; - expef = EFFECT_SPAWN_RED; - break; - - default: - case NADE_TYPE_NORMAL: - expef = EFFECT_NADE_EXPLODE(self.realowner.team); - break; - } - - if(expef) - Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1); - - sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - self.event_damage = func_null; // prevent somehow calling damage in the next call - - if(nade_blast) - { - RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); - Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); - } - - if(self.takedamage) - switch ( Nades[self.nade_type] ) - { - case NADE_TYPE_NAPALM: nade_napalm_boom(); break; - case NADE_TYPE_ICE: nade_ice_boom(); break; - case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break; - case NADE_TYPE_SPAWN: nade_spawn_boom(); break; - case NADE_TYPE_HEAL: nade_heal_boom(); break; - case NADE_TYPE_MONSTER: nade_monster_boom(); break; - } - - entity head; - for(head = world; (head = find(head, classname, "grapplinghook")); ) - if(head.aiment == self) - RemoveGrapplingHook(head.realowner); - - remove(self); -} - -void nade_touch() -{SELFPARAM(); - /*float is_weapclip = 0; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) - if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) - is_weapclip = 1;*/ - if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) - { - entity head; - for(head = world; (head = find(head, classname, "grapplinghook")); ) - if(head.aiment == self) - RemoveGrapplingHook(head.realowner); - remove(self); - return; - } - - PROJECTILE_TOUCH; - - //setsize(self, '-2 -2 -2', '2 2 2'); - //UpdateCSQCProjectile(self); - if(self.health == self.max_health) - { - spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM); - return; - } - - self.enemy = other; - nade_boom(); -} - -void nade_beep() -{SELFPARAM(); - sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - self.think = nade_boom; - self.nextthink = max(self.wait, time); -} - -void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - self.takedamage = DAMAGE_NO; - nade_boom(); - return; - } - - if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id) - return; - - if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) - { - force *= 1.5; - damage = 0; - } - - if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY)) - { - force *= 0.5; // too much - frag_damage = 0; - } - - if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) - { - force *= 6; - damage = self.max_health * 0.55; - } - - if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN) || DEATH_ISWEAPON(deathtype, WEP_HMG)) - damage = self.max_health * 0.1; - - if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO - if(deathtype & HITTYPE_SECONDARY) - { - damage = self.max_health * 0.1; - force *= 10; - } - else - damage = self.max_health * 1.15; - - self.velocity += force; - UpdateCSQCProjectile(self); - - if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker))) - return; - - if(self.health == self.max_health) - { - sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time); - self.think = nade_beep; - } - - self.health -= damage; - - if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) ) - self.realowner = attacker; - - if(self.health <= 0) - W_PrepareExplosionByDamage(attacker, nade_boom); - else - nade_burn_spawn(self); -} - -void toss_nade(entity e, vector _velocity, float _time) -{SELFPARAM(); - if(e.nade == world) - return; - - entity _nade = e.nade; - e.nade = world; - - remove(e.fake_nade); - e.fake_nade = world; - - makevectors(e.v_angle); - - W_SetupShot(e, false, false, "", CH_WEAPON_A, 0); - - Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES); - - vector offset = (v_forward * autocvar_g_nades_throw_offset.x) - + (v_right * autocvar_g_nades_throw_offset.y) - + (v_up * autocvar_g_nades_throw_offset.z); - if(autocvar_g_nades_throw_offset == '0 0 0') - offset = '0 0 0'; - - setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1); - //setmodel(_nade, MDL_PROJECTILE_NADE); - //setattachment(_nade, world, ""); - PROJECTILE_MAKETRIGGER(_nade); - setsize(_nade, '-16 -16 -16', '16 16 16'); - _nade.movetype = MOVETYPE_BOUNCE; - - tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade); - if (trace_startsolid) - setorigin(_nade, e.origin); - - if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH) - _nade.velocity = '0 0 100'; - else if(autocvar_g_nades_nade_newton_style == 1) - _nade.velocity = e.velocity + _velocity; - else if(autocvar_g_nades_nade_newton_style == 2) - _nade.velocity = _velocity; - else - _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true); - - _nade.touch = nade_touch; - _nade.health = autocvar_g_nades_nade_health; - _nade.max_health = _nade.health; - _nade.takedamage = DAMAGE_AIM; - _nade.event_damage = nade_damage; - _nade.customizeentityforclient = func_null; - _nade.exteriormodeltoclient = world; - _nade.traileffectnum = 0; - _nade.teleportable = true; - _nade.pushable = true; - _nade.gravity = 1; - _nade.missile_flags = MIF_SPLASH | MIF_ARC; - _nade.damagedbycontents = true; - _nade.angles = vectoangles(_nade.velocity); - _nade.flags = FL_PROJECTILE; - _nade.projectiledeathtype = DEATH_NADE.m_id; - _nade.toss_time = time; - _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); - - if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id) - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - else - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - - nade_spawn(_nade); - - if(_time) - { - _nade.think = nade_boom; - _nade.nextthink = _time; - } - - e.nade_refire = time + autocvar_g_nades_nade_refire; - e.nade_timer = 0; -} - -void nades_GiveBonus(entity player, float score) -{ - if (autocvar_g_nades) - if (autocvar_g_nades_bonus) - if (IS_REAL_CLIENT(player)) - if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max) - if (player.frozen == 0) - if (player.deadflag == DEAD_NO) - { - if ( player.bonus_nade_score < 1 ) - player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max; - - if ( player.bonus_nade_score >= 1 ) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); - play2(player, SND(KH_ALARM)); - player.bonus_nades++; - player.bonus_nade_score -= 1; - } - } -} - -void nades_RemoveBonus(entity player) -{ - player.bonus_nades = player.bonus_nade_score = 0; -} - -float nade_customize() -{SELFPARAM(); - //if(IS_SPEC(other)) { return false; } - if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner)) - { - // somewhat hide the model, but keep the glow - //self.effects = 0; - if(self.traileffectnum) - self.traileffectnum = 0; - self.alpha = -1; - } - else - { - //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - if(!self.traileffectnum) - self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades[self.nade_type].m_projectile[false], self.team).eent_eff_name); - self.alpha = 1; - } - - return true; -} - -void nade_prime() -{SELFPARAM(); - if(autocvar_g_nades_bonus_only) - if(!self.bonus_nades) - return; // only allow bonus nades - - if(self.nade) - remove(self.nade); - - if(self.fake_nade) - remove(self.fake_nade); - - entity n = spawn(), fn = spawn(); - - n.classname = "nade"; - fn.classname = "fake_nade"; - - if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength) - n.nade_type = self.nade_type; - else if (self.bonus_nades >= 1) - { - n.nade_type = self.nade_type; - n.pokenade_type = self.pokenade_type; - self.bonus_nades -= 1; - } - else - { - n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type); - n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); - } - - n.nade_type = bound(1, n.nade_type, Nades_COUNT); - - setmodel(n, MDL_PROJECTILE_NADE); - //setattachment(n, self, "bip01 l hand"); - n.exteriormodeltoclient = self; - n.customizeentityforclient = nade_customize; - n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades[n.nade_type].m_projectile[false], self.team).eent_eff_name); - n.colormod = Nades[n.nade_type].m_color; - n.realowner = self; - n.colormap = self.colormap; - n.glowmod = self.glowmod; - n.wait = time + autocvar_g_nades_nade_lifetime; - n.nade_time_primed = time; - n.think = nade_beep; - n.nextthink = max(n.wait - 3, time); - n.projectiledeathtype = DEATH_NADE.m_id; - - setmodel(fn, MDL_NADE_VIEW); - setattachment(fn, self.weaponentity, ""); - fn.realowner = fn.owner = self; - fn.colormod = Nades[n.nade_type].m_color; - fn.colormap = self.colormap; - fn.glowmod = self.glowmod; - fn.think = SUB_Remove; - fn.nextthink = n.wait; - - self.nade = n; - self.fake_nade = fn; -} - -float CanThrowNade() -{SELFPARAM(); - if(self.vehicle) - return false; - - if(gameover) - return false; - - if(self.deadflag != DEAD_NO) - return false; - - if (!autocvar_g_nades) - return false; // allow turning them off mid match - - if(forbidWeaponUse(self)) - return false; - - if (!IS_PLAYER(self)) - return false; - - return true; -} - -.bool nade_altbutton; - -void nades_CheckThrow() -{SELFPARAM(); - if(!CanThrowNade()) - return; - - entity held_nade = self.nade; - if (!held_nade) - { - self.nade_altbutton = true; - if(time > self.nade_refire) - { - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW); - nade_prime(); - self.nade_refire = time + autocvar_g_nades_nade_refire; - } - } - else - { - self.nade_altbutton = false; - if (time >= held_nade.nade_time_primed + 1) { - makevectors(self.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0); - } - } -} - -void nades_Clear(entity player) -{ - if(player.nade) - remove(player.nade); - if(player.fake_nade) - remove(player.fake_nade); - - player.nade = player.fake_nade = world; - player.nade_timer = 0; -} - -MUTATOR_HOOKFUNCTION(nades, VehicleEnter) -{ - if(vh_player.nade) - toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05)); - - return false; -} - -CLASS(NadeOffhand, OffhandWeapon) - METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) - { - entity held_nade = player.nade; - if (held_nade) - { - player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1); - // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed); - makevectors(player.angles); - held_nade.velocity = player.velocity; - setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); - held_nade.angles_y = player.angles.y; - - if (time + 0.1 >= held_nade.wait) - toss_nade(player, '0 0 0', time + 0.05); - } - - if (!CanThrowNade()) return; - if (!(time > player.nade_refire)) return; - if (key_pressed) { - if (!held_nade) { - nade_prime(); - held_nade = player.nade; - } - } else if (time >= held_nade.nade_time_primed + 1) { - if (held_nade) { - makevectors(player.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0); - } - } - } -ENDCLASS(NadeOffhand) -NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } - -MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) -{ - if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { - nades_CheckThrow(); - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) -{SELFPARAM(); - if (!IS_PLAYER(self)) { return false; } - - if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton); - - if(IS_PLAYER(self)) - { - if ( autocvar_g_nades_bonus && autocvar_g_nades ) - { - entity key; - float key_count = 0; - FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; } - - float time_score; - if(self.flagcarried || self.ballcarried) // this player is important - time_score = autocvar_g_nades_bonus_score_time_flagcarrier; - else - time_score = autocvar_g_nades_bonus_score_time; - - if(key_count) - time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding - - if(autocvar_g_nades_bonus_client_select) - { - self.nade_type = self.cvar_cl_nade_type; - self.pokenade_type = self.cvar_cl_pokenade_type; - } - else - { - self.nade_type = autocvar_g_nades_bonus_type; - self.pokenade_type = autocvar_g_nades_pokenade_monster_type; - } - - self.nade_type = bound(1, self.nade_type, Nades_COUNT); - - if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max) - nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max); - } - else - { - self.bonus_nades = self.bonus_nade_score = 0; - } - } - - float n = 0; - entity o = world; - if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout) - n = -1; - else - { - vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; - n = 0; - FOR_EACH_PLAYER(other) if(self != other) - { - if(other.deadflag == DEAD_NO) - if(other.frozen == 0) - if(SAME_TEAM(other, self)) - if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax)) - { - if(!o) - o = other; - if(self.frozen == 1) - other.reviving = true; - ++n; - } - } - } - - if(n && self.frozen == 3) // OK, there is at least one teammate reviving us - { - self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); - self.health = max(1, self.revive_progress * start_health); - - if(self.revive_progress >= 1) - { - Unfreeze(self); - - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); - Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname); - } - - FOR_EACH_PLAYER(other) if(other.reviving) - { - other.revive_progress = self.revive_progress; - other.reviving = false; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) -{SELFPARAM(); - if(autocvar_g_nades_spawn) - self.nade_refire = time + autocvar_g_spawnshieldtime; - else - self.nade_refire = time + autocvar_g_nades_nade_refire; - - if(autocvar_g_nades_bonus_client_select) - self.nade_type = self.cvar_cl_nade_type; - - self.nade_timer = 0; - - if (!self.offhand) self.offhand = OFFHAND_NADE; - - if(self.nade_spawnloc) - { - setorigin(self, self.nade_spawnloc.origin); - self.nade_spawnloc.cnt -= 1; - - if(self.nade_spawnloc.cnt <= 0) - { - remove(self.nade_spawnloc); - self.nade_spawnloc = world; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) -{ - if(frag_target.nade) - if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade) - toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05)); - - float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor); - - if(IS_PLAYER(frag_attacker)) - { - if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) - nades_RemoveBonus(frag_attacker); - else if(frag_target.flagcarried) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium); - else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1) - { - #define SPREE_ITEM(counta,countb,center,normal,gentle) \ - case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; } - switch(frag_attacker.killcount) - { - KILL_SPREE_LIST - default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break; - } - #undef SPREE_ITEM - } - else - nades_GiveBonus(frag_attacker, killcount_bonus); - } - - nades_RemoveBonus(frag_target); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate) -{ - if(frag_target.frozen) - if(autocvar_g_freezetag_revive_nade) - if(frag_attacker == frag_target) - if(frag_deathtype == DEATH_NADE.m_id) - if(time - frag_inflictor.toss_time <= 0.1) - { - Unfreeze(frag_target); - frag_target.health = autocvar_g_freezetag_revive_nade_health; - Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); - frag_damage = 0; - frag_force = '0 0 0'; - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname); - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, MonsterDies) -{SELFPARAM(); - if(IS_PLAYER(frag_attacker)) - if(DIFF_TEAM(frag_attacker, self)) - if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) -{ - if(frag_target.nade) - toss_nade(frag_target, '0 0 0', time + 0.05); - - return false; -} - -bool nades_RemovePlayer() -{SELFPARAM(); - nades_Clear(self); - nades_RemoveBonus(self); - return false; -} - -MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); } -MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); } -MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); } - -MUTATOR_HOOKFUNCTION(nades, SpectateCopy) -{SELFPARAM(); - self.nade_timer = other.nade_timer; - self.nade_type = other.nade_type; - self.pokenade_type = other.pokenade_type; - self.bonus_nades = other.bonus_nades; - self.bonus_nade_score = other.bonus_nade_score; - self.stat_healing_orb = other.stat_healing_orb; - self.stat_healing_orb_alpha = other.stat_healing_orb_alpha; - return false; -} - -MUTATOR_HOOKFUNCTION(nades, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type"); - GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type"); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Nades"); - return false; -} - -MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Nades"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_new_toys.qc b/qcsrc/server/mutators/mutator/mutator_new_toys.qc deleted file mode 100644 index 78904ffae7..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_new_toys.qc +++ /dev/null @@ -1,227 +0,0 @@ -#ifdef IMPLEMENTATION -/* - -CORE laser vortex lg rl cry gl elec hagar fireb hook - vaporizer porto - tuba - -NEW rifle hlac minel seeker -IDEAS OPEN flak OPEN FUN FUN FUN FUN - - - -How this mutator works: - ======================= - -When a gun tries to spawn, this mutator is called. It will provide alternate -weaponreplace lists. - -Entity: - -{ -"classname" "weapon_vortex" -"new_toys" "rifle" -} --> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortext" -"new_toys" "vortex rifle" -} --> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortex" -"new_toys" "vortex" -} --> This is always a Vortex. - -If the map specifies no "new_toys" argument - -There will be two default replacements selectable: "replace all" and "replace random". -In "replace all" mode, e.g. Vortex will have the default replacement "rifle". -In "replace random" mode, Vortex will have the default replacement "vortex rifle". - -This mutator's replacements run BEFORE regular weaponreplace! - -The New Toys guns do NOT get a spawn function, so they can only ever be spawned -when this mutator is active. - -Likewise, warmup, give all, give ALL and impulse 99 will not give them unless -this mutator is active. - -Outside this mutator, they still can be spawned by: -- setting their start weapon cvar to 1 -- give weaponname -- weaponreplace -- weaponarena (but all and most weapons arena again won't include them) - -This mutator performs the default replacements on the DEFAULTS of the -start weapon selection. - -These weapons appear in the menu's priority list, BUT get a suffix -"(Mutator weapon)". - -Picking up a "new toys" weapon will not play standard weapon pickup sound, but -roflsound "New toys, new toys!" sound. - -*/ - -bool nt_IsNewToy(int w); - -REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - if(time > 1) // game loads at time 1 - error("This cannot be added at runtime\n"); - - // mark the guns as ok to use by e.g. impulse 99 - for(int i = WEP_FIRST; i <= WEP_LAST; ++i) - if(nt_IsNewToy(i)) - get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - for(int i = WEP_FIRST; i <= WEP_LAST; ++i) - if(nt_IsNewToy(i)) - get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.string new_toys; - -float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; -const float NT_AUTOREPLACE_NEVER = 0; -const float NT_AUTOREPLACE_ALWAYS = 1; -const float NT_AUTOREPLACE_RANDOM = 2; - -MUTATOR_HOOKFUNCTION(nt, SetModname) -{ - modname = "NewToys"; - return 0; -} - -bool nt_IsNewToy(int w) -{ - switch(w) - { - case WEP_SEEKER.m_id: - case WEP_MINE_LAYER.m_id: - case WEP_HLAC.m_id: - case WEP_RIFLE.m_id: - case WEP_SHOCKWAVE.m_id: - return true; - default: - return false; - } -} - -string nt_GetFullReplacement(string w) -{ - switch(w) - { - case "hagar": return "seeker"; - case "devastator": return "minelayer"; - case "machinegun": return "hlac"; - case "vortex": return "rifle"; - //case "shotgun": return "shockwave"; - default: return string_null; - } -} - -string nt_GetReplacement(string w, float m) -{ - if(m == NT_AUTOREPLACE_NEVER) - return w; - string s = nt_GetFullReplacement(w); - if (!s) - return w; - if(m == NT_AUTOREPLACE_RANDOM) - s = strcat(w, " ", s); - return s; -} - -MUTATOR_HOOKFUNCTION(nt, SetStartItems) -{ - // rearrange start_weapon_default - // apply those bits that are set by start_weapon_defaultmask - // same for warmup - - float i, j, k, n; - - WepSet newdefault; - WepSet warmup_newdefault; - - newdefault = '0 0 0'; - warmup_newdefault = '0 0 0'; - - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - { - entity e = get_weaponinfo(i); - if(!e.weapon) - continue; - - n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace)); - - for(j = 0; j < n; ++j) - for(k = WEP_FIRST; k <= WEP_LAST; ++k) - if(get_weaponinfo(k).netname == argv(j)) - { - if(start_weapons & WepSet_FromWeapon(i)) - newdefault |= WepSet_FromWeapon(k); - if(warmup_start_weapons & WepSet_FromWeapon(i)) - warmup_newdefault |= WepSet_FromWeapon(k); - } - } - - newdefault &= start_weapons_defaultmask; - start_weapons &= ~start_weapons_defaultmask; - start_weapons |= newdefault; - - warmup_newdefault &= warmup_start_weapons_defaultmask; - warmup_start_weapons &= ~warmup_start_weapons_defaultmask; - warmup_start_weapons |= warmup_newdefault; - - return 0; -} - -MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) -{SELFPARAM(); - // otherwise, we do replace - if(self.new_toys) - { - // map defined replacement: - ret_string = self.new_toys; - } - else - { - // auto replacement: - ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace); - } - - // apply regular weaponreplace - ret_string = W_Apply_Weaponreplace(ret_string); - - return 0; -} - -MUTATOR_HOOKFUNCTION(nt, FilterItem) -{SELFPARAM(); - if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) { - self.item_pickupsound = string_null; - self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; - } - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_nix.qc b/qcsrc/server/mutators/mutator/mutator_nix.qc deleted file mode 100644 index 259547a05d..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_nix.qc +++ /dev/null @@ -1,267 +0,0 @@ -#ifdef IMPLEMENTATION -float g_nix_with_blaster; -// WEAPONTODO -int nix_weapon; -float nix_nextchange; -float nix_nextweapon; -.float nix_lastchange_id; -.float nix_lastinfotime; -.float nix_nextincr; - -bool NIX_CanChooseWeapon(int wpn); - -REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - g_nix_with_blaster = autocvar_g_nix_with_blaster; - - nix_nextchange = 0; - nix_nextweapon = 0; - - for (int i = WEP_FIRST; i <= WEP_LAST; ++i) - if (NIX_CanChooseWeapon(i)) { - Weapon w = get_weaponinfo(i); - w.wr_init(w); - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // as the PlayerSpawn hook will no longer run, NIX is turned off by this! - entity e; - FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) - { - e.ammo_cells = start_ammo_cells; - e.ammo_plasma = start_ammo_plasma; - e.ammo_shells = start_ammo_shells; - e.ammo_nails = start_ammo_nails; - e.ammo_rockets = start_ammo_rockets; - e.ammo_fuel = start_ammo_fuel; - e.weapons = start_weapons; - if(!client_hasweapon(e, e.weapon, true, false)) - e.switchweapon = w_getbestweapon(self); - } - } - - return 0; -} - -bool NIX_CanChooseWeapon(int wpn) -{ - entity e = get_weaponinfo(wpn); - if(!e.weapon) // skip dummies - return false; - if(g_weaponarena) - { - if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn))) - return false; - } - else - { - if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) - return false; - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) - return false; - if (!(e.spawnflags & WEP_FLAG_NORMAL)) - return false; - } - return true; -} -void NIX_ChooseNextWeapon() -{ - float j; - RandomSelection_Init(); - for(j = WEP_FIRST; j <= WEP_LAST; ++j) - if(NIX_CanChooseWeapon(j)) - RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon)); - nix_nextweapon = RandomSelection_chosen_float; -} - -void NIX_GiveCurrentWeapon() -{SELFPARAM(); - float dt; - - if(!nix_nextweapon) - NIX_ChooseNextWeapon(); - - dt = ceil(nix_nextchange - time); - - if(dt <= 0) - { - nix_weapon = nix_nextweapon; - nix_nextweapon = 0; - if (!nix_nextchange) // no round played yet? - nix_nextchange = time; // start the first round now! - else - nix_nextchange = time + autocvar_g_balance_nix_roundtime; - // Weapon w = get_weaponinfo(nix_weapon); - // w.wr_init(w); // forget it, too slow - } - - // get weapon info - entity e = get_weaponinfo(nix_weapon); - - if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round! - { - self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0; - - if(self.items & IT_UNLIMITED_WEAPON_AMMO) - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break; - case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break; - case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break; - case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break; - case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break; - case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break; - } - } - else - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; - case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; - case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; - case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; - case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; - case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; - } - } - - self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - if(dt >= 1 && dt <= 5) - self.nix_lastinfotime = -42; - else - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - - Weapon w = get_weaponinfo(nix_weapon); - w.wr_resetplayer(w); - - // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - self.(weapon_load[nix_weapon]) = e.reloading_ammo; - - // vortex too - if(WEP_CVAR(vortex, charge)) - { - if(WEP_CVAR_SEC(vortex, chargepool)) - self.vortex_chargepool_ammo = 1; - self.vortex_charge = WEP_CVAR(vortex, charge_start); - } - - // set last change info - self.nix_lastchange_id = nix_nextchange; - } - if(self.nix_lastinfotime != dt) - { - self.nix_lastinfotime = dt; // initial value 0 should count as "not seen" - if(dt >= 1 && dt <= 5) - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); - } - - if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr) - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; - case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; - case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; - case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; - case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; - case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; - } - - self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - } - - self.weapons = '0 0 0'; - if(g_nix_with_blaster) - self.weapons |= WEPSET(BLASTER); - self.weapons |= WepSet_FromWeapon(nix_weapon); - - if(self.switchweapon != nix_weapon) - if(!client_hasweapon(self, self.switchweapon, true, false)) - if(client_hasweapon(self, nix_weapon, true, false)) - W_SwitchWeapon(nix_weapon); -} - -MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) -{ - return 1; // no throwing in NIX -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":NIX"); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", NIX"); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, FilterItem) -{SELFPARAM(); - switch (self.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_HealthMedium.m_itemid: - case ITEM_HealthLarge.m_itemid: - case ITEM_HealthMega.m_itemid: - case ITEM_ArmorSmall.m_itemid: - case ITEM_ArmorMedium.m_itemid: - case ITEM_ArmorLarge.m_itemid: - case ITEM_ArmorMega.m_itemid: - if (autocvar_g_nix_with_healtharmor) - return 0; - break; - case ITEM_Strength.m_itemid: - case ITEM_Shield.m_itemid: - if (autocvar_g_nix_with_powerups) - return 0; - break; - } - - return 1; // delete all other items -} - -MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) -{SELFPARAM(); - if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) - return 1; - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) -{SELFPARAM(); - if(!intermission_running) - if(self.deadflag == DEAD_NO) - if(IS_PLAYER(self)) - NIX_GiveCurrentWeapon(); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) -{SELFPARAM(); - self.nix_lastchange_id = -1; - NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning - self.items |= IT_UNLIMITED_SUPERWEAPONS; - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) -{ - modname = "NIX"; - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_overkill.qc b/qcsrc/server/mutators/mutator/mutator_overkill.qc deleted file mode 100644 index ea5adbf5a6..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_overkill.qc +++ /dev/null @@ -1,363 +0,0 @@ -#ifdef IMPLEMENTATION -.vector ok_deathloc; -.float ok_spawnsys_timer; -.float ok_lastwep; -.float ok_item; - -.float ok_notice_time; -.float ammo_charge[Weapons_MAX]; -.float ok_use_ammocharge; -.float ok_ammo_charge; - -.float ok_pauseregen_finished; - -void(entity ent, float wep) ok_DecreaseCharge; - -void ok_Initialize(); - -REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") -{ - MUTATOR_ONADD - { - ok_Initialize(); - } - - MUTATOR_ONREMOVE - { - WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } - - return false; -} - -void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float); -spawnfunc(weapon_hmg); -spawnfunc(weapon_rpc); - -void ok_DecreaseCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return; - - entity wepent = get_weaponinfo(wep); - - if(wepent.weapon == 0) - return; // dummy - - ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -void ok_IncreaseCharge(entity ent, int wep) -{ - entity wepent = get_weaponinfo(wep); - - if(wepent.weapon == 0) - return; // dummy - - if(ent.ok_use_ammocharge) - if(!ent.BUTTON_ATCK) // not while attacking? - ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); -} - -float ok_CheckWeaponCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return true; - - entity wepent = get_weaponinfo(wep); - - if(wepent.weapon == 0) - return 0; // dummy - - return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) -{ - if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) - { - frag_damage = 0; - - if(frag_attacker != frag_target) - if(frag_target.health > 0) - if(frag_target.frozen == 0) - if(frag_target.deadflag == DEAD_NO) - { - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); - frag_force = '0 0 0'; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor) -{SELFPARAM(); - if(damage_take) - self.ok_pauseregen_finished = max(self.ok_pauseregen_finished, time + 2); - return false; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDies) -{SELFPARAM(); - entity targ = ((frag_attacker) ? frag_attacker : frag_target); - - if(IS_MONSTER(self)) - { - remove(other); // remove default item - other = world; - } - - setself(spawn()); - self.ok_item = true; - self.noalign = true; - self.pickup_anyway = true; - spawnfunc_item_armor_small(this); - self.movetype = MOVETYPE_TOSS; - self.gravity = 1; - self.reset = SUB_Remove; - setorigin(self, frag_target.origin + '0 0 32'); - self.velocity = '0 0 200' + normalize(targ.origin - self.origin) * 500; - self.classname = "droppedweapon"; // hax - SUB_SetFade(self, time + 5, 1); - setself(this); - - self.ok_lastwep = self.switchweapon; - - return false; -} -MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) { ok_PlayerDies(); } - -MUTATOR_HOOKFUNCTION(ok, PlayerRegen) -{SELFPARAM(); - // overkill's values are different, so use custom regen - if(!self.frozen) - { - self.armorvalue = CalcRotRegen(self.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, 1 * frametime * (time > self.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > self.pauserotarmor_finished), autocvar_g_balance_armor_limit); - self.health = CalcRotRegen(self.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > self.ok_pauseregen_finished), 200, 0, autocvar_g_balance_health_rotlinear, 1 * frametime * (time > self.pauserothealth_finished), autocvar_g_balance_health_limit); - - float minf, maxf, limitf; - - maxf = autocvar_g_balance_fuel_rotstable; - minf = autocvar_g_balance_fuel_regenstable; - limitf = autocvar_g_balance_fuel_limit; - - self.ammo_fuel = CalcRotRegen(self.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > self.pauseregen_finished) * ((self.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > self.pauserotfuel_finished), limitf); - } - return true; // return true anyway, as frozen uses no regen -} - -MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) -{SELFPARAM(); - if(intermission_running || gameover) - return false; - - if(self.deadflag != DEAD_NO || !IS_PLAYER(self) || self.frozen) - return false; - - if(self.ok_lastwep) - { - self.switchweapon = self.ok_lastwep; - self.ok_lastwep = 0; - } - - ok_IncreaseCharge(self, self.weapon); - - if(self.BUTTON_ATCK2) - if(!forbidWeaponUse(self) || self.weapon_blocked) // allow if weapon is blocked - if(time >= self.jump_interval) - { - self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(); - makevectors(self.v_angle); - - int oldwep = self.weapon; - self.weapon = WEP_BLASTER.m_id; - W_Blaster_Attack( - self, - WEP_BLASTER.m_id | HITTYPE_SECONDARY, - WEP_CVAR_SEC(vaporizer, shotangle), - WEP_CVAR_SEC(vaporizer, damage), - WEP_CVAR_SEC(vaporizer, edgedamage), - WEP_CVAR_SEC(vaporizer, radius), - WEP_CVAR_SEC(vaporizer, force), - WEP_CVAR_SEC(vaporizer, speed), - WEP_CVAR_SEC(vaporizer, spread), - WEP_CVAR_SEC(vaporizer, delay), - WEP_CVAR_SEC(vaporizer, lifetime) - ); - self.weapon = oldwep; - } - - self.weapon_blocked = false; - - self.ok_ammo_charge = self.ammo_charge[self.weapon]; - - if(self.ok_use_ammocharge) - if(!ok_CheckWeaponCharge(self, self.weapon)) - { - if(autocvar_g_overkill_ammo_charge_notice && time > self.ok_notice_time && self.BUTTON_ATCK && IS_REAL_CLIENT(self) && self.weapon == self.switchweapon) - { - //Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_OVERKILL_CHARGE); - self.ok_notice_time = time + 2; - play2(self, SND(DRYFIRE)); - } - Weapon wpn = get_weaponinfo(self.weapon); - if(self.weaponentity.state != WS_CLEAR) - w_ready(wpn, self, self.BUTTON_ATCK, self.BUTTON_ATCK2); - - self.weapon_blocked = true; - } - - self.BUTTON_ATCK2 = 0; - - return false; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) -{SELFPARAM(); - if(autocvar_g_overkill_ammo_charge) - { - for(int i = WEP_FIRST; i <= WEP_LAST; ++i) - self.ammo_charge[i] = autocvar_g_overkill_ammo_charge_limit; - - self.ok_use_ammocharge = 1; - self.ok_notice_time = time; - } - else - self.ok_use_ammocharge = 0; - - self.ok_pauseregen_finished = time + 2; - - return false; -} - -void _spawnfunc_weapon_hmg() { SELFPARAM(); spawnfunc_weapon_hmg(this); } -void _spawnfunc_weapon_rpc() { SELFPARAM(); spawnfunc_weapon_rpc(this); } - -MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) -{SELFPARAM(); - if(autocvar_g_powerups) - if(autocvar_g_overkill_powerups_replace) - { - if(self.classname == "item_strength") - { - entity wep = spawn(); - setorigin(wep, self.origin); - setmodel(wep, MDL_OK_HMG); - wep.classname = "weapon_hmg"; - wep.ok_item = true; - wep.noalign = self.noalign; - wep.cnt = self.cnt; - wep.team = self.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.think = _spawnfunc_weapon_hmg; - wep.nextthink = time + 0.1; - return true; - } - - if(self.classname == "item_invincible") - { - entity wep = spawn(); - setorigin(wep, self.origin); - setmodel(wep, MDL_OK_RPC); - wep.classname = "weapon_rpc"; - wep.ok_item = true; - wep.noalign = self.noalign; - wep.cnt = self.cnt; - wep.team = self.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.think = _spawnfunc_weapon_rpc; - wep.nextthink = time + 0.1; - return true; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ok, FilterItem) -{SELFPARAM(); - if(self.ok_item) - return false; - - switch(self.items) - { - case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway); - case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ok, SpectateCopy) -{SELFPARAM(); - self.ammo_charge[self.weapon] = other.ammo_charge[other.weapon]; - self.ok_use_ammocharge = other.ok_use_ammocharge; - - return false; -} - -MUTATOR_HOOKFUNCTION(ok, SetStartItems) -{ - WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); - - if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } - if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } - - start_items |= IT_UNLIMITED_WEAPON_AMMO; - start_weapons = warmup_start_weapons = ok_start_items; - - return false; -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":OK"); - return false; -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Overkill"); - return false; -} - -MUTATOR_HOOKFUNCTION(ok, SetModname) -{ - modname = "Overkill"; - return true; -} - -void ok_SetCvars() -{ - // hack to force overkill playermodels - cvar_settemp("sv_defaultcharacter", "1"); - cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); - cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); - cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); -} - -void ok_Initialize() -{ - ok_SetCvars(); - - precache_all_playermodels("models/ok_player/*.dpm"); - - addstat(STAT_OK_AMMO_CHARGE, AS_FLOAT, ok_use_ammocharge); - addstat(STAT_OK_AMMO_CHARGEPOOL, AS_FLOAT, ok_ammo_charge); - - WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - - WEP_SHOTGUN.mdl = "ok_shotgun"; - WEP_MACHINEGUN.mdl = "ok_mg"; - WEP_VORTEX.mdl = "ok_sniper"; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_physical_items.qc b/qcsrc/server/mutators/mutator/mutator_physical_items.qc deleted file mode 100644 index 58a01ca2e5..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_physical_items.qc +++ /dev/null @@ -1,139 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) -{ - // check if we have a physics engine - MUTATOR_ONADD - { - if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) - { - LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); - return -1; - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.vector spawn_origin, spawn_angles; - -void physical_item_think() -{SELFPARAM(); - self.nextthink = time; - - self.alpha = self.owner.alpha; // apply fading and ghosting - - if(!self.cnt) // map item, not dropped - { - // copy ghost item properties - self.colormap = self.owner.colormap; - self.colormod = self.owner.colormod; - self.glowmod = self.owner.glowmod; - - // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there - if(autocvar_g_physical_items_reset) - { - if(self.owner.wait > time) // awaiting respawn - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - self.solid = SOLID_NOT; - self.alpha = -1; - self.movetype = MOVETYPE_NONE; - } - else - { - self.alpha = 1; - self.solid = SOLID_CORPSE; - self.movetype = MOVETYPE_PHYSICS; - } - } - } - - if(!self.owner.modelindex) - remove(self); // the real item is gone, remove this -} - -void physical_item_touch() -{SELFPARAM(); - if(!self.cnt) // not for dropped items - if (ITEM_TOUCH_NEEDKILL()) - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - } -} - -void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(!self.cnt) // not for dropped items - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - } -} - -MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) -{SELFPARAM(); - if(self.owner == world && autocvar_g_physical_items <= 1) - return false; - if (self.spawnflags & 1) // floating item - return false; - - // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. - // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. - entity wep; - wep = spawn(); - _setmodel(wep, self.model); - setsize(wep, self.mins, self.maxs); - setorigin(wep, self.origin); - wep.angles = self.angles; - wep.velocity = self.velocity; - - wep.owner = self; - wep.solid = SOLID_CORPSE; - wep.movetype = MOVETYPE_PHYSICS; - wep.takedamage = DAMAGE_AIM; - wep.effects |= EF_NOMODELFLAGS; // disable the spinning - wep.colormap = self.owner.colormap; - wep.glowmod = self.owner.glowmod; - wep.damageforcescale = autocvar_g_physical_items_damageforcescale; - wep.dphitcontentsmask = self.dphitcontentsmask; - wep.cnt = (self.owner != world); - - wep.think = physical_item_think; - wep.nextthink = time; - wep.touch = physical_item_touch; - wep.event_damage = physical_item_damage; - - if(!wep.cnt) - { - // fix the spawn origin - setorigin(wep, wep.origin + '0 0 1'); - entity oldself; - oldself = self; - WITH(entity, self, wep, builtin_droptofloor()); - } - - wep.spawn_origin = wep.origin; - wep.spawn_angles = self.angles; - - self.effects |= EF_NODRAW; // hide the original weapon - self.movetype = MOVETYPE_FOLLOW; - self.aiment = wep; // attach the original weapon - self.SendEntity = func_null; - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_pinata.qc b/qcsrc/server/mutators/mutator/mutator_pinata.qc deleted file mode 100644 index a806b2958a..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_pinata.qc +++ /dev/null @@ -1,27 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); - -MUTATOR_HOOKFUNCTION(pinata, PlayerDies) -{SELFPARAM(); - for(int j = WEP_FIRST; j <= WEP_LAST; ++j) - if(self.weapons & WepSet_FromWeapon(j)) - if(self.switchweapon != j) - if(W_IsWeaponThrowable(j)) - W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325'); - - return true; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Pinata"); - return false; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Piñata"); - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_random_gravity.qc b/qcsrc/server/mutators/mutator/mutator_random_gravity.qc deleted file mode 100644 index 1b17c9f69a..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_random_gravity.qc +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef IMPLEMENTATION -// Random Gravity -// -// Mutator by Mario -// Inspired by Player 2 - -REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) -{ - MUTATOR_ONADD - { - cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end - } - - return false; -} - -float gravity_delay; - -MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) -{ - if(gameover || !cvar("g_random_gravity")) return false; - if(time < gravity_delay) return false; - if(time < game_starttime) return false; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; - - if(random() >= autocvar_g_random_gravity_negative_chance) - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); - else - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); - - gravity_delay = time + autocvar_g_random_gravity_delay; - - LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n"); - - return false; -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":RandomGravity"); - return 0; -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Random gravity"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_rocketflying.qc b/qcsrc/server/mutators/mutator/mutator_rocketflying.qc deleted file mode 100644 index f23d9918bd..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_rocketflying.qc +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); - -MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) -{ - if(other.classname == "rocket" || other.classname == "mine") - { - // kill detonate delay of rockets - other.spawnshieldtime = time; - } - return 0; -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":RocketFlying"); - return 0; -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Rocket Flying"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc b/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc deleted file mode 100644 index f8a1709dac..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc +++ /dev/null @@ -1,35 +0,0 @@ -#ifdef IMPLEMENTATION -#include "../../../common/deathtypes/all.qh" -#include "../../round_handler.qh" - -REGISTER_MUTATOR(rm, cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return false; } - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) - if(frag_attacker == frag_target || frag_target.classname == "nade") - frag_damage = 0; - - if(autocvar_g_rm_laser) - if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - frag_damage = 0; - - return false; -} - -MUTATOR_HOOKFUNCTION(rm, PlayerDies) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return false; } - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - frag_damage = 1000; // always gib if it was a vaporizer death - - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc b/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc deleted file mode 100644 index 24147b279a..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc +++ /dev/null @@ -1,167 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay); - -.entity msnt_lookat; - -.float msnt_timer; -.vector msnt_deathloc; - -.float cvar_cl_spawn_near_teammate; - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) -{SELFPARAM(); - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) - return 0; - - entity p; - - spawn_spot.msnt_lookat = world; - - if(!teamplay) - return 0; - - RandomSelection_Init(); - FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag) - { - float l = vlen(spawn_spot.origin - p.origin); - if(l > autocvar_g_spawn_near_teammate_distance) - continue; - if(l < 48) - continue; - if(!checkpvs(spawn_spot.origin, p)) - continue; - RandomSelection_Add(p, 0, string_null, 1, 1); - } - - if(RandomSelection_chosen_ent) - { - spawn_spot.msnt_lookat = RandomSelection_chosen_ent; - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; - } - else if(self.team == spawn_spot.team) - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) -{SELFPARAM(); - // Note: when entering this, fixangle is already set. - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) - self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; - - entity team_mate, best_mate = world; - vector best_spot = '0 0 0'; - float pc = 0, best_dist = 0, dist = 0; - FOR_EACH_PLAYER(team_mate) - { - if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) - if(team_mate.deadflag == DEAD_NO) - if(team_mate.msnt_timer < time) - if(SAME_TEAM(self, team_mate)) - if(time > team_mate.spawnshieldtime) // spawn shielding - if(team_mate.frozen == 0) - if(team_mate != self) - { - tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate); - if(trace_fraction != 1.0) - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) - { - pc = pointcontents(trace_endpos + '0 0 1'); - if(pc == CONTENT_EMPTY) - { - if(vlen(team_mate.velocity) > 5) - fixedmakevectors(vectoangles(team_mate.velocity)); - else - fixedmakevectors(team_mate.angles); - - for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate - { - switch(pc) - { - case 0: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate); - break; - case 1: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate); - break; - case 2: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); - break; - case 3: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); - break; - case 4: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate); - break; - } - - if(trace_fraction == 1.0) - { - traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate); - if(trace_fraction != 1.0) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - { - dist = vlen(trace_endpos - self.msnt_deathloc); - if(dist < best_dist || best_dist == 0) - { - best_dist = dist; - best_spot = trace_endpos; - best_mate = team_mate; - } - } - else - { - setorigin(self, trace_endpos); - self.angles = team_mate.angles; - self.angles_z = 0; // never spawn tilted even if the spot says to - team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - return 0; - } - } - } - } - } - } - } - } - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - if(best_dist) - { - setorigin(self, best_spot); - self.angles = best_mate.angles; - self.angles_z = 0; // never spawn tilted even if the spot says to - best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - } - } - else if(spawn_spot.msnt_lookat) - { - self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin); - self.angles_x = -self.angles.x; - self.angles_z = 0; // never spawn tilted even if the spot says to - /* - sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); - sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n"); - sprint(self, "angles: ", vtos(self.angles), "\n"); - */ - } - - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) -{SELFPARAM(); - self.msnt_deathloc = self.origin; - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_superspec.qc b/qcsrc/server/mutators/mutator/mutator_superspec.qc deleted file mode 100644 index 416df75b4f..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_superspec.qc +++ /dev/null @@ -1,480 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(superspec, cvar("g_superspectate")); - -#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" -#define _ISLOCAL ((edict_num(1) == self) ? true : false) - -const float ASF_STRENGTH = BIT(0); -const float ASF_SHIELD = BIT(1); -const float ASF_MEGA_AR = BIT(2); -const float ASF_MEGA_HP = BIT(3); -const float ASF_FLAG_GRAB = BIT(4); -const float ASF_OBSERVER_ONLY = BIT(5); -const float ASF_SHOWWHAT = BIT(6); -const float ASF_SSIM = BIT(7); -const float ASF_FOLLOWKILLER = BIT(8); -const float ASF_ALL = 0xFFFFFF; -.float autospec_flags; - -const float SSF_SILENT = 1; -const float SSF_VERBOSE = 2; -const float SSF_ITEMMSG = 4; -.float superspec_flags; - -.string superspec_itemfilter; //"classname1 classname2 ..." - -bool superspec_Spectate(entity _player) -{SELFPARAM(); - if(Spectate(_player) == 1) - self.classname = STR_SPECTATOR; - - return true; -} - -void superspec_save_client_conf() -{SELFPARAM(); - string fn = "superspec-local.options"; - float fh; - - if (!_ISLOCAL) - { - if(self.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); - } - - fh = fopen(fn, FILE_WRITE); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); - } - else - { - fputs(fh, _SSMAGIX); - fputs(fh, "\n"); - fputs(fh, ftos(self.autospec_flags)); - fputs(fh, "\n"); - fputs(fh, ftos(self.superspec_flags)); - fputs(fh, "\n"); - fputs(fh, self.superspec_itemfilter); - fputs(fh, "\n"); - fclose(fh); - } -} - -void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) -{ - sprint(_to, strcat(_con_title, _msg)); - - if(_to.superspec_flags & SSF_SILENT) - return; - - if(_spamlevel > 1) - if (!(_to.superspec_flags & SSF_VERBOSE)) - return; - - centerprint(_to, strcat(_center_title, _msg)); -} - -float superspec_filteritem(entity _for, entity _item) -{ - float i; - - if(_for.superspec_itemfilter == "") - return true; - - if(_for.superspec_itemfilter == "") - return true; - - float l = tokenize_console(_for.superspec_itemfilter); - for(i = 0; i < l; ++i) - { - if(argv(i) == _item.classname) - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ItemTouch) -{SELFPARAM(); - entity _item = self; - - entity e; - FOR_EACH_SPEC(e) - { - setself(e); - if(self.superspec_flags & SSF_ITEMMSG) - if(superspec_filteritem(self, _item)) - { - if(self.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1); - else - superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1); - if((self.autospec_flags & ASF_SSIM) && self.enemy != other) - { - superspec_Spectate(other); - - setself(this); - return MUT_ITEMTOUCH_CONTINUE; - } - } - - if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) || - (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) || - (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) || - (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) || - (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team")) - { - - if((self.enemy != other) || IS_OBSERVER(self)) - { - if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self)) - { - if(self.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2); - } - else - { - if(self.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2); - - superspec_Spectate(other); - } - } - } - } - - setself(this); - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) -{SELFPARAM(); -#define OPTIONINFO(flag,var,test,text,long,short) \ - var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ - var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") - - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - - if(IS_PLAYER(self)) - return false; - - if(cmd_name == "superspec_itemfilter") - { - if(argv(1) == "help") - { - string _aspeco; - _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; - _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); - _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); - superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1); - } - else if(argv(1) == "clear") - { - if(self.superspec_itemfilter != "") - strunzone(self.superspec_itemfilter); - - self.superspec_itemfilter = ""; - } - else if(argv(1) == "show" || argv(1) == "") - { - if(self.superspec_itemfilter == "") - { - superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1); - return true; - } - float i; - float l = tokenize_console(self.superspec_itemfilter); - string _msg = ""; - for(i = 0; i < l; ++i) - _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); - //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); - - _msg = strcat(_msg,"\n"); - - superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1); - } - else - { - if(self.superspec_itemfilter != "") - strunzone(self.superspec_itemfilter); - - self.superspec_itemfilter = strzone(argv(1)); - } - - return true; - } - - if(cmd_name == "superspec") - { - string _aspeco; - - if(cmd_argc > 1) - { - float i, _bits = 0, _start = 1; - if(argv(1) == "help") - { - _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); - _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); - _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); - _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); - superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1); - return true; - } - - if(argv(1) == "clear") - { - self.superspec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - self.superspec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - self.superspec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; - if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; - if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; - } - } - } - - _aspeco = ""; - OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); - OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); - OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); - - superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1); - - return true; - } - -///////////////////// - - if(cmd_name == "autospec") - { - string _aspeco; - if(cmd_argc > 1) - { - if(argv(1) == "help") - { - _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); - _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); - _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); - _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); - _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); - _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); - _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); - _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); - _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); - _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); - superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1); - return true; - } - - float i, _bits = 0, _start = 1; - if(argv(1) == "clear") - { - self.autospec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - self.autospec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - self.autospec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; - if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; - if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; - if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; - if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; - if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; - if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; - if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; - if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; - if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; - } - } - } - - _aspeco = ""; - OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); - - superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1); - return true; - } - - if(cmd_name == "followpowerup") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.strength_finished > time || _player.invincible_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active powerup\n", 1); - return true; - } - - if(cmd_name == "followstrength") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.strength_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active Strength\n", 1); - return true; - } - - if(cmd_name == "followshield") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.invincible_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active Shield\n", 1); - return true; - } - - return false; -#undef OPTIONINFO -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":SS"); - return 0; -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Super Spectators"); - return 0; -} - -void superspec_hello() -{SELFPARAM(); - if(self.enemy.crypto_idfp == "") - Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); - - remove(self); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientConnect) -{SELFPARAM(); - if(!IS_REAL_CLIENT(self)) - return false; - - string fn = "superspec-local.options"; - float fh; - - self.superspec_flags = SSF_VERBOSE; - self.superspec_itemfilter = ""; - - entity _hello = spawn(); - _hello.enemy = self; - _hello.think = superspec_hello; - _hello.nextthink = time + 5; - - if (!_ISLOCAL) - { - if(self.crypto_idfp == "") - return false; - - fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); - } - - fh = fopen(fn, FILE_READ); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); - } - else - { - string _magic = fgets(fh); - if(_magic != _SSMAGIX) - { - LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n"); - } - else - { - self.autospec_flags = stof(fgets(fh)); - self.superspec_flags = stof(fgets(fh)); - self.superspec_itemfilter = strzone(fgets(fh)); - } - fclose(fh); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, PlayerDies) -{SELFPARAM(); - entity e; - FOR_EACH_SPEC(e) - { - setself(e); - if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this) - { - if(self.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); - - superspec_Spectate(frag_attacker); - } - } - - setself(this); - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) -{ - superspec_save_client_conf(); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_touchexplode.qc b/qcsrc/server/mutators/mutator/mutator_touchexplode.qc deleted file mode 100644 index 29d9a2c603..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_touchexplode.qc +++ /dev/null @@ -1,43 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); - -.float touchexplode_time; - -void PlayerTouchExplode(entity p1, entity p2) -{SELFPARAM(); - vector org = (p1.origin + p2.origin) * 0.5; - org.z += (p1.mins.z + p2.mins.z) * 0.5; - - sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); - - entity e = spawn(); - setorigin(e, org); - RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world); - remove(e); -} - -MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) -{SELFPARAM(); - if(time > self.touchexplode_time) - if(!gameover) - if(!self.frozen) - if(IS_PLAYER(self)) - if(self.deadflag == DEAD_NO) - if (!IS_INDEPENDENT_PLAYER(self)) - FOR_EACH_PLAYER(other) if(self != other) - { - if(time > other.touchexplode_time) - if(!other.frozen) - if(other.deadflag == DEAD_NO) - if (!IS_INDEPENDENT_PLAYER(other)) - if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) - { - PlayerTouchExplode(self, other); - self.touchexplode_time = other.touchexplode_time = time + 0.2; - } - } - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_vampire.qc b/qcsrc/server/mutators/mutator/mutator_vampire.qc deleted file mode 100644 index 315da7dc6f..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_vampire.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) -{ - if(time >= frag_target.spawnshieldtime) - if(frag_target != frag_attacker) - if(frag_target.deadflag == DEAD_NO) - { - frag_attacker.health += bound(0, damage_take, frag_target.health); - frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Vampire"); - return 0; -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Vampire"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_vampirehook.qc b/qcsrc/server/mutators/mutator/mutator_vampirehook.qc deleted file mode 100644 index f669f6a962..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_vampirehook.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vh, cvar("g_vampirehook")); - -bool autocvar_g_vampirehook_teamheal; -float autocvar_g_vampirehook_damage; -float autocvar_g_vampirehook_damagerate; -float autocvar_g_vampirehook_health_steal; - -.float last_dmg; - -MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) -{SELFPARAM(); - entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment); - - if(IS_PLAYER(self.aiment)) - if(self.last_dmg < time) - if(!self.aiment.frozen) - if(time >= game_starttime) - if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal) - if(self.aiment.health > 0) - if(autocvar_g_vampirehook_damage) - { - self.last_dmg = time + autocvar_g_vampirehook_damagerate; - self.owner.damage_dealt += autocvar_g_vampirehook_damage; - Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0'); - if(SAME_TEAM(self.owner, self.aiment)) - self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - else - self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - - if(dmgent == self.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! - } - - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc b/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc deleted file mode 100644 index 5c82100778..0000000000 --- a/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc +++ /dev/null @@ -1,13 +0,0 @@ -#ifdef IMPLEMENTATION -// WEAPONTODO: rename the cvars -REGISTER_MUTATOR(weaponarena_random, true); - -MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) { - SELFPARAM(); - if (!g_weaponarena_random) return; - if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER); - W_RandomWeapons(this, g_weaponarena_random); - if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER); -} - -#endif diff --git a/qcsrc/server/mutators/mutator/sandbox.qc b/qcsrc/server/mutators/mutator/sandbox.qc deleted file mode 100644 index dbee6a1375..0000000000 --- a/qcsrc/server/mutators/mutator/sandbox.qc +++ /dev/null @@ -1,828 +0,0 @@ -#ifdef IMPLEMENTATION -float autosave_time; -void sandbox_Database_Load(); - -REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) -{ - MUTATOR_ONADD - { - autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame - if(autocvar_g_sandbox_storage_autoload) - sandbox_Database_Load(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // nothing to remove - } - - return false; -} - -const float MAX_STORAGE_ATTACHMENTS = 16; -float object_count; -.float object_flood; -.entity object_attach; -.string material; - -.float touch_timer; -void sandbox_ObjectFunction_Touch() -{SELFPARAM(); - // apply material impact effects - - if(!self.material) - return; - if(self.touch_timer > time) - return; // don't execute each frame - self.touch_timer = time + 0.1; - - // make particle count and sound volume depend on impact speed - float intensity; - intensity = vlen(self.velocity) + vlen(other.velocity); - if(intensity) // avoid divisions by 0 - intensity /= 2; // average the two velocities - if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) - return; // impact not strong enough to do anything - // now offset intensity and apply it to the effects - intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity - intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); - - _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); - Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 -} - -void sandbox_ObjectFunction_Think() -{SELFPARAM(); - entity e; - - // decide if and how this object can be grabbed - if(autocvar_g_sandbox_readonly) - self.grab = 0; // no grabbing - else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp) - self.grab = 1; // owner only - else - self.grab = 3; // anyone - - // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). - // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, - // since if the owning player disconnects, the object's owner should also be reset. - FOR_EACH_REALPLAYER(e) // bots can't have objects - { - if(self.crypto_idfp == e.crypto_idfp) - { - self.realowner = e; - break; - } - self.realowner = world; - } - - self.nextthink = time; - - CSQCMODEL_AUTOUPDATE(self); -} - -.float old_solid, old_movetype; -entity sandbox_ObjectEdit_Get(float permissions) -{SELFPARAM(); - // Returns the traced entity if the player can edit it, and world if not. - // If permissions if false, the object is returned regardless of editing rights. - // Attached objects are SOLID_NOT and do not get traced. - - crosshair_trace_plusvisibletriggers(self); - if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit) - return world; // out of trace range - if(trace_ent.classname != "object") - return world; // entity is not an object - if(!permissions) - return trace_ent; // don't check permissions, anyone can edit this object - if(trace_ent.crypto_idfp == "") - return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it - if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)) - return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server - return world; -} - -void sandbox_ObjectEdit_Scale(entity e, float f) -{ - e.scale = f; - if(e.scale) - { - e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); - _setmodel(e, e.model); // reset mins and maxs based on mesh - setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size - } -} - -void sandbox_ObjectAttach_Remove(entity e); -void sandbox_ObjectAttach_Set(entity e, entity parent, string s) -{ - // attaches e to parent on string s - - // we can't attach to an attachment, for obvious reasons - sandbox_ObjectAttach_Remove(e); - - e.old_solid = e.solid; // persist solidity - e.old_movetype = e.movetype; // persist physics - e.movetype = MOVETYPE_FOLLOW; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_NO; - - setattachment(e, parent, s); - e.owner = parent; -} - -void sandbox_ObjectAttach_Remove(entity e) -{ - // detaches any object attached to e - - entity head; - for(head = world; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - vector org; - org = gettaginfo(head, 0); - setattachment(head, world, ""); - head.owner = world; - - // objects change origin and angles when detached, so apply previous position - setorigin(head, org); - head.angles = e.angles; // don't allow detached objects to spin or roll - - head.solid = head.old_solid; // restore persisted solidity - head.movetype = head.old_movetype; // restore persisted physics - head.takedamage = DAMAGE_AIM; - } - } -} - -entity sandbox_ObjectSpawn(float database) -{SELFPARAM(); - // spawn a new object with default properties - - entity e = spawn(); - e.classname = "object"; - e.takedamage = DAMAGE_AIM; - e.damageforcescale = 1; - e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly - e.movetype = MOVETYPE_TOSS; - e.frame = 0; - e.skin = 0; - e.material = string_null; - e.touch = sandbox_ObjectFunction_Touch; - e.think = sandbox_ObjectFunction_Think; - e.nextthink = time; - //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? - - if(!database) - { - // set the object's owner via player UID - // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone - if(self.crypto_idfp != "") - e.crypto_idfp = strzone(self.crypto_idfp); - else - print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); - - // set public object information - e.netname = strzone(self.netname); // name of the owner - e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time - - // set origin and direction based on player position and view angle - makevectors(self.v_angle); - WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self); - setorigin(e, trace_endpos); - e.angles_y = self.v_angle.y; - } - - WITH(entity, self, e, CSQCMODEL_AUTOINIT(e)); - - object_count += 1; - return e; -} - -void sandbox_ObjectRemove(entity e) -{ - sandbox_ObjectAttach_Remove(e); // detach child objects - - // if the object being removed has been selected for attachment by a player, unset it - entity head; - FOR_EACH_REALPLAYER(head) // bots can't have objects - { - if(head.object_attach == e) - head.object_attach = world; - } - - if(e.material) { strunzone(e.material); e.material = string_null; } - if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } - if(e.netname) { strunzone(e.netname); e.netname = string_null; } - if(e.message) { strunzone(e.message); e.message = string_null; } - if(e.message2) { strunzone(e.message2); e.message2 = string_null; } - remove(e); - e = world; - - object_count -= 1; -} - -string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global - -string sandbox_ObjectPort_Save(entity e, float database) -{ - // save object properties, and return them as a string - float i = 0; - string s; - entity head; - - for(head = world; (head = find(head, classname, "object")); ) - { - // the main object needs to be first in the array [0] with attached objects following - float slot, physics, solidity; - if(head == e) // this is the main object, place it first - { - slot = 0; - solidity = head.solid; // applied solidity is normal solidity for children - physics = head.movetype; // applied physics are normal physics for parents - } - else if(head.owner == e) // child object, list them in order - { - i += 1; // children start from 1 - slot = i; - solidity = head.old_solid; // persisted solidity is normal solidity for children - physics = head.old_movetype; // persisted physics are normal physics for children - gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below - } - else - continue; - - // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- - if(slot) - { - // properties stored only for child objects - if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - } - else - { - // properties stored only for parent objects - if(database) - { - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); - } - } - // properties stored for all objects - port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); - port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); - port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); - port_string[slot] = strcat(port_string[slot], ftos(physics), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); - if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - if(database) - { - // properties stored only for the database - if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); - } - } - - // now apply the array to a simple string, with the ; symbol separating objects - s = ""; - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - { - if(port_string[i]) - s = strcat(s, port_string[i], "; "); - port_string[i] = string_null; // fully clear the string - } - - return s; -} - -entity sandbox_ObjectPort_Load(string s, float database) -{ - // load object properties, and spawn a new object with them - float n, i; - entity e = world, parent = world; - - // separate objects between the ; symbols - n = tokenizebyseparator(s, "; "); - for(i = 0; i < n; ++i) - port_string[i] = argv(i); - - // now separate and apply the properties of each object - for(i = 0; i < n; ++i) - { - float argv_num; - string tagname = string_null; - argv_num = 0; - tokenize_console(port_string[i]); - e = sandbox_ObjectSpawn(database); - - // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- - if(i) - { - // properties stored only for child objects - if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; - } - else - { - // properties stored only for parent objects - if(database) - { - setorigin(e, stov(argv(argv_num))); ++argv_num; - e.angles = stov(argv(argv_num)); ++argv_num; - } - parent = e; // mark parent objects as such - } - // properties stored for all objects - _setmodel(e, argv(argv_num)); ++argv_num; - e.skin = stof(argv(argv_num)); ++argv_num; - e.alpha = stof(argv(argv_num)); ++argv_num; - e.colormod = stov(argv(argv_num)); ++argv_num; - e.glowmod = stov(argv(argv_num)); ++argv_num; - e.frame = stof(argv(argv_num)); ++argv_num; - sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; - e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; - e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num; - e.damageforcescale = stof(argv(argv_num)); ++argv_num; - if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; - if(database) - { - // properties stored only for the database - if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; - if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; - if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; - if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; - } - - // attach last - if(i) - sandbox_ObjectAttach_Set(e, parent, tagname); - } - - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - port_string[i] = string_null; // fully clear the string - - return e; -} - -void sandbox_Database_Save() -{ - // saves all objects to the database file - entity head; - string file_name; - float file_get; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_WRITE); - fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); - fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); - - for(head = world; (head = find(head, classname, "object")); ) - { - // attached objects are persisted separately, ignore them here - if(head.owner != world) - continue; - - // use a line of text for each object, listing all properties - fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); - } - fclose(file_get); -} - -void sandbox_Database_Load() -{ - // loads all objects from the database file - string file_read, file_name; - float file_get, i; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_READ); - if(file_get < 0) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); - } - else - { - for (;;) - { - file_read = fgets(file_get); - if(file_read == "") - break; - if(substring(file_read, 0, 2) == "//") - continue; - if(substring(file_read, 0, 1) == "#") - continue; - - entity e; - e = sandbox_ObjectPort_Load(file_read, true); - - if(e.material) - { - // since objects are being loaded for the first time, precache material sounds for each - for (i = 1; i <= 5; i++) // 5 sounds in total - precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); - } - } - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); - } - fclose(file_get); -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - if(cmd_name == "g_sandbox") - { - if(autocvar_g_sandbox_readonly) - { - print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); - return true; - } - if(cmd_argc < 2) - { - print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); - return true; - } - - switch(argv(1)) - { - entity e; - float i; - string s; - - // ---------------- COMMAND: HELP ---------------- - case "help": - print_to(self, "You can use the following sandbox commands:"); - print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); - print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); - print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); - print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar"); - print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); - print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); - print_to(self, "^3get ^7- selects the object you are facing as the object to be attached"); - print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); - print_to(self, "^3remove ^7- detaches all objects from the object you are facing"); - print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); - print_to(self, "^3skin value ^7- changes the skin of the object"); - print_to(self, "^3alpha value ^7- sets object transparency"); - print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color"); - print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); - print_to(self, "^3frame value ^7- object animation frame, for self-animated models"); - print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); - print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); - print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); - print_to(self, "^3force value ^7- amount of force applied to objects that are shot"); - print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); - print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); - print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object"); - print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); - print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); - print_to(self, "^3attachments ^7- prints information about the object's attachments"); - print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); - return true; - - // ---------------- COMMAND: OBJECT, SPAWN ---------------- - case "object_spawn": - if(time < self.object_flood) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - self.object_flood = time + autocvar_g_sandbox_editor_flood; - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - if(cmd_argc < 3) - { - print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); - return true; - } - if (!(fexists(argv(2)))) - { - print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); - return true; - } - - e = sandbox_ObjectSpawn(false); - _setmodel(e, argv(2)); - - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); - return true; - - // ---------------- COMMAND: OBJECT, REMOVE ---------------- - case "object_remove": - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); - sandbox_ObjectRemove(e); - return true; - } - - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- - case "object_duplicate": - switch(argv(2)) - { - case "copy": - // copies customizable properties of the selected object to the clipboard cvar - e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? - if(e != world) - { - s = sandbox_ObjectPort_Save(e, false); - s = strreplace("\"", "\\\"", s); - stuffcmd(self, strcat("set ", argv(3), " \"", s, "\"")); - - print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard"); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); - return true; - - case "paste": - // spawns a new object using the properties in the player's clipboard cvar - if(time < self.object_flood) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - self.object_flood = time + autocvar_g_sandbox_editor_flood; - if(argv(3) == "") // no object in clipboard - { - print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); - return true; - } - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - e = sandbox_ObjectPort_Load(argv(3), false); - - print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully"); - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, ATTACH ---------------- - case "object_attach": - switch(argv(2)) - { - case "get": - // select e as the object as meant to be attached - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - self.object_attach = e; - print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment"); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); - return true; - case "set": - if(self.object_attach == world) - { - print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); - return true; - } - - // attaches the previously selected object to e - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - sandbox_ObjectAttach_Set(self.object_attach, e, argv(3)); - self.object_attach = world; // object was attached, no longer keep it scheduled for attachment - print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); - return true; - case "remove": - // removes e if it was attached - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - sandbox_ObjectAttach_Remove(e); - print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, EDIT ---------------- - case "object_edit": - if(argv(2) == "") - { - print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); - return true; - } - - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - switch(argv(2)) - { - case "skin": - e.skin = stof(argv(3)); - break; - case "alpha": - e.alpha = stof(argv(3)); - break; - case "color_main": - e.colormod = stov(argv(3)); - break; - case "color_glow": - e.glowmod = stov(argv(3)); - break; - case "frame": - e.frame = stof(argv(3)); - break; - case "scale": - sandbox_ObjectEdit_Scale(e, stof(argv(3))); - break; - case "solidity": - switch(argv(3)) - { - case "0": // non-solid - e.solid = SOLID_TRIGGER; - break; - case "1": // solid - e.solid = SOLID_BBOX; - break; - default: - break; - } - case "physics": - switch(argv(3)) - { - case "0": // static - e.movetype = MOVETYPE_NONE; - break; - case "1": // movable - e.movetype = MOVETYPE_TOSS; - break; - case "2": // physical - e.movetype = MOVETYPE_PHYSICS; - break; - default: - break; - } - break; - case "force": - e.damageforcescale = stof(argv(3)); - break; - case "material": - if(e.material) strunzone(e.material); - if(argv(3)) - { - for (i = 1; i <= 5; i++) // precache material sounds, 5 in total - precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); - e.material = strzone(argv(3)); - } - else - e.material = string_null; // no material - break; - default: - print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); - return true; - } - - // update last editing time - if(e.message2) strunzone(e.message2); - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); - - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, CLAIM ---------------- - case "object_claim": - // if the player can edit an object but is not its owner, this can be used to claim that object - if(self.crypto_idfp == "") - { - print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); - return true; - } - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - // update the owner's name - // Do this before checking if you're already the owner and skipping if such, so we - // also update the player's nickname if he changed it (but has the same player UID) - if(e.netname != self.netname) - { - if(e.netname) strunzone(e.netname); - e.netname = strzone(self.netname); - print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated"); - } - - if(e.crypto_idfp == self.crypto_idfp) - { - print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); - return true; - } - - if(e.crypto_idfp) strunzone(e.crypto_idfp); - e.crypto_idfp = strzone(self.crypto_idfp); - - print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, INFO ---------------- - case "object_info": - // prints public information about the object to the player - e = sandbox_ObjectEdit_Get(false); - if(e != world) - { - switch(argv(2)) - { - case "object": - print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); - return true; - case "mesh": - s = ""; - FOR_EACH_TAG(e) - s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); - print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); - return true; - case "attachments": - // this should show the same info as 'mesh' but for attachments - s = ""; - entity head; - i = 0; - for(head = world; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - ++i; // start from 1 - gettaginfo(e, head.tag_index); - s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame)); - s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); - } - } - if(i) // object contains attachments - print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s)); - else - print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments"); - return true; - } - } - print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); - return true; - - // ---------------- COMMAND: DEFAULT ---------------- - default: - print_to(self, "Invalid command. For usage information, type 'sandbox help'"); - return true; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) -{ - if(!autocvar_g_sandbox_storage_autosave) - return false; - if(time < autosave_time) - return false; - autosave_time = time + autocvar_g_sandbox_storage_autosave; - - sandbox_Database_Save(); - - return true; -} -#endif diff --git a/qcsrc/server/pathlib/main.qc b/qcsrc/server/pathlib/main.qc index 800f0335ef..9a199bd626 100644 --- a/qcsrc/server/pathlib/main.qc +++ b/qcsrc/server/pathlib/main.qc @@ -471,9 +471,8 @@ entity pathlib_astar(vector from,vector to) { LOG_TRACE("AStar: Goal found on first node!\n"); - open = spawn(); + open = new(path_end); open.owner = open; - open.classname = "path_end"; setorigin(open,path.origin); pathlib_cleanup(); diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 9b1d7194f9..1860046374 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -624,8 +624,7 @@ entity Portal_Spawn(entity own, vector org, vector ang) if(!CheckWireframeBox(own, org - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) return world; - portal = spawn(); - portal.classname = "portal"; + portal = new(portal); portal.aiment = own; setorigin(portal, org); portal.mangle = ang; diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index ab94c42bdd..08a9d4d22b 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -1,6 +1,8 @@ #include "../lib/_all.inc" #include "_all.qh" +#include "../common/effects/qc/all.qc" + #include "anticheat.qc" #include "antilag.qc" #include "campaign.qc" @@ -8,17 +10,13 @@ #include "cl_client.qc" #include "cl_impulse.qc" #include "cl_player.qc" -#include "controlpoint.qc" -#include "csqceffects.qc" #include "ent_cs.qc" #include "g_damage.qc" #include "g_hook.qc" // #include "g_lights.qc" // TODO: was never used #include "g_models.qc" #include "g_subs.qc" -#include "g_violence.qc" #include "g_world.qc" -#include "generator.qc" #include "ipban.qc" #include "item_key.qc" #include "mapvoting.qc" @@ -43,8 +41,6 @@ #include "command/all.qc" -#include "mutators/all.qc" - #include "pathlib/_all.inc" #include "weapons/accuracy.qc" @@ -63,30 +59,29 @@ #include "../common/campaign_setup.qc" #include "../common/effects/effectinfo.qc" #include "../common/mapinfo.qc" -#include "../common/monsters/spawn.qc" -#include "../common/monsters/sv_monsters.qc" #include "../common/minigames/minigames.qc" #include "../common/minigames/sv_minigames.qc" +#include "../common/monsters/spawn.qc" +#include "../common/monsters/sv_monsters.qc" #include "../common/movetypes/include.qc" #include "../common/net_notice.qc" #include "../common/notifications.qc" #include "../common/physics.qc" #include "../common/playerstats.qc" -#include "../common/viewloc.qc" #include "../common/triggers/include.qc" #include "../common/util.qc" +#include "../common/viewloc.qc" #include "../common/deathtypes/all.qc" -#include "../common/buffs/all.qc" #include "../common/effects/all.qc" #include "../common/gamemodes/all.qc" #include "../common/items/all.qc" #include "../common/monsters/all.qc" -#include "../common/mutators/all.qc" -#include "../common/nades/all.qc" #include "../common/turrets/all.qc" #include "../common/vehicles/all.qc" #include "../common/weapons/all.qc" +#include "../common/mutators/all.qc" +#include "mutators/all.qc" #include "../common/turrets/sv_turrets.qc" #include "../common/turrets/config.qc" diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index fe7606a39b..3d6e9e85e8 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -165,8 +165,7 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only if(!spec) msg_entity = e; WRITESPECTATABLE_MSG_ONE({ - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); if(spec) { WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING); @@ -184,8 +183,7 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only void race_send_recordtime(float msg) { // send the server best time - WriteByte(msg, SVC_TEMPENTITY); - WriteByte(msg, TE_CSQC_RACE); + WriteHeader(msg, TE_CSQC_RACE); WriteByte(msg, RACE_NET_SERVER_RECORD); WriteInt24_t(msg, race_readTime(GetMapname(), 1)); } @@ -194,8 +192,7 @@ void race_send_recordtime(float msg) void race_send_speedaward(float msg) { // send the best speed of the round - WriteByte(msg, SVC_TEMPENTITY); - WriteByte(msg, TE_CSQC_RACE); + WriteHeader(msg, TE_CSQC_RACE); WriteByte(msg, RACE_NET_SPEED_AWARD); WriteInt24_t(msg, floor(speedaward_speed+0.5)); WriteString(msg, speedaward_holder); @@ -204,8 +201,7 @@ void race_send_speedaward(float msg) void race_send_speedaward_alltimebest(float msg) { // send the best speed - WriteByte(msg, SVC_TEMPENTITY); - WriteByte(msg, TE_CSQC_RACE); + WriteHeader(msg, TE_CSQC_RACE); WriteByte(msg, RACE_NET_SPEED_AWARD_BEST); WriteInt24_t(msg, floor(speedaward_alltimebest+0.5)); WriteString(msg, speedaward_alltimebest_holder); @@ -213,8 +209,7 @@ void race_send_speedaward_alltimebest(float msg) void race_SendRankings(float pos, float prevpos, float del, float msg) { - WriteByte(msg, SVC_TEMPENTITY); - WriteByte(msg, TE_CSQC_RACE); + WriteHeader(msg, TE_CSQC_RACE); WriteByte(msg, RACE_NET_SERVER_RANKINGS); WriteShort(msg, pos); WriteShort(msg, prevpos); @@ -235,8 +230,7 @@ void race_SendStatus(float id, entity e) msg = MSG_ALL; msg_entity = e; WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, { - WriteByte(msg, SVC_TEMPENTITY); - WriteByte(msg, TE_CSQC_RACE); + WriteHeader(msg, TE_CSQC_RACE); WriteByte(msg, RACE_NET_SERVER_STATUS); WriteShort(msg, id); WriteString(msg, e.netname); @@ -447,8 +441,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) if(g_race_qualifying) { WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING); WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at WriteInt24_t(MSG_ONE, t); // time to that intermediate @@ -476,8 +469,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) { msg_entity = e; WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE); WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at if(e == oth) @@ -503,8 +495,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) { msg_entity = oth; WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT); WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at if(e == oth) @@ -537,8 +528,7 @@ void race_ClearTime(entity e) msg_entity = e; WRITESPECTATABLE_MSG_ONE({ - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_CLEAR); // next }); } @@ -1100,8 +1090,7 @@ void race_ImposePenaltyTime(entity pl, float penalty, string reason) { msg_entity = pl; WRITESPECTATABLE_MSG_ONE({ - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING); WriteShort(MSG_ONE, TIME_ENCODE(penalty)); WriteString(MSG_ONE, reason); @@ -1115,8 +1104,7 @@ void race_ImposePenaltyTime(entity pl, float penalty, string reason) { msg_entity = pl; WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteHeader(MSG_ONE, TE_CSQC_RACE); WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE); WriteShort(MSG_ONE, TIME_ENCODE(penalty)); WriteString(MSG_ONE, reason); diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc index e6a1334f88..8fb1a787be 100644 --- a/qcsrc/server/round_handler.qc +++ b/qcsrc/server/round_handler.qc @@ -4,113 +4,112 @@ #include "../common/util.qh" void round_handler_Think() -{SELFPARAM(); - float f; +{ + SELFPARAM(); - if(time < game_starttime) + if (time < game_starttime) { round_handler_Reset(game_starttime); return; } - if(gameover) + if (gameover) { round_handler_Reset(0); round_handler_Remove(); return; } - if(self.wait) + if (this.wait) { - self.wait = false; - self.cnt = self.count + 1; // init countdown - round_starttime = time + self.count; + this.wait = false; + this.cnt = this.count + 1; // init countdown + round_starttime = time + this.count; reset_map(true); } - if(self.cnt > 0) // countdown running + if (this.cnt > 0) // countdown running { - if(self.canRoundStart()) + if (this.canRoundStart()) { - if(self.cnt == self.count + 1) - round_starttime = time + self.count; - f = self.cnt - 1; - if(f == 0) + if (this.cnt == this.count + 1) round_starttime = time + this.count; + int f = this.cnt - 1; + if (f == 0) { - self.cnt = 0; - self.round_endtime = (self.round_timelimit) ? time + self.round_timelimit : 0; - self.nextthink = time; - if(self.roundStart) - self.roundStart(); + this.cnt = 0; + this.round_endtime = (this.round_timelimit) ? time + this.round_timelimit : 0; + this.nextthink = time; + if (this.roundStart) this.roundStart(); return; } - self.cnt = self.cnt - 1; + this.cnt = this.cnt - 1; } else { round_handler_Reset(0); } - self.nextthink = time + 1; // canRoundStart every second + this.nextthink = time + 1; // canRoundStart every second } else { - if(self.canRoundEnd()) + if (this.canRoundEnd()) { // schedule a new round - self.wait = true; - self.nextthink = time + self.delay; + this.wait = true; + this.nextthink = time + this.delay; } else { - self.nextthink = time; // canRoundEnd every frame + this.nextthink = time; // canRoundEnd every frame } } } void round_handler_Init(float the_delay, float the_count, float the_round_timelimit) { - round_handler.delay = (the_delay > 0) ? the_delay : 0; - round_handler.count = fabs(floor(the_count)); - round_handler.cnt = round_handler.count + 1; - round_handler.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0; + entity this = round_handler; + this.delay = (the_delay > 0) ? the_delay : 0; + this.count = fabs(floor(the_count)); + this.cnt = this.count + 1; + this.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0; } // NOTE: this is only needed because if round_handler spawns at time 1 // gamestarttime isn't initialized yet void round_handler_FirstThink() { - round_starttime = max(time, game_starttime) + round_handler.count; - round_handler.think = round_handler_Think; - round_handler.nextthink = max(time, game_starttime); + SELFPARAM(); + round_starttime = max(time, game_starttime) + this.count; + this.think = round_handler_Think; + this.nextthink = max(time, game_starttime); } void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func) { - if(round_handler) + if (round_handler) { backtrace("Can't spawn round_handler again!"); return; } - round_handler = spawn(); - round_handler.classname = "round_handler"; + entity this = round_handler = new(round_handler); - round_handler.think = round_handler_FirstThink; - round_handler.canRoundStart = canRoundStart_func; - round_handler.canRoundEnd = canRoundEnd_func; - round_handler.roundStart = roundStart_func; - round_handler.wait = false; + this.think = round_handler_FirstThink; + this.canRoundStart = canRoundStart_func; + this.canRoundEnd = canRoundEnd_func; + this.roundStart = roundStart_func; + this.wait = false; round_handler_Init(5, 5, 180); - round_handler.nextthink = time; + this.nextthink = time; } void round_handler_Reset(float next_think) { - round_handler.wait = false; - if(round_handler.count) - if(round_handler.cnt < round_handler.count + 1) - round_handler.cnt = round_handler.count + 1; - round_handler.nextthink = next_think; - round_starttime = (next_think) ? (next_think + round_handler.count) : -1; + entity this = round_handler; + this.wait = false; + if (this.count) + if (this.cnt < this.count + 1) this.cnt = this.count + 1; + this.nextthink = next_think; + round_starttime = (next_think) ? (next_think + this.count) : -1; } void round_handler_Remove() @@ -118,4 +117,3 @@ void round_handler_Remove() remove(round_handler); round_handler = world; } - diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index 678baec958..2eca07bc94 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -58,7 +58,7 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags) { float i, p, longflags; - WriteByte(MSG_ENTITY, ENT_CLIENT_TEAMSCORES); + WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES); WriteByte(MSG_ENTITY, self.team - 1); longflags = 0; @@ -87,9 +87,8 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags) void TeamScore_Spawn(float t, string name) { - entity ts; - ts = spawn(); - ts.classname = "csqc_score_team"; + entity ts = new(csqc_score_team); + make_pure(ts); ts.netname = name; // not used yet, FIXME ts.team = t; Net_LinkEntity(ts, false, 0, TeamScore_SendEntity); @@ -188,7 +187,7 @@ void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags) bool ScoreInfo_SendEntity(entity this, entity to, int sf) { float i; - WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES_INFO); + WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO); WriteInt24_t(MSG_ENTITY, MapInfo_LoadedGametype); for(i = 0; i < MAX_SCORE; ++i) { @@ -211,8 +210,8 @@ void ScoreInfo_Init(float teams) } else { - scores_initialized = spawn(); - scores_initialized.classname = "ent_client_scoreinfo"; + scores_initialized = new(ent_client_scoreinfo); + make_pure(scores_initialized); Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity); } if(teams >= 1) @@ -233,7 +232,7 @@ bool PlayerScore_SendEntity(entity this, entity to, float sendflags) { float i, p, longflags; - WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES); + WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES); WriteByte(MSG_ENTITY, num_for_edict(self.owner)); longflags = 0; @@ -316,10 +315,10 @@ void Score_ClearAll() void PlayerScore_Attach(entity player) { - entity sk; if(player.scorekeeper) error("player already has a scorekeeper"); - sk = spawn(); + entity sk = new(scorekeeper); + make_pure(sk); sk.owner = player; Net_LinkEntity(sk, false, 0, PlayerScore_SendEntity); player.scorekeeper = sk; @@ -530,6 +529,7 @@ void WinningConditionHelper() FOR_EACH_CLIENT(p) { + string s = ""; if(fullstatus) { s = GetPlayerScoreString(p, 1); @@ -927,7 +927,7 @@ void PlayerScore_PlayerStats(entity p) PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label[i]), s.(scores[i])); } -void PlayerScore_TeamStats(void) +void PlayerScore_TeamStats() { entity sk; float t, i; diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 07d7267910..2071b0c321 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -31,6 +31,9 @@ void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabl if(score_enabled) ScoreInfo_SetLabel_PlayerScore(SP_SCORE, "score", sprio); + + ScoreInfo_SetLabel_PlayerScore(SP_DMG, "damage", 0); + ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "damagetaken", SFL_LOWER_IS_BETTER); } void ScoreRules_basics_end() { diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index a1abfa58fc..36b08008e8 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -12,7 +12,7 @@ bool SpawnPoint_Send(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT); + WriteHeader(MSG_ENTITY, ENT_CLIENT_SPAWNPOINT); WriteByte(MSG_ENTITY, self.team); WriteShort(MSG_ENTITY, self.origin.x); @@ -26,7 +26,7 @@ bool SpawnEvent_Send(entity this, entity to, int sf) { float send; - WriteByte(MSG_ENTITY, ENT_CLIENT_SPAWNEVENT); + WriteHeader(MSG_ENTITY, ENT_CLIENT_SPAWNEVENT); if(autocvar_g_spawn_alloweffects) { @@ -117,9 +117,7 @@ void relocate_spawnpoint() { // show where spawnpoints point at too makevectors(self.angles); - entity e; - e = spawn(); - e.classname = "info_player_foo"; + entity e = new(info_player_foo); setorigin(e, self.origin + v_forward * 24); setsize(e, '-8 -8 -8', '8 8 8'); e.solid = SOLID_TRIGGER; diff --git a/qcsrc/server/steerlib.qc b/qcsrc/server/steerlib.qc index 4941b5521b..beaab08a4d 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -547,16 +547,13 @@ MODEL(FLOCKER, "models/turrets/rocket.md3"); void spawn_flocker() {SELFPARAM(); - entity flocker; - - flocker = spawn (); + entity flocker = new(flocker); setorigin(flocker, self.origin + '0 0 32'); setmodel (flocker, MDL_FLOCKER); setsize (flocker, '-3 -3 -3', '3 3 3'); flocker.flock_id = self.flock_id; - flocker.classname = "flocker"; flocker.owner = self; flocker.think = flocker_think; flocker.nextthink = time + random() * 5; @@ -649,12 +646,11 @@ spawnfunc(flockerspawn) self.think = flockerspawn_think; self.nextthink = time + 0.25; - self.enemy = spawn(); + self.enemy = new(FLock Hunter); setmodel(self.enemy, MDL_FLOCKER); setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128)); - self.enemy.classname = "FLock Hunter"; self.enemy.scale = 3; self.enemy.effects = EF_LOWPRECISION; self.enemy.movetype = MOVETYPE_BOUNCEMISSILE; diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 1ffcb60c58..0d5fa234f9 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -13,6 +13,7 @@ #include "../common/constants.qh" #include "../common/deathtypes/all.qh" +#include "../common/debug.qh" #include "../common/mapinfo.qh" #include "../common/util.qh" @@ -27,7 +28,7 @@ .float lastground; .int state; -void CreatureFrame (void) +void CreatureFrame () {SELFPARAM(); float dm; @@ -158,9 +159,9 @@ void CreatureFrame (void) if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS)) { if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) - GlobalSound(globalsound_metalstep, CH_PLAYER, VOICETYPE_PLAYERSOUND); + GlobalSound(GS_STEP_METAL, CH_PLAYER, VOICETYPE_PLAYERSOUND); else - GlobalSound(globalsound_step, CH_PLAYER, VOICETYPE_PLAYERSOUND); + GlobalSound(GS_STEP, CH_PLAYER, VOICETYPE_PLAYERSOUND); } } } @@ -422,7 +423,7 @@ void SV_OnEntityPreSpawnFunction() } } -void WarpZone_PostInitialize_Callback(void) +void WarpZone_PostInitialize_Callback() { // create waypoint links for warpzones entity e; diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index cb76f97f9a..f295777db7 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -26,6 +26,8 @@ #include "../lib/warpzone/util_server.qh" #endif +REGISTER_NET_LINKED(ENT_CLIENT_ITEM) + #ifdef CSQC void ItemDraw(entity self) { @@ -94,8 +96,8 @@ void Item_PreDraw() self.drawmask = MASK_NORMAL; } -void ItemRead(float _IsNew) -{SELFPARAM(); +NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) +{ int sf = ReadByte(); if(sf & ISF_LOCATION) @@ -240,6 +242,7 @@ void ItemRead(float _IsNew) if(self.ItemStatus & ITS_ANIMATE2) self.move_avelocity = '0 -90 0'; } + return true; } #endif @@ -252,7 +255,7 @@ bool ItemSend(entity this, entity to, int sf) else sf &= ~ISF_DROP; - WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM); + WriteHeader(MSG_ENTITY, ENT_CLIENT_ITEM); WriteByte(MSG_ENTITY, sf); //WriteByte(MSG_ENTITY, self.cnt); @@ -272,7 +275,8 @@ bool ItemSend(entity this, entity to, int sf) if(sf & ISF_SIZE) { - WriteByte(MSG_ENTITY, ((self.flags & FL_POWERUP) || self.health || self.armorvalue)); + Pickup p = this.itemdef; + WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor); } if(sf & ISF_STATUS) @@ -308,9 +312,9 @@ void ItemUpdate(entity item) item.SendFlags |= ISF_LOCATION; } -float have_pickup_item(void) -{SELFPARAM(); - if(self.flags & FL_POWERUP) +bool have_pickup_item(entity this) +{ + if(this.itemdef.instanceOfPowerup) { if(autocvar_g_powerups > 0) return true; @@ -324,7 +328,7 @@ float have_pickup_item(void) if(autocvar_g_pickup_items == 0) return false; if(g_weaponarena) - if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena + if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena return false; } return true; @@ -377,7 +381,12 @@ void Item_Show (entity e, float mode) e.spawnshieldtime = 1; e.ItemStatus &= ~ITS_AVAILABLE; } - else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay) + else { + entity def = e.itemdef; + bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons + || e.team // weapon stay isn't supported for teamed weapons + ; + if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay) { // make the item translucent and not touchable e.model = e.mdl; @@ -394,7 +403,7 @@ void Item_Show (entity e, float mode) e.glowmod = e.colormod; e.spawnshieldtime = 1; e.ItemStatus &= ~ITS_AVAILABLE; - } + }} if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid) e.ItemStatus |= ITS_POWERUP; @@ -430,7 +439,7 @@ float Item_ItemsTime_UpdateTime(entity e, float t); void Item_ItemsTime_SetTime(entity e, float t); void Item_ItemsTime_SetTimesForAllPlayers(); -void Item_Respawn (void) +void Item_Respawn () {SELFPARAM(); Item_Show(self, 1); // this is ugly... @@ -456,7 +465,7 @@ void Item_Respawn (void) Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); } -void Item_RespawnCountdown (void) +void Item_RespawnCountdown () {SELFPARAM(); if(self.count >= ITEM_RESPAWN_TICKS) { @@ -639,7 +648,7 @@ float Item_GiveTo(entity item, entity player) pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH); pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR); - if (item.flags & FL_WEAPON) + if (item.itemdef.instanceOfWeaponPickup) { WepSet it; it = item.weapons; @@ -690,7 +699,7 @@ float Item_GiveTo(entity item, entity player) return 0; // crude hack to enforce switching weapons - if(g_cts && (item.flags & FL_WEAPON)) + if(g_cts && item.itemdef.instanceOfWeaponPickup) { W_SwitchWeapon_Force(player, item.weapon); return 1; @@ -703,16 +712,16 @@ float Item_GiveTo(entity item, entity player) return 1; } -void Item_Touch (void) -{SELFPARAM(); - entity e, head; +void Item_Touch() +{ + SELFPARAM(); // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky) - if(self.classname == "droppedweapon") + if (this.classname == "droppedweapon") { if (ITEM_TOUCH_NEEDKILL()) { - remove(self); + remove(this); return; } } @@ -720,33 +729,33 @@ void Item_Touch (void) if(!(other.flags & FL_PICKUPITEMS) || other.frozen || other.deadflag - || (self.solid != SOLID_TRIGGER) - || (self.owner == other) - || (time < self.item_spawnshieldtime) - ) { return;} + || (this.solid != SOLID_TRIGGER) + || (this.owner == other) + || (time < this.item_spawnshieldtime) + ) { return; } - switch(MUTATOR_CALLHOOK(ItemTouch, self, other)) + switch (MUTATOR_CALLHOOK(ItemTouch, this, other)) { case MUT_ITEMTOUCH_RETURN: { return; } case MUT_ITEMTOUCH_PICKUP: { goto pickup; } } - if (self.classname == "droppedweapon") + if (this.classname == "droppedweapon") { - self.strength_finished = max(0, self.strength_finished - time); - self.invincible_finished = max(0, self.invincible_finished - time); - self.superweapons_finished = max(0, self.superweapons_finished - time); + this.strength_finished = max(0, this.strength_finished - time); + this.invincible_finished = max(0, this.invincible_finished - time); + this.superweapons_finished = max(0, this.superweapons_finished - time); } - entity it = self.itemdef; - bool gave = (it && it.instanceOfPickup) ? ITEM_HANDLE(Pickup, it, self, other) : Item_GiveTo(self, other); + entity it = this.itemdef; + bool gave = ITEM_HANDLE(Pickup, it, this, other); if (!gave) { - if (self.classname == "droppedweapon") + if (this.classname == "droppedweapon") { // undo what we did above - self.strength_finished += time; - self.invincible_finished += time; - self.superweapons_finished += time; + this.strength_finished += time; + this.invincible_finished += time; + this.superweapons_finished += time; } return; } @@ -755,17 +764,18 @@ void Item_Touch (void) other.last_pickup = time; - Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); - _sound (other, CH_TRIGGER, (self.item_pickupsound ? self.item_pickupsound : self.item_pickupsound_ent.sound_str()), VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + _sound (other, CH_TRIGGER, (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM); - if (self.classname == "droppedweapon") - remove (self); - else if (self.spawnshieldtime) + if (this.classname == "droppedweapon") + remove (this); + else if (this.spawnshieldtime) { - if(self.team) + entity e; + if(this.team) { RandomSelection_Init(); - for(head = world; (head = findfloat(head, team, self.team)); ) + for(entity head = world; (head = findfloat(head, team, this.team)); ) { if(head.flags & FL_ITEM) if(head.classname != "item_flag_team" && head.classname != "item_key_team") @@ -778,28 +788,29 @@ void Item_Touch (void) } else - e = self; + e = this; Item_ScheduleRespawn(e); } } -void Item_Reset() -{SELFPARAM(); - Item_Show(self, !self.state); - setorigin (self, self.origin); +void Item_Reset(entity this) +{ + Item_Show(this, !this.state); + setorigin(this, this.origin); - if(self.classname != "droppedweapon") + if (this.classname != "droppedweapon") { - self.think = Item_Think; - self.nextthink = time; + this.think = Item_Think; + this.nextthink = time; - if(self.waypointsprite_attached) - WaypointSprite_Kill(self.waypointsprite_attached); + if (this.waypointsprite_attached) + WaypointSprite_Kill(this.waypointsprite_attached); - if((self.flags & FL_POWERUP) || (self.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! - Item_ScheduleInitialRespawn(self); + if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! + Item_ScheduleInitialRespawn(this); } } +void Item_Reset_self() { SELFPARAM(); Item_Reset(this); } void Item_FindTeam() {SELFPARAM(); @@ -831,13 +842,13 @@ void Item_FindTeam() head.effects &= ~EF_NODRAW; } - Item_Reset(); + Item_Reset(self); } } // Savage: used for item garbage-collection // TODO: perhaps nice special effect? -void RemoveItem(void) +void RemoveItem() {SELFPARAM(); if(wasfreed(self) || !self) { return; } Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); @@ -971,238 +982,219 @@ void Item_Damage(entity inflictor, entity attacker, float damage, int deathtype, RemoveItem(); } -void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue) -{SELFPARAM(); - startitem_failed = false; - - if(self.model == "") - self.model = itemmodel; +void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter) +{ + string itemname = def.m_name; + Model itemmodel = def.m_model; + Sound pickupsound = def.m_sound; + float(entity player, entity item) pickupevalfunc = def.m_pickupevalfunc; + float pickupbasevalue = def.m_botvalue; + int itemflags = def.m_itemflags; - if(self.model == "") - { - error(strcat("^1Tried to spawn ", itemname, " with no model!\n")); - return; - } + startitem_failed = false; - if(self.item_pickupsound == "") - self.item_pickupsound = pickupsound; + this.item_model_ent = itemmodel; + this.item_pickupsound_ent = pickupsound; - if(!self.respawntime) // both need to be set + if(!this.respawntime) // both need to be set { - self.respawntime = defaultrespawntime; - self.respawntimejitter = defaultrespawntimejitter; + this.respawntime = defaultrespawntime; + this.respawntimejitter = defaultrespawntimejitter; } - self.items = itemid; - self.weapon = weaponid; + int itemid = def.m_itemid; + this.items = itemid; + int weaponid = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0; + this.weapon = weaponid; - if(!self.fade_end) + if(!this.fade_end) { - self.fade_start = autocvar_g_items_mindist; - self.fade_end = autocvar_g_items_maxdist; + this.fade_start = autocvar_g_items_mindist; + this.fade_end = autocvar_g_items_maxdist; } if(weaponid) - self.weapons = WepSet_FromWeapon(weaponid); + this.weapons = WepSet_FromWeapon(weaponid); - self.flags = FL_ITEM | itemflags; + this.flags = FL_ITEM | itemflags; - if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item + if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item { startitem_failed = true; - remove(self); + remove(this); return; } // is it a dropped weapon? - if (self.classname == "droppedweapon") + if (this.classname == "droppedweapon") { - self.reset = SUB_Remove; + this.reset = SUB_Remove; // it's a dropped weapon - self.movetype = MOVETYPE_TOSS; + this.movetype = MOVETYPE_TOSS; // Savage: remove thrown items after a certain period of time ("garbage collection") - self.think = RemoveItem; - self.nextthink = time + 20; + this.think = RemoveItem; + this.nextthink = time + 20; - self.takedamage = DAMAGE_YES; - self.event_damage = Item_Damage; + this.takedamage = DAMAGE_YES; + this.event_damage = Item_Damage; - if(self.strength_finished || self.invincible_finished || self.superweapons_finished) - /* - if(self.items == 0) - if(!(self.weapons & ~WEPSET_SUPERWEAPONS)) // only superweapons - if(self.ammo_nails == 0) - if(self.ammo_cells == 0) - if(self.ammo_rockets == 0) - if(self.ammo_shells == 0) - if(self.ammo_fuel == 0) - if(self.health == 0) - if(self.armorvalue == 0) - */ + if(this.strength_finished || this.invincible_finished || this.superweapons_finished) { // if item is worthless after a timer, have it expire then - self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished); + this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished); } // don't drop if in a NODROP zone (such as lava) - traceline(self.origin, self.origin, MOVE_NORMAL, self); + traceline(this.origin, this.origin, MOVE_NORMAL, this); if (trace_dpstartcontents & DPCONTENTS_NODROP) { startitem_failed = true; - remove(self); + remove(this); return; } } else { - if(!have_pickup_item()) + if(!have_pickup_item(this)) { startitem_failed = true; - remove (self); + remove (this); return; } - if(self.angles != '0 0 0') - self.SendFlags |= ISF_ANGLES; + if(this.angles != '0 0 0') + this.SendFlags |= ISF_ANGLES; - self.reset = Item_Reset; + this.reset = Item_Reset_self; // it's a level item - if(self.spawnflags & 1) - self.noalign = 1; - if (self.noalign > 0) - self.movetype = MOVETYPE_NONE; + if(this.spawnflags & 1) + this.noalign = 1; + if (this.noalign > 0) + this.movetype = MOVETYPE_NONE; else - self.movetype = MOVETYPE_TOSS; + this.movetype = MOVETYPE_TOSS; // do item filtering according to game mode and other things - if (self.noalign <= 0) + if (this.noalign <= 0) { // first nudge it off the floor a little bit to avoid math errors - setorigin(self, self.origin + '0 0 1'); + setorigin(this, this.origin + '0 0 1'); // set item size before we spawn a spawnfunc_waypoint - if((itemflags & FL_POWERUP) || self.health || self.armorvalue) - setsize (self, '-16 -16 0', '16 16 48'); - else - setsize (self, '-16 -16 0', '16 16 32'); - self.SendFlags |= ISF_SIZE; + setsize(this, def.m_mins, def.m_maxs); + this.SendFlags |= ISF_SIZE; // note droptofloor returns false if stuck/or would fall too far - if(!self.noalign) - droptofloor(); - waypoint_spawnforitem(self); + if (!this.noalign) + WITH(entity, self, this, droptofloor()); + waypoint_spawnforitem(this); } /* * can't do it that way, as it would break maps * TODO make a target_give like entity another way, that perhaps has * the weapon name in a key - if(self.targetname) + if(this.targetname) { // target_give not yet supported; maybe later - print("removed targeted ", self.classname, "\n"); + print("removed targeted ", this.classname, "\n"); startitem_failed = true; - remove (self); + remove (this); return; } */ if(autocvar_spawn_debug >= 2) { - entity otheritem; - for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain) + for(entity otheritem = findradius(this.origin, 3); otheritem; otheritem = otheritem.chain) { // why not flags & fl_item? if(otheritem.is_item) { - LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(self.origin)); + LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin)); LOG_TRACE(" vs ", otheritem.netname, vtos(otheritem.origin), "\n"); error("Mapper sucks."); } } - self.is_item = true; + this.is_item = true; } weaponsInMap |= WepSet_FromWeapon(weaponid); - precache_model (self.model); - precache_sound (self.item_pickupsound); + precache_model(this.model); + precache_sound(this.item_pickupsound); - if((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2))) - self.target = "###item###"; // for finding the nearest item using find() + if ( def.instanceOfPowerup + || def.instanceOfWeaponPickup + || (def.instanceOfHealth && def != ITEM_HealthSmall) + || (def.instanceOfArmor && def != ITEM_ArmorSmall) + || (itemid & (IT_KEY1 | IT_KEY2)) + ) this.target = "###item###"; // for finding the nearest item using find() - Item_ItemsTime_SetTime(self, 0); + Item_ItemsTime_SetTime(this, 0); } - self.bot_pickup = true; - self.bot_pickupevalfunc = pickupevalfunc; - self.bot_pickupbasevalue = pickupbasevalue; - self.mdl = self.model; - self.netname = itemname; - self.touch = Item_Touch; - setmodel(self, MDL_Null); // precision set below - //self.effects |= EF_LOWPRECISION; - - if((itemflags & FL_POWERUP) || self.health || self.armorvalue) - { - self.pos1 = '-16 -16 0'; - self.pos2 = '16 16 48'; - } - else - { - self.pos1 = '-16 -16 0'; - self.pos2 = '16 16 32'; - } - setsize (self, self.pos1, self.pos2); + this.bot_pickup = true; + this.bot_pickupevalfunc = pickupevalfunc; + this.bot_pickupbasevalue = pickupbasevalue; + this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str()); + this.netname = itemname; + this.touch = Item_Touch; + setmodel(this, MDL_Null); // precision set below + //this.effects |= EF_LOWPRECISION; - self.SendFlags |= ISF_SIZE; + setsize (this, this.pos1 = def.m_mins, this.pos2 = def.m_maxs); - if(!(self.spawnflags & 1024)) - { - if(itemflags & FL_POWERUP) - self.ItemStatus |= ITS_ANIMATE1; + this.SendFlags |= ISF_SIZE; - if(self.armorvalue || self.health) - self.ItemStatus |= ITS_ANIMATE2; + if (!(this.spawnflags & 1024)) { + if(def.instanceOfPowerup) + this.ItemStatus |= ITS_ANIMATE1; + + if(this.armorvalue || this.health) + this.ItemStatus |= ITS_ANIMATE2; } - if(itemflags & FL_WEAPON) + if(def.instanceOfWeaponPickup) { - if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely - self.colormap = 1024; // color shirt=0 pants=0 grey + if (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely + this.colormap = 1024; // color shirt=0 pants=0 grey else - self.gravity = 1; - - if(!(self.spawnflags & 1024)) - self.ItemStatus |= ITS_ANIMATE1; - self.ItemStatus |= ISF_COLORMAP; + this.gravity = 1; + if (!(this.spawnflags & 1024)) + this.ItemStatus |= ITS_ANIMATE1; + this.ItemStatus |= ISF_COLORMAP; } - self.state = 0; - if(self.team) // broken, no idea why. + this.state = 0; + if(this.team) // broken, no idea why. { - if(!self.cnt) - self.cnt = 1; // item probability weight + if(!this.cnt) + this.cnt = 1; // item probability weight - self.effects |= EF_NODRAW; // marker for item team search - InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET); + this.effects |= EF_NODRAW; // marker for item team search + InitializeEntity(this, Item_FindTeam, INITPRIO_FINDTARGET); } else - Item_Reset(); + Item_Reset(this); - Net_LinkEntity(self, !((itemflags & FL_POWERUP) || self.health || self.armorvalue), 0, ItemSend); + Net_LinkEntity(this, !(def.instanceOfPowerup || def.instanceOfHealth || def.instanceOfArmor), 0, ItemSend); // call this hook after everything else has been done - if(MUTATOR_CALLHOOK(Item_Spawn, self)) + if (MUTATOR_CALLHOOK(Item_Spawn, this)) { startitem_failed = true; - remove(self); + remove(this); return; } } -void StartItemA (entity a) -{SELFPARAM(); - self.itemdef = a; - StartItem(strzone(a.m_model.model_str()), a.m_sound, a.m_respawntime(), a.m_respawntimejitter(), a.m_name, a.m_itemid, 0, a.m_itemflags, a.m_pickupevalfunc, a.m_botvalue); +void StartItem(entity this, GameItem def) +{ + _StartItem( + this, + this.itemdef = def, + def.m_respawntime(), // defaultrespawntime + def.m_respawntimejitter() // defaultrespawntimejitter + ); } spawnfunc(item_rockets) @@ -1211,7 +1203,7 @@ spawnfunc(item_rockets) self.ammo_rockets = g_pickup_rockets; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_Rockets); + StartItem(this, ITEM_Rockets); } spawnfunc(item_bullets) @@ -1230,7 +1222,7 @@ spawnfunc(item_bullets) self.ammo_nails = g_pickup_nails; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_Bullets); + StartItem(this, ITEM_Bullets); } spawnfunc(item_cells) @@ -1239,7 +1231,7 @@ spawnfunc(item_cells) self.ammo_cells = g_pickup_cells; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_Cells); + StartItem(this, ITEM_Cells); } spawnfunc(item_plasma) @@ -1248,7 +1240,7 @@ spawnfunc(item_plasma) self.ammo_plasma = g_pickup_plasma; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_Plasma); + StartItem(this, ITEM_Plasma); } spawnfunc(item_shells) @@ -1267,7 +1259,7 @@ spawnfunc(item_shells) self.ammo_shells = g_pickup_shells; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_Shells); + StartItem(this, ITEM_Shells); } spawnfunc(item_armor_small) @@ -1278,7 +1270,7 @@ spawnfunc(item_armor_small) self.max_armorvalue = g_pickup_armorsmall_max; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_armorsmall_anyway; - StartItemA (ITEM_ArmorSmall); + StartItem(this, ITEM_ArmorSmall); } spawnfunc(item_armor_medium) @@ -1289,7 +1281,7 @@ spawnfunc(item_armor_medium) self.max_armorvalue = g_pickup_armormedium_max; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_armormedium_anyway; - StartItemA (ITEM_ArmorMedium); + StartItem(this, ITEM_ArmorMedium); } spawnfunc(item_armor_big) @@ -1300,7 +1292,7 @@ spawnfunc(item_armor_big) self.max_armorvalue = g_pickup_armorbig_max; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_armorbig_anyway; - StartItemA (ITEM_ArmorLarge); + StartItem(this, ITEM_ArmorLarge); } spawnfunc(item_armor_large) @@ -1311,7 +1303,7 @@ spawnfunc(item_armor_large) self.max_armorvalue = g_pickup_armorlarge_max; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_armorlarge_anyway; - StartItemA (ITEM_ArmorMega); + StartItem(this, ITEM_ArmorMega); } spawnfunc(item_health_small) @@ -1322,7 +1314,7 @@ spawnfunc(item_health_small) self.health = g_pickup_healthsmall; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_healthsmall_anyway; - StartItemA (ITEM_HealthSmall); + StartItem(this, ITEM_HealthSmall); } spawnfunc(item_health_medium) @@ -1333,7 +1325,7 @@ spawnfunc(item_health_medium) self.health = g_pickup_healthmedium; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_healthmedium_anyway; - StartItemA (ITEM_HealthMedium); + StartItem(this, ITEM_HealthMedium); } spawnfunc(item_health_large) @@ -1344,7 +1336,7 @@ spawnfunc(item_health_large) self.health = g_pickup_healthlarge; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_healthlarge_anyway; - StartItemA (ITEM_HealthLarge); + StartItem(this, ITEM_HealthLarge); } spawnfunc(item_health_mega) @@ -1355,7 +1347,7 @@ spawnfunc(item_health_mega) self.health = g_pickup_healthmega; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_healthmega_anyway; - StartItemA (ITEM_HealthMega); + StartItem(this, ITEM_HealthMega); } // support old misnamed entities @@ -1369,14 +1361,14 @@ spawnfunc(item_strength) { if(!self.strength_finished) self.strength_finished = autocvar_g_balance_powerup_strength_time; - StartItemA (ITEM_Strength); + StartItem(this, ITEM_Strength); } spawnfunc(item_invincible) { if(!self.invincible_finished) self.invincible_finished = autocvar_g_balance_powerup_invincible_time; - StartItemA (ITEM_Shield); + StartItem(this, ITEM_Shield); } // compatibility: @@ -1533,7 +1525,7 @@ spawnfunc(item_fuel) self.ammo_fuel = g_pickup_fuel; if(!self.pickup_anyway) self.pickup_anyway = g_pickup_ammo_anyway; - StartItemA (ITEM_JetpackFuel); + StartItem(this, ITEM_JetpackFuel); } spawnfunc(item_fuel_regen) @@ -1543,7 +1535,7 @@ spawnfunc(item_fuel_regen) spawnfunc_item_fuel(this); return; } - StartItemA (ITEM_JetpackRegen); + StartItem(this, ITEM_JetpackRegen); } spawnfunc(item_jetpack) @@ -1555,7 +1547,7 @@ spawnfunc(item_jetpack) spawnfunc_item_fuel(this); return; } - StartItemA (ITEM_Jetpack); + StartItem(this, ITEM_Jetpack); } float GiveWeapon(entity e, float wpn, float op, float val) @@ -1588,62 +1580,6 @@ float GiveWeapon(entity e, float wpn, float op, float val) return (v0 != v1); } -float GiveBit(entity e, .float fld, float bit, float op, float val) -{ - float v0, v1; - v0 = (e.(fld) & bit); - switch(op) - { - case OP_SET: - if(val > 0) - e.(fld) |= bit; - else - e.(fld) &= ~bit; - break; - case OP_MIN: - case OP_PLUS: - if(val > 0) - e.(fld) |= bit; - break; - case OP_MAX: - if(val <= 0) - e.(fld) &= ~bit; - break; - case OP_MINUS: - if(val > 0) - e.(fld) &= ~bit; - break; - } - v1 = (e.(fld) & bit); - return (v0 != v1); -} - -float GiveValue(entity e, .float fld, float op, float val) -{ - float v0, v1; - v0 = e.(fld); - switch(op) - { - case OP_SET: - e.(fld) = val; - break; - case OP_MIN: - e.(fld) = max(e.(fld), val); // min 100 cells = at least 100 cells - break; - case OP_MAX: - e.(fld) = min(e.(fld), val); - break; - case OP_PLUS: - e.(fld) += val; - break; - case OP_MINUS: - e.(fld) -= val; - break; - } - v1 = e.(fld); - return (v0 != v1); -} - void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string snd_decr) { if(v1 == v0) diff --git a/qcsrc/server/t_items.qh b/qcsrc/server/t_items.qh index 45b5f9e9c3..718944952c 100644 --- a/qcsrc/server/t_items.qh +++ b/qcsrc/server/t_items.qh @@ -25,7 +25,7 @@ const int ISF_SIZE = BIT(7); .float fade_end; #ifdef SVQC -void StartItemA (entity a); +void StartItem(entity this, entity a); #endif #ifdef CSQC @@ -45,8 +45,6 @@ string autocvar_cl_simpleitems_postfix = "_simple"; void ItemDraw(entity this); void ItemDrawSimple(entity this); -void ItemRead(float _IsNew); - #endif #ifdef SVQC spawnfunc(item_strength); @@ -60,7 +58,7 @@ float autocvar_sv_simple_items; bool ItemSend(entity this, entity to, int sf); -float have_pickup_item(void); +bool have_pickup_item(entity this); const float ITEM_RESPAWN_TICKS = 10; @@ -74,9 +72,9 @@ const float ITEM_RESPAWN_TICKS = 10; void Item_Show (entity e, float mode); -void Item_Respawn (void); +void Item_Respawn (); -void Item_RespawnCountdown (void); +void Item_RespawnCountdown (); void Item_ScheduleRespawnIn(entity e, float t); void Item_ScheduleRespawn(entity e); @@ -90,9 +88,9 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax float Item_GiveTo(entity item, entity player); -void Item_Touch (void); +void Item_Touch(); -void Item_Reset(); +void Item_Reset(entity this); void Item_FindTeam(); // Savage: used for item garbage-collection @@ -112,16 +110,10 @@ float commodity_pickupevalfunc(entity player, entity item); .float is_item; .entity itemdef; -void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue); - +void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter); -void target_items_use (void); -const float OP_SET = 0; -const float OP_MIN = 1; -const float OP_MAX = 2; -const float OP_PLUS = 3; -const float OP_MINUS = 4; +void target_items_use (); float GiveWeapon(entity e, float wpn, float op, float val); diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc index 94663fc365..82646da26a 100644 --- a/qcsrc/server/t_quake3.qc +++ b/qcsrc/server/t_quake3.qc @@ -1,6 +1,5 @@ #include "../common/weapons/all.qh" -#include "../common/buffs/all.qh" spawnfunc(weapon_crylink); spawnfunc(weapon_electro); @@ -155,10 +154,7 @@ spawnfunc(target_give) spawnfunc(item_flight) { - if(!cvar("g_buffs") || !cvar("g_buffs_flight")) - spawnfunc_item_jetpack(this); - else - buff_Init_Compat(self, BUFF_FLIGHT); + spawnfunc_item_jetpack(this); } .float notteam; diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 2f2bf54545..deb6032ce2 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -114,7 +114,7 @@ string GetClientVersionMessage() return versionmsg; } -string getwelcomemessage(void) +string getwelcomemessage() { string s, modifications, motd; @@ -132,8 +132,6 @@ string getwelcomemessage(void) modifications = strcat(modifications, ", No start weapons"); if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) modifications = strcat(modifications, ", Low gravity"); - if(g_cloaked && !g_cts) - modifications = strcat(modifications, ", Cloaked"); if(g_weapon_stay && !g_cts) modifications = strcat(modifications, ", Weapons stay"); if(g_jetpack) @@ -654,7 +652,7 @@ void SV_ChangeTeam(float _color) // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here if(!IS_CLIENT(self)) - Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_CONNECTING, self.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_CONNECTING, self.netname); SetPlayerTeam(self, dteam, steam, !IS_CLIENT(self)); diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index b934993235..6b3bdf0f2a 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -27,7 +27,7 @@ void InitGameplayMode(); string GetClientVersionMessage(); -string getwelcomemessage(void); +string getwelcomemessage(); void SetPlayerColors(entity pl, float _color); diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 7d1633f7f3..211ef8cec0 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -6,43 +6,32 @@ #include "../../common/util.qh" #include "../../common/weapons/all.qh" -float accuracy_byte(float n, float d) +int accuracy_byte(float n, float d) { - //printf("accuracy: %d / %d\n", n, d); - if(n <= 0) - return 0; - if(n > d) - return 255; + if (n <= 0) return 0; + if (n > d) return 255; return 1 + rint(n * 100.0 / d); } bool accuracy_send(entity this, entity to, int sf) { - int w, f; - entity a; - WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY); + WriteHeader(MSG_ENTITY, ENT_CLIENT_ACCURACY); - a = self.owner; - if(IS_SPEC(a)) - a = a.enemy; + entity a = this.owner; + if (IS_SPEC(a)) a = a.enemy; a = a.accuracy; - if(to != a.owner) - if (!(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share)) + if (to != a.owner) + if (!autocvar_sv_accuracy_data_share && !a.owner.cvar_cl_accuracy_data_share) sf = 0; // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy! WriteInt24_t(MSG_ENTITY, sf); - if(sf == 0) - return true; + if (sf == 0) return true; // note: we know that client and server agree about SendFlags... - for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) - { - if(sf & f) - WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w]))); - if(f == 0x800000) - f = 1; - else - f *= 2; + int f = 1; + for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) { + if (sf & f) WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w])); + f = (f == 0x800000) ? 1 : f * 2; } return true; } @@ -50,11 +39,11 @@ bool accuracy_send(entity this, entity to, int sf) // init/free void accuracy_init(entity e) { - e.accuracy = spawn(); - e.accuracy.owner = e; - e.accuracy.classname = "accuracy"; - e.accuracy.drawonlytoclient = e; - Net_LinkEntity(e.accuracy, false, 0, accuracy_send); + entity a = e.accuracy = new(accuracy); + make_pure(a); + a.owner = e; + a.drawonlytoclient = e; + Net_LinkEntity(a, false, 0, accuracy_send); } void accuracy_free(entity e) @@ -72,60 +61,53 @@ void accuracy_resend(entity e) .float hit_time; .float fired_time; -void accuracy_add(entity e, int w, float fired, float hit) +void accuracy_add(entity this, int w, int fired, int hit) { - entity a; - float b; - if(IS_INDEPENDENT_PLAYER(e)) - return; - a = e.accuracy; - if(!a || !(hit || fired)) - return; + if (IS_INDEPENDENT_PLAYER(this)) return; + entity a = this.accuracy; + if (!a) return; + if (!hit && !fired) return; w -= WEP_FIRST; - b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])); - if(hit) - a.(accuracy_hit[w]) += hit; - if(fired) - a.(accuracy_fired[w]) += fired; - - if(hit && a.hit_time != time) // only run this once per frame - { - a.(accuracy_cnt_hit[w]) += 1; + int b = accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w]); + if (hit) a.accuracy_hit [w] += hit; + if (fired) a.accuracy_fired[w] += fired; + + if (hit && a.hit_time != time) { // only run this once per frame + a.accuracy_cnt_hit[w] += 1; a.hit_time = time; } - if(fired && a.fired_time != time) // only run this once per frame - { - a.(accuracy_cnt_fired[w]) += 1; + if (fired && a.fired_time != time) { // only run this once per frame + a.accuracy_cnt_fired[w] += 1; a.fired_time = time; } - if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]))) - return; - w = pow(2, w % 24); - a.SendFlags |= w; - FOR_EACH_CLIENT(a) - if(IS_SPEC(a)) - if(a.enemy == e) - a.SendFlags |= w; + if (b == accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w])) return; // no change + int sf = 1 << (w % 24); + a.SendFlags |= sf; + entity e; FOR_EACH_CLIENT(e) if (IS_SPEC(e)) if (e.enemy == this) { + e.accuracy.SendFlags |= sf; + } } -float accuracy_isgooddamage(entity attacker, entity targ) +bool accuracy_isgooddamage(entity attacker, entity targ) { - float mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ); - - if(!warmup_stage) - if(targ.deadflag == DEAD_NO) - if(!targ.frozen) - if(mutator_check == MUT_ACCADD_INVALID || (mutator_check == MUT_ACCADD_VALID && IS_CLIENT(targ))) - if(DIFF_TEAM(attacker, targ)) - return true; - return false; + int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ); + + if (warmup_stage) return false; + if (targ.deadflag != DEAD_NO) return false; + if (targ.frozen) return false; + if (SAME_TEAM(attacker, targ)) return false; + + if (mutator_check == MUT_ACCADD_INVALID) return true; + + if (mutator_check != MUT_ACCADD_VALID) return false; + if (!IS_CLIENT(targ)) return false; + + return true; } -float accuracy_canbegooddamage(entity attacker) +bool accuracy_canbegooddamage(entity attacker) { - if(!warmup_stage) - return true; - return false; + return !warmup_stage; } diff --git a/qcsrc/server/weapons/accuracy.qh b/qcsrc/server/weapons/accuracy.qh index e8d22d98b1..33c1fbf392 100644 --- a/qcsrc/server/weapons/accuracy.qh +++ b/qcsrc/server/weapons/accuracy.qh @@ -1,8 +1,10 @@ #ifndef ACCURACY_H #define ACCURACY_H -.float cvar_cl_accuracy_data_share; -.float cvar_cl_accuracy_data_receive; +.bool cvar_cl_accuracy_data_share; +REPLICATE(cvar_cl_accuracy_data_share, bool, "cl_accuracy_data_share"); +.bool cvar_cl_accuracy_data_receive; +REPLICATE(cvar_cl_accuracy_data_receive, bool, "cl_accuracy_data_receive"); .entity accuracy; .float accuracy_frags[Weapons_MAX]; @@ -24,6 +26,6 @@ void accuracy_resend(entity e); void accuracy_add(entity e, float w, float fired, float hit); // helper -float accuracy_isgooddamage(entity attacker, entity targ); -float accuracy_canbegooddamage(entity attacker); +bool accuracy_isgooddamage(entity attacker, entity targ); +bool accuracy_canbegooddamage(entity attacker); #endif diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 5a5e30bf79..ef3d185c03 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -89,6 +89,8 @@ void W_PrepareExplosionByDamage(entity attacker, void() explode) self.realowner = attacker; } + MUTATOR_CALLHOOK(PrepareExplosionByDamage, self, attacker); + // do not explode NOW but in the NEXT FRAME! // because recursive calls to RadiusDamage are not allowed self.nextthink = time; diff --git a/qcsrc/server/weapons/csqcprojectile.qc b/qcsrc/server/weapons/csqcprojectile.qc index 1e254b804e..dfee551368 100644 --- a/qcsrc/server/weapons/csqcprojectile.qc +++ b/qcsrc/server/weapons/csqcprojectile.qc @@ -34,7 +34,7 @@ bool CSQCProjectile_SendEntity(entity this, entity to, int sf) if(self.gravity != 0) sf |= 0x10; - WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_PROJECTILE); WriteByte(MSG_ENTITY, sf); if(sf & 1) diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index b7eb2fe097..cfa68dc99e 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -12,8 +12,7 @@ void Send_WeaponComplain(entity e, float wpn, float type) { msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); + WriteHeader(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); WriteByte(MSG_ONE, wpn); WriteByte(MSG_ONE, type); } @@ -308,7 +307,7 @@ void W_PreviousWeapon(float list) } // previously used if exists and has ammo, (second) best otherwise -void W_LastWeapon(void) +void W_LastWeapon() {SELFPARAM(); if(client_hasweapon(self, self.cnt, true, false)) W_SwitchWeapon(self.cnt); diff --git a/qcsrc/server/weapons/selection.qh b/qcsrc/server/weapons/selection.qh index 69f644c70a..d51addd7d9 100644 --- a/qcsrc/server/weapons/selection.qh +++ b/qcsrc/server/weapons/selection.qh @@ -29,5 +29,5 @@ void W_NextWeapon(float list); void W_PreviousWeapon(float list); // previously used if exists and has ammo, (second) best otherwise -void W_LastWeapon(void); +void W_LastWeapon(); #endif diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc index 995154509c..244f6a5e6d 100644 --- a/qcsrc/server/weapons/spawning.qc +++ b/qcsrc/server/weapons/spawning.qc @@ -33,81 +33,75 @@ string W_Apply_Weaponreplace(string in) return substring(out, 1, -1); } -void weapon_defaultspawnfunc(float wpn) -{SELFPARAM(); - entity e; - float t; - string s; - float i, j; - int f; - - if(self.classname != "droppedweapon" && self.classname != "replacedweapon") +void weapon_defaultspawnfunc(entity this, Weapon e) +{ + int wpn = e.m_id; + if (this.classname != "droppedweapon" && this.classname != "replacedweapon") { - e = get_weaponinfo(wpn); - - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + if (e.spawnflags & WEP_FLAG_MUTATORBLOCKED) { objerror("Attempted to spawn a mutator-blocked weapon rejected"); startitem_failed = true; return; } - s = W_Apply_Weaponreplace(e.netname); - MUTATOR_CALLHOOK(SetWeaponreplace, self, e, s); + string s = W_Apply_Weaponreplace(e.netname); + MUTATOR_CALLHOOK(SetWeaponreplace, this, e, s); s = ret_string; - if(s == "") + if (s == "") { - remove(self); + remove(this); startitem_failed = true; return; } - t = tokenize_console(s); - if(t >= 2) + int t = tokenize_console(s); + if (t >= 2) { - self.team = --internalteam; - for(i = 1; i < t; ++i) + this.team = --internalteam; + for (int i = 1; i < t; ++i) { s = argv(i); - for(j = WEP_FIRST; j <= WEP_LAST; ++j) + int j; + for (j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - if(e.netname == s) + if (e.netname == s) { - setself(spawn()); - copyentity(this, self); - self.classname = "replacedweapon"; - weapon_defaultspawnfunc(j); + entity replacement = spawn(); + copyentity(this, replacement); + replacement.classname = "replacedweapon"; + weapon_defaultspawnfunc(replacement, e); break; } } - if(j > WEP_LAST) + if (j > WEP_LAST) { LOG_INFO("The weapon replace list for ", this.classname, " contains an unknown weapon ", s, ". Skipped.\n"); } } - setself(this); } - if(t >= 1) // always the case! + if (t >= 1) // always the case! { s = argv(0); wpn = 0; - for(j = WEP_FIRST; j <= WEP_LAST; ++j) + int j; + for (j = WEP_FIRST; j <= WEP_LAST; ++j) { e = get_weaponinfo(j); - if(e.netname == s) + if (e.netname == s) { wpn = j; break; } } - if(j > WEP_LAST) + if (j > WEP_LAST) { - LOG_INFO("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n"); + LOG_INFO("The weapon replace list for ", this.classname, " contains an unknown weapon ", s, ". Skipped.\n"); } } - if(wpn == 0) + if (wpn == 0) { - remove(self); + remove(this); startitem_failed = true; return; } @@ -115,73 +109,67 @@ void weapon_defaultspawnfunc(float wpn) e = get_weaponinfo(wpn); - if(!self.respawntime) + if (!this.respawntime) { - if(e.weapons & WEPSET_SUPERWEAPONS) + if (e.weapons & WEPSET_SUPERWEAPONS) { - self.respawntime = g_pickup_respawntime_superweapon; - self.respawntimejitter = g_pickup_respawntimejitter_superweapon; + this.respawntime = g_pickup_respawntime_superweapon; + this.respawntimejitter = g_pickup_respawntimejitter_superweapon; } else { - self.respawntime = g_pickup_respawntime_weapon; - self.respawntimejitter = g_pickup_respawntimejitter_weapon; + this.respawntime = g_pickup_respawntime_weapon; + this.respawntimejitter = g_pickup_respawntimejitter_weapon; } } - if(e.weapons & WEPSET_SUPERWEAPONS) - if(!self.superweapons_finished) - self.superweapons_finished = autocvar_g_balance_superweapons_time; + if (e.weapons & WEPSET_SUPERWEAPONS) + if (!this.superweapons_finished) + this.superweapons_finished = autocvar_g_balance_superweapons_time; // if we don't already have ammo, give us some ammo - if(!self.(e.ammo_field)) + if (!this.(e.ammo_field)) { - switch(e.ammo_field) + switch (e.ammo_field) { - case ammo_shells: self.ammo_shells = cvar("g_pickup_shells_weapon"); break; - case ammo_nails: self.ammo_nails = cvar("g_pickup_nails_weapon"); break; - case ammo_rockets: self.ammo_rockets = cvar("g_pickup_rockets_weapon"); break; - case ammo_cells: self.ammo_cells = cvar("g_pickup_cells_weapon"); break; - case ammo_plasma: self.ammo_plasma = cvar("g_pickup_plasma_weapon"); break; - case ammo_fuel: self.ammo_fuel = cvar("g_pickup_fuel_weapon"); break; + case ammo_shells: this.ammo_shells = cvar("g_pickup_shells_weapon"); break; + case ammo_nails: this.ammo_nails = cvar("g_pickup_nails_weapon"); break; + case ammo_rockets: this.ammo_rockets = cvar("g_pickup_rockets_weapon"); break; + case ammo_cells: this.ammo_cells = cvar("g_pickup_cells_weapon"); break; + case ammo_plasma: this.ammo_plasma = cvar("g_pickup_plasma_weapon"); break; + case ammo_fuel: this.ammo_fuel = cvar("g_pickup_fuel_weapon"); break; } } #if 0 // WEAPONTODO - if(e.items) + if (e.items) { - for(i = 0, j = 1; i < 24; ++i, j *= 2) + for (int i = 0, j = 1; i < 24; ++i, j <<= 1) { - if(e.items & j) + if (e.items & j) { ammotype = Item_CounterField(j); - if(!self.ammotype) - self.ammotype = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon")); + if (!this.ammotype) + this.ammotype = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon")); } } } #endif // pickup anyway - if(g_pickup_weapons_anyway) - self.pickup_anyway = true; - - f = FL_WEAPON; - - // no weapon-stay on superweapons - if(e.weapons & WEPSET_SUPERWEAPONS) - f |= FL_NO_WEAPON_STAY; - - // weapon stay isn't supported for teamed weapons - if(self.team) - f |= FL_NO_WEAPON_STAY; - - StartItem(strzone(e.m_model.model_str()), string_null, self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); - self.item_pickupsound_ent = SND_WEAPONPICKUP; + if (g_pickup_weapons_anyway) + this.pickup_anyway = true; + + GameItem def = e.m_pickup; + _StartItem( + this, + this.itemdef = def, + this.respawntime, // defaultrespawntime + this.respawntimejitter // defaultrespawntimejitter + ); #if 0 // WEAPONTODO - if (self.modelindex) { // don't precache if self was removed - Weapon w = get_weaponinfo(e.weapon); - w.wr_init(w); + if (this.modelindex) { // don't precache if this was removed + e.wr_init(e); } #endif } diff --git a/qcsrc/server/weapons/spawning.qh b/qcsrc/server/weapons/spawning.qh index e9cfad94eb..fd66a8e87b 100644 --- a/qcsrc/server/weapons/spawning.qh +++ b/qcsrc/server/weapons/spawning.qh @@ -3,5 +3,5 @@ string W_Apply_Weaponreplace(string in); -void weapon_defaultspawnfunc(float wpn); +void weapon_defaultspawnfunc(entity this, Weapon e); #endif diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc index 9e1b3e4dc9..ef98f69d38 100644 --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@ -34,12 +34,12 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto {SELFPARAM(); float thisammo, i; string s; - var .int ammotype = (get_weaponinfo(wpn)).ammo_field; + Weapon info = get_weaponinfo(wpn); + var .int ammotype = info.ammo_field; - entity wep = spawn(); + entity wep = new(droppedweapon); setorigin(wep, org); - wep.classname = "droppedweapon"; wep.velocity = velo; wep.owner = wep.enemy = own; wep.flags |= FL_TOSSED; @@ -75,7 +75,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto } } - WITH(entity, self, wep, weapon_defaultspawnfunc(wpn)); + weapon_defaultspawnfunc(wep, info); if(startitem_failed) return string_null; wep.glowmod = own.weaponentity_glowmod; @@ -171,7 +171,8 @@ void W_ThrowWeapon(vector velo, vector delta, float doreduce) return; if(!autocvar_g_weapon_throwable) return; - if(self.weaponentity.state != WS_READY) + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if(self.(weaponentity).state != WS_READY) return; if(!W_IsWeaponThrowable(w)) return; diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index 4c9e0e34cf..ea0faa845b 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -55,8 +55,10 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m W_HitPlotAnalysis(ent, v_forward, v_right, v_up); - if(ent.weaponentity.movedir.x > 0) - vecs = ent.weaponentity.movedir; + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + vector md = ent.(weaponentity).movedir; + if(md.x > 0) + vecs = md; else vecs = '0 0 0'; @@ -357,11 +359,11 @@ void fireBullet(vector start, vector dir, float spread, float max_solid_penetrat float total_damage = 0; if(tracereffects & EF_RED) - fireBullet_trace_callback_eff = particleeffectnum(EFFECT_RIFLE); + fireBullet_trace_callback_eff = EFFECT_RIFLE; else if(tracereffects & EF_BLUE) - fireBullet_trace_callback_eff = particleeffectnum(EFFECT_RIFLE_WEAK); + fireBullet_trace_callback_eff = EFFECT_RIFLE_WEAK; else - fireBullet_trace_callback_eff = particleeffectnum(EFFECT_BULLET); + fireBullet_trace_callback_eff = EFFECT_BULLET; float lag = ANTILAG_LATENCY(self); if(lag < 0.001) diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh index b2ab7d0822..c4f63835c3 100644 --- a/qcsrc/server/weapons/tracing.qh +++ b/qcsrc/server/weapons/tracing.qh @@ -52,7 +52,7 @@ void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float p .vector railgunforce; void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, int deathtype); -float fireBullet_trace_callback_eff; +entity fireBullet_trace_callback_eff; entity fireBullet_last_hit; void fireBullet_trace_callback(vector start, vector hit, vector end); void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects); diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 7fa1450868..3ea45ad468 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -16,11 +16,14 @@ vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn) { - switch(algn) + switch (algn) { default: case 3: break; // right alignment - case 4: vecs.y = -vecs.y; break; // left - case 1: case 2: vecs.y = 0; vecs.z -= 2; break; // center + case 4: vecs.y = -vecs.y; + break; // left + case 1: case 2: vecs.y = 0; + vecs.z -= 2; + break; // center } return vecs; @@ -30,25 +33,31 @@ vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn { string s; - if(visual) + if (visual) + { vecs = shotorg_adjustfromclient(vecs, y_is_right, algn); - else if(autocvar_g_shootfromeye) + } + else if (autocvar_g_shootfromeye) + { vecs.y = vecs.z = 0; - else if(autocvar_g_shootfromcenter) + } + else if (autocvar_g_shootfromcenter) { vecs.y = 0; vecs.z -= 2; } - else if((s = autocvar_g_shootfromfixedorigin) != "") + else if ((s = autocvar_g_shootfromfixedorigin) != "") { vector v = stov(s); - if(y_is_right) { v.y = -v.y; } - if(v.x != 0) { vecs.x = v.x; } + if (y_is_right) v.y = -v.y; + if (v.x != 0) vecs.x = v.x; vecs.y = v.y; vecs.z = v.z; } - else // just do the same as top + else // just do the same as top + { vecs = shotorg_adjustfromclient(vecs, y_is_right, algn); + } return vecs; } @@ -83,14 +92,14 @@ float W_WeaponSpeedFactor() } -void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func); +void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor, + .entity weaponentity, int fire) func); -float CL_Weaponentity_CustomizeEntityForClient() -{SELFPARAM(); - self.viewmodelforclient = self.owner; - if(IS_SPEC(other)) - if(other.enemy == self.owner) - self.viewmodelforclient = other; +bool CL_Weaponentity_CustomizeEntityForClient() +{ + SELFPARAM(); + this.viewmodelforclient = this.owner; + if (IS_SPEC(other) && other.enemy == this.owner) this.viewmodelforclient = other; return true; } @@ -137,395 +146,385 @@ float CL_Weaponentity_CustomizeEntityForClient() */ // writes: -// self.origin, self.angles -// self.weaponentity -// self.movedir, self.view_ofs +// this.origin, this.angles +// this.weaponentity +// this.movedir, this.view_ofs // attachment stuff // anim stuff // to free: // call again with "" // remove the ent -void CL_WeaponEntity_SetModel(string name) -{SELFPARAM(); - float v_shot_idx; +void CL_WeaponEntity_SetModel(entity this, .entity weaponentity, string name) +{ if (name != "") { // if there is a child entity, hide it until we're sure we use it - if (self.weaponentity) - self.weaponentity.model = ""; - _setmodel(self, W_Model(strcat("v_", name, ".md3"))); - v_shot_idx = gettagindex(self, "shot"); // used later - if(!v_shot_idx) - v_shot_idx = gettagindex(self, "tag_shot"); - - _setmodel(self, W_Model(strcat("h_", name, ".iqm"))); + if (this.(weaponentity)) this.(weaponentity).model = ""; + _setmodel(this, W_Model(strcat("v_", name, ".md3"))); + int v_shot_idx = gettagindex(this, "shot"); // used later + if (!v_shot_idx) v_shot_idx = gettagindex(this, "tag_shot"); + + _setmodel(this, W_Model(strcat("h_", name, ".iqm"))); // preset some defaults that work great for renamed zym files (which don't need an animinfo) - self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0'); - self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0'); - self.anim_idle = animfixfps(self, '2 1 0.01', '0 0 0'); - self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0'); + this.anim_fire1 = animfixfps(this, '0 1 0.01', '0 0 0'); + this.anim_fire2 = animfixfps(this, '1 1 0.01', '0 0 0'); + this.anim_idle = animfixfps(this, '2 1 0.01', '0 0 0'); + this.anim_reload = animfixfps(this, '3 1 0.01', '0 0 0'); // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model) // if we don't, this is a "real" animated model - if(gettagindex(self, "weapon")) + if (gettagindex(this, "weapon")) { - if (!self.weaponentity) - self.weaponentity = spawn(); - _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3"))); - setattachment(self.weaponentity, self, "weapon"); + if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity); + _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3"))); + setattachment(this.(weaponentity), this, "weapon"); } - else if(gettagindex(self, "tag_weapon")) + else if (gettagindex(this, "tag_weapon")) { - if (!self.weaponentity) - self.weaponentity = spawn(); - _setmodel(self.weaponentity, W_Model(strcat("v_", name, ".md3"))); - setattachment(self.weaponentity, self, "tag_weapon"); + if (!this.(weaponentity)) this.(weaponentity) = new(weaponentity); + _setmodel(this.(weaponentity), W_Model(strcat("v_", name, ".md3"))); + setattachment(this.(weaponentity), this, "tag_weapon"); } else { - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; + if (this.(weaponentity)) remove(this.(weaponentity)); + this.(weaponentity) = NULL; } - setorigin(self,'0 0 0'); - self.angles = '0 0 0'; - self.frame = 0; - self.viewmodelforclient = world; + setorigin(this, '0 0 0'); + this.angles = '0 0 0'; + this.frame = 0; + this.viewmodelforclient = NULL; float idx; - if(v_shot_idx) // v_ model attached to invisible h_ model + if (v_shot_idx) // v_ model attached to invisible h_ model { - self.movedir = gettaginfo(self.weaponentity, v_shot_idx); + this.movedir = gettaginfo(this.(weaponentity), v_shot_idx); } else { - idx = gettagindex(self, "shot"); - if(!idx) - idx = gettagindex(self, "tag_shot"); - if(idx) - self.movedir = gettaginfo(self, idx); + idx = gettagindex(this, "shot"); + if (!idx) idx = gettagindex(this, "tag_shot"); + if (idx) + { + this.movedir = gettaginfo(this, idx); + } else { - LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); - self.movedir = '0 0 0'; + LOG_INFO("WARNING: weapon model ", this.model, + " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); + this.movedir = '0 0 0'; } } - if(self.weaponentity) // v_ model attached to invisible h_ model + if (this.(weaponentity)) // v_ model attached to invisible h_ model { - idx = gettagindex(self.weaponentity, "shell"); - if(!idx) - idx = gettagindex(self.weaponentity, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self.weaponentity, idx); + idx = gettagindex(this.(weaponentity), "shell"); + if (!idx) idx = gettagindex(this.(weaponentity), "tag_shell"); + if (idx) this.spawnorigin = gettaginfo(this.(weaponentity), idx); } else + { idx = 0; - if(!idx) + } + if (!idx) { - idx = gettagindex(self, "shell"); - if(!idx) - idx = gettagindex(self, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self, idx); + idx = gettagindex(this, "shell"); + if (!idx) idx = gettagindex(this, "tag_shell"); + if (idx) + { + this.spawnorigin = gettaginfo(this, idx); + } else { - LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n"); - self.spawnorigin = self.movedir; + LOG_INFO("WARNING: weapon model ", this.model, + " does not support the 'shell' tag, will display casings wrong\n"); + this.spawnorigin = this.movedir; } } - if(v_shot_idx) + if (v_shot_idx) { - self.oldorigin = '0 0 0'; // use regular attachment + this.oldorigin = '0 0 0'; // use regular attachment } else { - if(self.weaponentity) + if (this.(weaponentity)) { - idx = gettagindex(self, "weapon"); - if(!idx) - idx = gettagindex(self, "tag_weapon"); + idx = gettagindex(this, "weapon"); + if (!idx) idx = gettagindex(this, "tag_weapon"); } else { - idx = gettagindex(self, "handle"); - if(!idx) - idx = gettagindex(self, "tag_handle"); + idx = gettagindex(this, "handle"); + if (!idx) idx = gettagindex(this, "tag_handle"); } - if(idx) + if (idx) { - self.oldorigin = self.movedir - gettaginfo(self, idx); + this.oldorigin = this.movedir - gettaginfo(this, idx); } else { - LOG_INFO("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n"); - self.oldorigin = '0 0 0'; // there is no way to recover from this + LOG_INFO("WARNING: weapon model ", this.model, + " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n"); + this.oldorigin = '0 0 0'; // there is no way to recover from this } } - self.viewmodelforclient = self.owner; + this.viewmodelforclient = this.owner; } else { - self.model = ""; - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; - self.movedir = '0 0 0'; - self.spawnorigin = '0 0 0'; - self.oldorigin = '0 0 0'; - self.anim_fire1 = '0 1 0.01'; - self.anim_fire2 = '0 1 0.01'; - self.anim_idle = '0 1 0.01'; - self.anim_reload = '0 1 0.01'; + this.model = ""; + if (this.(weaponentity)) remove(this.(weaponentity)); + this.(weaponentity) = NULL; + this.movedir = '0 0 0'; + this.spawnorigin = '0 0 0'; + this.oldorigin = '0 0 0'; + this.anim_fire1 = '0 1 0.01'; + this.anim_fire2 = '0 1 0.01'; + this.anim_idle = '0 1 0.01'; + this.anim_reload = '0 1 0.01'; } - self.view_ofs = '0 0 0'; + this.view_ofs = '0 0 0'; - if(self.movedir.x >= 0) + if (this.movedir.x >= 0) { - vector v0; - v0 = self.movedir; - self.movedir = shotorg_adjust(v0, false, false, self.owner.cvar_cl_gunalign); - self.view_ofs = shotorg_adjust(v0, false, true, self.owner.cvar_cl_gunalign) - v0; + vector v0 = this.movedir; + this.movedir = shotorg_adjust(v0, false, false, this.owner.cvar_cl_gunalign); + this.view_ofs = shotorg_adjust(v0, false, true, this.owner.cvar_cl_gunalign) - v0; } - self.owner.stat_shotorg = compressShotOrigin(self.movedir); - self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly + this.owner.stat_shotorg = compressShotOrigin(this.movedir); + this.movedir = decompressShotOrigin(this.owner.stat_shotorg); // make them match perfectly - self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount + this.spawnorigin += this.view_ofs; // offset the casings origin by the same amount // check if an instant weapon switch occurred - setorigin(self, self.view_ofs); + setorigin(this, this.view_ofs); // reset animstate now - self.wframe = WFRAME_IDLE; - setanim(self, self.anim_idle, true, false, true); + this.wframe = WFRAME_IDLE; + setanim(this, this.anim_idle, true, false, true); } vector CL_Weapon_GetShotOrg(float wpn) -{SELFPARAM(); - entity wi = get_weaponinfo(wpn); - setself(spawn()); - CL_WeaponEntity_SetModel(wi.mdl); - vector ret = self.movedir; - CL_WeaponEntity_SetModel(""); - remove(self); - setself(this); +{ + entity wi = Weapons_from(wpn); + entity e = spawn(); + .entity weaponentity = weaponentities[0]; + CL_WeaponEntity_SetModel(e, weaponentity, wi.mdl); + vector ret = e.movedir; + CL_WeaponEntity_SetModel(e, weaponentity, ""); + remove(e); return ret; } +..entity weaponentity_fld; + void CL_Weaponentity_Think() -{SELFPARAM(); - int tb; - self.nextthink = time; - if (intermission_running) - self.frame = self.anim_idle.x; - if (self.owner.weaponentity != self) +{ + SELFPARAM(); + this.nextthink = time; + if (intermission_running) this.frame = this.anim_idle.x; + .entity weaponentity = this.weaponentity_fld; + if (this.owner.(weaponentity) != this) { - if (self.weaponentity) - remove(self.weaponentity); - remove(self); + if (this.(weaponentity)) remove(this.(weaponentity)); + remove(this); return; } - if (self.owner.deadflag != DEAD_NO) + if (this.owner.deadflag != DEAD_NO) { - self.model = ""; - if (self.weaponentity) - self.weaponentity.model = ""; + this.model = ""; + if (this.(weaponentity)) this.(weaponentity).model = ""; return; } - if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex + || this.deadflag != this.owner.deadflag) { - self.weaponname = self.owner.weaponname; - self.dmg = self.owner.modelindex; - self.deadflag = self.owner.deadflag; + this.weaponname = this.owner.weaponname; + this.dmg = this.owner.modelindex; + this.deadflag = this.owner.deadflag; - CL_WeaponEntity_SetModel(self.owner.weaponname); + CL_WeaponEntity_SetModel(this, weaponentity, this.owner.weaponname); } - tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT)); - self.effects = self.owner.effects & EFMASK_CHEAP; - self.effects &= ~EF_LOWPRECISION; - self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it - self.effects &= ~EF_TELEPORT_BIT; - self.effects &= ~EF_RESTARTANIM_BIT; - self.effects |= tb; - - if(self.owner.alpha == default_player_alpha) - self.alpha = default_weapon_alpha; - else if(self.owner.alpha != 0) - self.alpha = self.owner.alpha; - else - self.alpha = 1; - - self.glowmod = self.owner.weaponentity_glowmod; - self.colormap = self.owner.colormap; - if (self.weaponentity) + int tb = (this.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT)); + this.effects = this.owner.effects & EFMASK_CHEAP; + this.effects &= ~EF_LOWPRECISION; + this.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it + this.effects &= ~EF_TELEPORT_BIT; + this.effects &= ~EF_RESTARTANIM_BIT; + this.effects |= tb; + if (weaponentity != weaponentities[0]) this.effects |= EF_NODRAW; + + if (this.owner.alpha == default_player_alpha) this.alpha = default_weapon_alpha; + else if (this.owner.alpha != 0) this.alpha = this.owner.alpha; + else this.alpha = 1; + + this.glowmod = this.owner.weaponentity_glowmod; + this.colormap = this.owner.colormap; + if (this.(weaponentity)) { - self.weaponentity.effects = self.effects; - self.weaponentity.alpha = self.alpha; - self.weaponentity.colormap = self.colormap; - self.weaponentity.glowmod = self.glowmod; + this.(weaponentity).effects = this.effects; + this.(weaponentity).alpha = this.alpha; + this.(weaponentity).colormap = this.colormap; + this.(weaponentity).glowmod = this.glowmod; } - self.angles = '0 0 0'; + this.angles = '0 0 0'; - float f = (self.owner.weapon_nextthink - time); - if (self.state == WS_RAISE && !intermission_running) + float f = this.weapon_nextthink - time; + if (this.state == WS_RAISE && !intermission_running) { - entity newwep = get_weaponinfo(self.owner.switchweapon); + entity newwep = Weapons_from(this.owner.switchweapon); f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise); - self.angles_x = -90 * f * f; + this.angles_x = -90 * f * f; } - else if (self.state == WS_DROP && !intermission_running) + else if (this.state == WS_DROP && !intermission_running) { - entity oldwep = get_weaponinfo(self.owner.weapon); + entity oldwep = Weapons_from(this.owner.weapon); f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop); - self.angles_x = -90 * f * f; + this.angles_x = -90 * f * f; } - else if (self.state == WS_CLEAR) + else if (this.state == WS_CLEAR) { f = 1; - self.angles_x = -90 * f * f; + this.angles_x = -90 * f * f; } } void CL_ExteriorWeaponentity_Think() -{SELFPARAM(); - float tag_found; - self.nextthink = time; - if (self.owner.exteriorweaponentity != self) +{ + SELFPARAM(); + this.nextthink = time; + if (this.owner.exteriorweaponentity != this) { - remove(self); + remove(this); return; } - if (self.owner.deadflag != DEAD_NO) + if (this.owner.deadflag != DEAD_NO) { - self.model = ""; + this.model = ""; return; } - if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + if (this.weaponname != this.owner.weaponname || this.dmg != this.owner.modelindex + || this.deadflag != this.owner.deadflag) { - self.weaponname = self.owner.weaponname; - self.dmg = self.owner.modelindex; - self.deadflag = self.owner.deadflag; - if (self.owner.weaponname != "") - _setmodel(self, W_Model(strcat("v_", self.owner.weaponname, ".md3"))); - else - self.model = ""; - - if((tag_found = gettagindex(self.owner, "tag_weapon"))) + this.weaponname = this.owner.weaponname; + this.dmg = this.owner.modelindex; + this.deadflag = this.owner.deadflag; + if (this.owner.weaponname != "") _setmodel(this, W_Model(strcat("v_", this.owner.weaponname, ".md3"))); + else this.model = ""; + + int tag_found; + if ((tag_found = gettagindex(this.owner, "tag_weapon"))) { - self.tag_index = tag_found; - self.tag_entity = self.owner; + this.tag_index = tag_found; + this.tag_entity = this.owner; } else - setattachment(self, self.owner, "bip01 r hand"); + { + setattachment(this, this.owner, "bip01 r hand"); + } } - self.effects = self.owner.effects; - self.effects |= EF_LOWPRECISION; - self.effects = self.effects & EFMASK_CHEAP; // eat performance - if(self.owner.alpha == default_player_alpha) - self.alpha = default_weapon_alpha; - else if(self.owner.alpha != 0) - self.alpha = self.owner.alpha; - else - self.alpha = 1; + this.effects = this.owner.effects; + this.effects |= EF_LOWPRECISION; + this.effects = this.effects & EFMASK_CHEAP; // eat performance + if (this.owner.alpha == default_player_alpha) this.alpha = default_weapon_alpha; + else if (this.owner.alpha != 0) this.alpha = this.owner.alpha; + else this.alpha = 1; - self.glowmod = self.owner.weaponentity_glowmod; - self.colormap = self.owner.colormap; + this.glowmod = this.owner.weaponentity_glowmod; + this.colormap = this.owner.colormap; - CSQCMODEL_AUTOUPDATE(self); + CSQCMODEL_AUTOUPDATE(this); } // spawning weaponentity for client -void CL_SpawnWeaponentity(entity e) +void CL_SpawnWeaponentity(entity e, .entity weaponentity) { - entity view = e.weaponentity = spawn(); - view.classname = "weaponentity"; + entity view = e.(weaponentity) = new(weaponentity); + make_pure(view); view.solid = SOLID_NOT; view.owner = e; - setmodel(view, MDL_Null); // precision set when changed + setmodel(view, MDL_Null); // precision set when changed setorigin(view, '0 0 0'); view.angles = '0 0 0'; view.viewmodelforclient = e; view.flags = 0; + view.weaponentity_fld = weaponentity; view.think = CL_Weaponentity_Think; view.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient; view.nextthink = time; - entity exterior = e.exteriorweaponentity = spawn(); - exterior.classname = "exteriorweaponentity"; - exterior.solid = SOLID_NOT; - exterior.exteriorweaponentity = exterior; - exterior.owner = e; - setorigin(exterior, '0 0 0'); - exterior.angles = '0 0 0'; - exterior.think = CL_ExteriorWeaponentity_Think; - exterior.nextthink = time; - - CSQCMODEL_AUTOINIT(exterior); + if (weaponentity == weaponentities[0]) + { + entity exterior = e.exteriorweaponentity = new(exteriorweaponentity); + make_pure(exterior); + exterior.solid = SOLID_NOT; + exterior.exteriorweaponentity = exterior; + exterior.owner = e; + setorigin(exterior, '0 0 0'); + exterior.angles = '0 0 0'; + exterior.think = CL_ExteriorWeaponentity_Think; + exterior.nextthink = time; + + CSQCMODEL_AUTOINIT(exterior); + } } // Weapon subs -void w_clear(Weapon thiswep, entity actor, bool fire1, bool fire2) +void w_clear(Weapon thiswep, entity actor, .entity weaponentity, int fire) { if (actor.weapon != -1) { actor.weapon = 0; actor.switchingweapon = 0; } - if (actor.weaponentity) + entity this = actor.(weaponentity); + if (this) { - actor.weaponentity.state = WS_CLEAR; - actor.weaponentity.effects = 0; + this.state = WS_CLEAR; + this.effects = 0; } } -void w_ready(Weapon thiswep, entity actor, bool fire1, bool fire2) +void w_ready(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - if (actor.weaponentity) - actor.weaponentity.state = WS_READY; - weapon_thinkf(actor, WFRAME_IDLE, 1000000, w_ready); + entity this = actor.(weaponentity); + if (this) this.state = WS_READY; + weapon_thinkf(actor, weaponentity, WFRAME_IDLE, 1000000, w_ready); } .float prevdryfire; .float prevwarntime; -bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, float secondary) +bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary) { if ((actor.items & IT_UNLIMITED_WEAPON_AMMO)) return true; bool ammo = false; - if (secondary) { - WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep)); - } else { - WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep)); - } + if (secondary) WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep)); + else WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep)); if (ammo) return true; // always keep the Mine Layer if we placed mines, so that we can detonate them - entity mine; - if(actor.weapon == WEP_MINE_LAYER.m_id) - for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == actor) - return false; + if (thiswep == WEP_MINE_LAYER) + for (entity mine; (mine = find(mine, classname, "mine")); ) + if (mine.owner == actor) return false; - if(actor.weapon == WEP_SHOTGUN.m_id) - if(!secondary && WEP_CVAR(shotgun, secondary) == 1) - return false; // no clicking, just allow + if (thiswep == WEP_SHOTGUN) + if (!secondary && WEP_CVAR(shotgun, secondary) == 1) return false; // no clicking, just allow - if(actor.weapon == actor.switchweapon && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons + if (thiswep == Weapons_from(actor.switchweapon) && time - actor.prevdryfire > 1) // only play once BEFORE starting to switch weapons { - sound (actor, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM); + sound(actor, CH_WEAPON_A, SND_DRYFIRE, VOL_BASE, ATTEN_NORM); actor.prevdryfire = time; } // check if the other firing mode has enough ammo bool ammo_other = false; - if (secondary) { - WITH(entity, self, actor, ammo = thiswep.wr_checkammo1(thiswep)); - } else { - WITH(entity, self, actor, ammo = thiswep.wr_checkammo2(thiswep)); - } + if (secondary) WITH(entity, self, actor, ammo_other = thiswep.wr_checkammo1(thiswep)); + else WITH(entity, self, actor, ammo_other = thiswep.wr_checkammo2(thiswep)); if (ammo_other) { if (time - actor.prevwarntime > 1) @@ -535,277 +534,283 @@ bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, float secondar actor, MSG_MULTI, ITEM_WEAPON_PRIMORSEC, - actor.weapon, + thiswep.m_id, secondary, (1 - secondary) - ); + ); } actor.prevwarntime = time; } - else // this weapon is totally unable to fire, switch to another one + else // this weapon is totally unable to fire, switch to another one { W_SwitchToOtherWeapon(actor); } return false; } + .float race_penalty; -bool weapon_prepareattack_check(Weapon thiswep, entity actor, bool secondary, float attacktime) +bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime) { - if(!weapon_prepareattack_checkammo(thiswep, actor, secondary)) - return false; + if (!weapon_prepareattack_checkammo(thiswep, actor, secondary)) return false; - //if sv_ready_restart_after_countdown is set, don't allow the player to shoot - //if all players readied up and the countdown is running - if(time < game_starttime || time < actor.race_penalty) { - return false; - } + // if sv_ready_restart_after_countdown is set, don't allow the player to shoot + // if all players readied up and the countdown is running + if (time < game_starttime || time < actor.race_penalty) return false; - if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused + if (timeout_status == TIMEOUT_ACTIVE) // don't allow the player to shoot while game is paused return false; // do not even think about shooting if switching - if(actor.switchweapon != actor.weapon) - return false; + if (actor.switchweapon != actor.weapon) return false; - if(attacktime >= 0) + if (attacktime >= 0) { + int slot = weaponslot(weaponentity); // don't fire if previous attack is not finished - if (ATTACK_FINISHED(actor) > time + actor.weapon_frametime * 0.5) - return false; + if (ATTACK_FINISHED(actor, slot) > time + actor.weapon_frametime * 0.5) return false; + entity this = actor.(weaponentity); // don't fire while changing weapon - if (actor.weaponentity.state != WS_READY) - return false; + if (this.state != WS_READY) return false; } return true; } -void weapon_prepareattack_do(entity actor, bool secondary, float attacktime) + +void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary, float attacktime) { - actor.weaponentity.state = WS_INUSE; + entity this = actor.(weaponentity); + this.state = WS_INUSE; - actor.spawnshieldtime = min(actor.spawnshieldtime, time); // kill spawn shield when you fire + actor.spawnshieldtime = min(actor.spawnshieldtime, time); // kill spawn shield when you fire // if the weapon hasn't been firing continuously, reset the timer - if(attacktime >= 0) + if (attacktime >= 0) { - if (ATTACK_FINISHED(actor) < time - actor.weapon_frametime * 1.5) + int slot = weaponslot(weaponentity); + if (ATTACK_FINISHED(actor, slot) < time - actor.weapon_frametime * 1.5) { - ATTACK_FINISHED(actor) = time; - //dprint("resetting attack finished to ", ftos(time), "\n"); + ATTACK_FINISHED(actor, slot) = time; + // dprint("resetting attack finished to ", ftos(time), "\n"); } - ATTACK_FINISHED(actor) = ATTACK_FINISHED(actor) + attacktime * W_WeaponRateFactor(); + ATTACK_FINISHED(actor, slot) = ATTACK_FINISHED(actor, slot) + attacktime * W_WeaponRateFactor(); } actor.bulletcounter += 1; - //dprint("attack finished ", ftos(ATTACK_FINISHED(actor)), "\n"); + // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, slot)), "\n"); } -bool weapon_prepareattack(Weapon thiswep, entity actor, bool secondary, float attacktime) + +bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime) { - if (weapon_prepareattack_check(thiswep, actor, secondary, attacktime)) { - weapon_prepareattack_do(actor, secondary, attacktime); + if (weapon_prepareattack_check(thiswep, actor, weaponentity, secondary, attacktime)) + { + weapon_prepareattack_do(actor, weaponentity, secondary, attacktime); return true; } return false; } -void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func) +void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor, + .entity weaponentity, int fire) func) { - vector a; - vector of, or, ou; - float restartanim; - - if(fr == WFRAME_DONTCHANGE) + entity this = actor.(weaponentity); + bool restartanim; + if (fr == WFRAME_DONTCHANGE) { - fr = actor.weaponentity.wframe; + fr = this.wframe; restartanim = false; } else if (fr == WFRAME_IDLE) + { restartanim = false; + } else + { restartanim = true; + } - of = v_forward; - or = v_right; - ou = v_up; + vector of = v_forward; + vector or = v_right; + vector ou = v_up; - if (actor.weaponentity) + if (this) { - actor.weaponentity.wframe = fr; - a = '0 0 0'; - if (fr == WFRAME_IDLE) - a = actor.weaponentity.anim_idle; - else if (fr == WFRAME_FIRE1) - a = actor.weaponentity.anim_fire1; - else if (fr == WFRAME_FIRE2) - a = actor.weaponentity.anim_fire2; - else // if (fr == WFRAME_RELOAD) - a = actor.weaponentity.anim_reload; + this.wframe = fr; + vector a = '0 0 0'; + if (fr == WFRAME_IDLE) a = this.anim_idle; + else if (fr == WFRAME_FIRE1) a = this.anim_fire1; + else if (fr == WFRAME_FIRE2) a = this.anim_fire2; + else // if (fr == WFRAME_RELOAD) + a = this.anim_reload; a.z *= g_weaponratefactor; - setanim(actor.weaponentity, a, restartanim == false, restartanim, restartanim); + setanim(this, a, restartanim == false, restartanim, restartanim); } v_forward = of; v_right = or; v_up = ou; - if(actor.weapon_think == w_ready && func != w_ready && actor.weaponentity.state == WS_RAISE) - { - backtrace("Tried to override initial weapon think function - should this really happen?"); - } + if (this.weapon_think == w_ready && func != w_ready && this.state == WS_RAISE) backtrace( + "Tried to override initial weapon think function - should this really happen?"); t *= W_WeaponRateFactor(); // VorteX: haste can be added here - if (actor.weapon_think == w_ready) + if (this.weapon_think == w_ready) { - actor.weapon_nextthink = time; - //dprint("started firing at ", ftos(time), "\n"); + this.weapon_nextthink = time; + // dprint("started firing at ", ftos(time), "\n"); } - if (actor.weapon_nextthink < time - actor.weapon_frametime * 1.5 || actor.weapon_nextthink > time + actor.weapon_frametime * 1.5) + if (this.weapon_nextthink < time - actor.weapon_frametime * 1.5 + || this.weapon_nextthink > time + actor.weapon_frametime * 1.5) { - actor.weapon_nextthink = time; - //dprint("reset weapon animation timer at ", ftos(time), "\n"); + this.weapon_nextthink = time; + // dprint("reset weapon animation timer at ", ftos(time), "\n"); } - actor.weapon_nextthink = actor.weapon_nextthink + t; - actor.weapon_think = func; - //dprint("next ", ftos(actor.weapon_nextthink), "\n"); + this.weapon_nextthink = this.weapon_nextthink + t; + this.weapon_think = func; + // dprint("next ", ftos(this.weapon_nextthink), "\n"); - if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) + if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) { - if((actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id) && fr == WFRAME_FIRE2) - animdecide_setaction(actor, ANIMACTION_MELEE, restartanim); - else - animdecide_setaction(actor, ANIMACTION_SHOOT, restartanim); + if ((actor.weapon == WEP_SHOCKWAVE.m_id || actor.weapon == WEP_SHOTGUN.m_id) + && fr == WFRAME_FIRE2) animdecide_setaction(actor, ANIMACTION_MELEE, restartanim); + else animdecide_setaction(actor, ANIMACTION_SHOOT, restartanim); } else { - if(actor.anim_upper_action == ANIMACTION_SHOOT || actor.anim_upper_action == ANIMACTION_MELEE) - actor.anim_upper_action = 0; + if (actor.anim_upper_action == ANIMACTION_SHOOT + || actor.anim_upper_action == ANIMACTION_MELEE) actor.anim_upper_action = 0; } } -float forbidWeaponUse(entity player) +bool forbidWeaponUse(entity player) { - if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown) - return 1; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) - return 1; - if(player.player_blocked) - return 1; - if(player.frozen) - return 1; - if(player.weapon_blocked) - return 1; - return 0; + if (time < game_starttime && !autocvar_sv_ready_restart_after_countdown) return true; + if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return true; + if (player.player_blocked) return true; + if (player.frozen) return true; + if (player.weapon_blocked) return true; + return false; } .bool hook_switchweapon; void W_WeaponFrame(entity actor) { - vector fo, ri, up; + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + entity this = actor.(weaponentity); + if (frametime) actor.weapon_frametime = frametime; - if (frametime) - actor.weapon_frametime = frametime; + if (!this || actor.health < 1) return; // Dead player can't use weapons and injure impulse commands - if (!actor.weaponentity || actor.health < 1) - return; // Dead player can't use weapons and injure impulse commands - if(forbidWeaponUse(actor)) - if(actor.weaponentity.state != WS_CLEAR) + if (forbidWeaponUse(actor)) { - Weapon wpn = get_weaponinfo(actor.weapon); - w_ready(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2); - return; + if (actor.(weaponentity).state != WS_CLEAR) + { + Weapon wpn = Weapons_from(actor.weapon); + w_ready(wpn, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0)); + return; + } } - if(!actor.switchweapon) + if (actor.switchweapon == 0) { actor.weapon = 0; actor.switchingweapon = 0; - actor.weaponentity.state = WS_CLEAR; + this.state = WS_CLEAR; actor.weaponname = ""; - //actor.items &= ~IT_AMMO; + // actor.items &= ~IT_AMMO; return; } makevectors(actor.v_angle); - fo = v_forward; // save them in case the weapon think functions change it - ri = v_right; - up = v_up; + vector fo = v_forward; // save them in case the weapon think functions change it + vector ri = v_right; + vector up = v_up; // Change weapon if (actor.weapon != actor.switchweapon) { - if (actor.weaponentity.state == WS_CLEAR) + switch (this.state) { - // end switching! - actor.switchingweapon = actor.switchweapon; - entity newwep = get_weaponinfo(actor.switchweapon); - - // the two weapon entities will notice this has changed and update their models - actor.weapon = actor.switchweapon; - actor.weaponname = newwep.mdl; - actor.bulletcounter = 0; - actor.ammo_field = newwep.ammo_field; - Weapon w = get_weaponinfo(actor.switchweapon); - w.wr_setup(w); - actor.weaponentity.state = WS_RAISE; - - // set our clip load to the load of the weapon we switched to, if it's reloadable - if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars + default: + LOG_WARNINGF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state); + break; + case WS_INUSE: + case WS_RAISE: + break; + case WS_CLEAR: { - actor.clip_load = actor.(weapon_load[actor.switchweapon]); - actor.clip_size = newwep.reloading_ammo; - } - else - actor.clip_load = actor.clip_size = 0; - - weapon_thinkf(actor, WFRAME_IDLE, newwep.switchdelay_raise, w_ready); - } - else if (actor.weaponentity.state == WS_DROP) - { - // in dropping phase we can switch at any time - actor.switchingweapon = actor.switchweapon; - } - else if (actor.weaponentity.state == WS_READY) - { - // start switching! - actor.switchingweapon = actor.switchweapon; - entity oldwep = get_weaponinfo(actor.weapon); + // end switching! + actor.switchingweapon = actor.switchweapon; + entity newwep = Weapons_from(actor.switchweapon); + + // the two weapon entities will notice this has changed and update their models + actor.weapon = actor.switchweapon; + actor.weaponname = newwep.mdl; + actor.bulletcounter = 0; + actor.ammo_field = newwep.ammo_field; + newwep.wr_setup(newwep); + this.state = WS_RAISE; + + // set our clip load to the load of the weapon we switched to, if it's reloadable + if ((newwep.spawnflags & WEP_FLAG_RELOADABLE) && newwep.reloading_ammo) // prevent accessing undefined cvars + { + actor.clip_load = actor.(weapon_load[actor.switchweapon]); + actor.clip_size = newwep.reloading_ammo; + } + else + { + actor.clip_load = actor.clip_size = 0; + } - // set up weapon switch think in the future, and start drop anim - #ifndef INDEPENDENT_ATTACK_FINISHED - if(ATTACK_FINISHED(actor) <= time + actor.weapon_frametime * 0.5) + weapon_thinkf(actor, weaponentity, WFRAME_IDLE, newwep.switchdelay_raise, w_ready); + break; + } + case WS_DROP: { - #endif - sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM); - actor.weaponentity.state = WS_DROP; - weapon_thinkf(actor, WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear); - #ifndef INDEPENDENT_ATTACK_FINISHED + // in dropping phase we can switch at any time + actor.switchingweapon = actor.switchweapon; + break; + } + case WS_READY: + { + // start switching! + actor.switchingweapon = actor.switchweapon; + entity oldwep = Weapons_from(actor.weapon); + + // set up weapon switch think in the future, and start drop anim + if ( +#if INDEPENDENT_ATTACK_FINISHED + true +#else + ATTACK_FINISHED(actor, slot) <= time + actor.weapon_frametime * 0.5 +#endif + ) + { + sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM); + this.state = WS_DROP; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear); + } + break; } - #endif } } // LordHavoc: network timing test code - //if (actor.button0) - // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor)), " >= ", ftos(actor.weapon_nextthink), "\n"); + // if (actor.button0) + // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, slot)), " >= ", ftos(this.weapon_nextthink), "\n"); - float w; - w = actor.weapon; + int w = actor.weapon; // call the think code which may fire the weapon // and do so multiple times to resolve framerate dependency issues if the // server framerate is very low and the weapon fire rate very high - float c; - c = 0; - while (c < W_TICSPERFRAME) + for (int c = 0; c < W_TICSPERFRAME; ++c) { - c = c + 1; - if(w && !(actor.weapons & WepSet_FromWeapon(w))) + if (w && !(actor.weapons & WepSet_FromWeapon(w))) { - if(actor.weapon == actor.switchweapon) - W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); + if (actor.weapon == actor.switchweapon) W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); w = 0; } @@ -816,100 +821,104 @@ void W_WeaponFrame(entity actor) bool block_weapon = false; { bool key_pressed = actor.BUTTON_HOOK && !actor.vehicle; - Weapon off = actor.offhand; - if (off && !(actor.weapons & WEPSET(HOOK))) { - if (off.offhand_think) off.offhand_think(off, actor, key_pressed); - } else { - if (key_pressed && actor.switchweapon != WEP_HOOK.m_id && !actor.hook_switchweapon) { - W_SwitchWeapon(WEP_HOOK.m_id); - } + Weapon off = actor.offhand; + if (off && !(actor.weapons & WEPSET(HOOK))) + { + if (off.offhand_think) off.offhand_think(off, actor, key_pressed); + } + else + { + if (key_pressed && actor.switchweapon != WEP_HOOK.m_id && !actor.hook_switchweapon) W_SwitchWeapon( + WEP_HOOK.m_id); actor.hook_switchweapon = key_pressed; Weapon h = WEP_HOOK; block_weapon = (actor.weapon == h.m_id && (actor.BUTTON_ATCK || key_pressed)); - h.wr_think(h, actor, block_weapon, false); - } - } + h.wr_think(h, actor, weaponentity, block_weapon ? 1 : 0); + } + } + + v_forward = fo; + v_right = ri; + v_up = up; if (!block_weapon) - if (w) { - Weapon e = get_weaponinfo(actor.weapon); - e.wr_think(e, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2); - } else { - Weapon w = get_weaponinfo(actor.weapon); - w.wr_gonethink(w); + { + if (w) + { + Weapon e = Weapons_from(actor.weapon); + e.wr_think(e, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0)); + } + else + { + Weapon w = Weapons_from(actor.weapon); + w.wr_gonethink(w); + } } - if (time + actor.weapon_frametime * 0.5 >= actor.weapon_nextthink) + if (time + actor.weapon_frametime * 0.5 >= this.weapon_nextthink) { - if(actor.weapon_think) + if (this.weapon_think) { v_forward = fo; v_right = ri; v_up = up; - Weapon wpn = get_weaponinfo(actor.weapon); - actor.weapon_think(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2); + Weapon wpn = Weapons_from(actor.weapon); + this.weapon_think(wpn, actor, weaponentity, + (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0)); } else + { bprint("\{1}^1ERROR: undefined weapon think function for ", actor.netname, "\n"); + } } } } void W_AttachToShotorg(entity actor, entity flash, vector offset) { - entity xflash; + .entity weaponentity = weaponentities[0]; flash.owner = actor; flash.angles_z = random() * 360; - if(gettagindex(actor.weaponentity, "shot")) - setattachment(flash, actor.weaponentity, "shot"); - else - setattachment(flash, actor.weaponentity, "tag_shot"); + entity this = actor.(weaponentity); + if (gettagindex(this, "shot")) setattachment(flash, this, "shot"); + else setattachment(flash, this, "tag_shot"); setorigin(flash, offset); - xflash = spawn(); + entity xflash = spawn(); copyentity(flash, xflash); flash.viewmodelforclient = actor; - if(actor.weaponentity.oldorigin.x > 0) + if (this.oldorigin.x > 0) { setattachment(xflash, actor.exteriorweaponentity, ""); - setorigin(xflash, actor.weaponentity.oldorigin + offset); + setorigin(xflash, this.oldorigin + offset); } else { - if(gettagindex(actor.exteriorweaponentity, "shot")) - setattachment(xflash, actor.exteriorweaponentity, "shot"); - else - setattachment(xflash, actor.exteriorweaponentity, "tag_shot"); + if (gettagindex(actor.exteriorweaponentity, "shot")) setattachment(xflash, actor.exteriorweaponentity, "shot"); + else setattachment(xflash, actor.exteriorweaponentity, "tag_shot"); setorigin(xflash, offset); } } void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use) { + if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor)) return; - if(cvar("g_overkill")) - if(actor.ok_use_ammocharge) - { - ok_DecreaseCharge(actor, actor.weapon); - return; // TODO - } - - if((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) - return; + if ((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) return; // if this weapon is reloadable, decrease its load. Else decrease the player's ammo - if(wep.reloading_ammo) + if (wep.reloading_ammo) { actor.clip_load -= ammo_use; actor.(weapon_load[actor.weapon]) = actor.clip_load; } - else if(wep.ammo_field != ammo_none) + else if (wep.ammo_field != ammo_none) { actor.(wep.ammo_field) -= ammo_use; - if(actor.(wep.ammo_field) < 0) + if (actor.(wep.ammo_field) < 0) { backtrace(sprintf( "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... " @@ -919,7 +928,7 @@ void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use) GetAmmoPicture(wep.ammo_field), actor.netname, actor.(wep.ammo_field) - )); + )); } } } @@ -930,21 +939,23 @@ void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use) .float reload_complain; .string reload_sound; -void W_ReloadedAndReady(Weapon thiswep, entity actor, bool fire1, bool fire2) +void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int fire) { // finish the reloading process, and do the ammo transfer - actor.clip_load = actor.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading + actor.clip_load = actor.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load - if(!actor.reload_ammo_min || actor.items & IT_UNLIMITED_WEAPON_AMMO || actor.ammo_field == ammo_none) + if (!actor.reload_ammo_min || actor.items & IT_UNLIMITED_WEAPON_AMMO || actor.ammo_field == ammo_none) + { actor.clip_load = actor.reload_ammo_amount; + } else { // make sure we don't add more ammo than we have float load = min(actor.reload_ammo_amount - actor.clip_load, actor.(actor.ammo_field)); - actor.clip_load += load; - actor.(actor.ammo_field) -= load; + actor.clip_load += load; + actor.(actor.ammo_field) -= load; } actor.(weapon_load[actor.weapon]) = actor.clip_load; @@ -952,21 +963,19 @@ void W_ReloadedAndReady(Weapon thiswep, entity actor, bool fire1, bool fire2) // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - //ATTACK_FINISHED(actor) -= actor.reload_time - 1; + // ATTACK_FINISHED(actor, slot) -= actor.reload_time - 1; - Weapon wpn = get_weaponinfo(actor.weapon); - w_ready(wpn, actor, actor.BUTTON_ATCK, actor.BUTTON_ATCK2); + Weapon wpn = Weapons_from(actor.weapon); + w_ready(wpn, actor, weaponentity, (actor.BUTTON_ATCK ? 1 : 0) | (actor.BUTTON_ATCK2 ? 2 : 0)); } void W_Reload(entity actor, float sent_ammo_min, string sent_sound) { + .entity weaponentity = weaponentities[0]; // set global values to work with - entity e; - e = get_weaponinfo(actor.weapon); + entity e = Weapons_from(actor.weapon); - if(cvar("g_overkill")) - if(actor.ok_use_ammocharge) - return; // TODO + if (MUTATOR_CALLHOOK(W_Reload, actor)) return; actor.reload_ammo_min = sent_ammo_min; actor.reload_ammo_amount = e.reloading_ammo; @@ -976,46 +985,49 @@ void W_Reload(entity actor, float sent_ammo_min, string sent_sound) // don't reload weapons that don't have the RELOADABLE flag if (!(e.spawnflags & WEP_FLAG_RELOADABLE)) { - LOG_TRACE("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n"); + LOG_TRACE( + "Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n"); return; } // return if reloading is disabled for this weapon - if(!actor.reload_ammo_amount) - return; + if (!actor.reload_ammo_amount) return; // our weapon is fully loaded, no need to reload - if (actor.clip_load >= actor.reload_ammo_amount) - return; + if (actor.clip_load >= actor.reload_ammo_amount) return; // no ammo, so nothing to load - if(actor.ammo_field != ammo_none) - if(!actor.(actor.ammo_field) && actor.reload_ammo_min) - if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + if (actor.ammo_field != ammo_none) { - if(IS_REAL_CLIENT(actor) && actor.reload_complain < time) + if (!actor.(actor.ammo_field) && actor.reload_ammo_min) { - play2(actor, SND(UNAVAILABLE)); - sprint(actor, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(actor.weapon), "\n")); - actor.reload_complain = time + 1; - } - // switch away if the amount of ammo is not enough to keep using this weapon - Weapon w = get_weaponinfo(actor.weapon); - if (!(w.wr_checkammo1(w) + w.wr_checkammo2(w))) - { - actor.clip_load = -1; // reload later - W_SwitchToOtherWeapon(actor); + if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + { + if (IS_REAL_CLIENT(actor) && actor.reload_complain < time) + { + play2(actor, SND(UNAVAILABLE)); + sprint(actor, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(actor.weapon), "\n")); + actor.reload_complain = time + 1; + } + // switch away if the amount of ammo is not enough to keep using this weapon + Weapon w = Weapons_from(actor.weapon); + if (!(w.wr_checkammo1(w) + w.wr_checkammo2(w))) + { + actor.clip_load = -1; // reload later + W_SwitchToOtherWeapon(actor); + } + return; + } } - return; } - if (actor.weaponentity) + entity this = actor.(weaponentity); + if (this) { - if (actor.weaponentity.wframe == WFRAME_RELOAD) - return; + if (this.wframe == WFRAME_RELOAD) return; // allow switching away while reloading, but this will cause a new reload! - actor.weaponentity.state = WS_READY; + this.state = WS_READY; } // now begin the reloading process @@ -1026,21 +1038,18 @@ void W_Reload(entity actor, float sent_ammo_min, string sent_sound) // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - //ATTACK_FINISHED(actor) = max(time, ATTACK_FINISHED(actor)) + actor.reload_time + 1; + // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + actor.reload_time + 1; - weapon_thinkf(actor, WFRAME_RELOAD, actor.reload_time, W_ReloadedAndReady); + weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, actor.reload_time, W_ReloadedAndReady); - if(actor.clip_load < 0) - actor.clip_load = 0; + if (actor.clip_load < 0) actor.clip_load = 0; actor.old_clip_load = actor.clip_load; actor.clip_load = actor.(weapon_load[actor.weapon]) = -1; } void W_DropEvent(.void(Weapon) event, entity player, float weapon_type, entity weapon_item) -{SELFPARAM(); - setself(player); +{ + Weapon w = Weapons_from(weapon_type); weapon_dropevent_item = weapon_item; - Weapon w = get_weaponinfo(weapon_type); - w.event(w); - setself(this); + WITH(entity, self, player, w.event(w)); } diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh index 86d3111956..97e6c72b4b 100644 --- a/qcsrc/server/weapons/weaponsystem.qh +++ b/qcsrc/server/weapons/weaponsystem.qh @@ -18,7 +18,7 @@ vector shotorg_adjust(vector vecs, bool y_is_right, bool visual, int algn); vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn); -void CL_SpawnWeaponentity(entity e); +void CL_SpawnWeaponentity(entity e, .entity weaponentity); vector CL_Weapon_GetShotOrg(float wpn); @@ -38,12 +38,12 @@ float W_WeaponRateFactor(); float W_WeaponSpeedFactor(); -bool weapon_prepareattack(Weapon thiswep, entity actor, bool secondary, float attacktime); +bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime); -bool weapon_prepareattack_check(Weapon thiswep, entity actor, float secondary, float attacktime); +bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponentity, float secondary, float attacktime); -void weapon_prepareattack_do(entity actor, float secondary, float attacktime); +void weapon_prepareattack_do(entity actor, .entity weaponentity, float secondary, float attacktime); -void weapon_thinkf(entity actor, float fr, float t, void(Weapon thiswep, entity actor, bool fire1, bool fire2) func); +void weapon_thinkf(entity actor, .entity weaponentity, float fr, float t, void(Weapon thiswep, entity actor, .entity weaponentity, int fire) func); #endif diff --git a/qcsrc/uncrustify.cfg b/qcsrc/uncrustify.cfg index 4709cdc2df..b09403f954 100644 --- a/qcsrc/uncrustify.cfg +++ b/qcsrc/uncrustify.cfg @@ -1016,23 +1016,23 @@ align_oc_decl_colon = false # false/true #ignore # # Whether to collapse empty blocks between '{' and '}' -nl_collapse_empty_body = false # false/true +nl_collapse_empty_body = true # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' # WARNING: Code doesn't seem to use this feature - delete from the config? -nl_assign_leave_one_liners = false # false/true +nl_assign_leave_one_liners = true # false/true # Don't split one-line braced statements inside a class xx { } body # WARNING: Code doesn't seem to use this feature - delete from the config? -nl_class_leave_one_liners = false # false/true +nl_class_leave_one_liners = true # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' # WARNING: Code doesn't seem to use this feature - delete from the config? -nl_enum_leave_one_liners = false # false/true +nl_enum_leave_one_liners = true # false/true # Don't split one-line get or set functions # WARNING: Code doesn't seem to use this feature - delete from the config? -nl_getset_leave_one_liners = false # false/true +nl_getset_leave_one_liners = true # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = true # false/true @@ -1200,7 +1200,7 @@ nl_switch_brace = add # ignore/add/remove/force nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. -nl_multi_line_define = false # false/true +nl_multi_line_define = true # false/true # Whether to put a newline before 'case' statement nl_before_case = false # false/true @@ -1307,7 +1307,7 @@ nl_fdef_brace = add # ignore/add/remove/force nl_return_expr = remove # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements -nl_after_semicolon = false # false/true +nl_after_semicolon = true # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. @@ -1340,7 +1340,7 @@ nl_after_vbrace_close = false # false/true nl_brace_struct_var = remove # ignore/add/remove/force # Whether to alter newlines in '#define' macros -nl_define_macro = false # false/true +nl_define_macro = true # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true @@ -1390,7 +1390,7 @@ nl_class_colon = ignore # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' -nl_create_if_one_liner = false # false/true +nl_create_if_one_liner = true # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' @@ -1436,7 +1436,7 @@ pos_class_colon = ignore # ignore/join/lead/lead_brea # # Try to limit code width to N number of columns -code_width = 0 # number +code_width = 120 # number # Whether to fully split long 'for' statements at semi-colons # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1636,7 +1636,7 @@ mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. -mod_sort_include = false # false/true +mod_sort_include = false # false/true # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1645,7 +1645,7 @@ mod_move_case_break = false # false/true # Will add or remove the braces around a fully braced case statement. # Will only remove the braces if there are no variable declarations in the block. # NOTE: is 78 worse than ignore -mod_case_brace = remove # ignore/add/remove/force +mod_case_brace = add # ignore/add/remove/force # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = true # false/true #force @@ -1696,7 +1696,7 @@ cmt_cpp_to_c = false # false/true # Whether to put a star on subsequent comment lines # WARNING: Code doesn't seem to use this feature - delete from the config? -cmt_star_cont = false # false/true +cmt_star_cont = true # false/true # The number of spaces to insert at the start of subsequent comment lines # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1748,14 +1748,14 @@ cmt_insert_before_preproc = false # false/true # Control indent of preprocessors inside #if blocks at brace level 0 # WARNING: Indifferent... please decide manually. -pp_indent = ignore # ignore/add/remove/force +pp_indent = force # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. # WARNING: Code doesn't seem to use this feature - delete from the config? -pp_indent_count = 0 # number +pp_indent_count = 4 # number # Add or remove space after # based on pp_level of #if blocks # NOTE: is 28 worse than ignore @@ -1771,14 +1771,14 @@ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion # WARNING: Code doesn't seem to use this feature - delete from the config? -pp_region_indent_code = false # false/true +pp_region_indent_code = true # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level # WARNING: Code doesn't seem to use this feature - delete from the config? -pp_indent_if = 0 # number +pp_indent_if = 4 # number # Control whether to indent the code between #if, #else and #endif when not at file-level -pp_if_indent_code = false # false/true +pp_if_indent_code = true # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = true # false/true @@ -1788,11 +1788,15 @@ pp_define_at_level = true # false/true # type myfoo1 myfoo2 type void +type bool +type int type float type vector type entity type string type .void +type .bool +type .int type .float type .vector type .entity @@ -1809,7 +1813,7 @@ type .string # You can assign any keyword to any type with the set option. # set func_call_user _ N_ -# menu QC OO +# QC OO macro-open CLASS macro-else EXTENDS macro-close ENDCLASS diff --git a/qcsrc/uncrustify.sh b/qcsrc/uncrustify.sh index 2df39aa3a8..b36953e366 100755 --- a/qcsrc/uncrustify.sh +++ b/qcsrc/uncrustify.sh @@ -3,7 +3,7 @@ fix_function_types() { # void(void) func; # ) wrong and removes the space between type and variable. Fix this by # a simple sed on ")letter" which should normally not occur. - sed -e 's/)\([A-Za-z0-9]\)/) \1/g' "$@" + sed -e 's/)\([A-Za-z_]\)/) \1/g' "$@" } if [ -z "$UNCRUSTIFY_CONFIG" ]; then @@ -16,7 +16,7 @@ case "$#" in fix_function_types ;; *) - uncrustify --replace --no-backup -c "$UNCRUSTIFY_CONFIG" "$@" &&\ + uncrustify --replace --no-backup -c "$UNCRUSTIFY_CONFIG" "$@" ;\ fix_function_types -i "$@" ;; esac diff --git a/quickmenu_example.txt b/quickmenu_example.txt new file mode 100644 index 0000000000..6f97e28bae --- /dev/null +++ b/quickmenu_example.txt @@ -0,0 +1,36 @@ +// This quickmenu example file can be loaded by setting +// hud_panel_quickmenu_file quickmenu_example.txt +// and executing "quickmenu file" +// For more options see "quickmenu help" + +"Chat" + "nice one" "say :-) / nice one" + "good game" "say good game" + "hi / good luck" "say hi / good luck and have fun" +"Chat" + +"Settings" + // nested submenu + "Sound settings" + "Hit sound" "toggle cl_hitsound" + "Chat sound" "toggle cl_chatsound" + "Sound settings" + + // A toggle command displays a checkbox showing the current cvar's state + "enable 3rd person view" "toggle chase_active" + // it's possible to invert the meaning of the checkbox by inverting the + // parameters of toggle from the implicit 1 0 to 0 1 + "disable 3rd person view" "toggle chase_active 0 1" +"Settings" + +"screenshot" "wait; screenshot" + +// Commands that accept a player's name as parameter (%s), followed by one of +// these special keywords: +// ALLPLAYERS_BUT_ME +// ALLPLAYERS +// OWNTEAMPLAYERS_BUT_ME +// OWNTEAMPLAYERS +// ENEMYTEAMPLAYERS +// lets you pick a player's name from a list: +"private chat with:" "commandmode tell \"%s\"" ALLPLAYERS_BUT_ME