quake-painkeep/qwsrc/grap.qc
1997-08-12 00:00:00 +00:00

1084 lines
24 KiB
C++

/*
* File: grap.qc
*
* Grappling Gun source.
*
*/
void() grap_touch;
float() grap_maintainRope;
void() grap_touchOwner;
void() grap_goToOwner;
void() grap_maintainHarpoon;
entity(entity ropeOwner) grap_createRope;
void(entity grapOwner) grap_remove;
void(entity grapOwner) grap_removeRope;
void() grap_ownerToTarget;
entity(entity start) grap_findHarpoon;
void(entity corner) grap_removeCorner;
void() grep_touchCorner;
void(entity corner, vector cornerOrigin) grep_createCornerRope;
float() grap_checkCornerLines;
float(entity cornerOwner) grap_createCorner;
float(entity cornerOwner) grap_forceCheckCorner;
float(entity owner, vector ownerOrigin, vector tryOrigin) grap_checkIfCanBeRemoved;
float CHAN_GRAPROPE = 5;
float GRAPROPE_MAXLEENGTH = 1500;
void() grap_fire =
{
local entity harpoon, temp;
local vector dir;
if(self.grap_firetest)
{
return;
}
temp = self;
self.grap_firetest = 1;
if(self.grap_cornerchainNext)
{ // Harpoon already out there
harpoon = grap_findHarpoon(self);
// Check if it is waiting to come back...
if(self.grap_state == GRAP_IDEAL)
{
// bring back...
self.grap_cornerchainNext.nextthink = time + 0.1;
self.grap_cornerchainNext.think = grap_ownerToTarget;
self.grap_cornerchainNext.grap_checkTimeout = time + 0.5;
self.grap_cornerchainNext.touch = grap_touchOwner;
self.grap_state = GRAP_TOHARPOON;
self.grap_checkTimeout = 0;
self.attack_finished = time + 0.4;
}
else
{
self.attack_finished = time + 1;
// remove harpoon
other = self;
self = harpoon;
grap_touchOwner();
}
self = temp;
return;
}
// check if near a wall
makevectors(self.v_angle);
dir = self.origin;
traceline (dir, dir + v_forward * 75, FALSE, self);
if(trace_fraction != 1.0 && !trace_ent.takedamage)
{
// To near a wall, can't throw turret
sprint(self, PRINT_HIGH, "Too close to a wall!!\n");
return;
}
// play firing sound
sound (self, CHAN_AUTO, "weapons/grapple/grapl_2.wav", 1, ATTN_NORM);
makevectors(self.v_angle);
dir = v_forward;
// create & launch harpoon
harpoon = spawn();
harpoon.grap_owner = self;
harpoon.angles = vectoangles(dir);
harpoon.movetype = MOVETYPE_FLYMISSILE;
harpoon.solid = SOLID_BBOX;
setmodel (harpoon, "progs/harpoon.mdl");
setsize (harpoon, VEC_ORIGIN, VEC_ORIGIN);
// PKQW
//harpoon.punchangle_x = -2;
harpoon.touch = grap_touch;
harpoon.grap_state = GRAP_HARPOON;
// For the correct death messages
harpoon.pk_currentitem = PK_IT_GRAPGUN;
harpoon.weapon = IT_AXE;
self.grap_cornerchainNext = harpoon;
harpoon.classname = "Harpoon";
setorigin (harpoon, self.origin + '0 0 16');
harpoon.velocity = dir * 2000;
// let out rope
self.grap_state = GRAP_OUT;
self.grap_checkTimeout = 0;
self.attack_finished = time + 0.5;
player_grapfire1();
self = temp;
};
void() grap_releaseHarpoon =
{
if(self.grap_cornerchainNext)
{ // Harpoon is out there
local entity harpoon, temp;
harpoon = grap_findHarpoon(self);
// remove harpoon
temp = self;
other = self;
self = harpoon;
grap_touchOwner();
self = temp;
}
};
float() grap_maintainRope =
{
local entity corner;
local vector cornerOrigin;
local float totalLength = 0;
// play rope sounds needed...
if(self.grap_state == GRAP_OUT)
{
if(self.grap_checkTimeout < time)
{
sound (self, CHAN_GRAPROPE, "weapons/grapple/grapr_2.wav", 1, ATTN_NORM);
self.grap_checkTimeout = time + 3.2;
}
}
else if(self.grap_state == GRAP_IN || self.grap_state == GRAP_TOHARPOON)
{
if(self.grap_checkTimeout < time)
{
sound (self, CHAN_GRAPROPE, "weapons/grapple/graps_2.wav", 1, ATTN_NORM);
self.grap_checkTimeout = time + 3.2;
}
}
else if(self.grap_checkTimeout > time)
{
sound (self, CHAN_GRAPROPE, "misc/null.wav", 1, ATTN_NORM);
self.grap_checkTimeout = 0;
}
// for each segment between corners
corner = self;
cornerOrigin = corner.origin + '0 0 16';
while(corner.grap_cornerchainNext)
{
totalLength = totalLength + vlen(corner.grap_cornerchainNext.origin - cornerOrigin);
grep_createCornerRope(corner, cornerOrigin);
corner = corner.grap_cornerchainNext;
cornerOrigin = corner.origin;
}
return totalLength;
};
void(entity corner, vector cornerOrigin) grep_createCornerRope =
{
local entity rope, ropeOwner;
local vector startPoint, dir, vangle;
// Calculate viewing angle
vangle = normalize(corner.grap_cornerchainNext.origin - cornerOrigin);
vangle = vectoangles(vangle);
ropeOwner = corner.grap_cornerchainNext;
ropeOwner.angles = vangle;
// if going out, create coil's
if(self.grap_state == GRAP_OUT)
{
if(vlen(cornerOrigin - ropeOwner.origin) < 86)
{
// if there is rope, then delete
if(ropeOwner.grap_ropechainPrev)
{
grap_removeRope(ropeOwner);
}
return;
}
// Calculate direction to owner.
dir = normalize(cornerOrigin - ropeOwner.origin);
// Calculate start point of the coil
startPoint = ropeOwner.origin + (dir * 68);
if(ropeOwner.grap_ropechainPrev)
{
rope = ropeOwner.grap_ropechainPrev;
}
else
{
rope = grap_createRope(ropeOwner);
rope.classname = "HarpoonCoil";
setmodel(rope, "progs/harpcoil.mdl");
}
rope.origin = startPoint;
rope.velocity = '0 0 0';
rope.angles = vangle;
ropeOwner = rope;
while(1)
{
// create a coil entity if there is room
if(vlen(cornerOrigin - ropeOwner.origin) < 32)
{
// if there is rope, then delete
if(ropeOwner.grap_ropechainPrev)
{
grap_removeRope(ropeOwner);
}
return;
}
// Calculate direction to owner.
dir = normalize(cornerOrigin - ropeOwner.origin);
// Calculate start point of the coil
startPoint = ropeOwner.origin + (dir * 32);
if(ropeOwner.grap_ropechainPrev)
{
rope = ropeOwner.grap_ropechainPrev;
}
else
{
rope = grap_createRope(ropeOwner);
rope.classname = "HarpoonCoil";
setmodel(rope, "progs/harpcoil.mdl");
}
rope.origin = startPoint;
rope.velocity = '0 0 0';
rope.angles = vangle;
ropeOwner = rope;
}
}
else // create rope instead
{
// create a coil entity if there is room
if(vlen(cornerOrigin - ropeOwner.origin) < 70)
{
// if there is rope, then delete
if(ropeOwner.grap_ropechainPrev)
{
grap_removeRope(ropeOwner);
}
return;
}
// Calculate direction to owner.
dir = normalize(cornerOrigin - ropeOwner.origin);
// Calculate start point of the coil
startPoint = ropeOwner.origin + (dir * 70);
if(ropeOwner.grap_ropechainPrev)
{
rope = ropeOwner.grap_ropechainPrev;
// if a coil, convert to a rop
if(rope.classname == "HarpoonCoil")
{
rope.classname = "HarpoonRope";
setmodel(rope, "progs/harprope.mdl");
}
}
else
{
rope = grap_createRope(ropeOwner);
rope.classname = "HarpoonRope";
setmodel(rope, "progs/harprope.mdl");
}
rope.origin = startPoint;
rope.velocity = '0 0 0';
rope.angles = vangle;
ropeOwner = rope;
while(1)
{
// create a coil entity if there is room
if(vlen(cornerOrigin - ropeOwner.origin) < 35)
{
// if there is rope, then delete
if(ropeOwner.grap_ropechainPrev)
{
grap_removeRope(ropeOwner);
}
return;
}
// Calculate direction to owner.
dir = normalize(cornerOrigin - ropeOwner.origin);
// Calculate start point of the coil
startPoint = ropeOwner.origin + (dir * 34);
if(ropeOwner.grap_ropechainPrev)
{
rope = ropeOwner.grap_ropechainPrev;
if(rope.classname == "HarpoonCoil")
{
rope.classname = "HarpoonRope";
setmodel(rope, "progs/harprope.mdl");
}
}
else
{
rope = grap_createRope(ropeOwner);
rope.classname = "HarpoonRope";
setmodel(rope, "progs/harprope.mdl");
}
rope.origin = startPoint;
rope.velocity = '0 0 0';
rope.angles = vangle;
ropeOwner = rope;
}
}
};
void() grap_touch =
{
local entity grapOwner;
local vector temp;
if (other == self.grap_owner)
return;
if (pointcontents(self.origin) == CONTENT_SKY)
{
self.grap_pull = self;
grap_touchOwner();
return;
}
self.touch = SUB_Null;
grapOwner = self.grap_owner;
while(grapOwner.grap_state == GRAP_CORNER)
{
grapOwner = grapOwner.grap_owner;
}
// hit something that bleeds
// if(other.takedamage)
if((other.health && other.solid != SOLID_BSP && other.solid != SOLID_NOT) || other.solid == SOLID_TRIGGER || grapOwner.grap_firetest)
{
self.grap_owner.grap_checkTimeout = 0;
self.touch = grap_touchOwner;
// set the corner touch functions.
grapOwner = self.grap_owner;
while(grapOwner.grap_state == GRAP_CORNER)
{
grapOwner.touch = grep_touchCorner;
grapOwner = grapOwner.grap_owner;
}
// set owner state
grapOwner.grap_state = GRAP_IN;
if(other.health && other.solid != SOLID_BSP && other.solid != SOLID_NOT)
{
// play hit sound
sound(self, CHAN_AUTO, "weapons/grapple/grapf_1.wav", 1, ATTN_NORM);
spawn_touchblood (9);
T_Damage(other, self, grapOwner, 9);
}
else
{
// play hit sound
sound (self, CHAN_AUTO, "weapons/grapple/graph_2b.wav", 1, ATTN_NORM);
}
// drag to the owner.
if((other.health && other.solid != SOLID_BSP && other.solid != SOLID_NOT) || other.solid == SOLID_TRIGGER)
{
self.grap_pull = other;
self.grap_pull.grap_owner = self.grap_owner;
self.grap_pull.movetype = MOVETYPE_FLY;
}
}
else
{
// play hit sound
sound (self, CHAN_AUTO, "weapons/grapple/graph_2b.wav", 1, ATTN_NORM);
grapOwner.grap_state = GRAP_IDEAL;
}
// come back to the grap_owner
// setting the velocity does some weird things
self.velocity = '0 0 0';
// stop creation of rope
if(self.grap_pull)
{
self.nextthink = time + 0.1;
self.think = grap_goToOwner;
self.grap_checkTimeout = time + 0.5;
self.grep_offset = (self.origin - self.grap_pull.origin);
}
else if(grapOwner.grap_firetest)
{
self.nextthink = time + 0.1;
self.think = grap_goToOwner;
self.grap_checkTimeout = time + 0.5;
}
};
void() grap_touchOwner =
{
local entity grapOwner;
// if touch grap_pull, don't bother to remove self, not there yet.
if(self.grap_pull == other)
{
return;
}
// check if hit a corner...
if(self.grap_state == GRAP_CORNER)
{
// send on to next corner...
grapOwner = self;
self = self.grap_cornerchainNext;
grap_removeCorner(grapOwner);
self.nextthink = time + 0.1;
self.think = grap_ownerToTarget;
self.grap_checkTimeout = time + 0.5;
self.touch = grap_touchOwner;
}
else
{
// stop sounds...
sound (self.grap_owner, CHAN_GRAPROPE, "misc/null.wav", 1, ATTN_NORM);
self.grap_owner.grap_checkTimeout = 0;
// remove everything
grapOwner = self.grap_owner;
while(grapOwner.grap_state == GRAP_CORNER)
{
grapOwner = grapOwner.grap_owner;
}
grap_remove(grapOwner);
}
};
void() grap_goToOwner =
{
local vector temp;
if(self.grap_checkTimeout < time)
{
if(self.origin == self.oldorigin)
{ // stuck on wall or something
other = self.grap_owner;
grap_touchOwner();
return;
}
}
self.oldorigin = self.origin;
self.nextthink = time + 0.1;
if(self.grap_pull)
if(self.grap_pull.solid != SOLID_TRIGGER)
if(self.grap_pull.health < 1 || self.grap_pull.solid == SOLID_NOT) // check if the entity is dead or "picked up"
{
self.grap_pull.grap_owner = world;
if(self.grap_pull.classname == "player")
{
if(!self.grap_pull.deadflag && !self.disconnectPlayer)
{
self.grap_pull.movetype = MOVETYPE_WALK;
}
}
else
{
self.grap_pull.movetype = MOVETYPE_STEP;
}
self.grap_pull = world;
}
if(self.grap_pull)
{
// give veelocity to target entity.
temp = normalize((self.grap_owner.origin) - self.grap_pull.origin);
self.grap_pull.velocity = temp * 500;
if(self.grap_pull.flags & FL_ONGROUND)
{
self.grap_pull.flags = self.grap_pull.flags - FL_ONGROUND;
setorigin(self.grap_pull, self.grap_pull.origin + '0 0 1');
}
// set current harpoon origin relative
self.origin = self.grap_pull.origin + self.grep_offset;
setorigin(self, self.origin);
}
else
{
temp = normalize((self.grap_owner.origin + '0 0 16') - self.origin);
self.velocity = temp * 500;
}
};
void() grap_ownerToTarget =
{
local float dist;
local string str;
// double check if is valid
if(self.grap_owner.grap_state != GRAP_TOHARPOON)
{
return;
}
if(self.grap_checkTimeout < time)
{
if(self.grap_owner.origin == self.grap_owner.oldorigin)
{ // stuck on wall or something
other = self.grap_owner;
grap_touchOwner();
return;
}
}
// only check if it's going to the harpoon
if(self.grap_state != GRAP_CORNER)
{
dist = vlen(self.grap_owner.origin - self.origin);
str = ftos(dist);
if(dist < 53) // must be in touching distance...
{
other = self.grap_owner;
grap_touchOwner();
return;
}
}
self.nextthink = time + 0.1;
};
void() grap_maintainHarpoon =
{
local vector harpoonDifference;
local float chainLength, vecfactor;
local entity temp;
temp = self;
// if(self.grap_owner) // if being pulled by a harpoon
// {
// local vector temp;
// temp = normalize((self.grap_owner.origin + '0 0 16') - self.origin);
// self.velocity = self.velocity = temp * 500;
// }
if(!self.grap_cornerchainNext) // if harpoon not out
{
self.grap_firetest = 0;
return;
}
if(self.weapon == IT_AXE && self.pk_currentitem == PK_IT_GRAPGUN)
{
if(!self.button0)
{
self.grap_firetest = 0;
}
}
else
{
self.grap_firetest = 0;
}
if((self.teleport_time > time) || // if player goes through teleport
(self.view_ofs == '0 0 0')) // or intermission
{
other = self;
self = grap_findHarpoon(self);
grap_touchOwner();
self = temp;
return;
}
if(grap_checkCornerLines())
{
other = self;
self = grap_findHarpoon(self);
grap_touchOwner();
self = temp;
return;
}
if(grap_maintainRope() > GRAPROPE_MAXLEENGTH)
{
if(self.grap_state == GRAP_IDEAL || self.grap_state == GRAP_OUT)
{
local entity harpoon;
// bring back...
harpoon = grap_findHarpoon(self);
harpoon.grap_owner.grap_checkTimeout = 0;
harpoon.touch = grap_touchOwner;
harpoon.grap_pull = world;
harpoon.nextthink = time + 0.1;
harpoon.think = grap_goToOwner;
harpoon.grap_checkTimeout = time + 0.5;
self.grap_state = GRAP_IN;
return;
}
else if(self.grap_state != GRAP_IN)
{ // remove...
other = self;
self = grap_findHarpoon(self);
grap_touchOwner();
self = temp;
return;
}
}
if(self.grap_state == GRAP_TOHARPOON)
{
local vector normDir;
normDir = normalize(self.grap_cornerchainNext.origin - (self.origin + '0 0 16'));
self.velocity = self.velocity = normDir * 500;
}
};
void(entity grapOwner) grap_remove =
{
local entity e, f;
e = grapOwner.grap_cornerchainNext;
while(e)
{
f = e;
e = e.grap_cornerchainNext;
if(f.grap_pull)
{
f.grap_pull.grap_owner = world;
if(f.grap_pull.classname == "player")
{
if(!f.grap_pull.deadflag && !self.disconnectPlayer)
{
f.grap_pull.movetype = MOVETYPE_WALK;
}
}
else
{
f.grap_pull.movetype = MOVETYPE_STEP;
}
}
grap_removeRope(f);
remove(f);
}
grapOwner.grap_cornerchainNext = world;
};
void(entity grapOwner) grap_removeRope =
{
local entity e, f;
e = grapOwner.grap_ropechainPrev;
while(e)
{
f = e;
e = e.grap_ropechainPrev;
if(f.grap_pull)
{
f.grap_pull.grap_owner = world;
if(f.grap_pull.classname == "player")
{
if(!f.grap_pull.deadflag && !self.disconnectPlayer)
{
f.grap_pull.movetype = MOVETYPE_WALK;
}
}
else
{
f.grap_pull.movetype = MOVETYPE_STEP;
}
}
remove(f);
}
grapOwner.grap_ropechainPrev = world;
};
entity(entity ropeOwner) grap_createRope =
{
local entity rope;
rope = spawn();
rope.movetype = MOVETYPE_FLYMISSILE;
rope.solid = SOLID_NOT;
setsize (rope, VEC_ORIGIN, VEC_ORIGIN);
// PKQW
//rope.punchangle_x = -2;
rope.angles = ropeOwner.angles;
rope.grap_owner = ropeOwner.grap_owner;
rope.owner = ropeOwner;
rope.grap_state = GRAP_ROPE;
// add to the end of the chain...
ropeOwner.grap_ropechainPrev = rope;
return rope;
};
// Find the harpoon entity.
entity(entity start) grap_findHarpoon =
{
while(start.grap_cornerchainNext)
{
start = start.grap_cornerchainNext;
if(start.grap_state != GRAP_CORNER)
{
return start;
}
}
return start;
};
void(entity corner) grap_removeCorner =
{
// remove the owner connection.
corner.grap_owner.grap_cornerchainNext = corner.grap_cornerchainNext;
// remove the next corner owner connection.
if(corner.grap_cornerchainNext)
{
corner.grap_cornerchainNext.grap_owner = corner.grap_owner;
}
// remove the rope for this corner.
grap_removeRope(corner);
// now finially remove the corner.
remove(corner);
};
void() grep_touchCorner =
{
if(other != self.grap_cornerchainNext)
{
// remove corner
grap_removeCorner(self);
// move onto the next corner.
// this is done in the grap_goToOwner
}
};
float() grap_checkCornerLines =
{
local entity corner, tryNext, del;
local vector cornerOrigin;
corner = self.grap_cornerchainNext;
cornerOrigin = self.origin + '0 0 16';
while(corner)
{
// check if the line is broken
traceline (cornerOrigin, corner.origin, TRUE, self.grap_pull);
if(trace_fraction < 1.0)
{ // line is broken, create a new corner.
if(grap_createCorner(corner.grap_owner))
{
// if it was the owner that went out of line, do a double check...
if(corner.grap_owner == self)
{
if(grap_forceCheckCorner(self))
{
return TRUE;
}
}
else
{
return TRUE;
}
}
}
else
{ // check if a corner can be deleted.
tryNext = corner.grap_cornerchainNext;
while(tryNext) {
if(grap_checkIfCanBeRemoved(corner.grap_owner, cornerOrigin, tryNext.origin))
{ // Up to this corner can be deleted.
while(corner != tryNext)
{
del = corner;
corner = corner.grap_cornerchainNext;
grap_removeCorner(del);
}
}
tryNext = tryNext.grap_cornerchainNext;
}
}
cornerOrigin = corner.origin;
corner = corner.grap_cornerchainNext;
}
// if going to target, resetup goto
if(self.grap_state == GRAP_TOHARPOON)
{
if(self.grap_cornerchainNext.nextthink < time)
{
self.grap_cornerchainNext.nextthink = time + 0.1;
self.grap_cornerchainNext.grap_checkTimeout = time + 0.5;
}
self.grap_cornerchainNext.think = grap_ownerToTarget;
self.grap_cornerchainNext.touch = grap_touchOwner;
}
return FALSE;
};
float(entity cornerOwner) grap_createCorner =
{
local vector dir, startPoint, checkPos, ownerOrigin;
local float checkAmount;
// try the plane normal out to 100 to see if we can find a corner.
dir = trace_plane_normal;
startPoint = trace_endpos;
checkAmount = 1;
if(cornerOwner.grap_state == GRAP_CORNER)
{
ownerOrigin = cornerOwner.origin;
}
else
{
ownerOrigin = cornerOwner.origin + '0 0 16';
}
while(checkAmount <= 100)
{
checkPos = startPoint + (dir * checkAmount);
traceline (ownerOrigin, checkPos, TRUE, self.grap_pull);
if(trace_fraction == 1.0)
{
traceline (cornerOwner.grap_cornerchainNext.origin, checkPos, TRUE, self.grap_pull);
if(trace_fraction == 1.0)
{
local entity corner;
// setup corner.
corner = spawn();
corner.solid = SOLID_NOT;
corner.grap_owner = cornerOwner;
corner.owner = self;
corner.grap_state = GRAP_CORNER;
// Add another 5 just for space...
checkPos = startPoint + (dir * (checkAmount + 5));
corner.origin = checkPos;
corner.grap_cornerchainNext = cornerOwner.grap_cornerchainNext;
cornerOwner.grap_cornerchainNext = corner;
corner.grap_cornerchainNext.grap_owner = corner;
return FALSE;
}
}
checkAmount = checkAmount + 1;
}
return TRUE;
};
float(entity cornerOwner) grap_forceCheckCorner =
{
local vector endPos, dir;
local float length;
endPos = cornerOwner.oldorigin + '0 0 16';
// double check that the old origin can see the next point.
traceline (cornerOwner.grap_cornerchainNext.origin, endPos, TRUE, self.grap_pull);
if(trace_fraction < 1.0)
{
return TRUE; // can't see the oldorigin, something must be very wrong here
}
length = vlen(endPos - (cornerOwner.origin + '0 0 16'));
dir = normalize(endPos - (cornerOwner.origin + '0 0 16'));
endPos = endPos + dir;
length = length - 1;
// try the difference between the origin & oldorigin
while(length > 0)
{
traceline (cornerOwner.grap_cornerchainNext.origin, endPos, TRUE, self.grap_pull);
if(trace_fraction < 1.0)
{
// found the first, can't see point.
// now run the grap_createCorner function
return grap_createCorner(cornerOwner);
}
endPos = endPos + dir;
length = length - 1;
}
// should never get here...
return TRUE;
};
float(entity ownerEntity, vector ownerOrigin, vector tryOrigin) grap_checkIfCanBeRemoved =
{
// try the line...
traceline (ownerOrigin, tryOrigin, TRUE, self.grap_pull);
if(trace_fraction == 1.0)
{
if(ownerEntity == self)
{
// if the owner... Try the full model size...
traceline (ownerOrigin + (VEC_HULL_MIN * 2), tryOrigin, TRUE, self.grap_pull);
if(trace_fraction < 1.0)
{
return FALSE;
}
traceline (ownerOrigin + (VEC_HULL_MAX * 2), tryOrigin, TRUE, self.grap_pull);
if(trace_fraction < 1.0)
{
return FALSE;
}
}
return TRUE;
}
return FALSE;
};
void() grap_checkIfGrap =
{
if(other.classname == "Harpoon")
{ // we have a harpoon from a grappling hook, drag back..
local entity temp;
local entity grapOwner;
// and it's going out...
grapOwner = other.grap_owner;
while(grapOwner.grap_state == GRAP_CORNER)
{
grapOwner = grapOwner.grap_owner;
}
if(grapOwner.grap_state == GRAP_OUT)
{
temp = self;
self = other;
other = temp;
grap_touch();
}
}
};