From: Mario Date: Tue, 14 Apr 2015 22:22:35 +0000 (+1000) Subject: Merge branch 'master' into Mario/turrets X-Git-Tag: xonotic-v0.8.2~2052^2~3 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=82dbcadfd0556053b74638f2e3ae2e57103ddf26;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into Mario/turrets Conflicts: qcsrc/client/progs.src qcsrc/client/tturrets.qh qcsrc/server/mutators/gamemode_onslaught.qc qcsrc/server/progs.src qcsrc/server/tturrets/include/turrets.qh qcsrc/server/tturrets/include/turrets_early.qh qcsrc/server/tturrets/system/system_aimprocs.qc qcsrc/server/tturrets/system/system_damage.qc qcsrc/server/tturrets/system/system_misc.qc qcsrc/server/tturrets/units/unit_hellion.qc qcsrc/server/tturrets/units/unit_hk.qc qcsrc/server/tturrets/units/unit_mlrs.qc qcsrc/server/tturrets/units/unit_phaser.qc --- 82dbcadfd0556053b74638f2e3ae2e57103ddf26 diff --cc qcsrc/client/main.qc index 000000000,36ec4f7b8..b753139c4 mode 000000,100644..100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@@ -1,0 -1,1317 +1,1319 @@@ + #include "mapvoting.qh" + #include "modeleffects.qh" + #include "particles.qh" + #include "scoreboard.qh" + #include "shownames.qh" + #include "target_music.qh" -#include "tturrets.qh" + #include "tuba.qh" + #include "wall.qh" + #include "waypointsprites.qh" + + #include "vehicles/vehicles.qh" + + #include "../server/vehicles/bumblebee.qh" + + #include "../common/net_notice.qh" + + #include "../common/monsters/monsters.qh" + ++#include "../common/turrets/cl_turrets.qh" ++#include "../common/turrets/turrets.qh" ++ + #include "../warpzonelib/client.qh" + + // -------------------------------------------------------------------------- + // BEGIN REQUIRED CSQC FUNCTIONS + //include "main.qh" + + entity clearentity_ent; + void clearentity(entity e) + { + if (!clearentity_ent) + { + clearentity_ent = spawn(); + clearentity_ent.classname = "clearentity"; + } + int n = e.entnum; + copyentity(clearentity_ent, e); + e.entnum = n; + } + + #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED + void menu_show_error() + { + drawstring('0 200 0', _("ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!"), '8 8 0', '1 0 0', 1, 0); + } + + // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load) + // Useful for precaching things + + void menu_sub_null() + { + } + + string forcefog; + void WaypointSprite_Load(); + void ConsoleCommand_macro_init(); + void CSQC_Init(void) + { + prvm_language = cvar_string("prvm_language"); + + #ifdef WATERMARK + dprintf("^4CSQC Build information: ^1%s\n", WATERMARK); + #endif + + int i; + + binddb = db_create(); + tempdb = db_create(); + ClientProgsDB = db_load("client.db"); + compressShortVector_init(); + + draw_endBoldFont(); + menu_visible = false; + menu_show = menu_show_error; + menu_action = func_null; + + for(i = 0; i < 255; ++i) + if(getplayerkeyvalue(i, "viewentity") == "") + break; + maxclients = i; + + //registercommand("hud_configure"); + //registercommand("hud_save"); + //registercommand("menu_action"); + + ConsoleCommand_macro_init(); + + registercvar("hud_usecsqc", "1"); + registercvar("scoreboard_columns", "default"); + + registercvar("cl_nade_type", "3"); + registercvar("cl_pokenade_type", "zombie"); + + gametype = 0; + + // hud_fields uses strunzone on the titles! + for(i = 0; i < MAX_HUD_FIELDS; ++i) + hud_title[i] = strzone("(null)"); + + Cmd_HUD_SetFields(0); + + postinit = false; + + calledhooks = 0; + + teams = Sort_Spawn(); + players = Sort_Spawn(); + + GetTeam(NUM_SPECTATOR, true); // add specs first + + // needs to be done so early because of the constants they create + CALL_ACCUMULATED_FUNCTION(RegisterWeapons); ++ CALL_ACCUMULATED_FUNCTION(RegisterTurrets); + CALL_ACCUMULATED_FUNCTION(RegisterMonsters); + CALL_ACCUMULATED_FUNCTION(RegisterGametypes); + CALL_ACCUMULATED_FUNCTION(RegisterNotifications); + CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); + CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels); + CALL_ACCUMULATED_FUNCTION(RegisterBuffs); + + WaypointSprite_Load(); + + // precaches + precache_model("null"); + precache_sound("misc/hit.wav"); + precache_sound("misc/typehit.wav"); + + Projectile_Precache(); + Hook_Precache(); + GibSplash_Precache(); + Casings_Precache(); + Vehicles_Precache(); - turrets_precache(); + Tuba_Precache(); + CSQCPlayer_Precache(); + + if(autocvar_cl_reticle) + { + precache_pic("gfx/reticle_normal"); + // weapon reticles are precached in weapon files + } + + get_mi_min_max_texcoords(1); // try the CLEVER way first + minimapname = strcat("gfx/", mi_shortname, "_radar.tga"); + shortmapname = mi_shortname; + + if(precache_pic(minimapname) == "") + { + // but maybe we have a non-clever minimap + minimapname = strcat("gfx/", mi_shortname, "_mini.tga"); + if(precache_pic(minimapname) == "") + minimapname = ""; // FAIL + else + get_mi_min_max_texcoords(0); // load new texcoords + } + + mi_center = (mi_min + mi_max) * 0.5; + mi_scale = mi_max - mi_min; + minimapname = strzone(minimapname); + + WarpZone_Init(); + + hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); + draw_currentSkin = strzone(strcat("gfx/menu/", cvar_string("menu_skin"))); + } + + // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) + void Shutdown(void) + { + WarpZone_Shutdown(); + + remove(teams); + remove(players); + db_close(binddb); + db_close(tempdb); + if(autocvar_cl_db_saveasdump) + db_dump(ClientProgsDB, "client.db"); + else + db_save(ClientProgsDB, "client.db"); + db_close(ClientProgsDB); + + if(camera_active) + cvar_set("chase_active",ftos(chase_active_backup)); + + // unset the event chasecam's chase_active + if(autocvar_chase_active < 0) + cvar_set("chase_active", "0"); + + if (!isdemo()) + { + if (!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart nop\n"); + if (!(calledhooks & HOOK_END)) + localcmd("\ncl_hook_gameend\n"); + } + } + + .float has_team; + float SetTeam(entity o, int Team) + { + entity tm; + if(teamplay) + { + switch(Team) + { + case -1: + case NUM_TEAM_1: + case NUM_TEAM_2: + case NUM_TEAM_3: + case NUM_TEAM_4: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + else + { + switch(Team) + { + case -1: + case 0: + break; + default: + if(GetTeam(Team, false) == world) + { + dprintf("trying to switch to unsupported team %d\n", Team); + Team = NUM_SPECTATOR; + } + break; + } + } + if(Team == -1) // leave + { + if(o.has_team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.has_team = 0; + return true; + } + } + else + { + if (!o.has_team) + { + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + o.has_team = 1; + return true; + } + else if(Team != o.team) + { + tm = GetTeam(o.team, false); + tm.team_size -= 1; + o.team = Team; + tm = GetTeam(Team, true); + tm.team_size += 1; + return true; + } + } + return false; + } + + void Playerchecker_Think() + { + int i; + entity e; + for(i = 0; i < maxclients; ++i) + { + e = playerslots[i]; + if(GetPlayerName(i) == "") + { + if(e.sort_prev) + { + // player disconnected + SetTeam(e, -1); + RemovePlayer(e); + e.sort_prev = world; + //e.gotscores = 0; + } + } + else + { + if (!e.sort_prev) + { + // player connected + if (!e) + playerslots[i] = e = spawn(); + e.sv_entnum = i; + e.ping = 0; + e.ping_packetloss = 0; + e.ping_movementloss = 0; + //e.gotscores = 0; // we might already have the scores... + SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with HUD_UpdatePlayerTeams + RegisterPlayer(e); + HUD_UpdatePlayerPos(e); + } + } + } + self.nextthink = time + 0.2; + } + + void Porto_Init(); + void TrueAim_Init(); + void PostInit(void) + { + entity playerchecker; + playerchecker = spawn(); + playerchecker.think = Playerchecker_Think; + playerchecker.nextthink = time + 0.2; + + Porto_Init(); + TrueAim_Init(); + + postinit = true; + } + + // 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 and 3 are 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. + // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos. + float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary) + { + float bSkipKey; + bSkipKey = false; + + if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + + if (MapVote_InputEvent(bInputType, nPrimary, nSecondary)) + return true; + + if(menu_visible && menu_action) + if(menu_action(bInputType, nPrimary, nSecondary)) + return true; + + return bSkipKey; + } + + // END REQUIRED CSQC FUNCTIONS + // -------------------------------------------------------------------------- + + // -------------------------------------------------------------------------- + // BEGIN OPTIONAL CSQC FUNCTIONS + void Ent_RemoveEntCS() + { + entcs_receiver[self.sv_entnum] = world; + } + void Ent_ReadEntCS() + { + int sf; + InterpolateOrigin_Undo(); + + self.classname = "entcs_receiver"; + sf = ReadByte(); + + if(sf & 1) + self.sv_entnum = ReadByte(); + if(sf & 2) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + } + if(sf & 4) + { + self.angles_y = ReadByte() * 360.0 / 256; + self.angles_x = self.angles_z = 0; + } + if(sf & 8) + self.healthvalue = ReadByte() * 10; + if(sf & 16) + self.armorvalue = ReadByte() * 10; + + entcs_receiver[self.sv_entnum] = self; + self.entremove = Ent_RemoveEntCS; + self.iflags |= IFLAG_ORIGIN; + + InterpolateOrigin_Note(); + } + + void Ent_Remove(); + + void Ent_RemovePlayerScore() + { + if(self.owner) { + SetTeam(self.owner, -1); + self.owner.gotscores = 0; + for(int i = 0; i < MAX_SCORE; ++i) { + self.owner.(scores[i]) = 0; // clear all scores + } + } + } + + void Ent_ReadPlayerScore() + { + int i, n; + bool isNew; + entity o; + + // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN + // (no I've never heard of M-x replace-string, sed, or anything like that) + isNew = !self.owner; // workaround for DP bug + n = ReadByte()-1; + + #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED + if(!isNew && n != self.sv_entnum) + { + //print("A CSQC entity changed its owner!\n"); + printf("A CSQC entity changed its owner! (edict: %d, classname: %s)\n", num_for_edict(self), self.classname); + isNew = true; + Ent_Remove(); + self.enttype = ENT_CLIENT_SCORES; + } + #endif + + self.sv_entnum = n; + + if (!(playerslots[self.sv_entnum])) + playerslots[self.sv_entnum] = spawn(); + o = self.owner = playerslots[self.sv_entnum]; + o.sv_entnum = self.sv_entnum; + o.gotscores = 1; + + //if (!o.sort_prev) + // RegisterPlayer(o); + //playerchecker will do this for us later, if it has not already done so + + int sf, lf; + #if MAX_SCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(scores[i]) = ReadInt24_t(); + else + o.(scores[i]) = ReadChar(); + } + + if(o.sort_prev) + HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet! + + self.entremove = Ent_RemovePlayerScore; + } + + void Ent_ReadTeamScore() + { + int i; + entity o; + + self.team = ReadByte(); + o = self.owner = GetTeam(self.team, true); // these team numbers can always be trusted + + int sf, lf; + #if MAX_TEAMSCORE <= 8 + sf = ReadByte(); + lf = ReadByte(); + #else + sf = ReadShort(); + lf = ReadShort(); + #endif + int p; + for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) + if(sf & p) + { + if(lf & p) + o.(teamscores[i]) = ReadInt24_t(); + else + o.(teamscores[i]) = ReadChar(); + } + + HUD_UpdateTeamPos(o); + } + + void Ent_ClientData() + { + float newspectatee_status; + + int f = ReadByte(); + + scoreboard_showscores_force = (f & 1); + + if(f & 2) + { + newspectatee_status = ReadByte(); + if(newspectatee_status == player_localnum + 1) + 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; + } + if (autocvar_hud_panel_healtharmor_progressbar_gfx) + { + if ( (spectatee_status == -1 && newspectatee_status > 0) //before observing, now spectating + || (spectatee_status > 0 && newspectatee_status > 0 && spectatee_status != newspectatee_status) //changed spectated player + ) + prev_p_health = -1; + else if(spectatee_status && !newspectatee_status) //before observing/spectating, now playing + prev_health = -1; + } + spectatee_status = newspectatee_status; + + // we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum + } + + void Ent_Nagger() + { + int i, j, b, f; + + int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS + + if(!(nags & 4)) + { + if(vote_called_vote) + strunzone(vote_called_vote); + vote_called_vote = string_null; + vote_active = 0; + } + else + { + vote_active = 1; + } + + if(nags & 64) + { + vote_yescount = ReadByte(); + vote_nocount = ReadByte(); + vote_needed = ReadByte(); + vote_highlighted = ReadChar(); + } + + 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 (!(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_EliminatedPlayers() + { + int i, j, b, f; + + int sf = ReadByte(); + if(sf & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].eliminated = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].eliminated = 0; + } + } + } + + void Ent_RandomSeed() + { + float s; + prandom_debug(); + s = ReadShort(); + psrandom(s); + } + + void Ent_ReadAccuracy(void) + { + int f, w; + int sf = ReadInt24_t(); + if(sf == 0) + { + for(w = 0; w <= WEP_LAST - WEP_FIRST; ++w) + weapon_accuracy[w] = -1; + return; + } + + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) + { + if(sf & f) + { + int b = ReadByte(); + if(b == 0) + weapon_accuracy[w] = -1; + else if(b == 255) + weapon_accuracy[w] = 1.0; // no better error handling yet, sorry + else + weapon_accuracy[w] = (b - 1.0) / 100.0; + } + if(f == 0x800000) + f = 1; + else + f *= 2; + } + } + + void Spawn_Draw(void) + { + pointparticles(self.cnt, self.origin + '0 0 28', '0 0 2', bound(0, frametime, 0.1)); + } + + void Ent_ReadSpawnPoint(float is_new) // entity for spawnpoint + { + float teamnum = (ReadByte() - 1); + vector spn_origin; + spn_origin.x = ReadShort(); + spn_origin.y = ReadShort(); + spn_origin.z = ReadShort(); + + if(is_new) + { + self.origin = spn_origin; + setsize(self, PL_MIN, PL_MAX); + droptofloor(); + + /*if(autocvar_cl_spawn_point_model) // needs a model first + { + self.mdl = "models/spawnpoint.md3"; + self.colormod = Team_ColorRGB(teamnum); + precache_model(self.mdl); + setmodel(self, self.mdl); + self.drawmask = MASK_NORMAL; + //self.movetype = MOVETYPE_NOCLIP; + //self.draw = Spawn_Draw; + }*/ + if(autocvar_cl_spawn_point_particles) + { + if((serverflags & SERVERFLAG_TEAMPLAY)) + { + switch(teamnum) + { + case NUM_TEAM_1: self.cnt = particleeffectnum("spawn_point_red"); break; + case NUM_TEAM_2: self.cnt = particleeffectnum("spawn_point_blue"); break; + case NUM_TEAM_3: self.cnt = particleeffectnum("spawn_point_yellow"); break; + case NUM_TEAM_4: self.cnt = particleeffectnum("spawn_point_pink"); break; + default: self.cnt = particleeffectnum("spawn_point_neutral"); break; + } + } + else { self.cnt = particleeffectnum("spawn_point_neutral"); } + + self.draw = Spawn_Draw; + } + } + + //printf("Ent_ReadSpawnPoint(is_new = %d); origin = %s, team = %d, effect = %d\n", is_new, vtos(self.origin), teamnum, self.cnt); + } + + void Ent_ReadSpawnEvent(float is_new) + { + // If entnum is 0, ONLY do the local spawn actions + // this way the server can disable the sending of + // spawn origin or such to clients if wanted. + float entnum = ReadByte(); + + if(entnum) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + + if(is_new) + { + float teamnum = GetPlayerColor(entnum - 1); + + if(autocvar_cl_spawn_event_particles) + { + switch(teamnum) + { + case NUM_TEAM_1: pointparticles(particleeffectnum("spawn_event_red"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_2: pointparticles(particleeffectnum("spawn_event_blue"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_3: pointparticles(particleeffectnum("spawn_event_yellow"), self.origin, '0 0 0', 1); break; + case NUM_TEAM_4: pointparticles(particleeffectnum("spawn_event_pink"), self.origin, '0 0 0', 1); break; + default: pointparticles(particleeffectnum("spawn_event_neutral"), self.origin, '0 0 0', 1); break; + } + } + if(autocvar_cl_spawn_event_sound) + { + sound(self, CH_TRIGGER, "misc/spawn.wav", VOL_BASE, ATTEN_NORM); + } + } + } + + // local spawn actions + if(is_new && (!entnum || (entnum == player_localentnum))) + { + zoomin_effect = 1; + current_viewzoom = (1 / bound(1, autocvar_cl_spawnzoom_factor, 16)); + + if(autocvar_cl_unpress_zoom_on_spawn) + { + localcmd("-zoom\n"); + button_zoom = false; + } + } + + //printf("Ent_ReadSpawnEvent(is_new = %d); origin = %s, entnum = %d, localentnum = %d\n", is_new, vtos(self.origin), entnum, player_localentnum); + } + + // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. + // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. + void Ent_RadarLink(); + void Ent_Init(); + void Ent_ScoresInfo(); + void CSQC_Ent_Update(float bIsNewEntity) + { + float t; + float savetime; + t = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t); + + // set up the "time" global for received entities to be correct for interpolation purposes + 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 || bIsNewEntity) + { + //print("A CSQC entity changed its type!\n"); + printf("A CSQC entity changed its type! (edict: %d, server: %d, type: %d -> %d)\n", num_for_edict(self), self.entnum, self.enttype, t); + Ent_Remove(); + clearentity(self); + bIsNewEntity = 1; + } + } + else + { + if(!bIsNewEntity) + { + printf("A CSQC entity appeared out of nowhere! (edict: %d, server: %d, type: %d)\n", num_for_edict(self), self.entnum, t); + bIsNewEntity = 1; + } + } + #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_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); 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(bIsNewEntity); break; + case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(bIsNewEntity); break; + case ENT_CLIENT_CASING: Ent_Casing(bIsNewEntity); break; + case ENT_CLIENT_INIT: Ent_Init(); break; + case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break; + case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break; + case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break; + case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break; + case ENT_CLIENT_WALL: Ent_Wall(); break; + case ENT_CLIENT_MODELEFFECT: Ent_ModelEffect(bIsNewEntity); break; + case ENT_CLIENT_TUBANOTE: Ent_TubaNote(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE: WarpZone_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_CAMERA: WarpZone_Camera_Read(bIsNewEntity); break; + case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break; + case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break; + case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break; + case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break; + case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; + case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; + case ENT_CLIENT_TURRET: ent_turret(); break; + case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break; + case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break; + case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break; + case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break; + case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break; + case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break; + case ENT_CLIENT_HEALING_ORB: ent_healer(); break; + + default: + //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); + error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname)); + break; + } + + 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(); + + if(self.skeletonindex) + { + skel_delete(self.skeletonindex); + self.skeletonindex = 0; + } + + if(self.snd_looping > 0) + { + sound(self, self.snd_looping, "misc/null.wav", VOL_BASE, autocvar_g_jetpack_attenuation); + self.snd_looping = 0; + } + + 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(autocvar_developer_csqcentities) + printf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype); + + if(wasfreed(self)) + { + print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); + return; + } + if(self.enttype) + Ent_Remove(); + remove(self); + } + + void Gamemode_Init() + { + if (!isdemo()) + { + if(!(calledhooks & HOOK_START)) + localcmd("\n_cl_hook_gamestart ", MapInfo_Type_ToString(gametype), "\n"); + calledhooks |= HOOK_START; + } + } + // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. + void CSQC_Parse_StuffCmd(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage); + + localcmd(strMessage); + } + // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided. To execute standard behavior, simply execute print with the string. + void CSQC_Parse_Print(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_Print(\"%s\")\n", strMessage); + + print(ColorTranslateRGB(strMessage)); + } + + // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided. + void CSQC_Parse_CenterPrint(string strMessage) + { + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage); + + centerprint_hud(strMessage); + } + + string notranslate_fogcmd1 = "\nfog "; + string notranslate_fogcmd2 = "\nr_fog_exp2 0\nr_drawfog 1\n"; + void Fog_Force() + { + // TODO somehow thwart prvm_globalset client ... + + if(autocvar_cl_orthoview && autocvar_cl_orthoview_nofog) + { localcmd("\nr_drawfog 0\n"); } + else if(forcefog != "") + { localcmd(strcat(notranslate_fogcmd1, forcefog, notranslate_fogcmd2)); } + } + + void Gamemode_Init(); + void Ent_ScoresInfo() + { + int i; + self.classname = "ent_client_scores_info"; + gametype = ReadInt24_t(); + HUD_ModIcons_SetFunc(); + for(i = 0; i < MAX_SCORE; ++i) + { + if(scores_label[i]) + strunzone(scores_label[i]); + scores_label[i] = strzone(ReadString()); + scores_flags[i] = ReadByte(); + } + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + if(teamscores_label[i]) + strunzone(teamscores_label[i]); + teamscores_label[i] = strzone(ReadString()); + teamscores_flags[i] = ReadByte(); + } + HUD_InitScores(); + Gamemode_Init(); + } + + void Ent_Init() + { + self.classname = "ent_client_init"; + + nb_pb_period = ReadByte() / 32; //Accuracy of 1/32th + + hook_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + + if(forcefog) + strunzone(forcefog); + forcefog = strzone(ReadString()); + + armorblockpercent = ReadByte() / 255.0; + + g_balance_mortar_bouncefactor = ReadCoord(); + g_balance_mortar_bouncestop = ReadCoord(); + g_balance_electro_secondary_bouncefactor = ReadCoord(); + g_balance_electro_secondary_bouncestop = ReadCoord(); + + vortex_scope = !ReadByte(); + rifle_scope = !ReadByte(); + + serverflags = ReadByte(); + + minelayer_maxmines = ReadByte(); + + hagar_maxrockets = ReadByte(); + + g_trueaim_minrange = ReadCoord(); + g_balance_porto_secondary = ReadByte(); + + if(!postinit) + PostInit(); + } + + void Net_ReadRace() + { + float b; + + b = ReadByte(); + + switch(b) + { + case RACE_NET_CHECKPOINT_HIT_QUALIFYING: + race_checkpoint = ReadByte(); + race_time = ReadInt24_t(); + race_previousbesttime = ReadInt24_t(); + if(race_previousbestname) + strunzone(race_previousbestname); + race_previousbestname = strzone(ColorTranslateRGB(ReadString())); + + race_checkpointtime = time; + + if(race_checkpoint == 0 || race_checkpoint == 254) + { + race_penaltyaccumulator = 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 = ReadInt24_t(); + 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 = ReadInt24_t(); + 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 = ReadInt24_t(); + race_othercheckpointlapsdelta = ReadByte(); + if(race_othercheckpointlapsdelta >= 128) + race_othercheckpointlapsdelta -= 256; + if(race_othercheckpointenemy) + strunzone(race_othercheckpointenemy); + race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString())); + break; + + case RACE_NET_PENALTY_RACE: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + //race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_PENALTY_QUALIFYING: + race_penaltyeventtime = time; + race_penaltytime = ReadShort(); + race_penaltyaccumulator += race_penaltytime; + if(race_penaltyreason) + strunzone(race_penaltyreason); + race_penaltyreason = strzone(ReadString()); + break; + + case RACE_NET_SERVER_RECORD: + race_server_record = ReadInt24_t(); + break; + case RACE_NET_SPEED_AWARD: + race_speedaward = ReadInt24_t(); + if(race_speedaward_holder) + strunzone(race_speedaward_holder); + race_speedaward_holder = strzone(ReadString()); + break; + case RACE_NET_SPEED_AWARD_BEST: + race_speedaward_alltimebest = ReadInt24_t(); + if(race_speedaward_alltimebest_holder) + strunzone(race_speedaward_alltimebest_holder); + race_speedaward_alltimebest_holder = strzone(ReadString()); + break; + case RACE_NET_SERVER_RANKINGS: + float prevpos, del; + int pos = ReadShort(); + prevpos = ReadShort(); + del = ReadShort(); + + // move other rankings out of the way + int i; + if (prevpos) { + for (i=prevpos-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } else if (del) { // a record has been deleted by the admin + for (i=pos-1; i<= RANKINGS_CNT-1; ++i) { + if (i == RANKINGS_CNT-1) { // clear out last record + grecordtime[i] = 0; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = string_null; + } + else { + grecordtime[i] = grecordtime[i+1]; + if (grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i+1]); + } + } + } else { // player has no ranked record yet + for (i=RANKINGS_CNT-1;i>pos-1;--i) { + grecordtime[i] = grecordtime[i-1]; + if(grecordholder[i]) + strunzone(grecordholder[i]); + grecordholder[i] = strzone(grecordholder[i-1]); + } + } + + // store new ranking + if(grecordholder[pos-1] != "") + strunzone(grecordholder[pos-1]); + grecordholder[pos-1] = strzone(ReadString()); + grecordtime[pos-1] = ReadInt24_t(); + if(grecordholder[pos-1] == GetPlayerName(player_localnum)) + race_myrank = pos; + break; + case RACE_NET_SERVER_STATUS: + race_status = ReadShort(); + if(race_status_name) + strunzone(race_status_name); + race_status_name = strzone(ReadString()); + } + } + + void Net_TeamNagger() + { + teamnagger = 1; + } + + void Net_ReadPingPLReport() + { + int e, pi, pl, ml; + e = ReadByte(); + pi = ReadShort(); + pl = ReadByte(); + ml = ReadByte(); + if (!(playerslots[e])) + return; + playerslots[e].ping = pi; + playerslots[e].ping_packetloss = pl / 255.0; + playerslots[e].ping_movementloss = ml / 255.0; + } + + void Net_WeaponComplain() + { + complain_weapon = ReadByte(); + + if(complain_weapon_name) + strunzone(complain_weapon_name); + complain_weapon_name = strzone(WEP_NAME(complain_weapon)); + + complain_weapon_type = ReadByte(); + + complain_weapon_time = time; + weapontime = time; // ping the weapon panel + + switch(complain_weapon_type) + { + case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, complain_weapon); break; + case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, complain_weapon); break; + default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, complain_weapon); break; + } + } + + // 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() + { + float bHandled; + bHandled = true; + // Acquire TE ID + float nTEID; + nTEID = ReadByte(); + + if(autocvar_developer_csqcentities) + printf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID); + + // NOTE: Could just do return instead of break... + switch(nTEID) + { + case TE_CSQC_TARGET_MUSIC: + Net_TargetMusic(); + bHandled = true; + break; + case TE_CSQC_PICTURE: + Net_MapVote_Picture(); + bHandled = true; + break; + case TE_CSQC_RACE: + Net_ReadRace(); + bHandled = true; + break; + case TE_CSQC_VORTEXBEAMPARTICLE: + Net_ReadVortexBeamParticle(); + bHandled = true; + break; + case TE_CSQC_TEAMNAGGER: + Net_TeamNagger(); + bHandled = true; + break; + case TE_CSQC_ARC: + Net_ReadArc(); + bHandled = true; + break; + case TE_CSQC_PINGPLREPORT: + Net_ReadPingPLReport(); + bHandled = true; + break; + case TE_CSQC_WEAPONCOMPLAIN: + Net_WeaponComplain(); + bHandled = true; + break; + case TE_CSQC_VEHICLESETUP: + Net_VehicleSetup(); + bHandled = true; + break; + case TE_CSQC_SVNOTICE: + cl_notice_read(); + bHandled = true; + break; + case TE_CSQC_SHOCKWAVEPARTICLE: + Net_ReadShockwaveParticle(); + 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 = 0; + + if (!autocvar_hud_showbinds) + return text; + + keys = db_get(binddb, command); + if (keys == "") + { + n = tokenize(findkeysforcommand(command, 0)); // 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 (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l) + break; + } + + } + if (keys == "") + keys = "NO_KEY"; + db_put(binddb, command, keys); + } + + if (keys == "NO_KEY") { + if (autocvar_hud_showbinds > 1) + return sprintf(_("%s (not bound)"), text); + else + return text; + } + else if (autocvar_hud_showbinds > 1) + return sprintf("%s (%s)", text, keys); + else + return keys; + } diff --cc qcsrc/client/progs.src index d14edd5d3,f80da18d6..cdd0b09d1 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@@ -1,136 -1,73 +1,76 @@@ ../../csprogs.dat ../common/util-pre.qh - sys-pre.qh - ../dpdefs/csprogsdefs.qc - sys-post.qh + ../dpdefs/csprogsdefs.qh - Defs.qc - ../dpdefs/keycodes.qc - ../common/constants.qh - ../common/stats.qh - - ../warpzonelib/anglestransform.qh - ../warpzonelib/mathlib.qh - ../warpzonelib/common.qh - ../warpzonelib/client.qh - - ../common/playerstats.qh - ../common/teams.qh - ../common/util.qh - ../common/nades.qh - ../common/buffs.qh - ../common/test.qh - ../common/counting.qh - ../common/weapons/weapons.qh // TODO - ../common/mapinfo.qh - ../common/command/markup.qh - ../common/command/rpn.qh - ../common/command/generic.qh - ../common/command/shared_defs.qh - ../common/urllib.qh - ../common/animdecide.qh - command/cl_cmd.qh - - ../common/monsters/monsters.qh - - autocvars.qh - - ../common/notifications.qh // must be after autocvars - ../common/deathtypes.qh // must be after notifications - - ../common/turrets/turrets.qh - ../common/turrets/cl_turrets.qh - - damage.qh - - ../csqcmodellib/interpolate.qh - teamradar.qh - hud.qh - scoreboard.qh - waypointsprites.qh - movetypes.qh - prandom.qh - bgmscript.qh - noise.qh - ../server/movelib.qc - main.qh - vehicles/vehicles.qh - ../common/csqcmodel_settings.qh - ../csqcmodellib/common.qh - ../csqcmodellib/cl_model.qh - ../csqcmodellib/cl_player.qh - weapons/projectile.qh // TODO - player_skeleton.qh - - sortlist.qc - miscfunctions.qc - ../server/t_items.qh - ../server/t_items.qc - - teamradar.qc - hud_config.qc - hud.qc - scoreboard.qc - mapvoting.qc + announcer.qc + bgmscript.qc + casings.qc csqcmodel_hooks.qc - ../common/net_notice.qc - - rubble.qc - hook.qc - particles.qc - laser.qc - weapons/projectile.qc // TODO - gibs.qc damage.qc - casings.qc - ../csqcmodellib/cl_model.qc - ../csqcmodellib/cl_player.qc effects.qc - wall.qc + gibs.qc + hook.qc + hud_config.qc + hud.qc + laser.qc + main.qc + mapvoting.qc + miscfunctions.qc modeleffects.qc - tuba.qc + movetypes.qc + noise.qc + particles.qc + player_skeleton.qc + prandom.qc + rubble.qc + scoreboard.qc + shownames.qc + sortlist.qc target_music.qc - + teamradar.qc -tturrets.qc + tuba.qc vehicles/vehicles.qc - ../server/vehicles/bumblebee.qc - shownames.qh - shownames.qc - - announcer.qc - Main.qc - View.qc - ../csqcmodellib/interpolate.qc + view.qc + wall.qc waypointsprites.qc - movetypes.qc - prandom.qc - bgmscript.qc - noise.qc + command/cl_cmd.qc + + weapons/projectile.qc // TODO + + ../common/animdecide.qc + ../common/buffs.qc + ../common/mapinfo.qc + ../common/nades.qc + ../common/net_notice.qc + ../common/notifications.qc + ../common/playerstats.qc ../common/test.qc + ../common/urllib.qc ../common/util.qc - ../common/playerstats.qc - ../common/notifications.qc + + ../common/command/generic.qc ../common/command/markup.qc ../common/command/rpn.qc - ../common/command/generic.qc - ../common/mapinfo.qc - ../common/weapons/weapons.qc // TODO - ../common/urllib.qc - command/cl_cmd.qc ../common/monsters/monsters.qc - ../common/nades.qc - ../common/buffs.qc ++../common/turrets/cl_turrets.qc ++../common/turrets/turrets.qc ++ + ../common/weapons/weapons.qc // TODO + + ../csqcmodellib/cl_model.qc + ../csqcmodellib/cl_player.qc + ../csqcmodellib/interpolate.qc + + ../server/movelib.qc + ../server/t_items.qc + ../server/vehicles/bumblebee.qc ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc - ../warpzonelib/common.qc ../warpzonelib/client.qc + - ../common/turrets/cl_turrets.qc - ../common/turrets/turrets.qc - - player_skeleton.qc - ../common/animdecide.qc + ../warpzonelib/common.qc + ../warpzonelib/mathlib.qc diff --cc qcsrc/client/tturrets.qc index ba640068e,d2469851c..000000000 deleted file mode 100644,100644 --- a/qcsrc/client/tturrets.qc +++ /dev/null @@@ -1,685 -1,687 +1,0 @@@ -#include "tturrets.qh" -#include "waypointsprites.qh" - -#include "../server/movelib.qh" - --string tid2info_base; --string tid2info_head; --string tid2info_name; --vector tid2info_min; --vector tid2info_max; -- --void turret_tid2info(float _tid); --void turret_precache(float _tid); --float turret_is_precache[TID_LAST]; -- --void turrets_precache() --{ -- turret_precache(TID_COMMON); --} -- - void turret_precache(float _tid) -void turret_precache(int _tid) --{ -- if (!turret_is_precache[TID_COMMON]) -- { -- precache_sound ("weapons/rocket_impact.wav"); -- 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/rocket.md3"); -- } -- turret_tid2info(_tid); -- if(turret_is_precache[_tid]) -- return; -- -- switch(_tid) -- { -- case TID_EWHEEL: -- precache_model ("models/turrets/ewheel-base2.md3"); -- precache_model ("models/turrets/ewheel-gun1.md3"); -- break; -- case TID_FLAC: -- precache_model ("models/turrets/flac.md3"); -- break; -- case TID_FUSION: -- precache_model ("models/turrets/reactor.md3"); -- break; -- case TID_HELLION: -- precache_model ("models/turrets/hellion.md3"); -- break; -- case TID_HK: -- precache_model ("models/turrets/hk.md3"); -- break; -- case TID_MACHINEGUN: -- precache_model ("models/turrets/machinegun.md3"); -- precache_sound ("weapons/uzi_fire.wav"); -- break; -- case TID_MLRS: -- precache_model ("models/turrets/mlrs.md3"); -- break; -- case TID_PHASER: -- precache_model ("models/turrets/phaser.md3"); -- precache_model ("models/turrets/phaser_beam.md3"); -- precache_sound ("turrets/phaser.wav"); -- break; -- case TID_PLASMA: -- precache_model ("models/turrets/plasma.md3"); -- break; -- case TID_PLASMA_DUAL: -- precache_model ("models/turrets/plasmad.md3"); -- break; -- case TID_TESLA: -- precache_model ("models/turrets/tesla_head.md3"); -- precache_model ("models/turrets/tesla_base.md3"); -- break; -- case TID_WALKER: -- precache_model ("models/turrets/walker_head_minigun.md3"); -- precache_model ("models/turrets/walker_body.md3"); -- precache_sound ("weapons/uzi_fire.wav"); -- break; -- } - turret_is_precache[_tid] = TRUE; - turret_is_precache[_tid] = true; --} -- --void turret_tid2info(float _tid) --{ -- tid2info_base = "models/turrets/base.md3"; -- tid2info_min = '-32 -32 0'; -- tid2info_max = '32 32 64'; -- -- switch(_tid) -- { -- case TID_EWHEEL: -- tid2info_base = "models/turrets/ewheel-base2.md3"; -- tid2info_head = "models/turrets/ewheel-gun1.md3"; -- tid2info_name = "eWheel"; -- break; -- case TID_FLAC: -- tid2info_head = "models/turrets/flac.md3"; -- tid2info_name = "Flac Cannon"; -- break; -- case TID_FUSION: -- tid2info_head = "models/turrets/reactor.md3"; -- tid2info_name = "Fusion Reactor"; -- tid2info_min = '-34 -34 0'; -- tid2info_max = '34 34 90'; -- break; -- case TID_HELLION: -- tid2info_head = "models/turrets/hellion.md3"; -- tid2info_name = "Hellion"; -- break; -- case TID_HK: -- tid2info_head = "models/turrets/hk.md3"; -- tid2info_name = "Hunter-Killer"; -- break; -- case TID_MACHINEGUN: -- tid2info_head = "models/turrets/machinegun.md3"; -- tid2info_name = "Machinegun"; -- break; -- case TID_MLRS: -- tid2info_head = "models/turrets/mlrs.md3"; -- tid2info_name = "MLRS"; -- break; -- case TID_PHASER: -- tid2info_head = "models/turrets/phaser.md3"; -- tid2info_name = "Phaser"; -- break; -- case TID_PLASMA: -- tid2info_head = "models/turrets/plasma.md3"; -- tid2info_name = "Plasma"; -- break; -- case TID_PLASMA_DUAL: -- tid2info_head = "models/turrets/plasmad.md3"; -- tid2info_name = "Dual Plasma"; -- break; -- case TID_TESLA: -- tid2info_base = "models/turrets/tesla_base.md3"; -- tid2info_head = "models/turrets/tesla_head.md3"; -- tid2info_name = "Tesla coil"; -- tid2info_min = '-60 -60 0'; -- tid2info_max ='60 60 128'; -- break; -- case TID_WALKER: -- tid2info_base = "models/turrets/walker_body.md3"; -- tid2info_head = "models/turrets/walker_head_minigun.md3"; -- tid2info_name = "Walker"; -- tid2info_min = '-70 -70 0'; -- tid2info_max = '70 70 95'; -- break; -- } --} -- --void turret_remove() --{ -- remove(self.tur_head); -- //remove(self.enemy); -- self.tur_head = world; --} -- --.vector glowmod; --void turret_changeteam() --{ -- switch(self.team - 1) -- { -- case NUM_TEAM_1: // Red -- self.glowmod = '2 0 0'; -- self.teamradar_color = '1 0 0'; -- break; -- -- case NUM_TEAM_2: // Blue -- self.glowmod = '0 0 2'; -- self.teamradar_color = '0 0 1'; -- break; -- -- case NUM_TEAM_3: // Yellow -- self.glowmod = '1 1 0'; -- self.teamradar_color = '1 1 0'; -- break; -- -- case NUM_TEAM_4: // Pink -- self.glowmod = '1 0 1'; -- self.teamradar_color = '1 0 1'; -- break; -- } -- -- if(self.team) -- self.colormap = 1024 + (self.team - 1) * 17; -- -- self.tur_head.colormap = self.colormap; -- self.tur_head.glowmod = self.glowmod; -- --} -- --void turret_head_draw() --{ -- self.drawmask = MASK_NORMAL; --} -- --void turret_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- self.tur_head.angles += dt * self.tur_head.move_avelocity; -- -- if (self.health < 127) -- { -- dt = random(); -- -- if(dt < 0.03) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); -- } -- -- if(self.health < 85) -- if(dt < 0.01) -- pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); -- -- if(self.health < 32) -- if(dt < 0.015) -- pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); -- --} -- --void turret_draw2d() --{ -- if(self.netname == "") -- return; -- -- if(!autocvar_g_waypointsprite_turrets) -- return; -- -- if(autocvar_cl_hidewaypoints) -- return; -- -- float dist = vlen(self.origin - view_origin); -- float t = (GetPlayerColor(player_localnum) + 1); -- -- vector o; -- string txt; -- -- if(autocvar_cl_vehicles_hud_tactical) -- if(dist < 10240 && t != self.team) -- { -- // TODO: Vehicle tactical hud -- o = project_3d_to_2d(self.origin + '0 0 32'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - if(o.z < 0 - || o.x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o.y < (vid_conheight * waypointsprite_edgeoffset_top) - || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) -- return; // Dont draw wp's for turrets out of view - o_z = 0; - o.z = 0; -- if(hud != HUD_NORMAL) -- { -- switch(hud) -- { -- case HUD_SPIDERBOT: -- case HUD_WAKIZASHI: -- case HUD_RAPTOR: -- case HUD_BUMBLEBEE: -- if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) -- txt = "gfx/vehicles/vth-mover.tga"; -- else -- txt = "gfx/vehicles/vth-stationary.tga"; -- -- vector pz = drawgetimagesize(txt) * 0.25; -- drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); -- break; -- } -- } -- } -- -- if(dist > self.maxdistance) -- return; -- -- string spriteimage = self.netname; -- float a = self.alpha * autocvar_hud_panel_fg_alpha; -- vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); -- -- -- if(self.maxdistance > waypointsprite_normdistance) -- a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); -- else if(self.maxdistance > 0) -- a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; -- -- if(rgb == '0 0 0') -- { -- self.teamradar_color = '1 0 1'; -- printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage); -- } -- -- txt = self.netname; -- if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) -- txt = _("Spam"); -- else -- txt = spritelookuptext(spriteimage); -- -- if(time - floor(time) > 0.5 && t == self.team) -- { -- if(self.helpme && time < self.helpme) -- { -- a *= SPRITE_HELPME_BLINK; -- txt = sprintf(_("%s under attack!"), txt); -- } -- else -- a *= spritelookupblinkvalue(spriteimage); -- } -- -- if(autocvar_g_waypointsprite_uppercase) -- txt = strtoupper(txt); -- -- if(a > 1) -- { -- rgb *= a; -- a = 1; -- } -- -- if(a <= 0) -- return; -- -- rgb = fixrgbexcess(rgb); -- -- o = project_3d_to_2d(self.origin + '0 0 64'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - if(o.z < 0 - || o.x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o.y < (vid_conheight * waypointsprite_edgeoffset_top) - || o.x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o.y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) -- return; // Dont draw wp's for turrets out of view -- - o_z = 0; - o.z = 0; -- -- float edgedistance_min, crosshairdistance; - edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), - (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), - (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, - (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); - edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)), - (o.x - (vid_conwidth * waypointsprite_edgeoffset_left)), - (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x, - (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y); -- -- float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); -- - crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); - crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) ); -- -- t = waypointsprite_scale * vidscale; -- a *= waypointsprite_alpha; -- -- { -- a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); -- } -- if (edgedistance_min < waypointsprite_edgefadedistance) { -- a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); -- } -- if(crosshairdistance < waypointsprite_crosshairfadedistance) { -- a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); -- t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); -- } -- -- o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); -- o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); -- drawhealthbar( -- o, -- 0, -- self.health / 255, -- '0 0 0', -- '0 0 0', -- 0.5 * SPRITE_HEALTHBAR_WIDTH * t, -- 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, -- SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, -- SPRITE_HEALTHBAR_BORDER * t, -- 0, -- rgb, -- a * SPRITE_HEALTHBAR_BORDERALPHA, -- rgb, -- a * SPRITE_HEALTHBAR_HEALTHALPHA, -- DRAWFLAG_NORMAL -- ); --} -- --void turret_walker_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- fixedmakevectors(self.angles); -- movelib_groundalign4point(300, 100, 0.25, 45); -- setorigin(self, self.origin + self.velocity * dt); -- self.tur_head.angles += dt * self.tur_head.move_avelocity; - self.angles_y = self.move_angles_y; - self.angles_y = self.move_angles.y; -- -- if (self.health < 127) -- if(random() < 0.15) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); --} -- --void turret_ewheel_draw() --{ -- float dt; -- -- dt = time - self.move_time; -- self.move_time = time; -- if(dt <= 0) -- return; -- -- fixedmakevectors(self.angles); -- setorigin(self, self.origin + self.velocity * dt); -- self.tur_head.angles += dt * self.tur_head.move_avelocity; - self.angles_y = self.move_angles_y; - self.angles_y = self.move_angles.y; -- -- if (self.health < 127) -- if(random() < 0.05) -- te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); --} -- - void(entity e, entity tagentity, string tagname) setattachment = #443; --void turret_construct() --{ -- if(self.tur_head == world) -- self.tur_head = spawn(); -- -- turret_tid2info(self.turret_type); -- self.netname = tid2info_name; -- -- setorigin(self, self.origin); -- setmodel(self, tid2info_base); -- setmodel(self.tur_head, tid2info_head); -- setsize(self, tid2info_min, tid2info_max); -- setsize(self.tur_head, '0 0 0', '0 0 0'); -- -- if(self.turret_type == TID_EWHEEL) -- setattachment(self.tur_head, self, ""); -- else -- setattachment(self.tur_head, self, "tag_head"); -- -- self.tur_head.classname = "turret_head"; -- self.tur_head.owner = self; -- self.tur_head.move_movetype = MOVETYPE_NOCLIP; -- self.move_movetype = MOVETYPE_NOCLIP; -- self.tur_head.angles = self.angles; -- self.health = 255; -- self.solid = SOLID_BBOX; -- self.tur_head.solid = SOLID_NOT; -- self.movetype = MOVETYPE_NOCLIP; -- self.tur_head.movetype = MOVETYPE_NOCLIP; -- self.draw = turret_draw; -- self.entremove = turret_remove; -- self.drawmask = MASK_NORMAL; -- self.tur_head.drawmask = MASK_NORMAL; -- self.anim_start_time = 0; -- self.draw2d = turret_draw2d; -- self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; -- self.teamradar_color = '1 0 0'; -- self.alpha = 1; -- -- if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) -- { -- self.gravity = 1; -- self.movetype = MOVETYPE_BOUNCE; -- self.move_movetype = MOVETYPE_BOUNCE; -- self.move_origin = self.origin; -- self.move_time = time; -- switch(self.turret_type) -- { -- case TID_EWHEEL: -- self.draw = turret_ewheel_draw; -- break; -- case TID_WALKER: -- self.draw = turret_walker_draw; -- break; -- -- } -- } --} -- --entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); --void turret_gibboom(); --void turret_gib_draw() --{ -- Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); -- -- self.drawmask = MASK_NORMAL; -- -- if(self.cnt) -- { -- if(time >= self.nextthink) -- { -- turret_gibboom(); -- remove(self); -- } -- } -- else -- { -- self.alpha = bound(0, self.nextthink - time, 1); -- if(self.alpha < ALPHA_MIN_VISIBLE) -- remove(self); -- } --} -- --void turret_gibboom() --{ -- float i; -- -- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); -- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); -- -- for (i = 1; i < 5; i = i + 1) - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false); --} -- --entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode) --{ -- entity gib; -- -- traceline(_from, _to, MOVE_NOMONSTERS, world); -- if(trace_startsolid) -- return world; -- -- gib = spawn(); -- setorigin(gib, _from); -- setmodel(gib, _model); -- gib.colormod = _cmod; -- gib.solid = SOLID_CORPSE; -- gib.draw = turret_gib_draw; -- gib.cnt = _explode; -- setsize(gib, '-1 -1 -1', '1 1 1'); -- if(_explode) -- { -- gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15)); -- gib.effects = EF_FLAME; -- } -- else -- gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); -- -- gib.gravity = 1; -- gib.move_movetype = MOVETYPE_BOUNCE; -- gib.move_origin = _from; -- setorigin(gib, _from); -- gib.move_velocity = _to; -- gib.move_avelocity = prandomvec() * 32; -- gib.move_time = time; -- gib.damageforcescale = 1; -- gib.classname = "turret_gib"; -- -- return gib; --} -- --void turret_die() --{ -- -- sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); -- pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); -- turret_tid2info(self.turret_type); -- if (!autocvar_cl_nogibs) -- { -- // Base -- if(self.turret_type == TID_EWHEEL) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true); -- else if (self.turret_type == TID_WALKER) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true); -- else if (self.turret_type == TID_TESLA) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false); -- else -- { -- if (random() > 0.5) -- { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); -- } -- else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true); -- - entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); - entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true); -- if(headgib) -- { -- headgib.angles = headgib.move_angles = self.tur_head.angles; -- headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity.y * 5; -- headgib.gravity = 0.5; -- } -- } -- } -- -- setmodel(self, "null"); -- setmodel(self.tur_head, "null"); --} -- --void ent_turret() --{ - float sf; - sf = ReadByte(); - int sf = ReadByte(); -- -- if(sf & TNSF_SETUP) -- { -- self.turret_type = ReadByte(); -- -- self.origin_x = ReadCoord(); -- self.origin_y = ReadCoord(); -- self.origin_z = ReadCoord(); -- setorigin(self, self.origin); -- -- self.angles_x = ReadAngle(); -- self.angles_y = ReadAngle(); -- -- turret_precache(self.turret_type); -- turret_construct(); -- self.colormap = 1024; -- self.glowmod = '0 1 1'; -- self.tur_head.colormap = self.colormap; -- self.tur_head.glowmod = self.glowmod; -- } -- -- if(sf & TNSF_ANG) -- { -- if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. -- self.tur_head = spawn(); -- -- self.tur_head.move_angles_x = ReadShort(); -- self.tur_head.move_angles_y = ReadShort(); -- //self.tur_head.angles = self.angles + self.tur_head.move_angles; -- self.tur_head.angles = self.tur_head.move_angles; -- } -- -- if(sf & TNSF_AVEL) -- { -- if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. -- self.tur_head = spawn(); -- -- self.tur_head.move_avelocity_x = ReadShort(); -- self.tur_head.move_avelocity_y = ReadShort(); -- } -- -- if(sf & TNSF_MOVE) -- { -- self.origin_x = ReadShort(); -- self.origin_y = ReadShort(); -- self.origin_z = ReadShort(); -- setorigin(self, self.origin); -- -- self.velocity_x = ReadShort(); -- self.velocity_y = ReadShort(); -- self.velocity_z = ReadShort(); -- -- self.move_angles_y = ReadShort(); -- -- self.move_time = time; -- self.move_velocity = self.velocity; -- self.move_origin = self.origin; -- } -- -- if(sf & TNSF_ANIM) -- { -- self.frame1time = ReadCoord(); -- self.frame = ReadByte(); -- } -- -- if(sf & TNSF_STATUS) -- { - float _tmp; - _tmp = ReadByte(); - int _tmp = ReadByte(); -- if(_tmp != self.team) -- { -- self.team = _tmp; -- turret_changeteam(); -- } -- -- _tmp = ReadByte(); -- if(_tmp == 0 && self.health != 0) -- turret_die(); -- else if(self.health && self.health != _tmp) -- self.helpme = servertime + 10; -- -- self.health = _tmp; -- } -- //self.enemy.health = self.health / 255; --} diff --cc qcsrc/common/command/generic.qc index 2438c0173,7f302e0a2..460410c28 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@@ -1,3 -1,6 +1,7 @@@ + #include "generic.qh" + #include "shared_defs.qh" ++#include "../turrets/config.qh" + // ========================================================= // Generic program common command code, written by Samual // Last updated: February 19th, 2012 diff --cc qcsrc/common/monsters/sv_monsters.qc index fd966f5d2,28430e961..4c443495f --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@@ -1,3 -1,29 +1,27 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/common.qh" + #include "../constants.qh" + #include "../teams.qh" + #include "../util.qh" + #include "monsters.qh" + #include "sv_monsters.qh" + #include "../weapons/weapons.qh" + #include "../../server/autocvars.qh" + #include "../../server/defs.qh" + #include "../deathtypes.qh" + #include "../../server/mutators/mutators_include.qh" - #include "../../server/tturrets/include/turrets_early.qh" + #include "../../server/vehicles/vehicles_def.qh" + #include "../../server/campaign.qh" + #include "../../server/command/common.qh" + #include "../../server/command/cmd.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../../server/round_handler.qh" - #include "../../server/tturrets/include/turrets.qh" + #endif + // ========================= // SVQC Monster Properties // ========================= diff --cc qcsrc/common/nades.qc index 03b15525c,4225a19f0..07a91a8cc --- a/qcsrc/common/nades.qc +++ b/qcsrc/common/nades.qc @@@ -1,8 -1,22 +1,21 @@@ - .float healer_lifetime; - .float healer_radius; + #if defined(CSQC) + #include "../dpdefs/csprogsdefs.qh" + #include "../client/defs.qh" + #include "nades.qh" + #include "buffs.qh" + #include "../client/movetypes.qh" - #include "../server/tturrets/include/turrets_early.qh" + #include "../client/main.qh" + #include "../csqcmodellib/cl_model.qh" + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "constants.qh" + #include "../server/constants.qh" + #endif + #ifdef SVQC - float healer_send(entity to, float sf) + float healer_send(entity to, int sf) { WriteByte(MSG_ENTITY, ENT_CLIENT_HEALING_ORB); WriteByte(MSG_ENTITY, sf); diff --cc qcsrc/common/turrets/cl_turrets.qc index 3365b9202,000000000..2f8e77d2d mode 100644,000000..100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@@ -1,446 -1,0 +1,445 @@@ +void turret_remove() +{ + remove(self.tur_head); + //remove(self.enemy); + self.tur_head = world; +} + +.vector glowmod; +void turret_changeteam() +{ + self.glowmod = Team_ColorRGB(self.team - 1) * 2; + self.teamradar_color = Team_ColorRGB(self.team - 1); + + if(self.team) + self.colormap = 1024 + (self.team - 1) * 17; + + self.tur_head.colormap = self.colormap; + self.tur_head.glowmod = self.glowmod; + +} + +void turret_head_draw() +{ + self.drawmask = MASK_NORMAL; +} + +void turret_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + self.tur_head.angles += dt * self.tur_head.move_avelocity; + + if (self.health < 127) + { + dt = random(); + + if(dt < 0.03) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); + } + + if(self.health < 85) + if(dt < 0.01) + pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + + if(self.health < 32) + if(dt < 0.015) + pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + +} + +void turret_draw2d() +{ + if(self.netname == "") + return; + + if(!autocvar_g_waypointsprite_turrets) + return; + + if(autocvar_cl_hidewaypoints) + return; + + float dist = vlen(self.origin - view_origin); + float t = (GetPlayerColor(player_localnum) + 1); + + vector o; + string txt; + + if(autocvar_cl_vehicles_hud_tactical) + if(dist < 10240 && t != self.team) + { + // TODO: Vehicle tactical hud + o = project_3d_to_2d(self.origin + '0 0 32'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + o_z = 0; + if(hud != HUD_NORMAL) + { + if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE) + txt = "gfx/vehicles/vth-mover.tga"; + else + txt = "gfx/vehicles/vth-stationary.tga"; + + vector pz = drawgetimagesize(txt) * 0.25; + drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); + } + } + + if(dist > self.maxdistance) + return; + + string spriteimage = self.netname; + float a = self.alpha * autocvar_hud_panel_fg_alpha; + vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); + + + if(self.maxdistance > waypointsprite_normdistance) + a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); + else if(self.maxdistance > 0) + a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; + + if(rgb == '0 0 0') + { + self.teamradar_color = '1 0 1'; + printf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage); + } + + txt = self.netname; + if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) + txt = _("Spam"); + else + txt = spritelookuptext(spriteimage); + + if(time - floor(time) > 0.5 && t == self.team) + { + if(self.helpme && time < self.helpme) + { + a *= SPRITE_HELPME_BLINK; + txt = sprintf(_("%s under attack!"), txt); + } + else + a *= spritelookupblinkvalue(spriteimage); + } + + if(autocvar_g_waypointsprite_uppercase) + txt = strtoupper(txt); + + if(a > 1) + { + rgb *= a; + a = 1; + } + + if(a <= 0) + return; + + rgb = fixrgbexcess(rgb); + + o = project_3d_to_2d(self.origin + '0 0 64'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + + o_z = 0; + + float edgedistance_min, crosshairdistance; + edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), + (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), + (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, + (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); + + float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); + + crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); + + t = waypointsprite_scale * vidscale; + a *= waypointsprite_alpha; + + { + a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + } + if (edgedistance_min < waypointsprite_edgefadedistance) { + a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + } + if(crosshairdistance < waypointsprite_crosshairfadedistance) { + a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + } + + o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); + o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); + drawhealthbar( + o, + 0, + self.health / 255, + '0 0 0', + '0 0 0', + 0.5 * SPRITE_HEALTHBAR_WIDTH * t, + 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, + SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, + SPRITE_HEALTHBAR_BORDER * t, + 0, + rgb, + a * SPRITE_HEALTHBAR_BORDERALPHA, + rgb, + a * SPRITE_HEALTHBAR_HEALTHALPHA, + DRAWFLAG_NORMAL + ); +} + - void(entity e, entity tagentity, string tagname) setattachment = #443; +void turret_construct() +{ + entity tur = get_turretinfo(self.turretid); + + if(self.tur_head == world) + self.tur_head = spawn(); + + self.netname = TUR_NAME(self.turretid); + + setorigin(self, self.origin); + setmodel(self, tur.model); + setmodel(self.tur_head, tur.head_model); + setsize(self, tur.mins, tur.maxs); + setsize(self.tur_head, '0 0 0', '0 0 0'); + + if(self.turretid == TUR_EWHEEL) + setattachment(self.tur_head, self, ""); + else + setattachment(self.tur_head, self, "tag_head"); + + self.tur_head.classname = "turret_head"; + self.tur_head.owner = self; + self.tur_head.move_movetype = MOVETYPE_NOCLIP; + self.move_movetype = MOVETYPE_NOCLIP; + self.tur_head.angles = self.angles; + self.health = 255; + self.solid = SOLID_BBOX; + self.tur_head.solid = SOLID_NOT; + self.movetype = MOVETYPE_NOCLIP; + self.tur_head.movetype = MOVETYPE_NOCLIP; + self.draw = turret_draw; + self.entremove = turret_remove; + self.drawmask = MASK_NORMAL; + self.tur_head.drawmask = MASK_NORMAL; + self.anim_start_time = 0; + self.draw2d = turret_draw2d; + self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; + self.teamradar_color = '1 0 0'; + self.alpha = 1; + + TUR_ACTION(self.turretid, TR_SETUP); +} + +entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); +void turret_gibboom(); +void turret_gib_draw() +{ + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + + self.drawmask = MASK_NORMAL; + + if(self.cnt) + { + if(time >= self.nextthink) + { + turret_gibboom(); + remove(self); + } + } + else + { + self.alpha = bound(0, self.nextthink - time, 1); + if(self.alpha < ALPHA_MIN_VISIBLE) + remove(self); + } +} + +void turret_gibboom() +{ + float i; + + sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + + for (i = 1; i < 5; i = i + 1) - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); ++ turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', false); +} + +entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode) +{ + entity gib; + + traceline(_from, _to, MOVE_NOMONSTERS, world); + if(trace_startsolid) + return world; + + gib = spawn(); + setorigin(gib, _from); + setmodel(gib, _model); + gib.colormod = _cmod; + gib.solid = SOLID_CORPSE; + gib.draw = turret_gib_draw; + gib.cnt = _explode; + setsize(gib, '-1 -1 -1', '1 1 1'); + if(_explode) + { + gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15)); + gib.effects = EF_FLAME; + } + else + gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); + + gib.gravity = 1; + gib.move_movetype = MOVETYPE_BOUNCE; + gib.move_origin = _from; + setorigin(gib, _from); + gib.move_velocity = _to; + gib.move_avelocity = prandomvec() * 32; + gib.move_time = time; + gib.damageforcescale = 1; + gib.classname = "turret_gib"; + + return gib; +} + +void turret_die() +{ + sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + if (!autocvar_cl_nogibs) + { + // Base + if(self.turretid == TUR_EWHEEL) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', true); + else if (self.turretid == TUR_WALKER) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', true); + else if (self.turretid == TUR_TESLA) - turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); ++ turret_gibtoss((get_turretinfo(self.turretid)).model, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', false); + else + { + if (random() > 0.5) + { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); ++ turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); ++ turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); ++ turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', false); + } + else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); ++ turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', true); + - entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); ++ entity headgib = turret_gibtoss((get_turretinfo(self.turretid)).head_model, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true); + if(headgib) + { + headgib.angles = headgib.move_angles = self.tur_head.angles; + headgib.avelocity = headgib.move_avelocity = self.tur_head.move_avelocity + randomvec() * 45; + headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; + headgib.gravity = 0.5; + } + } + } + + setmodel(self, "null"); + setmodel(self.tur_head, "null"); +} + +void ent_turret() +{ + float sf; + sf = ReadByte(); + + if(sf & TNSF_SETUP) + { + self.turretid = ReadByte(); + + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + + self.angles_x = ReadAngle(); + self.angles_y = ReadAngle(); + + turret_construct(); + self.colormap = 1024; + self.glowmod = '0 1 1'; + self.tur_head.colormap = self.colormap; + self.tur_head.glowmod = self.glowmod; + } + + if(sf & TNSF_ANG) + { + if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. + self.tur_head = spawn(); + + self.tur_head.move_angles_x = ReadShort(); + self.tur_head.move_angles_y = ReadShort(); + //self.tur_head.angles = self.angles + self.tur_head.move_angles; + self.tur_head.angles = self.tur_head.move_angles; + } + + if(sf & TNSF_AVEL) + { + if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. + self.tur_head = spawn(); + + self.tur_head.move_avelocity_x = ReadShort(); + self.tur_head.move_avelocity_y = ReadShort(); + } + + if(sf & TNSF_MOVE) + { + self.origin_x = ReadShort(); + self.origin_y = ReadShort(); + self.origin_z = ReadShort(); + setorigin(self, self.origin); + + self.velocity_x = ReadShort(); + self.velocity_y = ReadShort(); + self.velocity_z = ReadShort(); + + self.move_angles_y = ReadShort(); + + self.move_time = time; + self.move_velocity = self.velocity; + self.move_origin = self.origin; + } + + if(sf & TNSF_ANIM) + { + self.frame1time = ReadCoord(); + self.frame = ReadByte(); + } + + if(sf & TNSF_STATUS) + { + float _tmp; + _tmp = ReadByte(); + if(_tmp != self.team) + { + self.team = _tmp; + turret_changeteam(); + } + + _tmp = ReadByte(); + if(_tmp == 0 && self.health != 0) + turret_die(); + else if(self.health && self.health != _tmp) + self.helpme = servertime + 10; + + self.health = _tmp; + } + //self.enemy.health = self.health / 255; +} diff --cc qcsrc/common/turrets/cl_turrets.qh index b61678218,000000000..0f8ff9485 mode 100644,000000..100644 --- a/qcsrc/common/turrets/cl_turrets.qh +++ b/qcsrc/common/turrets/cl_turrets.qh @@@ -1,1 -1,0 +1,6 @@@ ++#ifndef CL_TURRETS_H ++#define CL_TURRETS_H ++ +void ent_turret(); ++ ++#endif diff --cc qcsrc/common/turrets/config.qh index ad3a0c72f,000000000..85cbd2006 mode 100644,000000..100644 --- a/qcsrc/common/turrets/config.qh +++ b/qcsrc/common/turrets/config.qh @@@ -1,29 -1,0 +1,34 @@@ - // ========================== - // Turret Config Generator - // ========================== ++#ifndef TURRETS_CONFIG_H ++#define TURRETS_CONFIG_H ++ ++#ifdef SVQC + +void Dump_Turret_Settings(void); +float tur_config_file; +float tur_config_alsoprint; + +#define MAX_TUR_CONFIG 256 +float TUR_CONFIG_COUNT; +string tur_config_queue[MAX_TUR_CONFIG]; + +#define TUR_CONFIG_QUEUE(a) { \ + tur_config_queue[TUR_CONFIG_COUNT] = a; \ + ++TUR_CONFIG_COUNT; } + +#define TUR_CONFIG_WRITETOFILE(a) { \ + fputs(tur_config_file, a); \ + if(tur_config_alsoprint) { print(a); } } + +#define TUR_CONFIG_WRITE_CVARS(turret,name) \ + { TUR_CONFIG_QUEUE( \ + sprintf("set g_turrets_unit_%s_%s %g\n", #turret, #name, \ + cvar(sprintf("g_turrets_unit_%s_%s", #turret, #name)))) } \ + +#define TUR_CONFIG_SETTINGS(tursettings) \ + #define TUR_ADD_CVAR(turret,name) TUR_CONFIG_WRITE_CVARS(turret,name) \ + tursettings \ + #undef TUR_ADD_CVAR ++ ++#endif ++ ++#endif diff --cc qcsrc/common/turrets/sv_turrets.qc index aca2b8ead,000000000..91d427fd8 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@@ -1,1396 -1,0 +1,1394 @@@ - // ========================= - // SVQC Turret Properties - // ========================= - ++#ifdef SVQC ++#include "../../server/autocvars.qh" + +// Generic aiming +vector turret_aim_generic() +{ + + vector pre_pos, prep; + float distance, impact_time = 0, i, mintime; + + turret_tag_fire_update(); + + if(self.aim_flags & TFL_AIM_SIMPLE) + return real_origin(self.enemy); + + mintime = max(self.attack_finished_single - time,0) + sys_frametime; + + // Baseline + pre_pos = real_origin(self.enemy); + + // Lead? + if (self.aim_flags & TFL_AIM_LEAD) + { + if (self.aim_flags & TFL_AIM_SHOTTIMECOMPENSATE) // Need to conpensate for shot traveltime + { + prep = pre_pos; + + distance = vlen(prep - self.tur_shotorg); + impact_time = distance / self.shot_speed; + + prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); + + if(self.aim_flags & TFL_AIM_ZPREDICT) + if(!(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_frametime) + { + vz = vz - (autocvar_sv_gravity * sys_frametime); + prep_z = prep_z + vz * sys_frametime; + } + } + pre_pos = prep; + } + else + pre_pos = pre_pos + self.enemy.velocity * mintime; + } + + if(self.aim_flags & TFL_AIM_SPLASH) + { + //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + if(trace_fraction != 1.0) + pre_pos = trace_endpos; + } + + return pre_pos; +} + +float turret_targetscore_support(entity _turret,entity _target) +{ + float score; // Total score + float s_score = 0, d_score; + + if (_turret.enemy == _target) s_score = 1; + + d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist); + + score = (d_score * _turret.target_select_rangebias) + + (s_score * _turret.target_select_samebias); + + return score; +} + +/* +* Generic bias aware score system. +*/ +float turret_targetscore_generic(entity _turret, entity _target) +{ + float d_dist; // Defendmode Distance + float score; // Total score + float d_score; // Distance score + float a_score; // Angular score + float m_score = 0; // missile score + float p_score = 0; // player score + float ikr; // ideal kill range + + if (_turret.tur_defend) + { + d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin); + ikr = vlen(_turret.origin - _turret.tur_defend.origin); + d_score = 1 - d_dist / _turret.target_range; + } + else + { + // Make a normlized value base on the targets distance from our optimal killzone + ikr = _turret.target_range_optimal; + d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist); + } + + a_score = 1 - tvt_thadf / _turret.aim_maxrotate; + + if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE)) + m_score = 1; + + if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target)) + 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 * _turret.target_select_rangebias) + + (a_score * _turret.target_select_anglebias) + + (m_score * _turret.target_select_missilebias) + + (p_score * _turret.target_select_playerbias); + + if(_turret.target_range < vlen(_turret.tur_shotorg - real_origin(_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 *= _turret.target_select_rangebias; + sdt = ftos(d_score); + + //sv = ftos(v_score); + //v_score *= _turret.target_select_samebias; + //svt = ftos(v_score); + + sa = ftos(a_score); + a_score *= _turret.target_select_anglebias; + sat = ftos(a_score); + + sm = ftos(m_score); + m_score *= _turret.target_select_missilebias; + smt = ftos(m_score); + + sp = ftos(p_score); + p_score *= _turret.target_select_playerbias; + spt = ftos(p_score); + + + ss = ftos(score); + bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _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; +} + +// Generic damage handling - void() turret_respawn; +void turret_hide() +{ + self.effects |= EF_NODRAW; + self.nextthink = time + self.respawntime - 0.2; + self.think = turret_respawn; +} + +void turret_die() +{ + self.deadflag = DEAD_DEAD; + self.tur_head.deadflag = self.deadflag; + +// Unsolidify and hide real parts + self.solid = SOLID_NOT; + self.tur_head.solid = self.solid; + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + self.health = 0; + +// Go boom + //RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,DEATH_TURRET,world); + + if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + { + TUR_ACTION(self.turretid, TR_DEATH); + + remove(self.tur_head); + remove(self); + } + else + { + // Setup respawn + self.SendFlags |= TNSF_STATUS; + self.nextthink = time + 0.2; + self.think = turret_hide; + + TUR_ACTION(self.turretid, TR_DEATH); + } +} + +void turret_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ - // Enougth allready! ++ // Enough already! + if(self.deadflag == DEAD_DEAD) + return; + + // Inactive turrets take no damage. (hm..) + if(!self.active) + return; + + if(SAME_TEAM(self, attacker)) + { + if(autocvar_g_friendlyfire) + damage = damage * autocvar_g_friendlyfire; + else + return; + } + + self.health -= damage; + + // thorw head slightly off aim when hit? + if (self.damage_flags & TFL_DMG_HEADSHAKE) + { + self.tur_head.angles_x = self.tur_head.angles_x + (-0.5 + random()) * damage; + self.tur_head.angles_y = self.tur_head.angles_y + (-0.5 + random()) * damage; + + self.SendFlags |= TNSF_ANG; + } + + if (self.turret_flags & TUR_FLAG_MOVE) + self.velocity = self.velocity + vforce; + + if (self.health <= 0) + { + self.event_damage = func_null; + self.tur_head.event_damage = func_null; + self.takedamage = DAMAGE_NO; + self.nextthink = time; + self.think = turret_die; + } + + self.SendFlags |= TNSF_STATUS; +} + +void() turret_think; +void turret_respawn() +{ + // Make sure all parts belong to the same team since + // this function doubles as "teamchange" function. + self.tur_head.team = self.team; + self.effects &= ~EF_NODRAW; + self.deadflag = DEAD_NO; + self.effects = EF_LOWPRECISION; + self.solid = SOLID_BBOX; + self.takedamage = DAMAGE_AIM; + self.event_damage = turret_damage; + self.avelocity = '0 0 0'; + self.tur_head.avelocity = self.avelocity; + self.tur_head.angles = self.idle_aim; + self.health = self.max_health; + self.enemy = world; + self.volly_counter = self.shot_volly; + self.ammo = self.ammo_max; + + self.nextthink = time + self.ticrate; + self.think = turret_think; + + self.SendFlags = TNSF_FULL_UPDATE; + + TUR_ACTION(self.turretid, TR_SETUP); +} + + +// Main functions +#define cvar_base "g_turrets_unit_" +.float clientframe; +void turrets_setframe(float _frame, float client_only) +{ + if((client_only ? self.clientframe : self.frame ) != _frame) + { + self.SendFlags |= TNSF_ANIM; + self.anim_start_time = time; + } + + if(client_only) + self.clientframe = _frame; + else + self.frame = _frame; + +} + +float turret_send(entity to, float sf) +{ + + WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); + WriteByte(MSG_ENTITY, sf); + if(sf & TNSF_SETUP) + { + WriteByte(MSG_ENTITY, self.turretid); + + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + WriteAngle(MSG_ENTITY, self.angles_x); + WriteAngle(MSG_ENTITY, self.angles_y); + } + + if(sf & TNSF_ANG) + { + WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x)); + WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y)); + } + + if(sf & TNSF_AVEL) + { + WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x)); + WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y)); + } + + if(sf & TNSF_MOVE) + { + WriteShort(MSG_ENTITY, rint(self.origin_x)); + WriteShort(MSG_ENTITY, rint(self.origin_y)); + WriteShort(MSG_ENTITY, rint(self.origin_z)); + + WriteShort(MSG_ENTITY, rint(self.velocity_x)); + WriteShort(MSG_ENTITY, rint(self.velocity_y)); + WriteShort(MSG_ENTITY, rint(self.velocity_z)); + + WriteShort(MSG_ENTITY, rint(self.angles_y)); + } + + if(sf & TNSF_ANIM) + { + WriteCoord(MSG_ENTITY, self.anim_start_time); + WriteByte(MSG_ENTITY, self.frame); + } + + if(sf & TNSF_STATUS) + { + WriteByte(MSG_ENTITY, self.team); + + if(self.health <= 0) + WriteByte(MSG_ENTITY, 0); + else + WriteByte(MSG_ENTITY, ceil((self.health / self.max_health) * 255)); + } + - return TRUE; ++ return true; +} + +void load_unit_settings(entity ent, string unitname, float is_reload) +{ + string sbase; + + 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'; + + ent.tur_head.angles = '0 0 0'; + } + + 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_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; + //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * 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_maxrotate = 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_rotate = cvar(strcat(sbase,"_track_accel_rot")); + ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate")); + + if(is_reload) + TUR_ACTION(self.turretid, TR_SETUP); +} + +void turret_projectile_explode() +{ + + self.takedamage = DAMAGE_NO; + self.event_damage = func_null; +#ifdef TURRET_DEBUG + float d; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); + self.owner.tur_debug_dmg_t_h = self.owner.tur_debug_dmg_t_h + d; + self.owner.tur_debug_dmg_t_f = self.owner.tur_debug_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void turret_projectile_touch() +{ + PROJECTILE_TOUCH; + turret_projectile_explode(); +} + +void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.velocity += vforce; + self.health -= damage; + //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets + if(self.health <= 0) + W_PrepareExplosionByDamage(self.owner, turret_projectile_explode); +} + +entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) +{ + entity proj; + + sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); + proj = spawn (); + setorigin(proj, self.tur_shotorg); + setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size); + proj.owner = self; + proj.realowner = self; - proj.bot_dodge = TRUE; ++ proj.bot_dodge = true; + proj.bot_dodgerating = self.shot_dmg; + proj.think = turret_projectile_explode; + proj.touch = turret_projectile_touch; + proj.nextthink = time + 9; + proj.movetype = MOVETYPE_FLYMISSILE; + proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; + proj.flags = FL_PROJECTILE; + proj.enemy = self.enemy; + proj.totalfrags = _death; + PROJECTILE_MAKETRIGGER(proj); + if(_health) + { + proj.health = _health; + proj.takedamage = DAMAGE_YES; + proj.event_damage = turret_projectile_damage; + } + else + proj.flags |= FL_NOTARGET; + + CSQCProjectile(proj, _cli_anim, _proj_type, _cull); + + return proj; +} + +/** +** updates enemy distances, predicted impact point/time +** and updated aim<->predict impact distance. +**/ +void turret_do_updates(entity t_turret) +{ + vector enemy_pos; + entity oldself; + + oldself = self; + self = t_turret; + + enemy_pos = real_origin(self.enemy); + + turret_tag_fire_update(); + + self.tur_shotdir_updated = 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) && (self.enemy)) + { + oldpos = self.enemy.origin; + setorigin(self.enemy, self.tur_aimpos); + tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); + setorigin(self.enemy, oldpos); + + if(trace_ent == self.enemy) + self.tur_dist_impact_to_aimpos = 0; + else + self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos); + } + else*/ + tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); + + self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5); + self.tur_impactent = trace_ent; + self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed; + + self = oldself; +} + +/** +** Handles head rotation according to +** the units .track_type and .track_flags +**/ +.float turret_framecounter; +void turret_track() +{ + vector target_angle; // This is where we want to aim + vector move_angle; // This is where we can aim + float f_tmp; + vector v1, v2; + v1 = self.tur_head.angles; + v2 = self.tur_head.avelocity; + + if (self.track_flags == TFL_TRACK_NO) + return; + + if(!self.active) + target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch); + else if (self.enemy == world) + { + if(time > self.lip) + target_angle = self.idle_aim + self.angles; + else + target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); + } + else + { + target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); + } + + self.tur_head.angles_x = anglemods(self.tur_head.angles_x); + self.tur_head.angles_y = anglemods(self.tur_head.angles_y); + + // Find the diffrence between where we currently aim and where we want to aim + //move_angle = target_angle - (self.angles + self.tur_head.angles); + //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles)); + + move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles; + move_angle = shortangle_vxy(move_angle, self.tur_head.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 > self.aim_maxpitch) + self.tur_head.angles_x = self.aim_maxpitch; + + if(self.tur_head.angles_x < -self.aim_maxpitch) + self.tur_head.angles_x = self.aim_maxpitch; + } + + if (self.track_flags & TFL_TRACK_ROTATE) + { + self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp); + if(self.tur_head.angles_y > self.aim_maxrotate) + self.tur_head.angles_y = self.aim_maxrotate; + + if(self.tur_head.angles_y < -self.aim_maxrotate) + self.tur_head.angles_y = self.aim_maxrotate; + } + + // CSQC + self.SendFlags |= TNSF_ANG; + + 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_rotate * 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) > self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = self.aim_maxpitch; + + self.SendFlags |= TNSF_ANG; + } + + if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch) + { + self.tur_head.avelocity_x = 0; + self.tur_head.angles_x = -self.aim_maxpitch; + + self.SendFlags |= TNSF_ANG; + } + } + + // rot + if (self.track_flags & TFL_TRACK_ROTATE) + { + self.tur_head.avelocity_y = move_angle_y; + + if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrotate) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = self.aim_maxrotate; + + self.SendFlags |= TNSF_ANG; + } + + if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrotate) + { + self.tur_head.avelocity_y = 0; + self.tur_head.angles_y = -self.aim_maxrotate; + + self.SendFlags |= TNSF_ANG; + } + } + + self.SendFlags |= TNSF_AVEL; + + // Force a angle update every 10'th frame + self.turret_framecounter += 1; + if(self.turret_framecounter >= 10) + { + self.SendFlags |= TNSF_ANG; + self.turret_framecounter = 0; + } +} + +/* + + TFL_TARGETSELECT_NO + + TFL_TARGETSELECT_LOS + + TFL_TARGETSELECT_PLAYERS + + TFL_TARGETSELECT_MISSILES + - TFL_TARGETSELECT_TRIGGERTARGET + + TFL_TARGETSELECT_ANGLELIMITS + + TFL_TARGETSELECT_RANGELIMITS + + TFL_TARGETSELECT_TEAMCHECK + - TFL_TARGETSELECT_NOBUILTIN + + TFL_TARGETSELECT_OWNTEAM +*/ + +/** +** Evaluate a entity for target valitity based on validate_flags +** NOTE: the caller must check takedamage before calling this, to inline this check. +**/ +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) + return -2; + + if(e_target.owner == e_turret) + return -0.5; + + if(!checkpvs(e_target.origin, e_turret)) + return -1; + + if(e_target.alpha <= 0.3) + return -1; + + 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.vehicle_flags & VHF_ISVEHICLE) + { + if (e_target.vehicle_health <= 0) + return -6; + } + else if (e_target.health <= 0) + return -6; + else if(e_target.frozen > 0) + return -6; + + // player + if (IS_CLIENT(e_target)) + { + if(!(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.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(!(validate_flags & TFL_TARGETSELECT_MISSILES)) + return -10; + + if (validate_flags & TFL_TARGETSELECT_MISSILESONLY) + if(!(e_target.flags & FL_PROJECTILE)) + return -10.5; + + // 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_RANGELIMITS) + { + 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? + tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, 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_maxrotate) + 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.origin + '0 0 16', 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; + + /* + if (e_target.classname == "func_button") + return -21; + */ + +#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 + + m_score = 0; + if(self.enemy && self.enemy.takedamage && 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; + } + else + e_enemy = self.enemy = world; + + e = findradius(self.origin, self.target_range); + + // Nothing to aim at? + if (!e) + return world; + + while (e) + { + if(e.takedamage) + { + float f = turret_validate_target(self, e, self.target_select_flags); + //dprint("F is: ", ftos(f), "\n"); + 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; +} + + +/* + + = 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_AMMO_OWN + + TFL_FIRECHECK_AMMO_OTHER + + TFL_FIRECHECK_REFIRE +*/ + +/** +** Preforms pre-fire checks based on the uints firecheck_flags +**/ +float turret_firecheck() +{ + // This one just dont care =) + if (self.firecheck_flags & TFL_FIRECHECK_NO) + return 1; + + if (self.enemy == world) + return 0; + + // 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) + if (self.volly_counter != self.shot_volly) + if(self.ammo >= self.shot_dmg) + 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; + + // Own ammo? + if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OWN) + if (self.ammo < self.shot_dmg) + return 0; + + // Other's ammo? (support-supply units) + if (self.firecheck_flags & TFL_FIRECHECK_AMMO_OTHER) + if (self.enemy.ammo >= self.enemy.ammo_max) + return 0; + + // Target of opertunity? + if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) + { + self.enemy = self.tur_impactent; + return 1; + } + + if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) + { + // To close? + if (self.tur_dist_aimpos < self.target_range_min) + if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) + return 1; // Target of opertunity? + else + 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) + 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; +} + +void turret_fire() +{ + if (autocvar_g_turrets_nofire != 0) + return; + + TUR_ACTION(self.turretid, TR_ATTACK); + + self.attack_finished_single = time + self.shot_refire; + 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_debug_rvec, self.tur_impacttime + 0.25); +#endif +} + +void turret_think() +{ + entity e; + + self.nextthink = time + self.ticrate; + + // ONS uses somewhat backwards linking. + if (teamplay) + { + if (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_respawn(); + } + +#ifdef TURRET_DEBUG + if (self.tur_debug_tmr1 < time) + { + if (self.enemy) paint_target (self.enemy,128,self.tur_debug_rvec,0.9); + paint_target(self,256,self.tur_debug_rvec,0.9); + self.tur_debug_tmr1 = time + 1; + } +#endif + + // Handle ammo + if (!(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(!self.active) + { + turret_track(); + 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(e.takedamage) + { + 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(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if(!(self.track_flags & TFL_TRACK_NO)) + turret_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(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + turret_do_updates(self); + + // Fire! + if (self.turret_firecheckfunc() != 0) + turret_fire(); + + TUR_ACTION(self.turretid, TR_THINK); + + return; + } + + // Check if we have a vailid enemy, and try to find one if we dont. + + // g_turrets_targetscan_maxdelay forces a target re-scan at least this often + float do_target_scan = 0; + if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) + do_target_scan = 1; + + // Old target (if any) invalid? + if(self.target_validate_time < time) + if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) + { + self.enemy = world; + self.target_validate_time = time + 0.5; + do_target_scan = 1; + } + + // But never more often then g_turrets_targetscan_mindelay! + if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) + do_target_scan = 0; + + if(do_target_scan) + { + self.enemy = turret_select_target(); + 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_track(); + + TUR_ACTION(self.turretid, TR_THINK); + + // And bail. + return; + } + else + self.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target. + + // Predict? + if(!(self.aim_flags & TFL_AIM_NO)) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if(!(self.track_flags & TFL_TRACK_NO)) + turret_track(); + + turret_do_updates(self); + + // Fire? + if (self.turret_firecheckfunc()) + turret_fire(); + } + + TUR_ACTION(self.turretid, TR_THINK); +} + +/* + When .used a turret switch team to activator.team. + If activator is world, the turret go inactive. +*/ +void turret_use() +{ + dprint("Turret ",self.netname, " used by ", activator.classname, "\n"); + + self.team = activator.team; + + if(self.team == 0) + self.active = ACTIVE_NOT; + else + self.active = ACTIVE_ACTIVE; + +} + +void turret_link() +{ - Net_LinkEntity(self, TRUE, 0, turret_send); ++ Net_LinkEntity(self, true, 0, turret_send); + self.think = turret_think; + self.nextthink = time; + self.tur_head.effects = EF_NODRAW; +} + +void turrets_manager_think() +{ + self.nextthink = time + 1; + + entity e; + if (autocvar_g_turrets_reloadcvars == 1) + { + e = nextent(world); + while (e) + { + if (e.turret_flags & TUR_FLAG_ISTURRET) + { + load_unit_settings(e,e.cvar_basename,1); + TUR_ACTION(self.turretid, TR_THINK); + } + + e = nextent(e); + } + cvar_set("g_turrets_reloadcvars","0"); + } +} + +float turret_initialize(float tur_id) +{ + if(!autocvar_g_turrets) - return FALSE; ++ return false; + + entity e; + entity tur = get_turretinfo(tur_id); + if(tur.turretid == 0) - return FALSE; // invalid turret ++ return false; // invalid turret + + if(!self.tur_head) { TUR_ACTION(tur_id, TR_PRECACHE); } // if tur_head exists, we can assume this turret re-spawned + + e = find(world, classname, "turret_manager"); + if(!e) + { + e = spawn(); + e.classname = "turret_manager"; + e.think = turrets_manager_think; + e.nextthink = time + 2; + } + + if(!(self.spawnflags & TSF_SUSPENDED)) + builtin_droptofloor(); + + self.cvar_basename = tur.cvar_basename; + load_unit_settings(self, self.cvar_basename, 0); + + if(!self.team || !teamplay) { self.team = MAX_SHOT_DISTANCE; } + if(!self.ticrate) { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } + if(!self.health) { self.health = 1000; } + if(!self.shot_refire) { self.shot_refire = 1; } + if(!self.tur_shotorg) { self.tur_shotorg = '50 0 50'; } + if(!self.turret_flags) { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; } + if(!self.damage_flags) { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; } + if(!self.aim_flags) { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; } + if(!self.track_type) { self.track_type = TFL_TRACKTYPE_STEPMOTOR; } + if(!self.track_flags) { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; } + if(!self.ammo_flags) { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; } + if(!self.target_select_flags) { self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; } + if(!self.firecheck_flags) { self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_LOS + | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_REFIRE; } + + if(self.track_type != TFL_TRACKTYPE_STEPMOTOR) + { + // Fluid / Ineria mode. Looks mutch nicer. + // Can reduce aim preformance alot, needs a bit diffrent aimspeed + + self.aim_speed = bound(0.1, ((!self.aim_speed) ? 180 : self.aim_speed), 1000); + + if(!self.track_accel_pitch) { self.track_accel_pitch = 0.5; } + if(!self.track_accel_rotate) { self.track_accel_rotate = 0.5; } + if(!self.track_blendrate) { self.track_blendrate = 0.35; } + } + + self.respawntime = max(-1, ((!self.respawntime) ? 60 : self.respawntime)); + self.shot_refire = bound(0.01, ((!self.shot_refire) ? 1 : self.shot_refire), 9999); + self.shot_dmg = max(1, ((!self.shot_dmg) ? self.shot_refire * 50 : self.shot_dmg)); + self.shot_radius = max(1, ((!self.shot_radius) ? self.shot_dmg * 0.5 : self.shot_radius)); + self.shot_speed = max(1, ((!self.shot_speed) ? 2500 : self.shot_speed)); + self.shot_spread = bound(0.0001, ((!self.shot_spread) ? 0.0125 : self.shot_spread), 500); + self.shot_force = bound(0.001, ((!self.shot_force) ? self.shot_dmg * 0.5 + self.shot_radius * 0.5 : self.shot_force), 5000); + self.shot_volly = bound(1, ((!self.shot_volly) ? 1 : self.shot_volly), floor(self.ammo_max / self.shot_dmg)); + self.shot_volly_refire = bound(self.shot_refire, ((!self.shot_volly_refire) ? self.shot_refire * self.shot_volly : self.shot_volly_refire), 60); + self.target_range = bound(0, ((!self.target_range) ? self.shot_speed * 0.5 : self.target_range), MAX_SHOT_DISTANCE); + self.target_range_min = bound(0, ((!self.target_range_min) ? self.shot_radius * 2 : self.target_range_min), MAX_SHOT_DISTANCE); + self.target_range_optimal = bound(0, ((!self.target_range_optimal) ? self.target_range * 0.5 : self.target_range_optimal), MAX_SHOT_DISTANCE); + self.aim_maxrotate = bound(0, ((!self.aim_maxrotate) ? 90 : self.aim_maxrotate), 360); + self.aim_maxpitch = bound(0, ((!self.aim_maxpitch) ? 20 : self.aim_maxpitch), 90); + self.aim_speed = bound(0.1, ((!self.aim_speed) ? 36 : self.aim_speed), 1000); + self.aim_firetolerance_dist = bound(0.1, ((!self.aim_firetolerance_dist) ? 5 + (self.shot_radius * 2) : self.aim_firetolerance_dist), MAX_SHOT_DISTANCE); + self.target_select_rangebias = bound(-10, ((!self.target_select_rangebias) ? 1 : self.target_select_rangebias), 10); + self.target_select_samebias = bound(-10, ((!self.target_select_samebias) ? 1 : self.target_select_samebias), 10); + self.target_select_anglebias = bound(-10, ((!self.target_select_anglebias) ? 1 : self.target_select_anglebias), 10); + self.target_select_missilebias = bound(-10, ((!self.target_select_missilebias) ? 1 : self.target_select_missilebias), 10); + self.target_select_playerbias = bound(-10, ((!self.target_select_playerbias) ? 1 : self.target_select_playerbias), 10); + self.ammo_max = max(self.shot_dmg, ((!self.ammo_max) ? self.shot_dmg * 10 : self.ammo_max)); + self.ammo_recharge = max(0, ((!self.ammo_recharge) ? self.shot_dmg * 0.5 : self.ammo_recharge)); + + self.turret_flags = TUR_FLAG_ISTURRET | (tur.spawnflags); + + if(self.turret_flags & TUR_FLAG_SPLASH) + self.aim_flags |= TFL_AIM_SPLASH; + + if(self.turret_flags & TUR_FLAG_MISSILE) + self.target_select_flags |= TFL_TARGETSELECT_MISSILES; + + if(self.turret_flags & TUR_FLAG_PLAYER) + self.target_select_flags |= TFL_TARGETSELECT_PLAYERS; + + if(self.spawnflags & TSL_NO_RESPAWN) + self.damage_flags |= TFL_DMG_DEATH_NORESPAWN; + + if (self.turret_flags & TUR_FLAG_SUPPORT) + self.turret_score_target = turret_targetscore_support; + else + self.turret_score_target = turret_targetscore_generic; + + ++turret_count; + + setmodel(self, tur.model); + setsize(self, tur.mins, tur.maxs); + + self.turretid = tur_id; + self.classname = "turret_main"; + self.active = ACTIVE_ACTIVE; + self.effects = EF_NODRAW; + self.netname = TUR_NAME(tur_id); + self.ticrate = bound(sys_frametime, self.ticrate, 60); + self.max_health = self.health; + self.target_validate_flags = self.target_select_flags; + self.ammo = self.ammo_max; + self.ammo_recharge *= self.ticrate; + self.solid = SOLID_BBOX; + self.takedamage = DAMAGE_AIM; + self.movetype = MOVETYPE_NOCLIP; + self.view_ofs = '0 0 0'; + self.turret_firecheckfunc = turret_firecheck; + self.event_damage = turret_damage; + self.use = turret_use; - self.bot_attack = TRUE; ++ self.bot_attack = true; + self.nextthink = time + 1; + self.nextthink += turret_count * sys_frametime; + + self.tur_head = spawn(); + setmodel(self.tur_head, tur.head_model); + setsize(self.tur_head, '0 0 0', '0 0 0'); + setorigin(self.tur_head, '0 0 0'); + setattachment(self.tur_head, self, "tag_head"); + + self.tur_head.netname = self.tur_head.classname = "turret_head"; + self.tur_head.team = self.team; + self.tur_head.owner = self; + self.tur_head.takedamage = DAMAGE_NO; + self.tur_head.solid = SOLID_NOT; + self.tur_head.movetype = self.movetype; + + 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"); + } + } + + 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'; + +#ifdef TURRET_DEBUG + self.tur_debug_start = self.nextthink; + while (vlen(self.tur_debug_rvec) < 2) + self.tur_debug_rvec = randomvec() * 4; + + self.tur_debug_rvec_x = fabs(self.tur_debug_rvec_x); + self.tur_debug_rvec_y = fabs(self.tur_debug_rvec_y); + self.tur_debug_rvec_z = fabs(self.tur_debug_rvec_z); +#endif + + turret_link(); + turret_respawn(); + turret_tag_fire_update(); + + TUR_ACTION(tur_id, TR_SETUP); + + if(MUTATOR_CALLHOOK(TurretSpawn)) - return FALSE; ++ return false; + - return TRUE; ++ return true; +} ++#endif diff --cc qcsrc/common/turrets/sv_turrets.qh index f89ef1581,000000000..c4ff13e91 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qh +++ b/qcsrc/common/turrets/sv_turrets.qh @@@ -1,106 -1,0 +1,113 @@@ ++#ifndef SV_TURRETS_H ++#define SV_TURRETS_H ++ +// turret fields +.float ticrate; // interal ai think rate +.vector aim_idle; // where to aim while idle +.entity tur_head; // top part of the turret +.entity tur_defend; // defend this entity +.vector tur_shotorg; // shot origin +.vector tur_aimpos; // aiming location +.float tur_impacttime; // predicted projectile impact time +.entity tur_impactent; // entity the projectile hit +.float tur_dist_enemy; // distance to enemy +.float tur_dist_aimpos; // distance to aim location +.float tur_dist_impact_to_aimpos; // distance impact<->aim +.float volly_counter; // decrement counter from .shot_volly to 0 + +.float shot_refire; // attack refire +.float shot_speed; // projectile speed +.float shot_spread; // inaccuracy +.float shot_dmg; // core damage of projectile +.float shot_radius; // projectile damage radius +.float shot_force; // projectile damage force +.float shot_volly; // smaller than 1 = shoot # times at target +.float shot_volly_refire; // refire after completed volly + +.float target_range; +.float target_range_min; +.float target_range_optimal; + +.float target_select_rangebias; +.float target_select_samebias; +.float target_select_anglebias; +.float target_select_missilebias; +.float target_select_playerbias; +.float target_select_time; // last time turret had a valid target +.float target_validate_time; // throttle re-validation of current target + +.float aim_firetolerance_dist; +.float aim_speed; +.float aim_maxpitch; +.float aim_maxrotate; + +.float ammo; // current ammo +.float ammo_recharge; // recharge rate +.float ammo_max; // maximum ammo + +.vector idle_aim; + +/// 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; + +// tracking type +.float track_type; +const float TFL_TRACKTYPE_STEPMOTOR = 1; // hard angle increments, ugly for fast turning with best accuracy +const float TFL_TRACKTYPE_FLUIDPRECISE = 2; // smooth absolute movement, looks OK with fair accuracy +const float TFL_TRACKTYPE_FLUIDINERTIA = 3; // simulated inertia ("wobbly" mode), worst accuracy, depends on below flags +.float track_accel_pitch; +.float track_accel_rotate; +.float track_blendrate; + ++void() turret_respawn; ++ +/// updates aim org, shot org, shot dir and enemy org for selected turret +void turret_do_updates(entity e_turret); +.vector tur_shotdir_updated; + +.float() turret_firecheckfunc; // TODO: deprecate! + +/// Function to use for target evaluation. usualy turret_targetscore_generic +.float(entity _turret, entity _target) turret_score_target; + +.float(entity e_target,entity e_sender) turret_addtarget; + +.entity pathcurrent; + +float turret_count; + +// debugging +// Uncomment below to enable various debug output. +//#define TURRET_DEBUG +//#define TURRET_DEBUG_TARGETVALIDATE +//#define TURRET_DEBUG_TARGETSELECT +#ifdef TURRET_DEBUG +.float tur_debug_dmg_t_h; // total damage that hit something (can be more than tur_debug_dmg_t_f since it should count radius damage) +.float tur_debug_dmg_t_f; // total damage +.float tur_debug_start; // turret initialization time +.float tur_debug_tmr1; // random timer +.float tur_debug_tmr2; // random timer +.float tur_debug_tmr3; // random timer +.vector tur_debug_rvec; // random vector +#endif + +// aiming +vector tvt_thadv; // turret head angle diff vector, updated by a successful call to turret_validate_target +vector tvt_tadv; // turret angle diff vector, updated by a successful call to turret_validate_target +float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target +float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target +float tvt_dist; // turret distance, updated by a successful call to turret_validate_target ++ ++#endif diff --cc qcsrc/common/turrets/turrets.qh index 5491746e9,000000000..b3736aff9 mode 100644,000000..100644 --- a/qcsrc/common/turrets/turrets.qh +++ b/qcsrc/common/turrets/turrets.qh @@@ -1,197 -1,0 +1,202 @@@ ++#ifndef TURRETS_H ++#define TURRETS_H ++ +// turret requests +#define TR_SETUP 1 // (BOTH) setup turret data +#define TR_THINK 2 // (SERVER) logic to run every frame +#define TR_DEATH 3 // (SERVER) called when turret dies +#define TR_PRECACHE 4 // (BOTH) precaches models/sounds used by this turret +#define TR_ATTACK 5 // (SERVER) called when turret attacks +#define TR_CONFIG 6 // (ALL) + +// functions: - entity get_turretinfo(float id); ++entity get_turretinfo(int id); + +// fields: +.entity tur_head; + +// target selection flags - .float target_select_flags; - .float target_validate_flags; - const float TFL_TARGETSELECT_NO = 2; // don't automatically find targets - const float TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets - const float TFL_TARGETSELECT_PLAYERS = 8; // target players - const float TFL_TARGETSELECT_MISSILES = 16; // target projectiles - const float TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events - const float TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection - const float TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range - const float TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates - const float TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered - const float TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates - const float TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets - const float TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range - const float TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles ++.int target_select_flags; ++.int target_validate_flags; ++const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets ++const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets ++const int TFL_TARGETSELECT_PLAYERS = 8; // target players ++const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles ++const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events ++const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection ++const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range ++const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates ++const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered ++const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates ++const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets ++const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range ++const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles + +// aim flags - .float aim_flags; - const float TFL_AIM_NO = 1; // no aiming - const float TFL_AIM_SPLASH = 2; // aim for ground around the target's feet - const float TFL_AIM_LEAD = 4; // try to predict target movement - const float TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading - const float TFL_AIM_ZPREDICT = 16; // predict target's z position at impact - const float TFL_AIM_SIMPLE = 32; // aim at player's current location ++.int aim_flags; ++const int TFL_AIM_NO = 1; // no aiming ++const int TFL_AIM_SPLASH = 2; // aim for ground around the target's feet ++const int TFL_AIM_LEAD = 4; // try to predict target movement ++const int TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading ++const int TFL_AIM_ZPREDICT = 16; // predict target's z position at impact ++const int TFL_AIM_SIMPLE = 32; // aim at player's current location + +// tracking flags - .float track_flags; - const float TFL_TRACK_NO = 2; // don't move head - const float TFL_TRACK_PITCH = 4; // pitch head - const float TFL_TRACK_ROTATE = 8; // rotate head ++.int track_flags; ++const int TFL_TRACK_NO = 2; // don't move head ++const int TFL_TRACK_PITCH = 4; // pitch head ++const int TFL_TRACK_ROTATE = 8; // rotate head + +// prefire checks - .float firecheck_flags; - const float TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?) - const float TFL_FIRECHECK_DISTANCES = 8; // another range check - const float TFL_FIRECHECK_LOS = 16; // line of sight - const float TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot - const float TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint - const float TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot - const float TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates - const float TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire - const float TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt - const float TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max - const float TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays - const float TFL_FIRECHECK_NO = 16384; // no prefire checks ++.int firecheck_flags; ++const int TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?) ++const int TFL_FIRECHECK_DISTANCES = 8; // another range check ++const int TFL_FIRECHECK_LOS = 16; // line of sight ++const int TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot ++const int TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint ++const int TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot ++const int TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates ++const int TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire ++const int TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt ++const int TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max ++const int TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays ++const int TFL_FIRECHECK_NO = 16384; // no prefire checks + +// attack flags - .float shoot_flags; - const float TFL_SHOOT_NO = 64; // no attacking - const float TFL_SHOOT_VOLLY = 2; // fire in vollies - const float TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost - const float TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets - const float TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode) - const float TFL_SHOOT_CUSTOM = 32; // custom attacking ++.int shoot_flags; ++const int TFL_SHOOT_NO = 64; // no attacking ++const int TFL_SHOOT_VOLLY = 2; // fire in vollies ++const int TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost ++const int TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets ++const int TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode) ++const int TFL_SHOOT_CUSTOM = 32; // custom attacking + +// turret capabilities - .float turret_flags; - const float TUR_FLAG_NONE = 0; // no abilities - const float TUR_FLAG_SNIPER = 2; // sniping turret - const float TUR_FLAG_SPLASH = 4; // can deal splash damage - const float TUR_FLAG_HITSCAN = 8; // hit scan - const float TUR_FLAG_MULTIGUN = 16; // multiple guns - const float TUR_FLAG_GUIDED = 32; // laser guided projectiles - const float TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles - const float TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles - const float TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles - const float TUR_FLAG_PLAYER = 512; // can damage players - const float TUR_FLAG_MISSILE = 1024; // can damage missiles - const float TUR_FLAG_SUPPORT = 2048; // supports other units - const float TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition - const float TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources - const float TUR_FLAG_MOVE = 16384; // can move - const float TUR_FLAG_ROAM = 32768; // roams around if not attacking - const float TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret ++.int turret_flags; ++const int TUR_FLAG_NONE = 0; // no abilities ++const int TUR_FLAG_SNIPER = 2; // sniping turret ++const int TUR_FLAG_SPLASH = 4; // can deal splash damage ++const int TUR_FLAG_HITSCAN = 8; // hit scan ++const int TUR_FLAG_MULTIGUN = 16; // multiple guns ++const int TUR_FLAG_GUIDED = 32; // laser guided projectiles ++const int TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles ++const int TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles ++const int TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles ++const int TUR_FLAG_PLAYER = 512; // can damage players ++const int TUR_FLAG_MISSILE = 1024; // can damage missiles ++const int TUR_FLAG_SUPPORT = 2048; // supports other units ++const int TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition ++const int TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources ++const int TUR_FLAG_MOVE = 16384; // can move ++const int TUR_FLAG_ROAM = 32768; // roams around if not attacking ++const int TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret + +// ammo types +#define ammo_flags currentammo - const float TFL_AMMO_NONE = 64; // doesn't use ammo - const float TFL_AMMO_ENERGY = 2; // uses power - const float TFL_AMMO_BULLETS = 4; // uses bullets - const float TFL_AMMO_ROCKETS = 8; // uses explosives - const float TFL_AMMO_RECHARGE = 16; // regenerates ammo - const float TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units ++const int TFL_AMMO_NONE = 64; // doesn't use ammo ++const int TFL_AMMO_ENERGY = 2; // uses power ++const int TFL_AMMO_BULLETS = 4; // uses bullets ++const int TFL_AMMO_ROCKETS = 8; // uses explosives ++const int TFL_AMMO_RECHARGE = 16; // regenerates ammo ++const int TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units + +// damage flags - .float damage_flags; - const float TFL_DMG_NO = 256; // doesn't take damage - const float TFL_DMG_YES = 2; // can be damaged - const float TFL_DMG_TEAM = 4; // can be damaged by teammates - const float TFL_DMG_RETALIATE = 8; // target attackers - const float TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team - const float TFL_DMG_TARGETLOSS = 32; // loses target when damaged - const float TFL_DMG_AIMSHAKE = 64; // damage throws off aim - const float TFL_DMG_HEADSHAKE = 128; // damage shakes head - const float TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning ++.int damage_flags; ++const int TFL_DMG_NO = 256; // doesn't take damage ++const int TFL_DMG_YES = 2; // can be damaged ++const int TFL_DMG_TEAM = 4; // can be damaged by teammates ++const int TFL_DMG_RETALIATE = 8; // target attackers ++const int TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team ++const int TFL_DMG_TARGETLOSS = 32; // loses target when damaged ++const int TFL_DMG_AIMSHAKE = 64; // damage throws off aim ++const int TFL_DMG_HEADSHAKE = 128; // damage shakes head ++const int TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning + +// spawn flags - const float TSF_SUSPENDED = 1; - const float TSF_TERRAINBASE = 2; // currently unused - const float TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration - const float TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible - const float TSL_NO_RESPAWN = 16; // don't re-spawn - const float TSL_ROAM = 32; // roam while idle ++const int TSF_SUSPENDED = 1; ++const int TSF_TERRAINBASE = 2; // currently unused ++const int TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration ++const int TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible ++const int TSL_NO_RESPAWN = 16; // don't re-spawn ++const int TSL_ROAM = 32; // roam while idle + +// send flags - const float TNSF_UPDATE = 2; - const float TNSF_STATUS = 4; - const float TNSF_SETUP = 8; - const float TNSF_ANG = 16; - const float TNSF_AVEL = 32; - const float TNSF_MOVE = 64; ++const int TNSF_UPDATE = 2; ++const int TNSF_STATUS = 4; ++const int TNSF_SETUP = 8; ++const int TNSF_ANG = 16; ++const int TNSF_AVEL = 32; ++const int TNSF_MOVE = 64; +.float anim_start_time; - const float TNSF_ANIM = 128; ++const int TNSF_ANIM = 128; + - const float TNSF_FULL_UPDATE = 16777215; ++const int TNSF_FULL_UPDATE = 16777215; + + +// entity properties of turretinfo: - .float turretid; // TUR_... ++.int turretid; // TUR_... +.string netname; // short name +.string turret_name; // human readable name +.float(float) turret_func; // m_... +.string mdl; // currently a copy of the model +.string model; // full name of model +.string head_model; // full name of tur_head model +.string cvar_basename; // TODO: deprecate! +.float spawnflags; +.vector mins, maxs; // turret hitbox size + +// other useful macros +#define TUR_ACTION(turrettype,mrequest) (get_turretinfo(turrettype)).turret_func(mrequest) +#define TUR_NAME(turrettype) (get_turretinfo(turrettype)).turret_name + +// ===================== +// Turret Registration +// ===================== + +float t_null(float dummy); +void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname); +void register_turrets_done(); + +const float TUR_MAXCOUNT = 24; +#define TUR_FIRST 1 +float TUR_COUNT; +float TUR_LAST; + +#define REGISTER_TURRET_2(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + float id; \ + float func(float); \ + void RegisterTurrets_##id() \ + { \ + TUR_LAST = (id = TUR_FIRST + TUR_COUNT); \ + ++TUR_COUNT; \ + register_turret(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname); \ + } \ + ACCUMULATE_FUNCTION(RegisterTurrets, RegisterTurrets_##id) +#ifdef MENUQC +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,t_null,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#else +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#endif + +#define TUR_DUPECHECK(dupecheck,cvar) \ + #ifndef dupecheck \ + #define dupecheck \ + float cvar; \ + #else \ + #error DUPLICATE TURRET CVAR: cvar \ + #endif + +#define TUR_ADD_CVAR(turret,name) \ + TUR_DUPECHECK(TUR_CVAR_##turret##_##name, autocvar_g_turrets_unit_##turret##_##name) + +#define TUR_CVAR(turret,name) autocvar_g_turrets_unit_##turret##_##name + +#include "all.qh" + +#undef TUR_ADD_CVAR +#undef REGISTER_TURRET +ACCUMULATE_FUNCTION(RegisterTurrets, register_turrets_done); ++ ++#endif diff --cc qcsrc/common/turrets/unit/ewheel.qc index 1c2dc5564,000000000..414ec4e82 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/ewheel.qc +++ b/qcsrc/common/turrets/unit/ewheel.qc @@@ -1,311 -1,0 +1,311 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ EWHEEL, +/* function */ t_ewheel, +/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM, +/* mins,maxs */ '-32 -32 0', '32 32 48', +/* model */ "ewheel-base2.md3", +/* head_model */ "ewheel-gun1.md3", +/* netname */ "ewheel", +/* fullname */ _("eWheel Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_ewheel_speed_fast; +float autocvar_g_turrets_unit_ewheel_speed_slow; +float autocvar_g_turrets_unit_ewheel_speed_slower; +float autocvar_g_turrets_unit_ewheel_speed_stop; +float autocvar_g_turrets_unit_ewheel_turnrate; + +const float ewheel_anim_stop = 0; +const float ewheel_anim_fwd_slow = 1; +const float ewheel_anim_fwd_fast = 2; +const float ewheel_anim_bck_slow = 3; +const float ewheel_anim_bck_fast = 4; + +//#define EWHEEL_FANCYPATH +void ewheel_move_path() +{ +#ifdef EWHEEL_FANCYPATH + // 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; + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; +#endif + + if (self.pathcurrent) + { + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4); + } +} + +void ewheel_move_enemy() +{ + float newframe; + + self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); + + self.moveto = self.origin + self.steerto * 128; + + if (self.tur_dist_enemy > self.target_range_optimal) + { + if ( self.tur_head.spawnshieldtime < 1 ) + { + newframe = ewheel_anim_fwd_fast; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_fast), 0.4); + } + else if (self.tur_head.spawnshieldtime < 2) + { + + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_ewheel_speed_slower), 0.4); + } + } + else if (self.tur_dist_enemy < self.target_range_optimal * 0.5) + { + newframe = ewheel_anim_bck_slow; + movelib_move_simple(v_forward * -1, (autocvar_g_turrets_unit_ewheel_speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_stop; + movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop)); + } + - turrets_setframe(newframe, FALSE); ++ turrets_setframe(newframe, false); +} + +void ewheel_move_idle() +{ + if(self.frame != 0) + { + self.SendFlags |= TNSF_ANIM; + self.anim_start_time = time; + } + + self.frame = 0; + if (vlen(self.velocity)) + movelib_beak_simple((autocvar_g_turrets_unit_ewheel_speed_stop)); +} + +void spawnfunc_turret_ewheel() { if(!turret_initialize(TUR_EWHEEL)) remove(self); } + +float t_ewheel(float req) +{ + switch(req) + { + case TR_ATTACK: + { + float i; + entity _mis; + + for (i = 0; i < 1; ++i) + { + turret_do_updates(self); + + _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE); + _mis.missile_flags = MIF_SPLASH; + + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + self.tur_head.frame += 2; + + if (self.tur_head.frame > 3) + self.tur_head.frame = 0; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + 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); + + fixedmakevectors(self.angles); + + 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); + + if(self.enemy) + ewheel_move_enemy(); + else if(self.pathcurrent) + ewheel_move_path(); + else + ewheel_move_idle(); + + self.velocity_z = vz; + + if(vlen(self.velocity)) + self.SendFlags |= TNSF_MOVE; + - return TRUE; ++ return true; + } + case TR_DEATH: + { + self.velocity = '0 0 0'; + +#ifdef EWHEEL_FANCYPATH + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); +#endif + self.pathcurrent = world; + - return TRUE; ++ return true; + } + case TR_SETUP: + { + entity e; + + if(self.movetype == MOVETYPE_WALK) + { + self.velocity = '0 0 0'; + self.enemy = world; + + 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 + { + +#ifdef EWHEEL_FANCYPATH + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; +#else + self.pathcurrent = e; +#endif + } + } + } + - self.iscreature = TRUE; ++ self.iscreature = true; + self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; ++ self.damagedbycontents = true; + self.movetype = MOVETYPE_WALK; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + self.idle_aim = '0 0 0'; + self.pos1 = self.origin; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.frame = self.tur_head.frame = 1; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + + // Convert from dgr / sec to dgr / tic + self.tur_head.aim_speed = (autocvar_g_turrets_unit_ewheel_turnrate); + self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/ewheel-base2.md3"); + precache_model ("models/turrets/ewheel-gun1.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +void ewheel_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.05) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_ewheel(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = ewheel_draw; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/flac.qc index 8c131e76e,000000000..8068ab1d7 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/flac.qc +++ b/qcsrc/common/turrets/unit/flac.qc @@@ -1,103 -1,0 +1,103 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ FLAC, +/* function */ t_flac, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "flac.md3", +/* netname */ "flac", +/* fullname */ _("FLAC Cannon") +); +#else +#ifdef SVQC +void turret_flac_projectile_think_explode() +{ + if(self.enemy != world) + if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) + setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); + +#ifdef TURRET_DEBUG + float d; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); + self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; + self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; +#else + RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void spawnfunc_turret_flac() { if(!turret_initialize(TUR_FLAC)) remove(self); } + +float t_flac(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity proj; + + turret_tag_fire_update(); + + proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + proj.think = turret_flac_projectile_think_explode; + proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + + self.tur_head.frame = self.tur_head.frame + 1; + if (self.tur_head.frame >= 4) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_THINK: + { - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/flac.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_flac(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/fusionreactor.qc index d2a3c408e,000000000..db4974566 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/fusionreactor.qc +++ b/qcsrc/common/turrets/unit/fusionreactor.qc @@@ -1,116 -1,0 +1,116 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ FUSIONREACTOR, +/* function */ t_fusionreactor, +/* spawnflags */ TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE, +/* mins,maxs */ '-34 -34 0', '34 34 90', +/* model */ "base.md3", +/* head_model */ "reactor.md3", +/* netname */ "fusionreactor", +/* fullname */ _("Fusion Reactor") +); +#else +#ifdef SVQC +float turret_fusionreactor_firecheck() +{ + if (self.attack_finished_single > time) + return 0; + + if (self.enemy.deadflag != DEAD_NO) + return 0; + + if (self.enemy == world) + return 0; + + if (self.ammo < self.shot_dmg) + return 0; + + if (self.enemy.ammo >= self.enemy.ammo_max) + return 0; + + if (vlen(self.enemy.origin - self.origin) > self.target_range) + return 0; + + if(self.team != self.enemy.team) + return 0; + + if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY)) + return 0; + + return 1; +} + +void spawnfunc_turret_fusionreactor() { if(!turret_initialize(TUR_FUSIONREACTOR)) remove(self); } + +float t_fusionreactor(float req) +{ + switch(req) + { + case TR_ATTACK: + { + vector fl_org; + + self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max); + fl_org = 0.5 * (self.enemy.absmin + self.enemy.absmax); + te_smallflash(fl_org); + - return TRUE; ++ return true; + } + case TR_THINK: + { + self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max); + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; + self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMITS; + self.firecheck_flags = TFL_FIRECHECK_AMMO_OWN | TFL_FIRECHECK_AMMO_OTHER | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD; + self.shoot_flags = TFL_SHOOT_HITALLVALID; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + + self.tur_head.scale = 0.75; + self.tur_head.avelocity = '0 50 0'; + + self.turret_firecheckfunc = turret_fusionreactor_firecheck; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/reactor.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_fusionreactor(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hellion.qc index 57dca3b79,000000000..d090ef7ce mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/hellion.qc +++ b/qcsrc/common/turrets/unit/hellion.qc @@@ -1,160 -1,0 +1,160 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ HELLION, +/* function */ t_hellion, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "hellion.md3", +/* netname */ "hellion", +/* fullname */ _("Hellion Missile Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_hellion_shot_speed_gain; +float autocvar_g_turrets_unit_hellion_shot_speed_max; + +void turret_hellion_missile_think() +{ + vector olddir,newdir; + vector pre_pos; + float itime; + + self.nextthink = time + 0.05; + + olddir = normalize(self.velocity); + + if(self.max_health < time) + turret_projectile_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_projectile_explode(); + + // Accelerate + self.velocity = olddir * min(vlen(self.velocity) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max)); + + UpdateCSQCProjectile(self); + + return; + } + + // Enemy in range? + if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) + turret_projectile_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; + + // 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) * (autocvar_g_turrets_unit_hellion_shot_speed_gain), (autocvar_g_turrets_unit_hellion_shot_speed_max)); + + if (itime < 0.05) + self.think = turret_projectile_explode; + + UpdateCSQCProjectile(self); +} + +void spawnfunc_turret_hellion() { if(!turret_initialize(TUR_HELLION)) remove(self); } + +float t_hellion(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + if(self.tur_head.frame != 0) + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire")); + else + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire2")); + + missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_HELLION, PROJECTILE_ROCKET, FALSE, FALSE); + te_explosion (missile.origin); + missile.think = turret_hellion_missile_think; + missile.nextthink = time; + missile.flags = FL_PROJECTILE; + missile.max_health = time + 9; + missile.tur_aimpos = randomvec() * 128; + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; + self.tur_head.frame += 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + self.tur_head.frame += 1; + + if (self.tur_head.frame >= 7) + self.tur_head.frame = 0; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.aim_flags = TFL_AIM_SIMPLE; + self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK ; + self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_AMMO_OWN; + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hellion.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hellion(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hk.qc index c0f55fa0f,000000000..414c71e4b mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/hk.qc +++ b/qcsrc/common/turrets/unit/hk.qc @@@ -1,361 -1,0 +1,361 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ HK, +/* function */ t_hk, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "hk.md3", +/* netname */ "hk", +/* fullname */ _("Hunter-Killer Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_hk_shot_speed; +float autocvar_g_turrets_unit_hk_shot_speed_accel; +float autocvar_g_turrets_unit_hk_shot_speed_accel2; +float autocvar_g_turrets_unit_hk_shot_speed_decel; +float autocvar_g_turrets_unit_hk_shot_speed_max; +float autocvar_g_turrets_unit_hk_shot_speed_turnrate; + +//#define TURRET_DEBUG_HK + +#ifdef TURRET_DEBUG_HK +.float atime; +#endif + +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 (IS_CLIENT(e_target)) + { + 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_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_projectile_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); ++ traceline(self.origin, pre_pos,true,self.enemy); + ve = normalize(pre_pos - self.origin); + fe = trace_fraction; + + } + else + { + edist = 0; + ve = '0 0 0'; + 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); ++ 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 > (autocvar_g_turrets_unit_hk_shot_speed)) ) + myspeed = max(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_decel), (autocvar_g_turrets_unit_hk_shot_speed)); + + // Failry clear, accelerate. + if ( (ff > 0.7) && (myspeed < (autocvar_g_turrets_unit_hk_shot_speed_max)) ) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel), (autocvar_g_turrets_unit_hk_shot_speed_max)); + + // 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); ++ 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); ++ 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); ++ 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); ++ 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 < (autocvar_g_turrets_unit_hk_shot_speed_max)) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max)); + + wishdir = ve; + } + + if ((myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) && (self.cnt > time)) + myspeed = min(myspeed * (autocvar_g_turrets_unit_hk_shot_speed_accel2),(autocvar_g_turrets_unit_hk_shot_speed_max)); + + // Ranoutagazfish? + if (self.cnt < time) + { + self.cnt = time + 0.25; + self.nextthink = 0; + self.movetype = MOVETYPE_BOUNCE; + return; + } + + // Calculate new heading + olddir = normalize(self.velocity); + newdir = normalize(olddir + wishdir * (autocvar_g_turrets_unit_hk_shot_speed_turnrate)); + + // 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); +} + +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; +} + +void spawnfunc_turret_hk() { if(!turret_initialize(TUR_HK)) remove(self); } + +float t_hk(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_HK, PROJECTILE_ROCKET, FALSE, FALSE); + te_explosion (missile.origin); + + missile.think = turret_hk_missile_think; + 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.cnt = time + 30; + missile.ticrate = max(autocvar_sys_ticrate, 0.05); + missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_AI; + + if (self.tur_head.frame == 0) + self.tur_head.frame = self.tur_head.frame + 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + 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; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + 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_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; + self.shoot_flags = TFL_SHOOT_CLEARTARGET; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; + + self.turret_addtarget = turret_hk_addtarget; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hk.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hk(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/machinegun.qc index 26cbc4882,000000000..f4a46212f mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/machinegun.qc +++ b/qcsrc/common/turrets/unit/machinegun.qc @@@ -1,79 -1,0 +1,79 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ MACHINEGUN, +/* function */ t_machinegun, +/* spawnflags */ TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "machinegun.md3", +/* netname */ "machinegun", +/* fullname */ _("Machinegun Turret") +); +#else +#ifdef SVQC +void spawnfunc_turret_machinegun() { if(!turret_initialize(TUR_MACHINEGUN)) remove(self); } + +float t_machinegun(float req) +{ + switch(req) + { + case TR_ATTACK: + { + fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0); + + W_MachineGun_MuzzleFlash(); + setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); + - return TRUE; ++ return true; + } + case TR_THINK: + { - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + self.turret_flags |= TUR_FLAG_HITSCAN; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/machinegun.md3"); + precache_sound ("weapons/uzi_fire.wav"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_machinegun(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/mlrs.qc index fa3c30efc,000000000..254a58948 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/mlrs.qc +++ b/qcsrc/common/turrets/unit/mlrs.qc @@@ -1,90 -1,0 +1,90 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ MLRS, +/* function */ t_mlrs, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "mlrs.md3", +/* netname */ "mlrs", +/* fullname */ _("MLRS Turret") +); +#else +#ifdef SVQC +void spawnfunc_turret_mlrs() { if(!turret_initialize(TUR_MLRS)) remove(self); } + +float t_mlrs(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + turret_tag_fire_update(); + missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE); + missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); + missile.missile_flags = MIF_SPLASH; + te_explosion (missile.origin); + - return TRUE; ++ return true; + } + case TR_THINK: + { + // 0 = full, 6 = empty + self.tur_head.frame = bound(0, 6 - floor(0.1 + self.ammo / self.shot_dmg), 6); + if(self.tur_head.frame < 0) + { + dprint("ammo:",ftos(self.ammo),"\n"); + dprint("shot_dmg:",ftos(self.shot_dmg),"\n"); + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; + self.volly_counter = self.shot_volly; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/mlrs.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_mlrs(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/phaser.qc index 441e041c7,000000000..32c392b66 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/phaser.qc +++ b/qcsrc/common/turrets/unit/phaser.qc @@@ -1,173 -1,0 +1,173 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PHASER, +/* function */ t_phaser, +/* spawnflags */ TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "phaser.md3", +/* netname */ "phaser", +/* fullname */ _("Phaser Cannon") +); +#else +#ifdef SVQC +.float fireflag; + +float turret_phaser_firecheck() +{ + if (self.fireflag != 0) return 0; + return turret_firecheck(); +} + +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, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); + remove(self); + return; + } + + turret_do_updates(self.owner); + + if (time - self.shot_spread > 0) + { + self.shot_spread = time + 2; + sound (self, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM); + } + + + self.nextthink = time + self.ticrate; + + self.owner.attack_finished_single = time + frametime; + entity oldself; + oldself = self; + self = self.owner; + 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_PHASER); + self = oldself; + self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256; + +} + +void spawnfunc_turret_phaser() { if(!turret_initialize(TUR_PHASER)) remove(self); } + +float t_phaser(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity beam; + + beam = spawn(); + beam.ticrate = 0.1; //autocvar_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_dodge = true; + beam.bot_dodgerating = beam.shot_dmg; + sound (beam, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM); + self.fireflag = 1; + + beam.attack_finished_single = self.attack_finished_single; + self.attack_finished_single = time; // + autocvar_sys_ticrate; + + setattachment(beam,self.tur_head,"tag_fire"); + + soundat (self, trace_endpos, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + - return TRUE; ++ return true; + } + case TR_THINK: + { + if (self.tur_head.frame != 0) + { + 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; + } + } + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD; + + self.turret_firecheckfunc = turret_phaser_firecheck; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/phaser.md3"); + precache_model ("models/turrets/phaser_beam.md3"); + precache_sound ("turrets/phaser.wav"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_phaser(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma.qc index ac623371f,000000000..095aa9d02 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/plasma.qc +++ b/qcsrc/common/turrets/unit/plasma.qc @@@ -1,111 -1,0 +1,111 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PLASMA, +/* function */ t_plasma, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "plasma.md3", +/* netname */ "plasma", +/* fullname */ _("Plasma Cannon") +); +#else +#ifdef SVQC +void spawnfunc_turret_plasma() { if(!turret_initialize(TUR_PLASMA)) remove(self); } + +float t_plasma(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_instagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + string s; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + s = strcat("TE_TEI_G3", ((self.team) ? Static_Team_ColorName_Upper(self.team) : "")); + + WarpZone_TrailParticles(world, particleeffectnum(s), self.tur_shotorg, v); + + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + } + else + { + entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + missile.missile_flags = MIF_SPLASH; + + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + 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; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.firecheck_flags |= TFL_FIRECHECK_AFF; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH; + + turret_do_updates(self); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasma.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma_dual.qc index 279b41ec4,000000000..110ae1dea mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/plasma_dual.qc +++ b/qcsrc/common/turrets/unit/plasma_dual.qc @@@ -1,109 -1,0 +1,109 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ PLASMA_DUAL, +/* function */ t_plasma_dual, +/* spawnflags */ TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER, +/* mins,maxs */ '-32 -32 0', '32 32 64', +/* model */ "base.md3", +/* head_model */ "plasmad.md3", +/* netname */ "plasma_dual", +/* fullname */ _("Dual Plasma Cannon") +); +#else +#ifdef SVQC +void spawnfunc_turret_plasma_dual() { if(!turret_initialize(TUR_PLASMA_DUAL)) remove(self); } + +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_instagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + + pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + string s; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + s = strcat("TE_TEI_G3", ((self.team) ? Static_Team_ColorName_Upper(self.team) : "")); + + WarpZone_TrailParticles(world, particleeffectnum(s), self.tur_shotorg, v); + + self.tur_head.frame += 1; + } + else + { + entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); + missile.missile_flags = MIF_SPLASH; + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + self.tur_head.frame += 1; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + 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; + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.damage_flags |= TFL_DMG_HEADSHAKE; + self.firecheck_flags |= TFL_FIRECHECK_AFF; + self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_SPLASH; + + turret_do_updates(self); + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasmad.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/tesla.qc index 2878c3187,000000000..61a9f5e30 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/tesla.qc +++ b/qcsrc/common/turrets/unit/tesla.qc @@@ -1,217 -1,0 +1,217 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ TESLA, +/* function */ t_tesla, +/* spawnflags */ TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE, +/* mins,maxs */ '-60 -60 0', '60 60 128', +/* model */ "tesla_base.md3", +/* head_model */ "tesla_head.md3", +/* netname */ "tesla", +/* fullname */ _("Tesla Coil") +); +#else +#ifdef SVQC +entity toast(entity from, float range, float damage) +{ + entity e; + entity etarget = world; + float d,dd; + float r; + + dd = range + 1; + + e = findradius(from.origin,range); + while (e) + { + if ((e.railgunhit != 1) && (e != from)) + { + r = turret_validate_target(self,e,self.target_validate_flags); + if (r > 0) + { + traceline(from.origin,0.5 * (e.absmin + e.absmax),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_csqc_lightningarc(from.origin,etarget.origin); + Damage(etarget, self, self, damage, DEATH_TURRET_TESLA, etarget.origin, '0 0 0'); + etarget.railgunhit = 1; + } + + return etarget; +} + +float turret_tesla_firecheck() +{ + // g_turrets_targetscan_maxdelay forces a target re-scan at least this often + float do_target_scan = 0; + + if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) + do_target_scan = 1; + + // Old target (if any) invalid? + if(self.target_validate_time < time) + if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) + { + self.enemy = world; + self.target_validate_time = time + 0.5; + do_target_scan = 1; + } + + // But never more often then g_turrets_targetscan_mindelay! + if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) + do_target_scan = 0; + + if(do_target_scan) + { + self.enemy = turret_select_target(); + self.target_select_time = time; + } + + if(!turret_firecheck()) + return 0; + + if(self.enemy) + return 1; + + return 0; +} + +void spawnfunc_turret_tesla() { if(!turret_initialize(TUR_TESLA)) remove(self); } + +float t_tesla(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity e, t; + float d, r, i; + + 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_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + t = toast(e,r,d); + remove(e); + - if (t == world) return TRUE; ++ if (t == world) return true; + + 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.75; + r *= 0.85; + t = toast(t, r, d); + if (t == world) break; + + } + + e = findchainfloat(railgunhit, 1); + while (e) + { + e.railgunhit = 0; + e = e.chain; + } + - return TRUE; ++ return true; + } + case TR_THINK: + { + if(!self.active) + { + self.tur_head.avelocity = '0 0 0'; - return TRUE; ++ return true; + } + + if(self.ammo < self.shot_dmg) + { + self.tur_head.avelocity = '0 45 0' * (self.ammo / self.shot_dmg); + } + else + { + self.tur_head.avelocity = '0 180 0' * (self.ammo / self.shot_dmg); + + if(self.attack_finished_single > time) - return TRUE; ++ return true; + + 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)); + } + - return TRUE; ++ return true; + } + case TR_DEATH: + { - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + self.turret_firecheckfunc = turret_tesla_firecheck; + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | + TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + + self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AMMO_OWN; + self.shoot_flags = TFL_SHOOT_CUSTOM; + self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_NO; + self.track_flags = TFL_TRACK_NO; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/tesla_base.md3"); + precache_model ("models/turrets/tesla_head.md3"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_tesla(float req) +{ + switch(req) + { + case TR_SETUP: + { - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/walker.qc index fa89d5a3e,000000000..0d5d31705 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/walker.qc +++ b/qcsrc/common/turrets/unit/walker.qc @@@ -1,696 -1,0 +1,698 @@@ +#ifdef REGISTER_TURRET +REGISTER_TURRET( +/* TUR_##id */ WALKER, +/* function */ t_walker, +/* spawnflags */ TUR_FLAG_PLAYER | TUR_FLAG_MOVE, +/* mins,maxs */ '-70 -70 0', '70 70 95', +/* model */ "walker_body.md3", +/* head_model */ "walker_head_minigun.md3", +/* netname */ "walker", +/* fullname */ _("Walker Turret") +); +#else +#ifdef SVQC +float autocvar_g_turrets_unit_walker_melee_damage; +float autocvar_g_turrets_unit_walker_melee_force; +float autocvar_g_turrets_unit_walker_melee_range; +float autocvar_g_turrets_unit_walker_rocket_damage; +float autocvar_g_turrets_unit_walker_rocket_radius; +float autocvar_g_turrets_unit_walker_rocket_force; +float autocvar_g_turrets_unit_walker_rocket_speed; +float autocvar_g_turrets_unit_walker_rocket_range; +float autocvar_g_turrets_unit_walker_rocket_range_min; +float autocvar_g_turrets_unit_walker_rocket_refire; +float autocvar_g_turrets_unit_walker_rocket_turnrate; +float autocvar_g_turrets_unit_walker_speed_stop; +float autocvar_g_turrets_unit_walker_speed_walk; +float autocvar_g_turrets_unit_walker_speed_run; +float autocvar_g_turrets_unit_walker_speed_jump; +float autocvar_g_turrets_unit_walker_speed_swim; +float autocvar_g_turrets_unit_walker_speed_roam; +float autocvar_g_turrets_unit_walker_turn; +float autocvar_g_turrets_unit_walker_turn_walk; +float autocvar_g_turrets_unit_walker_turn_strafe; +float autocvar_g_turrets_unit_walker_turn_swim; +float autocvar_g_turrets_unit_walker_turn_run; + +#define ANIM_NO 0 +#define ANIM_TURN 1 +#define ANIM_WALK 2 +#define ANIM_RUN 3 +#define ANIM_STRAFE_L 4 +#define ANIM_STRAFE_R 5 +#define ANIM_JUMP 6 +#define ANIM_LAND 7 +#define ANIM_PAIN 8 +#define ANIM_MELEE 9 +#define ANIM_SWIM 10 +#define ANIM_ROAM 11 + +.float animflag; +.float idletime; + +#define WALKER_PATH(s,e) pathlib_astar(s,e) + +float walker_firecheck() +{ + if (self.animflag == ANIM_MELEE) + return 0; + + return turret_firecheck(); +} + +void walker_melee_do_dmg() +{ + vector where; + entity e; + + makevectors(self.angles); + where = self.origin + v_forward * 128; + + e = findradius(where,32); + while (e) + { + if (turret_validate_target(self, e, self.target_validate_flags)) + if (e != self && e.owner != self) + Damage(e, self, self, (autocvar_g_turrets_unit_walker_melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * (autocvar_g_turrets_unit_walker_melee_force)); + + e = e.chain; + } +} + +void walker_setnoanim() +{ - turrets_setframe(ANIM_NO, FALSE); ++ turrets_setframe(ANIM_NO, false); + self.animflag = self.frame; +} +void walker_rocket_explode() +{ + RadiusDamage (self, self.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), self, world, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET, 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) + W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); +} + +#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, (autocvar_g_turrets_unit_walker_rocket_speed), (autocvar_g_turrets_unit_walker_rocket_turnrate)); 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 < 128) + self.tur_shotorg = '0 0 0'; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + self.nextthink = time; + return; + } + + if (self.shot_dmg != 1337 && random() < 0.01) + { + walker_rocket_loop(); + return; + } + + m_speed = vlen(self.velocity); + + // Enemy dead? just keep on the current heading then. + if (self.enemy == world || self.enemy.deadflag != DEAD_NO) + 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.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + 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.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + 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 300'; + self.think = walker_rocket_loop2; + self.shot_dmg = 1337; +} + +void walker_fire_rocket(vector org) +{ + entity rocket; + + fixedmakevectors(self.angles); + + te_explosion (org); + + rocket = spawn (); + setorigin(rocket, org); + + sound (self, CH_WEAPON_A, "weapons/hagar_fire.wav", VOL_BASE, ATTEN_NORM); + setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + rocket.classname = "walker_rocket"; + rocket.owner = self; - rocket.bot_dodge = TRUE; ++ rocket.bot_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; + rocket.movetype = MOVETYPE_FLY; + rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed); + rocket.angles = vectoangles(rocket.velocity); + rocket.touch = walker_rocket_explode; + rocket.flags = FL_PROJECTILE; + rocket.solid = SOLID_BBOX; + rocket.max_health = time + 9; + rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; + - CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound ++ CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound +} + +.vector enemy_last_loc; +.float enemy_last_time; +void walker_move_to(vector _target, float _dist) +{ + switch (self.waterlevel) + { + case WATERLEVEL_NONE: + if (_dist > 500) + self.animflag = ANIM_RUN; + else + self.animflag = ANIM_WALK; + case WATERLEVEL_WETFEET: + case WATERLEVEL_SWIMMING: + if (self.animflag != ANIM_SWIM) + self.animflag = ANIM_WALK; + else + self.animflag = ANIM_SWIM; + break; + case WATERLEVEL_SUBMERGED: + self.animflag = ANIM_SWIM; + } + + self.moveto = _target; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + if(self.enemy) + { + self.enemy_last_loc = _target; + self.enemy_last_time = time; + } +} + +//#define WALKER_FANCYPATHING + +void walker_move_path() +{ +#ifdef WALKER_FANCYPATHING + // 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; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + walker_move_to(self.moveto, 0); + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; + + if(!self.pathcurrent) + return; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + walker_move_to(self.moveto, 0); +#endif +} + +void spawnfunc_turret_walker() { if(!turret_initialize(TUR_WALKER)) remove(self); } + +float t_walker(float req) +{ + switch(req) + { + case TR_ATTACK: + { + sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM); + fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0); + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + - return TRUE; ++ return true; + } + case TR_THINK: + { + fixedmakevectors(self.angles); + + if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent) + walker_move_path(); + else if (self.enemy == world) + { + if(self.pathcurrent) + walker_move_path(); + else + { + if(self.enemy_last_time != 0) + { + if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10) + self.enemy_last_time = 0; + else + walker_move_to(self.enemy_last_loc, 0); + } + else + { + if(self.animflag != ANIM_NO) + { + traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self); + + if(trace_fraction != 1.0) + self.tur_head.idletime = -1337; + else + { + traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self); + if(trace_fraction == 1.0) + self.tur_head.idletime = -1337; + } + + if(self.tur_head.idletime == -1337) + { + self.moveto = self.origin + randomvec() * 256; + self.tur_head.idletime = 0; + } + + self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1; + self.moveto_z = self.origin_z + 64; + walker_move_to(self.moveto, 0); + } + + if(self.idletime < time) + { + if(random() < 0.5 || !(self.spawnflags & TSL_ROAM)) + { + self.idletime = time + 1 + random() * 5; + self.moveto = self.origin; + self.animflag = ANIM_NO; + } + else + { + self.animflag = ANIM_WALK; + self.idletime = time + 4 + random() * 2; + self.moveto = self.origin + randomvec() * 256; + self.tur_head.moveto = self.moveto; + self.tur_head.idletime = 0; + } + } + } + } + } + else + { + if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_melee_range) && self.animflag != ANIM_MELEE) + { + vector wish_angle; + + wish_angle = angleofs(self, self.enemy); + if (self.animflag != ANIM_SWIM) + if (fabs(wish_angle_y) < 15) + { + self.moveto = self.enemy.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + self.animflag = ANIM_MELEE; + } + } + else if (self.tur_head.attack_finished_single < time) + { + if(self.tur_head.shot_volly) + { + self.animflag = ANIM_NO; + + self.tur_head.shot_volly = self.tur_head.shot_volly -1; + if(self.tur_head.shot_volly == 0) + self.tur_head.attack_finished_single = time + (autocvar_g_turrets_unit_walker_rocket_refire); + else + self.tur_head.attack_finished_single = time + 0.2; + + if(self.tur_head.shot_volly > 1) + walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01"))); + else + walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02"))); + } + else + { + if (self.tur_dist_enemy > (autocvar_g_turrets_unit_walker_rocket_range_min)) + if (self.tur_dist_enemy < (autocvar_g_turrets_unit_walker_rocket_range)) + self.tur_head.shot_volly = 4; + } + } + else + { + if (self.animflag != ANIM_MELEE) + walker_move_to(self.enemy.origin, self.tur_dist_enemy); + } + } + + { + vector real_angle; + float turny = 0, turnx = 0; + float vz; + + real_angle = vectoangles(self.steerto) - self.angles; + vz = self.velocity_z; + + switch (self.animflag) + { + case ANIM_NO: + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_TURN: + turny = (autocvar_g_turrets_unit_walker_turn); + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_WALK: + turny = (autocvar_g_turrets_unit_walker_turn_walk); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_walk), 0.6); + break; + + case ANIM_RUN: + turny = (autocvar_g_turrets_unit_walker_turn_run); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_run), 0.6); + break; + + case ANIM_STRAFE_L: + turny = (autocvar_g_turrets_unit_walker_turn_strafe); + movelib_move_simple(v_right * -1, (autocvar_g_turrets_unit_walker_speed_walk), 0.8); + break; + + case ANIM_STRAFE_R: + turny = (autocvar_g_turrets_unit_walker_turn_strafe); + movelib_move_simple(v_right, (autocvar_g_turrets_unit_walker_speed_walk), 0.8); + break; + + case ANIM_JUMP: + self.velocity += '0 0 1' * (autocvar_g_turrets_unit_walker_speed_jump); + break; + + case ANIM_LAND: + break; + + case ANIM_PAIN: + if(self.frame != ANIM_PAIN) + defer(0.25, walker_setnoanim); + + break; + + case ANIM_MELEE: + if(self.frame != ANIM_MELEE) + { + defer(0.41, walker_setnoanim); + defer(0.21, walker_melee_do_dmg); + } + + movelib_beak_simple((autocvar_g_turrets_unit_walker_speed_stop)); + break; + + case ANIM_SWIM: + turny = (autocvar_g_turrets_unit_walker_turn_swim); + turnx = (autocvar_g_turrets_unit_walker_turn_swim); + + self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10); + movelib_move_simple(v_forward, (autocvar_g_turrets_unit_walker_speed_swim), 0.3); + vz = self.velocity_z + sin(time * 4) * 8; + break; + + case ANIM_ROAM: + turny = (autocvar_g_turrets_unit_walker_turn_walk); + movelib_move_simple(v_forward ,(autocvar_g_turrets_unit_walker_speed_roam), 0.5); + break; + } + + if(turny) + { + turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny ); + self.angles_y += turny; + } + + if(turnx) + { + turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx ); + self.angles_x += turnx; + } + + self.velocity_z = vz; + } + + + if(self.origin != self.oldorigin) + self.SendFlags |= TNSF_MOVE; + + self.oldorigin = self.origin; - turrets_setframe(self.animflag, FALSE); ++ turrets_setframe(self.animflag, false); + - return TRUE; ++ return true; + } + case TR_DEATH: + { +#ifdef WALKER_FANCYPATHING + if (self.pathcurrent) + pathlib_deletepath(self.pathcurrent.owner); +#endif + self.pathcurrent = world; + - return TRUE; ++ return true; + } + case TR_SETUP: + { + self.ticrate = 0.05; + + entity e; + + // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. + if(self.movetype == MOVETYPE_WALK) + { + if(self.pos1) + setorigin(self, self.pos1); + if(self.pos2) + self.angles = self.pos2; + } + + self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIEVE; + self.aim_flags = TFL_AIM_LEAD; + self.turret_flags |= TUR_FLAG_HITSCAN; + + self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; + self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.iscreature = TRUE; ++ self.iscreature = true; + self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; ++ self.damagedbycontents = true; + self.solid = SOLID_SLIDEBOX; + self.takedamage = DAMAGE_AIM; + if(self.movetype != MOVETYPE_WALK) + { + setorigin(self, self.origin); + tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self); + setorigin(self, trace_endpos + '0 0 4'); + self.pos1 = self.origin; + self.pos2 = self.angles; + } + self.movetype = MOVETYPE_WALK; + self.idle_aim = '0 0 0'; + self.turret_firecheckfunc = walker_firecheck; + + 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 + { +#ifdef WALKER_FANCYPATHING + self.pathcurrent = WALKER_PATH(self.origin, e.origin); + self.pathgoal = e; +#else + self.pathcurrent = e; +#endif + } + } + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/walker_body.md3"); + precache_model ("models/turrets/walker_head_minigun.md3"); + precache_model ("models/turrets/rocket.md3"); + precache_sound ("weapons/rocket_impact.wav"); - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // SVQC +#ifdef CSQC + ++#include "../../../server/movelib.qh" ++ +void walker_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + movelib_groundalign4point(300, 100, 0.25, 45); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.15) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_walker(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = walker_draw; + - return TRUE; ++ return true; + } + case TR_PRECACHE: + { - return TRUE; ++ return true; + } + } + - return TRUE; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/util.qc index 6de59027e,000000000..3d6d95a76 mode 100644,000000..100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@@ -1,331 -1,0 +1,330 @@@ +/* +* Return a angle within +/- 360. +*/ +float anglemods(float v) +{ + v = v - 360 * floor(v / 360); + + if(v >= 180) + return v - 360; + else if(v <= -180) + return v + 360; + else + return v; +} + +/* +* Return the short angle +*/ +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 = '0 0 0'; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + + return vtmp; +} + + +/* +* Get "real" origin, in worldspace, even if ent is attached to something else. +*/ +vector real_origin(entity ent) +{ + entity e; + vector v = ((ent.absmin + ent.absmax) * 0.5); + + e = ent.tag_entity; + while(e) + { + v = v + ((e.absmin + e.absmax) * 0.5); + e = e.tag_entity; + } + + return v; +} + +/* +* Return the angle between two enteties +*/ +vector angleofs(entity from, entity to) +{ + vector v_res; + + 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 angleofs3(vector from, vector from_a, entity to) +{ + vector v_res; + + 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; +} + +/* +* Update self.tur_shotorg by getting up2date bone info +* NOTICE this func overwrites the global v_forward, v_right and v_up vectors. +*/ - #define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward) - float turret_tag_fire_update_s() ++float turret_tag_fire_update() +{ + if(!self.tur_head) + { + error("Call to turret_tag_fire_update with self.tur_head missing!\n"); + self.tur_shotorg = '0 0 0'; - return FALSE; ++ return false; + } + + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire")); + v_forward = normalize(v_forward); + - return TRUE; ++ return true; +} + +/* +* Railgun-like beam, but has thickness and suppots slowing of target +*/ +void FireImoBeam (vector start, vector end, vector smin, vector smax, + float bforce, float f_dmg, float f_velfactor, float deathtype) + +{ + vector hitloc, force, endpoint, dir; + 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); ++ 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.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); ++ ent = findfloat(world, railgunhit, true); + while (ent) + { + // restore their solid type + ent.solid = ent.railgunhitsolidbackup; - ent = findfloat(ent, railgunhit, TRUE); ++ ent = findfloat(ent, railgunhit, true); + } + + // find all the entities the railgun hit and hurt them - ent = findfloat(world, railgunhit, TRUE); ++ 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; ++ 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); ++ ent = findfloat(ent, railgunhit, true); + } + trace_endpos = endpoint; +} + +#ifdef TURRET_DEBUG +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); +} +#endif diff --cc qcsrc/common/turrets/util.qh index 000000000,000000000..71ba0662e new file mode 100644 --- /dev/null +++ b/qcsrc/common/turrets/util.qh @@@ -1,0 -1,0 +1,13 @@@ ++#ifndef TURRETS_UTIL_H ++#define TURRETS_UTIL_H ++ ++vector real_origin(entity ent); ++float shortangle_f(float ang1, float ang2); ++float anglemods(float v); ++float turret_tag_fire_update(); ++vector shortangle_vxy(vector ang1, vector ang2); ++vector angleofs(entity from, entity to); ++vector angleofs3(vector from, vector from_a, entity to); ++void FireImoBeam (vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype); ++ ++#endif diff --cc qcsrc/server/g_damage.qc index 3f5ba0d62,d170cf4c0..aab2f9ced --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@@ -1,7 -1,31 +1,32 @@@ - .float dmg; - .float dmg_edge; - .float dmg_force; - .float dmg_radius; + #include "g_damage.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../warpzonelib/common.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" ++ #include "../common/turrets/turrets.qh" ++ #include "../common/turrets/sv_turrets.qh" + #include "vehicles/vehicles_def.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "spawnpoints.qh" + #endif float Damage_DamageInfo_SendEntity(entity to, float sf) { diff --cc qcsrc/server/g_damage.qh index 000000000,4cc3f6f63..68f9a0a38 mode 000000,100644..100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@@ -1,0 -1,123 +1,122 @@@ + #ifndef G_DAMAGE_H + #define G_DAMAGE_H + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../warpzonelib/common.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" + #include "vehicles/vehicles_def.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "spawnpoints.qh" + #endif + + .float dmg; + .float dmg_edge; + .float dmg_force; + .float dmg_radius; + + float Damage_DamageInfo_SendEntity(entity to, float sf); + + void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad, vector force, float deathtype, float bloodtype, entity dmgowner); + + float checkrules_firstblood; + + float yoda; + float damage_goodhits; + float damage_gooddamage; + + .float dmg_team; + .float teamkill_complain; + .float teamkill_soundtime; + .entity teamkill_soundsource; + .entity pusher; + .float istypefrag; + .float taunt_soundtime; + + float IsFlying(entity a); + + void UpdateFrags(entity player, float f); + + // NOTE: f=0 means still count as a (positive) kill, but count no frags for it + void W_SwitchWeapon_Force(entity e, float w); + entity GiveFrags_randomweapons; + void GiveFrags (entity attacker, entity targ, float f, float deathtype); + + string AppendItemcodes(string s, entity player); + + void LogDeath(string mode, float deathtype, entity killer, entity killed); + + void Obituary_SpecialDeath( + entity notif_target, + float murder, + float deathtype, + string s1, string s2, string s3, + float f1, float f2, float f3); + + float w_deathtype; + float Obituary_WeaponDeath( + entity notif_target, + float murder, + float deathtype, + string s1, string s2, string s3, + float f1, float f2); + + void Obituary(entity attacker, entity inflictor, entity targ, float deathtype); + + void Ice_Think(); + + void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint); + + void Unfreeze (entity targ); + + // these are updated by each Damage call for use in button triggering and such + entity damage_targ; + entity damage_inflictor; + entity damage_attacker; + + void Damage (entity targ, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); + + float RadiusDamage_running; + float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float deathtype, entity directhitentity); + // Returns total damage applies to creatures + + float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, float deathtype, entity directhitentity); + + .float fire_damagepersec; + .float fire_endtime; + .float fire_deathtype; + .entity fire_owner; + .float fire_hitsound; + .entity fire_burner; + + void fireburner_think(); + + float Fire_IsBurning(entity e); + + float Fire_AddDamage(entity e, entity o, float d, float t, float dt); + + void Fire_ApplyDamage(entity e); + + void Fire_ApplyEffect(entity e); + + void fireburner_think(); + #endif diff --cc qcsrc/server/g_world.qc index dc5d9d6e3,04f8ff7b3..24a3d211c --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@@ -1,4 -1,41 +1,42 @@@ - #define LATENCY_THINKRATE 10 + #include "g_world.qh" + + #include "../common/buffs.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../common/constants.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/monsters/sv_monsters.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/weaponstats.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "mutators/mutators_include.qh" + #include "campaign.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "command/vote.qh" + #include "command/getreplies.qh" + #include "command/sv_cmd.qh" + #include "anticheat.qh" + #include "cheats.qh" ++ #include "../common/turrets/turrets.qh" + #include "../common/playerstats.qh" + #include "g_hook.qh" + #include "scores.qh" + #include "mapvoting.qh" + #include "ipban.qh" + #include "race.qh" + #include "antilag.qh" + #include "secret.qh" + #endif + + const float LATENCY_THINKRATE = 10; .float latency_sum; .float latency_cnt; .float latency_time; diff --cc qcsrc/server/miscfunctions.qc index db706003b,d998ea799..eb457449e --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@@ -1,9 -1,35 +1,34 @@@ - var void remove(entity e); - void objerror(string s); - void droptofloor(); - .vector dropped_origin; + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "miscfunctions.qh" + #include "../dpdefs/progsdefs.qh" + #include "../dpdefs/dpextensions.qh" + #include "../common/playerstats.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/urllib.qh" + #include "../common/command/generic.qh" + #include "../common/weapons/weapons.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "t_items.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/notifications.qh" + #include "../common/deathtypes.qh" + #include "mutators/mutators_include.qh" - #include "tturrets/include/turrets_early.qh" + #include "../common/mapinfo.qh" + #include "command/common.qh" + #include "../csqcmodellib/sv_model.qh" + #include "ipban.qh" + #endif - void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); void crosshair_trace(entity pl) { traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); diff --cc qcsrc/server/mutators/mutators_include.qc index 0f52e34f0,a0170e480..95f9604a0 --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@@ -1,3 -1,85 +1,83 @@@ + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../dpdefs/progsdefs.qh" + #include "../../dpdefs/dpextensions.qh" + #include "../../warpzonelib/anglestransform.qh" + #include "../../warpzonelib/mathlib.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../../common/constants.qh" + #include "../../common/stats.qh" + #include "../../common/teams.qh" + #include "../../common/util.qh" + #include "../../common/nades.qh" + #include "../../common/buffs.qh" + #include "../../common/test.qh" + #include "../../common/counting.qh" + #include "../../common/urllib.qh" + #include "../../common/command/markup.qh" + #include "../../common/command/rpn.qh" + #include "../../common/command/generic.qh" + #include "../../common/command/shared_defs.qh" + #include "../../common/net_notice.qh" + #include "../../common/animdecide.qh" + #include "../../common/monsters/monsters.qh" + #include "../../common/monsters/sv_monsters.qh" + #include "../../common/monsters/spawn.qh" + #include "../../common/weapons/config.qh" + #include "../../common/weapons/weapons.qh" + #include "../weapons/accuracy.qh" + #include "../weapons/common.qh" + #include "../weapons/csqcprojectile.qh" + #include "../weapons/hitplot.qh" + #include "../weapons/selection.qh" + #include "../weapons/spawning.qh" + #include "../weapons/throwing.qh" + #include "../weapons/tracing.qh" + #include "../weapons/weaponstats.qh" + #include "../weapons/weaponsystem.qh" + #include "../t_items.qh" + #include "../autocvars.qh" + #include "../constants.qh" + #include "../defs.qh" + #include "../../common/notifications.qh" + #include "../../common/deathtypes.qh" + #include "mutators_include.qh" - #include "../tturrets/include/turrets_early.qh" + #include "../vehicles/vehicles_def.qh" + #include "../campaign.qh" + #include "../../common/campaign_common.qh" + #include "../../common/mapinfo.qh" + #include "../command/common.qh" + #include "../command/banning.qh" + #include "../command/radarmap.qh" + #include "../command/vote.qh" + #include "../command/getreplies.qh" + #include "../command/cmd.qh" + #include "../command/sv_cmd.qh" + #include "../../common/csqcmodel_settings.qh" + #include "../../csqcmodellib/common.qh" + #include "../../csqcmodellib/sv_model.qh" + #include "../anticheat.qh" + #include "../cheats.qh" + #include "../../common/playerstats.qh" + #include "../portals.qh" + #include "../g_hook.qh" + #include "../scores.qh" + #include "../spawnpoints.qh" + #include "../mapvoting.qh" + #include "../ipban.qh" + #include "../race.qh" + #include "../antilag.qh" + #include "../playerdemo.qh" + #include "../round_handler.qh" + #include "../item_key.qh" + #include "../secret.qh" + #include "../pathlib/pathlib.qh" - #include "../tturrets/include/turrets.qh" + #include "../vehicles/vehicles.qh" + #endif + #include "base.qc" #include "gamemode_assault.qc" #include "gamemode_ca.qc" diff --cc qcsrc/server/progs.src index 68ded63d8,85e75cda9..527c42712 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@@ -163,104 -83,32 +83,38 @@@ weapons/throwing.q weapons/tracing.qc weapons/weaponstats.qc weapons/weaponsystem.qc - ../common/weapons/config.qc - ../common/weapons/weapons.qc // TODO - t_items.qc - cl_impulse.qc - - ent_cs.qc - - cl_player.qc - cl_client.qc - t_plats.qc - antilag.qc - - //ctf.qc - //domination.qc - //mode_onslaught.qc - //nexball.qc - g_hook.qc - - t_swamp.qc - - campaign.qc + ../common/animdecide.qc + ../common/buffs.qc ../common/campaign_file.qc ../common/campaign_setup.qc - ../common/urllib.qc - + ../common/command/generic.qc ../common/command/markup.qc ../common/command/rpn.qc - ../common/command/generic.qc - ../common/net_notice.qc - - command/common.qc - command/banning.qc - command/radarmap.qc - command/vote.qc - command/getreplies.qc - command/cmd.qc - command/sv_cmd.qc - - //assault.qc - - ipban.qc - ../common/mapinfo.qc - - t_quake3.qc - t_halflife.qc - t_quake.qc - - race.qc - - - //// tZork Vehicles //// - vehicles/vehicles.qh - - scores.qc - - spawnpoints.qc - - portals.qc - - target_spawn.qc - func_breakable.qc - target_music.qc - + ../common/monsters/monsters.qc + ../common/monsters/spawn.qc + ../common/monsters/sv_monsters.qc ../common/nades.qc - ../common/buffs.qc - - ../csqcmodellib/sv_model.qc - - playerdemo.qc - - anticheat.qc - cheats.qc + ../common/net_notice.qc + ../common/notifications.qc ../common/playerstats.qc - - round_handler.qc - + ../common/test.qc + ../common/urllib.qc + ../common/util.qc +../common/turrets/sv_turrets.qc +../common/turrets/config.qc ++../common/turrets/util.qc +../common/turrets/turrets.qc +../common/turrets/checkpoint.qc +../common/turrets/targettrigger.qc + ../common/weapons/config.qc + ../common/weapons/weapons.qc // TODO - ../common/monsters/sv_monsters.qc - ../common/monsters/monsters.qc - - ../common/monsters/spawn.qc - - mutators/mutators_include.qc + ../csqcmodellib/sv_model.qc ../warpzonelib/anglestransform.qc - ../warpzonelib/mathlib.qc ../warpzonelib/common.qc - ../warpzonelib/util_server.qc + ../warpzonelib/mathlib.qc ../warpzonelib/server.qc - - ../common/animdecide.qc - ../common/test.qc - ../common/util.qc - ../common/notifications.qc + ../warpzonelib/util_server.qc diff --cc qcsrc/server/t_teleporters.qc index 5bed9ec93,46df0eb78..53704a06f --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@@ -1,3 -1,24 +1,23 @@@ + #include "t_teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../warpzonelib/common.qh" + #include "../warpzonelib/util_server.qh" + #include "../warpzonelib/server.qh" + #include "../common/constants.qh" + #include "../common/util.qh" + #include "weapons/csqcprojectile.qh" + #include "autocvars.qh" + #include "constants.qh" + #include "defs.qh" + #include "../common/deathtypes.qh" - #include "tturrets/include/turrets_early.qh" + #include "vehicles/vehicles_def.qh" + #include "../common/mapinfo.qh" + #include "anticheat.qh" + #endif + void trigger_teleport_use() { if(teamplay) diff --cc qcsrc/server/tturrets/system/system_main.qc index d56a81bbf,f2b0c56ce..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/system/system_main.qc +++ /dev/null @@@ -1,1378 -1,1378 +1,0 @@@ --#define cvar_base "g_turrets_unit_" --.float clientframe; --void turrets_setframe(float _frame, float client_only) --{ -- if((client_only ? self.clientframe : self.frame ) != _frame) -- { -- self.SendFlags |= TNSF_ANIM; -- self.anim_start_time = time; -- } -- -- if(client_only) -- self.clientframe = _frame; -- else -- self.frame = _frame; -- --} -- --float turret_send(entity to, float sf) --{ -- -- WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); -- WriteByte(MSG_ENTITY, sf); -- if(sf & TNSF_SETUP) -- { -- WriteByte(MSG_ENTITY, self.turret_type); -- - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - WriteCoord(MSG_ENTITY, self.origin.x); - WriteCoord(MSG_ENTITY, self.origin.y); - WriteCoord(MSG_ENTITY, self.origin.z); -- - WriteAngle(MSG_ENTITY, self.angles_x); - WriteAngle(MSG_ENTITY, self.angles_y); - WriteAngle(MSG_ENTITY, self.angles.x); - WriteAngle(MSG_ENTITY, self.angles.y); -- } -- -- if(sf & TNSF_ANG) -- { - WriteShort(MSG_ENTITY, rint(self.tur_head.angles_x)); - WriteShort(MSG_ENTITY, rint(self.tur_head.angles_y)); - WriteShort(MSG_ENTITY, rint(self.tur_head.angles.x)); - WriteShort(MSG_ENTITY, rint(self.tur_head.angles.y)); -- } -- -- if(sf & TNSF_AVEL) -- { - WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_x)); - WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity_y)); - WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity.x)); - WriteShort(MSG_ENTITY, rint(self.tur_head.avelocity.y)); -- } -- -- if(sf & TNSF_MOVE) -- { - WriteShort(MSG_ENTITY, rint(self.origin_x)); - WriteShort(MSG_ENTITY, rint(self.origin_y)); - WriteShort(MSG_ENTITY, rint(self.origin_z)); - WriteShort(MSG_ENTITY, rint(self.origin.x)); - WriteShort(MSG_ENTITY, rint(self.origin.y)); - WriteShort(MSG_ENTITY, rint(self.origin.z)); -- - WriteShort(MSG_ENTITY, rint(self.velocity_x)); - WriteShort(MSG_ENTITY, rint(self.velocity_y)); - WriteShort(MSG_ENTITY, rint(self.velocity_z)); - WriteShort(MSG_ENTITY, rint(self.velocity.x)); - WriteShort(MSG_ENTITY, rint(self.velocity.y)); - WriteShort(MSG_ENTITY, rint(self.velocity.z)); -- - WriteShort(MSG_ENTITY, rint(self.angles_y)); - WriteShort(MSG_ENTITY, rint(self.angles.y)); -- } -- -- if(sf & TNSF_ANIM) -- { -- WriteCoord(MSG_ENTITY, self.anim_start_time); -- WriteByte(MSG_ENTITY, self.frame); -- } -- -- if(sf & TNSF_STATUS) -- { -- WriteByte(MSG_ENTITY, self.team); -- -- if(self.health <= 0) -- WriteByte(MSG_ENTITY, 0); -- else -- WriteByte(MSG_ENTITY, ceil((self.health / self.tur_health) * 255)); -- } -- - return TRUE; - return true; --} -- --void load_unit_settings(entity ent, string unitname, float is_reload) --{ -- string sbase; -- -- 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'; -- -- ent.tur_head.angles = '0 0 0'; -- } -- -- 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_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; -- //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * 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(); --} -- --void turret_projectile_explode() --{ -- -- self.takedamage = DAMAGE_NO; -- self.event_damage = func_null; --#ifdef TURRET_DEBUG -- float d; -- d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); -- self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; -- self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; --#else -- RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); --#endif -- remove(self); --} -- --void turret_projectile_touch() --{ -- PROJECTILE_TOUCH; -- turret_projectile_explode(); --} -- --void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) --{ -- self.velocity += vforce; -- self.health -= damage; -- //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets -- if(self.health <= 0) -- W_PrepareExplosionByDamage(self.owner, turret_projectile_explode); --} -- --entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) --{ -- entity proj; -- -- sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); -- proj = spawn (); -- setorigin(proj, self.tur_shotorg); -- setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size); -- proj.owner = self; -- proj.realowner = self; - proj.bot_dodge = TRUE; - proj.bot_dodge = true; -- proj.bot_dodgerating = self.shot_dmg; -- proj.think = turret_projectile_explode; -- proj.touch = turret_projectile_touch; -- proj.nextthink = time + 9; -- proj.movetype = MOVETYPE_FLYMISSILE; -- proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; -- proj.flags = FL_PROJECTILE; -- proj.enemy = self.enemy; -- proj.totalfrags = _death; -- PROJECTILE_MAKETRIGGER(proj); -- if(_health) -- { -- proj.health = _health; -- proj.takedamage = DAMAGE_YES; -- proj.event_damage = turret_projectile_damage; -- } -- else -- proj.flags |= FL_NOTARGET; -- -- CSQCProjectile(proj, _cli_anim, _proj_type, _cull); -- -- return proj; --} -- --/** --** updates enemy distances, predicted impact point/time --** and updated aim<->predict impact distance. --**/ --void turret_do_updates(entity t_turret) --{ -- vector enemy_pos; -- entity oldself; -- -- oldself = self; -- self = t_turret; -- -- enemy_pos = real_origin(self.enemy); -- -- turret_tag_fire_update(); -- -- self.tur_shotdir_updated = 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) && (self.enemy)) -- { -- oldpos = self.enemy.origin; -- setorigin(self.enemy, self.tur_aimpos); -- tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); -- setorigin(self.enemy, oldpos); -- -- if(trace_ent == self.enemy) -- self.tur_dist_impact_to_aimpos = 0; -- else -- self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos); -- } -- else*/ -- tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); -- -- self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5); -- self.tur_impactent = trace_ent; -- 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 --**/ --.float turret_framecounter; --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; -- vector v1, v2; -- v1 = self.tur_head.angles; -- v2 = self.tur_head.avelocity; -- -- if (self.track_flags == TFL_TRACK_NO) -- return; -- -- if (!self.active) -- target_angle = self.idle_aim - ('1 0 0' * self.aim_maxpitch); -- else if (self.enemy == world) -- { -- if(time > self.lip) -- target_angle = self.idle_aim + self.angles; -- else -- target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); -- } -- else -- { -- target_angle = vectoangles(normalize(self.tur_aimpos - self.tur_shotorg)); -- } -- - self.tur_head.angles_x = anglemods(self.tur_head.angles_x); - self.tur_head.angles_y = anglemods(self.tur_head.angles_y); - self.tur_head.angles_x = anglemods(self.tur_head.angles.x); - self.tur_head.angles_y = anglemods(self.tur_head.angles.y); -- -- // Find the diffrence between where we currently aim and where we want to aim -- //move_angle = target_angle - (self.angles + self.tur_head.angles); -- //move_angle = shortangle_vxy(move_angle,(self.angles + self.tur_head.angles)); -- -- move_angle = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(self.angles), AnglesTransform_FromAngles(target_angle))) - self.tur_head.angles; -- move_angle = shortangle_vxy(move_angle, self.tur_head.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 > self.aim_maxpitch) - self.tur_head.angles_x += bound(-f_tmp,move_angle.x, f_tmp); - if(self.tur_head.angles.x > self.aim_maxpitch) -- self.tur_head.angles_x = self.aim_maxpitch; -- - if(self.tur_head.angles_x < -self.aim_maxpitch) - if(self.tur_head.angles.x < -self.aim_maxpitch) -- self.tur_head.angles_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 > self.aim_maxrot) - self.tur_head.angles_y += bound(-f_tmp, move_angle.y, f_tmp); - if(self.tur_head.angles.y > self.aim_maxrot) -- self.tur_head.angles_y = self.aim_maxrot; -- - if(self.tur_head.angles_y < -self.aim_maxrot) - if(self.tur_head.angles.y < -self.aim_maxrot) -- self.tur_head.angles_y = self.aim_maxrot; -- } -- -- // CSQC -- self.SendFlags |= TNSF_ANG; -- -- 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.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); - 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) > self.aim_maxpitch) - self.tur_head.avelocity_x = move_angle.x; - if((self.tur_head.angles.x + self.tur_head.avelocity.x * self.ticrate) > self.aim_maxpitch) -- { -- self.tur_head.avelocity_x = 0; -- self.tur_head.angles_x = self.aim_maxpitch; -- -- self.SendFlags |= TNSF_ANG; -- } -- - if((self.tur_head.angles_x + self.tur_head.avelocity_x * self.ticrate) < -self.aim_maxpitch) - if((self.tur_head.angles.x + self.tur_head.avelocity.x * self.ticrate) < -self.aim_maxpitch) -- { -- self.tur_head.avelocity_x = 0; -- self.tur_head.angles_x = -self.aim_maxpitch; -- -- self.SendFlags |= TNSF_ANG; -- } -- } -- -- // rot -- if (self.track_flags & TFL_TRACK_ROT) -- { - self.tur_head.avelocity_y = move_angle_y; - self.tur_head.avelocity_y = move_angle.y; -- - if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrot) - if((self.tur_head.angles.y + self.tur_head.avelocity.y * self.ticrate) > self.aim_maxrot) -- { -- self.tur_head.avelocity_y = 0; -- self.tur_head.angles_y = self.aim_maxrot; -- -- self.SendFlags |= TNSF_ANG; -- } -- - if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrot) - if((self.tur_head.angles.y + self.tur_head.avelocity.y * self.ticrate) < -self.aim_maxrot) -- { -- self.tur_head.avelocity_y = 0; -- self.tur_head.angles_y = -self.aim_maxrot; -- -- self.SendFlags |= TNSF_ANG; -- } -- } -- -- self.SendFlags |= TNSF_AVEL; -- -- // Force a angle update every 10'th frame -- self.turret_framecounter += 1; -- if(self.turret_framecounter >= 10) -- { -- self.SendFlags |= TNSF_ANG; -- self.turret_framecounter = 0; -- } --} -- -- --/* -- + = 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; -- -- if (self.enemy == world) -- return 0; -- -- // 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) -- if (self.volly_counter != self.shot_volly) -- if(self.ammo >= self.shot_dmg) -- 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; -- -- // 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; -- -- // Target of opertunity? -- if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) -- { -- self.enemy = self.tur_impactent; -- return 1; -- } -- -- if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) -- { -- // To close? -- if (self.tur_dist_aimpos < self.target_range_min) -- if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) -- return 1; // Target of opertunity? -- else -- 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) -- 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 --** NOTE: the caller must check takedamage before calling this, to inline this check. --**/ --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 (!checkpvs(e_target.origin, e_turret)) -- return -1; -- -- if (!e_target) -- 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.vehicle_flags & VHF_ISVEHICLE) -- { -- if (e_target.vehicle_health <= 0) -- return -6; -- } -- else if (e_target.health <= 0) -- return -6; -- -- // player -- if (IS_CLIENT(e_target)) -- { -- if (!(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 (!(validate_flags & TFL_TARGETSELECT_MISSILES)) -- return -10; -- -- if (validate_flags & TFL_TARGETSELECT_MISSILESONLY) -- if (!(e_target.flags & FL_PROJECTILE)) -- return -10.5; -- -- // 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? -- tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, 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) - if (fabs(tvt_tadv.x) > e_turret.aim_maxpitch) -- return -17; -- - if (fabs(tvt_tadv_y) > e_turret.aim_maxrot) - 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.origin + '0 0 16', 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; -- -- /* -- if (e_target.classname == "func_button") -- return -21; -- */ -- --#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 -- -- m_score = 0; -- if(self.enemy && self.enemy.takedamage && 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; -- } -- else -- e_enemy = self.enemy = world; -- -- e = findradius(self.origin, self.target_range); -- -- // Nothing to aim at? -- if (!e) -- return world; -- -- while (e) -- { -- if(e.takedamage) -- { -- float f = turret_validate_target(self, e, self.target_select_flags); -- //dprint("F is: ", ftos(f), "\n"); -- 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 (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(); -- } -- --#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 (!(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 (!self.active) -- { -- turret_stdproc_track(); -- 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(e.takedamage) -- { -- 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 (!(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 (!(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. -- -- // g_turrets_targetscan_maxdelay forces a target re-scan at least this often -- float do_target_scan = 0; -- if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) -- do_target_scan = 1; -- -- // Old target (if any) invalid? -- if(self.target_validate_time < time) -- if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) -- { -- self.enemy = world; -- self.target_validate_time = time + 0.5; -- do_target_scan = 1; -- } -- -- // But never more often then g_turrets_targetscan_mindelay! -- if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) -- do_target_scan = 0; -- -- if(do_target_scan) -- { -- self.enemy = turret_select_target(); -- 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 + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target. -- -- // Predict? -- if (!(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 custom per-turret stuff -- if(self.turret_postthink) -- self.turret_postthink(); --} -- --void turret_fire() --{ -- if (autocvar_g_turrets_nofire != 0) -- return; -- -- self.turret_firefunc(); -- -- self.attack_finished_single = time + self.shot_refire; -- 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 turret go inactive. --*/ --void turret_stdproc_use() --{ -- dprint("Turret ",self.netname, " used by ", activator.classname, "\n"); -- -- self.team = activator.team; -- -- if(self.team == 0) -- self.active = ACTIVE_NOT; -- else -- self.active = ACTIVE_ACTIVE; -- --} -- --void turret_link() --{ - Net_LinkEntity(self, TRUE, 0, turret_send); - Net_LinkEntity(self, true, 0, turret_send); -- self.think = turret_think; -- self.nextthink = time; -- self.tur_head.effects = EF_NODRAW; --} -- --void turrets_manager_think() --{ -- self.nextthink = time + 1; -- -- entity e; -- if (autocvar_g_turrets_reloadcvars == 1) -- { -- e = nextent(world); -- while (e) -- { -- if (e.turrcaps_flags & TFL_TURRCAPS_ISTURRET) -- { -- load_unit_settings(e,e.cvar_basename,1); -- if(e.turret_postthink) -- e.turret_postthink(); -- } -- -- e = nextent(e); -- } -- cvar_set("g_turrets_reloadcvars","0"); -- } --} -- --/* --* 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, string base, string head, float _turret_type) --{ -- entity e, ee = world; -- -- // Are turrets allowed? -- if (autocvar_g_turrets == 0) -- return 0; -- -- if(_turret_type < 1 || _turret_type > TID_LAST) -- { -- dprint("Invalid / Unkown turret type\"", ftos(_turret_type), "\", aborting!\n"); -- return 0; -- } -- self.turret_type = _turret_type; -- -- e = find(world, classname, "turret_manager"); -- if (!e) -- { -- e = spawn(); -- e.classname = "turret_manager"; -- e.think = turrets_manager_think; -- e.nextthink = time + 2; -- } -- -- if (!(self.spawnflags & TSF_SUSPENDED)) -- builtin_droptofloor(); // why can't we use regular droptofloor here? -- -- // Terrainbase spawnflag. This puts a enlongated model -- // under the turret, so it looks ok on uneaven surfaces. -- /* TODO: Handle this with CSQC -- if (self.spawnflags & TSF_TERRAINBASE) -- { -- entity tb; -- tb = spawn(); -- setmodel(tb,"models/turrets/terrainbase.md3"); -- setorigin(tb,self.origin); -- tb.solid = SOLID_BBOX; -- } -- */ -- -- self.cvar_basename = cvar_base_name; -- load_unit_settings(self, self.cvar_basename, 0); -- -- self.effects = EF_NODRAW; -- -- // Handle turret teams. -- if (!teamplay) -- self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so 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, so 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.ticrate) -- { -- if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) -- self.ticrate = 0.2; // Support units generaly dont need to have a high speed ai-loop -- else -- self.ticrate = 0.1; // 10 fps for normal turrets -- } -- -- self.ticrate = bound(sys_frametime, 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); - self.bot_attack = TRUE; - self.monster_attack = TRUE; - self.bot_attack = true; - self.monster_attack = true; -- -- 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, 5000); -- -- 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_DEAD | TFL_FIRECHECK_DISTANCES | -- TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK | -- TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE; -- --// 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_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_GROUNDGROUND; -- } -- -- if (!self.track_type) -- self.track_type = TFL_TRACKTYPE_STEPMOTOR; -- -- if (self.track_type != TFL_TRACKTYPE_STEPMOTOR) -- { -- // Fluid / Ineria mode. Looks mutch nicer. -- // Can reduce 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 (!(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'; -- -- if (!self.health) -- self.health = 150; -- --// Game hooks -- if(MUTATOR_CALLHOOK(TurretSpawn)) -- return 0; -- --// 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; -- -- setmodel(self, base); -- setmodel(self.tur_head, head); -- -- setsize(self, '-32 -32 0', '32 32 64'); -- setsize(self.tur_head, '0 0 0', '0 0 0'); -- -- setorigin(self.tur_head, '0 0 0'); -- setattachment(self.tur_head, self, "tag_head"); -- -- self.tur_health = self.health; -- self.solid = SOLID_BBOX; -- self.tur_head.solid = SOLID_NOT; -- self.takedamage = DAMAGE_AIM; -- self.tur_head.takedamage = DAMAGE_NO; -- self.movetype = MOVETYPE_NOCLIP; -- self.tur_head.movetype = MOVETYPE_NOCLIP; -- -- // 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"); -- } -- } -- -- // In target defend mode, aim on the spot to defend when idle. -- 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'; -- -- // Attach stdprocs. override when and what needed -- self.turret_firecheckfunc = turret_stdproc_firecheck; -- self.turret_firefunc = turret_stdproc_fire; -- self.event_damage = turret_stdproc_damage; -- -- if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) -- self.turret_score_target = turret_stdproc_targetscore_support; -- else -- self.turret_score_target = turret_stdproc_targetscore_generic; -- -- self.use = turret_stdproc_use; -- -- ++turret_count; -- self.nextthink = time + 1; -- self.nextthink += turret_count * sys_frametime; -- -- 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); - 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.turrcaps_flags |= TFL_TURRCAPS_ISTURRET; -- -- self.classname = "turret_main"; -- -- self.active = ACTIVE_ACTIVE; -- -- // 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(); -- } -- -- turret_link(); -- turret_stdproc_respawn(); -- turret_tag_fire_update(); -- -- return 1; --} -- -- diff --cc qcsrc/server/tturrets/units/unit_ewheel.qc index e8e677ac8,7eb1622ba..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/units/unit_ewheel.qc +++ /dev/null @@@ -1,310 -1,310 +1,0 @@@ - #define ewheel_amin_stop 0 - #define ewheel_amin_fwd_slow 1 - #define ewheel_amin_fwd_fast 2 - #define ewheel_amin_bck_slow 3 - #define ewheel_amin_bck_fast 4 -const float ewheel_amin_stop = 0; -const float ewheel_amin_fwd_slow = 1; -const float ewheel_amin_fwd_fast = 2; -const float ewheel_amin_bck_slow = 3; -const float ewheel_amin_bck_fast = 4; -- --void ewheel_attack() --{ -- float i; -- entity _mis; -- -- for (i = 0; i < 1; ++i) -- { -- turret_do_updates(self); -- - _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE); // WEAPONTODO: this is not a projectile made by the blaster, add separate effect for it - _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, true, true); // WEAPONTODO: this is not a projectile made by the blaster, add separate effect for it -- _mis.missile_flags = MIF_SPLASH; -- -- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -- -- self.tur_head.frame += 2; -- -- if (self.tur_head.frame > 3) -- self.tur_head.frame = 0; -- } -- --} --//#define EWHEEL_FANCYPATH --void ewheel_move_path() --{ --#ifdef EWHEEL_FANCYPATH -- // 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; -- --#else -- if (vlen(self.origin - self.pathcurrent.origin) < 64) -- self.pathcurrent = self.pathcurrent.enemy; --#endif -- -- if (self.pathcurrent) -- { -- -- self.moveto = self.pathcurrent.origin; -- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); -- -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_fast, 0.4); -- } --} -- --void ewheel_move_enemy() --{ -- -- float newframe; -- -- self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); -- -- //self.steerto = steerlib_standoff(self.enemy.origin,self.target_range_optimal); -- //self.steerto = steerlib_beamsteer(self.steerto,1024,64,68,256); -- self.moveto = self.origin + self.steerto * 128; -- -- if (self.tur_dist_enemy > self.target_range_optimal) -- { -- if ( self.tur_head.spawnshieldtime < 1 ) -- { -- newframe = ewheel_amin_fwd_fast; -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_fast, 0.4); -- } -- else if (self.tur_head.spawnshieldtime < 2) -- { -- -- newframe = ewheel_amin_fwd_slow; -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4); -- } -- else -- { -- newframe = ewheel_amin_fwd_slow; -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_slower, 0.4); -- } -- } -- else if (self.tur_dist_enemy < self.target_range_optimal * 0.5) -- { -- newframe = ewheel_amin_bck_slow; -- movelib_move_simple(v_forward * -1, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4); -- } -- else -- { -- newframe = ewheel_amin_stop; -- movelib_beak_simple(autocvar_g_turrets_unit_ewheel_speed_stop); -- } -- - turrets_setframe(newframe , FALSE); - turrets_setframe(newframe , false); -- -- /*if(self.frame != newframe) -- { -- self.frame = newframe; -- self.SendFlags |= TNSF_ANIM; -- self.anim_start_time = time; -- }*/ --} -- -- --void ewheel_move_idle() --{ -- if(self.frame != 0) -- { -- self.SendFlags |= TNSF_ANIM; -- self.anim_start_time = time; -- } -- -- self.frame = 0; -- if (vlen(self.velocity)) -- movelib_beak_simple(autocvar_g_turrets_unit_ewheel_speed_stop); --} -- --void ewheel_postthink() --{ -- float vz; -- vector wish_angle, real_angle; -- - vz = self.velocity_z; - vz = self.velocity.z; -- - self.angles_x = anglemods(self.angles_x); - self.angles_y = anglemods(self.angles_y); - self.angles_x = anglemods(self.angles.x); - self.angles_y = anglemods(self.angles.y); -- -- fixedmakevectors(self.angles); -- -- 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); - 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); -- -- if(self.enemy) -- ewheel_move_enemy(); -- else if(self.pathcurrent) -- ewheel_move_path(); -- else -- ewheel_move_idle(); -- -- -- self.velocity_z = vz; -- -- if(vlen(self.velocity)) -- self.SendFlags |= TNSF_MOVE; --} -- --void ewheel_respawnhook() --{ -- entity e; -- -- // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. -- if(self.movetype != MOVETYPE_WALK) -- return; -- -- self.velocity = '0 0 0'; -- self.enemy = world; -- -- 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 -- { -- --#ifdef EWHEEL_FANCYPATH -- self.pathcurrent = WALKER_PATH(self.origin,e.origin); -- self.pathgoal = e; --#else -- self.pathcurrent = e; --#endif -- } -- } --} -- --void ewheel_diehook() --{ -- self.velocity = '0 0 0'; -- --#ifdef EWHEEL_FANCYPATH -- if (self.pathcurrent) -- pathlib_deletepath(self.pathcurrent.owner); --#endif -- self.pathcurrent = world; --} -- --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 ; -- self.turret_respawnhook = ewheel_respawnhook; -- -- self.turret_diehook = ewheel_diehook; -- -- if (turret_stdproc_init("ewheel_std", "models/turrets/ewheel-base2.md3", "models/turrets/ewheel-gun1.md3", TID_EWHEEL) == 0) -- { -- remove(self); -- return; -- } -- -- self.frame = 1; -- 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.iscreature = true; -- self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; - self.damagedbycontents = true; -- self.movetype = MOVETYPE_WALK; -- self.solid = SOLID_SLIDEBOX; -- self.takedamage = DAMAGE_AIM; -- self.idle_aim = '0 0 0'; -- self.pos1 = self.origin; -- -- setsize(self, '-32 -32 0', '32 32 48'); -- -- // Our fire routine -- self.turret_firefunc = ewheel_attack; -- self.turret_postthink = ewheel_postthink; -- self.tur_head.frame = 1; -- -- // Convert from dgr / sec to dgr / tic -- self.tur_head.aim_speed = autocvar_g_turrets_unit_ewheel_turnrate; -- self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); -- -- //setorigin(self,self.origin + '0 0 128'); -- 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 -- { --#ifdef EWHEEL_FANCYPATH -- self.pathcurrent = WALKER_PATH(self.origin, e.origin); -- self.pathgoal = e; --#else -- self.pathcurrent = e; --#endif -- } -- } --} -- --void spawnfunc_turret_ewheel() --{ -- g_turrets_common_precash(); -- -- precache_model ("models/turrets/ewheel-base2.md3"); -- precache_model ("models/turrets/ewheel-gun1.md3"); -- -- self.think = turret_ewheel_dinit; -- self.nextthink = time + 0.5; --} diff --cc qcsrc/server/tturrets/units/unit_flac.qc index 3c9e55863,e398a83da..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/units/unit_flac.qc +++ /dev/null @@@ -1,74 -1,74 +1,0 @@@ --void spawnfunc_turret_flac(); --void turret_flac_dinit(); --void turret_flac_attack(); -- --void turret_flac_projectile_think_explode() --{ -- if(self.enemy != world) -- if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) -- setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); -- --#ifdef TURRET_DEBUG -- float d; -- d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); -- self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; -- self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; --#else -- RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); --#endif -- remove(self); --} -- --void turret_flac_attack() --{ -- entity proj; -- -- turret_tag_fire_update(); -- - proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); - proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, true, true); -- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -- proj.think = turret_flac_projectile_think_explode; -- proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); -- proj.missile_flags = MIF_SPLASH | MIF_PROXY; -- -- self.tur_head.frame = self.tur_head.frame + 1; -- if (self.tur_head.frame >= 4) -- self.tur_head.frame = 0; -- --} -- --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", "models/turrets/base.md3", "models/turrets/flac.md3", TID_FLAC) == 0) -- { -- remove(self); -- return; -- } -- setsize(self.tur_head,'-32 -32 0','32 32 64'); -- -- self.damage_flags |= TFL_DMG_HEADSHAKE; -- self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY; -- -- // 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"); -- -- self.think = turret_flac_dinit; -- self.nextthink = time + 0.5; --} -- diff --cc qcsrc/server/tturrets/units/unit_machinegun.qc index d235dfb32,d235dfb32..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/units/unit_machinegun.qc +++ /dev/null @@@ -1,52 -1,52 +1,0 @@@ --void spawnfunc_turret_machinegun(); --void turret_machinegun_std_init(); --void turret_machinegun_attack(); -- --//.float bulletcounter; --void turret_machinegun_attack() --{ -- fireBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0); -- -- W_MachineGun_MuzzleFlash(); // WEAPONTODO -- setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); --} -- -- --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; -- self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; -- -- self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; -- -- if (turret_stdproc_init("machinegun_std", "models/turrets/base.md3", "models/turrets/machinegun.md3", TID_MACHINEGUN) == 0) -- { -- remove(self); -- return; -- } -- -- self.damage_flags |= TFL_DMG_HEADSHAKE; -- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; -- -- // Our fire routine -- self.turret_firefunc = turret_machinegun_attack; -- --} -- -- --/*QUAKED turret_machinegun (0 .5 .8) ? --* machinegun turret. does what you'd expect --*/ --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 --cc qcsrc/server/tturrets/units/unit_plasma.qc index 26a3dc04e,1bfd8ac34..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/units/unit_plasma.qc +++ /dev/null @@@ -1,173 -1,173 +1,0 @@@ --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_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_minsta_attack (void) --{ -- float flying; -- flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last -- -- FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, -- 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); -- -- -- pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -- -- // teamcolor / hit beam effect -- vector v; -- v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); -- if(teamplay) -- { -- switch(self.team) -- { -- case NUM_TEAM_1: // Red -- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v); -- break; -- case NUM_TEAM_2: // Blue -- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v); -- break; -- case NUM_TEAM_3: // Yellow -- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v); -- break; -- case NUM_TEAM_4: // Pink -- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v); -- break; -- } -- } -- else -- WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), self.tur_shotorg, v); -- if (self.tur_head.frame == 0) -- self.tur_head.frame = 1; --} -- --void turret_plasma_attack() --{ - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, true, true); -- missile.missile_flags = MIF_SPLASH; -- -- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -- if (self.tur_head.frame == 0) -- self.tur_head.frame = 1; --} -- --void turret_plasma_dual_attack() --{ - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, true, true); -- missile.missile_flags = MIF_SPLASH; -- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -- self.tur_head.frame += 1; --} -- --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_GROUNDGROUND; -- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; -- -- if (turret_stdproc_init("plasma_std", "models/turrets/base.md3", "models/turrets/plasma.md3", TID_PLASMA) == 0) -- { -- remove(self); -- return; -- } -- -- self.damage_flags |= TFL_DMG_HEADSHAKE; -- self.firecheck_flags |= TFL_FIRECHECK_AFF; -- -- // Our fireing routine -- if(g_instagib) -- self.turret_firefunc = turret_plasma_minsta_attack; -- else -- 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_GROUNDGROUND ; -- self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; -- -- if (turret_stdproc_init("plasma_dual", "models/turrets/base.md3", "models/turrets/plasmad.md3", TID_PLASMA_DUAL) == 0) -- { -- remove(self); -- return; -- } -- -- self.damage_flags |= TFL_DMG_HEADSHAKE; -- self.firecheck_flags |= TFL_FIRECHECK_AFF; -- -- // 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 --cc qcsrc/server/tturrets/units/unit_walker.qc index a91daa190,4b1c44c15..000000000 deleted file mode 100644,100644 --- a/qcsrc/server/tturrets/units/unit_walker.qc +++ /dev/null @@@ -1,643 -1,643 +1,0 @@@ - #define ANIM_NO 0 - #define ANIM_TURN 1 - #define ANIM_WALK 2 - #define ANIM_RUN 3 - #define ANIM_STRAFE_L 4 - #define ANIM_STRAFE_R 5 - #define ANIM_JUMP 6 - #define ANIM_LAND 7 - #define ANIM_PAIN 8 - #define ANIM_MEELE 9 - #define ANIM_SWIM 10 - #define ANIM_ROAM 11 -const float ANIM_NO = 0; -const float ANIM_TURN = 1; -const float ANIM_WALK = 2; -const float ANIM_RUN = 3; -const float ANIM_STRAFE_L = 4; -const float ANIM_STRAFE_R = 5; -const float ANIM_JUMP = 6; -const float ANIM_LAND = 7; -const float ANIM_PAIN = 8; -const float ANIM_MEELE = 9; -const float ANIM_SWIM = 10; -const float ANIM_ROAM = 11; --.float animflag; -- - #define WALKER_MIN '-70 -70 0' - #define WALKER_MAX '70 70 95' -const vector WALKER_MIN = '-70 -70 0'; -const vector WALKER_MAX = '70 70 95'; -- --#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; -- -- e = findradius(where,32); -- while (e) -- { -- if (turret_validate_target(self, e, self.target_validate_flags)) -- if (e != self && e.owner != self) -- Damage(e, self, self, autocvar_g_turrets_unit_walker_std_meele_dmg, DEATH_TURRET_WALK_MEELE, '0 0 0', v_forward * autocvar_g_turrets_unit_walker_std_meele_force); -- -- e = e.chain; -- } --} -- --void walker_setnoanim() --{ - turrets_setframe(ANIM_NO, FALSE); - turrets_setframe(ANIM_NO, false); -- self.animflag = self.frame; --} --void walker_rocket_explode() --{ -- RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, world, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, 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) -- W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); --} -- --#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, autocvar_g_turrets_unit_walker_std_rocket_speed, autocvar_g_turrets_unit_walker_std_rocket_turnrate); 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 < 128) -- 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 && random() < 0.01) -- { -- walker_rocket_loop(); -- return; -- } -- -- m_speed = vlen(self.velocity); -- -- // Enemy dead? just keep on the current heading then. -- if (self.enemy == world || self.enemy.deadflag != DEAD_NO) -- 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) < 100 ) -- { -- 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) < 100 ) -- { -- 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 300'; -- self.think = walker_rocket_loop2; -- self.shot_dmg = 1337; --} -- --void walker_fire_rocket(vector org) --{ -- entity rocket; -- -- fixedmakevectors(self.angles); -- -- te_explosion (org); -- -- rocket = spawn (); -- setorigin(rocket, org); -- -- sound (self, CH_WEAPON_A, "weapons/hagar_fire.wav", VOL_BASE, ATTEN_NORM); -- setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot -- -- rocket.classname = "walker_rocket"; -- rocket.owner = self; - rocket.bot_dodge = TRUE; - rocket.bot_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; -- rocket.movetype = MOVETYPE_FLY; -- rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * autocvar_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; -- rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; -- - CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound - CSQCProjectile(rocket, false, PROJECTILE_ROCKET, false); // no culling, has fly sound --} -- --.vector enemy_last_loc; --.float enemy_last_time; --void walker_move_to(vector _target, float _dist) --{ -- switch (self.waterlevel) -- { -- case WATERLEVEL_NONE: -- if (_dist > 500) -- self.animflag = ANIM_RUN; -- else -- self.animflag = ANIM_WALK; -- case WATERLEVEL_WETFEET: -- case WATERLEVEL_SWIMMING: -- if (self.animflag != ANIM_SWIM) -- self.animflag = ANIM_WALK; -- else -- self.animflag = ANIM_SWIM; -- break; -- case WATERLEVEL_SUBMERGED: -- self.animflag = ANIM_SWIM; -- } -- -- self.moveto = _target; -- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); -- -- if(self.enemy) -- { -- self.enemy_last_loc = _target; -- self.enemy_last_time = time; -- } --} -- --//#define WALKER_FANCYPATHING -- --void walker_move_path() --{ --#ifdef WALKER_FANCYPATHING -- // 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; -- -- self.moveto = self.pathcurrent.origin; -- self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); -- walker_move_to(self.moveto, 0); -- --#else -- if (vlen(self.origin - self.pathcurrent.origin) < 64) -- self.pathcurrent = self.pathcurrent.enemy; -- -- if(!self.pathcurrent) -- return; -- -- self.moveto = self.pathcurrent.origin; -- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); -- walker_move_to(self.moveto, 0); --#endif --} -- --.float idletime; --void walker_postthink() --{ -- fixedmakevectors(self.angles); -- -- if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent) -- walker_move_path(); -- else if (self.enemy == world) -- { -- if(self.pathcurrent) -- walker_move_path(); -- else -- { -- if(self.enemy_last_time != 0) -- { -- if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10) -- self.enemy_last_time = 0; -- else -- walker_move_to(self.enemy_last_loc, 0); -- } -- else -- { -- if(self.animflag != ANIM_NO) -- { -- traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self); -- -- if(trace_fraction != 1.0) -- self.tur_head.idletime = -1337; -- else -- { -- traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self); -- if(trace_fraction == 1.0) -- self.tur_head.idletime = -1337; -- } -- -- if(self.tur_head.idletime == -1337) -- { -- self.moveto = self.origin + randomvec() * 256; -- self.tur_head.idletime = 0; -- } -- -- self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1; - self.moveto_z = self.origin_z + 64; - self.moveto_z = self.origin.z + 64; -- walker_move_to(self.moveto, 0); -- } -- -- if(self.idletime < time) -- { -- if(random() < 0.5 || !(self.spawnflags & TSL_ROAM)) -- { -- self.idletime = time + 1 + random() * 5; -- self.moveto = self.origin; -- self.animflag = ANIM_NO; -- } -- else -- { -- self.animflag = ANIM_WALK; -- self.idletime = time + 4 + random() * 2; -- self.moveto = self.origin + randomvec() * 256; -- self.tur_head.moveto = self.moveto; -- self.tur_head.idletime = 0; -- } -- } -- } -- } -- } -- else -- { -- if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_meele_range && self.animflag != ANIM_MEELE) -- { -- vector wish_angle; -- -- wish_angle = angleofs(self, self.enemy); -- if (self.animflag != ANIM_SWIM) - if (fabs(wish_angle_y) < 15) - if (fabs(wish_angle.y) < 15) -- { -- self.moveto = self.enemy.origin; -- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); -- self.animflag = ANIM_MEELE; -- } -- } -- else if (self.tur_head.attack_finished_single < time) -- { -- if(self.tur_head.shot_volly) -- { -- self.animflag = ANIM_NO; -- -- self.tur_head.shot_volly = self.tur_head.shot_volly -1; -- if(self.tur_head.shot_volly == 0) -- self.tur_head.attack_finished_single = time + autocvar_g_turrets_unit_walker_std_rocket_refire; -- else -- self.tur_head.attack_finished_single = time + 0.2; -- -- if(self.tur_head.shot_volly > 1) -- walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01"))); -- else -- walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02"))); -- } -- else -- { -- if (self.tur_dist_enemy > autocvar_g_turrets_unit_walker_std_rockets_range_min) -- if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_rockets_range) -- self.tur_head.shot_volly = 4; -- } -- } -- else -- { -- if (self.animflag != ANIM_MEELE) -- walker_move_to(self.enemy.origin, self.tur_dist_enemy); -- } -- } -- -- //if(self.animflag != ANIM_NO) -- { -- vector real_angle; -- float turny = 0, turnx = 0; -- float vz; -- -- real_angle = vectoangles(self.steerto) - self.angles; - vz = self.velocity_z; - vz = self.velocity.z; -- -- switch (self.animflag) -- { -- case ANIM_NO: -- movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); -- break; -- -- case ANIM_TURN: -- turny = autocvar_g_turrets_unit_walker_turn; -- movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); -- break; -- -- case ANIM_WALK: -- turny = autocvar_g_turrets_unit_walker_turn_walk; -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_walk, 0.6); -- break; -- -- case ANIM_RUN: -- turny = autocvar_g_turrets_unit_walker_turn_run; -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_run, 0.6); -- break; -- -- case ANIM_STRAFE_L: -- turny = autocvar_g_turrets_unit_walker_turn_strafe; -- movelib_move_simple(v_right * -1, autocvar_g_turrets_unit_walker_speed_walk, 0.8); -- break; -- -- case ANIM_STRAFE_R: -- turny = autocvar_g_turrets_unit_walker_turn_strafe; -- movelib_move_simple(v_right, autocvar_g_turrets_unit_walker_speed_walk, 0.8); -- break; -- -- case ANIM_JUMP: -- self.velocity += '0 0 1' * autocvar_g_turrets_unit_walker_speed_jump; -- break; -- -- case ANIM_LAND: -- break; -- -- case ANIM_PAIN: -- if(self.frame != ANIM_PAIN) -- defer(0.25, walker_setnoanim); -- -- break; -- -- case ANIM_MEELE: -- if(self.frame != ANIM_MEELE) -- { -- defer(0.41, walker_setnoanim); -- defer(0.21, walker_meele_do_dmg); -- } -- -- movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); -- break; -- -- case ANIM_SWIM: -- turny = autocvar_g_turrets_unit_walker_turn_swim; -- turnx = autocvar_g_turrets_unit_walker_turn_swim; -- - self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10); - self.angles_x += bound(-10, shortangle_f(real_angle.x, self.angles.x), 10); -- movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_swim, 0.3); -- vz = self.velocity_z + sin(time * 4) * 8; -- break; -- -- case ANIM_ROAM: -- turny = autocvar_g_turrets_unit_walker_turn_walk; -- movelib_move_simple(v_forward ,autocvar_g_turrets_unit_walker_speed_roam, 0.5); -- break; -- } -- -- if(turny) -- { - turny = bound( turny * -1, shortangle_f(real_angle_y, self.angles_y), turny ); - turny = bound( turny * -1, shortangle_f(real_angle.y, self.angles.y), turny ); -- self.angles_y += turny; -- } -- -- if(turnx) -- { - turnx = bound( turnx * -1, shortangle_f(real_angle_x, self.angles_x), turnx ); - turnx = bound( turnx * -1, shortangle_f(real_angle.x, self.angles.x), turnx ); -- self.angles_x += turnx; -- } -- -- self.velocity_z = vz; -- } -- -- -- if(self.origin != self.oldorigin) -- self.SendFlags |= TNSF_MOVE; -- -- self.oldorigin = self.origin; - turrets_setframe(self.animflag, FALSE); - turrets_setframe(self.animflag, false); --} -- --void walker_attack() --{ -- sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM); -- fireBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0); -- pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); --} -- -- --void walker_respawnhook() --{ -- entity e; -- -- // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. -- if(self.movetype != MOVETYPE_WALK) -- return; -- -- setorigin(self, self.pos1); -- self.angles = self.pos2; -- -- 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 -- { -- #ifdef WALKER_FANCYPATHING -- self.pathcurrent = WALKER_PATH(self.origin, e.origin); -- self.pathgoal = e; --#else -- self.pathcurrent = e; --#endif -- } -- } --} -- --void walker_diehook() --{ --#ifdef WALKER_FANCYPATHING -- if (self.pathcurrent) -- pathlib_deletepath(self.pathcurrent.owner); --#endif -- self.pathcurrent = world; --} -- --void turret_walker_dinit() --{ -- entity e; -- -- if (self.netname == "") self.netname = "Walker Turret"; -- -- self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; -- self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE ; -- self.aim_flags = TFL_AIM_LEAD; -- -- self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; -- -- -- self.turret_respawnhook = walker_respawnhook; -- self.turret_diehook = walker_diehook; -- -- self.ticrate = 0.05; -- if (turret_stdproc_init("walker_std", "models/turrets/walker_body.md3", "models/turrets/walker_head_minigun.md3", TID_WALKER) == 0) -- { -- remove(self); -- return; -- } -- setsize(self, WALKER_MIN, WALKER_MAX); -- 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.iscreature = true; -- self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; - self.damagedbycontents = true; -- self.movetype = MOVETYPE_WALK; -- self.solid = SOLID_SLIDEBOX; -- self.takedamage = DAMAGE_AIM; -- setorigin(self, self.origin); -- tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self); -- setorigin(self, trace_endpos + '0 0 4'); -- self.pos1 = self.origin; -- self.pos2 = self.angles; -- 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 -- { --#ifdef WALKER_FANCYPATHING -- self.pathcurrent = WALKER_PATH(self.origin, e.origin); -- self.pathgoal = e; --#else -- self.pathcurrent = e; --#endif -- } -- } --} -- -- --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/rocket.md3"); -- precache_sound ( "weapons/rocket_impact.wav" ); -- -- self.think = turret_walker_dinit; -- self.nextthink = time + 0.5; --} diff --cc qcsrc/server/vehicles/racer.qc index fc9cee865,87dfe52a7..45c3713af --- a/qcsrc/server/vehicles/racer.qc +++ b/qcsrc/server/vehicles/racer.qc @@@ -1,7 -1,7 +1,9 @@@ - #define RACER_MIN '-120 -120 -40' - #define RACER_MAX '120 120 40' + const vector RACER_MIN = '-120 -120 -40'; + const vector RACER_MAX = '120 120 40'; #ifdef SVQC ++#include "../../common/turrets/util.qh" ++ void racer_exit(float eject); void racer_enter(); diff --cc qcsrc/server/vehicles/vehicles.qc index 94656c2f4,96d54e3de..695e3db75 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@@ -1,3 -1,3 +1,5 @@@ ++#include "../../common/turrets/util.qh" ++ float autocvar_g_vehicles_crush_dmg; float autocvar_g_vehicles_crush_force; float autocvar_g_vehicles_delayspawn; diff --cc qcsrc/server/vehicles/vehicles_def.qh index 5b82788d1,080f891f4..ca40a93e8 --- a/qcsrc/server/vehicles/vehicles_def.qh +++ b/qcsrc/server/vehicles/vehicles_def.qh @@@ -1,3 -1,8 +1,6 @@@ + #ifndef VEHICLES_DEF_H + #define VEHICLES_DEF_H + -#include "../tturrets/include/turrets_early.qh" - // #define VEHICLES_USE_ODE #define VEHICLES_ENABLED #ifdef VEHICLES_ENABLED