// Copyright (C) 1999-2000 Id Software, Inc. // // cg_ents.c -- present snapshot entities, happens every single frame #include "cg_local.h" /* Ghoul2 Insert Start */ #include "../game/q_shared.h" #include "../ghoul2/G2.h" /* Ghoul2 Insert end */ extern qboolean CG_InFighter( void ); static void CG_Missile( centity_t *cent ); /* ====================== CG_PositionEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis ); entity->backlerp = parent->backlerp; } /* ====================== CG_PositionRotatedEntityOnTag Modifies the entities position and axis by the given tag location ====================== */ void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, qhandle_t parentModel, char *tagName ) { int i; orientation_t lerped; vec3_t tempAxis[3]; //AxisClear( entity->axis ); // lerp the tag trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName ); // FIXME: allow origin offsets along tag? VectorCopy( parent->origin, entity->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); } // had to cast away the const to avoid compiler problems... MatrixMultiply( entity->axis, lerped.axis, tempAxis ); MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis ); } /* ========================================================================== FUNCTIONS CALLED EACH FRAME ========================================================================== */ //only need to use the CG_S_ system when you want a looping sound that isn't going to add itself //via trap each frame. Such as ones generated by events. /* ====================== CG_SetEntitySoundPosition Also called by event processing code ====================== */ void CG_SetEntitySoundPosition( centity_t *cent ) { if ( cent->currentState.solid == SOLID_BMODEL ) { vec3_t origin; float *v; v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, origin ); trap_S_UpdateEntityPosition( cent->currentState.number, origin ); } else { trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin ); } } /* ================== CG_S_AddLoopingSound Set the current looping sounds on the entity. ================== */ void CG_S_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx) { centity_t *cent = &cg_entities[entityNum]; cgLoopSound_t *cSound = NULL; int i = 0; qboolean alreadyPlaying = qfalse; //first see if we're already looping this sound handle. while (i < cent->numLoopingSounds) { cSound = ¢->loopingSound[i]; if (cSound->sfx == sfx) { alreadyPlaying = qtrue; break; } } if (alreadyPlaying && cSound) { //if this is the case, just update the properties of the looping sound and return. VectorCopy(origin, cSound->origin); VectorCopy(velocity, cSound->velocity); } else if (cent->numLoopingSounds >= MAX_CG_LOOPSOUNDS) { //Just don't add it then I suppose. #ifdef _XBOX // We decreased this number, so I'd like to know if it gets overflowed Com_Printf( S_COLOR_YELLOW "Warning: MAX_CG_LOOPSOUNDS exceeded!!\n" ); #endif return; } //Add a new looping sound. cSound = ¢->loopingSound[cent->numLoopingSounds]; cSound->entityNum = entityNum; VectorCopy(origin, cSound->origin); VectorCopy(velocity, cSound->velocity); cSound->sfx = sfx; cent->numLoopingSounds++; } /* ================== CG_S_AddLoopingSound For now just redirect, might eventually do something different. ================== */ void CG_S_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx) { CG_S_AddLoopingSound(entityNum, origin, velocity, sfx); } /* ================== CG_S_AddLoopingSound Clear looping sounds. ================== */ void CG_S_StopLoopingSound(int entityNum, sfxHandle_t sfx) { centity_t *cent = &cg_entities[entityNum]; cgLoopSound_t *cSound; if (sfx == -1) { //clear all the looping sounds on the entity cent->numLoopingSounds = 0; } else { //otherwise, clear only the specified looping sound int i = 0; while (i < cent->numLoopingSounds) { cSound = ¢->loopingSound[i]; if (cSound->sfx == sfx) { //remove it then int x = i+1; while (x < cent->numLoopingSounds) { memcpy(¢->loopingSound[x-1], ¢->loopingSound[x], sizeof(cent->loopingSound[x])); x++; } cent->numLoopingSounds--; } i++; } } //trap_S_StopLoopingSound(entityNum); } /* ================== CG_S_UpdateLoopingSounds Update any existing looping sounds on the entity. ================== */ void CG_S_UpdateLoopingSounds(int entityNum) { centity_t *cent = &cg_entities[entityNum]; cgLoopSound_t *cSound; vec3_t lerpOrg; float *v; int i = 0; if (!cent->numLoopingSounds) { return; } if (cent->currentState.eType == ET_MOVER) { v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, lerpOrg ); } else { VectorCopy(cent->lerpOrigin, lerpOrg); } while (i < cent->numLoopingSounds) { cSound = ¢->loopingSound[i]; //trap_S_AddLoopingSound(entityNum, cSound->origin, cSound->velocity, cSound->sfx); //I guess just keep using lerpOrigin for now, trap_S_AddLoopingSound(entityNum, lerpOrg, cSound->velocity, cSound->sfx); i++; } } /* ================== CG_EntityEffects Add continuous entity effects, like local entity emission and lighting ================== */ static void CG_EntityEffects( centity_t *cent ) { // update sound origins CG_SetEntitySoundPosition( cent ); // add loop sound if ( cent->currentState.loopSound || (cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS) ) { sfxHandle_t realSoundIndex = -1; if (cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS) { //If this is so, then first get our soundset from the index, and loopSound actually contains which part of the set to //use rather than a sound index (BMS_START [0], BMS_MID [1], or BMS_END [2]). Typically loop sounds will be BMS_MID. const char *soundSet; soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex ); if (soundSet && soundSet[0]) { realSoundIndex = trap_AS_GetBModelSound(soundSet, cent->currentState.loopSound); } } else { realSoundIndex = cgs.gameSounds[ cent->currentState.loopSound ]; } //rww - doors and things with looping sounds have a crazy origin (being brush models and all) if (realSoundIndex != -1) { if ( cent->currentState.solid == SOLID_BMODEL ) { vec3_t origin; float *v; v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, origin ); trap_S_AddLoopingSound( cent->currentState.number, origin, vec3_origin, realSoundIndex ); } else if (cent->currentState.eType != ET_SPEAKER) { trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, realSoundIndex ); } else { trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, realSoundIndex ); } } } // constant light glow if ( cent->currentState.constantLight ) { int cl; int i, r, g, b; cl = cent->currentState.constantLight; r = cl & 255; g = ( cl >> 8 ) & 255; b = ( cl >> 16 ) & 255; i = ( ( cl >> 24 ) & 255 ) * 4; trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b ); } } localEntity_t *FX_AddOrientedLine(vec3_t start, vec3_t end, vec3_t normal, float stScale, float scale, float dscale, float startalpha, float endalpha, float killTime, qhandle_t shader) { localEntity_t *le; #ifdef _DEBUG if (!shader) { Com_Printf("FX_AddLine: NULL shader\n"); } #endif le = CG_AllocLocalEntity(); le->leType = LE_OLINE; le->startTime = cg.time; le->endTime = le->startTime + killTime; le->data.line.width = scale; le->data.line.dwidth = dscale; le->alpha = startalpha; le->dalpha = endalpha - startalpha; le->refEntity.data.line.stscale = stScale; le->refEntity.data.line.width = scale; le->refEntity.customShader = shader; // set origin VectorCopy ( start, le->refEntity.origin); VectorCopy ( end, le->refEntity.oldorigin ); AxisClear(le->refEntity.axis); VectorCopy( normal, le->refEntity.axis[0] ); RotateAroundDirection( le->refEntity.axis, 0); // le->refEntity.data.sprite.rotation ); This is roll in quad land le->refEntity.shaderRGBA[0] = 0xff; le->refEntity.shaderRGBA[1] = 0xff; le->refEntity.shaderRGBA[2] = 0xff; le->refEntity.shaderRGBA[3] = 0xff; le->color[0] = 1.0; le->color[1] = 1.0; le->color[2] = 1.0; le->color[3] = 1.0; le->lifeRate = 1.0 / ( le->endTime - le->startTime ); return(le); } void FX_DrawPortableShield(centity_t *cent) { //rww - this code differs a bit from the draw code in EF, I don't know why I had to do //it this way yet it worked in EF the other way. int xaxis, height, posWidth, negWidth, team; vec3_t start, end, normal; localEntity_t *le; qhandle_t shader; char buf[1024]; trap_Cvar_VariableStringBuffer("cl_paused", buf, sizeof(buf)); if (atoi(buf)) { //rww - fix to keep from rendering repeatedly while HUD menu is up return; } if (cent->currentState.eFlags & EF_NODRAW) { return; } // decode the data stored in time2 xaxis = ((cent->currentState.time2 >> 24) & 1); height = ((cent->currentState.time2 >> 16) & 255); posWidth = ((cent->currentState.time2 >> 8) & 255); negWidth = (cent->currentState.time2 & 255); team = (cent->currentState.otherEntityNum2); VectorClear(normal); VectorCopy(cent->lerpOrigin, start); VectorCopy(cent->lerpOrigin, end); if (xaxis) // drawing along x-axis { start[0] -= negWidth; end[0] += posWidth; } else { start[1] -= negWidth; end[1] += posWidth; } normal[0] = 1; normal[1] = 1; start[2] += height/2; end[2] += height/2; if (team == TEAM_RED) { if (cent->currentState.trickedentindex) { shader = trap_R_RegisterShader( "gfx/misc/red_dmgshield" ); } else { shader = trap_R_RegisterShader( "gfx/misc/red_portashield" ); } } else { if (cent->currentState.trickedentindex) { shader = trap_R_RegisterShader( "gfx/misc/blue_dmgshield" ); } else { shader = trap_R_RegisterShader( "gfx/misc/blue_portashield" ); } } le = FX_AddOrientedLine(start, end, normal, 1.0f, height, 0.0f, 1.0f, 1.0f, 50.0, shader); } /* ================== CG_Special ================== */ void CG_Special( centity_t *cent ) { entityState_t *s1; s1 = ¢->currentState; if (!s1) { return; } // if set to invisible, skip if (!s1->modelindex) { return; } if (s1->modelindex == HI_SHIELD) { // The portable shield should go through a different rendering function. FX_DrawPortableShield(cent); return; } } /* Ghoul2 Insert Start */ // Copy the ghoul2 data into the ref ent correctly void CG_SetGhoul2Info( refEntity_t *ent, centity_t *cent) { ent->ghoul2 = cent->ghoul2; VectorCopy( cent->modelScale, ent->modelScale); ent->radius = cent->radius; VectorCopy (cent->lerpAngles, ent->angles); } // create 8 new points on screen around a model so we can see it's bounding box void CG_CreateBBRefEnts(entityState_t *s1, vec3_t origin ) { /* //g2r #if _DEBUG refEntity_t point[8]; int i; vec3_t angles = {0,0,0}; for (i=0; i<8; i++) { memset (&point[i], 0, sizeof(refEntity_t)); point[i].reType = RT_SPRITE; point[i].radius = 1; point[i].customShader = trap_R_RegisterShader("textures/tests/circle"); point[i].shaderRGBA[0] = 255; point[i].shaderRGBA[1] = 255; point[i].shaderRGBA[2] = 255; point[i].shaderRGBA[3] = 255; AnglesToAxis( angles, point[i].axis ); // now, we need to put the correct origins into each origin from the mins and max's switch(i) { case 0: VectorCopy(s1->mins, point[i].origin); break; case 1: VectorCopy(s1->mins, point[i].origin); point[i].origin[0] = s1->maxs[0]; break; case 2: VectorCopy(s1->mins, point[i].origin); point[i].origin[1] = s1->maxs[1]; break; case 3: VectorCopy(s1->mins, point[i].origin); point[i].origin[0] = s1->maxs[0]; point[i].origin[1] = s1->maxs[1]; break; case 4: VectorCopy(s1->maxs, point[i].origin); break; case 5: VectorCopy(s1->maxs, point[i].origin); point[i].origin[0] = s1->mins[0]; break; case 6: VectorCopy(s1->maxs, point[i].origin); point[i].origin[1] = s1->mins[1]; break; case 7: VectorCopy(s1->maxs, point[i].origin); point[i].origin[0] = s1->mins[0]; point[i].origin[1] = s1->mins[1]; break; } // add the original origin to each point and then stuff them out there VectorAdd(point[i].origin, origin, point[i].origin); trap_R_AddRefEntityToScene (&point[i]); } #endif */ } // write in the axis and stuff void G2_BoltToGhoul2Model(centity_t *cent, refEntity_t *ent) { // extract the wraith ID from the bolt info int modelNum = cent->boltInfo >> MODEL_SHIFT; int boltNum = cent->boltInfo >> BOLT_SHIFT; int entNum = cent->boltInfo >> ENTITY_SHIFT; mdxaBone_t boltMatrix; modelNum &= MODEL_AND; boltNum &= BOLT_AND; entNum &= ENTITY_AND; //NOTENOTE I put this here because the cgs.gamemodels array no longer gets initialized. assert(0); // go away and get me the bolt position for this frame please trap_G2API_GetBoltMatrix(cent->ghoul2, modelNum, boltNum, &boltMatrix, cg_entities[entNum].currentState.angles, cg_entities[entNum].currentState.origin, cg.time, cgs.gameModels, cent->modelScale); // set up the axis and origin we need for the actual effect spawning ent->origin[0] = boltMatrix.matrix[0][3]; ent->origin[1] = boltMatrix.matrix[1][3]; ent->origin[2] = boltMatrix.matrix[2][3]; ent->axis[0][0] = boltMatrix.matrix[0][0]; ent->axis[0][1] = boltMatrix.matrix[1][0]; ent->axis[0][2] = boltMatrix.matrix[2][0]; ent->axis[1][0] = boltMatrix.matrix[0][1]; ent->axis[1][1] = boltMatrix.matrix[1][1]; ent->axis[1][2] = boltMatrix.matrix[2][1]; ent->axis[2][0] = boltMatrix.matrix[0][2]; ent->axis[2][1] = boltMatrix.matrix[1][2]; ent->axis[2][2] = boltMatrix.matrix[2][2]; } void ScaleModelAxis(refEntity_t *ent) { // scale the model should we need to if (ent->modelScale[0] && ent->modelScale[0] != 1.0f) { VectorScale( ent->axis[0], ent->modelScale[0] , ent->axis[0] ); ent->nonNormalizedAxes = qtrue; } if (ent->modelScale[1] && ent->modelScale[1] != 1.0f) { VectorScale( ent->axis[1], ent->modelScale[1] , ent->axis[1] ); ent->nonNormalizedAxes = qtrue; } if (ent->modelScale[2] && ent->modelScale[2] != 1.0f) { VectorScale( ent->axis[2], ent->modelScale[2] , ent->axis[2] ); ent->nonNormalizedAxes = qtrue; } } /* Ghoul2 Insert End */ char *forceHolocronModels[] = { "models/map_objects/mp/lt_heal.md3", //FP_HEAL, "models/map_objects/mp/force_jump.md3", //FP_LEVITATION, "models/map_objects/mp/force_speed.md3", //FP_SPEED, "models/map_objects/mp/force_push.md3", //FP_PUSH, "models/map_objects/mp/force_pull.md3", //FP_PULL, "models/map_objects/mp/lt_telepathy.md3", //FP_TELEPATHY, "models/map_objects/mp/dk_grip.md3", //FP_GRIP, "models/map_objects/mp/dk_lightning.md3", //FP_LIGHTNING, "models/map_objects/mp/dk_rage.md3", //FP_RAGE, "models/map_objects/mp/lt_protect.md3", //FP_PROTECT, "models/map_objects/mp/lt_absorb.md3", //FP_ABSORB, "models/map_objects/mp/lt_healother.md3", //FP_TEAM_HEAL, "models/map_objects/mp/dk_powerother.md3", //FP_TEAM_FORCE, "models/map_objects/mp/dk_drain.md3", //FP_DRAIN, "models/map_objects/mp/force_sight.md3", //FP_SEE, "models/map_objects/mp/saber_attack.md3", //FP_SABER_OFFENSE, "models/map_objects/mp/saber_defend.md3", //FP_SABER_DEFENSE, "models/map_objects/mp/saber_throw.md3" //FP_SABERTHROW }; void CG_Disintegration(centity_t *cent, refEntity_t *ent) { vec3_t tempAng, hitLoc; float tempLength; VectorCopy(cent->currentState.origin2, hitLoc); VectorSubtract( hitLoc, ent->origin, ent->oldorigin ); tempLength = VectorNormalize( ent->oldorigin ); vectoangles( ent->oldorigin, tempAng ); tempAng[YAW] -= cent->lerpAngles[YAW]; AngleVectors( tempAng, ent->oldorigin, NULL, NULL ); VectorScale( ent->oldorigin, tempLength, ent->oldorigin ); ent->endTime = cent->dustTrailTime; ent->renderfx |= RF_DISINTEGRATE2; ent->customShader = cgs.media.disruptorShader; trap_R_AddRefEntityToScene( ent ); ent->renderfx &= ~(RF_DISINTEGRATE2); ent->renderfx |= (RF_DISINTEGRATE1); ent->customShader = 0; trap_R_AddRefEntityToScene( ent ); if ( cg.time - ent->endTime < 1000 && (cg_timescale.value * cg_timescale.value * random()) > 0.05f ) { vec3_t fxOrg, fxDir; mdxaBone_t boltMatrix; int torsoBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar"); VectorSet(fxDir, 0, 1, 0); trap_G2API_GetBoltMatrix( cent->ghoul2, 0, torsoBolt, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, fxOrg ); VectorMA( fxOrg, -18, cg.refdef.viewaxis[0], fxOrg ); fxOrg[2] += crandom() * 20; trap_FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1 ); if ( random() > 0.5f ) { trap_FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1 ); } } } extern int cgSiegeEntityRender; static qboolean CG_RenderTimeEntBolt(centity_t *cent) { int clientNum = cent->currentState.boltToPlayer-1; centity_t *cl; mdxaBone_t matrix; vec3_t boltOrg, boltAng; int getBolt = -1; if (clientNum >= MAX_CLIENTS || clientNum < 0) { assert(0); return qfalse; } cl = &cg_entities[clientNum]; if (!cl->ghoul2) { assert(0); return qfalse; } if (clientNum == cg.predictedPlayerState.clientNum && !cg.renderingThirdPerson) { //If in first person and you have it then render the thing spinning around on your hud. cgSiegeEntityRender = cent->currentState.number; //set it to render at the end of the frame. return qfalse; } getBolt = trap_G2API_AddBolt(cl->ghoul2, 0, "lhand"); trap_G2API_GetBoltMatrix(cl->ghoul2, 0, getBolt, &matrix, cl->turAngles, cl->lerpOrigin, cg.time, cgs.gameModels, cl->modelScale); BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg); BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng); vectoangles(boltAng, boltAng); boltAng[PITCH] = boltAng[ROLL] = 0; VectorCopy(boltOrg, cent->lerpOrigin); VectorCopy(boltAng, cent->lerpAngles); return qtrue; } /* static void CG_SiegeEntRenderAboveHead(centity_t *cent) { int clientNum = cent->currentState.boltToPlayer-1; centity_t *cl; refEntity_t ent; vec3_t renderAngles; if (clientNum >= MAX_CLIENTS || clientNum < 0) { assert(0); return; } cl = &cg_entities[clientNum]; memset(&ent, 0, sizeof(ent)); //Set the angles to the global auto rotating ones, and the origin to slightly above the client VectorCopy(cg.autoAngles, renderAngles); AnglesToAxis( renderAngles, ent.axis ); VectorCopy(cl->lerpOrigin, ent.origin); ent.origin[2] += 50; //Set the model (ghoul2 or md3/other) if (cent->ghoul2) { ent.ghoul2 = cent->ghoul2; ent.hModel = 0; } else { ent.ghoul2 = NULL; ent.hModel = cgs.gameModels[cent->currentState.modelindex]; } //Scale it up ent.modelScale[0] = 1.5f; ent.modelScale[1] = 1.5f; ent.modelScale[2] = 1.5f; ScaleModelAxis(&ent); //Make it transparent ent.renderfx = RF_FORCE_ENT_ALPHA; ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 100; //And finally add it trap_R_AddRefEntityToScene(&ent); } */ void CG_AddRadarEnt(centity_t *cent) { if (cg.radarEntityCount == sizeof(cg.radarEntities)/sizeof(cg.radarEntities[0])) { #ifdef _DEBUG Com_Printf("^3Warning: CG_AddRadarEnt full. (%d max)\n", sizeof(cg.radarEntities)/sizeof(cg.radarEntities[0])); #endif return; } cg.radarEntities[cg.radarEntityCount++] = cent->currentState.number; } void CG_AddBracketedEnt(centity_t *cent) { if (cg.bracketedEntityCount == sizeof(cg.bracketedEntities)/sizeof(cg.bracketedEntities[0])) { #ifdef _DEBUG Com_Printf("^3Warning: CG_AddBracketedEnt full. (%d max)\n", sizeof(cg.radarEntities)/sizeof(cg.bracketedEntities[0])); #endif return; } cg.bracketedEntities[cg.bracketedEntityCount++] = cent->currentState.number; } /* ================== CG_General ================== */ void CG_G2ServerBoneAngles(centity_t *cent); #include "../namespace_begin.h" extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize ); #include "../namespace_end.h" static void CG_General( centity_t *cent ) { refEntity_t ent; entityState_t *s1; float val; int beamID; vec3_t beamOrg; mdxaBone_t matrix; qboolean doNotSetModel = qfalse; if (cent->currentState.modelGhoul2 == 127) { //not ready to be drawn or initialized.. return; } if (cent->ghoul2 && !cent->currentState.modelGhoul2 && cent->currentState.eType != ET_BODY && cent->currentState.number >= MAX_CLIENTS) { //this is a bad thing if (trap_G2_HaveWeGhoul2Models(cent->ghoul2)) { trap_G2API_CleanGhoul2Models(&(cent->ghoul2)); } } if (cent->currentState.eFlags & EF_RADAROBJECT) { CG_AddRadarEnt(cent); } if (cent->currentState.eFlags2 & EF2_BRACKET_ENTITY) { if ( CG_InFighter() ) {//only bracken when in a fighter CG_AddBracketedEnt(cent); } } if (cent->currentState.boltToPlayer) { //Shove it into the player's left hand then. centity_t *pl = &cg_entities[cent->currentState.boltToPlayer-1]; if (CG_IsMindTricked(pl->currentState.trickedentindex, pl->currentState.trickedentindex2, pl->currentState.trickedentindex3, pl->currentState.trickedentindex4, cg.predictedPlayerState.clientNum)) { //don't show if this guy is mindtricking return; } if (!CG_RenderTimeEntBolt(cent)) { //If this function returns qfalse we shouldn't render this ent at all. if (cent->currentState.boltToPlayer > 0 && cent->currentState.boltToPlayer <= MAX_CLIENTS) { VectorCopy(pl->lerpOrigin, cent->lerpOrigin); if (cent->currentState.eFlags & EF_CLIENTSMOOTH) { //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted. VectorCopy(cent->lerpOrigin, cent->turAngles); } } return; } if (cent->currentState.eFlags & EF_CLIENTSMOOTH) { //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted. VectorCopy(cent->lerpOrigin, cent->turAngles); } /* disabled for now if (pl->currentState.number != cg.predictedPlayerState.clientNum) { //don't render thing above head to self CG_SiegeEntRenderAboveHead(cent); } */ } else if (cent->currentState.eFlags & EF_CLIENTSMOOTH) { if (cent->currentState.groundEntityNum >= ENTITYNUM_WORLD) { float smoothFactor = 0.5f*cg_timescale.value; int k = 0; vec3_t posDif; //Use origin smoothing since dismembered limbs use ExPhys if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f) { VectorCopy(cent->lerpOrigin, cent->turAngles); } VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif); for (k=0;k<3;k++) { cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor); cent->lerpOrigin[k]=cent->turAngles[k]; } } else { //if we're sitting on an entity like a moving plat then we don't want to smooth either VectorCopy(cent->lerpOrigin, cent->turAngles); } } //rww - now do ragdoll stuff if (cent->ghoul2 && (cent->currentState.eType == ET_BODY || (cent->currentState.eFlags & EF_RAG))) { if (!(cent->currentState.eFlags & EF_NODRAW) && !(cent->currentState.eFlags & EF_DISINTEGRATION) && cent->bodyFadeTime <= cg.time) { vec3_t forcedAngles; VectorClear(forcedAngles); forcedAngles[YAW] = cent->lerpAngles[YAW]; CG_RagDoll(cent, forcedAngles); } } else if (cent->isRagging) { cent->isRagging = qfalse; if (cent->ghoul2 && trap_G2_HaveWeGhoul2Models(cent->ghoul2)) { //May not be valid, in the case of a ragged entity being removed and a non-g2 ent filling its slot. trap_G2API_SetRagDoll(cent->ghoul2, NULL); //calling with null parms resets to no ragdoll. } } if (cent->currentState.boneOrient && cent->ghoul2) { //server sent us some bone angles to use CG_G2ServerBoneAngles(cent); } if ((cent->currentState.eFlags & EF_G2ANIMATING) && cent->ghoul2) { //mini-animation routine for general objects that want to play quick ghoul2 anims //obviously lacks much of the functionality contained in player/npc animation. //we actually use torsoAnim as the start frame and legsAnim as the end frame and //always play the anim on the root bone. if (cent->currentState.torsoAnim != cent->pe.torso.animationNumber || cent->currentState.legsAnim != cent->pe.legs.animationNumber || cent->currentState.torsoFlip != cent->pe.torso.lastFlip) { trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", cent->currentState.torsoAnim, cent->currentState.legsAnim, (BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND), 1.0f, cg.time, -1, 100); cent->pe.torso.animationNumber = cent->currentState.torsoAnim; cent->pe.legs.animationNumber = cent->currentState.legsAnim; cent->pe.torso.lastFlip = cent->currentState.torsoFlip; } } memset (&ent, 0, sizeof(ent)); ent.shaderRGBA[0] = cent->currentState.customRGBA[0]; ent.shaderRGBA[1] = cent->currentState.customRGBA[1]; ent.shaderRGBA[2] = cent->currentState.customRGBA[2]; ent.shaderRGBA[3] = cent->currentState.customRGBA[3]; if (cent->currentState.modelGhoul2 >= G2_MODELPART_HEAD && cent->currentState.modelGhoul2 <= G2_MODELPART_RLEG && /*cent->currentState.modelindex < MAX_CLIENTS &&*/ cent->currentState.weapon == G2_MODEL_PART) { //special case for client limbs centity_t *clEnt; int dismember_settings = cg_dismember.integer; float smoothFactor = 0.5f*cg_timescale.value; int k = 0; vec3_t posDif; doNotSetModel = qtrue; if (cent->currentState.modelindex >= 0) { clEnt = &cg_entities[cent->currentState.modelindex]; } else { clEnt = &cg_entities[cent->currentState.otherEntityNum2]; } if (!dismember_settings) { //This client does not wish to see dismemberment. return; } if (dismember_settings < 2 && (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)) { //dismember settings are not high enough to display decaps and torso slashes return; } if (!cent->ghoul2) { const char *limbBone; const char *rotateBone; char limbName[MAX_QPATH]; char stubName[MAX_QPATH]; char limbCapName[MAX_QPATH]; char stubCapName[MAX_QPATH]; char *limbTagName; char *stubTagName; int limb_anim; int newBolt; int limbBit = (1 << (cent->currentState.modelGhoul2-10)); if (clEnt && (clEnt->torsoBolt & limbBit)) { //already have this limb missing! return; } if (clEnt && !(clEnt->currentState.eFlags & EF_DEAD)) { //death flag hasn't made it through yet for the limb owner, we cannot create the limb until he's flagged as dead return; } if (clEnt && (!BG_InDeathAnim(clEnt->currentState.torsoAnim) || !BG_InDeathAnim(clEnt->pe.torso.animationNumber))) { //don't make it unless we're in an actual death anim already if (clEnt->currentState.torsoAnim != BOTH_RIGHTHANDCHOPPEDOFF) { //exception return; } } cent->bolt4 = -1; cent->trailTime = 0; if (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD) { limbBone = "cervical"; rotateBone = "cranium"; Q_strncpyz( limbName , "head", sizeof( limbName ) ); Q_strncpyz( limbCapName, "head_cap_torso", sizeof( limbCapName ) ); Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) ); limbTagName = "*head_cap_torso"; stubTagName = "*torso_cap_head"; limb_anim = BOTH_DISMEMBER_HEAD1; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST) { limbBone = "pelvis"; if (clEnt->localAnimIndex <= 1) { //humanoid/rtrooper rotateBone = "thoracic"; } else { rotateBone = "pelvis"; } Q_strncpyz( limbName, "torso", sizeof( limbName ) ); Q_strncpyz( limbCapName, "torso_cap_hips", sizeof( limbCapName ) ); Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) ); limbTagName = "*torso_cap_hips"; stubTagName = "*hips_cap_torso"; limb_anim = BOTH_DISMEMBER_TORSO1; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_LARM) { limbBone = "lhumerus"; rotateBone = "lradius"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_arm", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName ); limbTagName = "*l_arm_cap_torso"; stubTagName = "*torso_cap_l_arm"; limb_anim = BOTH_DISMEMBER_LARM; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM) { limbBone = "rhumerus"; rotateBone = "rradius"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName ); limbTagName = "*r_arm_cap_torso"; stubTagName = "*torso_cap_r_arm"; limb_anim = BOTH_DISMEMBER_RARM; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_RHAND) { limbBone = "rradiusX"; rotateBone = "rhand"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_hand", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_r_arm", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName ); limbTagName = "*r_hand_cap_r_arm"; stubTagName = "*r_arm_cap_r_hand"; limb_anim = BOTH_DISMEMBER_RARM; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_LLEG) { limbBone = "lfemurYZ"; rotateBone = "ltibia"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_leg", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName ); limbTagName = "*l_leg_cap_hips"; stubTagName = "*hips_cap_l_leg"; limb_anim = BOTH_DISMEMBER_LLEG; } else if (cent->currentState.modelGhoul2 == G2_MODELPART_RLEG) { limbBone = "rfemurYZ"; rotateBone = "rtibia"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName ); limbTagName = "*r_leg_cap_hips"; stubTagName = "*hips_cap_r_leg"; limb_anim = BOTH_DISMEMBER_RLEG; } else {//umm... just default to the right leg, I guess (same as on server) limbBone = "rfemurYZ"; rotateBone = "rtibia"; BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) ); BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) ); Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName ); Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName ); limbTagName = "*r_leg_cap_hips"; stubTagName = "*hips_cap_r_leg"; limb_anim = BOTH_DISMEMBER_RLEG; } if (clEnt && clEnt->ghoul2) { if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 2)) { //don't want to bother dealing with a second saber on limbs and stuff, just remove the thing trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 2); } if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 3)) { //turn off jetpack also I suppose trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 3); } if (clEnt->localAnimIndex <= 0) { //humanoid trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time); } else { trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); } trap_G2API_DuplicateGhoul2Instance(clEnt->ghoul2, ¢->ghoul2); } if (!cent->ghoul2) { return; } newBolt = trap_G2API_AddBolt( cent->ghoul2, 0, limbTagName ); if ( newBolt != -1 ) { vec3_t boltOrg, boltAng; trap_G2API_GetBoltMatrix(cent->ghoul2, 0, newBolt, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale); BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg); BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng); trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1); } cent->bolt4 = newBolt; trap_G2API_SetRootSurface(cent->ghoul2, 0, limbName); trap_G2API_SetNewOrigin(cent->ghoul2, trap_G2API_AddBolt(cent->ghoul2, 0, rotateBone)); trap_G2API_SetSurfaceOnOff(cent->ghoul2, limbCapName, 0); trap_G2API_SetSurfaceOnOff(clEnt->ghoul2, limbName, 0x00000100); trap_G2API_SetSurfaceOnOff(clEnt->ghoul2, stubCapName, 0); newBolt = trap_G2API_AddBolt( clEnt->ghoul2, 0, stubTagName ); if ( newBolt != -1 ) { vec3_t boltOrg, boltAng; trap_G2API_GetBoltMatrix(clEnt->ghoul2, 0, newBolt, &matrix, clEnt->lerpAngles, clEnt->lerpOrigin, cg.time, cgs.gameModels, clEnt->modelScale); BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg); BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng); trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1); } if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM || cent->currentState.modelGhoul2 == G2_MODELPART_RHAND || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST) { //Cut his weapon holding arm off, so remove the weapon if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 1)) { trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 1); } } clEnt->torsoBolt |= limbBit; //reinit model after copying limbless one to queue //This causes issues after respawning.. just keep track of limbs cut/made on server or something. /* if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST) { clEnt->torsoBolt |= (1 << (G2_MODELPART_HEAD-10)); clEnt->torsoBolt |= (1 << (G2_MODELPART_RARM-10)); clEnt->torsoBolt |= (1 << (G2_MODELPART_LARM-10)); clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10)); } else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM) { clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10)); } */ VectorCopy(cent->lerpOrigin, cent->turAngles); // return; } //Use origin smoothing since dismembered limbs use ExPhys if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f) { VectorCopy(cent->lerpOrigin, cent->turAngles); } VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif); for (k=0;k<3;k++) { cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor); cent->lerpOrigin[k]=cent->turAngles[k]; } if (cent->ghoul2 && cent->bolt4 != -1 && cent->trailTime < cg.time) { if ( cent->bolt4 != -1 && (cent->currentState.pos.trDelta[0] || cent->currentState.pos.trDelta[1] || cent->currentState.pos.trDelta[2]) ) { vec3_t boltOrg, boltAng; trap_G2API_GetBoltMatrix(cent->ghoul2, 0, cent->bolt4, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale); BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg); BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng); if (!boltAng[0] && !boltAng[1] && !boltAng[2]) { boltAng[1] = 1; } trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1); cent->trailTime = cg.time + 400; } } ent.radius = cent->currentState.g2radius; ent.hModel = 0; } if (cent->currentState.number >= MAX_CLIENTS && cent->currentState.activeForcePass == NUM_FORCE_POWERS+1&& cent->currentState.NPC_class != CLASS_VEHICLE ) { vec3_t empAngles; centity_t *empOwn; empOwn = &cg_entities[cent->currentState.emplacedOwner]; if (cg.snap->ps.clientNum == empOwn->currentState.number && !cg.renderingThirdPerson) { VectorCopy(cg.refdef.viewangles, empAngles); } else { VectorCopy(empOwn->lerpAngles, empAngles); } if (empAngles[PITCH] > 40) { empAngles[PITCH] = 40; } empAngles[YAW] -= cent->currentState.angles[YAW]; trap_G2API_SetBoneAngles( cent->ghoul2, 0, "Bone02", empAngles, BONE_ANGLES_REPLACE, NEGATIVE_Y, NEGATIVE_X, POSITIVE_Z, NULL, 0, cg.time); } s1 = ¢->currentState; // if set to invisible, skip if ((!s1->modelindex) && !(trap_G2_HaveWeGhoul2Models(cent->ghoul2))) { return; } if ( ( s1->eFlags & EF_NODRAW ) ) { return; } // set frame if ( s1->eFlags & EF_SHADER_ANIM ) { // Deliberately setting it up so that shader anim will completely override any kind of model animation frame setting. ent.renderfx|=RF_SETANIMINDEX; ent.skinNum = s1->frame; } else { ent.frame = s1->frame; } ent.oldframe = ent.frame; ent.backlerp = 0; /* Ghoul2 Insert Start */ CG_SetGhoul2Info(&ent, cent); /* Ghoul2 Insert End */ VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); if (cent->currentState.modelGhoul2) { //If the game says this guy uses a ghoul2 model and the g2 instance handle is null, then initialize it if (!cent->ghoul2 && !cent->currentState.bolt1) { char skinName[MAX_QPATH]; const char *modelName = CG_ConfigString( CS_MODELS+cent->currentState.modelindex ); int l; int skin = 0; trap_G2API_InitGhoul2Model(¢->ghoul2, modelName, 0, 0, 0, 0, 0); if (cent->ghoul2 && trap_G2API_SkinlessModel(cent->ghoul2, 0)) { //well, you'd never want a skinless model, so try to get his skin... Q_strncpyz(skinName, modelName, MAX_QPATH); l = strlen(skinName); while (l > 0 && skinName[l] != '/') { //parse back to first / l--; } if (skinName[l] == '/') { //got it l++; skinName[l] = 0; Q_strcat(skinName, MAX_QPATH, "model_default.skin"); skin = trap_R_RegisterSkin(skinName); } trap_G2API_SetSkin(cent->ghoul2, 0, skin, skin); } } else if (cent->currentState.bolt1) { TurretClientRun(cent); } if (cent->ghoul2) { //give us a proper radius ent.radius = cent->currentState.g2radius; } } if (s1->eType == ET_BODY) { //bodies should have a radius as well ent.radius = cent->currentState.g2radius; if (cent->ghoul2) { //all bodies should already have a ghoul2 instance. Use it to set the torso/head angles to 0. cent->lerpAngles[PITCH] = 0; cent->lerpAngles[ROLL] = 0; trap_G2API_SetBoneAngles(cent->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time); trap_G2API_SetBoneAngles(cent->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time); trap_G2API_SetBoneAngles(cent->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(cent->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time); trap_G2API_SetBoneAngles(cent->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time); } } if (s1->eType == ET_HOLOCRON && s1->modelindex < -100) { //special render, it's a holocron //Using actual models now: ent.hModel = trap_R_RegisterModel(forceHolocronModels[s1->modelindex+128]); //Rotate them VectorCopy( cg.autoAngles, cent->lerpAngles ); AxisCopy( cg.autoAxis, ent.axis ); } else if (!doNotSetModel) { ent.hModel = cgs.gameModels[s1->modelindex]; } // player model if (s1->number == cg.snap->ps.clientNum) { ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors } // convert angles to axis AnglesToAxis( cent->lerpAngles, ent.axis ); if (cent->currentState.iModelScale) { //if the server says we have a custom scale then set it now. if ( cent->currentState.legsFlip ) {//scalar cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale; } else {//percentage cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale/100.0f; } VectorCopy(cent->modelScale, ent.modelScale); ScaleModelAxis(&ent); } else { VectorClear(cent->modelScale); } if ( cent->currentState.time > cg.time && cent->currentState.weapon == WP_EMPLACED_GUN ) { // make the gun pulse red to warn about it exploding val = (1.0f - (float)(cent->currentState.time - cg.time) / 3200.0f ) * 0.3f; ent.customShader = trap_R_RegisterShader( "gfx/effects/turretflashdie" ); ent.shaderRGBA[0] = (sin( cg.time * 0.04f ) * val * 0.4f + val) * 255; ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0; ent.shaderRGBA[3] = 100; trap_R_AddRefEntityToScene( &ent ); ent.customShader = 0; } else if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_EMPLACED_GUN) { ent.customShader = trap_R_RegisterShader( "models/map_objects/imp_mine/turret_chair_dmg.tga" ); //trap_R_AddRefEntityToScene( &ent ); } if ((cent->currentState.eFlags & EF_DISINTEGRATION) && cent->currentState.eType == ET_BODY) { if (!cent->dustTrailTime) { cent->dustTrailTime = cg.time; } CG_Disintegration(cent, &ent); return; } else if (cent->currentState.eType == ET_BODY) { if (cent->bodyFadeTime > cg.time) { qboolean lightSide = cent->teamPowerType; vec3_t hitLoc, tempAng; float tempLength; int curTimeDif = ((cg.time + 60000) - cent->bodyFadeTime); int tMult = curTimeDif*0.08; ent.renderfx |= RF_FORCE_ENT_ALPHA; /* if (!cent->bodyHeight) { cent->bodyHeight = ent.origin[2]; } */ if (curTimeDif*0.1 > 254) { ent.shaderRGBA[3] = 0; } else { ent.shaderRGBA[3] = (254 - tMult); } if (ent.shaderRGBA[3] >= 1) { //add the transparent body section trap_R_AddRefEntityToScene (&ent); } ent.renderfx &= ~RF_FORCE_ENT_ALPHA; ent.renderfx |= RF_RGB_TINT; if (tMult > 200) { //begin the disintegration effect ent.shaderRGBA[3] = 200; if (!cent->dustTrailTime) { cent->dustTrailTime = cg.time; if (lightSide) { trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/see.wav") ); } else { trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/lightning") ); } } ent.endTime = cent->dustTrailTime; ent.renderfx |= RF_DISINTEGRATE2; } else { //set the alpha on the to-be-disintegrated layer ent.shaderRGBA[3] = tMult; if (ent.shaderRGBA[3] < 1) { ent.shaderRGBA[3] = 1; } } //Set everything up on the disint ref ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = ent.shaderRGBA[3]; VectorCopy(cent->lerpOrigin, hitLoc); VectorSubtract( hitLoc, ent.origin, ent.oldorigin ); tempLength = VectorNormalize( ent.oldorigin ); vectoangles( ent.oldorigin, tempAng ); tempAng[YAW] -= cent->lerpAngles[YAW]; AngleVectors( tempAng, ent.oldorigin, NULL, NULL ); VectorScale( ent.oldorigin, tempLength, ent.oldorigin ); if (lightSide) { //might be temporary, dunno. ent.customShader = cgs.media.playerShieldDamage; } else { ent.customShader = cgs.media.redSaberGlowShader; } //slowly move the glowing part upward, out of the fading body /* cent->bodyHeight += 0.4f; ent.origin[2] = cent->bodyHeight; */ trap_R_AddRefEntityToScene( &ent ); ent.renderfx &= ~RF_DISINTEGRATE2; ent.customShader = 0; if (curTimeDif < 3400) { if (lightSide) { if (curTimeDif < 2200) { //probably temporary trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ) ); } } else { //probably temporary as well ent.renderfx |= RF_RGB_TINT; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0; ent.shaderRGBA[3] = 255; if ( rand() & 1 ) { ent.customShader = cgs.media.electricBodyShader; } else { ent.customShader = cgs.media.electricBody2Shader; } if ( random() > 0.9f ) { trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.crackleSound ); } trap_R_AddRefEntityToScene( &ent ); } } return; } else { cent->dustTrailTime = 0; } } if (cent->currentState.modelGhoul2 && !ent.ghoul2 && !ent.hModel) { return; } // add to refresh list trap_R_AddRefEntityToScene (&ent); if (cent->bolt3 == 999) { //this is an in-flight saber being rendered manually vec3_t org; float wv; int i; addspriteArgStruct_t fxSArgs; //refEntity_t sRef; //memcpy( &sRef, &ent, sizeof( sRef ) ); ent.customShader = cgs.media.solidWhite; ent.renderfx = RF_RGB_TINT; wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f; ent.shaderRGBA[0] = wv * 255; ent.shaderRGBA[1] = wv * 255; ent.shaderRGBA[2] = wv * 0; trap_R_AddRefEntityToScene (&ent); for ( i = -4; i < 10; i += 1 ) { VectorMA( ent.origin, -i, ent.axis[2], org ); VectorCopy(org, fxSArgs.origin); VectorClear(fxSArgs.vel); VectorClear(fxSArgs.accel); fxSArgs.scale = 5.5f; fxSArgs.dscale = 5.5f; fxSArgs.sAlpha = wv; fxSArgs.eAlpha = wv; fxSArgs.rotation = 0.0f; fxSArgs.bounce = 0.0f; fxSArgs.life = 1.0f; fxSArgs.shader = cgs.media.yellowDroppedSaberShader; fxSArgs.flags = 0x08000000; //trap_FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 ); trap_FX_AddSprite(&fxSArgs); } } else if (cent->currentState.trickedentindex3) { //holocron special effects vec3_t org; float wv; addspriteArgStruct_t fxSArgs; //refEntity_t sRef; //memcpy( &sRef, &ent, sizeof( sRef ) ); ent.customShader = cgs.media.solidWhite; ent.renderfx = RF_RGB_TINT; wv = sin( cg.time * 0.005f ) * 0.08f + 0.1f; //* 0.08f + 0.1f; if (cent->currentState.trickedentindex3 == 1) { //dark ent.shaderRGBA[0] = wv*255; ent.shaderRGBA[1] = 0; ent.shaderRGBA[2] = 0; } else if (cent->currentState.trickedentindex3 == 2) { //light ent.shaderRGBA[0] = wv*255; ent.shaderRGBA[1] = wv*255; ent.shaderRGBA[2] = wv*255; } else { //neutral if ((s1->modelindex+128) == FP_SABER_OFFENSE || (s1->modelindex+128) == FP_SABER_DEFENSE || (s1->modelindex+128) == FP_SABERTHROW) { //saber power ent.shaderRGBA[0] = 0; ent.shaderRGBA[1] = wv*255; ent.shaderRGBA[2] = 0; } else { ent.shaderRGBA[0] = 0; ent.shaderRGBA[1] = wv*255; ent.shaderRGBA[2] = wv*255; } } ent.modelScale[0] = 1.1; ent.modelScale[1] = 1.1; ent.modelScale[2] = 1.1; ent.origin[2] -= 2; ScaleModelAxis(&ent); trap_R_AddRefEntityToScene (&ent); VectorMA( ent.origin, 1, ent.axis[2], org ); org[2] += 18; wv = sin( cg.time * 0.002f ) * 0.08f + 0.1f; //* 0.08f + 0.1f; VectorCopy(org, fxSArgs.origin); VectorClear(fxSArgs.vel); VectorClear(fxSArgs.accel); fxSArgs.scale = wv*120;//16.0f; fxSArgs.dscale = wv*120;//16.0f; fxSArgs.sAlpha = wv*12; fxSArgs.eAlpha = wv*12; fxSArgs.rotation = 0.0f; fxSArgs.bounce = 0.0f; fxSArgs.life = 1.0f; fxSArgs.flags = 0x08000000|0x00000001; if (cent->currentState.trickedentindex3 == 1) { //dark fxSArgs.sAlpha *= 3; fxSArgs.eAlpha *= 3; fxSArgs.shader = cgs.media.redSaberGlowShader; trap_FX_AddSprite(&fxSArgs); } else if (cent->currentState.trickedentindex3 == 2) { //light fxSArgs.sAlpha *= 1.5; fxSArgs.eAlpha *= 1.5; fxSArgs.shader = cgs.media.redSaberGlowShader; trap_FX_AddSprite(&fxSArgs); fxSArgs.shader = cgs.media.greenSaberGlowShader; trap_FX_AddSprite(&fxSArgs); fxSArgs.shader = cgs.media.blueSaberGlowShader; trap_FX_AddSprite(&fxSArgs); } else { //neutral if ((s1->modelindex+128) == FP_SABER_OFFENSE || (s1->modelindex+128) == FP_SABER_DEFENSE || (s1->modelindex+128) == FP_SABERTHROW) { //saber power fxSArgs.sAlpha *= 1.5; fxSArgs.eAlpha *= 1.5; fxSArgs.shader = cgs.media.greenSaberGlowShader; trap_FX_AddSprite(&fxSArgs); } else { fxSArgs.sAlpha *= 0.5; fxSArgs.eAlpha *= 0.5; fxSArgs.shader = cgs.media.greenSaberGlowShader; trap_FX_AddSprite(&fxSArgs); fxSArgs.shader = cgs.media.blueSaberGlowShader; trap_FX_AddSprite(&fxSArgs); } } } if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_TRIP_MINE && (cent->currentState.eFlags & EF_FIRING) ) { //if force sight is active, render the laser multiple times up to the force sight level to increase visibility if (cent->currentState.bolt2 == 1) { VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward beamID = cgs.effects.tripmineGlowFX; trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 ); } else { int i = 0; VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward beamID = cgs.effects.tripmineLaserFX; if (cg.snap->ps.fd.forcePowersActive & (1 << FP_SEE)) { i = cg.snap->ps.fd.forcePowerLevel[FP_SEE]; while (i > 0) { trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 ); trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 ); i--; } } trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 ); } } /* Ghoul2 Insert Start */ if (cg_debugBB.integer) { CG_CreateBBRefEnts(s1, cent->lerpOrigin); } /* Ghoul2 Insert End */ } /* ================== CG_Speaker Speaker entities can automatically play sounds ================== */ static void CG_Speaker( centity_t *cent ) { if (cent->currentState.trickedentindex) { CG_S_StopLoopingSound(cent->currentState.number, -1); } if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum... return; // not auto triggering } if ( cg.time < cent->miscTime ) { return; } trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] ); // ent->s.frame = ent->wait * 10; // ent->s.clientNum = ent->random * 10; cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom(); } qboolean CG_GreyItem(int type, int tag, int plSide) { if (type == IT_POWERUP && (tag == PW_FORCE_ENLIGHTENED_LIGHT || tag == PW_FORCE_ENLIGHTENED_DARK)) { if (plSide == FORCE_LIGHTSIDE) { if (tag == PW_FORCE_ENLIGHTENED_DARK) { return qtrue; } } else if (plSide == FORCE_DARKSIDE) { if (tag == PW_FORCE_ENLIGHTENED_LIGHT) { return qtrue; } } } return qfalse; } /* ================== CG_Item ================== */ static void CG_Item( centity_t *cent ) { refEntity_t ent; entityState_t *es; gitem_t *item; int msec; float scale; weaponInfo_t *wi; es = ¢->currentState; if ( es->modelindex >= bg_numItems ) { CG_Error( "Bad item index %i on entity", es->modelindex ); } /* Ghoul2 Insert Start */ if ((es->eFlags & EF_NODRAW) && (es->eFlags & EF_ITEMPLACEHOLDER)) { es->eFlags &= ~EF_NODRAW; } if ( !es->modelindex ) { return; } item = &bg_itemlist[ es->modelindex ]; if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) && !(cent->currentState.eFlags & EF_DROPPEDWEAPON) && !cg_simpleItems.integer) { vec3_t uNorm; qboolean doGrey; VectorClear(uNorm); uNorm[2] = 1; memset( &ent, 0, sizeof( ent ) ); ent.customShader = 0; VectorCopy(cent->lerpOrigin, ent.origin); VectorCopy( cent->currentState.angles, cent->lerpAngles ); AnglesToAxis(cent->lerpAngles, ent.axis); ent.hModel = cgs.media.itemHoloModel; doGrey = CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide); if (doGrey) { ent.renderfx |= RF_RGB_TINT; ent.shaderRGBA[0] = 150; ent.shaderRGBA[1] = 150; ent.shaderRGBA[2] = 150; } trap_R_AddRefEntityToScene(&ent); if (!doGrey) { trap_FX_PlayEffectID(cgs.effects.itemCone, ent.origin, uNorm, -1, -1); } } // if set to invisible, skip if ( ( es->eFlags & EF_NODRAW ) ) { return; } /* Ghoul2 Insert End */ if ( cg_simpleItems.integer && item->giType != IT_TEAM ) { memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; VectorCopy( cent->lerpOrigin, ent.origin ); ent.radius = 14; ent.customShader = cg_items[es->modelindex].icon; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.origin[2] += 16; if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON) { ent.renderfx |= RF_FORCE_ENT_ALPHA; } if ( es->eFlags & EF_ITEMPLACEHOLDER ) { if (item->giType == IT_POWERUP && item->giTag == PW_FORCE_BOON) { return; } ent.shaderRGBA[0] = 200; ent.shaderRGBA[1] = 200; ent.shaderRGBA[2] = 200; ent.shaderRGBA[3] = 150 + sin(cg.time*0.01)*30; } else { ent.shaderRGBA[3] = 255; } if (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide)) { ent.shaderRGBA[0] = 100; ent.shaderRGBA[1] = 100; ent.shaderRGBA[2] = 100; ent.shaderRGBA[3] = 200; if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT) { ent.customShader = trap_R_RegisterShader("gfx/misc/mp_light_enlight_disable"); } else { ent.customShader = trap_R_RegisterShader("gfx/misc/mp_dark_enlight_disable"); } } trap_R_AddRefEntityToScene(&ent); return; } if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) && !(cent->currentState.eFlags & EF_DROPPEDWEAPON)) { cent->lerpOrigin[2] += 16; } if ((!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) && (item->giType == IT_WEAPON || item->giType == IT_POWERUP)) { // items bob up and down continuously scale = 0.005 + cent->currentState.number * 0.00001; cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4; } else { if (item->giType == IT_HOLDABLE) { if (item->giTag == HI_SEEKER) { cent->lerpOrigin[2] += 5; } if (item->giTag == HI_SHIELD) { cent->lerpOrigin[2] += 2; } if (item->giTag == HI_BINOCULARS) { cent->lerpOrigin[2] += 2; } } if (item->giType == IT_HEALTH) { cent->lerpOrigin[2] += 2; } if (item->giType == IT_ARMOR) { if (item->quantity == 100) { cent->lerpOrigin[2] += 7; } } } memset (&ent, 0, sizeof(ent)); if ( (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) && (item->giType == IT_WEAPON || item->giType == IT_POWERUP) ) { //only weapons and powerups rotate now // autorotate at one of two speeds VectorCopy( cg.autoAngles, cent->lerpAngles ); AxisCopy( cg.autoAxis, ent.axis ); } else { VectorCopy( cent->currentState.angles, cent->lerpAngles ); AnglesToAxis(cent->lerpAngles, ent.axis); } wi = NULL; // the weapons have their origin where they attatch to player // models, so we need to offset them or they will rotate // eccentricly if (!(cent->currentState.eFlags & EF_DROPPEDWEAPON)) { if ( item->giType == IT_WEAPON ) { wi = &cg_weapons[item->giTag]; cent->lerpOrigin[0] -= wi->weaponMidpoint[0] * ent.axis[0][0] + wi->weaponMidpoint[1] * ent.axis[1][0] + wi->weaponMidpoint[2] * ent.axis[2][0]; cent->lerpOrigin[1] -= wi->weaponMidpoint[0] * ent.axis[0][1] + wi->weaponMidpoint[1] * ent.axis[1][1] + wi->weaponMidpoint[2] * ent.axis[2][1]; cent->lerpOrigin[2] -= wi->weaponMidpoint[0] * ent.axis[0][2] + wi->weaponMidpoint[1] * ent.axis[1][2] + wi->weaponMidpoint[2] * ent.axis[2][2]; cent->lerpOrigin[2] += 8; // an extra height boost } } else { wi = &cg_weapons[item->giTag]; switch(item->giTag) { case WP_BLASTER: cent->lerpOrigin[2] -= 12; break; case WP_DISRUPTOR: cent->lerpOrigin[2] -= 13; break; case WP_BOWCASTER: cent->lerpOrigin[2] -= 16; break; case WP_REPEATER: cent->lerpOrigin[2] -= 12; break; case WP_DEMP2: cent->lerpOrigin[2] -= 10; break; case WP_FLECHETTE: cent->lerpOrigin[2] -= 6; break; case WP_ROCKET_LAUNCHER: cent->lerpOrigin[2] -= 11; break; case WP_THERMAL: cent->lerpOrigin[2] -= 12; break; case WP_TRIP_MINE: cent->lerpOrigin[2] -= 16; break; case WP_DET_PACK: cent->lerpOrigin[2] -= 16; break; default: cent->lerpOrigin[2] -= 8; break; } } ent.hModel = cg_items[es->modelindex].models[0]; /* Ghoul2 Insert Start */ ent.ghoul2 = cg_items[es->modelindex].g2Models[0]; ent.radius = cg_items[es->modelindex].radius[0]; VectorCopy (cent->lerpAngles, ent.angles); /* Ghoul2 Insert End */ VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); ent.nonNormalizedAxes = qfalse; // if just respawned, slowly scale up msec = cg.time - cent->miscTime; if (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide)) { ent.renderfx |= RF_RGB_TINT; ent.shaderRGBA[0] = 150; ent.shaderRGBA[1] = 150; ent.shaderRGBA[2] = 150; ent.renderfx |= RF_FORCE_ENT_ALPHA; ent.shaderRGBA[3] = 200; if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT) { ent.customShader = trap_R_RegisterShader("gfx/misc/mp_light_enlight_disable"); } else { ent.customShader = trap_R_RegisterShader("gfx/misc/mp_dark_enlight_disable"); } trap_R_AddRefEntityToScene( &ent ); return; } if ( es->eFlags & EF_ITEMPLACEHOLDER ) // item has been picked up { if ( es->eFlags & EF_DEAD ) // if item had been droped, don't show at all return; ent.renderfx |= RF_RGB_TINT; ent.shaderRGBA[0] = 0; ent.shaderRGBA[1] = 200; ent.shaderRGBA[2] = 85; ent.customShader = cgs.media.itemRespawningPlaceholder; } // increase the size of the weapons when they are presented as items if ( item->giType == IT_WEAPON ) { VectorScale( ent.axis[0], 1.5, ent.axis[0] ); VectorScale( ent.axis[1], 1.5, ent.axis[1] ); VectorScale( ent.axis[2], 1.5, ent.axis[2] ); ent.nonNormalizedAxes = qtrue; //trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound ); } if (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) && (item->giType == IT_WEAPON || item->giType == IT_POWERUP)) { ent.renderfx |= RF_MINLIGHT; } if (item->giType != IT_TEAM && msec >= 0 && msec < ITEM_SCALEUP_TIME && !(es->eFlags & EF_ITEMPLACEHOLDER) && !(es->eFlags & EF_DROPPEDWEAPON)) { // if just respawned, fade in, but don't do this for flags. float alpha; int a; alpha = (float)msec / ITEM_SCALEUP_TIME; a = alpha * 255.0; if (a <= 0) a=1; ent.shaderRGBA[3] = a; if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON) { //boon model uses a different blending mode for the sprite inside and doesn't look proper with this method ent.renderfx |= RF_FORCE_ENT_ALPHA; } trap_R_AddRefEntityToScene(&ent); ent.renderfx &= ~RF_FORCE_ENT_ALPHA; // Now draw the static shader over it. // Alpha in over half the time, out over half. //alpha = sin(M_PI*alpha); a = alpha * 255.0; a = 255 - a; if (a <= 0) a=1; if (a > 255) a=255; ent.customShader = cgs.media.itemRespawningRezOut; /* ent.shaderRGBA[0] = 0; ent.shaderRGBA[1] = a; ent.shaderRGBA[2] = a-100; if (ent.shaderRGBA[2] < 0) { ent.shaderRGBA[2] = 0; } */ /* ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = a; */ ent.renderfx |= RF_RGB_TINT; ent.shaderRGBA[0] = 0; ent.shaderRGBA[1] = 200; ent.shaderRGBA[2] = 85; trap_R_AddRefEntityToScene( &ent ); } else { // add to refresh list -- normal item if (item->giType == IT_TEAM && (item->giTag == PW_REDFLAG || item->giTag == PW_BLUEFLAG)) { ent.modelScale[0] = 0.7; ent.modelScale[1] = 0.7; ent.modelScale[2] = 0.7; ScaleModelAxis(&ent); } trap_R_AddRefEntityToScene(&ent); } //rww - As far as I can see, this is useless. /* if ( item->giType == IT_WEAPON && wi->barrelModel ) { refEntity_t barrel; memset( &barrel, 0, sizeof( barrel ) ); barrel.hModel = wi->barrelModel; VectorCopy( ent.lightingOrigin, barrel.lightingOrigin ); barrel.shadowPlane = ent.shadowPlane; barrel.renderfx = ent.renderfx; barrel.customShader = ent.customShader; CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" ); AxisCopy( ent.axis, barrel.axis ); barrel.nonNormalizedAxes = ent.nonNormalizedAxes; trap_R_AddRefEntityToScene( &barrel ); } */ // accompanying rings / spheres for powerups if ( !cg_simpleItems.integer ) { vec3_t spinAngles; VectorClear( spinAngles ); if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP ) { if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 ) { if ( item->giType == IT_POWERUP ) { ent.origin[2] += 12; spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f; } AnglesToAxis( spinAngles, ent.axis ); trap_R_AddRefEntityToScene( &ent ); } } } } //============================================================================ void CG_CreateDistortionTrailPart(centity_t *cent, float scale, vec3_t pos) { refEntity_t ent; vec3_t ang; float vLen; if (!cg_renderToTextureFX.integer) { return; } memset( &ent, 0, sizeof( ent ) ); VectorCopy( pos, ent.origin ); VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]); vLen = VectorLength(ent.axis[0]); if (VectorNormalize(ent.axis[0]) <= 0.1f) { // Entity is right on vieworg. quit. return; } VectorCopy(cent->lerpAngles, ang); ang[PITCH] += 90.0f; AnglesToAxis(ang, ent.axis); //radius must be a power of 2, and is the actual captured texture size if (vLen < 512) { ent.radius = 256; } else if (vLen < 1024) { ent.radius = 128; } else if (vLen < 2048) { ent.radius = 64; } else { ent.radius = 32; } ent.modelScale[0] = scale; ent.modelScale[1] = scale; ent.modelScale[2] = scale*16.0f; ScaleModelAxis(&ent); ent.hModel = trap_R_RegisterModel("models/weapons2/merr_sonn/trailmodel.md3"); ent.customShader = cgs.media.itemRespawningRezOut;//cgs.media.cloakedShader;//cgs.media.halfShieldShader; #if 1 ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA); ent.shaderRGBA[0] = 255.0f; ent.shaderRGBA[1] = 255.0f; ent.shaderRGBA[2] = 255.0f; ent.shaderRGBA[3] = 100.0f; #else //no alpha ent.renderfx = RF_DISTORTION; #endif trap_R_AddRefEntityToScene( &ent ); } //distortion trail effect for rockets -rww /* static void CG_DistortionTrail( centity_t *cent ) { vec3_t fwd; vec3_t pos; float overallScale = 4.0f; VectorCopy(cent->currentState.pos.trDelta, fwd); VectorNormalize(fwd); VectorMA(cent->lerpOrigin, -8.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 0.5f*overallScale, pos); VectorMA(cent->lerpOrigin, -12.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 0.6f*overallScale, pos); VectorMA(cent->lerpOrigin, -16.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 0.7f*overallScale, pos); VectorMA(cent->lerpOrigin, -20.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 0.8f*overallScale, pos); VectorMA(cent->lerpOrigin, -30.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 0.9f*overallScale, pos); VectorMA(cent->lerpOrigin, -40.0f*overallScale, fwd, pos); CG_CreateDistortionTrailPart(cent, 1.0f*overallScale, pos); } */ /* =============== CG_Missile =============== */ static void CG_Missile( centity_t *cent ) { refEntity_t ent; entityState_t *s1; const weaponInfo_t *weapon; // int col; s1 = ¢->currentState; if ( s1->weapon > WP_NUM_WEAPONS && s1->weapon != G2_MODEL_PART ) { s1->weapon = 0; } if (cent->ghoul2 && s1->weapon == G2_MODEL_PART) { weapon = &cg_weapons[WP_SABER]; } else { weapon = &cg_weapons[s1->weapon]; } if (cent->currentState.eFlags & EF_RADAROBJECT) { CG_AddRadarEnt(cent); } if (s1->weapon == WP_SABER) { if ((cent->currentState.modelindex != cent->serverSaberHitIndex || !cent->ghoul2) && !(s1->eFlags & EF_NODRAW)) { //no g2, or server changed the model we are using const char *saberModel = CG_ConfigString( CS_MODELS+cent->currentState.modelindex ); cent->serverSaberHitIndex = cent->currentState.modelindex; if (cent->ghoul2) { //clean if we already have one (because server changed model string index) trap_G2API_CleanGhoul2Models(&(cent->ghoul2)); cent->ghoul2 = 0; } if (saberModel && saberModel[0]) { trap_G2API_InitGhoul2Model(¢->ghoul2, saberModel, 0, 0, 0, 0, 0); } else { trap_G2API_InitGhoul2Model(¢->ghoul2, "models/weapons2/saber/saber_w.glm", 0, 0, 0, 0, 0); } return; } else if (s1->eFlags & EF_NODRAW) { return; } } if (cent->ghoul2) { //give us a proper radius ent.radius = cent->currentState.g2radius; } // calculate the axis VectorCopy( s1->angles, cent->lerpAngles); if ( s1->otherEntityNum2 && s1->weapon != WP_SABER ) {//using an over-ridden trail effect! vec3_t forward; if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) { forward[2] = 1.0f; } if ((s1->eFlags&EF_JETPACK_ACTIVE)//hack so we know we're a vehicle Weapon shot && (g_vehWeaponInfo[s1->otherEntityNum2].iShotFX || g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE) ) { //a vehicle with an override for the weapon trail fx or model trap_FX_PlayEffectID( g_vehWeaponInfo[s1->otherEntityNum2].iShotFX, cent->lerpOrigin, forward, -1, -1 ); if ( g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound ); } //add custom model if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel == NULL_HANDLE ) { return; } } else {//a regular missile trap_FX_PlayEffectID( cgs.gameEffects[s1->otherEntityNum2], cent->lerpOrigin, forward, -1, -1 ); if ( s1->loopSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, s1->loopSound ); } //FIXME: if has a custom model, too, then set it and do rest of code below? return; } } else if ( cent->currentState.eFlags & EF_ALT_FIRING ) { // add trails if ( weapon->altMissileTrailFunc ) { weapon->altMissileTrailFunc( cent, weapon ); } // add dynamic light if ( weapon->altMissileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->altMissileDlight, weapon->altMissileDlightColor[0], weapon->altMissileDlightColor[1], weapon->altMissileDlightColor[2] ); } // add missile sound if ( weapon->altMissileSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->altMissileSound ); } //Don't draw something without a model if ( weapon->altMissileModel == NULL_HANDLE ) return; } else { // add trails if ( weapon->missileTrailFunc ) { weapon->missileTrailFunc( cent, weapon ); } // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); } // add missile sound if ( weapon->missileSound ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity ); trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound ); } //Don't draw something without a model if ( weapon->missileModel == NULL_HANDLE && s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART ) //saber uses ghoul2 model, doesn't matter return; } // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); /* Ghoul2 Insert Start */ CG_SetGhoul2Info(&ent, cent); /* Ghoul2 Insert End */ // flicker between two skins ent.skinNum = cg.clientFrame & 1; ent.renderfx = /*weapon->missileRenderfx | */RF_NOSHADOW; if ( !(s1->eFlags&EF_JETPACK_ACTIVE) ) { if (s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART) { //if ( cent->currentState.eFlags | EF_ALT_FIRING ) //rww - why was this like this? if ( cent->currentState.eFlags & EF_ALT_FIRING ) { ent.hModel = weapon->altMissileModel; } else { ent.hModel = weapon->missileModel; } } } //add custom model else { if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE ) { ent.hModel = g_vehWeaponInfo[s1->otherEntityNum2].iModel; } else {//wtf? how did we get here? return; } } // spin as it moves if ( s1->apos.trType != TR_INTERPOLATE ) { // convert direction of travel into axis if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) { ent.axis[0][2] = 1; } // spin as it moves if ( s1->pos.trType != TR_STATIONARY ) { if ( s1->eFlags & EF_MISSILE_STICK ) { RotateAroundDirection( ent.axis, cg.time * 0.5f );//Did this so regular missiles don't get broken } else { RotateAroundDirection( ent.axis, cg.time * 0.25f );//JFM:FLOAT FIX } } else { if ( s1->eFlags & EF_MISSILE_STICK ) { RotateAroundDirection( ent.axis, (float)s1->pos.trTime * 0.5f ); } else { RotateAroundDirection( ent.axis, (float)s1->time ); } } } else { AnglesToAxis( cent->lerpAngles, ent.axis ); } if (s1->weapon == WP_SABER) { ent.radius = s1->g2radius; } // add to refresh list, possibly with quad glow CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE ); if (s1->weapon == WP_SABER && cgs.gametype == GT_JEDIMASTER) { //in jedimaster always make the saber glow when on the ground vec3_t org; float wv; int i; addspriteArgStruct_t fxSArgs; //refEntity_t sRef; //memcpy( &sRef, &ent, sizeof( sRef ) ); ent.customShader = cgs.media.solidWhite; ent.renderfx = RF_RGB_TINT; wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f; ent.shaderRGBA[0] = wv * 255; ent.shaderRGBA[1] = wv * 255; ent.shaderRGBA[2] = wv * 0; trap_R_AddRefEntityToScene (&ent); for ( i = -4; i < 10; i += 1 ) { VectorMA( ent.origin, -i, ent.axis[2], org ); VectorCopy(org, fxSArgs.origin); VectorClear(fxSArgs.vel); VectorClear(fxSArgs.accel); fxSArgs.scale = 5.5f; fxSArgs.dscale = 5.5f; fxSArgs.sAlpha = wv; fxSArgs.eAlpha = wv; fxSArgs.rotation = 0.0f; fxSArgs.bounce = 0.0f; fxSArgs.life = 1.0f; fxSArgs.shader = cgs.media.yellowDroppedSaberShader; fxSArgs.flags = 0x08000000; //trap_FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 ); trap_FX_AddSprite(&fxSArgs); } if (cgs.gametype == GT_JEDIMASTER) { ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 0; ent.renderfx |= RF_DEPTHHACK; ent.customShader = cgs.media.forceSightBubble; trap_R_AddRefEntityToScene( &ent ); } } if ( s1->eFlags & EF_FIRING ) {//special code for adding the beam to the attached tripwire mine vec3_t beamOrg; VectorMA( ent.origin, 8, ent.axis[0], beamOrg );// forward trap_FX_PlayEffectID( cgs.effects.mTripMineLaster, beamOrg, ent.axis[0], -1, -1 ); } } int CG_BMS_START = 0; int CG_BMS_MID = 1; int CG_BMS_END = 2; /* ------------------------- CG_PlayDoorLoopSound ------------------------- */ void CG_PlayDoorLoopSound( centity_t *cent ) { sfxHandle_t sfx; const char *soundSet; vec3_t origin; float *v; if ( !cent->currentState.soundSetIndex ) { return; } soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex ); if (!soundSet || !soundSet[0]) { return; } sfx = trap_AS_GetBModelSound( soundSet, CG_BMS_MID ); if ( sfx == -1 ) { return; } if (cent->currentState.eType == ET_MOVER) //shouldn't be in here otherwise, but just in case. { v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ]; VectorAdd( cent->lerpOrigin, v, origin ); } else { VectorCopy(cent->lerpOrigin, origin); } //ent->s.loopSound = sfx; CG_S_AddRealLoopingSound(cent->currentState.number, origin, vec3_origin, sfx); } /* ------------------------- CG_PlayDoorSound ------------------------- */ void CG_PlayDoorSound( centity_t *cent, int type ) { sfxHandle_t sfx; const char *soundSet; if ( !cent->currentState.soundSetIndex ) { return; } soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex ); if (!soundSet || !soundSet[0]) { return; } sfx = trap_AS_GetBModelSound( soundSet, type ); if ( sfx == -1 ) { return; } trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, sfx ); } /* =============== CG_Mover =============== */ static void CG_Mover( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); if ( (cent->currentState.eFlags2&EF2_HYPERSPACE) ) {//I'm the hyperspace brush qboolean drawMe = qfalse; if ( cg.predictedPlayerState.m_iVehicleNum && cg.predictedVehicleState.hyperSpaceTime && (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME && (cg.time-cg.predictedVehicleState.hyperSpaceTime) > 1000 ) { if ( cg.snap && cg.snap->ps.pm_type == PM_INTERMISSION ) {//in the intermission, stop drawing hyperspace ent } else if ( (cg.predictedVehicleState.eFlags2&EF2_HYPERSPACE) ) {//actually hyperspacing now float timeFrac = ((float)(cg.time-cg.predictedVehicleState.hyperSpaceTime-1000))/(HYPERSPACE_TIME-1000); if ( timeFrac < (HYPERSPACE_TELEPORT_FRAC+0.1f) ) {//still in hyperspace or just popped out const float alpha = timeFrac<0.5f?timeFrac/0.5f:1.0f; drawMe = qtrue; VectorMA( cg.refdef.vieworg, 1000.0f+((1.0f-timeFrac)*1000.0f), cg.refdef.viewaxis[0], cent->lerpOrigin ); VectorSet( cent->lerpAngles, cg.refdef.viewangles[PITCH], cg.refdef.viewangles[YAW]-90.0f, 0 );//cos( ( cg.time + 1000 ) * scale ) * 4 ); ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = alpha*255; } } } if ( !drawMe ) {//else, never draw return; } } if (cent->currentState.eFlags & EF_RADAROBJECT) { CG_AddRadarEnt(cent); } VectorCopy( cent->lerpOrigin, ent.origin); VectorCopy( cent->lerpOrigin, ent.oldorigin); AnglesToAxis( cent->lerpAngles, ent.axis ); ent.renderfx = RF_NOSHADOW; /* Ghoul2 Insert Start */ CG_SetGhoul2Info(&ent, cent); /* Ghoul2 Insert End */ // flicker between two skins (FIXME?) ent.skinNum = ( cg.time >> 6 ) & 1; // get the model, either as a bmodel or a modelindex if ( s1->solid == SOLID_BMODEL ) { ent.hModel = cgs.inlineDrawModel[s1->modelindex]; } else { ent.hModel = cgs.gameModels[s1->modelindex]; } if ( s1->eFlags & EF_SHADER_ANIM ) { ent.renderfx|=RF_SETANIMINDEX; ent.skinNum = s1->frame; //ent.shaderTime = cg.time*0.001f - s1->frame/s1->time;//NOTE: s1->time is number of frames } // add to refresh list trap_R_AddRefEntityToScene(&ent); // add the secondary model if ( s1->modelindex2 ) { ent.skinNum = 0; ent.hModel = cgs.gameModels[s1->modelindex2]; if (s1->iModelScale) { //custom model2 scale if ( s1->legsFlip ) {//scalar ent.modelScale[0] = ent.modelScale[1] = ent.modelScale[2] = s1->iModelScale; } else {//percentage ent.modelScale[0] = ent.modelScale[1] = ent.modelScale[2] = s1->iModelScale/100.0f; } ScaleModelAxis(&ent); } trap_R_AddRefEntityToScene(&ent); } } /* =============== CG_Beam Also called as an event =============== */ void CG_Beam( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( s1->pos.trBase, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); AxisClear( ent.axis ); ent.reType = RT_BEAM; ent.renderfx = RF_NOSHADOW; /* Ghoul2 Insert Start */ CG_SetGhoul2Info(&ent, cent); /* Ghoul2 Insert End */ // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* =============== CG_Portal =============== */ static void CG_Portal( centity_t *cent ) { refEntity_t ent; entityState_t *s1; s1 = ¢->currentState; // create the render entity memset (&ent, 0, sizeof(ent)); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( s1->origin2, ent.oldorigin ); ByteToDir( s1->eventParm, ent.axis[0] ); PerpendicularVector( ent.axis[1], ent.axis[0] ); // negating this tends to get the directions like they want // we really should have a camera roll value VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] ); CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] ); ent.reType = RT_PORTALSURFACE; ent.oldframe = s1->powerups; ent.frame = s1->frame; // rotation speed ent.skinNum = s1->clientNum/256.0 * 360; // roll offset /* Ghoul2 Insert Start */ CG_SetGhoul2Info(&ent, cent); /* Ghoul2 Insert End */ // add to refresh list trap_R_AddRefEntityToScene(&ent); } /* ========================= CG_AdjustPositionForMover Also called by client movement prediction code ========================= */ void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) { centity_t *cent; vec3_t oldOrigin, origin, deltaOrigin; vec3_t oldAngles, angles, deltaAngles; if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) { VectorCopy( in, out ); return; } cent = &cg_entities[ moverNum ]; if ( cent->currentState.eType != ET_MOVER ) { VectorCopy( in, out ); return; } BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles ); BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin ); BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles ); VectorSubtract( origin, oldOrigin, deltaOrigin ); VectorSubtract( angles, oldAngles, deltaAngles ); VectorAdd( in, deltaOrigin, out ); // FIXME: origin change when on a rotating object } /* ============================= CG_InterpolateEntityPosition ============================= */ static void CG_InterpolateEntityPosition( centity_t *cent ) { vec3_t current, next; float f; // it would be an internal error to find an entity that interpolates without // a snapshot ahead of the current one if ( cg.nextSnap == NULL ) { CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" ); } f = cg.frameInterpolation; // this will linearize a sine or parabolic curve, but it is important // to not extrapolate player positions if more recent data is available BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next ); cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] ); cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] ); cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current ); BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next ); cent->lerpAngles[0] = LerpAngle( current[0], next[0], f ); cent->lerpAngles[1] = LerpAngle( current[1], next[1], f ); cent->lerpAngles[2] = LerpAngle( current[2], next[2], f ); } /* =============== CG_CalcEntityLerpPositions =============== */ void CG_CalcEntityLerpPositions( centity_t *cent ) { qboolean goAway = qfalse; // if this player does not want to see extrapolated players if ( !cg_smoothClients.integer ) { // make sure the clients use TR_INTERPOLATE if ( cent->currentState.number < MAX_CLIENTS ) { cent->currentState.pos.trType = TR_INTERPOLATE; cent->nextState.pos.trType = TR_INTERPOLATE; } } if (cg.predictedPlayerState.m_iVehicleNum && cg.predictedPlayerState.m_iVehicleNum == cent->currentState.number && cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE) { //special case for vehicle we are riding centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum]; if (veh->currentState.owner == cg.predictedPlayerState.clientNum) { //only do this if the vehicle is pilotted by this client and predicting properly BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); return; } } if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) { CG_InterpolateEntityPosition( cent ); return; } // first see if we can interpolate between two snaps for // linear extrapolated clients if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP && cent->currentState.number < MAX_CLIENTS ) { CG_InterpolateEntityPosition( cent ); goAway = qtrue; } else if (cent->interpolate && cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE) { CG_InterpolateEntityPosition( cent ); goAway = qtrue; } else { // just use the current frame and evaluate as best we can BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin ); BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles ); } #if 0 if (cent->hasRagOffset && cent->ragOffsetTime < cg.time) { //take all of the offsets from last frame and normalize the total direction and add it in vec3_t slideDir; vec3_t preOffset; vec3_t addedOffset; vec3_t playerMins = {-15, -15, DEFAULT_MINS_2}; vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2}; trace_t tr; //VectorSubtract(cent->lerpOrigin, callData->bonePos, slideDir); VectorCopy(cent->ragOffsets, slideDir); VectorNormalize(slideDir); //Store it in case we want to go back VectorCopy(cent->lerpOriginOffset, preOffset); //just add a little at a time VectorMA(cent->lerpOriginOffset, 0.4f, slideDir, cent->lerpOriginOffset); if (VectorLength(cent->lerpOriginOffset) > 10.0f) { //don't go too far away VectorCopy(preOffset, cent->lerpOriginOffset); } else { //Let's trace there to make sure we can make it VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, addedOffset); CG_Trace(&tr, cent->lerpOrigin, playerMins, playerMaxs, addedOffset, cent->currentState.number, MASK_PLAYERSOLID); if (tr.startsolid || tr.allsolid || tr.fraction != 1.0f) { //can't get there VectorCopy(preOffset, cent->lerpOriginOffset); } else { /* if (cent->lerpOriginOffset[2] > 4.0f) { //don't go too far off the ground cent->lerpOriginOffset[2] = 4.0f; } */ //I guess I just don't want this happening. cent->lerpOriginOffset[2] = 0.0f; } } //done with this bit cent->hasRagOffset = qfalse; VectorClear(cent->ragOffsets); cent->ragOffsetTime = cg.time + 50; } //See if we should add in the offset for ragdoll if (cent->isRagging && ((cent->currentState.eFlags & EF_DEAD) || (cent->currentState.eFlags & EF_RAG))) { VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, cent->lerpOrigin); } #endif if (goAway) { return; } // adjust for riding a mover if it wasn't rolled into the predicted // player state if ( cent->currentState.number != cg.predictedPlayerState.clientNum ) { CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, cg.snap->serverTime, cg.time, cent->lerpOrigin ); } } /* =============== CG_TeamBase =============== */ static void CG_TeamBase( centity_t *cent ) { refEntity_t model; if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTY ) { // show the flag base memset(&model, 0, sizeof(model)); model.reType = RT_MODEL; VectorCopy( cent->lerpOrigin, model.lightingOrigin ); VectorCopy( cent->lerpOrigin, model.origin ); AnglesToAxis( cent->currentState.angles, model.axis ); if ( cent->currentState.modelindex == TEAM_RED ) { model.hModel = cgs.media.redFlagBaseModel; } else if ( cent->currentState.modelindex == TEAM_BLUE ) { model.hModel = cgs.media.blueFlagBaseModel; } else { model.hModel = cgs.media.neutralFlagBaseModel; } if (cent->currentState.eType != ET_NPC) { //do not do this for g2animents trap_R_AddRefEntityToScene( &model ); } } } void CG_G2Animated( centity_t *cent ); static void CG_FX( centity_t *cent ) { vec3_t fxDir; int efxIndex = 0; entityState_t *s1; const char *s; if (cent->miscTime > cg.time) { return; } s1 = ¢->currentState; if (!s1) { return; } if (s1->modelindex2 == FX_STATE_OFF) { // fx not active return; } if (s1->modelindex2 < FX_STATE_ONE_SHOT_LIMIT) { // fx is single shot if (cent->muzzleFlashTime == s1->modelindex2) { return; } cent->muzzleFlashTime = s1->modelindex2; } cent->miscTime = cg.time + s1->speed + random() * s1->time; AngleVectors(s1->angles, fxDir, 0, 0); if (!fxDir[0] && !fxDir[1] && !fxDir[2]) { fxDir[1] = 1; } if ( cgs.gameEffects[ s1->modelindex ] ) { efxIndex = cgs.gameEffects[s1->modelindex]; } else { s = CG_ConfigString( CS_EFFECTS + s1->modelindex ); if (s && s[0]) { efxIndex = trap_FX_RegisterEffect(s); cgs.gameEffects[s1->modelindex] = efxIndex; } } if (efxIndex) { if (s1->isPortalEnt) { trap_FX_PlayPortalEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1 ); } else { trap_FX_PlayEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1 ); } } } /* =============== CG_AddCEntity =============== */ static void CG_AddCEntity( centity_t *cent ) { // event-only entities will have been dealt with already if ( cent->currentState.eType >= ET_EVENTS ) { return; } if (cg.predictedPlayerState.pm_type == PM_INTERMISSION) { //don't render anything then if (cent->currentState.eType == ET_GENERAL || cent->currentState.eType == ET_PLAYER || cent->currentState.eType == ET_INVISIBLE) { return; } if ( cent->currentState.eType == ET_NPC ) {//NPC in intermission if ( cent->currentState.NPC_class == CLASS_VEHICLE ) {//don't render vehicles in intermissions, allow other NPCs for scripts return; } } } // calculate the current origin CG_CalcEntityLerpPositions( cent ); // add automatic effects CG_EntityEffects( cent ); /* Ghoul2 Insert Start */ // add local sound set if any if ( cent->currentState.soundSetIndex && cent->currentState.eType != ET_MOVER ) { const char *soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex ); if (soundSet && soundSet[0]) { trap_S_AddLocalSet(soundSet, cg.refdef.vieworg, cent->lerpOrigin, cent->currentState.number, cg.time); } } /* Ghoul2 Insert End */ switch ( cent->currentState.eType ) { default: CG_Error( "Bad entity type: %i\n", cent->currentState.eType ); break; case ET_FX: CG_FX( cent ); break; case ET_INVISIBLE: case ET_PUSH_TRIGGER: case ET_TELEPORT_TRIGGER: case ET_TERRAIN: break; case ET_GENERAL: CG_General( cent ); break; case ET_PLAYER: CG_Player( cent ); break; case ET_ITEM: CG_Item( cent ); break; case ET_MISSILE: CG_Missile( cent ); break; case ET_SPECIAL: CG_Special( cent ); break; case ET_HOLOCRON: CG_General( cent ); break; case ET_MOVER: CG_Mover( cent ); break; case ET_BEAM: CG_Beam( cent ); break; case ET_PORTAL: CG_Portal( cent ); break; case ET_SPEAKER: CG_Speaker( cent ); break; case ET_NPC: //An entity that wants to be able to use ghoul2 humanoid (and other) anims. Like a player, but not. CG_G2Animated( cent ); break; case ET_TEAM: CG_TeamBase( cent ); break; case ET_BODY: CG_General( cent ); break; } } void CG_ManualEntityRender(centity_t *cent) { CG_AddCEntity(cent); } /* =============== CG_AddPacketEntities =============== */ void CG_AddPacketEntities( qboolean isPortal ) { int num; centity_t *cent; playerState_t *ps; if (isPortal) { for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { cent = &cg_entities[ cg.snap->entities[ num ].number ]; if (cent->currentState.isPortalEnt) { CG_AddCEntity( cent ); } } return; } // set cg.frameInterpolation if ( cg.nextSnap ) { int delta; delta = (cg.nextSnap->serverTime - cg.snap->serverTime); if ( delta == 0 ) { cg.frameInterpolation = 0; } else { cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta; } } else { cg.frameInterpolation = 0; // actually, it should never be used, because // no entities should be marked as interpolating } // the auto-rotating items will all have the same axis cg.autoAngles[0] = 0; cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0; cg.autoAngles[2] = 0; cg.autoAnglesFast[0] = 0; cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; cg.autoAnglesFast[2] = 0; AnglesToAxis( cg.autoAngles, cg.autoAxis ); AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); // Reset radar entities cg.radarEntityCount = 0; cg.bracketedEntityCount = 0; // generate and add the entity from the playerstate ps = &cg.predictedPlayerState; CG_CheckPlayerG2Weapons(ps, &cg_entities[cg.predictedPlayerState.clientNum]); BG_PlayerStateToEntityState( ps, &cg_entities[cg.predictedPlayerState.clientNum].currentState, qfalse ); if (cg.predictedPlayerState.m_iVehicleNum) { //add the vehicle I'm riding first //BG_PlayerStateToEntityState( &cg.predictedVehicleState, &cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState, qfalse ); //cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState.eType = ET_NPC; centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum]; if (veh->currentState.owner == cg.predictedPlayerState.clientNum) { BG_PlayerStateToEntityState( &cg.predictedVehicleState, &veh->currentState, qfalse ); veh->currentState.eType = ET_NPC; veh->currentState.pos.trType = TR_INTERPOLATE; } CG_AddCEntity(veh); veh->bodyHeight = cg.time; //indicate we have already been added } CG_AddCEntity( &cg_entities[cg.predictedPlayerState.clientNum] ); /* // lerp the non-predicted value for lightning gun origins CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] ); */ //No longer have to do this. // add each entity sent over by the server for ( num = 0 ; num < cg.snap->numEntities ; num++ ) { // Don't re-add ents that have been predicted. if (cg.snap->entities[ num ].number != cg.snap->ps.clientNum) { cent = &cg_entities[ cg.snap->entities[ num ].number ]; if (cent->currentState.eType == ET_PLAYER && cent->currentState.m_iVehicleNum) { //add his veh first int j = 0; while (j < cg.snap->numEntities) { if (cg.snap->entities[j].number == cent->currentState.m_iVehicleNum) { centity_t *veh = &cg_entities[cg.snap->entities[j].number]; CG_AddCEntity(veh); veh->bodyHeight = cg.time; //indicate we have already been added break; } j++; } } else if (cent->currentState.eType == ET_NPC && cent->currentState.m_iVehicleNum && cent->bodyHeight == cg.time) { //never add a vehicle with a pilot, his pilot entity will get him added first. //if we were to add the vehicle after the pilot, the pilot's bolt would lag a frame behind. continue; } CG_AddCEntity( cent ); } } for(num=0;numcurrentValid) { CG_AddCEntity( cent ); } } } void CG_ROFF_NotetrackCallback( centity_t *cent, const char *notetrack) { int i = 0, r = 0, objectID = 0, anglesGathered = 0, posoffsetGathered = 0; char type[256]; char argument[512]; char addlArg[512]; char errMsg[256]; char t[64]; int addlArgs = 0; vec3_t parsedAngles, parsedOffset, useAngles, useOrigin, forward, right, up; if (!cent || !notetrack) { return; } //notetrack = "effect effects/explosion1.efx 0+0+64 0-0-1"; while (notetrack[i] && notetrack[i] != ' ') { type[i] = notetrack[i]; i++; } type[i] = '\0'; if (notetrack[i] != ' ') { //didn't pass in a valid notetrack type, or forgot the argument for it return; } i++; while (notetrack[i] && notetrack[i] != ' ') { argument[r] = notetrack[i]; r++; i++; } argument[r] = '\0'; if (!r) { return; } if (notetrack[i] == ' ') { //additional arguments... addlArgs = 1; i++; r = 0; while (notetrack[i]) { addlArg[r] = notetrack[i]; r++; i++; } addlArg[r] = '\0'; } if (strcmp(type, "effect") == 0) { if (!addlArgs) { //sprintf(errMsg, "Offset position argument for 'effect' type is invalid."); //goto functionend; VectorClear(parsedOffset); goto defaultoffsetposition; } i = 0; while (posoffsetGathered < 3) { r = 0; while (addlArg[i] && addlArg[i] != '+' && addlArg[i] != ' ') { t[r] = addlArg[i]; r++; i++; } t[r] = '\0'; i++; if (!r) { //failure.. //sprintf(errMsg, "Offset position argument for 'effect' type is invalid."); //goto functionend; VectorClear(parsedOffset); i = 0; goto defaultoffsetposition; } parsedOffset[posoffsetGathered] = atof(t); posoffsetGathered++; } if (posoffsetGathered < 3) { Com_sprintf(errMsg, sizeof(errMsg), "Offset position argument for 'effect' type is invalid."); goto functionend; } i--; if (addlArg[i] != ' ') { addlArgs = 0; } defaultoffsetposition: objectID = trap_FX_RegisterEffect(argument); if (objectID) { if (addlArgs) { //if there is an additional argument for an effect it is expected to be XANGLE-YANGLE-ZANGLE i++; while (anglesGathered < 3) { r = 0; while (addlArg[i] && addlArg[i] != '-') { t[r] = addlArg[i]; r++; i++; } t[r] = '\0'; i++; if (!r) { //failed to get a new part of the vector anglesGathered = 0; break; } parsedAngles[anglesGathered] = atof(t); anglesGathered++; } if (anglesGathered) { VectorCopy(parsedAngles, useAngles); } else { //failed to parse angles from the extra argument provided.. VectorCopy(cent->lerpAngles, useAngles); } } else { //if no constant angles, play in direction entity is facing VectorCopy(cent->lerpAngles, useAngles); } AngleVectors(useAngles, forward, right, up); VectorCopy(cent->lerpOrigin, useOrigin); //forward useOrigin[0] += forward[0]*parsedOffset[0]; useOrigin[1] += forward[1]*parsedOffset[0]; useOrigin[2] += forward[2]*parsedOffset[0]; //right useOrigin[0] += right[0]*parsedOffset[1]; useOrigin[1] += right[1]*parsedOffset[1]; useOrigin[2] += right[2]*parsedOffset[1]; //up useOrigin[0] += up[0]*parsedOffset[2]; useOrigin[1] += up[1]*parsedOffset[2]; useOrigin[2] += up[2]*parsedOffset[2]; trap_FX_PlayEffectID(objectID, useOrigin, useAngles, -1, -1); } } else if (strcmp(type, "sound") == 0) { objectID = trap_S_RegisterSound(argument); trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_BODY, objectID); } else if (strcmp(type, "loop") == 0) { //handled server-side return; } //else if ... else { if (type[0]) { Com_Printf("^3Warning: \"%s\" is an invalid ROFF notetrack function\n", type); } else { Com_Printf("^3Warning: Notetrack is missing function and/or arguments\n"); } } return; functionend: Com_Printf("^3Type-specific notetrack error: %s\n", errMsg); return; } void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha ) { vec3_t rot={0,0,0}; int vec[3]; int axis, i; addpolyArgStruct_t apArgs; memset (&apArgs, 0, sizeof(apArgs)); for ( axis = 0, vec[0] = 0, vec[1] = 1, vec[2] = 2; axis < 3; axis++, vec[0]++, vec[1]++, vec[2]++ ) { for ( i = 0; i < 3; i++ ) { if ( vec[i] > 2 ) { vec[i] = 0; } } apArgs.p[0][vec[1]] = mins[vec[1]]; apArgs.p[0][vec[2]] = mins[vec[2]]; apArgs.p[1][vec[1]] = mins[vec[1]]; apArgs.p[1][vec[2]] = maxs[vec[2]]; apArgs.p[2][vec[1]] = maxs[vec[1]]; apArgs.p[2][vec[2]] = maxs[vec[2]]; apArgs.p[3][vec[1]] = maxs[vec[1]]; apArgs.p[3][vec[2]] = mins[vec[2]]; //- face apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = mins[vec[0]]; apArgs.numVerts = 4; apArgs.alpha1 = apArgs.alpha2 = alpha; VectorCopy( color, apArgs.rgb1 ); VectorCopy( color, apArgs.rgb2 ); VectorCopy( rot, apArgs.rotationDelta ); apArgs.killTime = cg.frametime; apArgs.shader = cgs.media.solidWhite; trap_FX_AddPoly( &apArgs ); //+ face apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = maxs[vec[0]]; trap_FX_AddPoly( &apArgs ); } }