//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 integer(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 integer(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 integer 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= 1000) { f = TryFlybyVector(v_up + v_forward); if (f=1000) { f = TryFlybyVector(v_forward + v_right); if (f=1000) { f = TryFlybyVector(v_forward); if (f=1000) { f = TryFlybyVector('0 0 0' - v_up + v_forward + v_right); if (f=1000) { f = TryFlybyVector('0 0 0' - v_up + v_forward); if (f= 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 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"); } };