mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-11-22 03:51:12 +00:00
1419 lines
31 KiB
C++
1419 lines
31 KiB
C++
//Kasuha's DeathMatch Camera QC patch version 1.5
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// KasCam definitions
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// KasCam constants
|
|
|
|
float CAM_IDLE = 0; // Camera idle, nobody to look at
|
|
float CAM_FIXED = 1; // Fixed-position camera
|
|
float CAM_FLYBY = 2; // Fly-by camera
|
|
float CAM_FOLLOW = 3; // Camera following a player
|
|
float CAM_HAND = 4; // Hand-targeted camera
|
|
float CAM_FREE = 5; // Flying hand-targeted camera
|
|
float CAM_NOCLIP = 6; // No-clipping flying blabla
|
|
float CAM_DEATH = 7; // Death camera (automatic mode only)
|
|
|
|
// KasCam variables
|
|
|
|
// how the standard fields are used:
|
|
// .enemy ........... targeted entity (player, monster, robot, ...)
|
|
// .movetarget ...... previous targeted entity
|
|
// .goalentity ...... targeted entity if locked on it
|
|
// .attack_state .... TRUE if forced to FOLLOW
|
|
// .dest ............ current expected camera position
|
|
// .dest1 ........... current expected targeted point
|
|
// .dest2 ........... next target for IDLE mode
|
|
// .mangle .......... angle speed
|
|
// .state ........... camera state
|
|
// .pausetime ....... delay measure
|
|
// .delay ........... how long to keep current target / idle mode
|
|
// .search_time ..... when drop from current mode
|
|
// .aflag ........... "take"/"r.i.p." messages on/off
|
|
// .lip ............. max distance for flyby mode
|
|
// .cnt ............. entity passing counter
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// internal KasCam functions
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// give me the next possible target please...
|
|
// yes, I am testing KasCam with bots. It is easier than with live players :-)
|
|
|
|
entity(entity ent) CamCycle =
|
|
{
|
|
if (ent == world) {
|
|
self.cnt = 0;
|
|
}
|
|
if (self.cnt == 0) {
|
|
ent = find(ent,classname,"player");
|
|
if (ent == world) {
|
|
self.cnt = 1;
|
|
}
|
|
}
|
|
if (self.cnt == 1) {
|
|
ent = find(ent,classname,"bot");
|
|
}
|
|
return ent;
|
|
};
|
|
|
|
// simple now, but there can be differences
|
|
// for various target entity classnames
|
|
|
|
void(entity ent) CamVectors =
|
|
{
|
|
if (ent.classname == "player") {
|
|
makevectors(ent.v_angle);
|
|
} else {
|
|
makevectors(ent.angles); // for dmbot
|
|
}
|
|
};
|
|
|
|
// centerprinting prototypes
|
|
|
|
void(entity client, string s1) camcenterprint1 = #73;
|
|
void(entity client, string s1, string s2) camcenterprint2 = #73;
|
|
void(entity client, string s1, string s2, string s3) camcenterprint3 = #73;
|
|
void(entity client, string s1, string s2, string s3, string s4) camcenterprint4 = #73;
|
|
void(entity client, string s1, string s2, string s3, string s4, string s5) camcenterprint5 = #73;
|
|
void(entity client, string s1, string s2, string s3, string s4, string s5, string s6) camcenterprint6 = #73;
|
|
void(entity client, string s1, string s2, string s3, string s4, string s5, string s6, string s7) camcenterprint7 = #73;
|
|
|
|
// Now taking / r.i.p report
|
|
|
|
void(entity ent, string tit) CamReport =
|
|
{
|
|
local string s2,s3;
|
|
|
|
// + POX v1.11 - wait for 'next round' message before printing anything else
|
|
if (self.attack_finished > time)
|
|
return;
|
|
|
|
if (ent.frags < 1) {
|
|
s2 = "no";
|
|
} else {
|
|
s2 = ftos(ent.frags);
|
|
}
|
|
if (ent.frags != 1) {
|
|
s3 = " frags";
|
|
} else {
|
|
s3 = " frag";
|
|
}
|
|
camcenterprint6(self,tit,"\n\n\n",ent.netname," - ",s2,s3);
|
|
};
|
|
|
|
// There is NO square root ?! :-(((
|
|
|
|
float(float num) CamSqrt =
|
|
{
|
|
local float apr;
|
|
|
|
if (num < 0.001)
|
|
return 0;
|
|
|
|
if (num>1) {
|
|
apr = num;
|
|
} else {
|
|
apr = 1;
|
|
}
|
|
do {
|
|
apr = (num + (apr * apr)) / (2 * apr);
|
|
} while (fabs((apr * apr) - num) > (num * 0.001));
|
|
return apr;
|
|
};
|
|
|
|
// Angle normalization
|
|
|
|
float(float a) CamReAngle =
|
|
{
|
|
while (a>180) a = a - 360;
|
|
while (a<-180) a = a + 360;
|
|
return a;
|
|
};
|
|
|
|
// point visibility test
|
|
|
|
int(vector vec) CamVisible =
|
|
{
|
|
traceline(self.origin,vec,TRUE,self);
|
|
return ((trace_fraction == 1) && !((trace_inopen && trace_inwater)));
|
|
};
|
|
|
|
// entity visibility test
|
|
// suited for players and player size bots; may vary for various classnames
|
|
|
|
int(entity ent) CamVisibleEnt =
|
|
{
|
|
local vector vec;
|
|
|
|
// Three points: origin, bottom and eyes
|
|
if (CamVisible(ent.origin)) return TRUE;
|
|
vec = ent.origin;
|
|
vec_z = ent.absmin_z;
|
|
if (CamVisible(vec)) return TRUE;
|
|
vec_z = ent.absmax_z - 8;
|
|
return CamVisible(vec);
|
|
};
|
|
|
|
// find target closest to the screen centre
|
|
// returns WORLD if nobody visible
|
|
|
|
entity() CamShoot =
|
|
{
|
|
local entity ent,entx;
|
|
local vector vec;
|
|
local float d1,dx;
|
|
|
|
makevectors(self.v_angle);
|
|
entx = world;
|
|
ent = CamCycle(world);
|
|
dx = 0;
|
|
while (ent != world) {
|
|
if (ent.deadflag == DEAD_NO) {
|
|
vec = ent.origin - self.origin;
|
|
vec = normalize(vec);
|
|
d1 = (v_forward_x * vec_x) + (v_forward_y * vec_y) + (v_forward_z * vec_z);
|
|
if (d1 > dx) {
|
|
if (CamVisible(ent.origin)) {
|
|
entx = ent;
|
|
dx = d1;
|
|
}
|
|
}
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
return entx;
|
|
};
|
|
|
|
// smooth position movement
|
|
|
|
float(float d, float a) CamHurry =
|
|
{
|
|
local float t,tt;
|
|
|
|
if (d<0) a = 0 - a;
|
|
t = frametime;
|
|
tt = CamSqrt(d / a);
|
|
if (t > tt) {
|
|
return d;
|
|
} else {
|
|
return (a * t * ((2 * tt) - t));
|
|
}
|
|
};
|
|
|
|
// Watch out! This is probably the most sophisticated routine ;-)
|
|
// I spent 5 hours developing the algorithm
|
|
// If you understand how it works in 3 minutes you're a guru :-)))
|
|
|
|
// smooth viewing angle movement
|
|
// return value is not a vector - this is a hack to return
|
|
// two floats at once :-)
|
|
// parameters are: current position, current velocity, acceleration
|
|
// returns new position and new velocity
|
|
|
|
vector(float s, float v, float a) CamSmooth =
|
|
{
|
|
local float dt,t1,t2,v2,as,sv2;
|
|
local int b;
|
|
local vector vec;
|
|
|
|
s = CamReAngle(s);
|
|
v2 = v / 2;
|
|
as = a * s;
|
|
sv2 = v2 * v2;
|
|
dt = frametime;
|
|
if (fabs(as) < sv2) {
|
|
b = v > 0;
|
|
} else {
|
|
b = s > 0;
|
|
}
|
|
if (b) {
|
|
t2 = CamSqrt((sv2 + as) / 2) / a;
|
|
a = 0 - a;
|
|
} else {
|
|
t2 = CamSqrt((sv2 - as) / 2) / a;
|
|
}
|
|
t1 = t2 - (v2 / a);
|
|
if (t1 > dt) {
|
|
s = (a * dt + v) * dt + s;
|
|
v = a * dt * 2 + v;
|
|
} else {
|
|
s = (a * t1 + v) * t1 + s;
|
|
v = a * t1 * 2 + v;
|
|
t1 = dt - t1;
|
|
if (t1 < t2) {
|
|
s = (v - (a * t1)) * t1 + s;
|
|
v = v - (a * t1 * 2);
|
|
} else {
|
|
s = 0;
|
|
v = 0;
|
|
}
|
|
}
|
|
vec_x = s;
|
|
vec_y = v;
|
|
vec_z = 0;
|
|
return vec;
|
|
};
|
|
|
|
// Check a flyby vector if it is usable
|
|
// returns vector length
|
|
// 1111 is a sentinel - flyby vectors are taken
|
|
// only if shorter than 1000
|
|
// vector is passed from player's eyes
|
|
// may vary for various classnames
|
|
|
|
float(vector vec) TryFlybyVector =
|
|
{
|
|
local vector orig,vec1;
|
|
local float vl;
|
|
|
|
vec1 = normalize(vec);
|
|
vec = 700 * vec1;
|
|
vec1 = 5 * vec1;
|
|
orig = self.enemy.origin;
|
|
orig_z = self.enemy.absmax_z - 8; // .................. eye level
|
|
traceline(orig,orig+vec,FALSE,self.enemy);
|
|
if (trace_ent != world) return 1111;
|
|
if (trace_inopen && trace_inwater) return 1111;
|
|
trace_endpos = trace_endpos - vec1;
|
|
if (pointcontents(trace_endpos) == CONTENT_SOLID) return 1111;
|
|
vl = vlen(self.enemy.origin - trace_endpos);
|
|
if (vl<50) return 1111;
|
|
return fabs(333 - vl);
|
|
};
|
|
|
|
// Move camera towards ideal position (or just skip there)
|
|
// parameters are position and angle speed factor
|
|
// 0 means to skip
|
|
|
|
void(float speedv, float speeda) CamUpdatePos =
|
|
{
|
|
local vector vec, v1;
|
|
local float vl;
|
|
|
|
// Check destination visibility
|
|
// if invisible, just skip there
|
|
traceline(self.origin,self.dest,TRUE,self);
|
|
if (trace_fraction != 1) {
|
|
speedv = 0;
|
|
speeda = 0;
|
|
}
|
|
// first do camera movement
|
|
if (speedv == 0) {
|
|
setorigin(self,self.dest);
|
|
} else {
|
|
vec = self.dest - self.origin;
|
|
vl = vlen(vec);
|
|
vec = normalize(vec);
|
|
vl = CamHurry(vl,speedv);
|
|
vec = vl * vec;
|
|
setorigin(self,self.origin + vec);
|
|
}
|
|
// then view angle
|
|
self.fixangle = TRUE;
|
|
self.movetype = MOVETYPE_NONE;
|
|
// in death mode look exactly at the target even if camera moves
|
|
// in other modes look like camera is in its ideal position
|
|
if (self.state != CAM_DEATH) {
|
|
vec = vectoangles(self.dest1 - self.dest);
|
|
} else {
|
|
vec = vectoangles(self.dest1 - self.origin);
|
|
}
|
|
vec_z = 0;
|
|
vec_x = CamReAngle(360 - vec_x);
|
|
vec_y = CamReAngle(vec_y);
|
|
if (vec_x > 70) {
|
|
vec_x = 70;
|
|
} else {
|
|
if (vec_x < -70) vec_x = -70;
|
|
}
|
|
if (speeda == 0) {
|
|
self.angles = vec;
|
|
self.mangle = '0 0 0';
|
|
} else {
|
|
v1 = CamSmooth(self.angles_x - vec_x,self.mangle_x,speeda);
|
|
self.mangle_x = v1_y;
|
|
self.angles_x = vec_x + v1_x;
|
|
v1 = CamSmooth(self.angles_y - vec_y,self.mangle_y,speeda);
|
|
self.mangle_y = v1_y;
|
|
self.angles_y = vec_y + v1_x;
|
|
self.angles_x = CamReAngle(self.angles_x);
|
|
self.angles_y = CamReAngle(self.angles_y);
|
|
}
|
|
self.v_angle = self.angles;
|
|
};
|
|
|
|
// make camera look forward ;-)
|
|
// used when target disappeared etc.
|
|
|
|
void() CamSetAuto =
|
|
{
|
|
self.dest = self.origin;
|
|
makevectors(self.v_angle);
|
|
self.dest1 = self.origin+v_forward;
|
|
CamUpdatePos(0,0);
|
|
};
|
|
|
|
// go to idle state
|
|
|
|
void() CamGoIdle =
|
|
{
|
|
CamSetAuto();
|
|
self.state = CAM_IDLE;
|
|
self.pausetime = time + 2;
|
|
self.search_time = time;
|
|
self.dest2 = self.dest1;
|
|
self.enemy = self;
|
|
};
|
|
|
|
// go to death mode
|
|
|
|
void() CamGoDeath =
|
|
{
|
|
if (self.aflag) CamReport(self.enemy,"R.I.P."); // Report death
|
|
self.mangle = '0 0 0';
|
|
self.dest = self.origin;
|
|
self.state = CAM_DEATH;
|
|
self.pausetime = time + 1;
|
|
self.movetarget = world;
|
|
self.search_time = time + 5; // max. 5 seconds DEATH mode
|
|
};
|
|
|
|
// Get target point for flyby
|
|
|
|
vector(entity ent) CamFlybyTarget =
|
|
{
|
|
local vector vec;
|
|
vec = ent.origin;
|
|
vec_z = ent.absmax_z - 8;
|
|
return vec;
|
|
};
|
|
|
|
// FlyBy mode initialization
|
|
// Targeted player is in self.enemy
|
|
|
|
void(entity newtarg) CamInitFlybyMode =
|
|
{
|
|
local float f, max;
|
|
local vector vec;
|
|
|
|
// Keep camera at this point for some time...
|
|
self.pausetime = time + 0.4;
|
|
|
|
// Report selected player
|
|
if (self.enemy != newtarg) {
|
|
self.enemy = newtarg;
|
|
if (self.movetarget != self.enemy) {
|
|
if (self.aflag) {
|
|
CamReport(self.enemy,"Now taking");
|
|
}
|
|
self.movetarget = newtarg;
|
|
}
|
|
}
|
|
vec = self.enemy.angles;
|
|
vec_x = 0;
|
|
vec_y = 0;
|
|
makevectors (vec);
|
|
v_forward = 3 * v_forward;
|
|
max = 1000;
|
|
f = TryFlybyVector(v_up + v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up - v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up + v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up - v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
if (max >= 1000) {
|
|
f = TryFlybyVector(v_up + v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up - v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_up - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
}
|
|
if (max>=1000) {
|
|
f = TryFlybyVector(v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
}
|
|
if (max>=1000) {
|
|
f = TryFlybyVector(v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector(v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
}
|
|
if (max>=1000) {
|
|
f = TryFlybyVector('0 0 0' - v_up + v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up - v_forward + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up + v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up - v_forward - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
}
|
|
if (max>=1000) {
|
|
f = TryFlybyVector('0 0 0' - v_up + v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up + v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up - v_forward);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
f = TryFlybyVector('0 0 0' - v_up - v_right);
|
|
if (f<max) {
|
|
max = f;
|
|
vec = trace_endpos;
|
|
}
|
|
}
|
|
if (max >= 1000) {
|
|
CamGoIdle();
|
|
self.pausetime = time + 2;
|
|
return;
|
|
}
|
|
self.dest1 = CamFlybyTarget(self.enemy);
|
|
self.dest = vec;
|
|
self.state = CAM_FLYBY;
|
|
self.lip = 1.5 * vlen(self.dest - self.dest1);
|
|
if (self.lip < 500) self.lip = 500;
|
|
CamUpdatePos(0,0);
|
|
};
|
|
|
|
// get FOLLOW mode position
|
|
|
|
vector() GetFollowCam =
|
|
{
|
|
local vector vec, vec2;
|
|
|
|
CamVectors(self.enemy);
|
|
vec = self.enemy.origin + (self.enemy.maxs_z + 4) * v_up;
|
|
traceline(self.enemy.origin,vec,FALSE,self);
|
|
vec = trace_endpos - normalize(vec);
|
|
vec2 = -100 * v_forward;
|
|
traceline(vec,vec+vec2,TRUE,self);
|
|
vec2 = trace_endpos - normalize(vec2);
|
|
if (CamVisible(vec2)) {
|
|
return vec2;
|
|
} else {
|
|
return vec;
|
|
}
|
|
};
|
|
|
|
// get FOLLOW mode targetting point
|
|
|
|
vector() GetFollowTrg =
|
|
{
|
|
local vector vec;
|
|
|
|
if (self.enemy.deadflag == DEAD_NO) {
|
|
CamVectors(self.enemy);
|
|
vec = self.enemy.origin + 2048 * v_forward;
|
|
} else {
|
|
vec = self.enemy.origin;
|
|
}
|
|
return vec;
|
|
};
|
|
|
|
// Move the camera to the point it is looking at
|
|
|
|
void() CamHandJump =
|
|
{
|
|
makevectors(self.v_angle);
|
|
traceline(self.origin,self.origin+2000*v_forward,TRUE,self);
|
|
self.dest=trace_endpos - (5 * normalize(trace_endpos-self.origin));
|
|
self.dest1 = self.origin;
|
|
CamUpdatePos(0,0);
|
|
};
|
|
|
|
// find n-th saved position
|
|
|
|
entity(float n) CamGetPos =
|
|
{
|
|
local entity ent;
|
|
|
|
ent = find(world,classname,"CamSavedPos");
|
|
while((n>0) && (ent!=world)) {
|
|
ent = find(ent,classname,"CamSavedPos");
|
|
n = n - 1;
|
|
}
|
|
return ent;
|
|
};
|
|
|
|
// save actual position and angles
|
|
|
|
void(float n) CamSavePos =
|
|
{
|
|
local entity ent;
|
|
|
|
ent = CamGetPos(n);
|
|
if (ent!=world) {
|
|
ent.angles = self.angles;
|
|
ent.v_angle = self.v_angle;
|
|
setorigin(ent,self.origin);
|
|
}
|
|
};
|
|
|
|
// go to saved position
|
|
|
|
void(float n) CamLoadPos =
|
|
{
|
|
local entity ent;
|
|
|
|
if ((self.state == CAM_HAND) || (self.state == CAM_FREE) || (self.state == CAM_NOCLIP)) {
|
|
ent = CamGetPos(n);
|
|
if (ent!=world) {
|
|
self.fixangle = TRUE;
|
|
self.angles = ent.angles;
|
|
self.v_angle = ent.v_angle;
|
|
setorigin(self,ent.origin);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Impulse handling
|
|
|
|
void() DisplayRules; //POX - predeclare
|
|
|
|
void() CamImpulses =
|
|
{
|
|
local float c;
|
|
local entity ent;
|
|
|
|
//POX - Added so server rules are accessable in autocam mode
|
|
if (self.impulse == 253)
|
|
DisplayRules();
|
|
|
|
// Choose target automatically
|
|
if (self.impulse == 100) {
|
|
self.goalentity = world;
|
|
CamGoIdle();
|
|
// Choose target manually
|
|
} else if ((self.impulse > 100) && (self.impulse <150)) {
|
|
self.goalentity = world;
|
|
c = self.impulse - 100;
|
|
ent = world;
|
|
do {
|
|
ent = CamCycle(ent);
|
|
c = c - 1;
|
|
} while ((c>0) && (ent != world));
|
|
if (ent != world) {
|
|
if (ent.deadflag == DEAD_NO) {
|
|
CamInitFlybyMode(ent);
|
|
self.delay = time + 10;
|
|
self.search_time = time + 60; // keep target max. 60 seconds
|
|
}
|
|
}
|
|
// Save position
|
|
} else if((self.impulse>=180) && (self.impulse<=189)) {
|
|
c = self.impulse - 180;
|
|
CamSavePos (c);
|
|
// Load position
|
|
} else if((self.impulse>=190) && (self.impulse<=199)) {
|
|
c = self.impulse - 190;
|
|
CamLoadPos (c);
|
|
// Force FLYBY
|
|
} else if (self.impulse == 200) {
|
|
if (self.enemy != self) {
|
|
CamInitFlybyMode(self.enemy);
|
|
} else {
|
|
ent = CamShoot();
|
|
if (ent != world) {
|
|
CamInitFlybyMode(ent);
|
|
self.delay = time + 10;
|
|
self.search_time = time + 60; // keep target max. 60 seconds
|
|
}
|
|
}
|
|
// Force FOLLOW
|
|
} else if (self.impulse == 201) {
|
|
if (self.enemy != self) {
|
|
self.state = CAM_FOLLOW;
|
|
} else {
|
|
ent = CamShoot();
|
|
if (ent != world) {
|
|
self.movetarget = self.enemy = ent;
|
|
if (self.aflag) {
|
|
CamReport(self.enemy,"Now Taking");
|
|
}
|
|
self.state = CAM_FOLLOW;
|
|
self.search_time = time + 60; // keep target max. 60 seconds
|
|
self.mangle = '0 0 0';
|
|
}
|
|
}
|
|
// Force HAND
|
|
} else if (self.impulse == 202) {
|
|
if ((self.state != CAM_FLYBY) && (self.state != CAM_FIXED)) CamHandJump();
|
|
self.state = CAM_HAND;
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
// Force FREE (FLY)
|
|
} else if (self.impulse == 203) {
|
|
self.state = CAM_FREE;
|
|
self.movetype = MOVETYPE_FLY;
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
// Force NOCLIP
|
|
} else if (self.button2) {//(self.impulse == 204) { //POX v1.2 - JumpButton Forces NOCLIP
|
|
self.state = CAM_NOCLIP;
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
self.movetype = MOVETYPE_NOCLIP;
|
|
// Force FIXED
|
|
} else if (self.impulse == 205) {
|
|
if ((self.state == CAM_FLYBY) || (self.state == CAM_HAND)) {
|
|
CamSetAuto();
|
|
self.state = CAM_FIXED;
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
self.search_time = time + 100000;
|
|
self.pausetime = time + 3;
|
|
}
|
|
// Messages ON/OFF
|
|
} else if (self.impulse == 210) {
|
|
self.aflag = (float)!self.aflag; //FIXME qfcc issue
|
|
// Report position
|
|
} else if (self.impulse == 211) {
|
|
bprint("Current camera position: ");
|
|
bprint(vtos(self.origin));
|
|
bprint("\n");
|
|
// Messages ON
|
|
} else if (self.impulse == 212) {
|
|
self.aflag = TRUE;
|
|
// Messages OFF
|
|
} else if (self.impulse == 213) {
|
|
self.aflag = FALSE;
|
|
// FOLLOW always forced
|
|
} else if (self.impulse == 214) {
|
|
self.attack_state = TRUE;
|
|
// FOLLOW force normal
|
|
} else if (self.impulse == 215) {
|
|
self.attack_state = FALSE;
|
|
// Lock on current target
|
|
} else if (self.impulse == 216) {
|
|
if (self.enemy != self) {
|
|
self.goalentity = self.enemy;
|
|
}
|
|
// Switch to next target
|
|
} else if (self.button0) {//(self.impulse == 217) { //POX v1.2 - FireButton Switches Player
|
|
if (self.enemy != self) {
|
|
ent=CamCycle(world);
|
|
while ((ent != world) && (ent != self.enemy)) {
|
|
ent = CamCycle(ent);
|
|
}
|
|
if (ent != world) {
|
|
c = TRUE;
|
|
while (c) {
|
|
ent = CamCycle(ent);
|
|
if (ent == world) {
|
|
ent = CamCycle(ent);
|
|
}
|
|
if (ent == world) {
|
|
c = FALSE;
|
|
} else {
|
|
if (ent.deadflag == DEAD_NO) {
|
|
if (self.goalentity != world) {
|
|
self.goalentity = ent;
|
|
}
|
|
CamInitFlybyMode(ent);
|
|
self.delay = time + 10;
|
|
self.search_time = time + 60; // keep target max. 60 seconds
|
|
c = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.impulse = 0;
|
|
self.button0 = 0;//POX v1.2 - clear button hits
|
|
self.button2 = 0;//POX v1.2 - clear button hits
|
|
};
|
|
|
|
// Update camera state according to current target
|
|
|
|
void() CamUpdValues =
|
|
{
|
|
local float it;
|
|
|
|
if ((self.enemy != self) && (self.enemy.classname != "bodyque") && (self.enemy.health > 0)) {
|
|
// Mask out items which change view color
|
|
it = IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD;
|
|
self.items &= ~it;
|
|
self.health = self.enemy.health;
|
|
self.armorvalue = self.enemy.armorvalue;
|
|
self.ammo_shells = self.enemy.ammo_shells;
|
|
self.ammo_nails = self.enemy.ammo_nails;
|
|
self.ammo_rockets = self.enemy.ammo_rockets;
|
|
self.ammo_cells = self.enemy.ammo_cells;
|
|
self.weapon = self.enemy.weapon;
|
|
self.currentammo = self.enemy.currentammo;
|
|
} else {
|
|
self.items = 0;
|
|
self.health = 111;
|
|
self.armorvalue = 111;
|
|
self.ammo_shells = 111;
|
|
self.ammo_nails = 111;
|
|
self.ammo_rockets = 111;
|
|
self.ammo_cells = 111;
|
|
self.weapon = IT_BONESAW;
|
|
self.currentammo = 111;
|
|
}
|
|
};
|
|
|
|
// Overall health
|
|
|
|
float(entity ent) CamHealthVal =
|
|
{
|
|
local float alldmg;
|
|
|
|
// First evaluate max damage if infinite armor
|
|
alldmg = ent.health/(1-ent.armortype);
|
|
// Test for less armor
|
|
if (alldmg>(ent.health+ent.armorvalue)) {
|
|
alldmg = ent.health+ent.armorvalue;
|
|
}
|
|
// And return maximum damage entity can bear
|
|
return alldmg;
|
|
};
|
|
|
|
// IDLE mode think routine
|
|
|
|
void() CamIdleThink =
|
|
{
|
|
local entity ent,ent2;
|
|
local vector vec,vec2;
|
|
local float p1,p2;
|
|
local float pa,pb;
|
|
|
|
// Prepare a sentinel
|
|
ent2 = self;
|
|
if (self.movetarget != world) {
|
|
if (self.movetarget.deadflag == DEAD_NO) {
|
|
ent2 = self.movetarget;
|
|
}
|
|
}
|
|
// try to find some interesting target
|
|
p2 = -1;
|
|
p1 = 5 * random();
|
|
if (self.goalentity != world) {
|
|
if (self.goalentity.deadflag == DEAD_NO) {
|
|
ent2 = self.goalentity;
|
|
} else {
|
|
ent2 = self;
|
|
}
|
|
} else if (p1 < 1) {
|
|
ent = CamCycle(world);
|
|
while (ent != world) {
|
|
if ((ent != self.movetarget) && (ent.deadflag == DEAD_NO)) {
|
|
p1 = CamHealthVal(ent);
|
|
if (p2<p1) {
|
|
ent2 = ent;
|
|
p2 = p1;
|
|
}
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
} else if (p1 < 2) {
|
|
ent = CamCycle(world);
|
|
while (ent != world) {
|
|
// Dead or dying are not interesting at this point
|
|
if ((ent != self.movetarget) && (ent.deadflag == DEAD_NO)) {
|
|
p1 = ent.frags;
|
|
if (p1 < 0) p1 = 0;
|
|
if (p2<p1) {
|
|
ent2 = ent;
|
|
p2 = p1;
|
|
}
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
} else {
|
|
p2 = 0;
|
|
ent = CamCycle(world);
|
|
while (ent != world) {
|
|
if ((ent != self.movetarget) && (ent.deadflag == DEAD_NO)) {
|
|
p2 = p2 + 1;
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
if (p2 > 0) {
|
|
p2 = p2 * random();
|
|
ent = world;
|
|
do {
|
|
do {
|
|
ent = CamCycle(ent);
|
|
} while ((ent == self.movetarget) || (ent.deadflag != DEAD_NO));
|
|
p2 = p2 - 1;
|
|
} while (p2 > 0);
|
|
if (ent != world) ent2 = ent;
|
|
}
|
|
}
|
|
// If an interesting object found
|
|
if (ent2 != self) {
|
|
// Go to FlyBy mode
|
|
CamInitFlybyMode (ent2);
|
|
self.delay = time + 10;
|
|
self.search_time = time + 60;
|
|
} else {
|
|
// Idle moves
|
|
self.enemy = self;
|
|
traceline(self.dest, self.dest1, TRUE, self);
|
|
self.dest1 = trace_endpos;
|
|
vec_x = 40 * random () - 20;
|
|
vec_y = 360 * random ();
|
|
vec_z = 0;
|
|
pa = vec_y;
|
|
if (self.origin != self.dest) {
|
|
vec2 = self.origin - self.dest;
|
|
vec2 = vectoangles(vec2);
|
|
pb = vec2_y;
|
|
} else {
|
|
pb = 0;
|
|
}
|
|
pa = CamReAngle(pa);
|
|
makevectors(vec);
|
|
vec = 2000 * v_forward;
|
|
p2 = vlen(self.dest2 - self.dest);
|
|
if (fabs(CamReAngle(pa - pb)) > 60) {
|
|
vec2 = self.dest + vec;
|
|
traceline(self.dest, vec2, TRUE, self);
|
|
vec2 = trace_endpos;
|
|
p1 = vlen(vec2 - self.dest);
|
|
if (p1 > p2) {
|
|
self.dest2 = vec2;
|
|
p2 = p1;
|
|
}
|
|
}
|
|
pa = pa + 180;
|
|
pa = CamReAngle(pa);
|
|
if (fabs(CamReAngle(pa - pb)) > 60) {
|
|
vec2 = self.dest - vec;
|
|
traceline(self.dest, vec2, TRUE, self);
|
|
vec2 = trace_endpos;
|
|
p1 = vlen(vec2 - self.dest);
|
|
if (p1 > p2) {
|
|
self.dest2 = vec2;
|
|
p2 = p1;
|
|
}
|
|
}
|
|
if (self.pausetime < time) {
|
|
self.pausetime = time + 3 + (2 * random());
|
|
self.dest1 = self.dest2;
|
|
}
|
|
if (self.search_time < time) {
|
|
vec = self.dest - self.dest1;
|
|
vec = (5 + (50 * random())) * normalize(vec);
|
|
vec = self.dest1 + vec;
|
|
traceline(self.origin,vec, TRUE, self);
|
|
if (trace_fraction == 1) {
|
|
self.dest = vec;
|
|
self.dest2 = self.dest1;
|
|
self.search_time = time + 8 + (5 * random());
|
|
} else {
|
|
self.dest2 = self.dest;
|
|
self.search_time = time + 1 + random();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// FLYBY mode thinking routine
|
|
|
|
void() CamFlyByThink =
|
|
{
|
|
local float p0,p1,grad,p;
|
|
local vector vec;
|
|
local entity ent;
|
|
|
|
// self.dest1 is set to player
|
|
// Check if the player is not dead
|
|
if (self.enemy.deadflag != DEAD_NO) {
|
|
if (time < 31) {
|
|
CamGoIdle();
|
|
return;
|
|
}
|
|
CamGoDeath();
|
|
return;
|
|
}
|
|
// All other restrictions apply after some time
|
|
// Test player visibility
|
|
// If player not visible place another camera
|
|
p1 = vlen(self.dest - self.enemy.origin);
|
|
if ((CamVisibleEnt(self.enemy) && (self.lip > p1)) || (self.pausetime > time)) {
|
|
self.dest1 = CamFlybyTarget(self.enemy);
|
|
} else {
|
|
CamInitFlybyMode (self.enemy);
|
|
}
|
|
// Check for FOLLOW mode change
|
|
// Player has to be near
|
|
// And looking away
|
|
// And the destination point must be visible
|
|
if (self.attack_state) {
|
|
self.state = CAM_FOLLOW;
|
|
return;
|
|
} else {
|
|
p1 = vlen(self.enemy.origin - self.origin);
|
|
if (p1 < 170) {
|
|
// Player is near enough
|
|
// Now if it points away
|
|
grad = fabs(self.enemy.angles_y - self.angles_y);
|
|
if (grad > 180) grad = 360 - grad;
|
|
if (grad < 30) {
|
|
// Calculate following point
|
|
vec = GetFollowCam();
|
|
if (CamVisible(vec)) {
|
|
self.state = CAM_FOLLOW;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// check for FIXED mode change
|
|
if (self.goalentity == world) {
|
|
p0 = 0;
|
|
p1 = 0;
|
|
if (self.delay < time) {
|
|
ent = CamCycle(world);
|
|
while (ent != world) {
|
|
p0 = p0 + 1;
|
|
if (ent.deadflag == DEAD_NO) {
|
|
p = vlen(ent.origin - self.origin);
|
|
if (p < 1500) {
|
|
if (CamVisible(ent.origin)) {
|
|
p1 = p1 + 1;
|
|
}
|
|
}
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
}
|
|
if (p0<4) {
|
|
p0 = 2;
|
|
} else if (p0<8) {
|
|
p0 = 3;
|
|
} else {
|
|
p0 = 4;
|
|
}
|
|
if (p1 > p0) {
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
self.state = CAM_FIXED;
|
|
self.search_time = time + 15;
|
|
self.pausetime = time + 3;
|
|
return;
|
|
}
|
|
}
|
|
if (self.search_time < time) {
|
|
CamGoIdle();
|
|
}
|
|
};
|
|
|
|
// FOLLOW mode thinking routine
|
|
|
|
void() CamFollowThink =
|
|
{
|
|
// Check player death
|
|
if (self.enemy.deadflag != DEAD_NO) {
|
|
if (time < 31) {
|
|
CamGoIdle();
|
|
return;
|
|
}
|
|
CamGoDeath();
|
|
return;
|
|
} else {
|
|
if (CamVisibleEnt(self.enemy)) {
|
|
self.dest = GetFollowCam();
|
|
self.dest1 = GetFollowTrg();
|
|
if (pointcontents(self.dest) == CONTENT_SOLID) CamInitFlybyMode(self.enemy);
|
|
} else {
|
|
CamInitFlybyMode(self.enemy);
|
|
}
|
|
}
|
|
if (self.search_time < time) {
|
|
CamGoIdle();
|
|
}
|
|
};
|
|
|
|
// FIXED mode thinking routine
|
|
|
|
void() CamFixedThink =
|
|
{
|
|
local vector vec;
|
|
local entity ent;
|
|
local float cang;
|
|
local float a;
|
|
local float cscr; // count of targets on screen
|
|
local float maxscr; // minimum angle on screen
|
|
local float minscr; // maximum angle on screen
|
|
local float maxlo; // maximum angle of lowers
|
|
local float minhi; // minimum angle of highers
|
|
local vector scrv; // onscreen origin sum
|
|
local vector hiv = '0 0 0'; // lower optimum
|
|
local vector lov = '0 0 0'; // higher optimum
|
|
local float c;
|
|
|
|
cang = self.v_angle_y;
|
|
cscr = 0;
|
|
maxscr = -45;
|
|
minscr = 45;
|
|
maxlo = -181;
|
|
minhi = 181;
|
|
scrv = '0 0 0';
|
|
c = 0;
|
|
ent = CamCycle(world);
|
|
while (ent != world) {
|
|
if (ent.deadflag == DEAD_NO) {
|
|
a = vlen(ent.origin - self.origin);
|
|
if (a < 1500) {
|
|
if (CamVisible(ent.origin)) {
|
|
// Possible target is visible
|
|
// Look if it is "on screen"
|
|
c = c + 1;
|
|
vec = vectoangles(ent.origin - self.origin);
|
|
a = vec_y - cang;
|
|
while (a > 180) a = a - 360;
|
|
while (a < -180) a = a + 360;
|
|
if (fabs(a)<45) {
|
|
// entity is "on screen"
|
|
cscr = cscr + 1;
|
|
scrv = scrv + ent.origin;
|
|
if (a < minscr) minscr = a;
|
|
if (a > maxscr) maxscr = a;
|
|
} else {
|
|
if (a > 0) {
|
|
if (a < minhi) {
|
|
minhi = a;
|
|
hiv = ent.origin;
|
|
}
|
|
} else {
|
|
if (a > maxlo) {
|
|
maxlo = a;
|
|
lov = ent.origin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ent = CamCycle(ent);
|
|
}
|
|
if (c > 0) {
|
|
if (c > 1) self.pausetime = time + 3;
|
|
if (cscr < c) {
|
|
if ((cscr == 0) || ((maxscr - maxlo) <= 90) || ((minhi - minscr) <= 90)) {
|
|
cscr = cscr + 1;
|
|
if ((maxscr - maxlo) > (minhi - minscr)) {
|
|
scrv = scrv + hiv;
|
|
} else {
|
|
scrv = scrv + lov;
|
|
}
|
|
}
|
|
}
|
|
scrv_x = scrv_x / cscr;
|
|
scrv_y = scrv_y / cscr;
|
|
scrv_z = scrv_z / cscr;
|
|
self.dest1 = scrv;
|
|
}
|
|
self.dest = self.origin;
|
|
if ((self.pausetime < time) || (self.search_time < time)) {
|
|
CamGoIdle();
|
|
}
|
|
};
|
|
|
|
// DEATH mode think
|
|
void() CamDeathThink =
|
|
{
|
|
local float f;
|
|
|
|
if (self.search_time < time) {
|
|
CamGoIdle();
|
|
return;
|
|
}
|
|
if (self.enemy != self) {
|
|
f = vlen(self.origin - self.dest);
|
|
if ((f>1) || (self.mangle != '0 0 0')) {
|
|
f = time + 0.5;
|
|
if (self.pausetime < f) {
|
|
self.pausetime = f;
|
|
}
|
|
}
|
|
}
|
|
if (time > self.pausetime) {
|
|
CamGoIdle();
|
|
} else {
|
|
if (self.enemy != self) {
|
|
self.dest1 = self.enemy.origin;
|
|
}
|
|
}
|
|
if (self.enemy != self) {
|
|
traceline(self.origin,self.dest1,TRUE,self);
|
|
if (trace_fraction != 1){
|
|
if (self.enemy.velocity == '0 0 0') {
|
|
CamGoIdle();
|
|
} else {
|
|
CamInitFlybyMode(self.enemy);
|
|
self.state = CAM_DEATH;
|
|
self.pausetime = time + 2;
|
|
}
|
|
}
|
|
if (self.enemy.velocity != '0 0 0') {
|
|
self.pausetime = time + 2;
|
|
}
|
|
}
|
|
if (vlen(self.dest - self.dest1)>60) {
|
|
self.dest = self.dest - self.dest1;
|
|
self.dest = normalize(self.dest);
|
|
self.dest = 59 * self.dest + self.dest1;
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// interface functions begin here
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Main camera thinking routine
|
|
// Called every frame
|
|
|
|
void() CamThink =
|
|
{
|
|
// Update visible values (health, armor, ...)
|
|
CamUpdValues();
|
|
self.velocity = '0 0 0';
|
|
self.avelocity = '0 0 0';
|
|
// Status depending handlings
|
|
if (self.state == CAM_IDLE) {
|
|
CamIdleThink();
|
|
CamUpdatePos(1,80);
|
|
} else if (self.state == CAM_FLYBY) {
|
|
CamFlyByThink();
|
|
CamUpdatePos(0,500);
|
|
} else if (self.state == CAM_FOLLOW) {
|
|
CamFollowThink();
|
|
CamUpdatePos(800,1000);
|
|
} else if (self.state == CAM_FIXED) {
|
|
CamFixedThink();
|
|
CamUpdatePos(0,80);
|
|
} else if (self.state == CAM_DEATH) {
|
|
CamDeathThink();
|
|
CamUpdatePos(100,150);
|
|
// } else if (self.state == CAM_HAND) { -- nothing to do
|
|
// } else if (self.state == CAM_FREE) { -- nothing to do
|
|
// } else if (self.state == CAM_NOCLIP) { -- nothing to do
|
|
// } else if (self.state == ...
|
|
}
|
|
// Impulse handling (standard is not active)
|
|
CamImpulses();
|
|
};
|
|
|
|
// camera initialization
|
|
|
|
void() CamClientInit =
|
|
{
|
|
self.classname = "KasCam";
|
|
setmodel (self, string_null);
|
|
self.weaponmodel = string_null;
|
|
setsize (self, '0 0 0', '0 0 0');
|
|
self.velocity = '0 0 0';
|
|
self.view_ofs = '0 0 0';
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
self.takedamage = DAMAGE_NO;
|
|
self.fixangle = TRUE;
|
|
self.nextthink = -1;
|
|
self.colormap = 0;
|
|
self.effects = 0;
|
|
self.rad_time = 1;
|
|
self.radsuit_finished = 0;
|
|
self.invincible_time = 0;
|
|
self.invincible_finished = 0;
|
|
self.invisible_time = 0;
|
|
self.invisible_finished = 0;
|
|
self.super_time = 0;
|
|
self.super_damage_finished = 0;
|
|
self.dest = self.origin;
|
|
makevectors(self.v_angle);
|
|
self.dest1 = self.dest + 100 * v_forward;
|
|
self.enemy = self;
|
|
self.movetarget = world;
|
|
self.goalentity = world;
|
|
self.aflag = TRUE;
|
|
self.mangle = '0 0 0';
|
|
self.pausetime = time;
|
|
self.delay = time;
|
|
self.attack_state = FALSE;
|
|
|
|
// + POX v1.11 - Late LMS joiner...
|
|
if (self.LMS_late)
|
|
{
|
|
centerprint (self, "Last Man Standing Game in progress...\nObserving until next round.");
|
|
self.attack_finished = time + 10;//Pause the KasCam report function
|
|
stuffcmd (self, "wait;+lookup;wait;+lookdown\n");
|
|
}
|
|
else if (self.LMS_registered)
|
|
{
|
|
centerprint (self, "You have been Eliminated!\nObserving until next round.");
|
|
self.attack_finished = time + 10;//Pause the KasCam report function
|
|
stuffcmd (self, "wait;+lookup;wait;+lookdown\n");
|
|
}
|
|
// - POX v1.11
|
|
|
|
CamGoIdle();
|
|
//bprint("Camera running\n");
|
|
};
|
|
|
|
// Main initialization
|
|
|
|
void() CamSpawn =
|
|
{
|
|
local float i;
|
|
local entity ent;
|
|
local entity start;
|
|
|
|
// find startpoint
|
|
start=find(world,classname,"info_player_start");
|
|
// init saved positions
|
|
i=10;
|
|
while (i>0) {
|
|
ent = spawn();
|
|
ent.classname = "CamSavedPos";
|
|
ent.angles = start.angles;
|
|
ent.v_angle = start.v_angle;
|
|
|
|
//POX - 101b - defaults to player model?
|
|
setmodel (ent, string_null);
|
|
setorigin(ent, start.origin);
|
|
i = i - 1;
|
|
}
|
|
};
|
|
|
|
// Player is disconnecting
|
|
|
|
float() CamDisconnect =
|
|
{
|
|
local entity cam,oself;
|
|
|
|
if (self.classname == "KasCam") {
|
|
bprint("Camera deactivated\n");
|
|
return FALSE;
|
|
} else {
|
|
cam = find(world,classname,"KasCam");
|
|
while (cam) {
|
|
if (cam.enemy == self) {
|
|
oself = self;
|
|
self = cam;
|
|
CamGoIdle();
|
|
self = oself;
|
|
}
|
|
if (cam.movetarget == self) {
|
|
cam.movetarget = world;
|
|
}
|
|
cam=find(cam,classname,"KasCam");
|
|
}
|
|
return TRUE;
|
|
}
|
|
};
|
|
|
|
void(entity ent,entity que) CamCopyBody =
|
|
{
|
|
local entity oself,cam;
|
|
|
|
cam=find(world,classname,"KasCam");
|
|
while (cam) {
|
|
if (ent == cam.enemy) {
|
|
// target moved to body queue
|
|
if (cam.state != CAM_DEATH) {
|
|
oself = self;
|
|
self = cam;
|
|
CamGoDeath();
|
|
self = oself;
|
|
}
|
|
cam.enemy = que;
|
|
} else if (que == cam.enemy) {
|
|
// queue target used elsewhere
|
|
cam.enemy = cam;
|
|
}
|
|
cam=find(cam,classname,"KasCam");
|
|
}
|
|
};
|
|
|