/* =============== CL_SparkShower Creates 8 random tracers =============== */ void CL_SparkShower( const vec3_t org ) { vec3_t pos, dir; model_t *pmodel; float vel; int i; // randomize position pos[0] = org[0] + Com_RandomFloat( -2.0f, 2.0f ); pos[1] = org[1] + Com_RandomFloat( -2.0f, 2.0f ); pos[2] = org[2] + Com_RandomFloat( -2.0f, 2.0f ); pmodel = Mod_Handle( CL_FindModelIndex( "sprites/richo1.spr" )); CL_RicochetSprite( pos, pmodel, 0.0f, Com_RandomFloat( 0.4, 0.6f )); // create a 8 random spakle tracers for( i = 0; i < 8; i++ ) { dir[0] = Com_RandomFloat( -1.0f, 1.0f ); dir[1] = Com_RandomFloat( -1.0f, 1.0f ); dir[2] = Com_RandomFloat( -1.0f, 1.0f ); vel = Com_RandomFloat( SPARK_ELECTRIC_MINSPEED, SPARK_ELECTRIC_MAXSPEED ); CL_SparkleTracer( pos, dir, vel ); } }
/* * R_CullAliasModel */ qboolean R_CullAliasModel( entity_t *e ) { int i, j, clipped; qboolean frustum, query; unsigned int modhandle; model_t *mod; shader_t *shader; maliasmodel_t *aliasmodel; maliasmesh_t *mesh; mod = R_AliasModelLOD( e ); if( !( aliasmodel = ( ( maliasmodel_t * )mod->extradata ) ) || !aliasmodel->nummeshes ) return qtrue; R_AliasModelLerpBBox( e, mod ); modhandle = Mod_Handle( mod ); clipped = R_CullModel( e, alias_mins, alias_maxs, alias_radius ); frustum = clipped & 1; if( clipped & 2 ) return qtrue; query = OCCLUSION_QUERIES_ENABLED( ri ) && OCCLUSION_TEST_ENTITY( e ) ? qtrue : qfalse; if( !frustum && query ) R_IssueOcclusionQuery( R_GetOcclusionQueryNum( OQ_ENTITY, e - r_entities ), e, alias_mins, alias_maxs ); if( ( ri.refdef.rdflags & RDF_NOWORLDMODEL ) || ( r_shadows->integer != SHADOW_PLANAR && !( r_shadows->integer == SHADOW_MAPPING && ( e->flags & RF_PLANARSHADOW ) ) ) || R_CullPlanarShadow( e, alias_mins, alias_maxs, query ) ) return frustum; // entity is not in PVS or shadow is culled away by frustum culling for( i = 0, mesh = aliasmodel->meshes; i < aliasmodel->nummeshes; i++, mesh++ ) { shader = NULL; if( e->customSkin ) shader = R_FindShaderForSkinFile( e->customSkin, mesh->name ); else if( e->customShader ) shader = e->customShader; else if( mesh->numskins ) { for( j = 0; j < mesh->numskins; j++ ) { shader = mesh->skins[j].shader; if( shader && shader->sort <= SHADER_SORT_ALPHATEST ) break; shader = NULL; } } if( shader && ( shader->sort <= SHADER_SORT_ALPHATEST ) ) { R_AddModelMeshToList( modhandle, mesh->vbo, NULL, R_PlanarShadowShader(), i, 0, mesh->numverts, mesh->numtris * 3 ); } } return frustum; }
/* =============== V_SetupRefDef update refdef values each frame =============== */ void V_SetupRefDef( void ) { cl_entity_t *clent; clent = CL_GetLocalPlayer (); clgame.entities->curstate.scale = clgame.movevars.waveHeight; VectorCopy( cl.frame.local.client.punchangle, cl.refdef.punchangle ); clgame.viewent.curstate.modelindex = cl.frame.local.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; cl.refdef.movevars = &clgame.movevars; cl.refdef.onground = ( cl.frame.local.client.flags & FL_ONGROUND ) ? 1 : 0; cl.refdef.health = cl.frame.local.client.health; cl.refdef.playernum = cl.playernum; cl.refdef.max_entities = clgame.maxEntities; cl.refdef.maxclients = cl.maxclients; cl.refdef.time = cl.time; cl.refdef.frametime = cl.time - cl.oldtime; cl.refdef.demoplayback = cls.demoplayback; cl.refdef.smoothing = cl_smooth->integer; cl.refdef.waterlevel = cl.frame.local.client.waterlevel; cl.refdef.onlyClientDraw = 0; // reset clientdraw cl.refdef.viewsize = scr_viewsize->integer; cl.refdef.hardware = true; // always true cl.refdef.spectator = cl.spectator; cl.refdef.nextView = 0; // setup default viewport cl.refdef.viewport[0] = cl.refdef.viewport[1] = 0; cl.refdef.viewport[2] = scr_width->integer; cl.refdef.viewport[3] = scr_height->integer; // calc FOV cl.refdef.fov_x = cl.data.fov; // this is a final fov value cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] ); if( CL_IsPredicted( ) && !cl.refdef.demoplayback ) { VectorCopy( cl.predicted_origin, cl.refdef.simorg ); VectorCopy( cl.predicted_velocity, cl.refdef.simvel ); VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight ); } else { VectorCopy( cl.frame.local.client.origin, cl.refdef.simorg ); VectorCopy( cl.frame.local.client.view_ofs, cl.refdef.viewheight ); VectorCopy( cl.frame.local.client.velocity, cl.refdef.simvel ); } }
/* ============ SV_AllowPushRotate Allows to change entity yaw? ============ */ qboolean SV_AllowPushRotate( edict_t *ent ) { model_t *mod; mod = Mod_Handle( ent->v.modelindex ); if( !mod || mod->type != mod_brush ) return true; if( !sv_allow_rotate_pushables->integer ) return false; return (mod->flags & MODEL_HAS_ORIGIN) ? true : false; }
/* ==================== StudioGetAttachment ==================== */ void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *origin, float *angles ) { mstudioattachment_t *pAtt; vec3_t angles2; model_t *mod; mod = Mod_Handle( e->v.modelindex ); mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod ); if( !mod_studiohdr ) return; if( mod_studiohdr->numattachments <= 0 ) return; ASSERT( pBlendAPI != NULL ); if( mod_studiohdr->numattachments > MAXSTUDIOATTACHMENTS ) { mod_studiohdr->numattachments = MAXSTUDIOATTACHMENTS; // reduce it MsgDev( D_WARN, "SV_StudioGetAttahment: too many attachments on %s\n", mod_studiohdr->name ); } iAttachment = bound( 0, iAttachment, mod_studiohdr->numattachments ); // calculate attachment origin and angles pAtt = (mstudioattachment_t *)((byte *)mod_studiohdr + mod_studiohdr->attachmentindex); VectorCopy( e->v.angles, angles2 ); if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) angles2[PITCH] = -angles2[PITCH]; pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, angles2, e->v.origin, e->v.controller, e->v.blending, pAtt[iAttachment].bone, e ); // compute pos and angles if( origin != NULL ) Matrix3x4_VectorTransform( studio_bones[pAtt[iAttachment].bone], pAtt[iAttachment].org, origin ); if( sv_allow_studio_attachment_angles->integer && origin != NULL && angles != NULL ) { vec3_t forward, bonepos; Matrix3x4_OriginFromMatrix( studio_bones[pAtt[iAttachment].bone], bonepos ); VectorSubtract( origin, bonepos, forward ); // make forward VectorNormalizeFast( forward ); VectorAngles( forward, angles ); } }
/* ==================== GetBonePosition ==================== */ void Mod_GetBonePosition( const edict_t *e, int iBone, float *origin, float *angles ) { model_t *mod; mod = Mod_Handle( e->v.modelindex ); mod_studiohdr = (studiohdr_t *)Mod_Extradata( mod ); if( !mod_studiohdr ) return; ASSERT( pBlendAPI != NULL ); pBlendAPI->SV_StudioSetupBones( mod, e->v.frame, e->v.sequence, e->v.angles, e->v.origin, e->v.controller, e->v.blending, iBone, e ); if( origin ) Matrix3x4_OriginFromMatrix( studio_bones[iBone], origin ); if( angles ) VectorAngles( studio_bones[iBone][0], angles ); // bone forward to angles }
/* ============== CL_ParseStudioDecal Studio Decal message. Used by engine in case we need save\restore decals ============== */ void CL_ParseStudioDecal( sizebuf_t *msg ) { modelstate_t state; vec3_t start, pos; int decalIndex, entityIndex; int modelIndex = 0; int flags; pos[0] = BF_ReadCoord( msg ); pos[1] = BF_ReadCoord( msg ); pos[2] = BF_ReadCoord( msg ); start[0] = BF_ReadCoord( msg ); start[1] = BF_ReadCoord( msg ); start[2] = BF_ReadCoord( msg ); decalIndex = BF_ReadShort( msg ); entityIndex = BF_ReadShort( msg ); flags = BF_ReadByte( msg ); state.sequence = BF_ReadShort( msg ); state.frame = BF_ReadShort( msg ); state.blending[0] = BF_ReadByte( msg ); state.blending[1] = BF_ReadByte( msg ); state.controller[0] = BF_ReadByte( msg ); state.controller[1] = BF_ReadByte( msg ); state.controller[2] = BF_ReadByte( msg ); state.controller[3] = BF_ReadByte( msg ); if( cls.state == ca_connected ) { // this message came on restore. // read modelindex in case client models are not linked with entities // because first client frame has not yet received modelIndex = BF_ReadShort( msg ); } if( clgame.drawFuncs.R_StudioDecalShoot ) { int decalTexture = CL_DecalIndex( decalIndex ); cl_entity_t *ent = CL_GetEntityByIndex( entityIndex ); if( ent && !ent->model && modelIndex != 0 ) ent->model = Mod_Handle( modelIndex ); clgame.drawFuncs.R_StudioDecalShoot( decalTexture, ent, start, pos, flags, &state ); } }
/* ============== CL_ParseStudioDecal Studio Decal message. Used by engine in case we need save\restore decals ============== */ void CL_ParseStudioDecal( sizebuf_t *msg ) { modelstate_t state; vec3_t start, pos; int decalIndex, entityIndex; int modelIndex = 0; int flags; BF_ReadVec3Coord( msg, pos ); BF_ReadVec3Coord( msg, start ); decalIndex = BF_ReadWord( msg ); entityIndex = BF_ReadWord( msg ); flags = BF_ReadByte( msg ); state.sequence = BF_ReadShort( msg ); state.frame = BF_ReadShort( msg ); state.blending[0] = BF_ReadByte( msg ); state.blending[1] = BF_ReadByte( msg ); state.controller[0] = BF_ReadByte( msg ); state.controller[1] = BF_ReadByte( msg ); state.controller[2] = BF_ReadByte( msg ); state.controller[3] = BF_ReadByte( msg ); modelIndex = BF_ReadWord( msg ); state.body = BF_ReadByte( msg ); state.skin = BF_ReadByte( msg ); if( clgame.drawFuncs.R_StudioDecalShoot != NULL ) { int decalTexture = CL_DecalIndex( decalIndex ); cl_entity_t *ent = CL_GetEntityByIndex( entityIndex ); if( ent && !ent->model && modelIndex != 0 ) ent->model = Mod_Handle( modelIndex ); clgame.drawFuncs.R_StudioDecalShoot( decalTexture, ent, start, pos, flags, &state ); } }
void CL_UpdateEntityFields( cl_entity_t *ent ) { // parametric rockets code if( ent->curstate.starttime != 0.0f && ent->curstate.impacttime != 0.0f ) { float lerp = ( cl.time - ent->curstate.starttime ) / ( ent->curstate.impacttime - ent->curstate.starttime ); vec3_t dir; lerp = bound( 0.0f, lerp, 1.0f ); // first we need to calc actual origin VectorLerp( ent->curstate.startpos, lerp, ent->curstate.endpos, ent->curstate.origin ); VectorSubtract( ent->curstate.endpos, ent->curstate.startpos, dir ); VectorAngles( dir, ent->curstate.angles ); // re-aim projectile } ent->model = Mod_Handle( ent->curstate.modelindex ); ent->curstate.msg_time = cl.time; CL_InterpolateModel( ent ); if( ent->player && RP_LOCALCLIENT( ent )) // stupid Half-Life bug ent->angles[PITCH] = -ent->angles[PITCH] / 3.0f; // make me lerp if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f ) { float d, f = 0.0f; int i; // don't do it if the goalstarttime hasn't updated in a while. // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit // was increased to 1.0 s., which is 2x the max lag we are accounting for. if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime )) f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime ); f = f - 1.0f; ent->origin[0] += ( ent->origin[0] - ent->latched.prevorigin[0] ) * f; ent->origin[1] += ( ent->origin[1] - ent->latched.prevorigin[1] ) * f; ent->origin[2] += ( ent->origin[2] - ent->latched.prevorigin[2] ) * f; for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = ent->angles[i]; ang2 = ent->latched.prevangles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * f; } } else if( ent->curstate.eflags & EFLAG_SLERP ) { float d, f = 0.0f; cl_entity_t *m_pGround = NULL; int i; // don't do it if the goalstarttime hasn't updated in a while. // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit // was increased to 1.0 s., which is 2x the max lag we are accounting for. if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime )) f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime ); f = f - 1.0f; if( ent->curstate.movetype == MOVETYPE_FLY ) { ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f; ent->origin[1] += ( ent->curstate.origin[1] - ent->latched.prevorigin[1] ) * f; ent->origin[2] += ( ent->curstate.origin[2] - ent->latched.prevorigin[2] ) * f; for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = ent->curstate.angles[i]; ang2 = ent->latched.prevangles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * f; } } else if( ent->curstate.movetype == MOVETYPE_STEP ) { vec3_t vecSrc, vecEnd; pmtrace_t trace; if( ent->model ) { CL_SetTraceHull( 0 ); // g-cont. player hull for better detect moving platforms VectorSet( vecSrc, ent->origin[0], ent->origin[1], ent->origin[2] + ent->model->maxs[2] ); VectorSet( vecEnd, vecSrc[0], vecSrc[1], vecSrc[2] - ent->model->mins[2] - 8.0f ); CL_PlayerTraceExt( vecSrc, vecEnd, PM_STUDIO_IGNORE, CL_PushMoveFilter, &trace ); m_pGround = CL_GetEntityByIndex( pfnIndexFromTrace( &trace )); } if( m_pGround && m_pGround->curstate.movetype == MOVETYPE_PUSH ) { qboolean applyVel, applyAvel; applyVel = !VectorCompare( m_pGround->curstate.origin, m_pGround->prevstate.origin ); applyAvel = !VectorCompare( m_pGround->curstate.angles, m_pGround->prevstate.angles ); if( applyVel || applyAvel ) { ent->origin[0] += ( m_pGround->curstate.origin[0] - m_pGround->prevstate.origin[0] ) * -1.0f; ent->origin[1] += ( m_pGround->curstate.origin[1] - m_pGround->prevstate.origin[1] ) * -1.0f; // ent->origin[2] += ( m_pGround->curstate.origin[2] - m_pGround->prevstate.origin[2] ) * -1.0f; ent->latched.prevorigin[2] = ent->origin[2]; } if( applyAvel ) { for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = m_pGround->curstate.angles[i]; ang2 = m_pGround->prevstate.angles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * -1.0f; } } } // moved code from StudioSetupTransform here if( host.features & ENGINE_COMPUTE_STUDIO_LERP ) { ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f; ent->origin[1] += ( ent->curstate.origin[1] - ent->latched.prevorigin[1] ) * f; ent->origin[2] += ( ent->curstate.origin[2] - ent->latched.prevorigin[2] ) * f; for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = ent->angles[i]; ang2 = ent->latched.prevangles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * f; } } } } }
qboolean SV_CopyEdictToPhysEnt( physent_t *pe, edict_t *ed ) { model_t *mod = Mod_Handle( ed->v.modelindex ); if( !mod ) return false; pe->player = false; pe->info = NUM_FOR_EDICT( ed ); VectorCopy( ed->v.origin, pe->origin ); VectorCopy( ed->v.angles, pe->angles ); if( ed->v.flags & ( FL_CLIENT|FL_FAKECLIENT )) { // client or bot if ( svs.currentPlayer ) SV_GetTrueOrigin( svs.currentPlayer, (pe->info - 1), pe->origin ); Q_strncpy( pe->name, "player", sizeof( pe->name )); pe->player = pe->info; } else { // otherwise copy the modelname Q_strncpy( pe->name, mod->name, sizeof( pe->name )); } pe->model = pe->studiomodel = NULL; switch( ed->v.solid ) { case SOLID_NOT: case SOLID_BSP: pe->model = mod; VectorClear( pe->mins ); VectorClear( pe->maxs ); break; case SOLID_BBOX: if( mod && mod->type == mod_studio && mod->flags & STUDIO_TRACE_HITBOX ) pe->studiomodel = mod; VectorCopy( ed->v.mins, pe->mins ); VectorCopy( ed->v.maxs, pe->maxs ); break; case SOLID_CUSTOM: pe->model = (mod->type == mod_brush) ? mod : NULL; pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; VectorCopy( ed->v.mins, pe->mins ); VectorCopy( ed->v.maxs, pe->maxs ); break; default: pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; VectorCopy( ed->v.mins, pe->mins ); VectorCopy( ed->v.maxs, pe->maxs ); break; } pe->solid = ed->v.solid; pe->rendermode = ed->v.rendermode; pe->skin = ed->v.skin; pe->frame = ed->v.frame; pe->sequence = ed->v.sequence; Q_memcpy( &pe->controller[0], &ed->v.controller[0], 4 * sizeof( byte )); Q_memcpy( &pe->blending[0], &ed->v.blending[0], 2 * sizeof( byte )); pe->movetype = ed->v.movetype; pe->takedamage = ed->v.takedamage; pe->team = ed->v.team; pe->classnumber = ed->v.playerclass; pe->blooddecal = 0; // unused in GoldSrc // for mods pe->iuser1 = ed->v.iuser1; pe->iuser2 = ed->v.iuser2; pe->iuser3 = ed->v.iuser3; pe->iuser4 = ed->v.iuser4; pe->fuser1 = ed->v.fuser1; pe->fuser2 = ed->v.fuser2; pe->fuser3 = ed->v.fuser3; pe->fuser4 = ed->v.fuser4; VectorCopy( ed->v.vuser1, pe->vuser1 ); VectorCopy( ed->v.vuser2, pe->vuser2 ); VectorCopy( ed->v.vuser3, pe->vuser3 ); VectorCopy( ed->v.vuser4, pe->vuser4 ); return true; }
/* * R_AddAliasModelToList */ void R_AddAliasModelToList( entity_t *e ) { int i, j; unsigned int modhandle, entnum = e - r_entities; mfog_t *fog = NULL; model_t *mod; shader_t *shader; maliasmodel_t *aliasmodel; maliasmesh_t *mesh; float distance; mod = R_AliasModelLOD( e ); aliasmodel = ( maliasmodel_t * )mod->extradata; modhandle = Mod_Handle( mod ); // make sure weapon model is always closest to the viewer if( e->renderfx & RF_WEAPONMODEL ) { distance = 0; } else { distance = Distance( e->origin, ri.viewOrigin ) + 1; } if( ri.params & RP_SHADOWMAPVIEW ) { if( r_entShadowBits[entnum] & ri.shadowGroup->bit ) { if( !r_shadows_self_shadow->integer ) r_entShadowBits[entnum] &= ~ri.shadowGroup->bit; if( e->flags & RF_WEAPONMODEL ) return; } else { R_AliasModelLerpBBox( e, mod ); if( !R_CullModel( e, alias_mins, alias_maxs, alias_radius ) ) r_entShadowBits[entnum] |= ri.shadowGroup->bit; return; // mark as shadowed, proceed with caster otherwise } } else { fog = R_FogForSphere( e->origin, alias_radius ); #if 0 if( !( e->flags & RF_WEAPONMODEL ) && fog ) { R_AliasModelLerpBBox( e, mod ); if( R_CompletelyFogged( fog, e->origin, alias_radius ) ) return; } #endif } for( i = 0, mesh = aliasmodel->meshes; i < aliasmodel->nummeshes; i++, mesh++ ) { shader = NULL; if( e->customSkin ) shader = R_FindShaderForSkinFile( e->customSkin, mesh->name ); else if( e->customShader ) shader = e->customShader; else if( mesh->numskins ) { for( j = 0; j < mesh->numskins; j++ ) { shader = mesh->skins[j].shader; if( shader ) R_AddModelMeshToList( modhandle, mesh->vbo, fog, shader, i, distance, mesh->numverts, mesh->numtris * 3 ); } continue; } if( shader ) R_AddModelMeshToList( modhandle, mesh->vbo, fog, shader, i, distance, mesh->numverts, mesh->numtris * 3 ); } }
// 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 ); }
/* ========================================================================= FRAME PARSING ========================================================================= */ void CL_UpdateEntityFields( cl_entity_t *ent ) { VectorCopy( ent->curstate.origin, ent->origin ); VectorCopy( ent->curstate.angles, ent->angles ); ent->model = Mod_Handle( ent->curstate.modelindex ); ent->curstate.msg_time = cl.time; if( ent->player ) // stupid Half-Life bug ent->angles[PITCH] = -ent->angles[PITCH] / 3.0f; // make me lerp if( ent->model && ent->model->type == mod_brush && ent->curstate.animtime != 0.0f ) { float d, f = 0.0f; int i; // don't do it if the goalstarttime hasn't updated in a while. // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit // was increased to 1.0 s., which is 2x the max lag we are accounting for. if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime )) f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime ); f = f - 1.0f; ent->origin[0] += ( ent->origin[0] - ent->latched.prevorigin[0] ) * f; ent->origin[1] += ( ent->origin[1] - ent->latched.prevorigin[1] ) * f; ent->origin[2] += ( ent->origin[2] - ent->latched.prevorigin[2] ) * f; for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = ent->angles[i]; ang2 = ent->latched.prevangles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * f; } } else if( ent->curstate.eflags & EFLAG_SLERP ) { float d, f = 0.0f; cl_entity_t *m_pGround = NULL; int i; // don't do it if the goalstarttime hasn't updated in a while. // NOTE: Because we need to interpolate multiplayer characters, the interpolation time limit // was increased to 1.0 s., which is 2x the max lag we are accounting for. if(( cl.time < ent->curstate.animtime + 1.0f ) && ( ent->curstate.animtime != ent->latched.prevanimtime )) f = ( cl.time - ent->curstate.animtime ) / ( ent->curstate.animtime - ent->latched.prevanimtime ); f = f - 1.0f; if( ent->curstate.movetype == MOVETYPE_FLY ) { ent->origin[0] += ( ent->curstate.origin[0] - ent->latched.prevorigin[0] ) * f; ent->origin[1] += ( ent->curstate.origin[1] - ent->latched.prevorigin[1] ) * f; ent->origin[2] += ( ent->curstate.origin[2] - ent->latched.prevorigin[2] ) * f; for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = ent->curstate.angles[i]; ang2 = ent->latched.prevangles[i]; d = ang1 - ang2; if( d > 180.0f ) d -= 360.0f; else if( d < -180.0f ) d += 360.0f; ent->angles[i] += d * f; } } else if( ent->curstate.movetype == MOVETYPE_STEP ) { vec3_t vecSrc, vecEnd; pmtrace_t trace; if( ent->model ) { CL_SetTraceHull( 0 ); // g-cont. player hull for better detect moving platforms VectorSet( vecSrc, ent->origin[0], ent->origin[1], ent->origin[2] + ent->model->maxs[2] ); VectorSet( vecEnd, vecSrc[0], vecSrc[1], vecSrc[2] - ent->model->mins[2] - 8.0f ); CL_PlayerTraceExt( vecSrc, vecEnd, PM_STUDIO_IGNORE, CL_PushMoveFilter, &trace ); m_pGround = CL_GetEntityByIndex( pfnIndexFromTrace( &trace )); } if( m_pGround && m_pGround->curstate.movetype == MOVETYPE_PUSH ) { qboolean applyVel, applyAvel; d = -1.0f; applyVel = !VectorCompare( m_pGround->curstate.origin, m_pGround->prevstate.origin ); applyAvel = !VectorCompare( m_pGround->curstate.angles, m_pGround->prevstate.angles ); if( applyVel || applyAvel ) { ent->origin[0] += ( m_pGround->curstate.origin[0] - m_pGround->prevstate.origin[0] ) * d; ent->origin[1] += ( m_pGround->curstate.origin[1] - m_pGround->prevstate.origin[1] ) * d; // ent->origin[2] += ( m_pGround->curstate.origin[2] - m_pGround->prevstate.origin[2] ) * d; ent->latched.prevorigin[2] = ent->origin[2]; } if( applyAvel ) { for( i = 0; i < 3; i++ ) { float ang1, ang2; ang1 = m_pGround->curstate.angles[i]; ang2 = m_pGround->prevstate.angles[i]; f = ang1 - ang2; if( d > 180.0f ) f -= 360.0f; else if( d < -180.0f ) f += 360.0f; ent->angles[i] += d * f; } } } } } }
/* =============== V_SetupRefDef update refdef values each frame =============== */ void V_SetupRefDef( void ) { cl_entity_t *clent; int size; int sb_lines; clent = CL_GetLocalPlayer (); clgame.entities->curstate.scale = clgame.movevars.waveHeight; VectorCopy( cl.frame.local.client.punchangle, cl.refdef.punchangle ); clgame.viewent.curstate.modelindex = cl.frame.local.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); clgame.viewent.curstate.number = cl.playernum + 1; clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; cl.refdef.movevars = &clgame.movevars; cl.refdef.onground = ( cl.frame.local.client.flags & FL_ONGROUND ) ? 1 : 0; cl.refdef.health = cl.frame.local.client.health; cl.refdef.playernum = cl.playernum; cl.refdef.max_entities = clgame.maxEntities; cl.refdef.maxclients = cl.maxclients; cl.refdef.time = cl.time; cl.refdef.frametime = cl.time - cl.oldtime; cl.refdef.demoplayback = cls.demoplayback; cl.refdef.smoothing = cl_smooth->integer; cl.refdef.viewsize = scr_viewsize->integer; cl.refdef.waterlevel = cl.frame.local.client.waterlevel; cl.refdef.onlyClientDraw = 0; // reset clientdraw cl.refdef.hardware = true; // always true cl.refdef.spectator = (clent->curstate.spectator != 0); cl.refdef.nextView = 0; SCR_AddDirtyPoint( 0, 0 ); SCR_AddDirtyPoint( scr_width->integer - 1, scr_height->integer - 1 ); if( cl.refdef.viewsize >= 120 ) sb_lines = 0; // no status bar at all else if( cl.refdef.viewsize >= 110 ) sb_lines = 24; // no inventory else sb_lines = 48; size = min( scr_viewsize->integer, 100 ); cl.refdef.viewport[2] = scr_width->integer * size / 100; cl.refdef.viewport[3] = scr_height->integer * size / 100; if( cl.refdef.viewport[3] > scr_height->integer - sb_lines ) cl.refdef.viewport[3] = scr_height->integer - sb_lines; if( cl.refdef.viewport[3] > scr_height->integer ) cl.refdef.viewport[3] = scr_height->integer; cl.refdef.viewport[0] = (scr_width->integer - cl.refdef.viewport[2]) / 2; cl.refdef.viewport[1] = (scr_height->integer - sb_lines - cl.refdef.viewport[3]) / 2; // calc FOV cl.refdef.fov_x = cl.data.fov; // this is a final fov value cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] ); // adjust FOV for widescreen if( glState.wideScreen && r_adjust_fov->integer ) V_AdjustFov( &cl.refdef.fov_x, &cl.refdef.fov_y, cl.refdef.viewport[2], cl.refdef.viewport[3], false ); if( CL_IsPredicted( ) && !cl.refdef.demoplayback ) { VectorCopy( cl.predicted_origin, cl.refdef.simorg ); VectorCopy( cl.predicted_velocity, cl.refdef.simvel ); VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight ); } else { VectorCopy( cl.frame.local.client.origin, cl.refdef.simorg ); VectorCopy( cl.frame.local.client.view_ofs, cl.refdef.viewheight ); VectorCopy( cl.frame.local.client.velocity, cl.refdef.simvel ); } }
/* ================= CL_PrepVideo Call before entering a new level, or after changing dlls ================= */ void CL_PrepVideo( void ) { string mdlname, mapname; int i, mdlcount, step; int map_checksum; // dummy if( !cl.model_precache[1][0] ) return; // no map loaded Cvar_SetFloat( "scr_loading", 0.0f ); // reset progress bar MsgDev( D_NOTE, "CL_PrepVideo: %s\n", clgame.mapname ); // let the render dll load the map Q_strncpy( mapname, cl.model_precache[1], MAX_STRING ); Mod_LoadWorld( mapname, (uint *)&map_checksum, false ); cl.worldmodel = Mod_Handle( 1 ); // get world pointer Cvar_SetFloat( "scr_loading", 25.0f ); SCR_UpdateScreen(); // make sure what map is valid if( !cls.demoplayback && map_checksum != cl.checksum ) Host_Error( "Local map version differs from server: %i != '%i'\n", map_checksum, cl.checksum ); for( i = 0, mdlcount = 0; i < MAX_MODELS && cl.model_precache[i+1][0]; i++ ) mdlcount++; // total num models step = mdlcount/10; for( i = 0; i < MAX_MODELS && cl.model_precache[i+1][0]; i++ ) { Q_strncpy( mdlname, cl.model_precache[i+1], MAX_STRING ); Mod_RegisterModel( mdlname, i+1 ); Cvar_SetFloat( "scr_loading", scr_loading->value + 75.0f / mdlcount ); if( step && !( i % step ) && ( cl_allow_levelshots->integer || cl.background ) ) SCR_UpdateScreen(); } // update right muzzleflash indexes CL_RegisterMuzzleFlashes (); // invalidate all decal indexes Q_memset( cl.decal_index, 0, sizeof( cl.decal_index )); CL_ClearWorld (); R_NewMap(); // tell the render about new map V_SetupOverviewState(); // set overview bounds // must be called after lightmap loading! clgame.dllFuncs.pfnVidInit(); // release unused SpriteTextures for( i = 1; i < MAX_IMAGES; i++ ) { if( !clgame.sprites[i].name[0] ) continue; // free slot if( clgame.sprites[i].needload != clgame.load_sequence ) Mod_UnloadSpriteModel( &clgame.sprites[i] ); } Mod_FreeUnused (); Q_memset( cl.playermodels, 0, sizeof( cl.playermodels ) ); Cvar_SetFloat( "scr_loading", 100.0f ); // all done if( host.decalList ) { // need to reapply all decals after restarting for( i = 0; i < host.numdecals; i++ ) { decallist_t *entry = &host.decalList[i]; cl_entity_t *pEdict = CL_GetEntityByIndex( entry->entityIndex ); int decalIndex = CL_DecalIndex( CL_DecalIndexFromName( entry->name )); int modelIndex = 0; if( pEdict ) modelIndex = pEdict->curstate.modelindex; CL_DecalShoot( decalIndex, entry->entityIndex, modelIndex, entry->position, entry->flags ); } Z_Free( host.decalList ); } host.decalList = NULL; host.numdecals = 0; if( host.soundList ) { // need to reapply all ambient sounds after restarting for( i = 0; i < host.numsounds; i++ ) { soundlist_t *entry = &host.soundList[i]; if( entry->looping && entry->entnum != -1 ) { MsgDev( D_NOTE, "Restarting sound %s...\n", entry->name ); S_AmbientSound( entry->origin, entry->entnum, S_RegisterSound( entry->name ), entry->volume, entry->attenuation, entry->pitch, 0 ); } } } host.soundList = NULL; host.numsounds = 0; if( host.developer <= 2 ) Con_ClearNotify(); // clear any lines of console text SCR_UpdateScreen (); cl.video_prepped = true; cl.force_refdef = true; }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. ================ */ qboolean SV_SpawnServer( const char *mapname, const char *startspot ) { int i, current_skill; qboolean loadgame, paused; qboolean background, changelevel; // save state loadgame = sv.loadgame; background = sv.background; changelevel = sv.changelevel; paused = sv.paused; if( sv.state == ss_dead ) SV_InitGame(); // the game is just starting else if( !sv_maxclients->modified ) Cmd_ExecuteString( "latch\n", src_command ); else MsgDev( D_ERROR, "SV_SpawnServer: while 'maxplayers' was modified.\n" ); sv_maxclients->modified = false; deathmatch->modified = false; teamplay->modified = false; coop->modified = false; if( !svs.initialized ) return false; svgame.globals->changelevel = false; // will be restored later if needed svs.timestart = Sys_DoubleTime(); svs.spawncount++; // any partially connected client will be restarted if( startspot ) { MsgDev( D_INFO, "Spawn Server: %s [%s]\n", mapname, startspot ); } else { MsgDev( D_INFO, "Spawn Server: %s\n", mapname ); } sv.state = ss_dead; Host_SetServerState( sv.state ); Q_memset( &sv, 0, sizeof( sv )); // wipe the entire per-level structure // restore state sv.paused = paused; sv.loadgame = loadgame; sv.background = background; sv.changelevel = changelevel; sv.time = 1.0f; // server spawn time it's always 1.0 second svgame.globals->time = sv.time; // initialize buffers BF_Init( &sv.datagram, "Datagram", sv.datagram_buf, sizeof( sv.datagram_buf )); BF_Init( &sv.reliable_datagram, "Datagram R", sv.reliable_datagram_buf, sizeof( sv.reliable_datagram_buf )); BF_Init( &sv.multicast, "Multicast", sv.multicast_buf, sizeof( sv.multicast_buf )); BF_Init( &sv.signon, "Signon", sv.signon_buf, sizeof( sv.signon_buf )); BF_Init( &sv.spectator_datagram, "Spectator Datagram", sv.spectator_buf, sizeof( sv.spectator_buf )); // leave slots at start for clients only for( i = 0; i < sv_maxclients->integer; i++ ) { // needs to reconnect if( svs.clients[i].state > cs_connected ) svs.clients[i].state = cs_connected; } // make cvars consistant if( Cvar_VariableInteger( "coop" )) Cvar_SetFloat( "deathmatch", 0 ); current_skill = (int)(Cvar_VariableValue( "skill" ) + 0.5f); current_skill = bound( 0, current_skill, 3 ); Cvar_SetFloat( "skill", (float)current_skill ); if( sv.background ) { // tell the game parts about background state Cvar_FullSet( "sv_background", "1", CVAR_READ_ONLY ); Cvar_FullSet( "cl_background", "1", CVAR_READ_ONLY ); } else { Cvar_FullSet( "sv_background", "0", CVAR_READ_ONLY ); Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY ); } // make sure what server name doesn't contain path and extension FS_FileBase( mapname, sv.name ); if( startspot ) Q_strncpy( sv.startspot, startspot, sizeof( sv.startspot )); else sv.startspot[0] = '\0'; Q_snprintf( sv.model_precache[1], sizeof( sv.model_precache[0] ), "maps/%s.bsp", sv.name ); Mod_LoadWorld( sv.model_precache[1], &sv.checksum, false ); sv.worldmodel = Mod_Handle( 1 ); // get world pointer for( i = 1; i < sv.worldmodel->numsubmodels; i++ ) { Q_sprintf( sv.model_precache[i+1], "*%i", i ); Mod_RegisterModel( sv.model_precache[i+1], i+1 ); } // precache and static commands can be issued during map initialization sv.state = ss_loading; Host_SetServerState( sv.state ); // clear physics interaction links SV_ClearWorld(); // tell dlls about new level started svgame.dllFuncs.pfnParmsNewLevel(); return true; }
qboolean CL_CopyEntityToPhysEnt( physent_t *pe, cl_entity_t *ent ) { model_t *mod = Mod_Handle( ent->curstate.modelindex ); if( !mod ) return false; pe->player = false; if( ent->player ) { // client or bot Q_strncpy( pe->name, "player", sizeof( pe->name )); pe->player = (int)(ent - clgame.entities); } else { // otherwise copy the modelname Q_strncpy( pe->name, mod->name, sizeof( pe->name )); } pe->model = pe->studiomodel = NULL; switch( ent->curstate.solid ) { case SOLID_NOT: case SOLID_BSP: pe->model = mod; VectorClear( pe->mins ); VectorClear( pe->maxs ); break; case SOLID_BBOX: if( mod && mod->type == mod_studio && mod->flags & STUDIO_TRACE_HITBOX ) pe->studiomodel = mod; VectorCopy( ent->curstate.mins, pe->mins ); VectorCopy( ent->curstate.maxs, pe->maxs ); break; case SOLID_CUSTOM: pe->model = (mod->type == mod_brush) ? mod : NULL; pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; VectorCopy( ent->curstate.mins, pe->mins ); VectorCopy( ent->curstate.maxs, pe->maxs ); break; default: pe->studiomodel = (mod->type == mod_studio) ? mod : NULL; VectorCopy( ent->curstate.mins, pe->mins ); VectorCopy( ent->curstate.maxs, pe->maxs ); break; } pe->info = (int)(ent - clgame.entities); VectorCopy( ent->curstate.origin, pe->origin ); VectorCopy( ent->curstate.angles, pe->angles ); pe->solid = ent->curstate.solid; pe->rendermode = ent->curstate.rendermode; pe->skin = ent->curstate.skin; pe->frame = ent->curstate.frame; pe->sequence = ent->curstate.sequence; Q_memcpy( &pe->controller[0], &ent->curstate.controller[0], 4 * sizeof( byte )); Q_memcpy( &pe->blending[0], &ent->curstate.blending[0], 2 * sizeof( byte )); pe->movetype = ent->curstate.movetype; pe->takedamage = ( pe->player ) ? DAMAGE_AIM : DAMAGE_YES; pe->team = ent->curstate.team; pe->classnumber = ent->curstate.playerclass; pe->blooddecal = 0; // unused in GoldSrc // for mods pe->iuser1 = ent->curstate.iuser1; pe->iuser2 = ent->curstate.iuser2; pe->iuser3 = ent->curstate.iuser3; pe->iuser4 = ent->curstate.iuser4; pe->fuser1 = ent->curstate.fuser1; pe->fuser2 = ent->curstate.fuser2; pe->fuser3 = ent->curstate.fuser3; pe->fuser4 = ent->curstate.fuser4; VectorCopy( ent->curstate.vuser1, pe->vuser1 ); VectorCopy( ent->curstate.vuser2, pe->vuser2 ); VectorCopy( ent->curstate.vuser3, pe->vuser3 ); VectorCopy( ent->curstate.vuser4, pe->vuser4 ); return true; }