From 37e3c5bb44dd413533129d9481043f1469f0f7cb Mon Sep 17 00:00:00 2001 From: div0 Date: Tue, 30 Jun 2009 14:36:36 +0000 Subject: [PATCH] eol-style only git-svn-id: svn://svn.icculus.org/nexuiz/trunk@7131 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/client/Defs.qc | 470 +-- data/qcsrc/client/Main.qc | 2134 +++++++------- data/qcsrc/client/View.qc | 1726 +++++------ data/qcsrc/client/csqc_builtins.qc | 574 ++-- data/qcsrc/client/csqc_constants.qc | 384 +-- data/qcsrc/client/effects.qc | 200 +- data/qcsrc/client/rubble.qc | 68 +- data/qcsrc/server/csqceffects.qc | 26 +- data/qcsrc/server/movelib.qc | 468 +-- data/qcsrc/server/pathlib.qc | 2094 +++++++------- data/qcsrc/server/pathlib/costs.qc | 288 +- data/qcsrc/server/pathlib/debug.qc | 234 +- data/qcsrc/server/pathlib/expandnode.qc | 392 +-- data/qcsrc/server/pathlib/main.qc | 1068 +++---- data/qcsrc/server/pathlib/movenode.qc | 252 +- data/qcsrc/server/pathlib/pathlib.qh | 198 +- data/qcsrc/server/pathlib/utility.qc | 324 +-- data/qcsrc/server/tturrets/include/turrets.qh | 58 +- .../server/tturrets/include/turrets_early.qh | 1066 +++---- .../server/tturrets/system/system_aimprocs.qc | 244 +- .../server/tturrets/system/system_main.qc | 2516 ++++++++--------- .../server/tturrets/system/system_misc.qc | 812 +++--- .../tturrets/system/system_scoreprocs.qc | 332 +-- .../server/tturrets/units/unit_checkpoint.qc | 140 +- .../server/tturrets/units/unit_common.qc | 2 +- .../server/tturrets/units/unit_ewheel.qc | 1040 +++---- data/qcsrc/server/tturrets/units/unit_flac.qc | 256 +- .../tturrets/units/unit_fusionreactor.qc | 166 +- .../server/tturrets/units/unit_hellion.qc | 444 +-- data/qcsrc/server/tturrets/units/unit_hk.qc | 960 +++---- .../server/tturrets/units/unit_machinegun.qc | 162 +- data/qcsrc/server/tturrets/units/unit_mlrs.qc | 284 +- .../server/tturrets/units/unit_phaser.qc | 298 +- .../server/tturrets/units/unit_plasma.qc | 450 +-- .../tturrets/units/unit_targettrigger.qc | 84 +- .../server/tturrets/units/unit_tessla.qc | 356 +-- .../server/tturrets/units/unit_walker.qc | 2044 ++++++------- data/qcsrc/server/vehicles/vehicles.qh | 104 +- data/qcsrc/server/verbstack.qc | 400 +-- 39 files changed, 11559 insertions(+), 11559 deletions(-) diff --git a/data/qcsrc/client/Defs.qc b/data/qcsrc/client/Defs.qc index eb2df76ed..711f73adb 100644 --- a/data/qcsrc/client/Defs.qc +++ b/data/qcsrc/client/Defs.qc @@ -1,235 +1,235 @@ -#pragma flag off fastarrays // make dp behave with new fteqcc versions. remove when dp bug with fteqcc fastarrays is fixed - - -//NOTE: THIS IS AN INTERFACE FILE. DO NOT EDIT. -//MODIFYING THIS FILE CAN RESULT IN CRC ERRORS. -//YOU HAVE BEEN WARNED. - -//feel free to look though. :) - - - - - -/* -============================================================================== - - SOURCE FOR GLOBALVARS_T C STRUCTURE - -============================================================================== -*/ - -// -// system globals -// -entity self; -entity other; -entity world; -float time; -float frametime; - -float player_localentnum; //the entnum -float player_localnum; //the playernum -float maxclients; //a constant filled in by the engine. gah, portability eh? - -float clientcommandframe; //player movement -float servercommandframe; //clientframe echoed off the server - -string mapname; - -// -// global variables set by built in functions -// -vector v_forward, v_up, v_right; // set by makevectors() - -// set by traceline / tracebox -float trace_allsolid; -float trace_startsolid; -float trace_fraction; -vector trace_endpos; -vector trace_plane_normal; -float trace_plane_dist; -entity trace_ent; -float trace_inopen; -float trace_inwater; - -// -// required prog functions -// -void() CSQC_Init; -void() CSQC_Shutdown; -float(float f, float t, float n) CSQC_InputEvent; -void(float w, float h) CSQC_UpdateView; -float(string s) CSQC_ConsoleCommand; - -//these fields are read and set by the default player physics -vector pmove_org; -vector pmove_vel; -vector pmove_mins; -vector pmove_maxs; -//retrieved from the current movement commands (read by player physics) -float input_timelength; -vector input_angles; -vector input_movevalues; //forwards, right, up. -float input_buttons; //attack, use, jump (default physics only uses jump) - -float movevar_gravity; -float movevar_stopspeed; -float movevar_maxspeed; -float movevar_spectatormaxspeed; //used by NOCLIP movetypes. -float movevar_accelerate; -float movevar_airaccelerate; -float movevar_wateraccelerate; -float movevar_friction; -float movevar_waterfriction; -float movevar_entgravity; //the local player's gravity field. Is a multiple (1 is the normal value) - -//================================================ -void end_sys_globals; // flag for structure dumping -//================================================ - -/* -============================================================================== - - SOURCE FOR ENTVARS_T C STRUCTURE - -============================================================================== -*/ - -// -// system fields (*** = do not set in prog code, maintained by C code) -// -.float modelindex; // *** model index in the precached list -.vector absmin, absmax; // *** origin + mins / maxs - -.float entnum; // *** the ent number as on the server -.float drawmask; -.void() predraw; - -.float movetype; -.float solid; - -.vector origin; // *** -.vector oldorigin; // *** -.vector velocity; -.vector angles; -.vector avelocity; - -.string classname; // spawn function -.string model; -.float frame; -.float skin; -.float effects; - -.vector mins, maxs; // bounding box extents reletive to origin -.vector size; // maxs - mins - -.void() touch; -.void() use; -.void() think; -.void() blocked; // for doors or plats, called when can't push other - -.float nextthink; - -.entity chain; - -.string netname; - -.entity enemy; - -.float flags; - -.float colormap; - -.entity owner; // who launched a missile - -//================================================ -void end_sys_fields; // flag for structure dumping -//================================================ - -// Additional OPTIONAL Fields and Globals -float intermission; -float sb_showscores; -float sb_showaccuracy; -float sbar_currentammo; -.string message; -.float renderflags; -// float coop; -// float deathmatch; - -// float dmg_take; -// float dmg_save; -// vector dmg_origin; - -// Darkplaces Render Modifications -#if 0 -.float alpha; -.float renderflags; -.vector colormod; -.float scale; -#endif - -// Basic variables -.float enttype; // entity type sent from server -.float sv_entnum; // entity number sent from server -.float team; -.float team_size; - -float vid_conwidth, vid_conheight; -float binddb; - -// QUALIFYING -float race_checkpoint; -float race_time; -float race_laptime; -float race_checkpointtime; -float race_previousbesttime; -string race_previousbestname; -float race_nextcheckpoint; -float race_nextbesttime; -string race_nextbestname; - -// RACE -float race_mycheckpoint; -float race_mycheckpointtime; -float race_mycheckpointdelta; -float race_mycheckpointlapsdelta; -string race_mycheckpointenemy; -float race_othercheckpoint; -float race_othercheckpointtime; -float race_othercheckpointdelta; -float race_othercheckpointlapsdelta; -string race_othercheckpointenemy; -float sb_showscores_force; - -// Nexball -float nb_pb_period; - -// Spectating -float spectatee_status; - -// short mapname -string shortmapname; - -//remaining maptime announcer sounds, true when sound was already played -float announcer_1min; -float announcer_5min; - -// database for misc stuff -float tempdb; -vector hook_shotorigin; - -#ifdef BLURTEST -float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power; -#endif - -float servertime, serverprevtime, serverdeltatime; - -float ticrate; - -.float damageforcescale; -.void(float thisdmg, float hittype, vector org, vector thisforce) event_damage; - -// only for Porto -float angles_held_status; -vector angles_held; +#pragma flag off fastarrays // make dp behave with new fteqcc versions. remove when dp bug with fteqcc fastarrays is fixed + + +//NOTE: THIS IS AN INTERFACE FILE. DO NOT EDIT. +//MODIFYING THIS FILE CAN RESULT IN CRC ERRORS. +//YOU HAVE BEEN WARNED. + +//feel free to look though. :) + + + + + +/* +============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system globals +// +entity self; +entity other; +entity world; +float time; +float frametime; + +float player_localentnum; //the entnum +float player_localnum; //the playernum +float maxclients; //a constant filled in by the engine. gah, portability eh? + +float clientcommandframe; //player movement +float servercommandframe; //clientframe echoed off the server + +string mapname; + +// +// global variables set by built in functions +// +vector v_forward, v_up, v_right; // set by makevectors() + +// set by traceline / tracebox +float trace_allsolid; +float trace_startsolid; +float trace_fraction; +vector trace_endpos; +vector trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; + +// +// required prog functions +// +void() CSQC_Init; +void() CSQC_Shutdown; +float(float f, float t, float n) CSQC_InputEvent; +void(float w, float h) CSQC_UpdateView; +float(string s) CSQC_ConsoleCommand; + +//these fields are read and set by the default player physics +vector pmove_org; +vector pmove_vel; +vector pmove_mins; +vector pmove_maxs; +//retrieved from the current movement commands (read by player physics) +float input_timelength; +vector input_angles; +vector input_movevalues; //forwards, right, up. +float input_buttons; //attack, use, jump (default physics only uses jump) + +float movevar_gravity; +float movevar_stopspeed; +float movevar_maxspeed; +float movevar_spectatormaxspeed; //used by NOCLIP movetypes. +float movevar_accelerate; +float movevar_airaccelerate; +float movevar_wateraccelerate; +float movevar_friction; +float movevar_waterfriction; +float movevar_entgravity; //the local player's gravity field. Is a multiple (1 is the normal value) + +//================================================ +void end_sys_globals; // flag for structure dumping +//================================================ + +/* +============================================================================== + + SOURCE FOR ENTVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system fields (*** = do not set in prog code, maintained by C code) +// +.float modelindex; // *** model index in the precached list +.vector absmin, absmax; // *** origin + mins / maxs + +.float entnum; // *** the ent number as on the server +.float drawmask; +.void() predraw; + +.float movetype; +.float solid; + +.vector origin; // *** +.vector oldorigin; // *** +.vector velocity; +.vector angles; +.vector avelocity; + +.string classname; // spawn function +.string model; +.float frame; +.float skin; +.float effects; + +.vector mins, maxs; // bounding box extents reletive to origin +.vector size; // maxs - mins + +.void() touch; +.void() use; +.void() think; +.void() blocked; // for doors or plats, called when can't push other + +.float nextthink; + +.entity chain; + +.string netname; + +.entity enemy; + +.float flags; + +.float colormap; + +.entity owner; // who launched a missile + +//================================================ +void end_sys_fields; // flag for structure dumping +//================================================ + +// Additional OPTIONAL Fields and Globals +float intermission; +float sb_showscores; +float sb_showaccuracy; +float sbar_currentammo; +.string message; +.float renderflags; +// float coop; +// float deathmatch; + +// float dmg_take; +// float dmg_save; +// vector dmg_origin; + +// Darkplaces Render Modifications +#if 0 +.float alpha; +.float renderflags; +.vector colormod; +.float scale; +#endif + +// Basic variables +.float enttype; // entity type sent from server +.float sv_entnum; // entity number sent from server +.float team; +.float team_size; + +float vid_conwidth, vid_conheight; +float binddb; + +// QUALIFYING +float race_checkpoint; +float race_time; +float race_laptime; +float race_checkpointtime; +float race_previousbesttime; +string race_previousbestname; +float race_nextcheckpoint; +float race_nextbesttime; +string race_nextbestname; + +// RACE +float race_mycheckpoint; +float race_mycheckpointtime; +float race_mycheckpointdelta; +float race_mycheckpointlapsdelta; +string race_mycheckpointenemy; +float race_othercheckpoint; +float race_othercheckpointtime; +float race_othercheckpointdelta; +float race_othercheckpointlapsdelta; +string race_othercheckpointenemy; +float sb_showscores_force; + +// Nexball +float nb_pb_period; + +// Spectating +float spectatee_status; + +// short mapname +string shortmapname; + +//remaining maptime announcer sounds, true when sound was already played +float announcer_1min; +float announcer_5min; + +// database for misc stuff +float tempdb; +vector hook_shotorigin; + +#ifdef BLURTEST +float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power; +#endif + +float servertime, serverprevtime, serverdeltatime; + +float ticrate; + +.float damageforcescale; +.void(float thisdmg, float hittype, vector org, vector thisforce) event_damage; + +// only for Porto +float angles_held_status; +vector angles_held; diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 7deb61c21..5320d12bd 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -1,1067 +1,1067 @@ -// -------------------------------------------------------------------------- -// BEGIN REQUIRED CSQC FUNCTIONS -//include "main.qh" - -#define DP_CSQC_ENTITY_REMOVE_IS_B0RKED - -void cvar_clientsettemp(string cv, string val) -{ - entity e; - for(e = world; (e = find(e, classname, "saved_cvar_value")); ) - if(e.netname == cv) - goto saved; - e = spawn(); - e.classname = "saved_cvar_value"; - e.netname = strzone(cv); - e.message = strzone(cvar_string(cv)); -:saved - cvar_set(cv, val); -} - -void cvar_clientsettemp_restore() -{ - entity e; - for(e = world; (e = find(e, classname, "saved_cvar_value")); ) - cvar_set(e.netname, e.message); -} - -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 = -{ -}; - -#ifdef USE_FTE -float __engine_check; -#endif - -string forcefog; -void WaypointSprite_Load(); -void CSQC_Init(void) -{ -#ifdef USE_FTE -#pragma target ID - __engine_check = checkextension("DP_SV_WRITEPICTURE"); - if(!__engine_check) - { - print("^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n"); - localcmd("\ndisconnect\n"); - return; - } -#pragma target FTE -#endif - - check_unacceptable_compiler_bugs(); - - float i; - CSQC_CheckEngine(); - dprint_load(); - - binddb = db_create(); - tempdb = db_create(); - compressShortVector_init(); - - drawfont = 0; - menu_visible = FALSE; - menu_show = menu_show_error; - menu_action = menu_sub_null; - - for(i = 0; i < 255; ++i) - if(getplayerkey(i, "viewentity") == "") - break; - maxclients = i; - - //ctf_temp_1 = ""; - // localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used - //registercmd("ctf_menu"); - registercmd("ons_map"); - //registercmd("menu_action"); - - registercmd("+button3"); - registercmd("-button3"); - registercmd("+button4"); - registercmd("-button4"); - registercmd("+showaccuracy");registercmd("-showaccuracy"); - -#ifndef CAMERATEST - if(isdemo()) - { -#endif - registercmd("+forward");registercmd("-forward"); - registercmd("+back");registercmd("-back"); - registercmd("+moveup");registercmd("-moveup"); - registercmd("+movedown");registercmd("-movedown"); - registercmd("+moveright");registercmd("-moveright"); - registercmd("+moveleft");registercmd("-moveleft"); - registercmd("+roll_right");registercmd("-roll_right"); - registercmd("+roll_left");registercmd("-roll_left"); -#ifndef CAMERATEST - } -#endif - registercvar("sbar_usecsqc", "1"); - registercvar("sbar_columns", "default", CVAR_SAVE); - - gametype = 0; - - // sbar_fields uses strunzone on the titles! - for(i = 0; i < MAX_SBAR_FIELDS; ++i) - sbar_title[i] = strzone("(null)"); - - postinit = false; - - teams = Sort_Spawn(); - players = Sort_Spawn(); - - GetTeam(COLOR_SPECTATOR, true); // add specs first - - cvar_clientsettemp("_supports_weaponpriority", "1"); - - - - - cs_project_is_b0rked = TRUE; - R_SetView(VF_VIEWPORT, '0 0 0', '640 480 0'); - R_SetView(VF_FOV, '90 90 0'); - R_SetView(VF_ORIGIN, '0 0 0'); - R_SetView(VF_ANGLES, '0 0 0'); - R_SetView(VF_PERSPECTIVE, 1); - makevectors('0 0 0'); - vector v; - v = cs_project(v_forward); - if(v_x - 320 < +1) - if(v_x - 320 > -1) - if(v_y - 240 < +1) - if(v_y - 240 > -1) - cs_project_is_b0rked = FALSE; - - RegisterWeapons(); - - WaypointSprite_Load(); - - Projectile_Precache(); - GibSplash_Precache(); - Casings_Precache(); - DamageInfo_Precache(); - Announcer_Precache(); - - 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 - } - - mi_center = (mi_min + mi_max) * 0.5; - mi_scale = mi_max - mi_min; - minimapname = strzone(minimapname); -} - -// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) -void CSQC_Shutdown(void) -{ -#ifdef USE_FTE -#pragma TARGET id - if(!__engine_check) - return 0; -#pragma TARGET fte -#endif - - remove(teams); - remove(players); - db_close(binddb); - db_close(tempdb); - - cvar_clientsettemp_restore(); - - if(camera_active) - cvar_set("chase_active",ftos(chase_active_backup)); -} - -.float has_team; -float SetTeam(entity o, float Team) -{ - entity tm; - if(Team == -1) // leave - { - if(o.has_team) - { - //print("(DISCONNECT) leave team ", ftos(o.team), "\n"); - tm = GetTeam(o.team, false); - tm.team_size -= 1; - o.has_team = 0; - return TRUE; - } - } - else - { - if not(o.has_team) - { - //print("(CONNECT) enter team ", ftos(o.team), "\n"); - o.team = Team; - tm = GetTeam(Team, true); - tm.team_size += 1; - o.has_team = 1; - return TRUE; - } - else if(Team != o.team) - { - //print("(CHANGE) leave team ", ftos(o.team), "\n"); - tm = GetTeam(o.team, false); - tm.team_size -= 1; - o.team = Team; - //print("(CHANGE) enter team ", ftos(o.team), "\n"); - tm = GetTeam(Team, true); - tm.team_size += 1; - return TRUE; - } - } - return FALSE; -} - -void Playerchecker_Think() -{ - float i; - entity e; - for(i = 0; i < maxclients; ++i) - { - e = playerslots[i]; - if(GetPlayerName(i) == "") - { - if(e.sort_prev) - { - //print("playerchecker: KILL KILL KILL\n"); - // player disconnected - SetTeam(e, -1); - RemovePlayer(e); - e.sort_prev = world; - //e.gotscores = 0; - } - } - else - { - if not(e.sort_prev) - { - //print("playerchecker: SPAWN SPAWN SPAWN\n"); - // player connected - if not(e) - playerslots[i] = e = spawn(); - e.sv_entnum = i; - //e.gotscores = 0; // we might already have the scores... - SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with Sbar_UpdatePlayerTeams - RegisterPlayer(e); - Sbar_UpdatePlayerPos(e); - } - } - } - self.nextthink = time + 0.2; -} - -void Porto_Init(); -void TrueAim_Init(); -void PostInit(void) -{ - print(strcat("PostInit\n maxclients = ", ftos(maxclients), "\n")); - localcmd(strcat("\nsbar_columns_set ", cvar_string("sbar_columns"), ";\n")); - - entity playerchecker; - playerchecker = spawn(); - playerchecker.think = Playerchecker_Think; - playerchecker.nextthink = time + 0.2; - - Porto_Init(); - TrueAim_Init(); - - postinit = true; -} - -// CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function -// Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it. -float button_zoom; -void Cmd_Sbar_SetFields(float); -void Cmd_Sbar_Help(float); -float CSQC_ConsoleCommand(string strMessage) -{ - float argc; - // Tokenize String - //argc = tokenize(strMessage); - argc = tokenize_console(strMessage); - - // Acquire Command - local string strCmd; - strCmd = argv(0); - - if(strCmd == "+button4") { // zoom - // return false, because the message shall be sent to the server anyway (for demos/speccing) - if(ignore_plus_zoom) - { - --ignore_plus_zoom; - return false; - } - button_zoom = 1; - return true; - } else if(strCmd == "-button4") { // zoom - if(ignore_minus_zoom) - { - --ignore_minus_zoom; - return false; - } - button_zoom = 0; - return true; - } else if(strCmd == "+button3") { // secondary - button_attack2 = 1; - return false; - } else if(strCmd == "-button3") { // secondary - button_attack2 = 0; - return false; - } else if(strCmd == "+showscores") { - sb_showscores = true; - return true; - } else if(strCmd == "-showscores") { - sb_showscores = false; - return true; - } else if(strCmd == "+showaccuracy") { - sb_showaccuracy = true; - return true; - } else if(strCmd == "-showaccuracy") { - sb_showaccuracy = false; - return true; - } - - if(camera_active) - if(strCmd == "+forward" || strCmd == "-back") { - ++camera_direction_x; - return true; - } else if(strCmd == "-forward" || strCmd == "+back") { - --camera_direction_x; - return true; - } else if(strCmd == "+moveright" || strCmd == "-moveleft") { - --camera_direction_y; - return true; - } else if(strCmd == "-moveright" || strCmd == "+moveleft") { - ++camera_direction_y; - return true; - } else if(strCmd == "+moveup" || strCmd == "-movedown") { - ++camera_direction_z; - return true; - } else if(strCmd == "-moveup" || strCmd == "+movedown") { - --camera_direction_z; - return true; - } else if(strCmd == "+roll_right" || strCmd == "-roll_left") { - ++camera_roll; - return true; - } else if(strCmd == "+roll_left" || strCmd == "-roll_right") { - --camera_roll; - return true; - } - - return false; -} - -.vector view_ofs; -entity debug_shotorg; -void ShotOrg_Draw() -{ - self.origin = view_origin + view_forward * self.view_ofs_x + view_right * self.view_ofs_y + view_up * self.view_ofs_z; - self.angles = view_angles; - self.angles_x = -self.angles_x; - if not(self.cnt) - R_AddEntity(self); -} -void ShotOrg_Draw2D() -{ - vector coord2d_topleft, coord2d_topright, coord2d; - string s; - vector fs; - - s = vtos(self.view_ofs); - s = substring(s, 1, strlen(s) - 2); - if(tokenize_console(s) == 3) - s = strcat(argv(0), " ", argv(1), " ", argv(2)); - - coord2d_topleft = project_3d_to_2d(self.origin + view_up * 4 - view_right * 4); - coord2d_topright = project_3d_to_2d(self.origin + view_up * 4 + view_right * 4); - - fs = '1 1 0' * ((coord2d_topright_x - coord2d_topleft_x) / stringwidth(s, FALSE)); - - coord2d = coord2d_topleft; - if(fs_x < 8) - { - coord2d_x += (coord2d_topright_x - coord2d_topleft_x) * (1 - 8 / fs_x) * 0.5; - fs = '8 8 0'; - } - coord2d_y -= fs_y; - coord2d_z = 0; - drawstring(coord2d, s, fs, '1 1 1', 1, 0); -} - -void ShotOrg_Spawn() -{ - debug_shotorg = spawn(); - debug_shotorg.draw = ShotOrg_Draw; - debug_shotorg.draw2d = ShotOrg_Draw2D; - debug_shotorg.renderflags = RF_VIEWMODEL; - debug_shotorg.effects = EF_FULLBRIGHT; - precache_model("models/shotorg_adjuster.md3"); - setmodel(debug_shotorg, "models/shotorg_adjuster.md3"); - debug_shotorg.scale = 2; - debug_shotorg.view_ofs = '25 8 -8'; -} - -void GameCommand(string msg) -{ - float argc; - argc = tokenize_console(msg); - - if(argv(0) == "help" || argc == 0) - { - print("Usage: cl_cmd COMMAND..., where possible commands are:\n"); - print(" settemp cvar value\n"); - print(" radar\n"); - print(" sbar_columns_set ...\n"); - print(" sbar_columns_help\n"); - GameCommand_Generic("help"); - return; - } - - if(GameCommand_Generic(msg)) - return; - - string cmd; - cmd = argv(0); - if(cmd == "mv_download") { - Cmd_MapVote_MapDownload(argc); - } - else if(cmd == "settemp") { - cvar_clientsettemp(argv(1), argv(2)); - } - else if(cmd == "radar") { - ons_showmap = !ons_showmap; - } - else if(cmd == "sbar_columns_set") { - Cmd_Sbar_SetFields(argc); - } - else if(cmd == "sbar_columns_help") { - Cmd_Sbar_Help(argc); - } -#ifdef BLURTEST - else if(cmd == "blurtest") { - blurtest_time0 = time; - blurtest_time1 = time + stof(argv(1)); - blurtest_radius = stof(argv(2)); - blurtest_power = stof(argv(3)); - } -#endif - else if(cmd == "shotorg_move") { - if(!debug_shotorg) - ShotOrg_Spawn(); - else - debug_shotorg.view_ofs = debug_shotorg.view_ofs + stov(argv(1)); - localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); - } - else if(cmd == "shotorg_movez") { - if(!debug_shotorg) - ShotOrg_Spawn(); - else - debug_shotorg.view_ofs = debug_shotorg.view_ofs + stof(argv(1)) * (debug_shotorg.view_ofs * (1 / debug_shotorg.view_ofs_x)); // closer/farther, same xy pos - localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); - } - else if(cmd == "shotorg_set") { - if(!debug_shotorg) - ShotOrg_Spawn(); - else - debug_shotorg.view_ofs = stov(argv(1)); - localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); - } - else if(cmd == "shotorg_setz") { - if(!debug_shotorg) - ShotOrg_Spawn(); - else - debug_shotorg.view_ofs = debug_shotorg.view_ofs * (stof(argv(1)) / debug_shotorg.view_ofs_x); // closer/farther, same xy pos - localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); - } - else if(cmd == "shotorg_toggle_hide") { - if(debug_shotorg) - { - debug_shotorg.cnt = !debug_shotorg.cnt; - } - } - else if(cmd == "shotorg_end") { - if(debug_shotorg) - { - print(vtos(debug_shotorg.view_ofs), "\n"); - remove(debug_shotorg); - debug_shotorg = world; - } - localcmd("sv_cmd debug_shotorg\n"); - } - else - { - print("Invalid command. For a list of supported commands, try cl_cmd help.\n"); - } - - return; -} - -// CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client. -// Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine. -// All keys are in ascii. -// bInputType = 0 is key pressed, 1 is key released, 2 is mouse input. -// In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0. -// In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta. -float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) -{ - local float bSkipKey; - bSkipKey = false; - - if(menu_visible) - if(menu_action(bInputType, nPrimary, nSecondary)) - return TRUE; - return bSkipKey; -} - -// END REQUIRED CSQC FUNCTIONS -// -------------------------------------------------------------------------- - -// -------------------------------------------------------------------------- -// BEGIN OPTIONAL CSQC FUNCTIONS -void Ent_ReadEntCS() -{ - InterpolateOrigin_Undo(); - - self.classname = "entcs_receiver"; - self.sv_entnum = ReadByte() - 1; - self.origin_x = ReadShort(); - self.origin_y = ReadShort(); - self.origin_z = ReadShort(); - self.angles_y = ReadByte() * 360.0 / 256; - self.origin_z = self.angles_x = self.angles_z = 0; - - InterpolateOrigin_Note(); -} - -void Ent_Remove(); - -void Ent_RemovePlayerScore() -{ - float i; - - if(self.owner) - { - SetTeam(self.owner, -1); - self.owner.gotscores = 0; - for(i = 0; i < MAX_SCORE; ++i) - self.owner.(scores[i]) = 0; // clear all scores - } -} - -void Ent_ReadPlayerScore() -{ - float i, n; - float isNew; - entity o; - - // damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN - // (no I've never heard of M-x replace-string, sed, or anything like that) - isNew = !self.owner; // workaround for DP bug - n = ReadByte()-1; - -#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED - if(!isNew && n != self.sv_entnum) - { - print("A CSQC entity changed its owner!\n"); - isNew = true; - Ent_Remove(); - self.enttype = ENT_CLIENT_SCORES; - } -#endif - - self.sv_entnum = n; - - if not(playerslots[self.sv_entnum]) - playerslots[self.sv_entnum] = spawn(); - o = self.owner = playerslots[self.sv_entnum]; - o.sv_entnum = self.sv_entnum; - o.gotscores = 1; - - //if not(o.sort_prev) - // RegisterPlayer(o); - //playerchecker will do this for us later, if it has not already done so - -#if MAX_SCORE <= 3 - for(i = 0; i < MAX_SCORE; ++i) - o.(scores[i]) = ReadShort(); -#else - float sf; -#if MAX_SCORE <= 8 - sf = ReadByte(); -#else - sf = ReadShort(); -#endif - float p; - for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) - if(sf & p) - o.(scores[i]) = ReadShort(); -#endif - - if(o.sort_prev) - Sbar_UpdatePlayerPos(o); // if not registered, we cannot do this yet! - - self.entremove = Ent_RemovePlayerScore; -} - -void Ent_ReadTeamScore() -{ - float i; - entity o; - - self.team = ReadByte(); - o = self.owner = GetTeam(self.team, true); - -#if MAX_TEAMSCORE <= 3 - for(i = 0; i < MAX_TEAMSCORE; ++i) - o.(teamscores[i]) = ReadShort(); -#else - float sf; -#if MAX_TEAMSCORE <= 8 - sf = ReadByte(); -#else - sf = ReadShort(); -#endif - float p; - for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) - if(sf & p) - o.(teamscores[i]) = ReadShort(); -#endif - - Sbar_UpdateTeamPos(o); -} - -void Net_Reset() -{ -} - -void Ent_ClientData() -{ - float f; - float newspectatee_status; - - f = ReadByte(); - - sb_showscores_force = (f & 1); - - if(f & 2) - { - newspectatee_status = ReadByte(); - if(newspectatee_status == player_localentnum) - newspectatee_status = -1; // observing - } - else - newspectatee_status = 0; - - spectatorbutton_zoom = (f & 4); - - if(f & 8) - { - angles_held_status = 1; - angles_held_x = ReadAngle(); - angles_held_y = ReadAngle(); - angles_held_z = 0; - } - else - angles_held_status = 0; - - if(newspectatee_status != spectatee_status) - { - // clear race stuff - race_laptime = 0; - race_checkpointtime = 0; - } - spectatee_status = newspectatee_status; -} - -void Ent_Nagger() -{ - float nags, i, j, b, f; - - nags = ReadByte(); - - if(nags & 128) - { - if(vote_called_vote) - strunzone(vote_called_vote); - vote_called_vote = strzone(ColorTranslateRGB(ReadString())); - } - - if(nags & 1) - { - for(j = 0; j < maxclients; ++j) - if(playerslots[j]) - playerslots[j].ready = 1; - for(i = 1; i <= maxclients; i += 8) - { - f = ReadByte(); - for(j = i-1, b = 1; b < 256; b *= 2, ++j) - if not(f & b) - if(playerslots[j]) - playerslots[j].ready = 0; - } - } - - ready_waiting = (nags & 1); - ready_waiting_for_me = (nags & 2); - vote_waiting = (nags & 4); - vote_waiting_for_me = (nags & 8); - warmup_stage = (nags & 16); -} - -void Ent_RandomSeed() -{ - float s; - prandom_debug(); - s = ReadShort(); - psrandom(s); -} - -// 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(float bIsNewEntity) CSQC_Ent_Update = -{ - float t; - float savetime; - t = ReadByte(); - - // set up the "time" global for received entities to be correct for interpolation purposes - savetime = time; - if(servertime) - { - time = servertime; - } - else - { - serverprevtime = time; - serverdeltatime = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); - time = serverprevtime + serverdeltatime; - } - -#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED - if(self.enttype) - if(t != self.enttype) - { - print("A CSQC entity changed its type!\n"); - Ent_Remove(); - } -#endif - self.enttype = t; - switch(t) - { - 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_WAYPOINT: Ent_WaypointSprite(); break; - case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; - case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; - case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(); break; - case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(); break; - case ENT_CLIENT_CASING: Ent_Casing(); 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; - default: - error(strcat("unknown entity type in CSQC_Ent_Update: ", ftos(self.enttype), "\n")); - break; - } - - 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() -{ - if(self.entremove) - self.entremove(); - - self.enttype = 0; - self.classname = ""; - self.draw = menu_sub_null; - self.entremove = menu_sub_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. -void CSQC_Ent_Remove() -{ - if(self.enttype) - Ent_Remove(); - remove(self); -} - -void Gamemode_Init() -{ - if(gametype == GAME_ONSLAUGHT) { - print(strcat("Using ", minimapname, " as minimap.\n")); - precache_pic("gfx/ons-cp-neutral.tga"); - precache_pic("gfx/ons-cp-red.tga"); - precache_pic("gfx/ons-cp-blue.tga"); - precache_pic("gfx/ons-frame.tga"); - precache_pic("gfx/ons-frame-team.tga"); - } else if(gametype == GAME_KEYHUNT) { - precache_pic("gfx/sb_key_carrying"); - precache_pic("gfx/sb_key_carrying_outline"); - } -} -// 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) -{ - 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) -{ - print(ColorTranslateRGB(strMessage)); -} - -// CSQC_Parse_CenterPrint : Provides the centerprint string in the first parameter that the server provided. -void CSQC_Parse_CenterPrint(string strMessage) -{ - centerprint(strMessage); -} - -void Fog_Force() -{ - // TODO somehow thwart prvm_globalset client ... - - if(forcefog != "") - localcmd(strcat("\nfog ", forcefog, "\nr_fog_exp2 0\nr_drawfog 1\n")); -} - -void Gamemode_Init(); -void Ent_ScoresInfo() -{ - float i; - self.classname = "ent_client_scores_info"; - gametype = ReadByte(); - for(i = 0; i < MAX_SCORE; ++i) - { - scores_label[i] = strzone(ReadString()); - scores_flags[i] = ReadByte(); - } - for(i = 0; i < MAX_TEAMSCORE; ++i) - { - teamscores_label[i] = strzone(ReadString()); - teamscores_flags[i] = ReadByte(); - } - Sbar_InitScores(); - Gamemode_Init(); -} - -void Ent_Init() -{ - float i; - self.classname = "ent_client_init"; - - nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th - - for(i = 0; i < 24; ++i) - weaponimpulse[i] = ReadByte() - 1; - hook_shotorigin_x = ReadCoord(); - hook_shotorigin_y = ReadCoord(); - hook_shotorigin_z = ReadCoord(); - - if(forcefog) - strunzone(forcefog); - forcefog = strzone(ReadString()); - - armorblockpercent = ReadByte() / 255.0; - - if(!postinit) - PostInit(); -} - -void Net_ReadRace() -{ - float b; - - b = ReadByte(); - - switch(b) - { - case RACE_NET_CHECKPOINT_HIT_QUALIFYING: - race_checkpoint = ReadByte(); - race_time = ReadShort(); - race_previousbesttime = ReadShort(); - if(race_previousbestname) - strunzone(race_previousbestname); - race_previousbestname = strzone(ColorTranslateRGB(ReadString())); - - race_checkpointtime = time; - - if(race_checkpoint == 0) - race_laptime = time; // valid - - break; - - case RACE_NET_CHECKPOINT_CLEAR: - race_laptime = 0; - race_checkpointtime = 0; - break; - - case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING: - race_laptime = ReadCoord(); - race_checkpointtime = -99999; - // fall through - case RACE_NET_CHECKPOINT_NEXT_QUALIFYING: - race_nextcheckpoint = ReadByte(); - - race_nextbesttime = ReadShort(); - if(race_nextbestname) - strunzone(race_nextbestname); - race_nextbestname = strzone(ColorTranslateRGB(ReadString())); - break; - - case RACE_NET_CHECKPOINT_HIT_RACE: - race_mycheckpoint = ReadByte(); - race_mycheckpointtime = time; - race_mycheckpointdelta = ReadShort(); - race_mycheckpointlapsdelta = ReadByte(); - if(race_mycheckpointlapsdelta >= 128) - race_mycheckpointlapsdelta -= 256; - if(race_mycheckpointenemy) - strunzone(race_mycheckpointenemy); - race_mycheckpointenemy = strzone(ColorTranslateRGB(ReadString())); - break; - - case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT: - race_othercheckpoint = ReadByte(); - race_othercheckpointtime = time; - race_othercheckpointdelta = ReadShort(); - race_othercheckpointlapsdelta = ReadByte(); - if(race_othercheckpointlapsdelta >= 128) - race_othercheckpointlapsdelta -= 256; - if(race_othercheckpointenemy) - strunzone(race_othercheckpointenemy); - race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString())); - break; - } -} - -void Net_ReadSpawn() -{ - zoomin_effect = 1; - current_viewzoom = 0.6; -} - -// 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. -float CSQC_Parse_TempEntity() -{ - local float bHandled; - bHandled = true; - // Acquire TE ID - local float nTEID; - nTEID = ReadByte(); - - // NOTE: Could just do return instead of break... - switch(nTEID) - { - case TE_CSQC_PICTURE: - Net_MapVote_Picture(); - bHandled = true; - break; - case TE_CSQC_RACE: - Net_ReadRace(); - bHandled = true; - break; - case 13: // TE_BEAM - Net_GrapplingHook(); - bHandled = true; - break; - case TE_CSQC_SPAWN: - Net_ReadSpawn(); - bHandled = true; - break; - case TE_CSQC_ZCURVEPARTICLES: - Net_ReadZCurveParticles(); - bHandled = true; - break; - case TE_CSQC_NEXGUNBEAMPARTICLE: - Net_ReadNexgunBeamParticle(); - bHandled = true; - break; - case TE_CSQC_LIGHTNINGARC: - Net_ReadLightningarc(); - bHandled = true; - break; - default: - // No special logic for this temporary entity; return 0 so the engine can handle it - bHandled = false; - break; - } - - return bHandled; -} - -string getcommandkey(string text, string command) -{ - string keys; - float n, j, k, l; - - if (!sbar_showbinds) - return text; - - keys = db_get(binddb, command); - if (!keys) - { - n = tokenize(findkeysforcommand(command)); // uses '...' strings - for(j = 0; j < n; ++j) - { - k = stof(argv(j)); - if(k != -1) - { - if ("" == keys) - keys = keynumtostring(k); - else - keys = strcat(keys, ", ", keynumtostring(k)); - - ++l; - if (sbar_showbinds_limit > 0 && sbar_showbinds_limit >= l) break; - } - - } - db_put(binddb, command, keys); - } - - if ("" == keys) { - if (sbar_showbinds > 1) - return strcat(text, " (not bound)"); - else - return text; - } - else if (sbar_showbinds > 1) - return strcat(text, " (", keys, ")"); - else - return keys; -} +// -------------------------------------------------------------------------- +// BEGIN REQUIRED CSQC FUNCTIONS +//include "main.qh" + +#define DP_CSQC_ENTITY_REMOVE_IS_B0RKED + +void cvar_clientsettemp(string cv, string val) +{ + entity e; + for(e = world; (e = find(e, classname, "saved_cvar_value")); ) + if(e.netname == cv) + goto saved; + e = spawn(); + e.classname = "saved_cvar_value"; + e.netname = strzone(cv); + e.message = strzone(cvar_string(cv)); +:saved + cvar_set(cv, val); +} + +void cvar_clientsettemp_restore() +{ + entity e; + for(e = world; (e = find(e, classname, "saved_cvar_value")); ) + cvar_set(e.netname, e.message); +} + +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 = +{ +}; + +#ifdef USE_FTE +float __engine_check; +#endif + +string forcefog; +void WaypointSprite_Load(); +void CSQC_Init(void) +{ +#ifdef USE_FTE +#pragma target ID + __engine_check = checkextension("DP_SV_WRITEPICTURE"); + if(!__engine_check) + { + print("^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n"); + localcmd("\ndisconnect\n"); + return; + } +#pragma target FTE +#endif + + check_unacceptable_compiler_bugs(); + + float i; + CSQC_CheckEngine(); + dprint_load(); + + binddb = db_create(); + tempdb = db_create(); + compressShortVector_init(); + + drawfont = 0; + menu_visible = FALSE; + menu_show = menu_show_error; + menu_action = menu_sub_null; + + for(i = 0; i < 255; ++i) + if(getplayerkey(i, "viewentity") == "") + break; + maxclients = i; + + //ctf_temp_1 = ""; + // localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used + //registercmd("ctf_menu"); + registercmd("ons_map"); + //registercmd("menu_action"); + + registercmd("+button3"); + registercmd("-button3"); + registercmd("+button4"); + registercmd("-button4"); + registercmd("+showaccuracy");registercmd("-showaccuracy"); + +#ifndef CAMERATEST + if(isdemo()) + { +#endif + registercmd("+forward");registercmd("-forward"); + registercmd("+back");registercmd("-back"); + registercmd("+moveup");registercmd("-moveup"); + registercmd("+movedown");registercmd("-movedown"); + registercmd("+moveright");registercmd("-moveright"); + registercmd("+moveleft");registercmd("-moveleft"); + registercmd("+roll_right");registercmd("-roll_right"); + registercmd("+roll_left");registercmd("-roll_left"); +#ifndef CAMERATEST + } +#endif + registercvar("sbar_usecsqc", "1"); + registercvar("sbar_columns", "default", CVAR_SAVE); + + gametype = 0; + + // sbar_fields uses strunzone on the titles! + for(i = 0; i < MAX_SBAR_FIELDS; ++i) + sbar_title[i] = strzone("(null)"); + + postinit = false; + + teams = Sort_Spawn(); + players = Sort_Spawn(); + + GetTeam(COLOR_SPECTATOR, true); // add specs first + + cvar_clientsettemp("_supports_weaponpriority", "1"); + + + + + cs_project_is_b0rked = TRUE; + R_SetView(VF_VIEWPORT, '0 0 0', '640 480 0'); + R_SetView(VF_FOV, '90 90 0'); + R_SetView(VF_ORIGIN, '0 0 0'); + R_SetView(VF_ANGLES, '0 0 0'); + R_SetView(VF_PERSPECTIVE, 1); + makevectors('0 0 0'); + vector v; + v = cs_project(v_forward); + if(v_x - 320 < +1) + if(v_x - 320 > -1) + if(v_y - 240 < +1) + if(v_y - 240 > -1) + cs_project_is_b0rked = FALSE; + + RegisterWeapons(); + + WaypointSprite_Load(); + + Projectile_Precache(); + GibSplash_Precache(); + Casings_Precache(); + DamageInfo_Precache(); + Announcer_Precache(); + + 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 + } + + mi_center = (mi_min + mi_max) * 0.5; + mi_scale = mi_max - mi_min; + minimapname = strzone(minimapname); +} + +// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) +void CSQC_Shutdown(void) +{ +#ifdef USE_FTE +#pragma TARGET id + if(!__engine_check) + return 0; +#pragma TARGET fte +#endif + + remove(teams); + remove(players); + db_close(binddb); + db_close(tempdb); + + cvar_clientsettemp_restore(); + + if(camera_active) + cvar_set("chase_active",ftos(chase_active_backup)); +} + +.float has_team; +float SetTeam(entity o, float Team) +{ + entity tm; + if(Team == -1) // leave + { + if(o.has_team) + { + //print("(DISCONNECT) leave team ", ftos(o.team), "\n"); + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.has_team = 0; + return TRUE; + } + } + else + { + if not(o.has_team) + { + //print("(CONNECT) enter team ", ftos(o.team), "\n"); + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + o.has_team = 1; + return TRUE; + } + else if(Team != o.team) + { + //print("(CHANGE) leave team ", ftos(o.team), "\n"); + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.team = Team; + //print("(CHANGE) enter team ", ftos(o.team), "\n"); + tm = GetTeam(Team, true); + tm.team_size += 1; + return TRUE; + } + } + return FALSE; +} + +void Playerchecker_Think() +{ + float i; + entity e; + for(i = 0; i < maxclients; ++i) + { + e = playerslots[i]; + if(GetPlayerName(i) == "") + { + if(e.sort_prev) + { + //print("playerchecker: KILL KILL KILL\n"); + // player disconnected + SetTeam(e, -1); + RemovePlayer(e); + e.sort_prev = world; + //e.gotscores = 0; + } + } + else + { + if not(e.sort_prev) + { + //print("playerchecker: SPAWN SPAWN SPAWN\n"); + // player connected + if not(e) + playerslots[i] = e = spawn(); + e.sv_entnum = i; + //e.gotscores = 0; // we might already have the scores... + SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with Sbar_UpdatePlayerTeams + RegisterPlayer(e); + Sbar_UpdatePlayerPos(e); + } + } + } + self.nextthink = time + 0.2; +} + +void Porto_Init(); +void TrueAim_Init(); +void PostInit(void) +{ + print(strcat("PostInit\n maxclients = ", ftos(maxclients), "\n")); + localcmd(strcat("\nsbar_columns_set ", cvar_string("sbar_columns"), ";\n")); + + entity playerchecker; + playerchecker = spawn(); + playerchecker.think = Playerchecker_Think; + playerchecker.nextthink = time + 0.2; + + Porto_Init(); + TrueAim_Init(); + + postinit = true; +} + +// CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function +// Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it. +float button_zoom; +void Cmd_Sbar_SetFields(float); +void Cmd_Sbar_Help(float); +float CSQC_ConsoleCommand(string strMessage) +{ + float argc; + // Tokenize String + //argc = tokenize(strMessage); + argc = tokenize_console(strMessage); + + // Acquire Command + local string strCmd; + strCmd = argv(0); + + if(strCmd == "+button4") { // zoom + // return false, because the message shall be sent to the server anyway (for demos/speccing) + if(ignore_plus_zoom) + { + --ignore_plus_zoom; + return false; + } + button_zoom = 1; + return true; + } else if(strCmd == "-button4") { // zoom + if(ignore_minus_zoom) + { + --ignore_minus_zoom; + return false; + } + button_zoom = 0; + return true; + } else if(strCmd == "+button3") { // secondary + button_attack2 = 1; + return false; + } else if(strCmd == "-button3") { // secondary + button_attack2 = 0; + return false; + } else if(strCmd == "+showscores") { + sb_showscores = true; + return true; + } else if(strCmd == "-showscores") { + sb_showscores = false; + return true; + } else if(strCmd == "+showaccuracy") { + sb_showaccuracy = true; + return true; + } else if(strCmd == "-showaccuracy") { + sb_showaccuracy = false; + return true; + } + + if(camera_active) + if(strCmd == "+forward" || strCmd == "-back") { + ++camera_direction_x; + return true; + } else if(strCmd == "-forward" || strCmd == "+back") { + --camera_direction_x; + return true; + } else if(strCmd == "+moveright" || strCmd == "-moveleft") { + --camera_direction_y; + return true; + } else if(strCmd == "-moveright" || strCmd == "+moveleft") { + ++camera_direction_y; + return true; + } else if(strCmd == "+moveup" || strCmd == "-movedown") { + ++camera_direction_z; + return true; + } else if(strCmd == "-moveup" || strCmd == "+movedown") { + --camera_direction_z; + return true; + } else if(strCmd == "+roll_right" || strCmd == "-roll_left") { + ++camera_roll; + return true; + } else if(strCmd == "+roll_left" || strCmd == "-roll_right") { + --camera_roll; + return true; + } + + return false; +} + +.vector view_ofs; +entity debug_shotorg; +void ShotOrg_Draw() +{ + self.origin = view_origin + view_forward * self.view_ofs_x + view_right * self.view_ofs_y + view_up * self.view_ofs_z; + self.angles = view_angles; + self.angles_x = -self.angles_x; + if not(self.cnt) + R_AddEntity(self); +} +void ShotOrg_Draw2D() +{ + vector coord2d_topleft, coord2d_topright, coord2d; + string s; + vector fs; + + s = vtos(self.view_ofs); + s = substring(s, 1, strlen(s) - 2); + if(tokenize_console(s) == 3) + s = strcat(argv(0), " ", argv(1), " ", argv(2)); + + coord2d_topleft = project_3d_to_2d(self.origin + view_up * 4 - view_right * 4); + coord2d_topright = project_3d_to_2d(self.origin + view_up * 4 + view_right * 4); + + fs = '1 1 0' * ((coord2d_topright_x - coord2d_topleft_x) / stringwidth(s, FALSE)); + + coord2d = coord2d_topleft; + if(fs_x < 8) + { + coord2d_x += (coord2d_topright_x - coord2d_topleft_x) * (1 - 8 / fs_x) * 0.5; + fs = '8 8 0'; + } + coord2d_y -= fs_y; + coord2d_z = 0; + drawstring(coord2d, s, fs, '1 1 1', 1, 0); +} + +void ShotOrg_Spawn() +{ + debug_shotorg = spawn(); + debug_shotorg.draw = ShotOrg_Draw; + debug_shotorg.draw2d = ShotOrg_Draw2D; + debug_shotorg.renderflags = RF_VIEWMODEL; + debug_shotorg.effects = EF_FULLBRIGHT; + precache_model("models/shotorg_adjuster.md3"); + setmodel(debug_shotorg, "models/shotorg_adjuster.md3"); + debug_shotorg.scale = 2; + debug_shotorg.view_ofs = '25 8 -8'; +} + +void GameCommand(string msg) +{ + float argc; + argc = tokenize_console(msg); + + if(argv(0) == "help" || argc == 0) + { + print("Usage: cl_cmd COMMAND..., where possible commands are:\n"); + print(" settemp cvar value\n"); + print(" radar\n"); + print(" sbar_columns_set ...\n"); + print(" sbar_columns_help\n"); + GameCommand_Generic("help"); + return; + } + + if(GameCommand_Generic(msg)) + return; + + string cmd; + cmd = argv(0); + if(cmd == "mv_download") { + Cmd_MapVote_MapDownload(argc); + } + else if(cmd == "settemp") { + cvar_clientsettemp(argv(1), argv(2)); + } + else if(cmd == "radar") { + ons_showmap = !ons_showmap; + } + else if(cmd == "sbar_columns_set") { + Cmd_Sbar_SetFields(argc); + } + else if(cmd == "sbar_columns_help") { + Cmd_Sbar_Help(argc); + } +#ifdef BLURTEST + else if(cmd == "blurtest") { + blurtest_time0 = time; + blurtest_time1 = time + stof(argv(1)); + blurtest_radius = stof(argv(2)); + blurtest_power = stof(argv(3)); + } +#endif + else if(cmd == "shotorg_move") { + if(!debug_shotorg) + ShotOrg_Spawn(); + else + debug_shotorg.view_ofs = debug_shotorg.view_ofs + stov(argv(1)); + localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); + } + else if(cmd == "shotorg_movez") { + if(!debug_shotorg) + ShotOrg_Spawn(); + else + debug_shotorg.view_ofs = debug_shotorg.view_ofs + stof(argv(1)) * (debug_shotorg.view_ofs * (1 / debug_shotorg.view_ofs_x)); // closer/farther, same xy pos + localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); + } + else if(cmd == "shotorg_set") { + if(!debug_shotorg) + ShotOrg_Spawn(); + else + debug_shotorg.view_ofs = stov(argv(1)); + localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); + } + else if(cmd == "shotorg_setz") { + if(!debug_shotorg) + ShotOrg_Spawn(); + else + debug_shotorg.view_ofs = debug_shotorg.view_ofs * (stof(argv(1)) / debug_shotorg.view_ofs_x); // closer/farther, same xy pos + localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n"); + } + else if(cmd == "shotorg_toggle_hide") { + if(debug_shotorg) + { + debug_shotorg.cnt = !debug_shotorg.cnt; + } + } + else if(cmd == "shotorg_end") { + if(debug_shotorg) + { + print(vtos(debug_shotorg.view_ofs), "\n"); + remove(debug_shotorg); + debug_shotorg = world; + } + localcmd("sv_cmd debug_shotorg\n"); + } + else + { + print("Invalid command. For a list of supported commands, try cl_cmd help.\n"); + } + + return; +} + +// CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client. +// Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine. +// All keys are in ascii. +// bInputType = 0 is key pressed, 1 is key released, 2 is mouse input. +// In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0. +// In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta. +float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) +{ + local float bSkipKey; + bSkipKey = false; + + if(menu_visible) + if(menu_action(bInputType, nPrimary, nSecondary)) + return TRUE; + return bSkipKey; +} + +// END REQUIRED CSQC FUNCTIONS +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +// BEGIN OPTIONAL CSQC FUNCTIONS +void Ent_ReadEntCS() +{ + InterpolateOrigin_Undo(); + + self.classname = "entcs_receiver"; + self.sv_entnum = ReadByte() - 1; + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + self.angles_y = ReadByte() * 360.0 / 256; + self.origin_z = self.angles_x = self.angles_z = 0; + + InterpolateOrigin_Note(); +} + +void Ent_Remove(); + +void Ent_RemovePlayerScore() +{ + float i; + + if(self.owner) + { + SetTeam(self.owner, -1); + self.owner.gotscores = 0; + for(i = 0; i < MAX_SCORE; ++i) + self.owner.(scores[i]) = 0; // clear all scores + } +} + +void Ent_ReadPlayerScore() +{ + float i, n; + float isNew; + entity o; + + // damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN + // (no I've never heard of M-x replace-string, sed, or anything like that) + isNew = !self.owner; // workaround for DP bug + n = ReadByte()-1; + +#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(!isNew && n != self.sv_entnum) + { + print("A CSQC entity changed its owner!\n"); + isNew = true; + Ent_Remove(); + self.enttype = ENT_CLIENT_SCORES; + } +#endif + + self.sv_entnum = n; + + if not(playerslots[self.sv_entnum]) + playerslots[self.sv_entnum] = spawn(); + o = self.owner = playerslots[self.sv_entnum]; + o.sv_entnum = self.sv_entnum; + o.gotscores = 1; + + //if not(o.sort_prev) + // RegisterPlayer(o); + //playerchecker will do this for us later, if it has not already done so + +#if MAX_SCORE <= 3 + for(i = 0; i < MAX_SCORE; ++i) + o.(scores[i]) = ReadShort(); +#else + float sf; +#if MAX_SCORE <= 8 + sf = ReadByte(); +#else + sf = ReadShort(); +#endif + float p; + for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) + if(sf & p) + o.(scores[i]) = ReadShort(); +#endif + + if(o.sort_prev) + Sbar_UpdatePlayerPos(o); // if not registered, we cannot do this yet! + + self.entremove = Ent_RemovePlayerScore; +} + +void Ent_ReadTeamScore() +{ + float i; + entity o; + + self.team = ReadByte(); + o = self.owner = GetTeam(self.team, true); + +#if MAX_TEAMSCORE <= 3 + for(i = 0; i < MAX_TEAMSCORE; ++i) + o.(teamscores[i]) = ReadShort(); +#else + float sf; +#if MAX_TEAMSCORE <= 8 + sf = ReadByte(); +#else + sf = ReadShort(); +#endif + float p; + for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) + if(sf & p) + o.(teamscores[i]) = ReadShort(); +#endif + + Sbar_UpdateTeamPos(o); +} + +void Net_Reset() +{ +} + +void Ent_ClientData() +{ + float f; + float newspectatee_status; + + f = ReadByte(); + + sb_showscores_force = (f & 1); + + if(f & 2) + { + newspectatee_status = ReadByte(); + if(newspectatee_status == player_localentnum) + newspectatee_status = -1; // observing + } + else + newspectatee_status = 0; + + spectatorbutton_zoom = (f & 4); + + if(f & 8) + { + angles_held_status = 1; + angles_held_x = ReadAngle(); + angles_held_y = ReadAngle(); + angles_held_z = 0; + } + else + angles_held_status = 0; + + if(newspectatee_status != spectatee_status) + { + // clear race stuff + race_laptime = 0; + race_checkpointtime = 0; + } + spectatee_status = newspectatee_status; +} + +void Ent_Nagger() +{ + float nags, i, j, b, f; + + nags = ReadByte(); + + if(nags & 128) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = strzone(ColorTranslateRGB(ReadString())); + } + + if(nags & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].ready = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if not(f & b) + if(playerslots[j]) + playerslots[j].ready = 0; + } + } + + ready_waiting = (nags & 1); + ready_waiting_for_me = (nags & 2); + vote_waiting = (nags & 4); + vote_waiting_for_me = (nags & 8); + warmup_stage = (nags & 16); +} + +void Ent_RandomSeed() +{ + float s; + prandom_debug(); + s = ReadShort(); + psrandom(s); +} + +// 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(float bIsNewEntity) CSQC_Ent_Update = +{ + float t; + float savetime; + t = ReadByte(); + + // set up the "time" global for received entities to be correct for interpolation purposes + savetime = time; + if(servertime) + { + time = servertime; + } + else + { + serverprevtime = time; + serverdeltatime = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); + time = serverprevtime + serverdeltatime; + } + +#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(self.enttype) + if(t != self.enttype) + { + print("A CSQC entity changed its type!\n"); + Ent_Remove(); + } +#endif + self.enttype = t; + switch(t) + { + 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_WAYPOINT: Ent_WaypointSprite(); break; + case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; + case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; + case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(); break; + case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(); break; + case ENT_CLIENT_CASING: Ent_Casing(); 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; + default: + error(strcat("unknown entity type in CSQC_Ent_Update: ", ftos(self.enttype), "\n")); + break; + } + + 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() +{ + if(self.entremove) + self.entremove(); + + self.enttype = 0; + self.classname = ""; + self.draw = menu_sub_null; + self.entremove = menu_sub_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. +void CSQC_Ent_Remove() +{ + if(self.enttype) + Ent_Remove(); + remove(self); +} + +void Gamemode_Init() +{ + if(gametype == GAME_ONSLAUGHT) { + print(strcat("Using ", minimapname, " as minimap.\n")); + precache_pic("gfx/ons-cp-neutral.tga"); + precache_pic("gfx/ons-cp-red.tga"); + precache_pic("gfx/ons-cp-blue.tga"); + precache_pic("gfx/ons-frame.tga"); + precache_pic("gfx/ons-frame-team.tga"); + } else if(gametype == GAME_KEYHUNT) { + precache_pic("gfx/sb_key_carrying"); + precache_pic("gfx/sb_key_carrying_outline"); + } +} +// 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) +{ + 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) +{ + print(ColorTranslateRGB(strMessage)); +} + +// CSQC_Parse_CenterPrint : Provides the centerprint string in the first parameter that the server provided. +void CSQC_Parse_CenterPrint(string strMessage) +{ + centerprint(strMessage); +} + +void Fog_Force() +{ + // TODO somehow thwart prvm_globalset client ... + + if(forcefog != "") + localcmd(strcat("\nfog ", forcefog, "\nr_fog_exp2 0\nr_drawfog 1\n")); +} + +void Gamemode_Init(); +void Ent_ScoresInfo() +{ + float i; + self.classname = "ent_client_scores_info"; + gametype = ReadByte(); + for(i = 0; i < MAX_SCORE; ++i) + { + scores_label[i] = strzone(ReadString()); + scores_flags[i] = ReadByte(); + } + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + teamscores_label[i] = strzone(ReadString()); + teamscores_flags[i] = ReadByte(); + } + Sbar_InitScores(); + Gamemode_Init(); +} + +void Ent_Init() +{ + float i; + self.classname = "ent_client_init"; + + nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th + + for(i = 0; i < 24; ++i) + weaponimpulse[i] = ReadByte() - 1; + hook_shotorigin_x = ReadCoord(); + hook_shotorigin_y = ReadCoord(); + hook_shotorigin_z = ReadCoord(); + + if(forcefog) + strunzone(forcefog); + forcefog = strzone(ReadString()); + + armorblockpercent = ReadByte() / 255.0; + + if(!postinit) + PostInit(); +} + +void Net_ReadRace() +{ + float b; + + b = ReadByte(); + + switch(b) + { + case RACE_NET_CHECKPOINT_HIT_QUALIFYING: + race_checkpoint = ReadByte(); + race_time = ReadShort(); + race_previousbesttime = ReadShort(); + if(race_previousbestname) + strunzone(race_previousbestname); + race_previousbestname = strzone(ColorTranslateRGB(ReadString())); + + race_checkpointtime = time; + + if(race_checkpoint == 0) + race_laptime = time; // valid + + break; + + case RACE_NET_CHECKPOINT_CLEAR: + race_laptime = 0; + race_checkpointtime = 0; + break; + + case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING: + race_laptime = ReadCoord(); + race_checkpointtime = -99999; + // fall through + case RACE_NET_CHECKPOINT_NEXT_QUALIFYING: + race_nextcheckpoint = ReadByte(); + + race_nextbesttime = ReadShort(); + if(race_nextbestname) + strunzone(race_nextbestname); + race_nextbestname = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE: + race_mycheckpoint = ReadByte(); + race_mycheckpointtime = time; + race_mycheckpointdelta = ReadShort(); + race_mycheckpointlapsdelta = ReadByte(); + if(race_mycheckpointlapsdelta >= 128) + race_mycheckpointlapsdelta -= 256; + if(race_mycheckpointenemy) + strunzone(race_mycheckpointenemy); + race_mycheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT: + race_othercheckpoint = ReadByte(); + race_othercheckpointtime = time; + race_othercheckpointdelta = ReadShort(); + race_othercheckpointlapsdelta = ReadByte(); + if(race_othercheckpointlapsdelta >= 128) + race_othercheckpointlapsdelta -= 256; + if(race_othercheckpointenemy) + strunzone(race_othercheckpointenemy); + race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + } +} + +void Net_ReadSpawn() +{ + zoomin_effect = 1; + current_viewzoom = 0.6; +} + +// 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. +float CSQC_Parse_TempEntity() +{ + local float bHandled; + bHandled = true; + // Acquire TE ID + local float nTEID; + nTEID = ReadByte(); + + // NOTE: Could just do return instead of break... + switch(nTEID) + { + case TE_CSQC_PICTURE: + Net_MapVote_Picture(); + bHandled = true; + break; + case TE_CSQC_RACE: + Net_ReadRace(); + bHandled = true; + break; + case 13: // TE_BEAM + Net_GrapplingHook(); + bHandled = true; + break; + case TE_CSQC_SPAWN: + Net_ReadSpawn(); + bHandled = true; + break; + case TE_CSQC_ZCURVEPARTICLES: + Net_ReadZCurveParticles(); + bHandled = true; + break; + case TE_CSQC_NEXGUNBEAMPARTICLE: + Net_ReadNexgunBeamParticle(); + bHandled = true; + break; + case TE_CSQC_LIGHTNINGARC: + Net_ReadLightningarc(); + bHandled = true; + break; + default: + // No special logic for this temporary entity; return 0 so the engine can handle it + bHandled = false; + break; + } + + return bHandled; +} + +string getcommandkey(string text, string command) +{ + string keys; + float n, j, k, l; + + if (!sbar_showbinds) + return text; + + keys = db_get(binddb, command); + if (!keys) + { + n = tokenize(findkeysforcommand(command)); // uses '...' strings + for(j = 0; j < n; ++j) + { + k = stof(argv(j)); + if(k != -1) + { + if ("" == keys) + keys = keynumtostring(k); + else + keys = strcat(keys, ", ", keynumtostring(k)); + + ++l; + if (sbar_showbinds_limit > 0 && sbar_showbinds_limit >= l) break; + } + + } + db_put(binddb, command, keys); + } + + if ("" == keys) { + if (sbar_showbinds > 1) + return strcat(text, " (not bound)"); + else + return text; + } + else if (sbar_showbinds > 1) + return strcat(text, " (", keys, ")"); + else + return keys; +} diff --git a/data/qcsrc/client/View.qc b/data/qcsrc/client/View.qc index 3cfdce7da..a122ed302 100644 --- a/data/qcsrc/client/View.qc +++ b/data/qcsrc/client/View.qc @@ -1,863 +1,863 @@ -#define spider_rocket_icon "gfx/spiderbot/rocket_ico.tga" -#define spider_rocket_targ "gfx/spiderbot/target.tga" -#define SPIDER_CROSS "textures/spiderbot/cross.tga" -#define rkt_size 32 -#define rld_size_x 256 -#define rld_size_y 16 - -entity porto; -vector polyline[16]; -float trace_dphitcontents; -float trace_networkentity; -float Q3SURFACEFLAG_SLICK = 2; // low friction surface -float DPCONTENTS_SOLID = 1; // blocks player movement -float DPCONTENTS_BODY = 32; // blocks player movement -float DPCONTENTS_CORPSE = 64; // blocks player movement -float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement -void Porto_Draw() -{ - vector p, dir, ang, q, nextdir; - float idx, portal_number, portal1_idx; - - if(activeweapon != WEP_PORTO || spectatee_status || gametype == GAME_NEXBALL) - return; - if(intermission == 1) - return; - if(intermission == 2) - return; - if (getstati(STAT_HEALTH) <= 0) - return; - - dir = view_forward; - - if(angles_held_status) - { - makevectors(angles_held); - dir = v_forward; - } - - p = view_origin; - - polyline[0] = p; - idx = 1; - portal_number = 0; - nextdir = dir; - - for(;;) - { - dir = nextdir; - traceline(p, p + 65536 * dir, TRUE, porto); - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - return; - nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal - p = trace_endpos; - polyline[idx] = p; - ++idx; - if(idx >= 16) - return; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) - continue; - ++portal_number; - ang = vectoangles2(trace_plane_normal, dir); - ang_x = -ang_x; - makevectors(ang); - if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) - return; - if(portal_number == 1) - portal1_idx = idx; - if(portal_number >= 2) - break; - } - - while(idx >= 2) - { - p = polyline[idx-2]; - q = polyline[idx-1]; - if(idx == 2) - p = p - view_up * 16; - if(idx-1 >= portal1_idx) - { - Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL); - } - else - { - Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL); - } - --idx; - } -} - -/** - * Checks whether the server initiated a map restart (stat_game_starttime changed) - * - * TODO: Use a better solution where a common shared entitiy is used that contains - * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT - * and STAT_FRAGLIMIT to be auto-sent) - */ -void CheckForGamestartChange() { - float startTime; - startTime = getstatf(STAT_GAMESTARTTIME); - if (previous_game_starttime != startTime) { - if ((time + 5.0) < startTime) { - //if connecting to server while restart was active don't always play prepareforbattle - sound(self, CHAN_VOICE, "announcer/robotic/prepareforbattle.wav", VOL_BASEVOICE, ATTN_NONE); - } - if (time < startTime) { - restartAnnouncer = spawn(); - restartAnnouncer.think = restartAnnouncer_Think; - restartAnnouncer.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime - } - } - previous_game_starttime = startTime; -} - -void Porto_Init() -{ - porto = spawn(); - porto.classname = "porto"; - porto.draw = Porto_Draw; - porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; -} - -float drawtime; - -float tan(float x) -{ - return sin(x) / cos(x); -} -float atan2(float y, float x) -{ - vector v; - v = '1 0 0' * x + '0 1 0' * y; - v = vectoangles(v); - return v_y * 0.01745329251994329576; -} - -vector GetCurrentFov(float fov) -{ - float zoomsensitivity, zoomspeed, zoomfactor, zoomdir; - - zoomsensitivity = cvar("cl_zoomsensitivity"); - zoomfactor = cvar("cl_zoomfactor"); - if(zoomfactor < 1 || zoomfactor > 16) - zoomfactor = 2.5; - zoomspeed = cvar("cl_zoomspeed"); - if(zoomspeed >= 0) - if(zoomspeed < 0.5 || zoomspeed > 16) - zoomspeed = 3.5; - - zoomdir = button_zoom; - if(getstati(STAT_ACTIVEWEAPON) == WEP_NEX) // do NOT use switchweapon here - zoomdir += button_attack2; - if(spectatee_status > 0 || isdemo()) - { - if(spectatorbutton_zoom) - zoomdir = 0 + !zoomdir; - // do not even THINK about removing this 0 - // _I_ know what I am doing - // fteqcc does not - } - - if(zoomdir) - zoomin_effect = 0; - - if(zoomin_effect || camera_active) - { - current_viewzoom = min(1, current_viewzoom + drawframetime); - } - else - { - if(zoomspeed < 0) // instant zoom - { - if(zoomdir) - current_viewzoom = 1 / zoomfactor; - else - current_viewzoom = 1; - } - else - { - if(zoomdir) - current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor); - else - current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1); - } - } - - if(almost_equals(current_viewzoom, 1)) - current_zoomfraction = 0; - else if(almost_equals(current_viewzoom, 1/zoomfactor)) - current_zoomfraction = 1; - else - current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1); - - if(zoomsensitivity < 1) - setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity)); - else - setsensitivityscale(1); - - float frustumx, frustumy, fovx, fovy; - frustumy = tan(fov * 0.00872664625997164788) * 0.75 * current_viewzoom; - frustumx = frustumy * vid_width / vid_height / vid_pixelheight; - fovx = atan2(frustumx, 1) / 0.00872664625997164788; - fovy = atan2(frustumy, 1) / 0.00872664625997164788; - - return '1 0 0' * fovx + '0 1 0' * fovy; -} - -// this function must match W_SetupShot! -float zoomscript_caught; -entity trueaim; - -#define SHOTTYPE_HITTEAM 1 -#define SHOTTYPE_HITOBSTRUCTION 2 -#define SHOTTYPE_HITWORLD 3 -#define SHOTTYPE_HITENEMY 4 - -void TrueAim_Init() -{ - trueaim = spawn(); - trueaim.classname = "trueaim"; - trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; -} - -float EnemyHitCheck(vector start, vector mi, vector ma, vector end) -{ - float t; - tracebox(start, mi, ma, end, MOVE_NORMAL, trueaim); - if(trace_networkentity < 1) - return SHOTTYPE_HITWORLD; - if(trace_networkentity > maxclients) - return SHOTTYPE_HITWORLD; - t = GetPlayerColor(trace_networkentity - 1); - if(teamplay) - if(t == myteam) - return SHOTTYPE_HITTEAM; - if(t == COLOR_SPECTATOR) - return SHOTTYPE_HITWORLD; - return SHOTTYPE_HITENEMY; -} - -float TrueAimCheck() -{ - float nudge = 1; // added to traceline target and subtracted from result - vector vecs, trueaimpoint, w_shotorg; - vector mi, ma, dv; - float shottype; - - mi = ma = '0 0 0'; - - switch(activeweapon) - { - case WEP_TUBA: // no aim - case WEP_PORTO: // shoots from eye - case WEP_HOOK: // no trueaim - case WEP_GRENADE_LAUNCHER: // toss curve - return SHOTTYPE_HITWORLD; - case WEP_CAMPINGRIFLE: - if(zoomscript_caught) - return EnemyHitCheck(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE); - break; - case WEP_ROCKET_LAUNCHER: // projectile has a size! - mi = '-3 -3 -3'; - ma = '3 3 3'; - break; - case WEP_SEEKER: // projectile has a size! - mi = '-2 -2 -2'; - ma = '2 2 2'; - break; - case WEP_ELECTRO: // projectile has a size! - mi = '0 0 -3'; - ma = '0 0 -3'; - break; - } - - vecs = decompressShotOrigin(getstati(STAT_SHOTORG)); - - traceline(view_origin, view_origin + view_forward * MAX_SHOT_DISTANCE, MOVE_NOMONSTERS, trueaim); - trueaimpoint = trace_endpos; - - if(vecs_x > 0) - vecs_y = -vecs_y; - else - vecs = '0 0 0'; - - dv = view_right * vecs_y + view_up * vecs_z; - w_shotorg = view_origin + dv; - - // now move the vecs forward as much as requested if possible - tracebox(w_shotorg, mi, ma, w_shotorg + view_forward * (vecs_x + nudge), MOVE_NORMAL, trueaim); // FIXME this MOVE_NORMAL part will misbehave a little in csqc - w_shotorg = trace_endpos - view_forward * nudge; - - shottype = EnemyHitCheck(w_shotorg, mi, ma, trueaimpoint); - if(shottype != SHOTTYPE_HITWORLD) - return shottype; - - // now test whether we will actually hit the trueaimpoint... - tracebox(w_shotorg, mi, ma, trueaimpoint, MOVE_NOMONSTERS, trueaim); - - if(vlen(trace_endpos - trueaimpoint) > vlen(ma - mi) + 1) - // yes, this is an ugly hack... but it seems good enough to find out whether the test hits the same place as the initial trace - return SHOTTYPE_HITOBSTRUCTION; - - return SHOTTYPE_HITWORLD; -} - -void CSQC_common_hud(void); - -void CSQC_kh_hud(void); -void CSQC_ctf_hud(void); -void PostInit(void); -void CSQC_Demo_Camera(); -float Sbar_WouldDrawScoreboard (); -float view_set; -float camera_mode; -string NextFrameCommand; -void CSQC_spider_HUD(); -void CSQC_UpdateView(float w, float h) -{ - entity e; - float fov; - float f, i, j; - - dprint_load(); - WaypointSprite_Load(); - - myteam = GetPlayerColor(player_localentnum - 1); - - ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); - - // Render the Scene - if(!intermission || !view_set) - { - view_origin = pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT); - view_angles = input_angles; - makevectors(view_angles); - view_forward = v_forward; - view_right = v_right; - view_up = v_up; - view_set = 1; - } - - vid_width = w; - vid_height = h; - -#ifdef BLURTEST - if(time > blurtest_time0 && time < blurtest_time1) - { - float r, t; - - t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0); - r = t * blurtest_radius; - f = 1 / pow(t, blurtest_power) - 1; - - cvar_set("r_glsl_postprocess", "1"); - cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0")); - } - else - { - cvar_set("r_glsl_postprocess", "0"); - cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0"); - } -#endif - - Fog_Force(); - - drawframetime = max(0.000001, time - drawtime); - drawtime = time; - - // watch for gametype changes here... - // in ParseStuffCMD the cmd isn't executed yet :/ - // might even be better to add the gametype to TE_CSQC_INIT...? - if(!postinit) - PostInit(); - - CheckForGamestartChange(); - maptimeAnnouncer(); - - fov = cvar("fov"); - if(button_zoom || fov <= 59.5) - { - if(!zoomscript_caught) - { - localcmd("+button4\n"); - zoomscript_caught = 1; - ignore_plus_zoom += 1; - } - } - else - { - if(zoomscript_caught) - { - localcmd("-button4\n"); - zoomscript_caught = 0; - ignore_minus_zoom += 1; - } - } - - sbar_alpha_fg = cvar("sbar_alpha_fg" ); - sbar_currentammo = cvar("sbar_showcurrentammo"); - sbar_hudselector = cvar("sbar_hudselector"); - sbar_hud_accuracy = cvar("sbar_hud_accuracy"); - ColorTranslateMode = cvar("cl_stripcolorcodes"); - activeweapon = getstati(STAT_SWITCHWEAPON); - f = cvar("teamplay"); - if(f != teamplay) - { - teamplay = f; - Sbar_InitScores(); - } - - if(last_weapon != activeweapon) { - weapontime = time; - last_weapon = activeweapon; - } - - // ALWAYS Clear Current Scene First - R_ClearScene(); - - // Assign Standard Viewflags - // Draw the World (and sky) - R_SetView(VF_DRAWWORLD, 1); - - // Set the console size vars - vid_conwidth = cvar("vid_conwidth"); - vid_conheight = cvar("vid_conheight"); - vid_pixelheight = cvar("vid_pixelheight"); - - R_SetView(VF_FOV, GetCurrentFov(fov)); - - // Camera for demo playback - if(camera_active) - { - if(cvar("camera_enable")) - CSQC_Demo_Camera(); - else - { - cvar_set("chase_active", ftos(chase_active_backup)); - cvar_set("cl_demo_mousegrab", "0"); - camera_active = FALSE; - } - } -#ifdef CAMERATEST - else if(cvar("camera_enable")) -#else - else if(cvar("camera_enable") && isdemo()) -#endif - { - // Enable required Darkplaces cvars - chase_active_backup = cvar("chase_active"); - cvar_set("chase_active", "2"); - cvar_set("cl_demo_mousegrab", "1"); - camera_active = TRUE; - camera_mode = FALSE; - } - - // Draw the Crosshair - float scoreboard_active; - scoreboard_active = Sbar_WouldDrawScoreboard(); - R_SetView(VF_DRAWCROSSHAIR, 0); //Make sure engine crosshairs are always hidden - - // Draw the Engine Status Bar (the default Quake HUD) - R_SetView(VF_DRAWENGINESBAR, 0); - - // fetch this one only once per frame - sbar_showbinds = cvar("sbar_showbinds"); - sbar_showbinds_limit = cvar("sbar_showbinds_limit"); - - // Update the mouse position - /* - mousepos_x = vid_conwidth; - mousepos_y = vid_conheight; - mousepos = mousepos*0.5 + getmousepos(); - */ - - R_AddEntities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); - - e = self; - for(self = world; (self = nextent(self)); ) - if(self.draw) - self.draw(); - self = e; - R_RenderScene(); - - // now switch to 2D drawing mode by calling a 2D drawing function - // then polygon drawing will draw as 2D stuff, and NOT get queued until the - // next R_RenderScene call - drawstring('0 0 0', "", '1 1 0', '1 1 1', 0, 0); - - // Draw the mouse cursor - // NOTE: drawpic must happen after R_RenderScene for some reason - //drawpic(getmousepos(), "gfx/cursor.tga", '11 14 0', '1 1 1', 1, 0); - //drawstring('50 50', ftos(game), '10 10 0', '1 1 1', 1, 0); - //self = edict_num(player_localnum); - //drawstring('0 0', vtos(pmove_org), '8 8 0', '1 1 1', 1, 0); - //drawstring('0 8', strcat("ORG: ", vtos(self.origin), " state: ", ftos(self.ctf_state), " HP: ", ftos(self.health)), '8 8 0', '1 1 1', 1, 0); - // as long as the ctf part isn't in, this is useless - if(menu_visible) - menu_show(); - - /*if(gametype == GAME_CTF) - { - ctf_view(); - } else */ - - // draw 2D entities - e = self; - for(self = world; (self = nextent(self)); ) - if(self.draw2d) - self.draw2d(); - self = e; - - // draw radar - if( - ons_showmap - || - ( - !scoreboard_active - && - cvar_string("cl_teamradar") != "0" - && - ( - cvar("cl_teamradar") == 2 - || - teamplay - ) - ) - ) - teamradar_view(); - - if (cvar("cl_showpressedkeys")) // draw pressed keys when spectating and playing - { - if(spectatee_status > 0 || cvar("cl_showpressedkeys") >= 2) - Sbar_DrawPressedKeys(); - } - - if (cvar("cl_showspeed")) - Sbar_ShowSpeed(); - - // draw sbar - if(cvar("r_letterbox") == 0) - Sbar_DrawCenterPrint(); // draw centerprint messages even if viewsize >= 120 - - float hud; - hud = getstati(STAT_HUD); - if(hud == HUD_SPIDEBOT) - { - vector sz; - CSQC_spider_HUD(); - sz = drawgetimagesize(SPIDER_CROSS); - sz_x *= cvar_or("cl_vehicle_spiderbot_cross_size",1); - sz_y *= cvar_or("cl_vehicle_spiderbot_cross_size",1); - drawpic('0.5 0 0' * (vid_conwidth - sz_x) + '0 0.5 0' * (vid_conheight - sz_y), SPIDER_CROSS, sz, '1 1 1', cvar_or("cl_vehicle_spiderbot_cross_alpha",0.6), DRAWFLAG_NORMAL); - } - else - { - if(cvar("r_letterbox") == 0) - if(cvar("viewsize") < 120) - CSQC_common_hud(); - - // crosshair goes VERY LAST - if(!scoreboard_active && !ons_showmap && !camera_active) { - // TrueAim check - float shottype; - - if(cvar("crosshair_hittest")) - shottype = TrueAimCheck(); - else - shottype = SHOTTYPE_HITWORLD; - - string wcross_style; - wcross_style = cvar_string("crosshair"); - - if (wcross_style != "0") { - vector wcross_color, wcross_size; - string wcross_wep, wcross_name; - float wcross_alpha, wcross_sizefloat; - - wcross_color_x = cvar("crosshair_color_red"); - wcross_color_y = cvar("crosshair_color_green"); - wcross_color_z = cvar("crosshair_color_blue"); - wcross_alpha = cvar("crosshair_color_alpha"); - wcross_sizefloat = cvar("crosshair_size"); - if (cvar("crosshair_per_weapon")) { - e = get_weaponinfo(activeweapon); - if (e && e.netname != "") - { - wcross_wep = e.netname; - wcross_style = cvar_string(strcat("crosshair_", wcross_wep)); - if(wcross_style == "") - wcross_style = e.netname; - - if(!cvar("crosshair_color_override")) - { - wcross_color_x = cvar(strcat("crosshair_", wcross_wep, "_color_red")); - wcross_color_y = cvar(strcat("crosshair_", wcross_wep, "_color_green")); - wcross_color_z = cvar(strcat("crosshair_", wcross_wep, "_color_blue")); - } - - wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_color_alpha")); - wcross_sizefloat *= cvar(strcat("crosshair_", wcross_wep, "_size")); - } - } - - wcross_name = strcat("gfx/crosshair", wcross_style); - - if(shottype == SHOTTYPE_HITENEMY) - wcross_sizefloat *= cvar("crosshair_hittest"); // is not queried if hittest is 0 - if(shottype == SHOTTYPE_HITTEAM) - wcross_sizefloat /= cvar("crosshair_hittest"); // is not queried if hittest is 0 - - wcross_size = drawgetimagesize(wcross_name); - wcross_size_x *= wcross_sizefloat; - wcross_size_y *= wcross_sizefloat; - - if(shottype == SHOTTYPE_HITENEMY || shottype == SHOTTYPE_HITWORLD) - { - drawpic('0.5 0 0' * (vid_conwidth - wcross_size_x) + '0 0.5 0' * (vid_conheight - wcross_size_y), wcross_name, wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL); - } - else - { - wcross_alpha *= 0.04 * 0.75; - for(i = -2; i <= 2; ++i) - for(j = -2; j <= 2; ++j) - drawpic('0.5 0 0' * (vid_conwidth - wcross_size_x + i) + '0 0.5 0' * (vid_conheight - wcross_size_y + j), wcross_name, wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL); - } - } - } - } - - if(NextFrameCommand) - { - localcmd("\n", NextFrameCommand, "\n"); - NextFrameCommand = string_null; - } -} - -void Sbar_Draw(); -void CSQC_spider_HUD() -{ - float rockets,reload,heat,hp,shield,i; - vector p,pp; - - rockets = getstati(STAT_SPIDERBOT_ROCKETS); - heat = min(getstatf(STAT_SPIDERBOT_HEAT),1); - reload = min(getstatf(STAT_SPIDERBOT_RELOAD),1); - hp = min(getstatf(STAT_SPIDERBOT_HEALTH),1); - shield = min(getstatf(STAT_SPIDERBOT_SHIELD),1); - - // Draw health bar - p = '0.5 0 0' * (vid_conwidth - (rkt_size * 8)); - p = p + '0 1 0' * vid_conheight - '0 32 0'; - //pp = ('0 1 0' * hp) + ('1 0 0' * (1-hp)); - drawfill(p, '256 0 0' * shield + '0 8 0' , '0.5 0.5 1', 0.75, DRAWFLAG_NORMAL); - p_y += 8; - drawfill(p, '256 0 0' * hp + '0 8 0' , '0 1 0', 0.75, DRAWFLAG_NORMAL); - p_x += 256 * hp; - drawfill(p, '256 0 0' * (1-hp) + '0 8 0' , '0 0 0', 0.75, DRAWFLAG_NORMAL); - - // Draw minigun heat indicator - p = '0.5 0 0' * (vid_conwidth - 256); - p = p + '0 1 0' * vid_conheight - '0 34 0'; - drawfill(p, '256 0 0' * (1-heat) + '0 2 0' ,'0 0 1', 0.5, DRAWFLAG_NORMAL); - p_x += 256 * (1-heat); - drawfill(p, '256 0 0' * heat + '0 2 0' , '1 0 0', 0.5, DRAWFLAG_NORMAL); - - // Draw rocket icons for loaded/empty tubes. - pp = '0.5 0 0' * (vid_conwidth - (rkt_size * 8)); - pp += '0 1 0' * vid_conheight - '0 64 0'; - for(i = 0; i < 8; ++i) - { - p = pp + '1 0 0' * (rkt_size * i); - if(rockets == 8) - { - if(floor(reload * 8) == i) - { - drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 0 0' + '0 1 0' * ((reload*8)-i), 0.75 , DRAWFLAG_NORMAL); - } - else if(i < reload * 8) - drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 1 0', 0.75 , DRAWFLAG_NORMAL); - else - drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0.5 0.5 0.5', 0.75, DRAWFLAG_NORMAL); - } - else - { - if(i < rockets) - drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 0 0', 0.25, DRAWFLAG_NORMAL); - else - drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 1 0' * reload, 0.75, DRAWFLAG_NORMAL); - } - } - - if (sb_showscores) - { - Sbar_DrawScoreboard(); - Sbar_DrawCenterPrint(); - } - -} -void CSQC_common_hud(void) -{ - - // Sbar_SortFrags(); done in Sbar_Draw - float hud; - hud = getstati(STAT_HUD); - - //hud = 10; - switch(hud) - { - case HUD_NORMAL: - Sbar_Draw(); - break; - - case HUD_SPIDEBOT: - CSQC_spider_HUD(); - break; - } -} - - -// following vectors must be global to allow seamless switching between camera modes -vector camera_offset, current_camera_offset, mouse_angles, current_angles, current_origin, current_position; -void CSQC_Demo_Camera() -{ - float speed, attenuation, dimensions; - vector tmp, delta; - - if( cvar("camera_reset") || !camera_mode ) - { - camera_offset = '0 0 0'; - current_angles = '0 0 0'; - camera_direction = '0 0 0'; - camera_offset_z += 30; - camera_offset_x += 30 * -cos(current_angles_y * DEG2RAD); - camera_offset_y += 30 * -sin(current_angles_y * DEG2RAD); - current_origin = view_origin; - current_camera_offset = camera_offset; - cvar_set("camera_reset", "0"); - camera_mode = CAMERA_CHASE; - } - - // Camera angles - if( camera_roll ) - mouse_angles_z += camera_roll * cvar("camera_speed_roll"); - - if(cvar("camera_look_player")) - { - local vector dir; - local float n; - - dir = normalize(view_origin - current_position); - n = mouse_angles_z; - mouse_angles = vectoangles(dir); - mouse_angles_x = mouse_angles_x * -1; - mouse_angles_z = n; - } - else - { - tmp = getmousepos() * 0.1; - if(vlen(tmp)>cvar("camera_mouse_treshold")) - { - mouse_angles_x += tmp_y * cos(mouse_angles_z * DEG2RAD) + (tmp_x * sin(mouse_angles_z * DEG2RAD)); - mouse_angles_y -= tmp_x * cos(mouse_angles_z * DEG2RAD) + (tmp_y * -sin(mouse_angles_z * DEG2RAD)); - } - } - - while (mouse_angles_x < -180) mouse_angles_x = mouse_angles_x + 360; - while (mouse_angles_x > 180) mouse_angles_x = mouse_angles_x - 360; - while (mouse_angles_y < -180) mouse_angles_y = mouse_angles_y + 360; - while (mouse_angles_y > 180) mouse_angles_y = mouse_angles_y - 360; - - // Fix difference when angles don't have the same sign - delta = '0 0 0'; - if(mouse_angles_y < -60 && current_angles_y > 60) - delta = '0 360 0'; - if(mouse_angles_y > 60 && current_angles_y < -60) - delta = '0 -360 0'; - - if(cvar("camera_look_player")) - attenuation = cvar("camera_look_attenuation"); - else - attenuation = cvar("camera_speed_attenuation"); - - attenuation = 1 / max(1, attenuation); - current_angles += (mouse_angles - current_angles + delta) * attenuation; - - while (current_angles_x < -180) current_angles_x = current_angles_x + 360; - while (current_angles_x > 180) current_angles_x = current_angles_x - 360; - while (current_angles_y < -180) current_angles_y = current_angles_y + 360; - while (current_angles_y > 180) current_angles_y = current_angles_y - 360; - - // Camera position - tmp = '0 0 0'; - dimensions = 0; - - if( camera_direction_x ) - { - tmp_x = camera_direction_x * cos(current_angles_y * DEG2RAD); - tmp_y = camera_direction_x * sin(current_angles_y * DEG2RAD); - if( cvar("camera_forward_follows") && !cvar("camera_look_player") ) - tmp_z = camera_direction_x * -sin(current_angles_x * DEG2RAD); - ++dimensions; - } - - if( camera_direction_y ) - { - tmp_x += camera_direction_y * -sin(current_angles_y * DEG2RAD); - tmp_y += camera_direction_y * cos(current_angles_y * DEG2RAD) * cos(current_angles_z * DEG2RAD); - tmp_z += camera_direction_y * sin(current_angles_z * DEG2RAD); - ++dimensions; - } - - if( camera_direction_z ) - { - tmp_z += camera_direction_z * cos(current_angles_z * DEG2RAD); - ++dimensions; - } - - if(cvar("camera_free")) - speed = cvar("camera_speed_free"); - else - speed = cvar("camera_speed_chase"); - - if(dimensions) - { - speed = speed * sqrt(1 / dimensions); - camera_offset += tmp * speed; - } - - current_camera_offset += (camera_offset - current_camera_offset) * attenuation; - - // Camera modes - if( cvar("camera_free") ) - { - if ( camera_mode == CAMERA_CHASE ) - { - current_camera_offset = current_origin + current_camera_offset; - camera_offset = current_origin + camera_offset; - } - - camera_mode = CAMERA_FREE; - current_position = current_camera_offset; - } - else - { - if ( camera_mode == CAMERA_FREE ) - { - current_origin = view_origin; - camera_offset = camera_offset - current_origin; - current_camera_offset = current_camera_offset - current_origin; - } - - camera_mode = CAMERA_CHASE; - - if(cvar("camera_chase_smoothly")) - current_origin += (view_origin - current_origin) * attenuation; - else - current_origin = view_origin; - - current_position = current_origin + current_camera_offset; - } - - R_SetView(VF_ANGLES, current_angles); - R_SetView(VF_ORIGIN, current_position); -} +#define spider_rocket_icon "gfx/spiderbot/rocket_ico.tga" +#define spider_rocket_targ "gfx/spiderbot/target.tga" +#define SPIDER_CROSS "textures/spiderbot/cross.tga" +#define rkt_size 32 +#define rld_size_x 256 +#define rld_size_y 16 + +entity porto; +vector polyline[16]; +float trace_dphitcontents; +float trace_networkentity; +float Q3SURFACEFLAG_SLICK = 2; // low friction surface +float DPCONTENTS_SOLID = 1; // blocks player movement +float DPCONTENTS_BODY = 32; // blocks player movement +float DPCONTENTS_CORPSE = 64; // blocks player movement +float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement +void Porto_Draw() +{ + vector p, dir, ang, q, nextdir; + float idx, portal_number, portal1_idx; + + if(activeweapon != WEP_PORTO || spectatee_status || gametype == GAME_NEXBALL) + return; + if(intermission == 1) + return; + if(intermission == 2) + return; + if (getstati(STAT_HEALTH) <= 0) + return; + + dir = view_forward; + + if(angles_held_status) + { + makevectors(angles_held); + dir = v_forward; + } + + p = view_origin; + + polyline[0] = p; + idx = 1; + portal_number = 0; + nextdir = dir; + + for(;;) + { + dir = nextdir; + traceline(p, p + 65536 * dir, TRUE, porto); + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + return; + nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal + p = trace_endpos; + polyline[idx] = p; + ++idx; + if(idx >= 16) + return; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) + continue; + ++portal_number; + ang = vectoangles2(trace_plane_normal, dir); + ang_x = -ang_x; + makevectors(ang); + if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) + return; + if(portal_number == 1) + portal1_idx = idx; + if(portal_number >= 2) + break; + } + + while(idx >= 2) + { + p = polyline[idx-2]; + q = polyline[idx-1]; + if(idx == 2) + p = p - view_up * 16; + if(idx-1 >= portal1_idx) + { + Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL); + } + else + { + Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL); + } + --idx; + } +} + +/** + * Checks whether the server initiated a map restart (stat_game_starttime changed) + * + * TODO: Use a better solution where a common shared entitiy is used that contains + * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT + * and STAT_FRAGLIMIT to be auto-sent) + */ +void CheckForGamestartChange() { + float startTime; + startTime = getstatf(STAT_GAMESTARTTIME); + if (previous_game_starttime != startTime) { + if ((time + 5.0) < startTime) { + //if connecting to server while restart was active don't always play prepareforbattle + sound(self, CHAN_VOICE, "announcer/robotic/prepareforbattle.wav", VOL_BASEVOICE, ATTN_NONE); + } + if (time < startTime) { + restartAnnouncer = spawn(); + restartAnnouncer.think = restartAnnouncer_Think; + restartAnnouncer.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime + } + } + previous_game_starttime = startTime; +} + +void Porto_Init() +{ + porto = spawn(); + porto.classname = "porto"; + porto.draw = Porto_Draw; + porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; +} + +float drawtime; + +float tan(float x) +{ + return sin(x) / cos(x); +} +float atan2(float y, float x) +{ + vector v; + v = '1 0 0' * x + '0 1 0' * y; + v = vectoangles(v); + return v_y * 0.01745329251994329576; +} + +vector GetCurrentFov(float fov) +{ + float zoomsensitivity, zoomspeed, zoomfactor, zoomdir; + + zoomsensitivity = cvar("cl_zoomsensitivity"); + zoomfactor = cvar("cl_zoomfactor"); + if(zoomfactor < 1 || zoomfactor > 16) + zoomfactor = 2.5; + zoomspeed = cvar("cl_zoomspeed"); + if(zoomspeed >= 0) + if(zoomspeed < 0.5 || zoomspeed > 16) + zoomspeed = 3.5; + + zoomdir = button_zoom; + if(getstati(STAT_ACTIVEWEAPON) == WEP_NEX) // do NOT use switchweapon here + zoomdir += button_attack2; + if(spectatee_status > 0 || isdemo()) + { + if(spectatorbutton_zoom) + zoomdir = 0 + !zoomdir; + // do not even THINK about removing this 0 + // _I_ know what I am doing + // fteqcc does not + } + + if(zoomdir) + zoomin_effect = 0; + + if(zoomin_effect || camera_active) + { + current_viewzoom = min(1, current_viewzoom + drawframetime); + } + else + { + if(zoomspeed < 0) // instant zoom + { + if(zoomdir) + current_viewzoom = 1 / zoomfactor; + else + current_viewzoom = 1; + } + else + { + if(zoomdir) + current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor); + else + current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1); + } + } + + if(almost_equals(current_viewzoom, 1)) + current_zoomfraction = 0; + else if(almost_equals(current_viewzoom, 1/zoomfactor)) + current_zoomfraction = 1; + else + current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1); + + if(zoomsensitivity < 1) + setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity)); + else + setsensitivityscale(1); + + float frustumx, frustumy, fovx, fovy; + frustumy = tan(fov * 0.00872664625997164788) * 0.75 * current_viewzoom; + frustumx = frustumy * vid_width / vid_height / vid_pixelheight; + fovx = atan2(frustumx, 1) / 0.00872664625997164788; + fovy = atan2(frustumy, 1) / 0.00872664625997164788; + + return '1 0 0' * fovx + '0 1 0' * fovy; +} + +// this function must match W_SetupShot! +float zoomscript_caught; +entity trueaim; + +#define SHOTTYPE_HITTEAM 1 +#define SHOTTYPE_HITOBSTRUCTION 2 +#define SHOTTYPE_HITWORLD 3 +#define SHOTTYPE_HITENEMY 4 + +void TrueAim_Init() +{ + trueaim = spawn(); + trueaim.classname = "trueaim"; + trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; +} + +float EnemyHitCheck(vector start, vector mi, vector ma, vector end) +{ + float t; + tracebox(start, mi, ma, end, MOVE_NORMAL, trueaim); + if(trace_networkentity < 1) + return SHOTTYPE_HITWORLD; + if(trace_networkentity > maxclients) + return SHOTTYPE_HITWORLD; + t = GetPlayerColor(trace_networkentity - 1); + if(teamplay) + if(t == myteam) + return SHOTTYPE_HITTEAM; + if(t == COLOR_SPECTATOR) + return SHOTTYPE_HITWORLD; + return SHOTTYPE_HITENEMY; +} + +float TrueAimCheck() +{ + float nudge = 1; // added to traceline target and subtracted from result + vector vecs, trueaimpoint, w_shotorg; + vector mi, ma, dv; + float shottype; + + mi = ma = '0 0 0'; + + switch(activeweapon) + { + case WEP_TUBA: // no aim + case WEP_PORTO: // shoots from eye + case WEP_HOOK: // no trueaim + case WEP_GRENADE_LAUNCHER: // toss curve + return SHOTTYPE_HITWORLD; + case WEP_CAMPINGRIFLE: + if(zoomscript_caught) + return EnemyHitCheck(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE); + break; + case WEP_ROCKET_LAUNCHER: // projectile has a size! + mi = '-3 -3 -3'; + ma = '3 3 3'; + break; + case WEP_SEEKER: // projectile has a size! + mi = '-2 -2 -2'; + ma = '2 2 2'; + break; + case WEP_ELECTRO: // projectile has a size! + mi = '0 0 -3'; + ma = '0 0 -3'; + break; + } + + vecs = decompressShotOrigin(getstati(STAT_SHOTORG)); + + traceline(view_origin, view_origin + view_forward * MAX_SHOT_DISTANCE, MOVE_NOMONSTERS, trueaim); + trueaimpoint = trace_endpos; + + if(vecs_x > 0) + vecs_y = -vecs_y; + else + vecs = '0 0 0'; + + dv = view_right * vecs_y + view_up * vecs_z; + w_shotorg = view_origin + dv; + + // now move the vecs forward as much as requested if possible + tracebox(w_shotorg, mi, ma, w_shotorg + view_forward * (vecs_x + nudge), MOVE_NORMAL, trueaim); // FIXME this MOVE_NORMAL part will misbehave a little in csqc + w_shotorg = trace_endpos - view_forward * nudge; + + shottype = EnemyHitCheck(w_shotorg, mi, ma, trueaimpoint); + if(shottype != SHOTTYPE_HITWORLD) + return shottype; + + // now test whether we will actually hit the trueaimpoint... + tracebox(w_shotorg, mi, ma, trueaimpoint, MOVE_NOMONSTERS, trueaim); + + if(vlen(trace_endpos - trueaimpoint) > vlen(ma - mi) + 1) + // yes, this is an ugly hack... but it seems good enough to find out whether the test hits the same place as the initial trace + return SHOTTYPE_HITOBSTRUCTION; + + return SHOTTYPE_HITWORLD; +} + +void CSQC_common_hud(void); + +void CSQC_kh_hud(void); +void CSQC_ctf_hud(void); +void PostInit(void); +void CSQC_Demo_Camera(); +float Sbar_WouldDrawScoreboard (); +float view_set; +float camera_mode; +string NextFrameCommand; +void CSQC_spider_HUD(); +void CSQC_UpdateView(float w, float h) +{ + entity e; + float fov; + float f, i, j; + + dprint_load(); + WaypointSprite_Load(); + + myteam = GetPlayerColor(player_localentnum - 1); + + ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE); + + // Render the Scene + if(!intermission || !view_set) + { + view_origin = pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT); + view_angles = input_angles; + makevectors(view_angles); + view_forward = v_forward; + view_right = v_right; + view_up = v_up; + view_set = 1; + } + + vid_width = w; + vid_height = h; + +#ifdef BLURTEST + if(time > blurtest_time0 && time < blurtest_time1) + { + float r, t; + + t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0); + r = t * blurtest_radius; + f = 1 / pow(t, blurtest_power) - 1; + + cvar_set("r_glsl_postprocess", "1"); + cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0")); + } + else + { + cvar_set("r_glsl_postprocess", "0"); + cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0"); + } +#endif + + Fog_Force(); + + drawframetime = max(0.000001, time - drawtime); + drawtime = time; + + // watch for gametype changes here... + // in ParseStuffCMD the cmd isn't executed yet :/ + // might even be better to add the gametype to TE_CSQC_INIT...? + if(!postinit) + PostInit(); + + CheckForGamestartChange(); + maptimeAnnouncer(); + + fov = cvar("fov"); + if(button_zoom || fov <= 59.5) + { + if(!zoomscript_caught) + { + localcmd("+button4\n"); + zoomscript_caught = 1; + ignore_plus_zoom += 1; + } + } + else + { + if(zoomscript_caught) + { + localcmd("-button4\n"); + zoomscript_caught = 0; + ignore_minus_zoom += 1; + } + } + + sbar_alpha_fg = cvar("sbar_alpha_fg" ); + sbar_currentammo = cvar("sbar_showcurrentammo"); + sbar_hudselector = cvar("sbar_hudselector"); + sbar_hud_accuracy = cvar("sbar_hud_accuracy"); + ColorTranslateMode = cvar("cl_stripcolorcodes"); + activeweapon = getstati(STAT_SWITCHWEAPON); + f = cvar("teamplay"); + if(f != teamplay) + { + teamplay = f; + Sbar_InitScores(); + } + + if(last_weapon != activeweapon) { + weapontime = time; + last_weapon = activeweapon; + } + + // ALWAYS Clear Current Scene First + R_ClearScene(); + + // Assign Standard Viewflags + // Draw the World (and sky) + R_SetView(VF_DRAWWORLD, 1); + + // Set the console size vars + vid_conwidth = cvar("vid_conwidth"); + vid_conheight = cvar("vid_conheight"); + vid_pixelheight = cvar("vid_pixelheight"); + + R_SetView(VF_FOV, GetCurrentFov(fov)); + + // Camera for demo playback + if(camera_active) + { + if(cvar("camera_enable")) + CSQC_Demo_Camera(); + else + { + cvar_set("chase_active", ftos(chase_active_backup)); + cvar_set("cl_demo_mousegrab", "0"); + camera_active = FALSE; + } + } +#ifdef CAMERATEST + else if(cvar("camera_enable")) +#else + else if(cvar("camera_enable") && isdemo()) +#endif + { + // Enable required Darkplaces cvars + chase_active_backup = cvar("chase_active"); + cvar_set("chase_active", "2"); + cvar_set("cl_demo_mousegrab", "1"); + camera_active = TRUE; + camera_mode = FALSE; + } + + // Draw the Crosshair + float scoreboard_active; + scoreboard_active = Sbar_WouldDrawScoreboard(); + R_SetView(VF_DRAWCROSSHAIR, 0); //Make sure engine crosshairs are always hidden + + // Draw the Engine Status Bar (the default Quake HUD) + R_SetView(VF_DRAWENGINESBAR, 0); + + // fetch this one only once per frame + sbar_showbinds = cvar("sbar_showbinds"); + sbar_showbinds_limit = cvar("sbar_showbinds_limit"); + + // Update the mouse position + /* + mousepos_x = vid_conwidth; + mousepos_y = vid_conheight; + mousepos = mousepos*0.5 + getmousepos(); + */ + + R_AddEntities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); + + e = self; + for(self = world; (self = nextent(self)); ) + if(self.draw) + self.draw(); + self = e; + R_RenderScene(); + + // now switch to 2D drawing mode by calling a 2D drawing function + // then polygon drawing will draw as 2D stuff, and NOT get queued until the + // next R_RenderScene call + drawstring('0 0 0', "", '1 1 0', '1 1 1', 0, 0); + + // Draw the mouse cursor + // NOTE: drawpic must happen after R_RenderScene for some reason + //drawpic(getmousepos(), "gfx/cursor.tga", '11 14 0', '1 1 1', 1, 0); + //drawstring('50 50', ftos(game), '10 10 0', '1 1 1', 1, 0); + //self = edict_num(player_localnum); + //drawstring('0 0', vtos(pmove_org), '8 8 0', '1 1 1', 1, 0); + //drawstring('0 8', strcat("ORG: ", vtos(self.origin), " state: ", ftos(self.ctf_state), " HP: ", ftos(self.health)), '8 8 0', '1 1 1', 1, 0); + // as long as the ctf part isn't in, this is useless + if(menu_visible) + menu_show(); + + /*if(gametype == GAME_CTF) + { + ctf_view(); + } else */ + + // draw 2D entities + e = self; + for(self = world; (self = nextent(self)); ) + if(self.draw2d) + self.draw2d(); + self = e; + + // draw radar + if( + ons_showmap + || + ( + !scoreboard_active + && + cvar_string("cl_teamradar") != "0" + && + ( + cvar("cl_teamradar") == 2 + || + teamplay + ) + ) + ) + teamradar_view(); + + if (cvar("cl_showpressedkeys")) // draw pressed keys when spectating and playing + { + if(spectatee_status > 0 || cvar("cl_showpressedkeys") >= 2) + Sbar_DrawPressedKeys(); + } + + if (cvar("cl_showspeed")) + Sbar_ShowSpeed(); + + // draw sbar + if(cvar("r_letterbox") == 0) + Sbar_DrawCenterPrint(); // draw centerprint messages even if viewsize >= 120 + + float hud; + hud = getstati(STAT_HUD); + if(hud == HUD_SPIDEBOT) + { + vector sz; + CSQC_spider_HUD(); + sz = drawgetimagesize(SPIDER_CROSS); + sz_x *= cvar_or("cl_vehicle_spiderbot_cross_size",1); + sz_y *= cvar_or("cl_vehicle_spiderbot_cross_size",1); + drawpic('0.5 0 0' * (vid_conwidth - sz_x) + '0 0.5 0' * (vid_conheight - sz_y), SPIDER_CROSS, sz, '1 1 1', cvar_or("cl_vehicle_spiderbot_cross_alpha",0.6), DRAWFLAG_NORMAL); + } + else + { + if(cvar("r_letterbox") == 0) + if(cvar("viewsize") < 120) + CSQC_common_hud(); + + // crosshair goes VERY LAST + if(!scoreboard_active && !ons_showmap && !camera_active) { + // TrueAim check + float shottype; + + if(cvar("crosshair_hittest")) + shottype = TrueAimCheck(); + else + shottype = SHOTTYPE_HITWORLD; + + string wcross_style; + wcross_style = cvar_string("crosshair"); + + if (wcross_style != "0") { + vector wcross_color, wcross_size; + string wcross_wep, wcross_name; + float wcross_alpha, wcross_sizefloat; + + wcross_color_x = cvar("crosshair_color_red"); + wcross_color_y = cvar("crosshair_color_green"); + wcross_color_z = cvar("crosshair_color_blue"); + wcross_alpha = cvar("crosshair_color_alpha"); + wcross_sizefloat = cvar("crosshair_size"); + if (cvar("crosshair_per_weapon")) { + e = get_weaponinfo(activeweapon); + if (e && e.netname != "") + { + wcross_wep = e.netname; + wcross_style = cvar_string(strcat("crosshair_", wcross_wep)); + if(wcross_style == "") + wcross_style = e.netname; + + if(!cvar("crosshair_color_override")) + { + wcross_color_x = cvar(strcat("crosshair_", wcross_wep, "_color_red")); + wcross_color_y = cvar(strcat("crosshair_", wcross_wep, "_color_green")); + wcross_color_z = cvar(strcat("crosshair_", wcross_wep, "_color_blue")); + } + + wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_color_alpha")); + wcross_sizefloat *= cvar(strcat("crosshair_", wcross_wep, "_size")); + } + } + + wcross_name = strcat("gfx/crosshair", wcross_style); + + if(shottype == SHOTTYPE_HITENEMY) + wcross_sizefloat *= cvar("crosshair_hittest"); // is not queried if hittest is 0 + if(shottype == SHOTTYPE_HITTEAM) + wcross_sizefloat /= cvar("crosshair_hittest"); // is not queried if hittest is 0 + + wcross_size = drawgetimagesize(wcross_name); + wcross_size_x *= wcross_sizefloat; + wcross_size_y *= wcross_sizefloat; + + if(shottype == SHOTTYPE_HITENEMY || shottype == SHOTTYPE_HITWORLD) + { + drawpic('0.5 0 0' * (vid_conwidth - wcross_size_x) + '0 0.5 0' * (vid_conheight - wcross_size_y), wcross_name, wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL); + } + else + { + wcross_alpha *= 0.04 * 0.75; + for(i = -2; i <= 2; ++i) + for(j = -2; j <= 2; ++j) + drawpic('0.5 0 0' * (vid_conwidth - wcross_size_x + i) + '0 0.5 0' * (vid_conheight - wcross_size_y + j), wcross_name, wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL); + } + } + } + } + + if(NextFrameCommand) + { + localcmd("\n", NextFrameCommand, "\n"); + NextFrameCommand = string_null; + } +} + +void Sbar_Draw(); +void CSQC_spider_HUD() +{ + float rockets,reload,heat,hp,shield,i; + vector p,pp; + + rockets = getstati(STAT_SPIDERBOT_ROCKETS); + heat = min(getstatf(STAT_SPIDERBOT_HEAT),1); + reload = min(getstatf(STAT_SPIDERBOT_RELOAD),1); + hp = min(getstatf(STAT_SPIDERBOT_HEALTH),1); + shield = min(getstatf(STAT_SPIDERBOT_SHIELD),1); + + // Draw health bar + p = '0.5 0 0' * (vid_conwidth - (rkt_size * 8)); + p = p + '0 1 0' * vid_conheight - '0 32 0'; + //pp = ('0 1 0' * hp) + ('1 0 0' * (1-hp)); + drawfill(p, '256 0 0' * shield + '0 8 0' , '0.5 0.5 1', 0.75, DRAWFLAG_NORMAL); + p_y += 8; + drawfill(p, '256 0 0' * hp + '0 8 0' , '0 1 0', 0.75, DRAWFLAG_NORMAL); + p_x += 256 * hp; + drawfill(p, '256 0 0' * (1-hp) + '0 8 0' , '0 0 0', 0.75, DRAWFLAG_NORMAL); + + // Draw minigun heat indicator + p = '0.5 0 0' * (vid_conwidth - 256); + p = p + '0 1 0' * vid_conheight - '0 34 0'; + drawfill(p, '256 0 0' * (1-heat) + '0 2 0' ,'0 0 1', 0.5, DRAWFLAG_NORMAL); + p_x += 256 * (1-heat); + drawfill(p, '256 0 0' * heat + '0 2 0' , '1 0 0', 0.5, DRAWFLAG_NORMAL); + + // Draw rocket icons for loaded/empty tubes. + pp = '0.5 0 0' * (vid_conwidth - (rkt_size * 8)); + pp += '0 1 0' * vid_conheight - '0 64 0'; + for(i = 0; i < 8; ++i) + { + p = pp + '1 0 0' * (rkt_size * i); + if(rockets == 8) + { + if(floor(reload * 8) == i) + { + drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 0 0' + '0 1 0' * ((reload*8)-i), 0.75 , DRAWFLAG_NORMAL); + } + else if(i < reload * 8) + drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 1 0', 0.75 , DRAWFLAG_NORMAL); + else + drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0.5 0.5 0.5', 0.75, DRAWFLAG_NORMAL); + } + else + { + if(i < rockets) + drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 0 0', 0.25, DRAWFLAG_NORMAL); + else + drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 1 0' * reload, 0.75, DRAWFLAG_NORMAL); + } + } + + if (sb_showscores) + { + Sbar_DrawScoreboard(); + Sbar_DrawCenterPrint(); + } + +} +void CSQC_common_hud(void) +{ + + // Sbar_SortFrags(); done in Sbar_Draw + float hud; + hud = getstati(STAT_HUD); + + //hud = 10; + switch(hud) + { + case HUD_NORMAL: + Sbar_Draw(); + break; + + case HUD_SPIDEBOT: + CSQC_spider_HUD(); + break; + } +} + + +// following vectors must be global to allow seamless switching between camera modes +vector camera_offset, current_camera_offset, mouse_angles, current_angles, current_origin, current_position; +void CSQC_Demo_Camera() +{ + float speed, attenuation, dimensions; + vector tmp, delta; + + if( cvar("camera_reset") || !camera_mode ) + { + camera_offset = '0 0 0'; + current_angles = '0 0 0'; + camera_direction = '0 0 0'; + camera_offset_z += 30; + camera_offset_x += 30 * -cos(current_angles_y * DEG2RAD); + camera_offset_y += 30 * -sin(current_angles_y * DEG2RAD); + current_origin = view_origin; + current_camera_offset = camera_offset; + cvar_set("camera_reset", "0"); + camera_mode = CAMERA_CHASE; + } + + // Camera angles + if( camera_roll ) + mouse_angles_z += camera_roll * cvar("camera_speed_roll"); + + if(cvar("camera_look_player")) + { + local vector dir; + local float n; + + dir = normalize(view_origin - current_position); + n = mouse_angles_z; + mouse_angles = vectoangles(dir); + mouse_angles_x = mouse_angles_x * -1; + mouse_angles_z = n; + } + else + { + tmp = getmousepos() * 0.1; + if(vlen(tmp)>cvar("camera_mouse_treshold")) + { + mouse_angles_x += tmp_y * cos(mouse_angles_z * DEG2RAD) + (tmp_x * sin(mouse_angles_z * DEG2RAD)); + mouse_angles_y -= tmp_x * cos(mouse_angles_z * DEG2RAD) + (tmp_y * -sin(mouse_angles_z * DEG2RAD)); + } + } + + while (mouse_angles_x < -180) mouse_angles_x = mouse_angles_x + 360; + while (mouse_angles_x > 180) mouse_angles_x = mouse_angles_x - 360; + while (mouse_angles_y < -180) mouse_angles_y = mouse_angles_y + 360; + while (mouse_angles_y > 180) mouse_angles_y = mouse_angles_y - 360; + + // Fix difference when angles don't have the same sign + delta = '0 0 0'; + if(mouse_angles_y < -60 && current_angles_y > 60) + delta = '0 360 0'; + if(mouse_angles_y > 60 && current_angles_y < -60) + delta = '0 -360 0'; + + if(cvar("camera_look_player")) + attenuation = cvar("camera_look_attenuation"); + else + attenuation = cvar("camera_speed_attenuation"); + + attenuation = 1 / max(1, attenuation); + current_angles += (mouse_angles - current_angles + delta) * attenuation; + + while (current_angles_x < -180) current_angles_x = current_angles_x + 360; + while (current_angles_x > 180) current_angles_x = current_angles_x - 360; + while (current_angles_y < -180) current_angles_y = current_angles_y + 360; + while (current_angles_y > 180) current_angles_y = current_angles_y - 360; + + // Camera position + tmp = '0 0 0'; + dimensions = 0; + + if( camera_direction_x ) + { + tmp_x = camera_direction_x * cos(current_angles_y * DEG2RAD); + tmp_y = camera_direction_x * sin(current_angles_y * DEG2RAD); + if( cvar("camera_forward_follows") && !cvar("camera_look_player") ) + tmp_z = camera_direction_x * -sin(current_angles_x * DEG2RAD); + ++dimensions; + } + + if( camera_direction_y ) + { + tmp_x += camera_direction_y * -sin(current_angles_y * DEG2RAD); + tmp_y += camera_direction_y * cos(current_angles_y * DEG2RAD) * cos(current_angles_z * DEG2RAD); + tmp_z += camera_direction_y * sin(current_angles_z * DEG2RAD); + ++dimensions; + } + + if( camera_direction_z ) + { + tmp_z += camera_direction_z * cos(current_angles_z * DEG2RAD); + ++dimensions; + } + + if(cvar("camera_free")) + speed = cvar("camera_speed_free"); + else + speed = cvar("camera_speed_chase"); + + if(dimensions) + { + speed = speed * sqrt(1 / dimensions); + camera_offset += tmp * speed; + } + + current_camera_offset += (camera_offset - current_camera_offset) * attenuation; + + // Camera modes + if( cvar("camera_free") ) + { + if ( camera_mode == CAMERA_CHASE ) + { + current_camera_offset = current_origin + current_camera_offset; + camera_offset = current_origin + camera_offset; + } + + camera_mode = CAMERA_FREE; + current_position = current_camera_offset; + } + else + { + if ( camera_mode == CAMERA_FREE ) + { + current_origin = view_origin; + camera_offset = camera_offset - current_origin; + current_camera_offset = current_camera_offset - current_origin; + } + + camera_mode = CAMERA_CHASE; + + if(cvar("camera_chase_smoothly")) + current_origin += (view_origin - current_origin) * attenuation; + else + current_origin = view_origin; + + current_position = current_origin + current_camera_offset; + } + + R_SetView(VF_ANGLES, current_angles); + R_SetView(VF_ORIGIN, current_position); +} diff --git a/data/qcsrc/client/csqc_builtins.qc b/data/qcsrc/client/csqc_builtins.qc index 9a2b3bc4a..21c119117 100644 --- a/data/qcsrc/client/csqc_builtins.qc +++ b/data/qcsrc/client/csqc_builtins.qc @@ -1,287 +1,287 @@ -void (vector ang) makevectors = #1; -void(entity e, vector o) setorigin = #2; -void (entity e, string m) setmodel = #3; -void(entity e, vector min, vector max) setsize = #4; - -void () break = #6; -float () random = #7; -void (entity e, float chan, string samp, float vol, float atten) sound = #8; -vector (vector v) normalize = #9; -void (string e) error = #10; -void (string e) objerror = #11; -float (vector v) vlen = #12; -float (vector v) vectoyaw = #13; -entity () spawn = #14; -void (entity e) remove = #15; - -void (vector v1, vector v2, float nomonsters, entity forent) traceline = #16; - -entity (entity start, .string fld, string match) find = #18; -string (string s) precache_sound = #19; -string (string s) precache_model = #20; - -//void (string s) dprint = #25; -string (float f) ftos = #26; -string (vector v) vtos = #27; -void () coredump = #28; -void () traceon = #29; -void () traceoff = #30; -void (entity e) eprint = #31; - -float (float v) rint = #36; -float (float v) floor = #37; -float (float v) ceil = #38; - -float (vector v) pointcontents = #41; -float (float f) fabs = #43; - -float (string s) cvar = #45; -void (string s, ...) localcmd = #46; -entity (entity e) nextent = #47; - -vector (vector v) vectoangles = #51; -vector (vector v, vector w) vectoangles2 = #51; - -void (string var, string val) cvar_set = #72; - - -float() ReadByte = #360; -float() ReadChar = #361; -float() ReadShort = #362; -float() ReadLong = #363; -float() ReadCoord = #364; -float() ReadAngle = #365; -string() ReadString = #366; //warning: this returns a temporary! - -float(string s) stof = #81; - - -void (vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90; -float (string name, string value, ...) registercvar = #93; - -entity (entity start, .entity fld, entity match) findentity = #98; -entity (entity start, .float fld, float match) findfloat = #98; - -float (string s) checkextension = #99; - -float (string filename, float mode) fopen = #110; -void (float fhandle) fclose = #111; -string (float fhandle) fgets = #112; -void (float fhandle, string s) fputs = #113; -float (string s) strlen = #114; -string (...) strcat = #115; -string (string s, float start, float length) substring = #116; -vector (string s) stov = #117; -string (string s) strzone = #118; -void (string s) strunzone = #119; - -void () R_ClearScene = #300; -void (float mask) R_AddEntities = #301; -void (entity e) R_AddEntity = #302; -float (float property, ...) R_SetView = #303; -void () R_RenderScene = #304; -void (vector org, float radius, vector rgb) R_AddDynamicLight = #305; -void () R_CalcRefDef = #306; - -vector (vector v) cs_unproject = #310; -vector (vector v) cs_project = #311; - -void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags) = #315; -float iscachedpic(string name) = #316; -string precache_pic(string name, ...) = #317; -vector drawgetimagesize(string pic) = #318; -void freepic(string name) = #319; -float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #320; -float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #321; -float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #322; -float drawfill(vector position, vector size, vector rgb, float alpha, float flag) = #323; -void drawsetcliparea(float x, float y, float width, float height) = #324; -void drawresetcliparea(void) = #325; -float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) = #326; -float stringwidth(string text, float handleColors) = #327; -float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328; - - -float (float statnum) getstatf = #330; -float (float statnum) getstati = #331; -string (float statnum) getstats = #332; - -void (entity e, float i) setmodelindex = #333; -string (float i) modelnameforindex = #334; - -float(string efname) particleeffectnum = #335; -void(entity ent, float effectnum, vector start, vector end, ...) trailparticles = #336; -void (float efnum, vector org, vector vel, float countmultiplier, ...) pointparticles = #337; - -void (string s, ...) cprint = #338; -void (string s, ...) print = #339; - -void (float scale) setsensitivityscale = #346; - - -void (float framenum) RetrieveMovementFrame = #345; -void () DefaultPlayerPhysics = #347; - -string (float playernum, string key) getplayerkey = #348; -void (string cmdname) registercmd = #352; -vector () getmousepos = #344; - -string (string s) uncolorstring = #170; - -void (vector org, vector forward, vector right, vector up) setlistener = #351; - -float (vector start, vector end, float ignore, float csqcents) selecttraceline = #355; -float () isdemo = #349; -float () isserver = #350; - -void (float f) setwantsmousemove = #343; -string (float key) getkeybind = #342; -//string (float f) chr = #78; -string (float f) chr = #78; -float(string str, float ofs) str2chr = #222; -string(float c, ...) chr2str = #223; - -vector (vector org) getlight = #92; - -entity (.string fld, string match) findchain = #402; -entity (.float fld, float match) findchainflags = #450; -entity (.entity fld, entity match) findchainentity = #403; -entity (.float fld, float match) findchainfloat = #403; -entity (entity start, .float fld, float match) findflags = #449; - -float (string pattern, float caseinsensitive, float quiet) search_begin = #444; -void (float handle) search_end = #445; -float (float handle) search_getsize = #446; -string (float handle, float num) search_getfilename = #447; - - -float (entity e, float s) getsurfacenumpoints = #434; -vector (entity e, float s, float n) getsurfacepoint = #435; -vector (entity e, float s) getsurfacenormal = #436; -string (entity e, float s) getsurfacetexture = #437; -float (entity e, vector p) getsurfacenearpoint = #438; -vector (entity e, float s, vector p) getsurfaceclippedpoint = #439; - -float (float a, float b) min = #94; -float (float a, float b, float c) min3 = #94; -float (float a, float b, float c, float d) min4 = #94; -float (float a, float b, float c, float d, float e) min5 = #94; -float (float a, float b, float c, float d, float e, float f) min6 = #94; -float (float a, float b, float c, float d, float e, float f, float g) min7 = #94; -float (float a, float b, float c, float d, float e, float f, float g, float h) min8 = #94; -float (float a, float b) max = #95; -float (float a, float b, float c) max3 = #95; -float (float a, float b, float c, float d) max4 = #95; -float (float a, float b, float c, float d, float e) max5 = #95; -float (float a, float b, float c, float d, float e, float f) max6 = #95; -float (float a, float b, float c, float d, float e, float f, float g) max7 = #95; -float (float a, float b, float c, float d, float e, float f, float g, float h) max8 = #95; -float (float minimum, float val, float maximum) bound = #96; - -vector () randomvec = #91; - -float (float val) sin = #60; -float (float val) cos = #61; -float (float val) sqrt = #62; -float (float a, float b) pow = #97; - -void (vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; - -void (vector org, vector velocity, float howmany) te_blood = #405; -void (vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; -void (vector org, float radius, float lifetime, vector color) te_customflash = #417; -void(vector org, vector color) te_explosionrgb = #407; -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; -void(vector org) te_plasmaburn = #433; -void(vector org) te_gunshotquad = #412; -void(vector org) te_spikequad = #413; -void(vector org) te_superspikequad = #414; -void(vector org) te_explosionquad = #415; -void(vector org) te_smallflash = #416; -void(vector org, vector vel, float howmany) te_spark = #411; - -void(vector org) te_gunshot = #418; -void(vector org) te_spike = #419; -void(vector org) te_superspike = #420; -void(vector org) te_explosion = #421; -void(vector org) te_tarexplosion = #422; -void(vector org) te_wizspike = #423; -void(vector org) te_knightspike = #424; -void(vector org) te_lavasplash = #425; -void(vector org) te_teleport = #426; -void(vector org, float color, float colorlength) te_explosion2 = #427; -void(entity own, vector start, vector end) te_lightning1 = #428; -void(entity own, vector start, vector end) te_lightning2 = #429; -void(entity own, vector start, vector end) te_lightning3 = #430; -void(entity own, vector start, vector end) te_beam = #431; - -float (entity ent, string tagname) gettagindex = #451; -vector (entity ent, float tagindex) gettaginfo = #452; - -float (string s) tokenize = #441; -string (float argnum) argv = #442; - -string (string s) cvar_string = #448; - -float () buf_create = #460; -void (float bufhandle) buf_del = #461; -float (float bufhandle) buf_getsize = #462; -void (float bufhandle_from, float bufhandle_to) buf_copy = #463; -void (float bufhandle, float sortpower, float backward) buf_sort = #464; -string (float bufhandle, string glue) buf_implode = #465; -string (float bufhandle, float string_index) bufstr_get = #466; -void (float bufhandle, float string_index, string str) bufstr_set = #467; -float (float bufhandle, string str, float order) bufstr_add = #468; -void (float bufhandle, float string_index) bufstr_free = #469; - -float () onground = #355; - -void(string texturename, ...) R_BeginPolygon = #306; -void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; -void() R_EndPolygon = #308; - -float(string s, float num) charindex = #356; - -// Darkplaces Additional Functions -string(string s) strdecolorize = #477; -string(string s) strtolower = #480; // returns the passed in string in pure lowercase form -string(string s) strtoupper = #481; // returns the passed in string in pure uppercase form -float(string s) strlennocol = #476; - -void(vector origin, string sample, float volume, float attenuation) pointsound = #483; - -// added by blub - -string(string key) serverkey = #354; -float(string s1, string s2) strcasecmp = #229; -float(string s1, string s2, float len) strncasecmp = #230; -float(string str, string sub, float startoffs) strstrofs = #221; -//float(string str, string sub) strstrofs = #221; -entity(float num) edict_num = #459; -string(void) ReadPicture = #501; -string(string filename) whichpack = #503; -float(entity ent) num_for_edict = #512; -float(string s, string separator1, ...) tokenizebyseparator = #479; -string(string in) uri_unescape = #511; -float(float caseinsensitive, string s, ...) crc16 = #494; -string(string info, string key) infoget = #227; -string(string info, string key, string value, ...) infoadd = #226; -string(string in) uri_escape = #510; - -string keynumtostring(float keynum) = #520; -string findkeysforcommand(string command) = #521; - -string(float ccase, float calpha, float cnum, string s, ...) strconv = #224; -float(entity ent) wasfreed = #353; - -entity(vector org, float rad) findradius = #22; - -string(float uselocaltime, string format, ...) strftime = #478; -float(float timer) gettime = #519; -#define GETTIME_CDTRACK 4 - -float(string s) tokenize_console = #514; -float(float i) argv_start_index = #515; -float(float i) argv_end_index = #516; +void (vector ang) makevectors = #1; +void(entity e, vector o) setorigin = #2; +void (entity e, string m) setmodel = #3; +void(entity e, vector min, vector max) setsize = #4; + +void () break = #6; +float () random = #7; +void (entity e, float chan, string samp, float vol, float atten) sound = #8; +vector (vector v) normalize = #9; +void (string e) error = #10; +void (string e) objerror = #11; +float (vector v) vlen = #12; +float (vector v) vectoyaw = #13; +entity () spawn = #14; +void (entity e) remove = #15; + +void (vector v1, vector v2, float nomonsters, entity forent) traceline = #16; + +entity (entity start, .string fld, string match) find = #18; +string (string s) precache_sound = #19; +string (string s) precache_model = #20; + +//void (string s) dprint = #25; +string (float f) ftos = #26; +string (vector v) vtos = #27; +void () coredump = #28; +void () traceon = #29; +void () traceoff = #30; +void (entity e) eprint = #31; + +float (float v) rint = #36; +float (float v) floor = #37; +float (float v) ceil = #38; + +float (vector v) pointcontents = #41; +float (float f) fabs = #43; + +float (string s) cvar = #45; +void (string s, ...) localcmd = #46; +entity (entity e) nextent = #47; + +vector (vector v) vectoangles = #51; +vector (vector v, vector w) vectoangles2 = #51; + +void (string var, string val) cvar_set = #72; + + +float() ReadByte = #360; +float() ReadChar = #361; +float() ReadShort = #362; +float() ReadLong = #363; +float() ReadCoord = #364; +float() ReadAngle = #365; +string() ReadString = #366; //warning: this returns a temporary! + +float(string s) stof = #81; + + +void (vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90; +float (string name, string value, ...) registercvar = #93; + +entity (entity start, .entity fld, entity match) findentity = #98; +entity (entity start, .float fld, float match) findfloat = #98; + +float (string s) checkextension = #99; + +float (string filename, float mode) fopen = #110; +void (float fhandle) fclose = #111; +string (float fhandle) fgets = #112; +void (float fhandle, string s) fputs = #113; +float (string s) strlen = #114; +string (...) strcat = #115; +string (string s, float start, float length) substring = #116; +vector (string s) stov = #117; +string (string s) strzone = #118; +void (string s) strunzone = #119; + +void () R_ClearScene = #300; +void (float mask) R_AddEntities = #301; +void (entity e) R_AddEntity = #302; +float (float property, ...) R_SetView = #303; +void () R_RenderScene = #304; +void (vector org, float radius, vector rgb) R_AddDynamicLight = #305; +void () R_CalcRefDef = #306; + +vector (vector v) cs_unproject = #310; +vector (vector v) cs_project = #311; + +void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags) = #315; +float iscachedpic(string name) = #316; +string precache_pic(string name, ...) = #317; +vector drawgetimagesize(string pic) = #318; +void freepic(string name) = #319; +float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #320; +float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #321; +float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #322; +float drawfill(vector position, vector size, vector rgb, float alpha, float flag) = #323; +void drawsetcliparea(float x, float y, float width, float height) = #324; +void drawresetcliparea(void) = #325; +float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) = #326; +float stringwidth(string text, float handleColors) = #327; +float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328; + + +float (float statnum) getstatf = #330; +float (float statnum) getstati = #331; +string (float statnum) getstats = #332; + +void (entity e, float i) setmodelindex = #333; +string (float i) modelnameforindex = #334; + +float(string efname) particleeffectnum = #335; +void(entity ent, float effectnum, vector start, vector end, ...) trailparticles = #336; +void (float efnum, vector org, vector vel, float countmultiplier, ...) pointparticles = #337; + +void (string s, ...) cprint = #338; +void (string s, ...) print = #339; + +void (float scale) setsensitivityscale = #346; + + +void (float framenum) RetrieveMovementFrame = #345; +void () DefaultPlayerPhysics = #347; + +string (float playernum, string key) getplayerkey = #348; +void (string cmdname) registercmd = #352; +vector () getmousepos = #344; + +string (string s) uncolorstring = #170; + +void (vector org, vector forward, vector right, vector up) setlistener = #351; + +float (vector start, vector end, float ignore, float csqcents) selecttraceline = #355; +float () isdemo = #349; +float () isserver = #350; + +void (float f) setwantsmousemove = #343; +string (float key) getkeybind = #342; +//string (float f) chr = #78; +string (float f) chr = #78; +float(string str, float ofs) str2chr = #222; +string(float c, ...) chr2str = #223; + +vector (vector org) getlight = #92; + +entity (.string fld, string match) findchain = #402; +entity (.float fld, float match) findchainflags = #450; +entity (.entity fld, entity match) findchainentity = #403; +entity (.float fld, float match) findchainfloat = #403; +entity (entity start, .float fld, float match) findflags = #449; + +float (string pattern, float caseinsensitive, float quiet) search_begin = #444; +void (float handle) search_end = #445; +float (float handle) search_getsize = #446; +string (float handle, float num) search_getfilename = #447; + + +float (entity e, float s) getsurfacenumpoints = #434; +vector (entity e, float s, float n) getsurfacepoint = #435; +vector (entity e, float s) getsurfacenormal = #436; +string (entity e, float s) getsurfacetexture = #437; +float (entity e, vector p) getsurfacenearpoint = #438; +vector (entity e, float s, vector p) getsurfaceclippedpoint = #439; + +float (float a, float b) min = #94; +float (float a, float b, float c) min3 = #94; +float (float a, float b, float c, float d) min4 = #94; +float (float a, float b, float c, float d, float e) min5 = #94; +float (float a, float b, float c, float d, float e, float f) min6 = #94; +float (float a, float b, float c, float d, float e, float f, float g) min7 = #94; +float (float a, float b, float c, float d, float e, float f, float g, float h) min8 = #94; +float (float a, float b) max = #95; +float (float a, float b, float c) max3 = #95; +float (float a, float b, float c, float d) max4 = #95; +float (float a, float b, float c, float d, float e) max5 = #95; +float (float a, float b, float c, float d, float e, float f) max6 = #95; +float (float a, float b, float c, float d, float e, float f, float g) max7 = #95; +float (float a, float b, float c, float d, float e, float f, float g, float h) max8 = #95; +float (float minimum, float val, float maximum) bound = #96; + +vector () randomvec = #91; + +float (float val) sin = #60; +float (float val) cos = #61; +float (float val) sqrt = #62; +float (float a, float b) pow = #97; + +void (vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; + +void (vector org, vector velocity, float howmany) te_blood = #405; +void (vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; +void (vector org, float radius, float lifetime, vector color) te_customflash = #417; +void(vector org, vector color) te_explosionrgb = #407; +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; +void(vector org) te_plasmaburn = #433; +void(vector org) te_gunshotquad = #412; +void(vector org) te_spikequad = #413; +void(vector org) te_superspikequad = #414; +void(vector org) te_explosionquad = #415; +void(vector org) te_smallflash = #416; +void(vector org, vector vel, float howmany) te_spark = #411; + +void(vector org) te_gunshot = #418; +void(vector org) te_spike = #419; +void(vector org) te_superspike = #420; +void(vector org) te_explosion = #421; +void(vector org) te_tarexplosion = #422; +void(vector org) te_wizspike = #423; +void(vector org) te_knightspike = #424; +void(vector org) te_lavasplash = #425; +void(vector org) te_teleport = #426; +void(vector org, float color, float colorlength) te_explosion2 = #427; +void(entity own, vector start, vector end) te_lightning1 = #428; +void(entity own, vector start, vector end) te_lightning2 = #429; +void(entity own, vector start, vector end) te_lightning3 = #430; +void(entity own, vector start, vector end) te_beam = #431; + +float (entity ent, string tagname) gettagindex = #451; +vector (entity ent, float tagindex) gettaginfo = #452; + +float (string s) tokenize = #441; +string (float argnum) argv = #442; + +string (string s) cvar_string = #448; + +float () buf_create = #460; +void (float bufhandle) buf_del = #461; +float (float bufhandle) buf_getsize = #462; +void (float bufhandle_from, float bufhandle_to) buf_copy = #463; +void (float bufhandle, float sortpower, float backward) buf_sort = #464; +string (float bufhandle, string glue) buf_implode = #465; +string (float bufhandle, float string_index) bufstr_get = #466; +void (float bufhandle, float string_index, string str) bufstr_set = #467; +float (float bufhandle, string str, float order) bufstr_add = #468; +void (float bufhandle, float string_index) bufstr_free = #469; + +float () onground = #355; + +void(string texturename, ...) R_BeginPolygon = #306; +void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; +void() R_EndPolygon = #308; + +float(string s, float num) charindex = #356; + +// Darkplaces Additional Functions +string(string s) strdecolorize = #477; +string(string s) strtolower = #480; // returns the passed in string in pure lowercase form +string(string s) strtoupper = #481; // returns the passed in string in pure uppercase form +float(string s) strlennocol = #476; + +void(vector origin, string sample, float volume, float attenuation) pointsound = #483; + +// added by blub + +string(string key) serverkey = #354; +float(string s1, string s2) strcasecmp = #229; +float(string s1, string s2, float len) strncasecmp = #230; +float(string str, string sub, float startoffs) strstrofs = #221; +//float(string str, string sub) strstrofs = #221; +entity(float num) edict_num = #459; +string(void) ReadPicture = #501; +string(string filename) whichpack = #503; +float(entity ent) num_for_edict = #512; +float(string s, string separator1, ...) tokenizebyseparator = #479; +string(string in) uri_unescape = #511; +float(float caseinsensitive, string s, ...) crc16 = #494; +string(string info, string key) infoget = #227; +string(string info, string key, string value, ...) infoadd = #226; +string(string in) uri_escape = #510; + +string keynumtostring(float keynum) = #520; +string findkeysforcommand(string command) = #521; + +string(float ccase, float calpha, float cnum, string s, ...) strconv = #224; +float(entity ent) wasfreed = #353; + +entity(vector org, float rad) findradius = #22; + +string(float uselocaltime, string format, ...) strftime = #478; +float(float timer) gettime = #519; +#define GETTIME_CDTRACK 4 + +float(string s) tokenize_console = #514; +float(float i) argv_start_index = #515; +float(float i) argv_end_index = #516; diff --git a/data/qcsrc/client/csqc_constants.qc b/data/qcsrc/client/csqc_constants.qc index 93a94ac85..c68b0f5d9 100644 --- a/data/qcsrc/client/csqc_constants.qc +++ b/data/qcsrc/client/csqc_constants.qc @@ -1,192 +1,192 @@ -const entity NULL = world; - -// Mask Constants (set .drawmask on entities; use R_AddEntities to add all entities based on mask) -const float MASK_ENGINE = 1; -const float MASK_ENGINEVIEWMODELS = 2; -const float MASK_NORMAL = 4; - -// Renderflag Constants (used for CSQC entities) -const float RF_VIEWMODEL = 1; -const float RF_EXTERNALMODEL = 2; -const float RF_DEPTHHACK = 4; -const float RF_ADDITIVE = 8; -const float RF_USEAXIS = 16; - -// Viewflag Constants (use with R_SetView) -const float VF_MIN = 1; //(vector) -const float VF_MIN_X = 2; //(float) -const float VF_MIN_Y = 3; //(float) -const float VF_SIZE = 4; //(vector) (viewport size) -const float VF_SIZE_Y = 5; //(float) -const float VF_SIZE_X = 6; //(float) -const float VF_VIEWPORT = 7; //(vector, vector) -const float VF_FOV = 8; //(vector) -const float VF_FOVX = 9; //(float) -const float VF_FOVY = 10; //(float) -const float VF_ORIGIN = 11; //(vector) -const float VF_ORIGIN_X = 12; //(float) -const float VF_ORIGIN_Y = 13; //(float) -const float VF_ORIGIN_Z = 14; //(float) -const float VF_ANGLES = 15; //(vector) -const float VF_ANGLES_X = 16; //(float) -const float VF_ANGLES_Y = 17; //(float) -const float VF_ANGLES_Z = 18; //(float) -const float VF_DRAWWORLD = 19; //(float) -const float VF_DRAWENGINESBAR = 20; //(float) -const float VF_DRAWCROSSHAIR = 21; //(float) -const float VF_PERSPECTIVE = 200; //(float) - -const float VF_CL_VIEWANGLES = 33; //(vector) -const float VF_CL_VIEWANGLES_X = 34; //(float) -const float VF_CL_VIEWANGLES_Y = 35; //(float) -const float VF_CL_VIEWANGLES_Z = 36; //(float) - -// Server Autosent Stat Constants -const float STAT_HEALTH = 0; -const float STAT_WEAPONMODEL = 2; -const float STAT_AMMO = 3; -const float STAT_ARMOR = 4; -const float STAT_WEAPONFRAME = 5; -const float STAT_SHELLS = 6; -const float STAT_NAILS = 7; -const float STAT_ROCKETS = 8; -const float STAT_CELLS = 9; -const float STAT_ACTIVEWEAPON = 10; -const float STAT_TOTALSECRETS = 11; -const float STAT_TOTALMONSTERS = 12; -const float STAT_SECRETS = 13; -const float STAT_MONSTERS = 14; -const float STAT_ITEMS = 15; -const float STAT_VIEWHEIGHT = 16; -const float STAT_MOVEVARS_TICRATE = 240; -const float STAT_MOVEVARS_TIMESCALE = 241; -const float STAT_FRAGLIMIT = 235; -const float STAT_TIMELIMIT = 236; -const float STAT_MOVEVARS_GRAVITY = 242; - -// Sound Constants -//const float CHAN_AUTO = 0; -//const float CHAN_WEAPON = 1; -//const float CHAN_VOICE = 2; -//const float CHAN_ITEM = 3; -//const float CHAN_BODY = 4; - -//const float ATTN_NONE = 0; -//const float ATTN_NORM = 1; -//const float ATTN_IDLE = 2; -//const float ATTN_STATIC = 3; - -// Quake-style Point Contents -const float CONTENT_EMPTY = -1; -const float CONTENT_SOLID = -2; -const float CONTENT_WATER = -3; -const float CONTENT_SLIME = -4; -const float CONTENT_LAVA = -5; -const float CONTENT_SKY = -6; - -// Boolean Constants -const float true = 1; -const float false = 0; -const float TRUE = 1; -const float FALSE = 0; - -// Vector / Hull Constants -const vector VEC_1 = '1 1 1'; -const vector VEC_0 = '0 0 0'; -const vector VEC_M1 = '-1 -1 -1'; - -const vector VEC_HULL_MIN = '-16 -16 -24'; -const vector VEC_HULL_MAX = '16 16 32'; - -// Effect Constants -const float EF_NODRAW = 16; -const float EF_ADDITIVE = 32; -const float EF_BLUE = 64; -const float EF_RED = 128; -const float EF_FULLBRIGHT = 512; -const float EF_FLAME = 1024; -const float EF_STARDUST = 2048; -const float EF_NOSHADOW = 4096; -const float EF_NODEPTHTEST = 8192; - -// Quake Player Flag Constants -const float PFL_ONGROUND = 1; -const float PFL_CROUCH = 2; -const float PFL_DEAD = 4; -const float PFL_GIBBED = 8; - -// Quake Temporary Entity Constants -const float TE_SPIKE = 0; -const float TE_SUPERSPIKE = 1; -const float TE_GUNSHOT = 2; -const float TE_EXPLOSION = 3; -const float TE_TAREXPLOSION = 4; -const float TE_LIGHTNING1 = 5; -const float TE_LIGHTNING2 = 6; -const float TE_WIZSPIKE = 7; -const float TE_KNIGHTSPIKE = 8; -const float TE_LIGHTNING3 = 9; -const float TE_LAVASPLASH = 10; -const float TE_TELEPORT = 11; -const float TE_EXPLOSION2 = 12; -// Darkplaces Additions -const float TE_EXPLOSIONRGB = 53; -const float TE_GUNSHOTQUAD = 57; -const float TE_EXPLOSIONQUAD = 70; - -// Math Constants -const float M_PI = 3.14159265358979323846; -const float EXTRA_LOW = -99999999; -const float EXTRA_HIGH = 99999999; - -// Frik File Constants -const float FILE_READ = 0; -const float FILE_APPEND = 1; -const float FILE_WRITE = 2; - -// Button values used by input_buttons -const float BUTTON_ATTACK = 1; -const float BUTTON_JUMP = 2; -const float BUTTON_3 = 4; -const float BUTTON_4 = 8; -const float BUTTON_5 = 16; -const float BUTTON_6 = 32; -const float BUTTON7 = 64; -const float BUTTON8 = 128; -const float BUTTON_USE = 256; -const float BUTTON_CHAT = 512; -const float BUTTON_PRYDONCURSOR = 1024; -const float BUTTON_9 = 2048; -const float BUTTON_10 = 4096; -const float BUTTON_11 = 8192; -const float BUTTON_12 = 16384; -const float BUTTON_13 = 32768; -const float BUTTON_14 = 65536; -const float BUTTON_15 = 131072; -const float BUTTON_16 = 262144; - - -const float DRAWFLAG_NORMAL = 0; -const float DRAWFLAG_ADDITIVE = 1; -const float DRAWFLAG_MODULATE = 2; -const float DRAWFLAG_2XMODULATE = 3; -const float DRAWFLAG_SCREEN = 4; -const float DRAWFLAG_MIPMAP = 0x100; // only for R_BeginPolygon - -#define SOLID_NOT 0 // no interaction with other objects -#define SOLID_TRIGGER 1 // touch on edge, but not blocking -#define SOLID_BBOX 2 // touch on edge, block -#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground -#define SOLID_BSP 4 // bsp clip, touch on edge, block -#define SOLID_CORPSE 5 // same as SOLID_BBOX, except it behaves as SOLID_NOT against SOLID_SLIDEBOX objects (players/monsters) - -float MOVE_NORMAL = 0; // same as FALSE -float MOVE_NOMONSTERS = 1; // same as TRUE -float MOVE_MISSILE = 2; // save as movement with .movetype == MOVETYPE_FLYMISSILE -float MOVE_HITMODEL = 4; -float MOVE_WORLDONLY = 3; - -float CAMERA_FREE = 1; -float CAMERA_CHASE = 2; - -float EF_NOMODELFLAGS = 8388608; +const entity NULL = world; + +// Mask Constants (set .drawmask on entities; use R_AddEntities to add all entities based on mask) +const float MASK_ENGINE = 1; +const float MASK_ENGINEVIEWMODELS = 2; +const float MASK_NORMAL = 4; + +// Renderflag Constants (used for CSQC entities) +const float RF_VIEWMODEL = 1; +const float RF_EXTERNALMODEL = 2; +const float RF_DEPTHHACK = 4; +const float RF_ADDITIVE = 8; +const float RF_USEAXIS = 16; + +// Viewflag Constants (use with R_SetView) +const float VF_MIN = 1; //(vector) +const float VF_MIN_X = 2; //(float) +const float VF_MIN_Y = 3; //(float) +const float VF_SIZE = 4; //(vector) (viewport size) +const float VF_SIZE_Y = 5; //(float) +const float VF_SIZE_X = 6; //(float) +const float VF_VIEWPORT = 7; //(vector, vector) +const float VF_FOV = 8; //(vector) +const float VF_FOVX = 9; //(float) +const float VF_FOVY = 10; //(float) +const float VF_ORIGIN = 11; //(vector) +const float VF_ORIGIN_X = 12; //(float) +const float VF_ORIGIN_Y = 13; //(float) +const float VF_ORIGIN_Z = 14; //(float) +const float VF_ANGLES = 15; //(vector) +const float VF_ANGLES_X = 16; //(float) +const float VF_ANGLES_Y = 17; //(float) +const float VF_ANGLES_Z = 18; //(float) +const float VF_DRAWWORLD = 19; //(float) +const float VF_DRAWENGINESBAR = 20; //(float) +const float VF_DRAWCROSSHAIR = 21; //(float) +const float VF_PERSPECTIVE = 200; //(float) + +const float VF_CL_VIEWANGLES = 33; //(vector) +const float VF_CL_VIEWANGLES_X = 34; //(float) +const float VF_CL_VIEWANGLES_Y = 35; //(float) +const float VF_CL_VIEWANGLES_Z = 36; //(float) + +// Server Autosent Stat Constants +const float STAT_HEALTH = 0; +const float STAT_WEAPONMODEL = 2; +const float STAT_AMMO = 3; +const float STAT_ARMOR = 4; +const float STAT_WEAPONFRAME = 5; +const float STAT_SHELLS = 6; +const float STAT_NAILS = 7; +const float STAT_ROCKETS = 8; +const float STAT_CELLS = 9; +const float STAT_ACTIVEWEAPON = 10; +const float STAT_TOTALSECRETS = 11; +const float STAT_TOTALMONSTERS = 12; +const float STAT_SECRETS = 13; +const float STAT_MONSTERS = 14; +const float STAT_ITEMS = 15; +const float STAT_VIEWHEIGHT = 16; +const float STAT_MOVEVARS_TICRATE = 240; +const float STAT_MOVEVARS_TIMESCALE = 241; +const float STAT_FRAGLIMIT = 235; +const float STAT_TIMELIMIT = 236; +const float STAT_MOVEVARS_GRAVITY = 242; + +// Sound Constants +//const float CHAN_AUTO = 0; +//const float CHAN_WEAPON = 1; +//const float CHAN_VOICE = 2; +//const float CHAN_ITEM = 3; +//const float CHAN_BODY = 4; + +//const float ATTN_NONE = 0; +//const float ATTN_NORM = 1; +//const float ATTN_IDLE = 2; +//const float ATTN_STATIC = 3; + +// Quake-style Point Contents +const float CONTENT_EMPTY = -1; +const float CONTENT_SOLID = -2; +const float CONTENT_WATER = -3; +const float CONTENT_SLIME = -4; +const float CONTENT_LAVA = -5; +const float CONTENT_SKY = -6; + +// Boolean Constants +const float true = 1; +const float false = 0; +const float TRUE = 1; +const float FALSE = 0; + +// Vector / Hull Constants +const vector VEC_1 = '1 1 1'; +const vector VEC_0 = '0 0 0'; +const vector VEC_M1 = '-1 -1 -1'; + +const vector VEC_HULL_MIN = '-16 -16 -24'; +const vector VEC_HULL_MAX = '16 16 32'; + +// Effect Constants +const float EF_NODRAW = 16; +const float EF_ADDITIVE = 32; +const float EF_BLUE = 64; +const float EF_RED = 128; +const float EF_FULLBRIGHT = 512; +const float EF_FLAME = 1024; +const float EF_STARDUST = 2048; +const float EF_NOSHADOW = 4096; +const float EF_NODEPTHTEST = 8192; + +// Quake Player Flag Constants +const float PFL_ONGROUND = 1; +const float PFL_CROUCH = 2; +const float PFL_DEAD = 4; +const float PFL_GIBBED = 8; + +// Quake Temporary Entity Constants +const float TE_SPIKE = 0; +const float TE_SUPERSPIKE = 1; +const float TE_GUNSHOT = 2; +const float TE_EXPLOSION = 3; +const float TE_TAREXPLOSION = 4; +const float TE_LIGHTNING1 = 5; +const float TE_LIGHTNING2 = 6; +const float TE_WIZSPIKE = 7; +const float TE_KNIGHTSPIKE = 8; +const float TE_LIGHTNING3 = 9; +const float TE_LAVASPLASH = 10; +const float TE_TELEPORT = 11; +const float TE_EXPLOSION2 = 12; +// Darkplaces Additions +const float TE_EXPLOSIONRGB = 53; +const float TE_GUNSHOTQUAD = 57; +const float TE_EXPLOSIONQUAD = 70; + +// Math Constants +const float M_PI = 3.14159265358979323846; +const float EXTRA_LOW = -99999999; +const float EXTRA_HIGH = 99999999; + +// Frik File Constants +const float FILE_READ = 0; +const float FILE_APPEND = 1; +const float FILE_WRITE = 2; + +// Button values used by input_buttons +const float BUTTON_ATTACK = 1; +const float BUTTON_JUMP = 2; +const float BUTTON_3 = 4; +const float BUTTON_4 = 8; +const float BUTTON_5 = 16; +const float BUTTON_6 = 32; +const float BUTTON7 = 64; +const float BUTTON8 = 128; +const float BUTTON_USE = 256; +const float BUTTON_CHAT = 512; +const float BUTTON_PRYDONCURSOR = 1024; +const float BUTTON_9 = 2048; +const float BUTTON_10 = 4096; +const float BUTTON_11 = 8192; +const float BUTTON_12 = 16384; +const float BUTTON_13 = 32768; +const float BUTTON_14 = 65536; +const float BUTTON_15 = 131072; +const float BUTTON_16 = 262144; + + +const float DRAWFLAG_NORMAL = 0; +const float DRAWFLAG_ADDITIVE = 1; +const float DRAWFLAG_MODULATE = 2; +const float DRAWFLAG_2XMODULATE = 3; +const float DRAWFLAG_SCREEN = 4; +const float DRAWFLAG_MIPMAP = 0x100; // only for R_BeginPolygon + +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block +#define SOLID_CORPSE 5 // same as SOLID_BBOX, except it behaves as SOLID_NOT against SOLID_SLIDEBOX objects (players/monsters) + +float MOVE_NORMAL = 0; // same as FALSE +float MOVE_NOMONSTERS = 1; // same as TRUE +float MOVE_MISSILE = 2; // save as movement with .movetype == MOVETYPE_FLYMISSILE +float MOVE_HITMODEL = 4; +float MOVE_WORLDONLY = 3; + +float CAMERA_FREE = 1; +float CAMERA_CHASE = 2; + +float EF_NOMODELFLAGS = 8388608; diff --git a/data/qcsrc/client/effects.qc b/data/qcsrc/client/effects.qc index 2acd0ac45..bbee65800 100644 --- a/data/qcsrc/client/effects.qc +++ b/data/qcsrc/client/effects.qc @@ -1,100 +1,100 @@ -/* -.vector fx_start; -.vector fx_end; -.float fx_with; -.string fx_texture; -.float fx_lifetime; - -void SUB_Remove() -{ remove(self); } - -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); - 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); - -} -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_effetcs_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; - - steps = 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); - //b_make(pos_l, pos,"particles/lightning2",0.25,64); - if(random() < branchfactor) - cl_effetcs_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); - //b_make(pos_l, to,"particles/lightning2",0.25,64); - - } - else - te_lightning1(world,from,to); - //b_make(from, to,"particles/lightning2",0.25,64); - -} - -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(cvar("cl_effects_lightningarc_simple")) - { - te_lightning1(world,from,to); - } - else - { - float seglength, drifts, drifte, branchfactor, branchfactor_add; - - seglength = cvar("cl_effects_lightningarc_segmentlength"); - drifts = cvar("cl_effects_lightningarc_drift_start"); - drifte = cvar("cl_effects_lightningarc_drift_end"); - branchfactor = cvar("cl_effects_lightningarc_branchfactor_start"); - branchfactor = cvar("cl_effects_lightningarc_branchfactor_add"); - - cl_effetcs_lightningarc(from,to,seglength,drifts,drifte,branchfactor,branchfactor_add); - } - -} +/* +.vector fx_start; +.vector fx_end; +.float fx_with; +.string fx_texture; +.float fx_lifetime; + +void SUB_Remove() +{ remove(self); } + +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); + 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); + +} +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_effetcs_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; + + steps = 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); + //b_make(pos_l, pos,"particles/lightning2",0.25,64); + if(random() < branchfactor) + cl_effetcs_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); + //b_make(pos_l, to,"particles/lightning2",0.25,64); + + } + else + te_lightning1(world,from,to); + //b_make(from, to,"particles/lightning2",0.25,64); + +} + +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(cvar("cl_effects_lightningarc_simple")) + { + te_lightning1(world,from,to); + } + else + { + float seglength, drifts, drifte, branchfactor, branchfactor_add; + + seglength = cvar("cl_effects_lightningarc_segmentlength"); + drifts = cvar("cl_effects_lightningarc_drift_start"); + drifte = cvar("cl_effects_lightningarc_drift_end"); + branchfactor = cvar("cl_effects_lightningarc_branchfactor_start"); + branchfactor = cvar("cl_effects_lightningarc_branchfactor_add"); + + cl_effetcs_lightningarc(from,to,seglength,drifts,drifte,branchfactor,branchfactor_add); + } + +} diff --git a/data/qcsrc/client/rubble.qc b/data/qcsrc/client/rubble.qc index ff1805f19..1184cf845 100644 --- a/data/qcsrc/client/rubble.qc +++ b/data/qcsrc/client/rubble.qc @@ -1,34 +1,34 @@ -.float creationtime; - -void RubbleDrop(entity list, void() deleteproc) -{ - float t,tt; - entity rub,old_rub; - - rub = findchainentity(owner,list); - while(rub) - { - if(rub.creationtime > t) - { - old_rub = rub; - tt = t; - } - rub = rub.chain; - } - - rub = self; - self = old_rub; - deleteproc(); - self = rub; -} - -entity RubbleNew(entity list) -{ - entity rub; - - rub = spawn(); - rub.creationtime = time; - rub.owner = list; - - return rub; -} +.float creationtime; + +void RubbleDrop(entity list, void() deleteproc) +{ + float t,tt; + entity rub,old_rub; + + rub = findchainentity(owner,list); + while(rub) + { + if(rub.creationtime > t) + { + old_rub = rub; + tt = t; + } + rub = rub.chain; + } + + rub = self; + self = old_rub; + deleteproc(); + self = rub; +} + +entity RubbleNew(entity list) +{ + entity rub; + + rub = spawn(); + rub.creationtime = time; + rub.owner = list; + + return rub; +} diff --git a/data/qcsrc/server/csqceffects.qc b/data/qcsrc/server/csqceffects.qc index 8f93a1511..4b626546d 100644 --- a/data/qcsrc/server/csqceffects.qc +++ b/data/qcsrc/server/csqceffects.qc @@ -1,13 +1,13 @@ -void te_csqc_lightningarc(vector from,vector to) -{ - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_LIGHTNINGARC); - - 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); -} - +void te_csqc_lightningarc(vector from,vector to) +{ + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte(MSG_BROADCAST, TE_CSQC_LIGHTNINGARC); + + 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/data/qcsrc/server/movelib.qc b/data/qcsrc/server/movelib.qc index e028e0bf6..886896807 100644 --- a/data/qcsrc/server/movelib.qc +++ b/data/qcsrc/server/movelib.qc @@ -1,234 +1,234 @@ -.vector moveto; - -/** - Simulate drag - self.velocity = movelib_vdrag(self.velocity,0.02,0.5); -**/ -vector movelib_dragvec(float drag, float exp) -{ - float lspeed,ldrag; - - lspeed = vlen(self.velocity); - ldrag = lspeed * drag; - ldrag = ldrag * (drag * exp); - ldrag = 1 - (ldrag / lspeed); - - return self.velocity * ldrag; -} - -/** - Simulate drag - self.velocity = movelib_vdrag(somespeed,0.01,0.7); -**/ -float movelib_dragflt(float fspeed,float drag,float exp) -{ - float ldrag; - - ldrag = fspeed * drag; - ldrag = ldrag * ldrag * exp; - ldrag = 1 - (ldrag / fspeed); - - return ldrag; -} - -/** - Do a inertia simulation based on velocity. - Basicaly, this allows you to simulate loss of steering with higher speed. - self.velocity = movelib_inertia_fromspeed(self.velocity,newvel,1000,0.1,0.9); -**/ -vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax) -{ - float influense; - - influense = vlen(self.velocity) * (1 / vel_max); - - influense = bound(newmin,influense,oldmax); - - return (vel_new * (1 - influense)) + (self.velocity * influense); -} - -vector movelib_inertmove(vector new_vel,float new_bias) -{ - return new_vel * new_bias + self.velocity * (1-new_bias); -} - -.float movelib_lastupdate; -void movelib_move(vector force,float max_velocity,float drag,float mass,float breakforce) -{ - float deltatime; - float acceleration; - float mspeed; - vector breakvec; - - deltatime = time - self.movelib_lastupdate; - if (deltatime > 0.15) deltatime = 0; - self.movelib_lastupdate = time; - if (!deltatime) return; - - mspeed = vlen(self.velocity); - - if (mass) - acceleration = vlen(force) / mass; - else - acceleration = vlen(force); - - if (self.flags & FL_ONGROUND) - { - if (breakforce) - { - breakvec = (normalize(self.velocity) * (breakforce / mass) * deltatime); - self.velocity = self.velocity - breakvec; - } - - self.velocity = self.velocity + force * (acceleration * deltatime); - } - - if (drag) - self.velocity = movelib_dragvec(drag, 1); - - if (self.waterlevel > 1) - { - self.velocity = self.velocity + force * (acceleration * deltatime); - self.velocity = self.velocity + '0 0 0.05' * sv_gravity * deltatime; - } - else - self.velocity = self.velocity + '0 0 -1' * sv_gravity * deltatime; - - mspeed = vlen(self.velocity); - - if (max_velocity) - if (mspeed > max_velocity) - self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity; -} - -/* -.float mass; -.float side_friction; -.float ground_friction; -.float air_friction; -.float water_friction; -.float buoyancy; -float movelib_deltatime; - -void movelib_startupdate() -{ - movelib_deltatime = time - self.movelib_lastupdate; - - if (movelib_deltatime > 0.5) - movelib_deltatime = 0; - - self.movelib_lastupdate = time; -} - -void movelib_update(vector dir,float force) -{ - vector acceleration; - float old_speed; - float ffriction,v_z; - - vector breakvec; - vector old_dir; - vector ggravity; - vector old; - - if(!movelib_deltatime) - return; - v_z = self.velocity_z; - old_speed = vlen(self.velocity); - old_dir = normalize(self.velocity); - - //ggravity = (sv_gravity / self.mass) * '0 0 100'; - acceleration = (force / self.mass) * dir; - //acceleration -= old_dir * (old_speed / self.mass); - acceleration -= ggravity; - - if(self.waterlevel > 1) - { - ffriction = self.water_friction; - acceleration += self.buoyancy * '0 0 1'; - } - else - if(self.flags & FL_ONGROUND) - ffriction = self.ground_friction; - else - ffriction = self.air_friction; - - acceleration *= ffriction; - //self.velocity = self.velocity * (ffriction * movelib_deltatime); - self.velocity += acceleration * movelib_deltatime; - self.velocity_z = v_z; - -} -*/ - -void movelib_move_simple(vector newdir,float velo,float blendrate) -{ - self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo; -} -void movelib_beak_simple(float force) -{ - float mspeed; - vector mdir; - float vz; - - mspeed = max(0,vlen(self.velocity) - force); - mdir = normalize(self.velocity); - vz = self.velocity_z; - self.velocity = mdir * mspeed; - self.velocity_z = vz; -} - - -void movelib_groundalign4point(float spring_length,float spring_up,float blendrate) -{ - vector a,b,c,d,e,r,push_angle, ahead,side; - - push_angle_y = 0; - r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up); - e = v_up * spring_length; - - // Put springs slightly inside bbox - ahead = v_forward * (self.maxs_x * 0.85); - side = v_right * (self.maxs_y * 0.85); - - a = r + ahead + side; - b = r + ahead - side; - c = r - ahead + side; - d = r - ahead - side; - - traceline(a, a - e,MOVE_NORMAL,self); - a_z = (1 - trace_fraction); - r = trace_endpos; - - traceline(b, b - e,MOVE_NORMAL,self); - b_z = (1 - trace_fraction); - r += trace_endpos; - - traceline(c, c - e,MOVE_NORMAL,self); - c_z = (1 - trace_fraction); - r += trace_endpos; - - traceline(d, d - e,MOVE_NORMAL,self); - d_z = (1 - trace_fraction); - r += trace_endpos; - - a_x = r_z; - r = self.origin; - r_z = r_z; - - push_angle_x = (a_z - c_z) * 45; - push_angle_x += (b_z - d_z) * 45; - - push_angle_z = (b_z - a_z) * 45; - push_angle_z += (d_z - c_z) * 45; - - //self.angles_x += push_angle_x * 0.95; - //self.angles_z += push_angle_z * 0.95; - - self.angles_x = ((1-blendrate) * self.angles_x) + (push_angle_x * blendrate); - self.angles_z = ((1-blendrate) * self.angles_z) + (push_angle_z * blendrate); - - //a = self.origin; - setorigin(self,r); -} - +.vector moveto; + +/** + Simulate drag + self.velocity = movelib_vdrag(self.velocity,0.02,0.5); +**/ +vector movelib_dragvec(float drag, float exp) +{ + float lspeed,ldrag; + + lspeed = vlen(self.velocity); + ldrag = lspeed * drag; + ldrag = ldrag * (drag * exp); + ldrag = 1 - (ldrag / lspeed); + + return self.velocity * ldrag; +} + +/** + Simulate drag + self.velocity = movelib_vdrag(somespeed,0.01,0.7); +**/ +float movelib_dragflt(float fspeed,float drag,float exp) +{ + float ldrag; + + ldrag = fspeed * drag; + ldrag = ldrag * ldrag * exp; + ldrag = 1 - (ldrag / fspeed); + + return ldrag; +} + +/** + Do a inertia simulation based on velocity. + Basicaly, this allows you to simulate loss of steering with higher speed. + self.velocity = movelib_inertia_fromspeed(self.velocity,newvel,1000,0.1,0.9); +**/ +vector movelib_inertmove_byspeed(vector vel_new, float vel_max,float newmin,float oldmax) +{ + float influense; + + influense = vlen(self.velocity) * (1 / vel_max); + + influense = bound(newmin,influense,oldmax); + + return (vel_new * (1 - influense)) + (self.velocity * influense); +} + +vector movelib_inertmove(vector new_vel,float new_bias) +{ + return new_vel * new_bias + self.velocity * (1-new_bias); +} + +.float movelib_lastupdate; +void movelib_move(vector force,float max_velocity,float drag,float mass,float breakforce) +{ + float deltatime; + float acceleration; + float mspeed; + vector breakvec; + + deltatime = time - self.movelib_lastupdate; + if (deltatime > 0.15) deltatime = 0; + self.movelib_lastupdate = time; + if (!deltatime) return; + + mspeed = vlen(self.velocity); + + if (mass) + acceleration = vlen(force) / mass; + else + acceleration = vlen(force); + + if (self.flags & FL_ONGROUND) + { + if (breakforce) + { + breakvec = (normalize(self.velocity) * (breakforce / mass) * deltatime); + self.velocity = self.velocity - breakvec; + } + + self.velocity = self.velocity + force * (acceleration * deltatime); + } + + if (drag) + self.velocity = movelib_dragvec(drag, 1); + + if (self.waterlevel > 1) + { + self.velocity = self.velocity + force * (acceleration * deltatime); + self.velocity = self.velocity + '0 0 0.05' * sv_gravity * deltatime; + } + else + self.velocity = self.velocity + '0 0 -1' * sv_gravity * deltatime; + + mspeed = vlen(self.velocity); + + if (max_velocity) + if (mspeed > max_velocity) + self.velocity = normalize(self.velocity) * (mspeed - 50);//* max_velocity; +} + +/* +.float mass; +.float side_friction; +.float ground_friction; +.float air_friction; +.float water_friction; +.float buoyancy; +float movelib_deltatime; + +void movelib_startupdate() +{ + movelib_deltatime = time - self.movelib_lastupdate; + + if (movelib_deltatime > 0.5) + movelib_deltatime = 0; + + self.movelib_lastupdate = time; +} + +void movelib_update(vector dir,float force) +{ + vector acceleration; + float old_speed; + float ffriction,v_z; + + vector breakvec; + vector old_dir; + vector ggravity; + vector old; + + if(!movelib_deltatime) + return; + v_z = self.velocity_z; + old_speed = vlen(self.velocity); + old_dir = normalize(self.velocity); + + //ggravity = (sv_gravity / self.mass) * '0 0 100'; + acceleration = (force / self.mass) * dir; + //acceleration -= old_dir * (old_speed / self.mass); + acceleration -= ggravity; + + if(self.waterlevel > 1) + { + ffriction = self.water_friction; + acceleration += self.buoyancy * '0 0 1'; + } + else + if(self.flags & FL_ONGROUND) + ffriction = self.ground_friction; + else + ffriction = self.air_friction; + + acceleration *= ffriction; + //self.velocity = self.velocity * (ffriction * movelib_deltatime); + self.velocity += acceleration * movelib_deltatime; + self.velocity_z = v_z; + +} +*/ + +void movelib_move_simple(vector newdir,float velo,float blendrate) +{ + self.velocity = self.velocity * (1 - blendrate) + (newdir * blendrate) * velo; +} +void movelib_beak_simple(float force) +{ + float mspeed; + vector mdir; + float vz; + + mspeed = max(0,vlen(self.velocity) - force); + mdir = normalize(self.velocity); + vz = self.velocity_z; + self.velocity = mdir * mspeed; + self.velocity_z = vz; +} + + +void movelib_groundalign4point(float spring_length,float spring_up,float blendrate) +{ + vector a,b,c,d,e,r,push_angle, ahead,side; + + push_angle_y = 0; + r = (self.absmax + self.absmin) * 0.5 + (v_up * spring_up); + e = v_up * spring_length; + + // Put springs slightly inside bbox + ahead = v_forward * (self.maxs_x * 0.85); + side = v_right * (self.maxs_y * 0.85); + + a = r + ahead + side; + b = r + ahead - side; + c = r - ahead + side; + d = r - ahead - side; + + traceline(a, a - e,MOVE_NORMAL,self); + a_z = (1 - trace_fraction); + r = trace_endpos; + + traceline(b, b - e,MOVE_NORMAL,self); + b_z = (1 - trace_fraction); + r += trace_endpos; + + traceline(c, c - e,MOVE_NORMAL,self); + c_z = (1 - trace_fraction); + r += trace_endpos; + + traceline(d, d - e,MOVE_NORMAL,self); + d_z = (1 - trace_fraction); + r += trace_endpos; + + a_x = r_z; + r = self.origin; + r_z = r_z; + + push_angle_x = (a_z - c_z) * 45; + push_angle_x += (b_z - d_z) * 45; + + push_angle_z = (b_z - a_z) * 45; + push_angle_z += (d_z - c_z) * 45; + + //self.angles_x += push_angle_x * 0.95; + //self.angles_z += push_angle_z * 0.95; + + self.angles_x = ((1-blendrate) * self.angles_x) + (push_angle_x * blendrate); + self.angles_z = ((1-blendrate) * self.angles_z) + (push_angle_z * blendrate); + + //a = self.origin; + setorigin(self,r); +} + diff --git a/data/qcsrc/server/pathlib.qc b/data/qcsrc/server/pathlib.qc index 64aa9067e..4591a7afc 100644 --- a/data/qcsrc/server/pathlib.qc +++ b/data/qcsrc/server/pathlib.qc @@ -1,1047 +1,1047 @@ -//#define PATHLIB_RDFIELDS -#ifdef PATHLIB_RDFIELDS - #define path_next swampslug - #define path_prev lasertarget -#else - .entity path_next; - .entity path_prev; -#endif - -#define medium spawnshieldtime - -//#define DEBUGPATHING - -entity openlist; -entity closedlist; -entity scraplist; - -.float pathlib_node_g; -.float pathlib_node_h; -.float pathlib_node_f; - -float pathlib_open_cnt; -float pathlib_closed_cnt; -float pathlib_made_cnt; -float pathlib_merge_cnt; -float pathlib_recycle_cnt; -float pathlib_searched_cnt; - -#ifdef DEBUGPATHING - -#endif - -float pathlib_bestopen_seached; -float pathlib_bestcash_hits; -float pathlib_bestcash_saved; - -float pathlib_gridsize; - -float pathlib_movecost; -float pathlib_movecost_diag; -float pathlib_movecost_waterfactor; - -float pathlib_edge_check_size; - -float pathlib_foundgoal; -entity goal_node; - -entity best_open_node; -.float is_path_node; - - -#ifdef DEBUGPATHING -float edge_show(vector point,float fsize); -void mark_error(vector where,float lifetime); -void mark_info(vector where,float lifetime); -entity mark_misc(vector where,float lifetime); - -void pathlib_showpath(entity start) -{ - entity e; - e = start; - while(e.path_next) - { - te_lightning1(e,e.origin,e.path_next.origin); - e = e.path_next; - } -} - -void path_dbg_think() -{ - pathlib_showpath(self); - self.nextthink = time + 1; -} - -void __showpath2_think() -{ - mark_info(self.origin,1); - if(self.path_next) - { - self.path_next.think = __showpath2_think; - self.path_next.nextthink = time + 0.15; - } - else - { - self.owner.think = __showpath2_think; - self.owner.nextthink = time + 0.15; - } -} - -void pathlib_showpath2(entity path) -{ - path.think = __showpath2_think; - path.nextthink = time; -} - -#endif - -void pathlib_deletepath(entity start) -{ - entity e; - - e = findchainentity(owner, start); - while(e) - { - e.think = SUB_Remove; - e.nextthink = time; - e = e.chain; - } -} - -float fsnap(float val,float fsize) -{ - return rint(val / fsize) * fsize; -} - -vector vsnap(vector point,float fsize) -{ - vector vret; - - vret_x = rint(point_x / fsize) * fsize; - vret_y = rint(point_y / fsize) * fsize; - vret_z = ceil(point_z / fsize) * fsize; - - return vret; -} - -float walknode_stepsize; -vector walknode_stepup; -vector walknode_maxdrop; -vector walknode_boxup; -vector walknode_boxmax; -vector walknode_boxmin; -float pathlib_movenode_goodnode; - -float floor_ok(vector point) -{ - float pc; - - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - return 0; - - pc = pointcontents(point); - - switch(pc) - { - case CONTENT_SOLID: - case CONTENT_SLIME: - case CONTENT_LAVA: - case CONTENT_SKY: - return 0; - case CONTENT_EMPTY: - if not (pointcontents(point - '0 0 1') == CONTENT_SOLID) - return 0; - break; - case CONTENT_WATER: - return 1; - } - if(pointcontents(point - '0 0 1') == CONTENT_SOLID) - return 1; - - return 0; -} - -#define inwater(point) (pointcontents(point) == CONTENT_WATER) -/* -float inwater(vector point) -{ - if(pointcontents(point) == CONTENT_WATER) - return 1; - return 0; -} -*/ - -#define _pcheck(p) traceline(p+z_up,p-z_down,MOVE_WORLDONLY,self); if not(floor_ok(trace_endpos)) return 1 -float edge_check(vector point,float fsize) -{ - vector z_up,z_down; - - z_up = '0 0 1' * fsize; - z_down = '0 0 1' * fsize; - - _pcheck(point + ('1 1 0' * fsize)); - _pcheck(point + ('1 -1 0' * fsize)); - _pcheck(point + ('1 0 0' * fsize)); - - _pcheck(point + ('0 1 0' * fsize)); - _pcheck(point + ('0 -1 0' * fsize)); - - _pcheck(point + ('-1 0 0' * fsize)); - _pcheck(point + ('-1 1 0' * fsize)); - _pcheck(point + ('-1 -1 0' * fsize)); - - return 0; -} - -#ifdef DEBUGPATHING -#define _pshow(p) mark_error(p,10) -float edge_show(vector point,float fsize) -{ - - _pshow(point + ('1 1 0' * fsize)); - _pshow(point + ('1 -1 0' * fsize)); - _pshow(point + ('1 0 0' * fsize)); - - _pshow(point + ('0 1 0' * fsize)); - _pshow(point + ('0 -1 0' * fsize)); - - _pshow(point + ('-1 0 0' * fsize)); - _pshow(point + ('-1 1 0' * fsize)); - _pshow(point + ('-1 -1 0' * fsize)); - - return 0; -} -#endif - -var vector pathlib_movenode(vector start,vector end,float doedge); -vector pathlib_wateroutnode(vector start,vector end) -{ - vector surface; - - pathlib_movenode_goodnode = 0; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - traceline(end + ('0 0 0.25' * pathlib_gridsize),end - ('0 0 1' * pathlib_gridsize),MOVE_WORLDONLY,self); - end = trace_endpos; - - if not(pointcontents(end - '0 0 1') == CONTENT_SOLID) - return end; - - for(surface = start ; surface_z < (end_z + 32); ++surface_z) - { - if(pointcontents(surface) == CONTENT_EMPTY) - break; - } - - if(pointcontents(surface + '0 0 1') != CONTENT_EMPTY) - return end; - - tracebox(start + '0 0 64', walknode_boxmin,walknode_boxmax, end + '0 0 64', MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - if(fabs(surface_z - end_z) > 32) - pathlib_movenode_goodnode = 0; - - return end; -} - -vector pathlib_swimnode(vector start,vector end) -{ - pathlib_movenode_goodnode = 0; - - if(pointcontents(start) != CONTENT_WATER) - return end; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - if(pointcontents(end) == CONTENT_EMPTY) - return pathlib_wateroutnode( start, end); - - tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - return end; -} - -vector pathlib_flynode(vector start,vector end) -{ - pathlib_movenode_goodnode = 0; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - return end; -} - -vector pathlib_walknode(vector start,vector end,float doedge) -{ - vector direction,point,last_point,s,e; - float steps, distance, i,laststep; - - pathlib_movenode_goodnode = 0; - - s = start; - e = end; - e_z = 0; - s_z = 0; - direction = normalize(s - e); - - distance = vlen(start - end); - laststep = distance / walknode_stepsize; - steps = floor(laststep); - laststep = laststep - steps; - - point = start; - s = point + walknode_stepup; - e = point - walknode_maxdrop; - - traceline(s, e,MOVE_WORLDONLY,self); - if(trace_fraction == 1.0) - return trace_endpos; - - if (floor_ok(trace_endpos) == 0) - return trace_endpos; - - last_point = trace_endpos; - - for(i = 0; i < steps; ++i) - { - point = last_point + direction * walknode_stepsize; - - s = point + walknode_stepup; - e = point - walknode_maxdrop; - traceline(s, e,MOVE_WORLDONLY,self); - if(trace_fraction == 1.0) - return trace_endpos; - - point = trace_endpos; - if not(floor_ok(trace_endpos)) - return trace_endpos; - - tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self); - if(trace_fraction != 1.0) - return trace_endpos; - - if(doedge) - if(edge_check(point,pathlib_edge_check_size)) - return trace_endpos; - - last_point = point; - } - - point = last_point + direction * walknode_stepsize * laststep; - - point_x = fsnap(point_x, pathlib_gridsize); - point_y = fsnap(point_y, pathlib_gridsize); - - s = point + walknode_stepup; - e = point - walknode_maxdrop; - traceline(s, e,MOVE_WORLDONLY,self); - - if(trace_fraction == 1.0) - return trace_endpos; - - point = trace_endpos; - - if not(floor_ok(trace_endpos)) - return trace_endpos; - - tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self); - if(trace_fraction != 1.0) - return trace_endpos; - - pathlib_movenode_goodnode = 1; - return point; -} - -var float pathlib_cost(entity parent,vector to, float static_cost); -float pathlib_g_static(entity parent,vector to, float static_cost) -{ - if(inwater(to)) - return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; - else - return parent.pathlib_node_g + static_cost; -} - -float pathlib_g_static_water(entity parent,vector to, float static_cost) -{ - if(inwater(to)) - return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; - else - return parent.pathlib_node_g + static_cost; -} - -float pathlib_g_euclidean(entity parent,vector to, float static_cost) -{ - return parent.pathlib_node_g + vlen(parent.origin - to); -} -float pathlib_g_euclidean_water(entity parent,vector to, float static_cost) -{ - if(inwater(to)) - return parent.pathlib_node_g + vlen(parent.origin - to) * pathlib_movecost_waterfactor; - else - return parent.pathlib_node_g + vlen(parent.origin - to); -} - -var float(vector from,vector to) pathlib_heuristic; - -/** - Manhattan Menas we expect to move up,down left or right - No diagonal moves espected. (like moving bewteen city blocks) -**/ -float pathlib_h_manhattan(vector a,vector b) -{ - //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y)) - - float h; - h = fabs(a_x - b_x); - h += fabs(a_y - b_y); - h *= pathlib_gridsize; - - return h; -} - -/** - This heuristic consider both stright and disagonal moves - to have teh same cost. -**/ -float pathlib_h_diagonal(vector a,vector b) -{ - //h(n) = D * max(abs(n.x-goal.x), abs(n.y-goal.y)) - float h,x,y; - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - h = pathlib_movecost * max(x,y); - - return h; -} - -/** - This heuristic only considers the stright line distance. - Will usualy mean a lower H then G meaning A* Will speand more - and run slower. -**/ -float pathlib_h_euclidean(vector a,vector b) -{ - return vlen(a - b); -} - -/** - This heuristic consider both stright and disagonal moves, - But has a separate cost for diagonal moves. -**/ -float pathlib_h_diagonal2(vector a,vector b) -{ - float h_diag,h_str,h,x,y; - - /* - h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) - h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) - h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) - */ - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - - h_diag = min(x,y); - h_str = x + y; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - return h; -} - -/** - This heuristic consider both stright and disagonal moves, - But has a separate cost for diagonal moves. - - -**/ -float pathlib_h_diagonal2sdp(vector preprev,vector prev,vector point,vector end) -{ - float h_diag,h_str,h,x,y,z; - - //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) - //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) - //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) - - x = fabs(point_x - end_x); - y = fabs(point_y - end_y); - z = fabs(point_z - end_z); - - h_diag = min3(x,y,z); - h_str = x + y + z; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - float m; - vector d1,d2; - - d1 = normalize(preprev - point); - d2 = normalize(prev - point); - m = vlen(d1-d2); - //bprint("pathlib_h_diagonal2sdp-M = ",ftos(m),"\n"); - - return h * m; -} - - -float pathlib_h_diagonal3(vector a,vector b) -{ - float h_diag,h_str,h,x,y,z; - - //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) - //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) - //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - z = fabs(a_z - b_z); - - h_diag = min3(x,y,z); - h_str = x + y + z; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - return h; -} - -//#define PATHLIB_USE_NODESCRAP -#define PATHLIB_NODEEXPIRE 0.05 -float pathlib_scraplist_cnt; -entity newnode() -{ - entity n; -#ifdef PATHLIB_USE_NODESCRAP - if(pathlib_scraplist_cnt) - { - n = findentity(world,owner,scraplist); - if(n) - { - --pathlib_scraplist_cnt; - ++pathlib_recycle_cnt; - return n; - } - else - pathlib_scraplist_cnt = 0; - } -#endif - ++pathlib_made_cnt; - n = spawn(); -#ifdef PATHLIB_NODEEXPIRE - n.think = SUB_Remove; - n.nextthink = time + PATHLIB_NODEEXPIRE; - return n; -} - -void dumpnode(entity n) -{ -#ifdef PATHLIB_USE_NODESCRAP - ++pathlib_scraplist_cnt; - - n.path_next = world; - n.path_prev = world; - n.is_path_node = FALSE; - n.owner = scraplist; -#else - //n.is_path_node = FALSE; - n.think = SUB_Remove; - n.nextthink = time; -#endif -} - -entity pathlib_mknode(vector where,entity parent) -{ - entity node; - - node = newnode(); - node.is_path_node = TRUE; - node.owner = openlist; - node.path_prev = parent; - - setorigin(node, where); - - ++pathlib_open_cnt; - - node.medium = pointcontents(where); - - return node; -} - -var float pathlib_expandnode(entity node, vector start, vector goal); -float pathlib_expandnode_star(entity node, vector start, vector goal); -float pathlib_expandnode_box(entity node, vector start, vector goal); - -var float pathlib_makenode(entity parent,vector start, vector to, vector goal,float cost); -float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost) -{ - entity node; - float h,g,f,doedge; - vector where; - - ++pathlib_searched_cnt; - - if(inwater(parent.origin)) - { - pathlib_expandnode = pathlib_expandnode_box; - pathlib_movenode = pathlib_swimnode; - } - else - { - if(inwater(to)) - { - pathlib_expandnode = pathlib_expandnode_box; - pathlib_movenode = pathlib_swimnode; - } - else - { - - pathlib_expandnode = pathlib_expandnode_star; - pathlib_movenode = pathlib_walknode; - doedge = 1; - } - } - - where = pathlib_movenode(parent.origin,to,0); - if not(pathlib_movenode_goodnode) - return 0; - - if(doedge) - if(edge_check(where,pathlib_edge_check_size)) - return 0; - - if(parent.path_prev) - pathlib_h_diagonal2sdp(parent.path_prev.origin,parent.origin,where,goal); - - h = pathlib_heuristic(where,goal); - g = pathlib_cost(parent,where,cost); - f = g + h; - - node = findradius(where,pathlib_gridsize * 0.75); - while(node) - { - if(node.is_path_node == TRUE) - { - ++pathlib_merge_cnt; - if(node.owner == openlist) - { - if(node.pathlib_node_g > g) - { - node.pathlib_node_h = h; - node.pathlib_node_g = g; - node.pathlib_node_f = f; - node.path_prev = parent; - } - - if not (best_open_node) - best_open_node = node; - else if(best_open_node.pathlib_node_f > node.pathlib_node_f) - best_open_node = node; - } - - return 1; - } - node = node.chain; - } - - node = pathlib_mknode(where,parent); - node.pathlib_node_h = h; - node.pathlib_node_g = g; - node.pathlib_node_f = f; - - if not (best_open_node) - best_open_node = node; - else if(best_open_node.pathlib_node_f > node.pathlib_node_f) - best_open_node = node; - - return 1; -} - -entity pathlib_getbestopen() -{ - entity node; - entity bestnode; - - if(best_open_node) - { - ++pathlib_bestcash_hits; - pathlib_bestcash_saved += pathlib_open_cnt; - - return best_open_node; - } - - node = findchainentity(owner,openlist); - if(!node) - return world; - - bestnode = node; - while(node) - { - ++pathlib_bestopen_seached; - if(node.pathlib_node_f < bestnode.pathlib_node_f) - bestnode = node; - - node = node.chain; - } - - return bestnode; -} - -void pathlib_close_node(entity node,vector goal) -{ - - if(node.owner == closedlist) - { - dprint("Pathlib: Tried to close a closed node!\n"); - return; - } - - if(node == best_open_node) - best_open_node = world; - - ++pathlib_closed_cnt; - --pathlib_open_cnt; - - node.owner = closedlist; - - if(vlen(node.origin - goal) <= pathlib_gridsize) - { - vector goalmove; - - goalmove = pathlib_walknode(node.origin,goal,1); - if(pathlib_movenode_goodnode) - { - goal_node = node; - pathlib_foundgoal = TRUE; - } - } -} - -float pathlib_expandnode_star(entity node, vector start, vector goal) -{ - vector point; - vector where; - float nodecnt; - - where = node.origin; - - v_forward = '1 0 0'; - v_right = '0 1 0'; - - // Forward - point = where + v_forward * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Back - point = where - v_forward * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Right - point = where + v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Left - point = where - v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Forward-right - point = where + v_forward * pathlib_gridsize + v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Forward-left - point = where + v_forward * pathlib_gridsize - v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Back-right - point = where - v_forward * pathlib_gridsize + v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Back-left - point = where - v_forward * pathlib_gridsize - v_right * pathlib_gridsize; - nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - return pathlib_open_cnt; -} - -float pathlib_expandnode_box(entity node, vector start, vector goal) -{ - vector v; - - for(v_z = node.origin_z - pathlib_gridsize; v_z <= node.origin_z + pathlib_gridsize; v_z += pathlib_gridsize) - for(v_y = node.origin_y - pathlib_gridsize; v_y <= node.origin_y + pathlib_gridsize; v_y += pathlib_gridsize) - for(v_x = node.origin_x - pathlib_gridsize; v_x <= node.origin_x + pathlib_gridsize; v_x += pathlib_gridsize) - { - if(vlen(v - node.origin)) - pathlib_makenode(node,start,v,goal,pathlib_movecost); - } - - return pathlib_open_cnt; -} - -void pathlib_cleanup() -{ - entity node; - - node = findfloat(world,is_path_node, TRUE); - while(node) - { - dumpnode(node); - node = findfloat(node,is_path_node, TRUE); - } - - if(openlist) - remove(openlist); - - if(closedlist) - remove(closedlist); - - best_open_node = world; - openlist = world; - closedlist = world; -} - -var float buildpath_nodefilter(vector n,vector c,vector p); -float buildpath_nodefilter_directional(vector n,vector c,vector p) -{ - vector d1,d2; - - d2 = normalize(p - c); - d1 = normalize(c - n); - - if(vlen(d1-d2) < 0.25) - return 1; - - return 0; -} - -float buildpath_nodefilter_moveskip(vector n,vector c,vector p) -{ - pathlib_walknode(p,n,1); - if(pathlib_movenode_goodnode) - return 1; - - return 0; -} - -entity path_build(entity next, vector where, entity prev, entity start) -{ - entity path; - - if(prev && next) - if(buildpath_nodefilter) - if(buildpath_nodefilter(next.origin,where,prev.origin)) - return next; - - - path = spawn(); - path.owner = start; - path.path_next = next; - - setorigin(path,where); - - if(!next) - path.classname = "path_end"; - else - { - if(!prev) - path.classname = "path_start"; - else - path.classname = "path_node"; - } - - return path; -} - -entity pathlib_astar(vector from,vector to) -{ - entity path, start, end, open, n, ln; - float ptime, ftime, ctime; - - ptime = gettime(GETTIME_REALTIME); - - pathlib_cleanup(); - - // Select water<->land capable node make/link - pathlib_makenode = pathlib_makenode_adaptive; - // Select XYZ cost estimate - pathlib_heuristic = pathlib_h_diagonal3; - // Select distance + waterfactor cost - pathlib_cost = pathlib_g_euclidean_water; - // Select star expander - pathlib_expandnode = pathlib_expandnode_star; - // Select walk simulation movement test - pathlib_movenode = pathlib_walknode; - // Filter final nodes by direction - buildpath_nodefilter = buildpath_nodefilter_directional; - - // If the start is in water we need diffrent settings - if(inwater(from)) - { - // Select volumetric node expaner - pathlib_expandnode = pathlib_expandnode_box; - - // Water movement test - pathlib_movenode = pathlib_swimnode; - } - - if not(openlist) - openlist = spawn(); - - if not(closedlist) - closedlist = spawn(); - - if not(scraplist) - scraplist = spawn(); - - pathlib_closed_cnt = 0; - pathlib_open_cnt = 0; - pathlib_made_cnt = 0; - pathlib_merge_cnt = 0; - pathlib_searched_cnt = 0; - pathlib_bestopen_seached = 0; - pathlib_bestcash_hits = 0; - pathlib_bestcash_saved = 0; - pathlib_recycle_cnt = 0; - - pathlib_gridsize = 128; - pathlib_movecost = pathlib_gridsize; - pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize)); - pathlib_movecost_waterfactor = 1.1; - pathlib_foundgoal = 0; - - walknode_boxmax = self.maxs * 1.5; - walknode_boxmin = self.mins * 1.5; - - pathlib_edge_check_size = (vlen(walknode_boxmin - walknode_boxmax) * 0.5); - - walknode_boxup = '0 0 2' * self.maxs_z; - walknode_stepsize = 32; - walknode_stepup = '0 0 1' * walknode_stepsize; - walknode_maxdrop = '0 0 3' * walknode_stepsize; - - from_x = fsnap(from_x,pathlib_gridsize); - from_y = fsnap(from_y,pathlib_gridsize); - - to_x = fsnap(to_x,pathlib_gridsize); - to_y = fsnap(to_y,pathlib_gridsize); - - dprint("AStar init. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n"); - path = pathlib_mknode(from,world); - pathlib_close_node(path,to); - if(pathlib_foundgoal) - { - dprint("AStar: Goal found on first node!\n"); - - open = spawn(); - open.owner = open; - open.classname = "path_end"; - setorigin(open,path.origin); - - pathlib_cleanup(); - - return open; - } - - if(pathlib_expandnode(path,from,to) <= 0) - { - dprint("AStar path fail.\n"); - pathlib_cleanup(); - - return world; - } - - best_open_node = pathlib_getbestopen(); - n = best_open_node; - pathlib_close_node(best_open_node,to); - if(inwater(n.origin)) - pathlib_expandnode_box(n,from,to); - else - pathlib_expandnode_star(n,from,to); - - while(pathlib_open_cnt) - { - best_open_node = pathlib_getbestopen(); - n = best_open_node; - pathlib_close_node(best_open_node,to); - - if(inwater(n.origin)) - pathlib_expandnode_box(n,from,to); - else - pathlib_expandnode(n,from,to); - - if(pathlib_foundgoal) - { - dprint("Target found. Rebuilding and filtering path...\n"); - ftime = gettime(GETTIME_REALTIME); - ptime = ftime - ptime; - - start = path_build(world,path.origin,world,world); - end = path_build(world,goal_node.origin,world,start); - ln = end; - - open = goal_node; - for(open = goal_node; open.path_prev != path; open = open.path_prev) - { - n = path_build(ln,open.origin,open.path_prev,start); - ln.path_prev = n; - ln = n; - } - start.path_next = n; - n.path_prev = start; - ftime = gettime(GETTIME_REALTIME) - ftime; - - ctime = gettime(GETTIME_REALTIME); - pathlib_cleanup(); - ctime = gettime(GETTIME_REALTIME) - ctime; - - -#ifdef DEBUGPATHING - pathlib_showpath2(start); - - dprint("Time used - pathfinding: ", ftos(ptime),"\n"); - dprint("Time used - rebuild & filter: ", ftos(ftime),"\n"); - dprint("Time used - cleanup: ", ftos(ctime),"\n"); - dprint("Time used - total: ", ftos(ptime + ftime + ctime),"\n"); - dprint("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_ticrate)),"\n\n"); - dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); - dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); - dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); - dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); - dprint("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n"); - - if(pathlib_recycle_cnt) - dprint("Nodes - make/reuse: ", ftos(pathlib_made_cnt / pathlib_recycle_cnt),"\n"); - if(pathlib_recycle_cnt) - dprint("Nodes - reused: ", ftos(pathlib_recycle_cnt),"\n"); - - dprint("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n"); - dprint("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n"); - dprint("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n"); - dprint("AStar done. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n\n"); -#endif - return start; - } - } - - dprint("A* Faild to find a path! Try a smaller gridsize.\n"); - - pathlib_cleanup(); - - return world; -} - - - +//#define PATHLIB_RDFIELDS +#ifdef PATHLIB_RDFIELDS + #define path_next swampslug + #define path_prev lasertarget +#else + .entity path_next; + .entity path_prev; +#endif + +#define medium spawnshieldtime + +//#define DEBUGPATHING + +entity openlist; +entity closedlist; +entity scraplist; + +.float pathlib_node_g; +.float pathlib_node_h; +.float pathlib_node_f; + +float pathlib_open_cnt; +float pathlib_closed_cnt; +float pathlib_made_cnt; +float pathlib_merge_cnt; +float pathlib_recycle_cnt; +float pathlib_searched_cnt; + +#ifdef DEBUGPATHING + +#endif + +float pathlib_bestopen_seached; +float pathlib_bestcash_hits; +float pathlib_bestcash_saved; + +float pathlib_gridsize; + +float pathlib_movecost; +float pathlib_movecost_diag; +float pathlib_movecost_waterfactor; + +float pathlib_edge_check_size; + +float pathlib_foundgoal; +entity goal_node; + +entity best_open_node; +.float is_path_node; + + +#ifdef DEBUGPATHING +float edge_show(vector point,float fsize); +void mark_error(vector where,float lifetime); +void mark_info(vector where,float lifetime); +entity mark_misc(vector where,float lifetime); + +void pathlib_showpath(entity start) +{ + entity e; + e = start; + while(e.path_next) + { + te_lightning1(e,e.origin,e.path_next.origin); + e = e.path_next; + } +} + +void path_dbg_think() +{ + pathlib_showpath(self); + self.nextthink = time + 1; +} + +void __showpath2_think() +{ + mark_info(self.origin,1); + if(self.path_next) + { + self.path_next.think = __showpath2_think; + self.path_next.nextthink = time + 0.15; + } + else + { + self.owner.think = __showpath2_think; + self.owner.nextthink = time + 0.15; + } +} + +void pathlib_showpath2(entity path) +{ + path.think = __showpath2_think; + path.nextthink = time; +} + +#endif + +void pathlib_deletepath(entity start) +{ + entity e; + + e = findchainentity(owner, start); + while(e) + { + e.think = SUB_Remove; + e.nextthink = time; + e = e.chain; + } +} + +float fsnap(float val,float fsize) +{ + return rint(val / fsize) * fsize; +} + +vector vsnap(vector point,float fsize) +{ + vector vret; + + vret_x = rint(point_x / fsize) * fsize; + vret_y = rint(point_y / fsize) * fsize; + vret_z = ceil(point_z / fsize) * fsize; + + return vret; +} + +float walknode_stepsize; +vector walknode_stepup; +vector walknode_maxdrop; +vector walknode_boxup; +vector walknode_boxmax; +vector walknode_boxmin; +float pathlib_movenode_goodnode; + +float floor_ok(vector point) +{ + float pc; + + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) + return 0; + + pc = pointcontents(point); + + switch(pc) + { + case CONTENT_SOLID: + case CONTENT_SLIME: + case CONTENT_LAVA: + case CONTENT_SKY: + return 0; + case CONTENT_EMPTY: + if not (pointcontents(point - '0 0 1') == CONTENT_SOLID) + return 0; + break; + case CONTENT_WATER: + return 1; + } + if(pointcontents(point - '0 0 1') == CONTENT_SOLID) + return 1; + + return 0; +} + +#define inwater(point) (pointcontents(point) == CONTENT_WATER) +/* +float inwater(vector point) +{ + if(pointcontents(point) == CONTENT_WATER) + return 1; + return 0; +} +*/ + +#define _pcheck(p) traceline(p+z_up,p-z_down,MOVE_WORLDONLY,self); if not(floor_ok(trace_endpos)) return 1 +float edge_check(vector point,float fsize) +{ + vector z_up,z_down; + + z_up = '0 0 1' * fsize; + z_down = '0 0 1' * fsize; + + _pcheck(point + ('1 1 0' * fsize)); + _pcheck(point + ('1 -1 0' * fsize)); + _pcheck(point + ('1 0 0' * fsize)); + + _pcheck(point + ('0 1 0' * fsize)); + _pcheck(point + ('0 -1 0' * fsize)); + + _pcheck(point + ('-1 0 0' * fsize)); + _pcheck(point + ('-1 1 0' * fsize)); + _pcheck(point + ('-1 -1 0' * fsize)); + + return 0; +} + +#ifdef DEBUGPATHING +#define _pshow(p) mark_error(p,10) +float edge_show(vector point,float fsize) +{ + + _pshow(point + ('1 1 0' * fsize)); + _pshow(point + ('1 -1 0' * fsize)); + _pshow(point + ('1 0 0' * fsize)); + + _pshow(point + ('0 1 0' * fsize)); + _pshow(point + ('0 -1 0' * fsize)); + + _pshow(point + ('-1 0 0' * fsize)); + _pshow(point + ('-1 1 0' * fsize)); + _pshow(point + ('-1 -1 0' * fsize)); + + return 0; +} +#endif + +var vector pathlib_movenode(vector start,vector end,float doedge); +vector pathlib_wateroutnode(vector start,vector end) +{ + vector surface; + + pathlib_movenode_goodnode = 0; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + traceline(end + ('0 0 0.25' * pathlib_gridsize),end - ('0 0 1' * pathlib_gridsize),MOVE_WORLDONLY,self); + end = trace_endpos; + + if not(pointcontents(end - '0 0 1') == CONTENT_SOLID) + return end; + + for(surface = start ; surface_z < (end_z + 32); ++surface_z) + { + if(pointcontents(surface) == CONTENT_EMPTY) + break; + } + + if(pointcontents(surface + '0 0 1') != CONTENT_EMPTY) + return end; + + tracebox(start + '0 0 64', walknode_boxmin,walknode_boxmax, end + '0 0 64', MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + if(fabs(surface_z - end_z) > 32) + pathlib_movenode_goodnode = 0; + + return end; +} + +vector pathlib_swimnode(vector start,vector end) +{ + pathlib_movenode_goodnode = 0; + + if(pointcontents(start) != CONTENT_WATER) + return end; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + if(pointcontents(end) == CONTENT_EMPTY) + return pathlib_wateroutnode( start, end); + + tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + return end; +} + +vector pathlib_flynode(vector start,vector end) +{ + pathlib_movenode_goodnode = 0; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + tracebox(start, walknode_boxmin,walknode_boxmax, end, MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + return end; +} + +vector pathlib_walknode(vector start,vector end,float doedge) +{ + vector direction,point,last_point,s,e; + float steps, distance, i,laststep; + + pathlib_movenode_goodnode = 0; + + s = start; + e = end; + e_z = 0; + s_z = 0; + direction = normalize(s - e); + + distance = vlen(start - end); + laststep = distance / walknode_stepsize; + steps = floor(laststep); + laststep = laststep - steps; + + point = start; + s = point + walknode_stepup; + e = point - walknode_maxdrop; + + traceline(s, e,MOVE_WORLDONLY,self); + if(trace_fraction == 1.0) + return trace_endpos; + + if (floor_ok(trace_endpos) == 0) + return trace_endpos; + + last_point = trace_endpos; + + for(i = 0; i < steps; ++i) + { + point = last_point + direction * walknode_stepsize; + + s = point + walknode_stepup; + e = point - walknode_maxdrop; + traceline(s, e,MOVE_WORLDONLY,self); + if(trace_fraction == 1.0) + return trace_endpos; + + point = trace_endpos; + if not(floor_ok(trace_endpos)) + return trace_endpos; + + tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self); + if(trace_fraction != 1.0) + return trace_endpos; + + if(doedge) + if(edge_check(point,pathlib_edge_check_size)) + return trace_endpos; + + last_point = point; + } + + point = last_point + direction * walknode_stepsize * laststep; + + point_x = fsnap(point_x, pathlib_gridsize); + point_y = fsnap(point_y, pathlib_gridsize); + + s = point + walknode_stepup; + e = point - walknode_maxdrop; + traceline(s, e,MOVE_WORLDONLY,self); + + if(trace_fraction == 1.0) + return trace_endpos; + + point = trace_endpos; + + if not(floor_ok(trace_endpos)) + return trace_endpos; + + tracebox(last_point + walknode_boxup, walknode_boxmin,walknode_boxmax, point + walknode_boxup, MOVE_WORLDONLY, self); + if(trace_fraction != 1.0) + return trace_endpos; + + pathlib_movenode_goodnode = 1; + return point; +} + +var float pathlib_cost(entity parent,vector to, float static_cost); +float pathlib_g_static(entity parent,vector to, float static_cost) +{ + if(inwater(to)) + return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; + else + return parent.pathlib_node_g + static_cost; +} + +float pathlib_g_static_water(entity parent,vector to, float static_cost) +{ + if(inwater(to)) + return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; + else + return parent.pathlib_node_g + static_cost; +} + +float pathlib_g_euclidean(entity parent,vector to, float static_cost) +{ + return parent.pathlib_node_g + vlen(parent.origin - to); +} +float pathlib_g_euclidean_water(entity parent,vector to, float static_cost) +{ + if(inwater(to)) + return parent.pathlib_node_g + vlen(parent.origin - to) * pathlib_movecost_waterfactor; + else + return parent.pathlib_node_g + vlen(parent.origin - to); +} + +var float(vector from,vector to) pathlib_heuristic; + +/** + Manhattan Menas we expect to move up,down left or right + No diagonal moves espected. (like moving bewteen city blocks) +**/ +float pathlib_h_manhattan(vector a,vector b) +{ + //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y)) + + float h; + h = fabs(a_x - b_x); + h += fabs(a_y - b_y); + h *= pathlib_gridsize; + + return h; +} + +/** + This heuristic consider both stright and disagonal moves + to have teh same cost. +**/ +float pathlib_h_diagonal(vector a,vector b) +{ + //h(n) = D * max(abs(n.x-goal.x), abs(n.y-goal.y)) + float h,x,y; + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + h = pathlib_movecost * max(x,y); + + return h; +} + +/** + This heuristic only considers the stright line distance. + Will usualy mean a lower H then G meaning A* Will speand more + and run slower. +**/ +float pathlib_h_euclidean(vector a,vector b) +{ + return vlen(a - b); +} + +/** + This heuristic consider both stright and disagonal moves, + But has a separate cost for diagonal moves. +**/ +float pathlib_h_diagonal2(vector a,vector b) +{ + float h_diag,h_str,h,x,y; + + /* + h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) + h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) + h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) + */ + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + + h_diag = min(x,y); + h_str = x + y; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + return h; +} + +/** + This heuristic consider both stright and disagonal moves, + But has a separate cost for diagonal moves. + + +**/ +float pathlib_h_diagonal2sdp(vector preprev,vector prev,vector point,vector end) +{ + float h_diag,h_str,h,x,y,z; + + //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) + //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) + //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) + + x = fabs(point_x - end_x); + y = fabs(point_y - end_y); + z = fabs(point_z - end_z); + + h_diag = min3(x,y,z); + h_str = x + y + z; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + float m; + vector d1,d2; + + d1 = normalize(preprev - point); + d2 = normalize(prev - point); + m = vlen(d1-d2); + //bprint("pathlib_h_diagonal2sdp-M = ",ftos(m),"\n"); + + return h * m; +} + + +float pathlib_h_diagonal3(vector a,vector b) +{ + float h_diag,h_str,h,x,y,z; + + //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) + //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) + //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + z = fabs(a_z - b_z); + + h_diag = min3(x,y,z); + h_str = x + y + z; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + return h; +} + +//#define PATHLIB_USE_NODESCRAP +#define PATHLIB_NODEEXPIRE 0.05 +float pathlib_scraplist_cnt; +entity newnode() +{ + entity n; +#ifdef PATHLIB_USE_NODESCRAP + if(pathlib_scraplist_cnt) + { + n = findentity(world,owner,scraplist); + if(n) + { + --pathlib_scraplist_cnt; + ++pathlib_recycle_cnt; + return n; + } + else + pathlib_scraplist_cnt = 0; + } +#endif + ++pathlib_made_cnt; + n = spawn(); +#ifdef PATHLIB_NODEEXPIRE + n.think = SUB_Remove; + n.nextthink = time + PATHLIB_NODEEXPIRE; + return n; +} + +void dumpnode(entity n) +{ +#ifdef PATHLIB_USE_NODESCRAP + ++pathlib_scraplist_cnt; + + n.path_next = world; + n.path_prev = world; + n.is_path_node = FALSE; + n.owner = scraplist; +#else + //n.is_path_node = FALSE; + n.think = SUB_Remove; + n.nextthink = time; +#endif +} + +entity pathlib_mknode(vector where,entity parent) +{ + entity node; + + node = newnode(); + node.is_path_node = TRUE; + node.owner = openlist; + node.path_prev = parent; + + setorigin(node, where); + + ++pathlib_open_cnt; + + node.medium = pointcontents(where); + + return node; +} + +var float pathlib_expandnode(entity node, vector start, vector goal); +float pathlib_expandnode_star(entity node, vector start, vector goal); +float pathlib_expandnode_box(entity node, vector start, vector goal); + +var float pathlib_makenode(entity parent,vector start, vector to, vector goal,float cost); +float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost) +{ + entity node; + float h,g,f,doedge; + vector where; + + ++pathlib_searched_cnt; + + if(inwater(parent.origin)) + { + pathlib_expandnode = pathlib_expandnode_box; + pathlib_movenode = pathlib_swimnode; + } + else + { + if(inwater(to)) + { + pathlib_expandnode = pathlib_expandnode_box; + pathlib_movenode = pathlib_swimnode; + } + else + { + + pathlib_expandnode = pathlib_expandnode_star; + pathlib_movenode = pathlib_walknode; + doedge = 1; + } + } + + where = pathlib_movenode(parent.origin,to,0); + if not(pathlib_movenode_goodnode) + return 0; + + if(doedge) + if(edge_check(where,pathlib_edge_check_size)) + return 0; + + if(parent.path_prev) + pathlib_h_diagonal2sdp(parent.path_prev.origin,parent.origin,where,goal); + + h = pathlib_heuristic(where,goal); + g = pathlib_cost(parent,where,cost); + f = g + h; + + node = findradius(where,pathlib_gridsize * 0.75); + while(node) + { + if(node.is_path_node == TRUE) + { + ++pathlib_merge_cnt; + if(node.owner == openlist) + { + if(node.pathlib_node_g > g) + { + node.pathlib_node_h = h; + node.pathlib_node_g = g; + node.pathlib_node_f = f; + node.path_prev = parent; + } + + if not (best_open_node) + best_open_node = node; + else if(best_open_node.pathlib_node_f > node.pathlib_node_f) + best_open_node = node; + } + + return 1; + } + node = node.chain; + } + + node = pathlib_mknode(where,parent); + node.pathlib_node_h = h; + node.pathlib_node_g = g; + node.pathlib_node_f = f; + + if not (best_open_node) + best_open_node = node; + else if(best_open_node.pathlib_node_f > node.pathlib_node_f) + best_open_node = node; + + return 1; +} + +entity pathlib_getbestopen() +{ + entity node; + entity bestnode; + + if(best_open_node) + { + ++pathlib_bestcash_hits; + pathlib_bestcash_saved += pathlib_open_cnt; + + return best_open_node; + } + + node = findchainentity(owner,openlist); + if(!node) + return world; + + bestnode = node; + while(node) + { + ++pathlib_bestopen_seached; + if(node.pathlib_node_f < bestnode.pathlib_node_f) + bestnode = node; + + node = node.chain; + } + + return bestnode; +} + +void pathlib_close_node(entity node,vector goal) +{ + + if(node.owner == closedlist) + { + dprint("Pathlib: Tried to close a closed node!\n"); + return; + } + + if(node == best_open_node) + best_open_node = world; + + ++pathlib_closed_cnt; + --pathlib_open_cnt; + + node.owner = closedlist; + + if(vlen(node.origin - goal) <= pathlib_gridsize) + { + vector goalmove; + + goalmove = pathlib_walknode(node.origin,goal,1); + if(pathlib_movenode_goodnode) + { + goal_node = node; + pathlib_foundgoal = TRUE; + } + } +} + +float pathlib_expandnode_star(entity node, vector start, vector goal) +{ + vector point; + vector where; + float nodecnt; + + where = node.origin; + + v_forward = '1 0 0'; + v_right = '0 1 0'; + + // Forward + point = where + v_forward * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Back + point = where - v_forward * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Right + point = where + v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Left + point = where - v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Forward-right + point = where + v_forward * pathlib_gridsize + v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Forward-left + point = where + v_forward * pathlib_gridsize - v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Back-right + point = where - v_forward * pathlib_gridsize + v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Back-left + point = where - v_forward * pathlib_gridsize - v_right * pathlib_gridsize; + nodecnt += pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + return pathlib_open_cnt; +} + +float pathlib_expandnode_box(entity node, vector start, vector goal) +{ + vector v; + + for(v_z = node.origin_z - pathlib_gridsize; v_z <= node.origin_z + pathlib_gridsize; v_z += pathlib_gridsize) + for(v_y = node.origin_y - pathlib_gridsize; v_y <= node.origin_y + pathlib_gridsize; v_y += pathlib_gridsize) + for(v_x = node.origin_x - pathlib_gridsize; v_x <= node.origin_x + pathlib_gridsize; v_x += pathlib_gridsize) + { + if(vlen(v - node.origin)) + pathlib_makenode(node,start,v,goal,pathlib_movecost); + } + + return pathlib_open_cnt; +} + +void pathlib_cleanup() +{ + entity node; + + node = findfloat(world,is_path_node, TRUE); + while(node) + { + dumpnode(node); + node = findfloat(node,is_path_node, TRUE); + } + + if(openlist) + remove(openlist); + + if(closedlist) + remove(closedlist); + + best_open_node = world; + openlist = world; + closedlist = world; +} + +var float buildpath_nodefilter(vector n,vector c,vector p); +float buildpath_nodefilter_directional(vector n,vector c,vector p) +{ + vector d1,d2; + + d2 = normalize(p - c); + d1 = normalize(c - n); + + if(vlen(d1-d2) < 0.25) + return 1; + + return 0; +} + +float buildpath_nodefilter_moveskip(vector n,vector c,vector p) +{ + pathlib_walknode(p,n,1); + if(pathlib_movenode_goodnode) + return 1; + + return 0; +} + +entity path_build(entity next, vector where, entity prev, entity start) +{ + entity path; + + if(prev && next) + if(buildpath_nodefilter) + if(buildpath_nodefilter(next.origin,where,prev.origin)) + return next; + + + path = spawn(); + path.owner = start; + path.path_next = next; + + setorigin(path,where); + + if(!next) + path.classname = "path_end"; + else + { + if(!prev) + path.classname = "path_start"; + else + path.classname = "path_node"; + } + + return path; +} + +entity pathlib_astar(vector from,vector to) +{ + entity path, start, end, open, n, ln; + float ptime, ftime, ctime; + + ptime = gettime(GETTIME_REALTIME); + + pathlib_cleanup(); + + // Select water<->land capable node make/link + pathlib_makenode = pathlib_makenode_adaptive; + // Select XYZ cost estimate + pathlib_heuristic = pathlib_h_diagonal3; + // Select distance + waterfactor cost + pathlib_cost = pathlib_g_euclidean_water; + // Select star expander + pathlib_expandnode = pathlib_expandnode_star; + // Select walk simulation movement test + pathlib_movenode = pathlib_walknode; + // Filter final nodes by direction + buildpath_nodefilter = buildpath_nodefilter_directional; + + // If the start is in water we need diffrent settings + if(inwater(from)) + { + // Select volumetric node expaner + pathlib_expandnode = pathlib_expandnode_box; + + // Water movement test + pathlib_movenode = pathlib_swimnode; + } + + if not(openlist) + openlist = spawn(); + + if not(closedlist) + closedlist = spawn(); + + if not(scraplist) + scraplist = spawn(); + + pathlib_closed_cnt = 0; + pathlib_open_cnt = 0; + pathlib_made_cnt = 0; + pathlib_merge_cnt = 0; + pathlib_searched_cnt = 0; + pathlib_bestopen_seached = 0; + pathlib_bestcash_hits = 0; + pathlib_bestcash_saved = 0; + pathlib_recycle_cnt = 0; + + pathlib_gridsize = 128; + pathlib_movecost = pathlib_gridsize; + pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize)); + pathlib_movecost_waterfactor = 1.1; + pathlib_foundgoal = 0; + + walknode_boxmax = self.maxs * 1.5; + walknode_boxmin = self.mins * 1.5; + + pathlib_edge_check_size = (vlen(walknode_boxmin - walknode_boxmax) * 0.5); + + walknode_boxup = '0 0 2' * self.maxs_z; + walknode_stepsize = 32; + walknode_stepup = '0 0 1' * walknode_stepsize; + walknode_maxdrop = '0 0 3' * walknode_stepsize; + + from_x = fsnap(from_x,pathlib_gridsize); + from_y = fsnap(from_y,pathlib_gridsize); + + to_x = fsnap(to_x,pathlib_gridsize); + to_y = fsnap(to_y,pathlib_gridsize); + + dprint("AStar init. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n"); + path = pathlib_mknode(from,world); + pathlib_close_node(path,to); + if(pathlib_foundgoal) + { + dprint("AStar: Goal found on first node!\n"); + + open = spawn(); + open.owner = open; + open.classname = "path_end"; + setorigin(open,path.origin); + + pathlib_cleanup(); + + return open; + } + + if(pathlib_expandnode(path,from,to) <= 0) + { + dprint("AStar path fail.\n"); + pathlib_cleanup(); + + return world; + } + + best_open_node = pathlib_getbestopen(); + n = best_open_node; + pathlib_close_node(best_open_node,to); + if(inwater(n.origin)) + pathlib_expandnode_box(n,from,to); + else + pathlib_expandnode_star(n,from,to); + + while(pathlib_open_cnt) + { + best_open_node = pathlib_getbestopen(); + n = best_open_node; + pathlib_close_node(best_open_node,to); + + if(inwater(n.origin)) + pathlib_expandnode_box(n,from,to); + else + pathlib_expandnode(n,from,to); + + if(pathlib_foundgoal) + { + dprint("Target found. Rebuilding and filtering path...\n"); + ftime = gettime(GETTIME_REALTIME); + ptime = ftime - ptime; + + start = path_build(world,path.origin,world,world); + end = path_build(world,goal_node.origin,world,start); + ln = end; + + open = goal_node; + for(open = goal_node; open.path_prev != path; open = open.path_prev) + { + n = path_build(ln,open.origin,open.path_prev,start); + ln.path_prev = n; + ln = n; + } + start.path_next = n; + n.path_prev = start; + ftime = gettime(GETTIME_REALTIME) - ftime; + + ctime = gettime(GETTIME_REALTIME); + pathlib_cleanup(); + ctime = gettime(GETTIME_REALTIME) - ctime; + + +#ifdef DEBUGPATHING + pathlib_showpath2(start); + + dprint("Time used - pathfinding: ", ftos(ptime),"\n"); + dprint("Time used - rebuild & filter: ", ftos(ftime),"\n"); + dprint("Time used - cleanup: ", ftos(ctime),"\n"); + dprint("Time used - total: ", ftos(ptime + ftime + ctime),"\n"); + dprint("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_ticrate)),"\n\n"); + dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); + dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); + dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); + dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); + dprint("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n"); + + if(pathlib_recycle_cnt) + dprint("Nodes - make/reuse: ", ftos(pathlib_made_cnt / pathlib_recycle_cnt),"\n"); + if(pathlib_recycle_cnt) + dprint("Nodes - reused: ", ftos(pathlib_recycle_cnt),"\n"); + + dprint("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n"); + dprint("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n"); + dprint("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n"); + dprint("AStar done. ", ftos(pathlib_scraplist_cnt), " nodes on scrap\n\n"); +#endif + return start; + } + } + + dprint("A* Faild to find a path! Try a smaller gridsize.\n"); + + pathlib_cleanup(); + + return world; +} + + + diff --git a/data/qcsrc/server/pathlib/costs.qc b/data/qcsrc/server/pathlib/costs.qc index e8ed6b2a5..86f32e218 100644 --- a/data/qcsrc/server/pathlib/costs.qc +++ b/data/qcsrc/server/pathlib/costs.qc @@ -1,144 +1,144 @@ -float pathlib_g_static(entity parent,vector to, float static_cost) -{ - return parent.pathlib_node_g + static_cost; -} - -float pathlib_g_static_water(entity parent,vector to, float static_cost) -{ - if(inwater(to)) - return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; - else - return parent.pathlib_node_g + static_cost; -} - -float pathlib_g_euclidean(entity parent,vector to, float static_cost) -{ - return parent.pathlib_node_g + vlen(parent.origin - to); -} - -float pathlib_g_euclidean_water(entity parent,vector to, float static_cost) -{ - if(inwater(to)) - return parent.pathlib_node_g + vlen(parent.origin - to) * pathlib_movecost_waterfactor; - else - return parent.pathlib_node_g + vlen(parent.origin - to); -} - - -/** - Manhattan Menas we expect to move up,down left or right - No diagonal moves espected. (like moving bewteen city blocks) -**/ -float pathlib_h_manhattan(vector a,vector b) -{ - //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y)) - - float h; - h = fabs(a_x - b_x); - h += fabs(a_y - b_y); - h *= pathlib_gridsize; - - return h; -} - -/** - This heuristic consider both stright and disagonal moves - to have teh same cost. -**/ -float pathlib_h_diagonal(vector a,vector b) -{ - //h(n) = D * max(abs(n.x-goal.x), abs(n.y-goal.y)) - float h,x,y; - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - h = pathlib_movecost * max(x,y); - - return h; -} - -/** - This heuristic only considers the stright line distance. - Will usualy mean a lower H then G meaning A* Will speand more - and run slower. -**/ -float pathlib_h_euclidean(vector a,vector b) -{ - return vlen(a - b); -} - -/** - This heuristic consider both stright and disagonal moves, - But has a separate cost for diagonal moves. -**/ -float pathlib_h_diagonal2(vector a,vector b) -{ - float h_diag,h_str,h,x,y; - - /* - h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) - h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) - h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) - */ - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - - h_diag = min(x,y); - h_str = x + y; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - return h; -} - -/** - This heuristic consider both stright and disagonal moves, - But has a separate cost for diagonal moves. -**/ -float pathlib_h_diagonal2sdp(vector preprev,vector prev,vector point,vector end) -{ - float h_diag,h_str,h,x,y,z; - - //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) - //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) - //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) - - x = fabs(point_x - end_x); - y = fabs(point_y - end_y); - z = fabs(point_z - end_z); - - h_diag = min3(x,y,z); - h_str = x + y + z; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - float m; - vector d1,d2; - - d1 = normalize(preprev - point); - d2 = normalize(prev - point); - m = vlen(d1-d2); - - return h * m; -} - - -float pathlib_h_diagonal3(vector a,vector b) -{ - float h_diag,h_str,h,x,y,z; - - x = fabs(a_x - b_x); - y = fabs(a_y - b_y); - z = fabs(a_z - b_z); - - h_diag = min3(x,y,z); - h_str = x + y + z; - - h = pathlib_movecost_diag * h_diag; - h += pathlib_movecost * (h_str - 2 * h_diag); - - return h; -} +float pathlib_g_static(entity parent,vector to, float static_cost) +{ + return parent.pathlib_node_g + static_cost; +} + +float pathlib_g_static_water(entity parent,vector to, float static_cost) +{ + if(inwater(to)) + return parent.pathlib_node_g + static_cost * pathlib_movecost_waterfactor; + else + return parent.pathlib_node_g + static_cost; +} + +float pathlib_g_euclidean(entity parent,vector to, float static_cost) +{ + return parent.pathlib_node_g + vlen(parent.origin - to); +} + +float pathlib_g_euclidean_water(entity parent,vector to, float static_cost) +{ + if(inwater(to)) + return parent.pathlib_node_g + vlen(parent.origin - to) * pathlib_movecost_waterfactor; + else + return parent.pathlib_node_g + vlen(parent.origin - to); +} + + +/** + Manhattan Menas we expect to move up,down left or right + No diagonal moves espected. (like moving bewteen city blocks) +**/ +float pathlib_h_manhattan(vector a,vector b) +{ + //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y)) + + float h; + h = fabs(a_x - b_x); + h += fabs(a_y - b_y); + h *= pathlib_gridsize; + + return h; +} + +/** + This heuristic consider both stright and disagonal moves + to have teh same cost. +**/ +float pathlib_h_diagonal(vector a,vector b) +{ + //h(n) = D * max(abs(n.x-goal.x), abs(n.y-goal.y)) + float h,x,y; + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + h = pathlib_movecost * max(x,y); + + return h; +} + +/** + This heuristic only considers the stright line distance. + Will usualy mean a lower H then G meaning A* Will speand more + and run slower. +**/ +float pathlib_h_euclidean(vector a,vector b) +{ + return vlen(a - b); +} + +/** + This heuristic consider both stright and disagonal moves, + But has a separate cost for diagonal moves. +**/ +float pathlib_h_diagonal2(vector a,vector b) +{ + float h_diag,h_str,h,x,y; + + /* + h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) + h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) + h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) + */ + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + + h_diag = min(x,y); + h_str = x + y; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + return h; +} + +/** + This heuristic consider both stright and disagonal moves, + But has a separate cost for diagonal moves. +**/ +float pathlib_h_diagonal2sdp(vector preprev,vector prev,vector point,vector end) +{ + float h_diag,h_str,h,x,y,z; + + //h_diagonal(n) = min(abs(n.x-goal.x), abs(n.y-goal.y)) + //h_straight(n) = (abs(n.x-goal.x) + abs(n.y-goal.y)) + //h(n) = D2 * h_diagonal(n) + D * (h_straight(n) - 2*h_diagonal(n))) + + x = fabs(point_x - end_x); + y = fabs(point_y - end_y); + z = fabs(point_z - end_z); + + h_diag = min3(x,y,z); + h_str = x + y + z; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + float m; + vector d1,d2; + + d1 = normalize(preprev - point); + d2 = normalize(prev - point); + m = vlen(d1-d2); + + return h * m; +} + + +float pathlib_h_diagonal3(vector a,vector b) +{ + float h_diag,h_str,h,x,y,z; + + x = fabs(a_x - b_x); + y = fabs(a_y - b_y); + z = fabs(a_z - b_z); + + h_diag = min3(x,y,z); + h_str = x + y + z; + + h = pathlib_movecost_diag * h_diag; + h += pathlib_movecost * (h_str - 2 * h_diag); + + return h; +} diff --git a/data/qcsrc/server/pathlib/debug.qc b/data/qcsrc/server/pathlib/debug.qc index 7cd4dac0d..ac6249f99 100644 --- a/data/qcsrc/server/pathlib/debug.qc +++ b/data/qcsrc/server/pathlib/debug.qc @@ -1,117 +1,117 @@ - -void mark_error(vector where,float lifetime); -void mark_info(vector where,float lifetime); -entity mark_misc(vector where,float lifetime); - - -void pathlib_showpath(entity start) -{ - entity e; - e = start; - while(e.path_next) - { - te_lightning1(e,e.origin,e.path_next.origin); - e = e.path_next; - } -} - -void path_dbg_think() -{ - pathlib_showpath(self); - self.nextthink = time + 1; -} - -void __showpath2_think() -{ - mark_info(self.origin,1); - if(self.path_next) - { - self.path_next.think = __showpath2_think; - self.path_next.nextthink = time + 0.15; - } - else - { - self.owner.think = __showpath2_think; - self.owner.nextthink = time + 0.15; - } -} - -void pathlib_showpath2(entity path) -{ - path.think = __showpath2_think; - path.nextthink = time; -} - - -void pathlib_showsquare2(entity node ,vector ncolor,float align) -{ - - node.alpha = 0.25; - node.scale = pathlib_gridsize / 512.001; - node.solid = SOLID_NOT; - - setmodel(node,"models/pathlib/square.md3"); - setorigin(node,node.origin); - node.colormod = ncolor; - - if(align) - { - traceline(node.origin + '0 0 32',node.origin - '0 0 128',MOVE_WORLDONLY,node); - node.angles = vectoangles(trace_plane_normal); - node.angles_x -= 90; - } -} - -void pathlib_showsquare(vector where,float goodsquare,float lifetime) -{ - entity s; - - if(!lifetime) - lifetime = time + 30; - else - lifetime += time; - - s = spawn(); - s.alpha = 0.25; - s.think = SUB_Remove; - s.nextthink = lifetime; - s.scale = pathlib_gridsize / 512.001; - s.solid = SOLID_NOT; - - if(goodsquare) - setmodel(s,"models/pathlib/goodsquare.md3"); - else - setmodel(s,"models/pathlib/badsquare.md3"); - - - - traceline(where + '0 0 32',where - '0 0 128',MOVE_WORLDONLY,s); - - s.angles = vectoangles(trace_plane_normal); - s.angles_x -= 90; - setorigin(s,where); -} - -void pathlib_showedge(vector where,float lifetime,float rot) -{ - entity e; - - if(!lifetime) - lifetime = time + 30; - else - lifetime += time; - - e = spawn(); - e.alpha = 0.25; - e.think = SUB_Remove; - e.nextthink = lifetime; - e.scale = pathlib_gridsize / 512; - e.solid = SOLID_NOT; - setorigin(e,where); - setmodel(e,"models/pathlib/edge.md3"); - //traceline(where + '0 0 32',where - '0 0 128',MOVE_WORLDONLY,e); - //e.angles = vectoangles(trace_plane_normal); - e.angles_y = rot; - //e.angles_x += 90; - -} + +void mark_error(vector where,float lifetime); +void mark_info(vector where,float lifetime); +entity mark_misc(vector where,float lifetime); + + +void pathlib_showpath(entity start) +{ + entity e; + e = start; + while(e.path_next) + { + te_lightning1(e,e.origin,e.path_next.origin); + e = e.path_next; + } +} + +void path_dbg_think() +{ + pathlib_showpath(self); + self.nextthink = time + 1; +} + +void __showpath2_think() +{ + mark_info(self.origin,1); + if(self.path_next) + { + self.path_next.think = __showpath2_think; + self.path_next.nextthink = time + 0.15; + } + else + { + self.owner.think = __showpath2_think; + self.owner.nextthink = time + 0.15; + } +} + +void pathlib_showpath2(entity path) +{ + path.think = __showpath2_think; + path.nextthink = time; +} + + +void pathlib_showsquare2(entity node ,vector ncolor,float align) +{ + + node.alpha = 0.25; + node.scale = pathlib_gridsize / 512.001; + node.solid = SOLID_NOT; + + setmodel(node,"models/pathlib/square.md3"); + setorigin(node,node.origin); + node.colormod = ncolor; + + if(align) + { + traceline(node.origin + '0 0 32',node.origin - '0 0 128',MOVE_WORLDONLY,node); + node.angles = vectoangles(trace_plane_normal); + node.angles_x -= 90; + } +} + +void pathlib_showsquare(vector where,float goodsquare,float lifetime) +{ + entity s; + + if(!lifetime) + lifetime = time + 30; + else + lifetime += time; + + s = spawn(); + s.alpha = 0.25; + s.think = SUB_Remove; + s.nextthink = lifetime; + s.scale = pathlib_gridsize / 512.001; + s.solid = SOLID_NOT; + + if(goodsquare) + setmodel(s,"models/pathlib/goodsquare.md3"); + else + setmodel(s,"models/pathlib/badsquare.md3"); + + + + traceline(where + '0 0 32',where - '0 0 128',MOVE_WORLDONLY,s); + + s.angles = vectoangles(trace_plane_normal); + s.angles_x -= 90; + setorigin(s,where); +} + +void pathlib_showedge(vector where,float lifetime,float rot) +{ + entity e; + + if(!lifetime) + lifetime = time + 30; + else + lifetime += time; + + e = spawn(); + e.alpha = 0.25; + e.think = SUB_Remove; + e.nextthink = lifetime; + e.scale = pathlib_gridsize / 512; + e.solid = SOLID_NOT; + setorigin(e,where); + setmodel(e,"models/pathlib/edge.md3"); + //traceline(where + '0 0 32',where - '0 0 128',MOVE_WORLDONLY,e); + //e.angles = vectoangles(trace_plane_normal); + e.angles_y = rot; + //e.angles_x += 90; + +} diff --git a/data/qcsrc/server/pathlib/expandnode.qc b/data/qcsrc/server/pathlib/expandnode.qc index b4fa1b17f..81c0cd1e7 100644 --- a/data/qcsrc/server/pathlib/expandnode.qc +++ b/data/qcsrc/server/pathlib/expandnode.qc @@ -1,196 +1,196 @@ -vector plib_points2[8]; -vector plib_points[8]; -float plib_fvals[8]; - -float pathlib_expandnode_starf(entity node, vector start, vector goal) -{ - vector where,f,r,t; - float i,fc,fc2,c; - entity nap; - - where = node.origin; - - f = PLIB_FORWARD * pathlib_gridsize; - r = PLIB_RIGHT * pathlib_gridsize; - - // Forward - plib_points[0] = where + f; - - // Back - plib_points[1] = where - f; - - // Right - plib_points[2] = where + r; - - // Left - plib_points[3] = where - r; - - // Forward-right - plib_points[4] = where + f + r; - - // Forward-left - plib_points[5] = where + f - r; - - // Back-right - plib_points[6] = where - f + r; - - // Back-left - plib_points[7] = where - f - r; - - for(i=0;i < 8; ++i) - { - t = plib_points[i]; - fc = pathlib_heuristic(t,goal) + pathlib_cost(node,t,pathlib_gridsize); - plib_fvals[i] = fc; - - } - - fc = plib_fvals[0]; - plib_points2[0] = plib_points[0]; - vector bp; - bp = plib_points[0]; - for(i = 0; i < 8; ++i) - { - c = 0; - nap = pathlib_nodeatpoint(plib_points[i]); - if(nap) - if(nap.owner == openlist) - c = 1; - else - c = 1; - - if(c) - if(plib_fvals[i] < fc) - { - bp = plib_points[i]; - fc = plib_fvals[i]; - plib_points2[fc2] = plib_points[i]; - ++fc2; - } - - /* - nap = pathlib_nodeatpoint(plib_points[i]); - if(nap) - if not nap.owner == closedlist) - { - } - */ - } - - pathlib_makenode(node,start,bp,goal,pathlib_gridsize); - - for(i = 0; i < 3; ++i) - { - pathlib_makenode(node,start,plib_points2[i],goal,pathlib_gridsize); - } - - return pathlib_open_cnt; -} - -float pathlib_expandnode_star(entity node, vector start, vector goal) -{ - vector point,where,f,r; - - where = node.origin; - - f = PLIB_FORWARD * pathlib_gridsize; - r = PLIB_RIGHT * pathlib_gridsize; - - // Forward - point = where + f; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Back - point = where - f; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Right - point = where + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Left - point = where - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Forward-right - point = where + f + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Forward-left - point = where + f - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Back-right - point = where - f + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - // Back-left - point = where - f - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); - - return pathlib_open_cnt; -} - -float pathlib_expandnode_octagon(entity node, vector start, vector goal) -{ - vector point,where,f,r; - - where = node.origin; - - f = PLIB_FORWARD * pathlib_gridsize; - r = PLIB_RIGHT * pathlib_gridsize; - - // Forward - point = where + f; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Back - point = where - f; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Right - point = where + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Left - point = where - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - f = PLIB_FORWARD * pathlib_gridsize * 0.5; - r = PLIB_RIGHT * pathlib_gridsize * 0.5; - - // Forward-right - point = where + f + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - - // Forward-left - point = where + f - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - - // Back-right - point = where - f + r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - // Back-left - point = where - f - r; - pathlib_makenode(node,start,point,goal,pathlib_movecost); - - return pathlib_open_cnt; -} - -float pathlib_expandnode_box(entity node, vector start, vector goal) -{ - vector v; - - for(v_z = node.origin_z - pathlib_gridsize; v_z <= node.origin_z + pathlib_gridsize; v_z += pathlib_gridsize) - for(v_y = node.origin_y - pathlib_gridsize; v_y <= node.origin_y + pathlib_gridsize; v_y += pathlib_gridsize) - for(v_x = node.origin_x - pathlib_gridsize; v_x <= node.origin_x + pathlib_gridsize; v_x += pathlib_gridsize) - { - //if(vlen(v - node.origin)) - pathlib_makenode(node,start,v,goal,pathlib_movecost); - } - - return pathlib_open_cnt; -} +vector plib_points2[8]; +vector plib_points[8]; +float plib_fvals[8]; + +float pathlib_expandnode_starf(entity node, vector start, vector goal) +{ + vector where,f,r,t; + float i,fc,fc2,c; + entity nap; + + where = node.origin; + + f = PLIB_FORWARD * pathlib_gridsize; + r = PLIB_RIGHT * pathlib_gridsize; + + // Forward + plib_points[0] = where + f; + + // Back + plib_points[1] = where - f; + + // Right + plib_points[2] = where + r; + + // Left + plib_points[3] = where - r; + + // Forward-right + plib_points[4] = where + f + r; + + // Forward-left + plib_points[5] = where + f - r; + + // Back-right + plib_points[6] = where - f + r; + + // Back-left + plib_points[7] = where - f - r; + + for(i=0;i < 8; ++i) + { + t = plib_points[i]; + fc = pathlib_heuristic(t,goal) + pathlib_cost(node,t,pathlib_gridsize); + plib_fvals[i] = fc; + + } + + fc = plib_fvals[0]; + plib_points2[0] = plib_points[0]; + vector bp; + bp = plib_points[0]; + for(i = 0; i < 8; ++i) + { + c = 0; + nap = pathlib_nodeatpoint(plib_points[i]); + if(nap) + if(nap.owner == openlist) + c = 1; + else + c = 1; + + if(c) + if(plib_fvals[i] < fc) + { + bp = plib_points[i]; + fc = plib_fvals[i]; + plib_points2[fc2] = plib_points[i]; + ++fc2; + } + + /* + nap = pathlib_nodeatpoint(plib_points[i]); + if(nap) + if not nap.owner == closedlist) + { + } + */ + } + + pathlib_makenode(node,start,bp,goal,pathlib_gridsize); + + for(i = 0; i < 3; ++i) + { + pathlib_makenode(node,start,plib_points2[i],goal,pathlib_gridsize); + } + + return pathlib_open_cnt; +} + +float pathlib_expandnode_star(entity node, vector start, vector goal) +{ + vector point,where,f,r; + + where = node.origin; + + f = PLIB_FORWARD * pathlib_gridsize; + r = PLIB_RIGHT * pathlib_gridsize; + + // Forward + point = where + f; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Back + point = where - f; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Right + point = where + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Left + point = where - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Forward-right + point = where + f + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Forward-left + point = where + f - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Back-right + point = where - f + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + // Back-left + point = where - f - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost_diag); + + return pathlib_open_cnt; +} + +float pathlib_expandnode_octagon(entity node, vector start, vector goal) +{ + vector point,where,f,r; + + where = node.origin; + + f = PLIB_FORWARD * pathlib_gridsize; + r = PLIB_RIGHT * pathlib_gridsize; + + // Forward + point = where + f; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Back + point = where - f; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Right + point = where + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Left + point = where - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + f = PLIB_FORWARD * pathlib_gridsize * 0.5; + r = PLIB_RIGHT * pathlib_gridsize * 0.5; + + // Forward-right + point = where + f + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + + // Forward-left + point = where + f - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + + // Back-right + point = where - f + r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + // Back-left + point = where - f - r; + pathlib_makenode(node,start,point,goal,pathlib_movecost); + + return pathlib_open_cnt; +} + +float pathlib_expandnode_box(entity node, vector start, vector goal) +{ + vector v; + + for(v_z = node.origin_z - pathlib_gridsize; v_z <= node.origin_z + pathlib_gridsize; v_z += pathlib_gridsize) + for(v_y = node.origin_y - pathlib_gridsize; v_y <= node.origin_y + pathlib_gridsize; v_y += pathlib_gridsize) + for(v_x = node.origin_x - pathlib_gridsize; v_x <= node.origin_x + pathlib_gridsize; v_x += pathlib_gridsize) + { + //if(vlen(v - node.origin)) + pathlib_makenode(node,start,v,goal,pathlib_movecost); + } + + return pathlib_open_cnt; +} diff --git a/data/qcsrc/server/pathlib/main.qc b/data/qcsrc/server/pathlib/main.qc index 1f99743e3..f6f854033 100644 --- a/data/qcsrc/server/pathlib/main.qc +++ b/data/qcsrc/server/pathlib/main.qc @@ -1,534 +1,534 @@ -void pathlib_deletepath(entity start) -{ - entity e; - - e = findchainentity(owner, start); - while(e) - { - e.think = SUB_Remove; - e.nextthink = time; - e = e.chain; - } -} - -//#define PATHLIB_NODEEXPIRE 0.05 -#define PATHLIB_NODEEXPIRE 20 - -void dumpnode(entity n) -{ - n.is_path_node = FALSE; - n.think = SUB_Remove; - n.nextthink = time; -} - -entity pathlib_mknode(vector where,entity parent) -{ - entity node; - - node = pathlib_nodeatpoint(where); - if(node) - { - mark_error(where,60); - return node; - } - - node = spawn(); - - node.think = SUB_Remove; - node.nextthink = time + PATHLIB_NODEEXPIRE; - node.is_path_node = TRUE; - node.owner = openlist; - node.path_prev = parent; - - setmodel(node,"models/pathlib/square.md3"); - setsize(node,'0 0 0','0 0 0'); - node.colormod = randomvec() * 2; - node.alpha = 0.25; - node.scale = pathlib_gridsize / 512.001; - - //pathlib_showsquare2(node,'1 1 1',0);//(node.medium & CONTENT_EMPTY)); - setorigin(node, where); - node.medium = pointcontents(where); - - mark_info(where,60); - //pathlib_showsquare(where,1,30); - - - ++pathlib_made_cnt; - ++pathlib_open_cnt; - - return node; -} - -float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost) -{ - entity node; - float h,g,f,doedge; - vector where; - - ++pathlib_searched_cnt; - - if(inwater(parent.origin)) - { - pathlib_expandnode = pathlib_expandnode_box; - pathlib_movenode = pathlib_swimnode; - } - else - { - if(inwater(to)) - { - pathlib_expandnode = pathlib_expandnode_box; - pathlib_movenode = pathlib_walknode; - } - else - { - //if(edge_check(parent.origin)) - // return 0; - - pathlib_expandnode = pathlib_expandnode_star; - pathlib_movenode = pathlib_walknode; - doedge = 1; - } - } - - node = pathlib_nodeatpoint(to); - if(node) - { - ++pathlib_merge_cnt; - - if(node.owner == openlist) - { - h = pathlib_heuristic(node.origin,goal); - g = pathlib_cost(parent,node.origin,cost); - f = g + h; - - if(node.pathlib_node_g > g) - { - node.pathlib_node_h = h; - node.pathlib_node_g = g; - node.pathlib_node_f = f; - - node.path_prev = parent; - } - - if not (best_open_node) - best_open_node = node; - else if(best_open_node.pathlib_node_f > node.pathlib_node_f) - best_open_node = node; - } - - return 1; - } - - where = pathlib_movenode(parent.origin,to,0); - if not(pathlib_movenode_goodnode) - return 0; - - if(pathlib_nodeatpoint(where)) - { - dprint("NAP WHERE :",vtos(where),"\n"); - dprint("not NAP TO:",vtos(to),"\n"); - dprint("NAP-NNAP:",ftos(vlen(to-where)),"\n\n"); - return 0; - } - - if(doedge) - if not (tile_check(where)) - return 0; - - h = pathlib_heuristic(where,goal); - g = pathlib_cost(parent,where,cost); - f = g + h; - - - /* - node = findradius(where,pathlib_gridsize * 0.5); - while(node) - { - if(node.is_path_node == TRUE) - { - ++pathlib_merge_cnt; - if(node.owner == openlist) - { - if(node.pathlib_node_g > g) - { - //pathlib_movenode(where,node.origin,0); - //if(pathlib_movenode_goodnode) - //{ - //mark_error(node.origin + '0 0 128',30); - node.pathlib_node_h = h; - node.pathlib_node_g = g; - node.pathlib_node_f = f; - node.path_prev = parent; - //} - } - - if not (best_open_node) - best_open_node = node; - else if(best_open_node.pathlib_node_f > node.pathlib_node_f) - best_open_node = node; - } - - return 1; - } - node = node.chain; - } - */ - - node = pathlib_mknode(where,parent); - node.pathlib_node_h = h; - node.pathlib_node_g = g; - node.pathlib_node_f = f; - - if not (best_open_node) - best_open_node = node; - else if(best_open_node.pathlib_node_f > node.pathlib_node_f) - best_open_node = node; - - return 1; -} - -entity pathlib_getbestopen() -{ - entity node; - entity bestnode; - - if(best_open_node) - { - ++pathlib_bestcash_hits; - pathlib_bestcash_saved += pathlib_open_cnt; - - return best_open_node; - } - - node = findchainentity(owner,openlist); - if(!node) - return world; - - bestnode = node; - while(node) - { - ++pathlib_bestopen_seached; - if(node.pathlib_node_f < bestnode.pathlib_node_f) - bestnode = node; - - node = node.chain; - } - - return bestnode; -} - -void pathlib_close_node(entity node,vector goal) -{ - - if(node.owner == closedlist) - { - dprint("Pathlib: Tried to close a closed node!\n"); - return; - } - - if(node == best_open_node) - best_open_node = world; - - ++pathlib_closed_cnt; - --pathlib_open_cnt; - - node.owner = closedlist; - - if(vlen(node.origin - goal) <= pathlib_gridsize) - { - vector goalmove; - - goalmove = pathlib_walknode(node.origin,goal,1); - if(pathlib_movenode_goodnode) - { - goal_node = node; - pathlib_foundgoal = TRUE; - } - } -} - -void pathlib_cleanup() -{ - best_open_node = world; - - //return; - - entity node; - - node = findfloat(world,is_path_node, TRUE); - while(node) - { - /* - node.owner = openlist; - node.pathlib_node_g = 0; - node.pathlib_node_h = 0; - node.pathlib_node_f = 0; - node.path_prev = world; - */ - - dumpnode(node); - node = findfloat(node,is_path_node, TRUE); - } - - if(openlist) - remove(openlist); - - if(closedlist) - remove(closedlist); - - openlist = world; - closedlist = world; - -} - -float Cosine_Interpolate(float a, float b, float x) -{ - float ft,f; - - ft = x * 3.1415927; - f = (1 - cos(ft)) * 0.5; - - return a*(1-f) + b*f; -} - -float buildpath_nodefilter_directional(vector n,vector c,vector p) -{ - vector d1,d2; - - d2 = normalize(p - c); - d1 = normalize(c - n); - - if(vlen(d1-d2) < 0.25) - { - //mark_error(c,30); - return 1; - } - //mark_info(c,30); - return 0; -} - -float buildpath_nodefilter_moveskip(vector n,vector c,vector p) -{ - pathlib_walknode(p,n,1); - if(pathlib_movenode_goodnode) - return 1; - - return 0; -} - -entity path_build(entity next, vector where, entity prev, entity start) -{ - entity path; - - if(prev && next) - if(buildpath_nodefilter) - if(buildpath_nodefilter(next.origin,where,prev.origin)) - return next; - - - path = spawn(); - path.owner = start; - path.path_next = next; - - setorigin(path,where); - - if(!next) - path.classname = "path_end"; - else - { - if(!prev) - path.classname = "path_start"; - else - path.classname = "path_node"; - } - - return path; -} - -entity pathlib_astar(vector from,vector to) -{ - entity path, start, end, open, n, ln; - float ptime, ftime, ctime; - - ptime = gettime(GETTIME_REALTIME); - pathlib_starttime = ptime; - - pathlib_cleanup(); - - // Select water<->land capable node make/link - pathlib_makenode = pathlib_makenode_adaptive; - // Select XYZ cost estimate - //pathlib_heuristic = pathlib_h_diagonal3; - pathlib_heuristic = pathlib_h_diagonal; - // Select distance + waterfactor cost - pathlib_cost = pathlib_g_euclidean_water; - // Select star expander - pathlib_expandnode = pathlib_expandnode_star; - // Select walk simulation movement test - pathlib_movenode = pathlib_walknode; - // Filter final nodes by direction - buildpath_nodefilter = buildpath_nodefilter_directional; - // Filter tiles with cross pattern - tile_check = tile_check_cross; - - // If the start is in water we need diffrent settings - if(inwater(from)) - { - // Select volumetric node expaner - pathlib_expandnode = pathlib_expandnode_box; - - // Water movement test - pathlib_movenode = pathlib_swimnode; - } - - if not(openlist) - openlist = spawn(); - - if not(closedlist) - closedlist = spawn(); - - pathlib_closed_cnt = 0; - pathlib_open_cnt = 0; - pathlib_made_cnt = 0; - pathlib_merge_cnt = 0; - pathlib_searched_cnt = 0; - pathlib_bestopen_seached = 0; - pathlib_bestcash_hits = 0; - pathlib_bestcash_saved = 0; - - pathlib_gridsize = 128; - pathlib_movecost = pathlib_gridsize; - pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize)); - pathlib_movecost_waterfactor = 1; - pathlib_foundgoal = 0; - - movenode_boxmax = self.maxs * 1.25; - movenode_boxmin = self.mins * 1.25; - - movenode_stepsize = 32; - - tile_check_size = 65; - tile_check_up = '0 0 128'; - tile_check_down = '0 0 128'; - - movenode_stepup = '0 0 36'; - movenode_maxdrop = '0 0 128'; - movenode_boxup = '0 0 72'; - - from_x = fsnap(from_x,pathlib_gridsize); - from_y = fsnap(from_y,pathlib_gridsize); - - to_x = fsnap(to_x,pathlib_gridsize); - to_y = fsnap(to_y,pathlib_gridsize); - - dprint("AStar init\n"); - path = pathlib_mknode(from,world); - pathlib_close_node(path,to); - if(pathlib_foundgoal) - { - dprint("AStar: Goal found on first node!\n"); - - open = spawn(); - open.owner = open; - open.classname = "path_end"; - setorigin(open,path.origin); - - pathlib_cleanup(); - - return open; - } - - if(pathlib_expandnode(path,from,to) <= 0) - { - dprint("AStar path fail.\n"); - pathlib_cleanup(); - - return world; - } - - best_open_node = pathlib_getbestopen(); - n = best_open_node; - pathlib_close_node(best_open_node,to); - if(inwater(n.origin)) - pathlib_expandnode_box(n,from,to); - else - pathlib_expandnode_star(n,from,to); - - while(pathlib_open_cnt) - { - if((gettime(GETTIME_REALTIME) - pathlib_starttime) > pathlib_maxtime) - { - dprint("Path took to long to compute!\n"); - dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); - dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); - dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); - dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); - - pathlib_cleanup(); - return world; - } - - best_open_node = pathlib_getbestopen(); - n = best_open_node; - pathlib_close_node(best_open_node,to); - - if(inwater(n.origin)) - pathlib_expandnode_box(n,from,to); - else - pathlib_expandnode(n,from,to); - - if(pathlib_foundgoal) - { - dprint("Target found. Rebuilding and filtering path...\n"); - ftime = gettime(GETTIME_REALTIME); - ptime = ftime - ptime; - - start = path_build(world,path.origin,world,world); - end = path_build(world,goal_node.origin,world,start); - ln = end; - - open = goal_node; - for(open = goal_node; open.path_prev != path; open = open.path_prev) - { - n = path_build(ln,open.origin,open.path_prev,start); - ln.path_prev = n; - ln = n; - } - start.path_next = n; - n.path_prev = start; - ftime = gettime(GETTIME_REALTIME) - ftime; - - ctime = gettime(GETTIME_REALTIME); - pathlib_cleanup(); - ctime = gettime(GETTIME_REALTIME) - ctime; - - -#ifdef DEBUGPATHING - pathlib_showpath2(start); - - dprint("Time used - pathfinding: ", ftos(ptime),"\n"); - dprint("Time used - rebuild & filter: ", ftos(ftime),"\n"); - dprint("Time used - cleanup: ", ftos(ctime),"\n"); - dprint("Time used - total: ", ftos(ptime + ftime + ctime),"\n"); - dprint("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_ticrate)),"\n\n"); - dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); - dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); - dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); - dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); - dprint("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n"); - dprint("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n"); - dprint("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n"); - dprint("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n"); - dprint("AStar done.\n"); -#endif - return start; - } - } - - dprint("A* Faild to find a path! Try a smaller gridsize.\n"); - - pathlib_cleanup(); - - return world; -} +void pathlib_deletepath(entity start) +{ + entity e; + + e = findchainentity(owner, start); + while(e) + { + e.think = SUB_Remove; + e.nextthink = time; + e = e.chain; + } +} + +//#define PATHLIB_NODEEXPIRE 0.05 +#define PATHLIB_NODEEXPIRE 20 + +void dumpnode(entity n) +{ + n.is_path_node = FALSE; + n.think = SUB_Remove; + n.nextthink = time; +} + +entity pathlib_mknode(vector where,entity parent) +{ + entity node; + + node = pathlib_nodeatpoint(where); + if(node) + { + mark_error(where,60); + return node; + } + + node = spawn(); + + node.think = SUB_Remove; + node.nextthink = time + PATHLIB_NODEEXPIRE; + node.is_path_node = TRUE; + node.owner = openlist; + node.path_prev = parent; + + setmodel(node,"models/pathlib/square.md3"); + setsize(node,'0 0 0','0 0 0'); + node.colormod = randomvec() * 2; + node.alpha = 0.25; + node.scale = pathlib_gridsize / 512.001; + + //pathlib_showsquare2(node,'1 1 1',0);//(node.medium & CONTENT_EMPTY)); + setorigin(node, where); + node.medium = pointcontents(where); + + mark_info(where,60); + //pathlib_showsquare(where,1,30); + + + ++pathlib_made_cnt; + ++pathlib_open_cnt; + + return node; +} + +float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector goal,float cost) +{ + entity node; + float h,g,f,doedge; + vector where; + + ++pathlib_searched_cnt; + + if(inwater(parent.origin)) + { + pathlib_expandnode = pathlib_expandnode_box; + pathlib_movenode = pathlib_swimnode; + } + else + { + if(inwater(to)) + { + pathlib_expandnode = pathlib_expandnode_box; + pathlib_movenode = pathlib_walknode; + } + else + { + //if(edge_check(parent.origin)) + // return 0; + + pathlib_expandnode = pathlib_expandnode_star; + pathlib_movenode = pathlib_walknode; + doedge = 1; + } + } + + node = pathlib_nodeatpoint(to); + if(node) + { + ++pathlib_merge_cnt; + + if(node.owner == openlist) + { + h = pathlib_heuristic(node.origin,goal); + g = pathlib_cost(parent,node.origin,cost); + f = g + h; + + if(node.pathlib_node_g > g) + { + node.pathlib_node_h = h; + node.pathlib_node_g = g; + node.pathlib_node_f = f; + + node.path_prev = parent; + } + + if not (best_open_node) + best_open_node = node; + else if(best_open_node.pathlib_node_f > node.pathlib_node_f) + best_open_node = node; + } + + return 1; + } + + where = pathlib_movenode(parent.origin,to,0); + if not(pathlib_movenode_goodnode) + return 0; + + if(pathlib_nodeatpoint(where)) + { + dprint("NAP WHERE :",vtos(where),"\n"); + dprint("not NAP TO:",vtos(to),"\n"); + dprint("NAP-NNAP:",ftos(vlen(to-where)),"\n\n"); + return 0; + } + + if(doedge) + if not (tile_check(where)) + return 0; + + h = pathlib_heuristic(where,goal); + g = pathlib_cost(parent,where,cost); + f = g + h; + + + /* + node = findradius(where,pathlib_gridsize * 0.5); + while(node) + { + if(node.is_path_node == TRUE) + { + ++pathlib_merge_cnt; + if(node.owner == openlist) + { + if(node.pathlib_node_g > g) + { + //pathlib_movenode(where,node.origin,0); + //if(pathlib_movenode_goodnode) + //{ + //mark_error(node.origin + '0 0 128',30); + node.pathlib_node_h = h; + node.pathlib_node_g = g; + node.pathlib_node_f = f; + node.path_prev = parent; + //} + } + + if not (best_open_node) + best_open_node = node; + else if(best_open_node.pathlib_node_f > node.pathlib_node_f) + best_open_node = node; + } + + return 1; + } + node = node.chain; + } + */ + + node = pathlib_mknode(where,parent); + node.pathlib_node_h = h; + node.pathlib_node_g = g; + node.pathlib_node_f = f; + + if not (best_open_node) + best_open_node = node; + else if(best_open_node.pathlib_node_f > node.pathlib_node_f) + best_open_node = node; + + return 1; +} + +entity pathlib_getbestopen() +{ + entity node; + entity bestnode; + + if(best_open_node) + { + ++pathlib_bestcash_hits; + pathlib_bestcash_saved += pathlib_open_cnt; + + return best_open_node; + } + + node = findchainentity(owner,openlist); + if(!node) + return world; + + bestnode = node; + while(node) + { + ++pathlib_bestopen_seached; + if(node.pathlib_node_f < bestnode.pathlib_node_f) + bestnode = node; + + node = node.chain; + } + + return bestnode; +} + +void pathlib_close_node(entity node,vector goal) +{ + + if(node.owner == closedlist) + { + dprint("Pathlib: Tried to close a closed node!\n"); + return; + } + + if(node == best_open_node) + best_open_node = world; + + ++pathlib_closed_cnt; + --pathlib_open_cnt; + + node.owner = closedlist; + + if(vlen(node.origin - goal) <= pathlib_gridsize) + { + vector goalmove; + + goalmove = pathlib_walknode(node.origin,goal,1); + if(pathlib_movenode_goodnode) + { + goal_node = node; + pathlib_foundgoal = TRUE; + } + } +} + +void pathlib_cleanup() +{ + best_open_node = world; + + //return; + + entity node; + + node = findfloat(world,is_path_node, TRUE); + while(node) + { + /* + node.owner = openlist; + node.pathlib_node_g = 0; + node.pathlib_node_h = 0; + node.pathlib_node_f = 0; + node.path_prev = world; + */ + + dumpnode(node); + node = findfloat(node,is_path_node, TRUE); + } + + if(openlist) + remove(openlist); + + if(closedlist) + remove(closedlist); + + openlist = world; + closedlist = world; + +} + +float Cosine_Interpolate(float a, float b, float x) +{ + float ft,f; + + ft = x * 3.1415927; + f = (1 - cos(ft)) * 0.5; + + return a*(1-f) + b*f; +} + +float buildpath_nodefilter_directional(vector n,vector c,vector p) +{ + vector d1,d2; + + d2 = normalize(p - c); + d1 = normalize(c - n); + + if(vlen(d1-d2) < 0.25) + { + //mark_error(c,30); + return 1; + } + //mark_info(c,30); + return 0; +} + +float buildpath_nodefilter_moveskip(vector n,vector c,vector p) +{ + pathlib_walknode(p,n,1); + if(pathlib_movenode_goodnode) + return 1; + + return 0; +} + +entity path_build(entity next, vector where, entity prev, entity start) +{ + entity path; + + if(prev && next) + if(buildpath_nodefilter) + if(buildpath_nodefilter(next.origin,where,prev.origin)) + return next; + + + path = spawn(); + path.owner = start; + path.path_next = next; + + setorigin(path,where); + + if(!next) + path.classname = "path_end"; + else + { + if(!prev) + path.classname = "path_start"; + else + path.classname = "path_node"; + } + + return path; +} + +entity pathlib_astar(vector from,vector to) +{ + entity path, start, end, open, n, ln; + float ptime, ftime, ctime; + + ptime = gettime(GETTIME_REALTIME); + pathlib_starttime = ptime; + + pathlib_cleanup(); + + // Select water<->land capable node make/link + pathlib_makenode = pathlib_makenode_adaptive; + // Select XYZ cost estimate + //pathlib_heuristic = pathlib_h_diagonal3; + pathlib_heuristic = pathlib_h_diagonal; + // Select distance + waterfactor cost + pathlib_cost = pathlib_g_euclidean_water; + // Select star expander + pathlib_expandnode = pathlib_expandnode_star; + // Select walk simulation movement test + pathlib_movenode = pathlib_walknode; + // Filter final nodes by direction + buildpath_nodefilter = buildpath_nodefilter_directional; + // Filter tiles with cross pattern + tile_check = tile_check_cross; + + // If the start is in water we need diffrent settings + if(inwater(from)) + { + // Select volumetric node expaner + pathlib_expandnode = pathlib_expandnode_box; + + // Water movement test + pathlib_movenode = pathlib_swimnode; + } + + if not(openlist) + openlist = spawn(); + + if not(closedlist) + closedlist = spawn(); + + pathlib_closed_cnt = 0; + pathlib_open_cnt = 0; + pathlib_made_cnt = 0; + pathlib_merge_cnt = 0; + pathlib_searched_cnt = 0; + pathlib_bestopen_seached = 0; + pathlib_bestcash_hits = 0; + pathlib_bestcash_saved = 0; + + pathlib_gridsize = 128; + pathlib_movecost = pathlib_gridsize; + pathlib_movecost_diag = vlen(('1 1 0' * pathlib_gridsize)); + pathlib_movecost_waterfactor = 1; + pathlib_foundgoal = 0; + + movenode_boxmax = self.maxs * 1.25; + movenode_boxmin = self.mins * 1.25; + + movenode_stepsize = 32; + + tile_check_size = 65; + tile_check_up = '0 0 128'; + tile_check_down = '0 0 128'; + + movenode_stepup = '0 0 36'; + movenode_maxdrop = '0 0 128'; + movenode_boxup = '0 0 72'; + + from_x = fsnap(from_x,pathlib_gridsize); + from_y = fsnap(from_y,pathlib_gridsize); + + to_x = fsnap(to_x,pathlib_gridsize); + to_y = fsnap(to_y,pathlib_gridsize); + + dprint("AStar init\n"); + path = pathlib_mknode(from,world); + pathlib_close_node(path,to); + if(pathlib_foundgoal) + { + dprint("AStar: Goal found on first node!\n"); + + open = spawn(); + open.owner = open; + open.classname = "path_end"; + setorigin(open,path.origin); + + pathlib_cleanup(); + + return open; + } + + if(pathlib_expandnode(path,from,to) <= 0) + { + dprint("AStar path fail.\n"); + pathlib_cleanup(); + + return world; + } + + best_open_node = pathlib_getbestopen(); + n = best_open_node; + pathlib_close_node(best_open_node,to); + if(inwater(n.origin)) + pathlib_expandnode_box(n,from,to); + else + pathlib_expandnode_star(n,from,to); + + while(pathlib_open_cnt) + { + if((gettime(GETTIME_REALTIME) - pathlib_starttime) > pathlib_maxtime) + { + dprint("Path took to long to compute!\n"); + dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); + dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); + dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); + dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); + + pathlib_cleanup(); + return world; + } + + best_open_node = pathlib_getbestopen(); + n = best_open_node; + pathlib_close_node(best_open_node,to); + + if(inwater(n.origin)) + pathlib_expandnode_box(n,from,to); + else + pathlib_expandnode(n,from,to); + + if(pathlib_foundgoal) + { + dprint("Target found. Rebuilding and filtering path...\n"); + ftime = gettime(GETTIME_REALTIME); + ptime = ftime - ptime; + + start = path_build(world,path.origin,world,world); + end = path_build(world,goal_node.origin,world,start); + ln = end; + + open = goal_node; + for(open = goal_node; open.path_prev != path; open = open.path_prev) + { + n = path_build(ln,open.origin,open.path_prev,start); + ln.path_prev = n; + ln = n; + } + start.path_next = n; + n.path_prev = start; + ftime = gettime(GETTIME_REALTIME) - ftime; + + ctime = gettime(GETTIME_REALTIME); + pathlib_cleanup(); + ctime = gettime(GETTIME_REALTIME) - ctime; + + +#ifdef DEBUGPATHING + pathlib_showpath2(start); + + dprint("Time used - pathfinding: ", ftos(ptime),"\n"); + dprint("Time used - rebuild & filter: ", ftos(ftime),"\n"); + dprint("Time used - cleanup: ", ftos(ctime),"\n"); + dprint("Time used - total: ", ftos(ptime + ftime + ctime),"\n"); + dprint("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_ticrate)),"\n\n"); + dprint("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); + dprint("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); + dprint("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); + dprint("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); + dprint("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n"); + dprint("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n"); + dprint("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n"); + dprint("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n"); + dprint("AStar done.\n"); +#endif + return start; + } + } + + dprint("A* Faild to find a path! Try a smaller gridsize.\n"); + + pathlib_cleanup(); + + return world; +} diff --git a/data/qcsrc/server/pathlib/movenode.qc b/data/qcsrc/server/pathlib/movenode.qc index 1aa1c665c..59973b045 100644 --- a/data/qcsrc/server/pathlib/movenode.qc +++ b/data/qcsrc/server/pathlib/movenode.qc @@ -1,126 +1,126 @@ -vector pathlib_wateroutnode(vector start,vector end) -{ - vector surface; - - pathlib_movenode_goodnode = 0; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - traceline(end + ('0 0 0.25' * pathlib_gridsize),end - ('0 0 1' * pathlib_gridsize),MOVE_WORLDONLY,self); - end = trace_endpos; - - if not(pointcontents(end - '0 0 1') == CONTENT_SOLID) - return end; - - for(surface = start ; surface_z < (end_z + 32); ++surface_z) - { - if(pointcontents(surface) == CONTENT_EMPTY) - break; - } - - if(pointcontents(surface + '0 0 1') != CONTENT_EMPTY) - return end; - - tracebox(start + '0 0 64', movenode_boxmin,movenode_boxmax, end + '0 0 64', MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - if(fabs(surface_z - end_z) > 32) - pathlib_movenode_goodnode = 0; - - return end; -} - -vector pathlib_swimnode(vector start,vector end) -{ - pathlib_movenode_goodnode = 0; - - if(pointcontents(start) != CONTENT_WATER) - return end; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - if(pointcontents(end) == CONTENT_EMPTY) - return pathlib_wateroutnode( start, end); - - tracebox(start, movenode_boxmin,movenode_boxmax, end, MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - return end; -} - -vector pathlib_flynode(vector start,vector end) -{ - pathlib_movenode_goodnode = 0; - - end_x = fsnap(end_x, pathlib_gridsize); - end_y = fsnap(end_y, pathlib_gridsize); - - tracebox(start, movenode_boxmin,movenode_boxmax, end, MOVE_WORLDONLY, self); - if(trace_fraction == 1) - pathlib_movenode_goodnode = 1; - - return end; -} - -vector pathlib_walknode(vector start,vector end,float doedge) -{ - vector direction,point,last_point,s,e; - float steps, distance, i; - - pathlib_movenode_goodnode = 0; - - end_x = fsnap(end_x,pathlib_gridsize); - end_y = fsnap(end_y,pathlib_gridsize); - start_x = fsnap(start_x,pathlib_gridsize); - start_y = fsnap(start_y,pathlib_gridsize); - - // Find the floor - traceline(start + movenode_stepup, start - movenode_maxdrop,MOVE_WORLDONLY,self); - if(trace_fraction == 1.0) - return trace_endpos; - - start = trace_endpos; - - // Find the direcion, without Z - s = start; e = end; - //e_z = 0; s_z = 0; - direction = normalize(e - s); - - distance = vlen(start - end); - steps = rint(distance / movenode_stepsize); - - last_point = start; - for(i = 1; i < steps; ++i) - { - point = last_point + (direction * movenode_stepsize); - traceline(point + movenode_stepup,point - movenode_maxdrop,MOVE_WORLDONLY,self); - if(trace_fraction == 1.0) - return trace_endpos; - - last_point = trace_endpos; - } - - point = last_point + (direction * movenode_stepsize); - point_x = fsnap(point_x,pathlib_gridsize); - point_y = fsnap(point_y,pathlib_gridsize); - - //dprint("end_x: ",ftos(end_x), " end_y: ",ftos(end_y),"\n"); - //dprint("point_x:",ftos(point_x)," point_y:",ftos(point_y),"\n\n"); - - traceline(point + movenode_stepup, point - movenode_maxdrop,MOVE_WORLDONLY,self); - if(trace_fraction == 1.0) - return trace_endpos; - - last_point = trace_endpos; - - tracebox(start + movenode_boxup, movenode_boxmin,movenode_boxmax, last_point + movenode_boxup, MOVE_WORLDONLY, self); - if(trace_fraction != 1.0) - return trace_endpos; - - pathlib_movenode_goodnode = 1; - return last_point; -} +vector pathlib_wateroutnode(vector start,vector end) +{ + vector surface; + + pathlib_movenode_goodnode = 0; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + traceline(end + ('0 0 0.25' * pathlib_gridsize),end - ('0 0 1' * pathlib_gridsize),MOVE_WORLDONLY,self); + end = trace_endpos; + + if not(pointcontents(end - '0 0 1') == CONTENT_SOLID) + return end; + + for(surface = start ; surface_z < (end_z + 32); ++surface_z) + { + if(pointcontents(surface) == CONTENT_EMPTY) + break; + } + + if(pointcontents(surface + '0 0 1') != CONTENT_EMPTY) + return end; + + tracebox(start + '0 0 64', movenode_boxmin,movenode_boxmax, end + '0 0 64', MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + if(fabs(surface_z - end_z) > 32) + pathlib_movenode_goodnode = 0; + + return end; +} + +vector pathlib_swimnode(vector start,vector end) +{ + pathlib_movenode_goodnode = 0; + + if(pointcontents(start) != CONTENT_WATER) + return end; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + if(pointcontents(end) == CONTENT_EMPTY) + return pathlib_wateroutnode( start, end); + + tracebox(start, movenode_boxmin,movenode_boxmax, end, MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + return end; +} + +vector pathlib_flynode(vector start,vector end) +{ + pathlib_movenode_goodnode = 0; + + end_x = fsnap(end_x, pathlib_gridsize); + end_y = fsnap(end_y, pathlib_gridsize); + + tracebox(start, movenode_boxmin,movenode_boxmax, end, MOVE_WORLDONLY, self); + if(trace_fraction == 1) + pathlib_movenode_goodnode = 1; + + return end; +} + +vector pathlib_walknode(vector start,vector end,float doedge) +{ + vector direction,point,last_point,s,e; + float steps, distance, i; + + pathlib_movenode_goodnode = 0; + + end_x = fsnap(end_x,pathlib_gridsize); + end_y = fsnap(end_y,pathlib_gridsize); + start_x = fsnap(start_x,pathlib_gridsize); + start_y = fsnap(start_y,pathlib_gridsize); + + // Find the floor + traceline(start + movenode_stepup, start - movenode_maxdrop,MOVE_WORLDONLY,self); + if(trace_fraction == 1.0) + return trace_endpos; + + start = trace_endpos; + + // Find the direcion, without Z + s = start; e = end; + //e_z = 0; s_z = 0; + direction = normalize(e - s); + + distance = vlen(start - end); + steps = rint(distance / movenode_stepsize); + + last_point = start; + for(i = 1; i < steps; ++i) + { + point = last_point + (direction * movenode_stepsize); + traceline(point + movenode_stepup,point - movenode_maxdrop,MOVE_WORLDONLY,self); + if(trace_fraction == 1.0) + return trace_endpos; + + last_point = trace_endpos; + } + + point = last_point + (direction * movenode_stepsize); + point_x = fsnap(point_x,pathlib_gridsize); + point_y = fsnap(point_y,pathlib_gridsize); + + //dprint("end_x: ",ftos(end_x), " end_y: ",ftos(end_y),"\n"); + //dprint("point_x:",ftos(point_x)," point_y:",ftos(point_y),"\n\n"); + + traceline(point + movenode_stepup, point - movenode_maxdrop,MOVE_WORLDONLY,self); + if(trace_fraction == 1.0) + return trace_endpos; + + last_point = trace_endpos; + + tracebox(start + movenode_boxup, movenode_boxmin,movenode_boxmax, last_point + movenode_boxup, MOVE_WORLDONLY, self); + if(trace_fraction != 1.0) + return trace_endpos; + + pathlib_movenode_goodnode = 1; + return last_point; +} diff --git a/data/qcsrc/server/pathlib/pathlib.qh b/data/qcsrc/server/pathlib/pathlib.qh index 77de88940..19f6ef083 100644 --- a/data/qcsrc/server/pathlib/pathlib.qh +++ b/data/qcsrc/server/pathlib/pathlib.qh @@ -1,99 +1,99 @@ -.entity path_next; -.entity path_prev; - -#define inwater(point) (pointcontents(point) == CONTENT_WATER) -#define medium spawnshieldtime - -#define PLIB_FORWARD '0 1 0' -//#define PLIB_BACK '0 -1 0' -#define PLIB_RIGHT '1 0 0' -//#define PLIB_LEFT '-1 0 0' - -#define DEBUGPATHING -#ifdef DEBUGPATHING -void pathlib_showpath(entity start); -void pathlib_showpath2(entity path); -#endif - -entity openlist; -entity closedlist; -entity goal_node; - -.float is_path_node; -.float pathlib_node_g; -.float pathlib_node_h; -.float pathlib_node_f; - -float pathlib_open_cnt; -float pathlib_closed_cnt; -float pathlib_made_cnt; -float pathlib_merge_cnt; -float pathlib_searched_cnt; -float pathlib_bestopen_seached; -float pathlib_bestcash_hits; -float pathlib_bestcash_saved; -float pathlib_gridsize; -float pathlib_movecost; -float pathlib_movecost_diag; -float pathlib_movecost_waterfactor; -float pathlib_foundgoal; - -float pathlib_starttime; -#define pathlib_maxtime 5 - -entity best_open_node; - -vector tile_check_up; -vector tile_check_down; -float tile_check_size; -float tile_check_cross(vector where); -float tile_check_plus(vector where); -float tile_check_star(vector where); -var float tile_check(vector where); - -float movenode_stepsize; -vector movenode_stepup; -vector movenode_maxdrop; -vector movenode_boxup; -vector movenode_boxmax; -vector movenode_boxmin; -float pathlib_movenode_goodnode; - -vector pathlib_wateroutnode(vector start, vector end); -vector pathlib_swimnode(vector start, vector end); -vector pathlib_flynode(vector start, vector end); -vector pathlib_walknode(vector start, vector end, float doedge); -var vector pathlib_movenode(vector start, vector end, float doedge); - -float pathlib_expandnode_star(entity node, vector start, vector goal); -float pathlib_expandnode_box(entity node, vector start, vector goal); -float pathlib_expandnode_octagon(entity node, vector start, vector goal); -var float pathlib_expandnode(entity node, vector start, vector goal); - -float pathlib_g_static(entity parent, vector to, float static_cost); -float pathlib_g_static_water(entity parent, vector to, float static_cost); -float pathlib_g_euclidean(entity parent, vector to, float static_cost); -float pathlib_g_euclidean_water(entity parent, vector to, float static_cost); -var float pathlib_cost(entity parent, vector to, float static_cost); - -float pathlib_h_manhattan(vector a, vector b); -float pathlib_h_diagonal(vector a, vector b); -float pathlib_h_euclidean(vector a,vector b); -float pathlib_h_diagonal2(vector a, vector b); -float pathlib_h_diagonal3(vector a, vector b); -float pathlib_h_diagonal2sdp(vector preprev, vector prev, vector point, vector end); -var float pathlib_heuristic(vector from, vector to); - -var float pathlib_makenode(entity parent,vector start, vector to, vector goal,float cost); -var float buildpath_nodefilter(vector n,vector c,vector p); - - -#ifdef DEBUGPATHING -#include "debug.qc" -#endif - -#include "utility.qc" -#include "movenode.qc" -#include "costs.qc" -#include "expandnode.qc" -#include "main.qc" +.entity path_next; +.entity path_prev; + +#define inwater(point) (pointcontents(point) == CONTENT_WATER) +#define medium spawnshieldtime + +#define PLIB_FORWARD '0 1 0' +//#define PLIB_BACK '0 -1 0' +#define PLIB_RIGHT '1 0 0' +//#define PLIB_LEFT '-1 0 0' + +#define DEBUGPATHING +#ifdef DEBUGPATHING +void pathlib_showpath(entity start); +void pathlib_showpath2(entity path); +#endif + +entity openlist; +entity closedlist; +entity goal_node; + +.float is_path_node; +.float pathlib_node_g; +.float pathlib_node_h; +.float pathlib_node_f; + +float pathlib_open_cnt; +float pathlib_closed_cnt; +float pathlib_made_cnt; +float pathlib_merge_cnt; +float pathlib_searched_cnt; +float pathlib_bestopen_seached; +float pathlib_bestcash_hits; +float pathlib_bestcash_saved; +float pathlib_gridsize; +float pathlib_movecost; +float pathlib_movecost_diag; +float pathlib_movecost_waterfactor; +float pathlib_foundgoal; + +float pathlib_starttime; +#define pathlib_maxtime 5 + +entity best_open_node; + +vector tile_check_up; +vector tile_check_down; +float tile_check_size; +float tile_check_cross(vector where); +float tile_check_plus(vector where); +float tile_check_star(vector where); +var float tile_check(vector where); + +float movenode_stepsize; +vector movenode_stepup; +vector movenode_maxdrop; +vector movenode_boxup; +vector movenode_boxmax; +vector movenode_boxmin; +float pathlib_movenode_goodnode; + +vector pathlib_wateroutnode(vector start, vector end); +vector pathlib_swimnode(vector start, vector end); +vector pathlib_flynode(vector start, vector end); +vector pathlib_walknode(vector start, vector end, float doedge); +var vector pathlib_movenode(vector start, vector end, float doedge); + +float pathlib_expandnode_star(entity node, vector start, vector goal); +float pathlib_expandnode_box(entity node, vector start, vector goal); +float pathlib_expandnode_octagon(entity node, vector start, vector goal); +var float pathlib_expandnode(entity node, vector start, vector goal); + +float pathlib_g_static(entity parent, vector to, float static_cost); +float pathlib_g_static_water(entity parent, vector to, float static_cost); +float pathlib_g_euclidean(entity parent, vector to, float static_cost); +float pathlib_g_euclidean_water(entity parent, vector to, float static_cost); +var float pathlib_cost(entity parent, vector to, float static_cost); + +float pathlib_h_manhattan(vector a, vector b); +float pathlib_h_diagonal(vector a, vector b); +float pathlib_h_euclidean(vector a,vector b); +float pathlib_h_diagonal2(vector a, vector b); +float pathlib_h_diagonal3(vector a, vector b); +float pathlib_h_diagonal2sdp(vector preprev, vector prev, vector point, vector end); +var float pathlib_heuristic(vector from, vector to); + +var float pathlib_makenode(entity parent,vector start, vector to, vector goal,float cost); +var float buildpath_nodefilter(vector n,vector c,vector p); + + +#ifdef DEBUGPATHING +#include "debug.qc" +#endif + +#include "utility.qc" +#include "movenode.qc" +#include "costs.qc" +#include "expandnode.qc" +#include "main.qc" diff --git a/data/qcsrc/server/pathlib/utility.qc b/data/qcsrc/server/pathlib/utility.qc index f2c3cae90..81d06b2c9 100644 --- a/data/qcsrc/server/pathlib/utility.qc +++ b/data/qcsrc/server/pathlib/utility.qc @@ -1,162 +1,162 @@ -float fsnap(float val,float fsize) -{ - return rint(val / fsize) * fsize; -} - -vector vsnap(vector point,float fsize) -{ - vector vret; - - vret_x = rint(point_x / fsize) * fsize; - vret_y = rint(point_y / fsize) * fsize; - vret_z = ceil(point_z / fsize) * fsize; - - return vret; -} - -float location_isok(vector point, float water_isok, float air_isok) -{ - float pc,pc2; - - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - return 0; - - pc = pointcontents(point); - pc2 = pointcontents(point - '0 0 1'); - - switch(pc) - { - case CONTENT_SOLID: - break; - - case CONTENT_SLIME: - break; - - case CONTENT_LAVA: - break; - - case CONTENT_SKY: - break; - - case CONTENT_EMPTY: - if (pc2 == CONTENT_SOLID) - return 1; - - if (pc2 == CONTENT_EMPTY) - if(air_isok) - return 1; - - if (pc2 == CONTENT_WATER) - if(water_isok) - return 1; - - break; - - case CONTENT_WATER: - if (water_isok) - return 1; - - break; - } - - return 0; -} - -entity pathlib_nodeatpoint(vector where) -{ - entity node; - - ++pathlib_searched_cnt; - - where_x = fsnap(where_x,pathlib_gridsize); - where_y = fsnap(where_y,pathlib_gridsize); - - node = findradius(where,pathlib_gridsize * 0.5); - while(node) - { - if(node.is_path_node == TRUE) - return node; - - node = node.chain; - } - - return world; -} - -float tile_check_cross(vector where) -{ - vector p,f,r; - - f = PLIB_FORWARD * tile_check_size; - r = PLIB_RIGHT * tile_check_size; - - // forward-right - p = where + f + r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - // Forward-left - p = where + f - r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - // Back-right - p = where - f + r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - //Back-left - p = where - f - r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - return 1; -} - -float tile_check_plus(vector where) -{ - vector p,f,r; - - f = PLIB_FORWARD * tile_check_size; - r = PLIB_RIGHT * tile_check_size; - - // forward - p = where + f; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - //left - p = where - r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - - // Right - p = where + r; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - //Back - p = where - f; - traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); - if not (location_isok(trace_endpos,1,0)) - return 0; - - return 1; -} - -float tile_check_star(vector where) -{ - if(tile_check_plus(where)) - return tile_check_cross(where); - - return 0; -} - +float fsnap(float val,float fsize) +{ + return rint(val / fsize) * fsize; +} + +vector vsnap(vector point,float fsize) +{ + vector vret; + + vret_x = rint(point_x / fsize) * fsize; + vret_y = rint(point_y / fsize) * fsize; + vret_z = ceil(point_z / fsize) * fsize; + + return vret; +} + +float location_isok(vector point, float water_isok, float air_isok) +{ + float pc,pc2; + + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) + return 0; + + pc = pointcontents(point); + pc2 = pointcontents(point - '0 0 1'); + + switch(pc) + { + case CONTENT_SOLID: + break; + + case CONTENT_SLIME: + break; + + case CONTENT_LAVA: + break; + + case CONTENT_SKY: + break; + + case CONTENT_EMPTY: + if (pc2 == CONTENT_SOLID) + return 1; + + if (pc2 == CONTENT_EMPTY) + if(air_isok) + return 1; + + if (pc2 == CONTENT_WATER) + if(water_isok) + return 1; + + break; + + case CONTENT_WATER: + if (water_isok) + return 1; + + break; + } + + return 0; +} + +entity pathlib_nodeatpoint(vector where) +{ + entity node; + + ++pathlib_searched_cnt; + + where_x = fsnap(where_x,pathlib_gridsize); + where_y = fsnap(where_y,pathlib_gridsize); + + node = findradius(where,pathlib_gridsize * 0.5); + while(node) + { + if(node.is_path_node == TRUE) + return node; + + node = node.chain; + } + + return world; +} + +float tile_check_cross(vector where) +{ + vector p,f,r; + + f = PLIB_FORWARD * tile_check_size; + r = PLIB_RIGHT * tile_check_size; + + // forward-right + p = where + f + r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + // Forward-left + p = where + f - r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + // Back-right + p = where - f + r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + //Back-left + p = where - f - r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + return 1; +} + +float tile_check_plus(vector where) +{ + vector p,f,r; + + f = PLIB_FORWARD * tile_check_size; + r = PLIB_RIGHT * tile_check_size; + + // forward + p = where + f; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + //left + p = where - r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + + // Right + p = where + r; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + //Back + p = where - f; + traceline(p+tile_check_up,p-tile_check_down,MOVE_WORLDONLY,self); + if not (location_isok(trace_endpos,1,0)) + return 0; + + return 1; +} + +float tile_check_star(vector where) +{ + if(tile_check_plus(where)) + return tile_check_cross(where); + + return 0; +} + diff --git a/data/qcsrc/server/tturrets/include/turrets.qh b/data/qcsrc/server/tturrets/include/turrets.qh index 5247eb93a..60936410d 100644 --- a/data/qcsrc/server/tturrets/include/turrets.qh +++ b/data/qcsrc/server/tturrets/include/turrets.qh @@ -1,29 +1,29 @@ -#ifdef TTURRETS_ENABLED - -// Include section. -#include "../system/system_misc.qc" /// Assorted junk & jewls -#include "../system/system_main.qc" /// And routines -#include "../system/system_aimprocs.qc" /// Aiming realted stuff -#include "../system/system_scoreprocs.qc" /// Target calssification -#include "../system/system_damage.qc" /// Outch, they are hurting me! what should i do? - -// Non combat units -#include "../units/unit_fusionreactor.qc" /// Supply unites that need it with power -#include "../units/unit_targettrigger.qc" /// Hit me! -#include "../units/unit_checkpoint.qc" /// Halfsmart pathing. - -// Combat units -#include "../units/unit_plasma.qc" /// Basic energy cannon -#include "../units/unit_mlrs.qc" /// Basic multibay RL -#include "../units/unit_hellion.qc" /// Seeking missiles MLRS -#include "../units/unit_flac.qc" /// anti missile turret -#include "../units/unit_phaser.qc" /// ZzzapT -#include "../units/unit_hk.qc" /// Hunter killers -#include "../units/unit_machinegun.qc" /// whacka -#include "../units/unit_tessla.qc" /// Chain lightning capabale turret -#include "../units/unit_walker.qc" /// Moving minigun-rocket-meele err thing -#include "../units/unit_ewheel.qc" /// A evil wheel. with guns on. -//#include "../units/unit_repulsor.qc" /// Fires a wave that knocks foes back -//#include "../units/unit_hive.qc" /// Swarm AI - -#endif // TTURRETS_ENABLED +#ifdef TTURRETS_ENABLED + +// Include section. +#include "../system/system_misc.qc" /// Assorted junk & jewls +#include "../system/system_main.qc" /// And routines +#include "../system/system_aimprocs.qc" /// Aiming realted stuff +#include "../system/system_scoreprocs.qc" /// Target calssification +#include "../system/system_damage.qc" /// Outch, they are hurting me! what should i do? + +// Non combat units +#include "../units/unit_fusionreactor.qc" /// Supply unites that need it with power +#include "../units/unit_targettrigger.qc" /// Hit me! +#include "../units/unit_checkpoint.qc" /// Halfsmart pathing. + +// Combat units +#include "../units/unit_plasma.qc" /// Basic energy cannon +#include "../units/unit_mlrs.qc" /// Basic multibay RL +#include "../units/unit_hellion.qc" /// Seeking missiles MLRS +#include "../units/unit_flac.qc" /// anti missile turret +#include "../units/unit_phaser.qc" /// ZzzapT +#include "../units/unit_hk.qc" /// Hunter killers +#include "../units/unit_machinegun.qc" /// whacka +#include "../units/unit_tessla.qc" /// Chain lightning capabale turret +#include "../units/unit_walker.qc" /// Moving minigun-rocket-meele err thing +#include "../units/unit_ewheel.qc" /// A evil wheel. with guns on. +//#include "../units/unit_repulsor.qc" /// Fires a wave that knocks foes back +//#include "../units/unit_hive.qc" /// Swarm AI + +#endif // TTURRETS_ENABLED diff --git a/data/qcsrc/server/tturrets/include/turrets_early.qh b/data/qcsrc/server/tturrets/include/turrets_early.qh index d1b1f6915..cdcfa5035 100644 --- a/data/qcsrc/server/tturrets/include/turrets_early.qh +++ b/data/qcsrc/server/tturrets/include/turrets_early.qh @@ -1,533 +1,533 @@ -// Comment out below to skip turrets -#define TTURRETS_ENABLED - -#ifdef TTURRETS_ENABLED - -#message "with tZork turrets" - -vector real_origin(entity ent); - -/// Map time control over pain inflicted -.float turret_scale_damage; -/// Map time control targetting range -.float turret_scale_range; -/// Map time control refire -.float turret_scale_refire; -/// Map time control ammo held and recharged -.float turret_scale_ammo; -/// Map time control aim speed -.float turret_scale_aim; -/// Map time control health -.float turret_scale_health; -/// Map time control respawn time -.float turret_scale_respawn; - -/// Used for cvar reloading -.string cvar_basename; - -//.float spawnflags -/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces -#define TSF_TERRAINBASE 2 -/// Disable builtin ammo regeneration -#define TSF_NO_AMMO_REGEN 4 -/// Dont break path to chase enemys. will still fire at them if possible. -#define TSF_NO_PATHBREAK 8 -/// Dont respawn -#define TSL_NO_RESPAWN 16 - -/// target selection flags -.float target_select_flags; -/// target validatoin flags -.float target_validate_flags; -/// Dont select a target on its own. -#define TFL_TARGETSELECT_NO 2 -/// Need line of sight -#define TFL_TARGETSELECT_LOS 4 -/// Players are valid targets -#define TFL_TARGETSELECT_PLAYERS 8 -/// Missiles are valid targets -#define TFL_TARGETSELECT_MISSILES 16 -/// Responds to turret_trigger_target events -#define TFL_TARGETSELECT_TRIGGERTARGET 32 -/// Angular limitations of turret head limits target selection -#define TFL_TARGETSELECT_ANGLELIMITS 64 -/// Range limits apply in targetselection -#define TFL_TARGETSELECT_RANGELIMTS 128 -/// DOnt select targets with a .team matching its own -#define TFL_TARGETSELECT_TEAMCHECK 256 -/// Cant select targets on its own. needs to be triggerd or slaved. -#define TFL_TARGETSELECT_NOBUILTIN 512 -/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team) -#define TFL_TARGETSELECT_OWNTEAM 1024 -/// Turrets aren't valid targets -#define TFL_TARGETSELECT_NOTURRETS 2048 -/// Use feild of view -#define TFL_TARGETSELECT_FOV 4096 - -/// aim flags -.float aim_flags; -/// Dont aim. -#define TFL_AIM_NO 1 -/// Go for ground, not direct hit -#define TFL_AIM_GROUND 2 -/// Go for ground, not direct hit, but only if target is on ground. -#define TFL_AIM_GROUND2 4 -/// Use balistic aim. FIXME: not implemented -#define TFL_AIM_BALISTIC 8 -/// Try to predict target movement (does not account for gravity) -#define TFL_AIM_LEAD 16 -/// Compensate for shot traveltime when lead -#define TFL_AIM_SHOTTIMECOMPENSATE 32 -/// Aim slightly in front of target -#define TFL_AIM_INFRONT 64 -/// Aim slightly behind target -#define TFL_AIM_BEHIND 128 -/// blend real and predicted z positions. (fake bounce prediction) -#define TFL_AIM_ZEASE 256 -/// Try to do real prediction of targets z pos at impact. -#define TFL_AIM_ZPREDICT 512 -/// Simply aim at target's current location -#define TFL_AIM_SIMPLE 1024 - -/// track (turn and pitch head) flags -.float track_flags; -/// Dont move head -#define TFL_TRACK_NO 2 -/// Pitch the head -#define TFL_TRACK_PITCH 4 -/// Rotate the head -#define TFL_TRACK_ROT 8 - -/// How tracking is preformed -.float track_type; -/// Hard angle increments. Ugly for fast turning, best accuracy. -#define TFL_TRACKTYPE_STEPMOTOR 1 -/// Smoth absolute movement. Looks ok, fair accuracy. -#define TFL_TRACKTYPE_FLUIDPRECISE 2 -/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the feilds below are set -#define TFL_TRACKTYPE_FLUIDINERTIA 3 -/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier -.float track_accel_pitch; -/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier -.float track_accel_rot; -/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1 = only old, 0 = only new -.float track_blendrate; - -/// How prefire check is preformed -.float firecheck_flags; -/// Dont kill the world -#define TFL_FIRECHECK_WORLD 2 -/// Dont kill the dead -#define TFL_FIRECHECK_DEAD 4 -/// Range limits apply -#define TFL_FIRECHECK_DISTANCES 8 -/// Line Of Sight needs to be clear -#define TFL_FIRECHECK_LOS 16 -/// Consider distance inpactpoint<->aimspot -#define TFL_FIRECHECK_AIMDIST 32 -/// Consider enemy origin<->impactpoint -#define TFL_FIRECHECK_REALDIST 64 -/// Consider angular diff head<->aimspot -#define TFL_FIRECHECK_ANGLEDIST 128 -/// (re)consider target.team<->self.team -#define TFL_FIRECHECK_TEAMCECK 256 -/// Try to avoid friendly fire -#define TFL_FIRECHECK_AFF 512 -/// Own .ammo needs to be >= then own .shot_dmg -#define TFL_FIRECHECK_OWM_AMMO 1024 -/// Others ammo need to be < others .ammo_max -#define TFL_FIRECHECK_OTHER_AMMO 2048 -/// Check own .attack_finished_single vs time -#define TFL_FIRECHECK_REFIRE 4096 -/// Move the acctual target to aimspot before tracing impact (and back after) -#define TFL_FIRECHECK_VERIFIED 8192 -/// Dont do any chekcs -#define TFL_FIRECHECK_NO 16384 - -/// How shooting is done -.float shoot_flags; -/// Dont shoot -#define TFL_SHOOT_NO 64 -/// Fire in vollys (partial implementation through .shot_volly) -#define TFL_SHOOT_VOLLY 2 -/// Always do a full volly, even if target is lost or dead. (not implemented) -#define TFL_SHOOT_VOLLYALWAYS 4 -/// Loop though all valid tarters, and hit them. -#define TFL_SHOOT_HITALLVALID 8 -/// Fiering makes unit loose target (after volly is done, if in volly mode) -#define TFL_SHOOT_CLEARTARGET 16 -///Custom shooting; -#define TFL_SHOOT_CUSTOM 32 - -/// Information aboute the units capabilities -.float turrcaps_flags; -/// No kown capabilities -#define TFL_TURRCAPS_NONE 0 -/// Capable of sniping -#define TFL_TURRCAPS_SNIPER 2 -/// Capable of splasdamage -#define TFL_TURRCAPS_RADIUSDMG 4 -/// Has one or more cannons with zero shot traveltime -#define TFL_TURRCAPS_HITSCAN 8 -/// More then one (type of) gun -#define TFL_TURRCAPS_MULTIGUN 16 -/// Carries at least one guided weapon -#define TFL_TURRCAPS_GUIDED 32 -/// At least one gun fiers slow projectiles -#define TFL_TURRCAPS_SLOWPROJ 64 -/// At least one gun fiers medium speed projectiles -#define TFL_TURRCAPS_MEDPROJ 128 -/// At least one gun fiers fast projectiles -#define TFL_TURRCAPS_FASTPROJ 256 -/// At least one gun capable of damaging players -#define TFL_TURRCAPS_PLAYERKILL 512 -/// At least one gun that can shoot town missiles -#define TFL_TURRCAPS_MISSILEKILL 1024 -/// Has support capabilities. powerplants and sutch. -#define TFL_TURRCAPS_SUPPORT 2048 -/// Proveides at least one type of ammmo -#define TFL_TURRCAPS_AMMOSOURCE 4096 -/// Can recive targets from external sources -#define TFL_TURRCAPS_RECIVETARGETS 8192 -/// Capable of self-transport -#define TFL_TURRCAPS_MOVE 16384 -/// Will roam arround even if not chasing anyting -#define TFL_TURRCAPS_ROAM 32768 -#define TFL_TURRCAPS_HEADATTACHED 65536 - -/// Ammo types needed and/or provided -.float ammo_flags; -/// Has and needs no ammo -#define TFL_AMMO_NONE 64 -/// Uses power -#define TFL_AMMO_ENERGY 2 -/// Uses bullets -#define TFL_AMMO_BULLETS 4 -/// Uses explosives -#define TFL_AMMO_ROCKETS 8 -/// Regenerates ammo on its own -#define TFL_AMMO_RECHARGE 16 -/// Can recive ammo from others -#define TFL_AMMO_RECIVE 32 - -/// How incomming damage is handeld -.float damage_flags; -/// Cant be hurt -#define TFL_DMG_NO 256 -/// Can be damaged -#define TFL_DMG_YES 2 -/// Can be damaged by teammates -#define TFL_DMG_TAKEFROMTEAM 4 -/// Traget attackers -#define TFL_DMG_RETALIATE 8 -/// Target attackers, even is on own team -#define TFL_DMG_RETALIATEONTEAM 16 -/// Loses target when damaged -#define TFL_DMG_TARGETLOSS 32 -/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented. -#define TFL_DMG_AIMSHAKE 64 -/// Reciving damage slaps the head arround -#define TFL_DMG_HEADSHAKE 128 -/// Die and stay dead. -#define TFL_DMG_DEATH_NORESPAWN 256 -/// Supress std turret gibs on death -#define TFL_DMG_DEATH_NOGIBS 512 - -// Spawnflags -/// Spawn in teambased modes -#define TFL_SPAWN_TEAM 2 -/// Spawn in FFA modes -#define TFL_SPAWN_FFA 4 - - -/* -* Fields used by turrets -*/ -/// Turrets internal ai speed -.float ticrate; - -/// Where to point the when no target -.vector idle_aim; - -/// Top part of turret -.entity tur_head; - -/// Start/respawn health -.float tur_health; - -/// Defend this entity (or ratehr this entitys position) -.entity tur_defend; - -/// on/off toggle. -.float tur_active; - -// Aim from this point, -//.vector tur_aimorg; - -/// and shoot from here. (can be non constant, think MLRS) -.vector tur_shotorg; - -/// Aim at this spot -.vector tur_aimpos; - -/// Predicted time the round will impact -.float tur_impacttime; - -// Predicted place the round will impact -//.vector tur_impactpoint; // unused - -/// What entity the aimtrace hit, if any. -.entity tur_impactent; - -/// Distance to enemy -.float tur_dist_enemy; - -/// Distance to aimspot -.float tur_dist_aimpos; - -/// Distance impact<->aim -.float tur_dist_impact_to_aimpos; - -/// Decresment counter form .shot_volly to 0. -.float volly_counter; - -/* -* Projectile/missile. its up to the individual turret implementation to -** deal the damage, blow upp the missile or whatever. -*/ -/// Track then refireing is possible -//.float attack_finished; = attack_finished_single -/// Shoot this often -.float shot_refire; -/// Shots travel this fast, when appliable -.float shot_speed; -/// Inaccuracy -.float shot_spread; -/// Estimated (core) damage of projectiles. also reduce on ammo with this amount when fiering -.float shot_dmg; -/// If radius dmg, this is how big that radius is. -.float shot_radius; -/// Max force exserted by round impact -.float shot_force; -/// < 1 = shoot # times at target (if possible) -.float shot_volly; -/// Refire after a compleated volly. -.float shot_volly_refire; - -/// Consider targets within this range -.float target_range; -/// Dont consider targets closer then -.float target_range_min; -// Engage fire routine on targets within -//.float target_range_fire; // no practical use aymore, work with target_range insted. -/// Targets closer to this are prefered -.float target_range_optimal; - -/* -* The standard targetselection tries to select a target based on -* range, angle offset, target type, "is old target" -* Thise biases will allow score scaling to (dis)favor diffrent targets -*/ -/// (dis)Favor best range this mutch -.float target_select_rangebias; -/// (dis)Favor targeting my old enemy this mutch -.float target_select_samebias; -/// (dis)Favor targeting the enemy closest to my guns current angle this mutch -.float target_select_anglebias; -/// (dis)Favor Missiles? (-1 to diable targeting compleatly) -.float target_select_missilebias; -/// (dis)Favot living players (-1 to diable targeting compleatly) -.float target_select_playerbias; -/// Field of view -//.float target_select_fov; -/// Last thimestamp this surret aquierd a valid target -.float target_select_time; - -/* -* Aim refers to real aiming, not gun pos (thats done by track) -*/ -/// Maximum offset between impact and aim spot to fire -.float aim_firetolerance_dist; -// Maximum angular offset between head and aimspot to fire -//.float aim_firetolerance_angle; -/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes) -.float aim_speed; -/// cant aim higher/lower then this -.float aim_maxpitch; -/// I cant rotate more then this -.float aim_maxrot; - -// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating) -/// Staring & current ammo -.float ammo; -/// Regenerate this mutch ammo (per second) -.float ammo_recharge; -/// Max amount of ammo i can hold -.float ammo_max; - - -// Uncomment below to enable various debug output. -//#define TURRET_DEBUG -//#define TURRET_DEBUG_TARGETVALIDATE -//#define TURRET_DEBUG_TARGETSELECT - -#ifdef TURRET_DEBUG -.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg. -.float tur_dbg_dmg_t_f; // Total damage spent -.float tur_dbg_start; // When did i go online? -.float tur_dbg_tmr1; // timer for random use -.float tur_dbg_tmr2; // timer for random use -.float tur_dbg_tmr3; // timer for random use -.vector tur_dbg_rvec; // Random vector, mainly for coloruing stuff' -#endif - -// System main's -/// Main AI loop -void turret_think(); -/// Prefire checks and sutch -void turret_fire(); - -// Callbacks -/// implements the actual fiering -.void() turret_firefunc; -/// prefire checks go here. return 1 to go bang, 0 not to. -.float() turret_firecheckfunc; -// Execure BEFORE main ai loop. return 0 to cancel any following proccessing. -//.float() turret_prethink; -/// Execure AFTER main AI loop -.void() turret_postthink; - -/// Add a target -.float(entity e_target,entity e_sender) turret_addtarget; - -//.float call_diehook; -//.float call_respwnhook; -.void() turret_diehook; -.void() turret_respawnhook; - -/* -#define TEH_THINK 2 -#define TEH_DAMAGE 4 -#define TEH_DIE 8 -#define TEH_RESPAWN 16 -#define TEH_TRACK 32 -#define TEH_AIM 64 -#define TEH_SELECT 128 -.float(float event_id) turret_eventhook; -*/ - -/* -* Some turrets need other aimsystems then other. -* This should return the place to aim at, not acctualy turn or -* pitch anyting. -* -* use turret_stdproc_aim* or Make your own. -* Make sure you update tur_enemy_dist and tur_enemy_adist -* with the apropriate info, if you do. - -removed. -*/ -// function used to aim, usualy turret_stdproc_aim_generic -//.vector() turret_aim; - -/* -* This is where the acctual turret turning should take place -* Use turret_stdproc_track or make your own. -wkacked to save mem. -*/ -// Function used to turn and pitch the .tur_head usualy turret_stdproc_track -//.void() turret_track; - -/* -* Target selection, preferably but not nessesarely -* return a normalized result. -*/ -/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic -.float(entity e_turret, entity e_target) turret_score_target; - -/* -* Damage, death and respawn. -*/ -//void turret_gibs_precash(); -// generalized so save mem (on fields) -// Function to handle incomming damage. usualy turret_stdproc_damage -//.void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) turret_damagefunc; -// Function to handle the event of death. usualy turret_stdproc_die -//.void() turret_diefunc; -// Function that handles rebirth. usualy turret_stdproc_respawn -//.void() turret_spawnfunc; - -/* -* Stuff to plug into requierd but unused callbacks. -*/ -/// Always return 1 -//float turret_stdproc_true(); -/// Always return 0 -//float turret_stdproc_false(); -/// Always return nothing at all -//void turret_stdproc_nothing(); - -/* -* Target selection -*/ -// noting uses the following atm. -// "closeer is beter" selection -//float turret_stdproc_targetscore_close(entity e_turret, entity e_target); -// "further is beter" selection -//float turret_stdproc_targetscore_far(entity e_turret, entity e_target); -// only target_range_optimal -//float turret_stdproc_targetscore_optimal(entity e_turret, entity e_target); -// defendpos -//float turret_stdproc_targetscore_defend(entity e_turret, entity e_target); -/// Generic fairly smart bias-aware target selection. -float turret_stdproc_targetscore_generic(entity e_turret, entity e_target); -/// Experimental supportunits targetselector -float turret_stdproc_targetscore_support(entity e_turret,entity e_target); - -/* -* Aim functions -*/ -/// Generic aimer guided by self.aim_flags -vector turret_stdproc_aim_generic() -// Straight line, current location -//vector turret_stdproc_aim_simple() - -/* -* Turret turning & pitch -*/ -/// Tries to line up the turret head with the aimpos -void turret_stdproc_track(); - -/// Generic damage handeling. blows up the turret when health <= 0 -void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce); -/// Spawns a explotion, does some damage & trows bits arround. -void turret_stdproc_die(); -/// reassembles the turret. -void turret_stdproc_respawn(); - -/// Evaluate target validity -float turret_validate_target(entity e_turret,entity e_target,float validate_flags); -/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target -vector tvt_thadv; -/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target -vector tvt_tadv; -/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target -float tvt_thadf; -/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target -float tvt_tadf; -/// Distance. updated by a sucsessfull call to turret_validate_target -float tvt_dist; - -/// updates aim org, shot org, shot dir and enemy org for selected turret -void turret_do_updates(entity e_turret); -//.vector tur_aimorg_updated; // creates to much aim issues. using tur_shotorg_updated insted. -//.vector tur_shotorg_updated; // DP8815 fixes gettaginfo, no longer needed. -.vector tur_shotdir_updated; - -void turrets_precash(); - - - -#endif // TTURRETS_ENABLED - - +// Comment out below to skip turrets +#define TTURRETS_ENABLED + +#ifdef TTURRETS_ENABLED + +#message "with tZork turrets" + +vector real_origin(entity ent); + +/// Map time control over pain inflicted +.float turret_scale_damage; +/// Map time control targetting range +.float turret_scale_range; +/// Map time control refire +.float turret_scale_refire; +/// Map time control ammo held and recharged +.float turret_scale_ammo; +/// Map time control aim speed +.float turret_scale_aim; +/// Map time control health +.float turret_scale_health; +/// Map time control respawn time +.float turret_scale_respawn; + +/// Used for cvar reloading +.string cvar_basename; + +//.float spawnflags +/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces +#define TSF_TERRAINBASE 2 +/// Disable builtin ammo regeneration +#define TSF_NO_AMMO_REGEN 4 +/// Dont break path to chase enemys. will still fire at them if possible. +#define TSF_NO_PATHBREAK 8 +/// Dont respawn +#define TSL_NO_RESPAWN 16 + +/// target selection flags +.float target_select_flags; +/// target validatoin flags +.float target_validate_flags; +/// Dont select a target on its own. +#define TFL_TARGETSELECT_NO 2 +/// Need line of sight +#define TFL_TARGETSELECT_LOS 4 +/// Players are valid targets +#define TFL_TARGETSELECT_PLAYERS 8 +/// Missiles are valid targets +#define TFL_TARGETSELECT_MISSILES 16 +/// Responds to turret_trigger_target events +#define TFL_TARGETSELECT_TRIGGERTARGET 32 +/// Angular limitations of turret head limits target selection +#define TFL_TARGETSELECT_ANGLELIMITS 64 +/// Range limits apply in targetselection +#define TFL_TARGETSELECT_RANGELIMTS 128 +/// DOnt select targets with a .team matching its own +#define TFL_TARGETSELECT_TEAMCHECK 256 +/// Cant select targets on its own. needs to be triggerd or slaved. +#define TFL_TARGETSELECT_NOBUILTIN 512 +/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team) +#define TFL_TARGETSELECT_OWNTEAM 1024 +/// Turrets aren't valid targets +#define TFL_TARGETSELECT_NOTURRETS 2048 +/// Use feild of view +#define TFL_TARGETSELECT_FOV 4096 + +/// aim flags +.float aim_flags; +/// Dont aim. +#define TFL_AIM_NO 1 +/// Go for ground, not direct hit +#define TFL_AIM_GROUND 2 +/// Go for ground, not direct hit, but only if target is on ground. +#define TFL_AIM_GROUND2 4 +/// Use balistic aim. FIXME: not implemented +#define TFL_AIM_BALISTIC 8 +/// Try to predict target movement (does not account for gravity) +#define TFL_AIM_LEAD 16 +/// Compensate for shot traveltime when lead +#define TFL_AIM_SHOTTIMECOMPENSATE 32 +/// Aim slightly in front of target +#define TFL_AIM_INFRONT 64 +/// Aim slightly behind target +#define TFL_AIM_BEHIND 128 +/// blend real and predicted z positions. (fake bounce prediction) +#define TFL_AIM_ZEASE 256 +/// Try to do real prediction of targets z pos at impact. +#define TFL_AIM_ZPREDICT 512 +/// Simply aim at target's current location +#define TFL_AIM_SIMPLE 1024 + +/// track (turn and pitch head) flags +.float track_flags; +/// Dont move head +#define TFL_TRACK_NO 2 +/// Pitch the head +#define TFL_TRACK_PITCH 4 +/// Rotate the head +#define TFL_TRACK_ROT 8 + +/// How tracking is preformed +.float track_type; +/// Hard angle increments. Ugly for fast turning, best accuracy. +#define TFL_TRACKTYPE_STEPMOTOR 1 +/// Smoth absolute movement. Looks ok, fair accuracy. +#define TFL_TRACKTYPE_FLUIDPRECISE 2 +/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the feilds below are set +#define TFL_TRACKTYPE_FLUIDINERTIA 3 +/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier +.float track_accel_pitch; +/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier +.float track_accel_rot; +/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1 = only old, 0 = only new +.float track_blendrate; + +/// How prefire check is preformed +.float firecheck_flags; +/// Dont kill the world +#define TFL_FIRECHECK_WORLD 2 +/// Dont kill the dead +#define TFL_FIRECHECK_DEAD 4 +/// Range limits apply +#define TFL_FIRECHECK_DISTANCES 8 +/// Line Of Sight needs to be clear +#define TFL_FIRECHECK_LOS 16 +/// Consider distance inpactpoint<->aimspot +#define TFL_FIRECHECK_AIMDIST 32 +/// Consider enemy origin<->impactpoint +#define TFL_FIRECHECK_REALDIST 64 +/// Consider angular diff head<->aimspot +#define TFL_FIRECHECK_ANGLEDIST 128 +/// (re)consider target.team<->self.team +#define TFL_FIRECHECK_TEAMCECK 256 +/// Try to avoid friendly fire +#define TFL_FIRECHECK_AFF 512 +/// Own .ammo needs to be >= then own .shot_dmg +#define TFL_FIRECHECK_OWM_AMMO 1024 +/// Others ammo need to be < others .ammo_max +#define TFL_FIRECHECK_OTHER_AMMO 2048 +/// Check own .attack_finished_single vs time +#define TFL_FIRECHECK_REFIRE 4096 +/// Move the acctual target to aimspot before tracing impact (and back after) +#define TFL_FIRECHECK_VERIFIED 8192 +/// Dont do any chekcs +#define TFL_FIRECHECK_NO 16384 + +/// How shooting is done +.float shoot_flags; +/// Dont shoot +#define TFL_SHOOT_NO 64 +/// Fire in vollys (partial implementation through .shot_volly) +#define TFL_SHOOT_VOLLY 2 +/// Always do a full volly, even if target is lost or dead. (not implemented) +#define TFL_SHOOT_VOLLYALWAYS 4 +/// Loop though all valid tarters, and hit them. +#define TFL_SHOOT_HITALLVALID 8 +/// Fiering makes unit loose target (after volly is done, if in volly mode) +#define TFL_SHOOT_CLEARTARGET 16 +///Custom shooting; +#define TFL_SHOOT_CUSTOM 32 + +/// Information aboute the units capabilities +.float turrcaps_flags; +/// No kown capabilities +#define TFL_TURRCAPS_NONE 0 +/// Capable of sniping +#define TFL_TURRCAPS_SNIPER 2 +/// Capable of splasdamage +#define TFL_TURRCAPS_RADIUSDMG 4 +/// Has one or more cannons with zero shot traveltime +#define TFL_TURRCAPS_HITSCAN 8 +/// More then one (type of) gun +#define TFL_TURRCAPS_MULTIGUN 16 +/// Carries at least one guided weapon +#define TFL_TURRCAPS_GUIDED 32 +/// At least one gun fiers slow projectiles +#define TFL_TURRCAPS_SLOWPROJ 64 +/// At least one gun fiers medium speed projectiles +#define TFL_TURRCAPS_MEDPROJ 128 +/// At least one gun fiers fast projectiles +#define TFL_TURRCAPS_FASTPROJ 256 +/// At least one gun capable of damaging players +#define TFL_TURRCAPS_PLAYERKILL 512 +/// At least one gun that can shoot town missiles +#define TFL_TURRCAPS_MISSILEKILL 1024 +/// Has support capabilities. powerplants and sutch. +#define TFL_TURRCAPS_SUPPORT 2048 +/// Proveides at least one type of ammmo +#define TFL_TURRCAPS_AMMOSOURCE 4096 +/// Can recive targets from external sources +#define TFL_TURRCAPS_RECIVETARGETS 8192 +/// Capable of self-transport +#define TFL_TURRCAPS_MOVE 16384 +/// Will roam arround even if not chasing anyting +#define TFL_TURRCAPS_ROAM 32768 +#define TFL_TURRCAPS_HEADATTACHED 65536 + +/// Ammo types needed and/or provided +.float ammo_flags; +/// Has and needs no ammo +#define TFL_AMMO_NONE 64 +/// Uses power +#define TFL_AMMO_ENERGY 2 +/// Uses bullets +#define TFL_AMMO_BULLETS 4 +/// Uses explosives +#define TFL_AMMO_ROCKETS 8 +/// Regenerates ammo on its own +#define TFL_AMMO_RECHARGE 16 +/// Can recive ammo from others +#define TFL_AMMO_RECIVE 32 + +/// How incomming damage is handeld +.float damage_flags; +/// Cant be hurt +#define TFL_DMG_NO 256 +/// Can be damaged +#define TFL_DMG_YES 2 +/// Can be damaged by teammates +#define TFL_DMG_TAKEFROMTEAM 4 +/// Traget attackers +#define TFL_DMG_RETALIATE 8 +/// Target attackers, even is on own team +#define TFL_DMG_RETALIATEONTEAM 16 +/// Loses target when damaged +#define TFL_DMG_TARGETLOSS 32 +/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented. +#define TFL_DMG_AIMSHAKE 64 +/// Reciving damage slaps the head arround +#define TFL_DMG_HEADSHAKE 128 +/// Die and stay dead. +#define TFL_DMG_DEATH_NORESPAWN 256 +/// Supress std turret gibs on death +#define TFL_DMG_DEATH_NOGIBS 512 + +// Spawnflags +/// Spawn in teambased modes +#define TFL_SPAWN_TEAM 2 +/// Spawn in FFA modes +#define TFL_SPAWN_FFA 4 + + +/* +* Fields used by turrets +*/ +/// Turrets internal ai speed +.float ticrate; + +/// Where to point the when no target +.vector idle_aim; + +/// Top part of turret +.entity tur_head; + +/// Start/respawn health +.float tur_health; + +/// Defend this entity (or ratehr this entitys position) +.entity tur_defend; + +/// on/off toggle. +.float tur_active; + +// Aim from this point, +//.vector tur_aimorg; + +/// and shoot from here. (can be non constant, think MLRS) +.vector tur_shotorg; + +/// Aim at this spot +.vector tur_aimpos; + +/// Predicted time the round will impact +.float tur_impacttime; + +// Predicted place the round will impact +//.vector tur_impactpoint; // unused + +/// What entity the aimtrace hit, if any. +.entity tur_impactent; + +/// Distance to enemy +.float tur_dist_enemy; + +/// Distance to aimspot +.float tur_dist_aimpos; + +/// Distance impact<->aim +.float tur_dist_impact_to_aimpos; + +/// Decresment counter form .shot_volly to 0. +.float volly_counter; + +/* +* Projectile/missile. its up to the individual turret implementation to +** deal the damage, blow upp the missile or whatever. +*/ +/// Track then refireing is possible +//.float attack_finished; = attack_finished_single +/// Shoot this often +.float shot_refire; +/// Shots travel this fast, when appliable +.float shot_speed; +/// Inaccuracy +.float shot_spread; +/// Estimated (core) damage of projectiles. also reduce on ammo with this amount when fiering +.float shot_dmg; +/// If radius dmg, this is how big that radius is. +.float shot_radius; +/// Max force exserted by round impact +.float shot_force; +/// < 1 = shoot # times at target (if possible) +.float shot_volly; +/// Refire after a compleated volly. +.float shot_volly_refire; + +/// Consider targets within this range +.float target_range; +/// Dont consider targets closer then +.float target_range_min; +// Engage fire routine on targets within +//.float target_range_fire; // no practical use aymore, work with target_range insted. +/// Targets closer to this are prefered +.float target_range_optimal; + +/* +* The standard targetselection tries to select a target based on +* range, angle offset, target type, "is old target" +* Thise biases will allow score scaling to (dis)favor diffrent targets +*/ +/// (dis)Favor best range this mutch +.float target_select_rangebias; +/// (dis)Favor targeting my old enemy this mutch +.float target_select_samebias; +/// (dis)Favor targeting the enemy closest to my guns current angle this mutch +.float target_select_anglebias; +/// (dis)Favor Missiles? (-1 to diable targeting compleatly) +.float target_select_missilebias; +/// (dis)Favot living players (-1 to diable targeting compleatly) +.float target_select_playerbias; +/// Field of view +//.float target_select_fov; +/// Last thimestamp this surret aquierd a valid target +.float target_select_time; + +/* +* Aim refers to real aiming, not gun pos (thats done by track) +*/ +/// Maximum offset between impact and aim spot to fire +.float aim_firetolerance_dist; +// Maximum angular offset between head and aimspot to fire +//.float aim_firetolerance_angle; +/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes) +.float aim_speed; +/// cant aim higher/lower then this +.float aim_maxpitch; +/// I cant rotate more then this +.float aim_maxrot; + +// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating) +/// Staring & current ammo +.float ammo; +/// Regenerate this mutch ammo (per second) +.float ammo_recharge; +/// Max amount of ammo i can hold +.float ammo_max; + + +// Uncomment below to enable various debug output. +//#define TURRET_DEBUG +//#define TURRET_DEBUG_TARGETVALIDATE +//#define TURRET_DEBUG_TARGETSELECT + +#ifdef TURRET_DEBUG +.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg. +.float tur_dbg_dmg_t_f; // Total damage spent +.float tur_dbg_start; // When did i go online? +.float tur_dbg_tmr1; // timer for random use +.float tur_dbg_tmr2; // timer for random use +.float tur_dbg_tmr3; // timer for random use +.vector tur_dbg_rvec; // Random vector, mainly for coloruing stuff' +#endif + +// System main's +/// Main AI loop +void turret_think(); +/// Prefire checks and sutch +void turret_fire(); + +// Callbacks +/// implements the actual fiering +.void() turret_firefunc; +/// prefire checks go here. return 1 to go bang, 0 not to. +.float() turret_firecheckfunc; +// Execure BEFORE main ai loop. return 0 to cancel any following proccessing. +//.float() turret_prethink; +/// Execure AFTER main AI loop +.void() turret_postthink; + +/// Add a target +.float(entity e_target,entity e_sender) turret_addtarget; + +//.float call_diehook; +//.float call_respwnhook; +.void() turret_diehook; +.void() turret_respawnhook; + +/* +#define TEH_THINK 2 +#define TEH_DAMAGE 4 +#define TEH_DIE 8 +#define TEH_RESPAWN 16 +#define TEH_TRACK 32 +#define TEH_AIM 64 +#define TEH_SELECT 128 +.float(float event_id) turret_eventhook; +*/ + +/* +* Some turrets need other aimsystems then other. +* This should return the place to aim at, not acctualy turn or +* pitch anyting. +* +* use turret_stdproc_aim* or Make your own. +* Make sure you update tur_enemy_dist and tur_enemy_adist +* with the apropriate info, if you do. + +removed. +*/ +// function used to aim, usualy turret_stdproc_aim_generic +//.vector() turret_aim; + +/* +* This is where the acctual turret turning should take place +* Use turret_stdproc_track or make your own. +wkacked to save mem. +*/ +// Function used to turn and pitch the .tur_head usualy turret_stdproc_track +//.void() turret_track; + +/* +* Target selection, preferably but not nessesarely +* return a normalized result. +*/ +/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic +.float(entity e_turret, entity e_target) turret_score_target; + +/* +* Damage, death and respawn. +*/ +//void turret_gibs_precash(); +// generalized so save mem (on fields) +// Function to handle incomming damage. usualy turret_stdproc_damage +//.void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) turret_damagefunc; +// Function to handle the event of death. usualy turret_stdproc_die +//.void() turret_diefunc; +// Function that handles rebirth. usualy turret_stdproc_respawn +//.void() turret_spawnfunc; + +/* +* Stuff to plug into requierd but unused callbacks. +*/ +/// Always return 1 +//float turret_stdproc_true(); +/// Always return 0 +//float turret_stdproc_false(); +/// Always return nothing at all +//void turret_stdproc_nothing(); + +/* +* Target selection +*/ +// noting uses the following atm. +// "closeer is beter" selection +//float turret_stdproc_targetscore_close(entity e_turret, entity e_target); +// "further is beter" selection +//float turret_stdproc_targetscore_far(entity e_turret, entity e_target); +// only target_range_optimal +//float turret_stdproc_targetscore_optimal(entity e_turret, entity e_target); +// defendpos +//float turret_stdproc_targetscore_defend(entity e_turret, entity e_target); +/// Generic fairly smart bias-aware target selection. +float turret_stdproc_targetscore_generic(entity e_turret, entity e_target); +/// Experimental supportunits targetselector +float turret_stdproc_targetscore_support(entity e_turret,entity e_target); + +/* +* Aim functions +*/ +/// Generic aimer guided by self.aim_flags +vector turret_stdproc_aim_generic() +// Straight line, current location +//vector turret_stdproc_aim_simple() + +/* +* Turret turning & pitch +*/ +/// Tries to line up the turret head with the aimpos +void turret_stdproc_track(); + +/// Generic damage handeling. blows up the turret when health <= 0 +void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce); +/// Spawns a explotion, does some damage & trows bits arround. +void turret_stdproc_die(); +/// reassembles the turret. +void turret_stdproc_respawn(); + +/// Evaluate target validity +float turret_validate_target(entity e_turret,entity e_target,float validate_flags); +/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target +vector tvt_thadv; +/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target +vector tvt_tadv; +/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target +float tvt_thadf; +/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target +float tvt_tadf; +/// Distance. updated by a sucsessfull call to turret_validate_target +float tvt_dist; + +/// updates aim org, shot org, shot dir and enemy org for selected turret +void turret_do_updates(entity e_turret); +//.vector tur_aimorg_updated; // creates to much aim issues. using tur_shotorg_updated insted. +//.vector tur_shotorg_updated; // DP8815 fixes gettaginfo, no longer needed. +.vector tur_shotdir_updated; + +void turrets_precash(); + + + +#endif // TTURRETS_ENABLED + + diff --git a/data/qcsrc/server/tturrets/system/system_aimprocs.qc b/data/qcsrc/server/tturrets/system/system_aimprocs.qc index 0f35b8739..52fc9f20a 100644 --- a/data/qcsrc/server/tturrets/system/system_aimprocs.qc +++ b/data/qcsrc/server/tturrets/system/system_aimprocs.qc @@ -1,122 +1,122 @@ -/* -* Generic aim - -supports: -TFL_AIM_NO -TFL_AIM_GROUND -TFL_AIM_LEAD -TFL_AIM_SHOTTIMECOMPENSATE -TFL_AIM_INFRONT -TFL_AIM_BEHIND -TFL_AIM_ZEASE - -not supported: -TFL_AIM_BALISTIC -*/ -vector turret_stdproc_aim_generic() -{ - - vector pre_pos,prep; - float distance,impact_time,i,mintime; - - turret_tag_fire_update(); - - if(self.aim_flags & TFL_AIM_SIMPLE) - return real_origin(self.enemy); - - // Keep track of when we can shoot the next time and - // try to predict where the target will be then, so we can put our aimpoint there. - // + sys_ticrate for non hitscan, becouse spawned - // projectiles dont move during the first tic of their life. - if (self.turrcaps_flags & TFL_TURRCAPS_HITSCAN) - mintime = max(self.attack_finished_single - time,0); - else - mintime = max(self.attack_finished_single - time,0) + sys_ticrate; - - // Baseline - pre_pos = real_origin(self.enemy);// + (self.enemy.velocity * mintime); - - if (self.aim_flags & TFL_AIM_INFRONT) // Aim a bit in front of the target - pre_pos = pre_pos + (normalize(self.enemy.velocity) * 64); - - if (self.aim_flags & TFL_AIM_BEHIND) // Aim a bit behind the target - pre_pos = pre_pos - (normalize(self.enemy.velocity) * 32); - - // Lead? - if (self.aim_flags & TFL_AIM_LEAD) - if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime - { - // FIXME: this cant be the best way to do this.. - - - prep = pre_pos; - for(i = 0; i < 4; ++i) - { - distance = vlen(prep - self.tur_shotorg); - impact_time = distance / self.shot_speed; - prep = pre_pos + self.enemy.velocity * impact_time; - } - - - // tnx to Rudolf "div0" Polzer for this solution. - // hmm tobad it dont work. - /* - vector q; - q = solve_quadratic(self.enemy.velocity*self.enemy.velocity - self.shot_speed*self.shot_speed, 2*(pre_pos*self.enemy.velocity), pre_pos * pre_pos); - if(q_x > 0) - impact_time = q_x; - else - impact_time = q_y; - */ - - prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); - - if(self.aim_flags & TFL_AIM_ZPREDICT) - if not(self.enemy.flags & FL_ONGROUND) - if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE) - { - float vz; - prep_z = pre_pos_z; - vz = self.enemy.velocity_z; - for(i = 0; i < impact_time; i += sys_ticrate) - { - vz = vz - (sv_gravity * sys_ticrate); - prep_z = prep_z + vz * sys_ticrate; - } - } - - - pre_pos = prep; - } - else - pre_pos = pre_pos + self.enemy.velocity * mintime; - - // Smooth out predict-Z? - /* - if (self.aim_flags & TFL_AIM_ZEASE) - if (self.enemy.flags & FL_CLIENT) - { - vector v; - v = real_origin(self.enemy); - pre_pos_z = (pre_pos_z + v_z) * 0.5; - } - */ - - if(self.aim_flags & TFL_AIM_GROUND2) - { - tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); - if(trace_fraction != 1.0) - pre_pos = trace_endpos; - } - - /* - // This turret should hit the ground neer a target rather the do a direct hit - if (self.aim_flags & TFL_AIM_GROUND) - { - traceline(pre_pos + '0 0 8',pre_pos - '0 0 10000',MOVE_WORLDONLY,self.enemy); - pre_pos = trace_endpos; - } - */ - - return pre_pos; -} +/* +* Generic aim + +supports: +TFL_AIM_NO +TFL_AIM_GROUND +TFL_AIM_LEAD +TFL_AIM_SHOTTIMECOMPENSATE +TFL_AIM_INFRONT +TFL_AIM_BEHIND +TFL_AIM_ZEASE + +not supported: +TFL_AIM_BALISTIC +*/ +vector turret_stdproc_aim_generic() +{ + + vector pre_pos,prep; + float distance,impact_time,i,mintime; + + turret_tag_fire_update(); + + if(self.aim_flags & TFL_AIM_SIMPLE) + return real_origin(self.enemy); + + // Keep track of when we can shoot the next time and + // try to predict where the target will be then, so we can put our aimpoint there. + // + sys_ticrate for non hitscan, becouse spawned + // projectiles dont move during the first tic of their life. + if (self.turrcaps_flags & TFL_TURRCAPS_HITSCAN) + mintime = max(self.attack_finished_single - time,0); + else + mintime = max(self.attack_finished_single - time,0) + sys_ticrate; + + // Baseline + pre_pos = real_origin(self.enemy);// + (self.enemy.velocity * mintime); + + if (self.aim_flags & TFL_AIM_INFRONT) // Aim a bit in front of the target + pre_pos = pre_pos + (normalize(self.enemy.velocity) * 64); + + if (self.aim_flags & TFL_AIM_BEHIND) // Aim a bit behind the target + pre_pos = pre_pos - (normalize(self.enemy.velocity) * 32); + + // Lead? + if (self.aim_flags & TFL_AIM_LEAD) + if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime + { + // FIXME: this cant be the best way to do this.. + + + prep = pre_pos; + for(i = 0; i < 4; ++i) + { + distance = vlen(prep - self.tur_shotorg); + impact_time = distance / self.shot_speed; + prep = pre_pos + self.enemy.velocity * impact_time; + } + + + // tnx to Rudolf "div0" Polzer for this solution. + // hmm tobad it dont work. + /* + vector q; + q = solve_quadratic(self.enemy.velocity*self.enemy.velocity - self.shot_speed*self.shot_speed, 2*(pre_pos*self.enemy.velocity), pre_pos * pre_pos); + if(q_x > 0) + impact_time = q_x; + else + impact_time = q_y; + */ + + prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); + + if(self.aim_flags & TFL_AIM_ZPREDICT) + if not(self.enemy.flags & FL_ONGROUND) + if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE) + { + float vz; + prep_z = pre_pos_z; + vz = self.enemy.velocity_z; + for(i = 0; i < impact_time; i += sys_ticrate) + { + vz = vz - (sv_gravity * sys_ticrate); + prep_z = prep_z + vz * sys_ticrate; + } + } + + + pre_pos = prep; + } + else + pre_pos = pre_pos + self.enemy.velocity * mintime; + + // Smooth out predict-Z? + /* + if (self.aim_flags & TFL_AIM_ZEASE) + if (self.enemy.flags & FL_CLIENT) + { + vector v; + v = real_origin(self.enemy); + pre_pos_z = (pre_pos_z + v_z) * 0.5; + } + */ + + if(self.aim_flags & TFL_AIM_GROUND2) + { + tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + if(trace_fraction != 1.0) + pre_pos = trace_endpos; + } + + /* + // This turret should hit the ground neer a target rather the do a direct hit + if (self.aim_flags & TFL_AIM_GROUND) + { + traceline(pre_pos + '0 0 8',pre_pos - '0 0 10000',MOVE_WORLDONLY,self.enemy); + pre_pos = trace_endpos; + } + */ + + return pre_pos; +} diff --git a/data/qcsrc/server/tturrets/system/system_main.qc b/data/qcsrc/server/tturrets/system/system_main.qc index ebf7217c3..7f0b1dad1 100644 --- a/data/qcsrc/server/tturrets/system/system_main.qc +++ b/data/qcsrc/server/tturrets/system/system_main.qc @@ -1,1258 +1,1258 @@ -#define cvar_base "g_turrets_unit_" - -/* -float turret_customizeentityforclient() -{ -} - -float Turret_SendEntity(entity to, float sf) -{ - - WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); - WriteCoord(MSG_ENTITY, self.tur_head.angles_x); - WriteCoord(MSG_ENTITY, self.tur_head.angles_y); - WriteByte(MSG_ENTITY, self.tur_head.frame); - - //WriteCoord(MSG_ENTITY, self.tur_head.angles_z); - - return TRUE; -} -*/ - -void load_unit_settings(entity ent,string unitname,float is_reload) -{ - string sbase; - - // dprint("Reloading turret ",e_turret.netname,"\n"); - - if (ent == world) - return; - - if (!ent.turret_scale_damage) ent.turret_scale_damage = 1; - if (!ent.turret_scale_range) ent.turret_scale_range = 1; - if (!ent.turret_scale_refire) ent.turret_scale_refire = 1; - if (!ent.turret_scale_ammo) ent.turret_scale_ammo = 1; - if (!ent.turret_scale_aim) ent.turret_scale_aim = 1; - if (!ent.turret_scale_health) ent.turret_scale_health = 1; - if (!ent.turret_scale_respawn) ent.turret_scale_respawn = 1; - - sbase = strcat(cvar_base,unitname); - if (is_reload) - { - ent.enemy = world; - ent.tur_head.avelocity = '0 0 0'; - - if (ent.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - ent.tur_head.angles = '0 0 0'; - else - ent.tur_head.angles = ent.angles; - } - - ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; - ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; - - ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; - ent.shot_refire = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire; - ent.shot_radius = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage; - ent.shot_speed = cvar(strcat(sbase,"_shot_speed")); - ent.shot_spread = cvar(strcat(sbase,"_shot_spread")); - ent.shot_force = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage; - ent.shot_volly = cvar(strcat(sbase,"_shot_volly")); - ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire; - - ent.target_range = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range; - ent.target_range_min = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range; - //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range; - ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; - - ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias")); - ent.target_select_samebias = cvar(strcat(sbase,"_target_select_samebias")); - ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias")); - ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias")); - //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov")); - - ent.ammo_max = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo; - ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo; - - ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist")); - ent.aim_speed = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim; - ent.aim_maxrot = cvar(strcat(sbase,"_aim_maxrot")); - ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch")); - - ent.track_type = cvar(strcat(sbase,"_track_type")); - ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch")); - ent.track_accel_rot = cvar(strcat(sbase,"_track_accel_rot")); - ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate")); - - if(is_reload) - if(ent.turret_respawnhook) - ent.turret_respawnhook(); - -} - -/* -float turret_stdproc_true() -{ - return 1; -} - -float turret_stdproc_false() -{ - return 0; -} - - -void turret_stdproc_nothing() -{ - return; -} -*/ - -/** -** updates enemy distances, predicted impact point/time -** and updated aim<->predict impact distance. -**/ -void turret_do_updates(entity t_turret) -{ - vector enemy_pos,oldpos; - entity oldself; - - oldself = self; - self = t_turret; - - enemy_pos = real_origin(self.enemy); - - turret_tag_fire_update(); - - self.tur_shotdir_updated = normalize(v_forward); - - self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos); - self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos); - - if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) - if(self.enemy) - { - oldpos = self.enemy.origin; - setorigin(self.enemy,self.tur_aimpos); - } - - //dprint("NN: ", self.netname," THVN: ",self.tur_head.classname," frame:",ftos(self.tur_head.frame),"\n"); - //dprint("self.tur_shotorg: ",vtos(self.tur_shotorg),"\n"); - tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1',self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos),MOVE_NORMAL,self); - - if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) - if(self.enemy) - setorigin(self.enemy,oldpos); - - //self.tur_impactpoint = trace_endpos; - self.tur_impactent = trace_ent; - self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins)*0.5); - self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed; - - self = oldself; -} - -/* -vector turret_fovsearch_pingpong() -{ - vector wish_angle; - if(self.phase < time) - { - if( self.tur_head.phase ) - self.tur_head.phase = 0; - else - self.tur_head.phase = 1; - self.phase = time + 5; - } - - if( self.tur_head.phase) - wish_angle = self.idle_aim + '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); - else - wish_angle = self.idle_aim - '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); - - return wish_angle; -} - -vector turret_fovsearch_steprot() -{ - vector wish_angle; - //float rot_add; - - wish_angle = self.tur_head.angles; - wish_angle_x = self.idle_aim_x; - - if (self.phase < time) - { - //rot_add = self.aim_maxrot / self.target_select_fov; - wish_angle_y += (self.target_select_fov * 2); - - if(wish_angle_y > 360) - wish_angle_y = wish_angle_y - 360; - - self.phase = time + 1.5; - } - - return wish_angle; -} - -vector turret_fovsearch_random() -{ - vector wish_angle; - - if (self.phase < time) - { - wish_angle_y = random() * self.aim_maxrot; - if(random() < 0.5) - wish_angle_y *= -1; - - wish_angle_x = random() * self.aim_maxpitch; - if(random() < 0.5) - wish_angle_x *= -1; - - self.phase = time + 5; - - self.tur_aimpos = wish_angle; - } - - return self.idle_aim + self.tur_aimpos; -} -*/ - -/** -** Handles head rotation according to -** the units .track_type and .track_flags -**/ -//.entity aim_mark; -void turret_stdproc_track() -{ - vector target_angle; // This is where we want to aim - vector move_angle; // This is where we can aim - float f_tmp; - - if (self.track_flags == TFL_TRACK_NO) - return; - - if(!self.tur_active) - target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch); - else if (self.enemy == world) - { - if(time > self.lip) - if (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - target_angle = self.idle_aim + self.angles; - else - target_angle = self.idle_aim; - else - target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); - } - else - { - // Find the direction - target_angle = normalize(self.tur_aimpos - self.tur_shotorg); - target_angle = vectoangles(target_angle); // And make a angle - } - - self.tur_head.angles_x = safeangle(self.tur_head.angles_x); - self.tur_head.angles_y = safeangle(self.tur_head.angles_y); - - // Find the diffrence between where we currently aim and where we want to aim - vector a_off; - - - if (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - { - move_angle = target_angle - (self.angles + self.tur_head.angles); - move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles)); - a_off = '0 0 0'; - - } - else - { - move_angle = target_angle - self.tur_head.angles; - move_angle = shortangle_vxy(move_angle,self.tur_head.angles); - a_off = self.angles; - } - - switch(self.track_type) - { - case TFL_TRACKTYPE_STEPMOTOR: - f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic - if (self.track_flags & TFL_TRACK_PITCH) - { - self.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp); - if(self.tur_head.angles_x + a_off_x > self.aim_maxpitch) - self.tur_head.angles_x = a_off_x + self.aim_maxpitch; - - if(self.tur_head.angles_x + a_off_x < -self.aim_maxpitch) - self.tur_head.angles_x = a_off_x - self.aim_maxpitch; - } - - if (self.track_flags & TFL_TRACK_ROT) - { - self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp); - if((self.tur_head.angles_y - a_off_y) > self.aim_maxrot) - self.tur_head.angles_y = a_off_y + self.aim_maxrot; - - if((self.tur_head.angles_y - a_off_y) < -self.aim_maxrot) - self.tur_head.angles_y = a_off_y - self.aim_maxrot; - } - - return; - - case TFL_TRACKTYPE_FLUIDINERTIA: - f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic - move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp,self.aim_speed); - move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rot * f_tmp,self.aim_speed); - move_angle = (self.tur_head.avelocity * self.track_blendrate) + (move_angle * (1 - self.track_blendrate)); - break; - - case TFL_TRACKTYPE_FLUIDPRECISE: - - move_angle_y = bound(-self.aim_speed, move_angle_y, self.aim_speed); - move_angle_x = bound(-self.aim_speed, move_angle_x, self.aim_speed); - - break; - } - - // pitch - if (self.track_flags & TFL_TRACK_PITCH) - { - self.tur_head.avelocity_x = move_angle_x; - if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) + a_off_x > self.aim_maxpitch) - { - self.tur_head.avelocity_x = 0; - self.tur_head.angles_x = a_off_x + self.aim_maxpitch; - } - if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) + a_off_x < -self.aim_maxpitch) - { - self.tur_head.avelocity_x = 0; - self.tur_head.angles_x = a_off_x - self.aim_maxpitch; - } - - } - - // rot - if (self.track_flags & TFL_TRACK_ROT) - { - self.tur_head.avelocity_y = move_angle_y; - - if(((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate)- a_off_y) > self.aim_maxrot) - { - self.tur_head.avelocity_y = 0; - self.tur_head.angles_y = a_off_y + self.aim_maxrot; - } - - if(((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) - a_off_y) < -self.aim_maxrot) - { - self.tur_head.avelocity_y = 0; - self.tur_head.angles_y = a_off_y - self.aim_maxrot; - } - - } - -} - - -/* - + = implemented - - = not implemented - - + TFL_FIRECHECK_NO - + TFL_FIRECHECK_WORLD - + TFL_FIRECHECK_DEAD - + TFL_FIRECHECK_DISTANCES - - TFL_FIRECHECK_LOS - + TFL_FIRECHECK_AIMDIST - + TFL_FIRECHECK_REALDIST - - TFL_FIRECHECK_ANGLEDIST - - TFL_FIRECHECK_TEAMCECK - + TFL_FIRECHECK_AFF - + TFL_FIRECHECK_OWM_AMMO - + TFL_FIRECHECK_OTHER_AMMO - + TFL_FIRECHECK_REFIRE -*/ - -/** -** Preforms pre-fire checks based on the uints firecheck_flags -**/ -float turret_stdproc_firecheck() -{ - // This one just dont care =) - if (self.firecheck_flags & TFL_FIRECHECK_NO) return 1; - - // Ready? - if (self.firecheck_flags & TFL_FIRECHECK_REFIRE) - if (self.attack_finished_single >= 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) && (self.volly_counter != self.shot_volly)) - return 1; - - // Lack of zombies makes shooting dead things unnecessary :P - if (self.firecheck_flags & TFL_FIRECHECK_DEAD) - if (self.enemy.deadflag != DEAD_NO) return 0; - - // Plz stop killing the world! - if (self.firecheck_flags & TFL_FIRECHECK_WORLD) - if (self.enemy == world) return 0; - - // Own ammo? - if (self.firecheck_flags & TFL_FIRECHECK_OWM_AMMO) - if (self.ammo < self.shot_dmg) return 0; - - // Other's ammo? (support-supply units) - if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO) - if (self.enemy.ammo >= self.enemy.ammo_max) return 0; - - if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) - { - // Not close enougth? - //if (self.tur_dist_aimpos > self.target_range_fire) return 0; - - // To close? - if (self.tur_dist_aimpos < self.target_range_min) return 0; - } - - // Try to avoid FF? - if (self.firecheck_flags & TFL_FIRECHECK_AFF) - if (self.tur_impactent.team == self.team) return 0; - - // aim<->predicted impact - if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST) - if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist) - if (self.tur_impactent != self.enemy) - return 0; - - // Volly status - if (self.shot_volly > 1) - if (self.volly_counter == self.shot_volly) - if (self.ammo < (self.shot_dmg * self.shot_volly)) - return 0; - - if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) - if(self.tur_impactent != self.enemy) - return 0; - - return 1; -} - -/* - + TFL_TARGETSELECT_NO - + TFL_TARGETSELECT_LOS - + TFL_TARGETSELECT_PLAYERS - + TFL_TARGETSELECT_MISSILES - - TFL_TARGETSELECT_TRIGGERTARGET - + TFL_TARGETSELECT_ANGLELIMITS - + TFL_TARGETSELECT_RANGELIMTS - + TFL_TARGETSELECT_TEAMCHECK - - TFL_TARGETSELECT_NOBUILTIN - + TFL_TARGETSELECT_OWNTEAM -*/ - -/** -** Evaluate a entity for target valitity based on validate_flags -**/ -float turret_validate_target(entity e_turret,entity e_target,float validate_flags) -{ - vector v_tmp; - - //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN) - // return -0.5; - - if(e_target.owner == e_turret) - return -0.5; - - if not(checkpvs(e_target.origin, e_turret)) - return -1; - - if (!e_target)// == world) - return -2; - - if(g_onslaught) - if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! - return - 3; - - if (validate_flags & TFL_TARGETSELECT_NO) - return -4; - - // If only this was used more.. - if (e_target.flags & FL_NOTARGET) - return -5; - - // Cant touch this - if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) - return -6; - - // player - if (e_target.flags & FL_CLIENT) - { - if not (validate_flags & TFL_TARGETSELECT_PLAYERS) - return -7; - - if (e_target.deadflag != DEAD_NO) - return -8; - } - - // enemy turrets - if (validate_flags & TFL_TARGETSELECT_NOTURRETS) - if (e_target.turret_firefunc || e_target.owner.tur_head == e_target) - if(e_target.team != e_turret.team) // Dont break support units. - return -9; - - // Missile - if (e_target.flags & FL_PROJECTILE) - if not (validate_flags & TFL_TARGETSELECT_MISSILES) - return -10; - - // Team check - if (validate_flags & TFL_TARGETSELECT_TEAMCHECK) - { - if (validate_flags & TFL_TARGETSELECT_OWNTEAM) - { - if (e_target.team != e_turret.team) - return -11; - - if (e_turret.team != e_target.owner.team) - return -12; - } - else - { - if (e_target.team == e_turret.team) - return -13; - - if (e_turret.team == e_target.owner.team) - return -14; - } - } - - // Range limits? - tvt_dist = vlen(e_turret.origin - real_origin(e_target)); - if (validate_flags & TFL_TARGETSELECT_RANGELIMTS) - { - if (tvt_dist < e_turret.target_range_min) - return -15; - - if (tvt_dist > e_turret.target_range) - return -16; - } - - // Can we even aim this thing? - if(e_turret.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - { - tvt_thadv = angleofs3(e_turret.tur_head.origin,e_turret.angles + e_turret.tur_head.angles ,e_target); - //tvt_thadv = angleofs(e_turret.angles,e_target); - } - else - { - tvt_thadv = angleofs(e_turret.tur_head,e_target); - } - - tvt_tadv = shortangle_vxy(angleofs(e_turret,e_target),e_turret.angles); - tvt_thadf = vlen(tvt_thadv); - tvt_tadf = vlen(tvt_tadv); - - /* - if(validate_flags & TFL_TARGETSELECT_FOV) - { - if(e_turret.target_select_fov < tvt_thadf) - return -21; - } - */ - - if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS) - { - if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch) - return -17; - - if (fabs(tvt_tadv_y) > e_turret.aim_maxrot) - return -18; - } - - // Line of sight? - if (validate_flags & TFL_TARGETSELECT_LOS) - { - v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5); - traceline(e_turret.tur_shotorg,v_tmp,0,e_turret); - - if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos)) - return -19; - } - - if (e_target.classname == "grapplinghook") - return -20; - -#ifdef TURRET_DEBUG_TARGETSELECT - dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n"); -#endif - - return 1; -} - -entity turret_select_target() -{ - entity e; // target looper entity - float score; // target looper entity score - entity e_enemy; // currently best scoreing target - float m_score; // currently best scoreing target's score - float f; - - m_score = 0; - if(self.enemy) - if(turret_validate_target(self,self.enemy,self.target_validate_flags) > 0) - { - e_enemy = self.enemy; - m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias; - } - - e = findradius(self.origin,self.target_range); - - // Nothing to aim at? - if (!e) return world; - - while (e) - { - f = turret_validate_target(self,e,self.target_select_flags); - if (f > 0) - { - score = self.turret_score_target(self,e); - if ((score > m_score) && (score > 0)) - { - e_enemy = e; - m_score = score; - } - } - e = e.chain; - } - - return e_enemy; -} - -void turret_think() -{ - entity e; - - self.nextthink = time + self.ticrate; - - // ONS uses somewhat backwards linking. - if (teamplay) - { - if not (g_onslaught) - if (self.target) - { - e = find(world,targetname,self.target); - if (e != world) - self.team = e.team; - } - - if (self.team != self.tur_head.team) - turret_stdproc_respawn(); - } - - - if (cvar("g_turrets_reloadcvars") == 1) - { - e = nextent(world); - while (e) - { - if (e.tur_head != world) - { - - load_unit_settings(e,e.cvar_basename,1); - if(e.turret_postthink) - e.turret_postthink(); - } - - e = nextent(e); - } - - cvar_set("g_turrets_reloadcvars","0"); - } - -#ifdef TURRET_DEBUG - if (self.tur_dbg_tmr1 < time) - { - if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9); - paint_target(self,256,self.tur_dbg_rvec,0.9); - self.tur_dbg_tmr1 = time + 1; - } -#endif - - // Handle ammo - if not (self.spawnflags & TSF_NO_AMMO_REGEN) - if (self.ammo < self.ammo_max) - self.ammo = min(self.ammo + self.ammo_recharge,self.ammo_max); - - - // Inactive turrets needs to run the think loop, - // So they can handle animation and wake up if need be. - if not (self.tur_active) - { - turret_stdproc_track(); - return; - } - - //This is just wrong :| - if(self.deadflag != DEAD_NO) - { - dprint("WARNING: dead turret running the think function!\n"); - return; - } - - // This is typicaly used for zaping every target in range - // turret_fusionreactor uses this to recharge friendlys. - if (self.shoot_flags & TFL_SHOOT_HITALLVALID) - { - - // Do a self.turret_fire for every valid target. - e = findradius(self.origin,self.target_range); - while (e) - { - if (turret_validate_target(self,e,self.target_validate_flags)) - { - self.enemy = e; - - turret_do_updates(self); - - if (self.turret_firecheckfunc()) - turret_fire(); - } - - e = e.chain; - } - self.enemy = world; - } - else if(self.shoot_flags & TFL_SHOOT_CUSTOM) - { - // This one is doing something.. oddball. assume its handles what needs to be handled. - - // Predict? - if not((self.aim_flags & TFL_AIM_NO)) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch? - if (!self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - - // Fire? - if (self.turret_firecheckfunc()) - turret_fire(); - } - else - { - // Special case for volly always. if it fired once it must compleate the volly. - if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) - if(self.volly_counter != self.shot_volly) - { - // Predict or whatnot - if not((self.aim_flags & TFL_AIM_NO)) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch - if (!self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - - // Fire! - if (self.turret_firecheckfunc() != 0) - turret_fire(); - - if(self.turret_postthink) - self.turret_postthink(); - - return; - } - - // Check if we have a vailid enemy, and try to find one if we dont. - if( ((self.target_select_time + cvar("g_turrets_targetscan_maxdelay")) < time) - || (turret_validate_target(self,self.enemy,self.target_validate_flags) <= 0) ) - if not (self.target_select_time + cvar("g_turrets_targetscan_mindelay") > time) - { - self.enemy = turret_select_target(); - //if(self.enemy) - self.target_select_time = time; - - } - - - // No target, just go to idle, do any custom stuff and bail. - if (self.enemy == world) - { - // Turn & pitch - if (!self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - // do any per-turret stuff - if(self.turret_postthink) - self.turret_postthink(); - - // And bail. - return; - } - else - self.lip = time + cvar("g_turrets_aimidle_delay"); // Keep track of the last time we had a target. - - // Predict? - if not((self.aim_flags & TFL_AIM_NO)) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch? - if (!self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - // Fire? - if (self.turret_firecheckfunc()) - turret_fire(); - } - - // do any per-turret stuff - if(self.turret_postthink) - self.turret_postthink(); -} - -void turret_fire() -{ - if (cvar("g_turrets_nofire") != 0) - return; - - if ((!self.tur_active) || (self.deadflag != DEAD_NO)) - return; - - self.turret_firefunc(); - - self.attack_finished_single = time + self.shot_refire; - self.ammo = self.ammo - self.shot_dmg; - self.volly_counter = self.volly_counter - 1; - if (self.volly_counter <= 0) - { - self.volly_counter = self.shot_volly; - - if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) - self.enemy = world; - - if (self.shot_volly > 1) - self.attack_finished_single = time + self.shot_volly_refire; - } - - -#ifdef TURRET_DEBUG - if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_dbg_rvec, self.tur_impacttime + 0.25); -#endif -} - -void turret_stdproc_fire() -{ - dprint("^1Bang, ^3your dead^7 ",self.enemy.netname,"! ^1(turret with no real firefunc)\n"); -} - -/* - When .used a turret switch team to activator.team. - If activator is world, the turrets goes inactive. -*/ -void turret_stdproc_use() -{ - dprint("Turret ",self.netname, " used by ",activator.classname,"\n"); - - self.team = activator.team; - - if(self.team == 0) - self.tur_active = 0; - else - self.tur_active = 1; - -} - -void turret_link() -{ - //Net_LinkEntity(self, FALSE, 0, Turret_SendEntity); - self.think = turret_think; - self.nextthink = time; -} - -/* -* Standard turret initialization. use this! -* (unless you have a very good reason not to) -* if the return value is 0, the turret should be removed. -*/ -float turret_stdproc_init (string cvar_base_name, float csqc_shared) -{ - entity e,ee; - - if(csqc_shared) - { - dprint("turrets: csqc_shared requested but not implemented. expect strange things to happen.\n"); - csqc_shared = 0; - } - - // Are turrets allowed atm? - if (cvar("g_turrets") == 0) - return 0; - - // Better more then once then never. - // turret_gibs_precash(); - - // Terrainbase spawnflag. This puts a enlongated model - // under the turret, so it looks ok on uneaven surfaces. - if (self.spawnflags & TSF_TERRAINBASE) - { - entity tb; - //precache_model("models/turrets/terrainbase.md3"); - tb = spawn(); - setmodel(tb,"models/turrets/terrainbase.md3"); - setorigin(tb,self.origin); - tb.solid = SOLID_BBOX; - //makestatic(tb); - } - - self.cvar_basename = cvar_base_name; - load_unit_settings(self,self.cvar_basename,0); - - // Handle turret teams. - if (cvar("g_assult") != 0) - { - if (!self.team) - self.team = 14; // Assume turrets are on the defending side if not explicitly set otehrwize - } - else if (!teamplay) - self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team iso they dont kill eachother. - else if(g_onslaught && self.targetname) - { - e = find(world,target,self.targetname); - if(e != world) - { - self.team = e.team; - ee = e; - } - } - else if(!self.team) - self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team iso they dont kill eachother. - - - - /* - * Try to guess some reasonaly defaults - * for missing params and do sanety checks - * thise checks could produce some "interesting" results - * if it hits a glitch in my logic :P so try to set as mutch - * as possible beforehand. - */ - if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) - if (!self.ticrate) self.ticrate = 0.2; // Support units generaly dont need to have a high speed ai-loop - else - if (!self.ticrate) self.ticrate = 0.1; // 10 fps for normal turrets - - self.ticrate = bound(sys_ticrate,self.ticrate,60); // keep it sane - -// General stuff - if (self.netname == "") - self.netname = self.classname; - - if (!self.respawntime) - self.respawntime = 60; - self.respawntime = max(-1,self.respawntime); - - if (!self.health) - self.health = 1000; - self.tur_health = max(1,self.health); - - if (!self.turrcaps_flags) - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - - if (!self.damage_flags) - self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; - -// Shot stuff. - if (!self.shot_refire) - self.shot_refire = 1; - self.shot_refire = bound(0.01,self.shot_refire,9999); - - if (!self.shot_dmg) - self.shot_dmg = self.shot_refire * 50; - self.shot_dmg = max(1,self.shot_dmg); - - if (!self.shot_radius) - self.shot_radius = self.shot_dmg * 0.5; - self.shot_radius = max(1,self.shot_radius); - - if (!self.shot_speed) - self.shot_speed = 2500; - self.shot_speed = max(1,self.shot_speed); - - if (!self.shot_spread) - self.shot_spread = 0.0125; - self.shot_spread = bound(0.0001,self.shot_spread,500); - - if (!self.shot_force) - self.shot_force = self.shot_dmg * 0.5 + self.shot_radius * 0.5; - self.shot_force = bound(0.001,self.shot_force,MAX_SHOT_DISTANCE * 0.5); - - if (!self.shot_volly) - self.shot_volly = 1; - self.shot_volly = bound(1,self.shot_volly,floor(self.ammo_max / self.shot_dmg)); - - if (!self.shot_volly_refire) - self.shot_volly_refire = self.shot_refire * self.shot_volly; - self.shot_volly_refire = bound(self.shot_refire,self.shot_volly_refire,60); - - if (!self.firecheck_flags) - self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | - TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK | - TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_WORLD; - -// Range stuff. - if (!self.target_range) - self.target_range = self.shot_speed * 0.5; - self.target_range = bound(0,self.target_range,MAX_SHOT_DISTANCE); - - if (!self.target_range_min) - self.target_range_min = self.shot_radius * 2; - self.target_range_min = bound(0,self.target_range_min,MAX_SHOT_DISTANCE); - - //if (!self.target_range_fire) - // self.target_range_fire = self.target_range * 0.8; - //self.target_range_fire = bound(0,self.target_range_fire,MAX_SHOT_DISTANCE); - - if (!self.target_range_optimal) - self.target_range_optimal = self.target_range * 0.5; - self.target_range_optimal = bound(0,self.target_range_optimal,MAX_SHOT_DISTANCE); - - -// Aim stuff. - if (!self.aim_maxrot) - self.aim_maxrot = 90; - self.aim_maxrot = bound(0,self.aim_maxrot,360); - - if (!self.aim_maxpitch) - self.aim_maxpitch = 20; - self.aim_maxpitch = bound(0,self.aim_maxpitch,90); - - if (!self.aim_speed) - self.aim_speed = 36; - self.aim_speed = bound(0.1,self.aim_speed, 1000); - - if (!self.aim_firetolerance_dist) - self.aim_firetolerance_dist = 5 + (self.shot_radius * 2); - self.aim_firetolerance_dist = bound(0.1,self.aim_firetolerance_dist,MAX_SHOT_DISTANCE); - - if (!self.aim_flags) - { - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - if(self.turrcaps_flags & TFL_TURRCAPS_RADIUSDMG) - self.aim_flags |= TFL_AIM_GROUND2; - } - - // Sill the most tested (and aim-effective) - if (!self.track_type) self.track_type = TFL_TRACKTYPE_STEPMOTOR; - - if (self.track_type != TFL_TRACKTYPE_STEPMOTOR) - { - // Fluid / Ineria mode. Looks mutch nicer, bit experimental & - // Can inmapt aim preformance alot. - // needs a bit diffrent aimspeed - - if (!self.aim_speed) - self.aim_speed = 180; - self.aim_speed = bound(0.1,self.aim_speed, 1000); - - if (!self.track_accel_pitch) - self.track_accel_pitch = 0.5; - - if (!self.track_accel_rot) - self.track_accel_rot = 0.5; - - if (!self.track_blendrate) - self.track_blendrate = 0.35; - } - - if (!self.track_flags) - self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT; - - -// Target selection stuff. - if (!self.target_select_rangebias) - self.target_select_rangebias = 1; - self.target_select_rangebias = bound(-10,self.target_select_rangebias,10); - - if (!self.target_select_samebias) - self.target_select_samebias = 1; - self.target_select_samebias = bound(-10,self.target_select_samebias,10); - - if (!self.target_select_anglebias) - self.target_select_anglebias = 1; - self.target_select_anglebias = bound(-10,self.target_select_anglebias,10); - - if (!self.target_select_missilebias) - self.target_select_missilebias = -10; - - self.target_select_missilebias = bound(-10,self.target_select_missilebias,10); - self.target_select_playerbias = bound(-10,self.target_select_playerbias,10); - - if (!self.target_select_flags) - { - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK - | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS; - - if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL) - self.target_select_flags |= TFL_TARGETSELECT_MISSILES; - - if (self.turrcaps_flags & TFL_TURRCAPS_PLAYERKILL) - self.target_select_flags |= TFL_TARGETSELECT_PLAYERS; - //else - // self.target_select_flags = TFL_TARGETSELECT_NO; - } - - self.target_validate_flags = self.target_select_flags; - - -// Ammo stuff - if (!self.ammo_max) - self.ammo_max = self.shot_dmg * 10; - self.ammo_max = max(self.shot_dmg,self.ammo_max); - - if (!self.ammo) - self.ammo = self.shot_dmg * 5; - self.ammo = bound(0,self.ammo,self.ammo_max); - - if (!self.ammo_recharge) - self.ammo_recharge = self.shot_dmg * 0.5; - self.ammo_recharge = max(0,self.ammo_recharge); - - // Convert the recharge from X per sec to X per ticrate - self.ammo_recharge = self.ammo_recharge * self.ticrate; - - if (!self.ammo_flags) - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; - -// Damage stuff - if(self.spawnflags & TSL_NO_RESPAWN) - if not (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) - self.damage_flags |= TFL_DMG_DEATH_NORESPAWN; - -// Offsets & origins - if (!self.tur_shotorg) self.tur_shotorg = '50 0 50'; - -// End of default & sanety checks, start building the turret. - -// Spawn extra bits - self.tur_head = spawn(); - self.tur_head.netname = self.tur_head.classname = "turret_head"; - self.tur_head.team = self.team; - self.tur_head.owner = self; - - // Defend mode? - if(!self.tur_defend) - if (self.target != "") - { - self.tur_defend = find(world, targetname, self.target); - if (self.tur_defend == world) - { - self.target = ""; - dprint("Turret has invalid defendpoint!\n"); - } - } - -// Put pices in place - if not (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - setorigin(self.tur_head,self.origin); - - // In target defend mode, aim on the spot to defend when idle. - if(self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - { - if (self.tur_defend) - self.idle_aim = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend); - else - self.idle_aim = '0 0 0'; - } - else - { - if (self.tur_defend) - self.idle_aim = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend); - else - self.idle_aim = self.angles; - } - - if not (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) - self.tur_head.angles = self.idle_aim; - - if (!self.health) - self.health = 150; - - self.tur_health = self.health; - self.tur_head.health = self.health; - - self.solid = SOLID_BBOX; - self.tur_head.solid = SOLID_BBOX; - - self.takedamage = DAMAGE_AIM; - self.tur_head.takedamage = DAMAGE_AIM; - - self.movetype = MOVETYPE_NOCLIP; - self.tur_head.movetype = MOVETYPE_NOCLIP; - - // Team color - if (self.team == COLOR_TEAM1) self.colormod = '1.4 0.8 0.8'; - if (self.team == COLOR_TEAM2) self.colormod = '0.8 0.8 1.4'; - - // Attach stdprocs. override when and what needed - if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) - { - self.turret_score_target = turret_stdproc_targetscore_support; - self.turret_firecheckfunc = turret_stdproc_firecheck; - self.turret_firefunc = turret_stdproc_fire; - //self.turret_postthink = turret_stdproc_nothing; - self.event_damage = turret_stdproc_damage; - self.tur_head.event_damage = turret_stdproc_damage; - } - else - { - self.turret_score_target = turret_stdproc_targetscore_generic; - self.turret_firecheckfunc = turret_stdproc_firecheck; - self.turret_firefunc = turret_stdproc_fire; - //self.turret_postthink = turret_stdproc_nothing; - self.event_damage = turret_stdproc_damage; - self.tur_head.event_damage = turret_stdproc_damage; - //self.turret_addtarget = turret_stdproc_false; - } - - self.use = turret_stdproc_use; - self.bot_attack = TRUE; - - // Initiate the main AI loop - if(csqc_shared) - self.think = turret_link; - else - self.think = turret_think; - - self.nextthink = time + self.ticrate; - - self.tur_head.team = self.team; - self.view_ofs = '0 0 0'; - -#ifdef TURRET_DEBUG - self.tur_dbg_start = self.nextthink; - while (vlen(self.tur_dbg_rvec) < 2) - self.tur_dbg_rvec = randomvec() * 4; - - self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x); - self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y); - self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z); -#endif - - // Its all good. - self.classname = "turret_main"; - - self.tur_active = 1; - - // In ONS mode, and linked to a ONS ent. need to call the use to set team. - if (g_onslaught && ee) - { - activator = ee; - self.use(); - } - - return 1; -} - - +#define cvar_base "g_turrets_unit_" + +/* +float turret_customizeentityforclient() +{ +} + +float Turret_SendEntity(entity to, float sf) +{ + + WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); + WriteCoord(MSG_ENTITY, self.tur_head.angles_x); + WriteCoord(MSG_ENTITY, self.tur_head.angles_y); + WriteByte(MSG_ENTITY, self.tur_head.frame); + + //WriteCoord(MSG_ENTITY, self.tur_head.angles_z); + + return TRUE; +} +*/ + +void load_unit_settings(entity ent,string unitname,float is_reload) +{ + string sbase; + + // dprint("Reloading turret ",e_turret.netname,"\n"); + + if (ent == world) + return; + + if (!ent.turret_scale_damage) ent.turret_scale_damage = 1; + if (!ent.turret_scale_range) ent.turret_scale_range = 1; + if (!ent.turret_scale_refire) ent.turret_scale_refire = 1; + if (!ent.turret_scale_ammo) ent.turret_scale_ammo = 1; + if (!ent.turret_scale_aim) ent.turret_scale_aim = 1; + if (!ent.turret_scale_health) ent.turret_scale_health = 1; + if (!ent.turret_scale_respawn) ent.turret_scale_respawn = 1; + + sbase = strcat(cvar_base,unitname); + if (is_reload) + { + ent.enemy = world; + ent.tur_head.avelocity = '0 0 0'; + + if (ent.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + ent.tur_head.angles = '0 0 0'; + else + ent.tur_head.angles = ent.angles; + } + + ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; + ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; + + ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; + ent.shot_refire = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire; + ent.shot_radius = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage; + ent.shot_speed = cvar(strcat(sbase,"_shot_speed")); + ent.shot_spread = cvar(strcat(sbase,"_shot_spread")); + ent.shot_force = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage; + ent.shot_volly = cvar(strcat(sbase,"_shot_volly")); + ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire; + + ent.target_range = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range; + ent.target_range_min = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range; + //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range; + ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; + + ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias")); + ent.target_select_samebias = cvar(strcat(sbase,"_target_select_samebias")); + ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias")); + ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias")); + //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov")); + + ent.ammo_max = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo; + ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo; + + ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist")); + ent.aim_speed = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim; + ent.aim_maxrot = cvar(strcat(sbase,"_aim_maxrot")); + ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch")); + + ent.track_type = cvar(strcat(sbase,"_track_type")); + ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch")); + ent.track_accel_rot = cvar(strcat(sbase,"_track_accel_rot")); + ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate")); + + if(is_reload) + if(ent.turret_respawnhook) + ent.turret_respawnhook(); + +} + +/* +float turret_stdproc_true() +{ + return 1; +} + +float turret_stdproc_false() +{ + return 0; +} + + +void turret_stdproc_nothing() +{ + return; +} +*/ + +/** +** updates enemy distances, predicted impact point/time +** and updated aim<->predict impact distance. +**/ +void turret_do_updates(entity t_turret) +{ + vector enemy_pos,oldpos; + entity oldself; + + oldself = self; + self = t_turret; + + enemy_pos = real_origin(self.enemy); + + turret_tag_fire_update(); + + self.tur_shotdir_updated = normalize(v_forward); + + self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos); + self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos); + + if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) + if(self.enemy) + { + oldpos = self.enemy.origin; + setorigin(self.enemy,self.tur_aimpos); + } + + //dprint("NN: ", self.netname," THVN: ",self.tur_head.classname," frame:",ftos(self.tur_head.frame),"\n"); + //dprint("self.tur_shotorg: ",vtos(self.tur_shotorg),"\n"); + tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1',self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos),MOVE_NORMAL,self); + + if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) + if(self.enemy) + setorigin(self.enemy,oldpos); + + //self.tur_impactpoint = trace_endpos; + self.tur_impactent = trace_ent; + self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins)*0.5); + self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed; + + self = oldself; +} + +/* +vector turret_fovsearch_pingpong() +{ + vector wish_angle; + if(self.phase < time) + { + if( self.tur_head.phase ) + self.tur_head.phase = 0; + else + self.tur_head.phase = 1; + self.phase = time + 5; + } + + if( self.tur_head.phase) + wish_angle = self.idle_aim + '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); + else + wish_angle = self.idle_aim - '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); + + return wish_angle; +} + +vector turret_fovsearch_steprot() +{ + vector wish_angle; + //float rot_add; + + wish_angle = self.tur_head.angles; + wish_angle_x = self.idle_aim_x; + + if (self.phase < time) + { + //rot_add = self.aim_maxrot / self.target_select_fov; + wish_angle_y += (self.target_select_fov * 2); + + if(wish_angle_y > 360) + wish_angle_y = wish_angle_y - 360; + + self.phase = time + 1.5; + } + + return wish_angle; +} + +vector turret_fovsearch_random() +{ + vector wish_angle; + + if (self.phase < time) + { + wish_angle_y = random() * self.aim_maxrot; + if(random() < 0.5) + wish_angle_y *= -1; + + wish_angle_x = random() * self.aim_maxpitch; + if(random() < 0.5) + wish_angle_x *= -1; + + self.phase = time + 5; + + self.tur_aimpos = wish_angle; + } + + return self.idle_aim + self.tur_aimpos; +} +*/ + +/** +** Handles head rotation according to +** the units .track_type and .track_flags +**/ +//.entity aim_mark; +void turret_stdproc_track() +{ + vector target_angle; // This is where we want to aim + vector move_angle; // This is where we can aim + float f_tmp; + + if (self.track_flags == TFL_TRACK_NO) + return; + + if(!self.tur_active) + target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch); + else if (self.enemy == world) + { + if(time > self.lip) + if (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + target_angle = self.idle_aim + self.angles; + else + target_angle = self.idle_aim; + else + target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); + } + else + { + // Find the direction + target_angle = normalize(self.tur_aimpos - self.tur_shotorg); + target_angle = vectoangles(target_angle); // And make a angle + } + + self.tur_head.angles_x = safeangle(self.tur_head.angles_x); + self.tur_head.angles_y = safeangle(self.tur_head.angles_y); + + // Find the diffrence between where we currently aim and where we want to aim + vector a_off; + + + if (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + { + move_angle = target_angle - (self.angles + self.tur_head.angles); + move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles)); + a_off = '0 0 0'; + + } + else + { + move_angle = target_angle - self.tur_head.angles; + move_angle = shortangle_vxy(move_angle,self.tur_head.angles); + a_off = self.angles; + } + + switch(self.track_type) + { + case TFL_TRACKTYPE_STEPMOTOR: + f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic + if (self.track_flags & TFL_TRACK_PITCH) + { + self.tur_head.angles_x += bound(-f_tmp,move_angle_x, f_tmp); + if(self.tur_head.angles_x + a_off_x > self.aim_maxpitch) + self.tur_head.angles_x = a_off_x + self.aim_maxpitch; + + if(self.tur_head.angles_x + a_off_x < -self.aim_maxpitch) + self.tur_head.angles_x = a_off_x - self.aim_maxpitch; + } + + if (self.track_flags & TFL_TRACK_ROT) + { + self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp); + if((self.tur_head.angles_y - a_off_y) > self.aim_maxrot) + self.tur_head.angles_y = a_off_y + self.aim_maxrot; + + if((self.tur_head.angles_y - a_off_y) < -self.aim_maxrot) + self.tur_head.angles_y = a_off_y - self.aim_maxrot; + } + + return; + + case TFL_TRACKTYPE_FLUIDINERTIA: + f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic + move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp,self.aim_speed); + move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rot * f_tmp,self.aim_speed); + move_angle = (self.tur_head.avelocity * self.track_blendrate) + (move_angle * (1 - self.track_blendrate)); + break; + + case TFL_TRACKTYPE_FLUIDPRECISE: + + move_angle_y = bound(-self.aim_speed, move_angle_y, self.aim_speed); + move_angle_x = bound(-self.aim_speed, move_angle_x, self.aim_speed); + + break; + } + + // pitch + if (self.track_flags & TFL_TRACK_PITCH) + { + self.tur_head.avelocity_x = move_angle_x; + if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) + a_off_x > self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = a_off_x + self.aim_maxpitch; + } + if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) + a_off_x < -self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = a_off_x - self.aim_maxpitch; + } + + } + + // rot + if (self.track_flags & TFL_TRACK_ROT) + { + self.tur_head.avelocity_y = move_angle_y; + + if(((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate)- a_off_y) > self.aim_maxrot) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = a_off_y + self.aim_maxrot; + } + + if(((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) - a_off_y) < -self.aim_maxrot) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = a_off_y - self.aim_maxrot; + } + + } + +} + + +/* + + = implemented + - = not implemented + + + TFL_FIRECHECK_NO + + TFL_FIRECHECK_WORLD + + TFL_FIRECHECK_DEAD + + TFL_FIRECHECK_DISTANCES + - TFL_FIRECHECK_LOS + + TFL_FIRECHECK_AIMDIST + + TFL_FIRECHECK_REALDIST + - TFL_FIRECHECK_ANGLEDIST + - TFL_FIRECHECK_TEAMCECK + + TFL_FIRECHECK_AFF + + TFL_FIRECHECK_OWM_AMMO + + TFL_FIRECHECK_OTHER_AMMO + + TFL_FIRECHECK_REFIRE +*/ + +/** +** Preforms pre-fire checks based on the uints firecheck_flags +**/ +float turret_stdproc_firecheck() +{ + // This one just dont care =) + if (self.firecheck_flags & TFL_FIRECHECK_NO) return 1; + + // Ready? + if (self.firecheck_flags & TFL_FIRECHECK_REFIRE) + if (self.attack_finished_single >= 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) && (self.volly_counter != self.shot_volly)) + return 1; + + // Lack of zombies makes shooting dead things unnecessary :P + if (self.firecheck_flags & TFL_FIRECHECK_DEAD) + if (self.enemy.deadflag != DEAD_NO) return 0; + + // Plz stop killing the world! + if (self.firecheck_flags & TFL_FIRECHECK_WORLD) + if (self.enemy == world) return 0; + + // Own ammo? + if (self.firecheck_flags & TFL_FIRECHECK_OWM_AMMO) + if (self.ammo < self.shot_dmg) return 0; + + // Other's ammo? (support-supply units) + if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO) + if (self.enemy.ammo >= self.enemy.ammo_max) return 0; + + if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) + { + // Not close enougth? + //if (self.tur_dist_aimpos > self.target_range_fire) return 0; + + // To close? + if (self.tur_dist_aimpos < self.target_range_min) return 0; + } + + // Try to avoid FF? + if (self.firecheck_flags & TFL_FIRECHECK_AFF) + if (self.tur_impactent.team == self.team) return 0; + + // aim<->predicted impact + if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST) + if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist) + if (self.tur_impactent != self.enemy) + return 0; + + // Volly status + if (self.shot_volly > 1) + if (self.volly_counter == self.shot_volly) + if (self.ammo < (self.shot_dmg * self.shot_volly)) + return 0; + + if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) + if(self.tur_impactent != self.enemy) + return 0; + + return 1; +} + +/* + + TFL_TARGETSELECT_NO + + TFL_TARGETSELECT_LOS + + TFL_TARGETSELECT_PLAYERS + + TFL_TARGETSELECT_MISSILES + - TFL_TARGETSELECT_TRIGGERTARGET + + TFL_TARGETSELECT_ANGLELIMITS + + TFL_TARGETSELECT_RANGELIMTS + + TFL_TARGETSELECT_TEAMCHECK + - TFL_TARGETSELECT_NOBUILTIN + + TFL_TARGETSELECT_OWNTEAM +*/ + +/** +** Evaluate a entity for target valitity based on validate_flags +**/ +float turret_validate_target(entity e_turret,entity e_target,float validate_flags) +{ + vector v_tmp; + + //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN) + // return -0.5; + + if(e_target.owner == e_turret) + return -0.5; + + if not(checkpvs(e_target.origin, e_turret)) + return -1; + + if (!e_target)// == world) + return -2; + + if(g_onslaught) + if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + return - 3; + + if (validate_flags & TFL_TARGETSELECT_NO) + return -4; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return -5; + + // Cant touch this + if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) + return -6; + + // player + if (e_target.flags & FL_CLIENT) + { + if not (validate_flags & TFL_TARGETSELECT_PLAYERS) + return -7; + + if (e_target.deadflag != DEAD_NO) + return -8; + } + + // enemy turrets + if (validate_flags & TFL_TARGETSELECT_NOTURRETS) + if (e_target.turret_firefunc || e_target.owner.tur_head == e_target) + if(e_target.team != e_turret.team) // Dont break support units. + return -9; + + // Missile + if (e_target.flags & FL_PROJECTILE) + if not (validate_flags & TFL_TARGETSELECT_MISSILES) + return -10; + + // Team check + if (validate_flags & TFL_TARGETSELECT_TEAMCHECK) + { + if (validate_flags & TFL_TARGETSELECT_OWNTEAM) + { + if (e_target.team != e_turret.team) + return -11; + + if (e_turret.team != e_target.owner.team) + return -12; + } + else + { + if (e_target.team == e_turret.team) + return -13; + + if (e_turret.team == e_target.owner.team) + return -14; + } + } + + // Range limits? + tvt_dist = vlen(e_turret.origin - real_origin(e_target)); + if (validate_flags & TFL_TARGETSELECT_RANGELIMTS) + { + if (tvt_dist < e_turret.target_range_min) + return -15; + + if (tvt_dist > e_turret.target_range) + return -16; + } + + // Can we even aim this thing? + if(e_turret.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + { + tvt_thadv = angleofs3(e_turret.tur_head.origin,e_turret.angles + e_turret.tur_head.angles ,e_target); + //tvt_thadv = angleofs(e_turret.angles,e_target); + } + else + { + tvt_thadv = angleofs(e_turret.tur_head,e_target); + } + + tvt_tadv = shortangle_vxy(angleofs(e_turret,e_target),e_turret.angles); + tvt_thadf = vlen(tvt_thadv); + tvt_tadf = vlen(tvt_tadv); + + /* + if(validate_flags & TFL_TARGETSELECT_FOV) + { + if(e_turret.target_select_fov < tvt_thadf) + return -21; + } + */ + + if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS) + { + if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch) + return -17; + + if (fabs(tvt_tadv_y) > e_turret.aim_maxrot) + return -18; + } + + // Line of sight? + if (validate_flags & TFL_TARGETSELECT_LOS) + { + v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5); + traceline(e_turret.tur_shotorg,v_tmp,0,e_turret); + + if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos)) + return -19; + } + + if (e_target.classname == "grapplinghook") + return -20; + +#ifdef TURRET_DEBUG_TARGETSELECT + dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n"); +#endif + + return 1; +} + +entity turret_select_target() +{ + entity e; // target looper entity + float score; // target looper entity score + entity e_enemy; // currently best scoreing target + float m_score; // currently best scoreing target's score + float f; + + m_score = 0; + if(self.enemy) + if(turret_validate_target(self,self.enemy,self.target_validate_flags) > 0) + { + e_enemy = self.enemy; + m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias; + } + + e = findradius(self.origin,self.target_range); + + // Nothing to aim at? + if (!e) return world; + + while (e) + { + f = turret_validate_target(self,e,self.target_select_flags); + if (f > 0) + { + score = self.turret_score_target(self,e); + if ((score > m_score) && (score > 0)) + { + e_enemy = e; + m_score = score; + } + } + e = e.chain; + } + + return e_enemy; +} + +void turret_think() +{ + entity e; + + self.nextthink = time + self.ticrate; + + // ONS uses somewhat backwards linking. + if (teamplay) + { + if not (g_onslaught) + if (self.target) + { + e = find(world,targetname,self.target); + if (e != world) + self.team = e.team; + } + + if (self.team != self.tur_head.team) + turret_stdproc_respawn(); + } + + + if (cvar("g_turrets_reloadcvars") == 1) + { + e = nextent(world); + while (e) + { + if (e.tur_head != world) + { + + load_unit_settings(e,e.cvar_basename,1); + if(e.turret_postthink) + e.turret_postthink(); + } + + e = nextent(e); + } + + cvar_set("g_turrets_reloadcvars","0"); + } + +#ifdef TURRET_DEBUG + if (self.tur_dbg_tmr1 < time) + { + if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9); + paint_target(self,256,self.tur_dbg_rvec,0.9); + self.tur_dbg_tmr1 = time + 1; + } +#endif + + // Handle ammo + if not (self.spawnflags & TSF_NO_AMMO_REGEN) + if (self.ammo < self.ammo_max) + self.ammo = min(self.ammo + self.ammo_recharge,self.ammo_max); + + + // Inactive turrets needs to run the think loop, + // So they can handle animation and wake up if need be. + if not (self.tur_active) + { + turret_stdproc_track(); + return; + } + + //This is just wrong :| + if(self.deadflag != DEAD_NO) + { + dprint("WARNING: dead turret running the think function!\n"); + return; + } + + // This is typicaly used for zaping every target in range + // turret_fusionreactor uses this to recharge friendlys. + if (self.shoot_flags & TFL_SHOOT_HITALLVALID) + { + + // Do a self.turret_fire for every valid target. + e = findradius(self.origin,self.target_range); + while (e) + { + if (turret_validate_target(self,e,self.target_validate_flags)) + { + self.enemy = e; + + turret_do_updates(self); + + if (self.turret_firecheckfunc()) + turret_fire(); + } + + e = e.chain; + } + self.enemy = world; + } + else if(self.shoot_flags & TFL_SHOOT_CUSTOM) + { + // This one is doing something.. oddball. assume its handles what needs to be handled. + + // Predict? + if not((self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_stdproc_aim_generic(); + + // Turn & pitch? + if (!self.track_flags & TFL_TRACK_NO) + turret_stdproc_track(); + + turret_do_updates(self); + + // Fire? + if (self.turret_firecheckfunc()) + turret_fire(); + } + else + { + // Special case for volly always. if it fired once it must compleate the volly. + if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) + if(self.volly_counter != self.shot_volly) + { + // Predict or whatnot + if not((self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_stdproc_aim_generic(); + + // Turn & pitch + if (!self.track_flags & TFL_TRACK_NO) + turret_stdproc_track(); + + turret_do_updates(self); + + // Fire! + if (self.turret_firecheckfunc() != 0) + turret_fire(); + + if(self.turret_postthink) + self.turret_postthink(); + + return; + } + + // Check if we have a vailid enemy, and try to find one if we dont. + if( ((self.target_select_time + cvar("g_turrets_targetscan_maxdelay")) < time) + || (turret_validate_target(self,self.enemy,self.target_validate_flags) <= 0) ) + if not (self.target_select_time + cvar("g_turrets_targetscan_mindelay") > time) + { + self.enemy = turret_select_target(); + //if(self.enemy) + self.target_select_time = time; + + } + + + // No target, just go to idle, do any custom stuff and bail. + if (self.enemy == world) + { + // Turn & pitch + if (!self.track_flags & TFL_TRACK_NO) + turret_stdproc_track(); + + // do any per-turret stuff + if(self.turret_postthink) + self.turret_postthink(); + + // And bail. + return; + } + else + self.lip = time + cvar("g_turrets_aimidle_delay"); // Keep track of the last time we had a target. + + // Predict? + if not((self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_stdproc_aim_generic(); + + // Turn & pitch? + if (!self.track_flags & TFL_TRACK_NO) + turret_stdproc_track(); + + turret_do_updates(self); + // Fire? + if (self.turret_firecheckfunc()) + turret_fire(); + } + + // do any per-turret stuff + if(self.turret_postthink) + self.turret_postthink(); +} + +void turret_fire() +{ + if (cvar("g_turrets_nofire") != 0) + return; + + if ((!self.tur_active) || (self.deadflag != DEAD_NO)) + return; + + self.turret_firefunc(); + + self.attack_finished_single = time + self.shot_refire; + self.ammo = self.ammo - self.shot_dmg; + self.volly_counter = self.volly_counter - 1; + if (self.volly_counter <= 0) + { + self.volly_counter = self.shot_volly; + + if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) + self.enemy = world; + + if (self.shot_volly > 1) + self.attack_finished_single = time + self.shot_volly_refire; + } + + +#ifdef TURRET_DEBUG + if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_dbg_rvec, self.tur_impacttime + 0.25); +#endif +} + +void turret_stdproc_fire() +{ + dprint("^1Bang, ^3your dead^7 ",self.enemy.netname,"! ^1(turret with no real firefunc)\n"); +} + +/* + When .used a turret switch team to activator.team. + If activator is world, the turrets goes inactive. +*/ +void turret_stdproc_use() +{ + dprint("Turret ",self.netname, " used by ",activator.classname,"\n"); + + self.team = activator.team; + + if(self.team == 0) + self.tur_active = 0; + else + self.tur_active = 1; + +} + +void turret_link() +{ + //Net_LinkEntity(self, FALSE, 0, Turret_SendEntity); + self.think = turret_think; + self.nextthink = time; +} + +/* +* Standard turret initialization. use this! +* (unless you have a very good reason not to) +* if the return value is 0, the turret should be removed. +*/ +float turret_stdproc_init (string cvar_base_name, float csqc_shared) +{ + entity e,ee; + + if(csqc_shared) + { + dprint("turrets: csqc_shared requested but not implemented. expect strange things to happen.\n"); + csqc_shared = 0; + } + + // Are turrets allowed atm? + if (cvar("g_turrets") == 0) + return 0; + + // Better more then once then never. + // turret_gibs_precash(); + + // Terrainbase spawnflag. This puts a enlongated model + // under the turret, so it looks ok on uneaven surfaces. + if (self.spawnflags & TSF_TERRAINBASE) + { + entity tb; + //precache_model("models/turrets/terrainbase.md3"); + tb = spawn(); + setmodel(tb,"models/turrets/terrainbase.md3"); + setorigin(tb,self.origin); + tb.solid = SOLID_BBOX; + //makestatic(tb); + } + + self.cvar_basename = cvar_base_name; + load_unit_settings(self,self.cvar_basename,0); + + // Handle turret teams. + if (cvar("g_assult") != 0) + { + if (!self.team) + self.team = 14; // Assume turrets are on the defending side if not explicitly set otehrwize + } + else if (!teamplay) + self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team iso they dont kill eachother. + else if(g_onslaught && self.targetname) + { + e = find(world,target,self.targetname); + if(e != world) + { + self.team = e.team; + ee = e; + } + } + else if(!self.team) + self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team iso they dont kill eachother. + + + + /* + * Try to guess some reasonaly defaults + * for missing params and do sanety checks + * thise checks could produce some "interesting" results + * if it hits a glitch in my logic :P so try to set as mutch + * as possible beforehand. + */ + if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) + if (!self.ticrate) self.ticrate = 0.2; // Support units generaly dont need to have a high speed ai-loop + else + if (!self.ticrate) self.ticrate = 0.1; // 10 fps for normal turrets + + self.ticrate = bound(sys_ticrate,self.ticrate,60); // keep it sane + +// General stuff + if (self.netname == "") + self.netname = self.classname; + + if (!self.respawntime) + self.respawntime = 60; + self.respawntime = max(-1,self.respawntime); + + if (!self.health) + self.health = 1000; + self.tur_health = max(1,self.health); + + if (!self.turrcaps_flags) + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; + + if (!self.damage_flags) + self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; + +// Shot stuff. + if (!self.shot_refire) + self.shot_refire = 1; + self.shot_refire = bound(0.01,self.shot_refire,9999); + + if (!self.shot_dmg) + self.shot_dmg = self.shot_refire * 50; + self.shot_dmg = max(1,self.shot_dmg); + + if (!self.shot_radius) + self.shot_radius = self.shot_dmg * 0.5; + self.shot_radius = max(1,self.shot_radius); + + if (!self.shot_speed) + self.shot_speed = 2500; + self.shot_speed = max(1,self.shot_speed); + + if (!self.shot_spread) + self.shot_spread = 0.0125; + self.shot_spread = bound(0.0001,self.shot_spread,500); + + if (!self.shot_force) + self.shot_force = self.shot_dmg * 0.5 + self.shot_radius * 0.5; + self.shot_force = bound(0.001,self.shot_force,MAX_SHOT_DISTANCE * 0.5); + + if (!self.shot_volly) + self.shot_volly = 1; + self.shot_volly = bound(1,self.shot_volly,floor(self.ammo_max / self.shot_dmg)); + + if (!self.shot_volly_refire) + self.shot_volly_refire = self.shot_refire * self.shot_volly; + self.shot_volly_refire = bound(self.shot_refire,self.shot_volly_refire,60); + + if (!self.firecheck_flags) + self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | + TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK | + TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_WORLD; + +// Range stuff. + if (!self.target_range) + self.target_range = self.shot_speed * 0.5; + self.target_range = bound(0,self.target_range,MAX_SHOT_DISTANCE); + + if (!self.target_range_min) + self.target_range_min = self.shot_radius * 2; + self.target_range_min = bound(0,self.target_range_min,MAX_SHOT_DISTANCE); + + //if (!self.target_range_fire) + // self.target_range_fire = self.target_range * 0.8; + //self.target_range_fire = bound(0,self.target_range_fire,MAX_SHOT_DISTANCE); + + if (!self.target_range_optimal) + self.target_range_optimal = self.target_range * 0.5; + self.target_range_optimal = bound(0,self.target_range_optimal,MAX_SHOT_DISTANCE); + + +// Aim stuff. + if (!self.aim_maxrot) + self.aim_maxrot = 90; + self.aim_maxrot = bound(0,self.aim_maxrot,360); + + if (!self.aim_maxpitch) + self.aim_maxpitch = 20; + self.aim_maxpitch = bound(0,self.aim_maxpitch,90); + + if (!self.aim_speed) + self.aim_speed = 36; + self.aim_speed = bound(0.1,self.aim_speed, 1000); + + if (!self.aim_firetolerance_dist) + self.aim_firetolerance_dist = 5 + (self.shot_radius * 2); + self.aim_firetolerance_dist = bound(0.1,self.aim_firetolerance_dist,MAX_SHOT_DISTANCE); + + if (!self.aim_flags) + { + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + if(self.turrcaps_flags & TFL_TURRCAPS_RADIUSDMG) + self.aim_flags |= TFL_AIM_GROUND2; + } + + // Sill the most tested (and aim-effective) + if (!self.track_type) self.track_type = TFL_TRACKTYPE_STEPMOTOR; + + if (self.track_type != TFL_TRACKTYPE_STEPMOTOR) + { + // Fluid / Ineria mode. Looks mutch nicer, bit experimental & + // Can inmapt aim preformance alot. + // needs a bit diffrent aimspeed + + if (!self.aim_speed) + self.aim_speed = 180; + self.aim_speed = bound(0.1,self.aim_speed, 1000); + + if (!self.track_accel_pitch) + self.track_accel_pitch = 0.5; + + if (!self.track_accel_rot) + self.track_accel_rot = 0.5; + + if (!self.track_blendrate) + self.track_blendrate = 0.35; + } + + if (!self.track_flags) + self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT; + + +// Target selection stuff. + if (!self.target_select_rangebias) + self.target_select_rangebias = 1; + self.target_select_rangebias = bound(-10,self.target_select_rangebias,10); + + if (!self.target_select_samebias) + self.target_select_samebias = 1; + self.target_select_samebias = bound(-10,self.target_select_samebias,10); + + if (!self.target_select_anglebias) + self.target_select_anglebias = 1; + self.target_select_anglebias = bound(-10,self.target_select_anglebias,10); + + if (!self.target_select_missilebias) + self.target_select_missilebias = -10; + + self.target_select_missilebias = bound(-10,self.target_select_missilebias,10); + self.target_select_playerbias = bound(-10,self.target_select_playerbias,10); + + if (!self.target_select_flags) + { + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK + | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS; + + if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL) + self.target_select_flags |= TFL_TARGETSELECT_MISSILES; + + if (self.turrcaps_flags & TFL_TURRCAPS_PLAYERKILL) + self.target_select_flags |= TFL_TARGETSELECT_PLAYERS; + //else + // self.target_select_flags = TFL_TARGETSELECT_NO; + } + + self.target_validate_flags = self.target_select_flags; + + +// Ammo stuff + if (!self.ammo_max) + self.ammo_max = self.shot_dmg * 10; + self.ammo_max = max(self.shot_dmg,self.ammo_max); + + if (!self.ammo) + self.ammo = self.shot_dmg * 5; + self.ammo = bound(0,self.ammo,self.ammo_max); + + if (!self.ammo_recharge) + self.ammo_recharge = self.shot_dmg * 0.5; + self.ammo_recharge = max(0,self.ammo_recharge); + + // Convert the recharge from X per sec to X per ticrate + self.ammo_recharge = self.ammo_recharge * self.ticrate; + + if (!self.ammo_flags) + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; + +// Damage stuff + if(self.spawnflags & TSL_NO_RESPAWN) + if not (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + self.damage_flags |= TFL_DMG_DEATH_NORESPAWN; + +// Offsets & origins + if (!self.tur_shotorg) self.tur_shotorg = '50 0 50'; + +// End of default & sanety checks, start building the turret. + +// Spawn extra bits + self.tur_head = spawn(); + self.tur_head.netname = self.tur_head.classname = "turret_head"; + self.tur_head.team = self.team; + self.tur_head.owner = self; + + // Defend mode? + if(!self.tur_defend) + if (self.target != "") + { + self.tur_defend = find(world, targetname, self.target); + if (self.tur_defend == world) + { + self.target = ""; + dprint("Turret has invalid defendpoint!\n"); + } + } + +// Put pices in place + if not (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + setorigin(self.tur_head,self.origin); + + // In target defend mode, aim on the spot to defend when idle. + if(self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + { + if (self.tur_defend) + self.idle_aim = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend); + else + self.idle_aim = '0 0 0'; + } + else + { + if (self.tur_defend) + self.idle_aim = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend); + else + self.idle_aim = self.angles; + } + + if not (self.turrcaps_flags & TFL_TURRCAPS_HEADATTACHED) + self.tur_head.angles = self.idle_aim; + + if (!self.health) + self.health = 150; + + self.tur_health = self.health; + self.tur_head.health = self.health; + + self.solid = SOLID_BBOX; + self.tur_head.solid = SOLID_BBOX; + + self.takedamage = DAMAGE_AIM; + self.tur_head.takedamage = DAMAGE_AIM; + + self.movetype = MOVETYPE_NOCLIP; + self.tur_head.movetype = MOVETYPE_NOCLIP; + + // Team color + if (self.team == COLOR_TEAM1) self.colormod = '1.4 0.8 0.8'; + if (self.team == COLOR_TEAM2) self.colormod = '0.8 0.8 1.4'; + + // Attach stdprocs. override when and what needed + if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) + { + self.turret_score_target = turret_stdproc_targetscore_support; + self.turret_firecheckfunc = turret_stdproc_firecheck; + self.turret_firefunc = turret_stdproc_fire; + //self.turret_postthink = turret_stdproc_nothing; + self.event_damage = turret_stdproc_damage; + self.tur_head.event_damage = turret_stdproc_damage; + } + else + { + self.turret_score_target = turret_stdproc_targetscore_generic; + self.turret_firecheckfunc = turret_stdproc_firecheck; + self.turret_firefunc = turret_stdproc_fire; + //self.turret_postthink = turret_stdproc_nothing; + self.event_damage = turret_stdproc_damage; + self.tur_head.event_damage = turret_stdproc_damage; + //self.turret_addtarget = turret_stdproc_false; + } + + self.use = turret_stdproc_use; + self.bot_attack = TRUE; + + // Initiate the main AI loop + if(csqc_shared) + self.think = turret_link; + else + self.think = turret_think; + + self.nextthink = time + self.ticrate; + + self.tur_head.team = self.team; + self.view_ofs = '0 0 0'; + +#ifdef TURRET_DEBUG + self.tur_dbg_start = self.nextthink; + while (vlen(self.tur_dbg_rvec) < 2) + self.tur_dbg_rvec = randomvec() * 4; + + self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x); + self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y); + self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z); +#endif + + // Its all good. + self.classname = "turret_main"; + + self.tur_active = 1; + + // In ONS mode, and linked to a ONS ent. need to call the use to set team. + if (g_onslaught && ee) + { + activator = ee; + self.use(); + } + + return 1; +} + + diff --git a/data/qcsrc/server/tturrets/system/system_misc.qc b/data/qcsrc/server/tturrets/system/system_misc.qc index 7c9cfbfe6..ec5f2fe6e 100644 --- a/data/qcsrc/server/tturrets/system/system_misc.qc +++ b/data/qcsrc/server/tturrets/system/system_misc.qc @@ -1,406 +1,406 @@ -//--// Some support routines //--// - -#define anglemodss(a) (a - floor(a / 360) * 360) -float(float v) anglemods = -{ - v = v - 360 * floor(v / 360); - return v; -}; -float safeangle(float a) -{ - if((a > -361) && (a < 361)) - return a; - - a -= (360 * floor(a / 360)); - - return a; -} - -float shortangle_f(float ang1,float ang2) -{ - if(ang1 > ang2) - { - if(ang1 > 180) - return ang1 - 360; - } - else - { - if(ang1 < -180) - return ang1 + 360; - } - - return ang1; -} - -vector shortangle_v(vector ang1,vector ang2) -{ - vector vtmp; - - vtmp_x = shortangle_f(ang1_x,ang2_x); - vtmp_y = shortangle_f(ang1_y,ang2_y); - vtmp_z = shortangle_f(ang1_z,ang2_z); - - return vtmp; -} - -vector shortangle_vxy(vector ang1,vector ang2) -{ - vector vtmp; - - vtmp_x = shortangle_f(ang1_x,ang2_x); - vtmp_y = shortangle_f(ang1_y,ang2_y); - - return vtmp; -} - -// Get real origin -vector real_origin(entity ent) -{ - entity e; - vector v; - - e = ent.tag_entity; - while(e) - { - // v = v + e.origin; - v = v + ((e.absmin + e.absmax) * 0.5); - e = e.tag_entity; - } - //v = v + ent.origin; - v = v + ((ent.absmin + ent.absmax) * 0.5); - return v; -} - -// Plug this into wherever precache is done. -void g_turrets_common_precash() -{ - precache_model ("models/turrets/c512.md3"); - precache_model ("models/marker.md3"); -} - -void SUB_Remove(); -void marker_think() -{ - if(self.cnt) - if(self.cnt < time) - { - self.think = SUB_Remove; - self.nextthink = time; - return; - } - - self.frame += 1; - if(self.frame > 29) - self.frame = 0; - - self.nextthink = time; -} - -void mark_error(vector where,float lifetime) -{ - entity err; - - err = spawn(); - err.classname = "error_marker"; - setmodel(err,"models/marker.md3"); - setorigin(err,where); - err.movetype = MOVETYPE_NONE; - err.think = marker_think; - err.nextthink = time; - err.skin = 0; - if(lifetime) - err.cnt = lifetime + time; -} - -void mark_info(vector where,float lifetime) -{ - entity err; - - err = spawn(); - err.classname = "info_marker"; - setmodel(err,"models/marker.md3"); - setorigin(err,where); - err.movetype = MOVETYPE_NONE; - err.think = marker_think; - err.nextthink = time; - err.skin = 1; - if(lifetime) - err.cnt = lifetime + time; -} - -entity mark_misc(vector where,float lifetime) -{ - entity err; - - err = spawn(); - err.classname = "mark_misc"; - setmodel(err,"models/marker.md3"); - setorigin(err,where); - err.movetype = MOVETYPE_NONE; - err.think = marker_think; - err.nextthink = time; - err.skin = 3; - if(lifetime) - err.cnt = lifetime + time; - return err; -} - -/* -* Paint a v_color colord circle on target onwho -* that fades away over f_time -*/ -void paint_target(entity onwho, float f_size, vector v_color, float f_time) -{ - entity e; - - e = spawn(); - setmodel(e, "models/turrets/c512.md3"); // precision set above - e.scale = (f_size/512); - //setsize(e, '0 0 0', '0 0 0'); - //setattachment(e,onwho,""); - setorigin(e,onwho.origin + '0 0 1'); - e.alpha = 0.15; - e.movetype = MOVETYPE_FLY; - - e.velocity = (v_color * 32); // + '0 0 1' * 64; - - e.colormod = v_color; - SUB_SetFade(e,time,f_time); -} - -void paint_target2(entity onwho, float f_size, vector v_color, float f_time) -{ - entity e; - - e = spawn(); - setmodel(e, "models/turrets/c512.md3"); // precision set above - e.scale = (f_size/512); - setsize(e, '0 0 0', '0 0 0'); - - setorigin(e,onwho.origin + '0 0 1'); - e.alpha = 0.15; - e.movetype = MOVETYPE_FLY; - - e.velocity = (v_color * 32); // + '0 0 1' * 64; - e.avelocity_x = -128; - - e.colormod = v_color; - SUB_SetFade(e,time,f_time); -} - -void paint_target3(vector where, float f_size, vector v_color, float f_time) -{ - entity e; - e = spawn(); - setmodel(e, "models/turrets/c512.md3"); // precision set above - e.scale = (f_size/512); - setsize(e, '0 0 0', '0 0 0'); - setorigin(e,where+ '0 0 1'); - e.movetype = MOVETYPE_NONE; - e.velocity = '0 0 0'; - e.colormod = v_color; - SUB_SetFade(e,time,f_time); -} - -/* -* Return the angle between two enteties -*/ -vector angleofs(entity from, entity to) -{ - vector v_res; - - // makevectors(from.angles); - v_res = normalize(to.origin - from.origin); - v_res = vectoangles(v_res); - v_res = v_res - from.angles; - - if (v_res_x < 0) v_res_x += 360; - if (v_res_x > 180) v_res_x -= 360; - - if (v_res_y < 0) v_res_y += 360; - if (v_res_y > 180) v_res_y -= 360; - - return v_res; -} - -vector angleofs2(entity from, vector to) -{ - vector v_res; - - // makevectors(from.angles); - v_res = normalize(to - from.origin); - v_res = vectoangles(v_res); - v_res = v_res - from.angles; - - if (v_res_x < 0) v_res_x += 360; - if (v_res_x > 180) v_res_x -= 360; - - if (v_res_y < 0) v_res_y += 360; - if (v_res_y > 180) v_res_y -= 360; - - return v_res; -} - -vector angleofs3(vector from,vector from_a, entity to) -{ - vector v_res; - - // makevectors(from.angles); - v_res = normalize(to.origin - from); - v_res = vectoangles(v_res); - v_res = v_res - from_a; - - if (v_res_x < 0) v_res_x += 360; - if (v_res_x > 180) v_res_x -= 360; - - if (v_res_y < 0) v_res_y += 360; - if (v_res_y > 180) v_res_y -= 360; - - return v_res; -} - -float turret_tag_setup() -{ - if(!self.tur_head) - { - dprint("Call to turret_tag_setup with self.tur_head missing!\n"); - self.tur_shotorg = '0 0 0'; - return 0; - } - - setorigin(self.tur_head,gettaginfo(self,gettagindex(self,"tag_head"))); - self.tur_shotorg = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); - - v_forward = normalize(v_forward); - - return 1; -} - -float turret_tag_fire_update() -{ - if(!self.tur_head) - { - dprint("Call to turret_tag_fire_update with self.tur_head missing!\n"); - self.tur_shotorg = '0 0 0'; - return 0; - } - - self.tur_shotorg = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); - v_forward = normalize(v_forward); - - //dprint("update: tur_shotorg: ",vtos(self.tur_shotorg)," origin:", vtos(self.tur_head.origin), " angels: ", vtos(self.tur_head.angles),"\n"); - - return 1; -} - -void FireImoBeam (vector start,vector end,vector smin,vector smax, - float bforce,float f_dmg,float f_velfactor, float deathtype) - -{ - local vector hitloc, force, endpoint, dir; - local entity ent; - - dir = normalize(end - start); - force = dir * bforce; - - // go a little bit into the wall because we need to hit this wall later - end = end + dir; - - // trace multiple times until we hit a wall, each obstacle will be made unsolid. - // note down which entities were hit so we can damage them later - while (1) - { - tracebox(start, smin, smax, end, FALSE, self); - - // if it is world we can't hurt it so stop now - if (trace_ent == world || trace_fraction == 1) - break; - - if (trace_ent.solid == SOLID_BSP) - break; - - // make the entity non-solid so we can hit the next one - trace_ent.railgunhit = TRUE; - trace_ent.railgunhitloc = end; - trace_ent.railgunhitsolidbackup = trace_ent.solid; - - // stop if this is a wall - - // make the entity non-solid - trace_ent.solid = SOLID_NOT; - } - - endpoint = trace_endpos; - - // find all the entities the railgun hit and restore their solid state - ent = findfloat(world, railgunhit, TRUE); - while (ent) - { - // restore their solid type - ent.solid = ent.railgunhitsolidbackup; - ent = findfloat(ent, railgunhit, TRUE); - } - - // find all the entities the railgun hit and hurt them - ent = findfloat(world, railgunhit, TRUE); - while (ent) - { - // get the details we need to call the damage function - hitloc = ent.railgunhitloc; - ent.railgunhitloc = '0 0 0'; - ent.railgunhitsolidbackup = SOLID_NOT; - ent.railgunhit = FALSE; - - // apply the damage - if (ent.takedamage) - { - Damage (ent, self, self, f_dmg, deathtype, hitloc, force); - ent.velocity = ent.velocity * f_velfactor; - //ent.alpha = 0.25 + random() * 0.75; - } - - // advance to the next entity - ent = findfloat(ent, railgunhit, TRUE); - } - trace_endpos = endpoint; -} - -void turrets_precash() -{ - precache_model ("models/turrets/base-gib1.md3"); - precache_model ("models/turrets/base-gib2.md3"); - precache_model ("models/turrets/base-gib3.md3"); - precache_model ("models/turrets/base-gib4.md3"); - - precache_model ("models/turrets/head-gib1.md3"); - precache_model ("models/turrets/head-gib2.md3"); - precache_model ("models/turrets/head-gib3.md3"); - precache_model ("models/turrets/head-gib4.md3"); - precache_model ("models/turrets/terrainbase.md3"); - - //precache_model ("models/turrets/base.md3"); - //precache_model ("models/turrets/flac.md3"); - //precache_model ("models/turrets/pd_proj.md3"); - //precache_model ("models/turrets/reactor.md3"); - //precache_model ("models/turrets/mlrs_rocket.md3"); - //precache_model ("models/turrets/hellion.md3"); - //precache_model ("models/turrets/hunter2.md3"); - //precache_model ("models/turrets/hk.md3"); - //precache_model ("models/turrets/machinegun.md3"); - //precache_model ("models/turrets/rocket.md3"); - //precache_model ("models/turrets/mlrs.md3"); - //precache_model ("models/turrets/phaser.md3"); - //precache_model ("models/turrets/phaser_beam.md3"); - //precache_model ("models/turrets/plasmad.md3"); - //precache_model ("models/turrets/plasma.md3"); - //precache_model ("models/turrets/tesla_head.md3"); - //precache_model ("models/turrets/tesla_base.md3"); - -#if 0 - precache_model ("models/turrets/c512.md3"); - precache_model ("models/pathlib/goodsquare.md3"); - precache_model ("models/pathlib/badsquare.md3"); - precache_model ("models/pathlib/square.md3"); - precache_model ("models/pathlib/edge.md3"); -#endif -} +//--// Some support routines //--// + +#define anglemodss(a) (a - floor(a / 360) * 360) +float(float v) anglemods = +{ + v = v - 360 * floor(v / 360); + return v; +}; +float safeangle(float a) +{ + if((a > -361) && (a < 361)) + return a; + + a -= (360 * floor(a / 360)); + + return a; +} + +float shortangle_f(float ang1,float ang2) +{ + if(ang1 > ang2) + { + if(ang1 > 180) + return ang1 - 360; + } + else + { + if(ang1 < -180) + return ang1 + 360; + } + + return ang1; +} + +vector shortangle_v(vector ang1,vector ang2) +{ + vector vtmp; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + vtmp_z = shortangle_f(ang1_z,ang2_z); + + return vtmp; +} + +vector shortangle_vxy(vector ang1,vector ang2) +{ + vector vtmp; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + + return vtmp; +} + +// Get real origin +vector real_origin(entity ent) +{ + entity e; + vector v; + + e = ent.tag_entity; + while(e) + { + // v = v + e.origin; + v = v + ((e.absmin + e.absmax) * 0.5); + e = e.tag_entity; + } + //v = v + ent.origin; + v = v + ((ent.absmin + ent.absmax) * 0.5); + return v; +} + +// Plug this into wherever precache is done. +void g_turrets_common_precash() +{ + precache_model ("models/turrets/c512.md3"); + precache_model ("models/marker.md3"); +} + +void SUB_Remove(); +void marker_think() +{ + if(self.cnt) + if(self.cnt < time) + { + self.think = SUB_Remove; + self.nextthink = time; + return; + } + + self.frame += 1; + if(self.frame > 29) + self.frame = 0; + + self.nextthink = time; +} + +void mark_error(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "error_marker"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 0; + if(lifetime) + err.cnt = lifetime + time; +} + +void mark_info(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "info_marker"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 1; + if(lifetime) + err.cnt = lifetime + time; +} + +entity mark_misc(vector where,float lifetime) +{ + entity err; + + err = spawn(); + err.classname = "mark_misc"; + setmodel(err,"models/marker.md3"); + setorigin(err,where); + err.movetype = MOVETYPE_NONE; + err.think = marker_think; + err.nextthink = time; + err.skin = 3; + if(lifetime) + err.cnt = lifetime + time; + return err; +} + +/* +* Paint a v_color colord circle on target onwho +* that fades away over f_time +*/ +void paint_target(entity onwho, float f_size, vector v_color, float f_time) +{ + entity e; + + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + //setsize(e, '0 0 0', '0 0 0'); + //setattachment(e,onwho,""); + setorigin(e,onwho.origin + '0 0 1'); + e.alpha = 0.15; + e.movetype = MOVETYPE_FLY; + + e.velocity = (v_color * 32); // + '0 0 1' * 64; + + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} + +void paint_target2(entity onwho, float f_size, vector v_color, float f_time) +{ + entity e; + + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + setsize(e, '0 0 0', '0 0 0'); + + setorigin(e,onwho.origin + '0 0 1'); + e.alpha = 0.15; + e.movetype = MOVETYPE_FLY; + + e.velocity = (v_color * 32); // + '0 0 1' * 64; + e.avelocity_x = -128; + + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} + +void paint_target3(vector where, float f_size, vector v_color, float f_time) +{ + entity e; + e = spawn(); + setmodel(e, "models/turrets/c512.md3"); // precision set above + e.scale = (f_size/512); + setsize(e, '0 0 0', '0 0 0'); + setorigin(e,where+ '0 0 1'); + e.movetype = MOVETYPE_NONE; + e.velocity = '0 0 0'; + e.colormod = v_color; + SUB_SetFade(e,time,f_time); +} + +/* +* Return the angle between two enteties +*/ +vector angleofs(entity from, entity to) +{ + vector v_res; + + // makevectors(from.angles); + v_res = normalize(to.origin - from.origin); + v_res = vectoangles(v_res); + v_res = v_res - from.angles; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +vector angleofs2(entity from, vector to) +{ + vector v_res; + + // makevectors(from.angles); + v_res = normalize(to - from.origin); + v_res = vectoangles(v_res); + v_res = v_res - from.angles; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +vector angleofs3(vector from,vector from_a, entity to) +{ + vector v_res; + + // makevectors(from.angles); + v_res = normalize(to.origin - from); + v_res = vectoangles(v_res); + v_res = v_res - from_a; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +float turret_tag_setup() +{ + if(!self.tur_head) + { + dprint("Call to turret_tag_setup with self.tur_head missing!\n"); + self.tur_shotorg = '0 0 0'; + return 0; + } + + setorigin(self.tur_head,gettaginfo(self,gettagindex(self,"tag_head"))); + self.tur_shotorg = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); + + v_forward = normalize(v_forward); + + return 1; +} + +float turret_tag_fire_update() +{ + if(!self.tur_head) + { + dprint("Call to turret_tag_fire_update with self.tur_head missing!\n"); + self.tur_shotorg = '0 0 0'; + return 0; + } + + self.tur_shotorg = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire")); + v_forward = normalize(v_forward); + + //dprint("update: tur_shotorg: ",vtos(self.tur_shotorg)," origin:", vtos(self.tur_head.origin), " angels: ", vtos(self.tur_head.angles),"\n"); + + return 1; +} + +void FireImoBeam (vector start,vector end,vector smin,vector smax, + float bforce,float f_dmg,float f_velfactor, float deathtype) + +{ + local vector hitloc, force, endpoint, dir; + local entity ent; + + dir = normalize(end - start); + force = dir * bforce; + + // go a little bit into the wall because we need to hit this wall later + end = end + dir; + + // trace multiple times until we hit a wall, each obstacle will be made unsolid. + // note down which entities were hit so we can damage them later + while (1) + { + tracebox(start, smin, smax, end, FALSE, self); + + // if it is world we can't hurt it so stop now + if (trace_ent == world || trace_fraction == 1) + break; + + if (trace_ent.solid == SOLID_BSP) + break; + + // make the entity non-solid so we can hit the next one + trace_ent.railgunhit = TRUE; + trace_ent.railgunhitloc = end; + trace_ent.railgunhitsolidbackup = trace_ent.solid; + + // stop if this is a wall + + // make the entity non-solid + trace_ent.solid = SOLID_NOT; + } + + endpoint = trace_endpos; + + // find all the entities the railgun hit and restore their solid state + ent = findfloat(world, railgunhit, TRUE); + while (ent) + { + // restore their solid type + ent.solid = ent.railgunhitsolidbackup; + ent = findfloat(ent, railgunhit, TRUE); + } + + // find all the entities the railgun hit and hurt them + ent = findfloat(world, railgunhit, TRUE); + while (ent) + { + // get the details we need to call the damage function + hitloc = ent.railgunhitloc; + ent.railgunhitloc = '0 0 0'; + ent.railgunhitsolidbackup = SOLID_NOT; + ent.railgunhit = FALSE; + + // apply the damage + if (ent.takedamage) + { + Damage (ent, self, self, f_dmg, deathtype, hitloc, force); + ent.velocity = ent.velocity * f_velfactor; + //ent.alpha = 0.25 + random() * 0.75; + } + + // advance to the next entity + ent = findfloat(ent, railgunhit, TRUE); + } + trace_endpos = endpoint; +} + +void turrets_precash() +{ + precache_model ("models/turrets/base-gib1.md3"); + precache_model ("models/turrets/base-gib2.md3"); + precache_model ("models/turrets/base-gib3.md3"); + precache_model ("models/turrets/base-gib4.md3"); + + precache_model ("models/turrets/head-gib1.md3"); + precache_model ("models/turrets/head-gib2.md3"); + precache_model ("models/turrets/head-gib3.md3"); + precache_model ("models/turrets/head-gib4.md3"); + precache_model ("models/turrets/terrainbase.md3"); + + //precache_model ("models/turrets/base.md3"); + //precache_model ("models/turrets/flac.md3"); + //precache_model ("models/turrets/pd_proj.md3"); + //precache_model ("models/turrets/reactor.md3"); + //precache_model ("models/turrets/mlrs_rocket.md3"); + //precache_model ("models/turrets/hellion.md3"); + //precache_model ("models/turrets/hunter2.md3"); + //precache_model ("models/turrets/hk.md3"); + //precache_model ("models/turrets/machinegun.md3"); + //precache_model ("models/turrets/rocket.md3"); + //precache_model ("models/turrets/mlrs.md3"); + //precache_model ("models/turrets/phaser.md3"); + //precache_model ("models/turrets/phaser_beam.md3"); + //precache_model ("models/turrets/plasmad.md3"); + //precache_model ("models/turrets/plasma.md3"); + //precache_model ("models/turrets/tesla_head.md3"); + //precache_model ("models/turrets/tesla_base.md3"); + +#if 0 + precache_model ("models/turrets/c512.md3"); + precache_model ("models/pathlib/goodsquare.md3"); + precache_model ("models/pathlib/badsquare.md3"); + precache_model ("models/pathlib/square.md3"); + precache_model ("models/pathlib/edge.md3"); +#endif +} diff --git a/data/qcsrc/server/tturrets/system/system_scoreprocs.qc b/data/qcsrc/server/tturrets/system/system_scoreprocs.qc index 230600190..c78a3e018 100644 --- a/data/qcsrc/server/tturrets/system/system_scoreprocs.qc +++ b/data/qcsrc/server/tturrets/system/system_scoreprocs.qc @@ -1,166 +1,166 @@ -/* -.float target_select_flags; /// target selection flags -float TFL_TARGETSELECT_NO = 1; /// Dont select a target on its own. -float TFL_TARGETSELECT_LOS = 2; /// Need line of sight -float TFL_TARGETSELECT_PLAYERS = 4; /// Players are valid targets -float TFL_TARGETSELECT_MISSILES = 8; /// Missiles are valid targets -float TFL_TARGETSELECT_TRIGGERTARGET = 16; /// Responds to turret_trigger_target events -float TFL_TARGETSELECT_ANGLELIMITS = 32; /// Angular limitations of turret head limits target selection -float TFL_TARGETSELECT_RANGELIMTS = 64; /// Range limits apply in targetselection -float TFL_TARGETSELECT_TEAMCHECK = 128; /// Consider team own <-> targets team -float TFL_TARGETSELECT_NOBUILTIN = 256; /// Cant select targets on its own. needs to be triggerd or slaved. -float TFL_TARGETSELECT_OWNTEAM = 512; -*/ - -float turret_stdproc_targetscore_support(entity e_turret,entity e_target) -{ - float score; // Total score - float s_score,d_score; - - if (e_turret.enemy == e_target) s_score = 1; - - d_score = min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist); - - score = (d_score * e_turret.target_select_rangebias) + - (s_score * e_turret.target_select_samebias); - - return score; -} - -/* -* Generic bias aware score system. -*/ -float turret_stdproc_targetscore_generic(entity e_turret,entity e_target) -{ - //vector v_tmp; - float d_dist; // Defendmode Distance - - float score; // Total score - - float d_score; // Distance score - float a_score; // Angular score - float m_score; // missile score - float p_score; // player score - //float da_score; // Distance from aimpoint score - - float ikr; // ideal kill range - - if(!e_target) return 0; - - //if (e_target == e_turret.enemy) s_score = 1; - - if (e_turret.tur_defend) - { - d_dist = vlen(real_origin(e_target) - e_turret.tur_defend.origin); - ikr = vlen(e_turret.origin - e_turret.tur_defend.origin); - d_score = 1 - d_dist / e_turret.target_range; - } - else - { - // Make a normlized value base on the targets distance from our optimal killzone - ikr = e_turret.target_range_optimal; - d_score = min(ikr,tvt_dist) / max(ikr,tvt_dist); - } - - /* - // Determine the maximum time it could take this turrent to aim at someting. - max_aim_delay = (max(e_turret.aim_maxrot,e_turret.aim_maxpitch) / e_turret.aim_speed * 2); - - // Find out how long it would take to aim at this taget. - aim_delay = (thadf+0.01) / e_turret.aim_speed; - - // Turn this info into a normalized value. - aim_delay = (min(max_aim_delay,aim_delay) / max_aim_delay); - a_score = 1 - aim_delay; - */ - - //a_score = 1 - (tvt_thadf / max(e_turret.aim_maxrot,e_turret.aim_maxpitch)); - a_score = 1 - tvt_thadf / e_turret.aim_maxrot; - - if ((e_turret.target_select_missilebias > 0) && (e_target.flags & FL_PROJECTILE)) - m_score = 1; - - if ((e_turret.target_select_playerbias > 0) && (e_target.flags & FL_CLIENT)) - p_score = 1; - - d_score = max(d_score,0); - a_score = max(a_score,0); - m_score = max(m_score,0); - p_score = max(p_score,0); - - score = (d_score * e_turret.target_select_rangebias) + - (a_score * e_turret.target_select_anglebias) + - (m_score * e_turret.target_select_missilebias) + - (p_score * e_turret.target_select_playerbias); - - if(e_turret.target_range < vlen(e_turret.tur_shotorg - real_origin(e_target))) - { - dprint("Wtf?\n"); - score *= 0.001; - } - -#ifdef TURRET_DEBUG - string sd,sa,sm,sp,ss; - string sdt,sat,smt,spt; - - sd = ftos(d_score); - d_score *= e_turret.target_select_rangebias; - sdt = ftos(d_score); - - //sv = ftos(v_score); - //v_score *= e_turret.target_select_samebias; - //svt = ftos(v_score); - - sa = ftos(a_score); - a_score *= e_turret.target_select_anglebias; - sat = ftos(a_score); - - sm = ftos(m_score); - m_score *= e_turret.target_select_missilebias; - smt = ftos(m_score); - - sp = ftos(p_score); - p_score *= e_turret.target_select_playerbias; - spt = ftos(p_score); - - - ss = ftos(score); - bprint("^3Target scores^7 \[ ",e_turret.netname, " \] ^3for^7 \[ ", e_target.netname," \]\n"); - bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n"); - bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n"); - bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n"); - bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n"); - bprint("^3Total (w/bias):\[^1",ss,"\]\n"); - -#endif - - return score; -} - -/* -float turret_stdproc_targetscore_close(entity e_turret,entity e_target) -{ - return 1 - (tvt_dist / e_turret.target_range); -} - -float turret_stdproc_targetscore_far (entity e_turret,entity e_target) -{ - return tvt_dist / e_turret.target_range; -} - -float turret_stdproc_targetscore_optimal(entity e_turret,entity e_target) -{ - return min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist); -} - -float turret_stdproc_score_angular(entity e_turret,entity e_target) -{ - return 1 - (tvt_thadf / e_turret.aim_maxrot); -} - -float turret_stdproc_targetscore_defend(entity e_turret,entity e_target) -{ - return 0; - //min(e_target.origin,e_turret.tur_defend.origin) / max(e_target.origin,e_turret.tur_defend.origin); -} -*/ +/* +.float target_select_flags; /// target selection flags +float TFL_TARGETSELECT_NO = 1; /// Dont select a target on its own. +float TFL_TARGETSELECT_LOS = 2; /// Need line of sight +float TFL_TARGETSELECT_PLAYERS = 4; /// Players are valid targets +float TFL_TARGETSELECT_MISSILES = 8; /// Missiles are valid targets +float TFL_TARGETSELECT_TRIGGERTARGET = 16; /// Responds to turret_trigger_target events +float TFL_TARGETSELECT_ANGLELIMITS = 32; /// Angular limitations of turret head limits target selection +float TFL_TARGETSELECT_RANGELIMTS = 64; /// Range limits apply in targetselection +float TFL_TARGETSELECT_TEAMCHECK = 128; /// Consider team own <-> targets team +float TFL_TARGETSELECT_NOBUILTIN = 256; /// Cant select targets on its own. needs to be triggerd or slaved. +float TFL_TARGETSELECT_OWNTEAM = 512; +*/ + +float turret_stdproc_targetscore_support(entity e_turret,entity e_target) +{ + float score; // Total score + float s_score,d_score; + + if (e_turret.enemy == e_target) s_score = 1; + + d_score = min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist); + + score = (d_score * e_turret.target_select_rangebias) + + (s_score * e_turret.target_select_samebias); + + return score; +} + +/* +* Generic bias aware score system. +*/ +float turret_stdproc_targetscore_generic(entity e_turret,entity e_target) +{ + //vector v_tmp; + float d_dist; // Defendmode Distance + + float score; // Total score + + float d_score; // Distance score + float a_score; // Angular score + float m_score; // missile score + float p_score; // player score + //float da_score; // Distance from aimpoint score + + float ikr; // ideal kill range + + if(!e_target) return 0; + + //if (e_target == e_turret.enemy) s_score = 1; + + if (e_turret.tur_defend) + { + d_dist = vlen(real_origin(e_target) - e_turret.tur_defend.origin); + ikr = vlen(e_turret.origin - e_turret.tur_defend.origin); + d_score = 1 - d_dist / e_turret.target_range; + } + else + { + // Make a normlized value base on the targets distance from our optimal killzone + ikr = e_turret.target_range_optimal; + d_score = min(ikr,tvt_dist) / max(ikr,tvt_dist); + } + + /* + // Determine the maximum time it could take this turrent to aim at someting. + max_aim_delay = (max(e_turret.aim_maxrot,e_turret.aim_maxpitch) / e_turret.aim_speed * 2); + + // Find out how long it would take to aim at this taget. + aim_delay = (thadf+0.01) / e_turret.aim_speed; + + // Turn this info into a normalized value. + aim_delay = (min(max_aim_delay,aim_delay) / max_aim_delay); + a_score = 1 - aim_delay; + */ + + //a_score = 1 - (tvt_thadf / max(e_turret.aim_maxrot,e_turret.aim_maxpitch)); + a_score = 1 - tvt_thadf / e_turret.aim_maxrot; + + if ((e_turret.target_select_missilebias > 0) && (e_target.flags & FL_PROJECTILE)) + m_score = 1; + + if ((e_turret.target_select_playerbias > 0) && (e_target.flags & FL_CLIENT)) + p_score = 1; + + d_score = max(d_score,0); + a_score = max(a_score,0); + m_score = max(m_score,0); + p_score = max(p_score,0); + + score = (d_score * e_turret.target_select_rangebias) + + (a_score * e_turret.target_select_anglebias) + + (m_score * e_turret.target_select_missilebias) + + (p_score * e_turret.target_select_playerbias); + + if(e_turret.target_range < vlen(e_turret.tur_shotorg - real_origin(e_target))) + { + dprint("Wtf?\n"); + score *= 0.001; + } + +#ifdef TURRET_DEBUG + string sd,sa,sm,sp,ss; + string sdt,sat,smt,spt; + + sd = ftos(d_score); + d_score *= e_turret.target_select_rangebias; + sdt = ftos(d_score); + + //sv = ftos(v_score); + //v_score *= e_turret.target_select_samebias; + //svt = ftos(v_score); + + sa = ftos(a_score); + a_score *= e_turret.target_select_anglebias; + sat = ftos(a_score); + + sm = ftos(m_score); + m_score *= e_turret.target_select_missilebias; + smt = ftos(m_score); + + sp = ftos(p_score); + p_score *= e_turret.target_select_playerbias; + spt = ftos(p_score); + + + ss = ftos(score); + bprint("^3Target scores^7 \[ ",e_turret.netname, " \] ^3for^7 \[ ", e_target.netname," \]\n"); + bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n"); + bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n"); + bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n"); + bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n"); + bprint("^3Total (w/bias):\[^1",ss,"\]\n"); + +#endif + + return score; +} + +/* +float turret_stdproc_targetscore_close(entity e_turret,entity e_target) +{ + return 1 - (tvt_dist / e_turret.target_range); +} + +float turret_stdproc_targetscore_far (entity e_turret,entity e_target) +{ + return tvt_dist / e_turret.target_range; +} + +float turret_stdproc_targetscore_optimal(entity e_turret,entity e_target) +{ + return min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist); +} + +float turret_stdproc_score_angular(entity e_turret,entity e_target) +{ + return 1 - (tvt_thadf / e_turret.aim_maxrot); +} + +float turret_stdproc_targetscore_defend(entity e_turret,entity e_target) +{ + return 0; + //min(e_target.origin,e_turret.tur_defend.origin) / max(e_target.origin,e_turret.tur_defend.origin); +} +*/ diff --git a/data/qcsrc/server/tturrets/units/unit_checkpoint.qc b/data/qcsrc/server/tturrets/units/unit_checkpoint.qc index 878a5b0ff..ef97caa74 100644 --- a/data/qcsrc/server/tturrets/units/unit_checkpoint.qc +++ b/data/qcsrc/server/tturrets/units/unit_checkpoint.qc @@ -1,70 +1,70 @@ -/** - turret_checkpoint -**/ - - -#define checkpoint_target goalstack03 - -/* -#define checkpoint_cache_who flagcarried -#define checkpoint_cache_from lastrocket -#define checkpoint_cache_to selected_player -*/ - -#define pathgoal goalstack01 -#define pathcurrent goalstack02 - -/* -entity path_makeorcache(entity forwho,entity start, entity end) -{ - entity oldself; - entity pth; - oldself = self; - self = forwho; - - //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR); - - self = oldself; - return pth; -} -*/ - -void turret_checkpoint_use() -{ -} - -/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32) ------------KEYS------------ -target: .targetname of next waypoint in chain. -wait: Pause at this point # seconds. ------------SPAWNFLAGS----------- ----------NOTES---------- -If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly. -If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached. -*/ -void turret_checkpoint_init() -{ - traceline(self.origin, self.origin - '0 0 1024', MOVE_WORLDONLY, self); - setorigin(self,trace_endpos + '0 0 8'); - - if(self.target != "") - { - self.enemy = find(world,targetname,self.target); - if(self.enemy == world) - dprint("A turret_checkpoint faild to find its target!\n"); - } -} - -void spawnfunc_turret_checkpoint() -{ - setorigin(self,self.origin); - self.think = turret_checkpoint_init; - self.nextthink = time + 0.1; -} - -// Compat. -void spawnfunc_walker_checkpoint() -{ - self.classname = "turret_checkpoint"; - spawnfunc_turret_checkpoint(); -} +/** + turret_checkpoint +**/ + + +#define checkpoint_target goalstack03 + +/* +#define checkpoint_cache_who flagcarried +#define checkpoint_cache_from lastrocket +#define checkpoint_cache_to selected_player +*/ + +#define pathgoal goalstack01 +#define pathcurrent goalstack02 + +/* +entity path_makeorcache(entity forwho,entity start, entity end) +{ + entity oldself; + entity pth; + oldself = self; + self = forwho; + + //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR); + + self = oldself; + return pth; +} +*/ + +void turret_checkpoint_use() +{ +} + +/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32) +-----------KEYS------------ +target: .targetname of next waypoint in chain. +wait: Pause at this point # seconds. +-----------SPAWNFLAGS----------- +---------NOTES---------- +If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly. +If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached. +*/ +void turret_checkpoint_init() +{ + traceline(self.origin, self.origin - '0 0 1024', MOVE_WORLDONLY, self); + setorigin(self,trace_endpos + '0 0 8'); + + if(self.target != "") + { + self.enemy = find(world,targetname,self.target); + if(self.enemy == world) + dprint("A turret_checkpoint faild to find its target!\n"); + } +} + +void spawnfunc_turret_checkpoint() +{ + setorigin(self,self.origin); + self.think = turret_checkpoint_init; + self.nextthink = time + 0.1; +} + +// Compat. +void spawnfunc_walker_checkpoint() +{ + self.classname = "turret_checkpoint"; + spawnfunc_turret_checkpoint(); +} diff --git a/data/qcsrc/server/tturrets/units/unit_common.qc b/data/qcsrc/server/tturrets/units/unit_common.qc index d3f5a12fa..8b1378917 100644 --- a/data/qcsrc/server/tturrets/units/unit_common.qc +++ b/data/qcsrc/server/tturrets/units/unit_common.qc @@ -1 +1 @@ - + diff --git a/data/qcsrc/server/tturrets/units/unit_ewheel.qc b/data/qcsrc/server/tturrets/units/unit_ewheel.qc index 5251edb21..b97ee820d 100644 --- a/data/qcsrc/server/tturrets/units/unit_ewheel.qc +++ b/data/qcsrc/server/tturrets/units/unit_ewheel.qc @@ -1,520 +1,520 @@ -void turret_ewheel_projectile_explode() -{ - vector org2; - - org2 = findbetterlocation (self.origin, 8); - pointparticles(particleeffectnum("laser_impact"), org2, trace_plane_normal * 1000, 1); - //w_deathtypestring = "saw the eweel. to late."; -#ifdef TURRET_DEBUG - float d; - - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#else - RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); -#endif - sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM); - - remove (self); -} - - -void ewheel_attack() -{ - entity proj; - float i; - - for (i=0;i<1;++i) - { - turret_do_updates(self); - - sound (self, CHAN_WEAPON, "weapons/lasergun_fire.wav", VOL_BASE, ATTN_NORM); - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - - proj = spawn (); - setorigin(proj, self.tur_shotorg); - //setsize(proj, '-0.5 -0.5 -0.5', '0.5 0.5 0.5'); - //setmodel(proj, "models/laser.mdl"); // precision set above - proj.classname = "ewheel bolt"; - proj.owner = self; - proj.bot_dodge = FALSE; - proj.bot_dodgerating = self.shot_dmg; - proj.think = turret_ewheel_projectile_explode; - proj.nextthink = time + 9; - proj.solid = SOLID_BBOX; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - proj.angles = vectoangles(proj.velocity); - proj.touch = turret_ewheel_projectile_explode; - //proj.effects = EF_LOWPRECISION | EF_BRIGHTFIELD; - proj.enemy = self.enemy; - proj.flags = FL_PROJECTILE | FL_NOTARGET; - - CSQCProjectile(proj, TRUE, PROJECTILE_LASER, TRUE); - - self.tur_head.frame += 2; - - if (self.tur_head.frame > 3) - self.tur_head.frame = 0; - } - -} - -float ewheel_moveverb_roam(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (!self.enemy) - return verb.verb_static_value; - - return VS_CALL_NO; - - case VCM_DO: - self.angles_z = 0; - makevectors(self.angles); - self.moveto = v_forward * 128; - self.steerto = steerlib_beamsteer(v_forward,1024,32,36,128); - self.frame += 1; - movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); - - return VS_CALL_YES_DOING; - - case VCM_REMOVE: - - } - - return VS_CALL_YES_DONE; -} - -float ewheel_moveverb_path(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.pathcurrent) - return verb.verb_static_value; - - return VS_CALL_NO; - - case VCM_DO: - // Do we have a path? - if not(self.pathcurrent) - return VS_CALL_NO; - else - { - // Are we close enougth to a path node to switch to the next? - if (vlen(self.origin - self.pathcurrent.origin) < 64) - if (self.pathcurrent.path_next == world) - { - // Path endpoint reached - pathlib_deletepath(self.pathcurrent.owner); - self.pathcurrent = world; - - if (self.pathgoal) - { - if (self.pathgoal.use) - self.pathgoal.use(); - - if (self.pathgoal.enemy) - { - self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin); - self.pathgoal = self.pathgoal.enemy; - } - } - else - self.pathgoal = world; - } - else - self.pathcurrent = self.pathcurrent.path_next; - } - - - if (self.pathcurrent) - { - switch (self.waterlevel) - { - case 0: - case 1: - case 2: - case 3: - } - - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); - - self.frame += 1; - movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); - - return VS_CALL_YES_DOING; - } - else - return VS_CALL_YES_DONE; - - case VCM_REMOVE: - - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); - - self.pathcurrent = world; - - return VS_CALL_YES_DONE; - } - - return VS_CALL_YES_DONE; -} - -float ewheel_moveverb_enemy(float eval) -{ - switch (eval) - { - case VCM_EVAL: - if (self.enemy) - { - if (self.spawnflags & TSF_NO_PATHBREAK) - if (self.pathcurrent) - return VS_CALL_NO; - - return verb.verb_static_value; - } - - return VS_CALL_NO; - - case VCM_DO: - - self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); - self.steerto = steerlib_beamsteer(self.steerto,1024,64,68,256); - self.moveto = self.origin + self.steerto * 128; - - makevectors(self.angles); - - if (self.tur_dist_enemy > self.target_range_optimal) - { - if ( self.tur_head.spawnshieldtime < 1 ) - { - self.frame += 2; - movelib_move_simple(v_forward ,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); - } - else if (self.tur_head.spawnshieldtime < 2) - { - - self.frame += 1; - movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_slow"),0.4); - } - else - { - self.frame += 1; - movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_slower"),0.4); - } - } - else if (self.tur_dist_enemy < self.target_range_min) - { - self.frame -= 1; - movelib_move_simple(v_forward * -1,cvar("g_turrets_unit_ewheel_speed_slow"),0.4); - } - else - { - movelib_beak_simple(cvar("g_turrets_unit_ewheel_speed_stop")); - } - - return VS_CALL_YES_DOING; - } - - - return VS_CALL_YES_DONE; -} - -float ewheel_moveverb_runaway(float eval) -{ - switch (eval) - { - case VCM_EVAL: - if (self.spawnflags & TSF_NO_PATHBREAK) - if (self.pathcurrent) - return VS_CALL_NO; - - if (self.enemy) - if (self.health < 50) - return verb.verb_static_value; - - return VS_CALL_NO; - - case VCM_DO: - self.steerto = (steerlib_push(self.enemy.origin) * 0.7) + (steerlib_traceavoid_flat(0.3, 500, '0 0 128') * 0.3); - self.moveto = self.origin + self.steerto * 1000; - - self.frame += 2; - movelib_move_simple(v_forward ,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); - - return VS_CALL_YES_DOING; - - } - - return VS_CALL_YES_DONE; -} - -float ewheel_moveverb_idle(float eval) -{ - switch (eval) - { - case VCM_EVAL: - if (self.enemy) - return VS_CALL_NO; - - return verb.verb_static_value; - - case VCM_DO: - self.moveto = self.origin; - - if (vlen(self.velocity)) - movelib_beak_simple(cvar("g_turrets_unit_ewheel_speed_stop")); - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -void ewheel_postthink() -{ - float vz; - vector wish_angle,real_angle; - - vz = self.velocity_z; - - self.angles_x = anglemods(self.angles_x); - self.angles_y = anglemods(self.angles_y); - - self.angles_x *= -1; - makevectors(self.angles); - self.angles_x *= -1; - - wish_angle = normalize(self.steerto); - wish_angle = vectoangles(wish_angle); - real_angle = wish_angle - self.angles; - real_angle = shortangle_vxy(real_angle,self.tur_head.angles); - - self.tur_head.spawnshieldtime = fabs(real_angle_y); - real_angle_y = bound(-self.tur_head.aim_speed,real_angle_y,self.tur_head.aim_speed); - self.angles_y = (self.angles_y + real_angle_y); - - // Simulate banking - self.angles_z = bound(-45,real_angle_y * -2.5,45); - - verbstack_pop(self.verbs_move); - - if (self.frame > 40) - self.frame = 1; - - if (self.frame < 1) - self.frame = 40; - - - self.velocity_z = vz; -} - -void ewheel_respawnhook() -{ - entity e; - - setorigin(self,self.pos1); - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - self.pathcurrent = WALKER_PATH(self.origin,e.origin); - self.pathgoal = e; - } - } -} - -void ewheel_diehook() -{ - turret_trowgib2(self.origin,self.velocity + v_up * 400,'-0.6 -0.2 -02',self,time + random() * 2 +3); - - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); - - self.pathcurrent = world; - - if (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) - { - verbstack_flush(self.verbs_move); - remove(self.verbs_move); - } - -} - -/* -float test_stack_1(float eval) -{ - switch (eval) - { - case VCM_EVAL: - return verb.verb_static_value; - - case VCM_DO: - dprint("test_stack_1\n"); - return VS_CALL_REMOVE; - } - - return VS_CALL_REMOVE; -} - -float test_stack_2(float eval) -{ - switch (eval) - { - case VCM_EVAL: - return verb.verb_static_value; - - case VCM_DO: - dprint("test_stack_2\n"); - return VS_CALL_REMOVE; - } - - return VS_CALL_REMOVE; -} - -float ccnntt; -float test_stack_3(float eval) -{ - switch (eval) - { - case VCM_EVAL: - return verb.verb_static_value; - - case VCM_DO: - dprint("test_stack_3\n"); - ++ccnntt; - if(ccnntt > 3) - return VS_CALL_REMOVE; - else - return VS_CALL_YES_DONE; - } - - return VS_CALL_YES_DONE; -} - -.entity test_stack; -*/ -void turret_ewheel_dinit() -{ - entity e; - - if (self.netname == "") self.netname = "eWheel Turret"; - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - bprint("Warning! initital waypoint for ewheel does NOT exsist!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - self.goalcurrent = e; - } - - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_ROAM | TFL_TURRCAPS_HEADATTACHED; - //self.aim_flags = TFL_AIM_SIMPLE;// TFL_AIM_LEAD | TFL_AIM_ZEASE; - - self.turret_respawnhook = ewheel_respawnhook; - self.turret_diehook = ewheel_diehook; - - if (turret_stdproc_init("ewheel_std",0) == 0) - { - remove(self); - return; - } - - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;// | TFL_TARGETSELECT_LOS; - self.damage_flags |= TFL_DMG_DEATH_NOGIBS; - - self.iscreature = TRUE; - self.movetype = MOVETYPE_WALK; - self.solid = SOLID_SLIDEBOX; - self.takedamage = DAMAGE_AIM; - - setmodel(self,"models/turrets/ewheel-base.md3"); - setmodel(self.tur_head,"models/turrets/ewheel-gun1.md3"); - setattachment(self.tur_head,self,"tag_head"); - - self.pos1 = self.origin; - - self.idle_aim = '0 0 0'; - - // Our fire routine - self.turret_firefunc = ewheel_attack; - self.turret_postthink = ewheel_postthink; - self.tur_head.frame = 1; - - self.verbs_move = spawn(); - - //verbstack_push(self.verbs_move, ewheel_moveverb_roam, WVM_IDLE, 0, self); - verbstack_push(self.verbs_move, ewheel_moveverb_idle, WVM_IDLE, 0, self); - verbstack_push(self.verbs_move, ewheel_moveverb_enemy, WVM_ENEMY, 0, self); - verbstack_push(self.verbs_move, ewheel_moveverb_path, WVM_PATH, 0, self); - //verbstack_push(self.verbs_move, ewheel_moveverb_runaway,WVM_PANIC, 0, self); - - /* - self.test_stack = spawn(); - verbstack_push(self.test_stack, test_stack_1,1, 0, self); - verbstack_push(self.test_stack, test_stack_2,2, 0, self); - verbstack_push(self.test_stack, test_stack_3,100,0, self); - while(verbstack_popfifo(self.test_stack) != 0) - */ - - - - // Convert from dgr / sec to dgr / tic - self.tur_head.aim_speed = cvar("g_turrets_unit_ewheel_turnrate"); - self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - self.pathcurrent = WALKER_PATH(self.origin,e.origin); - self.pathgoal = e; - } - } -} - -void spawnfunc_turret_ewheel() -{ - g_turrets_common_precash(); - - precache_model ("models/turrets/ewheel-base.md3"); - precache_model ("models/turrets/ewheel-gun1.md3"); - - self.think = turret_ewheel_dinit; - self.nextthink = time + 0.5; -} +void turret_ewheel_projectile_explode() +{ + vector org2; + + org2 = findbetterlocation (self.origin, 8); + pointparticles(particleeffectnum("laser_impact"), org2, trace_plane_normal * 1000, 1); + //w_deathtypestring = "saw the eweel. to late."; +#ifdef TURRET_DEBUG + float d; + + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); +#endif + sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM); + + remove (self); +} + + +void ewheel_attack() +{ + entity proj; + float i; + + for (i=0;i<1;++i) + { + turret_do_updates(self); + + sound (self, CHAN_WEAPON, "weapons/lasergun_fire.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + proj = spawn (); + setorigin(proj, self.tur_shotorg); + //setsize(proj, '-0.5 -0.5 -0.5', '0.5 0.5 0.5'); + //setmodel(proj, "models/laser.mdl"); // precision set above + proj.classname = "ewheel bolt"; + proj.owner = self; + proj.bot_dodge = FALSE; + proj.bot_dodgerating = self.shot_dmg; + proj.think = turret_ewheel_projectile_explode; + proj.nextthink = time + 9; + proj.solid = SOLID_BBOX; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + proj.angles = vectoangles(proj.velocity); + proj.touch = turret_ewheel_projectile_explode; + //proj.effects = EF_LOWPRECISION | EF_BRIGHTFIELD; + proj.enemy = self.enemy; + proj.flags = FL_PROJECTILE | FL_NOTARGET; + + CSQCProjectile(proj, TRUE, PROJECTILE_LASER, TRUE); + + self.tur_head.frame += 2; + + if (self.tur_head.frame > 3) + self.tur_head.frame = 0; + } + +} + +float ewheel_moveverb_roam(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (!self.enemy) + return verb.verb_static_value; + + return VS_CALL_NO; + + case VCM_DO: + self.angles_z = 0; + makevectors(self.angles); + self.moveto = v_forward * 128; + self.steerto = steerlib_beamsteer(v_forward,1024,32,36,128); + self.frame += 1; + movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); + + return VS_CALL_YES_DOING; + + case VCM_REMOVE: + + } + + return VS_CALL_YES_DONE; +} + +float ewheel_moveverb_path(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.pathcurrent) + return verb.verb_static_value; + + return VS_CALL_NO; + + case VCM_DO: + // Do we have a path? + if not(self.pathcurrent) + return VS_CALL_NO; + else + { + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + } + + + if (self.pathcurrent) + { + switch (self.waterlevel) + { + case 0: + case 1: + case 2: + case 3: + } + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + + self.frame += 1; + movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); + + return VS_CALL_YES_DOING; + } + else + return VS_CALL_YES_DONE; + + case VCM_REMOVE: + + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); + + self.pathcurrent = world; + + return VS_CALL_YES_DONE; + } + + return VS_CALL_YES_DONE; +} + +float ewheel_moveverb_enemy(float eval) +{ + switch (eval) + { + case VCM_EVAL: + if (self.enemy) + { + if (self.spawnflags & TSF_NO_PATHBREAK) + if (self.pathcurrent) + return VS_CALL_NO; + + return verb.verb_static_value; + } + + return VS_CALL_NO; + + case VCM_DO: + + self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); + self.steerto = steerlib_beamsteer(self.steerto,1024,64,68,256); + self.moveto = self.origin + self.steerto * 128; + + makevectors(self.angles); + + if (self.tur_dist_enemy > self.target_range_optimal) + { + if ( self.tur_head.spawnshieldtime < 1 ) + { + self.frame += 2; + movelib_move_simple(v_forward ,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); + } + else if (self.tur_head.spawnshieldtime < 2) + { + + self.frame += 1; + movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_slow"),0.4); + } + else + { + self.frame += 1; + movelib_move_simple(v_forward,cvar("g_turrets_unit_ewheel_speed_slower"),0.4); + } + } + else if (self.tur_dist_enemy < self.target_range_min) + { + self.frame -= 1; + movelib_move_simple(v_forward * -1,cvar("g_turrets_unit_ewheel_speed_slow"),0.4); + } + else + { + movelib_beak_simple(cvar("g_turrets_unit_ewheel_speed_stop")); + } + + return VS_CALL_YES_DOING; + } + + + return VS_CALL_YES_DONE; +} + +float ewheel_moveverb_runaway(float eval) +{ + switch (eval) + { + case VCM_EVAL: + if (self.spawnflags & TSF_NO_PATHBREAK) + if (self.pathcurrent) + return VS_CALL_NO; + + if (self.enemy) + if (self.health < 50) + return verb.verb_static_value; + + return VS_CALL_NO; + + case VCM_DO: + self.steerto = (steerlib_push(self.enemy.origin) * 0.7) + (steerlib_traceavoid_flat(0.3, 500, '0 0 128') * 0.3); + self.moveto = self.origin + self.steerto * 1000; + + self.frame += 2; + movelib_move_simple(v_forward ,cvar("g_turrets_unit_ewheel_speed_fast"),0.4); + + return VS_CALL_YES_DOING; + + } + + return VS_CALL_YES_DONE; +} + +float ewheel_moveverb_idle(float eval) +{ + switch (eval) + { + case VCM_EVAL: + if (self.enemy) + return VS_CALL_NO; + + return verb.verb_static_value; + + case VCM_DO: + self.moveto = self.origin; + + if (vlen(self.velocity)) + movelib_beak_simple(cvar("g_turrets_unit_ewheel_speed_stop")); + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +void ewheel_postthink() +{ + float vz; + vector wish_angle,real_angle; + + vz = self.velocity_z; + + self.angles_x = anglemods(self.angles_x); + self.angles_y = anglemods(self.angles_y); + + self.angles_x *= -1; + makevectors(self.angles); + self.angles_x *= -1; + + wish_angle = normalize(self.steerto); + wish_angle = vectoangles(wish_angle); + real_angle = wish_angle - self.angles; + real_angle = shortangle_vxy(real_angle,self.tur_head.angles); + + self.tur_head.spawnshieldtime = fabs(real_angle_y); + real_angle_y = bound(-self.tur_head.aim_speed,real_angle_y,self.tur_head.aim_speed); + self.angles_y = (self.angles_y + real_angle_y); + + // Simulate banking + self.angles_z = bound(-45,real_angle_y * -2.5,45); + + verbstack_pop(self.verbs_move); + + if (self.frame > 40) + self.frame = 1; + + if (self.frame < 1) + self.frame = 40; + + + self.velocity_z = vz; +} + +void ewheel_respawnhook() +{ + entity e; + + setorigin(self,self.pos1); + + if (self.target != "") + { + e = find(world,targetname,self.target); + if (!e) + { + dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; + } + } +} + +void ewheel_diehook() +{ + turret_trowgib2(self.origin,self.velocity + v_up * 400,'-0.6 -0.2 -02',self,time + random() * 2 +3); + + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); + + self.pathcurrent = world; + + if (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + { + verbstack_flush(self.verbs_move); + remove(self.verbs_move); + } + +} + +/* +float test_stack_1(float eval) +{ + switch (eval) + { + case VCM_EVAL: + return verb.verb_static_value; + + case VCM_DO: + dprint("test_stack_1\n"); + return VS_CALL_REMOVE; + } + + return VS_CALL_REMOVE; +} + +float test_stack_2(float eval) +{ + switch (eval) + { + case VCM_EVAL: + return verb.verb_static_value; + + case VCM_DO: + dprint("test_stack_2\n"); + return VS_CALL_REMOVE; + } + + return VS_CALL_REMOVE; +} + +float ccnntt; +float test_stack_3(float eval) +{ + switch (eval) + { + case VCM_EVAL: + return verb.verb_static_value; + + case VCM_DO: + dprint("test_stack_3\n"); + ++ccnntt; + if(ccnntt > 3) + return VS_CALL_REMOVE; + else + return VS_CALL_YES_DONE; + } + + return VS_CALL_YES_DONE; +} + +.entity test_stack; +*/ +void turret_ewheel_dinit() +{ + entity e; + + if (self.netname == "") self.netname = "eWheel Turret"; + + if (self.target != "") + { + e = find(world,targetname,self.target); + if (!e) + { + bprint("Warning! initital waypoint for ewheel does NOT exsist!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + self.goalcurrent = e; + } + + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_ROAM | TFL_TURRCAPS_HEADATTACHED; + //self.aim_flags = TFL_AIM_SIMPLE;// TFL_AIM_LEAD | TFL_AIM_ZEASE; + + self.turret_respawnhook = ewheel_respawnhook; + self.turret_diehook = ewheel_diehook; + + if (turret_stdproc_init("ewheel_std",0) == 0) + { + remove(self); + return; + } + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;// | TFL_TARGETSELECT_LOS; + self.damage_flags |= TFL_DMG_DEATH_NOGIBS; + + self.iscreature = TRUE; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + + setmodel(self,"models/turrets/ewheel-base.md3"); + setmodel(self.tur_head,"models/turrets/ewheel-gun1.md3"); + setattachment(self.tur_head,self,"tag_head"); + + self.pos1 = self.origin; + + self.idle_aim = '0 0 0'; + + // Our fire routine + self.turret_firefunc = ewheel_attack; + self.turret_postthink = ewheel_postthink; + self.tur_head.frame = 1; + + self.verbs_move = spawn(); + + //verbstack_push(self.verbs_move, ewheel_moveverb_roam, WVM_IDLE, 0, self); + verbstack_push(self.verbs_move, ewheel_moveverb_idle, WVM_IDLE, 0, self); + verbstack_push(self.verbs_move, ewheel_moveverb_enemy, WVM_ENEMY, 0, self); + verbstack_push(self.verbs_move, ewheel_moveverb_path, WVM_PATH, 0, self); + //verbstack_push(self.verbs_move, ewheel_moveverb_runaway,WVM_PANIC, 0, self); + + /* + self.test_stack = spawn(); + verbstack_push(self.test_stack, test_stack_1,1, 0, self); + verbstack_push(self.test_stack, test_stack_2,2, 0, self); + verbstack_push(self.test_stack, test_stack_3,100,0, self); + while(verbstack_popfifo(self.test_stack) != 0) + */ + + + + // Convert from dgr / sec to dgr / tic + self.tur_head.aim_speed = cvar("g_turrets_unit_ewheel_turnrate"); + self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); + + if (self.target != "") + { + e = find(world,targetname,self.target); + if (!e) + { + dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; + } + } +} + +void spawnfunc_turret_ewheel() +{ + g_turrets_common_precash(); + + precache_model ("models/turrets/ewheel-base.md3"); + precache_model ("models/turrets/ewheel-gun1.md3"); + + self.think = turret_ewheel_dinit; + self.nextthink = time + 0.5; +} diff --git a/data/qcsrc/server/tturrets/units/unit_flac.qc b/data/qcsrc/server/tturrets/units/unit_flac.qc index 22be009c7..cc3543876 100644 --- a/data/qcsrc/server/tturrets/units/unit_flac.qc +++ b/data/qcsrc/server/tturrets/units/unit_flac.qc @@ -1,128 +1,128 @@ -void spawnfunc_turret_flac(); -void turret_flac_dinit(); -void turret_flac_attack(); -void turret_flac_projectile_explode(); - -void turret_flac_attack() -{ - local entity proj; - - turret_tag_fire_update(); - - sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); - proj = spawn (); - setorigin(proj, self.tur_shotorg); - // setmodel(proj, "models/turrets/pd_proj.md3"); - setsize(proj, '0 0 0', '0 0 0'); - proj.classname = "flac_projectile"; - proj.owner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = self.shot_dmg; - proj.solid = SOLID_BBOX; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.flags = FL_PROJECTILE; - // proj.effects = EF_LOWPRECISION; - proj.takedamage = DAMAGE_NO; - //proj.health = 100; - proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - proj.angles = vectoangles(proj.velocity); - proj.touch = turret_flac_projectile_explode; - proj.think = turret_flac_projectile_explode; - //proj.nextthink = time + vlen(self.tur_shotorg - self.enemy.origin) / self.shot_speed; - proj.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 3) / self.shot_speed); - proj.enemy = self.enemy; - proj.cnt = time + 5; - - CSQCProjectile(proj, TRUE, PROJECTILE_HAGAR, TRUE); - - self.tur_head.frame = self.tur_head.frame + 1; - if (self.tur_head.frame >= 4) self.tur_head.frame = 0; - -} - -void turret_flac_projectile_explode() -{ - float ftmp; - - // FIXME: tur_impacttime is not accurate enougth, this is a dirty hakk to make flac work. - - //w_deathtypestring = "got caught in the flack."; - - - - if( (self.enemy != world) && - (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) ) - { - // OMG HAXX! - setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); - } - - - - te_explosion (self.origin); - - ftmp = crandom(); - if (ftmp<-0.7) - sound (self, CHAN_PROJECTILE, "weapons/hagexp1.wav", VOL_BASE, ATTN_NORM); - else if (ftmp<0.4) - sound (self, CHAN_PROJECTILE, "weapons/hagexp2.wav", VOL_BASE, ATTN_NORM); - else if (ftmp<1) - sound (self, CHAN_PROJECTILE, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM); - - - self.event_damage = SUB_Null; - - -#ifdef TURRET_DEBUG - ftmp = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + ftmp; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#else - RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg * 0.5, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); -#endif - - remove (self); -} - - -void turret_flac_dinit() -{ - if (self.netname == "") self.netname = "FLAC Cannon"; - - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_MISSILEKILL; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - - if (turret_stdproc_init("flac_std",0) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/flac.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fire routine - self.turret_firefunc = turret_flac_attack; - -} -/*QUAKED turret_flac (0 .5 .8) ? -*/ - -void spawnfunc_turret_flac() -{ - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/flac.md3"); - //precache_model("models/turrets/pd_proj.md3"); - - self.think = turret_flac_dinit; - self.nextthink = time + 0.5; -} - +void spawnfunc_turret_flac(); +void turret_flac_dinit(); +void turret_flac_attack(); +void turret_flac_projectile_explode(); + +void turret_flac_attack() +{ + local entity proj; + + turret_tag_fire_update(); + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); + proj = spawn (); + setorigin(proj, self.tur_shotorg); + // setmodel(proj, "models/turrets/pd_proj.md3"); + setsize(proj, '0 0 0', '0 0 0'); + proj.classname = "flac_projectile"; + proj.owner = self; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = self.shot_dmg; + proj.solid = SOLID_BBOX; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.flags = FL_PROJECTILE; + // proj.effects = EF_LOWPRECISION; + proj.takedamage = DAMAGE_NO; + //proj.health = 100; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + proj.angles = vectoangles(proj.velocity); + proj.touch = turret_flac_projectile_explode; + proj.think = turret_flac_projectile_explode; + //proj.nextthink = time + vlen(self.tur_shotorg - self.enemy.origin) / self.shot_speed; + proj.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 3) / self.shot_speed); + proj.enemy = self.enemy; + proj.cnt = time + 5; + + CSQCProjectile(proj, TRUE, PROJECTILE_HAGAR, TRUE); + + self.tur_head.frame = self.tur_head.frame + 1; + if (self.tur_head.frame >= 4) self.tur_head.frame = 0; + +} + +void turret_flac_projectile_explode() +{ + float ftmp; + + // FIXME: tur_impacttime is not accurate enougth, this is a dirty hakk to make flac work. + + //w_deathtypestring = "got caught in the flack."; + + + + if( (self.enemy != world) && + (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) ) + { + // OMG HAXX! + setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); + } + + + + te_explosion (self.origin); + + ftmp = crandom(); + if (ftmp<-0.7) + sound (self, CHAN_PROJECTILE, "weapons/hagexp1.wav", VOL_BASE, ATTN_NORM); + else if (ftmp<0.4) + sound (self, CHAN_PROJECTILE, "weapons/hagexp2.wav", VOL_BASE, ATTN_NORM); + else if (ftmp<1) + sound (self, CHAN_PROJECTILE, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM); + + + self.event_damage = SUB_Null; + + +#ifdef TURRET_DEBUG + ftmp = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + ftmp; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg * 0.5, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); +#endif + + remove (self); +} + + +void turret_flac_dinit() +{ + if (self.netname == "") self.netname = "FLAC Cannon"; + + + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_MISSILEKILL; + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + + if (turret_stdproc_init("flac_std",0) == 0) + { + remove(self); + return; + } + + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/flac.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fire routine + self.turret_firefunc = turret_flac_attack; + +} +/*QUAKED turret_flac (0 .5 .8) ? +*/ + +void spawnfunc_turret_flac() +{ + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/flac.md3"); + //precache_model("models/turrets/pd_proj.md3"); + + self.think = turret_flac_dinit; + self.nextthink = time + 0.5; +} + diff --git a/data/qcsrc/server/tturrets/units/unit_fusionreactor.qc b/data/qcsrc/server/tturrets/units/unit_fusionreactor.qc index 021e09a43..d45a9cada 100644 --- a/data/qcsrc/server/tturrets/units/unit_fusionreactor.qc +++ b/data/qcsrc/server/tturrets/units/unit_fusionreactor.qc @@ -1,83 +1,83 @@ -void spawnfunc_turret_fusionreactor(); -void turret_fusionreactor_dinit(); -void turret_fusionreactor_fire(); - -float turret_fusionreactor_firecheck() -{ - if (self.enemy == world) return 0; - - if (!self.enemy.ammo_flags & TFL_AMMO_RECIVE) return 0; - if (!self.enemy.ammo_flags & TFL_AMMO_ENERGY) return 0; - - if (self.ammo < self.shot_dmg) return 0; - if (self.enemy.ammo >= self.enemy.ammo_max) return 0; - if (self.tur_dist_aimpos > self.target_range) return 0; - if (self.tur_dist_aimpos < self.target_range_min) return 0; - - return 1; -} - - -void turret_fusionreactor_fire() -{ - self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max); - //te_lightning1(world,self.origin,self.enemy.origin); -} - -void turret_fusionreactor_postthink() -{ -} - -void turret_fusionreactor_respawnhook() -{ - self.tur_head.avelocity = '0 50 0'; -} - -void turret_fusionreactor_dinit() -{ - if (self.netname == "") self.netname = "Fusionreactor"; - - self.turrcaps_flags = TFL_TURRCAPS_SUPPORT | TFL_TURRCAPS_AMMOSOURCE; - - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; - - self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMTS; - - self.firecheck_flags = TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_OTHER_AMMO | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_WORLD; - - self.shoot_flags = TFL_SHOOT_HITALLVALID; - self.aim_flags = TFL_AIM_NO; - self.track_flags = TFL_TRACK_NO; - self.turret_respawnhook = turret_fusionreactor_respawnhook; - if (turret_stdproc_init("fusreac_std",0) == 0) - { - remove(self); - return; - } - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/reactor.md3"); - - //if(!turret_tag_setup()) - // dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - self.tur_head.scale = 0.75; - setorigin(self.tur_head,self.origin + '0 0 25'); - self.tur_head.avelocity = '0 50 0'; - - self.turret_firecheckfunc = turret_fusionreactor_firecheck; - self.turret_firefunc = turret_fusionreactor_fire; - - self.turret_postthink = turret_fusionreactor_postthink; -} - -/*QUAKED turret_fusionreactor (0 .5 .8) ? -*/ -void spawnfunc_turret_fusionreactor() -{ - precache_model ("models/turrets/reactor.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_fusionreactor_dinit; - self.nextthink = time + 0.5; -} +void spawnfunc_turret_fusionreactor(); +void turret_fusionreactor_dinit(); +void turret_fusionreactor_fire(); + +float turret_fusionreactor_firecheck() +{ + if (self.enemy == world) return 0; + + if (!self.enemy.ammo_flags & TFL_AMMO_RECIVE) return 0; + if (!self.enemy.ammo_flags & TFL_AMMO_ENERGY) return 0; + + if (self.ammo < self.shot_dmg) return 0; + if (self.enemy.ammo >= self.enemy.ammo_max) return 0; + if (self.tur_dist_aimpos > self.target_range) return 0; + if (self.tur_dist_aimpos < self.target_range_min) return 0; + + return 1; +} + + +void turret_fusionreactor_fire() +{ + self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max); + //te_lightning1(world,self.origin,self.enemy.origin); +} + +void turret_fusionreactor_postthink() +{ +} + +void turret_fusionreactor_respawnhook() +{ + self.tur_head.avelocity = '0 50 0'; +} + +void turret_fusionreactor_dinit() +{ + if (self.netname == "") self.netname = "Fusionreactor"; + + self.turrcaps_flags = TFL_TURRCAPS_SUPPORT | TFL_TURRCAPS_AMMOSOURCE; + + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; + + self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMTS; + + self.firecheck_flags = TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_OTHER_AMMO | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_WORLD; + + self.shoot_flags = TFL_SHOOT_HITALLVALID; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + self.turret_respawnhook = turret_fusionreactor_respawnhook; + if (turret_stdproc_init("fusreac_std",0) == 0) + { + remove(self); + return; + } + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/reactor.md3"); + + //if(!turret_tag_setup()) + // dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + self.tur_head.scale = 0.75; + setorigin(self.tur_head,self.origin + '0 0 25'); + self.tur_head.avelocity = '0 50 0'; + + self.turret_firecheckfunc = turret_fusionreactor_firecheck; + self.turret_firefunc = turret_fusionreactor_fire; + + self.turret_postthink = turret_fusionreactor_postthink; +} + +/*QUAKED turret_fusionreactor (0 .5 .8) ? +*/ +void spawnfunc_turret_fusionreactor() +{ + precache_model ("models/turrets/reactor.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_fusionreactor_dinit; + self.nextthink = time + 0.5; +} diff --git a/data/qcsrc/server/tturrets/units/unit_hellion.qc b/data/qcsrc/server/tturrets/units/unit_hellion.qc index 45819f963..1950b7ef9 100644 --- a/data/qcsrc/server/tturrets/units/unit_hellion.qc +++ b/data/qcsrc/server/tturrets/units/unit_hellion.qc @@ -1,222 +1,222 @@ -.float shot_speed_max; -.float shot_speed_gain; - -void spawnfunc_turret_hellion(); -void turret_hellion_dinit(); -void turret_hellion_attack(); -void turret_hellion_missile_explode(); -void turret_hellion_missile_think(); -void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) - -void turret_hellion_postthink() -{ - if (cvar("g_turrets_reloadcvars")) - { - if (!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max"); - if (!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain"); - } - - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 7) - self.tur_head.frame = 0; -} - -void turret_hellion_attack() -{ - local entity missile; - - sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); - - missile = spawn (); - setorigin(missile, self.tur_shotorg); - setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - - missile.classname = "hellion_missile"; - missile.owner = self; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = self.shot_dmg; - missile.takedamage = DAMAGE_YES; - missile.event_damage = turret_hellion_missile_damage; - missile.damageforcescale = 2; - missile.health = 10; - missile.enemy = self.enemy; - missile.think = turret_hellion_missile_think; - missile.nextthink = time;// + 0.2; - missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLY; - missile.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - missile.angles = vectoangles(missile.velocity); - missile.touch = turret_hellion_missile_explode; - missile.flags = FL_PROJECTILE; - missile.solid = SOLID_BBOX; - missile.tur_health = time + 9; - missile.tur_aimpos = randomvec() * 128; - te_explosion (missile.origin); - - CSQCProjectile(missile, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound - - // switch tubes - self.tur_shotorg_y = self.tur_shotorg_y * -1; - - if (self.tur_head.frame == 0) - self.tur_head.frame = self.tur_head.frame + 1; - -} - -void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.health = self.health - damage; - self.velocity = self.velocity + vforce; - if (self.health <= 0) turret_hellion_missile_explode(); -} - -void turret_hellion_missile_think() -{ - vector olddir,newdir; - vector pre_pos; - float itime; - - self.nextthink = time + 0.05; - - olddir = normalize(self.velocity); - - if(self.tur_health < time) - turret_hellion_missile_explode(); - - // Enemy dead? just keep on the current heading then. - if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) - { - - // Make sure we dont return to tracking a respawned player - self.enemy = world; - - // Turn model - self.angles = vectoangles(self.velocity); - - if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) - turret_hellion_missile_explode(); - - // Accelerate - self.velocity = olddir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max); - - UpdateCSQCProjectile(self); - - return; - } - - // Enemy in range? - if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) - turret_hellion_missile_explode(); - - // Predict enemy position - itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); - pre_pos = self.enemy.origin + self.enemy.velocity * itime; - - pre_pos = (pre_pos + self.enemy.origin) * 0.5; - - //pre_pos += randomvec() * 128; //self.tur_aimpos * (sin(32) * time) ; - - // Find out the direction to that place - newdir = normalize(pre_pos - self.origin); - - // Turn - newdir = normalize(olddir + newdir * 0.35); - - // Turn model - self.angles = vectoangles(self.velocity); - - // Accelerate - self.velocity = newdir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max); - - if (itime < 0.05) - self.think = turret_hellion_missile_explode; - - UpdateCSQCProjectile(self); -} - -void turret_hellion_missile_explode() -{ - vector org2; - float d; - - if(self.event_damage != SUB_Null) - { - self.event_damage = SUB_Null; - self.think = turret_hellion_missile_explode; - self.nextthink = time; - return; - } - - sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - org2 = findbetterlocation (self.origin, 16); - - // LordHavoc: TE_TEI_BIGEXPLOSION - WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte (MSG_BROADCAST, 78); - WriteCoord (MSG_BROADCAST, org2_x); - WriteCoord (MSG_BROADCAST, org2_y); - WriteCoord (MSG_BROADCAST, org2_z); - - //w_deathtypestring = "could not dodge the twin missiles."; - self.event_damage = SUB_Null; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - -#ifdef TURRET_DEBUG - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#endif - - // Target dead, get another is still targeting the same. - if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) - self.owner.enemy = world; - - remove (self); -} - -void turret_hellion_dinit() -{ - if (self.netname == "") self.netname = "Hellion Missile Turret"; - - if (!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max"); - if (!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain"); - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; - self.aim_flags = TFL_AIM_SIMPLE; - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK ; - self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_OWM_AMMO; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - - if (turret_stdproc_init("hellion_std",0) == 0) - { - remove(self); - return; - } - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/hellion.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fire routine - self.turret_firefunc = turret_hellion_attack; - - // Custom animations and sutch - self.turret_postthink = turret_hellion_postthink; -} - -/*QUAKED turret_hellion (0 .5 .8) ? -*/ -void spawnfunc_turret_hellion() -{ - //precache_model ( "models/turrets/mlrs_rocket.md3"); - precache_model ("models/turrets/hellion.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_hellion_dinit; - self.nextthink = time + 0.5; -} - - +.float shot_speed_max; +.float shot_speed_gain; + +void spawnfunc_turret_hellion(); +void turret_hellion_dinit(); +void turret_hellion_attack(); +void turret_hellion_missile_explode(); +void turret_hellion_missile_think(); +void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) + +void turret_hellion_postthink() +{ + if (cvar("g_turrets_reloadcvars")) + { + if (!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max"); + if (!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain"); + } + + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 7) + self.tur_head.frame = 0; +} + +void turret_hellion_attack() +{ + local entity missile; + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); + + missile = spawn (); + setorigin(missile, self.tur_shotorg); + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + missile.classname = "hellion_missile"; + missile.owner = self; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = self.shot_dmg; + missile.takedamage = DAMAGE_YES; + missile.event_damage = turret_hellion_missile_damage; + missile.damageforcescale = 2; + missile.health = 10; + missile.enemy = self.enemy; + missile.think = turret_hellion_missile_think; + missile.nextthink = time;// + 0.2; + missile.solid = SOLID_BBOX; + missile.movetype = MOVETYPE_FLY; + missile.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + missile.angles = vectoangles(missile.velocity); + missile.touch = turret_hellion_missile_explode; + missile.flags = FL_PROJECTILE; + missile.solid = SOLID_BBOX; + missile.tur_health = time + 9; + missile.tur_aimpos = randomvec() * 128; + te_explosion (missile.origin); + + CSQCProjectile(missile, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound + + // switch tubes + self.tur_shotorg_y = self.tur_shotorg_y * -1; + + if (self.tur_head.frame == 0) + self.tur_head.frame = self.tur_head.frame + 1; + +} + +void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.health = self.health - damage; + self.velocity = self.velocity + vforce; + if (self.health <= 0) turret_hellion_missile_explode(); +} + +void turret_hellion_missile_think() +{ + vector olddir,newdir; + vector pre_pos; + float itime; + + self.nextthink = time + 0.05; + + olddir = normalize(self.velocity); + + if(self.tur_health < time) + turret_hellion_missile_explode(); + + // Enemy dead? just keep on the current heading then. + if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) + { + + // Make sure we dont return to tracking a respawned player + self.enemy = world; + + // Turn model + self.angles = vectoangles(self.velocity); + + if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) + turret_hellion_missile_explode(); + + // Accelerate + self.velocity = olddir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max); + + UpdateCSQCProjectile(self); + + return; + } + + // Enemy in range? + if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) + turret_hellion_missile_explode(); + + // Predict enemy position + itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); + pre_pos = self.enemy.origin + self.enemy.velocity * itime; + + pre_pos = (pre_pos + self.enemy.origin) * 0.5; + + //pre_pos += randomvec() * 128; //self.tur_aimpos * (sin(32) * time) ; + + // Find out the direction to that place + newdir = normalize(pre_pos - self.origin); + + // Turn + newdir = normalize(olddir + newdir * 0.35); + + // Turn model + self.angles = vectoangles(self.velocity); + + // Accelerate + self.velocity = newdir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max); + + if (itime < 0.05) + self.think = turret_hellion_missile_explode; + + UpdateCSQCProjectile(self); +} + +void turret_hellion_missile_explode() +{ + vector org2; + float d; + + if(self.event_damage != SUB_Null) + { + self.event_damage = SUB_Null; + self.think = turret_hellion_missile_explode; + self.nextthink = time; + return; + } + + sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + org2 = findbetterlocation (self.origin, 16); + + // LordHavoc: TE_TEI_BIGEXPLOSION + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 78); + WriteCoord (MSG_BROADCAST, org2_x); + WriteCoord (MSG_BROADCAST, org2_y); + WriteCoord (MSG_BROADCAST, org2_z); + + //w_deathtypestring = "could not dodge the twin missiles."; + self.event_damage = SUB_Null; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + +#ifdef TURRET_DEBUG + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#endif + + // Target dead, get another is still targeting the same. + if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) + self.owner.enemy = world; + + remove (self); +} + +void turret_hellion_dinit() +{ + if (self.netname == "") self.netname = "Hellion Missile Turret"; + + if (!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max"); + if (!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain"); + + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; + self.aim_flags = TFL_AIM_SIMPLE; + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK ; + self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_OWM_AMMO; + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + + if (turret_stdproc_init("hellion_std",0) == 0) + { + remove(self); + return; + } + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/hellion.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fire routine + self.turret_firefunc = turret_hellion_attack; + + // Custom animations and sutch + self.turret_postthink = turret_hellion_postthink; +} + +/*QUAKED turret_hellion (0 .5 .8) ? +*/ +void spawnfunc_turret_hellion() +{ + //precache_model ( "models/turrets/mlrs_rocket.md3"); + precache_model ("models/turrets/hellion.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_hellion_dinit; + self.nextthink = time + 0.5; +} + + diff --git a/data/qcsrc/server/tturrets/units/unit_hk.qc b/data/qcsrc/server/tturrets/units/unit_hk.qc index cb66db4f1..8b8182f21 100644 --- a/data/qcsrc/server/tturrets/units/unit_hk.qc +++ b/data/qcsrc/server/tturrets/units/unit_hk.qc @@ -1,480 +1,480 @@ -//#define TURRET_DEBUG_HK - -#ifdef TURRET_DEBUG_HK -.float atime; -#endif - -void spawnfunc_turret_hk(); -void turret_hk_dinit(); -void turret_hk_attack(); -void turret_hk_missile_explode(); -void turret_hk_missile_think(); -void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); -float turret_hk_addtarget(entity e_target,entity e_sender); -//void turret_hk_missile_touch(); - -float hk_maxspeed; -float hk_minspeed; -float hk_accel; -float hk_accel2; -float hk_decel; - -float turret_hk_addtarget(entity e_target,entity e_sender) -{ - if (e_target) - { - if (turret_validate_target(self,e_target,self.target_validate_flags) > 0) - { - self.enemy = e_target; - return 1; - } - } - - return 0; -} - -float hk_is_valid_target(entity e_target) -{ - if (e_target == world) - return 0; - - // If only this was used more.. - if (e_target.flags & FL_NOTARGET) - return 0; - - // Cant touch this - if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) - return 0; - - // player - if (e_target.flags & FL_CLIENT) - { - if (self.owner.target_select_playerbias < 0) - return 0; - - if (e_target.deadflag != DEAD_NO) - return 0; - } - - // Missile - if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0)) - return 0; - - // Team check - if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) - return 0; - - return 1; -} - -void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (attacker.team == self.team) - damage *= 0.5; - - self.velocity += force; - - self.health -= damage; - - if (self.health <= 0) - turret_hk_missile_explode(); -} - -void turret_hk_attack() -{ - local entity missile; - //local entity flash2; - - sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", VOL_BASE, ATTN_NORM); - - missile = spawn (); - missile.solid = SOLID_BBOX; - setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - setorigin(missile, self.tur_shotorg); - - missile.scale = 1; - missile.classname = "hk_missile"; - missile.owner = self; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = self.shot_dmg; - missile.takedamage = DAMAGE_YES; - missile.damageforcescale = 4; - missile.health = 10; - missile.think = turret_hk_missile_think; - missile.event_damage = turret_hk_missile_damage; - missile.nextthink = time + 0.25; - missile.movetype = MOVETYPE_BOUNCEMISSILE; - missile.velocity = self.tur_shotdir_updated * (self.shot_speed * 0.75); - missile.angles = vectoangles(missile.velocity); - missile.touch = turret_hk_missile_explode; //turret_hk_missile_touch; - missile.flags = FL_PROJECTILE; - missile.enemy = self.enemy; - missile.team = self.team; - missile.cnt = time + 30; - missile.ticrate = max(cvar("sys_ticrate"),0.05); - - CSQCProjectile(missile, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, fly sound - - te_explosion (missile.origin); - - if (self.tur_head.frame == 0) - self.tur_head.frame = self.tur_head.frame + 1; - -} - -/* -void turret_hk_missile_touch() -{ - if(other == self.enemy) - turret_hk_missile_explode(); - else - { - if(self.cnt < time) - { - self.cnt = time + 0.25; - self.health = self.health - 5; - if(self.health <= 0) - turret_hk_missile_explode(); - - } - } -} -*/ - -void turret_hk_missile_think() -{ - vector vu, vd, vf, vl, vr, ve; // Vector (direction) - float fu, fd, ff, fl, fr, fe; // Fraction to solid - vector olddir,wishdir,newdir; // Final direction - float lt_for; // Length of Trace FORwrad - float lt_seek; // Length of Trace SEEK (left, right, up down) - float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) - vector pre_pos; - float myspeed; - entity e; - float ad,edist; - - self.nextthink = time + self.ticrate; - - //if (self.cnt < time) - // turret_hk_missile_explode(); - - if (self.enemy.deadflag != DEAD_NO) - self.enemy = world; - - // Pick the closest valid target. - if (!self.enemy) - { - e = findradius(self.origin, 5000); - while (e) - { - if (hk_is_valid_target(e)) - { - if (!self.enemy) - self.enemy = e; - else - if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin)) - self.enemy = e; - } - e = e.chain; - } - } - - self.angles = vectoangles(self.velocity); - self.angles_x = self.angles_x * -1; - makevectors(self.angles); - self.angles_x = self.angles_x * -1; - - if (self.enemy) - { - edist = vlen(self.origin - self.enemy.origin); - // Close enougth to do decent damage? - if ( edist <= (self.owner.shot_radius * 0.25) ) - { - turret_hk_missile_explode(); - return; - } - - // Get data on enemy position - pre_pos = self.enemy.origin + - self.enemy.velocity * - min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5); - - traceline(self.origin, pre_pos,TRUE,self.enemy); - ve = normalize(pre_pos - self.origin); - fe = trace_fraction; - - } - else - { - fe = 0; - } - - if ((fe != 1) || (self.enemy == world) || (edist > 1000)) - { - myspeed = vlen(self.velocity); - - lt_for = myspeed * 3; - lt_seek = myspeed * 2.95; - - // Trace forward - traceline(self.origin, self.origin + v_forward * lt_for,FALSE,self); - vf = trace_endpos; - ff = trace_fraction; - - // Find angular offset - ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles); - - // To close to something, Slow down! - if ( ((ff < 0.7) || (ad > 4)) && (myspeed > hk_minspeed) ) - myspeed = max(myspeed * hk_decel,hk_minspeed); - - // Failry clear, accelerate. - if ( (ff > 0.7) && (myspeed < hk_maxspeed) ) - myspeed = min(myspeed * hk_accel,hk_maxspeed); - - // Setup trace pitch - pt_seek = 1 - ff; - pt_seek = bound(0.15,pt_seek,0.8); - if (ff < 0.5) pt_seek = 1; - - // Trace left - traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,FALSE,self); - vl = trace_endpos; - fl = trace_fraction; - - // Trace right - traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); - vr = trace_endpos; - fr = trace_fraction; - - // Trace up - traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); - vu = trace_endpos; - fu = trace_fraction; - - // Trace down - traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); - vd = trace_endpos; - fd = trace_fraction; - - vl = normalize(vl - self.origin); - vr = normalize(vr - self.origin); - vu = normalize(vu - self.origin); - vd = normalize(vd - self.origin); - - // Panic tresh passed, find a single direction and turn as hard as we can - if (pt_seek == 1) - { - wishdir = v_right; - if (fl > fr) wishdir = -1 * v_right; - if (fu > fl) wishdir = v_up; - if (fd > fu) wishdir = -1 * v_up; - } - else - { - // Normalize our trace vectors to make a smooth path - wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) ); - } - - if (self.enemy) - { - if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target - wishdir = (wishdir * (1 - fe)) + (ve * fe); - } - } - else - { - // Got a clear path to target, speed up fast (if not at full speed) and go straight for it. - myspeed = vlen(self.velocity); - if (myspeed < hk_maxspeed) - myspeed = min(myspeed * hk_accel2,hk_maxspeed); - - wishdir = ve; - //wishdir = normalize(self.enemy.origin - (self.enemy.origin + self.enemy.velocity)); - } - - if ((myspeed > hk_minspeed) && (self.cnt > time)) - myspeed = min(myspeed * hk_accel2,hk_maxspeed); - - // Ranoutagazfish? - if (self.cnt < time) - { - self.cnt = time + 0.25; - self.nextthink = 0; - self.movetype = MOVETYPE_BOUNCE; - sound (self, CHAN_VOICE, "", 0.4 * VOL_BASE, ATTN_NORM); - return; - } - - // Calculate new heading - olddir = normalize(self.velocity); - - newdir = normalize(olddir + wishdir * cvar("g_turrets_unit_hk_std_shot_speed_turnrate")); - - //fu = (1 / hk_maxspeed) * myspeed; - //fd = fu - (0.75 - 0.25); - //newdir = normalize(olddir + wishdir * fd); - - // Set heading & speed - self.velocity = newdir * myspeed; - - // Align model with new heading - self.angles = vectoangles(self.velocity); - - -#ifdef TURRET_DEBUG_HK - //if(self.atime < time) { - if ((fe <= 0.99)||(edist > 1000)) - { - te_lightning2(world,self.origin, self.origin + vr * lt_seek); - te_lightning2(world,self.origin, self.origin + vl * lt_seek); - te_lightning2(world,self.origin, self.origin + vu * lt_seek); - te_lightning2(world,self.origin, self.origin + vd * lt_seek); - te_lightning2(world,self.origin, vf); - } - else - { - te_lightning2(world,self.origin, self.enemy.origin); - } - bprint("Speed: ", ftos(rint(myspeed)), "\n"); - bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n"); - bprint("Trace to target:", ftos(rint(fe * 100)), "%\n"); - self.atime = time + 0.2; - //} -#endif - - UpdateCSQCProjectile(self); -} - -void turret_hk_missile_explode() -{ - vector org2; - float d; - - if(self.event_damage != SUB_Null) - { - self.event_damage = SUB_Null; - self.think = turret_hk_missile_explode; - self.nextthink = time; - return; - } - - if ((other == self.owner)||(other == self.owner.tur_head)) - return; - - //w_deathtypestring = "got hunted to extinction"; - //vector org2; - sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - org2 = findbetterlocation (self.origin, 16); - - // LordHavoc: TE_TEI_BIGEXPLOSION - WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte (MSG_BROADCAST, 78); - WriteCoord (MSG_BROADCAST, org2_x); - WriteCoord (MSG_BROADCAST, org2_y); - WriteCoord (MSG_BROADCAST, org2_z); - - self.event_damage = SUB_Null; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - -#ifdef TURRET_DEBUG - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#endif - - // Target dead, get another is still targeting the same. - if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) - self.owner.enemy = world; - - remove (self); -} - -void turret_hk_postthink() -{ - if (cvar("g_turrets_reloadcvars")) - { - hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max"); - hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed"); - hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel"); - hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2"); - hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel"); - } - - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 5) - self.tur_head.frame = 0; - -} - -void turret_hk_dinit() -{ - if (self.netname == "") self.netname = "Hunter-killer turret"; - - hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max"); - hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed"); - hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel"); - hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2"); - hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel"); - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_RECIVETARGETS; - - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - - self.aim_flags = TFL_AIM_SIMPLE; - - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; - - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.shoot_flags = TFL_SHOOT_CLEARTARGET; - - if (turret_stdproc_init("hk_std",0) == 0) - { - remove(self); - return; - } - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/hk.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fire routine - self.turret_firefunc = turret_hk_attack; - - // re-color badge & handle recoil effect - self.turret_postthink = turret_hk_postthink; - - // What to do when reciveing foreign target data - self.turret_addtarget = turret_hk_addtarget; -} - -/* -* Turret that fires Hunter-killer missiles. -* Missiles seek their target and try to avoid obstacles. If target dies early, they -* pick a new one on their own. -*/ - -/*QUAKED turret_hk (0 .5 .8) ? -hunter-killer missiles. -*/ - -void spawnfunc_turret_hk() -{ - //precache_model ( "models/turrets/hunter2.md3"); - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/hk.md3"); - - self.think = turret_hk_dinit; - self.nextthink = time + 0.5; -} - - +//#define TURRET_DEBUG_HK + +#ifdef TURRET_DEBUG_HK +.float atime; +#endif + +void spawnfunc_turret_hk(); +void turret_hk_dinit(); +void turret_hk_attack(); +void turret_hk_missile_explode(); +void turret_hk_missile_think(); +void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); +float turret_hk_addtarget(entity e_target,entity e_sender); +//void turret_hk_missile_touch(); + +float hk_maxspeed; +float hk_minspeed; +float hk_accel; +float hk_accel2; +float hk_decel; + +float turret_hk_addtarget(entity e_target,entity e_sender) +{ + if (e_target) + { + if (turret_validate_target(self,e_target,self.target_validate_flags) > 0) + { + self.enemy = e_target; + return 1; + } + } + + return 0; +} + +float hk_is_valid_target(entity e_target) +{ + if (e_target == world) + return 0; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return 0; + + // Cant touch this + if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) + return 0; + + // player + if (e_target.flags & FL_CLIENT) + { + if (self.owner.target_select_playerbias < 0) + return 0; + + if (e_target.deadflag != DEAD_NO) + return 0; + } + + // Missile + if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0)) + return 0; + + // Team check + if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) + return 0; + + return 1; +} + +void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if (attacker.team == self.team) + damage *= 0.5; + + self.velocity += force; + + self.health -= damage; + + if (self.health <= 0) + turret_hk_missile_explode(); +} + +void turret_hk_attack() +{ + local entity missile; + //local entity flash2; + + sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", VOL_BASE, ATTN_NORM); + + missile = spawn (); + missile.solid = SOLID_BBOX; + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + setorigin(missile, self.tur_shotorg); + + missile.scale = 1; + missile.classname = "hk_missile"; + missile.owner = self; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = self.shot_dmg; + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = 4; + missile.health = 10; + missile.think = turret_hk_missile_think; + missile.event_damage = turret_hk_missile_damage; + missile.nextthink = time + 0.25; + missile.movetype = MOVETYPE_BOUNCEMISSILE; + missile.velocity = self.tur_shotdir_updated * (self.shot_speed * 0.75); + missile.angles = vectoangles(missile.velocity); + missile.touch = turret_hk_missile_explode; //turret_hk_missile_touch; + missile.flags = FL_PROJECTILE; + missile.enemy = self.enemy; + missile.team = self.team; + missile.cnt = time + 30; + missile.ticrate = max(cvar("sys_ticrate"),0.05); + + CSQCProjectile(missile, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, fly sound + + te_explosion (missile.origin); + + if (self.tur_head.frame == 0) + self.tur_head.frame = self.tur_head.frame + 1; + +} + +/* +void turret_hk_missile_touch() +{ + if(other == self.enemy) + turret_hk_missile_explode(); + else + { + if(self.cnt < time) + { + self.cnt = time + 0.25; + self.health = self.health - 5; + if(self.health <= 0) + turret_hk_missile_explode(); + + } + } +} +*/ + +void turret_hk_missile_think() +{ + vector vu, vd, vf, vl, vr, ve; // Vector (direction) + float fu, fd, ff, fl, fr, fe; // Fraction to solid + vector olddir,wishdir,newdir; // Final direction + float lt_for; // Length of Trace FORwrad + float lt_seek; // Length of Trace SEEK (left, right, up down) + float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) + vector pre_pos; + float myspeed; + entity e; + float ad,edist; + + self.nextthink = time + self.ticrate; + + //if (self.cnt < time) + // turret_hk_missile_explode(); + + if (self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + // Pick the closest valid target. + if (!self.enemy) + { + e = findradius(self.origin, 5000); + while (e) + { + if (hk_is_valid_target(e)) + { + if (!self.enemy) + self.enemy = e; + else + if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin)) + self.enemy = e; + } + e = e.chain; + } + } + + self.angles = vectoangles(self.velocity); + self.angles_x = self.angles_x * -1; + makevectors(self.angles); + self.angles_x = self.angles_x * -1; + + if (self.enemy) + { + edist = vlen(self.origin - self.enemy.origin); + // Close enougth to do decent damage? + if ( edist <= (self.owner.shot_radius * 0.25) ) + { + turret_hk_missile_explode(); + return; + } + + // Get data on enemy position + pre_pos = self.enemy.origin + + self.enemy.velocity * + min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5); + + traceline(self.origin, pre_pos,TRUE,self.enemy); + ve = normalize(pre_pos - self.origin); + fe = trace_fraction; + + } + else + { + fe = 0; + } + + if ((fe != 1) || (self.enemy == world) || (edist > 1000)) + { + myspeed = vlen(self.velocity); + + lt_for = myspeed * 3; + lt_seek = myspeed * 2.95; + + // Trace forward + traceline(self.origin, self.origin + v_forward * lt_for,FALSE,self); + vf = trace_endpos; + ff = trace_fraction; + + // Find angular offset + ad = vlen(vectoangles(normalize(self.enemy.origin - self.origin)) - self.angles); + + // To close to something, Slow down! + if ( ((ff < 0.7) || (ad > 4)) && (myspeed > hk_minspeed) ) + myspeed = max(myspeed * hk_decel,hk_minspeed); + + // Failry clear, accelerate. + if ( (ff > 0.7) && (myspeed < hk_maxspeed) ) + myspeed = min(myspeed * hk_accel,hk_maxspeed); + + // Setup trace pitch + pt_seek = 1 - ff; + pt_seek = bound(0.15,pt_seek,0.8); + if (ff < 0.5) pt_seek = 1; + + // Trace left + traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,FALSE,self); + vl = trace_endpos; + fl = trace_fraction; + + // Trace right + traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); + vr = trace_endpos; + fr = trace_fraction; + + // Trace up + traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); + vu = trace_endpos; + fu = trace_fraction; + + // Trace down + traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self); + vd = trace_endpos; + fd = trace_fraction; + + vl = normalize(vl - self.origin); + vr = normalize(vr - self.origin); + vu = normalize(vu - self.origin); + vd = normalize(vd - self.origin); + + // Panic tresh passed, find a single direction and turn as hard as we can + if (pt_seek == 1) + { + wishdir = v_right; + if (fl > fr) wishdir = -1 * v_right; + if (fu > fl) wishdir = v_up; + if (fd > fu) wishdir = -1 * v_up; + } + else + { + // Normalize our trace vectors to make a smooth path + wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) ); + } + + if (self.enemy) + { + if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target + wishdir = (wishdir * (1 - fe)) + (ve * fe); + } + } + else + { + // Got a clear path to target, speed up fast (if not at full speed) and go straight for it. + myspeed = vlen(self.velocity); + if (myspeed < hk_maxspeed) + myspeed = min(myspeed * hk_accel2,hk_maxspeed); + + wishdir = ve; + //wishdir = normalize(self.enemy.origin - (self.enemy.origin + self.enemy.velocity)); + } + + if ((myspeed > hk_minspeed) && (self.cnt > time)) + myspeed = min(myspeed * hk_accel2,hk_maxspeed); + + // Ranoutagazfish? + if (self.cnt < time) + { + self.cnt = time + 0.25; + self.nextthink = 0; + self.movetype = MOVETYPE_BOUNCE; + sound (self, CHAN_VOICE, "", 0.4 * VOL_BASE, ATTN_NORM); + return; + } + + // Calculate new heading + olddir = normalize(self.velocity); + + newdir = normalize(olddir + wishdir * cvar("g_turrets_unit_hk_std_shot_speed_turnrate")); + + //fu = (1 / hk_maxspeed) * myspeed; + //fd = fu - (0.75 - 0.25); + //newdir = normalize(olddir + wishdir * fd); + + // Set heading & speed + self.velocity = newdir * myspeed; + + // Align model with new heading + self.angles = vectoangles(self.velocity); + + +#ifdef TURRET_DEBUG_HK + //if(self.atime < time) { + if ((fe <= 0.99)||(edist > 1000)) + { + te_lightning2(world,self.origin, self.origin + vr * lt_seek); + te_lightning2(world,self.origin, self.origin + vl * lt_seek); + te_lightning2(world,self.origin, self.origin + vu * lt_seek); + te_lightning2(world,self.origin, self.origin + vd * lt_seek); + te_lightning2(world,self.origin, vf); + } + else + { + te_lightning2(world,self.origin, self.enemy.origin); + } + bprint("Speed: ", ftos(rint(myspeed)), "\n"); + bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n"); + bprint("Trace to target:", ftos(rint(fe * 100)), "%\n"); + self.atime = time + 0.2; + //} +#endif + + UpdateCSQCProjectile(self); +} + +void turret_hk_missile_explode() +{ + vector org2; + float d; + + if(self.event_damage != SUB_Null) + { + self.event_damage = SUB_Null; + self.think = turret_hk_missile_explode; + self.nextthink = time; + return; + } + + if ((other == self.owner)||(other == self.owner.tur_head)) + return; + + //w_deathtypestring = "got hunted to extinction"; + //vector org2; + sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + org2 = findbetterlocation (self.origin, 16); + + // LordHavoc: TE_TEI_BIGEXPLOSION + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 78); + WriteCoord (MSG_BROADCAST, org2_x); + WriteCoord (MSG_BROADCAST, org2_y); + WriteCoord (MSG_BROADCAST, org2_z); + + self.event_damage = SUB_Null; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + +#ifdef TURRET_DEBUG + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#endif + + // Target dead, get another is still targeting the same. + if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) + self.owner.enemy = world; + + remove (self); +} + +void turret_hk_postthink() +{ + if (cvar("g_turrets_reloadcvars")) + { + hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max"); + hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed"); + hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel"); + hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2"); + hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel"); + } + + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 5) + self.tur_head.frame = 0; + +} + +void turret_hk_dinit() +{ + if (self.netname == "") self.netname = "Hunter-killer turret"; + + hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max"); + hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed"); + hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel"); + hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2"); + hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel"); + + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_RECIVETARGETS; + + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + + self.aim_flags = TFL_AIM_SIMPLE; + + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; + + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + self.shoot_flags = TFL_SHOOT_CLEARTARGET; + + if (turret_stdproc_init("hk_std",0) == 0) + { + remove(self); + return; + } + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/hk.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fire routine + self.turret_firefunc = turret_hk_attack; + + // re-color badge & handle recoil effect + self.turret_postthink = turret_hk_postthink; + + // What to do when reciveing foreign target data + self.turret_addtarget = turret_hk_addtarget; +} + +/* +* Turret that fires Hunter-killer missiles. +* Missiles seek their target and try to avoid obstacles. If target dies early, they +* pick a new one on their own. +*/ + +/*QUAKED turret_hk (0 .5 .8) ? +hunter-killer missiles. +*/ + +void spawnfunc_turret_hk() +{ + //precache_model ( "models/turrets/hunter2.md3"); + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hk.md3"); + + self.think = turret_hk_dinit; + self.nextthink = time + 0.5; +} + + diff --git a/data/qcsrc/server/tturrets/units/unit_machinegun.qc b/data/qcsrc/server/tturrets/units/unit_machinegun.qc index 81006bbd7..0f6e886f7 100644 --- a/data/qcsrc/server/tturrets/units/unit_machinegun.qc +++ b/data/qcsrc/server/tturrets/units/unit_machinegun.qc @@ -1,81 +1,81 @@ -void spawnfunc_turret_machinegun(); -void turret_machinegun_std_init(); -void turret_machinegun_attack(); - -//.float bulletcounter; -void turret_machinegun_attack() -{ - - entity flash; - - sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM); - fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, 0, self.shot_force, DEATH_TURRET, 0, 1, cvar("g_balance_uzi_bulletconstant")); - - //w_deathtypestring = "had an alergic reaction due to 10 kilos of led"; - te_smallflash(self.tur_shotorg); - // trailparticles(self,particleeffectnum("EF_MGTURRETTRAIL"),self.tur_shotorg_updated,trace_endpos); - - // muzzle flash for 3rd person view - flash = spawn(); - //setorigin(flash, '43 1 8'); - setmodel(flash, "models/uziflash.md3"); // precision set below - setattachment(flash, self.tur_head, "tag_fire"); - flash.think = W_Uzi_Flash_Go; - flash.nextthink = time + 0.02; - flash.frame = 2; - flash.angles_z = flash.v_angle_z + random() * 180; - flash.alpha = 1; - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; -} - - -void turret_machinegun_std_init() -{ - if (self.netname == "") self.netname = "Machinegun Turret"; - - self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL;// | TFL_TURRCAPS_MISSILEKILL; - self.aim_flags = TFL_AIM_LEAD; - - if(cvar("g_antilag_bullets")) - self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; - else - self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; - - if (turret_stdproc_init("machinegun_std",0) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/machinegun.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fire routine - self.turret_firefunc = turret_machinegun_attack; - -} - - - -/* -* machinegun turret. does what you'd expect -*/ - -/*QUAKED turret_machinegun (0 .5 .8) ? -*/ -void spawnfunc_turret_machinegun() -{ - precache_model ("models/turrets/machinegun.md3"); - precache_model ("models/turrets/base.md3"); - precache_sound ("weapons/uzi_fire.wav"); - - self.think = turret_machinegun_std_init; - self.nextthink = time + 0.5; -} - +void spawnfunc_turret_machinegun(); +void turret_machinegun_std_init(); +void turret_machinegun_attack(); + +//.float bulletcounter; +void turret_machinegun_attack() +{ + + entity flash; + + sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM); + fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, 0, self.shot_force, DEATH_TURRET, 0, 1, cvar("g_balance_uzi_bulletconstant")); + + //w_deathtypestring = "had an alergic reaction due to 10 kilos of led"; + te_smallflash(self.tur_shotorg); + // trailparticles(self,particleeffectnum("EF_MGTURRETTRAIL"),self.tur_shotorg_updated,trace_endpos); + + // muzzle flash for 3rd person view + flash = spawn(); + //setorigin(flash, '43 1 8'); + setmodel(flash, "models/uziflash.md3"); // precision set below + setattachment(flash, self.tur_head, "tag_fire"); + flash.think = W_Uzi_Flash_Go; + flash.nextthink = time + 0.02; + flash.frame = 2; + flash.angles_z = flash.v_angle_z + random() * 180; + flash.alpha = 1; + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; +} + + +void turret_machinegun_std_init() +{ + if (self.netname == "") self.netname = "Machinegun Turret"; + + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL;// | TFL_TURRCAPS_MISSILEKILL; + self.aim_flags = TFL_AIM_LEAD; + + if(cvar("g_antilag_bullets")) + self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; + else + self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; + + if (turret_stdproc_init("machinegun_std",0) == 0) + { + remove(self); + return; + } + + self.damage_flags |= TFL_DMG_HEADSHAKE; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/machinegun.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fire routine + self.turret_firefunc = turret_machinegun_attack; + +} + + + +/* +* machinegun turret. does what you'd expect +*/ + +/*QUAKED turret_machinegun (0 .5 .8) ? +*/ +void spawnfunc_turret_machinegun() +{ + precache_model ("models/turrets/machinegun.md3"); + precache_model ("models/turrets/base.md3"); + precache_sound ("weapons/uzi_fire.wav"); + + self.think = turret_machinegun_std_init; + self.nextthink = time + 0.5; +} + diff --git a/data/qcsrc/server/tturrets/units/unit_mlrs.qc b/data/qcsrc/server/tturrets/units/unit_mlrs.qc index efb086d53..9f62d1f37 100644 --- a/data/qcsrc/server/tturrets/units/unit_mlrs.qc +++ b/data/qcsrc/server/tturrets/units/unit_mlrs.qc @@ -1,142 +1,142 @@ -void spawnfunc_turret_mlrs(); -void turret_mlrs_dinit(); -void turret_mlrs_attack(); -void turret_mlrs_rocket_explode(); -void turret_mlrs_rocket_touch(); - -void turret_mlrs_postthink() -{ - - // 0 = full, 6 = empty - self.tur_head.frame = rint(6 - (self.ammo / self.shot_dmg)); -} - -void turret_mlrs_attack() -{ - entity missile; - - turret_tag_fire_update(); - - sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", VOL_BASE, ATTN_NORM); - - missile = spawn (); - //setsize (missile, '0 0 0', '0 0 0'); // give it some size so it can be shot - setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - setorigin(missile, self.tur_shotorg); - missile.classname = "mlrs_missile"; - missile.owner = self; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = self.shot_dmg; - missile.takedamage = DAMAGE_NO; - missile.damageforcescale = 4; - //missile.health = 25; - missile.think = turret_mlrs_rocket_explode; - - missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); - //missile.nextthink = missile.nextthink + random() * self.shot_spread; - - missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLYMISSILE; - missile.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - missile.angles = vectoangles(missile.velocity); - missile.touch = turret_mlrs_rocket_touch; - missile.flags = FL_PROJECTILE; - missile.solid = SOLID_BBOX; - missile.enemy = self.enemy; - - CSQCProjectile(missile, TRUE, PROJECTILE_ROCKET, FALSE); // no cull, fly sound - - te_explosion (missile.origin); - - //self.tur_head.frame = 7 - self.volly_counter; -} - -void turret_mlrs_rocket_touch() -{ - if( (other == self.owner) || (other == self.owner.tur_head) ) - return; - - PROJECTILE_TOUCH; - - turret_mlrs_rocket_explode(); -} - -void turret_mlrs_rocket_explode() -{ - vector org2; - - if(self.event_damage != SUB_Null) - { - self.event_damage = SUB_Null; - self.think = turret_mlrs_rocket_explode; - self.nextthink = time; - return; - } - - - sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - org2 = findbetterlocation (self.origin, 16); - pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); - //w_deathtypestring = "dident escape the rocket barrage"; -#ifdef TURRET_DEBUG - float d; - - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#else - RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg * 0.5, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); -#endif - - // Target dead, Tell turret. - if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) - self.owner.enemy = world; - - remove (self); -} - -void turret_mlrs_dinit() -{ - if (self.netname == "") self.netname = "MLRS turret"; - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_ZEASE | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_INFRONT; - - if (turret_stdproc_init("mlrs_std",0) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - - self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; - self.volly_counter = self.shot_volly; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/mlrs.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fire routine - self.turret_firefunc = turret_mlrs_attack; - self.turret_postthink = turret_mlrs_postthink; - -} - -/*QUAKED turret_mlrs (0 .5 .8) ? -*/ - -void spawnfunc_turret_mlrs() -{ - //precache_model ( "models/turrets/rocket.md3"); - precache_model ("models/turrets/mlrs.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_mlrs_dinit; - self.nextthink = time + 0.5; -} - - +void spawnfunc_turret_mlrs(); +void turret_mlrs_dinit(); +void turret_mlrs_attack(); +void turret_mlrs_rocket_explode(); +void turret_mlrs_rocket_touch(); + +void turret_mlrs_postthink() +{ + + // 0 = full, 6 = empty + self.tur_head.frame = rint(6 - (self.ammo / self.shot_dmg)); +} + +void turret_mlrs_attack() +{ + entity missile; + + turret_tag_fire_update(); + + sound (self, CHAN_WEAPON, "weapons/rocket_fire.wav", VOL_BASE, ATTN_NORM); + + missile = spawn (); + //setsize (missile, '0 0 0', '0 0 0'); // give it some size so it can be shot + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + setorigin(missile, self.tur_shotorg); + missile.classname = "mlrs_missile"; + missile.owner = self; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = self.shot_dmg; + missile.takedamage = DAMAGE_NO; + missile.damageforcescale = 4; + //missile.health = 25; + missile.think = turret_mlrs_rocket_explode; + + missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); + //missile.nextthink = missile.nextthink + random() * self.shot_spread; + + missile.solid = SOLID_BBOX; + missile.movetype = MOVETYPE_FLYMISSILE; + missile.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + missile.angles = vectoangles(missile.velocity); + missile.touch = turret_mlrs_rocket_touch; + missile.flags = FL_PROJECTILE; + missile.solid = SOLID_BBOX; + missile.enemy = self.enemy; + + CSQCProjectile(missile, TRUE, PROJECTILE_ROCKET, FALSE); // no cull, fly sound + + te_explosion (missile.origin); + + //self.tur_head.frame = 7 - self.volly_counter; +} + +void turret_mlrs_rocket_touch() +{ + if( (other == self.owner) || (other == self.owner.tur_head) ) + return; + + PROJECTILE_TOUCH; + + turret_mlrs_rocket_explode(); +} + +void turret_mlrs_rocket_explode() +{ + vector org2; + + if(self.event_damage != SUB_Null) + { + self.event_damage = SUB_Null; + self.think = turret_mlrs_rocket_explode; + self.nextthink = time; + return; + } + + + sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + org2 = findbetterlocation (self.origin, 16); + pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); + //w_deathtypestring = "dident escape the rocket barrage"; +#ifdef TURRET_DEBUG + float d; + + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg * 0.5, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); +#endif + + // Target dead, Tell turret. + if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy)) + self.owner.enemy = world; + + remove (self); +} + +void turret_mlrs_dinit() +{ + if (self.netname == "") self.netname = "MLRS turret"; + + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_ZEASE | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_INFRONT; + + if (turret_stdproc_init("mlrs_std",0) == 0) + { + remove(self); + return; + } + + self.damage_flags |= TFL_DMG_HEADSHAKE; + + self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; + self.volly_counter = self.shot_volly; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/mlrs.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fire routine + self.turret_firefunc = turret_mlrs_attack; + self.turret_postthink = turret_mlrs_postthink; + +} + +/*QUAKED turret_mlrs (0 .5 .8) ? +*/ + +void spawnfunc_turret_mlrs() +{ + //precache_model ( "models/turrets/rocket.md3"); + precache_model ("models/turrets/mlrs.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_mlrs_dinit; + self.nextthink = time + 0.5; +} + + diff --git a/data/qcsrc/server/tturrets/units/unit_phaser.qc b/data/qcsrc/server/tturrets/units/unit_phaser.qc index 76aeef781..34480afb2 100644 --- a/data/qcsrc/server/tturrets/units/unit_phaser.qc +++ b/data/qcsrc/server/tturrets/units/unit_phaser.qc @@ -1,149 +1,149 @@ -void spawnfunc_turret_phaser(); -void turret_phaser_dinit(); -void turret_phaser_attack(); - -.float fireflag; - -float turret_phaser_firecheck() -{ - if (self.fireflag != 0) return 0; - return turret_stdproc_firecheck(); -} - -void turret_phaser_postthink() -{ - if (self.tur_head.frame == 0) - return; - - if (self.fireflag == 1) - { - if (self.tur_head.frame == 10) - self.tur_head.frame = 1; - else - self.tur_head.frame = self.tur_head.frame +1; - } - else if (self.fireflag == 2 ) - { - self.tur_head.frame = self.tur_head.frame +1; - if (self.tur_head.frame == 15) - { - self.tur_head.frame = 0; - self.fireflag = 0; - } - } -} - -void beam_think() -{ - if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) - { - self.owner.attack_finished_single = time + self.owner.shot_refire; - self.owner.fireflag = 2; - self.owner.tur_head.frame = 10; - sound (self, CHAN_PROJECTILE, "", VOL_BASE, ATTN_NORM); - remove(self); - return; - } - - turret_do_updates(self.owner); - - if (time - self.shot_spread > 0) - { - self.shot_spread = time + 2; - sound (self, CHAN_VOICE, "turrets/phaser.wav", VOL_BASE, ATTN_NORM); - } - - - self.nextthink = time + self.ticrate; - - self.owner.attack_finished_single = time + frametime; - entity oldself; - oldself = self; - self = self.owner; - //w_deathtypestring = "was phased out of existence"; - FireImoBeam ( self.tur_shotorg, - self.tur_shotorg + self.tur_shotdir_updated * self.target_range, - '-1 -1 -1' * self.shot_radius, - '1 1 1' * self.shot_radius, - self.shot_force, - oldself.shot_dmg, - 0.75, - DEATH_TURRET); - self = oldself; - self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256; - -} - -void turret_phaser_attack() -{ - entity beam; - - beam = spawn(); - beam.ticrate = 0.1; //cvar("sys_ticrate"); - setmodel(beam,"models/turrets/phaser_beam.md3"); - beam.effects = EF_LOWPRECISION; - beam.solid = SOLID_NOT; - beam.think = beam_think; - beam.cnt = time + self.shot_speed; - beam.shot_spread = time + 2; - beam.nextthink = time; - beam.owner = self; - beam.shot_dmg = self.shot_dmg / (self.shot_speed / beam.ticrate); - beam.scale = self.target_range / 256; - beam.movetype = MOVETYPE_NONE; - beam.enemy = self.enemy; - beam.bot_dodge = TRUE; - beam.bot_dodgerating = beam.shot_dmg; - sound (beam, CHAN_PROJECTILE, "turrets/phaser.wav", VOL_BASE, ATTN_NORM); - self.fireflag = 1; - - beam.attack_finished_single = self.attack_finished_single; - self.attack_finished_single = time; // + cvar("sys_ticrate"); - - setattachment(beam,self.tur_head,"tag_fire"); - - soundat (self, trace_endpos, CHAN_PROJECTILE, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM); - - if (self.tur_head.frame == 0) - self.tur_head.frame = 1; -} - -void turret_phaser_dinit() -{ - if (self.netname == "") self.netname = "Phaser Cannon"; - - self.turrcaps_flags = TFL_TURRCAPS_SNIPER|TFL_TURRCAPS_HITSCAN|TFL_TURRCAPS_PLAYERKILL; - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.aim_flags = TFL_AIM_ZEASE | TFL_AIM_LEAD; - - if (turret_stdproc_init("phaser_std",0) == 0) - { - remove(self); - return; - } - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/phaser.md3"); - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - self.turret_firecheckfunc = turret_phaser_firecheck; - self.turret_firefunc = turret_phaser_attack; - self.turret_postthink = turret_phaser_postthink; - -} - -/*QUAKED turret_phaser(0 .5 .8) ? -*/ -void spawnfunc_turret_phaser() -{ - precache_sound ("turrets/phaser.wav"); - precache_model ("models/turrets/phaser.md3"); - precache_model ("models/turrets/phaser_beam.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_phaser_dinit; - self.nextthink = time + 0.5; -} - +void spawnfunc_turret_phaser(); +void turret_phaser_dinit(); +void turret_phaser_attack(); + +.float fireflag; + +float turret_phaser_firecheck() +{ + if (self.fireflag != 0) return 0; + return turret_stdproc_firecheck(); +} + +void turret_phaser_postthink() +{ + if (self.tur_head.frame == 0) + return; + + if (self.fireflag == 1) + { + if (self.tur_head.frame == 10) + self.tur_head.frame = 1; + else + self.tur_head.frame = self.tur_head.frame +1; + } + else if (self.fireflag == 2 ) + { + self.tur_head.frame = self.tur_head.frame +1; + if (self.tur_head.frame == 15) + { + self.tur_head.frame = 0; + self.fireflag = 0; + } + } +} + +void beam_think() +{ + if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) + { + self.owner.attack_finished_single = time + self.owner.shot_refire; + self.owner.fireflag = 2; + self.owner.tur_head.frame = 10; + sound (self, CHAN_PROJECTILE, "", VOL_BASE, ATTN_NORM); + remove(self); + return; + } + + turret_do_updates(self.owner); + + if (time - self.shot_spread > 0) + { + self.shot_spread = time + 2; + sound (self, CHAN_VOICE, "turrets/phaser.wav", VOL_BASE, ATTN_NORM); + } + + + self.nextthink = time + self.ticrate; + + self.owner.attack_finished_single = time + frametime; + entity oldself; + oldself = self; + self = self.owner; + //w_deathtypestring = "was phased out of existence"; + FireImoBeam ( self.tur_shotorg, + self.tur_shotorg + self.tur_shotdir_updated * self.target_range, + '-1 -1 -1' * self.shot_radius, + '1 1 1' * self.shot_radius, + self.shot_force, + oldself.shot_dmg, + 0.75, + DEATH_TURRET); + self = oldself; + self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256; + +} + +void turret_phaser_attack() +{ + entity beam; + + beam = spawn(); + beam.ticrate = 0.1; //cvar("sys_ticrate"); + setmodel(beam,"models/turrets/phaser_beam.md3"); + beam.effects = EF_LOWPRECISION; + beam.solid = SOLID_NOT; + beam.think = beam_think; + beam.cnt = time + self.shot_speed; + beam.shot_spread = time + 2; + beam.nextthink = time; + beam.owner = self; + beam.shot_dmg = self.shot_dmg / (self.shot_speed / beam.ticrate); + beam.scale = self.target_range / 256; + beam.movetype = MOVETYPE_NONE; + beam.enemy = self.enemy; + beam.bot_dodge = TRUE; + beam.bot_dodgerating = beam.shot_dmg; + sound (beam, CHAN_PROJECTILE, "turrets/phaser.wav", VOL_BASE, ATTN_NORM); + self.fireflag = 1; + + beam.attack_finished_single = self.attack_finished_single; + self.attack_finished_single = time; // + cvar("sys_ticrate"); + + setattachment(beam,self.tur_head,"tag_fire"); + + soundat (self, trace_endpos, CHAN_PROJECTILE, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; +} + +void turret_phaser_dinit() +{ + if (self.netname == "") self.netname = "Phaser Cannon"; + + self.turrcaps_flags = TFL_TURRCAPS_SNIPER|TFL_TURRCAPS_HITSCAN|TFL_TURRCAPS_PLAYERKILL; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + self.aim_flags = TFL_AIM_ZEASE | TFL_AIM_LEAD; + + if (turret_stdproc_init("phaser_std",0) == 0) + { + remove(self); + return; + } + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/phaser.md3"); + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + self.turret_firecheckfunc = turret_phaser_firecheck; + self.turret_firefunc = turret_phaser_attack; + self.turret_postthink = turret_phaser_postthink; + +} + +/*QUAKED turret_phaser(0 .5 .8) ? +*/ +void spawnfunc_turret_phaser() +{ + precache_sound ("turrets/phaser.wav"); + precache_model ("models/turrets/phaser.md3"); + precache_model ("models/turrets/phaser_beam.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_phaser_dinit; + self.nextthink = time + 0.5; +} + diff --git a/data/qcsrc/server/tturrets/units/unit_plasma.qc b/data/qcsrc/server/tturrets/units/unit_plasma.qc index f1e4837b6..c2307262e 100644 --- a/data/qcsrc/server/tturrets/units/unit_plasma.qc +++ b/data/qcsrc/server/tturrets/units/unit_plasma.qc @@ -1,225 +1,225 @@ -void spawnfunc_turret_plasma(); -void spawnfunc_turret_plasma_dual(); - -void turret_plasma_std_init(); -void turret_plasma_dual_init(); - -void turret_plasma_attack(); -void turret_plasma_projectile_explode(); - -void turret_plasma_postthink() -{ - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 5) - self.tur_head.frame = 0; -} - -void turret_plasma_dual_postthink() -{ - if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3)) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 6) - self.tur_head.frame = 0; -} - -void turret_plasma_attack() -{ - entity proj; - - sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - - proj = spawn (); - setorigin(proj, self.tur_shotorg); - setsize(proj, '-1 -1 -1', '1 1 1'); - proj.classname = "plasmabomb"; - proj.owner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = self.shot_dmg; - proj.think = turret_plasma_projectile_explode; - proj.nextthink = time + 9; - proj.solid = SOLID_BBOX; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - //proj.velocity = self.tur_shotdir_updated * self.shot_speed; - proj.touch = turret_plasma_projectile_explode; - proj.flags = FL_PROJECTILE; - proj.enemy = self.enemy; - proj.flags = FL_PROJECTILE | FL_NOTARGET; - - CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); - - if (self.tur_head.frame == 0) - self.tur_head.frame = 1; -} - -void turret_plasma_dual_attack() -{ - entity proj; - - sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); - proj = spawn (); - setorigin(proj, self.tur_shotorg); - setsize(proj, '0 0 0', '0 0 0'); - proj.classname = "plasmabomb"; - proj.owner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = self.shot_dmg; - proj.think = turret_plasma_projectile_explode; - proj.nextthink = time + 9; - proj.solid = SOLID_BBOX; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - //proj.velocity = self.tur_shotdir_updated * self.shot_speed; - proj.touch = turret_plasma_projectile_explode; - proj.flags = FL_PROJECTILE; - proj.enemy = self.enemy; - proj.flags = FL_PROJECTILE | FL_NOTARGET; - - self.tur_head.frame += 1; - - CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); -} - -void turret_plasma_projectile_explode() -{ - vector org2; - - org2 = findbetterlocation (self.origin, 8); - WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte (MSG_BROADCAST, 79); - WriteCoord (MSG_BROADCAST, org2_x); - WriteCoord (MSG_BROADCAST, org2_y); - WriteCoord (MSG_BROADCAST, org2_z); - WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter - WriteCoord (MSG_BROADCAST, 0); - WriteCoord (MSG_BROADCAST, 0); - WriteByte (MSG_BROADCAST, 155); - - self.event_damage = SUB_Null; - //w_deathtypestring = "ate to much plasma"; -#ifdef TURRET_DEBUG - float d; - - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#else - RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); -#endif - sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM); - - remove (self); -} - -void turret_plasma_std_init() -{ - if (self.netname == "") self.netname = "Plasma Cannon"; - - // What ammo to use - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - - // How to aim - //self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZPREDICT | TFL_AIM_GROUND2; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUND2; - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; - - if (turret_stdproc_init("plasma_std",FALSE) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - - //self.firecheck_flags |= (TFL_FIRECHECK_AFF | TFL_FIRECHECK_VERIFIED); - self.firecheck_flags |= TFL_FIRECHECK_AFF; - - //self.target_select_flags |= TFL_TARGETSELECT_FOV; - //self.target_select_fov = 45; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/plasma.md3"); - // self.tur_head.alpha = -1; - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fireing routine - self.turret_firefunc = turret_plasma_attack; - - // Custom per turret frame stuff. usualy animation. - self.turret_postthink = turret_plasma_postthink; - turret_do_updates(self); -} - - -void turret_plasma_dual_init() -{ - if (self.netname == "") self.netname = "Dual Plasma Cannon"; - - // What ammo to use - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - - // How to aim at targets - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUND2 ; - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - - if (turret_stdproc_init("plasma_dual",0) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - //self.firecheck_flags |= (TFL_FIRECHECK_AFF | TFL_FIRECHECK_VERIFIED); - //self.firecheck_flags |= TFL_FIRECHECK_AFF; - - setmodel(self,"models/turrets/base.md3"); - setmodel(self.tur_head,"models/turrets/plasmad.md3"); - - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - // Our fireing routine - self.turret_firefunc = turret_plasma_dual_attack; - - // Custom per turret frame stuff. usualy animation. - self.turret_postthink = turret_plasma_dual_postthink; -} - - -/* -* Basic moderate (std) or fast (dual) fireing, short-mid range energy cannon. -* Not too mutch of a therat on its own, but can be rather dangerous in groups. -* Regenerates ammo slowly, support with a fusionreactor(s) to do some real damage. -*/ - -/*QUAKED turret_plasma (0 .5 .8) ? -*/ -void spawnfunc_turret_plasma() -{ - g_turrets_common_precash(); - precache_model ("models/turrets/plasma.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_plasma_std_init; - self.nextthink = time + 0.5; -} - -/*QUAKED turret_plasma_dual (0 .5 .8) ? -*/ -void spawnfunc_turret_plasma_dual() -{ - - precache_model ("models/turrets/plasmad.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_plasma_dual_init; - self.nextthink = time + 0.5; -} - +void spawnfunc_turret_plasma(); +void spawnfunc_turret_plasma_dual(); + +void turret_plasma_std_init(); +void turret_plasma_dual_init(); + +void turret_plasma_attack(); +void turret_plasma_projectile_explode(); + +void turret_plasma_postthink() +{ + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 5) + self.tur_head.frame = 0; +} + +void turret_plasma_dual_postthink() +{ + if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3)) + self.tur_head.frame = self.tur_head.frame + 1; + + if (self.tur_head.frame > 6) + self.tur_head.frame = 0; +} + +void turret_plasma_attack() +{ + entity proj; + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + proj = spawn (); + setorigin(proj, self.tur_shotorg); + setsize(proj, '-1 -1 -1', '1 1 1'); + proj.classname = "plasmabomb"; + proj.owner = self; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = self.shot_dmg; + proj.think = turret_plasma_projectile_explode; + proj.nextthink = time + 9; + proj.solid = SOLID_BBOX; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + //proj.velocity = self.tur_shotdir_updated * self.shot_speed; + proj.touch = turret_plasma_projectile_explode; + proj.flags = FL_PROJECTILE; + proj.enemy = self.enemy; + proj.flags = FL_PROJECTILE | FL_NOTARGET; + + CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; +} + +void turret_plasma_dual_attack() +{ + entity proj; + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_NORM); + proj = spawn (); + setorigin(proj, self.tur_shotorg); + setsize(proj, '0 0 0', '0 0 0'); + proj.classname = "plasmabomb"; + proj.owner = self; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = self.shot_dmg; + proj.think = turret_plasma_projectile_explode; + proj.nextthink = time + 9; + proj.solid = SOLID_BBOX; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + //proj.velocity = self.tur_shotdir_updated * self.shot_speed; + proj.touch = turret_plasma_projectile_explode; + proj.flags = FL_PROJECTILE; + proj.enemy = self.enemy; + proj.flags = FL_PROJECTILE | FL_NOTARGET; + + self.tur_head.frame += 1; + + CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); +} + +void turret_plasma_projectile_explode() +{ + vector org2; + + org2 = findbetterlocation (self.origin, 8); + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, 79); + WriteCoord (MSG_BROADCAST, org2_x); + WriteCoord (MSG_BROADCAST, org2_y); + WriteCoord (MSG_BROADCAST, org2_z); + WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter + WriteCoord (MSG_BROADCAST, 0); + WriteCoord (MSG_BROADCAST, 0); + WriteByte (MSG_BROADCAST, 155); + + self.event_damage = SUB_Null; + //w_deathtypestring = "ate to much plasma"; +#ifdef TURRET_DEBUG + float d; + + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET, world); +#endif + sound (self, CHAN_PROJECTILE, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM); + + remove (self); +} + +void turret_plasma_std_init() +{ + if (self.netname == "") self.netname = "Plasma Cannon"; + + // What ammo to use + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + + // How to aim + //self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZPREDICT | TFL_AIM_GROUND2; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUND2; + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; + + if (turret_stdproc_init("plasma_std",FALSE) == 0) + { + remove(self); + return; + } + + self.damage_flags |= TFL_DMG_HEADSHAKE; + + //self.firecheck_flags |= (TFL_FIRECHECK_AFF | TFL_FIRECHECK_VERIFIED); + self.firecheck_flags |= TFL_FIRECHECK_AFF; + + //self.target_select_flags |= TFL_TARGETSELECT_FOV; + //self.target_select_fov = 45; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/plasma.md3"); + // self.tur_head.alpha = -1; + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fireing routine + self.turret_firefunc = turret_plasma_attack; + + // Custom per turret frame stuff. usualy animation. + self.turret_postthink = turret_plasma_postthink; + turret_do_updates(self); +} + + +void turret_plasma_dual_init() +{ + if (self.netname == "") self.netname = "Dual Plasma Cannon"; + + // What ammo to use + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + + // How to aim at targets + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUND2 ; + self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; + + if (turret_stdproc_init("plasma_dual",0) == 0) + { + remove(self); + return; + } + + self.damage_flags |= TFL_DMG_HEADSHAKE; + //self.firecheck_flags |= (TFL_FIRECHECK_AFF | TFL_FIRECHECK_VERIFIED); + //self.firecheck_flags |= TFL_FIRECHECK_AFF; + + setmodel(self,"models/turrets/base.md3"); + setmodel(self.tur_head,"models/turrets/plasmad.md3"); + + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + // Our fireing routine + self.turret_firefunc = turret_plasma_dual_attack; + + // Custom per turret frame stuff. usualy animation. + self.turret_postthink = turret_plasma_dual_postthink; +} + + +/* +* Basic moderate (std) or fast (dual) fireing, short-mid range energy cannon. +* Not too mutch of a therat on its own, but can be rather dangerous in groups. +* Regenerates ammo slowly, support with a fusionreactor(s) to do some real damage. +*/ + +/*QUAKED turret_plasma (0 .5 .8) ? +*/ +void spawnfunc_turret_plasma() +{ + g_turrets_common_precash(); + precache_model ("models/turrets/plasma.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_plasma_std_init; + self.nextthink = time + 0.5; +} + +/*QUAKED turret_plasma_dual (0 .5 .8) ? +*/ +void spawnfunc_turret_plasma_dual() +{ + + precache_model ("models/turrets/plasmad.md3"); + precache_model ("models/turrets/base.md3"); + + self.think = turret_plasma_dual_init; + self.nextthink = time + 0.5; +} + diff --git a/data/qcsrc/server/tturrets/units/unit_targettrigger.qc b/data/qcsrc/server/tturrets/units/unit_targettrigger.qc index ee3dab320..9d786f407 100644 --- a/data/qcsrc/server/tturrets/units/unit_targettrigger.qc +++ b/data/qcsrc/server/tturrets/units/unit_targettrigger.qc @@ -1,42 +1,42 @@ -void spawnfunc_turret_targettrigger(); -void turret_targettrigger_touch(); - -void turret_targettrigger_touch() -{ - entity e; - if (self.cnt > time) return; - entity oldself; - oldself = self; - - e = find(world, targetname, self.target); - while (e) - { - if (e.turrcaps_flags & TFL_TURRCAPS_RECIVETARGETS) - { - self = e; - if(e.turret_addtarget) - e.turret_addtarget(other,oldself); - } - - e = find(e, targetname, oldself.target); - } - - oldself.cnt = time + 0.5; - - self = oldself; -} - -/*QUAKED turret_targettrigger (.5 .5 .5) ? -*/ -void spawnfunc_turret_targettrigger() -{ - if (!cvar("g_turrets")) - { - remove(self); - return; - } - - InitTrigger (); - - self.touch = turret_targettrigger_touch; -} +void spawnfunc_turret_targettrigger(); +void turret_targettrigger_touch(); + +void turret_targettrigger_touch() +{ + entity e; + if (self.cnt > time) return; + entity oldself; + oldself = self; + + e = find(world, targetname, self.target); + while (e) + { + if (e.turrcaps_flags & TFL_TURRCAPS_RECIVETARGETS) + { + self = e; + if(e.turret_addtarget) + e.turret_addtarget(other,oldself); + } + + e = find(e, targetname, oldself.target); + } + + oldself.cnt = time + 0.5; + + self = oldself; +} + +/*QUAKED turret_targettrigger (.5 .5 .5) ? +*/ +void spawnfunc_turret_targettrigger() +{ + if (!cvar("g_turrets")) + { + remove(self); + return; + } + + InitTrigger (); + + self.touch = turret_targettrigger_touch; +} diff --git a/data/qcsrc/server/tturrets/units/unit_tessla.qc b/data/qcsrc/server/tturrets/units/unit_tessla.qc index 9f2899aab..863dcf33d 100644 --- a/data/qcsrc/server/tturrets/units/unit_tessla.qc +++ b/data/qcsrc/server/tturrets/units/unit_tessla.qc @@ -1,178 +1,178 @@ -void spawnfunc_turret_tesla(); -void turret_tesla_dinit(); -void turret_tesla_fire(); - -.float toasted; -entity toast(entity from, float range, float damage) -{ - entity e; - entity etarget; - float d,dd; - - dd = range + 1; - - e = findradius(from.origin,range); - while (e) - { - if ((e.toasted != 1) && (e != from)) - if (turret_validate_target(self,e,self.target_validate_flags) > 0) - { - traceline(from.origin,e.origin,MOVE_WORLDONLY,from); - if (trace_fraction == 1.0) - { - d = vlen(e.origin - from.origin); - if (d < dd) - { - dd = d; - etarget = e; - } - } - } - e = e.chain; - } - - if (etarget) - { - te_smallflash(etarget.origin); - te_csqc_lightningarc(from.origin,etarget.origin); - Damage(etarget,self,self,damage,DEATH_TURRET,etarget.origin,'0 0 0'); - etarget.toasted = 1; - } - - return etarget; -} - -float turret_tesla_firecheck() -{ - if not (turret_stdproc_firecheck()) - return 0; - - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.enemy = turret_select_target(); - - if(self.enemy) - return 1; - - return 0; - -} - -void turret_tesla_fire() -{ - entity e,t; - float d,r,i; - - //w_deathtypestring = "discoverd how a tesla coil works"; - - d = self.shot_dmg; - r = self.target_range; - e = spawn(); - setorigin(e,self.tur_shotorg); - - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - t = toast(e,r,d); - remove(e); - - if (t == world) return; - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_TEAMCHECK; - - self.attack_finished_single = time + self.shot_refire; - for (i = 0; i < 10; ++i) - { - d *= 0.5; - r *= 0.85; - t = toast(t,r,d); - if (t == world) break; - } - - e = findchainfloat(toasted, 1); - while (e) - { - e.toasted = 0; - e = e.chain; - } -} - -void turret_tesla_postthink() -{ - if not (self.tur_active) - { - self.tur_head.avelocity = '0 0 0'; - return; - } - - if(self.ammo < self.shot_dmg) - { - self.tur_head.avelocity = '0 9 0' * (self.ammo / self.shot_dmg); - } - else - { - self.tur_head.avelocity = '0 90 0' * (self.ammo / self.shot_dmg); - - if(self.attack_finished_single > time) - return; - - float f; - f = (self.ammo / self.ammo_max); - f = f*f; - if(f > random()) - if(random() < 0.1) - te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350)); - } -} - - -void turret_tesla_dinit() -{ - if (self.netname == "") self.netname = "Tesla Coil"; - - self.turrcaps_flags = TFL_TURRCAPS_HITSCAN | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; - - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_OWM_AMMO; - self.shoot_flags = TFL_SHOOT_CUSTOM; - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.aim_flags = TFL_AIM_NO; - self.track_flags = TFL_TRACK_NO; - - if (turret_stdproc_init("tesla_std",0) == 0) - { - remove(self); - return; - } - - setmodel(self,"models/turrets/tesla_base.md3"); - setmodel(self.tur_head,"models/turrets/tesla_head.md3"); - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - if (!turret_tag_setup()) - dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); - - self.turret_firefunc = turret_tesla_fire; - self.turret_postthink = turret_tesla_postthink; - self.turret_firecheckfunc = turret_tesla_firecheck; -} - -/*QUAKED turret_tesla (0 .5 .8) ? -*/ -void spawnfunc_turret_tesla() -{ - precache_model ("models/turrets/tesla_head.md3"); - precache_model ("models/turrets/tesla_base.md3"); - - - self.think = turret_tesla_dinit; - self.nextthink = time + 0.5; -} - +void spawnfunc_turret_tesla(); +void turret_tesla_dinit(); +void turret_tesla_fire(); + +.float toasted; +entity toast(entity from, float range, float damage) +{ + entity e; + entity etarget; + float d,dd; + + dd = range + 1; + + e = findradius(from.origin,range); + while (e) + { + if ((e.toasted != 1) && (e != from)) + if (turret_validate_target(self,e,self.target_validate_flags) > 0) + { + traceline(from.origin,e.origin,MOVE_WORLDONLY,from); + if (trace_fraction == 1.0) + { + d = vlen(e.origin - from.origin); + if (d < dd) + { + dd = d; + etarget = e; + } + } + } + e = e.chain; + } + + if (etarget) + { + te_smallflash(etarget.origin); + te_csqc_lightningarc(from.origin,etarget.origin); + Damage(etarget,self,self,damage,DEATH_TURRET,etarget.origin,'0 0 0'); + etarget.toasted = 1; + } + + return etarget; +} + +float turret_tesla_firecheck() +{ + if not (turret_stdproc_firecheck()) + return 0; + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + self.enemy = turret_select_target(); + + if(self.enemy) + return 1; + + return 0; + +} + +void turret_tesla_fire() +{ + entity e,t; + float d,r,i; + + //w_deathtypestring = "discoverd how a tesla coil works"; + + d = self.shot_dmg; + r = self.target_range; + e = spawn(); + setorigin(e,self.tur_shotorg); + + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + t = toast(e,r,d); + remove(e); + + if (t == world) return; + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_TEAMCHECK; + + self.attack_finished_single = time + self.shot_refire; + for (i = 0; i < 10; ++i) + { + d *= 0.5; + r *= 0.85; + t = toast(t,r,d); + if (t == world) break; + } + + e = findchainfloat(toasted, 1); + while (e) + { + e.toasted = 0; + e = e.chain; + } +} + +void turret_tesla_postthink() +{ + if not (self.tur_active) + { + self.tur_head.avelocity = '0 0 0'; + return; + } + + if(self.ammo < self.shot_dmg) + { + self.tur_head.avelocity = '0 9 0' * (self.ammo / self.shot_dmg); + } + else + { + self.tur_head.avelocity = '0 90 0' * (self.ammo / self.shot_dmg); + + if(self.attack_finished_single > time) + return; + + float f; + f = (self.ammo / self.ammo_max); + f = f*f; + if(f > random()) + if(random() < 0.1) + te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350)); + } +} + + +void turret_tesla_dinit() +{ + if (self.netname == "") self.netname = "Tesla Coil"; + + self.turrcaps_flags = TFL_TURRCAPS_HITSCAN | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_OWM_AMMO; + self.shoot_flags = TFL_SHOOT_CUSTOM; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + + if (turret_stdproc_init("tesla_std",0) == 0) + { + remove(self); + return; + } + + setmodel(self,"models/turrets/tesla_base.md3"); + setmodel(self.tur_head,"models/turrets/tesla_head.md3"); + + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; + + if (!turret_tag_setup()) + dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n"); + + self.turret_firefunc = turret_tesla_fire; + self.turret_postthink = turret_tesla_postthink; + self.turret_firecheckfunc = turret_tesla_firecheck; +} + +/*QUAKED turret_tesla (0 .5 .8) ? +*/ +void spawnfunc_turret_tesla() +{ + precache_model ("models/turrets/tesla_head.md3"); + precache_model ("models/turrets/tesla_base.md3"); + + + self.think = turret_tesla_dinit; + self.nextthink = time + 0.5; +} + diff --git a/data/qcsrc/server/tturrets/units/unit_walker.qc b/data/qcsrc/server/tturrets/units/unit_walker.qc index e24f8107e..8ebe3830d 100644 --- a/data/qcsrc/server/tturrets/units/unit_walker.qc +++ b/data/qcsrc/server/tturrets/units/unit_walker.qc @@ -1,1022 +1,1022 @@ -#define ANIM_NO 0 -#define ANIM_REVERSE 1 -#define ANIM_WALK 2 -#define ANIM_RUN 3 -#define ANIM_STRAFE_L 4 -#define ANIM_STRAFE_R 5 -#define ANIM_TURN 6 -#define ANIM_JUMP 7 -#define ANIM_LAND 8 -#define ANIM_PAIN 9 -#define ANIM_MEELE 10 -#define ANIM_SWIM 11 -#define ANIM_ROAM 12 - -#define WVM_IDLE_UP 25 -#define WVM_IDLE 50 - -#define WVM_PATH 1000 -#define WVM_ENEMY 2000 -#define WVM_STOP 3000 -#define WVM_DODGE 4000 -#define WVM_PANIC 10000 -#define walker_verbs_move verbs_move - -#define WVA_MINIGUN 100 -#define WVA_ROCKET 500 -#define WVA_MEELE 1000 -#define walker_verbs_attack verbs_attack - -#define WVI_IDLE 1 -#define WVI_ROAM 10 -#define walker_verbs_idle verbs_idle - -.float animflag; -.entity wkr_spawn; - -#define WALKER_MIN '-70 -70 5' -#define WALKER_MAX '70 70 70' - -#define WALKER_PATH(s,e) pathlib_astar(s,e) - -float walker_firecheck() -{ - if (self.animflag == ANIM_MEELE) - return 0; - - return turret_stdproc_firecheck(); -} - -void walker_meele_do_dmg() -{ - vector where; - entity e; - makevectors(self.angles); - where = self.origin + v_forward * 128; - - //w_deathtypestring = "tried to hug the cute spider thingy."; - e = findradius(where,64); - while (e) - { - if (turret_validate_target(self,e,self.target_validate_flags)) - if (e != self && e.owner != self) - Damage(e,self,self,cvar("g_turrets_unit_walker_std_meele_dmg"),DEATH_TURRET,'0 0 0', v_forward * cvar("g_turrets_unit_walker_std_meele_force") ); - - e = e.chain; - } -} - -void walker_animate() -{ - vector real_angle; - float vz; - - real_angle = vectoangles(self.steerto) - self.angles; - vz = self.velocity_z; - - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame +1; - - if (self.tur_head.frame > 12) - self.tur_head.frame = 0; - - switch (self.animflag) - { - - case ANIM_NO: - if (self.frame != 0) - self.frame = 0; - - movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); - - break; - - case ANIM_REVERSE: - if ((self.frame < 5) || (self.frame > 25)) - self.frame = 25; - - self.frame = self.frame -1; - movelib_move_simple(v_forward * -1 ,cvar("g_turrets_unit_walker_speed_walk"),0.6); - - if (self.frame < 5) - self.frame = 25; - - break; - - case ANIM_TURN: - - if ((self.frame < 30) || (self.frame > 55)) - self.frame = 30; - - self.frame = self.frame + 1; - - self.angles_y += bound(-15,shortangle_f(real_angle_y,self.angles_y),15); - - movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); - - if (self.frame > 55) - self.frame = 35; - - break; - - case ANIM_WALK: - if ((self.frame < 5) || (self.frame > 25)) - self.frame = 5; - - self.frame = self.frame +1; - self.angles_y += bound(-10,shortangle_f(real_angle_y,self.angles_y),10); - movelib_move_simple(v_forward ,cvar("g_turrets_unit_walker_speed_walk"),0.6); - - if (self.frame > 25) - self.frame = 5; - - break; - - case ANIM_ROAM: - if ((self.frame < 5) || (self.frame > 25)) - self.frame = 5; - - self.frame = self.frame +1; - - self.angles_y += bound(-5,shortangle_f(real_angle_y,self.angles_y),5); - - movelib_move_simple(v_forward ,cvar("g_turrets_unit_walker_speed_roam"),0.5); - - if (self.frame > 25) - self.frame = 5; - - break; - - case ANIM_SWIM: - if ((self.frame < 142) || (self.frame > 151)) - self.frame = 142; - - self.frame = self.frame +1; - - self.angles_y += bound(-10,shortangle_f(real_angle_y,self.angles_y),10); - self.angles_x += bound(-10,shortangle_f(real_angle_x,self.angles_x),10); - - movelib_move_simple(v_forward, cvar("g_turrets_unit_walker_speed_swim"),0.3); - vz = self.velocity_z + sin(time * 4) * 8; - - if (self.frame > 151) - self.frame = 146; - - break; - - case ANIM_RUN: - - if ((self.frame < 5) || (self.frame > 25)) - self.frame = 5; - - self.angles_y += bound(-5,shortangle_f(real_angle_y,self.angles_y),5); - movelib_move_simple(v_forward, cvar("g_turrets_unit_walker_speed_run"),0.6); - - if (self.frame > 25) - self.frame = 5; - - break; - - case ANIM_STRAFE_L: - if ((self.frame < 30) || (self.frame > 55)) - self.frame = 30; - - self.frame = self.frame + 1; - self.angles_y += bound(-2.5,shortangle_f(real_angle_y,self.angles_y),2.5); - movelib_move_simple(v_right * -1, cvar("g_turrets_unit_walker_speed_walk"),0.8); - - if (self.frame > 55) - self.frame = 35; - break; - - case ANIM_STRAFE_R: - if ((self.frame < 60) || (self.frame > 65)) - self.frame = 60; - - self.frame = self.frame + 1; - self.angles_y += bound(-2.5,shortangle_f(real_angle_y,self.angles_y),2.5); - movelib_move_simple(v_right, cvar("g_turrets_unit_walker_speed_walk"),0.8); - - if (self.frame > 85) - self.frame = 65; - - break; - - case ANIM_JUMP: - if ((self.frame < 95) || (self.frame > 100)) - self.frame = 95; - - self.velocity += '0 0 1' * cvar("g_turrets_unit_walker_speed_jump"); - - if (self.frame > 100) - self.frame = self.frame + 1; - - break; - - case ANIM_LAND: - if ((self.frame < 100) || (self.frame > 107)) - self.frame = 100; - - self.frame = self.frame + 1; - - if (self.frame > 107) - self.animflag = ANIM_NO; - - break; - - case ANIM_PAIN: - if ((self.frame < 60) || (self.frame > 95)) - self.frame = 90; - - self.frame = self.frame + 1; - - if (self.frame > 95) - self.animflag = ANIM_NO; - - break; - - case ANIM_MEELE: - if ((self.frame < 123) || (self.frame > 140)) - self.frame = 123; - - movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); - - self.frame = self.frame + 2; - - if (self.frame == 133) - walker_meele_do_dmg(); - - if (self.frame > 140) - { - self.animflag = ANIM_NO; - self.frame = 0; - } - } - - self.velocity_z = vz; - - if (self.flags & FL_ONGROUND) - movelib_groundalign4point(300,100,0.25); - -} - - -void walker_rocket_explode() -{ - vector org2; - - if (self.event_damage != SUB_Null) - { - self.event_damage = SUB_Null; - self.think = walker_rocket_explode; - self.nextthink = time; - return; - } - - sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); - org2 = findbetterlocation (self.origin, 16); - - pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); - //w_deathtypestring = "got blasted to oblivion"; - RadiusDamage (self, self.owner, cvar("g_turrets_unit_walker_std_rocket_dmg"), 0, cvar("g_turrets_unit_walker_std_rocket_radius"), world, cvar("g_turrets_unit_walker_std_rocket_force"), DEATH_TURRET, world); - - remove (self); -} - -void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.health = self.health - damage; - self.velocity = self.velocity + vforce; - if (self.health <= 0) - walker_rocket_explode(); -} - -#define WALKER_ROCKET_MOVE movelib_move_simple(newdir,cvar("g_turrets_unit_walker_std_rocket_speed"),cvar("g_turrets_unit_walker_std_rocket_tunrate")); UpdateCSQCProjectile(self) -void walker_rocket_loop(); -void walker_rocket_think() -{ - vector newdir; - float edist; - float itime; - float m_speed; - - self.nextthink = time; - - edist = vlen(self.enemy.origin - self.origin); - - // Simulate crude guidance - if (self.cnt < time) - { - if (edist < 1000) - self.tur_shotorg = randomvec() * min(edist,64); - else - self.tur_shotorg = randomvec() * min(edist,256); - - self.cnt = time + 0.5; - } - - if (edist < 256) - self.tur_shotorg = '0 0 0'; - - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - self.nextthink = time; - return; - } - - if (self.shot_dmg != 1337) - if (random() < 0.01) - { - walker_rocket_loop(); - return; - } - - m_speed = vlen(self.velocity) + cvar("g_turrets_unit_walker_std_rocket_speed_add"); - - // Enemy dead? just keep on the current heading then. - if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) - { - // Make sure we dont return to tracking a respawned player - self.enemy = world; - } - - if (self.enemy) - { - itime = max(edist / m_speed,1); - newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg); - } - else - { - newdir = normalize(self.velocity); - } - - WALKER_ROCKET_MOVE; -} - -void walker_rocket_loop3() -{ - vector newdir; - self.nextthink = time; - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - return; - } - - if (vlen(self.origin - self.tur_shotorg) < 128 ) - { - self.think = walker_rocket_think; - return; - } - - newdir = steerlib_pull(self.tur_shotorg); - WALKER_ROCKET_MOVE; - - self.angles = vectoangles(self.velocity); -} - -void walker_rocket_loop2() -{ - vector newdir; - - self.nextthink = time; - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - return; - } - - if (vlen(self.origin - self.tur_shotorg) < 128 ) - { - self.tur_shotorg = self.origin - '0 0 200'; - self.think = walker_rocket_loop3; - return; - } - - newdir = steerlib_pull(self.tur_shotorg); - WALKER_ROCKET_MOVE; -} - -void walker_rocket_loop() -{ - self.nextthink = time; - self.tur_shotorg = self.origin + '0 0 400'; - self.think = walker_rocket_loop2; - self.shot_dmg = 1337; -} - -void walker_fire_rocket(vector org) -{ - - entity rocket; - - - self.angles_x *= -1; - makevectors(self.angles); - self.angles_x *= -1; - - te_explosion (org); - - rocket = spawn (); - setorigin(rocket, org); - - sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_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; - - rocket.takedamage = DAMAGE_YES; - - rocket.damageforcescale = 2; - rocket.health = 25; - rocket.tur_shotorg = randomvec() * 512; - rocket.cnt = time + 1; - rocket.enemy = self.enemy; - - if (random() < 0.01) - rocket.think = walker_rocket_loop; - else - rocket.think = walker_rocket_think; - - rocket.event_damage = walker_rocket_damage; - - rocket.nextthink = time;// + 0.25; - rocket.movetype = MOVETYPE_FLY; - rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * cvar("g_turrets_unit_walker_std_rocket_speed"); - rocket.angles = vectoangles(rocket.velocity); - rocket.touch = walker_rocket_explode; - rocket.flags = FL_PROJECTILE; - rocket.solid = SOLID_BBOX; - rocket.tur_health = time + 9; - - CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound -} - -void rv_think() -{ - float f; - vector org; - entity oldself; - - if (self.owner.deadflag != DEAD_NO) - { - remove(self); - return; - } - - self.cnt = self.cnt -1; - - if (self.cnt < 0) - { - remove(self); - return; - } - - if (self.cnt > 1) - f = gettagindex(self.owner,"tag_rocket01"); - else - f = gettagindex(self.owner,"tag_rocket02"); - - org = gettaginfo(self.owner,f); - - self.nextthink = time + 0.2; - oldself = self; - self = self.owner; - walker_fire_rocket(org); - self = oldself; -} - -float walker_moveverb_path(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - if (self.pathcurrent) - return verb.verb_static_value; - - return VS_CALL_NO; - - case VCM_DO: - // Do we have a path? - if not(self.pathcurrent) - { - return VS_CALL_NO; - } - else - { - // Are we close enougth to a path node to switch to the next? - if (vlen(self.origin - self.pathcurrent.origin) < 64) - if (self.pathcurrent.path_next == world) - { - // Path endpoint reached - pathlib_deletepath(self.pathcurrent.owner); - self.pathcurrent = world; - - if (self.pathgoal) - { - if (self.pathgoal.use) - self.pathgoal.use(); - - if (self.pathgoal.enemy) - { - self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin); - self.pathgoal = self.pathgoal.enemy; - } - } - else - self.pathgoal = world; - } - else - self.pathcurrent = self.pathcurrent.path_next; - } - - - if (self.pathcurrent) - { - switch (self.waterlevel) - { - case 0: - self.animflag = ANIM_WALK; - case 1: - case 2: - if (self.animflag == ANIM_WALK) - self.animflag = ANIM_WALK; - else - self.animflag = ANIM_SWIM; - break; - case 3: - self.animflag = ANIM_SWIM; - } - - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); - - return VS_CALL_YES_DOING; - } - else - return VS_CALL_YES_DONE; - - case VCM_REMOVE: - - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); - - self.pathcurrent = world; - - return VS_CALL_YES_DONE; - } - - return VS_CALL_YES_DONE; -} - -float walker_moveverb_enemy(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.enemy) - if (self.spawnflags & TSF_NO_PATHBREAK) - if (self.pathcurrent) - return VS_CALL_NO; - - if (self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - if (self.enemy == world) - return VS_CALL_NO; - - //if (tracewalk(self.enemy, self.origin, self.mins, self.maxs, self.enemy.origin, MOVE_NORMAL)) - return verb.verb_static_value; - - //return VS_CALL_NO; - - case VCM_DO: - switch (self.waterlevel) - { - case 0: - if (self.tur_dist_enemy > 500) - self.animflag = ANIM_RUN; - else - self.animflag = ANIM_WALK; - case 1: - case 2: - if (self.animflag != ANIM_SWIM) - self.animflag = ANIM_WALK; - else - self.animflag = ANIM_SWIM; - break; - case 3: - self.animflag = ANIM_SWIM; - } - - self.moveto = self.enemy.origin; - self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -float walker_moveverb_idle_pause(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - return verb.verb_static_value; - - case VCM_DO: - - self.moveto = self.origin; - self.steerto = v_forward; - self.animflag = ANIM_NO; - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -float walker_moveverb_idle_roam(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - return verb.verb_static_value; - - case VCM_DO: - if(verb.wait < time) - { - trace_fraction = 0; - while(trace_fraction != 1.0) - { - self.moveto = self.origin + (v_forward * 256); - self.moveto += v_right * (random() * 256); - self.moveto -= v_right * (random() * 256); - - tracebox(self.origin+'0 0 64',self.mins,self.maxs,self.moveto + '0 0 64',MOVE_NORMAL,self); - } - verb.wait = time + 10; - } - else - { - if(verb.wait - time < 9) - if(vlen(self.moveto - self.origin) < 32) - { - verbstack_push(self.walker_verbs_move, walker_moveverb_idle_pause, WVM_IDLE + WVM_IDLE_UP, random() * (verb.wait - time), self); - self.animflag = ANIM_NO; - return VS_CALL_REMOVE; - } - } - - self.steerto = steerlib_attract(self.moveto,256); - te_lightning1(self,self.origin + '0 0 64',self.moveto + '0 0 64'); - - - - switch (self.waterlevel) - { - case 0: - self.animflag = ANIM_ROAM; - case 1: - case 2: - if (self.animflag != ANIM_SWIM) - self.animflag = ANIM_ROAM; - - break; - case 3: - self.animflag = ANIM_SWIM; - } - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -float walker_moveverb_idle(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - if (self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - return verb.verb_static_value; - - case VCM_DO: - - //if (random() < 0.5) - verbstack_push(self.walker_verbs_move, walker_moveverb_idle_pause, WVM_IDLE + WVM_IDLE_UP, random() * 5, self); - //else - // verbstack_push(self.walker_verbs_move, walker_moveverb_idle_roam, WVM_IDLE + WVM_IDLE_UP, random() * 15); - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -float walker_attackverb_meele(float eval) -{ - switch (eval) - { - case VCM_EVAL: - - vector wish_angle; - - if (cvar("g_turrets_nofire")) - return VS_CALL_NO; - - if (self.animflag == ANIM_SWIM || self.animflag == ANIM_MEELE) - return VS_CALL_NO; - - if (!self.enemy) - return VS_CALL_NO; - - wish_angle = angleofs(self,self.enemy); - - if (self.tur_dist_enemy < cvar("g_turrets_unit_walker_std_meele_range")) - if (fabs(wish_angle_y) < 15) - return verb.verb_static_value; - - return VS_CALL_NO; - - case VCM_DO: - - self.moveto = self.enemy.origin; - self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); - self.animflag = ANIM_MEELE; - - return VS_CALL_YES_DOING; - } - - return VS_CALL_YES_DONE; -} - -float walker_attackverb_rockets(float eval) -{ - switch (eval) - { - case VCM_EVAL: - if (self.tur_head.attack_finished_single > time) - return VS_CALL_NO; - - if not(self.enemy) - return VS_CALL_NO; - - if (cvar("g_turrets_nofire")) - return VS_CALL_NO; - - if (self.tur_dist_enemy < cvar("g_turrets_unit_walker_std_rockets_range_min")) - return VS_CALL_NO; - - if (self.tur_dist_enemy > cvar("g_turrets_unit_walker_std_rockets_range")) - return VS_CALL_NO; - - return verb.verb_static_value; - - case VCM_DO: - - entity rv; - - rv = spawn(); - rv.think = rv_think; - rv.nextthink = time; - rv.cnt = 4; - rv.owner = self; - - self.tur_head.attack_finished_single = time + cvar("g_turrets_unit_walker_std_rocket_refire"); - - return VS_CALL_YES_DONE; - } - - return VS_CALL_YES_DONE; -} - -void walker_postthink() -{ - - self.angles_x *= -1; - makevectors(self.angles); - self.angles_x *= -1; - - verbstack_pop(self.walker_verbs_attack); - verbstack_pop(self.walker_verbs_move); - - walker_animate(); -} - -void walker_attack() -{ - entity flash; - - //w_deathtypestring = "was miniguned"; - sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM); - fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, 0, self.shot_force, DEATH_TURRET, 0, 1, cvar("g_balance_uzi_bulletconstant")); - if (self.uzi_bulletcounter == 2) - { - - flash = spawn(); - - setmodel(flash, "models/uziflash.md3"); - setattachment(flash, self.tur_head, "tag_fire"); - - flash.scale = 3; - flash.think = W_Uzi_Flash_Go; - flash.nextthink = time + 0.02; - flash.frame = 2; - flash.angles_z = flash.v_angle_z + random() * 180; - flash.alpha = 1; - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - - self.uzi_bulletcounter = self.uzi_bulletcounter = 0; - } - - self.uzi_bulletcounter = self.uzi_bulletcounter + 1; - self.tur_head.frame = self.tur_head.frame + 1; -} - - -void walker_respawnhook() -{ - vector vtmp; - entity e; - - self.origin = self.wkr_spawn.origin; - //self.wkr_props.solid = SOLID_BBOX; - //self.wkr_props.alpha = 1; - - self.angles = self.wkr_spawn.angles; - vtmp = self.wkr_spawn.origin; - vtmp_z += self.wkr_spawn.maxs_z; - setorigin(self,vtmp); - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Warning! initital waypoint for Walker does NOT exsist!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - self.pathcurrent = WALKER_PATH(self.origin,e.origin); - self.pathgoal = e; - } - } -} -void walker_diehook() -{ - turret_trowgib2(self.origin,self.velocity + v_up * 200,'-0.6 -0.2 -02',self,time + random() * 1); - turret_trowgib2(self.origin + '0 0 64',self.velocity + v_forward * 150 + v_up * 150,'-0.2 -0.2 -02',self.tur_head,time + random() * 2 + 3); - - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); - - self.pathcurrent = world; - - if (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) - { - remove(self.wkr_spawn); - - verbstack_flush(self.walker_verbs_move); - verbstack_flush(self.walker_verbs_attack); - verbstack_flush(self.walker_verbs_idle); - - remove(self.walker_verbs_move); - remove(self.walker_verbs_attack); - remove(self.walker_verbs_idle); - } - -} - -void turret_walker_dinit() -{ - - entity e; - - if (self.netname == "") self.netname = "Walker Turret"; - self.wkr_spawn = spawn(); - - self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_HEADATTACHED; - self.aim_flags = TFL_AIM_LEAD; - - if (cvar("g_antilag_bullets")) - self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; - else - self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; - - - self.turret_respawnhook = walker_respawnhook; - self.turret_diehook = walker_diehook; - - self.ticrate = 0.05; - if (turret_stdproc_init("walker_std",0) == 0) - { - remove(self); - return; - } - - self.walker_verbs_move = spawn(); - self.walker_verbs_attack = spawn(); - self.walker_verbs_idle = spawn(); - - verbstack_push(self.walker_verbs_move, walker_moveverb_idle, WVM_IDLE, 0, self); - verbstack_push(self.walker_verbs_move, walker_moveverb_enemy, WVM_ENEMY, 0, self); - verbstack_push(self.walker_verbs_move, walker_moveverb_path, WVM_PATH, 0, self); - - verbstack_push(self.walker_verbs_attack, walker_attackverb_meele, WVA_MEELE, 0, self); - verbstack_push(self.walker_verbs_attack, walker_attackverb_rockets, WVA_ROCKET, 0, self); - - self.damage_flags |= TFL_DMG_DEATH_NOGIBS; - - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - - self.iscreature = TRUE; - self.movetype = MOVETYPE_WALK; - self.solid = SOLID_SLIDEBOX; - self.takedamage = DAMAGE_AIM; - - setmodel(self,"models/turrets/walker_body.md3"); - setmodel(self.tur_head,"models/turrets/walker_head_minigun.md3"); - setmodel(self.wkr_spawn,"models/turrets/walker_spawn.md3"); - - setattachment(self.tur_head,self,"tag_head"); - - self.wkr_spawn.angles = self.angles; - self.wkr_spawn.solid = SOLID_NOT; - - traceline(self.origin + '0 0 16', self.origin - '0 0 10000', MOVE_WORLDONLY, self); - setorigin(self.wkr_spawn,trace_endpos + '0 0 4'); - setorigin(self,self.wkr_spawn.origin); - - setsize(self,WALKER_MIN,WALKER_MAX); - - self.idle_aim = '0 0 0'; - self.turret_firecheckfunc = walker_firecheck; - self.turret_firefunc = walker_attack; - self.turret_postthink = walker_postthink; - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Initital waypoint for walker does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - self.pathcurrent = WALKER_PATH(self.origin,e.origin); - self.pathgoal = e; - } - } -} - - -void spawnfunc_turret_walker() -{ - g_turrets_common_precash(); - - precache_model ("models/turrets/walker_head_minigun.md3"); - precache_model ("models/turrets/walker_body.md3"); - precache_model ("models/turrets/walker_props.md3"); - precache_model ("models/turrets/walker_spawn.md3"); - precache_model ( "models/turrets/rocket.md3"); - precache_sound ( "weapons/rocket_impact.wav" ); - - self.think = turret_walker_dinit; - self.nextthink = time + 0.5; -} +#define ANIM_NO 0 +#define ANIM_REVERSE 1 +#define ANIM_WALK 2 +#define ANIM_RUN 3 +#define ANIM_STRAFE_L 4 +#define ANIM_STRAFE_R 5 +#define ANIM_TURN 6 +#define ANIM_JUMP 7 +#define ANIM_LAND 8 +#define ANIM_PAIN 9 +#define ANIM_MEELE 10 +#define ANIM_SWIM 11 +#define ANIM_ROAM 12 + +#define WVM_IDLE_UP 25 +#define WVM_IDLE 50 + +#define WVM_PATH 1000 +#define WVM_ENEMY 2000 +#define WVM_STOP 3000 +#define WVM_DODGE 4000 +#define WVM_PANIC 10000 +#define walker_verbs_move verbs_move + +#define WVA_MINIGUN 100 +#define WVA_ROCKET 500 +#define WVA_MEELE 1000 +#define walker_verbs_attack verbs_attack + +#define WVI_IDLE 1 +#define WVI_ROAM 10 +#define walker_verbs_idle verbs_idle + +.float animflag; +.entity wkr_spawn; + +#define WALKER_MIN '-70 -70 5' +#define WALKER_MAX '70 70 70' + +#define WALKER_PATH(s,e) pathlib_astar(s,e) + +float walker_firecheck() +{ + if (self.animflag == ANIM_MEELE) + return 0; + + return turret_stdproc_firecheck(); +} + +void walker_meele_do_dmg() +{ + vector where; + entity e; + makevectors(self.angles); + where = self.origin + v_forward * 128; + + //w_deathtypestring = "tried to hug the cute spider thingy."; + e = findradius(where,64); + while (e) + { + if (turret_validate_target(self,e,self.target_validate_flags)) + if (e != self && e.owner != self) + Damage(e,self,self,cvar("g_turrets_unit_walker_std_meele_dmg"),DEATH_TURRET,'0 0 0', v_forward * cvar("g_turrets_unit_walker_std_meele_force") ); + + e = e.chain; + } +} + +void walker_animate() +{ + vector real_angle; + float vz; + + real_angle = vectoangles(self.steerto) - self.angles; + vz = self.velocity_z; + + if (self.tur_head.frame != 0) + self.tur_head.frame = self.tur_head.frame +1; + + if (self.tur_head.frame > 12) + self.tur_head.frame = 0; + + switch (self.animflag) + { + + case ANIM_NO: + if (self.frame != 0) + self.frame = 0; + + movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); + + break; + + case ANIM_REVERSE: + if ((self.frame < 5) || (self.frame > 25)) + self.frame = 25; + + self.frame = self.frame -1; + movelib_move_simple(v_forward * -1 ,cvar("g_turrets_unit_walker_speed_walk"),0.6); + + if (self.frame < 5) + self.frame = 25; + + break; + + case ANIM_TURN: + + if ((self.frame < 30) || (self.frame > 55)) + self.frame = 30; + + self.frame = self.frame + 1; + + self.angles_y += bound(-15,shortangle_f(real_angle_y,self.angles_y),15); + + movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); + + if (self.frame > 55) + self.frame = 35; + + break; + + case ANIM_WALK: + if ((self.frame < 5) || (self.frame > 25)) + self.frame = 5; + + self.frame = self.frame +1; + self.angles_y += bound(-10,shortangle_f(real_angle_y,self.angles_y),10); + movelib_move_simple(v_forward ,cvar("g_turrets_unit_walker_speed_walk"),0.6); + + if (self.frame > 25) + self.frame = 5; + + break; + + case ANIM_ROAM: + if ((self.frame < 5) || (self.frame > 25)) + self.frame = 5; + + self.frame = self.frame +1; + + self.angles_y += bound(-5,shortangle_f(real_angle_y,self.angles_y),5); + + movelib_move_simple(v_forward ,cvar("g_turrets_unit_walker_speed_roam"),0.5); + + if (self.frame > 25) + self.frame = 5; + + break; + + case ANIM_SWIM: + if ((self.frame < 142) || (self.frame > 151)) + self.frame = 142; + + self.frame = self.frame +1; + + self.angles_y += bound(-10,shortangle_f(real_angle_y,self.angles_y),10); + self.angles_x += bound(-10,shortangle_f(real_angle_x,self.angles_x),10); + + movelib_move_simple(v_forward, cvar("g_turrets_unit_walker_speed_swim"),0.3); + vz = self.velocity_z + sin(time * 4) * 8; + + if (self.frame > 151) + self.frame = 146; + + break; + + case ANIM_RUN: + + if ((self.frame < 5) || (self.frame > 25)) + self.frame = 5; + + self.angles_y += bound(-5,shortangle_f(real_angle_y,self.angles_y),5); + movelib_move_simple(v_forward, cvar("g_turrets_unit_walker_speed_run"),0.6); + + if (self.frame > 25) + self.frame = 5; + + break; + + case ANIM_STRAFE_L: + if ((self.frame < 30) || (self.frame > 55)) + self.frame = 30; + + self.frame = self.frame + 1; + self.angles_y += bound(-2.5,shortangle_f(real_angle_y,self.angles_y),2.5); + movelib_move_simple(v_right * -1, cvar("g_turrets_unit_walker_speed_walk"),0.8); + + if (self.frame > 55) + self.frame = 35; + break; + + case ANIM_STRAFE_R: + if ((self.frame < 60) || (self.frame > 65)) + self.frame = 60; + + self.frame = self.frame + 1; + self.angles_y += bound(-2.5,shortangle_f(real_angle_y,self.angles_y),2.5); + movelib_move_simple(v_right, cvar("g_turrets_unit_walker_speed_walk"),0.8); + + if (self.frame > 85) + self.frame = 65; + + break; + + case ANIM_JUMP: + if ((self.frame < 95) || (self.frame > 100)) + self.frame = 95; + + self.velocity += '0 0 1' * cvar("g_turrets_unit_walker_speed_jump"); + + if (self.frame > 100) + self.frame = self.frame + 1; + + break; + + case ANIM_LAND: + if ((self.frame < 100) || (self.frame > 107)) + self.frame = 100; + + self.frame = self.frame + 1; + + if (self.frame > 107) + self.animflag = ANIM_NO; + + break; + + case ANIM_PAIN: + if ((self.frame < 60) || (self.frame > 95)) + self.frame = 90; + + self.frame = self.frame + 1; + + if (self.frame > 95) + self.animflag = ANIM_NO; + + break; + + case ANIM_MEELE: + if ((self.frame < 123) || (self.frame > 140)) + self.frame = 123; + + movelib_beak_simple(cvar("g_turrets_unit_walker_speed_stop")); + + self.frame = self.frame + 2; + + if (self.frame == 133) + walker_meele_do_dmg(); + + if (self.frame > 140) + { + self.animflag = ANIM_NO; + self.frame = 0; + } + } + + self.velocity_z = vz; + + if (self.flags & FL_ONGROUND) + movelib_groundalign4point(300,100,0.25); + +} + + +void walker_rocket_explode() +{ + vector org2; + + if (self.event_damage != SUB_Null) + { + self.event_damage = SUB_Null; + self.think = walker_rocket_explode; + self.nextthink = time; + return; + } + + sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + org2 = findbetterlocation (self.origin, 16); + + pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); + //w_deathtypestring = "got blasted to oblivion"; + RadiusDamage (self, self.owner, cvar("g_turrets_unit_walker_std_rocket_dmg"), 0, cvar("g_turrets_unit_walker_std_rocket_radius"), world, cvar("g_turrets_unit_walker_std_rocket_force"), DEATH_TURRET, world); + + remove (self); +} + +void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.health = self.health - damage; + self.velocity = self.velocity + vforce; + if (self.health <= 0) + walker_rocket_explode(); +} + +#define WALKER_ROCKET_MOVE movelib_move_simple(newdir,cvar("g_turrets_unit_walker_std_rocket_speed"),cvar("g_turrets_unit_walker_std_rocket_tunrate")); UpdateCSQCProjectile(self) +void walker_rocket_loop(); +void walker_rocket_think() +{ + vector newdir; + float edist; + float itime; + float m_speed; + + self.nextthink = time; + + edist = vlen(self.enemy.origin - self.origin); + + // Simulate crude guidance + if (self.cnt < time) + { + if (edist < 1000) + self.tur_shotorg = randomvec() * min(edist,64); + else + self.tur_shotorg = randomvec() * min(edist,256); + + self.cnt = time + 0.5; + } + + if (edist < 256) + self.tur_shotorg = '0 0 0'; + + + if (self.tur_health < time) + { + self.think = walker_rocket_explode; + self.nextthink = time; + return; + } + + if (self.shot_dmg != 1337) + if (random() < 0.01) + { + walker_rocket_loop(); + return; + } + + m_speed = vlen(self.velocity) + cvar("g_turrets_unit_walker_std_rocket_speed_add"); + + // Enemy dead? just keep on the current heading then. + if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) + { + // Make sure we dont return to tracking a respawned player + self.enemy = world; + } + + if (self.enemy) + { + itime = max(edist / m_speed,1); + newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg); + } + else + { + newdir = normalize(self.velocity); + } + + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop3() +{ + vector newdir; + self.nextthink = time; + + if (self.tur_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 128 ) + { + self.think = walker_rocket_think; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; + + self.angles = vectoangles(self.velocity); +} + +void walker_rocket_loop2() +{ + vector newdir; + + self.nextthink = time; + + if (self.tur_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 128 ) + { + self.tur_shotorg = self.origin - '0 0 200'; + self.think = walker_rocket_loop3; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop() +{ + self.nextthink = time; + self.tur_shotorg = self.origin + '0 0 400'; + self.think = walker_rocket_loop2; + self.shot_dmg = 1337; +} + +void walker_fire_rocket(vector org) +{ + + entity rocket; + + + self.angles_x *= -1; + makevectors(self.angles); + self.angles_x *= -1; + + te_explosion (org); + + rocket = spawn (); + setorigin(rocket, org); + + sound (self, CHAN_WEAPON, "weapons/hagar_fire.wav", VOL_BASE, ATTN_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; + + rocket.takedamage = DAMAGE_YES; + + rocket.damageforcescale = 2; + rocket.health = 25; + rocket.tur_shotorg = randomvec() * 512; + rocket.cnt = time + 1; + rocket.enemy = self.enemy; + + if (random() < 0.01) + rocket.think = walker_rocket_loop; + else + rocket.think = walker_rocket_think; + + rocket.event_damage = walker_rocket_damage; + + rocket.nextthink = time;// + 0.25; + rocket.movetype = MOVETYPE_FLY; + rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * cvar("g_turrets_unit_walker_std_rocket_speed"); + rocket.angles = vectoangles(rocket.velocity); + rocket.touch = walker_rocket_explode; + rocket.flags = FL_PROJECTILE; + rocket.solid = SOLID_BBOX; + rocket.tur_health = time + 9; + + CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound +} + +void rv_think() +{ + float f; + vector org; + entity oldself; + + if (self.owner.deadflag != DEAD_NO) + { + remove(self); + return; + } + + self.cnt = self.cnt -1; + + if (self.cnt < 0) + { + remove(self); + return; + } + + if (self.cnt > 1) + f = gettagindex(self.owner,"tag_rocket01"); + else + f = gettagindex(self.owner,"tag_rocket02"); + + org = gettaginfo(self.owner,f); + + self.nextthink = time + 0.2; + oldself = self; + self = self.owner; + walker_fire_rocket(org); + self = oldself; +} + +float walker_moveverb_path(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + if (self.pathcurrent) + return verb.verb_static_value; + + return VS_CALL_NO; + + case VCM_DO: + // Do we have a path? + if not(self.pathcurrent) + { + return VS_CALL_NO; + } + else + { + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + } + + + if (self.pathcurrent) + { + switch (self.waterlevel) + { + case 0: + self.animflag = ANIM_WALK; + case 1: + case 2: + if (self.animflag == ANIM_WALK) + self.animflag = ANIM_WALK; + else + self.animflag = ANIM_SWIM; + break; + case 3: + self.animflag = ANIM_SWIM; + } + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + + return VS_CALL_YES_DOING; + } + else + return VS_CALL_YES_DONE; + + case VCM_REMOVE: + + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); + + self.pathcurrent = world; + + return VS_CALL_YES_DONE; + } + + return VS_CALL_YES_DONE; +} + +float walker_moveverb_enemy(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.enemy) + if (self.spawnflags & TSF_NO_PATHBREAK) + if (self.pathcurrent) + return VS_CALL_NO; + + if (self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + if (self.enemy == world) + return VS_CALL_NO; + + //if (tracewalk(self.enemy, self.origin, self.mins, self.maxs, self.enemy.origin, MOVE_NORMAL)) + return verb.verb_static_value; + + //return VS_CALL_NO; + + case VCM_DO: + switch (self.waterlevel) + { + case 0: + if (self.tur_dist_enemy > 500) + self.animflag = ANIM_RUN; + else + self.animflag = ANIM_WALK; + case 1: + case 2: + if (self.animflag != ANIM_SWIM) + self.animflag = ANIM_WALK; + else + self.animflag = ANIM_SWIM; + break; + case 3: + self.animflag = ANIM_SWIM; + } + + self.moveto = self.enemy.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +float walker_moveverb_idle_pause(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + return verb.verb_static_value; + + case VCM_DO: + + self.moveto = self.origin; + self.steerto = v_forward; + self.animflag = ANIM_NO; + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +float walker_moveverb_idle_roam(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + return verb.verb_static_value; + + case VCM_DO: + if(verb.wait < time) + { + trace_fraction = 0; + while(trace_fraction != 1.0) + { + self.moveto = self.origin + (v_forward * 256); + self.moveto += v_right * (random() * 256); + self.moveto -= v_right * (random() * 256); + + tracebox(self.origin+'0 0 64',self.mins,self.maxs,self.moveto + '0 0 64',MOVE_NORMAL,self); + } + verb.wait = time + 10; + } + else + { + if(verb.wait - time < 9) + if(vlen(self.moveto - self.origin) < 32) + { + verbstack_push(self.walker_verbs_move, walker_moveverb_idle_pause, WVM_IDLE + WVM_IDLE_UP, random() * (verb.wait - time), self); + self.animflag = ANIM_NO; + return VS_CALL_REMOVE; + } + } + + self.steerto = steerlib_attract(self.moveto,256); + te_lightning1(self,self.origin + '0 0 64',self.moveto + '0 0 64'); + + + + switch (self.waterlevel) + { + case 0: + self.animflag = ANIM_ROAM; + case 1: + case 2: + if (self.animflag != ANIM_SWIM) + self.animflag = ANIM_ROAM; + + break; + case 3: + self.animflag = ANIM_SWIM; + } + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +float walker_moveverb_idle(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + if (self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + return verb.verb_static_value; + + case VCM_DO: + + //if (random() < 0.5) + verbstack_push(self.walker_verbs_move, walker_moveverb_idle_pause, WVM_IDLE + WVM_IDLE_UP, random() * 5, self); + //else + // verbstack_push(self.walker_verbs_move, walker_moveverb_idle_roam, WVM_IDLE + WVM_IDLE_UP, random() * 15); + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +float walker_attackverb_meele(float eval) +{ + switch (eval) + { + case VCM_EVAL: + + vector wish_angle; + + if (cvar("g_turrets_nofire")) + return VS_CALL_NO; + + if (self.animflag == ANIM_SWIM || self.animflag == ANIM_MEELE) + return VS_CALL_NO; + + if (!self.enemy) + return VS_CALL_NO; + + wish_angle = angleofs(self,self.enemy); + + if (self.tur_dist_enemy < cvar("g_turrets_unit_walker_std_meele_range")) + if (fabs(wish_angle_y) < 15) + return verb.verb_static_value; + + return VS_CALL_NO; + + case VCM_DO: + + self.moveto = self.enemy.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + self.animflag = ANIM_MEELE; + + return VS_CALL_YES_DOING; + } + + return VS_CALL_YES_DONE; +} + +float walker_attackverb_rockets(float eval) +{ + switch (eval) + { + case VCM_EVAL: + if (self.tur_head.attack_finished_single > time) + return VS_CALL_NO; + + if not(self.enemy) + return VS_CALL_NO; + + if (cvar("g_turrets_nofire")) + return VS_CALL_NO; + + if (self.tur_dist_enemy < cvar("g_turrets_unit_walker_std_rockets_range_min")) + return VS_CALL_NO; + + if (self.tur_dist_enemy > cvar("g_turrets_unit_walker_std_rockets_range")) + return VS_CALL_NO; + + return verb.verb_static_value; + + case VCM_DO: + + entity rv; + + rv = spawn(); + rv.think = rv_think; + rv.nextthink = time; + rv.cnt = 4; + rv.owner = self; + + self.tur_head.attack_finished_single = time + cvar("g_turrets_unit_walker_std_rocket_refire"); + + return VS_CALL_YES_DONE; + } + + return VS_CALL_YES_DONE; +} + +void walker_postthink() +{ + + self.angles_x *= -1; + makevectors(self.angles); + self.angles_x *= -1; + + verbstack_pop(self.walker_verbs_attack); + verbstack_pop(self.walker_verbs_move); + + walker_animate(); +} + +void walker_attack() +{ + entity flash; + + //w_deathtypestring = "was miniguned"; + sound (self, CHAN_WEAPON, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM); + fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, 0, self.shot_force, DEATH_TURRET, 0, 1, cvar("g_balance_uzi_bulletconstant")); + if (self.uzi_bulletcounter == 2) + { + + flash = spawn(); + + setmodel(flash, "models/uziflash.md3"); + setattachment(flash, self.tur_head, "tag_fire"); + + flash.scale = 3; + flash.think = W_Uzi_Flash_Go; + flash.nextthink = time + 0.02; + flash.frame = 2; + flash.angles_z = flash.v_angle_z + random() * 180; + flash.alpha = 1; + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + + self.uzi_bulletcounter = self.uzi_bulletcounter = 0; + } + + self.uzi_bulletcounter = self.uzi_bulletcounter + 1; + self.tur_head.frame = self.tur_head.frame + 1; +} + + +void walker_respawnhook() +{ + vector vtmp; + entity e; + + self.origin = self.wkr_spawn.origin; + //self.wkr_props.solid = SOLID_BBOX; + //self.wkr_props.alpha = 1; + + self.angles = self.wkr_spawn.angles; + vtmp = self.wkr_spawn.origin; + vtmp_z += self.wkr_spawn.maxs_z; + setorigin(self,vtmp); + + if (self.target != "") + { + e = find(world,targetname,self.target); + if (!e) + { + dprint("Warning! initital waypoint for Walker does NOT exsist!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; + } + } +} +void walker_diehook() +{ + turret_trowgib2(self.origin,self.velocity + v_up * 200,'-0.6 -0.2 -02',self,time + random() * 1); + turret_trowgib2(self.origin + '0 0 64',self.velocity + v_forward * 150 + v_up * 150,'-0.2 -0.2 -02',self.tur_head,time + random() * 2 + 3); + + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); + + self.pathcurrent = world; + + if (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + { + remove(self.wkr_spawn); + + verbstack_flush(self.walker_verbs_move); + verbstack_flush(self.walker_verbs_attack); + verbstack_flush(self.walker_verbs_idle); + + remove(self.walker_verbs_move); + remove(self.walker_verbs_attack); + remove(self.walker_verbs_idle); + } + +} + +void turret_walker_dinit() +{ + + entity e; + + if (self.netname == "") self.netname = "Walker Turret"; + self.wkr_spawn = spawn(); + + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; + self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_HEADATTACHED; + self.aim_flags = TFL_AIM_LEAD; + + if (cvar("g_antilag_bullets")) + self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; + else + self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; + + + self.turret_respawnhook = walker_respawnhook; + self.turret_diehook = walker_diehook; + + self.ticrate = 0.05; + if (turret_stdproc_init("walker_std",0) == 0) + { + remove(self); + return; + } + + self.walker_verbs_move = spawn(); + self.walker_verbs_attack = spawn(); + self.walker_verbs_idle = spawn(); + + verbstack_push(self.walker_verbs_move, walker_moveverb_idle, WVM_IDLE, 0, self); + verbstack_push(self.walker_verbs_move, walker_moveverb_enemy, WVM_ENEMY, 0, self); + verbstack_push(self.walker_verbs_move, walker_moveverb_path, WVM_PATH, 0, self); + + verbstack_push(self.walker_verbs_attack, walker_attackverb_meele, WVA_MEELE, 0, self); + verbstack_push(self.walker_verbs_attack, walker_attackverb_rockets, WVA_ROCKET, 0, self); + + self.damage_flags |= TFL_DMG_DEATH_NOGIBS; + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + + self.iscreature = TRUE; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + + setmodel(self,"models/turrets/walker_body.md3"); + setmodel(self.tur_head,"models/turrets/walker_head_minigun.md3"); + setmodel(self.wkr_spawn,"models/turrets/walker_spawn.md3"); + + setattachment(self.tur_head,self,"tag_head"); + + self.wkr_spawn.angles = self.angles; + self.wkr_spawn.solid = SOLID_NOT; + + traceline(self.origin + '0 0 16', self.origin - '0 0 10000', MOVE_WORLDONLY, self); + setorigin(self.wkr_spawn,trace_endpos + '0 0 4'); + setorigin(self,self.wkr_spawn.origin); + + setsize(self,WALKER_MIN,WALKER_MAX); + + self.idle_aim = '0 0 0'; + self.turret_firecheckfunc = walker_firecheck; + self.turret_firefunc = walker_attack; + self.turret_postthink = walker_postthink; + + if (self.target != "") + { + e = find(world,targetname,self.target); + if (!e) + { + dprint("Initital waypoint for walker does NOT exsist, fix your map!\n"); + self.target = ""; + } + + if (e.classname != "turret_checkpoint") + dprint("Warning: not a turrret path\n"); + else + { + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; + } + } +} + + +void spawnfunc_turret_walker() +{ + g_turrets_common_precash(); + + precache_model ("models/turrets/walker_head_minigun.md3"); + precache_model ("models/turrets/walker_body.md3"); + precache_model ("models/turrets/walker_props.md3"); + precache_model ("models/turrets/walker_spawn.md3"); + precache_model ( "models/turrets/rocket.md3"); + precache_sound ( "weapons/rocket_impact.wav" ); + + self.think = turret_walker_dinit; + self.nextthink = time + 0.5; +} diff --git a/data/qcsrc/server/vehicles/vehicles.qh b/data/qcsrc/server/vehicles/vehicles.qh index 28edd2b06..aa8e73055 100644 --- a/data/qcsrc/server/vehicles/vehicles.qh +++ b/data/qcsrc/server/vehicles/vehicles.qh @@ -1,52 +1,52 @@ -//#define VEHICLES_ENABLED -#ifdef VEHICLES_ENABLED - -#message "with tZork vehicles (experimental)" - -float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05 -float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A -float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 - -#define CCVAR(part) cvar(strcat(self.cvar_basename,part)) -//.string cvar_basename; - -.float vehicle_flags; -#define VHF_HASSHIELD 2 -#define VHF_SHIELDREGEN 4 -#define VHF_HEALTHREGEN 8 - -.float hud; -.float rockets; -.float rockets_reload; -.entity gun1; -.entity gun2; - -.float vehicle_health; -.float vehicle_shield; -.float vehicle_heat; - -.entity vehicle; -.entity vehicle_viewport; -.entity vehicle_hudmodel; - -.float anim_start; -.float anim_end; - -.float dmg_time; - -float server_fps; - -#define VHEF_NORMAL 0 -#define VHEF_EJECT 1 - -.void(float exit_flags) vehicle_exit; -.void() vehicle_enter; -.void() vehicle_die; -.void() vehicle_spawn; -.float(float message) vehicle_message; - -#include "vehicles.qc" -#include "spiderbot.qc" -#include "racer.qc" - -#endif +//#define VEHICLES_ENABLED +#ifdef VEHICLES_ENABLED + +#message "with tZork vehicles (experimental)" + +float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05 +float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A +float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 + +#define CCVAR(part) cvar(strcat(self.cvar_basename,part)) +//.string cvar_basename; + +.float vehicle_flags; +#define VHF_HASSHIELD 2 +#define VHF_SHIELDREGEN 4 +#define VHF_HEALTHREGEN 8 + +.float hud; +.float rockets; +.float rockets_reload; +.entity gun1; +.entity gun2; + +.float vehicle_health; +.float vehicle_shield; +.float vehicle_heat; + +.entity vehicle; +.entity vehicle_viewport; +.entity vehicle_hudmodel; + +.float anim_start; +.float anim_end; + +.float dmg_time; + +float server_fps; + +#define VHEF_NORMAL 0 +#define VHEF_EJECT 1 + +.void(float exit_flags) vehicle_exit; +.void() vehicle_enter; +.void() vehicle_die; +.void() vehicle_spawn; +.float(float message) vehicle_message; + +#include "vehicles.qc" +#include "spiderbot.qc" +#include "racer.qc" + +#endif diff --git a/data/qcsrc/server/verbstack.qc b/data/qcsrc/server/verbstack.qc index ed8c928c8..b7c29d48b 100644 --- a/data/qcsrc/server/verbstack.qc +++ b/data/qcsrc/server/verbstack.qc @@ -1,200 +1,200 @@ -/// Some default stacks. -.entity verbs_idle; -.entity verbs_attack; -.entity verbs_move; - -/// This global gets set to the verb in question each time the stack manager calls verb_call -entity verb; - -/// Execure this verb -#define VCM_DO 0 -/// Return the value of this verb. Return VS_CALL_REMOVE to delete it. -#define VCM_EVAL 1 -/// This verb is beeing removed NOW (not sent when verb_call returns VS_CALL_REMOVE) -#define VCM_REMOVE 2 - -/// Verb callback -.float(float message) verb_call; - -/// Points to this verb's stack. -.entity verbstack; - -/// Static value of this verb -.float verb_static_value; - -/// verb_call returns this when a verb in not doable -#define VS_CALL_NO 0 -/// verb_call(VCM_DO) returns this when a verb is executing -#define VS_CALL_YES_DOING -1 -/// verb_call(VCM_DO) returns this when a verb did execure and is done -#define VS_CALL_YES_DONE -2 -/// verb_call(VCM_DO) returns this when a verb should be deleted by the stack manager -#define VS_CALL_REMOVE -3 - -/** - Push a new verb onto the specified stack. Set vrb_life to make it time-limited. -**/ -entity verbstack_push(entity stack, float(float eval) vrb_call, float val_static, float vrb_life,entity verb_owner) -{ - entity vrb; - - if not(stack) - return world; - - if not(vrb_call) - return world; - - vrb = spawn(); - vrb.owner = verb_owner; - vrb.verbstack = stack; - vrb.verb_call = vrb_call; - vrb.verb_static_value = val_static; - - if(vrb_life) - { - vrb.think = SUB_Remove; - vrb.nextthink = time + vrb_life; - } - - return vrb; -} - -/** - Find the best verb in this stack and execurte it. - ALso remove any verbs returning VS_CALL_REMOVE on VCM_EVAL or VCM_DO -**/ -float verbstack_pop(entity stack) -{ - entity vrb, bestverb, oldself; - float value, bestvalue; - - oldself = self; - - vrb = findchainentity(verbstack,stack); - while(vrb) - { - verb = vrb; - vrb = vrb.chain; - self = verb.owner; - value = verb.verb_call(VCM_EVAL); - - if(value < 0) - { - if(value == VS_CALL_REMOVE) - remove(verb); - } - else - { - if(value > bestvalue) - { - bestverb = verb; - bestvalue = value; - } - } - } - - if(bestverb) - { - verb = bestverb; - self = verb.owner; - value = verb.verb_call(VCM_DO); - - if(value == VS_CALL_REMOVE) - remove(bestverb); - } - - self = oldself; - - return value; -} - -float verbstack_popfifo(entity stack) -{ - entity oldself; - float ret; - - oldself = self; - verb = findentity(stack,verbstack,stack); - if not (verb) - ret = 0; - else - { - self = verb.owner; - ret = verb.verb_call(VCM_DO); - - if(ret == VS_CALL_REMOVE) - remove(verb); - } - - self = oldself; - return ret; -} - -/** - Find the best verb in this stack and return it. - ALso remove any verbs returning VS_CALL_REMOVE on VCM_EVAL. -**/ -entity verbstack_pull(entity stack) -{ - entity vrb; - entity bestverb, oldself; - float value, bestvalue; - - oldself = self; - - vrb = findchainentity(verbstack,stack); - while(vrb) - { - self = vrb.owner; - - verb = vrb; - vrb = vrb.chain; - value = verb.verb_call(VCM_EVAL); - - if(value > 0) - { - if(value == VS_CALL_REMOVE) - remove(verb); - } - else - { - if(value > bestvalue) - { - bestverb = verb; - bestvalue = value; - } - } - } - - self = oldself; - - return bestverb; -} - -entity verbstack_pullfifo(entity stack) -{ - return findentity(stack,verbstack,stack); -} - -/** - Delete every verb on this stack, signaling them with VCM_REMOVE first. -**/ -void verbstack_flush(entity stack) -{ - entity vrb, oldself; - - oldself = self; - - vrb = findchainentity(verbstack,stack); - while(vrb) - { - self = vrb.owner; - - verb = vrb; - vrb = vrb.chain; - verb.verb_call(VCM_REMOVE); - remove(verb); - } - - self = oldself; -} +/// Some default stacks. +.entity verbs_idle; +.entity verbs_attack; +.entity verbs_move; + +/// This global gets set to the verb in question each time the stack manager calls verb_call +entity verb; + +/// Execure this verb +#define VCM_DO 0 +/// Return the value of this verb. Return VS_CALL_REMOVE to delete it. +#define VCM_EVAL 1 +/// This verb is beeing removed NOW (not sent when verb_call returns VS_CALL_REMOVE) +#define VCM_REMOVE 2 + +/// Verb callback +.float(float message) verb_call; + +/// Points to this verb's stack. +.entity verbstack; + +/// Static value of this verb +.float verb_static_value; + +/// verb_call returns this when a verb in not doable +#define VS_CALL_NO 0 +/// verb_call(VCM_DO) returns this when a verb is executing +#define VS_CALL_YES_DOING -1 +/// verb_call(VCM_DO) returns this when a verb did execure and is done +#define VS_CALL_YES_DONE -2 +/// verb_call(VCM_DO) returns this when a verb should be deleted by the stack manager +#define VS_CALL_REMOVE -3 + +/** + Push a new verb onto the specified stack. Set vrb_life to make it time-limited. +**/ +entity verbstack_push(entity stack, float(float eval) vrb_call, float val_static, float vrb_life,entity verb_owner) +{ + entity vrb; + + if not(stack) + return world; + + if not(vrb_call) + return world; + + vrb = spawn(); + vrb.owner = verb_owner; + vrb.verbstack = stack; + vrb.verb_call = vrb_call; + vrb.verb_static_value = val_static; + + if(vrb_life) + { + vrb.think = SUB_Remove; + vrb.nextthink = time + vrb_life; + } + + return vrb; +} + +/** + Find the best verb in this stack and execurte it. + ALso remove any verbs returning VS_CALL_REMOVE on VCM_EVAL or VCM_DO +**/ +float verbstack_pop(entity stack) +{ + entity vrb, bestverb, oldself; + float value, bestvalue; + + oldself = self; + + vrb = findchainentity(verbstack,stack); + while(vrb) + { + verb = vrb; + vrb = vrb.chain; + self = verb.owner; + value = verb.verb_call(VCM_EVAL); + + if(value < 0) + { + if(value == VS_CALL_REMOVE) + remove(verb); + } + else + { + if(value > bestvalue) + { + bestverb = verb; + bestvalue = value; + } + } + } + + if(bestverb) + { + verb = bestverb; + self = verb.owner; + value = verb.verb_call(VCM_DO); + + if(value == VS_CALL_REMOVE) + remove(bestverb); + } + + self = oldself; + + return value; +} + +float verbstack_popfifo(entity stack) +{ + entity oldself; + float ret; + + oldself = self; + verb = findentity(stack,verbstack,stack); + if not (verb) + ret = 0; + else + { + self = verb.owner; + ret = verb.verb_call(VCM_DO); + + if(ret == VS_CALL_REMOVE) + remove(verb); + } + + self = oldself; + return ret; +} + +/** + Find the best verb in this stack and return it. + ALso remove any verbs returning VS_CALL_REMOVE on VCM_EVAL. +**/ +entity verbstack_pull(entity stack) +{ + entity vrb; + entity bestverb, oldself; + float value, bestvalue; + + oldself = self; + + vrb = findchainentity(verbstack,stack); + while(vrb) + { + self = vrb.owner; + + verb = vrb; + vrb = vrb.chain; + value = verb.verb_call(VCM_EVAL); + + if(value > 0) + { + if(value == VS_CALL_REMOVE) + remove(verb); + } + else + { + if(value > bestvalue) + { + bestverb = verb; + bestvalue = value; + } + } + } + + self = oldself; + + return bestverb; +} + +entity verbstack_pullfifo(entity stack) +{ + return findentity(stack,verbstack,stack); +} + +/** + Delete every verb on this stack, signaling them with VCM_REMOVE first. +**/ +void verbstack_flush(entity stack) +{ + entity vrb, oldself; + + oldself = self; + + vrb = findchainentity(verbstack,stack); + while(vrb) + { + self = vrb.owner; + + verb = vrb; + vrb = vrb.chain; + verb.verb_call(VCM_REMOVE); + remove(verb); + } + + self = oldself; +} -- 2.39.2