diff options
-rw-r--r-- | src/cgame/cg_servercmds.c | 6 | ||||
-rw-r--r-- | src/game/bg_public.h | 1 | ||||
-rw-r--r-- | src/game/g_buildable.c | 213 | ||||
-rw-r--r-- | src/game/g_local.h | 5 | ||||
-rw-r--r-- | src/game/g_main.c | 118 | ||||
-rw-r--r-- | src/game/tremulous.h | 3 |
6 files changed, 263 insertions, 83 deletions
diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index 2d9b7038..7fa21530 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -686,6 +686,12 @@ void CG_Menu( int menu, int arg ) type = DT_BUILD; break; + case MN_H_RPWCAUSEOVRL: + longMsg = "This repeater would cause a power zone to overlap."; + shortMsg = "This repeater would cause a power zone to overlap"; + type = DT_BUILD; + break; + case MN_H_NOSLOTS: longMsg = "You have no room to carry this. Please sell any conflicting " "upgrades before purchasing this item."; diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 079d55e4..688b45e8 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -624,6 +624,7 @@ typedef enum MN_H_NODCC, MN_H_ONEREACTOR, MN_H_RPTPOWERHERE, + MN_H_RPWCAUSEOVRL, } dynMenu_t; // animations diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 40654f00..47ea4eed 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -144,10 +144,10 @@ G_FindPower attempt to find power for self, return qtrue if successful ================ */ -static qboolean G_FindPower( gentity_t *self ) +static qboolean G_FindPower( gentity_t *self, qboolean building ) { - int i; - gentity_t *ent; + int i, j; + gentity_t *ent, *ent2; gentity_t *closestPower = NULL; int distance = 0; int minDistance = REPEATER_BASESIZE + 1; @@ -156,24 +156,24 @@ static qboolean G_FindPower( gentity_t *self ) if( self->buildableTeam != TEAM_HUMANS ) return qfalse; - //reactor is always powered + // Reactor is always powered if( self->s.modelindex == BA_H_REACTOR ) return qtrue; - //if this already has power then stop now + // If this already has power then stop now if( self->parentNode && self->parentNode->powered ) return qtrue; - //reset parent + // Reset parent self->parentNode = NULL; - //iterate through entities + // Iterate through entities for( i = MAX_CLIENTS, 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 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 && ent->powered && ent->health > 0 ) { @@ -188,13 +188,49 @@ static qboolean G_FindPower( gentity_t *self ) } else if( distance < minDistance ) { - closestPower = ent; - minDistance = distance; + // If it's a repeater, check that enough BP will be available to power + // another buildable + // but only if self is a real buildable + + if( self->parentNode ) + { + int buildPoints = g_humanRepeaterBuildPoints.integer; + + // Scan the buildables in the repeater zone + for( j = MAX_CLIENTS, ent2 = g_entities + j; j < level.num_entities; j++, ent2++ ) + { + gentity_t *powerEntity; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + + powerEntity = ent2->parentNode; // FIXME: this assumes that parentNode is always the power source, if it exists + + if( powerEntity && powerEntity->s.modelindex == BA_H_REPEATER && powerEntity == self->parentNode ) + { + buildPoints -= BG_Buildable( ent2->s.modelindex )->buildPoints; + } + } + + if( building ) + buildPoints -= BG_Buildable( self->s.modelindex )->buildPoints; + + if( buildPoints >= 0 ) + { + closestPower = ent; + minDistance = distance; + } + } + else + { + closestPower = ent; + minDistance = distance; + } } } } - //if there were no power items nearby give up + // If there were no power items nearby give up if( closestPower ) { self->parentNode = closestPower; @@ -221,7 +257,7 @@ gentity_t *G_PowerEntityForPoint( vec3_t origin ) dummy.s.modelindex = BA_NONE; VectorCopy( origin, dummy.s.origin ); - if( G_FindPower( &dummy ) ) + if( G_FindPower( &dummy, qfalse ) ) return dummy.parentNode; else return NULL; @@ -229,6 +265,21 @@ gentity_t *G_PowerEntityForPoint( vec3_t origin ) /* ================ +G_PowerEntityForEntity + +Simple wrapper to G_FindPower to find the entity providing +power for the specified entity +================ +*/ +gentity_t *G_PowerEntityForEntity( gentity_t *ent ) +{ + if( G_FindPower( ent, qfalse ) ) + return ent->parentNode; + return NULL; +} + +/* +================ G_IsPowered Check if a location has power, returning the entity type @@ -252,7 +303,7 @@ G_RepeaterWouldOverlap Check if a repeater would create an overlapping power zone ================ */ -static qboolean G_RepeaterWouldOverlap( vec3_t origin ) +static gentity_t *G_RepeaterWouldOverlap( vec3_t origin ) { int i; gentity_t *ent; @@ -275,17 +326,17 @@ static qboolean G_RepeaterWouldOverlap( vec3_t origin ) if( ent->s.modelindex == BA_H_REACTOR ) { if( distance <= REACTOR_BASESIZE + REPEATER_BASESIZE ) - return qtrue; + return ent; } else if( ent->s.modelindex == BA_H_REPEATER ) { if( distance <= REPEATER_BASESIZE + REPEATER_BASESIZE ) - return qtrue; + return ent; } } } - return qfalse; + return NULL; } /* @@ -334,10 +385,59 @@ int G_GetBuildPoints( const vec3_t pos, team_t team, int dist ) buildPoints = level.alienBuildPoints; } + if( buildPoints < 0 ) + buildPoints = 0; + return buildPoints; } /* +================== +G_InPowerZone + +See if a buildable is inside of another power zone +(This doesn't check if power zones overlap) +================== +*/ +qboolean G_InPowerZone( gentity_t *self ) +{ + int i; + gentity_t *ent; + int distance; + vec3_t temp_v; + + for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( ent == self ) + continue; + + if( !ent->spawned ) + continue; + + if( ent->health <= 0 ) + 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 && ent->powered ) + { + VectorSubtract( self->s.origin, ent->s.origin, temp_v ); + distance = VectorLength( temp_v ); + + if( ent->s.modelindex == BA_H_REACTOR && distance <= REACTOR_BASESIZE ) + return qtrue; + else if( ent->s.modelindex == BA_H_REPEATER && distance <= REPEATER_BASESIZE ) + return qtrue; + } + } + + return qfalse; +} + +/* ================ G_FindDCC @@ -380,18 +480,32 @@ int G_FindDCC( gentity_t *self ) ================ G_IsDCCBuilt -simple wrapper to G_FindDCC to check for a dcc +See if any powered DCC exists ================ */ qboolean G_IsDCCBuilt( void ) { - gentity_t dummy; + int i; + gentity_t *ent; - memset( &dummy, 0, sizeof( gentity_t ) ); + for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; - dummy.buildableTeam = TEAM_HUMANS; + if( ent->s.modelindex != BA_H_DCC ) + continue; + + if( !ent->spawned ) + continue; + + if( ent->health <= 0 ) + continue; - return G_FindDCC( &dummy ); + return qtrue; + } + + return qfalse; } /* @@ -500,7 +614,7 @@ G_IsCreepHere simple wrapper to G_FindCreep to check if a location has creep ================ */ -static qboolean G_IsCreepHere( vec3_t origin ) +qboolean G_IsCreepHere( vec3_t origin ) { gentity_t dummy; @@ -1725,7 +1839,15 @@ void HRepeater_Think( gentity_t *self ) G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); } else + { self->count = -1; + } + + if( G_InPowerZone( self ) ) + { + // if the repeater is inside of another power zone then disappear + G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); + } self->powered = reactor; @@ -1884,7 +2006,7 @@ void HArmoury_Think( gentity_t *self ) //make sure we have power self->nextthink = level.time + POWER_REFRESH_TIME; - self->powered = G_FindPower( self ); + self->powered = G_FindPower( self, qfalse ); } @@ -1908,7 +2030,7 @@ void HDCC_Think( gentity_t *self ) //make sure we have power self->nextthink = level.time + POWER_REFRESH_TIME; - self->powered = G_FindPower( self ); + self->powered = G_FindPower( self, qfalse ); } @@ -1958,7 +2080,7 @@ void HMedistat_Think( gentity_t *self ) self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_ACTIVE; //make sure we have power - if( !( self->powered = G_FindPower( self ) ) ) + if( !( self->powered = G_FindPower( self, qfalse ) ) ) { if( self->active ) { @@ -2222,7 +2344,7 @@ void HMGTurret_Think( gentity_t *self ) self->s.eFlags &= ~EF_FIRING; // If not powered or spawned don't do anything - if( !( self->powered = G_FindPower( self ) ) ) + if( !( self->powered = G_FindPower( self, qfalse ) ) ) { self->nextthink = level.time + POWER_REFRESH_TIME; return; @@ -2292,7 +2414,7 @@ void HTeslaGen_Think( gentity_t *self ) self->nextthink = level.time + BG_Buildable( self->s.modelindex )->nextthink; //if not powered don't do anything and check again for power next think - if( !( self->powered = G_FindPower( self ) ) ) + if( !( self->powered = G_FindPower( self, qfalse ) ) ) { self->s.eFlags &= ~EF_FIRING; self->nextthink = level.time + POWER_REFRESH_TIME; @@ -2514,12 +2636,12 @@ void G_QueueBuildPoints( gentity_t *self ) break; case TEAM_HUMANS: - powerEntity = G_PowerEntityForPoint( self->s.origin ); + powerEntity = G_PowerEntityForEntity( self ); if( powerEntity ) - { + { switch( powerEntity->s.modelindex ) - { + { case BA_H_REACTOR: if( !level.humanBuildPointQueue ) level.humanNextQueueTime = level.time + g_humanBuildQueueTime.integer; @@ -3011,7 +3133,7 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, repeaterInRange = qfalse; // Don't allow marked buildables to be replaced in another zone, - // unless the marked buildable is unpowered + // unless the marked buildable isn't in a zone (and thus unpowered) if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER && G_PowerEntityForPoint( ent->s.origin ) != G_PowerEntityForPoint( origin ) && G_PowerEntityForPoint( ent->s.origin ) ) @@ -3080,7 +3202,7 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, return IBE_NOROOM; // There are one or more repeaters we can't remove - if( repeaterInRangeCount > 0 ) + if( repeaterInRangeCount > 0 && buildable != BA_H_REPEATER ) return IBE_RPTPOWERHERE; // Sort the list @@ -3257,11 +3379,24 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance tempent = G_FindBuildable( BA_H_REACTOR ); if( tempent == NULL ) // No reactor - reason = IBE_RPTNOREAC; - else if( G_RepeaterWouldOverlap( entity_origin ) ) - { - // The repeater would cause power zones to overlap + reason = IBE_RPTNOREAC; + else if( g_markDeconstruct.integer && G_IsPowered( entity_origin ) == BA_H_REACTOR ) + reason = IBE_RPTPOWERHERE; + else if( !g_markDeconstruct.integer && G_IsPowered( entity_origin ) ) reason = IBE_RPTPOWERHERE; + else if( !g_humanRepeaterAllowOverlap.integer ) + { + tempent = G_RepeaterWouldOverlap( entity_origin ); + + if( tempent ) + { + // If mark deconstruct is enabled, allow the marked repeater to be moved and replaced + if( !( g_markDeconstruct.integer > 1 && + tempent->s.modelindex == BA_H_REPEATER && tempent->deconstruct ) ) + { + reason = IBE_RPWCAUSEOVRL; + } + } } } @@ -3539,7 +3674,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori built->powered = qtrue; built->s.eFlags |= EF_B_POWERED; } - else if( ( built->powered = G_FindPower( built ) ) ) + else if( ( built->powered = G_FindPower( built, qtrue ) ) ) built->s.eFlags |= EF_B_POWERED; built->s.eFlags &= ~EF_B_SPAWNED; @@ -3632,6 +3767,10 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable ) G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTPOWERHERE ); return qfalse; + case IBE_RPWCAUSEOVRL: + G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPWCAUSEOVRL ); + return qfalse; + case IBE_LASTSPAWN: G_TriggerMenu( ent->client->ps.clientNum, MN_B_LASTSPAWN ); return qfalse; diff --git a/src/game/g_local.h b/src/game/g_local.h index 9b44d4c4..2dbdf33f 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -730,6 +730,7 @@ typedef enum IBE_TNODEWARN, // not currently used IBE_RPTNOREAC, IBE_RPTPOWERHERE, + IBE_RPWCAUSEOVRL, IBE_NOHUMANBP, IBE_NODCC, @@ -767,6 +768,9 @@ void G_BaseSelfDestruct( team_t team ); void G_QueueBuildPoints( gentity_t *self ); int G_GetBuildPoints( const vec3_t pos, team_t team, int dist ); gentity_t *G_PowerEntityForPoint( vec3_t origin ); +gentity_t *G_PowerEntityForEntity( gentity_t *ent ); +gentity_t *G_RepeaterEntityForPoint( vec3_t origin ); +qboolean G_InPowerZone( gentity_t *self ); // // g_utils.c @@ -1083,6 +1087,7 @@ extern vmCvar_t g_humanBuildQueueTime; extern vmCvar_t g_humanRepeaterBuildPoints; extern vmCvar_t g_humanRepeaterBuildQueueTime; extern vmCvar_t g_humanRepeaterMaxZones; +extern vmCvar_t g_humanRepeaterAllowOverlap; extern vmCvar_t g_humanStage; extern vmCvar_t g_humanCredits; extern vmCvar_t g_humanMaxStage; diff --git a/src/game/g_main.c b/src/game/g_main.c index be2ce83d..e797dab2 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -93,6 +93,7 @@ vmCvar_t g_humanBuildPoints; vmCvar_t g_humanBuildQueueTime; vmCvar_t g_humanRepeaterBuildPoints; vmCvar_t g_humanRepeaterBuildQueueTime; +vmCvar_t g_humanRepeaterAllowOverlap; vmCvar_t g_humanRepeaterMaxZones; vmCvar_t g_humanStage; vmCvar_t g_humanCredits; @@ -232,6 +233,7 @@ static cvarTable_t gameCvarTable[ ] = { &g_humanRepeaterBuildPoints, "g_humanRepeaterBuildPoints", DEFAULT_HUMAN_REPEATER_BUILDPOINTS, 0, 0, qfalse }, { &g_humanRepeaterMaxZones, "g_humanRepeaterMaxZones", DEFAULT_HUMAN_REPEATER_MAX_ZONES, 0, 0, qfalse }, { &g_humanRepeaterBuildQueueTime, "g_humanRepeaterBuildQueueTime", DEFAULT_HUMAN_REPEATER_QUEUE_TIME, 0, 0, qfalse }, + { &g_humanRepeaterAllowOverlap, "g_humanRepeaterAllowOverlap", DEFAULT_HUMAN_REPEATER_ALLOW_OVERLAP, 0, 0, qfalse }, { &g_humanStage, "g_humanStage", "0", 0, 0, qfalse }, { &g_humanCredits, "g_humanCredits", "0", 0, 0, qfalse }, { &g_humanMaxStage, "g_humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, 0, 0, qfalse }, @@ -1229,55 +1231,39 @@ void G_CalculateBuildPoints( void ) if( BG_Buildable( buildable )->team == TEAM_HUMANS ) { - gentity_t *powerEntity = G_PowerEntityForPoint( ent->s.origin ); - - if( powerEntity ) - { - switch( powerEntity->s.modelindex ) - { - case BA_H_REACTOR: - level.humanBuildPoints -= BG_Buildable( buildable )->buildPoints; - break; - - case BA_H_REPEATER: - if( powerEntity->usesZone && level.powerZones[powerEntity->zone].active ) - { - zone_t *zone = &level.powerZones[powerEntity->zone]; - - zone->totalBuildPoints -= BG_Buildable( buildable )->buildPoints; - powerEntity->s.misc = zone->totalBuildPoints - zone->queuedBuildPoints; - } - break; - - default: - break; - } - } - else + if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER ) { - // Unpowered buildables count as BP for the main reactor zone - level.humanBuildPoints -= BG_Buildable( buildable )->buildPoints; - } + gentity_t *powerEntity = G_PowerEntityForEntity( ent ); - if( buildable == BA_H_REPEATER ) - { - if( ent->usesZone && level.powerZones[ ent->zone ].active ) + if( powerEntity ) { - zone = &level.powerZones[ ent->zone ]; - - if( !level.suddenDeath ) - { - // BP queue updates - while( zone->queuedBuildPoints > 0 && - zone->nextQueueTime < level.time ) - { - zone->queuedBuildPoints--; - zone->nextQueueTime += (int)g_humanRepeaterBuildQueueTime.integer * (float)( 1 - ( (float)zone->queuedBuildPoints ) / zone->totalBuildPoints ); - } - } - else + switch( powerEntity->s.modelindex ) { - zone->totalBuildPoints = zone->queuedBuildPoints = 0; + case BA_H_REACTOR: + level.humanBuildPoints -= BG_Buildable( buildable )->buildPoints; + break; + + case BA_H_REPEATER: + if( powerEntity->usesZone && level.powerZones[ powerEntity->zone ].active ) + { + zone_t *zone = &level.powerZones[ powerEntity->zone ]; + // Only power as much as a repeater can power + if( zone->totalBuildPoints < 0 || zone->queuedBuildPoints > zone->totalBuildPoints ) + { + // Don't unpower spawns + if( buildable != BA_H_SPAWN ) + ent->powered = qfalse; + } + else + { + zone->totalBuildPoints -= BG_Buildable( buildable )->buildPoints; + } + } + + break; + + default: + break; } } } @@ -1289,6 +1275,48 @@ void G_CalculateBuildPoints( void ) } } + // Finally, update repeater zones and their queues + // note that this has to be done after the used BP is calculated + for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ ) + { + if( !ent->inuse ) + continue; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( ent->s.eFlags & EF_DEAD ) + continue; + + buildable = ent->s.modelindex; + + if( BG_Buildable( buildable )->team == TEAM_HUMANS ) + { + if( buildable == BA_H_REPEATER ) + { + if( ent->usesZone && level.powerZones[ ent->zone ].active ) + { + zone = &level.powerZones[ ent->zone ]; + + if( !level.suddenDeath ) + { + // BP queue updates + while( zone->queuedBuildPoints > 0 && + zone->nextQueueTime < level.time ) + { + zone->queuedBuildPoints--; + zone->nextQueueTime += abs( (int)g_humanRepeaterBuildQueueTime.integer * (float)( 1 - ( (float)zone->queuedBuildPoints ) / zone->totalBuildPoints ) ); // it is possible for queued BP to be great than total BP, in which case, treat it as if the leftover BP is positive + } + } + else + { + zone->totalBuildPoints = zone->queuedBuildPoints = 0; + } + } + } + } + } + if( level.humanBuildPoints < 0 ) level.humanBuildPoints = 0; diff --git a/src/game/tremulous.h b/src/game/tremulous.h index b71db8ae..776f9835 100644 --- a/src/game/tremulous.h +++ b/src/game/tremulous.h @@ -586,7 +586,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DC_SPLASHRADIUS 100 #define DC_ATTACK_PERIOD 10000 // how often to spam "under attack" #define DC_HEALRATE 3 -#define DC_RANGE 10000 +#define DC_RANGE 1000 #define DC_VALUE HBVM(DC_BP) #define ARMOURY_BP 10 @@ -669,6 +669,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DEFAULT_HUMAN_REPEATER_BUILDPOINTS "20" #define DEFAULT_HUMAN_REPEATER_QUEUE_TIME "7000" #define DEFAULT_HUMAN_REPEATER_MAX_ZONES "3" +#define DEFAULT_HUMAN_REPEATER_ALLOW_OVERLAP "1" #define DEFAULT_HUMAN_STAGE2_THRESH "4000" #define DEFAULT_HUMAN_STAGE3_THRESH "8000" #define DEFAULT_HUMAN_MAX_STAGE "2" |