From: TimePath Date: Sun, 23 Aug 2015 08:07:27 +0000 (+1000) Subject: Merge branch 'master' into Mario/turrets X-Git-Tag: xonotic-v0.8.2~2052^2~2 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=221325d0a55851348e3397354225f04cd472d42f;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into Mario/turrets # Conflicts: # qcsrc/client/main.qc # qcsrc/client/progs.src # qcsrc/client/tturrets.qc # qcsrc/client/tturrets.qh # qcsrc/common/command/generic.qc # qcsrc/common/monsters/sv_monsters.qc # qcsrc/common/nades.qc # qcsrc/common/triggers/teleporters.qc # qcsrc/server/autocvars.qh # qcsrc/server/g_damage.qc # qcsrc/server/g_damage.qh # qcsrc/server/g_world.qc # qcsrc/server/miscfunctions.qc # qcsrc/server/mutators/mutators_include.qc # qcsrc/server/progs.src # qcsrc/server/tturrets/include/turrets_early.qh # qcsrc/server/tturrets/system/system_damage.qc # qcsrc/server/tturrets/system/system_main.qc # qcsrc/server/tturrets/system/system_misc.qc # qcsrc/server/tturrets/units/unit_ewheel.qc # qcsrc/server/tturrets/units/unit_flac.qc # qcsrc/server/tturrets/units/unit_machinegun.qc # qcsrc/server/tturrets/units/unit_phaser.qc # qcsrc/server/tturrets/units/unit_plasma.qc # qcsrc/server/tturrets/units/unit_tessla.qc # qcsrc/server/tturrets/units/unit_walker.qc # qcsrc/server/vehicles/bumblebee.qc # qcsrc/server/vehicles/racer.qc # qcsrc/server/vehicles/vehicles.qc # qcsrc/server/vehicles/vehicles_def.qh --- 221325d0a55851348e3397354225f04cd472d42f diff --cc qcsrc/client/main.qc index b753139c4,231dd6867..6d075b2f8 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@@ -1,24 -13,44 +13,46 @@@ #include "mapvoting.qh" #include "modeleffects.qh" #include "particles.qh" + #include "prandom.qh" #include "scoreboard.qh" #include "shownames.qh" - #include "target_music.qh" + #include "sortlist.qh" -#include "tturrets.qh" #include "tuba.qh" + #include "t_items.qh" #include "wall.qh" #include "waypointsprites.qh" - #include "vehicles/vehicles.qh" + #include "../common/vehicles/unit/bumblebee.qh" + #include "../common/vehicles/cl_vehicles.qh" + #include "../common/vehicles/vehicles.qh" - #include "../server/vehicles/bumblebee.qh" + #include "weapons/projectile.qh" + #include "../common/buffs.qh" + #include "../common/deathtypes.qh" + #include "../common/effects.qh" + #include "../common/mapinfo.qh" + #include "../common/monsters/all.qh" + #include "../common/nades.qh" #include "../common/net_notice.qh" + #include "../common/notifications.qh" + #include "../common/stats.qh" + #include "../common/teams.qh" - #include "../common/monsters/monsters.qh" + #include "../common/items/all.qh" + + #include "../common/mutators/base.qh" + + #include "../common/weapons/all.qh" + + #include "../csqcmodellib/cl_model.qh" + #include "../csqcmodellib/interpolate.qh" + + #include "../common/triggers/include.qh" +#include "../common/turrets/cl_turrets.qh" +#include "../common/turrets/turrets.qh" + #include "../warpzonelib/client.qh" // -------------------------------------------------------------------------- @@@ -109,10 -144,7 +146,8 @@@ void CSQC_Init(void GetTeam(NUM_SPECTATOR, true); // add specs first // needs to be done so early because of the constants they create - CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + static_init(); + 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); @@@ -129,7 -162,7 +165,6 @@@ Hook_Precache(); GibSplash_Precache(); Casings_Precache(); - Vehicles_Precache(); - turrets_precache(); Tuba_Precache(); CSQCPlayer_Precache(); diff --cc qcsrc/client/progs.src index cdd0b09d1,ea61aed0d..6292ad5dd --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@@ -27,10 -27,10 +27,9 @@@ rubble.q scoreboard.qc shownames.qc sortlist.qc - target_music.qc teamradar.qc -tturrets.qc tuba.qc - vehicles/vehicles.qc + t_items.qc view.qc wall.qc waypointsprites.qc @@@ -50,16 -54,15 +53,20 @@@ weapons/projectile.qc // TOD ../common/urllib.qc ../common/util.qc - ../common/command/generic.qc - ../common/command/markup.qc - ../common/command/rpn.qc + ../common/viewloc.qc - ../common/monsters/monsters.qc ++../common/weapons/all.qc // TODO + + ../common/items/all.qc + + ../common/monsters/all.qc +../common/turrets/cl_turrets.qc +../common/turrets/turrets.qc + - ../common/weapons/weapons.qc // TODO + + ../common/weapons/all.qc // TODO + + ../common/triggers/include.qc ../csqcmodellib/cl_model.qc ../csqcmodellib/cl_player.qc @@@ -71,6 -74,8 +78,9 @@@ ../warpzonelib/anglestransform.qc ../warpzonelib/client.qc + ../warpzonelib/common.qc ../warpzonelib/mathlib.qc + ../warpzonelib/util_server.qc + + ../../mod/client/progs.inc diff --cc qcsrc/common/command/generic.qc index 460410c28,6aa93a024..9fa9872a2 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@@ -1,6 -1,28 +1,28 @@@ + #include "command.qh" #include "generic.qh" - #include "shared_defs.qh" - #include "../turrets/config.qh" + + #include "markup.qh" + #include "rpn.qh" + + #include "../mapinfo.qh" + #include "../test.qh" + + #ifndef MENUQC + #include "../notifications.qh" + #endif + + #ifdef CSQC + #include "../../client/command/cl_cmd.qh" + #endif + + #ifdef SVQC + #include "../../server/command/banning.qh" + #include "../../server/command/cmd.qh" + #include "../../server/command/common.qh" + #include "../../server/command/sv_cmd.qh" - ++ #include "../../common/turrets/config.qh" + #include "../../common/weapons/config.qh" + #endif // ========================================================= // Generic program common command code, written by Samual @@@ -682,8 -670,8 +723,9 @@@ void GenericCommand_(float request #define GENERIC_COMMANDS(request,arguments,command) \ GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \ GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \ + GENERIC_COMMAND("dumpitems", GenericCommand_dumpitems(request), "Dump all items to the console") \ GENERIC_COMMAND("dumpnotifs", GenericCommand_dumpnotifs(request), "Dump all notifications into notifications_dump.txt") \ + GENERIC_COMMAND("dumpturrets", GenericCommand_dumpturrets(request), "Dump all turrets into turrets_dump.txt") \ GENERIC_COMMAND("dumpweapons", GenericCommand_dumpweapons(request), "Dump all weapons into weapons_dump.txt") \ GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \ GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \ diff --cc qcsrc/common/monsters/sv_monsters.qc index 4c443495f,887ac20ec..b453ba257 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@@ -14,12 -14,15 +14,16 @@@ #include "../../server/defs.qh" #include "../deathtypes.qh" #include "../../server/mutators/mutators_include.qh" - #include "../../server/vehicles/vehicles_def.qh" - #include "../../server/tturrets/include/turrets_early.qh" ++ #include "../../server/steerlib.qh" ++ #include "../turrets/sv_turrets.qh" ++ #include "../turrets/util.qh" + #include "../vehicles/sv_vehicles.qh" #include "../../server/campaign.qh" #include "../../server/command/common.qh" #include "../../server/command/cmd.qh" + #include "../triggers/triggers.qh" #include "../../csqcmodellib/sv_model.qh" #include "../../server/round_handler.qh" - #include "../../server/tturrets/include/turrets.qh" #endif // ========================= diff --cc qcsrc/common/nades.qc index 07a91a8cc,fcdc5533e..f9ddb8e99 --- a/qcsrc/common/nades.qc +++ b/qcsrc/common/nades.qc @@@ -3,7 -3,8 +3,7 @@@ #include "../client/defs.qh" #include "nades.qh" #include "buffs.qh" - #include "../client/movetypes.qh" + #include "../common/movetypes/movetypes.qh" - #include "../server/tturrets/include/turrets_early.qh" #include "../client/main.qh" #include "../csqcmodellib/cl_model.qh" #elif defined(MENUQC) @@@ -11,6 -12,6 +11,7 @@@ #include "../dpdefs/progsdefs.qh" #include "constants.qh" #include "../server/constants.qh" ++ #include "../common/turrets/sv_turrets.qh" #endif diff --cc qcsrc/common/triggers/teleporters.qc index 000000000,a58371647..5cd098100 mode 000000,100644..100644 --- a/qcsrc/common/triggers/teleporters.qc +++ b/qcsrc/common/triggers/teleporters.qc @@@ -1,0 -1,253 +1,253 @@@ + #include "teleporters.qh" + + #if defined(CSQC) + #elif defined(MENUQC) + #elif defined(SVQC) + #include "../../server/_all.qh" + #include "../../warpzonelib/common.qh" + #include "../../warpzonelib/util_server.qh" + #include "../../warpzonelib/server.qh" + #include "../constants.qh" + #include "../triggers/subs.qh" + #include "../util.qh" + #include "../../server/weapons/csqcprojectile.qh" + #include "../../server/autocvars.qh" + #include "../../server/constants.qh" + #include "../../server/defs.qh" + #include "../deathtypes.qh" - #include "../../server/tturrets/include/turrets_early.qh" ++ #include "../turrets/sv_turrets.qh" + #include "../vehicles/sv_vehicles.qh" + #include "../mapinfo.qh" + #include "../../server/anticheat.qh" + #endif + + #ifdef SVQC + + float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) + { + if (IS_PLAYER(player) && player.health >= 1) + { + TDEATHLOOP(org) + { + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + if(IS_PLAYER(head)) + if(head.health >= 1) + return 1; + } + } + return 0; + } + + void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax) + { + TDEATHLOOP(player.origin) + { + if (IS_PLAYER(player) && player.health >= 1) + { + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + { + if(IS_PLAYER(head)) + if(head.health >= 1) + ++tdeath_hit; + Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG, head.origin, '0 0 0'); + } + } + else // dead bodies and monsters gib themselves instead of telefragging + Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG, telefragger.origin, '0 0 0'); + } + } + + void spawn_tdeath(vector v0, entity e, vector v) + { + tdeath(e, e, e, '0 0 0', '0 0 0'); + } + + void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) + { + entity telefragger; + vector from; + + if(teleporter.owner) + telefragger = teleporter.owner; + else + telefragger = player; + + makevectors (to_angles); + + if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers + { + if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps + { + if(tflags & TELEPORT_FLAG_SOUND) + sound (player, CH_TRIGGER, "misc/teleport.wav", VOL_BASE, ATTEN_NORM); + if(tflags & TELEPORT_FLAG_PARTICLES) + { + Send_Effect("teleport", player.origin, '0 0 0', 1); + Send_Effect("teleport", to + v_forward * 32, '0 0 0', 1); + } + self.pushltime = time + 0.2; + } + } + + // Relocate the player + // assuming to allows PL_MIN to PL_MAX box and some more + from = player.origin; + setorigin (player, to); + player.oldorigin = to; // don't undo the teleport by unsticking + player.angles = to_angles; + player.fixangle = true; + player.velocity = to_velocity; + BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); + + makevectors(player.angles); + Reset_ArcBeam(player, v_forward); + UpdateCSQCProjectileAfterTeleport(player); + + if(IS_PLAYER(player)) + { + if(tflags & TELEPORT_FLAG_TDEATH) + if(player.takedamage && player.deadflag == DEAD_NO && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) + tdeath(player, teleporter, telefragger, telefragmin, telefragmax); + + // player no longer is on ground + player.flags &= ~FL_ONGROUND; + + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + player.oldvelocity = player.velocity; + + // reset tracking of who pushed you into a hazard (for kill credit) + if(teleporter.owner) + { + player.pusher = teleporter.owner; + player.pushltime = time + autocvar_g_maxpushtime; + player.istypefrag = player.BUTTON_CHAT; + } + else + { + player.pushltime = 0; + player.istypefrag = 0; + } + + player.lastteleporttime = time; + } + } + + entity Simple_TeleportPlayer(entity teleporter, entity player) + { + vector locout; + entity e; + float p; + + // Find the output teleporter + if(teleporter.enemy) + { + e = teleporter.enemy; + } + else + { + RandomSelection_Init(); + for(e = world; (e = find(e, targetname, teleporter.target)); ) + { + p = 1; + if(autocvar_g_telefrags_avoid) + { + locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + if(check_tdeath(player, locout, '0 0 0', '0 0 0')) + p = 0; + } + RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p); + } + e = RandomSelection_chosen_ent; + } + + if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } + + makevectors(e.mangle); + + if(e.speed) + if(vlen(player.velocity) > e.speed) + player.velocity = normalize(player.velocity) * max(0, e.speed); + + if(autocvar_g_teleport_maxspeed) + if(vlen(player.velocity) > autocvar_g_teleport_maxspeed) + player.velocity = normalize(player.velocity) * max(0, autocvar_g_teleport_maxspeed); + + locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); + + return e; + } + + void teleport_findtarget (void) + { + entity e; + float n; + + n = 0; + for(e = world; (e = find(e, targetname, self.target)); ) + { + ++n; + if(e.movetype == MOVETYPE_NONE) + waypoint_spawnforteleporter(self, e.origin, 0); + if(e.classname != "info_teleport_destination") + print("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n"); + } + + if(n == 0) + { + // no dest! + objerror ("Teleporter with nonexistant target"); + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + self.enemy = find(e, targetname, self.target); + } + else + { + // have to use random selection every single time + self.enemy = world; + } + + // now enable touch + self.touch = Teleport_Touch; + } + + entity Teleport_Find(vector mi, vector ma) + { + entity e; + for(e = world; (e = find(e, classname, "trigger_teleport")); ) + if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, world)) + return e; + return world; + } + + void WarpZone_PostTeleportPlayer_Callback(entity pl) + { + makevectors(pl.angles); + Reset_ArcBeam(pl, v_forward); + UpdateCSQCProjectileAfterTeleport(pl); + { + entity oldself = self; + self = pl; + anticheat_fixangle(); + self = oldself; + } + // "disown" projectiles after teleport + if(pl.owner) + if(pl.owner == pl.realowner) + { + if(!(pl.flags & FL_PROJECTILE)) + print("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, ".\n"); + pl.owner = world; + } + if(IS_PLAYER(pl)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + pl.oldvelocity = pl.velocity; + // reset teleport time tracking too (or multijump can cause insane speeds) + pl.lastteleporttime = time; + } + } + #endif diff --cc qcsrc/common/turrets/cl_turrets.qc index 2f8e77d2d,000000000..c39b938b8 mode 100644,000000..100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@@ -1,445 -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"; ++ txt = "gfx/vehicles/turret_moving.tga"; + else - txt = "gfx/vehicles/vth-stationary.tga"; ++ txt = "gfx/vehicles/turret_stationary.tga"; + - vector pz = drawgetimagesize(txt) * 0.25; - drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); ++ vector pz = drawgetimagesize(txt) * autocvar_cl_vehicles_crosshair_size; ++ drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.7, 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 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); +} + +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); + 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); + 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); + 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); + } + else + 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); + 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/sv_turrets.qc index 91d427fd8,000000000..4c309b240 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@@ -1,1394 -1,0 +1,1394 @@@ +#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_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) ++void turret_damage (entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector vforce) +{ + // 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; +} + +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) ++void turret_projectile_damage(entity inflictor, entity attacker, float damage, int 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_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(IS_VEHICLE(e_target)) + { + 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); + 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) ++ if (IS_TURRET(e)) + { + 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; + + entity e; + entity tur = get_turretinfo(tur_id); + if(tur.turretid == 0) + 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.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)) ++ if(MUTATOR_CALLHOOK(TurretSpawn, self)) + return false; + + return true; +} +#endif diff --cc qcsrc/common/turrets/sv_turrets.qh index c4ff13e91,000000000..8bba1c4a4 mode 100644,000000..100644 --- a/qcsrc/common/turrets/sv_turrets.qh +++ b/qcsrc/common/turrets/sv_turrets.qh @@@ -1,113 -1,0 +1,117 @@@ +#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! + ++void turrets_setframe(float _frame, float client_only); ++ ++float turret_initialize(float tur_id); ++ +/// 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.qc index 4c588e956,000000000..a99d1f1e9 mode 100644,000000..100644 --- a/qcsrc/common/turrets/turrets.qc +++ b/qcsrc/common/turrets/turrets.qc @@@ -1,78 -1,0 +1,77 @@@ +#include "all.qh" + +// TURRET PLUGIN SYSTEM +entity turret_info[TUR_MAXCOUNT]; +entity dummy_turret_info; + +void turrets_common_precache() +{ + 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/base.md3"); + precache_model ("models/turrets/rocket.md3"); + + precache_model ("models/turrets/c512.md3"); + precache_model ("models/marker.md3"); + +#ifdef TURRET_DEBUG - precache_model ("models/turrets/terrainbase.md3"); + precache_model ("models/turrets/c512.md3"); + precache_model ("models/pathlib/goodsquare.md3"); + precache_model ("models/pathlib/badsquare.md3"); + precache_model ("models/pathlib/square.md3"); + precache_model ("models/pathlib/edge.md3"); +#endif +} + +void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname) +{ + entity e; + turret_info[id - 1] = e = spawn(); + e.classname = "turret_info"; + e.turretid = id; + e.netname = shortname; + e.turret_name = mname; + e.turret_func = func; + e.mdl = modelname; + e.cvar_basename = shortname; + e.spawnflags = turretflags; + e.mins = min_s; + e.maxs = max_s; + e.model = strzone(strcat("models/turrets/", modelname)); + e.head_model = strzone(strcat("models/turrets/", headmodelname)); + + #ifndef MENUQC + turrets_common_precache(); + #endif +} +float t_null(float dummy) { return 0; } +void register_turrets_done() +{ + dummy_turret_info = spawn(); + dummy_turret_info.classname = "turret_info"; + dummy_turret_info.turretid = 0; // you can recognize dummies by this + dummy_turret_info.netname = ""; + dummy_turret_info.turret_name = "Turret"; + dummy_turret_info.turret_func = t_null; + dummy_turret_info.mdl = ""; + dummy_turret_info.mins = '-0 -0 -0'; + dummy_turret_info.maxs = '0 0 0'; + dummy_turret_info.model = ""; +} +entity get_turretinfo(float id) +{ + entity m; + if(id < TUR_FIRST || id > TUR_LAST) + return dummy_turret_info; + m = turret_info[id - 1]; + if(m) + return m; + return dummy_turret_info; +} diff --cc qcsrc/common/turrets/unit/ewheel.qc index 414ec4e82,000000000..495e60ff3 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; ++ // 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; ++ if (vlen(self.origin - self.pathcurrent.origin) < 64) ++ self.pathcurrent = self.pathcurrent.enemy; +#endif + - if (self.pathcurrent) - { ++ if (self.pathcurrent) ++ { + - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); ++ 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); - } ++ 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); ++ 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); +} + +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)); ++ 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; - } - 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; - } - case TR_DEATH: - { - self.velocity = '0 0 0'; ++ 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; ++ ++ Send_Effect("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; ++ } ++ 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; ++ } ++ case TR_DEATH: ++ { ++ self.velocity = '0 0 0'; + +#ifdef EWHEEL_FANCYPATH - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); ++ if (self.pathcurrent) ++ pathlib_deletepath(self.pathcurrent.owner); +#endif - self.pathcurrent = world; - - 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 - { ++ self.pathcurrent = world; ++ ++ 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; ++ self.pathcurrent = WALKER_PATH(self.origin,e.origin); ++ self.pathgoal = e; +#else - self.pathcurrent = e; ++ self.pathcurrent = e; +#endif - } - } - } - - self.iscreature = true; - self.teleportable = TELEPORT_NORMAL; - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/ewheel-base2.md3"); - precache_model ("models/turrets/ewheel-gun1.md3"); - return true; - } - } - - return true; ++ } ++ } ++ } ++ ++ self.iscreature = true; ++ self.teleportable = TELEPORT_NORMAL; ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/ewheel-base2.md3"); ++ precache_model ("models/turrets/ewheel-gun1.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC + +void ewheel_draw() +{ - float dt; ++ float dt; + - dt = time - self.move_time; - self.move_time = time; - if(dt <= 0) - return; ++ 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; ++ 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); ++ 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; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/flac.qc index 8068ab1d7,000000000..614df1d2f 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); ++ 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; ++ 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); ++ 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); ++ 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; ++ switch(req) ++ { ++ case TR_ATTACK: ++ { ++ entity proj; + - turret_tag_fire_update(); ++ 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; ++ proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); ++ Send_Effect("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; - } - case TR_THINK: - { - return true; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/flac.md3"); - return true; - } - } ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/flac.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_flac(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } + - return true; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/fusionreactor.qc index db4974566,000000000..459c1263a 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.attack_finished_single > time) ++ return 0; + - if (self.enemy.deadflag != DEAD_NO) - return 0; ++ if (self.enemy.deadflag != DEAD_NO) ++ return 0; + - if (self.enemy == world) - return 0; ++ if (self.enemy == world) ++ return 0; + - if (self.ammo < self.shot_dmg) - return 0; ++ if (self.ammo < self.shot_dmg) ++ return 0; + - if (self.enemy.ammo >= self.enemy.ammo_max) - 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 (vlen(self.enemy.origin - self.origin) > self.target_range) ++ return 0; + - if(self.team != self.enemy.team) - return 0; ++ if(self.team != self.enemy.team) ++ return 0; + - if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY)) - return 0; ++ if(!(self.enemy.ammo_flags & TFL_AMMO_ENERGY)) ++ return 0; + - return 1; ++ 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; - } - case TR_THINK: - { - self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max); - - return true; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/reactor.md3"); - return true; - } - } - - return true; ++ 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; ++ } ++ case TR_THINK: ++ { ++ self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max); ++ ++ return true; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/reactor.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_fusionreactor(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hellion.qc index d090ef7ce,000000000..f64534f86 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; ++ vector olddir,newdir; ++ vector pre_pos; ++ float itime; + - self.nextthink = time + 0.05; ++ self.nextthink = time + 0.05; + - olddir = normalize(self.velocity); ++ olddir = normalize(self.velocity); + - if(self.max_health < time) - turret_projectile_explode(); ++ 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)) - { ++ // 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; ++ // Make sure we dont return to tracking a respawned player ++ self.enemy = world; + - // Turn model - self.angles = vectoangles(self.velocity); ++ // Turn model ++ self.angles = vectoangles(self.velocity); + - if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) - turret_projectile_explode(); ++ 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)); ++ // 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); ++ UpdateCSQCProjectile(self); + - return; - } ++ return; ++ } + - // Enemy in range? - if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) - turret_projectile_explode(); ++ // 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; ++ // Predict enemy position ++ itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); ++ pre_pos = self.enemy.origin + self.enemy.velocity * itime; + - pre_pos = (pre_pos + self.enemy.origin) * 0.5; ++ pre_pos = (pre_pos + self.enemy.origin) * 0.5; + - // Find out the direction to that place - newdir = normalize(pre_pos - self.origin); ++ // Find out the direction to that place ++ newdir = normalize(pre_pos - self.origin); + - // Turn - newdir = normalize(olddir + newdir * 0.35); ++ // Turn ++ newdir = normalize(olddir + newdir * 0.35); + - // Turn model - self.angles = vectoangles(self.velocity); ++ // 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)); ++ // 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; ++ if (itime < 0.05) ++ self.think = turret_projectile_explode; + - UpdateCSQCProjectile(self); ++ 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; - } - 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; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/hellion.md3"); - return true; - } - } - - return true; ++ 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; ++ } ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/hellion.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hellion(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/hk.qc index 414c71e4b,000000000..64d9a8c6a 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 (e_target == world) ++ return 0; + - // If only this was used more.. - if (e_target.flags & FL_NOTARGET) - 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; ++ // 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; ++ // player ++ if (IS_CLIENT(e_target)) ++ { ++ if (self.owner.target_select_playerbias < 0) ++ return 0; + - if (e_target.deadflag != DEAD_NO) - 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; ++ // 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; ++ // Team check ++ if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) ++ return 0; + - return 1; ++ 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); - 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); - 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); - vl = trace_endpos; - fl = trace_fraction; - - // Trace right - traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); - vr = trace_endpos; - fr = trace_fraction; - - // Trace up - traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); - vu = trace_endpos; - fu = trace_fraction; - - // Trace down - traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); - vd = trace_endpos; - fd = trace_fraction; - - vl = normalize(vl - self.origin); - vr = normalize(vr - self.origin); - vu = normalize(vu - self.origin); - vd = normalize(vd - self.origin); - - // Panic tresh passed, find a single direction and turn as hard as we can - if (pt_seek == 1) - { - wishdir = v_right; - if (fl > fr) wishdir = -1 * v_right; - if (fu > fl) wishdir = v_up; - if (fd > fu) wishdir = -1 * v_up; - } - else - { - // Normalize our trace vectors to make a smooth path - wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) ); - } - - if (self.enemy) - { - if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target - wishdir = (wishdir * (1 - fe)) + (ve * fe); - } - } - else - { - // Got a clear path to target, speed up fast (if not at full speed) and go straight for it. - myspeed = vlen(self.velocity); - if (myspeed < (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); ++ 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); ++ 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); ++ 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); ++ vl = trace_endpos; ++ fl = trace_fraction; ++ ++ // Trace right ++ traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); ++ vr = trace_endpos; ++ fr = trace_fraction; ++ ++ // Trace up ++ traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); ++ vu = trace_endpos; ++ fu = trace_fraction; ++ ++ // Trace down ++ traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,false,self); ++ vd = trace_endpos; ++ fd = trace_fraction; ++ ++ vl = normalize(vl - self.origin); ++ vr = normalize(vr - self.origin); ++ vu = normalize(vu - self.origin); ++ vd = normalize(vd - self.origin); ++ ++ // Panic tresh passed, find a single direction and turn as hard as we can ++ if (pt_seek == 1) ++ { ++ wishdir = v_right; ++ if (fl > fr) wishdir = -1 * v_right; ++ if (fu > fl) wishdir = v_up; ++ if (fd > fu) wishdir = -1 * v_up; ++ } ++ else ++ { ++ // Normalize our trace vectors to make a smooth path ++ wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) ); ++ } ++ ++ if (self.enemy) ++ { ++ if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target ++ wishdir = (wishdir * (1 - fe)) + (ve * fe); ++ } ++ } ++ else ++ { ++ // Got a clear path to target, speed up fast (if not at full speed) and go straight for it. ++ myspeed = vlen(self.velocity); ++ if (myspeed < (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; - //} ++ //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); ++ 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; ++ 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; - } - 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; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/hk.md3"); - return true; - } - } - - return true; ++ 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; ++ } ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/hk.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_hk(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/machinegun.qc index f4a46212f,000000000..f952d4b15 mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/machinegun.qc +++ b/qcsrc/common/turrets/unit/machinegun.qc @@@ -1,79 -1,0 +1,81 @@@ +#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); } + ++void W_MachineGun_MuzzleFlash(void); ++ +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); ++ 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"); ++ W_MachineGun_MuzzleFlash(); ++ setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); + - return true; - } - case TR_THINK: - { - return true; - } - case TR_DEATH: - { - 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; ++ } ++ case TR_THINK: ++ { ++ return true; ++ } ++ case TR_DEATH: ++ { ++ 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; - } - 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; ++ } ++ 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; +} + +#endif // SVQC +#ifdef CSQC +float t_machinegun(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } + - return true; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/mlrs.qc index 254a58948,000000000..014220215 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; ++ 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); ++ 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; - } - 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; - } - case TR_DEATH: - { - 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; ++ } ++ 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/mlrs.md3"); - return true; - } - } ++ return true; ++ } ++ case TR_DEATH: ++ { ++ return true; ++ } ++ case TR_SETUP: ++ { ++ self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; ++ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; + - return true; ++ self.damage_flags |= TFL_DMG_HEADSHAKE; ++ self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; ++ self.volly_counter = self.shot_volly; ++ ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/mlrs.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_mlrs(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } + - return true; ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/phaser.qc index 32c392b66,000000000..449c09f85 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(); ++ 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; ++ 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_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; - } - 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; - } - case TR_DEATH: - { - 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; - } - 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; ++ 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_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; ++ } ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ 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; +} + +#endif // SVQC +#ifdef CSQC +float t_phaser(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma.qc index 095aa9d02,000000000..da86d3bf7 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; - } - 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; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/plasma.md3"); - return true; - } - } - - return true; ++ 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); ++ ++ Send_Effect("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; ++ ++ Send_Effect("laser_muzzleflash", self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); ++ if (self.tur_head.frame == 0) ++ self.tur_head.frame = 1; ++ } ++ ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/plasma.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/plasma_dual.qc index 110ae1dea,000000000..f7cf54941 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; - } - 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; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/plasmad.md3"); - return true; - } - } - - return true; ++ 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); ++ ++ ++ Send_Effect("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; ++ Send_Effect("laser_muzzleflash", self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); ++ self.tur_head.frame += 1; ++ } ++ ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/base.md3"); ++ precache_model ("models/turrets/plasmad.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma_dual(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/tesla.qc index 61a9f5e30,000000000..05dbffbcb 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; ++ 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; ++ // 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; - - 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; - } - case TR_THINK: - { - if(!self.active) - { - self.tur_head.avelocity = '0 0 0'; - 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; - - 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; - } - case TR_DEATH: - { - 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; - } - case TR_PRECACHE: - { - precache_model ("models/turrets/tesla_base.md3"); - precache_model ("models/turrets/tesla_head.md3"); - return true; - } - } - - return true; ++ 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; ++ ++ 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; ++ } ++ case TR_THINK: ++ { ++ if(!self.active) ++ { ++ self.tur_head.avelocity = '0 0 0'; ++ 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; ++ ++ 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; ++ } ++ case TR_DEATH: ++ { ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ precache_model ("models/turrets/tesla_base.md3"); ++ precache_model ("models/turrets/tesla_head.md3"); ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // SVQC +#ifdef CSQC +float t_tesla(float req) +{ - switch(req) - { - case TR_SETUP: - { - return true; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ switch(req) ++ { ++ case TR_SETUP: ++ { ++ return true; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/unit/walker.qc index 0d5d31705,000000000..4a63f9dee mode 100644,000000..100644 --- a/qcsrc/common/turrets/unit/walker.qc +++ b/qcsrc/common/turrets/unit/walker.qc @@@ -1,698 -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; ++ if (self.animflag == ANIM_MELEE) ++ return 0; + - return turret_firecheck(); ++ return turret_firecheck(); +} + +void walker_melee_do_dmg() +{ - vector where; - entity e; ++ vector where; ++ entity e; + - makevectors(self.angles); - where = self.origin + v_forward * 128; ++ 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 = 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; - } ++ e = e.chain; ++ } +} + +void walker_setnoanim() +{ - turrets_setframe(ANIM_NO, false); - self.animflag = self.frame; ++ 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); ++ 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; ++ self.health = self.health - damage; ++ self.velocity = self.velocity + vforce; + - if (self.health <= 0) - W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); ++ 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; ++ 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; ++ vector newdir; ++ self.nextthink = time; + - if (self.max_health < time) - { - self.think = walker_rocket_explode; - return; - } ++ 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; - } ++ if (vlen(self.origin - self.tur_shotorg) < 100 ) ++ { ++ self.think = walker_rocket_think; ++ return; ++ } + - newdir = steerlib_pull(self.tur_shotorg); - WALKER_ROCKET_MOVE; ++ newdir = steerlib_pull(self.tur_shotorg); ++ WALKER_ROCKET_MOVE; + - self.angles = vectoangles(self.velocity); ++ self.angles = vectoangles(self.velocity); +} + +void walker_rocket_loop2() +{ - vector newdir; ++ vector newdir; + - self.nextthink = time; ++ self.nextthink = time; + - if (self.max_health < time) - { - self.think = walker_rocket_explode; - return; - } ++ 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; - } ++ 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; ++ 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; ++ 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_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 ++ 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_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 +} + +.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; - } ++ 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); ++ // 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 (vlen(self.origin - self.pathcurrent.origin) < 64) ++ self.pathcurrent = self.pathcurrent.enemy; + - if(!self.pathcurrent) - return; ++ 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); ++ 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; - } - 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); - - return true; - } - case TR_DEATH: - { ++ 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); ++ Send_Effect("laser_muzzleflash", self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); ++ ++ 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); ++ ++ return true; ++ } ++ case TR_DEATH: ++ { +#ifdef WALKER_FANCYPATHING - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); ++ if (self.pathcurrent) ++ pathlib_deletepath(self.pathcurrent.owner); +#endif - self.pathcurrent = world; - - 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.teleportable = TELEPORT_NORMAL; - 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 - { ++ self.pathcurrent = world; ++ ++ 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.teleportable = TELEPORT_NORMAL; ++ 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; ++ self.pathcurrent = WALKER_PATH(self.origin, e.origin); ++ self.pathgoal = e; +#else - self.pathcurrent = e; ++ self.pathcurrent = e; +#endif - } - } - - 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; ++ } ++ 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; +} + +#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 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; - } - case TR_PRECACHE: - { - return true; - } - } - - return true; ++ 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; ++ } ++ case TR_PRECACHE: ++ { ++ return true; ++ } ++ } ++ ++ return true; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --cc qcsrc/common/turrets/util.qc index 3d6d95a76,000000000..477df7b68 mode 100644,000000..100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@@ -1,330 -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. +*/ +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; + } + + self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire")); + v_forward = normalize(v_forward); + + 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) ++ float bforce, float f_dmg, float f_velfactor, int 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); + + // if it is world we can't hurt it so stop now + if (trace_ent == world || trace_fraction == 1) + break; + + if (trace_ent.solid == SOLID_BSP) + break; + + // make the entity non-solid so we can hit the next one + trace_ent.railgunhit = true; + trace_ent.railgunhitloc = end; + trace_ent.railgunhitsolidbackup = trace_ent.solid; + + // stop if this is a wall + + // make the entity non-solid + trace_ent.solid = SOLID_NOT; + } + + endpoint = trace_endpos; + + // find all the entities the railgun hit and restore their solid state + ent = findfloat(world, railgunhit, true); + while (ent) + { + // restore their solid type + ent.solid = ent.railgunhitsolidbackup; + ent = findfloat(ent, railgunhit, true); + } + + // find all the entities the railgun hit and hurt them + ent = findfloat(world, railgunhit, true); + while (ent) + { + // get the details we need to call the damage function + hitloc = ent.railgunhitloc; + ent.railgunhitloc = '0 0 0'; + ent.railgunhitsolidbackup = SOLID_NOT; + ent.railgunhit = false; + + // apply the damage + if (ent.takedamage) + { + Damage (ent, self, self, f_dmg, deathtype, hitloc, force); + ent.velocity = ent.velocity * f_velfactor; + //ent.alpha = 0.25 + random() * 0.75; + } + + // advance to the next entity + ent = findfloat(ent, railgunhit, true); + } + trace_endpos = endpoint; +} + +#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/vehicles/sv_vehicles.qh index 000000000,0de7184fe..6c0a6bd17 mode 000000,100644..100644 --- a/qcsrc/common/vehicles/sv_vehicles.qh +++ b/qcsrc/common/vehicles/sv_vehicles.qh @@@ -1,0 -1,110 +1,110 @@@ + #ifndef VEHICLES_DEF_H + #define VEHICLES_DEF_H + #ifdef SVQC + -#include "../server/tturrets/include/turrets_early.qh" ++#include "../turrets/sv_turrets.qh" + #include "sv_vehicles.qh" + + // #define VEHICLES_USE_ODE + + // vehicle cvars + float autocvar_g_vehicles; + float autocvar_g_vehicles_enter; + float autocvar_g_vehicles_enter_radius; + float autocvar_g_vehicles_steal; + float autocvar_g_vehicles_steal_show_waypoint; + float autocvar_g_vehicles_crush_dmg; + float autocvar_g_vehicles_crush_force; + float autocvar_g_vehicles_delayspawn; + float autocvar_g_vehicles_delayspawn_jitter; + float autocvar_g_vehicles_allow_bots; + float autocvar_g_vehicles_teams; + float autocvar_g_vehicles_teleportable; + float autocvar_g_vehicles_vortex_damagerate = 0.5; + float autocvar_g_vehicles_machinegun_damagerate = 0.5; + float autocvar_g_vehicles_rifle_damagerate = 0.75; + float autocvar_g_vehicles_vaporizer_damagerate = 0.001; + float autocvar_g_vehicles_tag_damagerate = 5; + float autocvar_g_vehicles_weapon_damagerate = 1; + + // flags: + .int vehicle_flags; + + // vehicle definitions + .entity gun1; + .entity gun2; + .entity gun3; + .entity vehicle_shieldent; /// Entity to disply the shild effect on damage + .entity vehicle; + .entity vehicle_viewport; + .entity vehicle_hudmodel; + .entity vehicle_controller; + + .entity gunner1; + .entity gunner2; + + .float vehicle_health; /// If self is player this is 0..100 indicating precentage of health left on vehicle. If self is vehile, this is the real health value. + .float vehicle_energy; /// If self is player this is 0..100 indicating precentage of energy left on vehicle. If self is vehile, this is the real energy value. + .float vehicle_shield; /// If self is player this is 0..100 indicating precentage of shield left on vehicle. If self is vehile, this is the real shield value. + + .float vehicle_ammo1; /// If self is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already stored in vehicle_energy. If self is vehile, this is the real ammo1 value. + .float vehicle_reload1; /// If self is player this is 0..100 indicating percentage of primary reload status. If self is vehile, this is the real reload1 value. + .float vehicle_ammo2; /// If self is player this is 0..100 indicating percentage of secondary ammo left. If self is vehile, this is the real ammo2 value. + .float vehicle_reload2; /// If self is player this is 0..100 indicating percentage of secondary reload status. If self is vehile, this is the real reload2 value. + + .float sound_nexttime; + const float VOL_VEHICLEENGINE = 1; + + const float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05 + const float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A + const float SVC_UPDATEENTITY = 128; // Net.Protocol 0x80 + + const float VHSF_NORMAL = 0; + const float VHSF_FACTORY = 2; + + .int hud; + .float dmg_time; + + .int volly_counter; + + const int MAX_AXH = 4; + .entity AuxiliaryXhair[MAX_AXH]; + + .entity wps_intruder; + + .entity lock_target; + .float lock_strength; + .float lock_time; + .float lock_soundtime; + const float DAMAGE_TARGETDRONE = 10; + + // vehicle functions + .void(int _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns + .bool(int _imp) vehicles_impulse; + .int vehicle_weapon2mode; + .void(int exit_flags) vehicle_exit; + .bool() vehicle_enter; + const int VHEF_NORMAL = 0; /// User pressed exit key + const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying + const int VHEF_RELEASE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) + + float force_fromtag_power; + float force_fromtag_normpower; + vector force_fromtag_origin; + + float vehicles_exit_running; + + // macros + #define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \ + ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 + + .float vehicle_enter_delay; // prevent players jumping to and from vehicles instantly + + void vehicles_exit(float eject); + float vehicle_initialize(float vehicle_id, float nodrop); + bool vehicle_impulse(int imp); + bool vehicles_crushable(entity e); + + #endif + + #endif diff --cc qcsrc/server/autocvars.qh index 69e523c0c,0b6d04475..153013530 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@@ -504,16 -503,51 +503,16 @@@ float autocvar_g_triggerimpulse_accel_m float autocvar_g_triggerimpulse_accel_power; float autocvar_g_triggerimpulse_directional_multiplier; float autocvar_g_triggerimpulse_radial_multiplier; - float autocvar_g_turrets; + bool autocvar_g_turrets; float autocvar_g_turrets_aimidle_delay; - float autocvar_g_turrets_nofire; - float autocvar_g_turrets_reloadcvars; + bool autocvar_g_turrets_nofire; + bool autocvar_g_turrets_reloadcvars; float autocvar_g_turrets_targetscan_maxdelay; float autocvar_g_turrets_targetscan_mindelay; - float autocvar_g_use_ammunition; - float autocvar_g_waypointeditor; - float autocvar_g_waypointeditor_auto; - float autocvar_g_waypoints_for_items; -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; -float autocvar_g_turrets_unit_hellion_std_shot_speed_gain; -float autocvar_g_turrets_unit_hellion_std_shot_speed_max; -float autocvar_g_turrets_unit_hk_std_shot_speed; -float autocvar_g_turrets_unit_hk_std_shot_speed_accel; -float autocvar_g_turrets_unit_hk_std_shot_speed_accel2; -float autocvar_g_turrets_unit_hk_std_shot_speed_decel; -float autocvar_g_turrets_unit_hk_std_shot_speed_max; -float autocvar_g_turrets_unit_hk_std_shot_speed_turnrate; -float autocvar_g_turrets_unit_walker_speed_jump; -float autocvar_g_turrets_unit_walker_speed_roam; -float autocvar_g_turrets_unit_walker_speed_run; -float autocvar_g_turrets_unit_walker_speed_stop; -float autocvar_g_turrets_unit_walker_speed_swim; -float autocvar_g_turrets_unit_walker_speed_walk; -float autocvar_g_turrets_unit_walker_std_meele_dmg; -float autocvar_g_turrets_unit_walker_std_meele_force; -float autocvar_g_turrets_unit_walker_std_meele_range; -float autocvar_g_turrets_unit_walker_std_rocket_dmg; -float autocvar_g_turrets_unit_walker_std_rocket_force; -float autocvar_g_turrets_unit_walker_std_rocket_radius; -float autocvar_g_turrets_unit_walker_std_rocket_refire; -float autocvar_g_turrets_unit_walker_std_rocket_speed; -float autocvar_g_turrets_unit_walker_std_rocket_turnrate; -float autocvar_g_turrets_unit_walker_std_rockets_range; -float autocvar_g_turrets_unit_walker_std_rockets_range_min; -float autocvar_g_turrets_unit_walker_turn; -float autocvar_g_turrets_unit_walker_turn_walk; -float autocvar_g_turrets_unit_walker_turn_run; -float autocvar_g_turrets_unit_walker_turn_strafe; -float autocvar_g_turrets_unit_walker_turn_swim; + bool autocvar_g_use_ammunition; + bool autocvar_g_waypointeditor; + int autocvar_g_waypointeditor_auto; -int autocvar_g_waypoints_for_items; ++bool autocvar_g_waypoints_for_items; float autocvar_g_weapon_charge_colormod_blue_full; float autocvar_g_weapon_charge_colormod_blue_half; float autocvar_g_weapon_charge_colormod_green_full; diff --cc qcsrc/server/g_damage.qc index aab2f9ced,42a45b507..ba9ed8898 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@@ -1,34 -1,30 +1,29 @@@ #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 "../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) + #include "_all.qh" + + #include "g_hook.qh" + #include "mutators/mutators_include.qh" + #include "scores.qh" + #include "waypointsprites.qh" + #include "spawnpoints.qh" -#include "tturrets/include/turrets_early.qh" + #include "t_items.qh" + #include "../common/vehicles/sv_vehicles.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "../common/buffs.qh" + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/notifications.qh" + #include "../common/movetypes/movetypes.qh" + #include "../common/playerstats.qh" + #include "../common/teams.qh" + #include "../common/util.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../warpzonelib/common.qh" + + float Damage_DamageInfo_SendEntity(entity to, int sf) { WriteByte(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO); WriteShort(MSG_ENTITY, self.projectiledeathtype); @@@ -866,7 -845,7 +844,7 @@@ void Damage (entity targ, entity inflic else victim = targ; - if(IS_PLAYER(victim) || ((victim.turret_flags & TUR_FLAG_ISTURRET) && victim.active == ACTIVE_ACTIVE) || (victim.flags & FL_MONSTER)) - if(IS_PLAYER(victim) || IS_TURRET(victim) || IS_MONSTER(victim)) ++ if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim)) { if(DIFF_TEAM(victim, attacker) && !victim.frozen) { diff --cc qcsrc/server/g_damage.qh index 68f9a0a38,f291fff7a..a7916957d --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@@ -21,7 -21,8 +21,8 @@@ #include "../common/notifications.qh" #include "../common/deathtypes.qh" #include "mutators/mutators_include.qh" - #include "vehicles/vehicles_def.qh" - #include "tturrets/include/turrets_early.qh" ++ #include "../common/turrets/sv_turrets.qh" + #include "../common/vehicles/sv_vehicles.qh" #include "../csqcmodellib/sv_model.qh" #include "../common/playerstats.qh" #include "g_hook.qh" diff --cc qcsrc/server/g_world.qc index 24a3d211c,da2983c4a..70a31dfdc --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@@ -558,13 -561,11 +561,12 @@@ void spawnfunc___init_dedicated_server( self.classname = "worldspawn"; // safeguard against various stuff ;) // needs to be done so early because of the constants they create - CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + static_init(); + CALL_ACCUMULATED_FUNCTION(RegisterTurrets); - CALL_ACCUMULATED_FUNCTION(RegisterMonsters); - CALL_ACCUMULATED_FUNCTION(RegisterGametypes); CALL_ACCUMULATED_FUNCTION(RegisterNotifications); CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); - CALL_ACCUMULATED_FUNCTION(RegisterBuffs); + CALL_ACCUMULATED_FUNCTION(RegisterEffects); + CALL_ACCUMULATED_FUNCTION(RegisterVehicles); MapInfo_Enumerate(); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); @@@ -607,13 -610,11 +611,12 @@@ void spawnfunc_worldspawn (void server_is_dedicated = (stof(cvar_defstring("is_dedicated")) ? true : false); // needs to be done so early because of the constants they create - CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + static_init(); + CALL_ACCUMULATED_FUNCTION(RegisterTurrets); - CALL_ACCUMULATED_FUNCTION(RegisterMonsters); - CALL_ACCUMULATED_FUNCTION(RegisterGametypes); CALL_ACCUMULATED_FUNCTION(RegisterNotifications); CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); - CALL_ACCUMULATED_FUNCTION(RegisterBuffs); + CALL_ACCUMULATED_FUNCTION(RegisterEffects); + CALL_ACCUMULATED_FUNCTION(RegisterVehicles); ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid)); diff --cc qcsrc/server/miscfunctions.qc index eb457449e,d77728215..482b0d853 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@@ -1,33 -1,30 +1,30 @@@ - #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 "../common/mapinfo.qh" - #include "command/common.qh" - #include "../csqcmodellib/sv_model.qh" - #include "ipban.qh" - #endif + #include "miscfunctions.qh" + #include "_all.qh" + #include "antilag.qh" + #include "command/common.qh" + #include "constants.qh" + #include "g_hook.qh" + #include "ipban.qh" + #include "mutators/mutators_include.qh" -#include "tturrets/include/turrets_early.qh" + #include "t_items.qh" + #include "weapons/accuracy.qh" + #include "weapons/csqcprojectile.qh" + #include "weapons/selection.qh" + #include "../common/command/generic.qh" + #include "../common/constants.qh" + #include "../common/deathtypes.qh" + #include "../common/mapinfo.qh" + #include "../common/notifications.qh" + #include "../common/playerstats.qh" + #include "../common/teams.qh" + #include "../common/triggers/subs.qh" + #include "../common/urllib.qh" + #include "../common/util.qh" ++#include "../common/turrets/sv_turrets.qh" + #include "../common/weapons/all.qh" + #include "../csqcmodellib/sv_model.qh" + #include "../warpzonelib/anglestransform.qh" + #include "../warpzonelib/server.qh" void crosshair_trace(entity pl) { diff --cc qcsrc/server/miscfunctions.qh index f19f53fd0,a3dd45e51..02863b8b4 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@@ -8,6 -8,6 +8,7 @@@ #include "../common/constants.qh" #include "../common/mapinfo.qh" ++#include "../common/turrets/turrets.qh" #ifdef RELEASE #define cvar_string_normal builtin_cvar_string @@@ -120,6 -157,10 +158,10 @@@ const string STR_OBSERVER = "observer" #define IS_REAL_CLIENT(v) (clienttype(v) == CLIENTTYPE_REAL) #define IS_NOT_A_CLIENT(v) (clienttype(v) == CLIENTTYPE_NOTACLIENT) + #define IS_MONSTER(v) (v.flags & FL_MONSTER) + #define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE) -#define IS_TURRET(v) (v.turrcaps_flags & TFL_TURRCAPS_ISTURRET) ++#define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET) + #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); ) #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(IS_CLIENT(v)) #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(IS_REAL_CLIENT(v)) diff --cc qcsrc/server/mutators/mutators_include.qc index 95f9604a0,ae13c7d02..7a241e4e7 --- a/qcsrc/server/mutators/mutators_include.qc +++ b/qcsrc/server/mutators/mutators_include.qc @@@ -45,7 -45,8 +45,8 @@@ #include "../../common/notifications.qh" #include "../../common/deathtypes.qh" #include "mutators_include.qh" - #include "../vehicles/vehicles_def.qh" - #include "../tturrets/include/turrets_early.qh" ++ #include "../../common/turrets/sv_turrets.qh" + #include "../../common/vehicles/sv_vehicles.qh" #include "../campaign.qh" #include "../../common/campaign_common.qh" #include "../../common/mapinfo.qh" @@@ -73,12 -74,11 +74,11 @@@ #include "../playerdemo.qh" #include "../round_handler.qh" #include "../item_key.qh" - #include "../secret.qh" #include "../pathlib/pathlib.qh" - #include "../vehicles/vehicles.qh" - #include "../tturrets/include/turrets.qh" ++ #include "../../common/vehicles/vehicles.qh" #endif - #include "base.qc" + #include "../../common/mutators/base.qh" #include "gamemode_assault.qc" #include "gamemode_ca.qc" #include "gamemode_ctf.qc" diff --cc qcsrc/server/progs.src index 527c42712,2ed1e03d6..10113b0de --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@@ -98,18 -94,20 +94,26 @@@ weapons/weaponsystem.q ../common/nades.qc ../common/net_notice.qc ../common/notifications.qc + ../common/physics.qc ../common/playerstats.qc + ../common/p2mathlib.qc ../common/test.qc + ../common/viewloc.qc + ../common/triggers/include.qc ../common/urllib.qc ../common/util.qc + ../common/vehicles/vehicles_include.qc + + ../common/items/all.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/weapons/all.qc // TODO ../csqcmodellib/sv_model.qc