r3857 - in trunk/data/qcsrc/server: . tturrets tturrets/include tturrets/system tturrets/units
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Mon Jul 21 05:55:08 EDT 2008
Author: tzork
Date: 2008-07-21 05:55:06 -0400 (Mon, 21 Jul 2008)
New Revision: 3857
Added:
trunk/data/qcsrc/server/server.cbp
trunk/data/qcsrc/server/tturrets/
trunk/data/qcsrc/server/tturrets/include/
trunk/data/qcsrc/server/tturrets/include/turret_tturrets.qh
trunk/data/qcsrc/server/tturrets/include/turret_tturrets_early.qh
trunk/data/qcsrc/server/tturrets/system/
trunk/data/qcsrc/server/tturrets/system/turret_system_aimprocs.qc
trunk/data/qcsrc/server/tturrets/system/turret_system_damage.qc
trunk/data/qcsrc/server/tturrets/system/turret_system_main.qc
trunk/data/qcsrc/server/tturrets/system/turret_system_misc.qc
trunk/data/qcsrc/server/tturrets/system/turret_system_scoreprocs.qc
trunk/data/qcsrc/server/tturrets/units/
trunk/data/qcsrc/server/tturrets/units/turret_unit_common.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_flac.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_fusionreactor.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_hellion.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_hk.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_machinegun.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_mlrs.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_phaser.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_plasma.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_targettrigger.qc
trunk/data/qcsrc/server/tturrets/units/turret_unit_tessla.qc
Modified:
trunk/data/qcsrc/server/assault.qc
trunk/data/qcsrc/server/constants.qh
trunk/data/qcsrc/server/g_damage.qc
trunk/data/qcsrc/server/progs.src
Log:
tZork's turrets, code part (disabled by default, see includes for details). and a code::blocks project file.
Modified: trunk/data/qcsrc/server/assault.qc
===================================================================
--- trunk/data/qcsrc/server/assault.qc 2008-07-21 08:54:24 UTC (rev 3856)
+++ trunk/data/qcsrc/server/assault.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -44,7 +44,7 @@
void assault_objective_use() {
// activate objective
- self.health = 100;
+ self.health = 100;
self.nextthink = time + 0.1;
}
@@ -57,7 +57,7 @@
//self.effects = EF_STARDUST;
self.nextthink = time + 0.1;
}
-
+
}
//=============================================================================
@@ -102,7 +102,7 @@
if(objective.classname == "target_objective") {
found = 1;
if(objective.health < ASSAULT_VALUE_INACTIVE) { // targeted objective is active
- if(self.cnt == 1 && self.max_health >= ASSAULT_VALUE_INACTIVE) {
+ if(self.cnt == 1 && self.max_health >= ASSAULT_VALUE_INACTIVE) {
// decrease was fired already, but objective did recover (round reset)
self.cnt = 0;
}
@@ -236,7 +236,7 @@
self.health = 100;
self.max_health = self.health;
-
+
self.cnt = 0; // not yet activated
self.classname = "func_assault_destructible";
@@ -283,7 +283,7 @@
void target_assault_roundend_reset() {
self.cnt = self.cnt + 1; // up round counter
- self.winning = 0; // up round
+ self.winning = 0; // up round
}
void target_assault_roundend_use() {
@@ -302,8 +302,33 @@
}
void assault_roundstart_use() {
+
activator = self;
SUB_UseTargets();
+
+#ifdef TTURRETS_ENABLED
+ entity ent,oldself;
+
+ //(Re)spawn all turrets
+ oldself = self;
+ ent = find(world, classname, "turret_main");
+ while(ent) {
+ // Swap turret teams
+ if(ent.team == COLOR_TEAM1)
+ ent.team = COLOR_TEAM2;
+ else
+ ent.team = COLOR_TEAM1;
+
+ self = ent;
+
+ // Dubbles as teamchange
+ ent.turret_spawnfunc();
+
+ ent = find(ent, classname, "turret_main");
+ }
+ self = oldself;
+#endif
+
}
void spawnfunc_target_assault_roundstart() {
@@ -317,7 +342,7 @@
// trigger new round
// reset objectives, toggle spawnpoints, reset triggers, ...
void assault_new_round() {
-
+
// up round counter
self.winning = self.winning + 1;
// set end time for next round
@@ -356,7 +381,7 @@
self = oldself;
ent = find(ent, classname, "info_player_deathmatch");
- }
+ }
// reset all objectives
ent = find(world, classname, "target_objective");
@@ -368,7 +393,7 @@
self = oldself;
ent = find(ent, classname, "target_objective");
- }
+ }
// reset round end triggers
ent = find(world, classname, "target_assault_roundend");
@@ -388,7 +413,7 @@
{
ent.cnt = 0;
ent = find(ent, classname, "target_objective_decrease");
- }
+ }
// reset all spawnfunc_func_assault_destructible
ent = find(world, classname, "func_assault_destructible");
@@ -396,6 +421,12 @@
{
oldself = self;
self = ent;
+
+ if(ent.team == COLOR_TEAM1)
+ ent.team = COLOR_TEAM2;
+ else
+ ent.team = COLOR_TEAM1;
+
assault_destructible_reset();
self = oldself;
ent = find(ent, classname, "func_assault_destructible");
@@ -419,6 +450,7 @@
PutClientInServer();
self = oldself;
ent = find(ent, classname, "player");
+
}
Modified: trunk/data/qcsrc/server/constants.qh
===================================================================
--- trunk/data/qcsrc/server/constants.qh 2008-07-21 08:54:24 UTC (rev 3856)
+++ trunk/data/qcsrc/server/constants.qh 2008-07-21 09:55:06 UTC (rev 3857)
@@ -151,7 +151,11 @@
float DEATH_SHOOTING_STAR = 10012;
float DEATH_ROT = 10013;
float DEATH_MIRRORDAMAGE = 10014;
+float DEATH_TURRET = 10100;
+
+
+
float IT_LASER = 4096;
float IT_SHOTGUN = 1;
float IT_UZI = 2;
Modified: trunk/data/qcsrc/server/g_damage.qc
===================================================================
--- trunk/data/qcsrc/server/g_damage.qc 2008-07-21 08:54:24 UTC (rev 3856)
+++ trunk/data/qcsrc/server/g_damage.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -169,7 +169,7 @@
else
centerprint(targ, "^1You grew too old without taking your medicine\n\n\n");
} else if (deathtype == DEATH_MIRRORDAMAGE) {
- if(sv_gentle)
+ if(sv_gentle)
centerprint(targ, "^1Don't go against team mates!\n\n\n");
else
centerprint(targ, "^1Don't shoot your team mates!\n\n\n");
@@ -187,7 +187,7 @@
bprint ("^1",s, "^1 didn't become friends with the Lord of Teamplay\n");
else
bprint ("^1",s, "^1 will be reinserted into the game due to his own actions\n");
-
+
if(deathtype != DEATH_TEAMCHANGE)
{
LogDeath("suicide", deathtype, targ, targ);
@@ -218,7 +218,7 @@
bprint ("^1",s, "^1 didn't become friends with the Lord of Teamplay\n");
else if (deathtype != DEATH_TEAMCHANGE)
bprint ("^1",s, "^1 couldn't resist the urge to self-destruct\n");
-
+
if(deathtype != DEATH_TEAMCHANGE)
{
LogDeath("suicide", deathtype, targ, targ);
@@ -235,7 +235,7 @@
if(sv_gentle) {
centerprint(attacker, "^1Moron! You went against a teammate!\n\n\n");
bprint ("^1", a, "^1 took action against a teammate\n");
- } else {
+ } else {
centerprint(attacker, "^1Moron! You fragged a teammate!\n\n\n");
bprint ("^1", a, "^1 mows down a teammate\n");
}
@@ -315,6 +315,8 @@
bprint ("^1",s, "^1 was conserved by ", a, "\n");
else if (deathtype == DEATH_HURTTRIGGER)
bprint ("^1",s, "^1 was thrown into a world of hurt by ", a, "\n");
+ else if(deathtype == DEATH_TURRET)
+ bprint ("^1",s, "^1 was pushed into the line of fire by ^1", a, "\n");
else
bprint ("^1",s, "^1 was fragged by ", a, "\n");
}
@@ -432,6 +434,8 @@
bprint ("^1",s, "^1 discovered a swamp\n");
else
bprint ("^1",s, "^1 is now conserved for centuries to come\n");
+ else if(deathtype == DEATH_TURRET)
+ bprint ("^1",s, "^1 was mowed down by a turret \n");
else
if(sv_gentle)
bprint ("^1",s, "^1 needs a restart\n");
Modified: trunk/data/qcsrc/server/progs.src
===================================================================
--- trunk/data/qcsrc/server/progs.src 2008-07-21 08:54:24 UTC (rev 3856)
+++ trunk/data/qcsrc/server/progs.src 2008-07-21 09:55:06 UTC (rev 3857)
@@ -8,6 +8,9 @@
extensions.qh
+//// tZork Turrets ////
+tturrets/include/turret_tturrets_early.qh
+
campaign.qh
../common/campaign_common.qh
../common/util.qh
@@ -114,3 +117,7 @@
t_quake3.qc
t_halflife.qc
t_quake.qc
+
+
+//// tZork Turrets ////
+tturrets/include/turret_tturrets.qh
\ No newline at end of file
Added: trunk/data/qcsrc/server/server.cbp
===================================================================
--- trunk/data/qcsrc/server/server.cbp (rev 0)
+++ trunk/data/qcsrc/server/server.cbp 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+ <FileVersion major="1" minor="6" />
+ <Project>
+ <Option title="server" />
+ <Option pch_mode="2" />
+ <Option compiler="quakec" />
+ <Build>
+ <Target title="Standard">
+ <Option output="bin\Debug\server" prefix_auto="1" extension_auto="1" />
+ <Option object_output="obj\Debug\" />
+ <Option type="1" />
+ <Option compiler="quakec" />
+ <Compiler>
+ <Add option="-g" />
+ </Compiler>
+ </Target>
+ </Build>
+ <Compiler>
+ <Add option="-Wall" />
+ </Compiler>
+ <Extensions>
+ <code_completion />
+ <envvars />
+ <debugger />
+ <lib_finder disable_auto="1" />
+ </Extensions>
+ </Project>
+</CodeBlocks_project_file>
Added: trunk/data/qcsrc/server/tturrets/include/turret_tturrets.qh
===================================================================
--- trunk/data/qcsrc/server/tturrets/include/turret_tturrets.qh (rev 0)
+++ trunk/data/qcsrc/server/tturrets/include/turret_tturrets.qh 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,31 @@
+#ifdef TTURRETS_ENABLED
+
+// Include section.
+#include "../system/turret_system_misc.qc" /// Assorted junk & jewls
+#include "../system/turret_system_main.qc" /// And routines
+#include "../system/turret_system_aimprocs.qc" /// Aiming realted stuff
+#include "../system/turret_system_scoreprocs.qc" /// Target calssification
+#include "../system/turret_system_damage.qc" /// Outch, they are hurting me! what should i do?
+
+#include "../units/turret_unit_common.qc" /// Some "make life easyer" funcs.
+
+// Non combat units
+#include "../units/turret_unit_fusionreactor.qc" /// Supply unites that need it with power
+// #include "../units/turret_unit_targetcomputer.qc" /// Optimized target selection for multiple units
+#include "../units/turret_unit_targettrigger.qc" /// Hit me!
+
+// Combat units
+#include "../units/turret_unit_plasma.qc" /// Basic energy cannon
+#include "../units/turret_unit_mlrs.qc" /// Basic multibay RL
+#include "../units/turret_unit_hellion.qc" /// Seeking missiles MLRS
+#include "../units/turret_unit_flac.qc" /// anti missile turret
+#include "../units/turret_unit_phaser.qc" /// ZzzapT
+//#include "../units/turret_unit_gauss.qc" /// Railgun
+#include "../units/turret_unit_hk.qc" /// Hunter killers
+#include "../units/turret_unit_machinegun.qc" /// whacka
+//#include "../units/turret_unit_minigun.qc" /// whacka whacka
+#include "../units/turret_unit_tessla.qc" /// Chain lightning capabale turret
+//#include "../units/turret_unit_walker.qc"
+//#include "../units/turret_unit_howitzer.qc"
+
+#endif // TTURRETS_ENABLED
Added: trunk/data/qcsrc/server/tturrets/include/turret_tturrets_early.qh
===================================================================
--- trunk/data/qcsrc/server/tturrets/include/turret_tturrets_early.qh (rev 0)
+++ trunk/data/qcsrc/server/tturrets/include/turret_tturrets_early.qh 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,459 @@
+// Comment out below to skip turrets
+//#define TTURRETS_ENABLED
+
+#ifdef TTURRETS_ENABLED
+
+#message "with tZork turrets rc1"
+
+vector real_origin(entity ent);
+
+/// target selection flags
+.float target_select_flags;
+.float target_validate_flags;
+/// Dont select a target on its own.
+#define TFL_TARGETSELECT_NO 1024
+/// Need line of sight
+#define TFL_TARGETSELECT_LOS 2
+/// Players are valid targets
+#define TFL_TARGETSELECT_PLAYERS 4
+/// Missiles are valid targets
+#define TFL_TARGETSELECT_MISSILES 8
+/// Responds to turret_trigger_target events
+#define TFL_TARGETSELECT_TRIGGERTARGET 16
+/// Angular limitations of turret head limits target selection
+#define TFL_TARGETSELECT_ANGLELIMITS 32
+/// Range limits apply in targetselection
+#define TFL_TARGETSELECT_RANGELIMTS 64
+/// DOnt select targets with a .team matching its own
+#define TFL_TARGETSELECT_TEAMCHECK 4096
+/// Cant select targets on its own. needs to be triggerd or slaved.
+#define TFL_TARGETSELECT_NOBUILTIN 256
+/// TFL_TARGETSELECT_TEAMCHECK is inverted (selects only mebers of own .team)
+#define TFL_TARGETSELECT_OWNTEAM 2048
+
+/// aim flags
+.float aim_flags;
+/// Dont aim.
+#define TFL_AIM_NO 512
+/// Go for ground, not direct hit
+#define TFL_AIM_GROUND 2
+/// Use balistic aim (not implemented)
+#define TFL_AIM_BALISTIC 4
+/// Try to predict target movement
+#define TFL_AIM_LEAD 8
+/// Compensate for shot traveltime when lead
+#define TFL_AIM_SHOTTIMECOMPENSATE 16
+/// Aim slightly in front of target
+#define TFL_AIM_INFRONT 32
+/// Aim slightly behind target
+#define TFL_AIM_BEHIND 64
+/// blend real and predicted z positions. (fake bounce prediction)
+#define TFL_AIM_ZEASE 128
+/// Just poitn at target's current location
+#define TFL_AIM_SIMPLE 256
+
+/// 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 terrible accuracy depending on how the feilds below are set
+#define TFL_TRACKTYPE_FLUIDINERTIA 3
+/// TFL_TRACKTYPE_FLUIDINERTIA: pitch multiplier
+.float track_accel_pitch;
+/// TFL_TRACKTYPE_FLUIDINERTIA: rotation multiplier
+.float track_accel_rot;
+/// TFL_TRACKTYPE_FLUIDINERTIA: Blendrate with old rotation (inertia simulation) 1 = only old, 0 = only new
+.float track_blendrate;
+
+/// How prefire check is preformed
+.float firecheck_flags;
+/// Dont do any chekcs
+#define TFL_FIRECHECK_NO 8192
+/// Dont kill the world
+#define TFL_FIRECHECK_WORLD 2
+/// Dont kill the dead
+#define TFL_FIRECHECK_DEAD 4
+/// Range limits apply
+#define TFL_FIRECHECK_DISTANCES 8
+/// Line Of Sight needs to be clear
+#define TFL_FIRECHECK_LOS 16
+/// Consider distance inpactpoint<->aimspot
+#define TFL_FIRECHECK_AIMDIST 32
+/// Consider enemy origin<->impactpoint
+#define TFL_FIRECHECK_REALDIST 64
+/// Consider angular diff head<->aimspot
+#define TFL_FIRECHECK_ANGLEDIST 128
+/// (re)consider target.team<->self.team
+#define TFL_FIRECHECK_TEAMCECK 256
+/// Try to avoid friendly fire
+#define TFL_FIRECHECK_AFF 512
+/// Own .ammo needs to be >= then own .shot_dmg
+#define TFL_FIRECHECK_OWM_AMMO 1024
+/// Others ammo need to be < others .ammo_max
+#define TFL_FIRECHECK_OTHER_AMMO 2048
+/// Check own .attack_finished vs time
+#define TFL_FIRECHECK_REFIRE 4096
+
+/// 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_LINKED 65536
+
+/// Ammo types needed and/or provided
+.float ammo_flags;
+/// Has and needs no ammo
+#define TFL_AMMO_NONE 64
+/// Uses power
+#define TFL_AMMO_ENERGY 2
+/// Uses bullets
+#define TFL_AMMO_BULLETS 4
+/// Uses explosives
+#define TFL_AMMO_ROCKETS 8
+/// Regenerates ammo on its own
+#define TFL_AMMO_RECHARGE 16
+ /// Can recive ammo from others
+#define TFL_AMMO_RECIVE 32
+
+/// How incomming damage is handeld
+.float damage_flags;
+/// Cant be hurt
+#define TFL_DMG_NO 256
+/// Can be damaged
+#define TFL_DMG_YES 2
+/// Can be damaged by teammates
+#define TFL_DMG_TAKEFROMTEAM 4
+/// Traget attackers
+#define TFL_DMG_RETALIATE 8
+/// Target attackers, even is on own team
+#define TFL_DMG_RETALIATEONTEAM 16
+/// Loses target when damaged
+#define TFL_DMG_TARGETLOSS 32
+/// Reciving damage trows off aim
+#define TFL_DMG_AIMSHAKE 64
+/// Reciving damage slaps the head arround
+#define TFL_DMG_HEADSHAKE 128
+
+// Spawnflags
+/// Spawn in teambased modes
+#define TFL_SPAWN_TEAM 2
+/// Spawn in FFA modes
+#define TFL_SPAWN_FFA 4
+/// Respawn after death
+#define TFL_SPAWN_RESPAWN 8
+
+/*
+* Fields commnly used by turrets
+*/
+/// Turrets internal ai speed
+.float ticrate;
+/// Where to point the head when no target
+.vector idle_aim;
+/// Top part of turret
+.entity tur_head;
+/// Start/respawn health
+.float tur_health;
+/// Defend this entity (or ratehr this entitys position)
+.entity tur_defend;
+/// on/off toggle.
+.float tur_active;
+/// Aim from this point,
+.vector tur_aimorg;
+/// and shoot from here. (could 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;
+/// What entity the aimtrace hit, if any.
+.entity tur_impactent;
+/// Distance to enemy
+.float tur_dist_enemy;
+/// Distance impact<->aim
+.float tur_dist_toaimpos;
+/// Decresment counter form .shot_volly to 0.
+.float volly_counter;
+/// Pointer to master when in slave mode
+.entity emaster; // A slave?
+
+/*
+* 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;
+/// 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 or borken volly.
+.float shot_volly_refire;
+
+/// Consider targets within this range
+.float target_range;
+/// Dont consider targets closer then
+.float target_range_min;
+/// Engage fire routine on targets within
+.float target_range_fire;
+/// 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;
+
+/*
+* Aim refers to real aiming, not gun pos (thats done by track)
+*/
+/// Maximum offset between impact and aim spot to fire
+.float aim_firetolerance_dist;
+/// Maximum angular offset between head and aimspot to fire
+.float aim_firetolerance_angle;
+/// How fast can i rotate/pitch (per second in stepmotor mode, base force in smooth modes)
+.float aim_speed;
+/// cant aim higher/lower then this
+.float aim_maxpitch;
+/// I cant rotate more then this
+.float aim_maxrot;
+
+// Ammo/power. keeping dmg and ammo on a one to one ratio is preferable (for rating)
+/// Staring & current ammo
+.float ammo;
+/// Regenerate this mutch ammo (per second)
+.float ammo_recharge;
+/// Max amount of ammo i can hold
+.float ammo_max;
+
+// Am i enslaved?
+// Slave turrets always target the same target as their master.
+// However; Depending on their individual capabiliteis, they may not fire at the same time
+// or at all. (slaves ranges, rotaion limits, ammo etc still apply)
+
+/// Set to another turrets .master_nameof to enslave me
+.string master_name;
+/// Set another turrets .master_name to this to enslave it
+.string master_nameof;
+
+// Uncomment below to enable various debug output.
+//#define TURRET_DEBUG
+//#define TURRET_DEBUG_TARGETVALIDATE
+//#define TURRET_DEBUG_TARGETSELECT
+
+#ifdef TURRET_DEBUG
+.float tur_dbg_dmg_t_h; // Total dmg that hit something (can be more then tur_dbg_dmg_t_f since it should count radius dmg.
+.float tur_dbg_dmg_t_f; // Total damage spent
+.float tur_dbg_start; // When did i go online?
+.float tur_dbg_tmr1; // timer for random use
+.float tur_dbg_tmr2; // timer for random use
+.float tur_dbg_tmr3; // timer for random use
+.vector tur_dbg_rvec; // Random vector, mainly for coloruing stuff'
+#endif
+
+// System main's
+/// Main AI loop
+void turret_think();
+/// Prefire checks and sutch
+void turret_fire();
+
+// Callbacks
+/// implements the actual fiering
+.void() turret_firefunc;
+/// prefire checks go here. return 1 to go bang, 0 not to.
+.float() turret_firecheckfunc;
+/// Execure BEFORE main ai loop. return 0 to cancel any following proccessing.
+.float() turret_prethink;
+/// Execure AFTER main AI loop UNLESS turret_prethink returnd 0
+.void() turret_postthink;
+
+/// Add a target
+.float(entity e_target,entity e_sender) turret_addtarget;
+
+.float call_diehook;
+.float call_respwnhook;
+.void() turret_diehook;
+.void() turret_respawnhook;
+
+/*
+* Some turrets need other aimsystems then other.
+* This should return the place to aim at, not acctualy turn or
+* pitch anyting.
+*
+* use turret_stdproc_aim# or Make your own.
+* Make sure you update tur_enemy_dist and tur_enemy_adist
+* with the apropriate info, if you do.
+*/
+/// function used to aim, usualy turret_stdproc_aim_generic
+.vector() turret_aim;
+
+/*
+* This is where the acctual turret turning should take place
+* Use turret_stdproc_track or make your own.
+*/
+/// Function used to turn and pitch the .tur_head usualy turret_stdproc_track
+.void() turret_track;
+
+/*
+* Target selection, preferably but not nessesarely
+* return a normalized result.
+*/
+/// Function to use for target evaluation. usualy turret_stdproc_targetscore_generic
+.float(entity e_turret, entity e_target) turret_score_target;
+
+/*
+* Damage, death and respawn.
+*/
+void turret_gibs_precash();
+/// Function to handle incomming damage. usualy turret_stdproc_damage
+.void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) turret_damagefunc;
+/// Function to handle the event of death. usualy turret_stdproc_die
+.void() turret_diefunc;
+ /// Function that handles rebirth. usualy turret_stdproc_respawn
+.void() turret_spawnfunc;
+
+/*
+* Stuff to plug into requierd but unused callbacks.
+*/
+/// Always return 1
+float turret_stdproc_true();
+/// Always return 0
+float turret_stdproc_false();
+/// Always return nothing at all
+void turret_stdproc_nothing();
+
+/*
+* Target selection
+*/
+/// "closeer is beter" selection
+float turret_stdproc_targetscore_close(entity e_turret, entity e_target);
+/// "further is beter" selection
+float turret_stdproc_targetscore_far(entity e_turret, entity e_target);
+/// only target_range_optimal
+float turret_stdproc_targetscore_optimal(entity e_turret, entity e_target);
+/// defendpos
+float turret_stdproc_targetscore_defend(entity e_turret, entity e_target);
+/// Generic fairly smart bias-aware target selection.
+float turret_stdproc_targetscore_generic(entity e_turret, entity e_target);
+/// Experimental supportunits targetselector
+float turret_stdproc_targetscore_support(entity e_turret,entity e_target);
+
+/*
+* Aim functions
+*/
+/// Generic aimer guided by self.aim_flags
+vector turret_stdproc_aim_generic()
+/// Straight line, current location
+vector turret_stdproc_aim_simple()
+
+/*
+* Turret turning & pitch
+*/
+/// Tries to line up the turret head with the aimpos
+void turret_stdproc_track();
+
+/// Generic damage handeling. blows up the turret when health <= 0
+void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce);
+/// Spawns a explotion, does some damage & trows bits arround.
+void turret_stdproc_die();
+/// reassembles the turret.
+void turret_stdproc_respawn();
+
+/// Evaluate target validity
+float turret_validate_target(entity e_turret,entity e_target,float validate_flags);
+/// Turret Head Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
+vector tvt_thadv;
+/// Turret Angle Diff Vector. updated by a sucsessfull call to turret_validate_target
+vector tvt_tadv;
+/// Turret Head Angle Diff Float. updated by a sucsessfull call to turret_validate_target
+float tvt_thadf;
+/// Turret Angle Diff Float. updated by a sucsessfull call to turret_validate_target
+float tvt_tadf;
+/// Distance. updated by a sucsessfull call to turret_validate_target
+float tvt_dist;
+
+/// updates aim org, shot org, shot dir and enemy org for selected turret
+void turret_do_updates(entity e_turret);
+.vector tur_aimorg_updated;
+.vector tur_shotorg_updated;
+.vector tur_shotdir_updated;
+
+#endif // TTURRETS_ENABLED
Added: trunk/data/qcsrc/server/tturrets/system/turret_system_aimprocs.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/system/turret_system_aimprocs.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/system/turret_system_aimprocs.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,119 @@
+/*
+* Straight line, Dead-on (no prediction)
+* Usefull for "stupid turrets" or ones
+* that launch guided weapons and just need to apeer to
+* somewhat face (and/or track) the target.
+
+supports:
+TFL_AIM_NO
+*/
+vector turret_stdproc_aim_simple()
+{
+ float s_bu; // Solidity backup (for ground shooters)
+ vector aim_pos;
+
+ if (self.aim_flags & TFL_AIM_NO) return self.idle_aim;
+
+ aim_pos = self.enemy.origin;
+
+ // Target ground?
+ if (self.aim_flags & TFL_AIM_GROUND)
+ {
+ s_bu = self.enemy.solid;
+ self.enemy.solid = SOLID_NOT;
+ traceline(self.enemy.origin + '0 0 128',self.enemy.origin + '0 0 -99999',1,self.enemy);
+ self.enemy.solid = s_bu;
+ aim_pos = trace_endpos;
+ }
+
+ // This is where its at.
+ return aim_pos;
+}
+
+/*
+* Generic aim
+
+supports:
+TFL_AIM_NO
+TFL_AIM_GROUND
+TFL_AIM_LEAD
+TFL_AIM_SHOTTIMECOMPENSATE
+TFL_AIM_INFRONT
+TFL_AIM_BEHIND
+TFL_AIM_ZEASE
+
+not supported:
+TFL_AIM_BALISTIC
+*/
+vector turret_stdproc_aim_generic()
+{
+ vector pre_pos;
+
+ if (self.aim_flags == TFL_AIM_NO)
+ return self.idle_aim;
+
+ // 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
+ pre_pos += self.enemy.velocity * (self.tur_dist_enemy / self.shot_speed);
+ else if (self.turrcaps_flags & TFL_TURRCAPS_HITSCAN) // Hitscan gun, conpensate for frametime and posibly refire offset.
+ pre_pos += self.enemy.velocity * (frametime + min(max(self.attack_finished - time,0),self.ticrate*2));
+ else // No lead
+ pre_pos += self.enemy.velocity;
+
+ // Smooth out predict-Z?
+ if (self.aim_flags & TFL_AIM_ZEASE)
+ {
+ vector v;
+ v = real_origin(self.enemy);
+ //pre_pos_z = (pre_pos_z * 0.5) + (v_z * 0.5);
+ pre_pos_z = (pre_pos_z + v_z) * 0.5;
+ }
+
+ if (self.aim_flags & TFL_AIM_INFRONT) // Aim a bit in front of the target
+ pre_pos -= normalize(self.tur_aimorg_updated - pre_pos) * 32;
+
+ if (self.aim_flags & TFL_AIM_BEHIND) // Aim a bit behind the target
+ pre_pos += normalize(self.tur_aimorg_updated - pre_pos) * 32;
+
+
+ // This turret should hit the ground neer a target rather the do a direct hit
+ if (self.aim_flags & TFL_AIM_GROUND)
+ {
+ traceline(pre_pos + '0 0 512',pre_pos - '0 0 2048',1,self.enemy);
+ pre_pos = trace_endpos;
+ }
+
+ // self.tur_impacttime = vlen(pre_pos - self.origin) / self.shot_speed;
+
+ return pre_pos;
+}
+
+
+/*
+* Aim where it is
+supports:
+TFL_AIM_NO
+*/
+vector turret_stdproc_aim_rail()
+{
+ vector pre_pos;
+
+ if (self.aim_flags & TFL_AIM_NO)
+ return self.idle_aim;
+
+ pre_pos = real_origin(self.enemy);
+
+ self.tur_dist_toaimpos = vlen(self.enemy.origin - self.tur_aimorg_updated);
+
+ self.tur_impacttime = time;
+
+ return pre_pos;
+
+}
+
+
+
Added: trunk/data/qcsrc/server/tturrets/system/turret_system_damage.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/system/turret_system_damage.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/system/turret_system_damage.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,307 @@
+/*
+* Trow a turret gib
+*/
+void turret_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+ self.velocity += vforce;
+}
+void turret_gibs_precash()
+{
+ precache_model("models/turrets/base-gib1.md3");
+ precache_model("models/turrets/base-gib2.md3");
+ precache_model("models/turrets/base-gib3.md3");
+ precache_model("models/turrets/base-gib4.md3");
+
+ precache_model("models/turrets/head-gib1.md3");
+ precache_model("models/turrets/head-gib2.md3");
+ precache_model("models/turrets/head-gib3.md3");
+ precache_model("models/turrets/head-gib4.md3");
+}
+
+void turret_trowgib(
+ vector v_from, vector v_to, vector v_colormod,
+ string smodel,
+ float f_lifetime, float f_fadetime, float b_burn)
+{
+ local entity gib;
+ local entity burn;
+
+ gib = spawn();
+
+ gib.classname = "turret_gib";
+ setmodel(gib,smodel);
+ setorigin(gib,v_from);
+ SUB_SetFade(gib,time + f_lifetime,2);
+
+ gib.solid = SOLID_BBOX;
+
+ gib.movetype = MOVETYPE_BOUNCE;
+ gib.takedamage = DAMAGE_YES;
+ gib.event_damage = turret_gib_damage;
+ gib.health = -1;
+ gib.effects = EF_LOWPRECISION;
+ gib.flags = FL_NOTARGET;
+ gib.colormod = v_colormod;
+ gib.velocity = v_to;
+
+ if (b_burn)
+ {
+ burn = spawn();
+ burn.effects = EF_LOWPRECISION|EF_FLAME;
+ setattachment(burn,gib,"");
+ setorigin(burn,(gib.mins + gib.maxs) * 0.5);
+ SUB_SetFade(burn,time + (f_lifetime * 0.5) ,2);
+ }
+}
+
+void turret_gib_boom()
+{
+ entity gib;
+ float i;
+ string s;
+
+ for(i=1;i<5;i++)
+ {
+ gib = spawn();
+ gib.classname = "turret_gib";
+
+ s = strcat("models/turrets/head-gib",ftos(i));
+ s = strcat(s,".md3");
+ // bprint("s:",s,"\n");
+ setmodel(gib,s);
+
+ setorigin(gib,self.origin);
+
+ SUB_SetFade(gib,time + 5,2);
+
+ gib.solid = SOLID_BBOX;
+
+ gib.movetype = MOVETYPE_BOUNCE;
+ gib.gravity = 0.5;
+ gib.damageforcescale = 2;
+ gib.takedamage = DAMAGE_YES;
+ gib.event_damage = turret_gib_damage;
+ gib.health = -1;
+ gib.effects = EF_LOWPRECISION;
+ gib.flags = FL_NOTARGET;
+ gib.velocity = self.velocity + (randomvec() * 700);
+ gib.avelocity = randomvec() * 64;
+ }
+
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, 78);
+ WriteCoord (MSG_BROADCAST, self.origin_x);
+ WriteCoord (MSG_BROADCAST, self.origin_y);
+ WriteCoord (MSG_BROADCAST, self.origin_z);
+
+ remove(self);
+}
+
+void turret_trowgib2(
+ vector v_from, vector v_to, vector v_colormod,
+ entity e_mimic, float boomtime)
+{
+ entity gib;
+
+ gib = spawn();
+
+ gib.classname = "turret_gib";
+ setmodel(gib,e_mimic.model);
+ setorigin(gib,v_from);
+
+ gib.solid = SOLID_BBOX;
+
+ gib.movetype = MOVETYPE_BOUNCE;
+ gib.gravity = 0.75;
+ gib.damageforcescale = 2;
+ gib.takedamage = DAMAGE_YES;
+ gib.event_damage = turret_gib_damage;
+ gib.health = -1;
+ gib.effects = EF_LOWPRECISION;
+ gib.flags = FL_NOTARGET;
+ gib.colormod = v_colormod;
+ gib.velocity = v_to;
+ gib.avelocity = randomvec() * 32;
+ gib.think = turret_gib_boom;
+ gib.nextthink = boomtime;
+ gib.effects = EF_FLAME;
+
+
+}
+/*
+* Spawn a boom, trow fake bits arround
+* and hide the real ones.
+*/
+void turret_stdproc_die()
+{
+ vector org2;
+ vector t_dir;
+
+ // self.tur_active = 0;
+
+ self.deadflag = DEAD_DEAD;
+ self.tur_head.deadflag = self.deadflag;
+
+ sound (self, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM);
+ org2 = self.origin + '0 0 40';
+
+// Explotion grafix
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, 78);
+ WriteCoord (MSG_BROADCAST, org2_x);
+ WriteCoord (MSG_BROADCAST, org2_y);
+ WriteCoord (MSG_BROADCAST, org2_z);
+
+// Unsolidify and hide real parts
+ self.solid = SOLID_NOT;
+ self.tur_head.solid = self.solid;
+
+ self.alpha = -1;
+ self.tur_head.alpha = -1;
+
+ self.takedamage = DAMAGE_NO;
+ self.tur_head.takedamage = self.takedamage;
+
+ self.effects = 0;
+ self.tur_head.effects = self.effects;
+
+ self.health = 0;
+
+
+// Trow fake parts arround
+
+ // base
+ makevectors(self.angles);
+ if(random() > 0.5)
+ {
+ turret_trowgib(self.origin, '0 0 0','1 1 1',"models/turrets/base-gib2.md3",min(self.respawntime,20),1,1);
+ t_dir = (v_up * 700) + (randomvec() * 300);
+ turret_trowgib(self.origin, t_dir,'1 1 1',"models/turrets/base-gib3.md3",min(self.respawntime,10),1,1);
+ t_dir = (v_up * 700) + (randomvec() * 300);
+ turret_trowgib(self.origin, t_dir,'1 1 1',"models/turrets/base-gib4.md3",min(self.respawntime,10),1,1);
+ } else {
+ turret_trowgib(self.origin, '0 0 0','1 1 1',"models/turrets/base-gib1.md3",min(self.respawntime,20),1,1);
+ }
+
+ // Blow the top part up into the air
+ turret_trowgib2( self.origin + (v_up * 50),
+ v_up * 150 + randomvec() * 50,
+ '0.2 0.2 0.2',
+ self.tur_head,time + 0.5 + (random() * 0.5));
+
+
+// Go boom
+ RadiusDamage (self,self, min(self.ammo,50),min(self.ammo,50) * 0.25,250,world,min(self.ammo,50)*5,IT_ROCKET_LAUNCHER);
+
+// Setup respawn
+ self.nextthink = time + self.respawntime;
+ self.think = self.turret_spawnfunc;
+
+ if(self.call_diehook)
+ self.turret_diehook();
+
+}
+
+void turret_stdproc_respawn()
+{
+ // self.tur_active = 1;
+
+ // Make sure all parts belong to the same team since
+ // this function doubles as "teamchange" function.
+ self.tur_head.team = self.team;
+ if (self.team == COLOR_TEAM1) self.colormod = '1.4 0.8 0.8';
+ if (self.team == COLOR_TEAM2) self.colormod = '0.8 0.8 1.4';
+
+ self.deadflag = DEAD_NO;
+ self.tur_head.deadflag = self.deadflag;
+ self.effects = self.tur_head.effects = 0;
+
+ self.solid = SOLID_BBOX;
+ self.tur_head.solid = self.solid;
+
+ self.alpha = 1;
+ self.tur_head.alpha = self.alpha;
+
+ self.takedamage = DAMAGE_YES;
+ self.tur_head.takedamage = self.takedamage;
+
+ 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;
+
+ if(self.call_respwnhook)
+ self.turret_respawnhook();
+
+}
+
+/*
+* Standard damage proc.
+*/
+void turret_stdproc_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+ entity baseent;
+ // entity player;
+
+ if (self.health <= 0) return;
+
+ // Damage func is shared on all parts as standard, we need to know what the master entity of this turret is.
+ // if ((self.classname == "turret_head")||(self.classname == "turret_gun")||(self.classname == "turret_badge"))
+ if(self.owner)
+ baseent = self.owner;
+ else
+ baseent = self;
+
+ if (teamplay != 0)
+ {
+ if (self.team == attacker.team)
+ {
+ sprint(attacker,"Turret: Im on your team!\n");
+ } else {
+ /*
+ // This will get enoying fast...
+ FOR_EACH_PLAYER(player)
+ if(player.team == self.team)
+ sprint(player, "The enemy is attacking your base!");
+
+ */
+ }
+
+ }
+
+ baseent.health = baseent.health - damage;
+
+ // thorw head slightly off aim when hit?
+ if((self.classname == "turret_head") || (self.classname == "turret_gun"))
+ if(self.damage_flags & TFL_DMG_HEADSHAKE)
+ {
+ // makevectors(baseent.tur_head.v_angle);
+ baseent.tur_head.angles = baseent.tur_head.angles + randomvec() * damage;
+ }
+
+ if(self.turrcaps_flags & TFL_TURRCAPS_MOVE)
+ {
+ self.velocity = self.velocity + vforce;
+ }
+
+
+ // Start burning when we have 10% or less health left
+ if (self.health < (self.tur_health * 0.1))
+ self.effects = EF_FLAME;
+
+ if (self.health <= 0)
+ {
+ self = baseent;
+ baseent.turret_diefunc();
+ }
+}
+
+
Added: trunk/data/qcsrc/server/tturrets/system/turret_system_main.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/system/turret_system_main.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/system/turret_system_main.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,999 @@
+#define cvar_base "g_turrets_unit_"
+
+.float tur_lastscore;
+.string cvar_basename;
+
+string cvar_gets(string s_base,string s_add)
+{
+ return strcat(s_base,s_add);
+}
+
+.float turret_scale_damage;
+.float turret_scale_range;
+.float turret_scale_refire;
+.float turret_scale_ammo;
+.float turret_scale_aim;
+.float turret_scale_health;
+.float turret_scale_respawn;
+
+void load_unit_settings(entity ent,string unitname,float is_reload)
+{
+
+ string sbase;
+
+ if (ent == world)
+ return;
+
+ if (!ent.turret_scale_damage) ent.turret_scale_damage = 1;
+ if (!ent.turret_scale_range) ent.turret_scale_range = 1;
+ if (!ent.turret_scale_refire) ent.turret_scale_refire = 1;
+ if (!ent.turret_scale_ammo) ent.turret_scale_ammo = 1;
+ if (!ent.turret_scale_aim) ent.turret_scale_aim = 1;
+ if (!ent.turret_scale_health) ent.turret_scale_health = 1;
+ if (!ent.turret_scale_respawn) ent.turret_scale_respawn = 1;
+
+ sbase = strcat(cvar_base,unitname);
+ if (is_reload)
+ {
+ ent.enemy = world;
+ ent.tur_head.avelocity = '0 0 0';
+ ent.tur_head.angles = ent.angles;
+ }
+ ent.health = cvar(cvar_gets(sbase,"_health")) * ent.turret_scale_health;
+ ent.respawntime = cvar(cvar_gets(sbase,"_respawntime")) * ent.turret_scale_respawn;
+
+ ent.shot_dmg = cvar(cvar_gets(sbase,"_shot_dmg")) * ent.turret_scale_damage;
+ ent.shot_refire = cvar(cvar_gets(sbase,"_shot_refire")) * ent.turret_scale_refire;
+ ent.shot_radius = cvar(cvar_gets(sbase,"_shot_radius")) * ent.turret_scale_damage;
+ ent.shot_speed = cvar(cvar_gets(sbase,"_shot_speed"));
+ ent.shot_spread = cvar(cvar_gets(sbase,"_shot_spread"));
+ ent.shot_force = cvar(cvar_gets(sbase,"_shot_force")) * ent.turret_scale_damage;
+ ent.shot_volly = cvar(cvar_gets(sbase,"_shot_volly"));
+ ent.shot_volly_refire = cvar(cvar_gets(sbase,"_shot_volly_refire")) * ent.turret_scale_refire;
+
+ ent.target_range = cvar(cvar_gets(sbase,"_target_range")) * ent.turret_scale_range;
+ ent.target_range_min = cvar(cvar_gets(sbase,"_target_range_min")) * ent.turret_scale_range;
+ ent.target_range_fire = cvar(cvar_gets(sbase,"_target_range_fire")) * ent.turret_scale_range;
+ ent.target_range_optimal = cvar(cvar_gets(sbase,"_target_range_optimal")) * ent.turret_scale_range;
+
+ ent.target_select_rangebias = cvar(cvar_gets(sbase,"_target_select_rangebias"));
+ ent.target_select_samebias = cvar(cvar_gets(sbase,"_target_select_samebias"));
+ ent.target_select_anglebias = cvar(cvar_gets(sbase,"_target_select_anglebias"));
+ ent.target_select_playerbias = cvar(cvar_gets(sbase,"_target_select_playerbias"));
+
+ ent.ammo_max = cvar(cvar_gets(sbase,"_ammo_max")) * ent.turret_scale_ammo;
+ //ent.ammo = cvar(cvar_gets(sbase,"_ammo"));
+ ent.ammo_recharge = cvar(cvar_gets(sbase,"_ammo_recharge")) * ent.turret_scale_ammo;
+
+ ent.aim_firetolerance_dist = cvar(cvar_gets(sbase,"_aim_firetolerance_dist"));
+ ent.aim_firetolerance_angle = cvar(cvar_gets(sbase,"_aim_firetolerance_angle"));
+ ent.aim_speed = cvar(cvar_gets(sbase,"_aim_speed")) * ent.turret_scale_aim;
+ ent.aim_maxrot = cvar(cvar_gets(sbase,"_aim_maxrot"));
+ ent.aim_maxpitch = cvar(cvar_gets(sbase,"_aim_maxpitch"));
+
+ ent.track_type = cvar(cvar_gets(sbase,"_track_type"));
+ ent.track_accel_pitch = cvar(cvar_gets(sbase,"_track_accel_pitch"));
+ ent.track_accel_rot = cvar(cvar_gets(sbase,"_track_accel_rot"));
+ ent.track_blendrate = cvar(cvar_gets(sbase,"_track_blendrate"));
+}
+
+float turret_stdproc_true()
+{
+ return 1;
+}
+
+float turret_stdproc_false()
+{
+ return 0;
+}
+
+void turret_stdproc_nothing()
+{
+ return;
+}
+
+/**
+** updates enemy distances, preicted impact point/time
+** & aim<->predict impact distance.
+** Also translates shoot & aimorgs by current rotation.
+**/
+void turret_do_updates(entity e_turret)
+{
+ //vector trueaimpoint;
+
+ if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)
+ {
+ e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;
+ e_turret.angles_x = e_turret.angles_x * -1;
+ makevectors(e_turret.tur_head.angles + e_turret.angles);
+ e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;
+ e_turret.angles_x = e_turret.angles_x * -1;
+ }
+ else
+ {
+ e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;
+ makevectors(e_turret.tur_head.angles);
+ e_turret.tur_head.angles_x = e_turret.tur_head.angles_x * -1;
+ }
+
+ //traceline_hitcorpse(e_turret, e_turret.origin, e_turret.origin + v_forward * MAX_SHOT_DISTANCE, MOVE_NOMONSTERS, e_turret);
+ //trueaimpoint = trace_endpos;
+
+ e_turret.tur_shotorg_updated = e_turret.origin + v_forward * e_turret.tur_shotorg_x + v_right * e_turret.tur_shotorg_y + v_up * e_turret.tur_shotorg_z;
+ //e_turret.tur_shotdir_updated = normalize(trueaimpoint - e_turret.tur_shotorg_updated);
+ e_turret.tur_shotdir_updated = normalize((e_turret.tur_shotorg_updated + v_forward * 512) - e_turret.tur_shotorg_updated);
+ e_turret.tur_aimorg_updated = e_turret.origin + v_forward * e_turret.tur_aimorg_x + v_right * e_turret.tur_aimorg_y + v_up * e_turret.tur_aimorg_z;
+ //e_turret.tur_aimorg_updated = e_turret.tur_shotorg_updated;
+
+ e_turret.tur_dist_enemy = vlen(e_turret.tur_aimorg_updated - real_origin(e_turret.enemy));
+ //e_turret.tur_dist_enemy = vlen(e_turret.tur_aimpos - e_turret.tur_shotorg_updated);
+
+ traceline(e_turret.tur_aimorg_updated,e_turret.tur_aimorg_updated+(e_turret.tur_shotdir_updated * e_turret.tur_dist_enemy),TRUE,e_turret);
+
+ e_turret.tur_impactpoint = trace_endpos;
+ e_turret.tur_impactent = trace_ent;
+ e_turret.tur_impacttime = e_turret.tur_dist_enemy / e_turret.shot_speed;
+ e_turret.tur_dist_toaimpos = vlen(trace_endpos - e_turret.tur_aimpos);
+}
+
+/**
+** Handles head rotation according to
+** the units .track_type and .track_flags
+**/
+void turret_stdproc_track()
+{
+ vector wish_angle; // This is where we'd need to be
+ vector real_angle; // This is where we can go
+ float f_tmp;
+
+ if (self.track_flags == TFL_TRACK_NO)
+ return;
+
+ if (self.enemy == world)
+ {
+ if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)
+ wish_angle = self.idle_aim + self.angles;
+ else
+ wish_angle = self.idle_aim;
+ }
+ else
+ {
+ if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)
+ wish_angle = normalize(self.tur_aimpos - self.origin); // Find the direction
+ else
+ wish_angle = normalize(self.tur_aimpos - self.tur_head.origin); // Find the direction
+
+ wish_angle = vectoangles(wish_angle); // And make a angle
+ }
+
+ // Find the diffrence between where we currently aim and where we want to aim
+ if (self.turrcaps_flags & TFL_TURRCAPS_LINKED)
+ real_angle = wish_angle - (self.tur_head.angles + self.angles);
+ else
+ real_angle = wish_angle - self.tur_head.angles;
+
+ // Constrain it within +/- 360
+ if (real_angle_x < 0) real_angle_x += 360;
+ if (real_angle_x > 180) real_angle_x -= 360;
+
+ if (real_angle_y < 0) real_angle_y += 360;
+ if (real_angle_y > 180) real_angle_y -= 360;
+
+
+ if (self.track_type == TFL_TRACKTYPE_STEPMOTOR)
+ {
+ f_tmp = self.aim_speed * self.ticrate; // dgr/sec -> dgr/tic
+
+ // Limit turning speed
+ real_angle_x = bound((-1 * f_tmp),real_angle_x, f_tmp);
+ real_angle_y = bound((-1 * f_tmp),real_angle_y, f_tmp);
+
+ // Limit pich and rot.
+ if (self.track_flags & TFL_TRACK_PITCH)
+ self.tur_head.angles_x = bound((-1 * self.aim_maxpitch),self.tur_head.angles_x + real_angle_x,self.aim_maxpitch);
+
+ if (self.track_flags & TFL_TRACK_ROT)
+ self.tur_head.angles_y = bound((-1 * self.aim_maxrot),self.tur_head.angles_y + real_angle_y,self.aim_maxrot);
+
+ return;
+ }
+
+ if (self.track_type == TFL_TRACKTYPE_FLUIDPRECISE)
+ {
+ if (self.track_flags & TFL_TRACK_PITCH)
+ self.tur_head.avelocity_x = real_angle_x;
+
+ if (self.track_flags & TFL_TRACK_ROT)
+ self.tur_head.avelocity_y = real_angle_y;
+ }
+ else if (self.track_type == TFL_TRACKTYPE_FLUIDINERTIA)
+ {
+ f_tmp = self.aim_speed * self.ticrate;
+
+ real_angle_y = bound(self.aim_speed * -1,real_angle_y * self.track_accel_rot * f_tmp,self.aim_speed);
+ real_angle_x = bound(self.aim_speed * -1,real_angle_x * self.track_accel_pitch * f_tmp,self.aim_speed);
+ real_angle = (self.tur_head.avelocity * self.track_blendrate) + (real_angle * (1 - self.track_blendrate));
+
+ if (self.track_flags & TFL_TRACK_PITCH) self.tur_head.avelocity_x = real_angle_x;
+ if (self.track_flags & TFL_TRACK_ROT) self.tur_head.avelocity_y = real_angle_y;
+ self.tur_head.avelocity_z = real_angle_z;
+ }
+
+ // Limit pitch
+ if (self.track_flags & TFL_TRACK_PITCH)
+ {
+ if (self.tur_head.angles_x > self.aim_maxpitch)
+ {
+ self.tur_head.angles_x = self.aim_maxpitch;
+ self.tur_head.avelocity_x = 0;
+ }
+
+ if (self.tur_head.angles_x < (self.aim_maxpitch * -1))
+ {
+ self.tur_head.angles_x = (self.aim_maxpitch * -1);
+ self.tur_head.avelocity_x = 0;
+ }
+ }
+
+ // Limit rot
+ if (self.track_flags & TFL_TRACK_ROT)
+ if (self.tur_head.angles_y > self.aim_maxrot)
+ {
+ self.tur_head.angles_y = self.aim_maxrot;
+ self.tur_head.avelocity_y = 0;
+ }
+
+ if (self.track_flags & TFL_TRACK_ROT)
+ if (self.tur_head.angles_y < (self.aim_maxrot * -1))
+ {
+ self.tur_head.angles_y = (self.aim_maxrot * -1);
+ self.tur_head.avelocity_y = 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;
+
+ // Ready?
+ if (self.firecheck_flags & TFL_FIRECHECK_REFIRE)
+ if (self.attack_finished >= time) return 0;
+
+ //
+ if (self.firecheck_flags & TFL_FIRECHECK_DEAD)
+ if (self.enemy.deadflag != DEAD_NO) return 0;
+
+ // Plz stop killing the world!
+ if (self.firecheck_flags & TFL_FIRECHECK_WORLD)
+ if (self.enemy == world) return 0;
+
+ // Own ammo?
+ if (self.firecheck_flags & TFL_FIRECHECK_OWM_AMMO)
+ if (self.ammo < self.shot_dmg) return 0;
+
+ // Other's ammo? (carefull using this...)
+ if (self.firecheck_flags & TFL_FIRECHECK_OTHER_AMMO)
+ if (self.enemy.ammo >= self.enemy.ammo_max) return 0;
+
+ if (self.firecheck_flags & TFL_FIRECHECK_DISTANCES)
+ {
+ // Not close enougth?
+ if (self.tur_dist_enemy > self.target_range_fire) return 0;
+
+ // To close?
+ if (self.tur_dist_enemy < self.target_range_min) return 0;
+ }
+
+ // Try to avoid FF?
+ if (self.firecheck_flags & TFL_FIRECHECK_AFF)
+ if (self.tur_impactent.team == self.team) return 0;
+
+ // aim<->predicted impact
+ if (self.firecheck_flags & TFL_FIRECHECK_AIMDIST)
+ if (self.tur_dist_toaimpos > 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 +1))
+ return 0;
+ }
+
+ //if(self.tur_enemy_adist >= self.aim_firetolerance) return 0;
+
+
+ return 1;
+}
+
+/*
+ + TFL_TARGETSELECT_NO
+ + TFL_TARGETSELECT_LOS
+ + TFL_TARGETSELECT_PLAYERS
+ + TFL_TARGETSELECT_MISSILES
+ - TFL_TARGETSELECT_TRIGGERTARGET
+ + TFL_TARGETSELECT_ANGLELIMITS
+ + TFL_TARGETSELECT_RANGELIMTS
+ + TFL_TARGETSELECT_TEAMCHECK
+ - TFL_TARGETSELECT_NOBUILTIN
+ + TFL_TARGETSELECT_OWNTEAM
+*/
+
+/**
+** Evaluate a entity for target valitity based on validate_flags
+**/
+float turret_validate_target(entity e_turret,entity e_target,float validate_flags)
+{
+ vector v_tmp;
+
+ //if(!validate_flags & TFL_TARGETSELECT_NOBUILTIN)
+ // return -0.5;
+
+ if (!e_target)// == world)
+ return -1;
+
+ if (e_target.classname == "grapplinghook")
+ return - 1.5;
+
+ if (validate_flags & TFL_TARGETSELECT_NO)
+ return -2;
+
+ // If only this was used more..
+ if (e_target.flags & FL_NOTARGET)
+ return -3;
+
+ // Cant touch this
+ if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
+ return -4;
+
+ // player
+ if (e_target.flags & FL_CLIENT)
+ {
+ if (!(validate_flags & TFL_TARGETSELECT_PLAYERS))
+ return -5;
+
+ if (e_target.deadflag != DEAD_NO)
+ return -6;
+ }
+
+ // Missile
+ if (e_target.flags & FL_PROJECTILE)
+ {
+ if (!(validate_flags & TFL_TARGETSELECT_MISSILES))
+ return -7;
+ }
+
+ // Team check
+ if (validate_flags & TFL_TARGETSELECT_TEAMCHECK)
+ {
+ if (validate_flags & TFL_TARGETSELECT_OWNTEAM)
+ {
+ if (e_target.team != e_turret.team)
+ return -8;
+
+ if (e_turret.team != e_target.owner.team)
+ return -8.5;
+
+ }
+ else
+ {
+ if (e_target.team == e_turret.team)
+ return -9;
+
+ if (e_turret.team == e_target.owner.team)
+ return -9.5;
+ }
+ }
+
+ // Line of sight?
+ if (validate_flags & TFL_TARGETSELECT_LOS)
+ {
+ v_tmp = real_origin(e_target) + ((e_target.mins + e_target.maxs) * 0.5);
+ //v_tmp = e_target.origin;
+ traceline(e_turret.origin,v_tmp,0,e_turret);
+
+ if (e_turret.aim_firetolerance_dist < vlen(v_tmp - trace_endpos))
+ return -10;
+ }
+
+ // Can we even aim this thing? (anglecheck)
+ tvt_thadv = angleofs(e_turret.tur_head,e_target);
+ tvt_tadv = angleofs(e_turret,e_target);
+ tvt_thadf = vlen(tvt_thadv);
+ tvt_tadf = vlen(tvt_tadv);
+
+ if (validate_flags & TFL_TARGETSELECT_ANGLELIMITS)
+ {
+ if (fabs(tvt_tadv_x) > e_turret.aim_maxpitch)
+ return -11;
+
+ if (fabs(tvt_tadv_y) > e_turret.aim_maxrot)
+ return -12;
+ }
+
+ // 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 -13;
+
+ if (tvt_dist > e_turret.target_range)
+ return -14;
+ }
+
+#ifdef TURRET_DEBUG_TARGETSELECT
+ bprint("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
+ entity e_enemy; // currently best scoreing enemy
+
+ float score; // current target (e) score
+ float m_score; // current best target (e_enemy) score
+ float f;
+ // string s;
+ e = findradius(self.origin,self.target_range);
+
+ // Nothing to aim at.
+ if (!e) return world;
+
+ m_score = 0;
+
+ while (e)
+ {
+ f = turret_validate_target(self,e,self.target_select_flags);
+ //s = ftos(f);
+ //bprint(e.netname, " = ",s,"\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;
+ }
+
+ self.tur_lastscore = m_score;
+
+ //if (self.enemy != e_enemy)
+ //self.volly_counter = 0;
+
+ return e_enemy;
+}
+
+void turret_think()
+{
+ entity e;
+
+ self.nextthink = (time + self.ticrate);
+
+ if (cvar("g_turrets_reloadcvars") == 1)
+ {
+ e = nextent(world);
+ while (e)
+ {
+ if (e.tur_head != world)
+ {
+ load_unit_settings(e,e.cvar_basename,1);
+ self.turret_postthink();
+ }
+
+ e = nextent(e);
+ }
+
+ cvar_set("g_turrets_reloadcvars","0");
+ }
+
+#ifdef TURRET_DEBUG
+ if (self.tur_dbg_tmr1 < time)
+ {
+ if (self.enemy) paint_target (self.enemy,128,self.tur_dbg_rvec,0.9);
+ paint_target(self,256,self.tur_dbg_rvec,0.9);
+ self.tur_dbg_tmr1 = time + 1;
+ }
+#endif
+
+ //Do custom prethink, and bail if it fails.
+ if (!self.turret_prethink()) return;
+
+ // Handle ammo
+ if (self.ammo < self.ammo_max)
+ self.ammo = min(self.ammo + self.ammo_recharge,self.ammo_max);
+
+
+ if ((!self.tur_active) || (self.deadflag != DEAD_NO))
+ {
+ dprint("Warning: Inactive or dead turret running the think function!\n");
+ self.enemy = world;
+ self.turret_track();
+ return;
+ }
+
+ if (self.shoot_flags & TFL_SHOOT_HITALLVALID)
+ {
+
+ // Do a self.turret_fire for every valid target.
+ e = findradius(self.origin,self.target_range);
+
+ while (e)
+ {
+ if (turret_validate_target(self,e,self.target_validate_flags))
+ {
+ self.enemy = e;
+
+ turret_do_updates(self);
+
+ if ( self.turret_firecheckfunc() ) turret_fire();
+ }
+
+ e = e.chain;
+ }
+ self.enemy = world;
+
+ }
+ else
+ {
+ // Check if we have a vailid enemy, and get one if we dont.
+ if (self.emaster != world)
+ {
+ self.enemy = self.emaster.enemy;
+ }
+ else
+ {
+ // turret_do_updates(self);
+ if (turret_validate_target(self,self.enemy,self.target_validate_flags) <= 0)// || (self.cnt < time))
+ {
+ self.enemy = turret_select_target();
+ self.cnt = time + self.ticrate * 5;
+ }
+ }
+
+ // No target, just go to idle, do any custom stuff and bail.
+ if (self.enemy == world)
+ {
+ // Turn & pitch
+ self.turret_track();
+
+ // do any per-turret stuff
+ self.turret_postthink();
+
+ // And bail.
+ return;
+ }
+
+ // Update
+ turret_do_updates(self);
+
+ // Predict or whatnot
+ if (!self.aim_flags & TFL_AIM_NO)
+ self.tur_aimpos = self.turret_aim();
+
+ // Turn & pitch
+ if (!self.track_flags & TFL_TRACK_NO)
+ self.turret_track();
+
+ // Update
+ turret_do_updates(self);
+
+ // Fire?
+ if (self.turret_firecheckfunc() != 0)
+ turret_fire();
+ }
+
+ // do any per-turret stuff
+ self.turret_postthink();
+}
+
+void turret_fire()
+{
+ if (cvar("g_turrets_nofire") != 0) return;
+ if ((!self.tur_active) || (self.deadflag != DEAD_NO)) return;
+
+ self.turret_firefunc();
+
+ self.attack_finished = time + self.shot_refire;
+ self.ammo = self.ammo - self.shot_dmg;
+ self.volly_counter = self.volly_counter - 1;
+
+
+ if (self.volly_counter <= 0)
+ {
+ self.volly_counter = self.shot_volly;
+ if (self.shoot_flags & TFL_SHOOT_CLEARTARGET) self.enemy = world;
+
+ if (self.shot_volly > 1)
+ self.attack_finished = 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");
+}
+
+void turret_stdproc_use()
+{
+ // bprint("Used:",self.netname,"\n");
+ if (self.tur_active)
+ self.tur_active = 0;
+ else
+ self.tur_active = 1;
+
+}
+
+/*
+* Standard turret initialization. use this!
+* (unless you have a very good reason not to.)
+* Any special stuff like multiple cannon models should be done
+* after this is proc called.
+* if the return value is 0, the turret should be removed.
+*/
+float turret_stdproc_init (string cvar_base_name)
+{
+ // Are turrets allowed atm?
+ if (cvar("g_turrets") == 0) return 0;
+
+ // Better more then once then never.
+ turret_gibs_precash();
+
+ if (self.spawnflags & 2)
+ {
+ entity tb;
+ precache_model("models/turrets/terrainbase.md3");
+ tb = spawn();
+ setmodel(tb,"models/turrets/terrainbase.md3");
+ setorigin(tb,self.origin);
+ tb.solid = SOLID_BBOX;
+ makestatic(tb);
+ }
+
+ self.cvar_basename = cvar_base_name;
+ load_unit_settings(self,self.cvar_basename,0);
+
+ // Group all turrets into the same team if in non teamplaymode, so they dont try to kill eachother.
+ if (cvar("g_assult") != 0)
+ {
+ if (!self.team)
+ self.team = 14; // Assume turrets are on the defending side if not explicitly set otehrwize
+ }
+ else
+ if ((!teamplay) || (!self.team))
+ self.team = MAX_SHOT_DISTANCE;
+
+
+ /*
+ * Try to guess some reasonaly defaults
+ * for missing params and do sanety checks
+ * thise checks could produce some "interesting" results
+ * if it hits a glitch in my logic :P so try to set as mutch
+ * as possible beforehand.
+ */
+ if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
+ {
+ // Support units generaly dont need to have a high speed ai-loop
+ if (!self.ticrate) self.ticrate = 0.25; // Speed of this turrets AI loop
+ }
+ else
+ {
+ if (!self.ticrate) self.ticrate = 0.1; // Speed of this turrets AI loop
+ }
+
+ self.ticrate = bound(0.01,self.ticrate,60); // keep it sane plz
+
+// General stuff
+ if (self.netname == "") self.netname = "turret";
+
+ if (!self.respawntime) self.respawntime = 60;
+ self.respawntime = max(-1,self.respawntime);
+
+ if (!self.health) self.health = 1000;
+ self.tur_health = max(1,self.health);
+
+ if (!self.turrcaps_flags) self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
+
+ if (!self.damage_flags) self.damage_flags = TFL_DMG_YES | TFL_DMG_RETALIATE | TFL_DMG_AIMSHAKE;
+
+// Shot stuff.
+ if (!self.shot_refire) self.shot_refire = 1;
+ self.shot_refire = bound(0.01,self.shot_refire,9999);
+
+ if (!self.shot_dmg) self.shot_dmg = self.shot_refire * 50;
+ self.shot_dmg = max(1,self.shot_dmg);
+
+ if (!self.shot_radius) self.shot_radius = self.shot_dmg * 0.5;
+ self.shot_radius = max(1,self.shot_radius);
+
+ if (!self.shot_speed) self.shot_speed = 2500;
+ self.shot_speed = max(1,self.shot_speed);
+
+ if (!self.shot_spread) self.shot_spread = 0.0125;
+ self.shot_spread = bound(0.0001,self.shot_spread,500);
+
+ if (!self.shot_force) self.shot_force = self.shot_dmg * 0.5 + self.shot_radius * 0.5;
+ self.shot_force = bound(0.001,self.shot_force,MAX_SHOT_DISTANCE * 0.5);
+
+ if (!self.shot_volly) self.shot_volly = 1;
+ self.shot_volly = bound(1,self.shot_volly,floor(self.ammo_max / self.shot_dmg));
+
+ if (!self.shot_volly_refire) self.shot_volly_refire = self.shot_refire * self.shot_volly;
+ self.shot_volly_refire = bound(self.shot_refire,self.shot_volly_refire,60);
+
+ if (!self.firecheck_flags)
+ self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES |
+ TFL_FIRECHECK_LOS | TFL_FIRECHECK_AIMDIST | TFL_FIRECHECK_TEAMCECK |
+ TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_WORLD;
+
+// Range stuff.
+ if (!self.target_range) self.target_range = self.shot_speed * 0.5;
+ self.target_range = bound(0,self.target_range,MAX_SHOT_DISTANCE);
+
+ if (!self.target_range_min) self.target_range_min = self.shot_radius * 2;
+ self.target_range_min = bound(0,self.target_range_min,MAX_SHOT_DISTANCE);
+
+ if (!self.target_range_fire) self.target_range_fire = self.target_range * 0.8;
+ self.target_range_fire = bound(0,self.target_range_fire,MAX_SHOT_DISTANCE);
+
+ if (!self.target_range_optimal) self.target_range_optimal = self.target_range_fire * 0.5;
+ self.target_range_optimal = bound(0,self.target_range_optimal,MAX_SHOT_DISTANCE);
+
+
+// Aim stuff.
+ if (!self.aim_maxrot) self.aim_maxrot = 45;
+ self.aim_maxrot = bound(0,self.aim_maxrot,361);
+
+ if (!self.aim_maxpitch) self.aim_maxpitch = 20;
+ self.aim_maxpitch = bound(0,self.aim_maxpitch,90);
+
+ if (!self.aim_speed) self.aim_speed = 36;
+ self.aim_speed = bound(0.1,self.aim_speed, 1000);
+
+ if (!self.aim_firetolerance_dist) self.aim_firetolerance_dist = 5 + (self.shot_radius * 2);
+ self.aim_firetolerance_dist = bound(0.1,self.aim_firetolerance_dist,MAX_SHOT_DISTANCE);
+
+ if (!self.aim_firetolerance_angle) self.aim_firetolerance_angle = 10;
+ self.aim_firetolerance_angle = bound(0.1,self.aim_firetolerance_angle,360);
+
+ if (!self.aim_flags) self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZEASE;
+
+ // Sill the most tested (and aim-effective)
+ if (!self.track_type) self.track_type = TFL_TRACKTYPE_STEPMOTOR;
+
+ if (self.track_type != TFL_TRACKTYPE_STEPMOTOR)
+ {
+ // Fluid / ineria mode. Looks mutch nicer, bit experimental &
+ // Can inmapt aim preformance alot.
+ // needs a bit diffrent aimspeed
+ if (!self.aim_speed) self.aim_speed = 180;
+ self.aim_speed = bound(0.1,self.aim_speed, 1000);
+
+ if (!self.track_accel_pitch) self.track_accel_pitch = 0.75;
+ if (!self.track_accel_rot) self.track_accel_rot = 0.75;
+ if (!self.track_blendrate) self.track_blendrate = 0.35;
+ }
+
+ if (!self.track_flags) self.track_flags = TFL_TRACK_PITCH | TFL_TRACK_ROT;
+
+
+// Target selection stuff.
+ if (!self.target_select_rangebias) self.target_select_rangebias = 1;
+ self.target_select_rangebias = bound(-10,self.target_select_rangebias,10);
+
+ if (!self.target_select_samebias) self.target_select_samebias = 1;
+ self.target_select_samebias = bound(-10,self.target_select_samebias,10);
+
+ if (!self.target_select_anglebias) self.target_select_anglebias = 1;
+ self.target_select_anglebias = bound(-10,self.target_select_anglebias,10);
+
+ if (!self.target_select_missilebias) self.target_select_missilebias = -10;
+ self.target_select_missilebias = bound(-10,self.target_select_missilebias,10);
+ self.target_select_playerbias = bound(-10,self.target_select_playerbias,10);
+
+ if (!self.target_select_flags)
+ if (self.turrcaps_flags & TFL_TURRCAPS_MISSILEKILL)
+ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_MISSILES |
+ TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS;
+ else
+ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS |
+ TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_ANGLELIMITS;
+
+ //if(!self.target_validate_flags)
+ self.target_validate_flags = self.target_select_flags;
+
+
+// Ammo stuff
+ if (!self.ammo_max) self.ammo_max = self.shot_dmg * 10;
+ self.ammo_max = max(self.shot_dmg,self.ammo_max);
+
+ if (!self.ammo) self.ammo = self.shot_dmg * 5;
+ self.ammo = bound(0,self.ammo,self.ammo_max);
+
+ if (!self.ammo_recharge) self.ammo_recharge = self.shot_dmg / 2;
+ self.ammo_recharge = max(0,self.ammo_recharge);
+
+ // Convert the recharge from X per sec to X per ticrate
+ self.ammo_recharge = self.ammo_recharge * self.ticrate;
+
+ if (!self.ammo_flags) self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
+
+// Offsets & origins
+ if (!self.tur_aimorg) self.tur_aimorg = '0 0 50';
+ if (!self.tur_shotorg) self.tur_shotorg = '50 0 50';
+
+// End of default & sanety checks, start building the turret.
+
+// Spawn extra bits
+ self.tur_head = spawn();
+
+ self.tur_head.netname = self.tur_head.classname = "turret_head";
+ self.tur_head.team = self.team;
+
+ //Slave mode?
+ if (self.master_nameof != "")
+ {
+ self.emaster = find(world, master_name, self.master_name);
+ if (self.emaster == world)
+ {
+ self.master_nameof = "";
+ dprint("Turret has bad invalid!\n");
+ }
+ }
+
+ // Defend mode?
+ if (self.target != "")
+ {
+ self.tur_defend = find(world, targetname, self.target);
+ if (self.tur_defend == world)
+ {
+ self.target = "";
+ dprint("Turret has invalid defendpoint!\n");
+ }
+ }
+
+// Claim ownership
+ self.tur_head.owner = self;
+
+// Put pices in place
+
+ if (!(self.turrcaps_flags & TFL_TURRCAPS_LINKED))
+ setorigin(self.tur_head,self.origin);
+
+ // In target defense mode, aim on the spot to defens when idle.
+ if (self.tur_defend)
+ self.idle_aim = self.tur_head.angles + angleofs(self.tur_head,self.tur_defend);
+ else
+ self.idle_aim = self.angles;
+
+ if (!(self.turrcaps_flags & TFL_TURRCAPS_LINKED))
+ self.tur_head.angles = self.idle_aim;
+
+ if (!self.health) self.health = 150;
+ self.tur_health = self.health;
+
+ //Solid bbox for preformance reasons
+ self.solid = SOLID_BBOX;
+ self.tur_head.solid = SOLID_BBOX;
+
+ self.takedamage = DAMAGE_AIM;
+ self.tur_head.takedamage = DAMAGE_AIM;
+
+ self.movetype = MOVETYPE_NOCLIP;
+ self.tur_head.movetype = MOVETYPE_NOCLIP;
+
+ // Team colouring?track
+ if (self.team == COLOR_TEAM1) self.colormod = '1.4 0.8 0.8';
+ if (self.team == COLOR_TEAM2) self.colormod = '0.8 0.8 1.4';
+
+ // Attach stdprocs. override when and what needed
+ if (self.turrcaps_flags & TFL_TURRCAPS_SUPPORT)
+ {
+ self.turret_prethink = turret_stdproc_true;
+ self.turret_score_target = turret_stdproc_targetscore_support;
+ self.turret_aim = turret_stdproc_aim_generic;
+ self.turret_track = turret_stdproc_track;
+ self.turret_firecheckfunc = turret_stdproc_firecheck;
+ self.turret_firefunc = turret_stdproc_fire;
+ self.turret_postthink = turret_stdproc_nothing;
+
+ self.turret_damagefunc = turret_stdproc_damage;
+ self.event_damage = turret_stdproc_damage;
+ self.tur_head.event_damage = turret_stdproc_damage;
+
+ self.turret_diefunc = turret_stdproc_die;
+ self.turret_spawnfunc = turret_stdproc_respawn;
+
+ }
+ else
+ {
+
+ self.turret_prethink = turret_stdproc_true;
+ self.turret_score_target = turret_stdproc_targetscore_generic;
+
+ if (self.aim_flags & TFL_AIM_SIMPLE)
+ self.turret_aim = turret_stdproc_aim_simple;
+ else
+ self.turret_aim = turret_stdproc_aim_generic;
+
+ self.turret_track = turret_stdproc_track;
+ self.turret_firecheckfunc = turret_stdproc_firecheck;
+ self.turret_firefunc = turret_stdproc_fire;
+ self.turret_postthink = turret_stdproc_nothing;
+
+ self.turret_damagefunc = turret_stdproc_damage;
+ self.event_damage = turret_stdproc_damage;
+ self.tur_head.event_damage = turret_stdproc_damage;
+
+ self.turret_diefunc = turret_stdproc_die;
+ self.turret_spawnfunc = turret_stdproc_respawn;
+ self.turret_addtarget = turret_stdproc_false;
+ }
+
+ self.use = turret_stdproc_use;
+
+ // Initiate the main AI loop
+ self.think = turret_think;
+ self.nextthink = time + self.ticrate;
+
+ self.tur_head.team = self.team;
+ self.view_ofs = '0 0 0';
+
+#ifdef TURRET_DEBUG
+ self.tur_dbg_start = self.nextthink;
+ while (vlen(self.tur_dbg_rvec) < 2)
+ self.tur_dbg_rvec = randomvec() * 4;
+
+ self.tur_dbg_rvec_x = fabs(self.tur_dbg_rvec_x);
+ self.tur_dbg_rvec_y = fabs(self.tur_dbg_rvec_y);
+ self.tur_dbg_rvec_z = fabs(self.tur_dbg_rvec_z);
+#endif
+
+ // Its all good.
+ self.classname = "turret_main";
+
+ self.tur_active = 1;
+
+ return 1;
+}
+
+
Added: trunk/data/qcsrc/server/tturrets/system/turret_system_misc.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/system/turret_system_misc.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/system/turret_system_misc.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,123 @@
+//--// Some support routines //--//
+
+// Get real origin
+vector real_origin(entity ent)
+{
+ entity e;
+ vector v;
+
+ e = ent.tag_entity;
+ while(e)
+ {
+ // v = v + e.origin;
+ v = v + ((e.absmin + e.absmax) * 0.5);
+ e = e.tag_entity;
+ }
+ //v = v + ent.origin;
+ v = v + ((ent.absmin + ent.absmax) * 0.5);
+ return v;
+
+}
+
+// Plug this into wherever precashing is done.
+void g_turrets_common_precash()
+{
+ precache_model ("models/turrets/c512.md3");
+}
+
+/*
+* Paint a v_color colord circle on target onwho
+* that fades away over f_time
+*/
+void paint_target(entity onwho, float f_size, vector v_color, float f_time)
+{
+ entity e;
+
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ setsize(e, '0 0 0', '0 0 0');
+ ///setattachment(e,onwho,"");
+ setorigin(e,onwho.origin + '0 0 1');
+ e.alpha = 0.15;
+ e.movetype = MOVETYPE_FLY;
+
+ e.velocity = (v_color * 32); // + '0 0 1' * 64;
+
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+
+void paint_target2(entity onwho, float f_size, vector v_color, float f_time)
+{
+ entity e;
+
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ setsize(e, '0 0 0', '0 0 0');
+
+ setorigin(e,onwho.origin + '0 0 1');
+ e.alpha = 0.15;
+ e.movetype = MOVETYPE_FLY;
+
+ e.velocity = (v_color * 32); // + '0 0 1' * 64;
+ e.avelocity_x = -128;
+
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+
+void paint_target3(vector where, float f_size, vector v_color, float f_time)
+{
+ entity e;
+ e = spawn();
+ setmodel(e, "models/turrets/c512.md3"); // precision set above
+ e.scale = (f_size/512);
+ setsize(e, '0 0 0', '0 0 0');
+ setorigin(e,where+ '0 0 1');
+ e.movetype = MOVETYPE_NONE;
+ e.velocity = '0 0 0';
+ e.colormod = v_color;
+ SUB_SetFade(e,time,f_time);
+}
+
+/*
+* Return the angle between two enteties
+*/
+vector angleofs(entity from, entity to)
+{
+ vector v_res;
+
+ // makevectors(from.angles);
+ v_res = normalize(to.origin - from.origin);
+ v_res = vectoangles(v_res);
+ v_res = v_res - from.angles;
+
+ if (v_res_x < 0) v_res_x += 360;
+ if (v_res_x > 180) v_res_x -= 360;
+
+ if (v_res_y < 0) v_res_y += 360;
+ if (v_res_y > 180) v_res_y -= 360;
+
+ return v_res;
+}
+
+vector angleofs2(entity from, vector to)
+{
+ vector v_res;
+
+ // makevectors(from.angles);
+ v_res = normalize(to - from.origin);
+ v_res = vectoangles(v_res);
+ v_res = v_res - from.angles;
+
+ if (v_res_x < 0) v_res_x += 360;
+ if (v_res_x > 180) v_res_x -= 360;
+
+ if (v_res_y < 0) v_res_y += 360;
+ if (v_res_y > 180) v_res_y -= 360;
+
+ return v_res;
+}
+
Added: trunk/data/qcsrc/server/tturrets/system/turret_system_scoreprocs.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/system/turret_system_scoreprocs.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/system/turret_system_scoreprocs.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,163 @@
+/*
+.float target_select_flags; /// target selection flags
+float TFL_TARGETSELECT_NO = 1; /// Dont select a target on its own.
+float TFL_TARGETSELECT_LOS = 2; /// Need line of sight
+float TFL_TARGETSELECT_PLAYERS = 4; /// Players are valid targets
+float TFL_TARGETSELECT_MISSILES = 8; /// Missiles are valid targets
+float TFL_TARGETSELECT_TRIGGERTARGET = 16; /// Responds to turret_trigger_target events
+float TFL_TARGETSELECT_ANGLELIMITS = 32; /// Angular limitations of turret head limits target selection
+float TFL_TARGETSELECT_RANGELIMTS = 64; /// Range limits apply in targetselection
+float TFL_TARGETSELECT_TEAMCHECK = 128; /// Consider team own <-> targets team
+float TFL_TARGETSELECT_NOBUILTIN = 256; /// Cant select targets on its own. needs to be triggerd or slaved.
+float TFL_TARGETSELECT_OWNTEAM = 512;
+*/
+
+float turret_stdproc_targetscore_support(entity e_turret,entity e_target)
+{
+ float score; // Total score
+ float s_score,d_score;
+
+ if (e_turret.enemy == e_target) s_score = 1;
+
+ d_score = min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist);
+
+ score = (d_score * e_turret.target_select_rangebias) +
+ (s_score * e_turret.target_select_samebias);
+
+ return score;
+}
+
+/*
+* Generic bias aware score system.
+*/
+float turret_stdproc_targetscore_generic(entity e_turret,entity e_target)
+{
+ //vector v_tmp;
+ float d_dist; // Defendmode Distance
+
+ float score; // Total score
+ float d_score; // Distance score
+ //float da_score; // Distance from aimpoint score
+ float a_score; // Angular score
+ float s_score; // samescore (same target as last time)
+ float m_score; // missile score
+ float p_score; // player score
+
+ float ikr; // ideal kill range
+
+ if(!e_target) return 0;
+
+ if (e_target == e_turret.enemy) s_score = 1;
+
+ if (e_turret.tur_defend)
+ {
+ d_dist = vlen(real_origin(e_target) - e_turret.tur_defend.origin);
+ ikr = vlen(e_turret.origin - e_turret.tur_defend.origin);
+ d_score = 1 - d_dist / e_turret.target_range;
+ }
+ else
+ {
+ // Make a normlized value base on the targets distance from our optimal killzone
+ ikr = e_turret.target_range_optimal;
+ d_score = min(ikr,tvt_dist) / max(ikr,tvt_dist);
+ }
+
+ /*
+ // Determine the maximum time it could take this turrent to aim at someting.
+ max_aim_delay = (max(e_turret.aim_maxrot,e_turret.aim_maxpitch) / e_turret.aim_speed * 2);
+
+ // Find out how long it would take to aim at this taget.
+ aim_delay = (thadf+0.01) / e_turret.aim_speed;
+
+ // Turn this info into a normalized value.
+ aim_delay = (min(max_aim_delay,aim_delay) / max_aim_delay);
+ a_score = 1 - aim_delay;
+ */
+
+ a_score = 1 - (tvt_thadf / max(e_turret.aim_maxrot,e_turret.aim_maxpitch));
+
+ if ((e_turret.target_select_missilebias > 0) && (e_target.flags & FL_PROJECTILE))
+ m_score = 1;
+
+ if ((e_turret.target_select_playerbias > 0) && (e_target.flags & FL_CLIENT))
+ p_score = 1;
+
+ d_score = max(d_score,0);
+ s_score = max(s_score,0);
+ a_score = max(a_score,0);
+ m_score = max(m_score,0);
+ p_score = max(p_score,0);
+
+ score = (d_score * e_turret.target_select_rangebias) +
+ (s_score * e_turret.target_select_samebias) +
+ (a_score * e_turret.target_select_anglebias) +
+ (m_score * e_turret.target_select_missilebias) +
+ (p_score * e_turret.target_select_playerbias);
+
+ if(e_turret.target_range_fire < vlen(e_turret.tur_shotorg_updated - real_origin(e_target)))
+ score *= 0.1;
+
+#ifdef TURRET_DEBUG
+ string sd,sv,sa,sm,sp,ss;
+ string sdt,svt,sat,smt,spt;
+
+ sd = ftos(d_score);
+ d_score *= e_turret.target_select_rangebias;
+ sdt = ftos(d_score);
+
+ //sv = ftos(v_score);
+ //v_score *= e_turret.target_select_samebias;
+ //svt = ftos(v_score);
+
+ sa = ftos(a_score);
+ a_score *= e_turret.target_select_anglebias;
+ sat = ftos(a_score);
+
+ sm = ftos(m_score);
+ m_score *= e_turret.target_select_missilebias;
+ smt = ftos(m_score);
+
+ sp = ftos(p_score);
+ p_score *= e_turret.target_select_playerbias;
+ spt = ftos(p_score);
+
+
+ ss = ftos(score);
+ bprint("^3Target scores^7 \[ ",e_turret.netname, " \] ^3for^7 \[ ", e_target.netname," \]\n");
+ bprint("^5Range:\[ ",sd, " \]^2+bias:\[ ",sdt," \]\n");
+ //bprint("^5Volly:\[ ",sv, " \]^2+bias:\[ ",svt," \]\n");
+ bprint("^5Angle:\[ ",sa, " \]^2+bias:\[ ",sat," \]\n");
+ bprint("^5Missile:\[ ",sm," \]^2+bias:\[ ",smt," \]\n");
+ bprint("^5Player:\[ ",sp, " \]^2+bias:\[ ",spt," \]\n");
+ bprint("^3Total (w/bias):\[^1",ss,"\]\n");
+
+#endif
+
+ return score;
+}
+
+float turret_stdproc_targetscore_close(entity e_turret,entity e_target)
+{
+ return 1 - (tvt_dist / e_turret.target_range);
+}
+
+float turret_stdproc_targetscore_far (entity e_turret,entity e_target)
+{
+ return tvt_dist / e_turret.target_range;
+}
+
+float turret_stdproc_targetscore_optimal(entity e_turret,entity e_target)
+{
+ return min(e_turret.target_range_optimal,tvt_dist) / max(e_turret.target_range_optimal,tvt_dist);
+}
+
+float turret_stdproc_score_angular(entity e_turret,entity e_target)
+{
+ return 1 - (tvt_thadf / e_turret.aim_maxrot);
+}
+
+float turret_stdproc_targetscore_defend(entity e_turret,entity e_target)
+{
+ return 0;
+ //min(e_target.origin,e_turret.tur_defend.origin) / max(e_target.origin,e_turret.tur_defend.origin);
+}
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_common.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_common.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_common.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,116 @@
+float turret_tag_setup(float linked)
+{
+ vector v;
+ float f;
+
+ // Laters dooz
+ if(linked)
+ return 0;
+
+ f = gettagindex(self,"tag_head");
+ v = gettaginfo(self,f);
+ v = v + self.origin;
+ setorigin(self.tur_head,v);
+
+ f = gettagindex(self.tur_head,"tag_fire");
+ v = gettaginfo(self.tur_head,f) + (self.tur_head.origin - self.origin);
+ v_y *= -1;
+ self.tur_shotorg = v;
+
+ f = gettagindex(self.tur_head,"tag_aim");
+ v = gettaginfo(self.tur_head,f) + (self.tur_head.origin - self.origin);
+ self.tur_aimorg = v;
+
+ return 1;
+}
+
+float turret_tag_fire_update()
+{
+ vector v;
+ float f;
+
+ f = gettagindex(self.tur_head,"tag_fire");
+ v = gettaginfo(self.tur_head,f) + (self.tur_head.origin - self.origin);
+ v_y *= -1;
+ self.tur_shotorg = v;
+
+ f = gettagindex(self.tur_head,"tag_aim");
+ v = gettaginfo(self.tur_head,f) + (self.tur_head.origin - self.origin);
+ self.tur_aimorg = v;
+
+ return 1;
+}
+
+void FireImoBeam (vector start,vector end,vector smin,vector smax,
+ float bforce,float f_dmg,float f_velfactor, float deathtype)
+
+{
+ local vector hitloc, force, endpoint, dir;
+ local entity ent;
+
+ dir = normalize(end - start);
+ force = dir * bforce;
+
+ // go a little bit into the wall because we need to hit this wall later
+ end = end + dir;
+
+ // trace multiple times until we hit a wall, each obstacle will be made unsolid.
+ // note down which entities were hit so we can damage them later
+ while (1)
+ {
+ tracebox(start, smin, smax, end, FALSE, self);
+
+ // if it is world we can't hurt it so stop now
+ if (trace_ent == world || trace_fraction == 1)
+ break;
+
+ if (trace_ent.solid == SOLID_BSP)
+ break;
+
+ // make the entity non-solid so we can hit the next one
+ trace_ent.railgunhit = TRUE;
+ trace_ent.railgunhitloc = end;
+ trace_ent.railgunhitsolidbackup = trace_ent.solid;
+
+ // stop if this is a wall
+
+
+ // make the entity non-solid
+ trace_ent.solid = SOLID_NOT;
+ }
+
+ endpoint = trace_endpos;
+
+ // find all the entities the railgun hit and restore their solid state
+ ent = findfloat(world, railgunhit, TRUE);
+ while (ent)
+ {
+ // restore their solid type
+ ent.solid = ent.railgunhitsolidbackup;
+ ent = findfloat(ent, railgunhit, TRUE);
+ }
+
+ // find all the entities the railgun hit and hurt them
+ ent = findfloat(world, railgunhit, TRUE);
+ while (ent)
+ {
+ // get the details we need to call the damage function
+ hitloc = ent.railgunhitloc;
+ ent.railgunhitloc = '0 0 0';
+ ent.railgunhitsolidbackup = SOLID_NOT;
+ ent.railgunhit = FALSE;
+
+ // apply the damage
+ if (ent.takedamage)
+ {
+ Damage (ent, self, self, f_dmg, deathtype, hitloc, force);
+ ent.velocity = ent.velocity * f_velfactor;
+ //ent.alpha = 0.25 + random() * 0.75;
+ }
+
+ // advance to the next entity
+ ent = findfloat(ent, railgunhit, TRUE);
+ }
+ trace_endpos = endpoint;
+}
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_flac.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_flac.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_flac.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,108 @@
+void spawnfunc_turret_flac();
+void turret_flac_dinit();
+void turret_flac_attack();
+void turret_flac_projectile_explode();
+
+void turret_flac_attack()
+{
+ local entity proj;
+
+ turret_tag_fire_update();
+
+ sound (self, CHAN_BODY, "weapons/hagar_fire.wav", 1, ATTN_NORM);
+ proj = spawn ();
+ setorigin(proj, self.tur_shotorg_updated);
+ setmodel(proj, "models/turrets/pd_proj.md3");
+ setsize(proj, '0 0 0', '0 0 0');
+ proj.classname = "turret_fire";
+ proj.owner = self;
+ proj.bot_dodge = TRUE;
+ proj.bot_dodgerating = self.shot_dmg;
+ proj.solid = SOLID_NOT;
+ proj.movetype = MOVETYPE_FLYMISSILE;
+ proj.flags = FL_PROJECTILE;
+ proj.effects = EF_LOWPRECISION;
+ proj.takedamage = DAMAGE_YES;
+ proj.health = 10;
+ proj.velocity = (self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
+ proj.angles = vectoangles(proj.velocity);
+ proj.touch = turret_flac_projectile_explode;
+ proj.think = turret_flac_projectile_explode;
+ proj.nextthink = time + (vlen(self.tur_aimpos - self.tur_shotorg_updated) / self.shot_speed) + (random() * self.shot_spread);
+
+ self.tur_head.frame = self.tur_head.frame + 1;
+ if(self.tur_head.frame >= 4) self.tur_head.frame = 0;
+
+}
+
+void turret_flac_projectile_explode()
+{
+ //vector org2;
+ float d;
+
+ float b;
+ //org2 = findbetterlocation (self.origin, 12);
+ te_explosion (self.origin);
+
+ b = crandom();
+ if (b<-0.7)
+ sound (self, CHAN_BODY, "weapons/hagexp1.wav", 1, ATTN_NORM);
+ else if (b<0.4)
+ sound (self, CHAN_BODY, "weapons/hagexp2.wav", 1, ATTN_NORM);
+ else if (b<1)
+ sound (self, CHAN_BODY, "weapons/hagexp3.wav", 1, ATTN_NORM);
+
+
+ self.event_damage = SUB_Null;
+
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+
+#ifdef TURRET_DEBUG
+ self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg;
+ self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#endif
+
+ remove (self);
+}
+
+
+void turret_flac_dinit()
+{
+ if (self.netname == "") self.netname = "FLAC Cannon";
+
+
+ self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_MISSILEKILL;
+ self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE;
+
+
+
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/flac.md3");
+ precache_model("models/turrets/pd_proj.md3");
+
+ if (turret_stdproc_init("flac_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/flac.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_flac_attack;
+
+}
+/*QUAKED turret_flac (0 .5 .8) ?
+*/
+
+void spawnfunc_turret_flac()
+{
+ self.think = turret_flac_dinit;
+ self.nextthink = time + 0.5;
+}
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_fusionreactor.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_fusionreactor.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_fusionreactor.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,82 @@
+void spawnfunc_turret_fusionreactor();
+void turret_fusionreactor_dinit();
+void turret_fusionreactor_fire();
+
+float turret_fusionreactor_firecheck()
+{
+ if (self.enemy == world) return 0;
+
+ if (!self.enemy.ammo_flags & TFL_AMMO_RECIVE) return 0;
+ if (!self.enemy.ammo_flags & TFL_AMMO_ENERGY) return 0;
+
+ if (self.ammo < self.shot_dmg) return 0;
+ if (self.enemy.ammo >= self.enemy.ammo_max) return 0;
+ if (self.tur_dist_enemy > self.target_range_fire) return 0;
+ if (self.tur_dist_enemy < self.target_range_min) return 0;
+
+ return 1;
+}
+
+
+void turret_fusionreactor_fire()
+{
+ self.enemy.ammo = min(self.enemy.ammo + self.shot_dmg,self.enemy.ammo_max);
+ //te_lightning1(world,self.origin,self.enemy.origin);
+}
+
+void turret_fusionreactor_postthink()
+{
+}
+
+
+void turret_fusionreactor_dinit()
+{
+ if (self.netname == "") self.netname = "Fusionreactor";
+
+ self.turrcaps_flags =TFL_TURRCAPS_SUPPORT | TFL_TURRCAPS_AMMOSOURCE;
+
+ self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE;
+
+ self.target_select_flags = TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_OWNTEAM | TFL_TARGETSELECT_RANGELIMTS;
+
+ self.firecheck_flags = TFL_FIRECHECK_OWM_AMMO | TFL_FIRECHECK_OTHER_AMMO | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_WORLD;
+
+ self.shoot_flags = TFL_SHOOT_HITALLVALID;
+
+ self.aim_flags = TFL_AIM_NO;
+
+ self.track_flags = TFL_TRACK_NO;
+
+ precache_model ("models/turrets/reactor.md3");
+ precache_model ("models/turrets/base.md3");
+ if (turret_stdproc_init("fusreac_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/reactor.md3");
+
+ //if(!turret_tag_setup(0))
+ // dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ self.tur_head.scale = 0.75;
+ setorigin(self.tur_head,self.origin + '0 0 25');
+ self.tur_head.avelocity = '0 50 0';
+
+ self.turret_firecheckfunc = turret_fusionreactor_firecheck;
+ self.turret_firefunc = turret_fusionreactor_fire;
+
+ // re-color badge & handle recoil effect
+ self.turret_postthink = turret_fusionreactor_postthink;
+}
+
+/*QUAKED turret_fusionreactor (0 .5 .8) ?
+*/
+void spawnfunc_turret_fusionreactor()
+{
+ // return;
+ self.think = turret_fusionreactor_dinit;
+ self.nextthink = time + 0.5;
+}
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_hellion.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_hellion.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_hellion.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,219 @@
+.float shot_speed_max;
+.float shot_speed_gain;
+
+void spawnfunc_turret_hellion();
+void turret_hellion_dinit();
+void turret_hellion_attack();
+void turret_hellion_missile_explode();
+void turret_hellion_missile_think();
+void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+
+void turret_hellion_postthink()
+{
+ if(cvar("g_turrets_reloadcvars"))
+ {
+ if(!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max");
+ if(!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain");
+ }
+
+ if(self.tur_head.frame != 0)
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+ if(self.tur_head.frame > 7)
+ self.tur_head.frame = 0;
+}
+
+void turret_hellion_attack()
+{
+ local entity missile;
+
+ sound (self, CHAN_BODY, "weapons/hagar_fire.wav", 1, ATTN_NORM);
+
+ missile = spawn ();
+ setorigin(missile, self.tur_shotorg_updated);
+ sound (missile, CHAN_BODY, "weapons/rocket_fly.wav", 0.4, ATTN_NORM);
+ setmodel (missile, "models/turrets/mlrs_rocket.md3"); // precision set below
+ setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+ missile.classname = "hellion_missile";
+ missile.owner = self;
+ missile.bot_dodge = TRUE;
+ missile.bot_dodgerating = self.shot_dmg;
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = 2;
+ missile.health = 50;
+ missile.enemy = self.enemy;
+ missile.think = turret_hellion_missile_think;
+ missile.nextthink = time + 0.2;
+ missile.solid = SOLID_BBOX;
+ missile.movetype = MOVETYPE_FLYMISSILE;
+ missile.effects = EF_LOWPRECISION;
+ missile.velocity = (self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed; // + ('0 0 1' * self.shot_speed * 0.15);
+ missile.angles = vectoangles(missile.velocity);
+ missile.touch = turret_hellion_missile_explode;
+ missile.flags = FL_PROJECTILE;
+ missile.solid = SOLID_BBOX;
+ missile.tur_health = time + 9;
+ missile.tur_aimpos = randomvec() * 128;
+ te_explosion (missile.origin);
+
+ // switch tubes
+ self.tur_shotorg_y = self.tur_shotorg_y * -1;
+
+ if(self.tur_head.frame == 0)
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+}
+
+void turret_hellion_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+ self.health = self.health - damage;
+ self.velocity = self.velocity + vforce;
+ if (self.health <= 0) turret_hellion_missile_explode();
+}
+
+void turret_hellion_missile_think()
+{
+ vector olddir,newdir;
+ vector pre_pos;
+ float itime;
+
+ self.nextthink = time + 0.1;
+
+ // Enemy in range?
+ if (vlen(self.origin - self.enemy.origin) < self.owner.shot_radius * 0.25)
+ turret_hellion_missile_explode();
+
+ olddir = normalize(self.velocity);
+
+ if (self.tur_health < time) turret_hellion_missile_explode();
+
+ // Enemy dead? just keep on the current heading then.
+ if ((self.enemy == world) || (self.enemy.deadflag != DEAD_NO))
+ {
+
+ // Make sure we dont return to tracking a respawned player
+ self.enemy = world;
+
+ // Accelerate
+ self.velocity = olddir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max);
+
+ // Turn model
+ self.angles = vectoangles(self.velocity);
+
+ //if ( (vlen(self.origin - self.owner.origin)) > (self.owner.shot_radius * 10) )
+ // turret_hellion_missile_explode();
+
+ // return;
+ }
+
+ olddir = normalize(self.velocity);
+
+ if(self.enemy)
+ {
+ // Predict enemy position
+ itime = vlen(self.enemy.origin - self.origin) / vlen(self.velocity);
+ pre_pos = self.enemy.origin + self.enemy.velocity * itime;
+ } else {
+ pre_pos = self.origin + olddir * 1024;
+ }
+
+ pre_pos = (pre_pos + self.enemy.origin) * 0.5;
+
+ //pre_pos += randomvec() * 128; //self.tur_aimpos * (sin(32) * time) ;
+
+
+ // Find out the direction to that place
+ newdir = normalize(pre_pos - self.origin);
+
+ // Turn
+ newdir = normalize(olddir + newdir * 0.5);
+
+ // Accelerate
+ self.velocity = newdir * min(vlen(self.velocity) * self.owner.shot_speed_gain,self.owner.shot_speed_max);
+
+ // Turn model
+ self.angles = vectoangles(self.velocity);
+
+ if (time+itime < time+0.1)
+ {
+ self.think = turret_hellion_missile_explode;
+ self.nextthink = time + itime;
+ }
+}
+
+void turret_hellion_missile_explode()
+{
+ vector org2;
+ float d;
+
+ sound (self, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM);
+ org2 = findbetterlocation (self.origin, 16);
+
+ // LordHavoc: TE_TEI_BIGEXPLOSION
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, 78);
+ WriteCoord (MSG_BROADCAST, org2_x);
+ WriteCoord (MSG_BROADCAST, org2_y);
+ WriteCoord (MSG_BROADCAST, org2_z);
+
+ self.event_damage = SUB_Null;
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+
+#ifdef TURRET_DEBUG
+ self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg;
+ self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#endif
+
+ // Target dead, get another is still targeting the same.
+ if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy))
+ self.owner.enemy = world;
+
+ remove (self);
+}
+
+void turret_hellion_dinit()
+{
+ if (self.netname == "") self.netname = "Hellion Missile Turret";
+
+ if(!self.shot_speed_max) self.shot_speed_max = cvar("g_turrets_unit_hellion_std_shot_speed_max");
+ if(!self.shot_speed_gain) self.shot_speed_gain = cvar("g_turrets_unit_hellion_std_shot_speed_gain");
+
+ self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_FASTPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_MISSILEKILL;
+ self.aim_flags = TFL_AIM_SIMPLE;
+ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK ;
+ self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_DISTANCES | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF | TFL_FIRECHECK_OWM_AMMO;
+ self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+
+ precache_model ( "models/turrets/mlrs_rocket.md3");
+ precache_model ("models/turrets/hellion.md3");
+ precache_model ("models/turrets/base.md3");
+
+ if (turret_stdproc_init("hellion_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/hellion.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_hellion_attack;
+
+ // Custom animations and sutch
+ self.turret_postthink = turret_hellion_postthink;
+}
+
+/*QUAKED turret_hellion (0 .5 .8) ?
+*/
+void spawnfunc_turret_hellion()
+{
+ self.think = turret_hellion_dinit;
+ self.nextthink = time + 0.5;
+}
+
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_hk.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_hk.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_hk.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,469 @@
+//#define TURRET_DEBUG_HK
+
+#ifdef TURRET_DEBUG_HK
+.float atime;
+#endif
+
+void spawnfunc_turret_hk();
+void turret_hk_dinit();
+void turret_hk_attack();
+void turret_hk_missile_explode();
+void turret_hk_missile_think();
+void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
+float turret_hk_addtarget(entity e_target,entity e_sender);
+//void turret_hk_missile_touch();
+
+float hk_maxspeed;
+float hk_minspeed;
+float hk_accel;
+float hk_accel2;
+float hk_decel;
+
+float turret_hk_addtarget(entity e_target,entity e_sender)
+{
+ if (e_target)
+ {
+ if (turret_validate_target(self,e_target,self.target_validate_flags) > 0)
+ {
+ self.enemy = e_target;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+float hk_is_valid_target(entity e_target)
+{
+ if (e_target == world)
+ return 0;
+
+ // If only this was used more..
+ if (e_target.flags & FL_NOTARGET)
+ return 0;
+
+ // Cant touch this
+ if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0))
+ return 0;
+
+ // player
+ if ((e_target.flags & FL_CLIENT) && (self.owner.target_select_playerbias < 0))
+ return 0;
+
+ if ((e_target.flags & FL_CLIENT) && (e_target.deadflag != DEAD_NO))
+ return 0;
+
+ // Missile
+ if ((e_target.flags & FL_PROJECTILE) && (self.owner.target_select_missilebias < 0))
+ return 0;
+
+ // Team check
+ if ((e_target.team == self.owner.team) || (self.owner.team == e_target.owner.team))
+ return 0;
+
+ return 1;
+}
+
+void turret_hk_missile_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+ if (attacker.team == self.team)
+ damage *= 0.5;
+
+ self.velocity += force;
+
+ self.health -= damage;
+
+ if (self.health <= 0)
+ turret_hk_missile_explode();
+}
+
+void turret_hk_attack()
+{
+ local entity missile;
+ //local entity flash2;
+
+ sound (self, CHAN_BODY, "weapons/rocket_fire.wav", 1, ATTN_NORM);
+
+ missile = spawn ();
+ missile.solid = SOLID_BBOX;
+ setmodel (missile, "models/turrets/hunter2.md3"); // precision set below
+ setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+ sound (missile, CHAN_BODY, "weapons/rocket_fly.wav", 0.4, ATTN_NORM);
+ setorigin(missile, self.tur_shotorg_updated);
+
+ missile.scale = 1;
+ missile.classname = "hk_missile";
+ missile.owner = self;
+ missile.bot_dodge = TRUE;
+ missile.bot_dodgerating = self.shot_dmg;
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = 2;
+ missile.health = 35;
+ missile.think = turret_hk_missile_think;
+ missile.event_damage = turret_hk_missile_damage;
+ missile.nextthink = time + 0.25;
+ missile.movetype = MOVETYPE_BOUNCEMISSILE;
+ missile.effects = EF_LOWPRECISION;
+ missile.velocity = self.tur_shotdir_updated * (self.shot_speed * 0.75);
+ missile.angles = vectoangles(missile.velocity);
+ missile.touch = turret_hk_missile_explode; //turret_hk_missile_touch;
+ missile.flags = FL_PROJECTILE;
+ missile.enemy = self.enemy;
+ missile.team = self.team;
+ missile.cnt = time + 30;
+
+ te_explosion (missile.origin);
+
+ if(self.tur_head.frame == 0)
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+}
+
+/*
+void turret_hk_missile_touch()
+{
+ if(other == self.enemy)
+ turret_hk_missile_explode();
+ else
+ {
+ if(self.cnt < time)
+ {
+ self.cnt = time + 0.25;
+ self.health = self.health - 5;
+ if(self.health <= 0)
+ turret_hk_missile_explode();
+
+ }
+ }
+}
+*/
+
+void turret_hk_missile_think()
+{
+ vector vu, vd, vf, vl, vr, ve; // Vector (direction)
+ float fu, fd, ff, fl, fr, fe; // Fraction to solid
+ vector olddir,wishdir,newdir; // Final direction
+ float lt_for; // Length of Trace FORwrad
+ float lt_seek; // Length of Trace SEEK (left, right, up down)
+ float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward)
+ vector pre_pos;
+ float myspeed;
+ entity e;
+ float ad;
+
+ // 25fps
+ self.nextthink = time + 0.04;
+
+ //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)
+ {
+ // 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;
+
+ // Close enougth to do decent damage?
+ if ( vlen(self.origin - self.enemy.origin) <= (self.owner.shot_radius * 0.25) )
+ {
+ turret_hk_missile_explode();
+ return;
+ }
+ }
+ else
+ {
+ fe = 0;
+ }
+
+ if ((fe != 1) || (self.enemy == world))
+ {
+ myspeed = vlen(self.velocity);
+
+ lt_for = myspeed * 2;
+ lt_seek = myspeed * 1.5;
+
+ // 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);
+
+ //Calculate new speed
+
+ // To close to something, Slow down!
+ if ( ((ff < 0.7) || (ad > 5)) && (myspeed > hk_minspeed) )
+ myspeed = max(myspeed * hk_decel,hk_minspeed);
+
+ // Failry clear, accelerate.
+ if ( (ff > 0.7) && (myspeed < hk_maxspeed) )
+ myspeed = min(myspeed * hk_accel,hk_maxspeed);
+
+ // Setup trace pitch
+ pt_seek = 1 - ff;
+ pt_seek = bound(0.125,pt_seek,0.8);
+ if (ff < 0.5) pt_seek = 1;
+
+ // Trace left
+ traceline(self.origin, self.origin + (-1 * (v_right * pt_seek) + (v_forward * ff)) * lt_seek,FALSE,self);
+ vl = trace_endpos;
+ fl = trace_fraction;
+
+ // Trace right
+ traceline(self.origin, self.origin + ((v_right * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self);
+ vr = trace_endpos;
+ fr = trace_fraction;
+
+ // Trace up
+ traceline(self.origin, self.origin + ((v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self);
+ vu = trace_endpos;
+ fu = trace_fraction;
+
+ // Trace down
+ traceline(self.origin, self.origin + (-1 * (v_up * pt_seek) + (v_forward * ff)) * lt_seek ,FALSE,self);
+ vd = trace_endpos;
+ fd = trace_fraction;
+
+ vl = normalize(vl - self.origin);
+ vr = normalize(vr - self.origin);
+ vu = normalize(vu - self.origin);
+ vd = normalize(vd - self.origin);
+
+ // Panic tresh passed, find a single direction and turn as hard as we can
+ if (pt_seek == 1)
+ {
+ wishdir = v_right;
+ if (fl > fr) wishdir = -1 * v_right;
+ if (fu > fl) wishdir = v_up;
+ if (fd > fu) wishdir = -1 * v_up;
+ }
+ else
+ {
+ // Normalize our trace vectors to make a smooth path
+ wishdir = normalize( (vl * fl) + (vr * fr) + (vu * fu) + (vd * fd) );
+ }
+
+ if (self.enemy)
+ {
+ if (fe < 0.1) fe = 0.1; // Make sure we always try to move sligtly towards our target
+ wishdir = (wishdir * (1 - fe)) + (ve * fe);
+ }
+ }
+ else
+ {
+ // Got a clear path to target, speed up fast (if not at full speed) and go straight for it.
+ myspeed = vlen(self.velocity);
+ if (myspeed < hk_maxspeed)
+ myspeed = min(myspeed * hk_accel2,hk_maxspeed);
+
+ wishdir = ve;
+ //wishdir = normalize(self.enemy.origin - (self.enemy.origin + self.enemy.velocity));
+ }
+
+ if((myspeed > hk_minspeed) && (self.cnt > time))
+ myspeed = min(myspeed * hk_accel2,hk_maxspeed);
+
+ // Ranoutagazfish?
+ if(self.cnt < time)
+ {
+ self.cnt = time + 0.25;
+ self.nextthink = 0;
+ self.movetype = MOVETYPE_BOUNCE;
+ sound (self, CHAN_BODY, "", 0.4, ATTN_NORM);
+ return;
+ }
+
+ // Calculate new heading
+ olddir = normalize(self.velocity);
+
+ newdir = normalize(olddir + wishdir * cvar("g_turrets_unit_hk_std_shot_speed_turnrate"));
+
+ //fu = (1 / hk_maxspeed) * myspeed;
+ //fd = fu - (0.75 - 0.25);
+ //newdir = normalize(olddir + wishdir * fd);
+
+ // Set heading & speed
+ self.velocity = newdir * myspeed;
+
+ // Align model with new heading
+ self.angles = vectoangles(self.velocity);
+
+
+#ifdef TURRET_DEBUG_HK
+ //if(self.atime < time) {
+ if (fe <= 0.99)
+ {
+ 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
+}
+
+void turret_hk_missile_explode()
+{
+ vector org2;
+ float d;
+
+ if ((other == self.owner)||(other == self.owner.tur_head))
+ return;
+
+ //vector org2;
+ sound (self, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM);
+ org2 = findbetterlocation (self.origin, 16);
+
+ // LordHavoc: TE_TEI_BIGEXPLOSION
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, 78);
+ WriteCoord (MSG_BROADCAST, org2_x);
+ WriteCoord (MSG_BROADCAST, org2_y);
+ WriteCoord (MSG_BROADCAST, org2_z);
+
+ self.event_damage = SUB_Null;
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+
+#ifdef TURRET_DEBUG
+ self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg;
+ self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#endif
+
+ // Target dead, get another is still targeting the same.
+ if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy))
+ self.owner.enemy = world;
+
+ remove (self);
+}
+
+void turret_hk_postthink()
+{
+ if(cvar("g_turrets_reloadcvars"))
+ {
+ hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max");
+ hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed");
+ hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel");
+ hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2");
+ hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel");
+ }
+
+ if(self.tur_head.frame != 0)
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+ if(self.tur_head.frame > 5)
+ self.tur_head.frame = 0;
+
+}
+
+void turret_hk_dinit()
+{
+ if (self.netname == "") self.netname = "Hunter-killer turret";
+
+ hk_maxspeed = cvar("g_turrets_unit_hk_std_shot_speed_max");
+ hk_minspeed = cvar("g_turrets_unit_hk_std_shot_speed");
+ hk_accel = cvar("g_turrets_unit_hk_std_shot_speed_accel");
+ hk_accel2 = cvar("g_turrets_unit_hk_std_shot_speed_accel2");
+ hk_decel = cvar("g_turrets_unit_hk_std_shot_speed_decel");
+
+ self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL | TFL_TURRCAPS_RECIVETARGETS;
+
+ self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+
+ self.aim_flags = TFL_AIM_SIMPLE;
+
+ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
+
+ self.firecheck_flags = TFL_FIRECHECK_WORLD | TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF;
+
+ self.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
+
+ self.shoot_flags = TFL_SHOOT_CLEARTARGET;
+
+
+ precache_model ( "models/turrets/hunter2.md3");
+
+ precache_model ("models/turrets/base.md3");
+ precache_model ("models/turrets/hk.md3");
+
+ if (turret_stdproc_init("hk_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK;
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/hk.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_hk_attack;
+
+ // re-color badge & handle recoil effect
+ self.turret_postthink = turret_hk_postthink;
+
+ // What to do when reciveing foreign target data
+ self.turret_addtarget = turret_hk_addtarget;
+}
+
+/*
+* Turret that fires Hunter-killer missiles.
+* Missiles seek their target and try to avoid obstacles. If target dies early, they
+* pick a new one on their own.
+*/
+
+/*QUAKED turret_hk (0 .5 .8) ?
+hunter-killer missiles.
+*/
+
+void spawnfunc_turret_hk()
+{
+ self.think = turret_hk_dinit;
+ self.nextthink = time + 0.5;
+}
+
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_machinegun.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_machinegun.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_machinegun.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,71 @@
+void spawnfunc_turret_machinegun();
+void turret_machinegun_std_init();
+void turret_machinegun_attack();
+
+//.float bulletcounter;
+void turret_machinegun_attack()
+{
+
+ entity flash;
+ sound (self, CHAN_BODY, "weapons/uzi_fire.wav", 1, ATTN_NORM);
+ fireBullet (self.tur_shotorg_updated, self.tur_shotdir_updated,self.shot_spread, self.shot_dmg, self.shot_force, DEATH_TURRET, FALSE);
+
+ te_smallflash(self.tur_shotorg_updated);
+ trailparticles(self,particleeffectnum("EF_MINIGUNTRAIL"),self.tur_shotorg_updated,trace_endpos);
+
+ // muzzle flash for 3rd person view
+ flash = spawn();
+ //setorigin(flash, '43 1 8');
+ setmodel(flash, "models/uziflash.md3"); // precision set below
+ setattachment(flash, self.tur_head, "tag_fire");
+ flash.think = W_Uzi_Flash_Go;
+ flash.nextthink = time + 0.02;
+ flash.frame = 2;
+ flash.angles_z = flash.v_angle_z + random() * 180;
+ flash.alpha = 1;
+ flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+}
+
+
+void turret_machinegun_std_init()
+{
+ if (self.netname == "") self.netname = "Machinegun Turret";
+
+ precache_model ("models/turrets/machinegun.md3");
+ precache_model ("models/turrets/base.md3");
+
+ self.ammo_flags = TFL_AMMO_BULLETS | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
+ self.turrcaps_flags = TFL_TURRCAPS_HITSCAN | TFL_TURRCAPS_PLAYERKILL;
+ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_ZEASE;
+
+ if (turret_stdproc_init("machinegun_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/machinegun.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_machinegun_attack;
+
+}
+
+
+
+/*
+* machinegun turret. does what you'd expect
+*/
+
+/*QUAKED turret_machinegun (0 .5 .8) ?
+*/
+void spawnfunc_turret_machinegun()
+{
+ self.think = turret_machinegun_std_init;
+ self.nextthink = time + 0.5;
+}
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_mlrs.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_mlrs.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_mlrs.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,115 @@
+void spawnfunc_turret_mlrs();
+void turret_mlrs_dinit();
+void turret_mlrs_attack();
+void turret_mlrs_projectile_explode();
+
+void turret_mlrs_postthink()
+{
+
+ if((self.tur_head.frame >= 6) && (self.attack_finished <= time))
+ self.tur_head.frame = 0;
+}
+
+void turret_mlrs_attack()
+{
+ entity missile;
+
+ turret_tag_fire_update();
+
+ sound (self, CHAN_BODY, "weapons/rocket_fire.wav", 1, ATTN_NORM);
+
+ missile = spawn ();
+ setmodel (missile, "models/turrets/rocket.md3"); // precision set below
+ setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+ sound (missile, CHAN_BODY, "weapons/rocket_fly.wav", 0.4, ATTN_NORM);
+ setorigin(missile, self.tur_shotorg_updated);
+ missile.classname = "mlrs_missile";
+ missile.owner = self;
+ missile.bot_dodge = TRUE;
+ missile.bot_dodgerating = self.shot_dmg;
+ missile.takedamage = DAMAGE_YES;
+ missile.damageforcescale = 4;
+ missile.health = 30;
+ missile.think = turret_mlrs_projectile_explode;
+ missile.nextthink = time + max(self.tur_impacttime,(self.shot_radius * 2) / self.shot_speed);
+ missile.solid = SOLID_BBOX;
+ missile.movetype = MOVETYPE_FLYMISSILE;
+ missile.effects = EF_LOWPRECISION;
+ missile.velocity = (self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
+ missile.angles = vectoangles(missile.velocity);
+ missile.touch = turret_mlrs_projectile_explode;
+ missile.flags = FL_PROJECTILE;
+ missile.solid = SOLID_BBOX;
+ missile.enemy = self.enemy;
+
+ te_explosion (missile.origin);
+
+ self.tur_head.frame = 7 - self.volly_counter;
+}
+
+void turret_mlrs_projectile_explode()
+{
+ vector org2;
+
+ //vector org2;
+ sound (self, CHAN_BODY, "weapons/rocket_impact.wav", 1, ATTN_NORM);
+ org2 = findbetterlocation (self.origin, 16);
+ pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+
+ self.event_damage = SUB_Null;
+ RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+
+#ifdef TURRET_DEBUG
+ float d;
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+ self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg;
+ self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#endif
+
+ // Target dead, get another is still targeting the same.
+ if ((self.enemy.deadflag != DEAD_NO) && (self.enemy == self.owner.enemy))
+ self.owner.enemy = world;
+
+ remove (self);
+}
+
+void turret_mlrs_dinit()
+{
+ if (self.netname == "") self.netname = "MLRS turret";
+
+ self.turrcaps_flags = TFL_TURRCAPS_RADIUSDMG | TFL_TURRCAPS_MEDPROJ | TFL_TURRCAPS_PLAYERKILL;
+ self.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE;
+ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_ZEASE | TFL_AIM_SHOTTIMECOMPENSATE;
+
+ precache_model ( "models/turrets/rocket.md3");
+ precache_model ("models/turrets/mlrs.md3");
+ precache_model ("models/turrets/base.md3");
+
+ if (turret_stdproc_init("mlrs_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/mlrs.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_mlrs_attack;
+ self.turret_postthink = turret_mlrs_postthink;
+
+}
+
+/*QUAKED turret_mlrs (0 .5 .8) ?
+*/
+
+void spawnfunc_turret_mlrs()
+{
+ self.think = turret_mlrs_dinit;
+ self.nextthink = time + 0.5;
+}
+
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_phaser.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_phaser.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_phaser.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,149 @@
+void spawnfunc_turret_phaser();
+void turret_phaser_dinit();
+void turret_phaser_attack();
+
+.float fireflag;
+
+float turret_phaser_firecheck()
+{
+ if(self.fireflag != 0) return 0;
+ return turret_stdproc_firecheck();
+}
+
+void turret_phaser_postthink()
+{
+ if(self.tur_head.frame == 0)
+ return;
+
+ if(self.fireflag == 1)
+ {
+ if(self.tur_head.frame == 10)
+ self.tur_head.frame = 1;
+ else
+ self.tur_head.frame = self.tur_head.frame +1;
+ }
+ else if (self.fireflag == 2 )
+ {
+ self.tur_head.frame = self.tur_head.frame +1;
+ if(self.tur_head.frame == 15)
+ {
+ self.tur_head.frame = 0;
+ self.fireflag = 0;
+ }
+ }
+}
+
+void beam_think()
+{
+ if((time > self.cnt)||(self.owner.deadflag != DEAD_NO))
+ {
+ self.owner.attack_finished = time + self.owner.shot_refire;
+ self.owner.fireflag = 2;
+ self.owner.tur_head.frame = 10;
+ sound (self, CHAN_BODY, "", 1, ATTN_NORM);
+ remove(self);
+ return;
+ }
+
+ turret_do_updates(self.owner);
+
+ if(time - self.shot_spread > 0)
+ {
+ self.shot_spread = time + 2;
+ sound (self, CHAN_BODY, "turrets/phaser.ogg", 1, ATTN_NORM);
+ }
+
+
+ self.nextthink = time + self.ticrate;
+
+ self.owner.attack_finished = time + frametime;
+ entity oldself;
+ oldself = self;
+ self = self.owner;
+ FireImoBeam ( self.tur_shotorg_updated,
+ self.tur_shotorg_updated + self.tur_shotdir_updated * self.target_range_fire,
+ '-1 -1 -1' * self.shot_radius,
+ '1 1 1' * self.shot_radius,
+ self.shot_force,
+ oldself.shot_dmg,
+ 0.75,
+ DEATH_TURRET);
+ self = oldself;
+ self.scale = vlen(self.owner.tur_shotorg_updated - trace_endpos) / 256;
+
+}
+
+void turret_phaser_attack()
+{
+ entity beam;
+
+ beam = spawn();
+ beam.ticrate = 0.1; //cvar("sys_ticrate");
+ setmodel(beam,"models/turrets/phaser_beam.md3");
+ beam.effects = EF_LOWPRECISION;
+ beam.solid = SOLID_NOT;
+ beam.think = beam_think;
+ beam.cnt = time + self.shot_speed;
+ beam.shot_spread = time + 2;
+ beam.nextthink = time;
+ beam.owner = self;
+ beam.shot_dmg = self.shot_dmg / (self.shot_speed / beam.ticrate);
+ beam.scale = self.target_range_fire / 256;
+ beam.movetype = MOVETYPE_NONE;
+ beam.enemy = self.enemy;
+ beam.bot_dodge = TRUE;
+ beam.bot_dodgerating = beam.shot_dmg;
+ sound (beam, CHAN_BODY, "turrets/phaser.ogg", 1, ATTN_NORM);
+ self.fireflag = 1;
+
+ beam.attack_finished = self.attack_finished;
+ self.attack_finished = time; // + cvar("sys_ticrate");
+
+ setattachment(beam,self.tur_head,"tag_fire");
+
+ PointSound (trace_endpos, "weapons/neximpact.wav", 1, ATTN_NORM);
+
+ if(self.tur_head.frame == 0)
+ self.tur_head.frame = 1;
+}
+
+void turret_phaser_dinit()
+{
+ if (self.netname == "") self.netname = "Phaser Cannon";
+
+ self.turrcaps_flags = TFL_TURRCAPS_SNIPER|TFL_TURRCAPS_HITSCAN|TFL_TURRCAPS_PLAYERKILL;
+ self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
+ self.aim_flags = TFL_AIM_ZEASE | TFL_AIM_LEAD;
+
+
+ precache_sound ("turrets/phaser.ogg");
+ precache_model ("models/turrets/phaser.md3");
+ precache_model ("models/turrets/phaser_beam.md3");
+ precache_model ("models/turrets/base.md3");
+
+ if (turret_stdproc_init("phaser_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/phaser.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ self.turret_firecheckfunc = turret_phaser_firecheck;
+ self.turret_firefunc = turret_phaser_attack;
+ self.turret_postthink = turret_phaser_postthink;
+
+}
+
+/*QUAKED turret_phaser(0 .5 .8) ?
+*/
+void spawnfunc_turret_phaser()
+{
+ self.think = turret_phaser_dinit;
+ self.nextthink = time + 0.5;
+}
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_plasma.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_plasma.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_plasma.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,180 @@
+void spawnfunc_turret_plasma();
+void spawnfunc_turret_plasma_dual();
+
+void turret_plasma_std_init();
+void turret_plasma_dual_init();
+
+void turret_plasma_attack();
+void turret_plasma_projectile_explode();
+
+void turret_plasma_postthink()
+{
+ if(self.tur_head.frame != 0)
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+ if(self.classname == "turret_plasma_dual")
+ {
+ if(self.tur_head.frame >= 6)
+ self.tur_head.frame = 0;
+ } else {
+ if(self.tur_head.frame >= 5)
+ self.tur_head.frame = 0;
+ }
+}
+
+void turret_plasma_attack()
+{
+ entity proj;
+
+ turret_tag_fire_update();
+
+ sound (self, CHAN_BODY, "weapons/hagar_fire.wav", 1, ATTN_NORM);
+ proj = spawn ();
+ setorigin(proj, self.tur_shotorg_updated);
+ setsize(proj, '0 0 0', '0 0 0');
+ setmodel(proj, "models/elaser.mdl"); // precision set above
+ proj.classname = "plasmabomb";
+ proj.owner = self;
+ proj.bot_dodge = TRUE;
+ proj.bot_dodgerating = self.shot_dmg;
+ proj.think = turret_plasma_projectile_explode;
+ proj.nextthink = time + 9;
+ proj.solid = SOLID_BBOX;
+ proj.movetype = MOVETYPE_FLYMISSILE;
+ proj.velocity = (self.tur_shotdir_updated + randomvec() * self.shot_spread) * self.shot_speed;
+ proj.angles = vectoangles(proj.velocity);
+ proj.touch = turret_plasma_projectile_explode;
+ proj.flags = FL_PROJECTILE;
+ proj.effects = EF_LOWPRECISION | EF_BRIGHTFIELD;
+ proj.enemy = self.enemy;
+ proj.flags = FL_PROJECTILE | FL_NOTARGET;
+
+ if(self.tur_head.frame == 0)
+ self.tur_head.frame = 1;
+
+ // trailparticles(proj,particleeffectnum("TR_REDPLASMA"),proj.origin,proj.origin + proj.velocity);
+
+ // Snapback the head
+ // self.tur_head.angles_x = self.tur_head.angles_x + min((self.shot_dmg * 0.05),self.aim_maxpitch);
+
+ //if(self.classname == "turret_plasma_dual")
+ // self.tur_shotorg_y = self.tur_shotorg_y * -1;
+
+}
+
+void turret_plasma_projectile_explode()
+{
+ vector org2;
+
+ org2 = findbetterlocation (self.origin, 8);
+ WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte (MSG_BROADCAST, 79);
+ WriteCoord (MSG_BROADCAST, org2_x);
+ WriteCoord (MSG_BROADCAST, org2_y);
+ WriteCoord (MSG_BROADCAST, org2_z);
+ WriteCoord (MSG_BROADCAST, 0); // SeienAbunae: groan... Useless clutter
+ WriteCoord (MSG_BROADCAST, 0);
+ WriteCoord (MSG_BROADCAST, 0);
+ WriteByte (MSG_BROADCAST, 155);
+
+ self.event_damage = SUB_Null;
+
+
+#ifdef TURRET_DEBUG
+ float d;
+
+ d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+ self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; //self.owner.shot_dmg;
+ self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
+#else
+ RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, world, self.owner.shot_force, DEATH_TURRET);
+#endif
+ sound (self, CHAN_BODY, "weapons/electro_impact.wav", 1, ATTN_NORM);
+
+ remove (self);
+}
+
+void turret_plasma_std_init()
+{
+ if (self.netname == "") self.netname = "Plasma Cannon";
+
+ precache_model ("models/turrets/plasma.md3");
+ precache_model ("models/turrets/base.md3");
+
+ self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
+ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZEASE;
+
+ if (turret_stdproc_init("plasma_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/plasma.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // Our fire routine
+ self.turret_firefunc = turret_plasma_attack;
+
+ // re-color badge & handle recoil effect
+ self.turret_postthink = turret_plasma_postthink;
+}
+
+
+void turret_plasma_dual_init()
+{
+ if (self.netname == "") self.netname = "Dual Plasma Cannon";
+
+ precache_model ("models/turrets/plasmad.md3");
+ precache_model ("models/turrets/base.md3");
+
+ self.ammo_flags = TFL_AMMO_ENERGY | TFL_AMMO_RECHARGE | TFL_AMMO_RECIVE;
+
+ if (turret_stdproc_init("plasma_dual") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ setmodel(self,"models/turrets/base.md3");
+ setmodel(self.tur_head,"models/turrets/plasmad.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ // select aim
+ self.aim_flags = TFL_AIM_LEAD | TFL_AIM_SHOTTIMECOMPENSATE | TFL_AIM_ZEASE;
+
+ // Our fire routine
+ self.turret_firefunc = turret_plasma_attack;
+
+ // re-color badge & handle recoil effect
+ self.turret_postthink = turret_plasma_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()
+{
+ self.think = turret_plasma_std_init;
+ self.nextthink = time + 0.5;
+}
+
+/*QUAKED turret_plasma_dual (0 .5 .8) ?
+*/
+void spawnfunc_turret_plasma_dual()
+{
+ self.think = turret_plasma_dual_init;
+ self.nextthink = time + 0.5;
+}
+
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_targettrigger.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_targettrigger.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_targettrigger.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,41 @@
+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;
+ e.turret_addtarget(other,oldself);
+ }
+
+ e = find(e, targetname, oldself.target);
+ }
+
+ oldself.cnt = time + 0.5;
+
+ self = oldself;
+}
+
+/*QUAKED turret_targettrigger (.5 .5 .5) ?
+*/
+void spawnfunc_turret_targettrigger()
+{
+ if(!cvar("g_turrets"))
+ {
+ remove(self);
+ return;
+ }
+
+ InitTrigger ();
+
+ self.touch = turret_targettrigger_touch;
+}
Added: trunk/data/qcsrc/server/tturrets/units/turret_unit_tessla.qc
===================================================================
--- trunk/data/qcsrc/server/tturrets/units/turret_unit_tessla.qc (rev 0)
+++ trunk/data/qcsrc/server/tturrets/units/turret_unit_tessla.qc 2008-07-21 09:55:06 UTC (rev 3857)
@@ -0,0 +1,147 @@
+void spawnfunc_turret_tesla();
+void turret_tesla_dinit();
+void turret_tesla_fire();
+
+.float toasted;
+entity toast(entity from, float range, float damage)
+{
+ entity e;
+ entity etarget;
+ float d,dd;
+
+ dd = range + 1;
+
+ e = findradius(from.origin,range);
+ while (e)
+ {
+ if((e.toasted != 1) && (e != from))
+ if (turret_validate_target(self,e,self.target_validate_flags) > 0)
+ {
+
+ traceline(from.origin,e.origin,0,from);
+ if(trace_fraction > 0.9)
+ {
+ d = vlen(e.origin - from.origin);
+ if(d < dd)
+ {
+ dd = d;
+ etarget = e;
+ }
+ }
+ }
+ e = e.chain;
+ }
+
+ if(etarget)
+ {
+ te_smallflash(etarget.origin);
+ te_lightning1(world,from.origin,etarget.origin);
+ Damage(etarget,self,self,damage,DEATH_TURRET,etarget.origin,'0 0 0');
+ etarget.toasted = 1;
+ }
+
+ return etarget;
+}
+
+void turret_tesla_fire()
+{
+ entity e,t;
+ float d,r,i;
+
+ if(cvar("g_turrets_nofire") != 0)
+ return;
+
+ if(self.attack_finished > time) return;
+
+ d = self.shot_dmg;
+ r = self.target_range;
+ e = spawn();
+ setorigin(e,self.origin + 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 = time + self.shot_refire;
+ self.ammo = self.ammo - self.shot_dmg;
+ for(i = 0;i < 10;i++)
+ {
+ d *= 0.5; r *= 0.75;
+ t = toast(t,r,d);
+ if(t == world) break;
+ }
+
+ e = findchainfloat(toasted, 1);
+ while(e)
+ {
+ e.toasted = 0;
+ e = e.chain;
+ }
+}
+
+void turret_tesla_postthink()
+{
+ turret_tesla_fire();
+
+ self.tur_head.frame = self.tur_head.frame + 1;
+
+ if(self.tur_head.frame >= 11)
+ self.tur_head.frame = 0;
+
+ if(self.tur_head.avelocity == '0 0 0')
+ self.tur_head.avelocity = '0 35 0';
+}
+
+
+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_NO;
+ self.firecheck_flags = TFL_FIRECHECK_REFIRE;
+ 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;
+
+ precache_model ("models/turrets/tesla_head.md3");
+ precache_model ("models/turrets/tesla_base.md3");
+
+ if (turret_stdproc_init("tesla_std") == 0)
+ {
+ remove(self);
+ return;
+ }
+
+ self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES |
+ TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
+
+
+ setmodel(self,"models/turrets/tesla_base.md3");
+ setmodel(self.tur_head,"models/turrets/tesla_head.md3");
+
+ if(!turret_tag_setup(0))
+ dprint("Warning: Turret ",self.classname, " faild to initialize md3 tags\n");
+
+ self.turret_firefunc = turret_stdproc_nothing;
+ self.turret_postthink = turret_tesla_postthink;
+}
+
+/*QUAKED turret_tesla (0 .5 .8) ?
+*/
+void spawnfunc_turret_tesla()
+{
+ // return;
+ self.think = turret_tesla_dinit;
+ self.nextthink = time + 0.5;
+}
+
More information about the nexuiz-commits
mailing list