From: Mario Date: Wed, 9 Oct 2013 05:50:09 +0000 (-0700) Subject: Clean up turrets system a bit X-Git-Tag: xonotic-v0.8.2~2052^2~11 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=04bbc270623d9f75f19d00c93b6f5b2c51734bf4;p=xonotic%2Fxonotic-data.pk3dir.git Clean up turrets system a bit --- diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 8e36cc9b54..6dd93184af 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -104,6 +104,7 @@ void CSQC_Init(void) // needs to be done so early because of the constants they create CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterTurrets); CALL_ACCUMULATED_FUNCTION(RegisterGametypes); CALL_ACCUMULATED_FUNCTION(RegisterNotifications); CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); @@ -122,7 +123,6 @@ void CSQC_Init(void) Casings_Precache(); DamageInfo_Precache(); Vehicles_Precache(); - turrets_precache(); Tuba_Precache(); CSQCPlayer_Precache(); diff --git a/qcsrc/client/damage.qc b/qcsrc/client/damage.qc index cd7ac7bfa4..3bab63b939 100644 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@ -319,7 +319,7 @@ void Ent_DamageInfo(float isNew) pointparticles(particleeffectnum("electro_impact"), self.origin, w_backoff * 1000, 1); break; - case DEATH_TURRET_WALK_MEELE: + case DEATH_TURRET_WALK_MELEE: sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_MIN); pointparticles(particleeffectnum("TE_SPARK"), self.origin, w_backoff * 1000, 1); break; diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index 114f0a5b5c..c69a86019f 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -34,6 +34,9 @@ autocvars.qh ../common/notifications.qh // must be after autocvars ../common/deathtypes.qh // must be after notifications +../common/turrets/turrets.qh +../common/turrets/cl_turrets.qh + damage.qh ../csqcmodellib/interpolate.qh @@ -45,8 +48,6 @@ movetypes.qh prandom.qh bgmscript.qh noise.qh -tturrets.qh -../server/tturrets/include/turrets_early.qh ../server/movelib.qc main.qh vehicles/vehicles.qh @@ -117,7 +118,9 @@ command/cl_cmd.qc ../warpzonelib/mathlib.qc ../warpzonelib/common.qc ../warpzonelib/client.qc -tturrets.qc + +../common/turrets/cl_turrets.qc +../common/turrets/turrets.qc player_skeleton.qc ../common/animdecide.qc diff --git a/qcsrc/client/tturrets.qc b/qcsrc/client/tturrets.qc deleted file mode 100644 index 161bfa385a..0000000000 --- a/qcsrc/client/tturrets.qc +++ /dev/null @@ -1,685 +0,0 @@ -string tid2info_base; -string tid2info_head; -string tid2info_name; -vector tid2info_min; -vector tid2info_max; - -void turret_tid2info(float _tid); -void turret_precache(float _tid); -float turret_is_precache[TID_LAST]; - -void turrets_precache() -{ - turret_precache(TID_COMMON); -} - -void turret_precache(float _tid) -{ - if (!turret_is_precache[TID_COMMON]) - { - precache_sound ("weapons/rocket_impact.wav"); - precache_model ("models/turrets/base-gib1.md3"); - precache_model ("models/turrets/base-gib2.md3"); - precache_model ("models/turrets/base-gib3.md3"); - precache_model ("models/turrets/base-gib4.md3"); - precache_model ("models/turrets/head-gib1.md3"); - precache_model ("models/turrets/head-gib2.md3"); - precache_model ("models/turrets/head-gib3.md3"); - precache_model ("models/turrets/head-gib4.md3"); - precache_model ("models/turrets/terrainbase.md3"); - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/rocket.md3"); - } - turret_tid2info(_tid); - if(turret_is_precache[_tid]) - return; - - switch(_tid) - { - case TID_EWHEEL: - precache_model ("models/turrets/ewheel-base2.md3"); - precache_model ("models/turrets/ewheel-gun1.md3"); - break; - case TID_FLAC: - precache_model ("models/turrets/flac.md3"); - break; - case TID_FUSION: - precache_model ("models/turrets/reactor.md3"); - break; - case TID_HELLION: - precache_model ("models/turrets/hellion.md3"); - break; - case TID_HK: - precache_model ("models/turrets/hk.md3"); - break; - case TID_MACHINEGUN: - precache_model ("models/turrets/machinegun.md3"); - precache_sound ("weapons/uzi_fire.wav"); - break; - case TID_MLRS: - precache_model ("models/turrets/mlrs.md3"); - break; - case TID_PHASER: - precache_model ("models/turrets/phaser.md3"); - precache_model ("models/turrets/phaser_beam.md3"); - precache_sound ("turrets/phaser.wav"); - break; - case TID_PLASMA: - precache_model ("models/turrets/plasma.md3"); - break; - case TID_PLASMA_DUAL: - precache_model ("models/turrets/plasmad.md3"); - break; - case TID_TESLA: - precache_model ("models/turrets/tesla_head.md3"); - precache_model ("models/turrets/tesla_base.md3"); - break; - case TID_WALKER: - precache_model ("models/turrets/walker_head_minigun.md3"); - precache_model ("models/turrets/walker_body.md3"); - precache_sound ("weapons/uzi_fire.wav"); - break; - } - turret_is_precache[_tid] = TRUE; -} - -void turret_tid2info(float _tid) -{ - tid2info_base = "models/turrets/base.md3"; - tid2info_min = '-32 -32 0'; - tid2info_max = '32 32 64'; - - switch(_tid) - { - case TID_EWHEEL: - tid2info_base = "models/turrets/ewheel-base2.md3"; - tid2info_head = "models/turrets/ewheel-gun1.md3"; - tid2info_name = "eWheel"; - break; - case TID_FLAC: - tid2info_head = "models/turrets/flac.md3"; - tid2info_name = "Flac Cannon"; - break; - case TID_FUSION: - tid2info_head = "models/turrets/reactor.md3"; - tid2info_name = "Fusion Reactor"; - tid2info_min = '-34 -34 0'; - tid2info_max = '34 34 90'; - break; - case TID_HELLION: - tid2info_head = "models/turrets/hellion.md3"; - tid2info_name = "Hellion"; - break; - case TID_HK: - tid2info_head = "models/turrets/hk.md3"; - tid2info_name = "Hunter-Killer"; - break; - case TID_MACHINEGUN: - tid2info_head = "models/turrets/machinegun.md3"; - tid2info_name = "Machinegun"; - break; - case TID_MLRS: - tid2info_head = "models/turrets/mlrs.md3"; - tid2info_name = "MLRS"; - break; - case TID_PHASER: - tid2info_head = "models/turrets/phaser.md3"; - tid2info_name = "Phaser"; - break; - case TID_PLASMA: - tid2info_head = "models/turrets/plasma.md3"; - tid2info_name = "Plasma"; - break; - case TID_PLASMA_DUAL: - tid2info_head = "models/turrets/plasmad.md3"; - tid2info_name = "Dual Plasma"; - break; - case TID_TESLA: - tid2info_base = "models/turrets/tesla_base.md3"; - tid2info_head = "models/turrets/tesla_head.md3"; - tid2info_name = "Tesla coil"; - tid2info_min = '-60 -60 0'; - tid2info_max ='60 60 128'; - break; - case TID_WALKER: - tid2info_base = "models/turrets/walker_body.md3"; - tid2info_head = "models/turrets/walker_head_minigun.md3"; - tid2info_name = "Walker"; - tid2info_min = '-70 -70 0'; - tid2info_max = '70 70 95'; - break; - } -} - -void turret_remove() -{ - remove(self.tur_head); - //remove(self.enemy); - self.tur_head = world; -} - -.vector glowmod; -void turret_changeteam() -{ - switch(self.team - 1) - { - case NUM_TEAM_1: // Red - self.glowmod = '2 0 0'; - self.teamradar_color = '1 0 0'; - break; - - case NUM_TEAM_2: // Blue - self.glowmod = '0 0 2'; - self.teamradar_color = '0 0 1'; - break; - - case NUM_TEAM_3: // Yellow - self.glowmod = '1 1 0'; - self.teamradar_color = '1 1 0'; - break; - - case NUM_TEAM_4: // Pink - self.glowmod = '1 0 1'; - self.teamradar_color = '1 0 1'; - break; - } - - if(self.team) - self.colormap = 1024 + (self.team - 1) * 17; - - self.tur_head.colormap = self.colormap; - self.tur_head.glowmod = self.glowmod; - -} - -void turret_head_draw() -{ - self.drawmask = MASK_NORMAL; -} - -void turret_draw() -{ - float dt; - - dt = time - self.move_time; - self.move_time = time; - if(dt <= 0) - return; - - self.tur_head.angles += dt * self.tur_head.move_avelocity; - - if (self.health < 127) - { - dt = random(); - - if(dt < 0.03) - te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); - } - - if(self.health < 85) - if(dt < 0.01) - pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); - - if(self.health < 32) - if(dt < 0.015) - pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); - -} - -void turret_draw2d() -{ - if(self.netname == "") - return; - - if(!autocvar_g_waypointsprite_turrets) - return; - - if(autocvar_cl_hidewaypoints) - return; - - float dist = vlen(self.origin - view_origin); - float t = (GetPlayerColor(player_localnum) + 1); - - vector o; - string txt; - - if(autocvar_cl_vehicles_hud_tactical) - if(dist < 10240 && t != self.team) - { - // TODO: Vehicle tactical hud - o = project_3d_to_2d(self.origin + '0 0 32'); - if(o_z < 0 - || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) - || o_y < (vid_conheight * waypointsprite_edgeoffset_top) - || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) - return; // Dont draw wp's for turrets out of view - o_z = 0; - if(hud != HUD_NORMAL) - { - switch(hud) - { - case HUD_SPIDERBOT: - case HUD_WAKIZASHI: - case HUD_RAPTOR: - case HUD_BUMBLEBEE: - if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) - txt = "gfx/vehicles/vth-mover.tga"; - else - txt = "gfx/vehicles/vth-stationary.tga"; - - vector pz = drawgetimagesize(txt) * 0.25; - drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); - break; - } - } - } - - if(dist > self.maxdistance) - return; - - string spriteimage = self.netname; - float a = self.alpha * autocvar_hud_panel_fg_alpha; - vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); - - - if(self.maxdistance > waypointsprite_normdistance) - a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); - else if(self.maxdistance > 0) - a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; - - if(rgb == '0 0 0') - { - self.teamradar_color = '1 0 1'; - print(sprintf("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_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); -} - -void turret_ewheel_draw() -{ - float dt; - - dt = time - self.move_time; - self.move_time = time; - if(dt <= 0) - return; - - fixedmakevectors(self.angles); - setorigin(self, self.origin + self.velocity * dt); - self.tur_head.angles += dt * self.tur_head.move_avelocity; - self.angles_y = self.move_angles_y; - - if (self.health < 127) - if(random() < 0.05) - te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); -} - -void(entity e, entity tagentity, string tagname) setattachment = #443; -void turret_construct() -{ - if(self.tur_head == world) - self.tur_head = spawn(); - - turret_tid2info(self.turret_type); - self.netname = tid2info_name; - - setorigin(self, self.origin); - setmodel(self, tid2info_base); - setmodel(self.tur_head, tid2info_head); - setsize(self, tid2info_min, tid2info_max); - setsize(self.tur_head, '0 0 0', '0 0 0'); - - if(self.turret_type == TID_EWHEEL) - setattachment(self.tur_head, self, ""); - else - setattachment(self.tur_head, self, "tag_head"); - - self.tur_head.classname = "turret_head"; - self.tur_head.owner = self; - self.tur_head.move_movetype = MOVETYPE_NOCLIP; - self.move_movetype = MOVETYPE_NOCLIP; - self.tur_head.angles = self.angles; - self.health = 255; - self.solid = SOLID_BBOX; - self.tur_head.solid = SOLID_NOT; - self.movetype = MOVETYPE_NOCLIP; - self.tur_head.movetype = MOVETYPE_NOCLIP; - self.draw = turret_draw; - self.entremove = turret_remove; - self.drawmask = MASK_NORMAL; - self.tur_head.drawmask = MASK_NORMAL; - self.anim_start_time = 0; - self.draw2d = turret_draw2d; - self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; - self.teamradar_color = '1 0 0'; - self.alpha = 1; - - if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER) - { - self.gravity = 1; - self.movetype = MOVETYPE_BOUNCE; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_origin = self.origin; - self.move_time = time; - switch(self.turret_type) - { - case TID_EWHEEL: - self.draw = turret_ewheel_draw; - break; - case TID_WALKER: - self.draw = turret_walker_draw; - break; - - } - } -} - -entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); -void turret_gibboom(); -void turret_gib_draw() -{ - Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); - - self.drawmask = MASK_NORMAL; - - if(self.cnt) - { - if(time >= self.nextthink) - { - turret_gibboom(); - remove(self); - } - } - else - { - self.alpha = bound(0, self.nextthink - time, 1); - if(self.alpha < ALPHA_MIN_VISIBLE) - remove(self); - } -} - -void turret_gibboom() -{ - float i; - - sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); - - for (i = 1; i < 5; i = i + 1) - turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); -} - -entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode) -{ - entity gib; - - traceline(_from, _to, MOVE_NOMONSTERS, world); - if(trace_startsolid) - return world; - - gib = spawn(); - setorigin(gib, _from); - setmodel(gib, _model); - gib.colormod = _cmod; - gib.solid = SOLID_CORPSE; - gib.draw = turret_gib_draw; - gib.cnt = _explode; - setsize(gib, '-1 -1 -1', '1 1 1'); - if(_explode) - { - gib.nextthink = time + 0.2 * (autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15)); - gib.effects = EF_FLAME; - } - else - gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); - - gib.gravity = 1; - gib.move_movetype = MOVETYPE_BOUNCE; - gib.move_origin = _from; - setorigin(gib, _from); - gib.move_velocity = _to; - gib.move_avelocity = prandomvec() * 32; - gib.move_time = time; - gib.damageforcescale = 1; - gib.classname = "turret_gib"; - - return gib; -} - -void turret_die() -{ - - sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); - turret_tid2info(self.turret_type); - if (!autocvar_cl_nogibs) - { - // Base - if(self.turret_type == TID_EWHEEL) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 400' + '0.1 0.1 1' * (random() * 400), '-1 -1 -1', TRUE); - else if (self.turret_type == TID_WALKER) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', self.velocity + '0 0 300' + '0.1 0.1 1' * (random() * 200), '-1 -1 -1', TRUE); - else if (self.turret_type == TID_TESLA) - turret_gibtoss(tid2info_base, self.origin + '0 0 18', '0 0 200', '-1 -1 -1', FALSE); - else - { - if (random() > 0.5) - { - turret_gibtoss("models/turrets/base-gib2.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib3.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - turret_gibtoss("models/turrets/base-gib4.md3", self.origin + '0 0 8', '0 0 50' + randomvec() * 150, '0 0 0', FALSE); - } - else - turret_gibtoss("models/turrets/base-gib1.md3", self.origin + '0 0 8', '0 0 0', '0 0 0', TRUE); - - entity headgib = turret_gibtoss(tid2info_head, self.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', TRUE); - 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.turret_type = ReadByte(); - - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - setorigin(self, self.origin); - - self.angles_x = ReadAngle(); - self.angles_y = ReadAngle(); - - turret_precache(self.turret_type); - turret_construct(); - self.colormap = 1024; - self.glowmod = '0 1 1'; - self.tur_head.colormap = self.colormap; - self.tur_head.glowmod = self.glowmod; - } - - if(sf & TNSF_ANG) - { - if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. - self.tur_head = spawn(); - - self.tur_head.move_angles_x = ReadShort(); - self.tur_head.move_angles_y = ReadShort(); - //self.tur_head.angles = self.angles + self.tur_head.move_angles; - self.tur_head.angles = self.tur_head.move_angles; - } - - if(sf & TNSF_AVEL) - { - if(self.tur_head == world) // aparenly this can happpen before TNSF_SETUP. great. - self.tur_head = spawn(); - - self.tur_head.move_avelocity_x = ReadShort(); - self.tur_head.move_avelocity_y = ReadShort(); - } - - if(sf & TNSF_MOVE) - { - self.origin_x = ReadShort(); - self.origin_y = ReadShort(); - self.origin_z = ReadShort(); - setorigin(self, self.origin); - - self.velocity_x = ReadShort(); - self.velocity_y = ReadShort(); - self.velocity_z = ReadShort(); - - self.move_angles_y = ReadShort(); - - self.move_time = time; - self.move_velocity = self.velocity; - self.move_origin = self.origin; - } - - if(sf & TNSF_ANIM) - { - self.frame1time = ReadCoord(); - self.frame = ReadByte(); - } - - if(sf & TNSF_STATUS) - { - float _tmp; - _tmp = ReadByte(); - 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 --git a/qcsrc/client/tturrets.qh b/qcsrc/client/tturrets.qh deleted file mode 100644 index cf74f48ad2..0000000000 --- a/qcsrc/client/tturrets.qh +++ /dev/null @@ -1,3 +0,0 @@ -void ent_turret(); -void turrets_precache(); -.entity tur_head; diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 4ca8b3780c..d00aa29972 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -277,6 +277,59 @@ void GenericCommand_dumpnotifs(float request) } } +void GenericCommand_dumpturrets(float request) +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + #ifdef SVQC + tur_config_file = -1; + tur_config_alsoprint = -1; + string filename = argv(1); + + if(filename == "") + { + filename = "turrets_dump.cfg"; + tur_config_alsoprint = FALSE; + } + else if(filename == "-") + { + filename = "turrets_dump.cfg"; + tur_config_alsoprint = TRUE; + } + tur_config_file = fopen(filename, FILE_WRITE); + + if(tur_config_file >= 0) + { + Dump_Turret_Settings(); + print(sprintf("Dumping turrets... File located in ^2data/data/%s^7.\n", filename)); + fclose(tur_config_file); + tur_config_file = -1; + tur_config_alsoprint = -1; + } + else + { + print(sprintf("^1Error: ^7Could not open file '%s'!\n", filename)); + } + #else + print(_("Turrets dump command only works with sv_cmd.\n")); + #endif + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpturrets [filename]")); + print(" Where 'filename' is the file to write (default is turrets_dump.cfg),\n"); + print(" if supplied with '-' output to console as well as default,\n"); + print(" if left blank, it will only write to default.\n"); + return; + } + } +} + void GenericCommand_maplist(float request, float argc) { switch(request) @@ -585,6 +638,7 @@ void GenericCommand_(float request) 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("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("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") \ GENERIC_COMMAND("qc_curl", GenericCommand_qc_curl(request, arguments), "Queries a URL") \ diff --git a/qcsrc/common/deathtypes.qh b/qcsrc/common/deathtypes.qh index cc7154d4b6..8a35450099 100644 --- a/qcsrc/common/deathtypes.qh +++ b/qcsrc/common/deathtypes.qh @@ -35,7 +35,7 @@ DEATHTYPE(DEATH_TURRET_PLASMA, DEATH_SELF_TURRET_PLASMA, NO_MSG, NORMAL_POS) \ DEATHTYPE(DEATH_TURRET_TESLA, DEATH_SELF_TURRET_TESLA, NO_MSG, NORMAL_POS) \ DEATHTYPE(DEATH_TURRET_WALK_GUN, DEATH_SELF_TURRET_WALK_GUN, NO_MSG, NORMAL_POS) \ - DEATHTYPE(DEATH_TURRET_WALK_MEELE, DEATH_SELF_TURRET_WALK_MEELE, NO_MSG, NORMAL_POS) \ + DEATHTYPE(DEATH_TURRET_WALK_MELEE, DEATH_SELF_TURRET_WALK_MELEE, NO_MSG, NORMAL_POS) \ DEATHTYPE(DEATH_TURRET_WALK_ROCKET, DEATH_SELF_TURRET_WALK_ROCKET, NO_MSG, DEATH_TURRET_LAST) \ DEATHTYPE(DEATH_VH_BUMB_DEATH, DEATH_SELF_VH_BUMB_DEATH, DEATH_MURDER_VH_BUMB_DEATH, DEATH_VHFIRST) \ DEATHTYPE(DEATH_VH_BUMB_GUN, NO_MSG, DEATH_MURDER_VH_BUMB_GUN, NORMAL_POS) \ diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index 0214ec3d61..edcfb81236 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -406,7 +406,7 @@ void Send_Notification_WOCOVA( MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_PLASMA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got served some superheated plasma from a turret%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_TESLA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was electrocuted by a Tesla turret%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_GUN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got served a lead enrichment by a Walker turret%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MEELE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was impaled by a Walker turret%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_TURRET_WALK_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was blasted away by a Walker turret%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_BUMB_DEATH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_CRUSH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was crushed by a vehicle%s%s"), "") \ @@ -732,7 +732,7 @@ void Send_Notification_WOCOVA( MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_PLASMA, NO_MSG, INFO_DEATH_SELF_TURRET_PLASMA, CENTER_DEATH_SELF_TURRET) \ MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_TESLA, NO_MSG, INFO_DEATH_SELF_TURRET_TESLA, CENTER_DEATH_SELF_TURRET) \ MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_GUN, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_GUN, CENTER_DEATH_SELF_TURRET_WALK) \ - MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MEELE, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_MEELE, CENTER_DEATH_SELF_TURRET_WALK) \ + MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_MELEE, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_MELEE, CENTER_DEATH_SELF_TURRET_WALK) \ MSG_MULTI_NOTIF(1, DEATH_SELF_TURRET_WALK_ROCKET, NO_MSG, INFO_DEATH_SELF_TURRET_WALK_ROCKET, CENTER_DEATH_SELF_TURRET_WALK) \ MSG_MULTI_NOTIF(1, DEATH_SELF_VH_BUMB_DEATH, NO_MSG, INFO_DEATH_SELF_VH_BUMB_DEATH, CENTER_DEATH_SELF_VH_BUMB_DEATH) \ MSG_MULTI_NOTIF(1, DEATH_SELF_VH_CRUSH, NO_MSG, INFO_DEATH_SELF_VH_CRUSH, CENTER_DEATH_SELF_VH_CRUSH) \ diff --git a/qcsrc/common/turrets/all.qh b/qcsrc/common/turrets/all.qh new file mode 100644 index 0000000000..04bb10f6a5 --- /dev/null +++ b/qcsrc/common/turrets/all.qh @@ -0,0 +1,12 @@ +#include "unit/ewheel.qc" +#include "unit/flac.qc" +#include "unit/fusionreactor.qc" +#include "unit/hellion.qc" +#include "unit/hk.qc" +#include "unit/machinegun.qc" +#include "unit/mlrs.qc" +#include "unit/phaser.qc" +#include "unit/plasma.qc" +#include "unit/plasma_dual.qc" +#include "unit/tesla.qc" +#include "unit/walker.qc" diff --git a/qcsrc/common/turrets/checkpoint.qc b/qcsrc/common/turrets/checkpoint.qc new file mode 100644 index 0000000000..dde5c28292 --- /dev/null +++ b/qcsrc/common/turrets/checkpoint.qc @@ -0,0 +1,82 @@ +/** + turret_checkpoint +**/ + + +//.entity checkpoint_target; + +/* +#define checkpoint_cache_who flagcarried +#define checkpoint_cache_from lastrocket +#define checkpoint_cache_to selected_player +*/ + +.entity pathgoal; + +/* +entity path_makeorcache(entity forwho,entity start, entity end) +{ + entity oldself; + entity pth; + oldself = self; + self = forwho; + + //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR); + + self = oldself; + return pth; +} +*/ + +void turret_checkpoint_use() +{ +} + +#if 0 +void turret_checkpoint_think() +{ + if(self.enemy) + te_lightning1(self,self.origin, self.enemy.origin); + + self.nextthink = time + 0.25; +} +#endif +/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32) +-----------KEYS------------ +target: .targetname of next waypoint in chain. +wait: Pause at this point # seconds. +-----------SPAWNFLAGS----------- +---------NOTES---------- +If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly. +If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached. +*/ +//float tc_acum; +void turret_checkpoint_init() +{ + traceline(self.origin + '0 0 16', self.origin - '0 0 1024', MOVE_WORLDONLY, self); + setorigin(self, trace_endpos + '0 0 32'); + + if(self.target != "") + { + self.enemy = find(world, targetname, self.target); + if(self.enemy == world) + dprint("A turret_checkpoint faild to find its target!\n"); + } + //self.think = turret_checkpoint_think; + //self.nextthink = time + tc_acum + 0.25; + //tc_acum += 0.25; +} + +void spawnfunc_turret_checkpoint() +{ + setorigin(self,self.origin); + self.think = turret_checkpoint_init; + self.nextthink = time + 0.2; +} + +// Compat. +void spawnfunc_walker_checkpoint() +{ + self.classname = "turret_checkpoint"; + spawnfunc_turret_checkpoint(); +} diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc new file mode 100644 index 0000000000..224f70377c --- /dev/null +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -0,0 +1,474 @@ +void turret_remove() +{ + remove(self.tur_head); + //remove(self.enemy); + self.tur_head = world; +} + +.vector glowmod; +void turret_changeteam() +{ + switch(self.team - 1) + { + case NUM_TEAM_1: // Red + self.glowmod = '2 0 0'; + self.teamradar_color = '1 0 0'; + break; + + case NUM_TEAM_2: // Blue + self.glowmod = '0 0 2'; + self.teamradar_color = '0 0 1'; + break; + + case NUM_TEAM_3: // Yellow + self.glowmod = '1 1 0'; + self.teamradar_color = '1 1 0'; + break; + + case NUM_TEAM_4: // Pink + self.glowmod = '1 0 1'; + self.teamradar_color = '1 0 1'; + break; + } + + if(self.team) + self.colormap = 1024 + (self.team - 1) * 17; + + self.tur_head.colormap = self.colormap; + self.tur_head.glowmod = self.glowmod; + +} + +void turret_head_draw() +{ + self.drawmask = MASK_NORMAL; +} + +void turret_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + self.tur_head.angles += dt * self.tur_head.move_avelocity; + + if (self.health < 127) + { + dt = random(); + + if(dt < 0.03) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); + } + + if(self.health < 85) + if(dt < 0.01) + pointparticles(particleeffectnum("smoke_large"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + + if(self.health < 32) + if(dt < 0.015) + pointparticles(particleeffectnum("smoke_small"), (self.origin + (randomvec() * 80)), '0 0 0', 1); + +} + +void turret_draw2d() +{ + if(self.netname == "") + return; + + if(!autocvar_g_waypointsprite_turrets) + return; + + if(autocvar_cl_hidewaypoints) + return; + + float dist = vlen(self.origin - view_origin); + float t = (GetPlayerColor(player_localnum) + 1); + + vector o; + string txt; + + if(autocvar_cl_vehicles_hud_tactical) + if(dist < 10240 && t != self.team) + { + // TODO: Vehicle tactical hud + o = project_3d_to_2d(self.origin + '0 0 32'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + o_z = 0; + if(hud != HUD_NORMAL) + { + switch(hud) + { + case HUD_SPIDERBOT: + case HUD_WAKIZASHI: + case HUD_RAPTOR: + case HUD_BUMBLEBEE: + if((get_turretinfo(self.turretid)).spawnflags & TUR_FLAG_MOVE) + txt = "gfx/vehicles/vth-mover.tga"; + else + txt = "gfx/vehicles/vth-stationary.tga"; + + vector pz = drawgetimagesize(txt) * 0.25; + drawpic(o - pz * 0.5, txt, pz , '1 1 1', 0.75, DRAWFLAG_NORMAL); + break; + } + } + } + + if(dist > self.maxdistance) + return; + + string spriteimage = self.netname; + float a = self.alpha * autocvar_hud_panel_fg_alpha; + vector rgb = spritelookupcolor(spriteimage, self.teamradar_color); + + + if(self.maxdistance > waypointsprite_normdistance) + a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); + else if(self.maxdistance > 0) + a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha; + + if(rgb == '0 0 0') + { + self.teamradar_color = '1 0 1'; + print(sprintf("WARNING: sprite of name %s has no color, using pink so you notice it\n", spriteimage)); + } + + txt = self.netname; + if(autocvar_g_waypointsprite_spam && waypointsprite_count >= autocvar_g_waypointsprite_spam) + txt = _("Spam"); + else + txt = spritelookuptext(spriteimage); + + if(time - floor(time) > 0.5 && t == self.team) + { + if(self.helpme && time < self.helpme) + { + a *= SPRITE_HELPME_BLINK; + txt = sprintf(_("%s under attack!"), txt); + } + else + a *= spritelookupblinkvalue(spriteimage); + } + + if(autocvar_g_waypointsprite_uppercase) + txt = strtoupper(txt); + + if(a > 1) + { + rgb *= a; + a = 1; + } + + if(a <= 0) + return; + + rgb = fixrgbexcess(rgb); + + o = project_3d_to_2d(self.origin + '0 0 64'); + if(o_z < 0 + || o_x < (vid_conwidth * waypointsprite_edgeoffset_left) + || o_y < (vid_conheight * waypointsprite_edgeoffset_top) + || o_x > (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) + || o_y > (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom))) + return; // Dont draw wp's for turrets out of view + + o_z = 0; + + float edgedistance_min, crosshairdistance; + edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), + (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), + (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, + (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); + + float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); + + crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); + + t = waypointsprite_scale * vidscale; + a *= waypointsprite_alpha; + + { + a = a * (1 - (1 - waypointsprite_distancefadealpha) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_distancefadescale) * (bound(0, dist/waypointsprite_distancefadedistance, 1))); + } + if (edgedistance_min < waypointsprite_edgefadedistance) { + a = a * (1 - (1 - waypointsprite_edgefadealpha) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + t = t * (1 - (1 - waypointsprite_edgefadescale) * (1 - bound(0, edgedistance_min/waypointsprite_edgefadedistance, 1))); + } + if(crosshairdistance < waypointsprite_crosshairfadedistance) { + a = a * (1 - (1 - waypointsprite_crosshairfadealpha) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); + } + + o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); + o = drawspritetext(o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); + drawhealthbar( + o, + 0, + self.health / 255, + '0 0 0', + '0 0 0', + 0.5 * SPRITE_HEALTHBAR_WIDTH * t, + 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, + SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, + SPRITE_HEALTHBAR_BORDER * t, + 0, + rgb, + a * SPRITE_HEALTHBAR_BORDERALPHA, + rgb, + a * SPRITE_HEALTHBAR_HEALTHALPHA, + DRAWFLAG_NORMAL + ); +} + +void(entity e, entity tagentity, string tagname) setattachment = #443; +void turret_construct() +{ + entity tur = get_turretinfo(self.turretid); + + if(self.tur_head == world) + self.tur_head = spawn(); + + self.netname = TUR_NAME(self.turretid); + + setorigin(self, self.origin); + setmodel(self, tur.model); + setmodel(self.tur_head, tur.head_model); + setsize(self, tur.mins, tur.maxs); + setsize(self.tur_head, '0 0 0', '0 0 0'); + + if(self.turretid == TUR_EWHEEL) + setattachment(self.tur_head, self, ""); + else + setattachment(self.tur_head, self, "tag_head"); + + self.tur_head.classname = "turret_head"; + self.tur_head.owner = self; + self.tur_head.move_movetype = MOVETYPE_NOCLIP; + self.move_movetype = MOVETYPE_NOCLIP; + self.tur_head.angles = self.angles; + self.health = 255; + self.solid = SOLID_BBOX; + self.tur_head.solid = SOLID_NOT; + self.movetype = MOVETYPE_NOCLIP; + self.tur_head.movetype = MOVETYPE_NOCLIP; + self.draw = turret_draw; + self.entremove = turret_remove; + self.drawmask = MASK_NORMAL; + self.tur_head.drawmask = MASK_NORMAL; + self.anim_start_time = 0; + self.draw2d = turret_draw2d; + self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist; + self.teamradar_color = '1 0 0'; + self.alpha = 1; + + TUR_ACTION(self.turretid, TR_SETUP); +} + +entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, float _explode); +void turret_gibboom(); +void turret_gib_draw() +{ + Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); + + self.drawmask = MASK_NORMAL; + + if(self.cnt) + { + if(time >= self.nextthink) + { + turret_gibboom(); + remove(self); + } + } + else + { + self.alpha = bound(0, self.nextthink - time, 1); + if(self.alpha < ALPHA_MIN_VISIBLE) + remove(self); + } +} + +void turret_gibboom() +{ + float i; + + sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); + pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1); + + for (i = 1; i < 5; i = i + 1) + turret_gibtoss(strcat("models/turrets/head-gib", ftos(i), ".md3"), self.origin + '0 0 2', self.velocity + randomvec() * 700, '0 0 0', FALSE); +} + +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 --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh new file mode 100644 index 0000000000..b616782187 --- /dev/null +++ b/qcsrc/common/turrets/cl_turrets.qh @@ -0,0 +1 @@ +void ent_turret(); diff --git a/qcsrc/common/turrets/config.qc b/qcsrc/common/turrets/config.qc new file mode 100644 index 0000000000..895b469f7a --- /dev/null +++ b/qcsrc/common/turrets/config.qc @@ -0,0 +1,62 @@ +// ========================== +// Turret Config Generator +// ========================== + +void T_Config_Queue_Swap(float root, float child, entity pass) +{ + string oldroot = tur_config_queue[root]; + tur_config_queue[root] = tur_config_queue[child]; + tur_config_queue[child] = oldroot; +} + +float T_Config_Queue_Compare(float root, float child, entity pass) +{ + float i, r, c; + + for(i = 1; i <= 100; ++i) + { + r = str2chr(tur_config_queue[root], i); + c = str2chr(tur_config_queue[child], i); + if(r == c) { continue; } + else if(c > r) { return -1; } + else { return 1; } + } + + return 0; +} + +void Dump_Turret_Settings(void) +{ + float i, x, totalsettings = 0; + for(i = TUR_FIRST; i <= TUR_LAST; ++i) + { + // step 1: clear the queue + TUR_CONFIG_COUNT = 0; + for(x = 0; x <= MAX_TUR_CONFIG; ++x) + { tur_config_queue[x] = string_null; } + + // step 2: build new queue + TUR_ACTION(i, TR_CONFIG); + + // step 3: sort queue + heapsort(TUR_CONFIG_COUNT, T_Config_Queue_Swap, T_Config_Queue_Compare, world); + + // step 4: write queue + TUR_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, TUR_NAME(i))) + for(x = 0; x <= TUR_CONFIG_COUNT; ++x) + { TUR_CONFIG_WRITETOFILE(tur_config_queue[x]) } + TUR_CONFIG_WRITETOFILE("// }}}\n") + + // step 5: debug info + print(sprintf("#%d: %s: %d settings...\n", i, TUR_NAME(i), TUR_CONFIG_COUNT)); + totalsettings += TUR_CONFIG_COUNT; + } + + // clear queue now that we're finished + TUR_CONFIG_COUNT = 0; + for(x = 0; x <= MAX_TUR_CONFIG; ++x) + { tur_config_queue[x] = string_null; } + + // extra information + print(sprintf("Totals: %d turrets, %d settings\n", (i - 1), totalsettings)); +} diff --git a/qcsrc/common/turrets/config.qh b/qcsrc/common/turrets/config.qh new file mode 100644 index 0000000000..ad3a0c72fe --- /dev/null +++ b/qcsrc/common/turrets/config.qh @@ -0,0 +1,29 @@ +// ========================== +// Turret Config Generator +// ========================== + +void Dump_Turret_Settings(void); +float tur_config_file; +float tur_config_alsoprint; + +#define MAX_TUR_CONFIG 256 +float TUR_CONFIG_COUNT; +string tur_config_queue[MAX_TUR_CONFIG]; + +#define TUR_CONFIG_QUEUE(a) { \ + tur_config_queue[TUR_CONFIG_COUNT] = a; \ + ++TUR_CONFIG_COUNT; } + +#define TUR_CONFIG_WRITETOFILE(a) { \ + fputs(tur_config_file, a); \ + if(tur_config_alsoprint) { print(a); } } + +#define TUR_CONFIG_WRITE_CVARS(turret,name) \ + { TUR_CONFIG_QUEUE( \ + sprintf("set g_turrets_unit_%s_%s %g\n", #turret, #name, \ + cvar(sprintf("g_turrets_unit_%s_%s", #turret, #name)))) } \ + +#define TUR_CONFIG_SETTINGS(tursettings) \ + #define TUR_ADD_CVAR(turret,name) TUR_CONFIG_WRITE_CVARS(turret,name) \ + tursettings \ + #undef TUR_ADD_CVAR diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc new file mode 100644 index 0000000000..aa15989626 --- /dev/null +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -0,0 +1,1399 @@ +// ========================= +// SVQC Turret Properties +// ========================= + + +// 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 + { + // FIXME: this cant be the best way to do this.. + prep = pre_pos; + + for(i = 0; i < 4; ++i) + { + distance = vlen(prep - self.tur_shotorg); + impact_time = distance / self.shot_speed; + prep = pre_pos + self.enemy.velocity * impact_time; + } + + prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); + + if(self.aim_flags & TFL_AIM_ZPREDICT) + if not(self.enemy.flags & FL_ONGROUND) + if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE) + { + float vz; + prep_z = pre_pos_z; + vz = self.enemy.velocity_z; + for(i = 0; i < impact_time; i += sys_frametime) + { + vz = vz - (autocvar_sv_gravity * sys_frametime); + prep_z = prep_z + vz * sys_frametime; + } + } + pre_pos = prep; + } + else + pre_pos = pre_pos + self.enemy.velocity * mintime; + } + + if(self.aim_flags & TFL_AIM_SPLASH) + { + //tracebox(pre_pos + '0 0 32',self.enemy.mins,self.enemy.maxs,pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + traceline(pre_pos + '0 0 32',pre_pos -'0 0 64',MOVE_WORLDONLY,self.enemy); + if(trace_fraction != 1.0) + pre_pos = trace_endpos; + } + + return pre_pos; +} + +float turret_targetscore_support(entity _turret,entity _target) +{ + float score; // Total score + float s_score = 0, d_score; + + if (_turret.enemy == _target) s_score = 1; + + d_score = min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist); + + score = (d_score * _turret.target_select_rangebias) + + (s_score * _turret.target_select_samebias); + + return score; +} + +/* +* Generic bias aware score system. +*/ +float turret_targetscore_generic(entity _turret, entity _target) +{ + float d_dist; // Defendmode Distance + float score; // Total score + float d_score; // Distance score + float a_score; // Angular score + float m_score = 0; // missile score + float p_score = 0; // player score + float ikr; // ideal kill range + + if (_turret.tur_defend) + { + d_dist = vlen(real_origin(_target) - _turret.tur_defend.origin); + ikr = vlen(_turret.origin - _turret.tur_defend.origin); + d_score = 1 - d_dist / _turret.target_range; + } + else + { + // Make a normlized value base on the targets distance from our optimal killzone + ikr = _turret.target_range_optimal; + d_score = min(ikr, tvt_dist) / max(ikr, tvt_dist); + } + + a_score = 1 - tvt_thadf / _turret.aim_maxrotate; + + if ((_turret.target_select_missilebias > 0) && (_target.flags & FL_PROJECTILE)) + m_score = 1; + + if ((_turret.target_select_playerbias > 0) && IS_CLIENT(_target)) + p_score = 1; + + d_score = max(d_score, 0); + a_score = max(a_score, 0); + m_score = max(m_score, 0); + p_score = max(p_score, 0); + + score = (d_score * _turret.target_select_rangebias) + + (a_score * _turret.target_select_anglebias) + + (m_score * _turret.target_select_missilebias) + + (p_score * _turret.target_select_playerbias); + + if(_turret.target_range < vlen(_turret.tur_shotorg - real_origin(_target))) + { + //dprint("Wtf?\n"); + score *= 0.001; + } + +#ifdef TURRET_DEBUG + string sd,sa,sm,sp,ss; + string sdt,sat,smt,spt; + + sd = ftos(d_score); + d_score *= _turret.target_select_rangebias; + sdt = ftos(d_score); + + //sv = ftos(v_score); + //v_score *= _turret.target_select_samebias; + //svt = ftos(v_score); + + sa = ftos(a_score); + a_score *= _turret.target_select_anglebias; + sat = ftos(a_score); + + sm = ftos(m_score); + m_score *= _turret.target_select_missilebias; + smt = ftos(m_score); + + sp = ftos(p_score); + p_score *= _turret.target_select_playerbias; + spt = ftos(p_score); + + + ss = ftos(score); + bprint("^3Target scores^7 \[ ",_turret.netname, " \] ^3for^7 \[ ", _target.netname," \]\n"); + bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n"); + bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n"); + bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n"); + bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n"); + bprint("^3Total (w/bias):\[^1",ss,"\]\n"); + +#endif + + return score; +} + +// Generic damage handling +void() turret_respawn; +void turret_hide() +{ + self.effects |= EF_NODRAW; + self.nextthink = time + self.respawntime - 0.2; + self.think = turret_respawn; +} + +void turret_die() +{ + self.deadflag = DEAD_DEAD; + self.tur_head.deadflag = self.deadflag; + +// Unsolidify and hide real parts + self.solid = SOLID_NOT; + self.tur_head.solid = self.solid; + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + self.health = 0; + +// Go boom + //RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,DEATH_TURRET,world); + + if(self.damage_flags & TFL_DMG_DEATH_NORESPAWN) + { + TUR_ACTION(self.turretid, TR_DEATH); + + remove(self.tur_head); + remove(self); + } + else + { + // Setup respawn + self.SendFlags |= TNSF_STATUS; + self.nextthink = time + 0.2; + self.think = turret_hide; + + TUR_ACTION(self.turretid, TR_DEATH); + } +} + +void turret_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + // Enougth allready! + if(self.deadflag == DEAD_DEAD) + return; + + // Inactive turrets take no damage. (hm..) + if not (self.active) + return; + + if (teamplay) + if (self.team == attacker.team) + { + // This does not happen anymore. Re-enable if you fix that. + if(IS_REAL_CLIENT(attacker)) + sprint(attacker, "\{1}Turret tells you: I'm on your team!\n"); + + 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 not (ent.turret_scale_damage) ent.turret_scale_damage = 1; + if not (ent.turret_scale_range) ent.turret_scale_range = 1; + if not (ent.turret_scale_refire) ent.turret_scale_refire = 1; + if not (ent.turret_scale_ammo) ent.turret_scale_ammo = 1; + if not (ent.turret_scale_aim) ent.turret_scale_aim = 1; + if not (ent.turret_scale_health) ent.turret_scale_health = 1; + if not (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, 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, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void turret_projectile_touch() +{ + PROJECTILE_TOUCH; + turret_projectile_explode(); +} + +void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.velocity += vforce; + self.health -= damage; + //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets + if(self.health <= 0) + W_PrepareExplosionByDamage(self.owner, turret_projectile_explode); +} + +entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) +{ + entity proj; + + sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); + proj = spawn (); + setorigin(proj, self.tur_shotorg); + setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size); + proj.owner = self; + proj.realowner = self; + proj.bot_dodge = TRUE; + proj.bot_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 not (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.owner == e_turret) + return -0.5; + + if not(checkpvs(e_target.origin, e_turret)) + return -1; + + if not (e_target) + return -2; + + if(g_onslaught) + if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + return - 3; + + if (validate_flags & TFL_TARGETSELECT_NO) + return -4; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return -5; + + // Cant touch this + if(e_target.vehicle_flags & VHF_ISVEHICLE) + { + if (e_target.vehicle_health <= 0) + return -6; + } + else if (e_target.health <= 0) + return -6; + + // player + if (IS_CLIENT(e_target)) + { + if not (validate_flags & TFL_TARGETSELECT_PLAYERS) + return -7; + + if (e_target.deadflag != DEAD_NO) + return -8; + } + + // enemy turrets + if(validate_flags & TFL_TARGETSELECT_NOTURRETS) + if(e_target.owner.tur_head == e_target) + if(e_target.team != e_turret.team) // Dont break support units. + return -9; + + // Missile + if (e_target.flags & FL_PROJECTILE) + if not (validate_flags & TFL_TARGETSELECT_MISSILES) + return -10; + + if (validate_flags & TFL_TARGETSELECT_MISSILESONLY) + if not (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 not (self.spawnflags & TSF_NO_AMMO_REGEN) + if (self.ammo < self.ammo_max) + self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max); + + // Inactive turrets needs to run the think loop, + // So they can handle animation and wake up if need be. + if not (self.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 not(self.aim_flags & TFL_AIM_NO) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if not(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 not(self.aim_flags & TFL_AIM_NO) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch + if not(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 not(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 not(self.aim_flags & TFL_AIM_NO) + self.tur_aimpos = turret_aim_generic(); + + // Turn & pitch? + if not(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) + { + 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 not(autocvar_g_turrets) + return FALSE; + + entity e; + entity tur = get_turretinfo(tur_id); + if(tur.turretid == 0) + return FALSE; // invalid turret + + e = find(world, classname, "turret_manager"); + if not (e) + { + e = spawn(); + e.classname = "turret_manager"; + e.think = turrets_manager_think; + e.nextthink = time + 2; + } + + if not(self.spawnflags & TSF_SUSPENDED) + builtin_droptofloor(); + + self.cvar_basename = tur.cvar_basename; + load_unit_settings(self, self.cvar_basename, 0); + + if not(self.team || teamplay) { self.team = MAX_SHOT_DISTANCE; } + if not(self.ticrate) { self.ticrate = ((self.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } + if not(self.health) { self.health = 1000; } + if not(self.shot_refire) { self.shot_refire = 1; } + if not(self.tur_shotorg) { self.tur_shotorg = '50 0 50'; } + if not(self.turret_flags) { self.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; } + if not(self.damage_flags) { self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; } + if not(self.aim_flags) { self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; } + if not(self.track_type) { self.track_type = TFL_TRACKTYPE_STEPMOTOR; } + if not(self.track_flags) { self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROTATE; } + if not(self.ammo_flags) { self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; } + if not(self.target_select_flags){ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_ANGLELIMITS; } + if not(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 not(self.track_accel_pitch) { self.track_accel_pitch = 0.5; } + if not(self.track_accel_rotate) { self.track_accel_rotate = 0.5; } + if not(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 not(self.tur_defend) + if(self.target != "") + { + self.tur_defend = find(world, targetname, self.target); + if (self.tur_defend == world) + { + self.target = ""; + dprint("Turret has invalid defendpoint!\n"); + } + } + + if (self.tur_defend) + self.idle_aim = self.tur_head.angles + angleofs(self.tur_head, self.tur_defend); + else + self.idle_aim = '0 0 0'; + +#ifdef TURRET_DEBUG + self.tur_debug_start = self.nextthink; + while (vlen(self.tur_debug_rvec) < 2) + self.tur_debug_rvec = randomvec() * 4; + + self.tur_debug_rvec_x = fabs(self.tur_debug_rvec_x); + self.tur_debug_rvec_y = fabs(self.tur_debug_rvec_y); + self.tur_debug_rvec_z = fabs(self.tur_debug_rvec_z); +#endif + + turret_link(); + turret_respawn(); + turret_tag_fire_update(); + + TUR_ACTION(tur_id, TR_SETUP); + + if(MUTATOR_CALLHOOK(TurretSpawn)) + return FALSE; + + return TRUE; +} \ No newline at end of file diff --git a/qcsrc/common/turrets/sv_turrets.qh b/qcsrc/common/turrets/sv_turrets.qh new file mode 100644 index 0000000000..f89ef1581b --- /dev/null +++ b/qcsrc/common/turrets/sv_turrets.qh @@ -0,0 +1,106 @@ +// 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; + +/// updates aim org, shot org, shot dir and enemy org for selected turret +void turret_do_updates(entity e_turret); +.vector tur_shotdir_updated; + +.float() turret_firecheckfunc; // TODO: deprecate! + +/// Function to use for target evaluation. usualy turret_targetscore_generic +.float(entity _turret, entity _target) turret_score_target; + +.float(entity e_target,entity e_sender) turret_addtarget; + +.entity pathcurrent; + +float turret_count; + +// debugging +// Uncomment below to enable various debug output. +//#define TURRET_DEBUG +//#define TURRET_DEBUG_TARGETVALIDATE +//#define TURRET_DEBUG_TARGETSELECT +#ifdef TURRET_DEBUG +.float tur_debug_dmg_t_h; // total damage that hit something (can be more than tur_debug_dmg_t_f since it should count radius damage) +.float tur_debug_dmg_t_f; // total damage +.float tur_debug_start; // turret initialization time +.float tur_debug_tmr1; // random timer +.float tur_debug_tmr2; // random timer +.float tur_debug_tmr3; // random timer +.vector tur_debug_rvec; // random vector +#endif + +// aiming +vector tvt_thadv; // turret head angle diff vector, updated by a successful call to turret_validate_target +vector tvt_tadv; // turret angle diff vector, updated by a successful call to turret_validate_target +float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target +float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target +float tvt_dist; // turret distance, updated by a successful call to turret_validate_target diff --git a/qcsrc/common/turrets/targettrigger.qc b/qcsrc/common/turrets/targettrigger.qc new file mode 100644 index 0000000000..01f2413900 --- /dev/null +++ b/qcsrc/common/turrets/targettrigger.qc @@ -0,0 +1,38 @@ +void spawnfunc_turret_targettrigger(); +void turret_targettrigger_touch(); + +void turret_targettrigger_touch() +{ + entity e; + if (self.cnt > time) return; + entity oldself; + oldself = self; + + e = find(world, targetname, self.target); + while (e) + { + if (e.turret_flags & TUR_FLAG_RECIEVETARGETS) + { + self = e; + if(e.turret_addtarget) + e.turret_addtarget(other,oldself); + } + + e = find(e, targetname, oldself.target); + } + + oldself.cnt = time + 0.5; + + self = oldself; +} + +/*QUAKED turret_targettrigger (.5 .5 .5) ? +*/ +void spawnfunc_turret_targettrigger() +{ + if not(autocvar_g_turrets) { remove(self); return; } + + InitTrigger (); + + self.touch = turret_targettrigger_touch; +} diff --git a/qcsrc/common/turrets/turrets.qc b/qcsrc/common/turrets/turrets.qc new file mode 100644 index 0000000000..01fb6607ab --- /dev/null +++ b/qcsrc/common/turrets/turrets.qc @@ -0,0 +1,79 @@ +#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/terrainbase.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/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(); + func(TR_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 --git a/qcsrc/common/turrets/turrets.qh b/qcsrc/common/turrets/turrets.qh new file mode 100644 index 0000000000..9f90d3e520 --- /dev/null +++ b/qcsrc/common/turrets/turrets.qh @@ -0,0 +1,197 @@ +// turret requests +#define TR_SETUP 1 // (BOTH) setup turret data +#define TR_THINK 2 // (SERVER) logic to run every frame +#define TR_DEATH 3 // (SERVER) called when turret dies +#define TR_PRECACHE 4 // (BOTH) precaches models/sounds used by this turret +#define TR_ATTACK 5 // (SERVER) called when turret attacks +#define TR_CONFIG 6 // (ALL) + +// functions: +entity get_turretinfo(float id); + +// fields: +.entity tur_head; + +// target selection flags +.float target_select_flags; +.float target_validate_flags; +const float TFL_TARGETSELECT_NO = 2; // don't automatically find targets +const float TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets +const float TFL_TARGETSELECT_PLAYERS = 8; // target players +const float TFL_TARGETSELECT_MISSILES = 16; // target projectiles +const float TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events +const float TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection +const float TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range +const float TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates +const float TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered +const float TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates +const float TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets +const float TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range +const float TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles + +// aim flags +.float aim_flags; +const float TFL_AIM_NO = 1; // no aiming +const float TFL_AIM_SPLASH = 2; // aim for ground around the target's feet +const float TFL_AIM_LEAD = 4; // try to predict target movement +const float TFL_AIM_SHOTTIMECOMPENSATE = 8; // compensate for shot traveltime when leading +const float TFL_AIM_ZPREDICT = 16; // predict target's z position at impact +const float TFL_AIM_SIMPLE = 32; // aim at player's current location + +// tracking flags +.float track_flags; +const float TFL_TRACK_NO = 2; // don't move head +const float TFL_TRACK_PITCH = 4; // pitch head +const float TFL_TRACK_ROTATE = 8; // rotate head + +// prefire checks +.float firecheck_flags; +const float TFL_FIRECHECK_DEAD = 4; // don't attack dead targets (zombies?) +const float TFL_FIRECHECK_DISTANCES = 8; // another range check +const float TFL_FIRECHECK_LOS = 16; // line of sight +const float TFL_FIRECHECK_AIMDIST = 32; // consider distance impactpoint<->aimspot +const float TFL_FIRECHECK_REALDIST = 64; // consider enemy origin<->impactpoint +const float TFL_FIRECHECK_ANGLEDIST = 128; // consider angular diff head<->aimspot +const float TFL_FIRECHECK_TEAMCHECK = 256; // don't attack teammates +const float TFL_FIRECHECK_AFF = 512; // try to avoid any friendly fire +const float TFL_FIRECHECK_AMMO_OWN = 1024; // own ammo needs to be larger than damage dealt +const float TFL_FIRECHECK_AMMO_OTHER = 2048; // target's ammo needs to be less than max +const float TFL_FIRECHECK_REFIRE = 4096; // check single attack finished delays +const float TFL_FIRECHECK_NO = 16384; // no prefire checks + +// attack flags +.float shoot_flags; +const float TFL_SHOOT_NO = 64; // no attacking +const float TFL_SHOOT_VOLLY = 2; // fire in vollies +const float TFL_SHOOT_VOLLYALWAYS = 4; // always do a full volly, even if target is lost +const float TFL_SHOOT_HITALLVALID = 8; // loop through all valid targets +const float TFL_SHOOT_CLEARTARGET = 16; // lose target after attack (after volly is done if in volly mode) +const float TFL_SHOOT_CUSTOM = 32; // custom attacking + +// turret capabilities +.float turret_flags; +const float TUR_FLAG_NONE = 0; // no abilities +const float TUR_FLAG_SNIPER = 2; // sniping turret +const float TUR_FLAG_SPLASH = 4; // can deal splash damage +const float TUR_FLAG_HITSCAN = 8; // hit scan +const float TUR_FLAG_MULTIGUN = 16; // multiple guns +const float TUR_FLAG_GUIDED = 32; // laser guided projectiles +const float TUR_FLAG_SLOWPROJ = 64; // turret fires slow projectiles +const float TUR_FLAG_MEDPROJ = 128; // turret fires medium projectiles +const float TUR_FLAG_FASTPROJ = 256; // turret fires fast projectiles +const float TUR_FLAG_PLAYER = 512; // can damage players +const float TUR_FLAG_MISSILE = 1024; // can damage missiles +const float TUR_FLAG_SUPPORT = 2048; // supports other units +const float TUR_FLAG_AMMOSOURCE = 4096; // can provide ammunition +const float TUR_FLAG_RECIEVETARGETS = 8192; // can recieve targets from external sources +const float TUR_FLAG_MOVE = 16384; // can move +const float TUR_FLAG_ROAM = 32768; // roams around if not attacking +const float TUR_FLAG_ISTURRET = 65536; // identifies this unit as a turret + +// ammo types +#define ammo_flags currentammo +const float TFL_AMMO_NONE = 64; // doesn't use ammo +const float TFL_AMMO_ENERGY = 2; // uses power +const float TFL_AMMO_BULLETS = 4; // uses bullets +const float TFL_AMMO_ROCKETS = 8; // uses explosives +const float TFL_AMMO_RECHARGE = 16; // regenerates ammo +const float TFL_AMMO_RECIEVE = 32; // can recieve ammo from support units + +// damage flags +.float damage_flags; +const float TFL_DMG_NO = 256; // doesn't take damage +const float TFL_DMG_YES = 2; // can be damaged +const float TFL_DMG_TEAM = 4; // can be damaged by teammates +const float TFL_DMG_RETALIATE = 8; // target attackers +const float TFL_DMG_RETALIATE_TEAM = 16; // target attackers, even if on same team +const float TFL_DMG_TARGETLOSS = 32; // loses target when damaged +const float TFL_DMG_AIMSHAKE = 64; // damage throws off aim +const float TFL_DMG_HEADSHAKE = 128; // damage shakes head +const float TFL_DMG_DEATH_NORESPAWN = 256; // no re-spawning + +// spawn flags +const float TSF_SUSPENDED = 1; +const float TSF_TERRAINBASE = 2; // currently unused +const float TSF_NO_AMMO_REGEN = 4; // disable builtin ammo regeneration +const float TSF_NO_PATHBREAK = 8; // don't break path to chase enemies, will still fire at them if possible +const float TSL_NO_RESPAWN = 16; // don't re-spawn +const float TSL_ROAM = 32; // roam while idle + +// send flags +const float TNSF_UPDATE = 2; +const float TNSF_STATUS = 4; +const float TNSF_SETUP = 8; +const float TNSF_ANG = 16; +const float TNSF_AVEL = 32; +const float TNSF_MOVE = 64; +.float anim_start_time; +const float TNSF_ANIM = 128; + +const float TNSF_FULL_UPDATE = 16777215; + + +// entity properties of turretinfo: +.float turretid; // TUR_... +.string netname; // short name +.string turret_name; // human readable name +.float(float) turret_func; // m_... +.string mdl; // currently a copy of the model +.string model; // full name of model +.string head_model; // full name of tur_head model +.string cvar_basename; // TODO: deprecate! +.float spawnflags; +.vector mins, maxs; // turret hitbox size + +// other useful macros +#define TUR_ACTION(turrettype,mrequest) (get_turretinfo(turrettype)).turret_func(mrequest) +#define TUR_NAME(turrettype) (get_turretinfo(turrettype)).turret_name + +// ===================== +// Turret Registration +// ===================== + +float m_null(float dummy); +void register_turret(float id, float(float) func, float turretflags, vector min_s, vector max_s, string modelname, string headmodelname, string shortname, string mname); +void register_turrets_done(); + +const float TUR_MAXCOUNT = 24; +#define TUR_FIRST 1 +float TUR_COUNT; +float TUR_LAST; + +#define REGISTER_TURRET_2(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + float id; \ + float func(float); \ + void RegisterTurrets_##id() \ + { \ + TUR_LAST = (id = TUR_FIRST + TUR_COUNT); \ + ++TUR_COUNT; \ + register_turret(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname); \ + } \ + ACCUMULATE_FUNCTION(RegisterTurrets, RegisterTurrets_##id) +#ifdef MENUQC +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,m_null,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#else +#define REGISTER_TURRET(id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) \ + REGISTER_TURRET_2(TUR_##id,func,turretflags,min_s,max_s,modelname,headmodelname,shortname,mname) +#endif + +#define TUR_DUPECHECK(dupecheck,cvar) \ + #ifndef dupecheck \ + #define dupecheck \ + float cvar; \ + #else \ + #error DUPLICATE TURRET CVAR: cvar \ + #endif + +#define TUR_ADD_CVAR(turret,name) \ + TUR_DUPECHECK(TUR_CVAR_##turret##_##name, autocvar_g_turrets_unit_##turret##_##name) + +#define TUR_CVAR(turret,name) autocvar_g_turrets_unit_##turret##_##name + +#include "all.qh" + +#undef TUR_ADD_CVAR +#undef REGISTER_TURRET +ACCUMULATE_FUNCTION(RegisterTurrets, register_turrets_done); diff --git a/qcsrc/common/turrets/unit/ewheel.qc b/qcsrc/common/turrets/unit/ewheel.qc new file mode 100644 index 0000000000..f0732e9718 --- /dev/null +++ b/qcsrc/common/turrets/unit/ewheel.qc @@ -0,0 +1,324 @@ +#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") +); + +#define EWHEEL_SETTINGS(turret) \ + TUR_ADD_CVAR(turret, speed_fast) \ + TUR_ADD_CVAR(turret, speed_slow) \ + TUR_ADD_CVAR(turret, speed_slower) \ + TUR_ADD_CVAR(turret, speed_stop) \ + TUR_ADD_CVAR(turret, turnrate) + + +#ifdef SVQC +EWHEEL_SETTINGS(ewheel) +#endif // SVQC +#else +#ifdef SVQC +const float ewheel_anim_stop = 0; +const float ewheel_anim_fwd_slow = 1; +const float ewheel_anim_fwd_fast = 2; +const float ewheel_anim_bck_slow = 3; +const float ewheel_anim_bck_fast = 4; + +//#define EWHEEL_FANCYPATH +void ewheel_move_path() +{ +#ifdef EWHEEL_FANCYPATH + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; +#endif + + if (self.pathcurrent) + { + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + movelib_move_simple(v_forward, TUR_CVAR(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, TUR_CVAR(ewheel, speed_fast), 0.4); + } + else if (self.tur_head.spawnshieldtime < 2) + { + + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, TUR_CVAR(ewheel, speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_fwd_slow; + movelib_move_simple(v_forward, TUR_CVAR(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, TUR_CVAR(ewheel, speed_slow), 0.4); + } + else + { + newframe = ewheel_anim_stop; + movelib_beak_simple(TUR_CVAR(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(TUR_CVAR(ewheel, speed_stop)); +} + +void spawnfunc_turret_ewheel() { if not(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_LASER, 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'; + +#ifdef EWHEEL_FANCYPATH + 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 + { + +#ifdef EWHEEL_FANCYPATH + self.pathcurrent = WALKER_PATH(self.origin,e.origin); + self.pathgoal = e; +#else + 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 = TUR_CVAR(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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(EWHEEL_SETTINGS(ewheel)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC + +void ewheel_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.05) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_ewheel(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = ewheel_draw; + + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/ewheel-base2.md3"); + precache_model ("models/turrets/ewheel-gun1.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/flac.qc b/qcsrc/common/turrets/unit/flac.qc new file mode 100644 index 0000000000..2c81a34738 --- /dev/null +++ b/qcsrc/common/turrets/unit/flac.qc @@ -0,0 +1,118 @@ +#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") +); + +#define FLAC_SETTINGS(turret) + + +#ifdef SVQC +FLAC_SETTINGS(flac) +#endif // SVQC +#else +#ifdef SVQC + +void turret_flac_projectile_think_explode() +{ + if(self.enemy != world) + if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) + setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); + +#ifdef TURRET_DEBUG + float d; + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, 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, self.owner.shot_force, self.totalfrags, world); +#endif + remove(self); +} + +void spawnfunc_turret_flac() { if not(turret_initialize(TUR_FLAC)) remove(self); } + +float t_flac(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity proj; + + turret_tag_fire_update(); + + proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); + pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + proj.think = turret_flac_projectile_think_explode; + proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + + self.tur_head.frame = self.tur_head.frame + 1; + if (self.tur_head.frame >= 4) + self.tur_head.frame = 0; + + return TRUE; + } + 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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(FLAC_SETTINGS(flac)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_flac(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/flac.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/fusionreactor.qc b/qcsrc/common/turrets/unit/fusionreactor.qc new file mode 100644 index 0000000000..a4d763f1a9 --- /dev/null +++ b/qcsrc/common/turrets/unit/fusionreactor.qc @@ -0,0 +1,131 @@ +#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") +); + +#define FUSIONREACTOR_SETTINGS(turret) + + +#ifdef SVQC +FUSIONREACTOR_SETTINGS(fusionreactor) +#endif // SVQC +#else +#ifdef SVQC + +float turret_fusionreactor_firecheck() +{ + if (self.attack_finished_single > time) + return 0; + + if (self.enemy.deadflag != DEAD_NO) + return 0; + + if (self.enemy == world) + return 0; + + if (self.ammo < self.shot_dmg) + return 0; + + if (self.enemy.ammo >= self.enemy.ammo_max) + return 0; + + if (vlen(self.enemy.origin - self.origin) > self.target_range) + return 0; + + if(self.team != self.enemy.team) + return 0; + + if not (self.enemy.ammo_flags & TFL_AMMO_ENERGY) + return 0; + + return 1; +} + +void spawnfunc_turret_fusionreactor() { if not(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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(FUSIONREACTOR_SETTINGS(fusionreactor)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_fusionreactor(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/reactor.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/hellion.qc b/qcsrc/common/turrets/unit/hellion.qc new file mode 100644 index 0000000000..1f94192f89 --- /dev/null +++ b/qcsrc/common/turrets/unit/hellion.qc @@ -0,0 +1,174 @@ +#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") +); + +#define HELLION_SETTINGS(turret) \ + TUR_ADD_CVAR(turret, shot_speed_gain) \ + TUR_ADD_CVAR(turret, shot_speed_max) + + +#ifdef SVQC +HELLION_SETTINGS(hellion) +#endif // SVQC +#else +#ifdef SVQC + +void turret_hellion_missile_think() +{ + vector olddir,newdir; + vector pre_pos; + float itime; + + self.nextthink = time + 0.05; + + olddir = normalize(self.velocity); + + if(self.max_health < time) + turret_projectile_explode(); + + // Enemy dead? just keep on the current heading then. + if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) + { + + // Make sure we dont return to tracking a respawned player + self.enemy = world; + + // Turn model + self.angles = vectoangles(self.velocity); + + if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) + turret_projectile_explode(); + + // Accelerate + self.velocity = olddir * min(vlen(self.velocity) * TUR_CVAR(hellion, shot_speed_gain), TUR_CVAR(hellion, shot_speed_max)); + + UpdateCSQCProjectile(self); + + return; + } + + // Enemy in range? + if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) + turret_projectile_explode(); + + // Predict enemy position + itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); + pre_pos = self.enemy.origin + self.enemy.velocity * itime; + + pre_pos = (pre_pos + self.enemy.origin) * 0.5; + + // Find out the direction to that place + newdir = normalize(pre_pos - self.origin); + + // Turn + newdir = normalize(olddir + newdir * 0.35); + + // Turn model + self.angles = vectoangles(self.velocity); + + // Accelerate + self.velocity = newdir * min(vlen(self.velocity) * TUR_CVAR(hellion, shot_speed_gain), TUR_CVAR(hellion, shot_speed_max)); + + if (itime < 0.05) + self.think = turret_projectile_explode; + + UpdateCSQCProjectile(self); +} + +void spawnfunc_turret_hellion() { if not(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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(HELLION_SETTINGS(hellion)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_hellion(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hellion.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/hk.qc b/qcsrc/common/turrets/unit/hk.qc new file mode 100644 index 0000000000..6c4e5d3a53 --- /dev/null +++ b/qcsrc/common/turrets/unit/hk.qc @@ -0,0 +1,375 @@ +#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") +); + +#define HK_SETTINGS(turret) \ + TUR_ADD_CVAR(turret, shot_speed) \ + TUR_ADD_CVAR(turret, shot_speed_accel) \ + TUR_ADD_CVAR(turret, shot_speed_accel2) \ + TUR_ADD_CVAR(turret, shot_speed_decel) \ + TUR_ADD_CVAR(turret, shot_speed_max) \ + TUR_ADD_CVAR(turret, shot_speed_turnrate) + + +#ifdef SVQC +HK_SETTINGS(hk) +#endif // SVQC +#else +#ifdef SVQC + +//#define TURRET_DEBUG_HK + +#ifdef TURRET_DEBUG_HK +.float atime; +#endif + +float hk_is_valid_target(entity e_target) +{ + if (e_target == world) + return 0; + + // If only this was used more.. + if (e_target.flags & FL_NOTARGET) + return 0; + + // Cant touch this + if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) + return 0; + + // player + if (IS_CLIENT(e_target)) + { + if (self.owner.target_select_playerbias < 0) + return 0; + + if (e_target.deadflag != DEAD_NO) + return 0; + } + + // Missile + if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0)) + return 0; + + // Team check + if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) + return 0; + + return 1; +} + +void turret_hk_missile_think() +{ + vector vu, vd, vf, vl, vr, ve; // Vector (direction) + float fu, fd, ff, fl, fr, fe; // Fraction to solid + vector olddir,wishdir,newdir; // Final direction + float lt_for; // Length of Trace FORwrad + float lt_seek; // Length of Trace SEEK (left, right, up down) + float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) + vector pre_pos; + float myspeed; + entity e; + float ad,edist; + + self.nextthink = time + self.ticrate; + + //if (self.cnt < time) + // turret_hk_missile_explode(); + + if (self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + // Pick the closest valid target. + if (!self.enemy) + { + e = findradius(self.origin, 5000); + while (e) + { + if (hk_is_valid_target(e)) + { + if (!self.enemy) + self.enemy = e; + else + if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin)) + self.enemy = e; + } + e = e.chain; + } + } + + self.angles = vectoangles(self.velocity); + self.angles_x = self.angles_x * -1; + makevectors(self.angles); + self.angles_x = self.angles_x * -1; + + if (self.enemy) + { + edist = vlen(self.origin - self.enemy.origin); + // Close enougth to do decent damage? + if ( edist <= (self.owner.shot_radius * 0.25) ) + { + turret_projectile_explode(); + return; + } + + // Get data on enemy position + pre_pos = self.enemy.origin + + self.enemy.velocity * + min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5); + + traceline(self.origin, pre_pos,TRUE,self.enemy); + 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 > TUR_CVAR(hk, shot_speed)) ) + myspeed = max(myspeed * TUR_CVAR(hk, shot_speed_decel), TUR_CVAR(hk, shot_speed)); + + // Failry clear, accelerate. + if ( (ff > 0.7) && (myspeed < TUR_CVAR(hk, shot_speed_max)) ) + myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel), TUR_CVAR(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 < TUR_CVAR(hk, shot_speed_max)) + myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel2),TUR_CVAR(hk, shot_speed_max)); + + wishdir = ve; + } + + if ((myspeed > TUR_CVAR(hk, shot_speed)) && (self.cnt > time)) + myspeed = min(myspeed * TUR_CVAR(hk, shot_speed_accel2),TUR_CVAR(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 * TUR_CVAR(hk, shot_speed_turnrate)); + + // Set heading & speed + self.velocity = newdir * myspeed; + + // Align model with new heading + self.angles = vectoangles(self.velocity); + + +#ifdef TURRET_DEBUG_HK + //if(self.atime < time) { + if ((fe <= 0.99)||(edist > 1000)) + { + te_lightning2(world,self.origin, self.origin + vr * lt_seek); + te_lightning2(world,self.origin, self.origin + vl * lt_seek); + te_lightning2(world,self.origin, self.origin + vu * lt_seek); + te_lightning2(world,self.origin, self.origin + vd * lt_seek); + te_lightning2(world,self.origin, vf); + } + else + { + te_lightning2(world,self.origin, self.enemy.origin); + } + bprint("Speed: ", ftos(rint(myspeed)), "\n"); + bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n"); + bprint("Trace to target:", ftos(rint(fe * 100)), "%\n"); + self.atime = time + 0.2; + //} +#endif + + UpdateCSQCProjectile(self); +} + +float turret_hk_addtarget(entity e_target,entity e_sender) +{ + if (e_target) + { + if (turret_validate_target(self,e_target,self.target_validate_flags) > 0) + { + self.enemy = e_target; + return 1; + } + } + + return 0; +} + +void spawnfunc_turret_hk() { if not(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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(HK_SETTINGS(hk)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_hk(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/hk.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/machinegun.qc b/qcsrc/common/turrets/unit/machinegun.qc new file mode 100644 index 0000000000..9481483e09 --- /dev/null +++ b/qcsrc/common/turrets/unit/machinegun.qc @@ -0,0 +1,97 @@ +#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") +); + +#define MACHINEGUN_SETTINGS(turret) + + +#ifdef SVQC +MACHINEGUN_SETTINGS(machinegun) +#endif // SVQC +#else +#ifdef SVQC + +void spawnfunc_turret_machinegun() { if not(turret_initialize(TUR_MACHINEGUN)) remove(self); } + +float t_machinegun(float req) +{ + switch(req) + { + case TR_ATTACK: + { + fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0, 1, autocvar_g_balance_uzi_bulletconstant); + endFireBallisticBullet(); + + UziFlash(); + 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; + + if not(autocvar_g_antilag_bullets) + 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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(MACHINEGUN_SETTINGS(machinegun)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_machinegun(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/machinegun.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/mlrs.qc b/qcsrc/common/turrets/unit/mlrs.qc new file mode 100644 index 0000000000..2bc26ab7d6 --- /dev/null +++ b/qcsrc/common/turrets/unit/mlrs.qc @@ -0,0 +1,105 @@ +#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") +); + +#define MLRS_SETTINGS(turret) + + +#ifdef SVQC +MLRS_SETTINGS(mlrs) +#endif // SVQC +#else +#ifdef SVQC + +void spawnfunc_turret_mlrs() { if not(turret_initialize(TUR_MLRS)) remove(self); } + +float t_mlrs(float req) +{ + switch(req) + { + case TR_ATTACK: + { + entity missile; + + turret_tag_fire_update(); + missile = turret_projectile("weapons/rocket_fire.wav", 6, 10, DEATH_TURRET_MLRS, PROJECTILE_ROCKET, TRUE, TRUE); + missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed); + missile.missile_flags = MIF_SPLASH; + te_explosion (missile.origin); + + return TRUE; + } + 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_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/mlrs.md3"); + return TRUE; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(MLRS_SETTINGS(mlrs)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_mlrs(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/mlrs.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/phaser.qc b/qcsrc/common/turrets/unit/phaser.qc new file mode 100644 index 0000000000..a36fa545d3 --- /dev/null +++ b/qcsrc/common/turrets/unit/phaser.qc @@ -0,0 +1,188 @@ +#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") +); + +#define PHASER_SETTINGS(turret) + + +#ifdef SVQC +PHASER_SETTINGS(phaser) +#endif // SVQC +#else +#ifdef SVQC + +.float fireflag; + +float turret_phaser_firecheck() +{ + if (self.fireflag != 0) return 0; + return turret_firecheck(); +} + +void beam_think() +{ + if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) + { + self.owner.attack_finished_single = time + self.owner.shot_refire; + self.owner.fireflag = 2; + self.owner.tur_head.frame = 10; + sound (self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); + remove(self); + return; + } + + turret_do_updates(self.owner); + + if (time - self.shot_spread > 0) + { + self.shot_spread = time + 2; + sound (self, CH_SHOTS_SINGLE, "turrets/phaser.wav", VOL_BASE, ATTEN_NORM); + } + + + self.nextthink = time + self.ticrate; + + self.owner.attack_finished_single = time + frametime; + entity oldself; + oldself = self; + self = self.owner; + FireImoBeam ( self.tur_shotorg, + self.tur_shotorg + self.tur_shotdir_updated * self.target_range, + '-1 -1 -1' * self.shot_radius, + '1 1 1' * self.shot_radius, + self.shot_force, + oldself.shot_dmg, + 0.75, + DEATH_TURRET_PHASER); + self = oldself; + self.scale = vlen(self.owner.tur_shotorg - trace_endpos) / 256; + +} + +void spawnfunc_turret_phaser() { if not(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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(PHASER_SETTINGS(phaser)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_phaser(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/phaser.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/plasma.qc b/qcsrc/common/turrets/unit/plasma.qc new file mode 100644 index 0000000000..670efd2921 --- /dev/null +++ b/qcsrc/common/turrets/unit/plasma.qc @@ -0,0 +1,142 @@ +#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") +); + +#define PLASMA_SETTINGS(turret) + + +#ifdef SVQC +PLASMA_SETTINGS(plasma) +#endif // SVQC +#else +#ifdef SVQC + +void spawnfunc_turret_plasma() { if not(turret_initialize(TUR_PLASMA)) remove(self); } + +float t_plasma(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_minstagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + + pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + if(teamplay) + { + switch(self.team) + { + case NUM_TEAM_1: // Red + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v); + break; + case NUM_TEAM_2: // Blue + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v); + break; + case NUM_TEAM_3: // Yellow + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v); + break; + case NUM_TEAM_4: // Pink + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v); + break; + } + } + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), self.tur_shotorg, v); + if (self.tur_head.frame == 0) + self.tur_head.frame = 1; + } + 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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(PLASMA_SETTINGS(plasma)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasma.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/plasma_dual.qc b/qcsrc/common/turrets/unit/plasma_dual.qc new file mode 100644 index 0000000000..5842478107 --- /dev/null +++ b/qcsrc/common/turrets/unit/plasma_dual.qc @@ -0,0 +1,140 @@ +#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") +); + +#define PLASMA_DUAL_SETTINGS(turret) + + +#ifdef SVQC +PLASMA_DUAL_SETTINGS(plasma_dual) +#endif // SVQC +#else +#ifdef SVQC + +void spawnfunc_turret_plasma_dual() { if not(turret_initialize(TUR_PLASMA_DUAL)) remove(self); } + +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_ATTACK: + { + if(g_minstagib) + { + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, + 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); + + + pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); + + // teamcolor / hit beam effect + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + if(teamplay) + { + switch(self.team) + { + case NUM_TEAM_1: // Red + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v); + break; + case NUM_TEAM_2: // Blue + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v); + break; + case NUM_TEAM_3: // Yellow + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v); + break; + case NUM_TEAM_4: // Pink + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v); + break; + } + } + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), self.tur_shotorg, v); + + 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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(PLASMA_DUAL_SETTINGS(plasma_dual)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_plasma_dual(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/base.md3"); + precache_model ("models/turrets/plasmad.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/tesla.qc b/qcsrc/common/turrets/unit/tesla.qc new file mode 100644 index 0000000000..23171c0aa0 --- /dev/null +++ b/qcsrc/common/turrets/unit/tesla.qc @@ -0,0 +1,232 @@ +#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") +); + +#define TESLA_SETTINGS(turret) + + +#ifdef SVQC +TESLA_SETTINGS(tesla) +#endif // SVQC +#else +#ifdef SVQC + +entity toast(entity from, float range, float damage) +{ + entity e; + entity etarget = world; + float d,dd; + float r; + + dd = range + 1; + + e = findradius(from.origin,range); + while (e) + { + if ((e.railgunhit != 1) && (e != from)) + { + r = turret_validate_target(self,e,self.target_validate_flags); + if (r > 0) + { + traceline(from.origin,0.5 * (e.absmin + e.absmax),MOVE_WORLDONLY,from); + if (trace_fraction == 1.0) + { + d = vlen(e.origin - from.origin); + if (d < dd) + { + dd = d; + etarget = e; + } + } + } + } + e = e.chain; + } + + if (etarget) + { + te_csqc_lightningarc(from.origin,etarget.origin); + Damage(etarget, self, self, damage, DEATH_TURRET_TESLA, etarget.origin, '0 0 0'); + etarget.railgunhit = 1; + } + + return etarget; +} + +float turret_tesla_firecheck() +{ + // g_turrets_targetscan_maxdelay forces a target re-scan at least this often + float do_target_scan = 0; + + if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) + do_target_scan = 1; + + // Old target (if any) invalid? + if(self.target_validate_time < time) + if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) + { + self.enemy = world; + self.target_validate_time = time + 0.5; + do_target_scan = 1; + } + + // But never more often then g_turrets_targetscan_mindelay! + if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) + do_target_scan = 0; + + if(do_target_scan) + { + self.enemy = turret_select_target(); + self.target_select_time = time; + } + + if not (turret_firecheck()) + return 0; + + if(self.enemy) + return 1; + + return 0; +} + +void spawnfunc_turret_tesla() { if not(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 not (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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(TESLA_SETTINGS(tesla)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC +float t_tesla(float req) +{ + switch(req) + { + case TR_SETUP: + { + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/tesla_base.md3"); + precache_model ("models/turrets/tesla_head.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/unit/walker.qc b/qcsrc/common/turrets/unit/walker.qc new file mode 100644 index 0000000000..267214f516 --- /dev/null +++ b/qcsrc/common/turrets/unit/walker.qc @@ -0,0 +1,715 @@ +#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") +); + +#define WALKER_SETTINGS(turret) \ + TUR_ADD_CVAR(turret, melee_damage) \ + TUR_ADD_CVAR(turret, melee_force) \ + TUR_ADD_CVAR(turret, melee_range) \ + TUR_ADD_CVAR(turret, rocket_damage) \ + TUR_ADD_CVAR(turret, rocket_radius) \ + TUR_ADD_CVAR(turret, rocket_force) \ + TUR_ADD_CVAR(turret, rocket_speed) \ + TUR_ADD_CVAR(turret, rocket_range) \ + TUR_ADD_CVAR(turret, rocket_range_min) \ + TUR_ADD_CVAR(turret, rocket_refire) \ + TUR_ADD_CVAR(turret, rocket_turnrate) \ + TUR_ADD_CVAR(turret, speed_stop) \ + TUR_ADD_CVAR(turret, speed_walk) \ + TUR_ADD_CVAR(turret, speed_run) \ + TUR_ADD_CVAR(turret, speed_jump) \ + TUR_ADD_CVAR(turret, speed_swim) \ + TUR_ADD_CVAR(turret, speed_roam) \ + TUR_ADD_CVAR(turret, turn) \ + TUR_ADD_CVAR(turret, turn_walk) \ + TUR_ADD_CVAR(turret, turn_strafe) \ + TUR_ADD_CVAR(turret, turn_swim) \ + TUR_ADD_CVAR(turret, turn_run) + + +#ifdef SVQC +WALKER_SETTINGS(walker) +#endif // SVQC +#else +#ifdef SVQC + +const float walker_anim_stop = 0; +const float walker_anim_turn = 1; +const float walker_anim_walk = 2; +const float walker_anim_run = 3; +const float walker_anim_strafeleft = 4; +const float walker_anim_straferight = 5; +const float walker_anim_jump = 6; +const float walker_anim_land = 7; +const float walker_anim_pain = 8; +const float walker_anim_melee = 9; +const float walker_anim_swim = 10; +const float walker_anim_roam = 11; + +.float animflag; +.float idletime; + +#define WALKER_PATH(s,e) pathlib_astar(s,e) + +float walker_firecheck() +{ + if (self.animflag == walker_anim_melee) + return 0; + + return turret_firecheck(); +} + +void walker_melee_do_dmg() +{ + vector where; + entity e; + + makevectors(self.angles); + where = self.origin + v_forward * 128; + + e = findradius(where,32); + while (e) + { + if (turret_validate_target(self, e, self.target_validate_flags)) + if (e != self && e.owner != self) + Damage(e, self, self, TUR_CVAR(walker, melee_damage), DEATH_TURRET_WALK_MELEE, '0 0 0', v_forward * TUR_CVAR(walker, melee_force)); + + e = e.chain; + } +} + +void walker_setnoanim() +{ + turrets_setframe(walker_anim_stop, FALSE); + self.animflag = self.frame; +} +void walker_rocket_explode() +{ + RadiusDamage (self, self.owner, TUR_CVAR(walker, rocket_damage), 0, TUR_CVAR(walker, rocket_radius), self, TUR_CVAR(walker, rocket_force), DEATH_TURRET_WALK_ROCKET, world); + remove (self); +} + +void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) +{ + self.health = self.health - damage; + self.velocity = self.velocity + vforce; + + if (self.health <= 0) + W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); +} + +#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, TUR_CVAR(walker, rocket_speed), TUR_CVAR(walker, rocket_turnrate)); UpdateCSQCProjectile(self) +void walker_rocket_loop(); +void walker_rocket_think() +{ + vector newdir; + float edist; + float itime; + float m_speed; + + self.nextthink = time; + + edist = vlen(self.enemy.origin - self.origin); + + // Simulate crude guidance + if (self.cnt < time) + { + if (edist < 1000) + self.tur_shotorg = randomvec() * min(edist, 64); + else + self.tur_shotorg = randomvec() * min(edist, 256); + + self.cnt = time + 0.5; + } + + if (edist < 128) + self.tur_shotorg = '0 0 0'; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + self.nextthink = time; + return; + } + + if (self.shot_dmg != 1337 && random() < 0.01) + { + walker_rocket_loop(); + return; + } + + m_speed = vlen(self.velocity); + + // Enemy dead? just keep on the current heading then. + if (self.enemy == world || self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + if (self.enemy) + { + itime = max(edist / m_speed, 1); + newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg); + } + else + newdir = normalize(self.velocity); + + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop3() +{ + vector newdir; + self.nextthink = time; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + self.think = walker_rocket_think; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; + + self.angles = vectoangles(self.velocity); +} + +void walker_rocket_loop2() +{ + vector newdir; + + self.nextthink = time; + + if (self.max_health < time) + { + self.think = walker_rocket_explode; + return; + } + + if (vlen(self.origin - self.tur_shotorg) < 100 ) + { + self.tur_shotorg = self.origin - '0 0 200'; + self.think = walker_rocket_loop3; + return; + } + + newdir = steerlib_pull(self.tur_shotorg); + WALKER_ROCKET_MOVE; +} + +void walker_rocket_loop() +{ + self.nextthink = time; + self.tur_shotorg = self.origin + '0 0 300'; + self.think = walker_rocket_loop2; + self.shot_dmg = 1337; +} + +void walker_fire_rocket(vector org) +{ + entity rocket; + + fixedmakevectors(self.angles); + + te_explosion (org); + + rocket = spawn (); + setorigin(rocket, org); + + sound (self, CH_WEAPON_A, "weapons/hagar_fire.wav", VOL_BASE, ATTEN_NORM); + setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + rocket.classname = "walker_rocket"; + rocket.owner = self; + rocket.bot_dodge = TRUE; + rocket.bot_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)) * TUR_CVAR(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 = walker_anim_run; + else + self.animflag = walker_anim_walk; + case WATERLEVEL_WETFEET: + case WATERLEVEL_SWIMMING: + if (self.animflag != walker_anim_swim) + self.animflag = walker_anim_walk; + else + self.animflag = walker_anim_swim; + break; + case WATERLEVEL_SUBMERGED: + self.animflag = walker_anim_swim; + } + + self.moveto = _target; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + + if(self.enemy) + { + self.enemy_last_loc = _target; + self.enemy_last_time = time; + } +} + +//#define WALKER_FANCYPATHING + +void walker_move_path() +{ +#ifdef WALKER_FANCYPATHING + // Are we close enougth to a path node to switch to the next? + if (vlen(self.origin - self.pathcurrent.origin) < 64) + if (self.pathcurrent.path_next == world) + { + // Path endpoint reached + pathlib_deletepath(self.pathcurrent.owner); + self.pathcurrent = world; + + if (self.pathgoal) + { + if (self.pathgoal.use) + self.pathgoal.use(); + + if (self.pathgoal.enemy) + { + self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin); + self.pathgoal = self.pathgoal.enemy; + } + } + else + self.pathgoal = world; + } + else + self.pathcurrent = self.pathcurrent.path_next; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); + walker_move_to(self.moveto, 0); + +#else + if (vlen(self.origin - self.pathcurrent.origin) < 64) + self.pathcurrent = self.pathcurrent.enemy; + + if(!self.pathcurrent) + return; + + self.moveto = self.pathcurrent.origin; + self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); + walker_move_to(self.moveto, 0); +#endif +} + +void spawnfunc_turret_walker() { if not(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); + fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0, 1, autocvar_g_balance_uzi_bulletconstant); + endFireBallisticBullet(); + 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 != walker_anim_stop) + { + 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 = walker_anim_stop; + } + else + { + self.animflag = walker_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 < TUR_CVAR(walker, melee_range) && self.animflag != walker_anim_melee) + { + vector wish_angle; + + wish_angle = angleofs(self, self.enemy); + if (self.animflag != walker_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 = walker_anim_melee; + } + } + else if (self.tur_head.attack_finished_single < time) + { + if(self.tur_head.shot_volly) + { + self.animflag = walker_anim_stop; + + 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 + TUR_CVAR(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 > TUR_CVAR(walker, rocket_range_min)) + if (self.tur_dist_enemy < TUR_CVAR(walker, rocket_range)) + self.tur_head.shot_volly = 4; + } + } + else + { + if (self.animflag != walker_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 walker_anim_stop: + movelib_beak_simple(TUR_CVAR(walker, speed_stop)); + break; + + case walker_anim_turn: + turny = TUR_CVAR(walker, turn); + movelib_beak_simple(TUR_CVAR(walker, speed_stop)); + break; + + case walker_anim_walk: + turny = TUR_CVAR(walker, turn_walk); + movelib_move_simple(v_forward, TUR_CVAR(walker, speed_walk), 0.6); + break; + + case walker_anim_run: + turny = TUR_CVAR(walker, turn_run); + movelib_move_simple(v_forward, TUR_CVAR(walker, speed_run), 0.6); + break; + + case walker_anim_strafeleft: + turny = TUR_CVAR(walker, turn_strafe); + movelib_move_simple(v_right * -1, TUR_CVAR(walker, speed_walk), 0.8); + break; + + case walker_anim_straferight: + turny = TUR_CVAR(walker, turn_strafe); + movelib_move_simple(v_right, TUR_CVAR(walker, speed_walk), 0.8); + break; + + case walker_anim_jump: + self.velocity += '0 0 1' * TUR_CVAR(walker, speed_jump); + break; + + case walker_anim_land: + break; + + case walker_anim_pain: + if(self.frame != walker_anim_pain) + defer(0.25, walker_setnoanim); + + break; + + case walker_anim_melee: + if(self.frame != walker_anim_melee) + { + defer(0.41, walker_setnoanim); + defer(0.21, walker_melee_do_dmg); + } + + movelib_beak_simple(TUR_CVAR(walker, speed_stop)); + break; + + case walker_anim_swim: + turny = TUR_CVAR(walker, turn_swim); + turnx = TUR_CVAR(walker, turn_swim); + + self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10); + movelib_move_simple(v_forward, TUR_CVAR(walker, speed_swim), 0.3); + vz = self.velocity_z + sin(time * 4) * 8; + break; + + case walker_anim_roam: + turny = TUR_CVAR(walker, turn_walk); + movelib_move_simple(v_forward ,TUR_CVAR(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); +#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; + + if (autocvar_g_antilag_bullets) + self.turret_flags |= TUR_FLAG_HITSCAN; + else + self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; + + 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; +#else + 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; + } + case TR_CONFIG: + { + TUR_CONFIG_SETTINGS(WALKER_SETTINGS(walker)) + return TRUE; + } + } + + return TRUE; +} + +#endif // SVQC +#ifdef CSQC + +void walker_draw() +{ + float dt; + + dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) + return; + + fixedmakevectors(self.angles); + movelib_groundalign4point(300, 100, 0.25, 45); + setorigin(self, self.origin + self.velocity * dt); + self.tur_head.angles += dt * self.tur_head.move_avelocity; + self.angles_y = self.move_angles_y; + + if (self.health < 127) + if(random() < 0.15) + te_spark(self.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); +} + +float t_walker(float req) +{ + switch(req) + { + case TR_SETUP: + { + self.gravity = 1; + self.movetype = MOVETYPE_BOUNCE; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_origin = self.origin; + self.move_time = time; + self.draw = walker_draw; + + return TRUE; + } + case TR_PRECACHE: + { + precache_model ("models/turrets/walker_body.md3"); + precache_model ("models/turrets/walker_head_minigun.md3"); + return TRUE; + } + } + + return TRUE; +} + +#endif // CSQC +#endif // REGISTER_TURRET diff --git a/qcsrc/common/turrets/util.qc b/qcsrc/common/turrets/util.qc new file mode 100644 index 0000000000..6de59027e3 --- /dev/null +++ b/qcsrc/common/turrets/util.qc @@ -0,0 +1,331 @@ +/* +* Return a angle within +/- 360. +*/ +float anglemods(float v) +{ + v = v - 360 * floor(v / 360); + + if(v >= 180) + return v - 360; + else if(v <= -180) + return v + 360; + else + return v; +} + +/* +* Return the short angle +*/ +float shortangle_f(float ang1, float ang2) +{ + if(ang1 > ang2) + { + if(ang1 > 180) + return ang1 - 360; + } + else + { + if(ang1 < -180) + return ang1 + 360; + } + + return ang1; +} + +vector shortangle_v(vector ang1, vector ang2) +{ + vector vtmp; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + vtmp_z = shortangle_f(ang1_z,ang2_z); + + return vtmp; +} + +vector shortangle_vxy(vector ang1, vector ang2) +{ + vector vtmp = '0 0 0'; + + vtmp_x = shortangle_f(ang1_x,ang2_x); + vtmp_y = shortangle_f(ang1_y,ang2_y); + + return vtmp; +} + + +/* +* Get "real" origin, in worldspace, even if ent is attached to something else. +*/ +vector real_origin(entity ent) +{ + entity e; + vector v = ((ent.absmin + ent.absmax) * 0.5); + + e = ent.tag_entity; + while(e) + { + v = v + ((e.absmin + e.absmax) * 0.5); + e = e.tag_entity; + } + + return v; +} + +/* +* Return the angle between two enteties +*/ +vector angleofs(entity from, entity to) +{ + vector v_res; + + v_res = normalize(to.origin - from.origin); + v_res = vectoangles(v_res); + v_res = v_res - from.angles; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +vector angleofs3(vector from, vector from_a, entity to) +{ + vector v_res; + + v_res = normalize(to.origin - from); + v_res = vectoangles(v_res); + v_res = v_res - from_a; + + if (v_res_x < 0) v_res_x += 360; + if (v_res_x > 180) v_res_x -= 360; + + if (v_res_y < 0) v_res_y += 360; + if (v_res_y > 180) v_res_y -= 360; + + return v_res; +} + +/* +* Update self.tur_shotorg by getting up2date bone info +* NOTICE this func overwrites the global v_forward, v_right and v_up vectors. +*/ +#define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward) +float turret_tag_fire_update_s() +{ + 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) + +{ + 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 --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 759aea6add..cba83c6fe7 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -1000,41 +1000,6 @@ float autocvar_g_turrets_nofire; float autocvar_g_turrets_reloadcvars; float autocvar_g_turrets_targetscan_maxdelay; float autocvar_g_turrets_targetscan_mindelay; -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; float autocvar_g_use_ammunition; float autocvar_g_waypointeditor; float autocvar_g_waypointeditor_auto; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index c01cddd216..f34f452c75 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -732,7 +732,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float else victim = targ; - if(IS_PLAYER(victim) || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + if(IS_PLAYER(victim) || victim.turret_flags & TUR_FLAG_ISTURRET) { if(DIFF_TEAM(victim, attacker)) { diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 68532192ec..baaaa37401 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -544,6 +544,7 @@ void spawnfunc___init_dedicated_server(void) // needs to be done so early because of the constants they create CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterTurrets); CALL_ACCUMULATED_FUNCTION(RegisterGametypes); CALL_ACCUMULATED_FUNCTION(RegisterNotifications); CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); @@ -592,6 +593,7 @@ void spawnfunc_worldspawn (void) // needs to be done so early because of the constants they create CALL_ACCUMULATED_FUNCTION(RegisterWeapons); + CALL_ACCUMULATED_FUNCTION(RegisterTurrets); CALL_ACCUMULATED_FUNCTION(RegisterGametypes); CALL_ACCUMULATED_FUNCTION(RegisterNotifications); CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 0b5b427812..7a6f972c49 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -1298,11 +1298,6 @@ void precache() // gamemode related things precache_model ("models/misc/chatbubble.spr"); -#ifdef TTURRETS_ENABLED - if (autocvar_g_turrets) - turrets_precash(); -#endif - // Precache all player models if desired if (autocvar_sv_precacheplayermodels) { diff --git a/qcsrc/server/mutators/gamemode_assault.qc b/qcsrc/server/mutators/gamemode_assault.qc index 94e104728f..4a6308ae1a 100644 --- a/qcsrc/server/mutators/gamemode_assault.qc +++ b/qcsrc/server/mutators/gamemode_assault.qc @@ -170,7 +170,6 @@ void assault_roundstart_use() activator = self; SUB_UseTargets(); -#ifdef TTURRETS_ENABLED entity ent, oldself; //(Re)spawn all turrets @@ -186,12 +185,11 @@ void assault_roundstart_use() self = ent; // Dubbles as teamchange - turret_stdproc_respawn(); + turret_respawn(); ent = find(ent, classname, "turret_main"); } self = oldself; -#endif } void assault_wall_think() diff --git a/qcsrc/server/mutators/gamemode_onslaught.qc b/qcsrc/server/mutators/gamemode_onslaught.qc index b5b6594d70..31a7fcea57 100644 --- a/qcsrc/server/mutators/gamemode_onslaught.qc +++ b/qcsrc/server/mutators/gamemode_onslaught.qc @@ -1644,11 +1644,34 @@ MUTATOR_HOOKFUNCTION(ons_PlayerSpawn) return 0; } +MUTATOR_HOOKFUNCTION(ons_TurretSpawn) +{ + entity e, ee = world; + if(self.targetname) + { + e = find(world, target, self.targetname); + if(e != world) + { + self.team = e.team; + ee = e; + } + } + + if(ee) + { + activator = ee; + self.use(); + } + + return FALSE; +} + MUTATOR_DEFINITION(gamemode_onslaught) { MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY); MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY); + MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY); //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY); MUTATOR_ONADD diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index e9e9a4b8c8..5b86e074d0 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -34,6 +34,11 @@ defs.qh // Should rename this, it has fields and globals ../common/notifications.qh // must be after autocvars ../common/deathtypes.qh // must be after notifications +../common/turrets/config.qh +../common/turrets/turrets.qh +../common/turrets/sv_turrets.qh +../common/turrets/util.qc + mutators/base.qh mutators/mutators.qh mutators/gamemode_assault.qh @@ -48,8 +53,7 @@ mutators/gamemode_lms.qh mutators/mutator_dodging.qh mutators/mutator_nades.qh -//// tZork Turrets //// -tturrets/include/turrets_early.qh +//// tZork Vehicles //// vehicles/vehicles_def.qh campaign.qh @@ -195,8 +199,7 @@ t_quake.qc race.qc -//// tZork Turrets //// -tturrets/include/turrets.qh +//// tZork Vehicles //// vehicles/vehicles.qh scores.qc @@ -226,6 +229,12 @@ round_handler.qc ../common/explosion_equation.qc +../common/turrets/sv_turrets.qc +../common/turrets/config.qc +../common/turrets/turrets.qc +../common/turrets/checkpoint.qc +../common/turrets/targettrigger.qc + mutators/base.qc mutators/gamemode_assault.qc mutators/gamemode_arena.qc diff --git a/qcsrc/server/t_teleporters.qc b/qcsrc/server/t_teleporters.qc index d61911ecdf..65131f8467 100644 --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@ -209,7 +209,7 @@ void Teleport_Touch (void) if(!other.vehicle.teleportable) return; - if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + if(other.turret_flags & TUR_FLAG_ISTURRET) return; if(other.deadflag != DEAD_NO) diff --git a/qcsrc/server/tturrets/include/turrets.qh b/qcsrc/server/tturrets/include/turrets.qh deleted file mode 100644 index 60936410d1..0000000000 --- a/qcsrc/server/tturrets/include/turrets.qh +++ /dev/null @@ -1,29 +0,0 @@ -#ifdef TTURRETS_ENABLED - -// Include section. -#include "../system/system_misc.qc" /// Assorted junk & jewls -#include "../system/system_main.qc" /// And routines -#include "../system/system_aimprocs.qc" /// Aiming realted stuff -#include "../system/system_scoreprocs.qc" /// Target calssification -#include "../system/system_damage.qc" /// Outch, they are hurting me! what should i do? - -// Non combat units -#include "../units/unit_fusionreactor.qc" /// Supply unites that need it with power -#include "../units/unit_targettrigger.qc" /// Hit me! -#include "../units/unit_checkpoint.qc" /// Halfsmart pathing. - -// Combat units -#include "../units/unit_plasma.qc" /// Basic energy cannon -#include "../units/unit_mlrs.qc" /// Basic multibay RL -#include "../units/unit_hellion.qc" /// Seeking missiles MLRS -#include "../units/unit_flac.qc" /// anti missile turret -#include "../units/unit_phaser.qc" /// ZzzapT -#include "../units/unit_hk.qc" /// Hunter killers -#include "../units/unit_machinegun.qc" /// whacka -#include "../units/unit_tessla.qc" /// Chain lightning capabale turret -#include "../units/unit_walker.qc" /// Moving minigun-rocket-meele err thing -#include "../units/unit_ewheel.qc" /// A evil wheel. with guns on. -//#include "../units/unit_repulsor.qc" /// Fires a wave that knocks foes back -//#include "../units/unit_hive.qc" /// Swarm AI - -#endif // TTURRETS_ENABLED diff --git a/qcsrc/server/tturrets/include/turrets_early.qh b/qcsrc/server/tturrets/include/turrets_early.qh deleted file mode 100644 index 4ce95fc396..0000000000 --- a/qcsrc/server/tturrets/include/turrets_early.qh +++ /dev/null @@ -1,473 +0,0 @@ -// Comment out below to skip turrets -#define TTURRETS_ENABLED - -#ifdef TTURRETS_ENABLED -#ifdef SVQC -//#message "with tZork turrets" - -float turret_count; - -vector real_origin(entity ent); - -/// Map time control over pain inflicted -.float turret_scale_damage; -/// Map time control targetting range -.float turret_scale_range; -/// Map time control refire -.float turret_scale_refire; -/// Map time control ammo held and recharged -.float turret_scale_ammo; -/// Map time control aim speed -.float turret_scale_aim; -/// Map time control health -.float turret_scale_health; -/// Map time control respawn time -.float turret_scale_respawn; - -/// Used for cvar reloading -.string cvar_basename; - -//.float spawnflags -#define TSF_SUSPENDED 1 -/// Spawn a pillar model under the turret to make it look ok on uneven ground surfaces -#define TSF_TERRAINBASE 2 -/// Disable builtin ammo regeneration -#define TSF_NO_AMMO_REGEN 4 -/// Dont break path to chase enemys. will still fire at them if possible. -#define TSF_NO_PATHBREAK 8 -/// Dont respawn -#define TSL_NO_RESPAWN 16 -/// Let this turret roam when idle. -#define TSL_ROAM 32 - -/// target selection flags -.float target_select_flags; -/// target validatoin flags -.float target_validate_flags; -/// Dont select a target on its own. -#define TFL_TARGETSELECT_NO 2 -/// Need line of sight -#define TFL_TARGETSELECT_LOS 4 -/// Players are valid targets -#define TFL_TARGETSELECT_PLAYERS 8 -/// Missiles are valid targets -#define TFL_TARGETSELECT_MISSILES 16 -/// Responds to turret_trigger_target events -#define TFL_TARGETSELECT_TRIGGERTARGET 32 -/// Angular limitations of turret head limits target selection -#define TFL_TARGETSELECT_ANGLELIMITS 64 -/// Range limits apply in targetselection -#define TFL_TARGETSELECT_RANGELIMTS 128 -/// DOnt select targets with a .team matching its own -#define TFL_TARGETSELECT_TEAMCHECK 256 -/// Cant select targets on its own. needs to be triggerd or slaved. -#define TFL_TARGETSELECT_NOBUILTIN 512 -/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team) -#define TFL_TARGETSELECT_OWNTEAM 1024 -/// Turrets aren't valid targets -#define TFL_TARGETSELECT_NOTURRETS 2048 -/// Use feild of view -#define TFL_TARGETSELECT_FOV 4096 - -#define TFL_TARGETSELECT_MISSILESONLY 8192 - -/// aim flags -.float aim_flags; -/// Dont aim. -#define TFL_AIM_NO 1 -/// Go for ground, not direct hit, but only if target is on ground. -#define TFL_AIM_GROUNDGROUND 2 -/// Try to predict target movement (does not account for gravity) -#define TFL_AIM_LEAD 4 -/// Compensate for shot traveltime when lead -#define TFL_AIM_SHOTTIMECOMPENSATE 8 -/// Try to do real prediction of targets z pos at impact. -#define TFL_AIM_ZPREDICT 16 -/// Simply aim at target's current location -#define TFL_AIM_SIMPLE 32 - -/// track (turn and pitch head) flags -.float track_flags; -/// Dont move head -#define TFL_TRACK_NO 2 -/// Pitch the head -#define TFL_TRACK_PITCH 4 -/// Rotate the head -#define TFL_TRACK_ROT 8 - -/// How tracking is preformed -.float track_type; -/// Hard angle increments. Ugly for fast turning, best accuracy. -#define TFL_TRACKTYPE_STEPMOTOR 1 -/// Smoth absolute movement. Looks ok, fair accuracy. -#define TFL_TRACKTYPE_FLUIDPRECISE 2 -/// Simulated inertia. "Wobbly mode" Looks kool, can mean really bad accuracy depending on how the fields below are set -#define TFL_TRACKTYPE_FLUIDINERTIA 3 -/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier -.float track_accel_pitch; -/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier -.float track_accel_rot; -/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1 = only old, 0 = only new -.float track_blendrate; - -/// How prefire check is preformed -.float firecheck_flags; -/// Dont kill the dead -#define TFL_FIRECHECK_DEAD 4 -/// Range limits apply -#define TFL_FIRECHECK_DISTANCES 8 -/// Line Of Sight needs to be clear -#define TFL_FIRECHECK_LOS 16 -/// Consider distance inpactpoint<->aimspot -#define TFL_FIRECHECK_AIMDIST 32 -/// Consider enemy origin<->impactpoint -#define TFL_FIRECHECK_REALDIST 64 -/// Consider angular diff head<->aimspot -#define TFL_FIRECHECK_ANGLEDIST 128 -/// (re)consider target.team<->self.team -#define TFL_FIRECHECK_TEAMCECK 256 -/// Try to avoid friendly fire -#define TFL_FIRECHECK_AFF 512 -/// Own .ammo needs to be >= then own .shot_dmg -#define TFL_FIRECHECK_OWM_AMMO 1024 -/// Others ammo need to be < others .ammo_max -#define TFL_FIRECHECK_OTHER_AMMO 2048 -/// Check own .attack_finished_single vs time -#define TFL_FIRECHECK_REFIRE 4096 -/// Move the acctual target to aimspot before tracing impact (and back after) -//#define TFL_FIRECHECK_VERIFIED 8192 -/// Dont do any chekcs -#define TFL_FIRECHECK_NO 16384 - -/// How shooting is done -.float shoot_flags; -/// Dont shoot -#define TFL_SHOOT_NO 64 -/// Fire in vollys (partial implementation through .shot_volly) -#define TFL_SHOOT_VOLLY 2 -/// Always do a full volly, even if target is lost or dead. (not implemented) -#define TFL_SHOOT_VOLLYALWAYS 4 -/// Loop though all valid tarters, and hit them. -#define TFL_SHOOT_HITALLVALID 8 -/// Fiering makes unit loose target (after volly is done, if in volly mode) -#define TFL_SHOOT_CLEARTARGET 16 -///Custom shooting; -#define TFL_SHOOT_CUSTOM 32 - -/// Information aboute the units capabilities -.float turrcaps_flags; -/// No kown capabilities -#define TFL_TURRCAPS_NONE 0 -/// Capable of sniping -#define TFL_TURRCAPS_SNIPER 2 -/// Capable of splasdamage -#define TFL_TURRCAPS_RADIUSDMG 4 -/// Has one or more cannons with zero shot traveltime -#define TFL_TURRCAPS_HITSCAN 8 -/// More then one (type of) gun -#define TFL_TURRCAPS_MULTIGUN 16 -/// Carries at least one guided weapon -#define TFL_TURRCAPS_GUIDED 32 -/// At least one gun fiers slow projectiles -#define TFL_TURRCAPS_SLOWPROJ 64 -/// At least one gun fiers medium speed projectiles -#define TFL_TURRCAPS_MEDPROJ 128 -/// At least one gun fiers fast projectiles -#define TFL_TURRCAPS_FASTPROJ 256 -/// At least one gun capable of damaging players -#define TFL_TURRCAPS_PLAYERKILL 512 -/// At least one gun that can shoot town missiles -#define TFL_TURRCAPS_MISSILEKILL 1024 -/// Has support capabilities. powerplants and sutch. -#define TFL_TURRCAPS_SUPPORT 2048 -/// Proveides at least one type of ammmo -#define TFL_TURRCAPS_AMMOSOURCE 4096 -/// Can recive targets from external sources -#define TFL_TURRCAPS_RECIVETARGETS 8192 -/// Capable of self-transport -#define TFL_TURRCAPS_MOVE 16384 -/// Will roam arround even if not chasing anyting -#define TFL_TURRCAPS_ROAM 32768 -#define TFL_TURRCAPS_ISTURRET 65536 - -/// Ammo types needed and/or provided -//.float ammo_flags; -#define ammo_flags currentammo -/// Has and needs no ammo -#define TFL_AMMO_NONE 64 -/// Uses power -#define TFL_AMMO_ENERGY 2 -/// Uses bullets -#define TFL_AMMO_BULLETS 4 -/// Uses explosives -#define TFL_AMMO_ROCKETS 8 -/// Regenerates ammo on its own -#define TFL_AMMO_RECHARGE 16 -/// Can recive ammo from others -#define TFL_AMMO_RECIVE 32 - -/// How incomming damage is handeld -.float damage_flags; -/// Cant be hurt -#define TFL_DMG_NO 256 -/// Can be damaged -#define TFL_DMG_YES 2 -/// Can be damaged by teammates -#define TFL_DMG_TAKEFROMTEAM 4 -/// Traget attackers -#define TFL_DMG_RETALIATE 8 -/// Target attackers, even is on own team -#define TFL_DMG_RETALIATEONTEAM 16 -/// Loses target when damaged -#define TFL_DMG_TARGETLOSS 32 -/// Reciving damage trows off aim (pointless atm, aim gets recalculated to fast). not implemented. -#define TFL_DMG_AIMSHAKE 64 -/// Reciving damage slaps the head arround -#define TFL_DMG_HEADSHAKE 128 -/// Die and stay dead. -#define TFL_DMG_DEATH_NORESPAWN 256 - -// Spawnflags -/// Spawn in teambased modes -#define TFL_SPAWN_TEAM 2 -/// Spawn in FFA modes -#define TFL_SPAWN_FFA 4 - - -/* -* Fields used by turrets -*/ -/// Turrets internal ai speed -.float ticrate; - -/// Where to point the when no target -.vector idle_aim; - -/// Top part of turret -.entity tur_head; - -/// Start/respawn health -.float tur_health; - -/// Defend this entity (or ratehr this entitys position) -.entity tur_defend; - -/// and shoot from here. (can be non constant, think MLRS) -.vector tur_shotorg; - -/// Aim at this spot -.vector tur_aimpos; - -/// Predicted time the round will impact -.float tur_impacttime; - -// Predicted place the round will impact -//.vector tur_impactpoint; // unused - -/// What entity the aimtrace hit, if any. -.entity tur_impactent; - -/// Distance to enemy -.float tur_dist_enemy; - -/// Distance to aimspot -.float tur_dist_aimpos; - -/// Distance impact<->aim -.float tur_dist_impact_to_aimpos; - -/// Decresment counter form .shot_volly to 0. -.float volly_counter; - -/* -* Projectile/missile. its up to the individual turret implementation to -** deal the damage, blow upp the missile or whatever. -*/ -/// Track then refireing is possible -//.float attack_finished; = attack_finished_single -/// Shoot this often -.float shot_refire; -/// Shots travel this fast, when appliable -.float shot_speed; -/// Inaccuracy -.float shot_spread; -/// Estimated (core) damage of projectiles. also reduce on ammo with this amount when fiering -.float shot_dmg; -/// If radius dmg, this is how big that radius is. -.float shot_radius; -/// Max force exserted by round impact -.float shot_force; -/// < 1 = shoot # times at target (if possible) -.float shot_volly; -/// Refire after a compleated volly. -.float shot_volly_refire; - -/// Consider targets within this range -.float target_range; -/// Dont consider targets closer then -.float target_range_min; -/// Targets closer to this are prefered -.float target_range_optimal; - -/* -* The standard targetselection tries to select a target based on -* range, angle offset, target type, "is old target" -* Thise biases will allow score scaling to (dis)favor diffrent targets -*/ -/// (dis)Favor best range this mutch -.float target_select_rangebias; -/// (dis)Favor targeting my old enemy this mutch -.float target_select_samebias; -/// (dis)Favor targeting the enemy closest to my guns current angle this mutch -.float target_select_anglebias; -/// (dis)Favor Missiles? (-1 to diable targeting compleatly) -.float target_select_missilebias; -/// (dis)Favot living players (-1 to diable targeting compleatly) -.float target_select_playerbias; -/// Field of view -//.float target_select_fov; -/// Last timestamp this turret aquierd a valid target -.float target_select_time; -/// Throttle re-validation of current target -.float target_validate_time; -/* -* Aim refers to real aiming, not gun pos (thats done by track) -*/ -/// Maximum offset between impact and aim spot to fire -.float aim_firetolerance_dist; -/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes) -.float aim_speed; -/// cant aim higher/lower then this -.float aim_maxpitch; -/// I cant rotate more then this -.float aim_maxrot; - -// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating) -/// Staring & current ammo -.float ammo; -/// Regenerate this mutch ammo (per second) -.float ammo_recharge; -/// Max amount of ammo i can hold -.float ammo_max; - - -// Uncomment below to enable various debug output. -//#define TURRET_DEBUG -//#define TURRET_DEBUG_TARGETVALIDATE -//#define TURRET_DEBUG_TARGETSELECT - -#ifdef TURRET_DEBUG -.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg. -.float tur_dbg_dmg_t_f; // Total damage spent -.float tur_dbg_start; // When did i go online? -.float tur_dbg_tmr1; // timer for random use -.float tur_dbg_tmr2; // timer for random use -.float tur_dbg_tmr3; // timer for random use -.vector tur_dbg_rvec; // Random vector, mainly for coloruing stuff' -#endif - -// System main's -/// Main AI loop -void turret_think(); -/// Prefire checks and sutch -void turret_fire(); - -// Callbacks -/// implements the actual fiering -.void() turret_firefunc; -/// prefire checks go here. return 1 to go bang, 0 not to. -.float() turret_firecheckfunc; -/// Execure AFTER main AI loop -.void() turret_postthink; - -/// Add a target -.float(entity e_target,entity e_sender) turret_addtarget; - -.void() turret_diehook; -.void() turret_respawnhook; - -/* -* Target selection, preferably but not nessesarely -* return a normalized result. -*/ -/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic -.float(entity _turret, entity _target) turret_score_target; - -/* -* Target selection -*/ -/// Generic, fairly smart, bias-aware target selection. -float turret_stdproc_targetscore_generic(entity _turret, entity _target); -/// Experimental supportunits targetselector -float turret_stdproc_targetscore_support(entity _turret,entity _target); - -/* -* Aim functions -*/ -/// Generic aimer guided by self.aim_flags -vector turret_stdproc_aim_generic(); - -/* -* Turret turning & pitch -*/ -/// Tries to line up the turret head with the aimpos -void turret_stdproc_track(); - -/// Generic damage handeling. blows up the turret when health <= 0 -void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce); -/// Spawns a explotion, does some damage & trows bits arround. -void turret_stdproc_die(); -/// reassembles the turret. -void turret_stdproc_respawn(); - -/// Evaluate target validity -float turret_validate_target(entity e_turret,entity e_target,float validate_flags); -/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target -vector tvt_thadv; -/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target -vector tvt_tadv; -/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target -float tvt_thadf; -/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target -float tvt_tadf; -/// Distance. updated by a sucsessfull call to turret_validate_target -float tvt_dist; - -/// updates aim org, shot org, shot dir and enemy org for selected turret -void turret_do_updates(entity e_turret); -.vector tur_shotdir_updated; - -void turrets_precash(); -#endif // SVQC - -// common -.float turret_type; -const float TID_COMMON = 1; -const float TID_EWHEEL = 2; -const float TID_FLAC = 3; -const float TID_FUSION = 4; -const float TID_HELLION = 5; -const float TID_HK = 6; -const float TID_MACHINEGUN = 7; -const float TID_MLRS = 8; -const float TID_PHASER = 9; -const float TID_PLASMA = 10; -const float TID_PLASMA_DUAL = 11; -const float TID_TESLA = 12; -const float TID_WALKER = 13; -const float TID_LAST = 13; - -const float TNSF_UPDATE = 2; -const float TNSF_STATUS = 4; -const float TNSF_SETUP = 8; -const float TNSF_ANG = 16; -const float TNSF_AVEL = 32; -const float TNSF_MOVE = 64; -.float anim_start_time; -const float TNSF_ANIM = 128; - -const float TNSF_FULL_UPDATE = 16777215; - -#endif // TTURRETS_ENABLED - - diff --git a/qcsrc/server/tturrets/system/system_aimprocs.qc b/qcsrc/server/tturrets/system/system_aimprocs.qc deleted file mode 100644 index 675b5de0db..0000000000 --- a/qcsrc/server/tturrets/system/system_aimprocs.qc +++ /dev/null @@ -1,73 +0,0 @@ -/* -* Generic aim - -supports: -TFL_AIM_NO -TFL_AIM_GROUNDGROUND -TFL_AIM_LEAD -TFL_AIM_SHOTTIMECOMPENSATE -*/ -vector turret_stdproc_aim_generic() -{ - - vector pre_pos, prep; - float distance, impact_time, i, mintime; - - turret_tag_fire_update(); - - if(self.aim_flags & TFL_AIM_SIMPLE) - return real_origin(self.enemy); - - 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 - { - // FIXME: this cant be the best way to do this.. - prep = pre_pos; -#ifdef GMQCC - impact_time = 0; -#endif - for(i = 0; i < 4; ++i) - { - distance = vlen(prep - self.tur_shotorg); - impact_time = distance / self.shot_speed; - prep = pre_pos + self.enemy.velocity * impact_time; - } - - prep = pre_pos + (self.enemy.velocity * (impact_time + mintime)); - - if(self.aim_flags & TFL_AIM_ZPREDICT) - if not(self.enemy.flags & FL_ONGROUND) - if(self.enemy.movetype == MOVETYPE_WALK || self.enemy.movetype == MOVETYPE_TOSS || self.enemy.movetype == MOVETYPE_BOUNCE) - { - float vz; - prep_z = pre_pos_z; - vz = self.enemy.velocity_z; - for(i = 0; i < impact_time; i += sys_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_GROUNDGROUND) - { - //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; -} diff --git a/qcsrc/server/tturrets/system/system_damage.qc b/qcsrc/server/tturrets/system/system_damage.qc deleted file mode 100644 index 4145a6ea34..0000000000 --- a/qcsrc/server/tturrets/system/system_damage.qc +++ /dev/null @@ -1,131 +0,0 @@ -/* -* Spawn a boom, trow fake bits arround -* and hide the real ones. -*/ -void turret_hide() -{ - self.effects |= EF_NODRAW; - self.nextthink = time + self.respawntime - 0.2; - self.think = turret_stdproc_respawn; -} - -void turret_stdproc_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) - { - if (self.turret_diehook) - self.turret_diehook(); - - remove(self.tur_head); - remove(self); - } - else - { - // Setup respawn - self.SendFlags |= TNSF_STATUS; - self.nextthink = time + 0.2; - self.think = turret_hide; - - if (self.turret_diehook) - self.turret_diehook(); - } -} - -void turret_stdproc_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_stdproc_damage; - - self.avelocity = '0 0 0'; - self.tur_head.avelocity = self.avelocity; - self.tur_head.angles = self.idle_aim; - self.health = self.tur_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; - - if (self.turret_respawnhook) - self.turret_respawnhook(); -} - -/* -* Standard damage proc. -*/ -void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - // Enougth allready! - if(self.deadflag == DEAD_DEAD) - return; - - // Inactive turrets take no damage. (hm..) - if not (self.active) - return; - - if (teamplay) - if (self.team == attacker.team) - { - // This does not happen anymore. Re-enable if you fix that. - if(IS_REAL_CLIENT(attacker)) - sprint(attacker, "\{1}Turret tells you: I'm on your team!\n"); - - if(autocvar_g_friendlyfire) - damage = damage * autocvar_g_friendlyfire; - else - return; - } - - self.health = 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.turrcaps_flags & TFL_TURRCAPS_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_stdproc_die; - } - - self.SendFlags |= TNSF_STATUS; -} diff --git a/qcsrc/server/tturrets/system/system_main.qc b/qcsrc/server/tturrets/system/system_main.qc deleted file mode 100644 index 5a77b17d91..0000000000 --- a/qcsrc/server/tturrets/system/system_main.qc +++ /dev/null @@ -1,1377 +0,0 @@ -#define cvar_base "g_turrets_unit_" -.float clientframe; -void turrets_setframe(float _frame, float client_only) -{ - if((client_only ? self.clientframe : self.frame ) != _frame) - { - self.SendFlags |= TNSF_ANIM; - self.anim_start_time = time; - } - - if(client_only) - self.clientframe = _frame; - else - self.frame = _frame; - -} - -float turret_send(entity to, float sf) -{ - - WriteByte(MSG_ENTITY, ENT_CLIENT_TURRET); - WriteByte(MSG_ENTITY, sf); - if(sf & TNSF_SETUP) - { - WriteByte(MSG_ENTITY, self.turret_type); - - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - - 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.tur_health) * 255)); - } - - return TRUE; -} - -void load_unit_settings(entity ent, string unitname, float is_reload) -{ - string sbase; - - if (ent == world) - return; - - if not (ent.turret_scale_damage) ent.turret_scale_damage = 1; - if not (ent.turret_scale_range) ent.turret_scale_range = 1; - if not (ent.turret_scale_refire) ent.turret_scale_refire = 1; - if not (ent.turret_scale_ammo) ent.turret_scale_ammo = 1; - if not (ent.turret_scale_aim) ent.turret_scale_aim = 1; - if not (ent.turret_scale_health) ent.turret_scale_health = 1; - if not (ent.turret_scale_respawn) ent.turret_scale_respawn = 1; - - sbase = strcat(cvar_base,unitname); - if (is_reload) - { - ent.enemy = world; - ent.tur_head.avelocity = '0 0 0'; - - ent.tur_head.angles = '0 0 0'; - } - - ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; - ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; - - ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; - ent.shot_refire = cvar(strcat(sbase,"_shot_refire")) * ent.turret_scale_refire; - ent.shot_radius = cvar(strcat(sbase,"_shot_radius")) * ent.turret_scale_damage; - ent.shot_speed = cvar(strcat(sbase,"_shot_speed")); - ent.shot_spread = cvar(strcat(sbase,"_shot_spread")); - ent.shot_force = cvar(strcat(sbase,"_shot_force")) * ent.turret_scale_damage; - ent.shot_volly = cvar(strcat(sbase,"_shot_volly")); - ent.shot_volly_refire = cvar(strcat(sbase,"_shot_volly_refire")) * ent.turret_scale_refire; - - ent.target_range = cvar(strcat(sbase,"_target_range")) * ent.turret_scale_range; - ent.target_range_min = cvar(strcat(sbase,"_target_range_min")) * ent.turret_scale_range; - ent.target_range_optimal = cvar(strcat(sbase,"_target_range_optimal")) * ent.turret_scale_range; - //ent.target_range_fire = cvar(strcat(sbase,"_target_range_fire")) * ent.turret_scale_range; - - ent.target_select_rangebias = cvar(strcat(sbase,"_target_select_rangebias")); - ent.target_select_samebias = cvar(strcat(sbase,"_target_select_samebias")); - ent.target_select_anglebias = cvar(strcat(sbase,"_target_select_anglebias")); - ent.target_select_playerbias = cvar(strcat(sbase,"_target_select_playerbias")); - //ent.target_select_fov = cvar(cvar_gets(sbase,"_target_select_fov")); - - ent.ammo_max = cvar(strcat(sbase,"_ammo_max")) * ent.turret_scale_ammo; - ent.ammo_recharge = cvar(strcat(sbase,"_ammo_recharge")) * ent.turret_scale_ammo; - - ent.aim_firetolerance_dist = cvar(strcat(sbase,"_aim_firetolerance_dist")); - ent.aim_speed = cvar(strcat(sbase,"_aim_speed")) * ent.turret_scale_aim; - ent.aim_maxrot = cvar(strcat(sbase,"_aim_maxrot")); - ent.aim_maxpitch = cvar(strcat(sbase,"_aim_maxpitch")); - - ent.track_type = cvar(strcat(sbase,"_track_type")); - ent.track_accel_pitch = cvar(strcat(sbase,"_track_accel_pitch")); - ent.track_accel_rot = cvar(strcat(sbase,"_track_accel_rot")); - ent.track_blendrate = cvar(strcat(sbase,"_track_blendrate")); - - if(is_reload) - if(ent.turret_respawnhook) - ent.turret_respawnhook(); -} - -void turret_projectile_explode() -{ - - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; -#ifdef TURRET_DEBUG - float d; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); - self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; - self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; -#else - RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); -#endif - remove(self); -} - -void turret_projectile_touch() -{ - PROJECTILE_TOUCH; - turret_projectile_explode(); -} - -void turret_projectile_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.velocity += vforce; - self.health -= damage; - //self.realowner = attacker; // Dont change realowner, it does not make much sense for turrets - if(self.health <= 0) - W_PrepareExplosionByDamage(self.owner, turret_projectile_explode); -} - -entity turret_projectile(string _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) -{ - entity proj; - - sound (self, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); - proj = spawn (); - setorigin(proj, self.tur_shotorg); - setsize(proj, '-0.5 -0.5 -0.5' * _size, '0.5 0.5 0.5' * _size); - proj.owner = self; - proj.realowner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = self.shot_dmg; - proj.think = turret_projectile_explode; - proj.touch = turret_projectile_touch; - proj.nextthink = time + 9; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.velocity = normalize(self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; - proj.flags = FL_PROJECTILE; - proj.enemy = self.enemy; - proj.totalfrags = _death; - PROJECTILE_MAKETRIGGER(proj); - if(_health) - { - proj.health = _health; - proj.takedamage = DAMAGE_YES; - proj.event_damage = turret_projectile_damage; - } - else - proj.flags |= FL_NOTARGET; - - CSQCProjectile(proj, _cli_anim, _proj_type, _cull); - - return proj; -} - -/** -** updates enemy distances, predicted impact point/time -** and updated aim<->predict impact distance. -**/ -void turret_do_updates(entity t_turret) -{ - vector enemy_pos; - entity oldself; - - oldself = self; - self = t_turret; - - enemy_pos = real_origin(self.enemy); - - turret_tag_fire_update(); - - self.tur_shotdir_updated = v_forward; - self.tur_dist_enemy = vlen(self.tur_shotorg - enemy_pos); - self.tur_dist_aimpos = vlen(self.tur_shotorg - self.tur_aimpos); - - /*if((self.firecheck_flags & TFL_FIRECHECK_VERIFIED) && (self.enemy)) - { - oldpos = self.enemy.origin; - setorigin(self.enemy, self.tur_aimpos); - tracebox(self.tur_shotorg, '-1 -1 -1', '1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); - setorigin(self.enemy, oldpos); - - if(trace_ent == self.enemy) - self.tur_dist_impact_to_aimpos = 0; - else - self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos); - } - else*/ - tracebox(self.tur_shotorg, '-1 -1 -1','1 1 1', self.tur_shotorg + (self.tur_shotdir_updated * self.tur_dist_aimpos), MOVE_NORMAL,self); - - self.tur_dist_impact_to_aimpos = vlen(trace_endpos - self.tur_aimpos) - (vlen(self.enemy.maxs - self.enemy.mins) * 0.5); - self.tur_impactent = trace_ent; - self.tur_impacttime = vlen(self.tur_shotorg - trace_endpos) / self.shot_speed; - - self = oldself; -} - -/* -vector turret_fovsearch_pingpong() -{ - vector wish_angle; - if(self.phase < time) - { - if( self.tur_head.phase ) - self.tur_head.phase = 0; - else - self.tur_head.phase = 1; - self.phase = time + 5; - } - - if( self.tur_head.phase) - wish_angle = self.idle_aim + '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); - else - wish_angle = self.idle_aim - '0 1 0' * (self.aim_maxrot * (self.target_select_fov / 360)); - - return wish_angle; -} - -vector turret_fovsearch_steprot() -{ - vector wish_angle; - //float rot_add; - - wish_angle = self.tur_head.angles; - wish_angle_x = self.idle_aim_x; - - if (self.phase < time) - { - //rot_add = self.aim_maxrot / self.target_select_fov; - wish_angle_y += (self.target_select_fov * 2); - - if(wish_angle_y > 360) - wish_angle_y = wish_angle_y - 360; - - self.phase = time + 1.5; - } - - return wish_angle; -} - -vector turret_fovsearch_random() -{ - vector wish_angle; - - if (self.phase < time) - { - wish_angle_y = random() * self.aim_maxrot; - if(random() < 0.5) - wish_angle_y *= -1; - - wish_angle_x = random() * self.aim_maxpitch; - if(random() < 0.5) - wish_angle_x *= -1; - - self.phase = time + 5; - - self.tur_aimpos = wish_angle; - } - - return self.idle_aim + self.tur_aimpos; -} -*/ - -/** -** Handles head rotation according to -** the units .track_type and .track_flags -**/ -.float turret_framecounter; -void turret_stdproc_track() -{ - vector target_angle; // This is where we want to aim - vector move_angle; // This is where we can aim - float f_tmp; - vector v1, v2; - v1 = self.tur_head.angles; - v2 = self.tur_head.avelocity; - - if (self.track_flags == TFL_TRACK_NO) - return; - - if not (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_ROT) - { - self.tur_head.angles_y += bound(-f_tmp, move_angle_y, f_tmp); - if(self.tur_head.angles_y > self.aim_maxrot) - self.tur_head.angles_y = self.aim_maxrot; - - if(self.tur_head.angles_y < -self.aim_maxrot) - self.tur_head.angles_y = self.aim_maxrot; - } - - // CSQC - self.SendFlags |= TNSF_ANG; - - return; - - case TFL_TRACKTYPE_FLUIDINERTIA: - f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic - move_angle_x = bound(-self.aim_speed, move_angle_x * self.track_accel_pitch * f_tmp, self.aim_speed); - move_angle_y = bound(-self.aim_speed, move_angle_y * self.track_accel_rot * f_tmp, self.aim_speed); - move_angle = (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_ROT) - { - self.tur_head.avelocity_y = move_angle_y; - - if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) > self.aim_maxrot) - { - self.tur_head.avelocity_y = 0; - self.tur_head.angles_y = self.aim_maxrot; - - self.SendFlags |= TNSF_ANG; - } - - if((self.tur_head.angles_y + self.tur_head.avelocity_y * self.ticrate) < -self.aim_maxrot) - { - self.tur_head.avelocity_y = 0; - self.tur_head.angles_y = -self.aim_maxrot; - - self.SendFlags |= TNSF_ANG; - } - } - - self.SendFlags |= TNSF_AVEL; - - // Force a angle update every 10'th frame - self.turret_framecounter += 1; - if(self.turret_framecounter >= 10) - { - self.SendFlags |= TNSF_ANG; - self.turret_framecounter = 0; - } -} - - -/* - + = implemented - - = not implemented - - + TFL_FIRECHECK_NO - + TFL_FIRECHECK_WORLD - + TFL_FIRECHECK_DEAD - + TFL_FIRECHECK_DISTANCES - - TFL_FIRECHECK_LOS - + TFL_FIRECHECK_AIMDIST - + TFL_FIRECHECK_REALDIST - - TFL_FIRECHECK_ANGLEDIST - - TFL_FIRECHECK_TEAMCECK - + TFL_FIRECHECK_AFF - + TFL_FIRECHECK_OWM_AMMO - + TFL_FIRECHECK_OTHER_AMMO - + TFL_FIRECHECK_REFIRE -*/ - -/** -** Preforms pre-fire checks based on the uints firecheck_flags -**/ -float turret_stdproc_firecheck() -{ - // This one just dont care =) - if (self.firecheck_flags & TFL_FIRECHECK_NO) - return 1; - - if (self.enemy == world) - return 0; - - // Ready? - if (self.firecheck_flags & TFL_FIRECHECK_REFIRE) - if (self.attack_finished_single > time) return 0; - - // Special case: volly fire turret that has to fire a full volly if a shot was fired. - if (self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) - if (self.volly_counter != self.shot_volly) - if(self.ammo >= self.shot_dmg) - return 1; - - // Lack of zombies makes shooting dead things unnecessary :P - if (self.firecheck_flags & TFL_FIRECHECK_DEAD) - if (self.enemy.deadflag != DEAD_NO) - return 0; - - // Own ammo? - if (self.firecheck_flags & TFL_FIRECHECK_OWM_AMMO) - if (self.ammo < self.shot_dmg) - return 0; - - // Other's ammo? (support-supply units) - if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO) - if (self.enemy.ammo >= self.enemy.ammo_max) - return 0; - - // Target of opertunity? - if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) - { - self.enemy = self.tur_impactent; - return 1; - } - - if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES) - { - // To close? - if (self.tur_dist_aimpos < self.target_range_min) - if(turret_validate_target(self, self.tur_impactent, self.target_validate_flags) > 0) - return 1; // Target of opertunity? - else - return 0; - } - - // Try to avoid FF? - if (self.firecheck_flags & TFL_FIRECHECK_AFF) - if (self.tur_impactent.team == self.team) - return 0; - - // aim<->predicted impact - if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST) - if (self.tur_dist_impact_to_aimpos > self.aim_firetolerance_dist) - return 0; - - // Volly status - if (self.shot_volly > 1) - if (self.volly_counter == self.shot_volly) - if (self.ammo < (self.shot_dmg * self.shot_volly)) - return 0; - - /*if(self.firecheck_flags & TFL_FIRECHECK_VERIFIED) - if(self.tur_impactent != self.enemy) - return 0;*/ - - return 1; -} - -/* - + TFL_TARGETSELECT_NO - + TFL_TARGETSELECT_LOS - + TFL_TARGETSELECT_PLAYERS - + TFL_TARGETSELECT_MISSILES - - TFL_TARGETSELECT_TRIGGERTARGET - + TFL_TARGETSELECT_ANGLELIMITS - + TFL_TARGETSELECT_RANGELIMTS - + TFL_TARGETSELECT_TEAMCHECK - - TFL_TARGETSELECT_NOBUILTIN - + TFL_TARGETSELECT_OWNTEAM -*/ - -/** -** Evaluate a entity for target valitity based on validate_flags -** NOTE: the caller must check takedamage before calling this, to inline this check. -**/ -float turret_validate_target(entity e_turret, entity e_target, float validate_flags) -{ - vector v_tmp; - - //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN) - // return -0.5; - - if(e_target.owner == e_turret) - return -0.5; - - if not(checkpvs(e_target.origin, e_turret)) - return -1; - - if not (e_target) - return -2; - - if(g_onslaught) - if (substring(e_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! - return - 3; - - if (validate_flags & TFL_TARGETSELECT_NO) - return -4; - - // If only this was used more.. - if (e_target.flags & FL_NOTARGET) - return -5; - - // Cant touch this - if(e_target.vehicle_flags & VHF_ISVEHICLE) - { - if (e_target.vehicle_health <= 0) - return -6; - } - else if (e_target.health <= 0) - return -6; - - // player - if (IS_CLIENT(e_target)) - { - if not (validate_flags & TFL_TARGETSELECT_PLAYERS) - return -7; - - if (e_target.deadflag != DEAD_NO) - return -8; - } - - // enemy turrets - if (validate_flags & TFL_TARGETSELECT_NOTURRETS) - if (e_target.turret_firefunc || e_target.owner.tur_head == e_target) - if(e_target.team != e_turret.team) // Dont break support units. - return -9; - - // Missile - if (e_target.flags & FL_PROJECTILE) - if not (validate_flags & TFL_TARGETSELECT_MISSILES) - return -10; - - if (validate_flags & TFL_TARGETSELECT_MISSILESONLY) - if not (e_target.flags & FL_PROJECTILE) - return -10.5; - - // Team check - if (validate_flags & TFL_TARGETSELECT_TEAMCHECK) - { - if (validate_flags & TFL_TARGETSELECT_OWNTEAM) - { - if (e_target.team != e_turret.team) - return -11; - - if (e_turret.team != e_target.owner.team) - return -12; - } - else - { - if (e_target.team == e_turret.team) - return -13; - - if (e_turret.team == e_target.owner.team) - return -14; - } - } - - // Range limits? - tvt_dist = vlen(e_turret.origin - real_origin(e_target)); - if (validate_flags & TFL_TARGETSELECT_RANGELIMTS) - { - if (tvt_dist < e_turret.target_range_min) - return -15; - - if (tvt_dist > e_turret.target_range) - return -16; - } - - // Can we even aim this thing? - tvt_thadv = angleofs3(e_turret.tur_head.origin, e_turret.angles + e_turret.tur_head.angles, e_target); - tvt_tadv = shortangle_vxy(angleofs(e_turret, e_target), e_turret.angles); - tvt_thadf = vlen(tvt_thadv); - tvt_tadf = vlen(tvt_tadv); - - /* - if(validate_flags & TFL_TARGETSELECT_FOV) - { - if(e_turret.target_select_fov < tvt_thadf) - return -21; - } - */ - - if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS) - { - if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch) - return -17; - - if (fabs(tvt_tadv_y) > e_turret.aim_maxrot) - return -18; - } - - // Line of sight? - if (validate_flags & TFL_TARGETSELECT_LOS) - { - v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5); - - traceline(e_turret.origin + '0 0 16', v_tmp, 0, e_turret); - - if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos)) - return -19; - } - - if (e_target.classname == "grapplinghook") - return -20; - - /* - if (e_target.classname == "func_button") - return -21; - */ - -#ifdef TURRET_DEBUG_TARGETSELECT - dprint("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n"); -#endif - - return 1; -} - -entity turret_select_target() -{ - entity e; // target looper entity - float score; // target looper entity score - entity e_enemy; // currently best scoreing target - float m_score; // currently best scoreing target's score - - m_score = 0; - if(self.enemy && self.enemy.takedamage && turret_validate_target(self,self.enemy,self.target_validate_flags) > 0) - { - e_enemy = self.enemy; - m_score = self.turret_score_target(self,e_enemy) * self.target_select_samebias; - } - else - e_enemy = self.enemy = world; - - e = findradius(self.origin, self.target_range); - - // Nothing to aim at? - if (!e) - return world; - - while (e) - { - if(e.takedamage) - { - float f = turret_validate_target(self, e, self.target_select_flags); - //dprint("F is: ", ftos(f), "\n"); - if ( f > 0) - { - score = self.turret_score_target(self,e); - if ((score > m_score) && (score > 0)) - { - e_enemy = e; - m_score = score; - } - } - } - e = e.chain; - } - - return e_enemy; -} - -void turret_think() -{ - entity e; - - self.nextthink = time + self.ticrate; - - // ONS uses somewhat backwards linking. - if (teamplay) - { - if (g_onslaught) - if (self.target) - { - e = find(world, targetname,self.target); - if (e != world) - self.team = e.team; - } - - if (self.team != self.tur_head.team) - turret_stdproc_respawn(); - } - -#ifdef TURRET_DEBUG - if (self.tur_dbg_tmr1 < time) - { - if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9); - paint_target(self,256,self.tur_dbg_rvec,0.9); - self.tur_dbg_tmr1 = time + 1; - } -#endif - - // Handle ammo - if not (self.spawnflags & TSF_NO_AMMO_REGEN) - if (self.ammo < self.ammo_max) - self.ammo = min(self.ammo + self.ammo_recharge, self.ammo_max); - - // Inactive turrets needs to run the think loop, - // So they can handle animation and wake up if need be. - if not (self.active) - { - turret_stdproc_track(); - return; - } - - // This is typicaly used for zaping every target in range - // turret_fusionreactor uses this to recharge friendlys. - if (self.shoot_flags & TFL_SHOOT_HITALLVALID) - { - // Do a self.turret_fire for every valid target. - e = findradius(self.origin,self.target_range); - while (e) - { - if(e.takedamage) - { - if (turret_validate_target(self,e,self.target_validate_flags)) - { - self.enemy = e; - - turret_do_updates(self); - - if (self.turret_firecheckfunc()) - turret_fire(); - } - } - - e = e.chain; - } - self.enemy = world; - } - else if(self.shoot_flags & TFL_SHOOT_CUSTOM) - { - // This one is doing something.. oddball. assume its handles what needs to be handled. - - // Predict? - if not(self.aim_flags & TFL_AIM_NO) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch? - if not(self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - - // Fire? - if (self.turret_firecheckfunc()) - turret_fire(); - } - else - { - // Special case for volly always. if it fired once it must compleate the volly. - if(self.shoot_flags & TFL_SHOOT_VOLLYALWAYS) - if(self.volly_counter != self.shot_volly) - { - // Predict or whatnot - if not(self.aim_flags & TFL_AIM_NO) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch - if not(self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - - // Fire! - if (self.turret_firecheckfunc() != 0) - turret_fire(); - - if(self.turret_postthink) - self.turret_postthink(); - - return; - } - - // Check if we have a vailid enemy, and try to find one if we dont. - - // g_turrets_targetscan_maxdelay forces a target re-scan at least this often - float do_target_scan = 0; - if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) - do_target_scan = 1; - - // Old target (if any) invalid? - if(self.target_validate_time < time) - if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) - { - self.enemy = world; - self.target_validate_time = time + 0.5; - do_target_scan = 1; - } - - // But never more often then g_turrets_targetscan_mindelay! - if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) - do_target_scan = 0; - - if(do_target_scan) - { - self.enemy = turret_select_target(); - self.target_select_time = time; - } - - // No target, just go to idle, do any custom stuff and bail. - if (self.enemy == world) - { - // Turn & pitch - if not(self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - // do any per-turret stuff - if(self.turret_postthink) - self.turret_postthink(); - - // And bail. - return; - } - else - self.lip = time + autocvar_g_turrets_aimidle_delay; // Keep track of the last time we had a target. - - // Predict? - if not(self.aim_flags & TFL_AIM_NO) - self.tur_aimpos = turret_stdproc_aim_generic(); - - // Turn & pitch? - if not(self.track_flags & TFL_TRACK_NO) - turret_stdproc_track(); - - turret_do_updates(self); - - // Fire? - if (self.turret_firecheckfunc()) - turret_fire(); - } - - // do any custom per-turret stuff - if(self.turret_postthink) - self.turret_postthink(); -} - -void turret_fire() -{ - if (autocvar_g_turrets_nofire != 0) - return; - - self.turret_firefunc(); - - self.attack_finished_single = time + self.shot_refire; - self.ammo -= self.shot_dmg; - self.volly_counter = self.volly_counter - 1; - - if (self.volly_counter <= 0) - { - self.volly_counter = self.shot_volly; - - if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) - self.enemy = world; - - if (self.shot_volly > 1) - self.attack_finished_single = time + self.shot_volly_refire; - } - -#ifdef TURRET_DEBUG - if (self.enemy) paint_target3(self.tur_aimpos, 64, self.tur_dbg_rvec, self.tur_impacttime + 0.25); -#endif -} - -void turret_stdproc_fire() -{ - dprint("^1Bang, ^3your dead^7 ",self.enemy.netname,"! ^1(turret with no real firefunc)\n"); -} - -/* - When .used a turret switch team to activator.team. - If activator is world, the turret go inactive. -*/ -void turret_stdproc_use() -{ - dprint("Turret ",self.netname, " used by ", activator.classname, "\n"); - - self.team = activator.team; - - if(self.team == 0) - self.active = ACTIVE_NOT; - else - self.active = ACTIVE_ACTIVE; - -} - -void turret_link() -{ - Net_LinkEntity(self, TRUE, 0, turret_send); - self.think = turret_think; - self.nextthink = time; - self.tur_head.effects = EF_NODRAW; -} - -void turrets_manager_think() -{ - self.nextthink = time + 1; - - entity e; - if (autocvar_g_turrets_reloadcvars == 1) - { - e = nextent(world); - while (e) - { - if (e.turrcaps_flags & TFL_TURRCAPS_ISTURRET) - { - load_unit_settings(e,e.cvar_basename,1); - if(e.turret_postthink) - e.turret_postthink(); - } - - e = nextent(e); - } - cvar_set("g_turrets_reloadcvars","0"); - } -} - -/* -* Standard turret initialization. use this! -* (unless you have a very good reason not to) -* if the return value is 0, the turret should be removed. -*/ -float turret_stdproc_init (string cvar_base_name, string base, string head, float _turret_type) -{ - entity e, ee = world; - - // Are turrets allowed? - if (autocvar_g_turrets == 0) - return 0; - - if(_turret_type < 1 || _turret_type > TID_LAST) - { - dprint("Invalid / Unkown turret type\"", ftos(_turret_type), "\", aborting!\n"); - return 0; - } - self.turret_type = _turret_type; - - e = find(world, classname, "turret_manager"); - if not (e) - { - e = spawn(); - e.classname = "turret_manager"; - e.think = turrets_manager_think; - e.nextthink = time + 2; - } - - if not (self.spawnflags & TSF_SUSPENDED) - builtin_droptofloor(); // why can't we use regular droptofloor here? - - // Terrainbase spawnflag. This puts a enlongated model - // under the turret, so it looks ok on uneaven surfaces. - /* TODO: Handle this with CSQC - if (self.spawnflags & TSF_TERRAINBASE) - { - entity tb; - tb = spawn(); - setmodel(tb,"models/turrets/terrainbase.md3"); - setorigin(tb,self.origin); - tb.solid = SOLID_BBOX; - } - */ - - self.cvar_basename = cvar_base_name; - load_unit_settings(self, self.cvar_basename, 0); - - self.effects = EF_NODRAW; - - // Handle turret teams. - if not (teamplay) - self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother. - else if(g_onslaught && self.targetname) - { - e = find(world,target,self.targetname); - if(e != world) - { - self.team = e.team; - ee = e; - } - } - else if(!self.team) - self.team = MAX_SHOT_DISTANCE; // Group all turrets into the same team, so they dont kill eachother. - - /* - * Try to guess some reasonaly defaults - * for missing params and do sanety checks - * thise checks could produce some "interesting" results - * if it hits a glitch in my logic :P so try to set as mutch - * as possible beforehand. - */ - if not(self.ticrate) - { - if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) - self.ticrate = 0.2; // Support units generaly dont need to have a high speed ai-loop - else - self.ticrate = 0.1; // 10 fps for normal turrets - } - - self.ticrate = bound(sys_frametime, self.ticrate, 60); // keep it sane - -// General stuff - if (self.netname == "") - self.netname = self.classname; - - if not (self.respawntime) - self.respawntime = 60; - self.respawntime = max(-1, self.respawntime); - - if not (self.health) - self.health = 1000; - self.tur_health = max(1, self.health); - - if not (self.turrcaps_flags) - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - - if not (self.damage_flags) - self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE; - -// Shot stuff. - if not (self.shot_refire) - self.shot_refire = 1; - self.shot_refire = bound(0.01, self.shot_refire, 9999); - - if not (self.shot_dmg) - self.shot_dmg = self.shot_refire * 50; - self.shot_dmg = max(1, self.shot_dmg); - - if not (self.shot_radius) - self.shot_radius = self.shot_dmg * 0.5; - self.shot_radius = max(1, self.shot_radius); - - if not (self.shot_speed) - self.shot_speed = 2500; - self.shot_speed = max(1, self.shot_speed); - - if not (self.shot_spread) - self.shot_spread = 0.0125; - self.shot_spread = bound(0.0001, self.shot_spread, 500); - - if not (self.shot_force) - self.shot_force = self.shot_dmg * 0.5 + self.shot_radius * 0.5; - self.shot_force = bound(0.001, self.shot_force, 5000); - - if not (self.shot_volly) - self.shot_volly = 1; - self.shot_volly = bound(1, self.shot_volly, floor(self.ammo_max / self.shot_dmg)); - - if not (self.shot_volly_refire) - self.shot_volly_refire = self.shot_refire * self.shot_volly; - self.shot_volly_refire = bound(self.shot_refire, self.shot_volly_refire, 60); - - if not (self.firecheck_flags) - self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | - TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK | - TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE; - -// Range stuff. - if not (self.target_range) - self.target_range = self.shot_speed * 0.5; - self.target_range = bound(0, self.target_range, MAX_SHOT_DISTANCE); - - if not (self.target_range_min) - self.target_range_min = self.shot_radius * 2; - self.target_range_min = bound(0, self.target_range_min, MAX_SHOT_DISTANCE); - - if not (self.target_range_optimal) - self.target_range_optimal = self.target_range * 0.5; - self.target_range_optimal = bound(0, self.target_range_optimal, MAX_SHOT_DISTANCE); - - -// Aim stuff. - if not (self.aim_maxrot) - self.aim_maxrot = 90; - self.aim_maxrot = bound(0, self.aim_maxrot, 360); - - if not (self.aim_maxpitch) - self.aim_maxpitch = 20; - self.aim_maxpitch = bound(0, self.aim_maxpitch, 90); - - if not (self.aim_speed) - self.aim_speed = 36; - self.aim_speed = bound(0.1, self.aim_speed, 1000); - - if not (self.aim_firetolerance_dist) - self.aim_firetolerance_dist = 5 + (self.shot_radius * 2); - self.aim_firetolerance_dist = bound(0.1, self.aim_firetolerance_dist, MAX_SHOT_DISTANCE); - - if not (self.aim_flags) - { - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - if(self.turrcaps_flags & TFL_TURRCAPS_RADIUSDMG) - self.aim_flags |= TFL_AIM_GROUNDGROUND; - } - - if not (self.track_type) - self.track_type = TFL_TRACKTYPE_STEPMOTOR; - - if (self.track_type != TFL_TRACKTYPE_STEPMOTOR) - { - // Fluid / Ineria mode. Looks mutch nicer. - // Can reduce aim preformance alot, needs a bit diffrent aimspeed - - if not (self.aim_speed) - self.aim_speed = 180; - self.aim_speed = bound(0.1, self.aim_speed, 1000); - - if not (self.track_accel_pitch) - self.track_accel_pitch = 0.5; - - if not (self.track_accel_rot) - self.track_accel_rot = 0.5; - - if not (self.track_blendrate) - self.track_blendrate = 0.35; - } - - if (!self.track_flags) - self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT; - - -// Target selection stuff. - if not (self.target_select_rangebias) - self.target_select_rangebias = 1; - self.target_select_rangebias = bound(-10, self.target_select_rangebias, 10); - - if not (self.target_select_samebias) - self.target_select_samebias = 1; - self.target_select_samebias = bound(-10, self.target_select_samebias, 10); - - if not (self.target_select_anglebias) - self.target_select_anglebias = 1; - self.target_select_anglebias = bound(-10, self.target_select_anglebias, 10); - - if not (self.target_select_missilebias) - self.target_select_missilebias = -10; - - self.target_select_missilebias = bound(-10, self.target_select_missilebias, 10); - self.target_select_playerbias = bound(-10, self.target_select_playerbias, 10); - - if not (self.target_select_flags) - { - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_TEAMCHECK - | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS; - - if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL) - self.target_select_flags |= TFL_TARGETSELECT_MISSILES; - - if (self.turrcaps_flags & TFL_TURRCAPS_PLAYERKILL) - self.target_select_flags |= TFL_TARGETSELECT_PLAYERS; - //else - // self.target_select_flags = TFL_TARGETSELECT_NO; - } - - self.target_validate_flags = self.target_select_flags; - -// Ammo stuff - if not (self.ammo_max) - self.ammo_max = self.shot_dmg * 10; - self.ammo_max = max(self.shot_dmg, self.ammo_max); - - if not (self.ammo) - self.ammo = self.shot_dmg * 5; - self.ammo = bound(0,self.ammo, self.ammo_max); - - if not (self.ammo_recharge) - self.ammo_recharge = self.shot_dmg * 0.5; - self.ammo_recharge = max(0 ,self.ammo_recharge); - - // Convert the recharge from X per sec to X per ticrate - self.ammo_recharge = self.ammo_recharge * self.ticrate; - - if not (self.ammo_flags) - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; - -// Damage stuff - if(self.spawnflags & TSL_NO_RESPAWN) - if not (self.damage_flags & TFL_DMG_DEATH_NORESPAWN) - self.damage_flags |= TFL_DMG_DEATH_NORESPAWN; - -// Offsets & origins - if (!self.tur_shotorg) self.tur_shotorg = '50 0 50'; - - if (!self.health) - self.health = 150; - -// Game hooks - if(MUTATOR_CALLHOOK(TurretSpawn)) - return 0; - -// End of default & sanety checks, start building the turret. - -// Spawn extra bits - self.tur_head = spawn(); - self.tur_head.netname = self.tur_head.classname = "turret_head"; - self.tur_head.team = self.team; - self.tur_head.owner = self; - - setmodel(self, base); - setmodel(self.tur_head, head); - - setsize(self, '-32 -32 0', '32 32 64'); - setsize(self.tur_head, '0 0 0', '0 0 0'); - - setorigin(self.tur_head, '0 0 0'); - setattachment(self.tur_head, self, "tag_head"); - - self.tur_health = self.health; - self.solid = SOLID_BBOX; - self.tur_head.solid = SOLID_NOT; - self.takedamage = DAMAGE_AIM; - self.tur_head.takedamage = DAMAGE_NO; - self.movetype = MOVETYPE_NOCLIP; - self.tur_head.movetype = MOVETYPE_NOCLIP; - - // Defend mode? - if not (self.tur_defend) - if (self.target != "") - { - self.tur_defend = find(world, targetname, self.target); - if (self.tur_defend == world) - { - self.target = ""; - dprint("Turret has invalid defendpoint!\n"); - } - } - - // In target defend mode, aim on the spot to defend when idle. - if (self.tur_defend) - self.idle_aim = self.tur_head.angles + angleofs(self.tur_head, self.tur_defend); - else - self.idle_aim = '0 0 0'; - - // Attach stdprocs. override when and what needed - self.turret_firecheckfunc = turret_stdproc_firecheck; - self.turret_firefunc = turret_stdproc_fire; - self.event_damage = turret_stdproc_damage; - - if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT) - self.turret_score_target = turret_stdproc_targetscore_support; - else - self.turret_score_target = turret_stdproc_targetscore_generic; - - self.use = turret_stdproc_use; - self.bot_attack = TRUE; - - ++turret_count; - self.nextthink = time + 1; - self.nextthink += turret_count * sys_frametime; - - self.tur_head.team = self.team; - self.view_ofs = '0 0 0'; - -#ifdef TURRET_DEBUG - self.tur_dbg_start = self.nextthink; - while (vlen(self.tur_dbg_rvec) < 2) - self.tur_dbg_rvec = randomvec() * 4; - - self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x); - self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y); - self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z); -#endif - - // Its all good. - self.turrcaps_flags |= TFL_TURRCAPS_ISTURRET; - - self.classname = "turret_main"; - - self.active = ACTIVE_ACTIVE; - - // In ONS mode, and linked to a ONS ent. need to call the use to set team. - if (g_onslaught && ee) - { - activator = ee; - self.use(); - } - - turret_link(); - turret_stdproc_respawn(); - turret_tag_fire_update(); - - return 1; -} - - diff --git a/qcsrc/server/tturrets/system/system_misc.qc b/qcsrc/server/tturrets/system/system_misc.qc deleted file mode 100644 index 2e3a006881..0000000000 --- a/qcsrc/server/tturrets/system/system_misc.qc +++ /dev/null @@ -1,355 +0,0 @@ -/* -* Return a angle within +/- 360. -*/ -float anglemods(float v) -{ - v = v - 360 * floor(v / 360); - - if(v >= 180) - return v - 360; - else if(v <= -180) - return v + 360; - else - return v; -} - -/* -* Return the short angle -*/ -float shortangle_f(float ang1, float ang2) -{ - if(ang1 > ang2) - { - if(ang1 > 180) - return ang1 - 360; - } - else - { - if(ang1 < -180) - return ang1 + 360; - } - - return ang1; -} - -vector shortangle_v(vector ang1, vector ang2) -{ - vector vtmp; - - vtmp_x = shortangle_f(ang1_x,ang2_x); - vtmp_y = shortangle_f(ang1_y,ang2_y); - vtmp_z = shortangle_f(ang1_z,ang2_z); - - return vtmp; -} - -vector shortangle_vxy(vector ang1, vector ang2) -{ - vector vtmp = '0 0 0'; - - vtmp_x = shortangle_f(ang1_x,ang2_x); - vtmp_y = shortangle_f(ang1_y,ang2_y); - - return vtmp; -} - - -/* -* Get "real" origin, in worldspace, even if ent is attached to something else. -*/ -vector real_origin(entity ent) -{ - entity e; - vector v = ((ent.absmin + ent.absmax) * 0.5); - - e = ent.tag_entity; - while(e) - { - v = v + ((e.absmin + e.absmax) * 0.5); - e = e.tag_entity; - } - - return v; -} - -/* -* Return the angle between two enteties -*/ -vector angleofs(entity from, entity to) -{ - vector v_res; - - v_res = normalize(to.origin - from.origin); - v_res = vectoangles(v_res); - v_res = v_res - from.angles; - - if (v_res_x < 0) v_res_x += 360; - if (v_res_x > 180) v_res_x -= 360; - - if (v_res_y < 0) v_res_y += 360; - if (v_res_y > 180) v_res_y -= 360; - - return v_res; -} - -vector angleofs3(vector from, vector from_a, entity to) -{ - vector v_res; - - v_res = normalize(to.origin - from); - v_res = vectoangles(v_res); - v_res = v_res - from_a; - - if (v_res_x < 0) v_res_x += 360; - if (v_res_x > 180) v_res_x -= 360; - - if (v_res_y < 0) v_res_y += 360; - if (v_res_y > 180) v_res_y -= 360; - - return v_res; -} - -/* -* Update self.tur_shotorg by getting up2date bone info -* NOTICE this func overwrites the global v_forward, v_right and v_up vectors. -*/ -#define turret_tag_fire_update() self.tur_shotorg = gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_fire"));v_forward = normalize(v_forward) -float turret_tag_fire_update_s() -{ - 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) - -{ - 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; -} - -// Plug this into wherever precache is done. -void g_turrets_common_precash() -{ - precache_model ("models/turrets/c512.md3"); - precache_model ("models/marker.md3"); -} - -void turrets_precache_debug_models() -{ - 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"); -} - -void turrets_precash() -{ - #ifdef TURRET_DEBUG - turrets_precache_debug_models(); - #endif -} - - -#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 --git a/qcsrc/server/tturrets/system/system_scoreprocs.qc b/qcsrc/server/tturrets/system/system_scoreprocs.qc deleted file mode 100644 index 539be2ad9e..0000000000 --- a/qcsrc/server/tturrets/system/system_scoreprocs.qc +++ /dev/null @@ -1,130 +0,0 @@ -float turret_stdproc_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_stdproc_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_maxrot; - - 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; -} - -/* -float turret_stdproc_targetscore_close(entity _turret,entity _target) -{ - return 1 - (tvt_dist / _turret.target_range); -} - -float turret_stdproc_targetscore_far (entity _turret,entity _target) -{ - return tvt_dist / _turret.target_range; -} - -float turret_stdproc_targetscore_optimal(entity _turret,entity _target) -{ - return min(_turret.target_range_optimal,tvt_dist) / max(_turret.target_range_optimal,tvt_dist); -} - -float turret_stdproc_score_angular(entity _turret,entity _target) -{ - return 1 - (tvt_thadf / _turret.aim_maxrot); -} - -float turret_stdproc_targetscore_defend(entity _turret,entity _target) -{ - return 0; - //min(_target.origin,_turret.tur_defend.origin) / max(_target.origin,_turret.tur_defend.origin); -} -*/ diff --git a/qcsrc/server/tturrets/units/unit_checkpoint.qc b/qcsrc/server/tturrets/units/unit_checkpoint.qc deleted file mode 100644 index 481b4a285d..0000000000 --- a/qcsrc/server/tturrets/units/unit_checkpoint.qc +++ /dev/null @@ -1,83 +0,0 @@ -/** - turret_checkpoint -**/ - - -//.entity checkpoint_target; - -/* -#define checkpoint_cache_who flagcarried -#define checkpoint_cache_from lastrocket -#define checkpoint_cache_to selected_player -*/ - -.entity pathgoal; -.entity pathcurrent; - -/* -entity path_makeorcache(entity forwho,entity start, entity end) -{ - entity oldself; - entity pth; - oldself = self; - self = forwho; - - //pth = pathlib_makepath(start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR); - - self = oldself; - return pth; -} -*/ - -void turret_checkpoint_use() -{ -} - -#if 0 -void turret_checkpoint_think() -{ - if(self.enemy) - te_lightning1(self,self.origin, self.enemy.origin); - - self.nextthink = time + 0.25; -} -#endif -/*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32) ------------KEYS------------ -target: .targetname of next waypoint in chain. -wait: Pause at this point # seconds. ------------SPAWNFLAGS----------- ----------NOTES---------- -If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly. -If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached. -*/ -//float tc_acum; -void turret_checkpoint_init() -{ - traceline(self.origin + '0 0 16', self.origin - '0 0 1024', MOVE_WORLDONLY, self); - setorigin(self, trace_endpos + '0 0 32'); - - if(self.target != "") - { - self.enemy = find(world, targetname, self.target); - if(self.enemy == world) - dprint("A turret_checkpoint faild to find its target!\n"); - } - //self.think = turret_checkpoint_think; - //self.nextthink = time + tc_acum + 0.25; - //tc_acum += 0.25; -} - -void spawnfunc_turret_checkpoint() -{ - setorigin(self,self.origin); - self.think = turret_checkpoint_init; - self.nextthink = time + 0.2; -} - -// Compat. -void spawnfunc_walker_checkpoint() -{ - self.classname = "turret_checkpoint"; - spawnfunc_turret_checkpoint(); -} diff --git a/qcsrc/server/tturrets/units/unit_ewheel.qc b/qcsrc/server/tturrets/units/unit_ewheel.qc deleted file mode 100644 index 31b984e391..0000000000 --- a/qcsrc/server/tturrets/units/unit_ewheel.qc +++ /dev/null @@ -1,310 +0,0 @@ -#define ewheel_amin_stop 0 -#define ewheel_amin_fwd_slow 1 -#define ewheel_amin_fwd_fast 2 -#define ewheel_amin_bck_slow 3 -#define ewheel_amin_bck_fast 4 - -void ewheel_attack() -{ - float i; - entity _mis; - - for (i = 0; i < 1; ++i) - { - turret_do_updates(self); - - _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_LASER, 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; - } - -} -//#define EWHEEL_FANCYPATH -void ewheel_move_path() -{ -#ifdef EWHEEL_FANCYPATH - // Are we close enougth to a path node to switch to the next? - if (vlen(self.origin - self.pathcurrent.origin) < 64) - if (self.pathcurrent.path_next == world) - { - // Path endpoint reached - pathlib_deletepath(self.pathcurrent.owner); - self.pathcurrent = world; - - if (self.pathgoal) - { - if (self.pathgoal.use) - self.pathgoal.use(); - - if (self.pathgoal.enemy) - { - self.pathcurrent = pathlib_astar(self.pathgoal.origin,self.pathgoal.enemy.origin); - self.pathgoal = self.pathgoal.enemy; - } - } - else - self.pathgoal = world; - } - else - self.pathcurrent = self.pathcurrent.path_next; - -#else - if (vlen(self.origin - self.pathcurrent.origin) < 64) - self.pathcurrent = self.pathcurrent.enemy; -#endif - - if (self.pathcurrent) - { - - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); - - movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_fast, 0.4); - } -} - -void ewheel_move_enemy() -{ - - float newframe; - - self.steerto = steerlib_arrive(self.enemy.origin,self.target_range_optimal); - - //self.steerto = steerlib_standoff(self.enemy.origin,self.target_range_optimal); - //self.steerto = steerlib_beamsteer(self.steerto,1024,64,68,256); - self.moveto = self.origin + self.steerto * 128; - - if (self.tur_dist_enemy > self.target_range_optimal) - { - if ( self.tur_head.spawnshieldtime < 1 ) - { - newframe = ewheel_amin_fwd_fast; - movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_fast, 0.4); - } - else if (self.tur_head.spawnshieldtime < 2) - { - - newframe = ewheel_amin_fwd_slow; - movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4); - } - else - { - newframe = ewheel_amin_fwd_slow; - movelib_move_simple(v_forward, autocvar_g_turrets_unit_ewheel_speed_slower, 0.4); - } - } - else if (self.tur_dist_enemy < self.target_range_optimal * 0.5) - { - newframe = ewheel_amin_bck_slow; - movelib_move_simple(v_forward * -1, autocvar_g_turrets_unit_ewheel_speed_slow, 0.4); - } - else - { - newframe = ewheel_amin_stop; - movelib_beak_simple(autocvar_g_turrets_unit_ewheel_speed_stop); - } - - turrets_setframe(newframe , FALSE); - - /*if(self.frame != newframe) - { - self.frame = newframe; - self.SendFlags |= TNSF_ANIM; - self.anim_start_time = time; - }*/ -} - - -void ewheel_move_idle() -{ - if(self.frame != 0) - { - self.SendFlags |= TNSF_ANIM; - self.anim_start_time = time; - } - - self.frame = 0; - if (vlen(self.velocity)) - movelib_beak_simple(autocvar_g_turrets_unit_ewheel_speed_stop); -} - -void ewheel_postthink() -{ - float vz; - vector wish_angle, real_angle; - - vz = self.velocity_z; - - 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; -} - -void ewheel_respawnhook() -{ - entity e; - - // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. - if(self.movetype != MOVETYPE_WALK) - return; - - self.velocity = '0 0 0'; - self.enemy = world; - - setorigin(self, self.pos1); - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - -#ifdef EWHEEL_FANCYPATH - self.pathcurrent = WALKER_PATH(self.origin,e.origin); - self.pathgoal = e; -#else - self.pathcurrent = e; -#endif - } - } -} - -void ewheel_diehook() -{ - self.velocity = '0 0 0'; - -#ifdef EWHEEL_FANCYPATH - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); -#endif - self.pathcurrent = world; -} - -void turret_ewheel_dinit() -{ - entity e; - - if (self.netname == "") - self.netname = "eWheel Turret"; - - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - bprint("Warning! initital waypoint for ewheel does NOT exsist!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - self.goalcurrent = e; - } - - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE | TFL_TURRCAPS_ROAM ; - self.turret_respawnhook = ewheel_respawnhook; - - self.turret_diehook = ewheel_diehook; - - if (turret_stdproc_init("ewheel_std", "models/turrets/ewheel-base2.md3", "models/turrets/ewheel-gun1.md3", TID_EWHEEL) == 0) - { - remove(self); - return; - } - - self.frame = 1; - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.iscreature = TRUE; - self.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; - - setsize(self, '-32 -32 0', '32 32 48'); - - // Our fire routine - self.turret_firefunc = ewheel_attack; - self.turret_postthink = ewheel_postthink; - self.tur_head.frame = 1; - - // Convert from dgr / sec to dgr / tic - self.tur_head.aim_speed = autocvar_g_turrets_unit_ewheel_turnrate; - self.tur_head.aim_speed = self.tur_head.aim_speed / (1 / self.ticrate); - - //setorigin(self,self.origin + '0 0 128'); - if (self.target != "") - { - e = find(world,targetname,self.target); - if (!e) - { - dprint("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { -#ifdef EWHEEL_FANCYPATH - self.pathcurrent = WALKER_PATH(self.origin, e.origin); - self.pathgoal = e; -#else - self.pathcurrent = e; -#endif - } - } -} - -void spawnfunc_turret_ewheel() -{ - g_turrets_common_precash(); - - precache_model ("models/turrets/ewheel-base2.md3"); - precache_model ("models/turrets/ewheel-gun1.md3"); - - self.think = turret_ewheel_dinit; - self.nextthink = time + 0.5; -} diff --git a/qcsrc/server/tturrets/units/unit_flac.qc b/qcsrc/server/tturrets/units/unit_flac.qc deleted file mode 100644 index 7c21ba7abe..0000000000 --- a/qcsrc/server/tturrets/units/unit_flac.qc +++ /dev/null @@ -1,74 +0,0 @@ -void spawnfunc_turret_flac(); -void turret_flac_dinit(); -void turret_flac_attack(); - -void turret_flac_projectile_think_explode() -{ - if(self.enemy != world) - if(vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 3) - setorigin(self,self.enemy.origin + randomvec() * self.owner.shot_radius); - -#ifdef TURRET_DEBUG - float d; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, 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, self.owner.shot_force, self.totalfrags, world); -#endif - remove(self); -} - -void turret_flac_attack() -{ - entity proj; - - turret_tag_fire_update(); - - proj = turret_projectile("weapons/hagar_fire.wav", 5, 0, DEATH_TURRET_FLAC, PROJECTILE_HAGAR, TRUE, TRUE); - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - proj.think = turret_flac_projectile_think_explode; - proj.nextthink = time + self.tur_impacttime + (random() * 0.01 - random() * 0.01); - proj.missile_flags = MIF_SPLASH | MIF_PROXY; - - self.tur_head.frame = self.tur_head.frame + 1; - if (self.tur_head.frame >= 4) - self.tur_head.frame = 0; - -} - -void turret_flac_dinit() -{ - if (self.netname == "") - self.netname = "FLAC Cannon"; - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_MISSILEKILL; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - - if (turret_stdproc_init("flac_std", "models/turrets/base.md3", "models/turrets/flac.md3", TID_FLAC) == 0) - { - remove(self); - return; - } - setsize(self.tur_head,'-32 -32 0','32 32 64'); - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.target_select_flags |= TFL_TARGETSELECT_NOTURRETS | TFL_TARGETSELECT_MISSILESONLY; - - // Our fire routine - self.turret_firefunc = turret_flac_attack; - -} -/*QUAKED turret_flac (0 .5 .8) ? -*/ - -void spawnfunc_turret_flac() -{ - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/flac.md3"); - - self.think = turret_flac_dinit; - self.nextthink = time + 0.5; -} - diff --git a/qcsrc/server/tturrets/units/unit_fusionreactor.qc b/qcsrc/server/tturrets/units/unit_fusionreactor.qc deleted file mode 100644 index 8118b8f234..0000000000 --- a/qcsrc/server/tturrets/units/unit_fusionreactor.qc +++ /dev/null @@ -1,94 +0,0 @@ -void spawnfunc_turret_fusionreactor(); -void turret_fusionreactor_dinit(); -void turret_fusionreactor_fire(); - -void turret_fusionreactor_fire() -{ - 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); -} - -void turret_fusionreactor_postthink() -{ - self.tur_head.avelocity = '0 250 0' * (self.ammo / self.ammo_max); -} - -/* -void turret_fusionreactor_respawnhook() -{ - self.tur_head.avelocity = '0 50 0'; -} -*/ - -/** -** Preforms pre-fire checks for fusionreactor -**/ -float turret_fusionreactor_firecheck() -{ - if (self.attack_finished_single > time) - return 0; - - if (self.enemy.deadflag != DEAD_NO) - return 0; - - if (self.enemy == world) - return 0; - - if (self.ammo < self.shot_dmg) - return 0; - - if (self.enemy.ammo >= self.enemy.ammo_max) - return 0; - - if (vlen(self.enemy.origin - self.origin) > self.target_range) - return 0; - - if(self.team != self.enemy.team) - return 0; - - if not (self.enemy.ammo_flags & TFL_AMMO_ENERGY) - return 0; - - return 1; -} - -void turret_fusionreactor_dinit() -{ - if (self.netname == "") self.netname = "Fusionreactor"; - - self.turrcaps_flags = TFL_TURRCAPS_SUPPORT | TFL_TURRCAPS_AMMOSOURCE; - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE; - self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMTS; - self.firecheck_flags = TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_OTHER_AMMO | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD; - self.shoot_flags = TFL_SHOOT_HITALLVALID; - self.aim_flags = TFL_AIM_NO; - self.track_flags = TFL_TRACK_NO; - // self.turret_respawnhook = turret_fusionreactor_respawnhook; - - if (turret_stdproc_init("fusreac_std", "models/turrets/base.md3", "models/turrets/reactor.md3", TID_FUSION) == 0) - { - remove(self); - return; - } - self.tur_head.scale = 0.75; - self.tur_head.avelocity = '0 50 0'; - setsize(self,'-34 -34 0','34 34 90'); - - self.turret_firecheckfunc = turret_fusionreactor_firecheck; - self.turret_firefunc = turret_fusionreactor_fire; - self.turret_postthink = turret_fusionreactor_postthink; -} - -/*QUAKED turret_fusionreactor (0 .5 .8) ? -*/ -void spawnfunc_turret_fusionreactor() -{ - precache_model ("models/turrets/reactor.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_fusionreactor_dinit; - self.nextthink = time + 0.5; -} diff --git a/qcsrc/server/tturrets/units/unit_hellion.qc b/qcsrc/server/tturrets/units/unit_hellion.qc deleted file mode 100644 index 75360bcd71..0000000000 --- a/qcsrc/server/tturrets/units/unit_hellion.qc +++ /dev/null @@ -1,126 +0,0 @@ -void spawnfunc_turret_hellion(); -void turret_hellion_dinit(); -void turret_hellion_attack(); - -void turret_hellion_missile_think() -{ - vector olddir,newdir; - vector pre_pos; - float itime; - - self.nextthink = time + 0.05; - - olddir = normalize(self.velocity); - - if(self.tur_health < time) - turret_projectile_explode(); - - // Enemy dead? just keep on the current heading then. - if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO)) - { - - // Make sure we dont return to tracking a respawned player - self.enemy = world; - - // Turn model - self.angles = vectoangles(self.velocity); - - if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 5) ) - turret_projectile_explode(); - - // Accelerate - self.velocity = olddir * min(vlen(self.velocity) * autocvar_g_turrets_unit_hellion_std_shot_speed_gain, autocvar_g_turrets_unit_hellion_std_shot_speed_max); - - UpdateCSQCProjectile(self); - - return; - } - - // Enemy in range? - if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.2) - turret_projectile_explode(); - - // Predict enemy position - itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity); - pre_pos = self.enemy.origin + self.enemy.velocity * itime; - - pre_pos = (pre_pos + self.enemy.origin) * 0.5; - - // Find out the direction to that place - newdir = normalize(pre_pos - self.origin); - - // Turn - newdir = normalize(olddir + newdir * 0.35); - - // Turn model - self.angles = vectoangles(self.velocity); - - // Accelerate - self.velocity = newdir * min(vlen(self.velocity) * autocvar_g_turrets_unit_hellion_std_shot_speed_gain, autocvar_g_turrets_unit_hellion_std_shot_speed_max); - - if (itime < 0.05) - self.think = turret_projectile_explode; - - UpdateCSQCProjectile(self); -} -void turret_hellion_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.tur_health = time + 9; - missile.tur_aimpos = randomvec() * 128; - missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; - self.tur_head.frame += 1; -} - -void turret_hellion_postthink() -{ - if (self.tur_head.frame != 0) - self.tur_head.frame += 1; - - if (self.tur_head.frame >= 7) - self.tur_head.frame = 0; -} - -void turret_hellion_dinit() -{ - if (self.netname == "") self.netname = "Hellion Missile Turret"; - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; - self.aim_flags = TFL_AIM_SIMPLE; - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK ; - self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_OWM_AMMO; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - - if (turret_stdproc_init("hellion_std", "models/turrets/base.md3", "models/turrets/hellion.md3", TID_HELLION) == 0) - { - remove(self); - return; - } - - self.turret_firefunc = turret_hellion_attack; - self.turret_postthink = turret_hellion_postthink; -} - -/*QUAKED turret_hellion (0 .5 .8) ? -*/ -void spawnfunc_turret_hellion() -{ - precache_model ("models/turrets/hellion.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_hellion_dinit; - self.nextthink = time + 0.5; -} - - diff --git a/qcsrc/server/tturrets/units/unit_hk.qc b/qcsrc/server/tturrets/units/unit_hk.qc deleted file mode 100644 index c7d7396e8f..0000000000 --- a/qcsrc/server/tturrets/units/unit_hk.qc +++ /dev/null @@ -1,336 +0,0 @@ -//#define TURRET_DEBUG_HK - -#ifdef TURRET_DEBUG_HK -.float atime; -#endif - -void spawnfunc_turret_hk(); -void turret_hk_dinit(); -void turret_hk_attack(); - - -float hk_is_valid_target(entity e_target) -{ - if (e_target == world) - return 0; - - // If only this was used more.. - if (e_target.flags & FL_NOTARGET) - return 0; - - // Cant touch this - if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) - return 0; - - // player - if (IS_CLIENT(e_target)) - { - if (self.owner.target_select_playerbias < 0) - return 0; - - if (e_target.deadflag != DEAD_NO) - return 0; - } - - // Missile - if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0)) - return 0; - - // Team check - if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team)) - return 0; - - return 1; -} -void turret_hk_missile_think() -{ - vector vu, vd, vf, vl, vr, ve; // Vector (direction) - float fu, fd, ff, fl, fr, fe; // Fraction to solid - vector olddir,wishdir,newdir; // Final direction - float lt_for; // Length of Trace FORwrad - float lt_seek; // Length of Trace SEEK (left, right, up down) - float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) - vector pre_pos; - float myspeed; - entity e; - float ad,edist; - - self.nextthink = time + self.ticrate; - - //if (self.cnt < time) - // turret_hk_missile_explode(); - - if (self.enemy.deadflag != DEAD_NO) - self.enemy = world; - - // Pick the closest valid target. - if (!self.enemy) - { - e = findradius(self.origin, 5000); - while (e) - { - if (hk_is_valid_target(e)) - { - if (!self.enemy) - self.enemy = e; - else - if (vlen(self.origin - e.origin) < vlen(self.origin - self.enemy.origin)) - self.enemy = e; - } - e = e.chain; - } - } - - self.angles = vectoangles(self.velocity); - self.angles_x = self.angles_x * -1; - makevectors(self.angles); - self.angles_x = self.angles_x * -1; - - if (self.enemy) - { - edist = vlen(self.origin - self.enemy.origin); - // Close enougth to do decent damage? - if ( edist <= (self.owner.shot_radius * 0.25) ) - { - turret_projectile_explode(); - return; - } - - // Get data on enemy position - pre_pos = self.enemy.origin + - self.enemy.velocity * - min((vlen(self.enemy.origin - self.origin) / vlen(self.velocity)),0.5); - - traceline(self.origin, pre_pos,TRUE,self.enemy); - 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_std_shot_speed) ) - myspeed = max(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_decel, autocvar_g_turrets_unit_hk_std_shot_speed); - - // Failry clear, accelerate. - if ( (ff > 0.7) && (myspeed < autocvar_g_turrets_unit_hk_std_shot_speed_max) ) - myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel, autocvar_g_turrets_unit_hk_std_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_std_shot_speed_max) - myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel2,autocvar_g_turrets_unit_hk_std_shot_speed_max); - - wishdir = ve; - } - - if ((myspeed > autocvar_g_turrets_unit_hk_std_shot_speed) && (self.cnt > time)) - myspeed = min(myspeed * autocvar_g_turrets_unit_hk_std_shot_speed_accel2,autocvar_g_turrets_unit_hk_std_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_std_shot_speed_turnrate); - - // Set heading & speed - self.velocity = newdir * myspeed; - - // Align model with new heading - self.angles = vectoangles(self.velocity); - - -#ifdef TURRET_DEBUG_HK - //if(self.atime < time) { - if ((fe <= 0.99)||(edist > 1000)) - { - te_lightning2(world,self.origin, self.origin + vr * lt_seek); - te_lightning2(world,self.origin, self.origin + vl * lt_seek); - te_lightning2(world,self.origin, self.origin + vu * lt_seek); - te_lightning2(world,self.origin, self.origin + vd * lt_seek); - te_lightning2(world,self.origin, vf); - } - else - { - te_lightning2(world,self.origin, self.enemy.origin); - } - bprint("Speed: ", ftos(rint(myspeed)), "\n"); - bprint("Trace to solid: ", ftos(rint(ff * 100)), "%\n"); - bprint("Trace to target:", ftos(rint(fe * 100)), "%\n"); - self.atime = time + 0.2; - //} -#endif - - UpdateCSQCProjectile(self); -} - -void turret_hk_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; - -} - -void turret_hk_postthink() -{ - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 5) - self.tur_head.frame = 0; -} - -float turret_hk_addtarget(entity e_target,entity e_sender) -{ - if (e_target) - { - if (turret_validate_target(self,e_target,self.target_validate_flags) > 0) - { - self.enemy = e_target; - return 1; - } - } - - return 0; -} - -void turret_hk_dinit() -{ - if (self.netname == "") - self.netname = "Hunter-killer turret"; - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_RECIVETARGETS; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - self.aim_flags = TFL_AIM_SIMPLE; - self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - self.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; - self.shoot_flags = TFL_SHOOT_CLEARTARGET; - - if (turret_stdproc_init("hk_std", "models/turrets/base.md3", "models/turrets/hk.md3", TID_HK) == 0) - { - remove(self); - return; - } - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; - - // Our fire routine - self.turret_firefunc = turret_hk_attack; - - // re-color badge & handle recoil effect - self.turret_postthink = turret_hk_postthink; - - // What to do when reciveing foreign target data - self.turret_addtarget = turret_hk_addtarget; -} - - -/*QUAKED turret_hk (0 .5 .8) ? -* Turret that fires Hunter-killer missiles. -* Missiles seek their target and try to avoid obstacles. If target dies early, they -* pick a new one on their own. -*/ - -void spawnfunc_turret_hk() -{ - precache_model ("models/turrets/base.md3"); - precache_model ("models/turrets/hk.md3"); - - self.think = turret_hk_dinit; - self.nextthink = time + 0.5; -} - - diff --git a/qcsrc/server/tturrets/units/unit_machinegun.qc b/qcsrc/server/tturrets/units/unit_machinegun.qc deleted file mode 100644 index 8f18d845a9..0000000000 --- a/qcsrc/server/tturrets/units/unit_machinegun.qc +++ /dev/null @@ -1,54 +0,0 @@ -void spawnfunc_turret_machinegun(); -void turret_machinegun_std_init(); -void turret_machinegun_attack(); - -//.float bulletcounter; -void turret_machinegun_attack() -{ - fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0, 1, autocvar_g_balance_uzi_bulletconstant); - endFireBallisticBullet(); - - UziFlash(); - setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); -} - - -void turret_machinegun_std_init() -{ - if (self.netname == "") self.netname = "Machinegun Turret"; - - self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - - if not (autocvar_g_antilag_bullets) - self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; - - if (turret_stdproc_init("machinegun_std", "models/turrets/base.md3", "models/turrets/machinegun.md3", TID_MACHINEGUN) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - // Our fire routine - self.turret_firefunc = turret_machinegun_attack; - -} - - -/*QUAKED turret_machinegun (0 .5 .8) ? -* machinegun turret. does what you'd expect -*/ -void spawnfunc_turret_machinegun() -{ - precache_model ("models/turrets/machinegun.md3"); - precache_model ("models/turrets/base.md3"); - precache_sound ("weapons/uzi_fire.wav"); - - self.think = turret_machinegun_std_init; - self.nextthink = time + 0.5; -} - diff --git a/qcsrc/server/tturrets/units/unit_mlrs.qc b/qcsrc/server/tturrets/units/unit_mlrs.qc deleted file mode 100644 index a12d9d7259..0000000000 --- a/qcsrc/server/tturrets/units/unit_mlrs.qc +++ /dev/null @@ -1,63 +0,0 @@ -void spawnfunc_turret_mlrs(); -void turret_mlrs_dinit(); -void turret_mlrs_attack(); - -void turret_mlrs_postthink() -{ - // 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"); - } -} - -void turret_mlrs_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); -} - -void turret_mlrs_dinit() -{ - if (self.netname == "") self.netname = "MLRS turret"; - - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE; - - if (turret_stdproc_init("mlrs_std", "models/turrets/base.md3", "models/turrets/mlrs.md3", TID_MLRS) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.shoot_flags |= TFL_SHOOT_VOLLYALWAYS; - self.volly_counter = self.shot_volly; - - // Our fire routine - self.turret_firefunc = turret_mlrs_attack; - self.turret_postthink = turret_mlrs_postthink; - -} - -/*QUAKED turret_mlrs (0 .5 .8) ? -*/ - -void spawnfunc_turret_mlrs() -{ - precache_model ("models/turrets/mlrs.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_mlrs_dinit; - self.nextthink = time + 0.5; -} - - diff --git a/qcsrc/server/tturrets/units/unit_phaser.qc b/qcsrc/server/tturrets/units/unit_phaser.qc deleted file mode 100644 index c704aa1115..0000000000 --- a/qcsrc/server/tturrets/units/unit_phaser.qc +++ /dev/null @@ -1,142 +0,0 @@ -void spawnfunc_turret_phaser(); -void turret_phaser_dinit(); -void turret_phaser_attack(); - -.float fireflag; - -float turret_phaser_firecheck() -{ - if (self.fireflag != 0) return 0; - return turret_stdproc_firecheck(); -} - -void turret_phaser_postthink() -{ - if (self.tur_head.frame == 0) - return; - - if (self.fireflag == 1) - { - if (self.tur_head.frame == 10) - self.tur_head.frame = 1; - else - self.tur_head.frame = self.tur_head.frame +1; - } - else if (self.fireflag == 2 ) - { - self.tur_head.frame = self.tur_head.frame +1; - if (self.tur_head.frame == 15) - { - self.tur_head.frame = 0; - self.fireflag = 0; - } - } -} - -void beam_think() -{ - if ((time > self.cnt) || (self.owner.deadflag != DEAD_NO)) - { - self.owner.attack_finished_single = time + self.owner.shot_refire; - self.owner.fireflag = 2; - self.owner.tur_head.frame = 10; - sound (self, 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 turret_phaser_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; -} - -void turret_phaser_dinit() -{ - if (self.netname == "") self.netname = "Phaser Cannon"; - - self.turrcaps_flags = TFL_TURRCAPS_SNIPER|TFL_TURRCAPS_HITSCAN|TFL_TURRCAPS_PLAYERKILL; - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.aim_flags = TFL_AIM_LEAD; - - if (turret_stdproc_init("phaser_std", "models/turrets/base.md3","models/turrets/phaser.md3", TID_PHASER) == 0) - { - remove(self); - return; - } - - self.turret_firecheckfunc = turret_phaser_firecheck; - self.turret_firefunc = turret_phaser_attack; - self.turret_postthink = turret_phaser_postthink; - -} - -/*QUAKED turret_phaser(0 .5 .8) ? -*/ -void spawnfunc_turret_phaser() -{ - precache_sound ("turrets/phaser.wav"); - precache_model ("models/turrets/phaser.md3"); - precache_model ("models/turrets/phaser_beam.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_phaser_dinit; - self.nextthink = time + 0.5; -} - diff --git a/qcsrc/server/tturrets/units/unit_plasma.qc b/qcsrc/server/tturrets/units/unit_plasma.qc deleted file mode 100644 index f4a60e545a..0000000000 --- a/qcsrc/server/tturrets/units/unit_plasma.qc +++ /dev/null @@ -1,173 +0,0 @@ -void spawnfunc_turret_plasma(); -void spawnfunc_turret_plasma_dual(); - -void turret_plasma_std_init(); -void turret_plasma_dual_init(); - -void turret_plasma_attack(); - - -void turret_plasma_postthink() -{ - if (self.tur_head.frame != 0) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 5) - self.tur_head.frame = 0; -} - -void turret_plasma_dual_postthink() -{ - if ((self.tur_head.frame != 0) && (self.tur_head.frame != 3)) - self.tur_head.frame = self.tur_head.frame + 1; - - if (self.tur_head.frame > 6) - self.tur_head.frame = 0; -} - -void turret_plasma_minsta_attack (void) -{ - float flying; - flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last - - FireRailgunBullet (self.tur_shotorg, self.tur_shotorg + self.tur_shotdir_updated * MAX_SHOT_DISTANCE, 10000000000, - 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA); - - - pointparticles(particleeffectnum("nex_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - - // teamcolor / hit beam effect - vector v; - v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); - if(teamplay) - { - switch(self.team) - { - case NUM_TEAM_1: // Red - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), self.tur_shotorg, v); - break; - case NUM_TEAM_2: // Blue - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), self.tur_shotorg, v); - break; - case NUM_TEAM_3: // Yellow - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), self.tur_shotorg, v); - break; - case NUM_TEAM_4: // Pink - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), self.tur_shotorg, v); - break; - } - } - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), self.tur_shotorg, v); - if (self.tur_head.frame == 0) - self.tur_head.frame = 1; -} - -void turret_plasma_attack() -{ - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); - missile.missile_flags = MIF_SPLASH; - - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - if (self.tur_head.frame == 0) - self.tur_head.frame = 1; -} - -void turret_plasma_dual_attack() -{ - entity missile = turret_projectile("weapons/hagar_fire.wav", 1, 0, DEATH_TURRET_PLASMA, PROJECTILE_ELECTRO_BEAM, TRUE, TRUE); - missile.missile_flags = MIF_SPLASH; - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); - self.tur_head.frame += 1; -} - -void turret_plasma_std_init() -{ - if (self.netname == "") self.netname = "Plasma Cannon"; - - // What ammo to use - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - - // How to aim - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUNDGROUND; - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - - if (turret_stdproc_init("plasma_std", "models/turrets/base.md3", "models/turrets/plasma.md3", TID_PLASMA) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.firecheck_flags |= TFL_FIRECHECK_AFF; - - // Our fireing routine - if(g_minstagib) - self.turret_firefunc = turret_plasma_minsta_attack; - else - self.turret_firefunc = turret_plasma_attack; - - // Custom per turret frame stuff. usualy animation. - self.turret_postthink = turret_plasma_postthink; - turret_do_updates(self); -} - - -void turret_plasma_dual_init() -{ - if (self.netname == "") self.netname = "Dual Plasma Cannon"; - - // What ammo to use - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - - // How to aim at targets - self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_GROUNDGROUND ; - self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL; - - if (turret_stdproc_init("plasma_dual", "models/turrets/base.md3", "models/turrets/plasmad.md3", TID_PLASMA_DUAL) == 0) - { - remove(self); - return; - } - - self.damage_flags |= TFL_DMG_HEADSHAKE; - self.firecheck_flags |= TFL_FIRECHECK_AFF; - - // Our fireing routine - self.turret_firefunc = turret_plasma_dual_attack; - - // Custom per turret frame stuff. usualy animation. - self.turret_postthink = turret_plasma_dual_postthink; -} - - -/* -* Basic moderate (std) or fast (dual) fireing, short-mid range energy cannon. -* Not too mutch of a therat on its own, but can be rather dangerous in groups. -* Regenerates ammo slowly, support with a fusionreactor(s) to do some real damage. -*/ - -/*QUAKED turret_plasma (0 .5 .8) ? -*/ -void spawnfunc_turret_plasma() -{ - g_turrets_common_precash(); - precache_model ("models/turrets/plasma.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_plasma_std_init; - self.nextthink = time + 0.5; -} - -/*QUAKED turret_plasma_dual (0 .5 .8) ? -*/ -void spawnfunc_turret_plasma_dual() -{ - - precache_model ("models/turrets/plasmad.md3"); - precache_model ("models/turrets/base.md3"); - - self.think = turret_plasma_dual_init; - self.nextthink = time + 0.5; -} - diff --git a/qcsrc/server/tturrets/units/unit_targettrigger.qc b/qcsrc/server/tturrets/units/unit_targettrigger.qc deleted file mode 100644 index 0f2de3c869..0000000000 --- a/qcsrc/server/tturrets/units/unit_targettrigger.qc +++ /dev/null @@ -1,42 +0,0 @@ -void spawnfunc_turret_targettrigger(); -void turret_targettrigger_touch(); - -void turret_targettrigger_touch() -{ - entity e; - if (self.cnt > time) return; - entity oldself; - oldself = self; - - e = find(world, targetname, self.target); - while (e) - { - if (e.turrcaps_flags & TFL_TURRCAPS_RECIVETARGETS) - { - self = e; - if(e.turret_addtarget) - e.turret_addtarget(other,oldself); - } - - e = find(e, targetname, oldself.target); - } - - oldself.cnt = time + 0.5; - - self = oldself; -} - -/*QUAKED turret_targettrigger (.5 .5 .5) ? -*/ -void spawnfunc_turret_targettrigger() -{ - if (!autocvar_g_turrets) - { - remove(self); - return; - } - - InitTrigger (); - - self.touch = turret_targettrigger_touch; -} diff --git a/qcsrc/server/tturrets/units/unit_tessla.qc b/qcsrc/server/tturrets/units/unit_tessla.qc deleted file mode 100644 index 5969938912..0000000000 --- a/qcsrc/server/tturrets/units/unit_tessla.qc +++ /dev/null @@ -1,191 +0,0 @@ -void spawnfunc_turret_tesla(); -void turret_tesla_dinit(); -void turret_tesla_fire(); - -entity toast(entity from, float range, float damage) -{ - entity e; - entity etarget = world; - float d,dd; - float r; - - dd = range + 1; - - e = findradius(from.origin,range); - while (e) - { - if ((e.railgunhit != 1) && (e != from)) - { - r = turret_validate_target(self,e,self.target_validate_flags); - if (r > 0) - { - traceline(from.origin,0.5 * (e.absmin + e.absmax),MOVE_WORLDONLY,from); - if (trace_fraction == 1.0) - { - d = vlen(e.origin - from.origin); - if (d < dd) - { - dd = d; - etarget = e; - } - } - } - } - e = e.chain; - } - - if (etarget) - { - te_csqc_lightningarc(from.origin,etarget.origin); - Damage(etarget, self, self, damage, DEATH_TURRET_TESLA, etarget.origin, '0 0 0'); - etarget.railgunhit = 1; - } - - return etarget; -} - -float turret_tesla_firecheck() -{ - // g_turrets_targetscan_maxdelay forces a target re-scan at least this often - float do_target_scan = 0; - - if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time) - do_target_scan = 1; - - // Old target (if any) invalid? - if(self.target_validate_time < time) - if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0) - { - self.enemy = world; - self.target_validate_time = time + 0.5; - do_target_scan = 1; - } - - // But never more often then g_turrets_targetscan_mindelay! - if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time) - do_target_scan = 0; - - if(do_target_scan) - { - self.enemy = turret_select_target(); - self.target_select_time = time; - } - - if not (turret_stdproc_firecheck()) - return 0; - - if(self.enemy) - return 1; - - return 0; -} - - -void turret_tesla_fire() -{ - 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_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - t = toast(e,r,d); - remove(e); - - if (t == world) return; - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_TEAMCHECK; - - self.attack_finished_single = time + self.shot_refire; - for (i = 0; i < 10; ++i) - { - d *= 0.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; - } -} - -void turret_tesla_postthink() -{ - if not (self.active) - { - self.tur_head.avelocity = '0 0 0'; - return; - } - - 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; - - float f; - f = (self.ammo / self.ammo_max); - f = f * f; - if(f > random()) - if(random() < 0.1) - te_csqc_lightningarc(self.tur_shotorg,self.tur_shotorg + (randomvec() * 350)); - } -} - - -void turret_tesla_dinit() -{ - if (self.netname == "") self.netname = "Tesla Coil"; - - self.turrcaps_flags = TFL_TURRCAPS_HITSCAN | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL; - - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.firecheck_flags = TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_OWM_AMMO; - self.shoot_flags = TFL_SHOOT_CUSTOM; - self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.aim_flags = TFL_AIM_NO; - self.track_flags = TFL_TRACK_NO; - - if (turret_stdproc_init("tesla_std", "models/turrets/tesla_base.md3", "models/turrets/tesla_head.md3", TID_TESLA) == 0) - { - remove(self); - return; - } - setsize(self,'-60 -60 0','60 60 128'); - - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | - TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK; - - self.turret_firefunc = turret_tesla_fire; - self.turret_postthink = turret_tesla_postthink; - self.turret_firecheckfunc = turret_tesla_firecheck; -} - -/*QUAKED turret_tesla (0 .5 .8) ? -*/ -void spawnfunc_turret_tesla() -{ - precache_model ("models/turrets/tesla_head.md3"); - precache_model ("models/turrets/tesla_base.md3"); - - - self.think = turret_tesla_dinit; - self.nextthink = time + 0.5; -} - diff --git a/qcsrc/server/tturrets/units/unit_walker.qc b/qcsrc/server/tturrets/units/unit_walker.qc deleted file mode 100644 index 5e0feea35b..0000000000 --- a/qcsrc/server/tturrets/units/unit_walker.qc +++ /dev/null @@ -1,646 +0,0 @@ -#define ANIM_NO 0 -#define ANIM_TURN 1 -#define ANIM_WALK 2 -#define ANIM_RUN 3 -#define ANIM_STRAFE_L 4 -#define ANIM_STRAFE_R 5 -#define ANIM_JUMP 6 -#define ANIM_LAND 7 -#define ANIM_PAIN 8 -#define ANIM_MEELE 9 -#define ANIM_SWIM 10 -#define ANIM_ROAM 11 -.float animflag; - -#define WALKER_MIN '-70 -70 0' -#define WALKER_MAX '70 70 95' - -#define WALKER_PATH(s,e) pathlib_astar(s,e) - -float walker_firecheck() -{ - if (self.animflag == ANIM_MEELE) - return 0; - - return turret_stdproc_firecheck(); -} - -void walker_meele_do_dmg() -{ - vector where; - entity e; - - makevectors(self.angles); - where = self.origin + v_forward * 128; - - e = findradius(where,32); - while (e) - { - if (turret_validate_target(self, e, self.target_validate_flags)) - if (e != self && e.owner != self) - Damage(e, self, self, autocvar_g_turrets_unit_walker_std_meele_dmg, DEATH_TURRET_WALK_MEELE, '0 0 0', v_forward * autocvar_g_turrets_unit_walker_std_meele_force); - - e = e.chain; - } -} - -void walker_setnoanim() -{ - turrets_setframe(ANIM_NO, FALSE); - self.animflag = self.frame; -} -void walker_rocket_explode() -{ - RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, world); - remove (self); -} - -void walker_rocket_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) -{ - self.health = self.health - damage; - self.velocity = self.velocity + vforce; - - if (self.health <= 0) - W_PrepareExplosionByDamage(self.owner, walker_rocket_explode); -} - -#define WALKER_ROCKET_MOVE movelib_move_simple(newdir, autocvar_g_turrets_unit_walker_std_rocket_speed, autocvar_g_turrets_unit_walker_std_rocket_turnrate); UpdateCSQCProjectile(self) -void walker_rocket_loop(); -void walker_rocket_think() -{ - vector newdir; - float edist; - float itime; - float m_speed; - - self.nextthink = time; - - edist = vlen(self.enemy.origin - self.origin); - - // Simulate crude guidance - if (self.cnt < time) - { - if (edist < 1000) - self.tur_shotorg = randomvec() * min(edist, 64); - else - self.tur_shotorg = randomvec() * min(edist, 256); - - self.cnt = time + 0.5; - } - - if (edist < 128) - self.tur_shotorg = '0 0 0'; - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - self.nextthink = time; - return; - } - - if (self.shot_dmg != 1337 && random() < 0.01) - { - walker_rocket_loop(); - return; - } - - m_speed = vlen(self.velocity); - - // Enemy dead? just keep on the current heading then. - if (self.enemy == world || self.enemy.deadflag != DEAD_NO) - self.enemy = world; - - if (self.enemy) - { - itime = max(edist / m_speed, 1); - newdir = steerlib_pull(self.enemy.origin + self.tur_shotorg); - } - else - newdir = normalize(self.velocity); - - WALKER_ROCKET_MOVE; -} - -void walker_rocket_loop3() -{ - vector newdir; - self.nextthink = time; - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - return; - } - - if (vlen(self.origin - self.tur_shotorg) < 100 ) - { - self.think = walker_rocket_think; - return; - } - - newdir = steerlib_pull(self.tur_shotorg); - WALKER_ROCKET_MOVE; - - self.angles = vectoangles(self.velocity); -} - -void walker_rocket_loop2() -{ - vector newdir; - - self.nextthink = time; - - if (self.tur_health < time) - { - self.think = walker_rocket_explode; - return; - } - - if (vlen(self.origin - self.tur_shotorg) < 100 ) - { - self.tur_shotorg = self.origin - '0 0 200'; - self.think = walker_rocket_loop3; - return; - } - - newdir = steerlib_pull(self.tur_shotorg); - WALKER_ROCKET_MOVE; -} - -void walker_rocket_loop() -{ - self.nextthink = time; - self.tur_shotorg = self.origin + '0 0 300'; - self.think = walker_rocket_loop2; - self.shot_dmg = 1337; -} - -void walker_fire_rocket(vector org) -{ - entity rocket; - - fixedmakevectors(self.angles); - - te_explosion (org); - - rocket = spawn (); - setorigin(rocket, org); - - sound (self, CH_WEAPON_A, "weapons/hagar_fire.wav", VOL_BASE, ATTEN_NORM); - setsize (rocket, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - - rocket.classname = "walker_rocket"; - rocket.owner = self; - rocket.bot_dodge = TRUE; - rocket.bot_dodgerating = 50; - rocket.takedamage = DAMAGE_YES; - rocket.damageforcescale = 2; - rocket.health = 25; - rocket.tur_shotorg = randomvec() * 512; - rocket.cnt = time + 1; - rocket.enemy = self.enemy; - - if (random() < 0.01) - rocket.think = walker_rocket_loop; - else - rocket.think = walker_rocket_think; - - rocket.event_damage = walker_rocket_damage; - - rocket.nextthink = time; - rocket.movetype = MOVETYPE_FLY; - rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * autocvar_g_turrets_unit_walker_std_rocket_speed; - rocket.angles = vectoangles(rocket.velocity); - rocket.touch = walker_rocket_explode; - rocket.flags = FL_PROJECTILE; - rocket.solid = SOLID_BBOX; - rocket.tur_health = time + 9; - rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; - - CSQCProjectile(rocket, FALSE, PROJECTILE_ROCKET, FALSE); // no culling, has fly sound -} - -.vector enemy_last_loc; -.float enemy_last_time; -void walker_move_to(vector _target, float _dist) -{ - switch (self.waterlevel) - { - case WATERLEVEL_NONE: - if (_dist > 500) - self.animflag = ANIM_RUN; - else - self.animflag = ANIM_WALK; - case WATERLEVEL_WETFEET: - case WATERLEVEL_SWIMMING: - if (self.animflag != ANIM_SWIM) - self.animflag = ANIM_WALK; - else - self.animflag = ANIM_SWIM; - break; - case WATERLEVEL_SUBMERGED: - self.animflag = ANIM_SWIM; - } - - self.moveto = _target; - self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); - - if(self.enemy) - { - self.enemy_last_loc = _target; - self.enemy_last_time = time; - } -} - -//#define WALKER_FANCYPATHING - -void walker_move_path() -{ -#ifdef WALKER_FANCYPATHING - // Are we close enougth to a path node to switch to the next? - if (vlen(self.origin - self.pathcurrent.origin) < 64) - if (self.pathcurrent.path_next == world) - { - // Path endpoint reached - pathlib_deletepath(self.pathcurrent.owner); - self.pathcurrent = world; - - if (self.pathgoal) - { - if (self.pathgoal.use) - self.pathgoal.use(); - - if (self.pathgoal.enemy) - { - self.pathcurrent = WALKER_PATH(self.pathgoal.origin,self.pathgoal.enemy.origin); - self.pathgoal = self.pathgoal.enemy; - } - } - else - self.pathgoal = world; - } - else - self.pathcurrent = self.pathcurrent.path_next; - - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto,0.5,500,0.95); - walker_move_to(self.moveto, 0); - -#else - if (vlen(self.origin - self.pathcurrent.origin) < 64) - self.pathcurrent = self.pathcurrent.enemy; - - if(!self.pathcurrent) - return; - - self.moveto = self.pathcurrent.origin; - self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); - walker_move_to(self.moveto, 0); -#endif -} - -.float idletime; -void walker_postthink() -{ - fixedmakevectors(self.angles); - - if (self.spawnflags & TSF_NO_PATHBREAK && self.pathcurrent) - walker_move_path(); - else if (self.enemy == world) - { - if(self.pathcurrent) - walker_move_path(); - else - { - if(self.enemy_last_time != 0) - { - if(vlen(self.origin - self.enemy_last_loc) < 128 || time - self.enemy_last_time > 10) - self.enemy_last_time = 0; - else - walker_move_to(self.enemy_last_loc, 0); - } - else - { - if(self.animflag != ANIM_NO) - { - traceline(self.origin + '0 0 64', self.origin + '0 0 64' + v_forward * 128, MOVE_NORMAL, self); - - if(trace_fraction != 1.0) - self.tur_head.idletime = -1337; - else - { - traceline(trace_endpos, trace_endpos - '0 0 256', MOVE_NORMAL, self); - if(trace_fraction == 1.0) - self.tur_head.idletime = -1337; - } - - if(self.tur_head.idletime == -1337) - { - self.moveto = self.origin + randomvec() * 256; - self.tur_head.idletime = 0; - } - - self.moveto = self.moveto * 0.9 + ((self.origin + v_forward * 500) + randomvec() * 400) * 0.1; - self.moveto_z = self.origin_z + 64; - walker_move_to(self.moveto, 0); - } - - if(self.idletime < time) - { - if(random() < 0.5 || !(self.spawnflags & TSL_ROAM)) - { - self.idletime = time + 1 + random() * 5; - self.moveto = self.origin; - self.animflag = ANIM_NO; - } - else - { - self.animflag = ANIM_WALK; - self.idletime = time + 4 + random() * 2; - self.moveto = self.origin + randomvec() * 256; - self.tur_head.moveto = self.moveto; - self.tur_head.idletime = 0; - } - } - } - } - } - else - { - if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_meele_range && self.animflag != ANIM_MEELE) - { - vector wish_angle; - - wish_angle = angleofs(self, self.enemy); - if (self.animflag != ANIM_SWIM) - if (fabs(wish_angle_y) < 15) - { - self.moveto = self.enemy.origin; - self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95); - self.animflag = ANIM_MEELE; - } - } - else if (self.tur_head.attack_finished_single < time) - { - if(self.tur_head.shot_volly) - { - self.animflag = ANIM_NO; - - self.tur_head.shot_volly = self.tur_head.shot_volly -1; - if(self.tur_head.shot_volly == 0) - self.tur_head.attack_finished_single = time + autocvar_g_turrets_unit_walker_std_rocket_refire; - else - self.tur_head.attack_finished_single = time + 0.2; - - if(self.tur_head.shot_volly > 1) - walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket01"))); - else - walker_fire_rocket(gettaginfo(self, gettagindex(self, "tag_rocket02"))); - } - else - { - if (self.tur_dist_enemy > autocvar_g_turrets_unit_walker_std_rockets_range_min) - if (self.tur_dist_enemy < autocvar_g_turrets_unit_walker_std_rockets_range) - self.tur_head.shot_volly = 4; - } - } - else - { - if (self.animflag != ANIM_MEELE) - walker_move_to(self.enemy.origin, self.tur_dist_enemy); - } - } - - //if(self.animflag != ANIM_NO) - { - vector real_angle; - float turny = 0, turnx = 0; - float vz; - - real_angle = vectoangles(self.steerto) - self.angles; - vz = self.velocity_z; - - switch (self.animflag) - { - case ANIM_NO: - movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); - break; - - case ANIM_TURN: - turny = autocvar_g_turrets_unit_walker_turn; - movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); - break; - - case ANIM_WALK: - turny = autocvar_g_turrets_unit_walker_turn_walk; - movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_walk, 0.6); - break; - - case ANIM_RUN: - turny = autocvar_g_turrets_unit_walker_turn_run; - movelib_move_simple(v_forward, autocvar_g_turrets_unit_walker_speed_run, 0.6); - break; - - case ANIM_STRAFE_L: - turny = autocvar_g_turrets_unit_walker_turn_strafe; - movelib_move_simple(v_right * -1, autocvar_g_turrets_unit_walker_speed_walk, 0.8); - break; - - case ANIM_STRAFE_R: - turny = autocvar_g_turrets_unit_walker_turn_strafe; - movelib_move_simple(v_right, autocvar_g_turrets_unit_walker_speed_walk, 0.8); - break; - - case ANIM_JUMP: - self.velocity += '0 0 1' * autocvar_g_turrets_unit_walker_speed_jump; - break; - - case ANIM_LAND: - break; - - case ANIM_PAIN: - if(self.frame != ANIM_PAIN) - defer(0.25, walker_setnoanim); - - break; - - case ANIM_MEELE: - if(self.frame != ANIM_MEELE) - { - defer(0.41, walker_setnoanim); - defer(0.21, walker_meele_do_dmg); - } - - movelib_beak_simple(autocvar_g_turrets_unit_walker_speed_stop); - break; - - case ANIM_SWIM: - turny = autocvar_g_turrets_unit_walker_turn_swim; - turnx = autocvar_g_turrets_unit_walker_turn_swim; - - self.angles_x += bound(-10, shortangle_f(real_angle_x, self.angles_x), 10); - 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); -} - -void walker_attack() -{ - sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM); - fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0, 1, autocvar_g_balance_uzi_bulletconstant); - endFireBallisticBullet(); - pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); -} - - -void walker_respawnhook() -{ - entity e; - - // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. - if(self.movetype != MOVETYPE_WALK) - return; - - setorigin(self, self.pos1); - self.angles = self.pos2; - - if (self.target != "") - { - e = find(world, targetname, self.target); - if (!e) - { - dprint("Warning! initital waypoint for Walker does NOT exsist!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { - #ifdef WALKER_FANCYPATHING - self.pathcurrent = WALKER_PATH(self.origin, e.origin); - self.pathgoal = e; -#else - self.pathcurrent = e; -#endif - } - } -} - -void walker_diehook() -{ -#ifdef WALKER_FANCYPATHING - if (self.pathcurrent) - pathlib_deletepath(self.pathcurrent.owner); -#endif - self.pathcurrent = world; -} - -void turret_walker_dinit() -{ - entity e; - - if (self.netname == "") self.netname = "Walker Turret"; - - self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE; - self.turrcaps_flags = TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MOVE ; - self.aim_flags = TFL_AIM_LEAD; - - if (autocvar_g_antilag_bullets) - self.turrcaps_flags |= TFL_TURRCAPS_HITSCAN; - else - self.aim_flags |= TFL_AIM_SHOTTIMECOMPENSATE; - - - self.turret_respawnhook = walker_respawnhook; - self.turret_diehook = walker_diehook; - - self.ticrate = 0.05; - if (turret_stdproc_init("walker_std", "models/turrets/walker_body.md3", "models/turrets/walker_head_minigun.md3", TID_WALKER) == 0) - { - remove(self); - return; - } - setsize(self, WALKER_MIN, WALKER_MAX); - self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS; - self.iscreature = TRUE; - self.teleportable = TELEPORT_NORMAL; - self.damagedbycontents = TRUE; - self.movetype = MOVETYPE_WALK; - self.solid = SOLID_SLIDEBOX; - self.takedamage = DAMAGE_AIM; - setorigin(self, self.origin); - tracebox(self.origin + '0 0 128', self.mins, self.maxs, self.origin - '0 0 10000', MOVE_NORMAL, self); - setorigin(self, trace_endpos + '0 0 4'); - self.pos1 = self.origin; - self.pos2 = self.angles; - self.idle_aim = '0 0 0'; - self.turret_firecheckfunc = walker_firecheck; - self.turret_firefunc = walker_attack; - self.turret_postthink = walker_postthink; - - if (self.target != "") - { - e = find(world, targetname, self.target); - if (!e) - { - dprint("Initital waypoint for walker does NOT exsist, fix your map!\n"); - self.target = ""; - } - - if (e.classname != "turret_checkpoint") - dprint("Warning: not a turrret path\n"); - else - { -#ifdef WALKER_FANCYPATHING - self.pathcurrent = WALKER_PATH(self.origin, e.origin); - self.pathgoal = e; -#else - self.pathcurrent = e; -#endif - } - } -} - - -void spawnfunc_turret_walker() -{ - g_turrets_common_precash(); - - precache_model ("models/turrets/walker_head_minigun.md3"); - precache_model ("models/turrets/walker_body.md3"); - precache_model ( "models/turrets/rocket.md3"); - precache_sound ( "weapons/rocket_impact.wav" ); - - self.think = turret_walker_dinit; - self.nextthink = time + 0.5; -} diff --git a/qcsrc/server/vehicles/bumblebee.qc b/qcsrc/server/vehicles/bumblebee.qc index dff26d6ea3..29116c7c49 100644 --- a/qcsrc/server/vehicles/bumblebee.qc +++ b/qcsrc/server/vehicles/bumblebee.qc @@ -569,7 +569,7 @@ float bumb_pilot_frame() trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); } - else if(trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET) + else if(trace_ent.turret_flags & TUR_FLAG_ISTURRET) { if(trace_ent.health <= trace_ent.tur_health && autocvar_g_vehicle_bumblebee_healgun_hps) trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_health); diff --git a/qcsrc/server/vehicles/vehicles.qc b/qcsrc/server/vehicles/vehicles.qc index 75fa9407e6..58f1d81327 100644 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@ -276,7 +276,7 @@ void vehicles_locktarget(float incr, float decr, float _lock_time) trace_ent = world; if not (trace_ent.vehicle_flags & VHF_ISVEHICLE || - trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET || + trace_ent.turret_flags & TUR_FLAG_ISTURRET || trace_ent.takedamage == DAMAGE_TARGETDRONE) trace_ent = world; } diff --git a/qcsrc/server/vehicles/vehicles_def.qh b/qcsrc/server/vehicles/vehicles_def.qh index 393563f18b..e3bdd49d89 100644 --- a/qcsrc/server/vehicles/vehicles_def.qh +++ b/qcsrc/server/vehicles/vehicles_def.qh @@ -47,6 +47,10 @@ const float VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a mul .float vehicle_respawntime; //.void() vehicle_spawn; +.float volly_counter; + +.float tur_health; + void vehicles_exit(float eject); .void(float exit_flags) vehicle_exit; const float VHEF_NORMAL = 0; /// User pressed exit key diff --git a/turrets.cfg b/turrets.cfg index 876862b1c5..11c5fa4387 100644 --- a/turrets.cfg +++ b/turrets.cfg @@ -2,57 +2,487 @@ set g_turrets 1 set g_turrets_reloadcvars 0 set g_turrets_nofire 0 -// Target scanning and validation can be resource intensive -// Dont let turrets look for new targets more frequently then this -set g_turrets_targetscan_mindelay 0.1 +set g_turrets_targetscan_mindelay 0.1 "delay target rescanning to lower resource usage" +set g_turrets_targetscan_maxdelay 1 "scan at least this often" +set g_turrets_aimidle_delay 5 "become idle if target is lost for this long" -// Do a targetscan at least this often regarless. -set g_turrets_targetscan_maxdelay 1 +alias g_turrets_reload "set g_turrets_reloadcvars 1" -// Turrets with no target returns to their idle aim after this much time. -set g_turrets_aimidle_delay 5 +// {{{ #1: eWheel Turret +set g_turrets_unit_ewheel_health 200 +set g_turrets_unit_ewheel_respawntime 30 -// --- Units --- +set g_turrets_unit_ewheel_turnrate 200 -// Machinegun on a stick. -exec unit_machinegun.cfg +set g_turrets_unit_ewheel_speed_fast 500 +set g_turrets_unit_ewheel_speed_slow 150 +set g_turrets_unit_ewheel_speed_slower 50 +set g_turrets_unit_ewheel_speed_stop 25 -// Hunter killer rocket turret. "smart rockets" -exec unit_hk.cfg +set g_turrets_unit_ewheel_shot_dmg 30 +set g_turrets_unit_ewheel_shot_refire 0.1 +set g_turrets_unit_ewheel_shot_spread 0.025 +set g_turrets_unit_ewheel_shot_force 125 +set g_turrets_unit_ewheel_shot_radius 50 +set g_turrets_unit_ewheel_shot_speed 9000 -// Fires a pair of accelerating, simple homing rockets. -exec unit_hellion.cfg +set g_turrets_unit_ewheel_shot_volly 2 +set g_turrets_unit_ewheel_shot_volly_refire 1 -// Fire lots of dumbfire rockets -exec unit_mlrs.cfg +set g_turrets_unit_ewheel_target_range 5000 +set g_turrets_unit_ewheel_target_range_optimal 900 +set g_turrets_unit_ewheel_target_range_min 0.1 -// Kills killable enemy missiles. -exec unit_flac.cfg +set g_turrets_unit_ewheel_target_select_rangebias 0.25 +set g_turrets_unit_ewheel_target_select_samebias 2 +set g_turrets_unit_ewheel_target_select_anglebias 0.5 +set g_turrets_unit_ewheel_target_select_playerbias 1 +set g_turrets_unit_ewheel_target_select_missilebias 0 -// Support unit. Recharges friendly energy based turrets in range -exec unit_fusreac.cfg +set g_turrets_unit_ewheel_ammo_max 4000 +set g_turrets_unit_ewheel_ammo 500 +set g_turrets_unit_ewheel_ammo_recharge 50 -// "Electro" turret. -exec unit_plasma.cfg +set g_turrets_unit_ewheel_aim_firetolerance_dist 150 +set g_turrets_unit_ewheel_aim_speed 90 +set g_turrets_unit_ewheel_aim_maxrot 20 +set g_turrets_unit_ewheel_aim_maxpitch 45 -// The the all new "Electro" turret, same ting with two barrels. -exec unit_plasma2.cfg +set g_turrets_unit_ewheel_track_type 1 +set g_turrets_unit_ewheel_track_accel_pitch 0 +set g_turrets_unit_ewheel_track_accel_rot 0 +// }}} +// {{{ #2: FLAC Cannon +set g_turrets_unit_flac_health 700 +set g_turrets_unit_flac_respawntime 90 -// AAAaaaarg! Bzzaat! yber turret. chain lightning missile and player killing. -exec unit_tesla.cfg +set g_turrets_unit_flac_shot_dmg 20 +set g_turrets_unit_flac_shot_refire 0.1 +set g_turrets_unit_flac_shot_radius 100 +set g_turrets_unit_flac_shot_speed 9000 +set g_turrets_unit_flac_shot_spread 0.02 +set g_turrets_unit_flac_shot_force 25 +set g_turrets_unit_flac_shot_volly 0 +set g_turrets_unit_flac_shot_volly_refire 0 -// Fires a constant beam that slows down and slowly damages its target. -exec unit_phaser.cfg +set g_turrets_unit_flac_target_range 4000 +set g_turrets_unit_flac_target_range_min 500 +set g_turrets_unit_flac_target_range_optimal 1250 -// The bastred son of a turret and a quake monster. -// A walking minigun with longrage missiles and closerange meele attack. -exec unit_walker.cfg +set g_turrets_unit_flac_target_select_rangebias 0.25 +set g_turrets_unit_flac_target_select_samebias 1 +set g_turrets_unit_flac_target_select_anglebias 0.5 +set g_turrets_unit_flac_target_select_playerbias 0 +set g_turrets_unit_flac_target_select_missilebias 1 -// OMG! Its the Evil Wheel! :O -exec unit_ewheel.cfg +set g_turrets_unit_flac_ammo_max 1000 +set g_turrets_unit_flac_ammo 400 +set g_turrets_unit_flac_ammo_recharge 100 -// It is so repulsive, it doesn't even exist! -// exec unit_repulsor.cfg +set g_turrets_unit_flac_aim_firetolerance_dist 150 +set g_turrets_unit_flac_aim_speed 200 +set g_turrets_unit_flac_aim_maxrot 360 +set g_turrets_unit_flac_aim_maxpitch 35 -set g_turrets_reloadcvars 0 // reload when this cfg has been exec'd -alias g_turrets_reload "set g_turrets_reloadcvars 1" +set g_turrets_unit_flac_track_type 3 +set g_turrets_unit_flac_track_accel_pitch 0.5 +set g_turrets_unit_flac_track_accel_rot 0.7 +set g_turrets_unit_flac_track_blendrate 0.2 +// }}} +// {{{ #3: Fusion Reactor +set g_turrets_unit_fusreac_health 700 +set g_turrets_unit_fusreac_respawntime 90 + +set g_turrets_unit_fusreac_shot_speed 1 +set g_turrets_unit_fusreac_shot_dmg 20 +set g_turrets_unit_fusreac_shot_refire 0.2 + +set g_turrets_unit_fusreac_target_range 1024 +set g_turrets_unit_fusreac_target_range_min 1 + +set g_turrets_unit_fusreac_ammo_max 100 +set g_turrets_unit_fusreac_ammo 0 +set g_turrets_unit_fusreac_ammo_recharge 100 + +set g_turrets_unit_fusreac_shot_radius 0 +set g_turrets_unit_fusreac_shot_spread 0 +set g_turrets_unit_fusreac_shot_force 0 +set g_turrets_unit_fusreac_shot_volly 0 +set g_turrets_unit_fusreac_shot_volly_refire 0 +set g_turrets_unit_fusreac_target_range_optimal 0 +set g_turrets_unit_fusreac_target_select_rangebias 0 +set g_turrets_unit_fusreac_target_select_samebias 0 +set g_turrets_unit_fusreac_target_select_anglebias 0 +set g_turrets_unit_fusreac_target_select_playerbias 0 +set g_turrets_unit_fusreac_aim_firetolerance_dist 0 +set g_turrets_unit_fusreac_aim_speed 0 +set g_turrets_unit_fusreac_aim_maxrot 0 +set g_turrets_unit_fusreac_aim_maxpitch 0 +set g_turrets_unit_fusreac_track_type 0 +set g_turrets_unit_fusreac_track_accel_pitch 0 +set g_turrets_unit_fusreac_track_accel_rot 0 +set g_turrets_unit_fusreac_track_blendrate 0 +// }}} +// {{{ #4: Hellion Missile Turret +set g_turrets_unit_hellion_health 500 +set g_turrets_unit_hellion_respawntime 90 + +set g_turrets_unit_hellion_shot_dmg 50 +set g_turrets_unit_hellion_shot_refire 0.2 +set g_turrets_unit_hellion_shot_radius 80 + +set g_turrets_unit_hellion_shot_speed 650 +set g_turrets_unit_hellion_shot_speed_max 4000 +set g_turrets_unit_hellion_shot_speed_gain 1.01 + +set g_turrets_unit_hellion_shot_spread 0.08 +set g_turrets_unit_hellion_shot_force 250 +set g_turrets_unit_hellion_shot_volly 2 +set g_turrets_unit_hellion_shot_volly_refire 4 + +set g_turrets_unit_hellion_target_range 6000 +set g_turrets_unit_hellion_target_range_min 150 +set g_turrets_unit_hellion_target_range_optimal 4500 + +set g_turrets_unit_hellion_target_select_rangebias 0.7 +set g_turrets_unit_hellion_target_select_samebias 0.01 +set g_turrets_unit_hellion_target_select_anglebias 0.01 +set g_turrets_unit_hellion_target_select_playerbias 1 +set g_turrets_unit_hellion_target_select_missilebias 0 + +set g_turrets_unit_hellion_ammo_max 200 +set g_turrets_unit_hellion_ammo 100 +set g_turrets_unit_hellion_ammo_recharge 50 + +set g_turrets_unit_hellion_aim_firetolerance_dist 200 +set g_turrets_unit_hellion_aim_speed 100 +set g_turrets_unit_hellion_aim_maxrot 360 +set g_turrets_unit_hellion_aim_maxpitch 20 + +set g_turrets_unit_hellion_track_type 3 +set g_turrets_unit_hellion_track_accel_pitch 0.25 +set g_turrets_unit_hellion_track_accel_rot 0.6 +set g_turrets_unit_hellion_track_blendrate 0.25 +// }}} +// {{{ #5: Hunter-Killer Turret +set g_turrets_unit_hk_health 500 +set g_turrets_unit_hk_respawntime 90 + +set g_turrets_unit_hk_shot_dmg 120 +set g_turrets_unit_hk_shot_refire 5 +set g_turrets_unit_hk_shot_radius 200 + +set g_turrets_unit_hk_shot_speed 500 +set g_turrets_unit_hk_shot_speed_max 1000 +set g_turrets_unit_hk_shot_speed_accel 1.025 +set g_turrets_unit_hk_shot_speed_accel2 1.05 +set g_turrets_unit_hk_shot_speed_decel 0.9 +set g_turrets_unit_hk_shot_speed_turnrate 0.25 + +set g_turrets_unit_hk_shot_spread 0 +set g_turrets_unit_hk_shot_force 600 +set g_turrets_unit_hk_shot_volly 0 +set g_turrets_unit_hk_shot_volly_refire 0 + +set g_turrets_unit_hk_target_range 6000 +set g_turrets_unit_hk_target_range_min 220 +set g_turrets_unit_hk_target_range_optimal 5000 + +set g_turrets_unit_hk_target_select_rangebias 0.5 +set g_turrets_unit_hk_target_select_samebias 0.01 +set g_turrets_unit_hk_target_select_anglebias 0.1 +set g_turrets_unit_hk_target_select_playerbias 1 +set g_turrets_unit_hk_target_select_missilebias 0 + +set g_turrets_unit_hk_ammo_max 240 +set g_turrets_unit_hk_ammo 120 +set g_turrets_unit_hk_ammo_recharge 16 + +set g_turrets_unit_hk_aim_firetolerance_dist 500 +set g_turrets_unit_hk_aim_speed 100 +set g_turrets_unit_hk_aim_maxrot 360 +set g_turrets_unit_hk_aim_maxpitch 20 + +set g_turrets_unit_hk_track_type 3 +set g_turrets_unit_hk_track_accel_pitch 0.25 +set g_turrets_unit_hk_track_accel_rot 0.6 +set g_turrets_unit_hk_track_blendrate 0.2 +// }}} +// {{{ #6: Machinegun Turret +set g_turrets_unit_machinegun_health 256 +set g_turrets_unit_machinegun_respawntime 60 + +set g_turrets_unit_machinegun_shot_dmg 10 +set g_turrets_unit_machinegun_shot_refire 0.1 +set g_turrets_unit_machinegun_shot_spread 0.015 +set g_turrets_unit_machinegun_shot_force 20 +set g_turrets_unit_machinegun_shot_radius 0 +set g_turrets_unit_machinegun_shot_speed 34920 + +set g_turrets_unit_machinegun_shot_volly 5 +set g_turrets_unit_machinegun_shot_volly_refire 0.5 + +set g_turrets_unit_machinegun_target_range 4500 +set g_turrets_unit_machinegun_target_range_min 2 +set g_turrets_unit_machinegun_target_range_optimal 1000 + +set g_turrets_unit_machinegun_target_select_rangebias 0.25 +set g_turrets_unit_machinegun_target_select_samebias 0.25 +set g_turrets_unit_machinegun_target_select_anglebias 0.5 +set g_turrets_unit_machinegun_target_select_playerbias 1 +set g_turrets_unit_machinegun_target_select_missilebias 0 + +set g_turrets_unit_machinegun_ammo_max 1500 +set g_turrets_unit_machinegun_ammo 300 +set g_turrets_unit_machinegun_ammo_recharge 75 + +set g_turrets_unit_machinegun_aim_firetolerance_dist 25 +set g_turrets_unit_machinegun_aim_speed 120 +set g_turrets_unit_machinegun_aim_maxrot 360 +set g_turrets_unit_machinegun_aim_maxpitch 25 + +set g_turrets_unit_machinegun_track_type 3 +set g_turrets_unit_machinegun_track_accel_pitch 0.4 +set g_turrets_unit_machinegun_track_accel_rot 0.9 +set g_turrets_unit_machinegun_track_blendrate 0.2 +// }}} +// {{{ #7: MLRS Turret +set g_turrets_unit_mlrs_health 500 +set g_turrets_unit_mlrs_respawntime 60 + +set g_turrets_unit_mlrs_shot_dmg 50 +set g_turrets_unit_mlrs_shot_refire 0.1 +set g_turrets_unit_mlrs_shot_radius 125 +set g_turrets_unit_mlrs_shot_speed 2000 +set g_turrets_unit_mlrs_shot_spread 0.05 +set g_turrets_unit_mlrs_shot_force 25 + +set g_turrets_unit_mlrs_shot_volly 6 + +set g_turrets_unit_mlrs_shot_volly_refire 4 + +set g_turrets_unit_mlrs_target_range 3000 +set g_turrets_unit_mlrs_target_range_min 500 +set g_turrets_unit_mlrs_target_range_optimal 500 + +set g_turrets_unit_mlrs_target_select_rangebias 0.25 +set g_turrets_unit_mlrs_target_select_samebias 0.5 +set g_turrets_unit_mlrs_target_select_anglebias 0.5 +set g_turrets_unit_mlrs_target_select_playerbias 1 +set g_turrets_unit_mlrs_target_select_missilebias 0 + +set g_turrets_unit_mlrs_ammo_max 300 +set g_turrets_unit_mlrs_ammo 300 +set g_turrets_unit_mlrs_ammo_recharge 75 + +set g_turrets_unit_mlrs_aim_firetolerance_dist 120 +set g_turrets_unit_mlrs_aim_speed 100 +set g_turrets_unit_mlrs_aim_maxrot 360 +set g_turrets_unit_mlrs_aim_maxpitch 20 + +set g_turrets_unit_mlrs_track_type 3 +set g_turrets_unit_mlrs_track_accel_pitch 0.5 +set g_turrets_unit_mlrs_track_accel_rot 0.7 +set g_turrets_unit_mlrs_track_blendrate 0.2 +// }}} +// {{{ #8: Phaser Cannon +set g_turrets_unit_phaser_health 500 + +set g_turrets_unit_phaser_respawntime 90 + +set g_turrets_unit_phaser_shot_dmg 100 +set g_turrets_unit_phaser_shot_refire 4 +set g_turrets_unit_phaser_shot_radius 8 +set g_turrets_unit_phaser_shot_speed 4 +set g_turrets_unit_phaser_shot_spread 0 +set g_turrets_unit_phaser_shot_force 5 +set g_turrets_unit_phaser_shot_volly 0 +set g_turrets_unit_phaser_shot_volly_refire 5 + +set g_turrets_unit_phaser_target_range 3000 +set g_turrets_unit_phaser_target_range_min 0 +set g_turrets_unit_phaser_target_range_optimal 1500 + +set g_turrets_unit_phaser_target_select_rangebias 0.85 +set g_turrets_unit_phaser_target_select_samebias 0 +set g_turrets_unit_phaser_target_select_anglebias 0.25 +set g_turrets_unit_phaser_target_select_playerbias 1 +set g_turrets_unit_phaser_target_select_missilebias 0 + +set g_turrets_unit_phaser_ammo_max 2000 +set g_turrets_unit_phaser_ammo 1000 +set g_turrets_unit_phaser_ammo_recharge 25 + +set g_turrets_unit_phaser_aim_firetolerance_dist 100 + +set g_turrets_unit_phaser_aim_speed 300 +set g_turrets_unit_phaser_aim_maxrot 360 +set g_turrets_unit_phaser_aim_maxpitch 30 + +set g_turrets_unit_phaser_track_type 3 +set g_turrets_unit_phaser_track_accel_pitch 0.5 +set g_turrets_unit_phaser_track_accel_rot 0.65 +set g_turrets_unit_phaser_track_blendrate 0.2 +// }}} +// {{{ #9: Plasma Cannon +set g_turrets_unit_plasma_health 500 +set g_turrets_unit_plasma_respawntime 60 + +set g_turrets_unit_plasma_shot_dmg 80 +set g_turrets_unit_plasma_shot_refire 0.6 +set g_turrets_unit_plasma_shot_radius 150 +set g_turrets_unit_plasma_shot_speed 2000 +set g_turrets_unit_plasma_shot_spread 0.015 +set g_turrets_unit_plasma_shot_force 100 +set g_turrets_unit_plasma_shot_volly 0 +set g_turrets_unit_plasma_shot_volly_refire 0 + +set g_turrets_unit_plasma_target_range 3500 +set g_turrets_unit_plasma_target_range_min 200 +set g_turrets_unit_plasma_target_range_optimal 500 + +set g_turrets_unit_plasma_target_select_rangebias 0.5 +set g_turrets_unit_plasma_target_select_samebias 0.01 +set g_turrets_unit_plasma_target_select_anglebias 0.25 +set g_turrets_unit_plasma_target_select_playerbias 1 +set g_turrets_unit_plasma_target_select_missilebias 0 + +set g_turrets_unit_plasma_ammo_max 640 +set g_turrets_unit_plasma_ammo 320 +set g_turrets_unit_plasma_ammo_recharge 40 + +set g_turrets_unit_plasma_aim_firetolerance_dist 120 +set g_turrets_unit_plasma_aim_speed 200 +set g_turrets_unit_plasma_aim_maxrot 360 +set g_turrets_unit_plasma_aim_maxpitch 30 + +set g_turrets_unit_plasma_track_type 3 +set g_turrets_unit_plasma_track_accel_pitch 0.5 +set g_turrets_unit_plasma_track_accel_rot 0.7 +set g_turrets_unit_plasma_track_blendrate 0.2 +// }}} +// {{{ #10: Dual Plasma Cannon +set g_turrets_unit_plasma_dual_health 500 +set g_turrets_unit_plasma_dual_respawntime 60 + +set g_turrets_unit_plasma_dual_shot_dmg 80 +set g_turrets_unit_plasma_dual_shot_refire 0.35 +set g_turrets_unit_plasma_dual_shot_radius 150 +set g_turrets_unit_plasma_dual_shot_speed 2000 +set g_turrets_unit_plasma_dual_shot_spread 0.015 +set g_turrets_unit_plasma_dual_shot_force 100 + +set g_turrets_unit_plasma_dual_shot_volly 0 +set g_turrets_unit_plasma_dual_shot_volly_refire 0 + +set g_turrets_unit_plasma_dual_target_range 3000 +set g_turrets_unit_plasma_dual_target_range_min 80 +set g_turrets_unit_plasma_dual_target_range_optimal 1000 + +set g_turrets_unit_plasma_dual_target_select_rangebias 0.2 +set g_turrets_unit_plasma_dual_target_select_samebias 0.4 +set g_turrets_unit_plasma_dual_target_select_anglebias 0.4 +set g_turrets_unit_plasma_dual_target_select_playerbias 1 +set g_turrets_unit_plasma_dual_target_select_missilebias 0 + +set g_turrets_unit_plasma_dual_ammo_max 640 +set g_turrets_unit_plasma_dual_ammo 320 +set g_turrets_unit_plasma_dual_ammo_recharge 40 + +set g_turrets_unit_plasma_dual_aim_firetolerance_dist 200 + +set g_turrets_unit_plasma_dual_aim_speed 100 +set g_turrets_unit_plasma_dual_aim_maxrot 360 +set g_turrets_unit_plasma_dual_aim_maxpitch 30 + +set g_turrets_unit_plasma_dual_track_type 3 +set g_turrets_unit_plasma_dual_track_accel_pitch 0.5 +set g_turrets_unit_plasma_dual_track_accel_rot 0.7 +set g_turrets_unit_plasma_dual_track_blendrate 0.2 +// }}} +// {{{ #11: Tesla Coil +set g_turrets_unit_tesla_health 1000 +set g_turrets_unit_tesla_respawntime 120 + +set g_turrets_unit_tesla_shot_dmg 200 +set g_turrets_unit_tesla_shot_refire 1.5 +set g_turrets_unit_tesla_shot_force 400 + +set g_turrets_unit_tesla_shot_volly 1 +set g_turrets_unit_tesla_shot_volly_refire 2.5 + +set g_turrets_unit_tesla_target_range_min 0 +set g_turrets_unit_tesla_target_range 1000 + +set g_turrets_unit_tesla_target_select_playerbias 1 +set g_turrets_unit_tesla_target_select_missilebias 1 + +set g_turrets_unit_tesla_ammo_max 1000 +set g_turrets_unit_tesla_ammo 200 +set g_turrets_unit_tesla_ammo_recharge 15 +// }}} +// {{{ #12: Walker Turret +set g_turrets_unit_walker_health 500 +set g_turrets_unit_walker_respawntime 60 + +set g_turrets_unit_walker_speed_run 300 +set g_turrets_unit_walker_speed_roam 100 +set g_turrets_unit_walker_speed_walk 200 +set g_turrets_unit_walker_speed_swim 200 +set g_turrets_unit_walker_speed_jump 800 +set g_turrets_unit_walker_speed_stop 90 + +set g_turrets_unit_walker_turn 20 +set g_turrets_unit_walker_turn_walk 15 +set g_turrets_unit_walker_turn_run 7 +set g_turrets_unit_walker_turn_swim 10 +set g_turrets_unit_walker_turn_strafe 5 + +set g_turrets_unit_walker_shot_dmg 5 +set g_turrets_unit_walker_shot_refire 0.05 +set g_turrets_unit_walker_shot_spread 0.025 +set g_turrets_unit_walker_shot_force 10 +set g_turrets_unit_walker_shot_radius 0 +set g_turrets_unit_walker_shot_speed 18000 + +set g_turrets_unit_walker_shot_volly 10 +set g_turrets_unit_walker_shot_volly_refire 1 + +set g_turrets_unit_walker_target_range 5000 +set g_turrets_unit_walker_target_range_optimal 100 +set g_turrets_unit_walker_target_range_min 0 + +set g_turrets_unit_walker_target_select_rangebias 0.25 +set g_turrets_unit_walker_target_select_samebias 0.25 +set g_turrets_unit_walker_target_select_anglebias 0.5 +set g_turrets_unit_walker_target_select_playerbias 1 +set g_turrets_unit_walker_target_select_missilebias 0 + +set g_turrets_unit_walker_ammo_max 4000 +set g_turrets_unit_walker_ammo 500 +set g_turrets_unit_walker_ammo_recharge 100 + +set g_turrets_unit_walker_aim_firetolerance_dist 100 +set g_turrets_unit_walker_aim_speed 45 +set g_turrets_unit_walker_aim_maxrot 90 +set g_turrets_unit_walker_aim_maxpitch 15 + +set g_turrets_unit_walker_track_type 1 + +set g_turrets_unit_walker_rockets_range 4000 +set g_turrets_unit_walker_rockets_range_min 500 +set g_turrets_unit_walker_rocket_refire 10 +set g_turrets_unit_walker_rocket_dmg 45 +set g_turrets_unit_walker_rocket_radius 150 +set g_turrets_unit_walker_rocket_force 150 +set g_turrets_unit_walker_rocket_turnrate 0.05 +set g_turrets_unit_walker_rocket_speed 1000 + +set g_turrets_unit_walker_melee_range 100 +set g_turrets_unit_walker_melee_dmg 100 +set g_turrets_unit_walker_melee_force 600 + +set g_turrets_unit_walker_track_accel_pitch 0.5 +set g_turrets_unit_walker_track_accel_rot 0.8 +set g_turrets_unit_walker_track_blendrate 0.2 +// }}} diff --git a/unit_ewheel.cfg b/unit_ewheel.cfg deleted file mode 100644 index df0562af9a..0000000000 --- a/unit_ewheel.cfg +++ /dev/null @@ -1,43 +0,0 @@ -set g_turrets_unit_ewheel_std_health 200 -set g_turrets_unit_ewheel_std_respawntime 30 - -// dgr / sec -set g_turrets_unit_ewheel_turnrate 200 - -set g_turrets_unit_ewheel_speed_fast 500 -set g_turrets_unit_ewheel_speed_slow 150 -set g_turrets_unit_ewheel_speed_slower 50 -set g_turrets_unit_ewheel_speed_stop 25 - -set g_turrets_unit_ewheel_std_shot_dmg 30 -set g_turrets_unit_ewheel_std_shot_refire 0.1 -set g_turrets_unit_ewheel_std_shot_spread 0.025 -set g_turrets_unit_ewheel_std_shot_force 125 -set g_turrets_unit_ewheel_std_shot_radius 50 -set g_turrets_unit_ewheel_std_shot_speed 9000 - -set g_turrets_unit_ewheel_std_shot_volly 2 -set g_turrets_unit_ewheel_std_shot_volly_refire 1 - -set g_turrets_unit_ewheel_std_target_range 5000 -set g_turrets_unit_ewheel_std_target_range_optimal 900 -set g_turrets_unit_ewheel_std_target_range_min 0.1 - -set g_turrets_unit_ewheel_std_target_select_rangebias 0.25 -set g_turrets_unit_ewheel_std_target_select_samebias 2 -set g_turrets_unit_ewheel_std_target_select_anglebias 0.5 -set g_turrets_unit_ewheel_std_target_select_playerbias 1 -set g_turrets_unit_ewheel_std_target_select_missilebias 0 - -set g_turrets_unit_ewheel_std_ammo_max 4000 -set g_turrets_unit_ewheel_std_ammo 500 -set g_turrets_unit_ewheel_std_ammo_recharge 50 - -set g_turrets_unit_ewheel_std_aim_firetolerance_dist 150 -set g_turrets_unit_ewheel_std_aim_speed 90 -set g_turrets_unit_ewheel_std_aim_maxrot 20 -set g_turrets_unit_ewheel_std_aim_maxpitch 45 - -set g_turrets_unit_ewheel_std_track_type 1 -set g_turrets_unit_ewheel_std_track_accel_pitch 0 -set g_turrets_unit_ewheel_std_track_accel_rot 0 diff --git a/unit_flac.cfg b/unit_flac.cfg deleted file mode 100644 index 17ba6a5aba..0000000000 --- a/unit_flac.cfg +++ /dev/null @@ -1,35 +0,0 @@ -set g_turrets_unit_flac_std_health 700 -set g_turrets_unit_flac_std_respawntime 90 - -set g_turrets_unit_flac_std_shot_dmg 20 -set g_turrets_unit_flac_std_shot_refire 0.1 -set g_turrets_unit_flac_std_shot_radius 100 -set g_turrets_unit_flac_std_shot_speed 9000 -set g_turrets_unit_flac_std_shot_spread 0.02 -set g_turrets_unit_flac_std_shot_force 25 -set g_turrets_unit_flac_std_shot_volly 0 -set g_turrets_unit_flac_std_shot_volly_refire 0 - -set g_turrets_unit_flac_std_target_range 4000 -set g_turrets_unit_flac_std_target_range_min 500 -set g_turrets_unit_flac_std_target_range_optimal 1250 - -set g_turrets_unit_flac_std_target_select_rangebias 0.25 -set g_turrets_unit_flac_std_target_select_samebias 1 -set g_turrets_unit_flac_std_target_select_anglebias 0.5 -set g_turrets_unit_flac_std_target_select_playerbias 0 -set g_turrets_unit_flac_std_target_select_missilebias 1 - -set g_turrets_unit_flac_std_ammo_max 1000 -set g_turrets_unit_flac_std_ammo 400 -set g_turrets_unit_flac_std_ammo_recharge 100 - -set g_turrets_unit_flac_std_aim_firetolerance_dist 150 -set g_turrets_unit_flac_std_aim_speed 200 -set g_turrets_unit_flac_std_aim_maxrot 360 -set g_turrets_unit_flac_std_aim_maxpitch 35 - -set g_turrets_unit_flac_std_track_type 3 -set g_turrets_unit_flac_std_track_accel_pitch 0.5 -set g_turrets_unit_flac_std_track_accel_rot 0.7 -set g_turrets_unit_flac_std_track_blendrate 0.2 diff --git a/unit_fusreac.cfg b/unit_fusreac.cfg deleted file mode 100644 index c9373f68fe..0000000000 --- a/unit_fusreac.cfg +++ /dev/null @@ -1,32 +0,0 @@ -set g_turrets_unit_fusreac_std_health 700 -set g_turrets_unit_fusreac_std_respawntime 90 - -set g_turrets_unit_fusreac_std_shot_speed 1 -set g_turrets_unit_fusreac_std_shot_dmg 20 -set g_turrets_unit_fusreac_std_shot_refire 0.2 - -set g_turrets_unit_fusreac_std_target_range 1024 -set g_turrets_unit_fusreac_std_target_range_min 1 - -set g_turrets_unit_fusreac_std_ammo_max 100 -set g_turrets_unit_fusreac_std_ammo 0 -set g_turrets_unit_fusreac_std_ammo_recharge 100 - -set g_turrets_unit_fusreac_std_shot_radius 0 -set g_turrets_unit_fusreac_std_shot_spread 0 -set g_turrets_unit_fusreac_std_shot_force 0 -set g_turrets_unit_fusreac_std_shot_volly 0 -set g_turrets_unit_fusreac_std_shot_volly_refire 0 -set g_turrets_unit_fusreac_std_target_range_optimal 0 -set g_turrets_unit_fusreac_std_target_select_rangebias 0 -set g_turrets_unit_fusreac_std_target_select_samebias 0 -set g_turrets_unit_fusreac_std_target_select_anglebias 0 -set g_turrets_unit_fusreac_std_target_select_playerbias 0 -set g_turrets_unit_fusreac_std_aim_firetolerance_dist 0 -set g_turrets_unit_fusreac_std_aim_speed 0 -set g_turrets_unit_fusreac_std_aim_maxrot 0 -set g_turrets_unit_fusreac_std_aim_maxpitch 0 -set g_turrets_unit_fusreac_std_track_type 0 -set g_turrets_unit_fusreac_std_track_accel_pitch 0 -set g_turrets_unit_fusreac_std_track_accel_rot 0 -set g_turrets_unit_fusreac_std_track_blendrate 0 \ No newline at end of file diff --git a/unit_hellion.cfg b/unit_hellion.cfg deleted file mode 100644 index cdf7546b8c..0000000000 --- a/unit_hellion.cfg +++ /dev/null @@ -1,40 +0,0 @@ -set g_turrets_unit_hellion_std_health 500 -set g_turrets_unit_hellion_std_respawntime 90 - -set g_turrets_unit_hellion_std_shot_dmg 50 -set g_turrets_unit_hellion_std_shot_refire 0.2 -set g_turrets_unit_hellion_std_shot_radius 80 - -set g_turrets_unit_hellion_std_shot_speed 650 -set g_turrets_unit_hellion_std_shot_speed_max 4000 -set g_turrets_unit_hellion_std_shot_speed_gain 1.01 - -set g_turrets_unit_hellion_std_shot_spread 0.08 -set g_turrets_unit_hellion_std_shot_force 250 -set g_turrets_unit_hellion_std_shot_volly 2 -set g_turrets_unit_hellion_std_shot_volly_refire 4 - -set g_turrets_unit_hellion_std_target_range 6000 -set g_turrets_unit_hellion_std_target_range_min 150 -set g_turrets_unit_hellion_std_target_range_optimal 4500 - -set g_turrets_unit_hellion_std_target_select_rangebias 0.7 -set g_turrets_unit_hellion_std_target_select_samebias 0.01 -set g_turrets_unit_hellion_std_target_select_anglebias 0.01 -set g_turrets_unit_hellion_std_target_select_playerbias 1 -set g_turrets_unit_hellion_std_target_select_missilebias 0 - -set g_turrets_unit_hellion_std_ammo_max 200 -set g_turrets_unit_hellion_std_ammo 100 -set g_turrets_unit_hellion_std_ammo_recharge 50 - -set g_turrets_unit_hellion_std_aim_firetolerance_dist 200 -set g_turrets_unit_hellion_std_aim_speed 100 -set g_turrets_unit_hellion_std_aim_maxrot 360 -set g_turrets_unit_hellion_std_aim_maxpitch 20 - -set g_turrets_unit_hellion_std_track_type 3 -set g_turrets_unit_hellion_std_track_accel_pitch 0.25 -set g_turrets_unit_hellion_std_track_accel_rot 0.6 -set g_turrets_unit_hellion_std_track_blendrate 0.25 - diff --git a/unit_hk.cfg b/unit_hk.cfg deleted file mode 100644 index 590181e09d..0000000000 --- a/unit_hk.cfg +++ /dev/null @@ -1,43 +0,0 @@ -set g_turrets_unit_hk_std_health 500 -set g_turrets_unit_hk_std_respawntime 90 - -set g_turrets_unit_hk_std_shot_dmg 120 -set g_turrets_unit_hk_std_shot_refire 5 -set g_turrets_unit_hk_std_shot_radius 200 - -set g_turrets_unit_hk_std_shot_speed 500 -set g_turrets_unit_hk_std_shot_speed_max 1000 -set g_turrets_unit_hk_std_shot_speed_accel 1.025 -set g_turrets_unit_hk_std_shot_speed_accel2 1.05 -set g_turrets_unit_hk_std_shot_speed_decel 0.9 -set g_turrets_unit_hk_std_shot_speed_turnrate 0.25 - -set g_turrets_unit_hk_std_shot_spread 0 -set g_turrets_unit_hk_std_shot_force 600 -set g_turrets_unit_hk_std_shot_volly 0 -set g_turrets_unit_hk_std_shot_volly_refire 0 - -set g_turrets_unit_hk_std_target_range 6000 -set g_turrets_unit_hk_std_target_range_min 220 -set g_turrets_unit_hk_std_target_range_optimal 5000 - -set g_turrets_unit_hk_std_target_select_rangebias 0.5 -set g_turrets_unit_hk_std_target_select_samebias 0.01 -set g_turrets_unit_hk_std_target_select_anglebias 0.1 -set g_turrets_unit_hk_std_target_select_playerbias 1 -set g_turrets_unit_hk_std_target_select_missilebias 0 - -set g_turrets_unit_hk_std_ammo_max 240 -set g_turrets_unit_hk_std_ammo 120 -set g_turrets_unit_hk_std_ammo_recharge 16 - -set g_turrets_unit_hk_std_aim_firetolerance_dist 500 -set g_turrets_unit_hk_std_aim_speed 100 -set g_turrets_unit_hk_std_aim_maxrot 360 -set g_turrets_unit_hk_std_aim_maxpitch 20 - -set g_turrets_unit_hk_std_track_type 3 -set g_turrets_unit_hk_std_track_accel_pitch 0.25 -set g_turrets_unit_hk_std_track_accel_rot 0.6 -set g_turrets_unit_hk_std_track_blendrate 0.2 - diff --git a/unit_machinegun.cfg b/unit_machinegun.cfg deleted file mode 100644 index 1fa1e48d5c..0000000000 --- a/unit_machinegun.cfg +++ /dev/null @@ -1,38 +0,0 @@ -set g_turrets_unit_machinegun_std_health 256 -set g_turrets_unit_machinegun_std_respawntime 60 - -set g_turrets_unit_machinegun_std_shot_dmg 10 -set g_turrets_unit_machinegun_std_shot_refire 0.1 -set g_turrets_unit_machinegun_std_shot_spread 0.015 -set g_turrets_unit_machinegun_std_shot_force 20 -set g_turrets_unit_machinegun_std_shot_radius 0 -set g_turrets_unit_machinegun_std_shot_speed 34920 - -set g_turrets_unit_machinegun_std_shot_volly 5 -set g_turrets_unit_machinegun_std_shot_volly_refire 0.5 - -set g_turrets_unit_machinegun_std_target_range 4500 -set g_turrets_unit_machinegun_std_target_range_min 2 -set g_turrets_unit_machinegun_std_target_range_optimal 1000 - -set g_turrets_unit_machinegun_std_target_select_rangebias 0.25 -set g_turrets_unit_machinegun_std_target_select_samebias 0.25 -set g_turrets_unit_machinegun_std_target_select_anglebias 0.5 -set g_turrets_unit_machinegun_std_target_select_playerbias 1 -set g_turrets_unit_machinegun_std_target_select_missilebias 0 - -set g_turrets_unit_machinegun_std_ammo_max 1500 -set g_turrets_unit_machinegun_std_ammo 300 -set g_turrets_unit_machinegun_std_ammo_recharge 75 - -set g_turrets_unit_machinegun_std_aim_firetolerance_dist 25 -set g_turrets_unit_machinegun_std_aim_speed 120 -set g_turrets_unit_machinegun_std_aim_maxrot 360 -set g_turrets_unit_machinegun_std_aim_maxpitch 25 - -set g_turrets_unit_machinegun_std_track_type 3 -set g_turrets_unit_machinegun_std_track_accel_pitch 0.4 -set g_turrets_unit_machinegun_std_track_accel_rot 0.9 -set g_turrets_unit_machinegun_std_track_blendrate 0.2 - - diff --git a/unit_mlrs.cfg b/unit_mlrs.cfg deleted file mode 100644 index c3f3f7859d..0000000000 --- a/unit_mlrs.cfg +++ /dev/null @@ -1,40 +0,0 @@ -set g_turrets_unit_mlrs_std_health 500 -set g_turrets_unit_mlrs_std_respawntime 60 - -set g_turrets_unit_mlrs_std_shot_dmg 50 -set g_turrets_unit_mlrs_std_shot_refire 0.1 -set g_turrets_unit_mlrs_std_shot_radius 125 -set g_turrets_unit_mlrs_std_shot_speed 2000 -set g_turrets_unit_mlrs_std_shot_spread 0.05 -set g_turrets_unit_mlrs_std_shot_force 25 - -set g_turrets_unit_mlrs_std_shot_volly 6 - -// !must be correctly matched with ammo_recharge as this unit use -// volly_always. (means ammo_recharge * ammo_max must be eaqual to volly_refire) -set g_turrets_unit_mlrs_std_shot_volly_refire 4 - -set g_turrets_unit_mlrs_std_target_range 3000 -set g_turrets_unit_mlrs_std_target_range_min 500 -set g_turrets_unit_mlrs_std_target_range_optimal 500 - -set g_turrets_unit_mlrs_std_target_select_rangebias 0.25 -set g_turrets_unit_mlrs_std_target_select_samebias 0.5 -set g_turrets_unit_mlrs_std_target_select_anglebias 0.5 -set g_turrets_unit_mlrs_std_target_select_playerbias 1 -set g_turrets_unit_mlrs_std_target_select_missilebias 0 - -// !must be shot_dmg * 6 as this unit uses ammo to control the animation -set g_turrets_unit_mlrs_std_ammo_max 300 -set g_turrets_unit_mlrs_std_ammo 300 -set g_turrets_unit_mlrs_std_ammo_recharge 75 - -set g_turrets_unit_mlrs_std_aim_firetolerance_dist 120 -set g_turrets_unit_mlrs_std_aim_speed 100 -set g_turrets_unit_mlrs_std_aim_maxrot 360 -set g_turrets_unit_mlrs_std_aim_maxpitch 20 - -set g_turrets_unit_mlrs_std_track_type 3 -set g_turrets_unit_mlrs_std_track_accel_pitch 0.5 -set g_turrets_unit_mlrs_std_track_accel_rot 0.7 -set g_turrets_unit_mlrs_std_track_blendrate 0.2 diff --git a/unit_phaser.cfg b/unit_phaser.cfg deleted file mode 100644 index 31de904894..0000000000 --- a/unit_phaser.cfg +++ /dev/null @@ -1,38 +0,0 @@ -set g_turrets_unit_phaser_std_health 500 - -set g_turrets_unit_phaser_std_respawntime 90 - -set g_turrets_unit_phaser_std_shot_dmg 100 -set g_turrets_unit_phaser_std_shot_refire 4 -set g_turrets_unit_phaser_std_shot_radius 8 -set g_turrets_unit_phaser_std_shot_speed 4 // Used for how long to sustain the beam for this turret (seconds) -set g_turrets_unit_phaser_std_shot_spread 0 -set g_turrets_unit_phaser_std_shot_force 5 -set g_turrets_unit_phaser_std_shot_volly 0 -set g_turrets_unit_phaser_std_shot_volly_refire 5 - -set g_turrets_unit_phaser_std_target_range 3000 -set g_turrets_unit_phaser_std_target_range_min 0 -set g_turrets_unit_phaser_std_target_range_optimal 1500 - -set g_turrets_unit_phaser_std_target_select_rangebias 0.85 -set g_turrets_unit_phaser_std_target_select_samebias 0 -set g_turrets_unit_phaser_std_target_select_anglebias 0.25 -set g_turrets_unit_phaser_std_target_select_playerbias 1 -set g_turrets_unit_phaser_std_target_select_missilebias 0 - -set g_turrets_unit_phaser_std_ammo_max 2000 -set g_turrets_unit_phaser_std_ammo 1000 -set g_turrets_unit_phaser_std_ammo_recharge 25 - -set g_turrets_unit_phaser_std_aim_firetolerance_dist 100 - -set g_turrets_unit_phaser_std_aim_speed 300 -set g_turrets_unit_phaser_std_aim_maxrot 360 -set g_turrets_unit_phaser_std_aim_maxpitch 30 - -set g_turrets_unit_phaser_std_track_type 3 -set g_turrets_unit_phaser_std_track_accel_pitch 0.5 -set g_turrets_unit_phaser_std_track_accel_rot 0.65 -set g_turrets_unit_phaser_std_track_blendrate 0.2 - diff --git a/unit_plasma.cfg b/unit_plasma.cfg deleted file mode 100644 index 949a3e84bd..0000000000 --- a/unit_plasma.cfg +++ /dev/null @@ -1,64 +0,0 @@ -set g_turrets_unit_plasma_std_health 500 -set g_turrets_unit_plasma_std_respawntime 60 - -// Do this mutch damage -set g_turrets_unit_plasma_std_shot_dmg 80 -//This often -set g_turrets_unit_plasma_std_shot_refire 0.6 -//Over this mutch area -set g_turrets_unit_plasma_std_shot_radius 150 -//Traveling at this speed -set g_turrets_unit_plasma_std_shot_speed 2000 -//With a random spread of -set g_turrets_unit_plasma_std_shot_spread 0.015 -//Pushing things this hard -set g_turrets_unit_plasma_std_shot_force 100 -//Each volly is this many shots (0=1) -set g_turrets_unit_plasma_std_shot_volly 0 -// Refire time upon compleated volly -set g_turrets_unit_plasma_std_shot_volly_refire 0 - -// Scan for targets within this range -set g_turrets_unit_plasma_std_target_range 3500 -// But no close then this -set g_turrets_unit_plasma_std_target_range_min 200 -// If we have a choise, prefer the ones closer to this distance -set g_turrets_unit_plasma_std_target_range_optimal 500 - - -// Targetselect is made for each turret based on range, angle (turrets needs to turn to aim at), if its a player / missile -// scale range score this mucth -set g_turrets_unit_plasma_std_target_select_rangebias 0.5 -// scale 'same' score this mutch (stick with same target) -set g_turrets_unit_plasma_std_target_select_samebias 0.01 -// and so on -set g_turrets_unit_plasma_std_target_select_anglebias 0.25 -set g_turrets_unit_plasma_std_target_select_playerbias 1 -set g_turrets_unit_plasma_std_target_select_missilebias 0 - -// Can carry this mutch ammo. one dmg = one ammo -set g_turrets_unit_plasma_std_ammo_max 640 -// Start with this mutch ammo -set g_turrets_unit_plasma_std_ammo 320 -// Regain ammo this fast (per sec) -set g_turrets_unit_plasma_std_ammo_recharge 40 - -// If predicted emeypos is this or closer to predicted impact, fire is ok -set g_turrets_unit_plasma_std_aim_firetolerance_dist 120 -// Aim how fast. for track_type 1 this is dgr/sec, for 2 & 3 its the maximum angle speed added each second -set g_turrets_unit_plasma_std_aim_speed 200 -// Max rottation of head -set g_turrets_unit_plasma_std_aim_maxrot 360 -// Max pitch of head -set g_turrets_unit_plasma_std_aim_maxpitch 30 - -// How the head turns. -// 1 = hard steps, good for aiming preformace, bad for visuals. -// 2 = smooth w/o inertia -// 3 = smmoth with simulated inertia -set g_turrets_unit_plasma_std_track_type 3 -// Following controls how _track_type = 3 works. -set g_turrets_unit_plasma_std_track_accel_pitch 0.5 -set g_turrets_unit_plasma_std_track_accel_rot 0.7 -set g_turrets_unit_plasma_std_track_blendrate 0.2 - diff --git a/unit_plasma2.cfg b/unit_plasma2.cfg deleted file mode 100644 index 723f6441ae..0000000000 --- a/unit_plasma2.cfg +++ /dev/null @@ -1,38 +0,0 @@ -set g_turrets_unit_plasma_dual_health 500 -set g_turrets_unit_plasma_dual_respawntime 60 - -set g_turrets_unit_plasma_dual_shot_dmg 80 -set g_turrets_unit_plasma_dual_shot_refire 0.35 -set g_turrets_unit_plasma_dual_shot_radius 150 -set g_turrets_unit_plasma_dual_shot_speed 2000 -set g_turrets_unit_plasma_dual_shot_spread 0.015 -set g_turrets_unit_plasma_dual_shot_force 100 - -set g_turrets_unit_plasma_dual_shot_volly 0 -set g_turrets_unit_plasma_dual_shot_volly_refire 0 - -set g_turrets_unit_plasma_dual_target_range 3000 -set g_turrets_unit_plasma_dual_target_range_min 80 -set g_turrets_unit_plasma_dual_target_range_optimal 1000 - -set g_turrets_unit_plasma_dual_target_select_rangebias 0.2 -set g_turrets_unit_plasma_dual_target_select_samebias 0.4 -set g_turrets_unit_plasma_dual_target_select_anglebias 0.4 -set g_turrets_unit_plasma_dual_target_select_playerbias 1 -set g_turrets_unit_plasma_dual_target_select_missilebias 0 - -set g_turrets_unit_plasma_dual_ammo_max 640 -set g_turrets_unit_plasma_dual_ammo 320 -set g_turrets_unit_plasma_dual_ammo_recharge 40 - -set g_turrets_unit_plasma_dual_aim_firetolerance_dist 200 - -set g_turrets_unit_plasma_dual_aim_speed 100 -set g_turrets_unit_plasma_dual_aim_maxrot 360 -set g_turrets_unit_plasma_dual_aim_maxpitch 30 - -set g_turrets_unit_plasma_dual_track_type 3 -set g_turrets_unit_plasma_dual_track_accel_pitch 0.5 -set g_turrets_unit_plasma_dual_track_accel_rot 0.7 -set g_turrets_unit_plasma_dual_track_blendrate 0.2 - diff --git a/unit_tesla.cfg b/unit_tesla.cfg deleted file mode 100644 index 3a66b5f968..0000000000 --- a/unit_tesla.cfg +++ /dev/null @@ -1,19 +0,0 @@ -set g_turrets_unit_tesla_std_health 1000 -set g_turrets_unit_tesla_std_respawntime 120 - -set g_turrets_unit_tesla_std_shot_dmg 200 -set g_turrets_unit_tesla_std_shot_refire 1.5 -set g_turrets_unit_tesla_std_shot_force 400 - -set g_turrets_unit_tesla_std_shot_volly 1 -set g_turrets_unit_tesla_std_shot_volly_refire 2.5 - -set g_turrets_unit_tesla_std_target_range_min 0 -set g_turrets_unit_tesla_std_target_range 1000 - -set g_turrets_unit_tesla_std_target_select_playerbias 1 -set g_turrets_unit_tesla_std_target_select_missilebias 1 - -set g_turrets_unit_tesla_std_ammo_max 1000 -set g_turrets_unit_tesla_std_ammo 200 -set g_turrets_unit_tesla_std_ammo_recharge 15 diff --git a/unit_walker.cfg b/unit_walker.cfg deleted file mode 100644 index e08374945e..0000000000 --- a/unit_walker.cfg +++ /dev/null @@ -1,67 +0,0 @@ -set g_turrets_unit_walker_std_health 500 -set g_turrets_unit_walker_std_respawntime 60 - -set g_turrets_unit_walker_speed_run 300 -set g_turrets_unit_walker_speed_roam 100 -set g_turrets_unit_walker_speed_walk 200 -set g_turrets_unit_walker_speed_swim 200 -set g_turrets_unit_walker_speed_jump 800 -set g_turrets_unit_walker_speed_stop 90 - -set g_turrets_unit_walker_turn 20 -set g_turrets_unit_walker_turn_walk 15 -set g_turrets_unit_walker_turn_run 7 -set g_turrets_unit_walker_turn_swim 10 -set g_turrets_unit_walker_turn_strafe 5 - -// Main machineguns prop's -set g_turrets_unit_walker_std_shot_dmg 5 -set g_turrets_unit_walker_std_shot_refire 0.05 -set g_turrets_unit_walker_std_shot_spread 0.025 -set g_turrets_unit_walker_std_shot_force 10 -set g_turrets_unit_walker_std_shot_radius 0 -set g_turrets_unit_walker_std_shot_speed 18000 - -set g_turrets_unit_walker_std_shot_volly 10 -set g_turrets_unit_walker_std_shot_volly_refire 1 - -set g_turrets_unit_walker_std_target_range 5000 -set g_turrets_unit_walker_std_target_range_optimal 100 -set g_turrets_unit_walker_std_target_range_min 0 - -set g_turrets_unit_walker_std_target_select_rangebias 0.25 -set g_turrets_unit_walker_std_target_select_samebias 0.25 -set g_turrets_unit_walker_std_target_select_anglebias 0.5 -set g_turrets_unit_walker_std_target_select_playerbias 1 -set g_turrets_unit_walker_std_target_select_missilebias 0 - -set g_turrets_unit_walker_std_ammo_max 4000 -set g_turrets_unit_walker_std_ammo 500 -set g_turrets_unit_walker_std_ammo_recharge 100 - -set g_turrets_unit_walker_std_aim_firetolerance_dist 100 -set g_turrets_unit_walker_std_aim_speed 45 -set g_turrets_unit_walker_std_aim_maxrot 90 -set g_turrets_unit_walker_std_aim_maxpitch 15 - -// Head (minigun) is attached. must use tractype 1 -set g_turrets_unit_walker_std_track_type 1 - -// "Wobbly" homing rockets that sometimes loop -set g_turrets_unit_walker_std_rockets_range 4000 -set g_turrets_unit_walker_std_rockets_range_min 500 -set g_turrets_unit_walker_std_rocket_refire 10 -set g_turrets_unit_walker_std_rocket_dmg 45 -set g_turrets_unit_walker_std_rocket_radius 150 -set g_turrets_unit_walker_std_rocket_force 150 -set g_turrets_unit_walker_std_rocket_turnrate 0.05 -set g_turrets_unit_walker_std_rocket_speed 1000 - -// Meele attack. Only happens when theres a target directly in front -set g_turrets_unit_walker_std_meele_range 100 -set g_turrets_unit_walker_std_meele_dmg 100 -set g_turrets_unit_walker_std_meele_force 600 - -set g_turrets_unit_walker_std_track_accel_pitch 0.5 -set g_turrets_unit_walker_std_track_accel_rot 0.8 -set g_turrets_unit_walker_std_track_blendrate 0.2