summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cgame/cg_servercmds.c6
-rw-r--r--src/game/bg_public.h1
-rw-r--r--src/game/g_buildable.c213
-rw-r--r--src/game/g_local.h5
-rw-r--r--src/game/g_main.c118
-rw-r--r--src/game/tremulous.h3
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"