564 lines
14 KiB
C++
564 lines
14 KiB
C++
|
/*
|
||
|
* 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;
|
||
|
};
|
||
|
|