/** * Adds momentum for building a buildable. * * Will save the reward with the buildable. */ float G_AddMomentumForBuilding(gentity_t* buildable) { float value, reward; team_t team; gentity_t* builder; if (!buildable || buildable->s.eType != ET_BUILDABLE) { return 0.0f; } value = BG_Buildable(buildable->s.modelindex)->buildPoints; team = BG_Buildable(buildable->s.modelindex)->team; if (buildable->builtBy->slot != -1) { builder = &g_entities[buildable->builtBy->slot]; } else { builder = nullptr; } reward = AddMomentum(CONF_BUILDING, team, value, builder, false); // Save reward with buildable so it can be reverted buildable->momentumEarned = reward; return reward; }
/* =============== CG_BuildableInRange =============== */ static entityState_t *CG_BuildableInRange( playerState_t *ps, float *healthFraction ) { vec3_t view, point; trace_t trace; entityState_t *es; int health; AngleVectors( cg.refdefViewAngles, view, nullptr, nullptr ); VectorMA( cg.refdef.vieworg, 64, view, point ); CG_Trace( &trace, cg.refdef.vieworg, nullptr, nullptr, point, ps->clientNum, MASK_SHOT, 0 ); es = &cg_entities[ trace.entityNum ].currentState; if ( healthFraction ) { health = es->generic1; *healthFraction = ( float ) health / BG_Buildable( es->modelindex )->health; } if ( es->eType == ET_BUILDABLE && ps->persistant[ PERS_TEAM ] == BG_Buildable( es->modelindex )->team ) { return es; } else { return nullptr; } }
/* ================== CG_Creep ================== */ static void CG_Creep( centity_t *cent ) { int msec; float size, frac; trace_t tr; vec3_t temp, origin; int scaleUpTime = BG_Buildable( cent->currentState.modelindex )->buildTime; int time; // int creepSize; time = cent->currentState.time; //should the creep be growing or receding? if( time >= 0 ) { msec = cg.time - time; if( msec >= 0 && msec < scaleUpTime ) frac = (float)msec / scaleUpTime; else frac = 1.0f; } else if( time < 0 ) { msec = cg.time + time; if( msec >= 0 && msec < CREEP_SCALEDOWN_TIME ) frac = 1.0f - ( (float)msec / CREEP_SCALEDOWN_TIME ); else frac = 0.0f; } VectorCopy( cent->currentState.origin2, temp ); VectorScale( temp, -CREEP_DISTANCE, temp ); VectorAdd( temp, cent->lerpOrigin, temp ); CG_Trace( &tr, cent->lerpOrigin, NULL, NULL, temp, cent->currentState.number, MASK_PLAYERSOLID ); VectorCopy( tr.endpos, origin ); // size = CREEP_SIZE * frac; size = BG_Buildable( cent->currentState.modelindex )->creepSize * frac; if( size > 0.0f && tr.fraction < 1.0f ) CG_ImpactMark( cgs.media.creepShader, //qhandle_t markShader origin, //const vec3_t origin cent->currentState.origin2, //const vec3_t dir 0.0f, //float orientation 1.0f, //red 1.0f, //green 1.0f, //blue 1.0f, //float alpha qtrue, //qboolean alphaFade size, //float radius qtrue);//qboolean temporary }
/* ================== CG_BuildableParticleEffects ================== */ static void CG_BuildableParticleEffects( centity_t *cent ) { entityState_t *es = ¢->currentState; team_t team = BG_Buildable( es->modelindex )->team; int health = es->generic1; float healthFrac = (float)health / BG_Buildable( es->modelindex )->health; if( !( es->eFlags & EF_B_SPAWNED ) ) return; //ORGANIC BULB LIGHT / BA_H_LIGHT - VIA PS if( es->modelindex == BA_A_ORGANIC_BULB ) { if( !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.organicbulbPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } } else if( team == TEAM_HUMANS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } else if( team == TEAM_ALIENS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } }
/* ================ G_Bounce ================ */ static void G_Bounce( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; float minNormal; qboolean invert = qfalse; // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta ); if ( ent->s.eType == ET_BUILDABLE ) { minNormal = BG_Buildable( ent->s.modelindex )->minNormal; invert = BG_Buildable( ent->s.modelindex )->invertNormal; } else { minNormal = 0.707f; } // cut the velocity to keep from bouncing forever if ( ( trace->plane.normal[ 2 ] >= minNormal || ( invert && trace->plane.normal[ 2 ] <= -minNormal ) ) && trace->entityNum == ENTITYNUM_WORLD ) { VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta ); } else { VectorScale( ent->s.pos.trDelta, 0.3f, ent->s.pos.trDelta ); } if ( VectorLength( ent->s.pos.trDelta ) < 10 ) { VectorMA( trace->endpos, 0.5f, trace->plane.normal, trace->endpos ); // make sure it is off ground G_SetOrigin( ent, trace->endpos ); ent->s.groundEntityNum = trace->entityNum; VectorCopy( trace->plane.normal, ent->s.origin2 ); VectorSet( ent->s.pos.trDelta, 0.0f, 0.0f, 0.0f ); return; } VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin ); ent->s.pos.trTime = level.time; }
/* =============== CG_HumanCkitText =============== */ static void CG_HumanCkitText( char *text, playerState_t *ps ) { buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT; entityState_t *es; if( buildable > BA_NONE ) { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to place the %s\n", CG_KeyNameForCommand( "+attack" ), BG_Buildable( buildable )->humanName ) ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to cancel placing the %s\n", CG_KeyNameForCommand( "+button5" ), BG_Buildable( buildable )->humanName ) ); } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to build a structure\n", CG_KeyNameForCommand( "+attack" ) ) ); } if( ( es = CG_BuildableInRange( ps, NULL ) ) ) { if( cgs.markDeconstruct ) { if( es->eFlags & EF_B_MARKED ) { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to unmark this structure\n", CG_KeyNameForCommand( "deconstruct" ) ) ); } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to mark this structure\n", CG_KeyNameForCommand( "deconstruct" ) ) ); } } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( "Press %s to destroy this structure\n", CG_KeyNameForCommand( "deconstruct" ) ) ); } } }
/** * Removes momentum for deconstructing a buildable. */ float G_RemoveMomentumForDecon( gentity_t *buildable, gentity_t *deconner ) { float value; team_t team; // sanity check buildable if ( !buildable || buildable->s.eType != entityType_t::ET_BUILDABLE ) { return 0.0f; } team = BG_Buildable( buildable->s.modelindex )->team; if ( buildable->momentumEarned ) { value = buildable->momentumEarned; } else { // assume the buildable has just been placed value = G_PredictMomentumForBuilding( buildable ); } // Remove only partial momentum as the lost health fraction awards momentum to the enemy. value *= buildable->entity->Get<HealthComponent>()->HealthFraction(); return AddMomentum( CONF_DECONSTRUCTING, team, -value, deconner, false ); }
/* =============== buildFire =============== */ void buildFire( gentity_t *ent, dynMenu_t menu ) { //TODO find a solution to move dependency of ent->s.number and &ent->eventTime outside this function playerState_t *ps=&ent->client->ps; buildable_t buildable = ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ); if ( buildable > BA_NONE ) { if ( ps->stats[ STAT_MISC ] > 0 ) { G_AddPlayerEvent( ps, EV_BUILD_DELAY, ps->clientNum, ent->s.number, &ent->eventTime ); return; } if ( G_BuildIfValid( ent, buildable ) ) { if ( !g_cheats.integer ) { ps->stats[ STAT_MISC ] += BG_Buildable( buildable )->buildTime; } ps->stats[ STAT_BUILDABLE ] = BA_NONE; } return; } G_TriggerMenu( ps->clientNum, menu ); }
static void CG_Rocket_DFCMHumanBuildables( int handle, const char *data ) { buildable_t buildable = ( buildable_t ) atoi( Info_ValueForKey( data, "1" ) ); const char *Class = ""; const char *Icon = ""; const char *action = ""; int value, valueMarked; value = cg.snap->ps.persistant[ PERS_BP ]; valueMarked = cg.snap->ps.persistant[ PERS_MARKEDBP ]; if ( BG_BuildableDisabled( buildable ) || !BG_BuildableUnlocked( buildable ) ) { Class = "locked"; //Padlock icon. UTF-8 encoding of \uf023 Icon = "<icon>\xEF\x80\xA3</icon>"; } else if ( BG_Buildable( buildable )->buildPoints > value + valueMarked ) { Class = "expensive"; //$1 bill icon. UTF-8 encoding of \uf0d6 Icon = "<icon>\xEF\x83\x96</icon>"; } else { Class = "available"; action = va( "onClick='Cmd.exec(\"build %s\") Events.pushevent(\"hide %s\", event)'", BG_Buildable( buildable )->name, rocketInfo.menu[ ROCKETMENU_HUMANBUILD ].id ); } Rocket_DataFormatterFormattedData( handle, va( "<button class='%s' onMouseover='Events.pushevent(\"setDS humanBuildList default %s\", event)' %s>%s<img src='/%s'/></button>", Class, Info_ValueForKey( data, "2" ), action, Icon, CG_GetShaderNameFromHandle( cg_buildables[ buildable ].buildableIcon ) ), false ); }
/* =============== buildFire =============== */ void buildFire( gentity_t *ent, dynMenu_t menu ) { buildable_t buildable = ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ); if( buildable > BA_NONE ) { if( ent->client->ps.stats[ STAT_BUILD_TIMER ] > 0 ) { G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum ); return; } if( G_BuildIfValid( ent, buildable ) ) { if( !g_cheats.integer ) { ent->client->ps.stats[ STAT_BUILD_TIMER ] += BG_Buildable( buildable )->buildTime; } ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; } return; } G_TriggerMenu( ent->client->ps.clientNum, menu ); }
/* ================ G_SelectSpawnBuildable find the nearest buildable of the right type that is spawned/healthy/unblocked etc. ================ */ static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable ) { gentity_t *search, *spot; search = spot = NULL; while( ( search = G_Find( search, FOFS( classname ), BG_Buildable( buildable )->entityName ) ) != NULL ) { if( !search->spawned ) continue; if( search->health <= 0 ) continue; if( !search->s.groundEntityNum ) continue; if( search->clientSpawnTime > 0 ) continue; if( G_CheckSpawnPoint( search->s.number, search->s.origin, search->s.origin2, buildable, NULL ) != NULL ) continue; if( !spot || DistanceSquared( preference, search->s.origin ) < DistanceSquared( preference, spot->s.origin ) ) spot = search; } return spot; }
/** * Predicts the momentum reward for building a buildable. * * Is used for the buildlog entry, which is written before the actual reward *happens. * Also used to calculate the deconstruction penalty for preplaced buildables. */ float G_PredictMomentumForBuilding(gentity_t* buildable) { if (!buildable || buildable->s.eType != ET_BUILDABLE) { return 0.0f; } return BG_Buildable(buildable->s.modelindex)->buildPoints * MomentumMod(CONF_BUILDING); }
/* =============== G_FindZapChainTargets =============== */ static void G_FindZapChainTargets( zap_t *zap ) { gentity_t *ent = zap->targets[ 0 ]; // the source int entityList[ MAX_GENTITIES ]; vec3_t range = { LEVEL2_AREAZAP_CHAIN_RANGE, LEVEL2_AREAZAP_CHAIN_RANGE, LEVEL2_AREAZAP_CHAIN_RANGE }; vec3_t mins, maxs; int i, num; gentity_t *enemy; trace_t tr; float distance; VectorAdd( ent->s.origin, range, maxs ); VectorSubtract( ent->s.origin, range, mins ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( i = 0; i < num; i++ ) { enemy = &g_entities[ entityList[ i ] ]; // don't chain to self; noclippers can be listed, don't chain to them either if ( enemy == ent || ( enemy->client && enemy->client->noclip ) ) { continue; } distance = Distance( ent->s.origin, enemy->s.origin ); if ( ( ( enemy->client && enemy->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) || ( enemy->s.eType == ET_BUILDABLE && BG_Buildable( enemy->s.modelindex )->team == TEAM_HUMANS ) ) && enemy->health > 0 && // only chain to living targets distance <= LEVEL2_AREAZAP_CHAIN_RANGE ) { // world-LOS check: trace against the world, ignoring other BODY entities trap_Trace( &tr, ent->s.origin, NULL, NULL, enemy->s.origin, ent->s.number, CONTENTS_SOLID ); if ( tr.entityNum == ENTITYNUM_NONE ) { zap->targets[ zap->numTargets ] = enemy; zap->distances[ zap->numTargets ] = distance; if ( ++zap->numTargets >= LEVEL2_AREAZAP_MAX_TARGETS ) { return; } } } } }
int G_BuildableDeconValue(gentity_t *ent) { HealthComponent* healthComponent = ent->entity->Get<HealthComponent>(); if (!healthComponent->Alive()) { return 0; } return (int)roundf((float)BG_Buildable(ent->s.modelindex)->buildPoints * healthComponent->HealthFraction()); }
static void FireBuild( gentity_t *self, dynMenu_t menu ) { buildable_t buildable; if ( !self->client ) { return; } buildable = (buildable_t) ( self->client->ps.stats[ STAT_BUILDABLE ] & SB_BUILDABLE_MASK ); // open build menu if ( buildable <= BA_NONE ) { G_TriggerMenu( self->client->ps.clientNum, menu ); return; } // can't build just yet if ( self->client->ps.stats[ STAT_MISC ] > 0 ) { G_AddEvent( self, EV_BUILD_DELAY, self->client->ps.clientNum ); return; } // build if ( G_BuildIfValid( self, buildable ) ) { if ( !g_cheats.integer ) { int buildTime = BG_Buildable( buildable )->buildTime; switch ( self->client->ps.persistant[ PERS_TEAM ] ) { case TEAM_ALIENS: buildTime *= ALIEN_BUILDDELAY_MOD; break; case TEAM_HUMANS: buildTime *= HUMAN_BUILDDELAY_MOD; break; default: break; } self->client->ps.stats[ STAT_MISC ] += buildTime; } self->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; } }
static const char *UnlockableHumanName( unlockable_t *unlockable ) { switch ( unlockable->type ) { case UNLT_WEAPON: return BG_Weapon( unlockable->num )->humanName; case UNLT_UPGRADE: return BG_Upgrade( unlockable->num )->humanName; case UNLT_BUILDABLE: return BG_Buildable( unlockable->num )->humanName; case UNLT_CLASS: return BG_ClassModelConfig( unlockable->num )->humanName; } Com_Error( ERR_FATAL, "UnlockableHumanName: Unlockable has unknown type" ); return nullptr; }
static void CG_CompleteBuild( void ) { int i; for ( i = 0; i < BA_NUM_BUILDABLES; i++ ) { const buildableAttributes_t *item = BG_Buildable( i ); if ( item->team == cgs.clientinfo[ cg.clientNum ].team ) { trap_CompleteCallback( item->name ); } } }
/* ================== CG_PlayerIsBuilder ================== */ static qboolean CG_PlayerIsBuilder( buildable_t buildable ) { switch( cg.predictedPlayerState.weapon ) { case WP_ABUILD: case WP_HBUILD: return BG_Buildable( buildable )->team == BG_Weapon( cg.predictedPlayerState.weapon )->team; default: return qfalse; } }
/* =============== CG_BuildableAnimation =============== */ static void CG_BuildableAnimation( centity_t *cent, int *old, int *now, float *backLerp ) { entityState_t *es = ¢->currentState; //if no animation is set default to idle anim if( cent->buildableAnim == BANIM_NONE ) cent->buildableAnim = es->torsoAnim; //display the first frame of the construction anim if not yet spawned if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) { animation_t *anim = &cg_buildables[ es->modelindex ].animations[ BANIM_CONSTRUCT1 ]; //so that when animation starts for real it has sensible numbers cent->lerpFrame.oldFrameTime = cent->lerpFrame.frameTime = cent->lerpFrame.animationTime = cg.time; *old = cent->lerpFrame.oldFrame = anim->firstFrame; *now = cent->lerpFrame.frame = anim->firstFrame; *backLerp = cent->lerpFrame.backlerp = 0.0f; //ensure that an animation is triggered once the buildable has spawned cent->oldBuildableAnim = BANIM_NONE; } else { if( ( cent->oldBuildableAnim ^ es->legsAnim ) & ANIM_TOGGLEBIT ) { if( cg_debugAnim.integer ) CG_Printf( "%d->%d l:%d t:%d %s(%d)\n", cent->oldBuildableAnim, cent->buildableAnim, es->legsAnim, es->torsoAnim, BG_Buildable( es->modelindex )->humanName, es->number ); if( cent->buildableAnim == es->torsoAnim || es->legsAnim & ANIM_FORCEBIT ) cent->buildableAnim = cent->oldBuildableAnim = es->legsAnim; else cent->buildableAnim = cent->oldBuildableAnim = es->torsoAnim; } CG_RunBuildableLerpFrame( cent ); *old = cent->lerpFrame.oldFrame; *now = cent->lerpFrame.frame; *backLerp = cent->lerpFrame.backlerp; } }
void AlienBuildableComponent::Think(int timeDelta) { // TODO: Port gentity_t.powered. entity.oldEnt->powered = (G_ActiveOvermind() != nullptr); // Suicide if creep is gone. if (BG_Buildable(entity.oldEnt->s.modelindex)->creepTest && !G_FindCreep(entity.oldEnt)) { G_Kill(entity.oldEnt, entity.oldEnt->powerSource ? &g_entities[entity.oldEnt->powerSource->killedBy] : nullptr, MOD_NOCREEP); } // TODO: Find an elegant way to access per-buildable configuration. float creepSize = (float)BG_Buildable((buildable_t)entity.oldEnt->s.modelindex)->creepSize; // Slow close humans. ForEntities<HumanClassComponent>([&] (Entity& other, HumanClassComponent& humanClassComponent) { // TODO: Add LocationComponent. if (G_Distance(entity.oldEnt, other.oldEnt) > creepSize) return; // TODO: Send (Creep)Slow message instead. if (other.oldEnt->flags & FL_NOTARGET) return; if (other.oldEnt->client->ps.groundEntityNum == ENTITYNUM_NONE) return; other.oldEnt->client->ps.stats[STAT_STATE] |= SS_CREEPSLOWED; other.oldEnt->client->lastCreepSlowTime = level.time; }); }
/* =============== CG_BuilderText =============== */ static void CG_BuilderText( char *text, playerState_t *ps ) { buildable_t buildable = ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT; entityState_t *es; if ( buildable > BA_NONE ) { const char *item = _( BG_Buildable( buildable )->humanName ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to place the %s\n"), CG_KeyNameForCommand( "+attack" ), item ) ); Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to cancel placing the %s\n" ), CG_KeyNameForCommand( "+attack2" ), item ) ); } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to build a structure\n" ), CG_KeyNameForCommand( "+attack" ) ) ); } if ( ( es = CG_BuildableInRange( ps, NULL ) ) ) { const char *key = CG_KeyNameForCommand( "if alt \"/deconstruct marked\" /deconstruct" ); if ( cgs.markDeconstruct ) { if ( es->eFlags & EF_B_MARKED ) { Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to unmark this structure for replacement\n" ), key ) ); } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to mark this structure for replacement\n" ), key ) ); } } else { Q_strcat( text, MAX_TUTORIAL_TEXT, va( _( "Press %s to destroy this structure\n" ), key ) ); } } }
/* ================ G_SelectSpawnBuildable find the nearest buildable of the right type that is spawned/healthy/unblocked etc. ================ */ static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable ) { gentity_t *search = nullptr; gentity_t *spot = nullptr; while ( ( search = G_IterateEntitiesOfClass( search, BG_Buildable( buildable )->entityName ) ) != nullptr ) { if ( !search->spawned ) { continue; } if ( G_Dead( search ) ) { continue; } if ( search->s.groundEntityNum == ENTITYNUM_NONE ) { continue; } if ( search->clientSpawnTime > 0 ) { continue; } Entity* blocker = nullptr; Vec3 spawnPoint; search->entity->CheckSpawnPoint(blocker, spawnPoint); if (blocker) { continue; } if ( !spot || DistanceSquared( preference, search->s.origin ) < DistanceSquared( preference, spot->s.origin ) ) { spot = search; } } return spot; }
/* ====================================================================== BUILD GUN ====================================================================== */ void CheckCkitRepair( gentity_t *ent ) { vec3_t viewOrigin, forward, end; trace_t tr; gentity_t *traceEnt; int bHealth; //TODO find a solution to move dependency of ent->s.number and &ent->eventTime outside this function playerState_t *ps=&ent->client->ps; if ( ps->weaponTime > 0 || ps->stats[ STAT_MISC ] > 0 ) { return; } BG_GetClientViewOrigin( ps, viewOrigin ); AngleVectors( ps->viewangles, forward, NULL, NULL ); VectorMA( viewOrigin, 100, forward, end ); trap_Trace( &tr, viewOrigin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); traceEnt = &g_entities[ tr.entityNum ]; if ( tr.fraction < 1.0f && traceEnt->spawned && traceEnt->health > 0 && traceEnt->s.eType == ET_BUILDABLE && traceEnt->buildableTeam == TEAM_HUMANS ) { bHealth = BG_Buildable( traceEnt->s.modelindex )->health; if ( traceEnt->health < bHealth ) { traceEnt->health += HBUILD_HEALRATE; if ( traceEnt->health >= bHealth ) { traceEnt->health = bHealth; G_AddPlayerEvent( ps, EV_BUILD_REPAIRED, 0, ent->s.number, &ent->eventTime ); } else { G_AddPlayerEvent( ps, EV_BUILD_REPAIR, 0, ent->s.number, &ent->eventTime ); } ps->weaponTime += BG_Weapon( ps->weapon )->repeatRate1; } } }
/* ================= CG_Bleed This is the spurt of blood when a character gets hit ================= */ void CG_Bleed( vec3_t origin, vec3_t normal, int entityNum ) { team_t team; qhandle_t bleedPS; particleSystem_t *ps; if( !cg_blood.integer ) return; if( cg_entities[ entityNum ].currentState.eType == ET_PLAYER ) { team = cgs.clientinfo[ entityNum ].team; if( team == TEAM_ALIENS ) bleedPS = cgs.media.alienBleedPS; else if( team == TEAM_HUMANS ) bleedPS = cgs.media.humanBleedPS; else return; } else if( cg_entities[ entityNum ].currentState.eType == ET_BUILDABLE ) { //ew team = BG_Buildable( cg_entities[ entityNum ].currentState.modelindex, NULL )->team; if( team == TEAM_ALIENS ) bleedPS = cgs.media.alienBuildableBleedPS; else if( team == TEAM_HUMANS ) bleedPS = cgs.media.humanBuildableBleedPS; else return; } else return; ps = CG_SpawnNewParticleSystem( bleedPS ); if( CG_IsParticleSystemValid( &ps ) ) { CG_SetAttachmentPoint( &ps->attachment, origin ); CG_SetAttachmentCent( &ps->attachment, &cg_entities[ entityNum ] ); CG_AttachToPoint( &ps->attachment ); CG_SetParticleSystemNormal( ps, normal ); } }
/* =============== CG_PrevWeapon_f =============== */ void CG_PrevWeapon_f( void ) { int i; int original; if( !cg.snap ) return; if( cg.snap->ps.pm_flags & PMF_FOLLOW ) { trap_SendClientCommand( "follownext\n" ); return; } if( BG_Buildable(cg.snap->ps.stats[STAT_BUILDABLE]&~SB_VALID_TOGGLEBIT,NULL)->cuboid ) { CG_CuboidResize(qfalse); return; } cg.weaponSelectTime = cg.time; original = cg.weaponSelect; for( i = 0; i < 64; i++ ) { cg.weaponSelect--; if( cg.weaponSelect == -1 ) cg.weaponSelect = 63; if( cg.weaponSelect < 32 ) { if( CG_WeaponSelectable( cg.weaponSelect ) ) break; } else { if( CG_UpgradeSelectable( cg.weaponSelect - 32 ) ) break; } } if( i == 64 ) cg.weaponSelect = original; }
// TODO: Move this to the client side. void AlienBuildableComponent::CreepRecede(int timeDelta) { alienBuildableLogger.Debug("Starting creep recede."); G_AddEvent(entity.oldEnt, EV_BUILD_DESTROY, 0); if (entity.oldEnt->spawned) { entity.oldEnt->s.time = -level.time; } else { entity.oldEnt->s.time = -(level.time - (int)( (float)CREEP_SCALEDOWN_TIME * (1.0f - ((float)(level.time - entity.oldEnt->creationTime) / (float)BG_Buildable(entity.oldEnt->s.modelindex)->buildTime))) ); } // Remove buildable when done. GetBuildableComponent().REGISTER_THINKER(Remove, ThinkingComponent::SCHEDULER_AFTER, CREEP_SCALEDOWN_TIME); GetBuildableComponent().GetThinkingComponent().UnregisterActiveThinker(); }
/* ====================================================================== BUILD GUN ====================================================================== */ void CheckCkitRepair( gentity_t *ent ) { vec3_t viewOrigin, forward, end; trace_t tr; gentity_t *traceEnt; int bHealth; if ( ent->client->ps.weaponTime > 0 || ent->client->ps.stats[ STAT_MISC ] > 0 ) { return; } BG_GetClientViewOrigin( &ent->client->ps, viewOrigin ); AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL ); VectorMA( viewOrigin, 100, forward, end ); trap_Trace( &tr, viewOrigin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); traceEnt = &g_entities[ tr.entityNum ]; if ( tr.fraction < 1.0f && traceEnt->spawned && traceEnt->health > 0 && traceEnt->s.eType == ET_BUILDABLE && traceEnt->buildableTeam == TEAM_HUMANS ) { bHealth = BG_Buildable( traceEnt->s.modelindex )->health; if ( traceEnt->health < bHealth ) { traceEnt->health += HBUILD_HEALRATE; if ( traceEnt->health >= bHealth ) { traceEnt->health = bHealth; G_AddEvent( ent, EV_BUILD_REPAIRED, 0 ); } else { G_AddEvent( ent, EV_BUILD_REPAIR, 0 ); } ent->client->ps.weaponTime += BG_Weapon( ent->client->ps.weapon )->repeatRate1; } } }
/* ================== CG_Creep ================== */ static void CG_Creep( centity_t *cent ) { int msec; float size, frac; trace_t tr; vec3_t temp, origin; int scaleUpTime = BG_Buildable( cent->currentState.modelindex )->buildTime; int time; time = cent->currentState.time; //should the creep be growing or receding? if( time >= 0 ) { msec = cg.time - time; if( msec >= 0 && msec < scaleUpTime ) frac = (float)msec / scaleUpTime; else frac = 1.0f; } else if( time < 0 ) { msec = cg.time + time; if( msec >= 0 && msec < CREEP_SCALEDOWN_TIME ) frac = 1.0f - ( (float)msec / CREEP_SCALEDOWN_TIME ); else frac = 0.0f; } VectorCopy( cent->currentState.origin2, temp ); VectorScale( temp, -CREEP_DISTANCE, temp ); VectorAdd( temp, cent->lerpOrigin, temp ); CG_Trace( &tr, cent->lerpOrigin, NULL, NULL, temp, cent->currentState.number, MASK_PLAYERSOLID ); VectorCopy( tr.endpos, origin ); size = CREEP_SIZE * frac; if( size > 0.0f && tr.fraction < 1.0f ) CG_ImpactMark( cgs.media.creepShader, origin, cent->currentState.origin2, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, qfalse, size, qtrue ); }
static void FireAreaZap( gentity_t *ent ) { trace_t tr; gentity_t *traceEnt; G_WideTrace( &tr, ent, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_WIDTH, LEVEL2_AREAZAP_WIDTH, &traceEnt ); if ( traceEnt == NULL ) { return; } if ( ( traceEnt->client && traceEnt->client->pers.team == TEAM_HUMANS ) || ( traceEnt->s.eType == ET_BUILDABLE && BG_Buildable( traceEnt->s.modelindex )->team == TEAM_HUMANS ) ) { CreateNewZap( ent, traceEnt ); } }
/* ================ G_SelectSpawnBuildable find the nearest buildable of the right type that is spawned/healthy/unblocked etc. ================ */ static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable ) { gentity_t *search = NULL; gentity_t *spot = NULL; while ( ( search = G_IterateEntitiesOfClass( search, BG_Buildable( buildable )->entityName ) ) != NULL ) { if ( !search->spawned ) { continue; } if ( search->health <= 0 ) { continue; } if ( search->s.groundEntityNum == ENTITYNUM_NONE ) { continue; } if ( search->clientSpawnTime > 0 ) { continue; } if ( G_CheckSpawnPoint( search->s.number, search->s.origin, search->s.origin2, buildable, NULL ) != NULL ) { continue; } if ( !spot || DistanceSquared( preference, search->s.origin ) < DistanceSquared( preference, spot->s.origin ) ) { spot = search; } } return spot; }