diff --git a/src/g_phys.c b/src/g_phys.c index ca8cd57..70d6b4c 100644 --- a/src/g_phys.c +++ b/src/g_phys.c @@ -1,288 +1,358 @@ -// g_phys.c +/* ======================================================================= + * + * Quake IIs legendary physic engine. + * + * ======================================================================= + */ + #include "header/local.h" +#define STOP_EPSILON 0.1 +#define MAX_CLIP_PLANES 5 + +#define sv_stopspeed 100 +#define sv_friction 6 +#define sv_waterfriction 1 + /* + * pushmove objects do not obey gravity, and do not interact + * with each other or trigger fields, but block normal movement + * and push normal objects when they move. + * + * onground is set for toss objects when they come to a complete + * rest. It is set for steping or walking objects. + * + * doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH + * bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS + * corpses are SOLID_NOT and MOVETYPE_TOSS + * crates are SOLID_BBOX and MOVETYPE_TOSS + * walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP + * flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY + * + * solid_edge items only clip against bsp models. + */ - -pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. - -onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects - -doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH -bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS -corpses are SOLID_NOT and MOVETYPE_TOSS -crates are SOLID_BBOX and MOVETYPE_TOSS -walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP -flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY - -solid_edge items only clip against bsp models. - -*/ - - -/* -============ -SV_TestEntityPosition - -============ -*/ -edict_t *SV_TestEntityPosition (edict_t *ent) -{ - trace_t trace; - int mask; +edict_t * +SV_TestEntityPosition(edict_t *ent) +{ + if (!ent) + { + return NULL; + } + + trace_t trace; + int mask; if (ent->clipmask) + { mask = ent->clipmask; + } else + { mask = MASK_SOLID; - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); - + } + + trace = gi.trace(ent->s.origin, + ent->mins, + ent->maxs, + ent->s.origin, + ent, + mask); + if (trace.startsolid) + { return g_edicts; - + } + return NULL; } - -/* -================ -SV_CheckVelocity -================ -*/ -void SV_CheckVelocity (edict_t *ent) +void +SV_CheckVelocity(edict_t *ent) { - int i; - - // - // bound velocity - // - for (i=0 ; i<3 ; i++) + int i; + + if (!ent) + { + return; + } + + /* bound velocity */ + for (i = 0; i < 3; i++) { if (ent->velocity[i] > sv_maxvelocity->value) + { ent->velocity[i] = sv_maxvelocity->value; + } else if (ent->velocity[i] < -sv_maxvelocity->value) + { ent->velocity[i] = -sv_maxvelocity->value; + } } } /* -============= -SV_RunThink - -Runs thinking code for this frame if necessary -============= -*/ -qboolean SV_RunThink (edict_t *ent) + * Runs thinking code for + * this frame if necessary + */ +qboolean +SV_RunThink(edict_t *ent) { - float thinktime; - + float thinktime; + + if (!ent) + { + return false; + } + thinktime = ent->nextthink; + if (thinktime <= 0) + { return true; - if (thinktime > level.time+0.001) + } + + if (thinktime > level.time + 0.001) + { return true; - + } + ent->nextthink = 0; + if (!ent->think) - gi.error ("NULL ent->think"); - ent->think (ent); + { + gi.error("NULL ent->think"); + } + + ent->think(ent); return false; } /* -================== -SV_Impact - -Two entities have touched, so run their touch functions -================== -*/ -void SV_Impact (edict_t *e1, trace_t *trace) + * Two entities have touched, so + * run their touch functions + */ +void +SV_Impact(edict_t *e1, trace_t *trace) { - edict_t *e2; + edict_t *e2; + + if (!e1 || !trace) + { + return; + } e2 = trace->ent; - if (e1->touch && e1->solid != SOLID_NOT) - e1->touch (e1, e2, &trace->plane, trace->surface); - - if (e2->touch && e2->solid != SOLID_NOT) - e2->touch (e2, e1, NULL, NULL); + if (e1->touch && (e1->solid != SOLID_NOT)) + { + e1->touch(e1, e2, &trace->plane, trace->surface); + } + + if (e2->touch && (e2->solid != SOLID_NOT)) + { + e2->touch(e2, e1, NULL, NULL); + } } - /* -================== -ClipVelocity - -Slide off of the impacting object -returns the blocked flags (1 = floor, 2 = step / wall) -================== -*/ -#define STOP_EPSILON 0.1 - -int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) + * Slide off of the impacting object + * returns the blocked flags: + * 1 = floor + * 2 = step / wall + */ +int +ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { - float backoff; - float change; - int i, blocked; - - blocked = 0; - if (normal[2] > 0) - blocked |= 1; // floor - if (!normal[2]) - blocked |= 2; // step - - backoff = DotProduct (in, normal) * overbounce; + float backoff; + float change; + int i, blocked; - for (i=0 ; i<3 ; i++) + blocked = 0; + + if (normal[2] > 0) { - change = normal[i]*backoff; + blocked |= 1; /* floor */ + } + + if (!normal[2]) + { + blocked |= 2; /* step */ + } + + backoff = DotProduct(in, normal) * overbounce; + + for (i = 0; i < 3; i++) + { + change = normal[i] * backoff; out[i] = in[i] - change; - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + + if ((out[i] > -STOP_EPSILON) && (out[i] < STOP_EPSILON)) + { out[i] = 0; + } } return blocked; } - /* -============ -SV_FlyMove - -The basic solid body movement clip that slides along multiple planes -Returns the clipflags if the velocity was modified (hit something solid) -1 = floor -2 = wall / step -4 = dead stop -============ -*/ -#define MAX_CLIP_PLANES 5 -int SV_FlyMove (edict_t *ent, float time, int mask) + * The basic solid body movement clip that slides + * along multiple planes. Returns the clipflags if + * the velocity was modified (hit something solid): + * 1 = floor + * 2 = wall / step + * 4 = dead stop + */ +int +SV_FlyMove(edict_t *ent, float time, int mask) { - edict_t *hit; - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity, new_velocity; - int i, j; - trace_t trace; - vec3_t end; - float time_left; - int blocked; - + edict_t *hit; + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity, new_velocity; + int i, j; + trace_t trace; + vec3_t end; + float time_left; + int blocked; + + if (!ent) + { + return 0; + } + numbumps = 4; - + blocked = 0; - VectorCopy (ent->velocity, original_velocity); - VectorCopy (ent->velocity, primal_velocity); + VectorCopy(ent->velocity, original_velocity); + VectorCopy(ent->velocity, primal_velocity); numplanes = 0; - + time_left = time; ent->groundentity = NULL; - for (bumpcount=0 ; bumpcounts.origin[i] + time_left * ent->velocity[i]; - trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, mask); + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) + { + for (i = 0; i < 3; i++) + { + end[i] = ent->s.origin[i] + time_left * ent->velocity[i]; + } + + trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, ent, mask); if (trace.allsolid) - { // entity is trapped in another solid - VectorCopy (vec3_origin, ent->velocity); + { + /* entity is trapped in another solid */ + VectorCopy(vec3_origin, ent->velocity); return 3; } if (trace.fraction > 0) - { // actually covered some distance - VectorCopy (trace.endpos, ent->s.origin); - VectorCopy (ent->velocity, original_velocity); + { + /* actually covered some distance */ + VectorCopy(trace.endpos, ent->s.origin); + VectorCopy(ent->velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) - break; // moved the entire distance + { + break; /* moved the entire distance */ + } hit = trace.ent; if (trace.plane.normal[2] > 0.7) { - blocked |= 1; // floor - if ( hit->solid == SOLID_BSP) + blocked |= 1; /* floor */ + + if (hit->solid == SOLID_BSP) { ent->groundentity = hit; ent->groundentity_linkcount = hit->linkcount; } } + if (!trace.plane.normal[2]) { - blocked |= 2; // step + blocked |= 2; /* step */ } - // - // run the impact function - // - SV_Impact (ent, &trace); - if (!ent->inuse) - break; // removed by the impact function + /* run the impact function */ + SV_Impact(ent, &trace); + + if (!ent->inuse) + { + break; /* removed by the impact function */ + } - time_left -= time_left * trace.fraction; - - // cliped to another plane + + /* cliped to another plane */ if (numplanes >= MAX_CLIP_PLANES) - { // this shouldn't really happen - VectorCopy (vec3_origin, ent->velocity); + { + /* this shouldn't really happen */ + VectorCopy(vec3_origin, ent->velocity); return 3; } - VectorCopy (trace.plane.normal, planes[numplanes]); + VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; - // - // modify original_velocity so it parallels all of the clip planes - // - for (i=0 ; ivelocity); + { + /* go along this plane */ + VectorCopy(new_velocity, ent->velocity); } else - { // go along the crease + { + /* go along the crease */ if (numplanes != 2) { - VectorCopy (vec3_origin, ent->velocity); + VectorCopy(vec3_origin, ent->velocity); return 7; } - CrossProduct (planes[0], planes[1], dir); - d = DotProduct (dir, ent->velocity); - VectorScale (dir, d, ent->velocity); + + CrossProduct(planes[0], planes[1], dir); + d = DotProduct(dir, ent->velocity); + VectorScale(dir, d, ent->velocity); } - // - // if original velocity is against the original velocity, stop dead - // to avoid tiny occilations in sloping corners - // - if (DotProduct (ent->velocity, primal_velocity) <= 0) + /* If original velocity is against the original + velocity, stop dead to avoid tiny occilations + in sloping corners */ + if (DotProduct(ent->velocity, primal_velocity) <= 0) { - VectorCopy (vec3_origin, ent->velocity); + VectorCopy(vec3_origin, ent->velocity); return blocked; } } @@ -290,23 +360,22 @@ int SV_FlyMove (edict_t *ent, float time, int mask) return blocked; } - -/* -============ -SV_AddGravity - -============ -*/ -void SV_AddGravity (edict_t *ent) -{ +void +SV_AddGravity(edict_t *ent) +{ + if (!ent) + { + return; + } + ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; } /* - * Returns the actual bounding box of a bmodel. + * Returns the actual bounding box of a bmodel. * This is a big improvement over what q2 normally - * does with rotating bmodels - q2 sets absmin, - * absmax to a cube that will completely contain + * does with rotating bmodels - q2 sets absmin, + * absmax to a cube that will completely contain * the bmodel at *any* rotation on *any* axis, whether * the bmodel can actually rotate to that angle or not. * This leads to a lot of false block tests in SV_Push @@ -386,27 +455,27 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) { mins[0] = p[i][0]; } - + if (mins[1] > p[i][1]) { mins[1] = p[i][1]; - } - + } + if (mins[2] > p[i][2]) { mins[2] = p[i][2]; - } + } if (maxs[0] < p[i][0]) { maxs[0] = p[i][0]; } - if (maxs[1] < p[i][1]) + if (maxs[1] < p[i][1]) { maxs[1] = p[i][1]; } - + if (maxs[2] < p[i][2]) { maxs[2] = p[i][2]; @@ -414,143 +483,162 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) } } -/* -=============================================================================== +/* =============================================================================== */ -PUSHMOVE - -=============================================================================== -*/ +/* PUSHMOVE */ /* -============ -SV_PushEntity - -Does not change the entities velocity at all -============ -*/ -trace_t SV_PushEntity (edict_t *ent, vec3_t push) + * Does not change the entities velocity at all + */ +trace_t +SV_PushEntity(edict_t *ent, vec3_t push) { - trace_t trace; - vec3_t start; - vec3_t end; - int mask; + trace_t trace; + vec3_t start; + vec3_t end; + int mask; - VectorCopy (ent->s.origin, start); - VectorAdd (start, push, end); + VectorCopy(ent->s.origin, start); + VectorAdd(start, push, end); retry: - if (ent->clipmask) - mask = ent->clipmask; - else - mask = MASK_SOLID; - trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask); - - VectorCopy (trace.endpos, ent->s.origin); - gi.linkentity (ent); + if (ent->clipmask) + { + mask = ent->clipmask; + } + else + { + mask = MASK_SOLID; + } + + trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask); + + VectorCopy(trace.endpos, ent->s.origin); + gi.linkentity(ent); if (trace.fraction != 1.0) { - SV_Impact (ent, &trace); + SV_Impact(ent, &trace); - // if the pushed entity went away and the pusher is still there + /* if the pushed entity went away and the pusher is still there */ if (!trace.ent->inuse && ent->inuse) { - // move the pusher back and try again - VectorCopy (start, ent->s.origin); - gi.linkentity (ent); + /* move the pusher back and try again */ + VectorCopy(start, ent->s.origin); + gi.linkentity(ent); goto retry; } } if (ent->inuse) - G_TouchTriggers (ent); + { + G_TouchTriggers(ent); + } return trace; -} - +} typedef struct { - edict_t *ent; - vec3_t origin; - vec3_t angles; - float deltayaw; + edict_t *ent; + vec3_t origin; + vec3_t angles; + float deltayaw; } pushed_t; -pushed_t pushed[MAX_EDICTS], *pushed_p; -edict_t *obstacle; +pushed_t pushed[MAX_EDICTS], *pushed_p; +edict_t *obstacle; /* -============ -SV_Push - -Objects need to be moved back on a failed push, -otherwise riders would continue to slide. -============ -*/ -qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove) + * Objects need to be moved back on a failed push, + * otherwise riders would continue to slide. + */ +qboolean +SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) { - int i, e; - edict_t *check, *block; - pushed_t *p; - vec3_t org, org2, move2, forward, right, up; - vec3_t realmins, realmaxs; + int i, e; + edict_t *check, *block; + pushed_t *p; + vec3_t org, org2, move2, forward, right, up; + vec3_t realmins, realmaxs; - // clamp the move to 1/8 units, so the position will - // be accurate for client side prediction - for (i=0 ; i<3 ; i++) + if (!pusher) { - float temp; - temp = move[i]*8.0; + return false; + } + + /* clamp the move to 1/8 units, so the position will + be accurate for client side prediction */ + for (i = 0; i < 3; i++) + { + float temp; + temp = move[i] * 8.0; + if (temp > 0.0) + { temp += 0.5; + } else + { temp -= 0.5; + } + move[i] = 0.125 * (int)temp; } - // we need this for pushing things later - VectorSubtract (vec3_origin, amove, org); - AngleVectors (org, forward, right, up); + /* we need this for pushing things later */ + VectorSubtract(vec3_origin, amove, org); + AngleVectors(org, forward, right, up); - // save the pusher's original position + /* save the pusher's original position */ pushed_p->ent = pusher; - VectorCopy (pusher->s.origin, pushed_p->origin); - VectorCopy (pusher->s.angles, pushed_p->angles); + VectorCopy(pusher->s.origin, pushed_p->origin); + VectorCopy(pusher->s.angles, pushed_p->angles); + if (pusher->client) + { pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW]; + } + pushed_p++; - // move the pusher to it's final position - VectorAdd (pusher->s.origin, move, pusher->s.origin); - VectorAdd (pusher->s.angles, amove, pusher->s.angles); - gi.linkentity (pusher); + /* move the pusher to it's final position */ + VectorAdd(pusher->s.origin, move, pusher->s.origin); + VectorAdd(pusher->s.angles, amove, pusher->s.angles); + gi.linkentity(pusher); - /* Create a real bounding box for + /* Create a real bounding box for rotating brush models. */ - RealBoundingBox(pusher,realmins,realmaxs); + RealBoundingBox(pusher, realmins, realmaxs); + + /* see if any solid entities are inside the final position */ + check = g_edicts + 1; - // see if any solid entities are inside the final position - check = g_edicts+1; for (e = 1; e < globals.num_edicts; e++, check++) { if (!check->inuse) + { continue; - if (check->movetype == MOVETYPE_PUSH - || check->movetype == MOVETYPE_STOP - || check->movetype == MOVETYPE_NONE - || check->movetype == MOVETYPE_NOCLIP) + } + + if ((check->movetype == MOVETYPE_PUSH) || + (check->movetype == MOVETYPE_STOP) || + (check->movetype == MOVETYPE_NONE) || + (check->movetype == MOVETYPE_NOCLIP)) + { continue; + } if (!check->area.prev) - continue; // not linked in anywhere + { + continue; /* not linked in anywhere */ + } - // if the entity is standing on the pusher, it will definitely be moved + /* if the entity is standing on the pusher, it will definitely be moved */ if (check->groundentity != pusher) { - // see if the ent needs to be tested + /* see if the ent needs to be tested */ if ((check->absmin[0] >= realmaxs[0]) || (check->absmin[1] >= realmaxs[1]) || (check->absmin[2] >= realmaxs[2]) || @@ -561,504 +649,624 @@ qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove) continue; } - // see if the ent's bbox is inside the pusher's final position - if (!SV_TestEntityPosition (check)) + /* see if the ent's bbox is inside the pusher's final position */ + if (!SV_TestEntityPosition(check)) + { continue; + } } - if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher)) + if ((pusher->movetype == MOVETYPE_PUSH) || + (check->groundentity == pusher)) { - // move this entity + /* move this entity */ pushed_p->ent = check; - VectorCopy (check->s.origin, pushed_p->origin); - VectorCopy (check->s.angles, pushed_p->angles); + VectorCopy(check->s.origin, pushed_p->origin); + VectorCopy(check->s.angles, pushed_p->angles); pushed_p++; - // try moving the contacted entity - VectorAdd (check->s.origin, move, check->s.origin); + /* try moving the contacted entity */ + VectorAdd(check->s.origin, move, check->s.origin); + if (check->client) - { // FIXME: doesn't rotate monsters? + { check->client->ps.pmove.delta_angles[YAW] += amove[YAW]; } - // figure movement due to the pusher's amove - VectorSubtract (check->s.origin, pusher->s.origin, org); - org2[0] = DotProduct (org, forward); - org2[1] = -DotProduct (org, right); + /* figure movement due to the pusher's amove */ + VectorSubtract(check->s.origin, pusher->s.origin, org); + org2[0] = DotProduct(org, forward); + org2[1] = -DotProduct(org, right); - // Quirk for blocking Elevators when running - // under amd64 + /* Quirk for blocking Elevators when + running under amd64. This is most + likey caused by a too high float + precision. -_- */ if (((pusher->s.number == 285) && - (Q_strcasecmp(level.mapname, "xcompnd2") == 0)) || - ((pusher->s.number == 520) && - (Q_strcasecmp(level.mapname, "xsewer2") == 0))) - org2[2] = DotProduct (org, up) + 2; + (Q_strcasecmp(level.mapname, "xcompnd2") == 0)) || + ((pusher->s.number == 520) && + (Q_strcasecmp(level.mapname, "xsewer2") == 0))) + { + org2[2] = DotProduct(org, up) + 2; + } else - org2[2] = DotProduct (org, up); + { + org2[2] = DotProduct(org, up); + } - VectorSubtract (org2, org, move2); - VectorAdd (check->s.origin, move2, check->s.origin); + VectorSubtract(org2, org, move2); + VectorAdd(check->s.origin, move2, check->s.origin); - // may have pushed them off an edge + /* may have pushed them off an edge */ if (check->groundentity != pusher) + { check->groundentity = NULL; + } + + block = SV_TestEntityPosition(check); - block = SV_TestEntityPosition (check); if (!block) - { // pushed ok - gi.linkentity (check); - // impact? + { + /* pushed ok */ + gi.linkentity(check); + + /* impact? */ continue; } - // if it is ok to leave in the old position, do it - // this is only relevent for riding entities, not pushed - // FIXME: this doesn't acount for rotation - VectorSubtract (check->s.origin, move, check->s.origin); - block = SV_TestEntityPosition (check); + /* if it is ok to leave in the old position, do it this + is only relevent for riding entities, not pushed */ + VectorSubtract(check->s.origin, move, check->s.origin); + block = SV_TestEntityPosition(check); + if (!block) { pushed_p--; continue; } } - - // save off the obstacle so we can call the block function + + /* save off the obstacle so we can call the block function */ obstacle = check; - // move back any entities we already moved - // go backwards, so if the same entity was pushed - // twice, it goes back to the original position - for (p=pushed_p-1 ; p>=pushed ; p--) + /* move back any entities we already moved + go backwards, so if the same entity was pushed + twice, it goes back to the original position */ + for (p = pushed_p - 1; p >= pushed; p--) { - VectorCopy (p->origin, p->ent->s.origin); - VectorCopy (p->angles, p->ent->s.angles); + VectorCopy(p->origin, p->ent->s.origin); + VectorCopy(p->angles, p->ent->s.angles); + if (p->ent->client) { p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw; } - gi.linkentity (p->ent); + + gi.linkentity(p->ent); } + return false; } - // see if anything we moved has touched a trigger - for (p=pushed_p-1 ; p>=pushed ; p--) - G_TouchTriggers (p->ent); + /* see if anything we moved has touched a trigger */ + for (p = pushed_p - 1; p >= pushed; p--) + { + G_TouchTriggers(p->ent); + } return true; } /* -================ -SV_Physics_Pusher - -Bmodel objects don't interact with each other, but -push all box objects -================ -*/ -void SV_Physics_Pusher (edict_t *ent) + * Bmodel objects don't interact with each + * other, but push all box objects + */ +void +SV_Physics_Pusher(edict_t *ent) { - vec3_t move, amove; - edict_t *part, *mv; - - // if not a team captain, so movement will be handled elsewhere - if ( ent->flags & FL_TEAMSLAVE) + vec3_t move, amove; + edict_t *part, *mv; + + if (!ent) + { return; + } + + /* if not a team captain, so movement + will be handled elsewhere */ + if (ent->flags & FL_TEAMSLAVE) + { + return; + } - // make sure all team slaves can move before commiting - // any moves or calling any think functions - // if the move is blocked, all moved objects will be backed out + /* make sure all team slaves can move before commiting + any moves or calling any think functions. if the move + is blocked, all moved objects will be backed out */ pushed_p = pushed; - for (part = ent ; part ; part=part->teamchain) + + for (part = ent; part; part = part->teamchain) { if (part->velocity[0] || part->velocity[1] || part->velocity[2] || - part->avelocity[0] || part->avelocity[1] || part->avelocity[2] - ) - { // object is moving - VectorScale (part->velocity, FRAMETIME, move); - VectorScale (part->avelocity, FRAMETIME, amove); + part->avelocity[0] || part->avelocity[1] || part->avelocity[2]) + { + /* object is moving */ + VectorScale(part->velocity, FRAMETIME, move); + VectorScale(part->avelocity, FRAMETIME, amove); - if (!SV_Push (part, move, amove)) - break; // move was blocked - } - } - if (pushed_p > &pushed[MAX_EDICTS]) - gi.error (ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted"); - - if (part) - { - // the move failed, bump all nextthink times and back out moves - for (mv = ent ; mv ; mv=mv->teamchain) - { - if (mv->nextthink > 0) - mv->nextthink += FRAMETIME; - } - - // if the pusher has a "blocked" function, call it - // otherwise, just stay in place until the obstacle is gone - if (part->blocked) - part->blocked (part, obstacle); - } - else - { - // the move succeeded, so call all think functions - for (part = ent ; part ; part=part->teamchain) - { - SV_RunThink (part); - } - } -} - -//================================================================== - -/* -============= -SV_Physics_None - -Non moving objects can only think -============= -*/ -void SV_Physics_None (edict_t *ent) -{ - // regular thinking - SV_RunThink (ent); -} - -/* -============= -SV_Physics_Noclip - -A moving object that doesn't obey physics -============= -*/ -void SV_Physics_Noclip (edict_t *ent) -{ - // regular thinking - if (!SV_RunThink (ent)) - return; - - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - VectorMA (ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin); - - gi.linkentity (ent); -} - -/* -============================================================================== - -TOSS / BOUNCE - -============================================================================== -*/ - -/* -============= -SV_Physics_Toss - -Toss, bounce, and fly movement. When onground, do nothing. -============= -*/ -void SV_Physics_Toss (edict_t *ent) -{ - trace_t trace; - vec3_t move; - float backoff; - edict_t *slave; - qboolean wasinwater; - qboolean isinwater; - vec3_t old_origin; - - // regular thinking - SV_RunThink (ent); - - // if not a team captain, so movement will be handled elsewhere - if ( ent->flags & FL_TEAMSLAVE) - return; - - if (ent->velocity[2] > 0) - ent->groundentity = NULL; - - // check for the groundentity going away - if (ent->groundentity) - if (!ent->groundentity->inuse) - ent->groundentity = NULL; - - // if onground, return without moving - if ( ent->groundentity ) - return; - - VectorCopy (ent->s.origin, old_origin); - - SV_CheckVelocity (ent); - - // add gravity - if (ent->movetype != MOVETYPE_FLY - && ent->movetype != MOVETYPE_FLYMISSILE - // RAFAEL - // move type for rippergun projectile - && ent->movetype != MOVETYPE_WALLBOUNCE) - SV_AddGravity (ent); - - // move angles - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); - - // move origin - VectorScale (ent->velocity, FRAMETIME, move); - trace = SV_PushEntity (ent, move); - if (!ent->inuse) - return; - - if (trace.fraction < 1) - { - // RAFAEL - if (ent->movetype == MOVETYPE_WALLBOUNCE) - backoff = 2.0; - // RAFAEL ( else ) - else if (ent->movetype == MOVETYPE_BOUNCE) - backoff = 1.5; - else - backoff = 1; - - ClipVelocity (ent->velocity, trace.plane.normal, ent->velocity, backoff); - - // RAFAEL - if (ent->movetype == MOVETYPE_WALLBOUNCE) - vectoangles (ent->velocity, ent->s.angles); - - // stop if on ground - // RAFAEL - if (trace.plane.normal[2] > 0.7 && ent->movetype != MOVETYPE_WALLBOUNCE) - { - if (ent->velocity[2] < 60 || ent->movetype != MOVETYPE_BOUNCE ) + if (!SV_Push(part, move, amove)) { - ent->groundentity = trace.ent; - ent->groundentity_linkcount = trace.ent->linkcount; - VectorCopy (vec3_origin, ent->velocity); - VectorCopy (vec3_origin, ent->avelocity); + break; /* move was blocked */ } } } - - // check for water transition - wasinwater = (ent->watertype & MASK_WATER); - ent->watertype = gi.pointcontents (ent->s.origin); - isinwater = ent->watertype & MASK_WATER; - if (isinwater) - ent->waterlevel = 1; - else - ent->waterlevel = 0; - - if (!wasinwater && isinwater) - gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - else if (wasinwater && !isinwater) - gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); - - // move teamslaves - for (slave = ent->teamchain; slave; slave = slave->teamchain) + if (pushed_p > &pushed[MAX_EDICTS]) { - VectorCopy (ent->s.origin, slave->s.origin); - gi.linkentity (slave); + gi.error(ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted"); + } + + if (part) + { + /* the move failed, bump all nextthink times and back out moves */ + for (mv = ent; mv; mv = mv->teamchain) + { + if (mv->nextthink > 0) + { + mv->nextthink += FRAMETIME; + } + } + + /* if the pusher has a "blocked" function, call it + otherwise, just stay in place until the obstacle + is gone */ + if (part->blocked) + { + part->blocked(part, obstacle); + } + } + else + { + /* the move succeeded, so call all think functions */ + for (part = ent; part; part = part->teamchain) + { + SV_RunThink(part); + } } } -/* -=============================================================================== - -STEPPING MOVEMENT - -=============================================================================== -*/ +/* ================================================================== */ /* -============= -SV_Physics_Step + * Non moving objects can only think + */ +void +SV_Physics_None(edict_t *ent) +{ + if (!ent) + { + return; + } + + /* regular thinking */ + SV_RunThink(ent); +} -Monsters freefall when they don't have a ground entity, otherwise -all movement is done with discrete steps. +/* + * A moving object that doesn't obey physics + */ +void +SV_Physics_Noclip(edict_t *ent) +{ + if (!ent) + { + return; + } + + /* regular thinking */ + if (!SV_RunThink(ent)) + { + return; + } -This is also used for objects that have become still on the ground, but -will fall if the floor is pulled out from under them. -FIXME: is this true? -============= -*/ + VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); + VectorMA(ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin); -//FIXME: hacked in for E3 demo -#define sv_stopspeed 100 -#define sv_friction 6 -#define sv_waterfriction 1 + gi.linkentity(ent); +} -void SV_AddRotationalFriction (edict_t *ent) +/* ============================================================================== */ + +/* TOSS / BOUNCE */ + +/* + * Toss, bounce, and fly movement. When onground, do nothing. + */ +void +SV_Physics_Toss(edict_t *ent) { - int n; - float adjustment; + trace_t trace; + vec3_t move; + float backoff; + edict_t *slave; + qboolean wasinwater; + qboolean isinwater; + vec3_t old_origin; + + if (!ent) + { + return; + } + + /* regular thinking */ + SV_RunThink(ent); - VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); + /* if not a team captain, so movement will be handled elsewhere */ + if (ent->flags & FL_TEAMSLAVE) + { + return; + } + + if (ent->velocity[2] > 0) + { + ent->groundentity = NULL; + } + + /* check for the groundentity going away */ + if (ent->groundentity) + { + if (!ent->groundentity->inuse) + { + ent->groundentity = NULL; + } + } + + /* if onground, return without moving */ + if (ent->groundentity) + { + return; + } + + VectorCopy(ent->s.origin, old_origin); + + SV_CheckVelocity(ent); + + /* add gravity */ + if ((ent->movetype != MOVETYPE_FLY) && + (ent->movetype != MOVETYPE_FLYMISSILE) + && (ent->movetype != MOVETYPE_WALLBOUNCE)) + { + SV_AddGravity(ent); + } + + /* move angles */ + VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); + + /* move origin */ + VectorScale(ent->velocity, FRAMETIME, move); + trace = SV_PushEntity(ent, move); + + if (!ent->inuse) + { + return; + } + + if (trace.fraction < 1) + { + if (ent->movetype == MOVETYPE_WALLBOUNCE) + { + backoff = 2.0; + } + else if (ent->movetype == MOVETYPE_BOUNCE) + { + backoff = 1.5; + } + else + { + backoff = 1; + } + + ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff); + + if (ent->movetype == MOVETYPE_WALLBOUNCE) + { + vectoangles(ent->velocity, ent->s.angles); + } + + /* stop if on ground */ + if ((trace.plane.normal[2] > 0.7) && + (ent->movetype != MOVETYPE_WALLBOUNCE)) + { + if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE)) + { + ent->groundentity = trace.ent; + ent->groundentity_linkcount = trace.ent->linkcount; + VectorCopy(vec3_origin, ent->velocity); + VectorCopy(vec3_origin, ent->avelocity); + } + } + } + + /* check for water transition */ + wasinwater = (ent->watertype & MASK_WATER); + ent->watertype = gi.pointcontents(ent->s.origin); + isinwater = ent->watertype & MASK_WATER; + + if (isinwater) + { + ent->waterlevel = 1; + } + else + { + ent->waterlevel = 0; + } + + if (!wasinwater && isinwater) + { + gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, + gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + } + else if (wasinwater && !isinwater) + { + gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, + gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); + } + + /* move teamslaves */ + for (slave = ent->teamchain; slave; slave = slave->teamchain) + { + VectorCopy(ent->s.origin, slave->s.origin); + gi.linkentity(slave); + } +} + +/* =============================================================================== */ + +/* STEPPING MOVEMENT */ + +/* + * Monsters freefall when they don't have a ground entity, otherwise + * all movement is done with discrete steps. + * + * This is also used for objects that have become still on the ground, but + * will fall if the floor is pulled out from under them. + */ + +void +SV_AddRotationalFriction(edict_t *ent) +{ + int n; + float adjustment; + + if (!ent) + { + return; + } + + VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); adjustment = FRAMETIME * sv_stopspeed * sv_friction; + for (n = 0; n < 3; n++) { if (ent->avelocity[n] > 0) { ent->avelocity[n] -= adjustment; + if (ent->avelocity[n] < 0) + { ent->avelocity[n] = 0; + } } else { ent->avelocity[n] += adjustment; + if (ent->avelocity[n] > 0) + { ent->avelocity[n] = 0; + } } } } -void SV_Physics_Step (edict_t *ent) +void +SV_Physics_Step(edict_t *ent) { - qboolean wasonground; - qboolean hitsound = false; - float *vel; - float speed, newspeed, control; - float friction; - edict_t *groundentity; - int mask; - - // airborn monsters should always check for ground + qboolean wasonground; + qboolean hitsound = false; + float *vel; + float speed, newspeed, control; + float friction; + edict_t *groundentity; + int mask; + + if (!ent) + { + return; + } + + /* airborn monsters should always check for ground */ if (!ent->groundentity) - M_CheckGround (ent); + { + M_CheckGround(ent); + } groundentity = ent->groundentity; - SV_CheckVelocity (ent); + SV_CheckVelocity(ent); if (groundentity) + { wasonground = true; + } else + { wasonground = false; - - if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) - SV_AddRotationalFriction (ent); + } - // add gravity except: - // flying monsters - // swimming monsters who are in the water - if (! wasonground) + if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) + { + SV_AddRotationalFriction(ent); + } + + /* add gravity except: + - flying monsters + - swimming monsters who are in the water */ + if (!wasonground) + { if (!(ent->flags & FL_FLY)) + { if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2))) { - if (ent->velocity[2] < sv_gravity->value*-0.1) + if (ent->velocity[2] < sv_gravity->value * -0.1) + { hitsound = true; - if (ent->waterlevel == 0) - SV_AddGravity (ent); - } + } - // friction for flying monsters that have been given vertical velocity + if (ent->waterlevel == 0) + { + SV_AddGravity(ent); + } + } + } + } + + /* friction for flying monsters that have been given vertical velocity */ if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < sv_stopspeed ? sv_stopspeed : speed; - friction = sv_friction/3; + friction = sv_friction / 3; newspeed = speed - (FRAMETIME * control * friction); + if (newspeed < 0) + { newspeed = 0; + } + newspeed /= speed; ent->velocity[2] *= newspeed; } - // friction for flying monsters that have been given vertical velocity + /* friction for flying monsters that have been given vertical velocity */ if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < sv_stopspeed ? sv_stopspeed : speed; newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel); + if (newspeed < 0) + { newspeed = 0; + } + newspeed /= speed; ent->velocity[2] *= newspeed; } if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { - // apply friction - // let dead monsters who aren't completely onground slide - if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY))) - if (!(ent->health <= 0.0 && !M_CheckBottom(ent))) + /* let dead monsters who aren't completely onground slide */ + if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY))) + { + if (!((ent->health <= 0.0) && !M_CheckBottom(ent))) { vel = ent->velocity; - speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); + speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); + if (speed) { friction = sv_friction; control = speed < sv_stopspeed ? sv_stopspeed : speed; - newspeed = speed - FRAMETIME*control*friction; + newspeed = speed - FRAMETIME * control * friction; if (newspeed < 0) + { newspeed = 0; + } + newspeed /= speed; vel[0] *= newspeed; vel[1] *= newspeed; } } + } if (ent->svflags & SVF_MONSTER) + { mask = MASK_MONSTERSOLID; + } else + { mask = MASK_SOLID; - SV_FlyMove (ent, FRAMETIME, mask); + } + + SV_FlyMove(ent, FRAMETIME, mask); + + gi.linkentity(ent); + G_TouchTriggers(ent); - gi.linkentity (ent); - G_TouchTriggers (ent); if (!ent->inuse) + { return; + } if (ent->groundentity) + { if (!wasonground) + { if (hitsound) - gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0); + { + gi.sound(ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0); + } + } + } } - // regular thinking - SV_RunThink (ent); + /* regular thinking */ + SV_RunThink(ent); } -//============================================================================ -/* -================ -G_RunEntity +/* ============================================================================ */ -================ -*/ -void G_RunEntity (edict_t *ent) -{ - if (ent->prethink) - ent->prethink (ent); - - switch ( (int)ent->movetype) +void +G_RunEntity(edict_t *ent) +{ + if (!ent) { - case MOVETYPE_PUSH: - case MOVETYPE_STOP: - SV_Physics_Pusher (ent); - break; - case MOVETYPE_NONE: - SV_Physics_None (ent); - break; - case MOVETYPE_NOCLIP: - SV_Physics_Noclip (ent); - break; - case MOVETYPE_STEP: - SV_Physics_Step (ent); - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_FLY: - case MOVETYPE_FLYMISSILE: - // RAFAEL - case MOVETYPE_WALLBOUNCE: - SV_Physics_Toss (ent); - break; - default: - gi.error ("SV_Physics: bad movetype %i", (int)ent->movetype); + return; + } + + if (ent->prethink) + { + ent->prethink(ent); + } + + switch ((int)ent->movetype) + { + case MOVETYPE_PUSH: + case MOVETYPE_STOP: + SV_Physics_Pusher(ent); + break; + case MOVETYPE_NONE: + SV_Physics_None(ent); + break; + case MOVETYPE_NOCLIP: + SV_Physics_Noclip(ent); + break; + case MOVETYPE_STEP: + SV_Physics_Step(ent); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_FLY: + case MOVETYPE_FLYMISSILE: + case MOVETYPE_WALLBOUNCE: + SV_Physics_Toss(ent); + break; + default: + gi.error("SV_Physics: bad movetype %i", (int)ent->movetype); } } +