/* ================================================================ 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; } } }