sin-2015/rope.cpp
1999-04-22 00:00:00 +00:00

1508 lines
42 KiB
C++

/*
================================================================
ROPES
================================================================
Copyright (C) 1998 by 2015, Inc.
All rights reserved.
This source is may not be distributed and/or modified without
expressly written permission by 2015, Inc.
NOTES:
Rope piece length can be varied anywhere between 16 and 32.
For the movement dampening values, lower values make fore stiffer movement.
*/
#include "rope.h"
#include "player.h"
// functions used from g_phys.c
void G_CheckVelocity(Entity *ent);
int G_ClipVelocity (Vector& in, Vector& normal, Vector& out, float overbounce, int gravaxis);
void G_Impact(Entity *e1, trace_t *trace);
void G_AddCurrents (Entity *ent, Vector *basevel);
/*================================================================
main physics functions
================================================================*/
void rope_set_angles(RopePiece *piece)
{
Vector angvec;
int tmpflt;
angvec = piece->prev_piece->origin - piece->origin;
// helps eliminate an annoying wavering like effect
if(fabs(angvec.x) < 0.2)
angvec.x = 0;
if(fabs(angvec.y) < 0.2)
angvec.y = 0;
piece->angles = angvec.toAngles();
piece->angles[PITCH] = -(piece->angles[PITCH]);
piece->setAngles(piece->angles);
//also set rope segment frame here
tmpflt = angvec.length();
if(tmpflt < 16)
tmpflt = 16;
else if(tmpflt > 40)
tmpflt = 40;
piece->edict->s.frame = tmpflt - 16;
}
/*
Like G_PushEntity, except that it doesn't use the
actual entity's bounding box size and doesn't
bother with doing touching with non-client entities.
*/
trace_t G_PushRopePiece (RopePiece *ent, Vector push)
{
trace_t trace;
Vector start;
Vector end;
int mask;
start = ent->worldorigin;
end = start + push;
if (ent->edict->clipmask)
mask = ent->edict->clipmask;
else
mask = MASK_SOLID;
trace = G_Trace( start, ent->rope_base->mins, ent->rope_base->maxs, end, ent, mask, "G_PushRopePiece");
ent->setOrigin(trace.endpos);
if (trace.fraction != 1.0)
{
// only bother with touching a client
if(trace.ent->entity->isSubclassOf(Player))
G_Impact(ent, &trace);
}
return trace;
}
void G_Physics_Rope(RopePiece *ent)
{
RopePiece *piece, *tmpent;
trace_t trace;
Vector move, tmpvec;
Vector old_origin;
float f1;
Vector basevel;
edict_t *edict;
qboolean onconveyor;
const gravityaxis_t &grav = gravity_axis[ent->gravaxis];
// rope piece movement is done by the base
if(ent->rope_base)
return;
// check PVS physics cancelation
if(!((RopeBase *)ent)->clientinpvs)
return;
// move rope base if it has a velocity
ent->origin += ent->velocity*FRAMETIME;
ent->setOrigin(ent->origin);
// calc velocities for rope_pieces
if(ent->rope)
((RopeBase *)ent)->FixAttachedRope(ent, (RopePiece *)ent->next_piece.Ptr());
else
((RopeBase *)ent)->RestrictFreeRope((RopePiece *)ent->next_piece.Ptr());
// do actual movement of rope pieces
piece = (RopePiece *)ent->next_piece.Ptr();
while(piece)
{
piece->groundentity = NULL;
G_AddCurrents(piece, &basevel);
onconveyor = (basevel != vec_zero);
if(fabs(piece->velocity[grav.x]) < 0.5)
piece->velocity[grav.x] = 0;
if(fabs(piece->velocity[grav.y]) < 0.5)
piece->velocity[grav.y] = 0;
old_origin = piece->origin;
G_CheckVelocity (piece);
// move origin
move = (piece->velocity + basevel)*FRAMETIME;
edict = piece->edict;
trace = G_PushRopePiece(piece, move);
if (!piece->edict->inuse)
gi.error("rope_piece removed from game (not a good thing)\n");
if (trace.fraction < 1)
{
G_ClipVelocity (piece->velocity, Vector(trace.plane.normal), piece->velocity, 1, ent->gravaxis);
// set groundentity if needed
if ((trace.plane.normal[grav.z]*grav.sign) > 0.7)
{
piece->groundentity = trace.ent;
piece->groundentity_linkcount = trace.ent->linkcount;
piece->groundplane = trace.plane;
piece->groundsurface = trace.surface;
piece->groundcontents = trace.contents;
}
if((move[grav.z] == 0) && onconveyor)
{
// Check if we still have a ground
piece->CheckGround();
}
move = piece->prev_piece->velocity;
f1 = move.length();
// dampen rope movement from dragging across the floor
if(f1 > 30)
{
tmpent = piece;
while(tmpent->rope_base && !tmpent->rope)
{
tmpent->velocity[grav.x] *= 0.98;
tmpent->velocity[grav.y] *= 0.98;
// also dampen movement of rope grabbers
if(tmpent->rope == ROPE_GRABBED)
{
tmpent->grabber->velocity[grav.x] *= 0.98;
tmpent->grabber->velocity[grav.y] *= 0.98;
}
tmpent = (RopePiece *)tmpent->prev_piece.Ptr();
}
}
// try moving a bit more if we can
f1 = 1 - trace.fraction;
move *= f1;
trace = G_PushRopePiece(piece, move);
}
piece->moveorg = piece->origin;
// keeps piece from getting too far away.
move = piece->origin - piece->prev_piece->origin;
f1 = ((RopeBase *)ent)->piecelength + 8;
if(move.length() > f1)
{
move.normalize();
move *= f1;
move += piece->prev_piece->origin;
trace = G_Trace(piece->prev_piece->origin, ent->mins, ent->maxs, move, piece, piece->edict->clipmask, "G_Physics_Rope");
piece->setOrigin(trace.endpos);
}
// move on to next rope_piece
tmpent = piece;
piece = (RopePiece *)piece->next_piece.Ptr();
}
// if the rope is attached to something, restrict it's movement properly
f1 = ((RopeBase *)ent)->piecelength + 9;
piece = tmpent;
while(piece && piece->rope_base)
{
if(piece->rope)
{
if(piece->attachent)
{
piece->setOrigin(piece->attachent->origin);
}
else if(piece->next_piece)
{
move = piece->origin - piece->next_piece->origin;
if(move.length() > f1)
{
move.normalize();
move *= f1;
move += piece->next_piece->origin;
trace = G_Trace(piece->origin, ent->mins, ent->maxs, move, piece, piece->edict->clipmask, "G_Physics_Rope");
piece->setOrigin(trace.endpos);
}
}
}
piece = (RopePiece *)piece->prev_piece.Ptr();
}
// set the angles now that all the pieces have been moved
piece = (RopePiece *)ent->next_piece.Ptr();
while(piece)
{
rope_set_angles(piece);
piece = (RopePiece *)piece->next_piece.Ptr();
}
}
/*================================================================
RopePiece class | main setup stuff
================================================================*/
/*SINED rope_piece (.6 .5 .1) (-8 -8 -8) (8 8 8) STEAM WIGGLE ATT_STEAM ATT_WIGGLE
Rope Piece - A single piece of a rope
STEAM : Makes this piece of the rope shoot out steam. Only does this while not attached.
WIGGLE : Makes this piece of the rope wiggle about randomly. Only does this while not attached.
ATT_STEAM : Makes this piece of the rope spray steam even while attached. STEAM must also be marked for this to work.
ATT_WIGGLE : Makes this piece of the rope wiggle around even while attached. WIGGLE must also be marked for this to work.
"target" : the "targetname" of the next piece in the rope. This should be blank if it's the last piece in the rope.
If a non-rope_piece entity is targeted, it will attach itself to it.
"targetname" : used for the previous piece in the rope to find and link to this piece of the rope.
"target2" : the targetname of the entity to attach this piece of the rope to.
Any piece of a rope can be attached to something and be triggered at any time to detach it.
Triggering the base of an attached rope will detach all attached points on that rope.
"wigglemove" : the amount of force the random wiggling has. Also used for the number of particles to throw while steaming
Default = 32
"steamtime" : number of seconds between each time the rope shoots out steam.
Default = 0.2
"wiggletime" : number of seconds between each time the rope wiggles.
Default = 0.5
"damage" : Amount of damage the rope piece does to things when it touches them. Will not hurt things if the rope is an untriggered START_STILL rope.
Default = 0
All other settings are set in the rope's rope_base entity.
*/
CLASS_DECLARATION(ScriptSlave, RopePiece, "rope_piece");
Event EV_RopePiece_Setup("ropepiece_setup");
ResponseDef RopePiece::Responses[] =
{
{&EV_RopePiece_Setup, (Response)RopePiece::Setup},
{&EV_Touch, (Response)RopePiece::CheckTouch},
{&EV_Activate, (Response)RopePiece::PieceTriggered},
{NULL, NULL}
};
RopePiece::RopePiece()
{
setSolidType(SOLID_TRIGGER);
setMoveType(MOVETYPE_NONE); // movement done by the RopeBase
takedamage = DAMAGE_NO;
edict->clipmask = MASK_SOLID;
wigglemove = G_GetIntArg("wigglemove", 32);
steamtime = G_GetFloatArg("steamtime", 0.2);
wiggletime = G_GetFloatArg("wiggletime", 0.5);
touchdamage = G_GetIntArg("damage", 0);
rope = ROPE_NONE;
attachent = NULL;
grabber = NULL;
if(!LoadingSavegame)
PostEvent(EV_RopePiece_Setup, 0.4 + G_Random(0.2));
}
void RopePiece::Setup(Event *ev)
{
int num;
Vector tmpvec;
Entity *tmpent;
// a rope base shouldn't do this setup stuff
if(this->isSubclassOf(RopeBase))
{
return;
}
if(!Targeted())
gi.error("rope_piece without targetname at (%i,%i,%i)\n", (int)origin[0], (int)origin[1], (int)origin[2]);
if(Target() && strcmp(Target(), ""))
{
num = G_FindTarget(0, Target());
if(!num)
{
gi.error("rope_piece at (%i,%i,%i) can't find its target\n", (int)origin[0], (int)origin[1], (int)origin[2]);
}
tmpent = G_GetEntity(num);
if(tmpent->isSubclassOf(RopePiece))
next_piece = (RopePiece *)tmpent;
else
{
next_piece = NULL;
attachent = tmpent;
rope = ROPE_ATTACHED;
}
}
if(Target2() && strcmp(Target2(), ""))
{
num = G_FindTarget(0, Target2());
if(!num)
gi.error("rope_piece at (%i,%i,%i) can't find attach target2\n", (int)origin[0], (int)origin[1], (int)origin[2]);
if(attachent && (Target() != Target2()))
gi.error("rope_piece at (%i,%i,%i) conflicting target & target2 attach names\n", (int)origin[0], (int)origin[1], (int)origin[2]);
next_piece = NULL;
attachent = G_GetEntity(num);
rope = ROPE_ATTACHED;
}
}
/*================================================================
RopePiece class | function for being touched
================================================================*/
void RopePiece::CheckTouch(Event *ev)
{
Entity *other;
RopePiece *curr_piece;
Vector org;
// shouldn't happen, but just in case
if(!rope_base)
gi.error("rope_piece without a base\n");
// don't touch if in a still rope
if(rope_base->movetype != MOVETYPE_ROPE)
return;
other = ev->GetEntity(1);
assert(other);
// do damage if applicable
if(touchdamage)
{
if(other->takedamage)
other->Damage(this, rope_base, touchdamage, origin, velocity, vec_zero, 0, 0, MOD_ROCKET, -1, -1, 1.0f );
}
// only get pushed by sentients
if(!other->isSubclassOf(Sentient))
return;
// if other is a player, also check for grabbing
if(other->isSubclassOf(Player))
{
// player already grabbed a rope
if(((Player *)other)->rope_grabbed)
return;
// nudge the rope if not trying to grab it
if(!(((Player *)other)->Buttons() & BUTTON_USE))
{
Pushed(other);
return;
}
// can't grab ropes with the NO_GRAB spawnflag set
if(rope_base->spawnflags & ROPE_NOGRAB)
return;
// can't grab a rope that's attached to something
// (this is to prevent major physics problems
// also can't grab a rope piece that's already grabbed
if(rope & (ROPE_GRABBED | ROPE_ATTACHED | ROPE_ABELOW))
return;
// check the player's grab debounce timmer
if(((Player *)other)->rope_debounce_time > level.time)
return;
// make sure that rope is infront of player
org = other->origin;
org[0] += 32*other->orientation[0][gravity_axis[other->gravaxis].x];
org[1] += 32*other->orientation[0][gravity_axis[other->gravaxis].y]*gravity_axis[other->gravaxis].sign;
org[2] += 32*other->orientation[0][gravity_axis[other->gravaxis].z]*gravity_axis[other->gravaxis].sign;
org = origin - org;
if(org.length() > (((RopeBase *)rope_base.Ptr())->piecelength + 16))
return;
grabber = ((Sentient *)other);
grabber->rope_grabbed = this;
rope = ROPE_GRABBED;
curr_piece = (RopePiece *)prev_piece.Ptr();
while(curr_piece)
{
if(curr_piece->rope != ROPE_NONE)
break;
curr_piece->rope = ROPE_TBELOW;
curr_piece = (RopePiece *)curr_piece->prev_piece.Ptr();
}
}
else
{
Pushed(other);
}
}
void RopePiece::Pushed(Entity *other)
{
Vector tmpvec;
if(push_debounce_time > level.time)
{
return;
}
// don't push a rope that's laying on the ground
if(groundentity)
return;
if(other->velocity.length() < 10)
return;
tmpvec = other->velocity*0.2;
((RopeBase *)rope_base.Ptr())->SetTouchVelocity(tmpvec, this, 0);
push_debounce_time = level.time + 0.5;
}
/*================================================================
RopePiece class | grabber climb and release stuff
================================================================*/
// releases any entity that's grabbing this rope piece
void RopePiece::Release(void)
{
RopePiece *curr_piece;
// only release if actually grabbed
if(!grabber)
return;
// take care of grabber first
grabber->rope_grabbed = NULL;
grabber->rope_debounce_time = level.time + 0.5;
if(!grabber->groundentity)
{
Vector tmpvec;
tmpvec = Vector(grabber->orientation[0])*200;
tmpvec.z += 200;
grabber->velocity[gravity_axis[grabber->gravaxis].x] += tmpvec.x;
grabber->velocity[gravity_axis[grabber->gravaxis].y] += tmpvec.y*gravity_axis[grabber->gravaxis].sign;
grabber->velocity[gravity_axis[grabber->gravaxis].z] += tmpvec.z*gravity_axis[grabber->gravaxis].sign;
grabber->TempAnim("jump", NULL);
}
// now take care of the rope
grabber = NULL;
rope = ROPE_TBELOW;
if(next_piece) // if grabbed below, everything's fine
if(((RopePiece *)next_piece.Ptr())->rope != ROPE_NONE)
return;
curr_piece = this;
while(curr_piece)
{
if(curr_piece->rope & ROPE_GRABBED)
return;
curr_piece->rope = ROPE_NONE;
curr_piece = (RopePiece *)(curr_piece->prev_piece.Ptr());
}
}
// move grabber down to the next piece
// releases if moves off the end of the rope
void RopePiece::ClimbDown(void)
{
trace_t trace;
Vector tmpvec;
// make sure we've got a grabber
if(!grabber)
return;
// check climbing timmer
if(grabber->rope_debounce_time > level.time)
return;
// try to move player down a full rope piece length immediatly
tmpvec = grabber->origin;
tmpvec[gravity_axis[grabber->gravaxis].z] -= ((RopeBase *)rope_base.Ptr())->piecelength*gravity_axis[grabber->gravaxis].sign;
trace = G_Trace(grabber->origin, grabber->mins, grabber->maxs, tmpvec, grabber, grabber->edict->clipmask, "RopePiece::ClimbDown");
grabber->setOrigin(trace.endpos);
// climbing debounce timmer
grabber->rope_debounce_time = level.time + 0.2;
// give the player a bit of a downward boost to get down
grabber->velocity[gravity_axis[grabber->gravaxis].z] -= 50*gravity_axis[grabber->gravaxis].sign;
// no more rope, so completely release rope
if(!next_piece)
{
Release();
return;
}
// don't move down if it's already grabbed or attached
if(((RopePiece *)next_piece.Ptr())->rope & (ROPE_GRABBED | ROPE_ATTACHED))
return;
//all clear to climb down, so do it
grabber->rope_grabbed = next_piece;
((RopePiece *)next_piece.Ptr())->grabber = grabber;
((RopePiece *)next_piece.Ptr())->rope = ROPE_GRABBED;
rope = ROPE_TBELOW;
grabber = NULL;
}
// move grabber up to the next piece
// doesn't climb past the first piece of the rope
void RopePiece::ClimbUp(void)
{
trace_t trace;
Vector tmpvec;
// make sure we've got a grabber
if(!grabber)
return;
// check climbing timmer
if(grabber->rope_debounce_time > level.time)
return;
// check for initing ropesound timmer
if(level.time - grabber->rope_debounce_time > 0.5)
grabber->ropesound = true;
// climbing debounce timmer
grabber->rope_debounce_time = level.time + 0.3;
// check if there's more rope up above
if(!((RopePiece *)prev_piece.Ptr())->rope_base)
return;
// try to move player up a full rope piece length immediatly
tmpvec = grabber->origin;
tmpvec[gravity_axis[grabber->gravaxis].z] += ((RopeBase *)rope_base.Ptr())->piecelength*gravity_axis[grabber->gravaxis].sign;
trace = G_Trace(grabber->origin, grabber->mins, grabber->maxs, tmpvec, grabber, grabber->edict->clipmask, "RopePiece::ClimbUp");
grabber->setOrigin(trace.endpos);
// don't move up if it's already grabbed or attached
if(((RopePiece *)prev_piece.Ptr())->rope & (ROPE_GRABBED | ROPE_ATTACHED))
return;
// make the climbing sound
if(grabber->ropesound)
{
grabber->RandomGlobalSound("sound_rope", 1, CHAN_BODY);
grabber->ropesound = false;
}
else
{
grabber->ropesound = true;
}
//all clear to climb down, so do it
grabber->rope_grabbed = prev_piece;
// give a bit of a vertical boost
grabber->velocity[gravity_axis[grabber->gravaxis].z] -= 50*gravity_axis[grabber->gravaxis].sign;
((RopePiece *)prev_piece.Ptr())->grabber = grabber;
((RopePiece *)prev_piece.Ptr())->rope = ROPE_GRABBED;
if(next_piece && ((RopePiece *)next_piece.Ptr())->rope)
rope = ROPE_TBELOW;
else
rope = ROPE_NONE;
grabber = NULL;
}
void RopePiece::Detach(void)
{
qboolean attbelow;
//only if we're attached to something
if(!attachent)
return;
// adjust all the rope values of this rope's pieces
// to allow it to move freely and for it to be grabbed.
attachent = NULL;
if(next_piece)
{
if(((RopePiece *)next_piece.Ptr())->rope & (ROPE_ATTACHED | ROPE_ABELOW))
attbelow = true;
else
attbelow = false;
}
else
{
attbelow = false;
}
if(attbelow)
{
rope = ROPE_ABELOW;
}
else
{
RopePiece *curr_piece;
curr_piece = this;
while(curr_piece)
{
curr_piece->rope = ROPE_NONE;
curr_piece = (RopePiece *)curr_piece->prev_piece.Ptr();
}
}
}
void RopePiece::PieceTriggered(Event *ev)
{
Detach();
}
/*================================================================
RopePiece class | speaming and wiggling, oh my
================================================================*/
void RopePiece::Steam(void)
{
Vector tmpvec;
if(steam_debounce_time > level.time)
return;
steam_debounce_time = level.time + steamtime;
tmpvec = velocity*(-1);
tmpvec.normalize();
SpawnSparks(origin, tmpvec, wigglemove/2);
}
void RopePiece::Wiggle(void)
{
Vector tmpvec;
int i;
if(wiggle_debounce_time > level.time)
return;
wiggle_debounce_time = level.time + wiggletime;
tmpvec = velocity;
for(i = 0; i < 3; i++)
tmpvec[i] += crandom()*wigglemove;
((RopeBase *)rope_base.Ptr())->SetTouchVelocity(tmpvec, this, 1);
}
/*================================================================
RopeBase class | main setup stuff
================================================================*/
/*SINED rope_base (.7 .6 .2) (-8 -8 -8) (8 8 8) NOATTGRAB START_STILL NO_GRAB
Rope Base - the main control and top end attachment entity for ropes
This entity is the point to where ropes attach their top end. It's a stationary point entity. All setting for the whole rope are specified through this entity. If you want/need to trigger a rope to do something, then this is the entity to trigger. Trying to trigger a rope_piece will do nothing.
NOATTGRAB : makes the entire rope non-grabbable when attached anywhere along the rope.
START_STILL : Specifies that the whole rope will be completely stationary untill it is either triggered, or grabbed.
NO_GRAB : Specifies that player's can not grab the rope. They'll still bump it from walking through it, but they can not grab it.
"targetname" : The name that the rope is triggered with.
"target" : The "targetname" of the first rope_piece in the rope.
"piecelength" : The distance between each piece of the rope.
Default = 24
"piecemodel" : The model to use for the rope pieces.
Default = rope.def
"pieceskin" : The skin number of the model to use for the rope pieces.
Default = 0
"ropedampener" : Horizontal velocity dampener for the rope.
Default = 0.8
"playerdampener" : Horizontal velocity dampener for a player grabbing the rope.
Default = 0.95
"stiffness" : Movement restricter on the amount that the rope can flex and bend. Valid values are from -1 (no restriction) to 1 (tried to be perfectly straight. The position of the first rope piece determines what direction the rope is pushed from the base of the rope.
Default = -1
"strength" : How strongly a stiff rope goes to position.
Default = 1;
"gravityaxis" sets the axis of gravity for the rope. Valid values are 0 to 5. Here's list of what orientation goes with which value.
0: upright
1: South is down
2: East is down
3: upsidedown
4: North is down
5: West is down
*/
CLASS_DECLARATION(RopePiece, RopeBase, "rope_base");
Event EV_RopeBase_Setup("ropebase_setup");
Event EV_RopeBase_PVSCheck("ropebase_pvscheck");
ResponseDef RopeBase::Responses[] =
{
{&EV_RopeBase_Setup, (Response)RopeBase::setup},
{&EV_RopeBase_PVSCheck, (Response)RopeBase::PVSCheck},
{&EV_Activate, (Response)RopeBase::Activate},
{NULL, NULL}
};
RopeBase::RopeBase()
{
str tmpstr;
setOrigin(origin);
setSolidType(SOLID_NOT);
setMoveType(MOVETYPE_NONE); // till after setup
gravaxis = G_GetIntArg("gravityaxis", 0);
if(gravaxis < 0)
gravaxis = 0;
else if(gravaxis > 5)
gravaxis = 5;
SetGravityAxis(gravaxis);
piecelength = G_GetFloatArg("piecelength", 24);
pieceframe = floor(piecelength - 16);
pieceskin = G_GetIntArg("pieceskin", 0);
dotlimit = G_GetFloatArg("stiffness", -1);
if(dotlimit < -1)
dotlimit = -1;
else if(dotlimit > 1)
dotlimit = 1;
strength = G_GetFloatArg("strength", 1);
playerdampener = G_GetFloatArg("playerdampener", 0.95);
if(playerdampener < 0.001)
playerdampener = 0.001;
else if(playerdampener > 1)
playerdampener = 1;
ropedampener = G_GetFloatArg("ropedampener", 0.75);
if(ropedampener < 0.001)
ropedampener = 0.001;
else if(ropedampener > 1)
ropedampener = 1;
piecemodel = G_GetStringArg("piecemodel", "rope.def");
modelIndex(piecemodel.c_str());
hideModel(); // make sure the base doesn't have a visual model
rope_base = NULL;
prev_piece = NULL;
rope = ROPE_NONE;
if(!LoadingSavegame)
{
// setup all the pieces of the rope
PostEvent(EV_RopeBase_Setup, 1 + G_Random(0.2));
// allow the rope to move a bit at first
clientinpvs = true;
PostEvent(EV_RopeBase_PVSCheck, 5);
}
}
void RopeBase::setup(Event *ev)
{
RopePiece *lastpiece, *currpiece;
int num;
Vector smin, smax, tmpvec;
if(!strcmp(Target(), ""))
gi.error("rope_base without target\n");
num = G_FindTarget(0, Target());
if(!num)
gi.error("rope_base can not find target\n");
next_piece = (RopePiece *)G_GetEntity(num);
// set rope direction for stiff ropes
if(dotlimit)
{
ropedir = next_piece->origin - origin;
ropedir.normalize();
}
// set the size for the rope piece's touching box
smin.x = smin.y = smin.z = -piecelength;
smax.x = smax.y = smax.z = piecelength;
//set rope piece id numbers for use in various things
piecenum = 0;
num = 0;
currpiece = (RopePiece *)next_piece.Ptr();
lastpiece = this;
while(currpiece)
{
num++;
currpiece->piecenum = num;
currpiece->moveorg = currpiece->origin;
currpiece->setMoveType(MOVETYPE_ROPE);
currpiece->setSize(smin, smax);
currpiece->setModel(piecemodel);
currpiece->edict->s.frame = pieceframe;
currpiece->edict->s.skinnum = pieceskin;
currpiece->rope_base = this;
currpiece->prev_piece = lastpiece;
tmpvec = lastpiece->origin - currpiece->origin;
currpiece->angles = tmpvec.toAngles();
currpiece->angles[PITCH] = -(currpiece->angles[PITCH]);
currpiece->setAngles(currpiece->angles);
currpiece->SetGravityAxis(gravaxis);
lastpiece = currpiece;
currpiece = (RopePiece *)currpiece->next_piece.Ptr();
}
// go through the pieces again to set attach flags
currpiece = lastpiece;
num = 0;
while(currpiece)
{
// mark all pieces above this as attached
if(currpiece->rope & ROPE_ATTACHED)
num = 1;
if(currpiece->rope == ROPE_NONE && num)
currpiece->rope = ROPE_ABELOW;
currpiece = (RopePiece *)currpiece->prev_piece.Ptr();
}
if(!(spawnflags & ROPE_START_STILL))
{
setMoveType(MOVETYPE_ROPE);
}
}
void RopeBase::Activate(Event *ev)
{
setMoveType(MOVETYPE_ROPE);
if(rope == ROPE_ABELOW)
{
RopePiece *curr_piece;
curr_piece = (RopePiece *)next_piece.Ptr();
while(curr_piece)
{
curr_piece->Detach();
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
}
}
}
/* This check if the rope is in a client's PVS. If not, then it
prevents it from checking it's physics
*/
void RopeBase::PVSCheck(Event *ev)
{
Entity *ent;
int i;
RopePiece *curr_piece;
// if the rope is in use, don't bother PVS checking
if(rope)
{
clientinpvs = true;
PostEvent(EV_RopeBase_PVSCheck, 2);
return;
}
for(i = 1; i <= maxclients->value; i++)
{
if(!g_edicts[i].inuse || !g_edicts[i].entity)
{
continue;
}
ent = g_edicts[i].entity;
curr_piece = this;
while(curr_piece)
{
if(gi.inPVS(curr_piece->worldorigin.vec3(), ent->worldorigin.vec3()))
{
clientinpvs = true;
PostEvent(EV_RopeBase_PVSCheck, 2);
return;
}
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
}
}
clientinpvs = false;
PostEvent(EV_RopeBase_PVSCheck, 1);
}
/*================================================================
RopeBase class | free hanging rope restriction
================================================================*/
void RopeBase::RestrictFreeRope(RopePiece *curr_piece)
{
Vector ropevec; // rope piece vector
Vector velpart; // velocity component moving to or away from rope piece
float ropelen; // length of extended rope
float f1, f2; // restrainment forces
float i1, i2; // intermediate values
Vector tmpvec;
while(curr_piece)
{
// add in velocity from gravity
curr_piece->velocity[gravity_axis[gravaxis].z] -= gravity*sv_gravity->value*FRAMETIME*gravity_axis[gravaxis].sign;
ropevec = curr_piece->prev_piece->origin - curr_piece->moveorg;
ropelen = ropevec.length();
// if location is beyond the rope's reach
if (ropelen > (piecelength - 2))
{
// inertial dampener to reduce exagerated motion.
curr_piece->velocity[gravity_axis[gravaxis].x] *= ropedampener;
curr_piece->velocity[gravity_axis[gravaxis].y] *= ropedampener;
// determine velocity component of rope vector
i1 = DotProduct(curr_piece->velocity.vec3(), ropevec.vec3());
i2 = DotProduct(ropevec.vec3(), ropevec.vec3());
velpart = ropevec*(i1/i2);
// restrainment default force
f2 = (ropelen - (piecelength - 3)) * 5;
// if velocity heading is away from the rope piece
if (i1 < 0)
{
// if rope has streched too much
if (ropelen > piecelength+64)
{
// remove velocity component moving away from rope base
curr_piece->velocity -= velpart;
}
f1 = f2;
}
else // if velocity heading is towards the rope piece
{
if (velpart.length() < f2)
f1 = f2 - velpart.length();
else
f1 = 0;
}
if(f1) // applies rope restrainment
{
ropevec.normalize();
curr_piece->velocity += ropevec*f1;
}
}
// stiffen the rope if needed
if(dotlimit > -1)
{
// get direction of destination
if(((RopePiece *)curr_piece->prev_piece.Ptr())->rope_base) // rope piece above
{
ropevec = curr_piece->prev_piece->origin - ((RopePiece *)curr_piece->prev_piece.Ptr())->prev_piece->origin;
ropevec.normalize();
}
else // rope base above current piece
{
ropevec = ropedir;
}
// current direction
tmpvec = curr_piece->prev_piece->origin - curr_piece->origin;
ropelen = tmpvec.normalize2();
// get to dot product
f1 = DotProduct(ropevec.vec3(), tmpvec.vec3());
if((f1 - 0.2) < dotlimit)
{
i1 = DotProduct(curr_piece->velocity.vec3(), ropevec.vec3());
velpart = ropevec*i1;
if((f1 + 0.2) < dotlimit)
f2 = (dotlimit - (f1 + 0.2))*150*strength;
else if(f1 < dotlimit)
f2 = (dotlimit - f1)*100*strength;
else if((f1 - 0.2) < dotlimit)
f2 = (dotlimit - (f1 - 0.1))*50*strength;
else
f2 = (dotlimit - (f1 - 0.2))*20*strength;
if(i1 < 0)
curr_piece->velocity -= velpart;
curr_piece->velocity += ropevec*f2;
}
}
// check if piece is wiggling
if(curr_piece->spawnflags & PIECE_WIGGLE)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTWIGGLE) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Wiggle();
}
}
// check if piece is steaming
if(curr_piece->spawnflags & PIECE_STEAM)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTSTEAM) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Steam();
}
}
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
}
}
/*================================================================
RopeBase class | velocity setting for touching
================================================================*/
void RopeBase::SetTouchVelocity(Vector fullvel, RopePiece *touched_piece, int set_z)
{
float tmpflt;
RopePiece *currpiece;
currpiece = touched_piece;
while(currpiece->rope_base)
{
tmpflt = (float)currpiece->piecenum / (float)touched_piece->piecenum;
if(set_z)
{
currpiece->velocity += fullvel*tmpflt;
if(currpiece->rope & ROPE_GRABBED)
{
tmpflt *= 0.5;
currpiece->grabber->velocity += fullvel*tmpflt;
}
}
else
{
currpiece->velocity[gravity_axis[gravaxis].x] += fullvel[gravity_axis[gravaxis].x]*tmpflt;
currpiece->velocity[gravity_axis[gravaxis].y] += fullvel[gravity_axis[gravaxis].y]*tmpflt;
if(currpiece->rope & ROPE_GRABBED)
{
tmpflt *= 0.5;
currpiece->grabber->velocity[gravity_axis[gravaxis].x] += fullvel[gravity_axis[gravaxis].x]*tmpflt;
currpiece->grabber->velocity[gravity_axis[gravaxis].y] += fullvel[gravity_axis[gravaxis].y]*tmpflt;
}
}
currpiece = (RopePiece *)currpiece->prev_piece.Ptr();
}
}
/*================================================================
RopeBase class | restrict a grabbed or attached rope
================================================================*/
void RopeBase::FixAttachedRope(RopePiece *curr_base, RopePiece *curr_piece)
{
Sentient *grabber;
Vector tmpvec;
while(curr_piece)
{
// a player has grabbed the rope at this piece
if(curr_piece->rope & ROPE_GRABBED)
{
grabber = curr_piece->grabber;
if(!grabber)
gi.error("FixAttachedRope: can not find rope grabber\n");
// restrict the player to the rope
RestrictPlayer(curr_base, curr_piece);
// set position and velocity of grabbed piece
tmpvec[gravity_axis[grabber->gravaxis].x] = grabber->orientation[0][0]*16 + grabber->orientation[1][0]*6;
tmpvec[gravity_axis[grabber->gravaxis].y] = (grabber->orientation[0][1]*16 + grabber->orientation[1][1]*6)*gravity_axis[grabber->gravaxis].sign;
tmpvec[gravity_axis[grabber->gravaxis].z] = (grabber->orientation[0][2]*16 + grabber->orientation[1][2]*6 + 50)*gravity_axis[grabber->gravaxis].sign;
tmpvec += grabber->origin;
tmpvec -= grabber->velocity*FRAMETIME;
// don't do right after climbing up or down though
if(curr_piece->grabber->rope_debounce_time < (level.time + 0.2))
{
curr_piece->setOrigin(tmpvec);
curr_piece->velocity = grabber->velocity;
}
//just make the rope straight if player isn't on the ground
if((!grabber->groundentity) && (curr_piece->rope & ROPE_STRAIGHTEN))
StraightenRope(curr_base, curr_piece);
else // player is on the ground, so do it all
RestrictGrabbedRope(curr_base, curr_piece);
curr_piece->rope &= ~ROPE_STRAIGHTEN;
if(curr_piece->next_piece)
curr_base = curr_piece;
}
else if(curr_piece->rope == ROPE_ATTACHED)
{
float max, curr;
curr_piece->setOrigin(curr_piece->attachent->origin);
curr_piece->velocity = curr_piece->attachent->velocity;
// check if the rope's pulled tight
tmpvec = curr_piece->origin - curr_base->origin;
curr = tmpvec.length();
max = (curr_piece->piecenum - curr_base->piecenum)*piecelength;
if(curr > max)
StraightenRope(curr_base, curr_piece);
else
RestrictGrabbedRope(curr_base, curr_piece);
if(curr_piece->next_piece)
curr_base = curr_piece;
}
// nothing attached to rest of rope
else if(!curr_piece->rope)
{
RestrictFreeRope(curr_piece);
return;
}
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
}
}
void RopeBase::RestrictGrabbedRope(RopePiece *fix_base, RopePiece *grabbed_piece)
{
RopePiece *curr_piece;
Vector ropevec; // rope piece vector
Vector velpart; // velocity component moving to or away from rope piece
float ropelen; // length of extended rope
float f1, f2; // restrainment forces
float i1, i2; // intermediate values
Vector tmpvec;
trace_t trace;
//first restrict the rope to the player
curr_piece = (RopePiece *)grabbed_piece->prev_piece.Ptr();
while(curr_piece != fix_base)
{
// add in velocity from gravity
curr_piece->velocity[gravity_axis[gravaxis].z] -= gravity*sv_gravity->value*FRAMETIME*gravity_axis[gravaxis].sign;
ropevec = curr_piece->next_piece->origin - curr_piece->origin;
ropelen = ropevec.length();
// if location is beyond the rope's reach
if (ropelen > piecelength)
{
// inertial dampener for the free rope's motion.
curr_piece->velocity[gravity_axis[gravaxis].x] *= ropedampener;
curr_piece->velocity[gravity_axis[gravaxis].y] *= ropedampener;
// determine velocity component of rope vector
i1 = DotProduct(curr_piece->velocity.vec3(), ropevec.vec3());
i2 = DotProduct(ropevec.vec3(), ropevec.vec3());
velpart = ropevec*(i1 / i2);
// restrainment default force
f2 = (ropelen - piecelength) * 5;
// if velocity heading is away from the rope piece
if (i1 < 0)
{
if (ropelen > piecelength+1)
{
// remove velocity component moving away from hook
curr_piece->velocity -= velpart;
}
f1 = f2;
}
else // if velocity heading is towards the rope piece
{
if(velpart.length() < f2)
f1 = f2 - velpart.length();
else
f1 = 0;
}
ropevec.normalize();
if(f1) // applies rope restrainment
curr_piece->velocity += ropevec*f1;
}
// keeps piece from getting too far away.
if (ropelen > piecelength)
{
tmpvec = ropevec.normalize();
tmpvec *= piecelength;
tmpvec = curr_piece->next_piece->origin - tmpvec;
trace = G_Trace(curr_piece->next_piece->origin, mins, maxs, tmpvec, curr_piece, curr_piece->edict->clipmask, "RopeBase::RestrictGrabbedRope");
curr_piece->setOrigin(trace.endpos);
}
curr_piece = (RopePiece *)curr_piece->prev_piece.Ptr();
}
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
// now restrict the rope to the rope base
while(!(curr_piece->rope & (ROPE_GRABBED | ROPE_ATTACHED)))
{
ropevec = curr_piece->prev_piece->origin - curr_piece->origin;
ropelen = ropevec.length();
// if location is beyond the rope's reach
if (ropelen > piecelength)
{
// inertial dampener to reduce exagerated motion.
curr_piece->velocity[gravity_axis[gravaxis].x] *= ropedampener;
curr_piece->velocity[gravity_axis[gravaxis].y] *= ropedampener;
// determine velocity component of rope vector
i1 = DotProduct(curr_piece->velocity.vec3(), ropevec.vec3());
i2 = DotProduct(ropevec.vec3(), ropevec.vec3());
velpart = ropevec*(i1/i2);
// restrainment default force
f2 = (ropelen - (piecelength - 1)) * 5;
// if velocity heading is away from the rope piece
if (i1 < 0)
{
// if rope has streched a bit
if (ropelen > piecelength+32)
{
// remove velocity component moving away from rope base
curr_piece->velocity-= velpart;
}
f1 = f2;
}
else // if velocity heading is towards the rope piece
{
if(velpart.length() < f2)
f1 = f2 - velpart.length();
else
f1 = 0;
}
ropevec.normalize();
if(f1) // applies rope restrainment
curr_piece->velocity += ropevec*f1;
}
// keeps piece from getting too far away.
if(ropelen > piecelength)
{
tmpvec = ropevec;
tmpvec.normalize();
tmpvec *= piecelength;
tmpvec = curr_piece->prev_piece->origin - tmpvec;
trace = G_Trace( curr_piece->prev_piece->origin, mins, maxs, tmpvec, curr_piece, curr_piece->edict->clipmask, "RopeBase::RestrictGrabbedRope");
curr_piece->setOrigin(trace.endpos);
}
// check if piece is wiggling
if(curr_piece->spawnflags & PIECE_WIGGLE)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTWIGGLE) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Wiggle();
}
}
// check if piece is steaming
if(curr_piece->spawnflags & PIECE_STEAM)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTSTEAM) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Steam();
}
}
curr_piece = (RopePiece *)curr_piece->next_piece.Ptr();
}
}
// for when the player is pulling the rope tight
void RopeBase::StraightenRope(RopePiece *fix_base, RopePiece *grabbed_piece)
{
RopePiece *curr_piece;
Vector ropevec, tmpvec;
int ropelen = 0; //this keeps track of the length position of the current piece
float tnum, f1, f2;
float plength;
//this calculates the vector along which to set the rope pieces
ropevec = fix_base->origin - grabbed_piece->origin;
plength = ropevec.normalize2();
tnum = grabbed_piece->piecenum - fix_base->piecenum;
plength /= tnum;
curr_piece = grabbed_piece;
while(curr_piece != fix_base)
{
f1 = curr_piece->piecenum - fix_base->piecenum;
f2 = f1/tnum;
curr_piece->velocity = grabbed_piece->velocity*f2;
f2 = 1 - f2;
curr_piece->velocity += fix_base->velocity*f2;
tmpvec = grabbed_piece->origin + ropevec*ropelen;
curr_piece->setOrigin(tmpvec);
ropelen += plength;
// check if piece is wiggling
if(curr_piece->spawnflags & PIECE_WIGGLE)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTWIGGLE) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Wiggle();
}
}
// check if piece is steaming
if(curr_piece->spawnflags & PIECE_STEAM)
{
// if rope is attached, check if we still should steam
if((curr_piece->spawnflags & PIECE_ATTSTEAM) ||
!(rope & (ROPE_ATTACHED | ROPE_ABELOW)))
{
curr_piece->Steam();
}
}
curr_piece = (RopePiece *)curr_piece->prev_piece.Ptr();
}
}
/*================================================================
RopeBase class | restrict a player to the rope he grabbed
================================================================*/
EXPORT_FROM_DLL void RopeBase::RestrictPlayer(RopePiece *curr_base, RopePiece *grabbed_piece)
{
Sentient *grabber;
Vector ropevec; // rope piece vector
Vector velpart; // velocity component moving to or away from rope piece
float ropelen; // length of extended rope
int ropemax; // max dist player can get away while on rope.
float f1, f2; // restrainment forces
float i1, i2; // intermediate values
Vector tmpvec;
grabber = grabbed_piece->grabber;
if(!grabber)
gi.error("RestrictPlayer: can't find rope grabber\n");
tmpvec[gravity_axis[grabber->gravaxis].x] = grabber->orientation[0][0]*16 - grabber->orientation[1][0]*4;
tmpvec[gravity_axis[grabber->gravaxis].y] = (grabber->orientation[0][1]*16 - grabber->orientation[1][1]*4)*gravity_axis[grabber->gravaxis].sign;
tmpvec[gravity_axis[grabber->gravaxis].z] = (grabber->orientation[0][2]*16 - grabber->orientation[1][2]*4 + 50)*gravity_axis[grabber->gravaxis].sign;
tmpvec += grabber->origin;
ropevec = curr_base->origin - tmpvec;
ropelen = ropevec.length();
ropemax = (grabbed_piece->piecenum - curr_base->piecenum)*piecelength;
// keep it from snapping together from its usual semi-streached state
ropemax *= 1.1;
// if location is beyond the rope's reach
if (ropelen > (ropemax - 8))
{
// inertial dampener for the player's movement while on the rope.
grabber->velocity[gravity_axis[grabber->gravaxis].x] *= playerdampener;
grabber->velocity[gravity_axis[grabber->gravaxis].y] *= playerdampener;
// determine velocity component of rope vector
i1 = DotProduct(grabber->velocity.vec3(), ropevec.vec3());
i2 = DotProduct(ropevec.vec3(), ropevec.vec3());
i2 = i1 / i2;
velpart = ropevec*i2;
// restrainment default force
f2 = (ropelen - (ropemax - 8)) * 5;
// if velocity heading is away from the rope piece
if (i1 < 0)
{
// if rope has streched a bit, remove velocity
// component moving away from hook
if (ropelen > ropemax)
grabber->velocity -= velpart;
f1 = f2;
}
else // if velocity heading is towards the rope piece
{
i2 = velpart.length();
if (i2 < f2)
f1 = f2 - i2;
else
f1 = 0;
}
ropevec.normalize();
if(f1) // applies rope restrainment
{
grabber->velocity += ropevec*f1;
grabbed_piece->rope |= ROPE_STRAIGHTEN;
}
}
}