/*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ #include #include "mathlib.h" #include "const.h" #include "usercmd.h" #include "pm_defs.h" #include "pm_shared.h" #include "pm_movevars.h" #include "pm_debug.h" #include #include #include #include #include #ifdef CLIENT_DLL int iJumpSpectator; float vJumpOrigin[3]; float vJumpAngles[3]; #endif static int pm_shared_initialized = 0; #ifdef _MSC_VER #pragma warning(disable:4101) #pragma warning(disable:4305) #endif typedef enum { mod_brush, mod_sprite, mod_alias, mod_studio } modtype_t; playermove_t *pmove = NULL; typedef struct { int planenum; short children[2]; } dclipnode_t; typedef struct mplane_s { vec3_t normal; float dist; byte type; byte signbits; byte pad[2]; } mplane_t; typedef struct hull_s { dclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; #define TIME_TO_DUCK 0.4 #define VEC_DUCK_HULL_MIN -18 #define VEC_DUCK_HULL_MAX 32 #define VEC_DUCK_VIEW 12 #define PM_DEAD_VIEWHEIGHT -8 #define MAX_CLIMB_SPEED 200 #define STUCK_MOVEUP 1 #define STUCK_MOVEDOWN -1 #define VEC_HULL_MIN -36 #define VEC_HULL_MAX 36 #define VEC_VIEW 17 #define STOP_EPSILON 0.1 #define CTEXTURESMAX 1024 #define CBTEXTURENAMEMAX 17 #define CHAR_TEX_CONCRETE 'C' #define CHAR_TEX_METAL 'M' #define CHAR_TEX_DIRT 'D' #define CHAR_TEX_VENT 'V' #define CHAR_TEX_GRATE 'G' #define CHAR_TEX_TILE 'T' #define CHAR_TEX_SLOSH 'S' #define CHAR_TEX_WOOD 'W' #define CHAR_TEX_COMPUTER 'P' #define CHAR_TEX_GLASS 'Y' #define CHAR_TEX_FLESH 'F' #define CHAR_TEX_SNOW 'N' #define STEP_CONCRETE 0 #define STEP_METAL 1 #define STEP_DIRT 2 #define STEP_VENT 3 #define STEP_GRATE 4 #define STEP_TILE 5 #define STEP_SLOSH 6 #define STEP_WADE 7 #define STEP_LADDER 8 #define STEP_SNOW 9 #define PLAYER_FATAL_FALL_SPEED 1024 #define PLAYER_MAX_SAFE_FALL_SPEED 580 #define DAMAGE_FOR_FALL_SPEED (float)100 / (PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED) #define PLAYER_MIN_BOUNCE_SPEED 350 #define PLAYER_FALL_PUNCH_THRESHHOLD (float)250 #define PLAYER_LONGJUMP_SPEED 350 #ifdef MSC_VER #pragma warning(disable : 4244) #endif #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) < (b)) ? (a) : (b)) #define PITCH 0 #define YAW 1 #define ROLL 2 #define MAX_CLIENTS 32 #define CONTENTS_CURRENT_0 -9 #define CONTENTS_CURRENT_90 -10 #define CONTENTS_CURRENT_180 -11 #define CONTENTS_CURRENT_270 -12 #define CONTENTS_CURRENT_UP -13 #define CONTENTS_CURRENT_DOWN -14 #define CONTENTS_TRANSLUCENT -15 static vec3_t rgv3tStuckTable[54]; static int rgStuckLast[MAX_CLIENTS][2]; static int gcTextures = 0; static char grgszTextureName[CTEXTURESMAX][CBTEXTURENAMEMAX]; static char grgchTextureType[CTEXTURESMAX]; int g_onladder = 0; void PM_SwapTextures(int i, int j) { char chTemp; char szTemp[CBTEXTURENAMEMAX]; strcpy(szTemp, grgszTextureName[i]); chTemp = grgchTextureType[i]; strcpy(grgszTextureName[i], grgszTextureName[j]); grgchTextureType[i] = grgchTextureType[j]; strcpy(grgszTextureName[j], szTemp); grgchTextureType[j] = chTemp; } void PM_SortTextures(void) { int i, j; for (i = 0 ; i < gcTextures; i++) { for (j = i + 1; j < gcTextures; j++) { if (stricmp(grgszTextureName[i], grgszTextureName[j]) > 0) PM_SwapTextures(i, j); } } } void PM_InitTextureTypes(void) { char buffer[512]; int i, j; byte *pMemFile; int fileSize, filePos = 0; static qboolean bTextureTypeInit = false; if (bTextureTypeInit) return; memset(&(grgszTextureName[0][0]), 0, CTEXTURESMAX * CBTEXTURENAMEMAX); memset(grgchTextureType, 0, CTEXTURESMAX); gcTextures = 0; memset(buffer, 0, 512); fileSize = pmove->COM_FileSize("sound/materials.txt"); pMemFile = pmove->COM_LoadFile("sound/materials.txt", 5, NULL); if (!pMemFile) return; filePos = 0; while (pmove->memfgets(pMemFile, fileSize, &filePos, buffer, 511) != NULL && (gcTextures < CTEXTURESMAX)) { i = 0; while (buffer[i] && isspace(buffer[i])) i++; if (!buffer[i]) continue; if (buffer[i] == '/' || !isalpha(buffer[i])) continue; grgchTextureType[gcTextures] = toupper(buffer[i++]); while (buffer[i] && isspace(buffer[i])) i++; if (!buffer[i]) continue; j = i; while (buffer[j] && !isspace(buffer[j])) j++; if (!buffer[j]) continue; j = min (j, CBTEXTURENAMEMAX - 1 + i); buffer[j] = 0; strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); } pmove->COM_FreeFile(pMemFile); PM_SortTextures(); bTextureTypeInit = true; } char PM_FindTextureType(char *name) { int left, right, pivot; int val; assert(pm_shared_initialized); left = 0; right = gcTextures - 1; while (left <= right) { pivot = (left + right) / 2; val = strnicmp(name, grgszTextureName[pivot], CBTEXTURENAMEMAX - 1); if (val == 0) { return grgchTextureType[pivot]; } else if (val > 0) { left = pivot + 1; } else if (val < 0) { right = pivot - 1; } } return CHAR_TEX_CONCRETE; } void PM_PlayStepSound(int step, float fvol) { static int iSkipStep = 0; int irand; pmove->iStepLeft = !pmove->iStepLeft; if (!pmove->runfuncs) return; irand = pmove->RandomLong(0, 1) + (pmove->iStepLeft * 2); if (pmove->multiplayer && !pmove->movevars->footsteps) return; switch (step) { default: case STEP_CONCRETE: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_METAL: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_DIRT: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_VENT: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_GRATE: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_TILE: { if (!pmove->RandomLong(0, 4)) irand = 4; switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 4: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile5.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_SLOSH: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_WADE: { if (iSkipStep == 0) { iSkipStep++; break; } if (iSkipStep++ == 3) iSkipStep = 0; switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_LADDER: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } case STEP_SNOW: { switch (irand) { case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; } break; } } } int PM_MapTextureTypeStepType( char chTextureType) { switch (chTextureType) { default: case CHAR_TEX_CONCRETE: return STEP_CONCRETE; case CHAR_TEX_METAL: return STEP_METAL; case CHAR_TEX_DIRT: return STEP_DIRT; case CHAR_TEX_VENT: return STEP_VENT; case CHAR_TEX_GRATE: return STEP_GRATE; case CHAR_TEX_TILE: return STEP_TILE; case CHAR_TEX_SLOSH: return STEP_SLOSH; case CHAR_TEX_SNOW: return STEP_SNOW; } } void PM_CatagorizeTextureType(void) { vec3_t start, end; const char *pTextureName; VectorCopy(pmove->origin, start); VectorCopy(pmove->origin, end); end[2] -= 64; pmove->sztexturename[0] = '\0'; pmove->chtexturetype = CHAR_TEX_CONCRETE; pTextureName = pmove->PM_TraceTexture(pmove->onground, start, end); if (!pTextureName) return; if (*pTextureName == '-' || *pTextureName == '+') pTextureName += 2; if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') pTextureName++; strcpy(pmove->sztexturename, pTextureName); pmove->sztexturename[CBTEXTURENAMEMAX - 1] = 0; pmove->chtexturetype = PM_FindTextureType(pmove->sztexturename); } void PM_UpdateStepSound(void) { float fvol; vec3_t knee; vec3_t feet; float height; float speed; int fLadder; int step; if (pmove->flTimeStepSound > 0) return; if (pmove->flags & FL_FROZEN) return; speed = Length(pmove->velocity); if (speed < 150) { pmove->flTimeStepSound = 400; return; } fLadder = (pmove->movetype == MOVETYPE_FLY); if (fLadder || (pmove->onground != -1)) { PM_CatagorizeTextureType(); VectorCopy(pmove->origin, knee); VectorCopy(pmove->origin, feet); height = pmove->player_maxs[pmove->usehull][2] - pmove->player_mins[pmove->usehull][2]; knee[2] = pmove->origin[2] - 0.3 * height; feet[2] = pmove->origin[2] - 0.5 * height; if (fLadder) { step = STEP_LADDER; fvol = 0.35; pmove->flTimeStepSound = 350; } else if (pmove->PM_PointContents(knee, NULL) == CONTENTS_WATER) { step = STEP_WADE; fvol = 0.65; pmove->flTimeStepSound = 600; } else if (pmove->PM_PointContents(feet, NULL) == CONTENTS_WATER) { step = STEP_SLOSH; fvol = 0.5; pmove->flTimeStepSound = 300; } else { step = PM_MapTextureTypeStepType(pmove->chtexturetype); switch (pmove->chtexturetype) { case CHAR_TEX_CONCRETE: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_METAL: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_DIRT: { fvol = 0.55; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_VENT: { fvol = 0.7; pmove->flTimeStepSound = 300; break; } default: { fvol = 0.5; break; } case CHAR_TEX_GRATE: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_TILE: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_SLOSH: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } case CHAR_TEX_SNOW: { fvol = 0.5; pmove->flTimeStepSound = 300; break; } pmove->flTimeStepSound = 300; } } if (pmove->flags & FL_DUCKING || fLadder) { pmove->flTimeStepSound += 100; if (pmove->flags & FL_DUCKING && pmove->flDuckTime < 950.0) fvol *= 0.35; } PM_PlayStepSound(step, fvol); } } qboolean PM_AddToTouched(pmtrace_t tr, vec3_t impactvelocity) { int i; for (i = 0; i < pmove->numtouch; i++) { if (pmove->touchindex[i].ent == tr.ent) break; } if (i != pmove->numtouch) return false; VectorCopy(impactvelocity, tr.deltavelocity); if (pmove->numtouch >= MAX_PHYSENTS) pmove->Con_DPrintf("Too many entities were touched!\n"); pmove->touchindex[pmove->numtouch++] = tr; return true; } void PM_CheckVelocity(void) { int i; for (i = 0; i < 3; i++) { if (IS_NAN(pmove->velocity[i])) { pmove->Con_Printf("PM Got a NaN velocity %i\n", i); pmove->velocity[i] = 0; } if (IS_NAN(pmove->origin[i])) { pmove->Con_Printf("PM Got a NaN origin on %i\n", i); pmove->origin[i] = 0; } if (pmove->velocity[i] > pmove->movevars->maxvelocity) { pmove->Con_DPrintf("PM Got a velocity too high on %i\n", i); pmove->velocity[i] = pmove->movevars->maxvelocity; } else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) { pmove->Con_DPrintf("PM Got a velocity too low on %i\n", i); pmove->velocity[i] = -pmove->movevars->maxvelocity; } } } int PM_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; float angle; int i, blocked; angle = normal[2]; blocked = 0x00; if (angle > 0) blocked |= 0x01; if (!angle) blocked |= 0x02; 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) out[i] = 0; } return blocked; } void PM_AddCorrectGravity(void) { float ent_gravity; if (pmove->waterjumptime) return; if (pmove->gravity) ent_gravity = pmove->gravity; else ent_gravity = 1.0; pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * 0.5 * pmove->frametime); pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; pmove->basevelocity[2] = 0; PM_CheckVelocity(); } void PM_FixupGravityVelocity(void) { float ent_gravity; if (pmove->waterjumptime) return; if (pmove->gravity) ent_gravity = pmove->gravity; else ent_gravity = 1.0; pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime * 0.5); PM_CheckVelocity(); } int PM_FlyMove(void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity; vec3_t new_velocity; int i, j; pmtrace_t trace; vec3_t end; float time_left, allFraction; int blocked; numbumps = 4; blocked = 0; numplanes = 0; VectorCopy(pmove->velocity, original_velocity); VectorCopy(pmove->velocity, primal_velocity); allFraction = 0; time_left = pmove->frametime; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) break; for (i = 0; i < 3; i++) end[i] = pmove->origin[i] + time_left * pmove->velocity[i]; trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); allFraction += trace.fraction; if (trace.allsolid) { VectorCopy(vec3_origin, pmove->velocity); return 4; } if (trace.fraction > 0) { VectorCopy(trace.endpos, pmove->origin); VectorCopy(pmove->velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; PM_AddToTouched(trace, pmove->velocity); if (trace.plane.normal[2] > 0.7) blocked |= 1; if (!trace.plane.normal[2]) blocked |= 2; time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { VectorCopy(vec3_origin, pmove->velocity); break; } VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; if (pmove->movetype == MOVETYPE_WALK && ((pmove->onground == -1) || (pmove->friction != 1))) { for (i = 0; i < numplanes; i++) { if (planes[i][2] > 0.7) { PM_ClipVelocity(original_velocity, planes[i], new_velocity, 1); VectorCopy(new_velocity, original_velocity); } else PM_ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + pmove->movevars->bounce * (1 - pmove->friction)); } VectorCopy(new_velocity, pmove->velocity); VectorCopy(new_velocity, original_velocity); } else { for (i = 0; i < numplanes; i++) { PM_ClipVelocity(original_velocity, planes[i], pmove->velocity, 1); for (j = 0; j velocity, planes[j]) < 0) break; } } if (j == numplanes) break; } if (i != numplanes) { } else { if (numplanes != 2) { VectorCopy(vec3_origin, pmove->velocity); break; } CrossProduct(planes[0], planes[1], dir); d = DotProduct(dir, pmove->velocity); VectorScale(dir, d, pmove->velocity); } if (DotProduct(pmove->velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, pmove->velocity); break; } } } if (allFraction == 0) VectorCopy(vec3_origin, pmove->velocity); return blocked; } void PM_Accelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed; if (pmove->dead) return; if (pmove->waterjumptime) return; currentspeed = DotProduct(pmove->velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = accel * pmove->frametime * wishspeed * pmove->friction; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove->velocity[i] += accelspeed * wishdir[i]; } void PM_WalkMove(void) { int oldonground; int i; vec3_t wishvel; float spd; float fmove, smove; vec3_t wishdir; float wishspeed; vec3_t dest; vec3_t original, originalvel; vec3_t down, downvel; float downdist, updist; pmtrace_t trace; if (pmove->fuser2 > 0.0) { float factor; factor = (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01; pmove->velocity[0] *= factor; pmove->velocity[1] *= factor; } fmove = pmove->cmd.forwardmove; smove = pmove->cmd.sidemove; pmove->forward[2] = 0; pmove->right[2] = 0; VectorNormalize(pmove->forward); VectorNormalize(pmove->right); for (i = 0; i < 2; i++) wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; wishvel[2] = 0; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > pmove->maxspeed) { VectorScale(wishvel, pmove->maxspeed/wishspeed, wishvel); wishspeed = pmove->maxspeed; } pmove->velocity[2] = 0; PM_Accelerate(wishdir, wishspeed, pmove->movevars->accelerate); pmove->velocity[2] = 0; VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); spd = Length(pmove->velocity); if (spd < 1.0f) { VectorClear(pmove->velocity); return; } oldonground = pmove->onground; dest[0] = pmove->origin[0] + pmove->velocity[0]*pmove->frametime; dest[1] = pmove->origin[1] + pmove->velocity[1]*pmove->frametime; dest[2] = pmove->origin[2]; trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); if (trace.fraction == 1) { VectorCopy(trace.endpos, pmove->origin); return; } if (oldonground == -1 && pmove->waterlevel == 0) return; if (pmove->waterjumptime) return; VectorCopy(pmove->origin, original); VectorCopy(pmove->velocity, originalvel); PM_FlyMove(); VectorCopy(pmove->origin, down); VectorCopy(pmove->velocity, downvel); VectorCopy(original, pmove->origin); VectorCopy(originalvel, pmove->velocity); VectorCopy(pmove->origin, dest); dest[2] += pmove->movevars->stepsize; trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); if (!trace.startsolid && !trace.allsolid) VectorCopy(trace.endpos, pmove->origin); PM_FlyMove(); VectorCopy(pmove->origin, dest); dest[2] -= pmove->movevars->stepsize; trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); if (trace.plane.normal[2] < 0.7) goto usedown; if (!trace.startsolid && !trace.allsolid) VectorCopy(trace.endpos, pmove->origin); VectorCopy(pmove->origin, pmove->up); downdist = (down[0] - original[0])*(down[0] - original[0]) + (down[1] - original[1])*(down[1] - original[1]); updist = (pmove->up[0] - original[0])*(pmove->up[0] - original[0]) + (pmove->up[1] - original[1])*(pmove->up[1] - original[1]); if (downdist > updist) { usedown: VectorCopy(down, pmove->origin); VectorCopy(downvel, pmove->velocity); } else pmove->velocity[2] = downvel[2]; } void PM_Friction(void) { float *vel; float speed, newspeed, control; float friction; float drop; vec3_t newvel; if (pmove->waterjumptime) return; vel = pmove->velocity; speed = sqrt(vel[0] * vel[0] +vel[1] * vel[1] + vel[2] * vel[2]); if (speed < 0.1f) return; drop = 0; if (pmove->onground != -1) { vec3_t start, stop; pmtrace_t trace; start[0] = stop[0] = pmove->origin[0] + vel[0] / speed * 16; start[1] = stop[1] = pmove->origin[1] + vel[1] / speed * 16; start[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2]; stop[2] = start[2] - 34; trace = pmove->PM_PlayerTrace(start, stop, PM_NORMAL, -1); if (trace.fraction == 1.0) friction = pmove->movevars->friction * pmove->movevars->edgefriction; else friction = pmove->movevars->friction; friction *= pmove->friction; control = (speed < pmove->movevars->stopspeed) ? pmove->movevars->stopspeed : speed; drop += control*friction*pmove->frametime; } newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; newvel[0] = vel[0] * newspeed; newvel[1] = vel[1] * newspeed; newvel[2] = vel[2] * newspeed; VectorCopy(newvel, pmove->velocity); } void PM_AirAccelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed, wishspd = wishspeed; if (pmove->dead) return; if (pmove->waterjumptime) return; if (wishspd > 30) wishspd = 30; currentspeed = DotProduct(pmove->velocity, wishdir); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; accelspeed = accel * wishspeed * pmove->frametime * pmove->friction; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove->velocity[i] += accelspeed * wishdir[i]; } void PM_WaterMove(void) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; vec3_t start, dest; vec3_t temp; pmtrace_t trace; float speed, newspeed, addspeed, accelspeed; for (i = 0; i < 3; i++) wishvel[i] = pmove->forward[i] * pmove->cmd.forwardmove + pmove->right[i] * pmove->cmd.sidemove; if (!pmove->cmd.forwardmove && !pmove->cmd.sidemove && !pmove->cmd.upmove) wishvel[2] -= 60; else wishvel[2] += pmove->cmd.upmove; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > pmove->maxspeed) { VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); wishspeed = pmove->maxspeed; } wishspeed *= 0.8; VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); VectorCopy(pmove->velocity, temp); speed = VectorNormalize(temp); if (speed) { newspeed = speed - pmove->frametime * speed * pmove->movevars->friction * pmove->friction; if (newspeed < 0) newspeed = 0; VectorScale(pmove->velocity, newspeed / speed, pmove->velocity); } else newspeed = 0; if (wishspeed < 0.1f) return; addspeed = wishspeed - newspeed; if (addspeed > 0) { VectorNormalize(wishvel); accelspeed = pmove->movevars->accelerate * wishspeed * pmove->frametime * pmove->friction; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove->velocity[i] += accelspeed * wishvel[i]; } VectorMA(pmove->origin, pmove->frametime, pmove->velocity, dest); VectorCopy(dest, start); start[2] += pmove->movevars->stepsize + 1; trace = pmove->PM_PlayerTrace(start, dest, PM_NORMAL, -1); if (!trace.startsolid && !trace.allsolid) { VectorCopy(trace.endpos, pmove->origin); return; } PM_FlyMove(); } void PM_AirMove(void) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; fmove = pmove->cmd.forwardmove; smove = pmove->cmd.sidemove; pmove->forward[2] = 0; pmove->right[2] = 0; VectorNormalize(pmove->forward); VectorNormalize(pmove->right); for (i = 0; i < 2; i++) { wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; } wishvel[2] = 0; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > pmove->maxspeed) { VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); wishspeed = pmove->maxspeed; } PM_AirAccelerate(wishdir, wishspeed, pmove->movevars->airaccelerate); VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); PM_FlyMove(); } qboolean PM_InWater(void) { return (pmove->waterlevel > 1); } qboolean PM_CheckWater(void) { vec3_t point; int cont; int truecont; float height; float heightover2; point[0] = pmove->origin[0] + (pmove->player_mins[pmove->usehull][0] + pmove->player_maxs[pmove->usehull][0]) * 0.5; point[1] = pmove->origin[1] + (pmove->player_mins[pmove->usehull][1] + pmove->player_maxs[pmove->usehull][1]) * 0.5; point[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; pmove->waterlevel = 0; pmove->watertype = CONTENTS_EMPTY; cont = pmove->PM_PointContents(point, &truecont); if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) { pmove->watertype = cont; pmove->waterlevel = 1; height = (pmove->player_mins[pmove->usehull][2] + pmove->player_maxs[pmove->usehull][2]); heightover2 = height * 0.5; point[2] = pmove->origin[2] + heightover2; cont = pmove->PM_PointContents(point, NULL); if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) { pmove->waterlevel = 2; point[2] = pmove->origin[2] + pmove->view_ofs[2]; cont = pmove->PM_PointContents(point, NULL); if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) pmove->waterlevel = 3; } if ((truecont <= CONTENTS_CURRENT_0) && (truecont >= CONTENTS_CURRENT_DOWN)) { static vec3_t current_table[] = { { 1, 0, 0 }, { 0, 1, 0 }, { -1, 0, 0 }, { 0, -1, 0 }, { 0, 0, 1 }, { 0, 0, -1 } }; VectorMA(pmove->basevelocity, 50.0 * pmove->waterlevel, current_table[CONTENTS_CURRENT_0 - truecont], pmove->basevelocity); } } return pmove->waterlevel > 1; } void PM_CatagorizePosition(void) { vec3_t point; pmtrace_t tr; PM_CheckWater(); point[0] = pmove->origin[0]; point[1] = pmove->origin[1]; point[2] = pmove->origin[2] - 2; if (pmove->velocity[2] > 180) { pmove->onground = -1; } else { tr = pmove->PM_PlayerTrace(pmove->origin, point, PM_NORMAL, -1); if (tr.plane.normal[2] < 0.7) pmove->onground = -1; else pmove->onground = tr.ent; if (pmove->onground != -1) { pmove->waterjumptime = 0; if (pmove->waterlevel < 2 && !tr.startsolid && !tr.allsolid) VectorCopy(tr.endpos, pmove->origin); } if (tr.ent > 0) { PM_AddToTouched(tr, pmove->velocity); } } } int PM_GetRandomStuckOffsets(int nIndex, int server, vec3_t offset) { int idx; idx = rgStuckLast[nIndex][server]++; VectorCopy(rgv3tStuckTable[idx % 54], offset); return (idx % 54); } void PM_ResetStuckOffsets(int nIndex, int server) { rgStuckLast[nIndex][server] = 0; } #define PM_CHECKSTUCK_MINTIME 0.05 int PM_CheckStuck(void) { vec3_t base; vec3_t offset; vec3_t test; int hitent; int idx; float fTime; int i; pmtrace_t traceresult; static float rgStuckCheckTime[MAX_CLIENTS][2]; hitent = pmove->PM_TestPlayerPosition(pmove->origin, &traceresult); if (hitent == -1) { PM_ResetStuckOffsets(pmove->player_index, pmove->server); return 0; } VectorCopy(pmove->origin, base); if (!pmove->server) { if ((hitent == 0) || (pmove->physents[hitent].model != NULL)) { int nReps = 0; PM_ResetStuckOffsets(pmove->player_index, pmove->server); do { i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); VectorAdd(base, offset, test); if (pmove->PM_TestPlayerPosition(test, &traceresult) == -1) { PM_ResetStuckOffsets(pmove->player_index, pmove->server); VectorCopy(test, pmove->origin); return 0; } nReps++; } while (nReps < 54); } } if (pmove->server) idx = 0; else idx = 1; fTime = pmove->Sys_FloatTime(); if (rgStuckCheckTime[pmove->player_index][idx] >= (fTime - PM_CHECKSTUCK_MINTIME)) return 1; rgStuckCheckTime[pmove->player_index][idx] = fTime; pmove->PM_StuckTouch(hitent, &traceresult); i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); VectorAdd(base, offset, test); if ((hitent = pmove->PM_TestPlayerPosition(test, NULL)) == -1) { PM_ResetStuckOffsets(pmove->player_index, pmove->server); if (i >= 27) VectorCopy(test, pmove->origin); return 0; } if (pmove->cmd.buttons & (IN_JUMP | IN_DUCK | IN_ATTACK) && ( pmove->physents[hitent].player != 0)) { float x, y, z; float xystep = 8.0; float zstep = 18.0; float xyminmax = xystep; float zminmax = 4 * zstep; for (z = 0; z <= zminmax; z += zstep) { for (x = -xyminmax; x <= xyminmax; x += xystep) { for (y = -xyminmax; y <= xyminmax; y += xystep) { VectorCopy(base, test); test[0] += x; test[1] += y; test[2] += z; if (pmove->PM_TestPlayerPosition(test, NULL) == -1) { VectorCopy(test, pmove->origin); return 0; } } } } } return 1; } void PM_SpectatorMove(void) { float speed, drop, friction, control, newspeed; float currentspeed, addspeed, accelspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; if (pmove->iuser1 == OBS_ROAMING) { #ifdef CLIENT_DLL if (iJumpSpectator) { VectorCopy(vJumpOrigin, pmove->origin); VectorCopy(vJumpAngles, pmove->angles); VectorCopy(vec3_origin, pmove->velocity); iJumpSpectator = 0; return; } #endif speed = Length(pmove->velocity); if (speed < 1) { VectorCopy(vec3_origin, pmove->velocity) } else { drop = 0; friction = pmove->movevars->friction * 1.5; control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; drop += control*friction*pmove->frametime; newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; VectorScale(pmove->velocity, newspeed, pmove->velocity); } fmove = pmove->cmd.forwardmove; smove = pmove->cmd.sidemove; VectorNormalize(pmove->forward); VectorNormalize(pmove->right); for (i = 0; i < 3; i++) wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; wishvel[2] += pmove->cmd.upmove; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > pmove->movevars->spectatormaxspeed) { VectorScale(wishvel, pmove->movevars->spectatormaxspeed / wishspeed, wishvel); wishspeed = pmove->movevars->spectatormaxspeed; } currentspeed = DotProduct(pmove->velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = pmove->movevars->accelerate * pmove->frametime * wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove->velocity[i] += accelspeed * wishdir[i]; VectorMA(pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); } else { int target; if (pmove->iuser2 <= 0) return; for (target = 0; target < pmove->numphysent; target++) { if (pmove->physents[target].info == pmove->iuser2) break; } if (target == pmove->numphysent) return; VectorCopy(pmove->physents[target].angles, pmove->angles); VectorCopy(pmove->physents[target].origin, pmove->origin); VectorCopy(vec3_origin, pmove->velocity); } } float PM_SplineFraction(float value, float scale) { float valueSquared; value = scale * value; valueSquared = value * value; return 3 * valueSquared - 2 * valueSquared * value; } void PM_FixPlayerCrouchStuck(int direction) { int hitent; int i; vec3_t test; hitent = pmove->PM_TestPlayerPosition(pmove->origin, NULL); if (hitent == -1) return; VectorCopy(pmove->origin, test); for (i = 0; i < 36; i++) { pmove->origin[2] += direction; hitent = pmove->PM_TestPlayerPosition(pmove->origin, NULL); if (hitent == -1) return; } VectorCopy(test, pmove->origin); } void PM_UnDuck(void) { pmtrace_t trace; vec3_t newOrigin; VectorCopy(pmove->origin, newOrigin); if (pmove->onground != -1) { newOrigin[2] += 18.0; } trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); if (!trace.startsolid) { pmove->usehull = 0; trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); if (trace.startsolid) { pmove->usehull = 1; return; } pmove->flags &= ~FL_DUCKING; pmove->bInDuck = false; pmove->view_ofs[2] = VEC_VIEW; pmove->flDuckTime = 0; pmove->flTimeStepSound -= 100; if (pmove->flTimeStepSound < 0) { pmove->flTimeStepSound = 0; } VectorCopy(newOrigin, pmove->origin); PM_CatagorizePosition(); } } void PM_Duck(void) { float time; float duckFraction; int buttonsChanged = (pmove->oldbuttons ^ pmove->cmd.buttons); int nButtonPressed = buttonsChanged & pmove->cmd.buttons; if (pmove->cmd.buttons & IN_DUCK) { pmove->oldbuttons |= IN_DUCK; } else { pmove->oldbuttons &= ~IN_DUCK; } if (pmove->dead) return; if ((pmove->cmd.buttons & IN_DUCK) || (pmove->bInDuck) || (pmove->flags & FL_DUCKING)) { pmove->cmd.forwardmove *= 0.333; pmove->cmd.sidemove *= 0.333; pmove->cmd.upmove *= 0.333; if (pmove->cmd.buttons & IN_DUCK) { if ((nButtonPressed & IN_DUCK) && !(pmove->flags & FL_DUCKING)) { pmove->flDuckTime = 1000; pmove->bInDuck = true; } time = max(0.0, ( 1.0 - (float)pmove->flDuckTime / 1000.0)); if (pmove->bInDuck) { if (((float)pmove->flDuckTime / 1000.0 <= (1.0 - TIME_TO_DUCK)) || (pmove->onground == -1)) { pmove->usehull = 1; pmove->view_ofs[2] = VEC_DUCK_VIEW; pmove->flags |= FL_DUCKING; pmove->bInDuck = false; if (pmove->onground != -1) { pmove->origin[2] = pmove->origin[2] - 18.0; PM_FixPlayerCrouchStuck(STUCK_MOVEUP); PM_CatagorizePosition(); } } else { float fMore = (VEC_DUCK_HULL_MIN - VEC_HULL_MIN); duckFraction = PM_SplineFraction(time, (1.0 / TIME_TO_DUCK)); pmove->view_ofs[2] = ((VEC_DUCK_VIEW - fMore) * duckFraction) + (VEC_VIEW * (1 - duckFraction)); } } } else { PM_UnDuck(); } } } void PM_LadderMove(physent_t *pLadder) { vec3_t ladderCenter; trace_t trace; qboolean onFloor; vec3_t floor; vec3_t modelmins, modelmaxs; if (pmove->movetype == MOVETYPE_NOCLIP) return; pmove->PM_GetModelBounds(pLadder->model, modelmins, modelmaxs); VectorAdd(modelmins, modelmaxs, ladderCenter); VectorScale(ladderCenter, 0.5, ladderCenter); pmove->movetype = MOVETYPE_FLY; VectorCopy(pmove->origin, floor); floor[2] += pmove->player_mins[pmove->usehull][2] - 1; if (pmove->PM_PointContents(floor, NULL) == CONTENTS_SOLID) onFloor = true; else onFloor = false; pmove->gravity = 0; pmove->PM_TraceModel(pLadder, pmove->origin, ladderCenter, &trace); if (trace.fraction != 1.0) { float forward = 0, right = 0; vec3_t vpn, v_right; float flSpeed = MAX_CLIMB_SPEED; if (pmove->maxspeed < MAX_CLIMB_SPEED) flSpeed = pmove->maxspeed; AngleVectors(pmove->angles, vpn, v_right, NULL); if (pmove->flags & FL_DUCKING) { flSpeed *= 0.333; } if (pmove->cmd.buttons & IN_BACK) forward -= flSpeed; if (pmove->cmd.buttons & IN_FORWARD) forward += flSpeed; if (pmove->cmd.buttons & IN_MOVELEFT) right -= flSpeed; if (pmove->cmd.buttons & IN_MOVERIGHT) right += flSpeed; if (pmove->cmd.buttons & IN_JUMP) { pmove->movetype = MOVETYPE_WALK; VectorScale(trace.plane.normal, 270, pmove->velocity); } else { if (forward != 0 || right != 0) { vec3_t velocity, perp, cross, lateral, tmp; float normal; VectorScale(vpn, forward, velocity); VectorMA(velocity, right, v_right, velocity); VectorClear(tmp); tmp[2] = 1; CrossProduct(tmp, trace.plane.normal, perp); VectorNormalize(perp); normal = DotProduct(velocity, trace.plane.normal); VectorScale(trace.plane.normal, normal, cross); VectorSubtract(velocity, cross, lateral); CrossProduct(trace.plane.normal, perp, tmp); VectorMA(lateral, -normal, tmp, pmove->velocity); if (onFloor && normal > 0) { VectorMA(pmove->velocity, MAX_CLIMB_SPEED, trace.plane.normal, pmove->velocity); } } else { VectorClear(pmove->velocity); } } } } physent_t *PM_Ladder(void) { int i; physent_t *pe; hull_t *hull; int num; vec3_t test; for (i = 0; i < pmove->nummoveent; i++) { pe = &pmove->moveents[i]; if (pe->model && (modtype_t)pmove->PM_GetModelType(pe->model) == mod_brush && pe->skin == CONTENTS_LADDER) { hull = (hull_t *)pmove->PM_HullForBsp(pe, test); num = hull->firstclipnode; VectorSubtract(pmove->origin, test, test); if (pmove->PM_HullPointContents (hull, num, test) == CONTENTS_EMPTY) continue; return pe; } } return NULL; } void PM_WaterJump(void) { if (pmove->waterjumptime > 10000) pmove->waterjumptime = 10000; if (!pmove->waterjumptime) return; pmove->waterjumptime -= pmove->cmd.msec; if (pmove->waterjumptime < 0 || !pmove->waterlevel) { pmove->waterjumptime = 0; pmove->flags &= ~FL_WATERJUMP; } pmove->velocity[0] = pmove->movedir[0]; pmove->velocity[1] = pmove->movedir[1]; } void PM_AddGravity(void) { float ent_gravity; if (pmove->gravity) ent_gravity = pmove->gravity; else ent_gravity = 1.0; pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime); pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; pmove->basevelocity[2] = 0; PM_CheckVelocity(); } pmtrace_t PM_PushEntity(vec3_t push) { pmtrace_t trace; vec3_t end; VectorAdd(pmove->origin, push, end); trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); VectorCopy(trace.endpos, pmove->origin); if (trace.fraction < 1.0 && !trace.allsolid) { PM_AddToTouched(trace, pmove->velocity); } return trace; } void PM_Physics_Toss(void) { pmtrace_t trace; vec3_t move; float backoff; PM_CheckWater(); if (pmove->velocity[2] > 0) pmove->onground = -1; if (pmove->onground != -1) { if (VectorCompare(pmove->basevelocity, vec3_origin) && VectorCompare(pmove->velocity, vec3_origin)) return; } PM_CheckVelocity(); if (pmove->movetype != MOVETYPE_FLY && pmove->movetype != MOVETYPE_BOUNCEMISSILE && pmove->movetype != MOVETYPE_FLYMISSILE) PM_AddGravity(); VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); PM_CheckVelocity(); VectorScale(pmove->velocity, pmove->frametime, move); VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); trace = PM_PushEntity(move); PM_CheckVelocity(); if (trace.allsolid) { pmove->onground = trace.ent; VectorCopy(vec3_origin, pmove->velocity); return; } if (trace.fraction == 1) { PM_CheckWater(); return; } if (pmove->movetype == MOVETYPE_BOUNCE) backoff = 2.0 - pmove->friction; else if (pmove->movetype == MOVETYPE_BOUNCEMISSILE) backoff = 2.0; else backoff = 1; PM_ClipVelocity(pmove->velocity, trace.plane.normal, pmove->velocity, backoff); if (trace.plane.normal[2] > 0.7) { float vel; vec3_t base; VectorClear(base); if (pmove->velocity[2] < pmove->movevars->gravity * pmove->frametime) { pmove->onground = trace.ent; pmove->velocity[2] = 0; } vel = DotProduct(pmove->velocity, pmove->velocity); if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) { pmove->onground = trace.ent; VectorCopy(vec3_origin, pmove->velocity); } else { VectorScale(pmove->velocity, (1.0 - trace.fraction) * pmove->frametime * 0.9, move); trace = PM_PushEntity(move); } VectorSubtract(pmove->velocity, base, pmove->velocity) } PM_CheckWater(); } void PM_NoClip(void) { int i; vec3_t wishvel; float fmove, smove; fmove = pmove->cmd.forwardmove; smove = pmove->cmd.sidemove; VectorNormalize(pmove->forward); VectorNormalize(pmove->right); for (i = 0; i < 3; i++) { wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; } wishvel[2] += pmove->cmd.upmove; VectorMA(pmove->origin, pmove->frametime, wishvel, pmove->origin); VectorClear(pmove->velocity); } #define BUNNYJUMP_MAX_SPEED_FACTOR 1.2f void PM_PreventMegaBunnyJumping(void) { float spd; float fraction; float maxscaledspeed; maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed; if (maxscaledspeed <= 0.0f) return; spd = Length(pmove->velocity); if (spd <= maxscaledspeed) return; fraction = (maxscaledspeed / spd) * 0.8; VectorScale(pmove->velocity, fraction, pmove->velocity); } void PM_Jump(void) { if (pmove->dead) { pmove->oldbuttons |= IN_JUMP; return; } if (pmove->waterjumptime) { pmove->waterjumptime -= pmove->cmd.msec; if (pmove->waterjumptime < 0) { pmove->waterjumptime = 0; } return; } if (pmove->waterlevel >= 2) { pmove->onground = -1; if (pmove->watertype == CONTENTS_WATER) pmove->velocity[2] = 100; else if (pmove->watertype == CONTENTS_SLIME) pmove->velocity[2] = 80; else pmove->velocity[2] = 50; if (pmove->flSwimTime <= 0) { pmove->flSwimTime = 1000; switch (pmove->RandomLong(0, 3)) { case 0: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 1: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 2: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 3: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } } } return; } if (pmove->onground == -1) { pmove->oldbuttons |= IN_JUMP; return; } if (~pmove->oldbuttons & IN_JUMP && (!pmove->bInDuck || ~pmove->flags & FL_DUCKING)) { pmove->onground = -1; PM_CatagorizeTextureType(); PM_PreventMegaBunnyJumping(); if (Length(pmove->velocity) >= 150.0) { PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), 1.0); } pmove->velocity[2] = sqrt(2 * 800 * 45.0); if (pmove->fuser2 > 0.0) pmove->velocity[2] *= (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01; pmove->fuser2 = 1315.7894; PM_FixupGravityVelocity(); pmove->oldbuttons |= IN_JUMP; } } #define WJ_HEIGHT 8 void PM_CheckWaterJump(void) { vec3_t vecStart, vecEnd; vec3_t flatforward; vec3_t flatvelocity; float curspeed; pmtrace_t tr; int savehull; if (pmove->waterjumptime) return; if (pmove->velocity[2] < -180) return; flatvelocity[0] = pmove->velocity[0]; flatvelocity[1] = pmove->velocity[1]; flatvelocity[2] = 0; curspeed = VectorNormalize(flatvelocity); flatforward[0] = pmove->forward[0]; flatforward[1] = pmove->forward[1]; flatforward[2] = 0; VectorNormalize (flatforward); if (curspeed != 0.0 && (DotProduct(flatvelocity, flatforward) < 0.0)) return; VectorCopy(pmove->origin, vecStart); vecStart[2] += WJ_HEIGHT; VectorMA(vecStart, 24, flatforward, vecEnd); savehull = pmove->usehull; pmove->usehull = 2; tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); if (tr.fraction < 1.0 && fabs(tr.plane.normal[2]) < 0.1f) { vecStart[2] += pmove->player_maxs[savehull][2] - WJ_HEIGHT; VectorMA(vecStart, 24, flatforward, vecEnd); VectorMA(vec3_origin, -50, tr.plane.normal, pmove->movedir); tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); if (tr.fraction == 1.0) { pmove->waterjumptime = 2000; pmove->velocity[2] = 225; pmove->oldbuttons |= IN_JUMP; pmove->flags |= FL_WATERJUMP; } } pmove->usehull = savehull; } void PM_CheckFalling(void) { if (pmove->onground != -1 && !pmove->dead && pmove->flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD) { float fvol = 0.5; if (pmove->waterlevel > 0) { } else if (pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED) { fvol = 1.0; } else if (pmove->flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2) { fvol = 0.85; } else if (pmove->flFallVelocity < PLAYER_MIN_BOUNCE_SPEED) { fvol = 0; } if (fvol > 0.0) { PM_CatagorizeTextureType(); PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), fvol); pmove->flTimeStepSound = 300; pmove->punchangle[2] = pmove->flFallVelocity * 0.013; if (pmove->punchangle[0] > 8) pmove->punchangle[0] = 8; } } if (pmove->onground != -1) { pmove->flFallVelocity = 0; } } void PM_PlayWaterSounds(void) { if ((pmove->oldwaterlevel == 0 && pmove->waterlevel != 0) || (pmove->oldwaterlevel != 0 && pmove->waterlevel == 0)) { switch (pmove->RandomLong(0, 3)) { case 0: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 1: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 2: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } case 3: { pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", 1, ATTN_NORM, 0, PITCH_NORM); break; } } } } float PM_CalcRoll(vec3_t angles, vec3_t velocity, float rollangle, float rollspeed) { float sign; float side; float value; vec3_t forward, right, up; AngleVectors(angles, forward, right, up); side = DotProduct(velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = rollangle; if (side < rollspeed) { side = side * value / rollspeed; } else { side = value; } return side * sign; } void PM_DropPunchAngle(vec3_t punchangle) { float len; len = VectorNormalize (punchangle); len -= (10.0 + len * 0.5) * pmove->frametime; len = max(len, 0.0); VectorScale(punchangle, len, punchangle); } void PM_CheckParamters(void) { float spd; float maxspeed; vec3_t v_angle; spd = (pmove->cmd.forwardmove * pmove->cmd.forwardmove) + (pmove->cmd.sidemove * pmove->cmd.sidemove) + (pmove->cmd.upmove * pmove->cmd.upmove); spd = sqrt(spd); maxspeed = pmove->clientmaxspeed; if (maxspeed != 0.0) pmove->maxspeed = min(maxspeed, pmove->maxspeed); if ((spd != 0.0) && (spd > pmove->maxspeed)) { float fRatio = pmove->maxspeed / spd; pmove->cmd.forwardmove *= fRatio; pmove->cmd.sidemove *= fRatio; pmove->cmd.upmove *= fRatio; } if (pmove->flags & FL_FROZEN || pmove->flags & FL_ONTRAIN || pmove->dead) { pmove->cmd.forwardmove = 0; pmove->cmd.sidemove = 0; pmove->cmd.upmove = 0; } PM_DropPunchAngle(pmove->punchangle); if (!pmove->dead) { VectorCopy(pmove->cmd.viewangles, v_angle); VectorAdd(v_angle, pmove->punchangle, v_angle); pmove->angles[ROLL] = PM_CalcRoll(v_angle, pmove->velocity, pmove->movevars->rollangle, pmove->movevars->rollspeed) * 4; pmove->angles[PITCH] = v_angle[PITCH]; pmove->angles[YAW] = v_angle[YAW]; } else { VectorCopy(pmove->oldangles, pmove->angles); } if (pmove->dead) { pmove->view_ofs[2] = PM_DEAD_VIEWHEIGHT; } if (pmove->angles[YAW] > 180.0f) { pmove->angles[YAW] -= 360.0f; } } void PM_ReduceTimers(void) { if (pmove->flTimeStepSound > 0) { pmove->flTimeStepSound -= pmove->cmd.msec; if (pmove->flTimeStepSound < 0) { pmove->flTimeStepSound = 0; } } if (pmove->flDuckTime > 0) { pmove->flDuckTime -= pmove->cmd.msec; if (pmove->flDuckTime < 0) { pmove->flDuckTime = 0; } } if (pmove->flSwimTime > 0) { pmove->flSwimTime -= pmove->cmd.msec; if (pmove->flSwimTime < 0) { pmove->flSwimTime = 0; } } if (pmove->fuser2 > 0.0) { pmove->fuser2 -= pmove->cmd.msec; if (pmove->fuser2 < 0) pmove->fuser2 = 0; } } void PM_PlayerMove(qboolean server) { physent_t *pLadder = NULL; pmove->server = server; PM_CheckParamters(); pmove->numtouch = 0; pmove->frametime = pmove->cmd.msec * 0.001; PM_ReduceTimers(); AngleVectors(pmove->angles, pmove->forward, pmove->right, pmove->up); if ((pmove->spectator || pmove->iuser1 > 0) && (pmove->iuser3 <= 0 || pmove->deadflag == DEAD_DEAD)) { PM_SpectatorMove(); PM_CatagorizePosition(); return; } if (pmove->movetype != MOVETYPE_NOCLIP && pmove->movetype != MOVETYPE_NONE) { if (PM_CheckStuck()) { return; } } PM_CatagorizePosition(); pmove->oldwaterlevel = pmove->waterlevel; if (pmove->onground == -1) { pmove->flFallVelocity = -pmove->velocity[2]; } g_onladder = 0; if (!pmove->dead && !(pmove->flags & FL_ONTRAIN)) { pLadder = PM_Ladder(); if (pLadder) { g_onladder = 1; } } PM_Duck(); PM_UpdateStepSound(); if (!pmove->dead && !(pmove->flags & FL_ONTRAIN)) { if (pLadder) { PM_LadderMove(pLadder); } else if (pmove->movetype != MOVETYPE_WALK && pmove->movetype != MOVETYPE_NOCLIP) { pmove->movetype = MOVETYPE_WALK; } } switch (pmove->movetype) { default: { pmove->Con_DPrintf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); break; } case MOVETYPE_NONE: { break; } case MOVETYPE_NOCLIP: { PM_NoClip(); break; } case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: { PM_Physics_Toss(); break; } case MOVETYPE_FLY: { PM_CheckWater(); if (pmove->cmd.buttons & IN_JUMP) { if (!pLadder) { PM_Jump(); } } else { pmove->oldbuttons &= ~IN_JUMP; } VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); PM_FlyMove(); VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); break; } case MOVETYPE_WALK: { if (!PM_InWater()) { PM_AddCorrectGravity(); } if (pmove->waterjumptime) { PM_WaterJump(); PM_FlyMove(); PM_CheckWater(); return; } if (pmove->waterlevel >= 2) { if (pmove->waterlevel == 2) { PM_CheckWaterJump(); } if (pmove->velocity[2] < 0 && pmove->waterjumptime) { pmove->waterjumptime = 0; } if (pmove->cmd.buttons & IN_JUMP) { PM_Jump(); } else { pmove->oldbuttons &= ~IN_JUMP; } PM_WaterMove(); VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); PM_CatagorizePosition(); } else { if (pmove->cmd.buttons & IN_JUMP) { if (!pLadder) { PM_Jump(); } } else { pmove->oldbuttons &= ~IN_JUMP; } if (pmove->onground != -1) { pmove->velocity[2] = 0.0; PM_Friction(); } PM_CheckVelocity(); if (pmove->onground != -1) { PM_WalkMove(); } else { PM_AirMove(); } PM_CatagorizePosition(); VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); PM_CheckVelocity(); if (!PM_InWater()) { PM_FixupGravityVelocity(); } if (pmove->onground != -1) { pmove->velocity[2] = 0; } PM_CheckFalling(); } PM_PlayWaterSounds(); break; } } } void PM_CreateStuckTable(void) { float x, y, z; int idx; int i; float zi[3]; memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable)); idx = 0; x = y = 0; for (z = -0.125; z <= 0.125; z += 0.125, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } x = z = 0; for (y = -0.125; y <= 0.125; y += 0.125, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } y = z = 0; for (x = -0.125; x <= 0.125; x += 0.125, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } for (x = -0.125; x <= 0.125; x += 0.250) { for (y = -0.125; y <= 0.125; y += 0.250) { for (z = -0.125; z <= 0.125; z += 0.250, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } } } x = y = 0; zi[0] = 0; zi[1] = 1; zi[2] = 6; for (i = 0; i < 3; i++, idx++) { z = zi[i]; rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } x = z = 0; for (y = -2; y <= 2; y += 2, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } y = z = 0; for (x = -2; x <= 2; x += 2, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } for (i = 0; i < 3; i++) { z = zi[i]; for (y = -2; y <= 2; y += 2) { for (z = -2; z <= 2; z += 2, idx++) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; } } } } void PM_Move (struct playermove_s *ppmove, int server) { assert(pm_shared_initialized); pmove = ppmove; PM_PlayerMove((server != 0) ? true : false); if (pmove->onground != -1) { pmove->flags |= FL_ONGROUND; } else { pmove->flags &= ~FL_ONGROUND; } if (!pmove->multiplayer && (pmove->movetype == MOVETYPE_WALK)) { pmove->friction = 1.0f; } } int PM_GetVisEntInfo(int ent) { if (ent >= 0 && ent <= pmove->numvisent) { return pmove->visents[ent].info; } return -1; } int PM_GetPhysEntInfo(int ent) { if (ent >= 0 && ent <= pmove->numphysent) return pmove->physents[ent].info; return -1; } void PM_Init(struct playermove_s *ppmove) { assert(!pm_shared_initialized); pmove = ppmove; PM_CreateStuckTable(); PM_InitTextureTypes(); pm_shared_initialized = 1; }