diff -urBb --new-file ..\quake-c\src106/chasecam.qc .\src/chasecam.qc --- ..\quake-c\src106/chasecam.qc Thu Jan 1 00:00:00 1970 +++ .\src/chasecam.qc Sat Dec 21 11:13:26 1996 @@ -0,0 +1,1413 @@ +/* + ##################### + ### chase cam mod ### + Rob Albin, 09 Sep 96 + + orig functions modified: + WEAPONS.QC + W_SetCurrentAmmo + ImpulseCommands + CLIENT.QC + SetChangeParms + SetNewParms + DecodeLevelParms + PutClientInServer + +// ref. defs.qc + +// message protocol defines +float SVC_SETVIEWPORT = 5; +float SVC_SETVIEWANGLES = 10; + +// free player entity variable +// '.float speed' bit-flag defines: +float HUD_CENTER_PRINT = 32; +float HUD_ON = 16; +float CHSCAM_MONSTER = 8; +float CHSCAM_ON = 4; +float LASERTARG_LIT = 2; +float LASERTARG_ON = 1; +*/ + + +void() Keep_cam_chasing_owner; +void( float opt ) Remove_chase_cam; + +// (moved to defs.qc) +//float chasecam_dist = 118, chasecam_zmult = 0.30000, chasecam_zofs = 24; +// set in client.qc for initial values + +// Resets weapon model after changing view +// called by chase cam or player entities +void( entity player_ent ) Chase_cam_setweapon = +{ + local entity e; + + e = self; + self = player_ent; + W_SetCurrentAmmo (); + self = e; +}; + +// called either by player or chase cam entities (to restart) +void( entity cam_owner ) Start_chase_cam = +{ + + local entity chase_cam; + + chase_cam = spawn(); + + chase_cam.owner=cam_owner; + + // turn on bit-flag + chase_cam.owner.speed = chase_cam.owner.speed | CHSCAM_ON; + + chase_cam.solid = SOLID_NOT; + chase_cam.movetype = MOVETYPE_FLY; + + + chase_cam.angles = chase_cam.owner.angles; + + setmodel (chase_cam, "progs/eyes.mdl" ); + setsize (chase_cam, '0 0 0', '0 0 0'); + setorigin( chase_cam, chase_cam.owner.origin ); + chase_cam.classname = "chase_cam"; + + chase_cam.nextthink = time + 0.1; + chase_cam.think = Keep_cam_chasing_owner; + + msg_entity = chase_cam.owner; // target of message + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity (MSG_ONE, chase_cam); // view port + + Chase_cam_setweapon( cam_owner ); + + // distance clipping + chase_cam.ammo_shells = chasecam_dist / 3; // chasecam_dist; + +}; + +// secondary think for cam entities +void() Reable_chase_cam = +{ + self.nextthink = time + 0.1; + + // debug + // sprint( self.owner, "Reable_chase_cam was called\n" ); + + // clears bug of not being able to hit fire to restart + // after dying in water + if (self.owner.health <= 0) + { + remove( self ); + return; + } + + if (self.owner.waterlevel) + return; + + Start_chase_cam( self.owner ); + remove( self ); + +}; + +// called only by chase cam entities +// opt values +// TRUE = remove completely +// FALSE = remove view but keep alive with Reable_chase_cam(); +void( float opt ) Remove_chase_cam = +{ + // turn off bit-flag + self.owner.speed = self.owner.speed - (self.owner.speed & CHSCAM_ON); + + // makes entity appear gone even if going into keep alive state + setmodel( self, "" ); + self.velocity = '0 0 0'; + + // set view-point back to normal + msg_entity = self.owner; // target of message + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity (MSG_ONE, self.owner); // view port + + Chase_cam_setweapon( self.owner ); + + if ( !opt ) + { + self.nextthink = time + 0.1; + self.think = Reable_chase_cam; + } + else + remove( self ); + +}; + +/* + main think function for cam entities + self.ammo_shells = distance clipping + self.ammo_nails = hang-up flag + + .v_angle_x: + 78.8 (max down aim) + -68.9 (max up aim) +*/ +void() Keep_cam_chasing_owner = +{ + local vector goal, dir; + local float dist, cap, + f_f; + //debug var + //local string s; + + self.nextthink = time + 0.1; + + // check if player toggled + // or in water + if (! (self.owner.speed & CHSCAM_ON)) + { + Remove_chase_cam( TRUE ); + return; + } + + if ( self.owner.waterlevel ) + { + if (self.owner.health > 0) + { + Remove_chase_cam( FALSE ); + return; + } + } + + // get player velocity relative to player's + // current yaw + // f_f (based on running calcs (maxspeed = 400) + // (back ~= 640, forward ~= 0) + dir_y = self.owner.v_angle_y; + makevectors( dir ); + f_f = vlen( (v_forward * 320) - self.owner.velocity ); + + // held for use after second makevectors call for + // v_forward based only on yaw + dir = v_forward; + +// local string s; +// sprint( self.owner, "\n\n f_f = " ); +// s = ftos( f_f ); +// sprint( self.owner, s ); +// sprint( self.owner, "\n f_r = " ); +// s = ftos( f_r ); +// sprint( self.owner, s ); +// sprint( self.owner, "\n" ); + + makevectors( self.owner.v_angle ); + + // set spot before clipping + goal = self.owner.origin - (v_forward * self.ammo_shells); + goal_z = goal_z + 14 + (self.ammo_shells * chasecam_zmult); + if (self.owner.v_angle_x > 0) + { + goal = goal + dir * self.owner.v_angle_x; + goal_z = goal_z - (self.owner.v_angle_x * (self.ammo_shells * 0.01)); + if (goal_z < self.owner.origin_z + 28) + goal_z = self.owner.origin_z + 28; + } + else if (goal_z < self.owner.origin_z - 6) + goal_z = self.owner.origin_z - 6; + + traceline (self.owner.origin, goal, FALSE, self.owner); + + // for fading from walls and up-aim auto rising + if (trace_fraction < 1 || self.owner.v_angle_x < -16) + self.ammo_shells = 8 + vlen(trace_endpos - self.owner.origin); + + // avoids most hang-ups along walls + goal = trace_endpos + ( v_forward * 2 ); + + // clip from ceiling if too close + traceline (goal, goal + '0 0 32', FALSE, self.owner ); + if (trace_fraction < 1 ) + { + dir = trace_endpos - '0 0 32'; + + traceline (goal, goal - '0 0 32', FALSE, self.owner ); + if (trace_fraction == 1) + goal = dir; + } + + dir = normalize(goal - self.origin); + dist = vlen(goal - self.origin); + + if ( visible(self.owner) ) + { + self.angles = self.owner.angles; + + cap = dist * 0.2; + + if (cap > 5.2) + self.velocity = dir * dist * 5.2; + else if (cap > 1) + self.velocity = dir * dist * cap; + else + self.velocity = dir * dist; + + // tighten up if owner running backwards + if ( f_f > 560) + { + self.velocity = self.velocity * 2; + } + + } + else + setorigin( self, goal ); + + // fade back from walls + self.ammo_shells = self.ammo_shells + 4.5; + if (self.ammo_shells > chasecam_dist) + self.ammo_shells = chasecam_dist; + + // respawn if missile ent. get's hung up + if( self.oldorigin == self.origin ) + { + if( dist > 30 ) + self.ammo_nails = self.ammo_nails + 1; + if( self.ammo_nails > 2 ) + { + Start_chase_cam( self.owner ); + remove( self ); + return; + } + + } + self.oldorigin = self.origin; + +}; + + +// called by player only +void() Toggle_chase_cam = +{ + + if (self.waterlevel) + return; + + if( (self.speed & CHSCAM_ON) ) + { + // will be noticed by next think + // of player's chase cam entity + self.speed = self.speed - CHSCAM_ON; + } + else + Start_chase_cam( self ); + +}; + +//////////////////////////////////////////// +// laser targeter functions + +// targeter ent. think function + +void() LaserTargeterTrack = { + local vector src; + + if (! (self.owner.speed & LASERTARG_ON)) + { + if( (self.owner.speed & LASERTARG_LIT) ) + { + self.owner.speed = self.owner.speed | LASERTARG_ON; + self.effects = self.effects | EF_DIMLIGHT; + } + else + { + remove( self ); + return; + } + } + + if (chasecam_intermission) + { + setmodel( self, "" ); + self.effects = 0; + } + + makevectors( self.owner.v_angle ); + + src = self.owner.origin + v_forward * 10; + src_z = self.owner.absmin_z + self.owner.size_z * 0.7; + + traceline( src, src + v_forward * 2048, FALSE, self.owner); + + setorigin( self, (0.1 * src + 0.9 * trace_endpos) ); + + self.nextthink = time + 0.05; + +}; + +void( entity targ_owner ) LaserTargeterToggle = +{ + + local entity e; + + if( (targ_owner.speed & LASERTARG_ON) ) + { + if( (targ_owner.speed & LASERTARG_LIT) ) + targ_owner.speed = targ_owner.speed - LASERTARG_LIT; + else + targ_owner.speed = targ_owner.speed | LASERTARG_LIT; + + targ_owner.speed = targ_owner.speed - LASERTARG_ON; + } + else + { + targ_owner.speed = targ_owner.speed | LASERTARG_ON; + + e = spawn(); + e.owner = targ_owner; + + e.movetype = MOVETYPE_NONE; + e.solid = SOLID_NOT; + + setmodel( e, "progs/s_bubble.spr" ); + setsize( e, VEC_ORIGIN, VEC_ORIGIN ); + setorigin( e, e.owner.origin ); + + if( (e.owner.speed & LASERTARG_LIT) ) + e.effects = e.effects | EF_DIMLIGHT; + + e.nextthink = time + 0.1; + e.think = LaserTargeterTrack; + + } + +}; +//////////////////////////////////////////// + +void(entity HUD_owner, float parm_init) Spawn_HUD_entity; +void() Chase_cam_lvlstart_think = +{ + if ( (self.owner.speed & CHSCAM_ON) ) + Start_chase_cam( self.owner ); + + if ( (self.owner.speed & LASERTARG_ON) ) + { + self.owner.speed = self.owner.speed - LASERTARG_ON; + LaserTargeterToggle( self.owner ); + } + + // TRUE flag = level init call (reset on .speed bit-flags) + Spawn_HUD_entity( self.owner, TRUE ); + + remove( self ); +}; + + +// called in CLIENT.QC by void() PutClientInServer +// player.speed is set and saved between levels using parm16 +// in CLIENT.QC +void() Chase_cam_level_start = +{ + + local entity e; + + e = spawn(); + e.owner = self; + e.nextthink = time + 0.2; + e.think = Chase_cam_lvlstart_think; + +}; + + +// opt: +// 0 = minus +// 1 = plus +// 2 = read cvar temp1 for value +void(float opt) Chase_cam_change_dist = +{ + local string s; + + if (!opt) + { + chasecam_dist = chasecam_dist - 2; + if (chasecam_dist < 10) + chasecam_dist = 10; + } + else if (opt == 1) + chasecam_dist = chasecam_dist + 2; + else + chasecam_dist = cvar( "temp1" ); + + sprint( self, "chasecam distance = " ); + s = ftos( chasecam_dist ); + sprint( self, s ); + sprint( self, "\n" ); + + +}; + +// opt: +// 0 = minus +// 1 = plus +// 2 = read cvar temp1 for value +void(float opt) Chase_cam_change_zmult = +{ + local string str; + local float f; + + if (!opt) + { + chasecam_zmult = chasecam_zmult - 0.02; + if (chasecam_zmult < 0) + chasecam_zmult = 0; + } + else if (opt == 1) + chasecam_zmult = chasecam_zmult + 0.02; + else + { + f = cvar( "temp1" ); + chasecam_zmult = f * 0.01; + } + + sprint( self, "chasecam verticle offset = " ); + + // convert and strip for display + // apparantly, + // chasecam_zmult = chasecam_zmult +- 0.02; + // don't _exactly_ add/sub 0.02 + f = chasecam_zmult * 100; + f = rint( f ); + str = ftos( f ); + + sprint( self, str ); + sprint( self, "\n" ); + + +}; + +/* +Reads current values of cvar temp1: +opt: + 0 = into chasecam_dist + 1 = into chasecam_zmult +*/ +void(float opt) Chase_cam_read_temp1 = +{ + if (!opt) + Chase_cam_change_dist(2); + else + Chase_cam_change_zmult(2); + /* + chasecam_dist = cvar( "deathmatch" ); + chasecam_zmult = cvar( "teamplay" ); + + sprint( self, "Chasecam distance: " ); + str = ftos( chasecam_dist ); + sprint( self, str ); + + sprint( self, "\n vertical offset: " ); + str = ftos( chasecam_zmult ); + sprint( self, str ); + + sprint( self, "\n" ); + */ + +}; + + + + +/* ### chase view spy mod ### + orig functions modified: + WEAPONS.QC + W_Attack + ImpulseCommands +*/ + +void() MKeep_cam_chasing_owner; + +float modelindex_eyes, modelindex_player; + +void() MFake_player_think = +{ + local float f; + + self.nextthink = time + 0.1; + + f = self.max_health - self.health; + if (f > 0) + { + self.max_health = self.health; + + // damage here will never reduce player's health to less than 1 HP + // mainly due to bug where player dies in transition back to normal + // view causing client unable to restart by pressing 'attack' key + if (f >= self.owner.health) + f = self.owner.health - 1; + if (f > 0) + T_Damage(self.owner, world, world, f); + if ((self.owner.speed & CHSCAM_ON)) + self.owner.speed = self.owner.speed - CHSCAM_ON; + return; + } + + if ((self.owner.speed & CHSCAM_ON)) + return; + + self.solid = SOLID_NOT; + + // player appear + self.owner.angles = self.angles; + self.owner.fixangle = 1; + self.owner.solid = SOLID_SLIDEBOX; + self.owner.movetype = MOVETYPE_WALK; + setmodel (self.owner, "progs/eyes.mdl"); + modelindex_eyes = self.owner.modelindex; + setmodel (self.owner, "progs/player.mdl"); + modelindex_player = self.owner.modelindex; + setsize (self.owner, VEC_HULL_MIN, VEC_HULL_MAX); + setorigin( self.owner, self.origin ); + //////////////// + + self.owner.flags = self.owner.flags - (self.owner.flags & FL_NOTARGET); + + remove( self ); + +}; + +void( entity cam_owner, vector cam_pos ) MFake_player_appear = +{ + local entity e; + local vector plr_pos, plr_angles; + + plr_pos = cam_owner.origin; + plr_angles = cam_owner.angles; + + // player vanish + cam_owner.flags = cam_owner.flags | FL_NOTARGET; + cam_owner.solid = SOLID_NOT; + cam_owner.movetype = MOVETYPE_NONE; + setmodel (cam_owner, ""); + modelindex_eyes = cam_owner.modelindex; + modelindex_player = cam_owner.modelindex; + + cam_owner.angles = cam_owner.goalentity.angles; + cam_owner.fixangle = 1; + // position player to chase view's location + setsize (cam_owner, VEC_ORIGIN, VEC_ORIGIN); + setorigin( cam_owner, cam_pos ); + //////////////// + + + e = spawn(); + e.owner = cam_owner; + e.classname = "fake_player"; + e.angles = plr_angles; + + e.solid = SOLID_SLIDEBOX; + e.movetype = MOVETYPE_STEP; + setmodel (e, "progs/player.mdl"); + setsize (e, VEC_HULL_MIN, VEC_HULL_MAX); + e.health = cam_owner.health; + e.max_health = cam_owner.health; + + // called by quake if entity's health drops to 0 + e.th_die = SUB_Null; + + e.takedamage = DAMAGE_AIM; + e.view_ofs = '0 0 25'; + + e.nextthink = time + 0.1; + e.think = MFake_player_think; + + // position fake player on player's original spot + e.origin = plr_pos; + e.origin_z = e.origin_z + 1; // raise off floor a bit + droptofloor(); + + // debug line + //self.goalentity = e; + +}; + +// called either by player or chase cam entities (to restart) +//opt: +// 0 = don't make fake player (called to restart by tracking function) +// 1 = make fake player +void( entity cam_owner, entity g_e, float opt ) MStart_chase_cam = +{ + + local entity chase_cam; + + cam_owner.goalentity = g_e; + + chase_cam = spawn(); + + chase_cam.owner = cam_owner; + + // turn on bit-flags + chase_cam.owner.speed = chase_cam.owner.speed | CHSCAM_ON; + chase_cam.owner.speed = chase_cam.owner.speed | CHSCAM_MONSTER; + + chase_cam.solid = SOLID_NOT; + chase_cam.movetype = MOVETYPE_FLY; + + chase_cam.angles = cam_owner.goalentity.angles; + + setmodel (chase_cam, "progs/eyes.mdl" ); + setsize (chase_cam, VEC_ORIGIN, VEC_ORIGIN); + setorigin( chase_cam, cam_owner.goalentity.origin ); + + if (opt > 0) + MFake_player_appear( cam_owner, chase_cam.origin ); + + chase_cam.classname = "chase_cam"; + + chase_cam.nextthink = time + 0.1; + chase_cam.think = MKeep_cam_chasing_owner; + + msg_entity = chase_cam.owner; // target of message + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity (MSG_ONE, chase_cam); // view port + + Chase_cam_setweapon( cam_owner ); + + // distance clipping + chase_cam.ammo_shells = chasecam_dist / 3; // chasecam_dist; + +}; + +// called only by chase cam entities +// opt values +// TRUE = remove completely +// FALSE = remove view but keep alive with Reable_chase_cam(); +void( float opt ) MRemove_chase_cam = +{ + // turn off bit-flags + self.owner.speed = self.owner.speed - (self.owner.speed & CHSCAM_ON); + self.owner.speed = self.owner.speed - (self.owner.speed & CHSCAM_MONSTER); + + // makes entity appear gone even if going into keep alive state + setmodel( self, "" ); + self.velocity = '0 0 0'; + + + // set view-point back to normal + msg_entity = self.owner; // target of message + WriteByte (MSG_ONE, SVC_SETVIEWPORT); + WriteEntity (MSG_ONE, self.owner); // view port + + + if (deathmatch || coop) + { + sprint( self.owner.goalentity, self.owner.netname ); + sprint( self.owner.goalentity, " quit tracking\n" ); + } + + Chase_cam_setweapon( self.owner ); + + + remove( self ); + +}; + +// main think function for cam entities +// self.ammo_shells = distance clipping +// self.ammo_nails = hang-up flag + +void() MKeep_cam_chasing_owner = +{ + local vector goal, dir; + local float dist, cap, + f_f; + //debug var + local string s; + + self.nextthink = time + 0.1; + + // check if player toggled + // or in water + if (! (self.owner.speed & CHSCAM_ON)) + { + MRemove_chase_cam( TRUE ); + return; + } + + makevectors( self.owner.goalentity.angles ); + + // set spot before clipping + goal = self.owner.goalentity.origin - (v_forward * self.ammo_shells); + goal_z = goal_z + chasecam_zofs; + + + traceline (self.owner.goalentity.origin, goal, FALSE, self.owner.goalentity); + + // for fading from walls and up-aim auto rising + if (trace_fraction < 1) + self.ammo_shells = 8 + vlen(trace_endpos - self.owner.goalentity.origin); + + // avoids most hang-ups along walls + goal = trace_endpos + ( v_forward * 2 ); + + // clip from ceiling if too close + traceline (goal, goal + '0 0 32', FALSE, self.owner.goalentity ); + if (trace_fraction < 1 ) + { + dir = trace_endpos - '0 0 32'; + + traceline (goal, goal - '0 0 32', FALSE, self.owner.goalentity ); + if (trace_fraction == 1) + goal = dir; + } + + dir = normalize(goal - self.origin); + dist = vlen(goal - self.origin); + + + if ( visible(self.owner.goalentity) ) + { + self.angles = self.owner.goalentity.angles; + + cap = dist * 0.2; + + if (cap > 5.2) + self.velocity = dir * dist * 5.2; + else if (cap > 1) + self.velocity = dir * dist * cap; + else + self.velocity = dir * dist; + } + else + setorigin( self, goal ); + + setorigin( self.owner, self.origin ); + + // fade back from walls + self.ammo_shells = self.ammo_shells + 4.5; + if (self.ammo_shells > chasecam_dist) + self.ammo_shells = chasecam_dist; + + // respawn if missile ent. get's hung up + if( self.oldorigin == self.origin ) + { + if( dist > 30 ) + self.ammo_nails = self.ammo_nails + 1; + if( self.ammo_nails > 2 ) + { + MStart_chase_cam( self.owner, self.owner.goalentity, 0 ); + remove( self ); + return; + } + + } + self.oldorigin = self.origin; + +}; + + +float Mcam_monster_count = 0; +float Mcam_monster_maxcount; + +entity() MChasecam_get_maxcount = +{ + local entity e, ret_e; + local float flg2; + + e = world; + ret_e = world; + Mcam_monster_maxcount = 0; + + if (deathmatch || coop) + { + do + { + e = nextent( e ); + if (e) + { + if (e.health > 0) + { + if (e != self && e.classname == "player") + { + Mcam_monster_maxcount = Mcam_monster_maxcount + 1; + if (Mcam_monster_count == Mcam_monster_maxcount) + ret_e = e; + } + } + } + }while (e); + } + else + { + do + { + e = nextent( e ); + if (e) + { + if (e.health > 0) + { + if ( (e.flags & FL_MONSTER) ) + { + Mcam_monster_maxcount = Mcam_monster_maxcount + 1; + if (Mcam_monster_count == Mcam_monster_maxcount) + ret_e = e; + } + } + } + }while (e); + } + + return ret_e; + +}; + + +//opt: +//0 = back +//1 = forward +void(float opt) Chasecam_find_monster = +{ + local entity e; + local string s; + + // disable if in actual chasecam view + if( (self.speed & CHSCAM_ON) && !(self.speed & CHSCAM_MONSTER) ) + return; + + // don't run unless on the ground + if (! (self.flags & FL_ONGROUND) ) + return; + + + e = MChasecam_get_maxcount(); + if (opt == 0) + { + Mcam_monster_count = Mcam_monster_count - 1; + if (Mcam_monster_count < 1) + Mcam_monster_count = Mcam_monster_maxcount; + } + else + Mcam_monster_count = Mcam_monster_count + 1; + + if (Mcam_monster_count > Mcam_monster_maxcount) + Mcam_monster_count = 1; + e = MChasecam_get_maxcount(); + + if (e != world) + { + s = ftos( Mcam_monster_count ); + sprint( self, s ); + sprint( self, " of " ); + s = ftos( Mcam_monster_maxcount ); + sprint( self, s ); + sprint( self, ": " ); + if (deathmatch || coop) + { + sprint( e, "You are being tracked by " ); + sprint( e, self.netname ); + sprint( e, "\n" ); + + sprint( self, e.netname ); + } + else + { + sprint( self, e.classname ); + } + sprint( self, "\n" ); + + if( (self.speed & CHSCAM_MONSTER) ) + { + self.angles = e.angles; + self.fixangle = 1; + self.goalentity = e; + } + else + MStart_chase_cam( self, e, 1 ); + } + else + { + if (deathmatch || coop) + sprint( self, "No other living players in level...\n" ); + else + sprint( self, "No living monsters found...\n" ); + } + +}; + +// ### chase cam mod ### +// ##################### + +// ############### +// ### HUD mod ### +/* + Axe, Sh, D_brl, Nl, S_Nl, Grnd, Rckt, Ltng +.armortype (0.3, 0.6, 0.8) +.armorvalue +.currentammo +.weapon +*/ + +/* + opt: + 1 = other than health + 2 = health was reason for print +*/ +void( float opt) HUD_print_info = +{ + local string str; + + // reset print conditions + self.dest_x = time + 2.5; + self.ammo_shells = self.owner.ammo_shells; + self.ammo_nails = self.owner.ammo_nails; + self.ammo_rockets = self.owner.ammo_rockets; + self.ammo_cells = self.owner.ammo_cells; + self.health = self.owner.health; + self.armortype = self.owner.armortype; + self.weapon = self.owner.weapon; + + // ammo + sprint( self.owner, "\n" ); + str = ftos( self.ammo_shells ); + sprint( self.owner, str ); + sprint( self.owner, ","); + str = ftos( self.ammo_nails ); + sprint( self.owner, str ); + sprint( self.owner, ","); + str = ftos( self.ammo_rockets ); + sprint( self.owner, str ); + sprint( self.owner, ","); + str = ftos( self.ammo_cells ); + sprint( self.owner, str ); + + // armor + if (self.armortype == 0) str = "\n\n-/"; + else if (self.armortype == 0.3) str = "\n\nG/"; + else if (self.armortype == 0.6) str = "\n\nY/"; + else if (self.armortype == 0.8) str = "\n\nR/"; + else str = "\n\n*/"; + sprint( self.owner, str ); + str = ftos( self.owner.armorvalue ); + sprint( self.owner, str ); + sprint( self.owner, " " ); + + // health + str = ftos( self.health ); + sprint( self.owner, str ); + if (opt == 2) + if ((self.owner.speed & HUD_CENTER_PRINT) && self.health < 101) + centerprint( self.owner, str ); + + // weapon + if ( (self.owner.speed & CHSCAM_ON) ) + { + if (self.weapon == IT_LIGHTNING) str = " Ltng\n"; + else if (self.weapon == IT_ROCKET_LAUNCHER) str = " Rckt\n"; + else if (self.weapon == IT_GRENADE_LAUNCHER) str = " Grnd\n"; + else if (self.weapon == IT_SUPER_NAILGUN) str = " S_Nl\n"; + else if (self.weapon == IT_NAILGUN) str = " Nl\n"; + else if (self.weapon == IT_SUPER_SHOTGUN) str = " D_Brl\n"; + else if (self.weapon == IT_SHOTGUN) str = " Sh\n"; + else if (self.weapon == IT_AXE) str = " Axe\n"; + } + else + str = "\n"; + + sprint( self.owner, str ); +}; + +void() HUD_entity_think = +{ + local float flag; + + self.nextthink = time + 0.3; + + if (! (self.owner.speed & HUD_ON) ) + { + if ( (self.owner.speed & HUD_CENTER_PRINT) ) + { + self.owner.speed = self.owner.speed - HUD_CENTER_PRINT; + sprint( self.owner, "\n\n\n\n" ); + centerprint( self.owner, " " ); + remove( self ); + return; + } + + self.owner.speed = self.owner.speed | HUD_ON; + self.owner.speed = self.owner.speed | HUD_CENTER_PRINT; + centerprint( self.owner, "Center-print health\n\non" ); + } + + if (self.owner.health != self.health) flag = 2; + + else if (self.dest_x < time) flag = 1; + else if (self.owner.ammo_shells != self.ammo_shells) flag = 1; + else if (self.owner.ammo_nails != self.ammo_nails) flag = 1; + else if (self.owner.ammo_rockets != self.ammo_rockets) flag = 1; + else if (self.owner.ammo_cells != self.ammo_cells) flag = 1; + else if (self.owner.armortype != self.armortype) flag = 1; + else if (self.owner.weapon != self.weapon) flag = 1; + // hack to disable backpack messages + else if (HUD_backpack_refresh) + { + HUD_backpack_refresh = FALSE; flag = 1; + } + else + flag = FALSE; + + if (flag) + HUD_print_info( flag ); +}; + +/* + entity .vars used: + .dest_x = time between inactive message refreshes + parm_init: + TRUE/FALSE saved settings in SP +*/ +void(entity HUD_owner, float parm_init) Spawn_HUD_entity = +{ + local entity e; + + // can't bring down screen and stop game time, so disable + if (deathmatch || coop) + return; + + if (parm_init) + { + if ( (HUD_owner.speed & HUD_ON) ) + HUD_owner.speed = HUD_owner.speed - HUD_ON; + else + return; + } + + if ( (HUD_owner.speed & HUD_ON) ) + { + HUD_owner.speed = HUD_owner.speed - HUD_ON; + } + else + { + HUD_owner.speed = HUD_owner.speed | HUD_ON; + e = spawn(); + e.owner = HUD_owner; + e.nextthink = time + 0.3; + e.think = HUD_entity_think; + } + +}; + + + +// ### HUD mod ### +// ############### + + + + + +/* + ###################### + ### Multiskin v1.1 ### + + Orig. functions modifyed: + WEAPONS.QC + ImpulseCommands + CLIENT.QC + PutClientInServer +*/ + +// opt: +// 0 = up +// 1 = down +void( float opt ) Choose_multiskin = +{ + if (opt == 0) + { + self.skin = self.skin + 1; + if (self.skin == 19) + self.skin = 0; + } + else + { + self.skin = self.skin - 1; + if (self.skin == -1) + self.skin = 18; + } + + if (self.skin == 0) + centerprint(self, "SKIN: the Good Guy Himself (1)"); + else if (self.skin == 1) + centerprint(self, "SKIN: Duke Nukem 3d (2)"); + else if (self.skin == 2) + centerprint(self, "SKIN: Mr. Toad (3)"); + else if (self.skin == 3) + centerprint(self, "SKIN: the Stormtrooper (4)"); + else if (self.skin == 4) + centerprint(self, "SKIN: Max (5)"); + else if (self.skin == 5) + centerprint(self, "SKIN: the Terminator (6)"); + else if (self.skin == 6) + centerprint(self, "SKIN: Judge Dredd (7)"); + else if (self.skin == 7) + centerprint(self, "SKIN: Camouflaged soldier (8)"); + else if (self.skin == 8) + centerprint(self, "SKIN: Captain Picard (9)"); + else if (self.skin == 9) + centerprint(self, "SKIN: the Wizzard (10)"); + else if (self.skin == 10) + centerprint(self,"SKIN: the Predator (11)"); + else if (self.skin == 11) + centerprint(self,"SKIN: Skeleton (12)"); + else if (self.skin == 12) + centerprint(self,"SKIN: Wan-Fu (13)"); + else if (self.skin == 13) + centerprint(self,"SKIN: Henry Rollins (14)"); + else if (self.skin == 14) + centerprint(self,"SKIN: He-Man (15)"); + else if (self.skin == 15) + centerprint(self,"SKIN: Boba (16)"); + else if (self.skin == 16) + centerprint(self,"SKIN: Superman (17)"); + else if (self.skin == 17) + centerprint(self,"SKIN: NYPD Cop (18)"); + else if (self.skin == 18) + centerprint(self,"SKIN: Red/Yellow women dude (19)"); +}; + +// ### Multiskin v1.1 ### +// ###################### + +/* + ####################### + ### Hot Key Weapons ### + + Rob Albin, Oct 7 96 + + Orig. functions modifyed: + WEAPONS.QC + ImpulseCommands + + Abstract: + Provides hot-key impulses for axe, grenade, and rocket launcher + that return to previous weapon. + New cycle weapons impulses that don't select these weapons. + + uses free player entity variable (float point of vector) dest_x + (only used for doors) + + + CFG file use: + + alias +axe_hotkey "impulse 41; +attack" + alias -axe_hotkey "impulse 40; -attack" + + alias +grenade_hotkey "impulse 42; +attack" + alias -grenade_hotkey "impulse 40; -attack" + + alias +rocket_hotkey "impulse 43; +attack" + alias -rocket_hotkey "impulse 40; -attack" + + bind "" "+axe_hotkey" + bind "" "+grenade_hotkey" + bind "" "+rocket_hotkey" + + // new weapons cycle commands (skips hot-key weapons) + bind "" "impulse 44" + bind "" "impulse 45" + +*/ + + +// opt: +// 1 = axe +// 2 = grenade launcher +// 3 = rocket launcher +void( float opt ) HotKey_weapon = +{ + local float w; + + if (self.weapon != IT_GRENADE_LAUNCHER && + self.weapon != IT_ROCKET_LAUNCHER && + self.weapon != IT_AXE) + self.dest_x = self.weapon; + + if (opt == 1) + { + w = IT_AXE; + } + else if (opt == 2) + { + if (! (self.items & IT_GRENADE_LAUNCHER) ) + return; + w = IT_GRENADE_LAUNCHER; + } + else + { + if (! (self.items & IT_ROCKET_LAUNCHER) ) + return; + w = IT_ROCKET_LAUNCHER; + } + + self.weapon = w; + W_SetCurrentAmmo(); +}; + +void() HotKey_previous_weapon = +{ + if (! self.dest_x) + return; + + self.weapon = self.dest_x; + W_SetCurrentAmmo(); +}; + +void() HotKey_CycleWeaponCommand = +{ + local float w, it, am, c; + + c = 0; + w = self.weapon; + + it = self.items; + self.impulse = 0; + + while (1) + { + am = 0; + + if (w == IT_LIGHTNING) + { + w = IT_SHOTGUN; + if (self.ammo_shells < 1) + am = 1; + } + else if (w == IT_AXE) + { + w = IT_SHOTGUN; + if (self.ammo_shells < 1) + am = 1; + } + else if (w == IT_SHOTGUN) + { + w = IT_SUPER_SHOTGUN; + if (self.ammo_shells < 2) + am = 1; + } + else if (w == IT_SUPER_SHOTGUN) + { + w = IT_NAILGUN; + if (self.ammo_nails < 1) + am = 1; + } + else if (w == IT_NAILGUN) + { + w = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (w == IT_SUPER_NAILGUN) + { + w = IT_LIGHTNING; + if (self.ammo_cells < 1) + am = 1; + } + else if (w == IT_GRENADE_LAUNCHER) + { + w = IT_LIGHTNING; + if (self.ammo_cells < 1) + am = 1; + } + else if (w == IT_ROCKET_LAUNCHER) + { + w = IT_LIGHTNING; + if (self.ammo_cells < 1) + am = 1; + } + + if ( (it & w) && am == 0) + { + self.weapon = w; + W_SetCurrentAmmo (); + return; + } + + if (c > 8) return; + c = c + 1; + } + +}; + + +void() HotKey_CycleWeaponReverseCommand = +{ + local float w, it, am, c; + + c = 0; + w = self.weapon; + + it = self.items; + self.impulse = 0; + + while (1) + { + am = 0; + + if (w == IT_LIGHTNING) + { + w = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (w == IT_ROCKET_LAUNCHER) + { + w = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (w == IT_GRENADE_LAUNCHER) + { + w = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (w == IT_SUPER_NAILGUN) + { + w = IT_NAILGUN; + if (self.ammo_nails < 1) + am = 1; + } + else if (w == IT_NAILGUN) + { + w = IT_SUPER_SHOTGUN; + if (self.ammo_shells < 2) + am = 1; + } + else if (w == IT_SUPER_SHOTGUN) + { + w = IT_SHOTGUN; + if (self.ammo_shells < 1) + am = 1; + } + else if (w == IT_SHOTGUN) + { + w = IT_LIGHTNING; + if (self.ammo_cells < 1) + am = 1; + } + else if (w == IT_AXE) + { + w = IT_LIGHTNING; + if (self.ammo_cells < 1) + am = 1; + } + + if ( (it & w) && am == 0) + { + self.weapon = w; + W_SetCurrentAmmo (); + return; + } + + if (c > 8) return; + c = c + 1; + } + +}; + +// ### Hot Key Weapons mod ### +// ########################### diff -urBb --new-file ..\quake-c\src106/client.qc .\src/client.qc --- ..\quake-c\src106/client.qc Sun Sep 29 23:29:00 1996 +++ .\src/client.qc Sat Dec 21 11:13:26 1996 @@ -57,8 +60,18 @@ parm5 = self.ammo_nails; parm6 = self.ammo_rockets; parm7 = self.ammo_cells; + parm8 = self.weapon; parm9 = self.armortype * 100; + + // ### chase cam ### + // holds current state of cam and targeter between levels + if( ! deathmatch && ! coop ) + parm16 = self.speed; + parm14 = chasecam_dist; + parm15 = chasecam_zmult; + + }; void() SetNewParms = @@ -72,6 +85,15 @@ parm7 = 0; parm8 = 1; parm9 = 0; + + // ### chase cam ### + // state of cam and targeter held in player.speed + if( ! deathmatch && ! coop ) + parm16 = 0; + // default offsets + parm14 = 118; //dist + parm15 = 0.3; //zmult + }; void() DecodeLevelParms = @@ -91,6 +113,16 @@ self.ammo_cells = parm7; self.weapon = parm8; self.armortype = parm9 * 0.01; + + // ### chase cam ### + // state of cam and targeter held in player.speed + if( ! deathmatch && ! coop ) + self.speed = parm16; + chasecam_dist = parm14; + chasecam_zmult = parm15; + + chasecam_intermission = FALSE; + }; /* @@ -284,6 +316,8 @@ } WriteByte (MSG_ALL, SVC_INTERMISSION); + + chasecam_intermission = TRUE; }; @@ -475,6 +509,9 @@ void() DecodeLevelParms; void() PlayerDie; +// ### chase cam mod ### +void() Chase_cam_level_start; + void() PutClientInServer = { @@ -537,6 +574,45 @@ } spawn_tdeath (self.origin, self); + + // ### chase cam mod ### + // reset view and targeter according to what was set in parm16 + if( ! deathmatch && ! coop ) + Chase_cam_level_start(); + +// ************************************************************************* +// ** ** +// ** M U L T I S K I N 1.1 (start) ** +// ** ** +// ************************************************************************* + + if (self.skin == 0) centerprint(self, "Mr. Quake himself!"); else + if (self.skin == 1) centerprint(self, "No time to play with yourself here!"); else + if (self.skin == 2) centerprint(self, "You're one pretty toad!"); else + if (self.skin == 3) centerprint(self, "Wow Stormtrooper, you're though!"); else + if (self.skin == 4) centerprint(self, "Hi Max, looking yellow/blue today!"); else + if (self.skin == 5) centerprint(self, "You are back!"); else + if (self.skin == 6) centerprint(self, "Judge Dredd! Let's restore some order!"); else + if (self.skin == 7) centerprint(self, "Camo! Can't see you, where are you!"); else + if (self.skin == 8) centerprint(self, "Okay Captain Picard, make it so!"); else + if (self.skin == 9) centerprint(self, "Whizz whizz.. Wizzard!"); else + if (self.skin == 10) centerprint(self,"I'm the Predator, you're the prey!"); else + if (self.skin == 11) centerprint(self,"Welcome Skeleton, looking good!"); else + if (self.skin == 12) centerprint(self,"Wan-Fu, whoever you are :)"); else + if (self.skin == 13) centerprint(self,"Oh no, it's Henry Rollins!"); else + if (self.skin == 14) centerprint(self,"Ooh no, it's She.. eh.. He-Man"); else + if (self.skin == 15) centerprint(self,"If it isn't Boba, go get Han Solo!"); else + if (self.skin == 16) centerprint(self,"It's SUPERMAN!"); else + if (self.skin == 17) centerprint(self,"Protect the innocent, uphold your law"); else + if (self.skin == 18) centerprint(self,"Why is that symbol on your suit?"); + +// ************************************************************************* +// ** ** +// ** M U L T I S K I N 1.1 (end) ** +// ** ** +// ************************************************************************* + + }; @@ -766,6 +842,9 @@ self.button2 = 0; // player jumping sound sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM); + if (self.v_angle_x < -3) + self.velocity_z = self.velocity_z + (270 + (fabs(self.v_angle_x)*5)); + else self.velocity_z = self.velocity_z + 270; }; diff -urBb --new-file ..\quake-c\src106/defs.qc .\src/defs.qc --- ..\quake-c\src106/defs.qc Thu Sep 26 11:15:38 1996 +++ .\src/defs.qc Sat Dec 21 11:13:26 1996 @@ -691,3 +691,40 @@ float(entity targ, entity inflictor) CanDamage; +/* + ##################### + ### chase cam mod ### + Rob Albin, 09 Sep 96 + + orig functions modified: + WEAPONS.QC + W_SetCurrentAmmo + ImpulseCommands + CLIENT.QC + SetChangeParms + SetNewParms + DecodeLevelParms + PutClientInServer +*/ + +// message protocol defines +float SVC_SETVIEWPORT = 5; +float SVC_SETVIEWANGLES = 10; + +// free player entity variable +// '.float speed' bit-flag defines: +float HUD_CENTER_PRINT = 32; +float HUD_ON = 16; +float CHSCAM_MONSTER = 8; +float CHSCAM_ON = 4; +float LASERTARG_LIT = 2; +float LASERTARG_ON = 1; + +// chasecam globals for view offsets +// ref. client.qc for value setting +float chasecam_dist, chasecam_zmult; +float chasecam_zofs = 24; // used by spycam +float chasecam_intermission; // TRUE if level intermission running + +// hack variable, ref. items.qc/chasecam.qc +float HUD_backpack_refresh; diff -urBb --new-file ..\quake-c\src106/items.qc .\src/items.qc --- ..\quake-c\src106/items.qc Thu Sep 26 11:15:38 1996 +++ .\src/items.qc Sat Dec 21 11:13:26 1996 @@ -168,11 +168,13 @@ return; } + if (! (other.speed & HUD_ON) ) + { sprint(other, "You receive "); s = ftos(self.healamount); sprint(other, s); sprint(other, " health\n"); - + } // health touch sound sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM); @@ -275,6 +277,7 @@ self.nextthink = time + 20; self.think = SUB_regen; + if (! (other.speed & HUD_ON) ) sprint(other, "You got armor\n"); // armor touch sound sound(other, CHAN_ITEM, "items/armor1.wav", 1, ATTN_NORM); @@ -460,9 +463,12 @@ else objerror ("weapon_touch: unknown classname"); + if (! (other.speed & HUD_ON) ) + { sprint (other, "You got the "); sprint (other, self.netname); sprint (other, "\n"); + } // weapon touch sound sound (other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM); stuffcmd (other, "bf\n"); @@ -645,9 +651,12 @@ bound_other_ammo (); + if (! (other.speed & HUD_ON) ) + { sprint (other, "You got the "); sprint (other, self.netname); sprint (other, "\n"); + } // ammo touch sound sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); stuffcmd (other, "bf\n"); @@ -1322,6 +1331,15 @@ Deathmatch_Weapon (old, new); W_SetCurrentAmmo (); + + if ( (other.speed & HUD_ON) ) + { + // hack global to disable backpack messages in SP + // ensures HUD entities update after clearing display + HUD_backpack_refresh = TRUE; + sprint( other, "\n\n\n\n" ); + } + }; /* diff -urBb --new-file ..\quake-c\src106/progs.src .\src/progs.src --- ..\quake-c\src106/progs.src Thu Sep 26 11:15:40 1996 +++ .\src/progs.src Sat Dec 21 11:13:26 1996 @@ -6,6 +6,9 @@ ai.qc combat.qc items.qc + +chasecam.qc + weapons.qc world.qc client.qc diff -urBb --new-file ..\quake-c\src106/weapons.qc .\src/weapons.qc --- ..\quake-c\src106/weapons.qc Mon Sep 30 03:08:08 1996 +++ .\src/weapons.qc Sat Dec 21 11:13:26 1996 @@ -822,8 +825,16 @@ self.weaponmodel = ""; self.weaponframe = 0; } + + // ### chase cam mod ### + // disable model in chase view + if ( (self.speed & CHSCAM_ON) ) + self.weaponmodel = ""; + }; + + float() W_BestWeapon = { local float it; @@ -882,6 +893,10 @@ if (!W_CheckNoAmmo ()) return; + // ### chasecam mod ### + if ( (self.speed & CHSCAM_MONSTER) ) + return; + makevectors (self.v_angle); // calculate forward angle for velocity self.show_hostile = time + 1; // wake monsters up @@ -999,8 +1014,6 @@ am = 1; } - self.impulse = 0; - if (!(self.items & fl)) { // don't have the weapon or the ammo sprint (self, "no weapon.\n"); @@ -1025,12 +1038,52 @@ CheatCommand ============ */ +/* void() CheatCommand = { if (deathmatch || coop) return; self.ammo_rockets = 100; + self.ammo_nails = 400; + self.ammo_shells = 100; + self.items = self.items | + IT_AXE | + IT_SHOTGUN | + IT_SUPER_SHOTGUN | + IT_NAILGUN | + IT_SUPER_NAILGUN | + IT_GRENADE_LAUNCHER | + IT_ROCKET_LAUNCHER | + IT_KEY1 | IT_KEY2; + + self.ammo_cells = 400; + self.items = self.items | IT_LIGHTNING; + + self.weapon = IT_ROCKET_LAUNCHER; + W_SetCurrentAmmo (); +}; +*/ + +void() CheatCommand_think = +{ + local float f; + local entity e; + + self.nextthink = time + 3; + + if (!self.ammo_shells) + { + centerprint( self.owner, "Impulse 9 selected,\n\npreparing to\n\ncheat..." ); + } + else if (self.ammo_shells == 1) + { + if (!deathmatch && !coop) + { + e = self; + self = self.owner; + + self.ammo_rockets = 100; self.ammo_nails = 200; self.ammo_shells = 100; self.items = self.items | @@ -1047,10 +1100,66 @@ self.items = self.items | IT_LIGHTNING; self.weapon = IT_ROCKET_LAUNCHER; - self.impulse = 0; W_SetCurrentAmmo (); + + self = e; + } + } + else if (self.ammo_shells == 2) + { + T_Damage(self.owner, world, world, 50000); + } + else if (self.ammo_shells == 3) + { + if (self.owner.health > 0) + { + remove( self ); + return; + } + + f = random(); + if (f < 0.25) + centerprint( self.owner, "Yikes!\n\nWhat a mess :)" ); + else if (f < 0.50) + centerprint( self.owner, "Game wins :)" ); + else if (f < 0.75) + centerprint( self.owner, "That had to\n\nhurt :)" ); + else + { + centerprint( self.owner, "You on a diet?" ); + self.ammo_shells = self.ammo_shells + 1; + + } + } + else if (self.ammo_shells == 4) + { + remove( self ); + return; + } + else if (self.ammo_shells > 4) + { + centerprint( self.owner, "You look like you've\n\nlost some weight :)" ); + remove( self ); + return; + } + + self.ammo_shells = self.ammo_shells + 1; + +}; + +void() CheatCommand = +{ + local entity e; + + e = spawn(); + e.owner = self; + e.nextthink = time + 0.4; + e.think = CheatCommand_think; + e.ammo_shells = 0; + }; + /* ============ CycleWeaponCommand @@ -1063,7 +1172,6 @@ local float it, am; it = self.items; - self.impulse = 0; while (1) { @@ -1137,7 +1245,6 @@ local float it, am; it = self.items; - self.impulse = 0; while (1) { @@ -1229,20 +1337,44 @@ */ void() ImpulseCommands = { - if (self.impulse >= 1 && self.impulse <= 8) - W_ChangeWeapon (); + if (self.impulse < 9) W_ChangeWeapon (); - if (self.impulse == 9) - CheatCommand (); - if (self.impulse == 10) - CycleWeaponCommand (); - if (self.impulse == 11) - ServerflagsCommand (); - if (self.impulse == 12) - CycleWeaponReverseCommand (); + else if (self.impulse == 9 ) CheatCommand (); + else if (self.impulse == 10) CycleWeaponCommand (); + else if (self.impulse == 11) ServerflagsCommand (); + else if (self.impulse == 12) CycleWeaponReverseCommand (); + + // ### chase cam mods ### + else if (self.impulse == 30) Toggle_chase_cam(); + else if (self.impulse == 31) LaserTargeterToggle( self ); + + else if (self.impulse == 32) Chase_cam_change_zmult(0); // minus + else if (self.impulse == 33) Chase_cam_change_zmult(1); // plus + else if (self.impulse == 34) Chase_cam_change_dist(0); // minus + else if (self.impulse == 35) Chase_cam_change_dist(1); // plus + + else if (self.impulse == 36) Chasecam_find_monster(0); // minus + else if (self.impulse == 37) Chasecam_find_monster(1); // plus + + else if (self.impulse == 38) Chase_cam_read_temp1(0); // read cvar temp1 into chasecam_dist + else if (self.impulse == 39) Chase_cam_read_temp1(1); // read cvar temp1 into chasecam_zmult + + // ### hot key weapon mod ### + else if (self.impulse == 40) HotKey_previous_weapon(); + else if (self.impulse == 41) HotKey_weapon(1); // axe + else if (self.impulse == 42) HotKey_weapon(2); // grenade + else if (self.impulse == 43) HotKey_weapon(3); // rocket + else if (self.impulse == 44) HotKey_CycleWeaponReverseCommand(); + else if (self.impulse == 45) HotKey_CycleWeaponCommand(); + + // ### HUD mod ### + else if (self.impulse == 50) Spawn_HUD_entity(self, FALSE); + + // ### Multiskin v1.1 ### + else if (self.impulse == 200) Choose_multiskin(0); // up + else if (self.impulse == 201) Choose_multiskin(1); // down - if (self.impulse == 255) - QuadCheat (); + else if (self.impulse == 255) QuadCheat (); self.impulse = 0; }; @@ -1259,6 +1391,7 @@ if (time < self.attack_finished) return; + if (self.impulse) ImpulseCommands (); // check for attack