/* ================ R_PlaneForMirror Get transformed mirrorplane and entity matrix ================ */ void R_PlaneForMirror( msurface_t *surf, mplane_t *out, matrix4x4 m ) { cl_entity_t *ent; ASSERT( out != NULL ); ent = RI.currententity; // setup mirror plane *out = *surf->plane; if( surf->flags & SURF_PLANEBACK ) { VectorNegate( out->normal, out->normal ); out->dist = -out->dist; } if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles )) { mplane_t tmp; if( !VectorIsNull( ent->angles )) Matrix4x4_CreateFromEntity( m, ent->angles, ent->origin, 1.0f ); else Matrix4x4_CreateFromEntity( m, vec3_origin, ent->origin, 1.0f ); tmp = *out; // transform mirror plane by entity matrix Matrix4x4_TransformPositivePlane( m, tmp.normal, tmp.dist, out->normal, &out->dist ); } else Matrix4x4_LoadIdentity( m ); }
/* ============= CL_ParseReliableEvent ============= */ void CL_ParseReliableEvent( sizebuf_t *msg ) { int event_index; event_args_t nullargs, args; float delay = 0.0f; cl_entity_t *pEnt; Q_memset( &nullargs, 0, sizeof( nullargs )); event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); if( BF_ReadOneBit( msg )) delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); // reliable events not use delta-compression just null-compression MSG_ReadDeltaEvent( msg, &nullargs, &args ); if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) { if( VectorIsNull( args.origin )) VectorCopy( pEnt->curstate.origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( pEnt->curstate.angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( pEnt->curstate.velocity, args.velocity ); } CL_QueueEvent( FEV_RELIABLE|FEV_SERVER, event_index, delay, &args ); }
/* =============== R_StaticEntity Static entity is the brush which has no custom origin and not rotated typically is a func_wall, func_breakable, func_ladder etc =============== */ static qboolean R_StaticEntity( cl_entity_t *ent ) { if( !gl_allow_static->integer ) return false; if( ent->curstate.rendermode != kRenderNormal ) return false; if( ent->model->type != mod_brush ) return false; if( ent->curstate.effects & ( EF_NOREFLECT|EF_REFLECTONLY )) return false; if( ent->curstate.frame || ent->model->flags & MODEL_CONVEYOR ) return false; if( ent->curstate.scale ) // waveheight specified return false; if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles )) return false; return true; }
/* ================ SV_Physics_Pusher ================ */ void SV_Physics_Pusher( edict_t *ent ) { float oldtime, oldtime2; float thinktime, movetime; edict_t *pBlocker; pBlocker = NULL; oldtime = ent->v.ltime; thinktime = ent->v.nextthink; if( thinktime < oldtime + host.frametime ) { movetime = thinktime - oldtime; if( movetime < 0.0f ) movetime = 0.0f; } else movetime = host.frametime; if( movetime ) { if( !VectorIsNull( ent->v.avelocity )) { if( !VectorIsNull( ent->v.velocity )) { pBlocker = SV_PushRotate( ent, movetime ); if( !pBlocker ) { oldtime2 = ent->v.ltime; // reset the local time to what it was before we rotated ent->v.ltime = oldtime; pBlocker = SV_PushMove( ent, movetime ); if( oldtime2 < ent->v.ltime ) ent->v.ltime = oldtime2; } } else { pBlocker = SV_PushRotate( ent, movetime ); } } else { pBlocker = SV_PushMove( ent, movetime ); } } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if( pBlocker ) svgame.dllFuncs.pfnBlocked( ent, pBlocker ); if( thinktime > oldtime && (( ent->v.flags & FL_ALWAYSTHINK ) || thinktime <= ent->v.ltime )) { ent->v.nextthink = 0.0f; svgame.globals->time = sv.time; svgame.dllFuncs.pfnThink( ent ); if( ent->free ) return; } }
/* ============= CL_PlaybackEvent ============= */ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ) { event_args_t args; int invokerIndex = 0; // first check event for out of bounds if( eventindex < 1 || eventindex > MAX_EVENTS ) { MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); return; } if( flags & FEV_SERVER ) { MsgDev( D_WARN, "CL_PlaybackEvent: event with FEV_SERVER flag!\n" ); return; } // check event for precached if( !CL_EventIndex( cl.event_precache[eventindex] )) { MsgDev( D_ERROR, "CL_PlaybackEvent: event %i was not precached\n", eventindex ); return; } flags |= FEV_CLIENT; // it's a client event flags &= ~(FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL); if( delay < 0.0f ) delay = 0.0f; // fixup negative delays invokerIndex = cl.playernum + 1; // only local client can issue client events args.flags = 0; args.entindex = invokerIndex; if( !angles || VectorIsNull( angles )) VectorCopy( cl.refdef.cl_viewangles, args.angles ); else VectorCopy( angles, args.angles ); if( !origin || VectorIsNull( origin )) VectorCopy( cl.frame.local.client.origin, args.origin ); else VectorCopy( origin, args.origin ); VectorCopy( cl.frame.local.client.velocity, args.velocity ); args.ducking = cl.frame.local.playerstate.usehull == 1; args.fparam1 = fparam1; args.fparam2 = fparam2; args.iparam1 = iparam1; args.iparam2 = iparam2; args.bparam1 = bparam1; args.bparam2 = bparam2; CL_QueueEvent( flags, eventindex, delay, &args ); }
/* ==================== CL_AddLinksToPmove collect solid entities ==================== */ void CL_AddLinksToPmove() { cl_entity_t *check; physent_t *pe; int i, solid, idx; for( i = 0; i < cl.frame.num_entities; i++ ) { idx = cls.packet_entities[(cl.frame.first_entity + i) % cls.num_client_entities].number; check = CL_GetEntityByIndex( idx ); // don't add the world and clients here if( !check || check == &clgame.entities[0] ) continue; if( clgame.pmove->numvisent < MAX_PHYSENTS ) { pe = &clgame.pmove->visents[clgame.pmove->numvisent]; if( CL_CopyEntityToPhysEnt( pe, check )) clgame.pmove->numvisent++; } // players will be added later if( check->player ) continue; // can't collide with zeroed hull if( VectorIsNull( check->curstate.mins ) && VectorIsNull( check->curstate.maxs )) continue; solid = check->curstate.solid; if( solid == SOLID_BSP || solid == SOLID_BBOX || solid == SOLID_SLIDEBOX || solid == SOLID_CUSTOM ) { // reserve slots for all the clients if( clgame.pmove->numphysent < ( MAX_PHYSENTS - cl.maxclients )) { pe = &clgame.pmove->physents[clgame.pmove->numphysent]; if( CL_CopyEntityToPhysEnt( pe, check )) clgame.pmove->numphysent++; } } else if( solid == SOLID_NOT && check->curstate.skin != CONTENTS_NONE ) { if( clgame.pmove->nummoveent < MAX_MOVEENTS ) { pe = &clgame.pmove->moveents[clgame.pmove->nummoveent]; if( CL_CopyEntityToPhysEnt( pe, check )) clgame.pmove->nummoveent++; } } } }
/* ============= 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; }
/* ================== PM_TraceTexture find the face where the traceline hit assume physentity is valid ================== */ msurface_t *PM_TraceSurface( physent_t *pe, vec3_t start, vec3_t end ) { matrix4x4 matrix; model_t *bmodel; hull_t *hull; vec3_t start_l, end_l; vec3_t offset; bmodel = pe->model; if( !bmodel || bmodel->type != mod_brush ) return NULL; hull = &pe->model->hulls[0]; VectorSubtract( hull->clip_mins, vec3_origin, offset ); VectorAdd( offset, pe->origin, offset ); VectorSubtract( start, offset, start_l ); VectorSubtract( end, offset, end_l ); // rotate start and end into the models frame of reference if( !VectorIsNull( pe->angles )) { Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, start, start_l ); Matrix4x4_VectorITransform( matrix, end, end_l ); } return PM_RecursiveSurfCheck( bmodel, &bmodel->nodes[hull->firstclipnode], start_l, end_l ); }
//============================================================================ static void SV_Physics_Entity( edict_t *ent ) { // user dll can override movement type (Xash3D extension) if( svgame.physFuncs.SV_PhysicsEntity && svgame.physFuncs.SV_PhysicsEntity( ent )) return; // overrided SV_UpdateBaseVelocity( ent ); if(!( ent->v.flags & FL_BASEVELOCITY ) && !VectorIsNull( ent->v.basevelocity )) { // Apply momentum (add in half of the previous frame of velocity first) VectorMA( ent->v.velocity, 1.0f + (host.frametime * 0.5f), ent->v.basevelocity, ent->v.velocity ); VectorClear( ent->v.basevelocity ); } ent->v.flags &= ~FL_BASEVELOCITY; if( svgame.globals->force_retouch != 0.0f ) { // force retouch even for stationary SV_LinkEdict( ent, true ); } switch( ent->v.movetype ) { case MOVETYPE_NONE: SV_Physics_None( ent ); break; case MOVETYPE_NOCLIP: SV_Physics_Noclip( ent ); break; case MOVETYPE_FOLLOW: SV_Physics_Follow( ent ); break; case MOVETYPE_COMPOUND: SV_Physics_Compound( ent ); break; case MOVETYPE_STEP: case MOVETYPE_PUSHSTEP: SV_Physics_Step( ent ); break; case MOVETYPE_FLY: case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: case MOVETYPE_FLYMISSILE: case MOVETYPE_BOUNCEMISSILE: SV_Physics_Toss( ent ); break; case MOVETYPE_PUSH: SV_Physics_Pusher( ent ); break; case MOVETYPE_WALK: Host_Error( "SV_Physics: bad movetype %i\n", ent->v.movetype ); break; } // g-cont. don't alow free entities during loading because // this produce a corrupted baselines if( sv.state == ss_active && ent->v.flags & FL_KILLME ) SV_FreeEdict( ent ); }
/* ============= CL_TruePointContents ============= */ int CL_TruePointContents( const vec3_t p ) { int i, contents; int oldhull; hull_t *hull; vec3_t test, offset; physent_t *pe; // sanity check if( !p ) return CONTENTS_NONE; oldhull = clgame.pmove->usehull; // get base contents from world contents = PM_HullPointContents( &cl.worldmodel->hulls[0], 0, p ); for( i = 0; i < clgame.pmove->nummoveent; i++ ) { pe = &clgame.pmove->moveents[i]; if( pe->solid != SOLID_NOT ) // disabled ? continue; // only brushes can have special contents if( !pe->model || pe->model->type != mod_brush ) continue; // check water brushes accuracy clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = oldhull; // offset the test point appropriately for this hull. VectorSubtract( p, offset, test ); if( (pe->model->flags & MODEL_HAS_ORIGIN) && !VectorIsNull( pe->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, p, test ); }; // test hull for intersection with this model if( PM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY ) continue; // compare contents ranking if( RankForContents( pe->skin ) > RankForContents( contents )) contents = pe->skin; // new content has more priority }; return contents; }
/* ============= CL_WaterEntity ============= */ int CL_WaterEntity( const float *rgflPos ) { physent_t *pe; hull_t *hull; vec3_t test, offset; int i, oldhull; if( !rgflPos ) return -1; oldhull = clgame.pmove->usehull; for( i = 0; i < clgame.pmove->nummoveent; i++ ) { pe = &clgame.pmove->moveents[i]; if( pe->solid != SOLID_NOT ) // disabled ? continue; // only brushes can have special contents if( !pe->model || pe->model->type != mod_brush ) continue; // check water brushes accuracy clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = oldhull; // offset the test point appropriately for this hull. VectorSubtract( rgflPos, offset, test ); if( (pe->model->flags & MODEL_HAS_ORIGIN) && !VectorIsNull( pe->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, rgflPos, test ); }; // test hull for intersection with this model if( PM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY ) continue; // found water entity return pe->info; } return -1; }
// // sound engine implementation // qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin, float *pradius ) { cl_entity_t *ent; qboolean valid_origin; ASSERT( origin != NULL ); if( entnum == 0 ) return true; // static sound if(( entnum - 1 ) == cl.playernum ) { VectorCopy( cl.frame.local.client.origin, origin ); return true; } valid_origin = VectorIsNull( origin ) ? false : true; ent = CL_GetEntityByIndex( entnum ); // entity is not present on the client but has valid origin if( !ent || !ent->index ) return valid_origin; if( ent->curstate.messagenum == 0 ) { // entity is never has updates on the client // so we should use static origin instead return valid_origin; } #if 0 // uncomment this if you want enable additional check by PVS if( ent->curstate.messagenum != cl.parsecount ) return valid_origin; #endif // setup origin VectorAverage( ent->curstate.mins, ent->curstate.maxs, origin ); VectorAdd( origin, ent->curstate.origin, origin ); // setup radius if( pradius ) { if( ent->model != NULL && ent->model->radius ) *pradius = ent->model->radius; else *pradius = RadiusFromBounds( ent->curstate.mins, ent->curstate.maxs ); } return true; }
static float pfnTraceModel( physent_t *pe, float *start, float *end, trace_t *trace ) { int old_usehull; vec3_t start_l, end_l; vec3_t offset, temp; qboolean rotated; matrix4x4 matrix; hull_t *hull; old_usehull = clgame.pmove->usehull; clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = old_usehull; if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) rotated = true; else rotated = false; if( rotated ) { Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, start, start_l ); Matrix4x4_VectorITransform( matrix, end, end_l ); } else { VectorSubtract( start, offset, start_l ); VectorSubtract( end, offset, end_l ); } SV_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, trace ); trace->ent = NULL; if( rotated ) { VectorCopy( trace->plane.normal, temp ); Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist ); } VectorLerp( start, trace->fraction, end, trace->endpos ); return trace->fraction; }
/* ============ SV_FlyMove The basic solid body movement clip that slides along multiple planes *steptrace - if not NULL, the trace results of any vertical wall hit will be stored Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop ============ */ int SV_FlyMove( edict_t *ent, float time, trace_t *steptrace ) { int i, j, numplanes, bumpcount, blocked; vec3_t dir, end, planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; float d, time_left, allFraction; trace_t trace; blocked = 0; VectorCopy( ent->v.velocity, original_velocity ); VectorCopy( ent->v.velocity, primal_velocity ); numplanes = 0; allFraction = 0.0f; time_left = time; for( bumpcount = 0; bumpcount < MAX_CLIP_PLANES - 1; bumpcount++ ) { if( VectorIsNull( ent->v.velocity )) break; VectorMA( ent->v.origin, time_left, ent->v.velocity, end ); trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent ); allFraction += trace.fraction; if( trace.allsolid ) { // entity is trapped in another solid VectorClear( ent->v.velocity ); return 4; } if( trace.fraction > 0.0f ) { // actually covered some distance VectorCopy( trace.endpos, ent->v.origin ); VectorCopy( ent->v.velocity, original_velocity ); numplanes = 0; } if( trace.fraction == 1.0f ) break; // moved the entire distance if( !trace.ent ) MsgDev( D_ERROR, "SV_FlyMove: trace.ent == NULL\n" ); if( trace.plane.normal[2] > 0.7f ) { blocked |= 1; // floor if( trace.ent->v.solid == SOLID_BSP || trace.ent->v.solid == SOLID_SLIDEBOX || trace.ent->v.movetype == MOVETYPE_PUSHSTEP || (trace.ent->v.flags & FL_CLIENT)) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; } } if( trace.plane.normal[2] == 0.0f ) { blocked |= 2; // step if( steptrace ) *steptrace = trace; // save for player extrafriction } // run the impact function SV_Impact( ent, trace.ent, &trace ); // break if removed by the impact function if( ent->free ) break; time_left -= time_left * trace.fraction; // clipped to another plane if( numplanes >= MAX_CLIP_PLANES ) { // this shouldn't really happen VectorClear( ent->v.velocity ); break; } VectorCopy( trace.plane.normal, planes[numplanes] ); numplanes++; // modify original_velocity so it parallels all of the clip planes for( i = 0; i < numplanes; i++ ) { SV_ClipVelocity( original_velocity, planes[i], new_velocity, 1.0f ); for( j = 0; j < numplanes; j++ ) { if( j != i ) { if( DotProduct( new_velocity, planes[j] ) < 0.0f ) break; // not ok } } if( j == numplanes ) break; } if( i != numplanes ) { // go along this plane VectorCopy( new_velocity, ent->v.velocity ); } else { // go along the crease if( numplanes != 2 ) { VectorClear( ent->v.velocity ); break; } CrossProduct( planes[0], planes[1], dir ); d = DotProduct( dir, ent->v.velocity ); VectorScale( dir, d, ent->v.velocity ); } // if current velocity is against the original velocity, // stop dead to avoid tiny occilations in sloping corners if( DotProduct( ent->v.velocity, primal_velocity ) <= 0.0f ) { VectorClear( ent->v.velocity ); break; } } if( allFraction == 0.0f ) VectorClear( ent->v.velocity ); return blocked; }
// Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, vec3_t saxis, float scale ) { decalinfo_t decalInfo; hull_t *hull; cl_entity_t *ent = NULL; model_t *model = NULL; int width, height; if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES ) { MsgDev( D_ERROR, "Decal has invalid texture!\n" ); return; } if( entityIndex > 0 ) { ent = CL_GetEntityByIndex( entityIndex ); if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else if( ent != NULL ) model = Mod_Handle( ent->curstate.modelindex ); else return; } else if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else model = cl.worldmodel; if( !model ) return; if( model->type != mod_brush ) { MsgDev( D_ERROR, "Decals must hit mod_brush!\n" ); return; } decalInfo.m_pModel = model; hull = &model->hulls[0]; // always use #0 hull if( ent && !( flags & FDECAL_LOCAL_SPACE )) { vec3_t pos_l; // transform decal position in local bmodel space if( !VectorIsNull( ent->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, ent->angles, ent->origin, 1.0f ); Matrix4x4_VectorITransform( matrix, pos, pos_l ); } else { VectorSubtract( pos, ent->origin, pos_l ); } VectorCopy( pos_l, decalInfo.m_Position ); flags |= FDECAL_LOCAL_SPACE; // decal position moved into local space } else { // pass position in global VectorCopy( pos, decalInfo.m_Position ); } // deal with the s axis if one was passed in if( saxis ) { flags |= FDECAL_USESAXIS; VectorCopy( saxis, decalInfo.m_SAxis ); } // this decal must use landmark for correct transition if(!( model->flags & MODEL_HAS_ORIGIN )) { flags |= FDECAL_USE_LANDMARK; } // more state used by R_DecalNode() decalInfo.m_iTexture = textureIndex; decalInfo.m_Entity = entityIndex; decalInfo.m_Flags = flags; R_GetDecalDimensions( textureIndex, &width, &height ); decalInfo.m_Size = width >> 1; if(( height >> 1 ) > decalInfo.m_Size ) decalInfo.m_Size = height >> 1; decalInfo.m_scale = bound( MIN_DECAL_SCALE, scale, MAX_DECAL_SCALE ); // compute the decal dimensions in world space decalInfo.m_decalWidth = width / decalInfo.m_scale; decalInfo.m_decalHeight = height / decalInfo.m_scale; R_DecalNode( model, &model->nodes[hull->firstclipnode], &decalInfo ); }
/* ==================== 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 ); }
/* ================= R_FindBmodelMirrors Check all bmodel surfaces and make personal mirror chain ================= */ void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity ) { mextrasurf_t *extrasurf; vec3_t mins, maxs; msurface_t *psurf; model_t *clmodel; qboolean rotated; int i, clipFlags; clmodel = e->model; if( static_entity ) { Matrix4x4_LoadIdentity( RI.objectMatrix ); if( R_CullBox( clmodel->mins, clmodel->maxs, RI.clipFlags )) return; VectorCopy( RI.cullorigin, tr.modelorg ); clipFlags = RI.clipFlags; } else { if( !VectorIsNull( e->angles )) { for( i = 0; i < 3; i++ ) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } rotated = true; } else { VectorAdd( e->origin, clmodel->mins, mins ); VectorAdd( e->origin, clmodel->maxs, maxs ); rotated = false; } if( R_CullBox( mins, maxs, RI.clipFlags )) return; if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles )) { if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f ); else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f ); } else Matrix4x4_LoadIdentity( RI.objectMatrix ); e->visframe = tr.framecount; // visible if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); clipFlags = 0; } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) { if(!( psurf->flags & SURF_REFLECT )) continue; if( R_CullSurface( psurf, clipFlags )) continue; extrasurf = SURF_INFO( psurf, RI.currentmodel ); extrasurf->mirrorchain = tr.mirror_entities[tr.num_mirror_entities].chain; tr.mirror_entities[tr.num_mirror_entities].chain = extrasurf; } // store new mirror entity if( !static_entity && tr.mirror_entities[tr.num_mirror_entities].chain != NULL ) { tr.mirror_entities[tr.num_mirror_entities].ent = RI.currententity; tr.num_mirror_entities++; } }
/* ================ R_DrawMirrors Draw all viewpasess from mirror position Mirror textures will be drawn in normal pass ================ */ void R_DrawMirrors( void ) { ref_instance_t oldRI; mplane_t plane; msurface_t *surf, *surf2; int i, oldframecount; mextrasurf_t *es, *tmp, *mirrorchain; vec3_t forward, right, up; vec3_t origin, angles; matrix4x4 mirrormatrix; cl_entity_t *e; model_t *m; float d; if( !tr.num_mirror_entities ) return; // mo mirrors for this frame oldRI = RI; // make refinst backup oldframecount = tr.framecount; for( i = 0; i < tr.num_mirror_entities; i++ ) { mirrorchain = tr.mirror_entities[i].chain; for( es = mirrorchain; es != NULL; es = es->mirrorchain ) { RI.currententity = e = tr.mirror_entities[i].ent; RI.currentmodel = m = RI.currententity->model; surf = INFO_SURF( es, m ); ASSERT( RI.currententity != NULL ); ASSERT( RI.currentmodel != NULL ); // NOTE: copy mirrortexture and mirrormatrix from another surfaces // from this entity\world that has same planes and reduce number of viewpasses // make sure what we have one pass at least if( es != mirrorchain ) { for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain ) { surf2 = INFO_SURF( tmp, m ); if( !tmp->mirrortexturenum ) continue; // not filled? if( surf->plane->dist != surf2->plane->dist ) continue; if( !VectorCompare( surf->plane->normal, surf2->plane->normal )) continue; // found surface with same plane! break; } if( tmp != es && tmp && tmp->mirrortexturenum ) { // just copy reflection texture from surface with same plane Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix ); es->mirrortexturenum = tmp->mirrortexturenum; continue; // pass skiped } } R_PlaneForMirror( surf, &plane, mirrormatrix ); d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist ); VectorMA( RI.vieworg, d, plane.normal, origin ); d = -2.0f * DotProduct( RI.vforward, plane.normal ); VectorMA( RI.vforward, d, plane.normal, forward ); VectorNormalize( forward ); d = -2.0f * DotProduct( RI.vright, plane.normal ); VectorMA( RI.vright, d, plane.normal, right ); VectorNormalize( right ); d = -2.0f * DotProduct( RI.vup, plane.normal ); VectorMA( RI.vup, d, plane.normal, up ); VectorNormalize( up ); VectorsAngles( forward, right, up, angles ); angles[ROLL] = -angles[ROLL]; RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF; RI.clipPlane = plane; RI.clipFlags |= ( 1<<5 ); RI.frustum[5] = plane; RI.frustum[5].signbits = SignbitsForPlane( RI.frustum[5].normal ); RI.frustum[5].type = PLANE_NONAXIAL; RI.refdef.viewangles[0] = anglemod( angles[0] ); RI.refdef.viewangles[1] = anglemod( angles[1] ); RI.refdef.viewangles[2] = anglemod( angles[2] ); VectorCopy( origin, RI.refdef.vieworg ); VectorCopy( origin, RI.cullorigin ); // put pvsorigin before the mirror plane to avoid get full visibility on world mirrors if( RI.currententity == clgame.entities ) { VectorMA( es->origin, 1.0f, plane.normal, origin ); } else { Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin ); VectorMA( origin, 1.0f, plane.normal, origin ); } VectorCopy( origin, RI.pvsorigin ); // combine two leafs from client and mirror views r_viewleaf = Mod_PointInLeaf( oldRI.pvsorigin, cl.worldmodel->nodes ); r_viewleaf2 = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ); if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { // allow screen size RI.viewport[2] = bound( 96, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 72, RI.viewport[3], 768 ); } else { RI.viewport[2] = NearestPOW( RI.viewport[2], true ); RI.viewport[3] = NearestPOW( RI.viewport[3], true ); RI.viewport[2] = bound( 128, RI.viewport[2], 1024 ); RI.viewport[3] = bound( 64, RI.viewport[3], 512 ); } tr.framecount++; R_RenderScene( &RI.refdef ); r_stats.c_mirror_passes++; es->mirrortexturenum = R_AllocateMirrorTexture(); // create personal projection matrix for mirror if( VectorIsNull( e->origin ) && VectorIsNull( e->angles )) { Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix ); } else { Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix ); Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix ); } RI = oldRI; // restore ref instance } // clear chain for this entity for( es = mirrorchain; es != NULL; ) { tmp = es->mirrorchain; es->mirrorchain = NULL; es = tmp; } tr.mirror_entities[i].chain = NULL; // done tr.mirror_entities[i].ent = NULL; } r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame tr.framecount = oldframecount; // restore real framecount tr.num_mirror_entities = 0; tr.num_mirrors_used = 0; }
/* ============= 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 ); }
/* ============= CL_ParseEvent ============= */ void CL_ParseEvent( sizebuf_t *msg ) { int event_index; int i, num_events; int packet_ent; event_args_t nullargs, args; qboolean has_update; entity_state_t *state; cl_entity_t *pEnt; float delay; Q_memset( &nullargs, 0, sizeof( nullargs )); num_events = BF_ReadUBitLong( msg, 5 ); // parse events queue for( i = 0 ; i < num_events; i++ ) { event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); Q_memset( &args, 0, sizeof( args )); has_update = false; if( BF_ReadOneBit( msg )) { packet_ent = BF_ReadUBitLong( msg, MAX_ENTITY_BITS ); if( BF_ReadOneBit( msg )) { MSG_ReadDeltaEvent( msg, &nullargs, &args ); has_update = true; } } else packet_ent = -1; if( packet_ent != -1 ) state = &cls.packet_entities[(cl.frame.first_entity+packet_ent)%cls.num_client_entities]; else state = NULL; // it's a client. Override some params if( args.entindex >= 1 && args.entindex <= cl.maxclients ) { if(( args.entindex - 1 ) == cl.playernum ) { if( state && !CL_IsPredicted( )) { // restore viewangles from angles args.angles[PITCH] = -state->angles[PITCH] * 3; args.angles[YAW] = state->angles[YAW]; args.angles[ROLL] = 0; // no roll } else { // get the predicted angles VectorCopy( cl.refdef.cl_viewangles, args.angles ); } VectorCopy( cl.frame.local.client.origin, args.origin ); VectorCopy( cl.frame.local.client.velocity, args.velocity ); } else if( state ) { // restore viewangles from angles args.angles[PITCH] = -state->angles[PITCH] * 3; args.angles[YAW] = state->angles[YAW]; args.angles[ROLL] = 0; // no roll // if we restore origin and velocity everytime, why don't do it here also? if( VectorIsNull( args.origin )) VectorCopy( state->origin, args.origin ); if( VectorIsNull( args.velocity )) VectorCopy( state->velocity, args.velocity ); } } else if( state ) { if( VectorIsNull( args.origin )) VectorCopy( state->origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( state->angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( state->velocity, args.velocity ); } else if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) { if( VectorIsNull( args.origin )) VectorCopy( pEnt->curstate.origin, args.origin ); if( VectorIsNull( args.angles )) VectorCopy( pEnt->curstate.angles, args.angles ); if( VectorIsNull( args.velocity )) VectorCopy( pEnt->curstate.velocity, args.velocity ); } if( BF_ReadOneBit( msg )) delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); else delay = 0.0f; // g-cont. should we need find the event with same index? CL_QueueEvent( 0, event_index, delay, &args ); } }
/* ============= 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 ); }
/* =========== SV_RunCmd =========== */ void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed ) { usercmd_t lastcmd; edict_t *clent, *touch; double frametime; int i, oldmsec; pmtrace_t *pmtrace; trace_t trace; vec3_t oldvel; clent = cl->edict; if( cl->next_movetime > host.realtime ) { cl->last_movetime += ( ucmd->msec * 0.001f ); return; } cl->next_movetime = 0.0; // chop up very long commands if( ucmd->msec > 50 ) { lastcmd = *ucmd; oldmsec = ucmd->msec; lastcmd.msec = oldmsec / 2; SV_RunCmd( cl, &lastcmd, random_seed ); lastcmd.msec = oldmsec / 2 + (oldmsec & 1); // give them back thier msec. lastcmd.impulse = 0; SV_RunCmd( cl, &lastcmd, random_seed ); return; } if( !cl->fakeclient ) { SV_SetupMoveInterpolant( cl ); } lastcmd = *ucmd; svgame.dllFuncs.pfnCmdStart( cl->edict, ucmd, random_seed ); frametime = ucmd->msec * 0.001; cl->timebase += frametime; cl->last_movetime += frametime; PM_CheckMovingGround( clent, frametime ); VectorCopy( clent->v.v_angle, svgame.pmove->oldangles ); // save oldangles if( !clent->v.fixangle ) VectorCopy( ucmd->viewangles, clent->v.v_angle ); VectorClear( clent->v.clbasevelocity ); // copy player buttons clent->v.button = ucmd->buttons; if( ucmd->impulse ) clent->v.impulse = ucmd->impulse; if( ucmd->impulse == 204 ) { // force client.dll update SV_RefreshUserinfo(); } svgame.globals->time = cl->timebase; svgame.dllFuncs.pfnPlayerPreThink( clent ); SV_PlayerRunThink( clent, frametime, cl->timebase ); // If conveyor, or think, set basevelocity, then send to client asap too. if( !VectorIsNull( clent->v.basevelocity )) VectorCopy( clent->v.basevelocity, clent->v.clbasevelocity ); // setup playermove state SV_SetupPMove( svgame.pmove, cl, ucmd, cl->physinfo ); // motor! svgame.dllFuncs.pfnPM_Move( svgame.pmove, true ); // copy results back to client SV_FinishPMove( svgame.pmove, cl ); // link into place and touch triggers SV_LinkEdict( clent, true ); VectorCopy( clent->v.velocity, oldvel ); // save velocity // touch other objects for( i = 0; i < svgame.pmove->numtouch; i++ ) { // never touch the objects when "playersonly" is active if( i == MAX_PHYSENTS || ( sv.hostflags & SVF_PLAYERSONLY )) break; pmtrace = &svgame.pmove->touchindex[i]; touch = EDICT_NUM( svgame.pmove->physents[pmtrace->ent].info ); if( touch == clent ) continue; VectorCopy( pmtrace->deltavelocity, clent->v.velocity ); SV_ConvertPMTrace( &trace, pmtrace, touch ); SV_Impact( touch, clent, &trace ); } // restore velocity VectorCopy( oldvel, clent->v.velocity ); svgame.pmove->numtouch = 0; svgame.globals->time = cl->timebase; svgame.globals->frametime = frametime; // run post-think svgame.dllFuncs.pfnPlayerPostThink( clent ); svgame.dllFuncs.pfnCmdEnd( clent ); if( !cl->fakeclient ) { SV_RestoreMoveInterpolant( cl ); } }
int CL_InterpolateModel( cl_entity_t *e ) { position_history_t *ph0, *ph1; vec3_t origin, angles, delta; float t, t1, t2, frac; int i; VectorCopy( e->curstate.origin, e->origin ); VectorCopy( e->curstate.angles, e->angles ); if( e->model == NULL ) return 1; t = cl.time - cl_interp->value; if( !CL_FindInterpolationUpdates( e, t, &ph0, &ph1, NULL )) return 0; if( ph0 == NULL || ph1 == NULL ) return 0; t1 = ph0->animtime; t2 = ph1->animtime; if( t - t2 < 0.0f ) return 0; if( t2 == 0.0f || VectorIsNull( ph1->origin ) && !VectorIsNull( ph0->origin )) { VectorCopy( ph0->origin, e->origin ); VectorCopy( ph0->angles, e->angles ); return 0; } if( t2 == t1 ) { VectorCopy( ph0->origin, e->origin ); VectorCopy( ph0->angles, e->angles ); return 1; } VectorSubtract( ph0->origin, ph1->origin, delta ); frac = (t - t2) / (t1 - t2); if( frac < 0.0f ) return 0; if( frac > 1.0f ) frac = 1.0f; VectorMA( ph1->origin, frac, delta, origin ); for( i = 0; i < 3; i++ ) { float d, ang1, ang2; ang1 = ph0->angles[i]; ang2 = ph1->angles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; angles[i] = ang2 + d * frac; } VectorCopy( origin, e->origin ); VectorCopy( angles, e->angles ); return 1; }
/* ================= FinishBrush produces a final brush based on the buildBrush->sides array and links it to the current entity ================= */ brush_t *FinishBrush( void ) { brush_t *b; // create windings for sides and bounds for brush if( !CreateBrushWindings( buildBrush )) return NULL; // origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity. // after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush if( buildBrush->compileFlags & C_ORIGIN ) { char string[32]; vec3_t size, movedir, origin; if( numEntities == 1 ) { Msg( "Entity %i, Brush %i: origin brushes not allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } // calcualte movedir (Xash 0.4 style) VectorAverage( buildBrush->mins, buildBrush->maxs, origin ); VectorSubtract( buildBrush->maxs, buildBrush->mins, size ); if( size[2] > size[0] && size[2] > size[1] ) VectorSet( movedir, 0.0f, 1.0f, 0.0f ); // x-rotate else if( size[1] > size[2] && size[1] > size[0] ) VectorSet( movedir, 1.0f, 0.0f, 0.0f ); // y-rotate else if( size[0] > size[2] && size[0] > size[1] ) VectorSet( movedir, 0.0f, 0.0f, 1.0f ); // z-rotate else VectorClear( movedir ); // custom movedir #if 0 if( !VectorIsNull( movedir )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)movedir[0], (int)movedir[1], (int)movedir[2] ); SetKeyValue( &entities[numEntities - 1], "movedir", string ); } #endif if(!VectorIsNull( origin )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] ); SetKeyValue( &entities[numEntities - 1], "origin", string ); VectorCopy( origin, entities[numEntities - 1].origin ); } // don't keep this brush return NULL; } // determine if the brush is an area portal if( buildBrush->compileFlags & C_AREAPORTAL ) { if( numEntities != 1 ) { Msg( "Entity %i, Brush %i: areaportals only allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } } AddBrushBevels(); b = CopyBrush( buildBrush ); b->entityNum = mapEnt->mapEntityNum; b->brushNum = entitySourceBrushes; b->original = b; // link opaque brushes to head of list, translucent brushes to end if( b->opaque || mapEnt->lastBrush == NULL ) { b->next = mapEnt->brushes; mapEnt->brushes = b; if( mapEnt->lastBrush == NULL ) mapEnt->lastBrush = b; } else { b->next = NULL; mapEnt->lastBrush->next = b; mapEnt->lastBrush = b; } // link colorMod volume brushes to the entity directly if( b->contentShader != NULL && b->contentShader->colorMod != NULL && b->contentShader->colorMod->type == CM_VOLUME ) { b->nextColorModBrush = mapEnt->colorModBrushes; mapEnt->colorModBrushes = b; } return b; }
/* PM_GroundMove Player is on ground, with no upwards velocity */ void PM_GroundMove (void) { vec3_t start, dest; trace_t trace; vec3_t original, originalvel, down, up, downvel; float downdist, updist; pmove.velocity[2] = 0; if (VectorIsNull(pmove.velocity)) return; // first try just moving to the destination dest[0] = pmove.origin[0] + pmove.velocity[0] * frametime; dest[1] = pmove.origin[1] + pmove.velocity[1] * frametime; dest[2] = pmove.origin[2]; // first try moving directly to the next spot VectorCopy (dest, start); trace = PM_PlayerMove (pmove.origin, dest); if (trace.fraction == 1) { VectorCopy (trace.endpos, pmove.origin); return; } // try sliding forward both on ground and up 16 pixels // take the move that goes farthest VectorCopy (pmove.origin, original); VectorCopy (pmove.velocity, originalvel); // slide move PM_FlyMove (); VectorCopy (pmove.origin, down); VectorCopy (pmove.velocity, downvel); VectorCopy (original, pmove.origin); VectorCopy (originalvel, pmove.velocity); // move up a stair height VectorCopy (pmove.origin, dest); dest[2] += STEPSIZE; trace = PM_PlayerMove (pmove.origin, dest); if (!trace.startsolid && !trace.allsolid) { VectorCopy (trace.endpos, pmove.origin); } // slide move PM_FlyMove (); // press down the stepheight VectorCopy (pmove.origin, dest); dest[2] -= STEPSIZE; trace = PM_PlayerMove (pmove.origin, dest); if (trace.plane.normal[2] < 0.7) goto usedown; if (!trace.startsolid && !trace.allsolid) { VectorCopy (trace.endpos, pmove.origin); } VectorCopy (pmove.origin, up); // decide which one went farther downdist = (down[0] - original[0]) * (down[0] - original[0]) + (down[1] - original[1]) * (down[1] - original[1]); updist = (up[0] - original[0]) * (up[0] - original[0]) + (up[1] - original[1]) * (up[1] - original[1]); if (downdist > updist) { usedown: VectorCopy (down, pmove.origin); VectorCopy (downvel, pmove.velocity); } else // copy z value from slide move pmove.velocity[2] = downvel[2]; // if at a dead stop, retry the move with nudges to get around lips }
/* ============ 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; }
void PR_ExecuteProgram (progs_t *pr, func_t fnum) { dstatement_t *st; dfunction_t *f, *newf; edict_t *ed; int exitdepth; eval_t *ptr; int profile, startprofile; if (!fnum || fnum >= pr->progs->numfunctions) { if (pr->pr_global_struct->self) ED_Print (pr, PROG_TO_EDICT (pr, pr->pr_global_struct->self)); SV_Error ("PR_ExecuteProgram: NULL function"); } f = &pr->pr_functions[fnum]; pr->pr_trace = false; // make a stack frame exitdepth = pr->pr_depth; st = &pr->pr_statements[PR_EnterFunction (pr, f)]; startprofile = profile = 0; while (1) { st++; if (++profile > 1000000) // LordHavoc: increased runaway loop // limit 10x { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "runaway loop error"); } if (pr->pr_trace) PR_PrintStatement (pr, st); switch (st->op) { case OP_ADD_F: OPC->_float = OPA->_float + OPB->_float; break; case OP_ADD_V: OPC->vector[0] = OPA->vector[0] + OPB->vector[0]; OPC->vector[1] = OPA->vector[1] + OPB->vector[1]; OPC->vector[2] = OPA->vector[2] + OPB->vector[2]; break; case OP_SUB_F: OPC->_float = OPA->_float - OPB->_float; break; case OP_SUB_V: OPC->vector[0] = OPA->vector[0] - OPB->vector[0]; OPC->vector[1] = OPA->vector[1] - OPB->vector[1]; OPC->vector[2] = OPA->vector[2] - OPB->vector[2]; break; case OP_MUL_F: OPC->_float = OPA->_float * OPB->_float; break; case OP_MUL_V: OPC->_float = OPA->vector[0] * OPB->vector[0] + OPA->vector[1] * OPB->vector[1] + OPA->vector[2] * OPB->vector[2]; break; case OP_MUL_FV: OPC->vector[0] = OPA->_float * OPB->vector[0]; OPC->vector[1] = OPA->_float * OPB->vector[1]; OPC->vector[2] = OPA->_float * OPB->vector[2]; break; case OP_MUL_VF: OPC->vector[0] = OPB->_float * OPA->vector[0]; OPC->vector[1] = OPB->_float * OPA->vector[1]; OPC->vector[2] = OPB->_float * OPA->vector[2]; break; case OP_DIV_F: OPC->_float = OPA->_float / OPB->_float; break; case OP_BITAND: OPC->_float = (int) OPA->_float & (int) OPB->_float; break; case OP_BITOR: OPC->_float = (int) OPA->_float | (int) OPB->_float; break; case OP_GE: OPC->_float = OPA->_float >= OPB->_float; break; case OP_LE: OPC->_float = OPA->_float <= OPB->_float; break; case OP_GT: OPC->_float = OPA->_float > OPB->_float; break; case OP_LT: OPC->_float = OPA->_float < OPB->_float; break; case OP_AND: OPC->_float = OPA->_float && OPB->_float; break; case OP_OR: OPC->_float = OPA->_float || OPB->_float; break; case OP_NOT_F: OPC->_float = !OPA->_float; break; case OP_NOT_V: OPC->_float = VectorIsNull(OPA->vector); break; case OP_NOT_S: OPC->_float = !OPA->string || !*PR_GetString (pr, OPA->string); break; case OP_NOT_FNC: OPC->_float = !OPA->function; break; case OP_NOT_ENT: OPC->_float = (PROG_TO_EDICT (pr, OPA->edict) == *pr->edicts); break; case OP_EQ_F: OPC->_float = OPA->_float == OPB->_float; break; case OP_EQ_V: OPC->_float = (OPA->vector[0] == OPB->vector[0]) && (OPA->vector[1] == OPB->vector[1]) && (OPA->vector[2] == OPB->vector[2]); break; case OP_EQ_S: OPC->_float = !strcmp (PR_GetString (pr, OPA->string), PR_GetString (pr, OPB->string)); break; case OP_EQ_E: OPC->_float = OPA->_int == OPB->_int; break; case OP_EQ_FNC: OPC->_float = OPA->function == OPB->function; break; case OP_NE_F: OPC->_float = OPA->_float != OPB->_float; break; case OP_NE_V: OPC->_float = (OPA->vector[0] != OPB->vector[0]) || (OPA->vector[1] != OPB->vector[1]) || (OPA->vector[2] != OPB->vector[2]); break; case OP_NE_S: OPC->_float = strcmp (PR_GetString (pr, OPA->string), PR_GetString (pr, OPB->string)); break; case OP_NE_E: OPC->_float = OPA->_int != OPB->_int; break; case OP_NE_FNC: OPC->_float = OPA->function != OPB->function; break; // ================== case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: case OP_STORE_FNC: // pointers OPB->_int = OPA->_int; break; case OP_STORE_V: OPB->vector[0] = OPA->vector[0]; OPB->vector[1] = OPA->vector[1]; OPB->vector[2] = OPA->vector[2]; break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int + 4 > pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an out of bounds edict\n"); return; } if (pr_boundscheck->int_val && (OPB->_int % pr->pr_edict_size < ((byte *) & (*pr->edicts)->v - (byte *) *pr->edicts))) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an engine edict field\n"); return; } ptr = (eval_t *) ((byte *) *pr->edicts + OPB->_int); ptr->_int = OPA->_int; break; case OP_STOREP_V: if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int + 12 > pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an out of bounds edict\n"); return; } ptr = (eval_t *) ((byte *) *pr->edicts + OPB->_int); ptr->vector[0] = OPA->vector[0]; ptr->vector[1] = OPA->vector[1]; ptr->vector[2] = OPA->vector[2]; break; case OP_ADDRESS: if (pr_boundscheck->int_val && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to address an out of bounds edict\n"); return; } if (pr_boundscheck->int_val && (OPA->edict == 0 && pr->null_bad)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "assignment to world entity"); return; } if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to address an invalid field in an edict\n"); return; } ed = PROG_TO_EDICT (pr, OPA->edict); OPC->_int = (byte *) ((int *) &ed->v + OPB->_int) - (byte *) *pr->edicts; break; case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: if (pr_boundscheck->int_val && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an out of bounds edict number\n"); return; } if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an invalid field in an edict\n"); return; } ed = PROG_TO_EDICT (pr, OPA->edict); OPC->_int = ((eval_t *) ((int *) &ed->v + OPB->_int))->_int; break; case OP_LOAD_V: if (pr_boundscheck->int_val && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an out of bounds edict number\n"); return; } if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int + 2 >= pr->progs->entityfields)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an invalid field in an edict\n"); return; } ed = PROG_TO_EDICT (pr, OPA->edict); OPC->vector[0] = ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[0]; OPC->vector[1] = ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[1]; OPC->vector[2] = ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[2]; break; // ================== case OP_IFNOT: if (!OPA->_int) st += st->b - 1; // offset the s++ break; case OP_IF: if (OPA->_int) st += st->b - 1; // offset the s++ break; case OP_GOTO: st += st->a - 1; // offset the s++ break; case OP_CALL0: case OP_CALL1: case OP_CALL2: case OP_CALL3: case OP_CALL4: case OP_CALL5: case OP_CALL6: case OP_CALL7: case OP_CALL8: pr->pr_xfunction->profile += profile - startprofile; startprofile = profile; pr->pr_xstatement = st - pr->pr_statements; pr->pr_argc = st->op - OP_CALL0; if (!OPA->function) PR_RunError (pr, "NULL function"); newf = &pr->pr_functions[OPA->function]; if (newf->first_statement < 0) { // negative // statements are // built in functions int i = -newf->first_statement; if (i >= pr_numbuiltins) PR_RunError (pr, "Bad builtin call number"); pr_builtins[i] (pr); break; } st = &pr->pr_statements[PR_EnterFunction (pr, newf)]; break; case OP_DONE: case OP_RETURN: pr->pr_globals[OFS_RETURN] = pr->pr_globals[(unsigned short) st->a]; pr->pr_globals[OFS_RETURN + 1] = pr->pr_globals[(unsigned short) st->a + 1]; pr->pr_globals[OFS_RETURN + 2] = pr->pr_globals[(unsigned short) st->a + 2]; st = &pr->pr_statements[PR_LeaveFunction (pr)]; if (pr->pr_depth == exitdepth) return; // all done break; case OP_STATE: ed = PROG_TO_EDICT (pr, pr->pr_global_struct->self); ed->v.v.nextthink = pr->pr_global_struct->time + 0.1; ed->v.v.frame = OPA->_float; ed->v.v.think = OPB->function; break; // LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized /* case OP_ADD_I: OPC->_int = OPA->_int + OPB->_int; break; case OP_ADD_IF: OPC->_int = OPA->_int + (int) OPB->_float; break; case OP_ADD_FI: OPC->_float = OPA->_float + (float) OPB->_int; break; case OP_SUB_I: OPC->_int = OPA->_int - OPB->_int; break; case OP_SUB_IF: OPC->_int = OPA->_int - (int) OPB->_float; break; case OP_SUB_FI: OPC->_float = OPA->_float - (float) OPB->_int; break; case OP_MUL_I: OPC->_int = OPA->_int * OPB->_int; break; case OP_MUL_IF: OPC->_int = OPA->_int * (int) OPB->_float; break; case OP_MUL_FI: OPC->_float = OPA->_float * (float) OPB->_int; break; case OP_MUL_VI: OPC->vector[0] = (float) OPB->_int * OPA->vector[0]; OPC->vector[1] = (float) OPB->_int * OPA->vector[1]; OPC->vector[2] = (float) OPB->_int * OPA->vector[2]; break; case OP_DIV_VF: { float temp = 1.0f / OPB->_float; OPC->vector[0] = temp * OPA->vector[0]; OPC->vector[1] = temp * OPA->vector[1]; OPC->vector[2] = temp * OPA->vector[2]; } break; case OP_DIV_I: OPC->_int = OPA->_int / OPB->_int; break; case OP_DIV_IF: OPC->_int = OPA->_int / (int) OPB->_float; break; case OP_DIV_FI: OPC->_float = OPA->_float / (float) OPB->_int; break; case OP_CONV_IF: OPC->_float = OPA->_int; break; case OP_CONV_FI: OPC->_int = OPA->_float; break; case OP_BITAND_I: OPC->_int = OPA->_int & OPB->_int; break; case OP_BITOR_I: OPC->_int = OPA->_int | OPB->_int; break; case OP_BITAND_IF: OPC->_int = OPA->_int & (int) OPB->_float; break; case OP_BITOR_IF: OPC->_int = OPA->_int | (int) OPB->_float; break; case OP_BITAND_FI: OPC->_float = (int) OPA->_float & OPB->_int; break; case OP_BITOR_FI: OPC->_float = (int) OPA->_float | OPB->_int; break; case OP_GE_I: OPC->_float = OPA->_int >= OPB->_int; break; case OP_LE_I: OPC->_float = OPA->_int <= OPB->_int; break; case OP_GT_I: OPC->_float = OPA->_int > OPB->_int; break; case OP_LT_I: OPC->_float = OPA->_int < OPB->_int; break; case OP_AND_I: OPC->_float = OPA->_int && OPB->_int; break; case OP_OR_I: OPC->_float = OPA->_int || OPB->_int; break; case OP_GE_IF: OPC->_float = (float) OPA->_int >= OPB->_float; break; case OP_LE_IF: OPC->_float = (float) OPA->_int <= OPB->_float; break; case OP_GT_IF: OPC->_float = (float) OPA->_int > OPB->_float; break; case OP_LT_IF: OPC->_float = (float) OPA->_int < OPB->_float; break; case OP_AND_IF: OPC->_float = (float) OPA->_int && OPB->_float; break; case OP_OR_IF: OPC->_float = (float) OPA->_int || OPB->_float; break; case OP_GE_FI: OPC->_float = OPA->_float >= (float) OPB->_int; break; case OP_LE_FI: OPC->_float = OPA->_float <= (float) OPB->_int; break; case OP_GT_FI: OPC->_float = OPA->_float > (float) OPB->_int; break; case OP_LT_FI: OPC->_float = OPA->_float < (float) OPB->_int; break; case OP_AND_FI: OPC->_float = OPA->_float && (float) OPB->_int; break; case OP_OR_FI: OPC->_float = OPA->_float || (float) OPB->_int; break; case OP_NOT_I: OPC->_float = !OPA->_int; break; case OP_EQ_I: OPC->_float = OPA->_int == OPB->_int; break; case OP_EQ_IF: OPC->_float = (float) OPA->_int == OPB->_float; break; case OP_EQ_FI: OPC->_float = OPA->_float == (float) OPB->_int; break; case OP_NE_I: OPC->_float = OPA->_int != OPB->_int; break; case OP_NE_IF: OPC->_float = (float) OPA->_int != OPB->_float; break; case OP_NE_FI: OPC->_float = OPA->_float != (float) OPB->_int; break; case OP_STORE_I: OPB->_int = OPA->_int; break; case OP_STOREP_I: if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int + 4 > pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an out of bounds edict\n"); return; } if (pr_boundscheck->int_val && (OPB->_int % pr->pr_edict_size < ((byte *) & (*pr->edicts)->v - (byte *) *pr->edicts))) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an engine edict field\n"); return; } ptr = (eval_t *) ((byte *) *pr->edicts + OPB->_int); ptr->_int = OPA->_int; break; case OP_LOAD_I: if (pr_boundscheck->int_val && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an out of bounds edict number\n"); return; } if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an invalid field in an edict\n"); return; } ed = PROG_TO_EDICT (pr, OPA->edict); OPC->_int = ((eval_t *) ((int *) &ed->v + OPB->_int))->_int; break; case OP_GSTOREP_I: case OP_GSTOREP_F: case OP_GSTOREP_ENT: case OP_GSTOREP_FLD: // integers case OP_GSTOREP_S: case OP_GSTOREP_FNC: // pointers if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int >= pr->pr_globaldefs)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an invalid indexed global\n"); return; } pr->pr_globals[OPB->_int] = OPA->_float; break; case OP_GSTOREP_V: if (pr_boundscheck->int_val && (OPB->_int < 0 || OPB->_int + 2 >= pr->pr_globaldefs)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to write to an invalid indexed global\n"); return; } pr->pr_globals[OPB->_int] = OPA->vector[0]; pr->pr_globals[OPB->_int + 1] = OPA->vector[1]; pr->pr_globals[OPB->_int + 2] = OPA->vector[2]; break; case OP_GADDRESS: i = OPA->_int + (int) OPB->_float; if (pr_boundscheck->int_val && (i < 0 || i >= pr->pr_globaldefs)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to address an out of bounds global\n"); return; } OPC->_float = pr->pr_globals[i]; break; case OP_GLOAD_I: case OP_GLOAD_F: case OP_GLOAD_FLD: case OP_GLOAD_ENT: case OP_GLOAD_S: case OP_GLOAD_FNC: if (pr_boundscheck->int_val && (OPA->_int < 0 || OPA->_int >= pr->pr_globaldefs)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an invalid indexed global\n"); return; } OPC->_float = pr->pr_globals[OPA->_int]; break; case OP_GLOAD_V: if (pr_boundscheck->int_val && (OPA->_int < 0 || OPA->_int + 2 >= pr->pr_globaldefs)) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs attempted to read an invalid indexed global\n"); return; } OPC->vector[0] = pr->pr_globals[OPA->_int]; OPC->vector[1] = pr->pr_globals[OPA->_int + 1]; OPC->vector[2] = pr->pr_globals[OPA->_int + 2]; break; case OP_BOUNDCHECK: if (OPA->_int < 0 || OPA->_int >= st->b) { pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Progs boundcheck failed at line number %d, value is < 0 or >= %d\n", st->b, st->c); return; } break; */ default: pr->pr_xstatement = st - pr->pr_statements; PR_RunError (pr, "Bad opcode %i", st->op); } } }