// Copyright (C) 1999-2000 Id Software, Inc. // // bg_slidemove.c -- part of bg_pmove functionality #include "q_shared.h" #include "bg_public.h" #include "bg_local.h" #ifdef QAGAME //yeah, this is kind of bad #include "g_local.h" #endif /* input: origin, velocity, bounds, groundPlane, trace function output: origin, velocity, impacts, stairup boolean */ //do vehicle impact stuff // slight rearrangement by BTO (VV) so that we only have one namespace include #ifdef QAGAME extern void G_FlyVehicleSurfaceDestruction(gentity_t *veh, trace_t *trace, int magnitude, qboolean force ); //g_vehicle.c extern qboolean G_CanBeEnemy(gentity_t *self, gentity_t *enemy); //w_saber.c #endif extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh ); #include "../namespace_begin.h" extern bgEntity_t *pm_entSelf; extern bgEntity_t *pm_entVeh; //vehicle impact stuff continued... #ifndef QAGAME //kind of hacky extern void trap_FX_PlayEffectID( int id, vec3_t org, vec3_t fwd, int vol, int rad ); #endif #ifdef QAGAME extern qboolean FighterIsLanded( Vehicle_t *pVeh, playerState_t *parentPS ); extern void G_DamageFromKiller( gentity_t *pEnt, gentity_t *pVehEnt, gentity_t *attacker, vec3_t org, int damage, int dflags, int mod ); #endif extern void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd); #define MAX_IMPACT_TURN_ANGLE 45.0f void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace) { // See if the vehicle has crashed into the ground. Vehicle_t *pSelfVeh = pEnt->m_pVehicle; float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f; qboolean forceSurfDestruction = qfalse; #ifdef QAGAME gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL; if (!hitEnt || //nothing to hit (pSelfVeh && pSelfVeh->m_pPilot &&//I'm a piloted vehicle hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse &&//that hit a missile hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number)//and the missile is owned by my pilot ) {//don't hit it return; } if ( pSelfVeh//I have a vehicle struct && pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed {//spiralling to our deaths, explode on any solid impact if ( hitEnt->s.NPC_class == CLASS_VEHICLE ) {//hit another vehicle, explode! //Give credit to whoever got me into this death spiral state G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_COLLISION ); return; } else if ( !VectorCompare( trace->plane.normal, vec3_origin ) && (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) ) {//have a valid hit plane and we hit a solid brush vec3_t moveDir; float impactDot; VectorCopy( pm->ps->velocity, moveDir ); VectorNormalize( moveDir ); impactDot = DotProduct( moveDir, trace->plane.normal ); if ( impactDot <= -0.7f )//hit rather head-on and hard {// Just DIE now //Give credit to whoever got me into this death spiral state G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING ); return; } } } if ( trace->entityNum < ENTITYNUM_WORLD && hitEnt->s.eType == ET_MOVER && hitEnt->s.apos.trType != TR_STATIONARY//rotating && (hitEnt->spawnflags&16) //IMPACT && Q_stricmp( "func_rotating", hitEnt->classname ) == 0 ) {//hit a func_rotating that is supposed to destroy anything it touches! //guarantee the hit will happen, thereby taking off a piece of the ship forceSurfDestruction = qtrue; } else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #endif /* if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) && trace->plane.normal[2] > 0.7f && fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f && fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/ {//we're landing, we're cool //FIXME: some sort of landing "thump", not the impactFX /* if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { vec3_t up = {0,0,1}; #ifdef QAGAME G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up ); #else trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up, -1, -1 ); #endif } */ //this was annoying me -rww //FIXME: this shouldn't even be getting called when the vehicle is at rest! #ifdef QAGAME if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //always smack players } else #endif { return; } } if ( pSelfVeh && (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's.. (magnitude >= 100||forceSurfDestruction) ) { if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime || forceSurfDestruction ) {//a bit of a hack, may conflict with getting shot, but... //FIXME: impact sound and effect should be gotten from g_vehicleInfo...? //FIXME: should pass in trace.endpos and trace.plane.normal vec3_t vehUp; #ifdef QAGAME qboolean noDamage = qfalse; #else bgEntity_t *hitEnt; #endif if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction ) { qboolean turnFromImpact = qfalse, turnHitEnt = qfalse; float l = pm->ps->speed*0.5f; vec3_t bounceDir; #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of { //bounce off in the opposite direction of the impact if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER) { pm->ps->speed *= pml.frametime; VectorCopy(trace->plane.normal, bounceDir); } else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on && pSelfVeh->m_LandTrace.fraction < 1.0f //ground present && pm->ps->speed <= MIN_LANDING_SPEED ) {//could land here, don't bounce off, in fact, return altogether! return; } else { if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { turnFromImpact = qtrue; } VectorCopy(trace->plane.normal, bounceDir); } } else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER ) {//check for impact with another fighter #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle && hitEnt->m_pVehicle->m_pVehicleInfo && hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) {//two vehicles hit each other, turn away from the impact turnFromImpact = qtrue; turnHitEnt = qtrue; #ifndef QAGAME VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir ); #else VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir ); #endif VectorNormalize( bounceDir ); } } if ( turnFromImpact ) {//bounce off impact surf and turn away vec3_t pushDir={0}, turnAwayAngles, turnDelta; float turnStrength, pitchTurnStrength, yawTurnStrength; vec3_t moveDir; float bounceDot, turnDivider; //bounce if ( !turnHitEnt ) {//hit wall VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir); } else {//hit another fighter #ifndef QAGAME VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir ); #else if ( hitEnt->client ) { VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir ); } else { VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir ); } #endif VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir); VectorScale(pushDir, 0.1f, pushDir); } VectorNormalize2( pm->ps->velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity); //turn turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } turnStrength = (magnitude/2000.0f); if ( turnStrength < 0.1f ) { turnStrength = 0.1f; } else if ( turnStrength > 2.0f ) { turnStrength = 2.0f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //pSelfVeh->m_vOrientation[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } /* PM_SetPMViewAngle(pm->ps, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); if ( pm_entVeh ) {//I'm a vehicle, so pm_entVeh is actually my pilot bgEntity_t *pilot = pm_entVeh; if ( !BG_UnrestrainedPitchRoll( pilot->playerState, pSelfVeh ) ) { //set the rider's viewangles to the vehicle's viewangles PM_SetPMViewAngle(pilot->playerState, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd); } } */ #ifdef QAGAME//server-side, turn the guy we hit away from us, too if ( turnHitEnt//make the other guy turn and get pushed && hitEnt->client //must be a valid client && !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed && !(hitEnt->spawnflags&2) )//and not if suspended { l = hitEnt->client->ps.speed; //now bounce *them* away and turn them //flip the bounceDir VectorScale( bounceDir, -1, bounceDir ); //do bounce VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir ); VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir); VectorNormalize2( hitEnt->client->ps.velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity); //turn turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //hitEnt->m_pVehicle->m_vOrientation[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } //hitEnt->m_pVehicle->m_vOrientation[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //NOTE: will these angle changes stick or will they be stomped // when the vehicle goes through its own update and re-grabs // its angles from its pilot...? Should we do a // SetClientViewAngles on the pilot? /* SetClientViewAngle( hitEnt, hitEnt->m_pVehicle->m_vOrientation ); if ( hitEnt->m_pVehicle->m_pPilot && ((gentity_t *)hitEnt->m_pVehicle->m_pPilot)->client ) { SetClientViewAngle( (gentity_t *)hitEnt->m_pVehicle->m_pPilot, hitEnt->m_pVehicle->m_vOrientation ); } */ } #endif } } #ifdef QAGAME if (!hitEnt) { return; } AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { //G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp ); //tempent use bad! G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX); } pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; if (hitEnt && (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)) { //don't damage the vehicle from terrain that doesn't want to damage vehicles gentity_t *killer = NULL; if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //increase the damage... float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f); if (mult < 1.0f) { mult = 1.0f; } if (hitEnt->inuse && hitEnt->takedamage) { //if the other guy takes damage, don't hurt us a lot for ramming him //unless it's a vehicle, then we get 1.5 times damage if (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle) { mult = 1.5f; } else { mult = 0.5f; } } magnitude *= mult; } pSelfVeh->m_iLastImpactDmg = magnitude; //FIXME: what about proper death credit to the guy who shot you down? //FIXME: actually damage part of the ship that impacted? if ( hitEnt->s.eType == ET_MISSILE )//missile { //FIX: NEVER do or take impact damage from a missile... noDamage = qtrue; if ( (hitEnt->s.eFlags&EF_JETPACK_ACTIVE)//vehicle missile && ((gentity_t *)hitEnt)->r.ownerNum < MAX_CLIENTS )//valid client owner {//I ran into a missile and died because of the impact, give credit to the missile's owner (PROBLEM: might this ever accidently give improper credit to client 0?) /* if ( ((gentity_t *)hitEnt)->r.ownerNum == pEnt->s.number ) {//hit our own missile? Don't damage ourselves or it... (so we don't kill ourselves!) if it hits *us*, then fine, but not here noDamage = qtrue; } */ killer = &g_entities[((gentity_t *)hitEnt)->r.ownerNum]; } } if ( !noDamage ) { G_Damage( (gentity_t *)pEnt, ((gentity_t*)hitEnt), killer!=NULL?killer:((gentity_t *)hitEnt), NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING) );//FIXME: MOD_IMPACT } if (pSelfVeh->m_pVehicleInfo->surfDestruction) { G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction ); } pSelfVeh->m_ulFlags |= VEH_CRASHING; } if (hitEnt && hitEnt->inuse && hitEnt->takedamage) { //damage this guy because we hit him float pmult = 1.0f; int finalD; gentity_t *attackEnt; if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) || (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) ) { //probably a humanoid, or something if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //player die good.. if me fighter pmult = 2000.0f; } else { pmult = 40.0f; } if (hitEnt->client && BG_KnockDownable(&hitEnt->client->ps) && G_CanBeEnemy((gentity_t *)pEnt, hitEnt)) { //smash! if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100; hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } hitEnt->client->ps.otherKiller = pEnt->s.number; hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000; hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100; hitEnt->client->otherKillerMOD = MOD_COLLISION; hitEnt->client->otherKillerVehWeapon = 0; hitEnt->client->otherKillerWeaponType = WP_NONE; //add my velocity into his to force him along in the correct direction from impact VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity); //upward thrust hitEnt->client->ps.velocity[2] += 200.0f; } } if (pSelfVeh->m_pPilot) { attackEnt = (gentity_t *)pSelfVeh->m_pPilot; } else { attackEnt = (gentity_t *)pEnt; } finalD = magnitude*pmult; if (finalD < 1) { finalD = 1; } if ( !noDamage ) { G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING/*MOD_MELEE*/) );//FIXME: MOD_IMPACT } } #else //this is gonna result in "double effects" for the client doing the prediction. //it doesn't look bad though. could just use predicted events, but I'm too lazy. hitEnt = PM_BGEntForNum(trace->entityNum); if (!hitEnt || hitEnt->s.owner != pEnt->s.number) { //don't hit your own missiles! AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 ); pSelfVeh->m_ulFlags |= VEH_CRASHING; } #endif } } } qboolean PM_GroundSlideOkay( float zNormal ) { if ( zNormal > 0 ) { if ( pm->ps->velocity[2] > 0 ) { if ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND || BG_InReboundJump( pm->ps->legsAnim )) { return qfalse; } } } return qtrue; } /* =============== qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf ) =============== */ #ifdef QAGAME extern void Client_CheckImpactBBrush( gentity_t *self, gentity_t *other ); qboolean PM_ClientImpact( trace_t *trace ) { //don't try to predict this gentity_t *traceEnt; int otherEntityNum = trace->entityNum; if ( !pm_entSelf ) { return qfalse; } if ( otherEntityNum >= ENTITYNUM_WORLD ) { return qfalse; } traceEnt = &g_entities[otherEntityNum]; if( VectorLength( pm->ps->velocity ) >= 100 && pm_entSelf->s.NPC_class != CLASS_VEHICLE && pm->ps->lastOnGround+100 < level.time ) //&& pm->ps->groundEntityNum == ENTITYNUM_NONE ) { Client_CheckImpactBBrush( (gentity_t *)(pm_entSelf), &g_entities[otherEntityNum] ); } if ( !traceEnt || !(traceEnt->r.contents&pm->tracemask) ) {//it's dead or not in my way anymore, don't clip against it return qtrue; } return qfalse; } #endif /* ================== PM_SlideMove Returns qtrue if the velocity was clipped in some way ================== */ #define MAX_CLIP_PLANES 5 qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t normal, planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; //qboolean damageSelf = qtrue; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) ) {// slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); if ( !PM_GroundSlideOkay( planes[0][2] ) ) { planes[0][2] = 0; VectorNormalize( planes[0] ); } } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); if (pm->ps->clientNum >= MAX_CLIENTS) { bgEntity_t *pEnt = pm_entSelf; if (pEnt && pEnt->s.eType == ET_NPC && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle) { //do vehicle impact stuff then PM_VehicleImpact(pEnt, &trace); } } #ifdef QAGAME else { if ( PM_ClientImpact( &trace ) ) { continue; } } #endif time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } VectorCopy( trace.plane.normal, normal ); if ( !PM_GroundSlideOkay( normal[2] ) ) {//wall-running //never push up off a sloped wall normal[2] = 0; VectorNormalize( normal ); } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) {//no sliding if stuck to wall! for ( i = 0 ; i < numplanes ; i++ ) { if ( VectorCompare( normal, planes[i] ) ) {//DotProduct( normal, planes[i] ) > 0.99 ) { VectorAdd( normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } } VectorCopy (normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a triple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return ( bumpcount != 0 ); } /* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; // float down_dist, up_dist; // vec3_t delta, delta2; vec3_t up, down; float stepSize; qboolean isGiant = qfalse; bgEntity_t *pEnt; qboolean skipStep = qfalse; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( BG_InReboundHold( pm->ps->legsAnim ) ) { gravity = qfalse; } if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } pEnt = pm_entSelf; if (pm->ps->clientNum >= MAX_CLIENTS) { if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) { return; } } VectorCopy(start_o, down); down[2] -= STEPSIZE; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); if (pm->ps->clientNum >= MAX_CLIENTS) { // apply ground friction, even if on ladder if (pEnt && pEnt->s.NPC_class == CLASS_ATST || (pEnt->s.NPC_class == CLASS_VEHICLE && pEnt->m_pVehicle && pEnt->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) ) {//AT-STs can step high up[2] += 66.0f; isGiant = qtrue; } else if ( pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//also can step up high up[2] += 64.0f; isGiant = qtrue; } else { up[2] += STEPSIZE; } } else { up[2] += STEPSIZE; } // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid ) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); PM_SlideMove( gravity ); // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( pm->stepSlideFix ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) {//normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_RANCOR ) {//Rancor don't step on clients if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } /* else if ( pm->ps->clientNum >= MAX_CLIENTS//NPC && isGiant && trace.entityNum < MAX_CLIENTS && pEnt && pEnt->s.NPC_class == CLASS_ATST && OnSameTeam( pEnt, traceEnt) ) {//NPC AT-ST's don't step up on allies VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } */ else { VectorCopy (trace.endpos, pm->ps->origin); if ( pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( pm->stepSlideFix ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !pm->stepSlideFix ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } #if 0 // if the down trace can trace back to the original position directly, don't step pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { // use the original move VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); if ( pm->debugLevel ) { Com_Printf("%i:bend\n", c_pmove); } } else #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } } #include "../namespace_end.h"