diff options
Diffstat (limited to 'src/game/g_buildable.c')
-rw-r--r-- | src/game/g_buildable.c | 2987 |
1 files changed, 0 insertions, 2987 deletions
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c deleted file mode 100644 index 083e0662..00000000 --- a/src/game/g_buildable.c +++ /dev/null @@ -1,2987 +0,0 @@ -/* - * Portions Copyright (C) 2000-2001 Tim Angus - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the OSML - Open Source Modification License v1.0 as - * described in the file COPYING which is distributed with this source - * code. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - -#include "g_local.h" - -/* -================ -G_setBuildableAnim - -Triggers an animation client side -================ -*/ -void G_setBuildableAnim( gentity_t *ent, buildableAnimNumber_t anim, qboolean force ) -{ - int localAnim = anim; - - if( force ) - localAnim |= ANIM_FORCEBIT; - - localAnim |= ( ( ent->s.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ); - - ent->s.legsAnim = localAnim; -} - -/* -================ -G_setIdleBuildableAnim - -Set the animation to use whilst no other animations are running -================ -*/ -void G_setIdleBuildableAnim( gentity_t *ent, buildableAnimNumber_t anim ) -{ - ent->s.torsoAnim = anim; -} - -/* -=============== -G_CheckSpawnPoint - -Check if a spawn at a specified point is valid -=============== -*/ -gentity_t *G_CheckSpawnPoint( int spawnNum, vec3_t origin, vec3_t normal, - buildable_t spawn, vec3_t spawnOrigin ) -{ - float displacement; - vec3_t mins, maxs; - vec3_t cmins, cmaxs; - vec3_t localOrigin; - trace_t tr; - - BG_FindBBoxForBuildable( spawn, mins, maxs ); - - if( spawn == BA_A_SPAWN ) - { - VectorSet( cmins, -MAX_ALIEN_BBOX, -MAX_ALIEN_BBOX, -MAX_ALIEN_BBOX ); - VectorSet( cmaxs, MAX_ALIEN_BBOX, MAX_ALIEN_BBOX, MAX_ALIEN_BBOX ); - - displacement = ( maxs[ 2 ] + MAX_ALIEN_BBOX ) * M_ROOT3; - VectorMA( origin, displacement, normal, localOrigin ); - - trap_Trace( &tr, origin, NULL, NULL, localOrigin, spawnNum, MASK_SHOT ); - - if( tr.entityNum != ENTITYNUM_NONE ) - return &g_entities[ tr.entityNum ]; - - trap_Trace( &tr, localOrigin, cmins, cmaxs, localOrigin, -1, MASK_SHOT ); - - if( tr.entityNum == ENTITYNUM_NONE ) - { - if( spawnOrigin != NULL ) - VectorCopy( localOrigin, spawnOrigin ); - - return NULL; - } - else - return &g_entities[ tr.entityNum ]; - } - else if( spawn == BA_H_SPAWN ) - { - BG_FindBBoxForClass( PCL_HUMAN, cmins, cmaxs, NULL, NULL, NULL ); - - VectorCopy( origin, localOrigin ); - localOrigin[ 2 ] += maxs[ 2 ] + fabs( cmins[ 2 ] ) + 1.0f; - - trap_Trace( &tr, origin, NULL, NULL, localOrigin, spawnNum, MASK_SHOT ); - - if( tr.entityNum != ENTITYNUM_NONE ) - return &g_entities[ tr.entityNum ]; - - trap_Trace( &tr, localOrigin, cmins, cmaxs, localOrigin, -1, MASK_SHOT ); - - if( tr.entityNum == ENTITYNUM_NONE ) - { - if( spawnOrigin != NULL ) - VectorCopy( localOrigin, spawnOrigin ); - - return NULL; - } - else - return &g_entities[ tr.entityNum ]; - } - - return NULL; -} - -/* -================ -G_NumberOfDependants - -Return number of entities that depend on this one -================ -*/ -static int G_NumberOfDependants( gentity_t *self ) -{ - int i, n = 0; - gentity_t *ent; - - for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( ent->parentNode == self ) - n++; - } - - return n; -} - -#define POWER_REFRESH_TIME 2000 - -/* -================ -findPower - -attempt to find power for self, return qtrue if successful -================ -*/ -static qboolean findPower( gentity_t *self ) -{ - int i; - gentity_t *ent; - gentity_t *closestPower = NULL; - int distance = 0; - int minDistance = 10000; - vec3_t temp_v; - qboolean foundPower = qfalse; - - if( self->biteam != BIT_HUMANS ) - return qfalse; - - //reactor is always powered - if( self->s.modelindex == BA_H_REACTOR ) - return qtrue; - - //if this already has power then stop now - if( self->parentNode && self->parentNode->powered ) - return qtrue; - - //reset parent - self->parentNode = NULL; - - //iterate through entities - for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - //if entity is a power item calculate the distance to it - if( ( ent->s.modelindex == BA_H_REACTOR || ent->s.modelindex == BA_H_REPEATER ) && - ent->spawned ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); - if( distance < minDistance && ent->powered ) - { - closestPower = ent; - minDistance = distance; - foundPower = qtrue; - } - } - } - - //if there were no power items nearby give up - if( !foundPower ) - return qfalse; - - //bleh - if( ( closestPower->s.modelindex == BA_H_REACTOR && ( minDistance <= REACTOR_BASESIZE ) ) || - ( closestPower->s.modelindex == BA_H_REPEATER && ( minDistance <= REPEATER_BASESIZE ) && - closestPower->powered ) ) - { - self->parentNode = closestPower; - - return qtrue; - } - else - return qfalse; -} - -/* -================ -G_isPower - -Simple wrapper to findPower to check if a location has power -================ -*/ -qboolean G_isPower( vec3_t origin ) -{ - gentity_t dummy; - - dummy.parentNode = NULL; - dummy.biteam = BIT_HUMANS; - VectorCopy( origin, dummy.s.origin ); - - return findPower( &dummy ); -} - -/* -================ -findDCC - -attempt to find a controlling DCC for self, return qtrue if successful -================ -*/ -static qboolean findDCC( gentity_t *self ) -{ - int i; - gentity_t *ent; - gentity_t *closestDCC = NULL; - int distance = 0; - int minDistance = 10000; - vec3_t temp_v; - qboolean foundDCC = qfalse; - - if( self->biteam != BIT_HUMANS ) - return qfalse; - - //if this already has dcc then stop now - if( self->dccNode && self->dccNode->powered ) - return qtrue; - - //reset parent - self->dccNode = NULL; - - //iterate through entities - for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - //if entity is a dcc calculate the distance to it - if( ent->s.modelindex == BA_H_DCC && ent->spawned ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); - if( distance < minDistance && ent->powered ) - { - closestDCC = ent; - minDistance = distance; - foundDCC = qtrue; - } - } - } - - //if there were no power items nearby give up - if( !foundDCC ) - return qfalse; - - self->dccNode = closestDCC; - - return qtrue; -} - -/* -================ -G_isDCC - -simple wrapper to findDCC to check for a dcc -================ -*/ -qboolean G_isDCC( void ) -{ - gentity_t dummy; - - dummy.dccNode = NULL; - dummy.biteam = BIT_HUMANS; - - return findDCC( &dummy ); -} - -/* -================ -findOvermind - -Attempt to find an overmind for self -================ -*/ -static qboolean findOvermind( gentity_t *self ) -{ - int i; - gentity_t *ent; - - if( self->biteam != BIT_ALIENS ) - return qfalse; - - //if this already has overmind then stop now - if( self->overmindNode && self->overmindNode->health > 0 ) - return qtrue; - - //reset parent - self->overmindNode = NULL; - - //iterate through entities - for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - //if entity is an overmind calculate the distance to it - if( ent->s.modelindex == BA_A_OVERMIND && ent->spawned && ent->health > 0 ) - { - self->overmindNode = ent; - return qtrue; - } - } - - return qfalse; -} - -/* -================ -G_isOvermind - -Simple wrapper to findOvermind to check if a location has an overmind -================ -*/ -qboolean G_isOvermind( void ) -{ - gentity_t dummy; - - dummy.overmindNode = NULL; - dummy.biteam = BIT_ALIENS; - - return findOvermind( &dummy ); -} - -/* -================ -findCreep - -attempt to find creep for self, return qtrue if successful -================ -*/ -static qboolean findCreep( gentity_t *self ) -{ - int i; - gentity_t *ent; - gentity_t *closestSpawn = NULL; - int distance = 0; - int minDistance = 10000; - vec3_t temp_v; - - //don't check for creep if flying through the air - if( self->s.groundEntityNum == -1 ) - return qtrue; - - //if self does not have a parentNode or it's parentNode is invalid find a new one - if( ( self->parentNode == NULL ) || !self->parentNode->inuse ) - { - for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( ( ent->s.modelindex == BA_A_SPAWN || ent->s.modelindex == BA_A_OVERMIND ) && - ent->spawned ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); - if( distance < minDistance ) - { - closestSpawn = ent; - minDistance = distance; - } - } - } - - if( minDistance <= CREEP_BASESIZE ) - { - self->parentNode = closestSpawn; - return qtrue; - } - else - return qfalse; - } - - //if we haven't returned by now then we must already have a valid parent - return qtrue; -} - -/* -================ -isCreep - -simple wrapper to findCreep to check if a location has creep -================ -*/ -static qboolean isCreep( vec3_t origin ) -{ - gentity_t dummy; - - dummy.parentNode = NULL; - VectorCopy( origin, dummy.s.origin ); - - return findCreep( &dummy ); -} - -/* -================ -creepSlow - -Set any nearby humans' SS_CREEPSLOWED flag -================ -*/ -static void creepSlow( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range; - vec3_t mins, maxs; - int i, num; - gentity_t *enemy; - buildable_t buildable = self->s.modelindex; - float creepSize = (float)BG_FindCreepSizeForBuildable( buildable ); - - VectorSet( range, creepSize, creepSize, creepSize ); - - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - //find humans - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && - enemy->client->ps.groundEntityNum != ENTITYNUM_NONE && - G_Visible( self, enemy ) ) - { - enemy->client->ps.stats[ STAT_STATE ] |= SS_CREEPSLOWED; - enemy->client->lastCreepSlowTime = level.time; - } - } -} - -/* -================ -nullDieFunction - -hack to prevent compilers complaining about function pointer -> NULL conversion -================ -*/ -static void nullDieFunction( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) -{ -} - -/* -================ -freeBuildable -================ -*/ -static void freeBuildable( gentity_t *self ) -{ - G_FreeEntity( self ); -} - - -//================================================================================== - - - -/* -================ -A_CreepRecede - -Called when an alien spawn dies -================ -*/ -void A_CreepRecede( gentity_t *self ) -{ - //if the creep just died begin the recession - if( !( self->s.eFlags & EF_DEAD ) ) - { - self->s.eFlags |= EF_DEAD; - G_AddEvent( self, EV_BUILD_DESTROY, 0 ); - - if( self->spawned ) - self->s.time = -level.time; - else - self->s.time = -( level.time - - (int)( (float)CREEP_SCALEDOWN_TIME * - ( 1.0f - ( (float)( level.time - self->buildTime ) / - (float)BG_FindBuildTimeForBuildable( self->s.modelindex ) ) ) ) ); - } - - //creep is still receeding - if( ( self->timestamp + 10000 ) > level.time ) - self->nextthink = level.time + 500; - else //creep has died - G_FreeEntity( self ); -} - - - - -//================================================================================== - - - - -/* -================ -ASpawn_Melt - -Called when an alien spawn dies -================ -*/ -void ASpawn_Melt( gentity_t *self ) -{ - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath, PTE_ALIENS ); - - //start creep recession - if( !( self->s.eFlags & EF_DEAD ) ) - { - self->s.eFlags |= EF_DEAD; - G_AddEvent( self, EV_BUILD_DESTROY, 0 ); - - if( self->spawned ) - self->s.time = -level.time; - else - self->s.time = -( level.time - - (int)( (float)CREEP_SCALEDOWN_TIME * - ( 1.0f - ( (float)( level.time - self->buildTime ) / - (float)BG_FindBuildTimeForBuildable( self->s.modelindex ) ) ) ) ); - } - - //not dead yet - if( ( self->timestamp + 10000 ) > level.time ) - self->nextthink = level.time + 500; - else //dead now - G_FreeEntity( self ); -} - -/* -================ -ASpawn_Blast - -Called when an alien spawn dies -================ -*/ -void ASpawn_Blast( gentity_t *self ) -{ - vec3_t dir; - - VectorCopy( self->s.origin2, dir ); - - //do a bit of radius damage - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath, PTE_ALIENS ); - - //pretty events and item cleanup - self->s.eFlags |= EF_NODRAW; //don't draw the model once it's destroyed - G_AddEvent( self, EV_ALIEN_BUILDABLE_EXPLOSION, DirToByte( dir ) ); - self->timestamp = level.time; - self->think = ASpawn_Melt; - self->nextthink = level.time + 500; //wait .5 seconds before damaging others - - self->r.contents = 0; //stop collisions... - trap_LinkEntity( self ); //...requires a relink -} - -/* -================ -ASpawn_Die - -Called when an alien spawn dies -================ -*/ -void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) -{ - G_setBuildableAnim( self, BANIM_DESTROY1, qtrue ); - G_setIdleBuildableAnim( self, BANIM_DESTROYED ); - - self->die = nullDieFunction; - self->think = ASpawn_Blast; - - if( self->spawned ) - self->nextthink = level.time + 5000; - else - self->nextthink = level.time; //blast immediately - - self->s.eFlags &= ~EF_FIRING; //prevent any firing effects - - if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - if( self->s.modelindex == BA_A_OVERMIND ) - G_AddCreditToClient( attacker->client, OVERMIND_VALUE, qtrue ); - else if( self->s.modelindex == BA_A_SPAWN ) - G_AddCreditToClient( attacker->client, ASPAWN_VALUE, qtrue ); - } -} - -/* -================ -ASpawn_Think - -think function for Alien Spawn -================ -*/ -void ASpawn_Think( gentity_t *self ) -{ - gentity_t *ent; - - if( self->spawned ) - { - //only suicide if at rest - if( self->s.groundEntityNum ) - { - if( ( ent = G_CheckSpawnPoint( self->s.number, self->s.origin, - self->s.origin2, BA_A_SPAWN, NULL ) ) != NULL ) - { - if( ent->s.eType == ET_BUILDABLE || ent->s.number == ENTITYNUM_WORLD || - ent->s.eType == ET_MOVER ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - if( ent->s.eType == ET_CORPSE ) - G_FreeEntity( ent ); //quietly remove - } - } - } - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - -/* -================ -ASpawn_Pain - -pain function for Alien Spawn -================ -*/ -void ASpawn_Pain( gentity_t *self, gentity_t *attacker, int damage ) -{ - G_setBuildableAnim( self, BANIM_PAIN1, qfalse ); -} - - - - - -//================================================================================== - - - - - -#define OVERMIND_ATTACK_PERIOD 10000 -#define OVERMIND_DYING_PERIOD 5000 -#define OVERMIND_SPAWNS_PERIOD 30000 - -/* -================ -AOvermind_Think - -Think function for Alien Overmind -================ -*/ -void AOvermind_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range = { OVERMIND_ATTACK_RANGE, OVERMIND_ATTACK_RANGE, OVERMIND_ATTACK_RANGE }; - vec3_t mins, maxs; - int i, num; - gentity_t *enemy; - - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - if( self->spawned && ( self->health > 0 ) ) - { - //do some damage - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - self->timestamp = level.time; - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, MOD_OVERMIND, PTE_ALIENS ); - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - } - } - - //low on spawns - if( level.numAlienSpawns <= 0 && level.time > self->overmindSpawnsTimer ) - { - self->overmindSpawnsTimer = level.time + OVERMIND_SPAWNS_PERIOD; - G_BroadcastEvent( EV_OVERMIND_SPAWNS, 0 ); - } - - //overmind dying - if( self->health < ( OVERMIND_HEALTH / 10.0f ) && level.time > self->overmindDyingTimer ) - { - self->overmindDyingTimer = level.time + OVERMIND_DYING_PERIOD; - G_BroadcastEvent( EV_OVERMIND_DYING, 0 ); - } - - //overmind under attack - if( self->health < self->lastHealth && level.time > self->overmindAttackTimer ) - { - self->overmindAttackTimer = level.time + OVERMIND_ATTACK_PERIOD; - G_BroadcastEvent( EV_OVERMIND_ATTACK, 0 ); - } - - self->lastHealth = self->health; - } - else - self->overmindSpawnsTimer = level.time + OVERMIND_SPAWNS_PERIOD; - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - - - - - - -//================================================================================== - - - - - -/* -================ -ABarricade_Pain - -pain function for Alien Spawn -================ -*/ -void ABarricade_Pain( gentity_t *self, gentity_t *attacker, int damage ) -{ - if( rand( ) % 1 ) - G_setBuildableAnim( self, BANIM_PAIN1, qfalse ); - else - G_setBuildableAnim( self, BANIM_PAIN2, qfalse ); -} - -/* -================ -ABarricade_Blast - -Called when an alien spawn dies -================ -*/ -void ABarricade_Blast( gentity_t *self ) -{ - vec3_t dir; - - VectorCopy( self->s.origin2, dir ); - - //do a bit of radius damage - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath, PTE_ALIENS ); - - //pretty events and item cleanup - self->s.eFlags |= EF_NODRAW; //don't draw the model once its destroyed - G_AddEvent( self, EV_ALIEN_BUILDABLE_EXPLOSION, DirToByte( dir ) ); - self->timestamp = level.time; - self->think = A_CreepRecede; - self->nextthink = level.time + 500; //wait .5 seconds before damaging others - - self->r.contents = 0; //stop collisions... - trap_LinkEntity( self ); //...requires a relink -} - -/* -================ -ABarricade_Die - -Called when an alien spawn dies -================ -*/ -void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) -{ - G_setBuildableAnim( self, BANIM_DESTROY1, qtrue ); - G_setIdleBuildableAnim( self, BANIM_DESTROYED ); - - self->die = nullDieFunction; - self->think = ABarricade_Blast; - self->s.eFlags &= ~EF_FIRING; //prevent any firing effects - - if( self->spawned ) - self->nextthink = level.time + 5000; - else - self->nextthink = level.time; //blast immediately -} - -/* -================ -ABarricade_Think - -Think function for Alien Barricade -================ -*/ -void ABarricade_Think( gentity_t *self ) -{ - //if there is no creep nearby die - if( !findCreep( self ) ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - - - - -//================================================================================== - - - - -void AAcidTube_Think( gentity_t *self ); - -/* -================ -AAcidTube_Damage - -Damage function for Alien Acid Tube -================ -*/ -void AAcidTube_Damage( gentity_t *self ) -{ - if( self->spawned ) - { - if( !( self->s.eFlags & EF_FIRING ) ) - { - self->s.eFlags |= EF_FIRING; - G_AddEvent( self, EV_ALIEN_ACIDTUBE, DirToByte( self->s.origin2 ) ); - } - - if( ( self->timestamp + ACIDTUBE_REPEAT ) > level.time ) - self->think = AAcidTube_Damage; - else - { - self->think = AAcidTube_Think; - self->s.eFlags &= ~EF_FIRING; - } - - //do some damage - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath, PTE_ALIENS ); - } - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - -/* -================ -AAcidTube_Think - -Think function for Alien Acid Tube -================ -*/ -void AAcidTube_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range = { ACIDTUBE_RANGE, ACIDTUBE_RANGE, ACIDTUBE_RANGE }; - vec3_t mins, maxs; - int i, num; - gentity_t *enemy; - - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - //if there is no creep nearby die - if( !findCreep( self ) ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - if( self->spawned && findOvermind( self ) ) - { - //do some damage - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( !G_Visible( self, enemy ) ) - continue; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - self->timestamp = level.time; - self->think = AAcidTube_Damage; - self->nextthink = level.time + 100; - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - return; - } - } - } - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - - - - -//================================================================================== - - - - -/* -================ -AHive_Think - -Think function for Alien Hive -================ -*/ -void AHive_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range = { ACIDTUBE_RANGE, ACIDTUBE_RANGE, ACIDTUBE_RANGE }; - vec3_t mins, maxs; - int i, num; - gentity_t *enemy; - vec3_t dirToTarget; - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); - - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - //if there is no creep nearby die - if( !findCreep( self ) ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - if( self->timestamp < level.time ) - self->active = qfalse; //nothing has returned in HIVE_REPEAT seconds, forget about it - - if( self->spawned && !self->active && findOvermind( self ) ) - { - //do some damage - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( enemy->health <= 0 ) - continue; - - if( !G_Visible( self, enemy ) ) - continue; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - self->active = qtrue; - self->target_ent = enemy; - self->timestamp = level.time + HIVE_REPEAT; - - VectorSubtract( enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget ); - VectorNormalize( dirToTarget ); - vectoangles( dirToTarget, self->turretAim ); - - //fire at target - FireWeapon( self ); - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - return; - } - } - } - - creepSlow( self ); -} - - - - -//================================================================================== - - - - -#define HOVEL_TRACE_DEPTH 128.0f - -/* -================ -AHovel_Blocked - -Is this hovel entrance blocked? -================ -*/ -qboolean AHovel_Blocked( gentity_t *hovel, gentity_t *player, qboolean provideExit ) -{ - vec3_t forward, normal, origin, start, end, angles, hovelMaxs; - vec3_t mins, maxs; - float displacement; - trace_t tr; - - BG_FindBBoxForBuildable( BA_A_HOVEL, NULL, hovelMaxs ); - BG_FindBBoxForClass( player->client->ps.stats[ STAT_PCLASS ], - mins, maxs, NULL, NULL, NULL ); - - VectorCopy( hovel->s.origin2, normal ); - AngleVectors( hovel->s.angles, forward, NULL, NULL ); - VectorInverse( forward ); - - displacement = VectorMaxComponent( maxs ) * M_ROOT3 + - VectorMaxComponent( hovelMaxs ) * M_ROOT3 + 1.0f; - - VectorMA( hovel->s.origin, displacement, forward, origin ); - vectoangles( forward, angles ); - - VectorMA( origin, HOVEL_TRACE_DEPTH, normal, start ); - - //compute a place up in the air to start the real trace - trap_Trace( &tr, origin, mins, maxs, start, player->s.number, MASK_PLAYERSOLID ); - VectorMA( origin, ( HOVEL_TRACE_DEPTH * tr.fraction ) - 1.0f, normal, start ); - VectorMA( origin, -HOVEL_TRACE_DEPTH, normal, end ); - - trap_Trace( &tr, start, mins, maxs, end, player->s.number, MASK_PLAYERSOLID ); - - if( tr.startsolid ) - return qtrue; - - VectorCopy( tr.endpos, origin ); - - trap_Trace( &tr, origin, mins, maxs, origin, player->s.number, MASK_PLAYERSOLID ); - - if( provideExit ) - { - G_SetOrigin( player, origin ); - VectorCopy( origin, player->client->ps.origin ); - VectorCopy( vec3_origin, player->client->ps.velocity ); - SetClientViewAngle( player, angles ); - } - - if( tr.fraction < 1.0f ) - return qtrue; - else - return qfalse; -} - -/* -================ -APropHovel_Blocked - -Wrapper to test a hovel placement for validity -================ -*/ -static qboolean APropHovel_Blocked( vec3_t origin, vec3_t angles, vec3_t normal, - gentity_t *player ) -{ - gentity_t hovel; - - VectorCopy( origin, hovel.s.origin ); - VectorCopy( angles, hovel.s.angles ); - VectorCopy( normal, hovel.s.origin2 ); - - return AHovel_Blocked( &hovel, player, qfalse ); -} - -/* -================ -AHovel_Use - -Called when an alien uses a hovel -================ -*/ -void AHovel_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) -{ - vec3_t hovelOrigin, hovelAngles, inverseNormal; - - if( self->spawned && findOvermind( self ) ) - { - if( self->active ) - { - //this hovel is in use - G_TriggerMenu( activator->client->ps.clientNum, MN_A_HOVEL_OCCUPIED ); - } - else if( ( ( activator->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 ) || - ( activator->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ) ) && - activator->health > 0 && self->health > 0 ) - { - if( AHovel_Blocked( self, activator, qfalse ) ) - { - //you can get in, but you can't get out - G_TriggerMenu( activator->client->ps.clientNum, MN_A_HOVEL_BLOCKED ); - return; - } - - self->active = qtrue; - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - - //prevent lerping - activator->client->ps.eFlags ^= EF_TELEPORT_BIT; - activator->client->ps.eFlags |= EF_NODRAW; - - activator->client->ps.stats[ STAT_STATE ] |= SS_HOVELING; - activator->client->hovel = self; - self->builder = activator; - - VectorCopy( self->s.pos.trBase, hovelOrigin ); - VectorMA( hovelOrigin, 128.0f, self->s.origin2, hovelOrigin ); - - VectorCopy( self->s.origin2, inverseNormal ); - VectorInverse( inverseNormal ); - vectoangles( inverseNormal, hovelAngles ); - - VectorCopy( activator->s.pos.trBase, activator->client->hovelOrigin ); - - G_SetOrigin( activator, hovelOrigin ); - VectorCopy( hovelOrigin, activator->client->ps.origin ); - SetClientViewAngle( activator, hovelAngles ); - } - } -} - - -/* -================ -AHovel_Think - -Think for alien hovel -================ -*/ -void AHovel_Think( gentity_t *self ) -{ - if( self->spawned ) - { - if( self->active ) - G_setIdleBuildableAnim( self, BANIM_IDLE2 ); - else - G_setIdleBuildableAnim( self, BANIM_IDLE1 ); - } - - creepSlow( self ); - - self->nextthink = level.time + 200; -} - -/* -================ -AHovel_Die - -Die for alien hovel -================ -*/ -void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) -{ - vec3_t dir; - - VectorCopy( self->s.origin2, dir ); - - //do a bit of radius damage - G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath, PTE_ALIENS ); - - //pretty events and item cleanup - self->s.eFlags |= EF_NODRAW; //don't draw the model once its destroyed - G_AddEvent( self, EV_ALIEN_BUILDABLE_EXPLOSION, DirToByte( dir ) ); - self->s.eFlags &= ~EF_FIRING; //prevent any firing effects - self->timestamp = level.time; - self->think = ASpawn_Melt; - self->nextthink = level.time + 500; //wait .5 seconds before damaging others - - //if the hovel is occupied free the occupant - if( self->active ) - { - gentity_t *builder = self->builder; - vec3_t newOrigin; - vec3_t newAngles; - - VectorCopy( self->s.angles, newAngles ); - newAngles[ ROLL ] = 0; - - VectorCopy( self->s.origin, newOrigin ); - VectorMA( newOrigin, 1.0f, self->s.origin2, newOrigin ); - - //prevent lerping - builder->client->ps.eFlags ^= EF_TELEPORT_BIT; - builder->client->ps.eFlags &= ~EF_NODRAW; - - G_SetOrigin( builder, newOrigin ); - VectorCopy( newOrigin, builder->client->ps.origin ); - SetClientViewAngle( builder, newAngles ); - - //client leaves hovel - builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; - } - - self->r.contents = 0; //stop collisions... - trap_LinkEntity( self ); //...requires a relink -} - - - - - -//================================================================================== - - - - -/* -================ -ABooster_Touch - -Called when an alien touches a booster -================ -*/ -void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace ) -{ - gclient_t *client = other->client; - - if( !self->spawned ) - return; - - if( !findOvermind( self ) ) - return; - - if( !client ) - return; - - if( client && client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - return; - - //only allow boostage once every 30 seconds - if( client->lastBoostedTime + BOOSTER_INTERVAL > level.time ) - return; - - if( !( client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) ) - { - client->ps.stats[ STAT_STATE ] |= SS_BOOSTED; - client->lastBoostedTime = level.time; - } -} - - - - -//================================================================================== - -#define TRAPPER_ACCURACY 10 // lower is better - -/* -================ -ATrapper_FireOnEnemy - -Used by ATrapper_Think to fire at enemy -================ -*/ -void ATrapper_FireOnEnemy( gentity_t *self, int firespeed, float range ) -{ - gentity_t *enemy = self->enemy; - vec3_t dirToTarget; - vec3_t halfAcceleration, thirdJerk; - float distanceToTarget = BG_FindRangeForBuildable( self->s.modelindex ); - int lowMsec = 0; - int highMsec = (int)( ( - ( ( distanceToTarget * LOCKBLOB_SPEED ) + - ( distanceToTarget * BG_FindSpeedForClass( enemy->client->ps.stats[ STAT_PCLASS ] ) ) ) / - ( LOCKBLOB_SPEED * LOCKBLOB_SPEED ) ) * 1000.0f ); - - VectorScale( enemy->acceleration, 1.0f / 2.0f, halfAcceleration ); - VectorScale( enemy->jerk, 1.0f / 3.0f, thirdJerk ); - - // highMsec and lowMsec can only move toward - // one another, so the loop must terminate - while( highMsec - lowMsec > TRAPPER_ACCURACY ) - { - int partitionMsec = ( highMsec + lowMsec ) / 2; - float time = (float)partitionMsec / 1000.0f; - float projectileDistance = LOCKBLOB_SPEED * time; - - VectorMA( enemy->s.pos.trBase, time, enemy->s.pos.trDelta, dirToTarget ); - VectorMA( dirToTarget, time * time, halfAcceleration, dirToTarget ); - VectorMA( dirToTarget, time * time * time, thirdJerk, dirToTarget ); - VectorSubtract( dirToTarget, self->s.pos.trBase, dirToTarget ); - distanceToTarget = VectorLength( dirToTarget ); - - if( projectileDistance < distanceToTarget ) - lowMsec = partitionMsec; - else if( projectileDistance > distanceToTarget ) - highMsec = partitionMsec; - else if( projectileDistance == distanceToTarget ) - break; // unlikely to happen - } - - VectorNormalize( dirToTarget ); - vectoangles( dirToTarget, self->turretAim ); - - //fire at target - FireWeapon( self ); - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - self->count = level.time + firespeed; -} - -/* -================ -ATrapper_CheckTarget - -Used by ATrapper_Think to check enemies for validity -================ -*/ -qboolean ATrapper_CheckTarget( gentity_t *self, gentity_t *target, int range ) -{ - vec3_t distance; - trace_t trace; - - if( !target ) // Do we have a target? - return qfalse; - if( !target->inuse ) // Does the target still exist? - return qfalse; - if( target == self ) // is the target us? - return qfalse; - if( !target->client ) // is the target a bot or player? - return qfalse; - if( target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) // one of us? - return qfalse; - if( target->client->sess.sessionTeam == TEAM_SPECTATOR ) // is the target alive? - return qfalse; - if( target->health <= 0 ) // is the target still alive? - return qfalse; - if( target->client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ) // locked? - return qfalse; - - VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, distance ); - if( VectorLength( distance ) > range ) // is the target within range? - return qfalse; - - //only allow a narrow field of "vision" - VectorNormalize( distance ); //is now direction of target - if( DotProduct( distance, self->s.origin2 ) < LOCKBLOB_DOT ) - return qfalse; - - trap_Trace( &trace, self->s.pos.trBase, NULL, NULL, target->s.pos.trBase, self->s.number, MASK_SHOT ); - if ( trace.contents & CONTENTS_SOLID ) // can we see the target? - return qfalse; - - return qtrue; -} - -/* -================ -ATrapper_FindEnemy - -Used by ATrapper_Think to locate enemy gentities -================ -*/ -void ATrapper_FindEnemy( gentity_t *ent, int range ) -{ - gentity_t *target; - - //iterate through entities - for( target = g_entities; target < &g_entities[ level.num_entities ]; target++ ) - { - //if target is not valid keep searching - if( !ATrapper_CheckTarget( ent, target, range ) ) - continue; - - //we found a target - ent->enemy = target; - return; - } - - //couldn't find a target - ent->enemy = NULL; -} - -/* -================ -ATrapper_Think - -think function for Alien Defense -================ -*/ -void ATrapper_Think( gentity_t *self ) -{ - int range = BG_FindRangeForBuildable( self->s.modelindex ); - int firespeed = BG_FindFireSpeedForBuildable( self->s.modelindex ); - - creepSlow( self ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); - - //if there is no creep nearby die - if( !findCreep( self ) ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - if( self->spawned && findOvermind( self ) ) - { - //if the current target is not valid find a new one - if( !ATrapper_CheckTarget( self, self->enemy, range ) ) - ATrapper_FindEnemy( self, range ); - - //if a new target cannot be found don't do anything - if( !self->enemy ) - return; - - //if we are pointing at our target and we can fire shoot it - if( self->count < level.time ) - ATrapper_FireOnEnemy( self, firespeed, range ); - } -} - - - -//================================================================================== - - - -/* -================ -HRepeater_Think - -Think for human power repeater -================ -*/ -void HRepeater_Think( gentity_t *self ) -{ - int i; - qboolean reactor = qfalse; - gentity_t *ent; - - if( self->spawned ) - { - //iterate through entities - for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( ent->s.modelindex == BA_H_REACTOR && ent->spawned ) - reactor = qtrue; - } - } - - if( G_NumberOfDependants( self ) == 0 ) - { - //if no dependants for x seconds then disappear - if( self->count < 0 ) - self->count = level.time; - else if( self->count > 0 && ( ( level.time - self->count ) > REPEATER_INACTIVE_TIME ) ) - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - } - else - self->count = -1; - - self->powered = reactor; - - self->nextthink = level.time + POWER_REFRESH_TIME; -} - -/* -================ -HRepeater_Use - -Use for human power repeater -================ -*/ -void HRepeater_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) -{ - if( self->health <= 0 ) - return; - - if( !self->spawned ) - return; - - if( other ) - G_GiveClientMaxAmmo( other, qtrue ); -} - - -#define DCC_ATTACK_PERIOD 10000 - -/* -================ -HReactor_Think - -Think function for Human Reactor -================ -*/ -void HReactor_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range = { REACTOR_ATTACK_RANGE, REACTOR_ATTACK_RANGE, REACTOR_ATTACK_RANGE }; - vec3_t mins, maxs; - int i, num; - gentity_t *enemy, *tent; - - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - if( self->spawned && ( self->health > 0 ) ) - { - //do some damage - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - self->timestamp = level.time; - G_SelectiveRadiusDamage( self->s.pos.trBase, self, REACTOR_ATTACK_DAMAGE, - REACTOR_ATTACK_RANGE, self, MOD_REACTOR, PTE_HUMANS ); - - tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL ); - - VectorCopy( self->s.pos.trBase, tent->s.origin2 ); - - tent->s.generic1 = self->s.number; //src - tent->s.clientNum = enemy->s.number; //dest - } - } - - //reactor under attack - if( self->health < self->lastHealth && - level.time > level.humanBaseAttackTimer && G_isDCC( ) ) - { - level.humanBaseAttackTimer = level.time + DCC_ATTACK_PERIOD; - G_BroadcastEvent( EV_DCC_ATTACK, 0 ); - } - - self->lastHealth = self->health; - } - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - -//================================================================================== - - - -/* -================ -HArmoury_Activate - -Called when a human activates an Armoury -================ -*/ -void HArmoury_Activate( gentity_t *self, gentity_t *other, gentity_t *activator ) -{ - if( self->spawned ) - { - //only humans can activate this - if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS ) - return; - - //if this is powered then call the armoury menu - if( self->powered ) - G_TriggerMenu( activator->client->ps.clientNum, MN_H_ARMOURY ); - else - G_TriggerMenu( activator->client->ps.clientNum, MN_H_NOTPOWERED ); - } -} - -/* -================ -HArmoury_Think - -Think for armoury -================ -*/ -void HArmoury_Think( gentity_t *self ) -{ - //make sure we have power - self->nextthink = level.time + POWER_REFRESH_TIME; - - self->powered = findPower( self ); -} - - - - -//================================================================================== - - - - - -/* -================ -HDCC_Think - -Think for dcc -================ -*/ -void HDCC_Think( gentity_t *self ) -{ - //make sure we have power - self->nextthink = level.time + POWER_REFRESH_TIME; - - self->powered = findPower( self ); -} - - - - -//================================================================================== - -/* -================ -HMedistat_Think - -think function for Human Medistation -================ -*/ -void HMedistat_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t mins, maxs; - int i, num; - gentity_t *player; - qboolean occupied = qfalse; - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); - - //make sure we have power - if( !( self->powered = findPower( self ) ) ) - { - self->nextthink = level.time + POWER_REFRESH_TIME; - return; - } - - if( self->spawned ) - { - VectorAdd( self->s.origin, self->r.maxs, maxs ); - VectorAdd( self->s.origin, self->r.mins, mins ); - - mins[ 2 ] += fabs( self->r.mins[ 2 ] ) + self->r.maxs[ 2 ]; - maxs[ 2 ] += 60; //player height - - //if active use the healing idle - if( self->active ) - G_setIdleBuildableAnim( self, BANIM_IDLE2 ); - - //check if a previous occupier is still here - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - player = &g_entities[ entityList[ i ] ]; - - if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] && - player->client->ps.pm_type != PM_DEAD && - self->enemy == player ) - occupied = qtrue; - } - } - - if( !occupied ) - { - self->enemy = NULL; - - //look for something to heal - for( i = 0; i < num; i++ ) - { - player = &g_entities[ entityList[ i ] ]; - - if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] && - player->client->ps.pm_type != PM_DEAD ) - { - self->enemy = player; - - //start the heal anim - if( !self->active ) - { - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - self->active = qtrue; - } - } - else if( !BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) ) - BG_AddUpgradeToInventory( UP_MEDKIT, player->client->ps.stats ); - } - } - } - - //nothing left to heal so go back to idling - if( !self->enemy && self->active ) - { - G_setBuildableAnim( self, BANIM_CONSTRUCT2, qtrue ); - G_setIdleBuildableAnim( self, BANIM_IDLE1 ); - - self->active = qfalse; - } - else if( self->enemy ) //heal! - { - if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED ) - self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; - - if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ) - self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; - - self->enemy->health++; - - //if they're completely healed, give them a medkit - if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] && - !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) ) - BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats ); - } - } -} - - - - -//================================================================================== - - - - -/* -================ -HMGTurret_TrackEnemy - -Used by HMGTurret_Think to track enemy location -================ -*/ -qboolean HMGTurret_TrackEnemy( gentity_t *self ) -{ - vec3_t dirToTarget, dttAdjusted, angleToTarget, angularDiff, xNormal; - vec3_t refNormal = { 0.0f, 0.0f, 1.0f }; - float temp, rotAngle; - float accuracyTolerance, angularSpeed; - - if( self->lev1Grabbed ) - { - //can't turn fast if grabbed - accuracyTolerance = MGTURRET_GRAB_ACCURACYTOLERANCE; - angularSpeed = MGTURRET_GRAB_ANGULARSPEED; - } - else if( self->dcced ) - { - accuracyTolerance = MGTURRET_DCC_ACCURACYTOLERANCE; - angularSpeed = MGTURRET_DCC_ANGULARSPEED; - } - else - { - accuracyTolerance = MGTURRET_ACCURACYTOLERANCE; - angularSpeed = MGTURRET_ANGULARSPEED; - } - - VectorSubtract( self->enemy->s.pos.trBase, self->s.pos.trBase, dirToTarget ); - - VectorNormalize( dirToTarget ); - - CrossProduct( self->s.origin2, refNormal, xNormal ); - VectorNormalize( xNormal ); - rotAngle = RAD2DEG( acos( DotProduct( self->s.origin2, refNormal ) ) ); - RotatePointAroundVector( dttAdjusted, xNormal, dirToTarget, rotAngle ); - - vectoangles( dttAdjusted, angleToTarget ); - - angularDiff[ PITCH ] = AngleSubtract( self->s.angles2[ PITCH ], angleToTarget[ PITCH ] ); - angularDiff[ YAW ] = AngleSubtract( self->s.angles2[ YAW ], angleToTarget[ YAW ] ); - - //if not pointing at our target then move accordingly - if( angularDiff[ PITCH ] < (-accuracyTolerance) ) - self->s.angles2[ PITCH ] += angularSpeed; - else if( angularDiff[ PITCH ] > accuracyTolerance ) - self->s.angles2[ PITCH ] -= angularSpeed; - else - self->s.angles2[ PITCH ] = angleToTarget[ PITCH ]; - - //disallow vertical movement past a certain limit - temp = fabs( self->s.angles2[ PITCH ] ); - if( temp > 180 ) - temp -= 360; - - if( temp < -MGTURRET_VERTICALCAP ) - self->s.angles2[ PITCH ] = (-360) + MGTURRET_VERTICALCAP; - - //if not pointing at our target then move accordingly - if( angularDiff[ YAW ] < (-accuracyTolerance) ) - self->s.angles2[ YAW ] += angularSpeed; - else if( angularDiff[ YAW ] > accuracyTolerance ) - self->s.angles2[ YAW ] -= angularSpeed; - else - self->s.angles2[ YAW ] = angleToTarget[ YAW ]; - - AngleVectors( self->s.angles2, dttAdjusted, NULL, NULL ); - RotatePointAroundVector( dirToTarget, xNormal, dttAdjusted, -rotAngle ); - vectoangles( dirToTarget, self->turretAim ); - - //if pointing at our target return true - if( abs( angleToTarget[ YAW ] - self->s.angles2[ YAW ] ) <= accuracyTolerance && - abs( angleToTarget[ PITCH ] - self->s.angles2[ PITCH ] ) <= accuracyTolerance ) - return qtrue; - - return qfalse; -} - - -/* -================ -HMGTurret_CheckTarget - -Used by HMGTurret_Think to check enemies for validity -================ -*/ -qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ignorePainted ) -{ - trace_t trace; - gentity_t *traceEnt; - - if( !target ) - return qfalse; - - if( !target->client ) - return qfalse; - - if( target->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) - return qfalse; - - if( target->health <= 0 ) - return qfalse; - - if( Distance( self->s.origin, target->s.pos.trBase ) > MGTURRET_RANGE ) - return qfalse; - - //some turret has already selected this target - if( self->dcced && target->targeted && target->targeted->powered && !ignorePainted ) - return qfalse; - - trap_Trace( &trace, self->s.pos.trBase, NULL, NULL, target->s.pos.trBase, self->s.number, MASK_SHOT ); - - traceEnt = &g_entities[ trace.entityNum ]; - - if( !traceEnt->client ) - return qfalse; - - if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS ) - return qfalse; - - return qtrue; -} - - -/* -================ -HMGTurret_FindEnemy - -Used by HMGTurret_Think to locate enemy gentities -================ -*/ -void HMGTurret_FindEnemy( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range; - vec3_t mins, maxs; - int i, num; - gentity_t *target; - - VectorSet( range, MGTURRET_RANGE, MGTURRET_RANGE, MGTURRET_RANGE ); - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - //find aliens - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - target = &g_entities[ entityList[ i ] ]; - - if( target->client && target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - //if target is not valid keep searching - if( !HMGTurret_CheckTarget( self, target, qfalse ) ) - continue; - - //we found a target - self->enemy = target; - return; - } - } - - if( self->dcced ) - { - //check again, this time ignoring painted targets - for( i = 0; i < num; i++ ) - { - target = &g_entities[ entityList[ i ] ]; - - if( target->client && target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - //if target is not valid keep searching - if( !HMGTurret_CheckTarget( self, target, qtrue ) ) - continue; - - //we found a target - self->enemy = target; - return; - } - } - } - - //couldn't find a target - self->enemy = NULL; -} - - -/* -================ -HMGTurret_Think - -Think function for MG turret -================ -*/ -void HMGTurret_Think( gentity_t *self ) -{ - int firespeed = BG_FindFireSpeedForBuildable( self->s.modelindex ); - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); - - //used for client side muzzle flashes - self->s.eFlags &= ~EF_FIRING; - - //if not powered don't do anything and check again for power next think - if( !( self->powered = findPower( self ) ) ) - { - self->nextthink = level.time + POWER_REFRESH_TIME; - return; - } - - if( self->spawned ) - { - //find a dcc for self - self->dcced = findDCC( self ); - - //if the current target is not valid find a new one - if( !HMGTurret_CheckTarget( self, self->enemy, qfalse ) ) - { - if( self->enemy ) - self->enemy->targeted = NULL; - - HMGTurret_FindEnemy( self ); - } - - //if a new target cannot be found don't do anything - if( !self->enemy ) - return; - - self->enemy->targeted = self; - - //if we are pointing at our target and we can fire shoot it - if( HMGTurret_TrackEnemy( self ) && ( self->count < level.time ) ) - { - //fire at target - FireWeapon( self ); - - self->s.eFlags |= EF_FIRING; - G_AddEvent( self, EV_FIRE_WEAPON, 0 ); - G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - - self->count = level.time + firespeed; - } - } -} - - - - -//================================================================================== - - - - -/* -================ -HTeslaGen_Think - -Think function for Tesla Generator -================ -*/ -void HTeslaGen_Think( gentity_t *self ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range; - vec3_t mins, maxs; - vec3_t dir; - int i, num; - gentity_t *enemy; - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); - - //if not powered don't do anything and check again for power next think - if( !( self->powered = findPower( self ) ) || !( self->dcced = findDCC( self ) ) ) - { - self->s.eFlags &= ~EF_FIRING; - self->nextthink = level.time + POWER_REFRESH_TIME; - return; - } - - if( self->spawned && self->count < level.time ) - { - //used to mark client side effects - self->s.eFlags &= ~EF_FIRING; - - VectorSet( range, TESLAGEN_RANGE, TESLAGEN_RANGE, TESLAGEN_RANGE ); - VectorAdd( self->s.origin, range, maxs ); - VectorSubtract( self->s.origin, range, mins ); - - //find aliens - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - - if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && - enemy->health > 0 ) - { - VectorSubtract( enemy->s.pos.trBase, self->s.pos.trBase, dir ); - VectorNormalize( dir ); - vectoangles( dir, self->turretAim ); - - //fire at target - FireWeapon( self ); - } - } - - if( self->s.eFlags & EF_FIRING ) - { - G_AddEvent( self, EV_FIRE_WEAPON, 0 ); - - //doesn't really need an anim - //G_setBuildableAnim( self, BANIM_ATTACK1, qfalse ); - - self->count = level.time + TESLAGEN_REPEAT; - } - } -} - - - - -//================================================================================== - - - - -/* -================ -HSpawn_Disappear - -Called when a human spawn is destroyed before it is spawned -think function -================ -*/ -void HSpawn_Disappear( gentity_t *self ) -{ - vec3_t dir; - - // we don't have a valid direction, so just point straight up - dir[ 0 ] = dir[ 1 ] = 0; - dir[ 2 ] = 1; - - self->s.eFlags |= EF_NODRAW; //don't draw the model once its destroyed - self->timestamp = level.time; - - self->think = freeBuildable; - self->nextthink = level.time + 100; - - self->r.contents = 0; //stop collisions... - trap_LinkEntity( self ); //...requires a relink -} - - -/* -================ -HSpawn_blast - -Called when a human spawn explodes -think function -================ -*/ -void HSpawn_Blast( gentity_t *self ) -{ - vec3_t dir; - - // we don't have a valid direction, so just point straight up - dir[ 0 ] = dir[ 1 ] = 0; - dir[ 2 ] = 1; - - self->s.eFlags |= EF_NODRAW; //don't draw the model once its destroyed - G_AddEvent( self, EV_HUMAN_BUILDABLE_EXPLOSION, DirToByte( dir ) ); - self->timestamp = level.time; - - //do some radius damage - G_RadiusDamage( self->s.pos.trBase, self, self->splashDamage, - self->splashRadius, self, self->splashMethodOfDeath ); - - self->think = freeBuildable; - self->nextthink = level.time + 100; - - self->r.contents = 0; //stop collisions... - trap_LinkEntity( self ); //...requires a relink -} - - -/* -================ -HSpawn_die - -Called when a human spawn dies -================ -*/ -void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) -{ - //pretty events and cleanup - G_setBuildableAnim( self, BANIM_DESTROY1, qtrue ); - G_setIdleBuildableAnim( self, BANIM_DESTROYED ); - - self->die = nullDieFunction; - self->powered = qfalse; //free up power - self->s.eFlags &= ~EF_FIRING; //prevent any firing effects - - if( self->spawned ) - { - self->think = HSpawn_Blast; - self->nextthink = level.time + HUMAN_DETONATION_DELAY; - } - else - { - self->think = HSpawn_Disappear; - self->nextthink = level.time; //blast immediately - } - - if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - if( self->s.modelindex == BA_H_REACTOR ) - G_AddCreditToClient( attacker->client, REACTOR_VALUE, qtrue ); - else if( self->s.modelindex == BA_H_SPAWN ) - G_AddCreditToClient( attacker->client, HSPAWN_VALUE, qtrue ); - } -} - -/* -================ -HSpawn_Think - -Think for human spawn -================ -*/ -void HSpawn_Think( gentity_t *self ) -{ - gentity_t *ent; - - //make sure we have power - self->powered = findPower( self ); - - if( self->spawned ) - { - //only suicide if at rest - if( self->s.groundEntityNum ) - { - if( ( ent = G_CheckSpawnPoint( self->s.number, self->s.origin, - self->s.origin2, BA_H_SPAWN, NULL ) ) != NULL ) - { - if( ent->s.eType == ET_BUILDABLE || ent->s.number == ENTITYNUM_WORLD || - ent->s.eType == ET_MOVER ) - { - G_Damage( self, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); - return; - } - - if( ent->s.eType == ET_CORPSE ) - G_FreeEntity( ent ); //quietly remove - } - } - - //spawn under attack - if( self->health < self->lastHealth && - level.time > level.humanBaseAttackTimer && G_isDCC( ) ) - { - level.humanBaseAttackTimer = level.time + DCC_ATTACK_PERIOD; - G_BroadcastEvent( EV_DCC_ATTACK, 0 ); - } - - self->lastHealth = self->health; - } - - self->nextthink = level.time + BG_FindNextThinkForBuildable( self->s.modelindex ); -} - - - - -//================================================================================== - - -/* -============ -G_BuildableTouchTriggers - -Find all trigger entities that a buildable touches. -============ -*/ -void G_BuildableTouchTriggers( gentity_t *ent ) -{ - int i, num; - int touch[ MAX_GENTITIES ]; - gentity_t *hit; - trace_t trace; - vec3_t mins, maxs; - vec3_t bmins, bmaxs; - static vec3_t range = { 10, 10, 10 }; - - // dead buildables don't activate triggers! - if( ent->health <= 0 ) - return; - - BG_FindBBoxForBuildable( ent->s.modelindex, bmins, bmaxs ); - - VectorAdd( ent->s.origin, bmins, mins ); - VectorAdd( ent->s.origin, bmaxs, maxs ); - - VectorSubtract( mins, range, mins ); - VectorAdd( maxs, range, maxs ); - - num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); - - VectorAdd( ent->s.origin, bmins, mins ); - VectorAdd( ent->s.origin, bmaxs, maxs ); - - for( i = 0; i < num; i++ ) - { - hit = &g_entities[ touch[ i ] ]; - - if( !hit->touch ) - continue; - - if( !( hit->r.contents & CONTENTS_TRIGGER ) ) - continue; - - //ignore buildables not yet spawned - if( !ent->spawned ) - continue; - - if( !trap_EntityContact( mins, maxs, hit ) ) - continue; - - memset( &trace, 0, sizeof( trace ) ); - - if( hit->touch ) - hit->touch( hit, ent, &trace ); - } -} - - -/* -=============== -G_BuildableThink - -General think function for buildables -=============== -*/ -void G_BuildableThink( gentity_t *ent, int msec ) -{ - int bHealth = BG_FindHealthForBuildable( ent->s.modelindex ); - int bRegen = BG_FindRegenRateForBuildable( ent->s.modelindex ); - int bTime = BG_FindBuildTimeForBuildable( ent->s.modelindex ); - - //pack health, power and dcc - - //toggle spawned flag for buildables - if( !ent->spawned ) - { - if( ent->buildTime + bTime < level.time ) - ent->spawned = qtrue; - } - - ent->s.generic1 = (int)( ( (float)ent->health / (float)bHealth ) * B_HEALTH_SCALE ); - - if( ent->s.generic1 < 0 ) - ent->s.generic1 = 0; - - if( ent->powered ) - ent->s.generic1 |= B_POWERED_TOGGLEBIT; - - if( ent->dcced ) - ent->s.generic1 |= B_DCCED_TOGGLEBIT; - - if( ent->spawned ) - ent->s.generic1 |= B_SPAWNED_TOGGLEBIT; - - ent->time1000 += msec; - - if( ent->time1000 >= 1000 ) - { - ent->time1000 -= 1000; - - if( !ent->spawned ) - ent->health += (int)( ceil( (float)bHealth / (float)( bTime * 0.001 ) ) ); - else if( ent->biteam == BIT_ALIENS && ent->health > 0 && ent->health < bHealth && - bRegen && ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time ) - ent->health += bRegen; - - if( ent->health > bHealth ) - ent->health = bHealth; - } - - if( ent->lev1Grabbed && ent->lev1GrabTime + LEVEL1_GRAB_TIME < level.time ) - ent->lev1Grabbed = qfalse; - - if( ent->clientSpawnTime > 0 ) - ent->clientSpawnTime -= msec; - - if( ent->clientSpawnTime < 0 ) - ent->clientSpawnTime = 0; - - //check if this buildable is touching any triggers - G_BuildableTouchTriggers( ent ); - - //fall back on normal physics routines - G_Physics( ent, msec ); -} - - -/* -=============== -G_BuildableRange - -Check whether a point is within some range of a type of buildable -=============== -*/ -qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable ) -{ - int entityList[ MAX_GENTITIES ]; - vec3_t range; - vec3_t mins, maxs; - int i, num; - gentity_t *ent; - - VectorSet( range, r, r, r ); - VectorAdd( origin, range, maxs ); - VectorSubtract( origin, range, mins ); - - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - ent = &g_entities[ entityList[ i ] ]; - - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( ent->biteam == BIT_HUMANS && !ent->powered ) - continue; - - if( ent->s.modelindex == buildable && ent->spawned ) - return qtrue; - } - - return qfalse; -} - - -/* -================ -G_itemFits - -Checks to see if an item fits in a specific area -================ -*/ -itemBuildError_t G_itemFits( gentity_t *ent, buildable_t buildable, int distance, vec3_t origin ) -{ - vec3_t angles; - vec3_t entity_origin, normal; - vec3_t mins, maxs; - trace_t tr1, tr2, tr3; - int i; - itemBuildError_t reason = IBE_NONE; - gentity_t *tempent; - float minNormal; - qboolean invert; - int contents; - playerState_t *ps = &ent->client->ps; - - BG_FindBBoxForBuildable( buildable, mins, maxs ); - - BG_PositionBuildableRelativeToPlayer( ps, mins, maxs, trap_Trace, entity_origin, angles, &tr1 ); - - trap_Trace( &tr2, entity_origin, mins, maxs, entity_origin, ent->s.number, MASK_PLAYERSOLID ); - trap_Trace( &tr3, ps->origin, NULL, NULL, entity_origin, ent->s.number, MASK_PLAYERSOLID ); - - VectorCopy( entity_origin, origin ); - - //this item does not fit here - if( tr2.fraction < 1.0 || tr3.fraction < 1.0 ) - return IBE_NOROOM; //NO other reason is allowed to override this - - VectorCopy( tr1.plane.normal, normal ); - minNormal = BG_FindMinNormalForBuildable( buildable ); - invert = BG_FindInvertNormalForBuildable( buildable ); - - //can we build at this angle? - if( !( normal[ 2 ] >= minNormal || ( invert && normal[ 2 ] <= -minNormal ) ) ) - return IBE_NORMAL; - - if( tr1.entityNum != ENTITYNUM_WORLD ) - return IBE_NORMAL; - - //check there is enough room to spawn from (presuming this is a spawn) - if( G_CheckSpawnPoint( -1, origin, normal, buildable, NULL ) != NULL ) - return IBE_NORMAL; - - contents = trap_PointContents( entity_origin, -1 ); - - if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - //alien criteria - - if( buildable == BA_A_HOVEL ) - { - vec3_t builderMins, builderMaxs; - - //this assumes the adv builder is the biggest thing that'll use the hovel - BG_FindBBoxForClass( PCL_ALIEN_BUILDER0_UPG, builderMins, builderMaxs, NULL, NULL, NULL ); - - if( APropHovel_Blocked( angles, origin, normal, ent ) ) - reason = IBE_HOVELEXIT; - } - - //check there is creep near by for building on - if( BG_FindCreepTestForBuildable( buildable ) ) - { - if( !isCreep( entity_origin ) ) - reason = IBE_NOCREEP; - } - - //check permission to build here - if( tr1.surfaceFlags & SURF_NOALIENBUILD || tr1.surfaceFlags & SURF_NOBUILD || - contents & CONTENTS_NOALIENBUILD || contents & CONTENTS_NOBUILD ) - reason = IBE_PERMISSION; - - //look for a hivemind - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - if( tempent->s.modelindex == BA_A_OVERMIND && tempent->spawned ) - break; - } - - //if none found... - if( i >= level.num_entities && buildable != BA_A_OVERMIND ) - reason = IBE_NOOVERMIND; - - //can we only have one of these? - if( BG_FindUniqueTestForBuildable( buildable ) ) - { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == buildable ) - { - switch( buildable ) - { - case BA_A_OVERMIND: - reason = IBE_OVERMIND; - break; - - case BA_A_HOVEL: - reason = IBE_HOVEL; - break; - - default: - Com_Error( ERR_FATAL, "No reason for denying build of %d\n", buildable ); - break; - } - - break; - } - } - } - - if( level.alienBuildPoints - BG_FindBuildPointsForBuildable( buildable ) < 0 ) - reason = IBE_NOASSERT; - } - else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - //human criteria - if( !G_isPower( entity_origin ) ) - { - //tell player to build a repeater to provide power - if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER ) - reason = IBE_REPEATER; - } - - //this buildable requires a DCC - if( BG_FindDCCTestForBuildable( buildable ) && !G_isDCC( ) ) - reason = IBE_NODCC; - - //check that there is a parent reactor when building a repeater - if( buildable == BA_H_REPEATER ) - { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == BA_H_REACTOR ) - break; - } - - if( i >= level.num_entities ) - { - //no reactor present - - //check for other nearby repeaters - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == BA_H_REPEATER && - Distance( tempent->s.origin, entity_origin ) < REPEATER_BASESIZE ) - { - reason = IBE_RPTWARN2; - break; - } - } - - if( reason == IBE_NONE ) - reason = IBE_RPTWARN; - } - else if( G_isPower( entity_origin ) ) - reason = IBE_RPTWARN2; - } - - //check permission to build here - if( tr1.surfaceFlags & SURF_NOHUMANBUILD || tr1.surfaceFlags & SURF_NOBUILD || - contents & CONTENTS_NOHUMANBUILD || contents & CONTENTS_NOBUILD ) - reason = IBE_PERMISSION; - - //can we only build one of these? - if( BG_FindUniqueTestForBuildable( buildable ) ) - { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == BA_H_REACTOR ) - { - reason = IBE_REACTOR; - break; - } - } - } - - if( level.humanBuildPoints - BG_FindBuildPointsForBuildable( buildable ) < 0 ) - reason = IBE_NOPOWER; - } - - return reason; -} - - -/* -================ -G_buildItem - -Spawns a buildable -================ -*/ -gentity_t *G_buildItem( gentity_t *builder, buildable_t buildable, vec3_t origin, vec3_t angles ) -{ - gentity_t *built; - vec3_t normal; - - //spawn the buildable - built = G_Spawn(); - - built->s.eType = ET_BUILDABLE; - - built->classname = BG_FindEntityNameForBuildable( buildable ); - - built->s.modelindex = buildable; //so we can tell what this is on the client side - built->biteam = built->s.modelindex2 = BG_FindTeamForBuildable( buildable ); - - BG_FindBBoxForBuildable( buildable, built->r.mins, built->r.maxs ); - built->health = 1; - - built->splashDamage = BG_FindSplashDamageForBuildable( buildable ); - built->splashRadius = BG_FindSplashRadiusForBuildable( buildable ); - built->splashMethodOfDeath = BG_FindMODForBuildable( buildable ); - - built->nextthink = BG_FindNextThinkForBuildable( buildable ); - - built->takedamage = qtrue; - built->spawned = qfalse; - built->buildTime = built->s.time = level.time; - - //things that vary for each buildable that aren't in the dbase - switch( buildable ) - { - case BA_A_SPAWN: - built->die = ASpawn_Die; - built->think = ASpawn_Think; - built->pain = ASpawn_Pain; - break; - - case BA_A_BARRICADE: - built->die = ABarricade_Die; - built->think = ABarricade_Think; - built->pain = ABarricade_Pain; - break; - - case BA_A_BOOSTER: - built->die = ABarricade_Die; - built->think = ABarricade_Think; - built->pain = ABarricade_Pain; - built->touch = ABooster_Touch; - break; - - case BA_A_ACIDTUBE: - built->die = ABarricade_Die; - built->think = AAcidTube_Think; - built->pain = ASpawn_Pain; - break; - - case BA_A_HIVE: - built->die = ABarricade_Die; - built->think = AHive_Think; - built->pain = ASpawn_Pain; - break; - - case BA_A_TRAPPER: - built->die = ABarricade_Die; - built->think = ATrapper_Think; - built->pain = ASpawn_Pain; - break; - - case BA_A_OVERMIND: - built->die = ASpawn_Die; - built->think = AOvermind_Think; - built->pain = ASpawn_Pain; - break; - - case BA_A_HOVEL: - built->die = AHovel_Die; - built->use = AHovel_Use; - built->think = AHovel_Think; - built->pain = ASpawn_Pain; - break; - - case BA_H_SPAWN: - built->die = HSpawn_Die; - built->think = HSpawn_Think; - break; - - case BA_H_MGTURRET: - built->die = HSpawn_Die; - built->think = HMGTurret_Think; - break; - - case BA_H_TESLAGEN: - built->die = HSpawn_Die; - built->think = HTeslaGen_Think; - break; - - case BA_H_ARMOURY: - built->think = HArmoury_Think; - built->die = HSpawn_Die; - built->use = HArmoury_Activate; - break; - - case BA_H_DCC: - built->think = HDCC_Think; - built->die = HSpawn_Die; - break; - - case BA_H_MEDISTAT: - built->think = HMedistat_Think; - built->die = HSpawn_Die; - break; - - case BA_H_REACTOR: - built->think = HReactor_Think; - built->die = HSpawn_Die; - built->use = HRepeater_Use; - built->powered = built->active = qtrue; - break; - - case BA_H_REPEATER: - built->think = HRepeater_Think; - built->die = HSpawn_Die; - built->use = HRepeater_Use; - built->count = -1; - break; - - default: - //erk - break; - } - - built->s.number = built - g_entities; - built->r.contents = CONTENTS_BODY; - built->clipmask = MASK_PLAYERSOLID; - built->enemy = NULL; - built->s.weapon = BG_FindProjTypeForBuildable( buildable ); - - if( builder->client ) - built->builtBy = builder->client->ps.clientNum; - else - built->builtBy = -1; - - G_SetOrigin( built, origin ); - VectorCopy( angles, built->s.angles ); - built->s.angles[ PITCH ] = 0.0f; - built->s.angles2[ YAW ] = angles[ YAW ]; - built->s.pos.trType = BG_FindTrajectoryForBuildable( buildable ); - built->s.pos.trTime = level.time; - built->physicsBounce = BG_FindBounceForBuildable( buildable ); - built->s.groundEntityNum = -1; - - if( builder->client && builder->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) - { - if( builder->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) - VectorSet( normal, 0.0f, 0.0f, -1.0f ); - else - VectorCopy( builder->client->ps.grapplePoint, normal ); - - //gently nudge the buildable onto the surface :) - VectorScale( normal, -50.0f, built->s.pos.trDelta ); - } - else - VectorSet( normal, 0.0f, 0.0f, 1.0f ); - - built->s.generic1 = (int)( ( (float)built->health / - (float)BG_FindHealthForBuildable( buildable ) ) * B_HEALTH_SCALE ); - - if( built->s.generic1 < 0 ) - built->s.generic1 = 0; - - if( ( built->powered = findPower( built ) ) ) - built->s.generic1 |= B_POWERED_TOGGLEBIT; - - if( ( built->dcced = findDCC( built ) ) ) - built->s.generic1 |= B_DCCED_TOGGLEBIT; - - built->s.generic1 &= ~B_SPAWNED_TOGGLEBIT; - - VectorCopy( normal, built->s.origin2 ); - - G_AddEvent( built, EV_BUILD_CONSTRUCT, 0 ); - - G_setIdleBuildableAnim( built, BG_FindAnimForBuildable( buildable ) ); - - if( built->builtBy >= 0 ) - G_setBuildableAnim( built, BANIM_CONSTRUCT1, qtrue ); - - trap_LinkEntity( built ); - - return built; -} - -/* -================= -G_ValidateBuild -================= -*/ -qboolean G_ValidateBuild( gentity_t *ent, buildable_t buildable ) -{ - float dist; - vec3_t origin; - - dist = BG_FindBuildDistForClass( ent->client->ps.stats[ STAT_PCLASS ] ); - - switch( G_itemFits( ent, buildable, dist, origin ) ) - { - case IBE_NONE: - G_buildItem( ent, buildable, origin, ent->s.apos.trBase ); - return qtrue; - - case IBE_NOASSERT: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NOASSERT ); - return qfalse; - - case IBE_NOOVERMIND: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NOOVMND ); - return qfalse; - - case IBE_NOCREEP: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NOCREEP ); - return qfalse; - - case IBE_OVERMIND: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_OVERMIND ); - return qfalse; - - case IBE_HOVEL: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL ); - return qfalse; - - case IBE_HOVELEXIT: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL_EXIT ); - return qfalse; - - case IBE_NORMAL: - if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - G_TriggerMenu( ent->client->ps.clientNum, MN_H_NORMAL ); - else - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NORMAL ); - return qfalse; - - case IBE_PERMISSION: - if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - G_TriggerMenu( ent->client->ps.clientNum, MN_H_NORMAL ); - else - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NORMAL ); - return qfalse; - - case IBE_REACTOR: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_REACTOR ); - return qfalse; - - case IBE_REPEATER: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_REPEATER ); - return qfalse; - - case IBE_NOROOM: - if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOROOM ); - else - G_TriggerMenu( ent->client->ps.clientNum, MN_A_NOROOM ); - return qfalse; - - case IBE_NOPOWER: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOPOWER ); - return qfalse; - - case IBE_NODCC: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_NODCC ); - return qfalse; - - case IBE_SPWNWARN: - G_TriggerMenu( ent->client->ps.clientNum, MN_A_SPWNWARN ); - G_buildItem( ent, buildable, origin, ent->s.apos.trBase ); - return qtrue; - - case IBE_TNODEWARN: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_TNODEWARN ); - G_buildItem( ent, buildable, origin, ent->s.apos.trBase ); - return qtrue; - - case IBE_RPTWARN: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTWARN ); - G_buildItem( ent, buildable, origin, ent->s.apos.trBase ); - return qtrue; - - case IBE_RPTWARN2: - G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTWARN2 ); - return qfalse; - - default: - break; - } - - return qfalse; -} - -/* -================ -FinishSpawningBuildable - -Traces down to find where an item should rest, instead of letting them -free fall from their spawn points -================ -*/ -void FinishSpawningBuildable( gentity_t *ent ) -{ - trace_t tr; - vec3_t dest; - gentity_t *built; - buildable_t buildable = ent->s.modelindex; - - built = G_buildItem( ent, buildable, ent->s.pos.trBase, ent->s.angles ); - G_FreeEntity( ent ); - - built->takedamage = qtrue; - built->spawned = qtrue; //map entities are already spawned - built->health = BG_FindHealthForBuildable( buildable ); - built->s.generic1 |= B_SPAWNED_TOGGLEBIT; - - // drop to floor - if( buildable != BA_NONE && BG_FindTrajectoryForBuildable( buildable ) == TR_BUOYANCY ) - VectorSet( dest, built->s.origin[ 0 ], built->s.origin[ 1 ], built->s.origin[ 2 ] + 4096 ); - else - VectorSet( dest, built->s.origin[ 0 ], built->s.origin[ 1 ], built->s.origin[ 2 ] - 4096 ); - - trap_Trace( &tr, built->s.origin, built->r.mins, built->r.maxs, dest, built->s.number, built->clipmask ); - - if( tr.startsolid ) - { - G_Printf( S_COLOR_YELLOW "FinishSpawningBuildable: %s startsolid at %s\n", built->classname, vtos( built->s.origin ) ); - G_FreeEntity( built ); - return; - } - - //point items in the correct direction - VectorCopy( tr.plane.normal, built->s.origin2 ); - - // allow to ride movers - built->s.groundEntityNum = tr.entityNum; - - G_SetOrigin( built, tr.endpos ); - - trap_LinkEntity( built ); -} - -/* -============ -G_SpawnBuildable - -Sets the clipping size and plants the object on the floor. - -Items can't be immediately dropped to floor, because they might -be on an entity that hasn't spawned yet. -============ -*/ -void G_SpawnBuildable( gentity_t *ent, buildable_t buildable ) -{ - ent->s.modelindex = buildable; - - // some movers spawn on the second frame, so delay item - // spawns until the third frame so they can ride trains - ent->nextthink = level.time + FRAMETIME * 2; - ent->think = FinishSpawningBuildable; -} |