/* ================== SV_WriteEntitiesToClient ================== */ void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg ) { edict_t *clent; edict_t *viewent; // may be NULL client_frame_t *frame; entity_state_t *state; static sv_ents_t frame_ents; int i, send_pings; clent = cl->edict; if( !SV_IsValidEdict( clent ) ) { SV_DropClient ( cl ); return; } viewent = cl->pViewEntity; // himself or trigger_camera frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK]; send_pings = SV_ShouldUpdatePing( cl ); sv.net_framenum++; // now all portal-through entities are invalidate sv.hostflags &= ~SVF_PORTALPASS; // clear everything in this snapshot frame_ents.num_entities = c_fullsend = 0; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( frame_ents.entities, frame_ents.num_entities, sizeof( frame_ents.entities[0] ), SV_EntityNumbers ); // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.next_client_entities; for( i = 0; i < frame_ents.num_entities; i++ ) { // add it to the circular packet_entities array state = &svs.packet_entities[svs.next_client_entities % svs.num_client_entities]; *state = frame_ents.entities[i]; svs.next_client_entities++; // this should never hit, map should always be restarted first in SV_Frame if( svs.next_client_entities >= 0x7FFFFFFE ) Host_Error( "svs.next_client_entities wrapped\n" ); frame->num_entities++; } SV_EmitPacketEntities( cl, frame, msg ); SV_EmitEvents( cl, frame, msg ); if( send_pings ) SV_EmitPings( msg ); }
static void pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) { edict_t *ent; ent = EDICT_NUM( svgame.pmove->player_index + 1 ); if( !SV_IsValidEdict( ent )) return; SV_StartSound( ent, channel, sample, volume, attenuation, fFlags, pitch ); }
/* ============= SV_CheckMover test thing (applies the friction to pushables while standing on moving platform) ============= */ qboolean SV_CheckMover( edict_t *ent ) { edict_t *gnd = ent->v.groundentity; if( !SV_IsValidEdict( gnd )) return false; if( gnd->v.movetype != MOVETYPE_PUSH ) return false; if( VectorIsNull( gnd->v.velocity ) && VectorIsNull( gnd->v.avelocity )) return false; return true; }
/* ================ SV_CreateBaseline Entity baselines are used to compress the update messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ void SV_CreateBaseline( void ) { edict_t *pEdict; int e; for( e = 0; e < svgame.numEntities; e++ ) { pEdict = EDICT_NUM( e ); if( !SV_IsValidEdict( pEdict )) continue; SV_BaselineForEntity( pEdict ); } // create the instanced baselines svgame.dllFuncs.pfnCreateInstancedBaselines(); }
/* ================== SV_Kill_f ================== */ void SV_Kill_f( void ) { if( !SV_SetPlayer() || sv.background ) return; if( !svs.currentPlayer || !SV_IsValidEdict( svs.currentPlayer->edict )) return; if( svs.currentPlayer->edict->v.health <= 0.0f ) { SV_ClientPrintf( svs.currentPlayer, PRINT_HIGH, "Can't suicide -- allready dead!\n"); return; } svgame.dllFuncs.pfnClientKill( svs.currentPlayer->edict ); }
static void pfnPlaybackEventFull( int flags, int clientindex, word eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ) { edict_t *ent; ent = EDICT_NUM( clientindex + 1 ); if( !SV_IsValidEdict( ent )) return; if( host.type == HOST_DEDICATED ) flags |= FEV_NOTHOST; // no local clients for dedicated server SV_PlaybackEventFull( flags, ent, eventindex, delay, origin, angles, fparam1, fparam2, iparam1, iparam2, bparam1, bparam2 ); }
/* ================ SV_CheckAllEnts ================ */ void SV_CheckAllEnts( void ) { edict_t *e; int i; if( !sv_check_errors->integer || sv.state != ss_active ) return; // check edicts errors for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) { e = EDICT_NUM( i ); // DEBUG: check 'gamestate' for using by mods if( e->v.gamestate != 0 ) { MsgDev( D_INFO, "Entity %s[%i] uses gamestate %i\n", SV_ClassName( e ), NUM_FOR_EDICT( e ), e->v.gamestate ); } if( e->free && e->pvPrivateData != NULL ) { MsgDev( D_ERROR, "Freed entity %s (%i) has private data.\n", SV_ClassName( e ), i ); continue; } if( !SV_IsValidEdict( e )) continue; if( !e->v.pContainingEntity || e->v.pContainingEntity != e ) { MsgDev( D_ERROR, "Entity %s (%i) has invalid container, fixed.\n", SV_ClassName( e ), i ); e->v.pContainingEntity = e; continue; } if( !e->pvPrivateData || !Mem_IsAllocatedExt( svgame.mempool, e->pvPrivateData )) { MsgDev( D_ERROR, "Entity %s (%i) trashed private data.\n", SV_ClassName( e ), i ); e->pvPrivateData = NULL; continue; } SV_CheckVelocity( e ); } }
/* ================ SV_Physics ================ */ void SV_Physics( void ) { edict_t *ent; int i; SV_CheckAllEnts (); svgame.globals->time = sv.time; // let the progs know that a new frame has started svgame.dllFuncs.pfnStartFrame(); // treat each object in turn for( i = 0; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( !SV_IsValidEdict( ent )) continue; if( i > 0 && i <= svgame.globals->maxClients ) continue; SV_Physics_Entity( ent ); } if( svgame.physFuncs.SV_EndFrame != NULL ) svgame.physFuncs.SV_EndFrame(); // animate lightstyles (used for GetEntityIllum) SV_RunLightStyles (); if( sv_skyspeed->value ) { // evaluate sky rotation. float skyAngle = sv_skyangle->value + sv_skyspeed->value * host.frametime; Cvar_SetFloat( "sv_skyangle", anglemod( skyAngle )); } // decrement svgame.numEntities if the highest number entities died for( ; EDICT_NUM( svgame.numEntities - 1 )->free; svgame.numEntities-- ); if( svgame.globals->force_retouch != 0.0f ) svgame.globals->force_retouch--; }
/* ============= SV_Physics_Follow just copy angles and origin of parent ============= */ void SV_Physics_Follow( edict_t *ent ) { edict_t *parent; // regular thinking if( !SV_RunThink( ent )) return; parent = ent->v.aiment; if( !SV_IsValidEdict( parent )) { MsgDev( D_ERROR, "%s have MOVETYPE_FOLLOW with no corresponding ent!", SV_ClassName( ent )); ent->v.movetype = MOVETYPE_NONE; return; } VectorAdd( parent->v.origin, ent->v.v_angle, ent->v.origin ); VectorCopy( parent->v.angles, ent->v.angles ); SV_LinkEdict( ent, true ); }
/* ============ SV_PushEntity Does not change the entities velocity at all ============ */ trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apush, int *blocked ) { trace_t trace; int type; vec3_t end; VectorAdd( ent->v.origin, lpush, end ); if( ent->v.movetype == MOVETYPE_FLYMISSILE ) type = MOVE_MISSILE; else if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT ) type = MOVE_NOMONSTERS; // only clip against bmodels else type = MOVE_NORMAL; trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, end, type, ent ); if( trace.fraction != 0.0f ) { VectorCopy( trace.endpos, ent->v.origin ); if( sv.state == ss_active && apush[YAW] && ( ent->v.flags & FL_CLIENT )) { ent->v.avelocity[1] += apush[1]; ent->v.fixangle = 2; } // don't rotate pushables! if( SV_AllowPushRotate( ent )) ent->v.angles[YAW] += trace.fraction * apush[YAW]; } SV_LinkEdict( ent, true ); if( blocked ) *blocked = !VectorCompare( ent->v.origin, end ); // can't move full distance // so we can run impact function afterwards. if( SV_IsValidEdict( trace.ent )) SV_Impact( ent, trace.ent, &trace ); return trace; }
/* ================ SV_UpdateBaseVelocity ================ */ void SV_UpdateBaseVelocity( edict_t *ent ) { if( ent->v.flags & FL_ONGROUND ) { edict_t *groundentity = ent->v.groundentity; if( SV_IsValidEdict( groundentity )) { // On conveyor belt that's moving? if( groundentity->v.flags & FL_CONVEYOR ) { vec3_t new_basevel; VectorScale( groundentity->v.movedir, groundentity->v.speed, new_basevel ); if( ent->v.flags & FL_BASEVELOCITY ) VectorAdd( new_basevel, ent->v.basevelocity, new_basevel ); ent->v.flags |= FL_BASEVELOCITY; VectorCopy( new_basevel, ent->v.basevelocity ); } } } }
/* =============== SV_EntityInfo_f =============== */ void SV_EntityInfo_f( void ) { edict_t *ent; int i; if( sv.state != ss_active ) { Msg( "^3No server running.\n" ); return; } for( i = 0; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); if( !SV_IsValidEdict( ent )) continue; Msg( "%5i origin: %.f %.f %.f", i, ent->v.origin[0], ent->v.origin[1], ent->v.origin[2] ); if( ent->v.classname ) Msg( ", class: %s", STRING( ent->v.classname )); if( ent->v.globalname ) Msg( ", global: %s", STRING( ent->v.globalname )); if( ent->v.targetname ) Msg( ", name: %s", STRING( ent->v.targetname )); if( ent->v.target ) Msg( ", target: %s", STRING( ent->v.target )); if( ent->v.model ) Msg( ", model: %s", STRING( ent->v.model )); Msg( "\n" ); } }
qboolean SV_MoveStep( edict_t *ent, vec3_t move, qboolean relink ) { int i; trace_t trace; vec3_t oldorg, neworg, end; edict_t *enemy; float dz; VectorCopy( ent->v.origin, oldorg ); VectorAdd( ent->v.origin, move, neworg ); // well, try it. Flying and swimming monsters are easiest. if( ent->v.flags & ( FL_SWIM|FL_FLY )) { // try one move with vertical motion, then one without for( i = 0; i < 2; i++ ) { VectorAdd( ent->v.origin, move, neworg ); enemy = ent->v.enemy; if( i == 0 && enemy != NULL ) { dz = ent->v.origin[2] - enemy->v.origin[2]; if( dz > 40 ) neworg[2] -= 8; else if( dz < 30 ) neworg[2] += 8; } trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, neworg, MOVE_NORMAL, ent ); if( trace.fraction == 1.0f ) { svs.groupmask = ent->v.groupinfo; // that move takes us out of the water. // apparently though, it's okay to travel into solids, lava, sky, etc :) if(( ent->v.flags & FL_SWIM ) && SV_PointContents( trace.endpos ) == CONTENTS_EMPTY ) return 0; VectorCopy( trace.endpos, ent->v.origin ); if( relink ) SV_LinkEdict( ent, true ); return 1; } else { if( !SV_IsValidEdict( enemy )) break; } } return 0; } else { dz = svgame.movevars.stepsize; neworg[2] += dz; VectorCopy( neworg, end ); end[2] -= dz * 2.0f; trace = SV_Move( neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent ); if( trace.allsolid ) return 0; if( trace.startsolid != 0 ) { neworg[2] -= dz; trace = SV_Move( neworg, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent ); if( trace.allsolid != 0 || trace.startsolid != 0 ) return 0; } if( trace.fraction == 1.0f ) { if( ent->v.flags & FL_PARTIALGROUND ) { VectorAdd( ent->v.origin, move, ent->v.origin ); if( relink ) SV_LinkEdict( ent, true ); ent->v.flags &= ~FL_ONGROUND; return 1; } return 0; } else { VectorCopy( trace.endpos, ent->v.origin ); if( SV_CheckBottom( ent, MOVE_NORMAL ) == 0 ) { if( ent->v.flags & FL_PARTIALGROUND ) { if( relink ) SV_LinkEdict( ent, true ); return 1; } VectorCopy( oldorg, ent->v.origin ); return 0; } else { ent->v.flags &= ~FL_PARTIALGROUND; ent->v.groundentity = trace.ent; if( relink ) SV_LinkEdict( ent, true ); return 1; } } } }
/* ============ SV_PushMove ============ */ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) { int i, e, block; int num_moved, oldsolid; vec3_t mins, maxs, lmove; sv_pushed_t *p, *pushed_p; edict_t *check; if( svgame.globals->changelevel || VectorIsNull( pusher->v.velocity )) { pusher->v.ltime += movetime; return NULL; } for( i = 0; i < 3; i++ ) { lmove[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + lmove[i]; maxs[i] = pusher->v.absmax[i] + lmove[i]; } pushed_p = svgame.pushed; // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->v.origin, pushed_p->origin ); VectorCopy( pusher->v.angles, pushed_p->angles ); pushed_p++; // move the pusher to it's final position SV_LinearMove( pusher, movetime, 0.0f ); SV_LinkEdict( pusher, false ); pusher->v.ltime += movetime; oldsolid = pusher->v.solid; // non-solid pushers can't push anything if( pusher->v.solid == SOLID_NOT ) return NULL; // see if any solid entities are inside the final position num_moved = 0; for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check, NULL )) continue; } // remove the onground flag for non-players if( check->v.movetype != MOVETYPE_WALK ) check->v.flags &= ~FL_ONGROUND; // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); VectorCopy( check->v.angles, pushed_p->angles ); pushed_p++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity( check, lmove, vec3_origin, &block ); pusher->v.solid = oldsolid; // if it is still inside the pusher, block if( SV_TestEntityPosition( check, NULL ) && block ) { if( !SV_CanBlock( check )) continue; pusher->v.ltime -= movetime; // 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 >= svgame.pushed; p-- ) { VectorCopy( p->origin, p->ent->v.origin ); VectorCopy( p->angles, p->ent->v.angles ); SV_LinkEdict( p->ent, (p->ent == check) ? true : false ); } return check; } } return NULL; }
/* ============ SV_PushRotate ============ */ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) { int i, e, block, oldsolid; matrix4x4 start_l, end_l; vec3_t lmove, amove; sv_pushed_t *p, *pushed_p; vec3_t org, org2, temp; edict_t *check; if( svgame.globals->changelevel || VectorIsNull( pusher->v.avelocity )) { pusher->v.ltime += movetime; return NULL; } for( i = 0; i < 3; i++ ) amove[i] = pusher->v.avelocity[i] * movetime; // create pusher initial position Matrix4x4_CreateFromEntity( start_l, pusher->v.angles, pusher->v.origin, 1.0f ); pushed_p = svgame.pushed; // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->v.origin, pushed_p->origin ); VectorCopy( pusher->v.angles, pushed_p->angles ); pushed_p++; // move the pusher to it's final position SV_AngularMove( pusher, movetime, pusher->v.friction ); SV_LinkEdict( pusher, false ); pusher->v.ltime += movetime; oldsolid = pusher->v.solid; // non-solid pushers can't push anything if( pusher->v.solid == SOLID_NOT ) return NULL; // create pusher final position Matrix4x4_CreateFromEntity( end_l, pusher->v.angles, pusher->v.origin, 1.0f ); // see if any solid entities are inside the final position for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) continue; // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check, NULL )) continue; } // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); VectorCopy( check->v.angles, pushed_p->angles ); pushed_p->fixangle = check->v.fixangle; pushed_p++; // calculate destination position if( check->v.movetype == MOVETYPE_PUSHSTEP || check->v.movetype == MOVETYPE_STEP ) VectorAverage( check->v.absmin, check->v.absmax, org ); else VectorCopy( check->v.origin, org ); Matrix4x4_VectorITransform( start_l, org, temp ); Matrix4x4_VectorTransform( end_l, temp, org2 ); VectorSubtract( org2, org, lmove ); // i can't clear FL_ONGROUND in all cases because many bad things may be happen if( check->v.movetype != MOVETYPE_WALK ) { if( lmove[2] != 0.0f ) check->v.flags &= ~FL_ONGROUND; if( lmove[2] < 0.0f && !pusher->v.dmg ) lmove[2] = 0.0f; // let's the free falling } // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity( check, lmove, amove, &block ); pusher->v.solid = oldsolid; // pushed entity blocked by wall if( block && check->v.movetype != MOVETYPE_WALK ) check->v.flags &= ~FL_ONGROUND; // if it is still inside the pusher, block if( SV_TestEntityPosition( check, NULL ) && block ) { if( !SV_CanBlock( check )) continue; pusher->v.ltime -= movetime; // 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 >= svgame.pushed; p-- ) { VectorCopy( p->origin, p->ent->v.origin ); VectorCopy( p->angles, p->ent->v.angles ); SV_LinkEdict( p->ent, (p->ent == check) ? true : false ); p->ent->v.fixangle = p->fixangle; } return check; } } return NULL; }
/* ============= SV_AddEntitiesToPacket ============= */ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents ) { edict_t *ent; byte *pset; qboolean fullvis = false; sv_client_t *netclient; sv_client_t *cl = NULL; entity_state_t *state; int e, player; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specifically check for it if( !sv.state ) return; cl = SV_ClientFromEdict( pClient, true ); //ASSERT( cl != NULL ); if( cl == NULL ) { //MsgDev( D_ERROR, "SV_AddEntitiesToPacket: you have broken clients!\n"); return; } if( pClient && !( sv.hostflags & SVF_PORTALPASS )) { // portals can't change hostflags sv.hostflags &= ~SVF_SKIPLOCALHOST; // setup hostflags if( cl->local_weapons ) { sv.hostflags |= SVF_SKIPLOCALHOST; } // reset cameras each frame cl->num_cameras = 0; } svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, &clientpvs, &clientphs ); if( !clientpvs ) fullvis = true; for( e = 1; e < svgame.numEntities; e++ ) { ent = EDICT_NUM( e ); if( ent->free ) continue; // don't double add an entity through portals (already added) // HACHACK: use pushmsec to keep net_framenum if( ent->v.pushmsec == sv.net_framenum ) continue; if( ent->v.effects & EF_REQUEST_PHS ) pset = clientphs; else pset = clientpvs; state = &ents->entities[ents->num_entities]; netclient = SV_ClientFromEdict( ent, true ); player = ( netclient != NULL ); // add entity to the net packet if( svgame.dllFuncs.pfnAddToFullPack( state, e, ent, pClient, sv.hostflags, player, pset )) { // to prevent adds it twice through portals ent->v.pushmsec = sv.net_framenum; if( netclient && netclient->modelindex ) // apply custom model if present state->modelindex = netclient->modelindex; if( SV_IsValidEdict( ent->v.aiment ) && ( ent->v.aiment->v.effects & EF_MERGE_VISIBILITY )) { if( cl != NULL && cl->num_cameras < MAX_CAMERAS ) { cl->cameras[cl->num_cameras] = ent->v.aiment; cl->num_cameras++; } } // if we are full, silently discard entities if( ents->num_entities < MAX_VISIBLE_PACKET ) { ents->num_entities++; // entity accepted c_fullsend++; // debug counter } else { // visibility list is full MsgDev( D_ERROR, "too many entities in visible packet list\n" ); break; } } if( fullvis ) continue; // portal ents will be added anyway, ignore recursion // if its a portal entity, add everything visible from its camera position if( !( sv.hostflags & SVF_PORTALPASS ) && ent->v.effects & EF_MERGE_VISIBILITY ) { sv.hostflags |= SVF_PORTALPASS; SV_AddEntitiesToPacket( ent, pClient, frame, ents ); sv.hostflags &= ~SVF_PORTALPASS; } } }
/* ============= SV_Physics_Compound a glue two entities together ============= */ void SV_Physics_Compound( edict_t *ent ) { edict_t *parent; // regular thinking if( !SV_RunThink( ent )) return; parent = ent->v.aiment; if( !SV_IsValidEdict( parent )) { MsgDev( D_ERROR, "%s have MOVETYPE_COMPOUND with no corresponding ent!", SV_ClassName( ent )); ent->v.movetype = MOVETYPE_NONE; return; } if( ent->v.solid != SOLID_TRIGGER ) ent->v.solid = SOLID_NOT; switch( parent->v.movetype ) { case MOVETYPE_PUSH: case MOVETYPE_PUSHSTEP: break; default: return; } // not initialized ? if( ent->v.ltime == 0.0f ) { VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); ent->v.ltime = host.frametime; return; } if( !VectorCompare( parent->v.origin, ent->v.oldorigin ) || !VectorCompare( parent->v.angles, ent->v.avelocity )) { matrix4x4 start_l, end_l, temp_l, child; // create parent old position Matrix4x4_CreateFromEntity( temp_l, ent->v.avelocity, ent->v.oldorigin, 1.0f ); Matrix4x4_Invert_Simple( start_l, temp_l ); // create parent actual position Matrix4x4_CreateFromEntity( end_l, parent->v.angles, parent->v.origin, 1.0f ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; // create child actual position Matrix4x4_CreateFromEntity( child, ent->v.angles, ent->v.origin, 1.0f ); // transform child from start to end Matrix4x4_ConcatTransforms( temp_l, start_l, child ); Matrix4x4_ConcatTransforms( child, end_l, temp_l ); // create child final position Matrix4x4_ConvertToEntity( child, ent->v.angles, ent->v.origin ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; } // notsolid ents never touch triggers SV_LinkEdict( ent, (ent->v.solid == SOLID_NOT) ? false : true ); // shuffle states VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); }
/* ============= 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 *ground; SV_CheckWater( ent ); // regular thinking if( !SV_RunThink( ent )) return; ground = ent->v.groundentity; if( ent->v.velocity[2] > 0.0f || !SV_IsValidEdict( ground ) || ground->v.flags & (FL_MONSTER|FL_CLIENT) || svgame.globals->changelevel ) { ent->v.flags &= ~FL_ONGROUND; } // if on ground and not moving, return. if( ent->v.flags & FL_ONGROUND && VectorIsNull( ent->v.velocity )) { VectorClear( ent->v.avelocity ); if( VectorIsNull( ent->v.basevelocity )) return; // at rest } SV_CheckVelocity( ent ); // add gravity switch( ent->v.movetype ) { case MOVETYPE_FLY: case MOVETYPE_FLYMISSILE: case MOVETYPE_BOUNCEMISSILE: break; default: SV_AddGravity( ent ); break; } // move angles (with friction) switch( ent->v.movetype ) { case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_AngularMove( ent, host.frametime, ent->v.friction ); break; default: SV_AngularMove( ent, host.frametime, 0.0f ); break; } // move origin // Base velocity is not properly accounted for since this entity will move again // after the bounce without taking it into account VectorAdd( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); VectorScale( ent->v.velocity, host.frametime, move ); VectorSubtract( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); trace = SV_PushEntity( ent, move, vec3_origin, NULL ); if( ent->free ) return; SV_CheckVelocity( ent ); if( trace.allsolid ) { // entity is trapped in another solid VectorClear( ent->v.avelocity ); VectorClear( ent->v.velocity ); return; } if( trace.fraction == 1.0f ) { SV_CheckWaterTransition( ent ); return; } if( ent->v.movetype == MOVETYPE_BOUNCE ) backoff = 2.0f - ent->v.friction; else if( ent->v.movetype == MOVETYPE_BOUNCEMISSILE ) backoff = 2.0f; else backoff = 1.0f; SV_ClipVelocity( ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff ); // stop if on ground if( trace.plane.normal[2] > 0.7f ) { float vel; VectorAdd( ent->v.velocity, ent->v.basevelocity, move ); vel = DotProduct( move, move ); if( ent->v.velocity[2] < sv_gravity->value * host.frametime ) { // we're rolling on the ground, add static friction. ent->v.groundentity = trace.ent; ent->v.flags |= FL_ONGROUND; ent->v.velocity[2] = 0.0f; } if( vel < 900.0f || ( ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE )) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; VectorClear( ent->v.avelocity ); VectorClear( ent->v.velocity ); } else { VectorScale( ent->v.velocity, (1.0f - trace.fraction) * host.frametime * 0.9f, move ); VectorMA( move, (1.0f - trace.fraction) * host.frametime * 0.9f, ent->v.basevelocity, move ); trace = SV_PushEntity( ent, move, vec3_origin, NULL ); if( ent->free ) return; } } // check for in water SV_CheckWaterTransition( ent ); }
/* ==================== SV_AddLinksToPmove collect solid entities ==================== */ void SV_AddLinksToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3_t pmove_maxs ) { link_t *l, *next; edict_t *check, *pl; vec3_t mins, maxs; physent_t *pe; pl = EDICT_NUM( svgame.pmove->player_index + 1 ); //ASSERT( SV_IsValidEdict( pl )); if( !SV_IsValidEdict( pl ) ) { MsgDev( D_ERROR, "SV_AddLinksToPmove: you have broken clients!\n"); return; } // touch linked edicts for( l = node->solid_edicts.next; l != &node->solid_edicts; l = next ) { next = l->next; check = EDICT_FROM_AREA( l ); if( check->v.groupinfo != 0 ) { if(( !svs.groupop && (check->v.groupinfo & pl->v.groupinfo ) == 0) || ( svs.groupop == 1 && ( check->v.groupinfo & pl->v.groupinfo ) != 0 )) continue; } if( ( ( check->v.owner > 0) && check->v.owner == pl ) || check->v.solid == SOLID_TRIGGER ) continue; // player or player's own missile if( svgame.pmove->numvisent < MAX_PHYSENTS ) { pe = &svgame.pmove->visents[svgame.pmove->numvisent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numvisent++; } if( check->v.solid == SOLID_NOT && ( check->v.skin == CONTENTS_NONE || check->v.modelindex == 0 )) continue; // ignore monsterclip brushes if(( check->v.flags & FL_MONSTERCLIP ) && check->v.solid == SOLID_BSP ) continue; if( check == pl ) continue; // himself if( !sv_corpse_solid->value ) { if((( check->v.flags & FL_CLIENT ) && check->v.health <= 0 ) || check->v.deadflag == DEAD_DEAD ) continue; // dead body } if( VectorIsNull( check->v.size )) continue; VectorCopy( check->v.absmin, mins ); VectorCopy( check->v.absmax, maxs ); if( check->v.flags & FL_CLIENT ) { // trying to get interpolated values if( svs.currentPlayer ) SV_GetTrueMinMax( svs.currentPlayer, ( NUM_FOR_EDICT( check ) - 1), mins, maxs ); } if( !BoundsIntersect( pmove_mins, pmove_maxs, mins, maxs )) continue; if( svgame.pmove->numphysent < MAX_PHYSENTS ) { pe = &svgame.pmove->physents[svgame.pmove->numphysent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numphysent++; } } // recurse down both sides if( node->axis == -1 ) return; if( pmove_maxs[node->axis] > node->dist ) SV_AddLinksToPmove( node->children[0], pmove_mins, pmove_maxs ); if( pmove_mins[node->axis] < node->dist ) SV_AddLinksToPmove( node->children[1], pmove_mins, pmove_maxs ); }
/* ============= SV_Physics_Step 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_Physics_Step( edict_t *ent ) { qboolean inwater; qboolean wasonground; qboolean wasonmover; vec3_t mins, maxs; vec3_t point; trace_t trace; int x, y; SV_WaterMove( ent ); SV_CheckVelocity( ent ); wasonground = (ent->v.flags & FL_ONGROUND); wasonmover = SV_CheckMover( ent ); inwater = SV_CheckWater( ent ); if( ent->v.flags & FL_FLOAT && ent->v.waterlevel > 0 ) { float buoyancy = SV_Submerged( ent ) * ent->v.skin * host.frametime; SV_AddGravity( ent ); ent->v.velocity[2] += buoyancy; } if( !wasonground && !( ent->v.flags & FL_FLY ) && (!( ent->v.flags & FL_SWIM ) || ent->v.waterlevel <= 0 )) { if( !inwater ) SV_AddGravity( ent ); } if( !VectorIsNull( ent->v.velocity ) || !VectorIsNull( ent->v.basevelocity )) { ent->v.flags &= ~FL_ONGROUND; if(( wasonground || wasonmover ) && ( ent->v.health > 0 || SV_CheckBottom( ent, MOVE_NORMAL ))) { float *vel = ent->v.velocity; float control, speed, newspeed; float friction; speed = sqrt(( vel[0] * vel[0] ) + ( vel[1] * vel[1] )); // DotProduct2D if( speed ) { friction = sv_friction->value * ent->v.friction; // factor ent->v.friction = 1.0f; // g-cont. ??? if( wasonmover ) friction *= 0.5f; // add a little friction control = (speed < sv_stopspeed->value) ? sv_stopspeed->value : speed; newspeed = speed - (host.frametime * control * friction); if( newspeed < 0 ) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; } } VectorAdd( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); SV_FlyMove( ent, host.frametime, NULL ); if( ent->free ) return; SV_CheckVelocity( ent ); VectorSubtract( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); VectorAdd( ent->v.origin, ent->v.mins, mins ); VectorAdd( ent->v.origin, ent->v.maxs, maxs ); point[2] = mins[2] - 1.0f; for( x = 0; x <= 1; x++ ) { if( ent->v.flags & FL_ONGROUND ) break; for( y = 0; y <= 1; y++ ) { point[0] = x ? maxs[0] : mins[0]; point[1] = y ? maxs[1] : mins[1]; trace = SV_Move( point, vec3_origin, vec3_origin, point, MOVE_NORMAL, ent ); if( trace.startsolid ) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; ent->v.friction = 1.0f; break; } } } SV_LinkEdict( ent, true ); } else { if( svgame.globals->force_retouch != 0 ) { trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent ); // hentacle impact code if(( trace.fraction < 1.0f || trace.startsolid ) && SV_IsValidEdict( trace.ent )) { SV_Impact( ent, trace.ent, &trace ); if( ent->free ) return; } } } if( !SV_RunThink( ent )) return; SV_CheckWaterTransition( ent ); }