/* * File: bolt.qc * */ void() bolt_findNextTarget; void(vector org, entity target, entity boltOwner, vector endPos, float first, float dam) bolt_doDamage; float(entity check, entity checkFor) bolt_checkForLoops; void(entity boltOwner) bolt_createTimeoutEntity; void() bolt_timeoutNextEntity; void(entity boltTarget) bolt_removeTarget; float(entity targetEntity) bolt_infront; void() bolt_fire = { if (self.ammo_cells < 2) { bolt_removeTarget(self.bolt_target); self.bolt_target = world; // disable the firing target... self.weapon = W_BestWeapon (); W_SetCurrentAmmo (); return; } // explode if under water if (self.waterlevel > 1) { local float cells; cells = self.ammo_cells; self.ammo_cells = 0; bolt_removeTarget(self.bolt_target); self.bolt_target = world; // disable the firing target... W_SetCurrentAmmo (); T_RadiusDamage (self, self, 35*cells, world); return; } if (self.t_width < time) { sound (self, CHAN_WEAPON, "weapons/clight/clight_1.wav", 1, ATTN_NORM); self.t_width = time + 1; } // PKQW //self.punchangle_x = -2; msg_entity = self; WriteByte (MSG_ONE, SVC_SMALLKICK); // org = self.origin + '0 0 16'; bolt_findNextTarget(); if(self.bolt_target == world && self.ammo_cells >= 2) { // Can't find any targets at all. // fire as normal.. local vector org, endpos; if (deathmatch != 4) self.currentammo = self.ammo_cells = self.ammo_cells - 2; // PKQW //self.punchangle_x = -2; msg_entity = self; WriteByte (MSG_ONE, SVC_SMALLKICK); org = self.origin + '0 0 16'; traceline (org, org + v_forward*600, TRUE, self); endpos = trace_endpos; WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_LIGHTNING3); WriteEntity (MSG_BROADCAST, self); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); WriteCoord (MSG_BROADCAST, endpos_x); WriteCoord (MSG_BROADCAST, endpos_y); WriteCoord (MSG_BROADCAST, endpos_z); traceline (self.origin, endpos + v_forward*4, FALSE, self); // if hit a entity that can take damage & can see & is not invisible, then target for next time. if(trace_ent.takedamage) if(!(trace_inopen && trace_inwater)) if(trace_ent.health > 1) if(trace_ent.classname != "player" || !(trace_ent.items & IT_INVISIBILITY)) { self.bolt_target = trace_ent; self.bolt_targetTimeout = time + 0.5; // set target timeout // entity conduit timeout if the entity dies & handles being a conduit self.bolt_target.bolt_conduitTimeout = time + 0.5; // set it as a gib conduit entity // self.bolt_target.flags = self.bolt_target.flags | FL_GIBCONDUIT; } LightningDamage (self.origin, endpos + v_forward*4, self, 15); } // check if the conduit target is dead? if(self.bolt_target) if(self.bolt_target.health <= 0) { // set the die timeout. self.bolt_target.bolt_targetTimeout = time + 0.5; } if(self.bolt_target == world) if(self.weapon == IT_AXE) if(self.pk_currentitem == PK_IT_CLIGHT) if(self.bolt_targetTimeout < time + 0.2) { self.bolt_targetTimeout = time + 0.2; // set modal timeout } if(self.bolt_changeModelTimeout > time) { self.bolt_changeModelTimeout = self.bolt_changeModelTimeout + 0.21; // set the weapon model change back.. } else { self.bolt_changeModelTimeout = time + 1; // set the weapon model change back.. } }; void() bolt_findNextTarget = { local entity boltorg, head, targetEntity; local vector org, endPos; local float first, dist, tempDist; local float maxTargets; // the lighting will only last for 20 targets.. local float loopBlow, cellsToBlow; loopBlow = FALSE; first = TRUE; boltorg = self; targetEntity = boltorg.bolt_target; maxTargets = 20; cellsToBlow = 0; // check for a loop... if a loop blow... // also Check all current targets are still in line of fire. while(targetEntity != world && maxTargets >= 1) { // check if still a valid target.. org = boltorg.origin + '0 0 16'; traceline(org, targetEntity.origin, TRUE, boltorg); dist = vlen(boltorg.origin - targetEntity.origin); if(trace_fraction >= 1 && // can see... !(trace_inopen && trace_inwater) && // can't see the target if it goes tho water & air. dist <= 600 && // within distance (!first || bolt_infront(targetEntity)) && // in front if the first target.. // (first || targetEntity.health > 0) && // not dead... (the first target always acts as a conduit...) (targetEntity.solid != SOLID_NOT) && // make shore it's solid... (targetEntity.classname != "player" || !(targetEntity.items & IT_INVISIBILITY))) // not invisible.. { // check if loop if(targetEntity == self) { loopBlow = TRUE; } cellsToBlow = cellsToBlow + targetEntity.ammo_cells; if(first) // reset the target timeout... { boltorg.bolt_targetTimeout = time + 0.5; // set it as a gib conduit entity // targetEntity.flags = targetEntity.flags | FL_GIBCONDUIT; } else { bolt_createTimeoutEntity(boltorg); } // entity conduit timeout if the entity dies & handles being a conduit targetEntity.bolt_conduitTimeout = time + 0.5; first = FALSE; // get the next target boltorg = targetEntity; targetEntity = boltorg.bolt_targetNext; maxTargets = maxTargets - 1; } else { // disable next target... (the rest of the path will time out if not used...) if(first) { bolt_removeTarget(targetEntity); boltorg.bolt_target = world; } else { boltorg.bolt_targetNext = world; } targetEntity = world; } } if(maxTargets == 19) // only one target, check if firing at each other if(self.bolt_target.bolt_target == self) // & other player is firing back { cellsToBlow = cellsToBlow + targetEntity.ammo_cells; loopBlow = TRUE; } first = TRUE; boltorg = self; targetEntity = boltorg.bolt_target; maxTargets = 20; // do damage to current target list... while(targetEntity) { // do damage... if(loopBlow) { // loop.. blow targetEntity.ammo_cells = 0; self.ammo_cells = cellsToBlow; bolt_doDamage(org, targetEntity, boltorg, trace_endpos, first, cellsToBlow); self.ammo_cells = cellsToBlow; } else { bolt_doDamage(org, targetEntity, boltorg, trace_endpos, first, 2); } first = FALSE; // get the next target boltorg = targetEntity; if(targetEntity == self) { targetEntity = world; } else { targetEntity = boltorg.bolt_targetNext; } maxTargets = maxTargets - 1; // check to see if we have run out of ammo if(self.ammo_cells < 2) { // remove the FL_GIBCONDUIT if on bolt_removeTarget(self.bolt_target); self.bolt_target = world; // disable the firing target... self.weapon = W_BestWeapon (); W_SetCurrentAmmo (); return; } // if max targets reached, do no more... if(maxTargets < 1) { return; } } if(loopBlow) { if(maxTargets == 19) // only one target, check if firing at each other { // loop.. blow self.ammo_cells = cellsToBlow; bolt_doDamage(org, self, self, trace_endpos, first, cellsToBlow); } self.ammo_cells = 0; // remove the FL_GIBCONDUIT if on bolt_removeTarget(self.bolt_target); self.bolt_target = world; // disable the firing target... self.weapon = W_BestWeapon (); W_SetCurrentAmmo (); return; } // if max targets reached or if it's finding the first target, do no more... if(maxTargets < 1 || first) { return; } // find the closest target within the current view of the turret. // max distance that the lighting can "jump" dist = 600; org = boltorg.origin + '0 0 16'; head = findradius(boltorg.origin, dist); while(head) { if(head.health > 1) // can take damage && is not dead if(head.takedamage) if(head != self) // not the shotter if(head != boltorg) // and not the origin //if(head.classname != "beartrap") // JON Don't target beartraps if(head.classname == "player" || head.classname == "turret" || head.classname == "turret_base") if(bolt_checkForLoops(head, boltorg)) // not already in a this fire chain... { tempDist = vlen(head.origin - self.origin); traceline(org, head.origin, TRUE, self); if(trace_fraction >= 1) // can see the player if(tempDist < dist) // closer than the last entity if(!(trace_inopen && trace_inwater)) // can see it (ie can't see if it goes tho water & air) if(targetEntity.solid != SOLID_NOT) // make shore it's solid... if(head.classname != "player" || !(head.items & IT_INVISIBILITY)) // & is not invisiable { // this will NEVER be the first target... so we can use just bolt_targetNext boltorg.bolt_targetNext = head; dist = tempDist; endPos = trace_endpos; } } head = head.chain; } // target found? if(boltorg.bolt_targetNext != world) { bolt_createTimeoutEntity(boltorg); // entity conduit timeout if the entity dies & handles being a conduit boltorg.bolt_targetNext.bolt_conduitTimeout = time + 0.5; // do damage... bolt_doDamage(org, boltorg.bolt_targetNext, boltorg, endPos, FALSE, 2); } }; // check for target path loops... float(entity check, entity checkFor) bolt_checkForLoops = { local string str; while(check.bolt_targetNext) { if(check.bolt_targetNext == checkFor) { return FALSE; } check = check.bolt_targetNext; } return TRUE; }; void(vector org, entity boltTarget, entity boltOwner, vector endPos, float first, float dam) bolt_doDamage = { local entity lightEntity; local vector vangle; // use up ammo if (deathmatch != 4) self.currentammo = self.ammo_cells = self.ammo_cells - dam; dam = ((dam / 2) * 15); dam = floor(dam); // display & do damage // PKQW //particle(endPos, '0 0 100', 225, dam * 4); // only do damage if the target is not dead... if(boltTarget.health > 0) { T_Damage(boltTarget, self, self, dam); } if(boltOwner != boltTarget) { if(first) { lightEntity = boltOwner; } else { if(!boltOwner.bolt_lightningFire) { boltOwner.bolt_lightningFire = spawn(); boltOwner.bolt_lightningFire.solid = SOLID_NOT; boltOwner.bolt_lightningFire.movetype = MOVETYPE_NONE; } boltOwner.bolt_lightningFire.origin = boltOwner.origin; vangle = normalize(boltTarget.origin - org); boltOwner.bolt_lightningFire.angles = vectoangles(vangle); lightEntity = boltTarget.bolt_lightningFire; } // display the bolt WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_LIGHTNING3); WriteEntity (MSG_BROADCAST, lightEntity); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); WriteCoord (MSG_BROADCAST, boltTarget.origin_x); WriteCoord (MSG_BROADCAST, boltTarget.origin_y); WriteCoord (MSG_BROADCAST, boltTarget.origin_z); } }; // Used to timeout the targets, called from PlayerPostThink void() bolt_checkTargets = { if(self.bolt_target) if(self.bolt_targetTimeout < time) { bolt_removeTarget(self.bolt_target); self.bolt_target = world; } if(self.weapon == IT_AXE) if(self.pk_currentitem == PK_IT_CLIGHT) if(self.bolt_changeModelTimeout < time) { self.weaponmodel = "progs/v_cl_off.mdl"; } if(self.bolt_targetNext) if(self.bolt_targetNextTimeout < time) { self.bolt_targetNext = world; } }; void(entity boltOwner) bolt_createTimeoutEntity = { if(boltOwner.classname == "player") { boltOwner.bolt_targetNextTimeout = time + 0.5; // set the target timeout } if(boltOwner.bolt_targetTimeoutEntity == world) { boltOwner.bolt_targetTimeoutEntity = spawn(); boltOwner.bolt_targetTimeoutEntity.think = bolt_timeoutNextEntity; boltOwner.bolt_targetTimeoutEntity.owner = boltOwner; } boltOwner.bolt_targetTimeoutEntity.nextthink = time + 0.5; }; void() bolt_timeoutNextEntity = { bolt_removeTarget(self.owner); // remove the target pointers self.owner.bolt_targetNext = world; self.owner.bolt_targetTimeoutEntity = world; // remove timeout entity. remove(self); }; void(entity boltTarget) bolt_removeTarget = { // remove the FL_GIBCONDUIT if on // if(boltTarget) // if(boltTarget.flags & FL_GIBCONDUIT) // if(boltTarget.health > 0) // { // boltTarget.flags = boltTarget.flags - FL_GIBCONDUIT; // } if(boltTarget.bolt_lightningFire) { remove(boltTarget.bolt_lightningFire); boltTarget.bolt_lightningFire = world; } }; $frame fryguy1 fryguy2 fryguy3 fryguy4 void() player_conduitJerk =[ $fryguy1, player_conduitJerk ] { // check if conduit times up if(self.bolt_conduitTimeout < time) // it's the conduit time for this monster... { // blow the fuck up!!! // if(self.flags & FL_GIBCONDUIT) // { self.health = -99; // } PlayerDie (); return; } // choose the next frame if(self.frame > 2) { self.frame = $fryguy1; } else { self.frame = self.frame + 1; } }; float(entity targetEntity) bolt_infront = { local vector vec; local float dot; makevectors (self.angles); vec = normalize (targetEntity.origin - self.origin); dot = vec * v_forward; if ( dot > 0.85) { return TRUE; } return FALSE; };