summaryrefslogtreecommitdiff
path: root/src/game/g_buildable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/g_buildable.c')
-rw-r--r--src/game/g_buildable.c1038
1 files changed, 853 insertions, 185 deletions
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index 3290ccc..5a2698b 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -513,6 +513,54 @@ static void freeBuildable( gentity_t *self )
G_FreeEntity( self );
}
+/*
+================
+G_RecoverBuildPoints
+
+Schedule build points for delayed recovery when a buildable dies
+ credit to Mercenaries Guild dev team (mgdev) for the whole idea
+================
+*/
+void G_RecoverBuildPoints( gentity_t *self )
+{
+ int team;
+ int value;
+
+ if( g_buildPointsRecoverRate.integer < 1 )
+ return;
+
+ if( self->killedBy != ENTITYNUM_NONE )
+ return;
+
+ team = BG_FindTeamForBuildable( self->s.modelindex );
+ value = BG_FindBuildPointsForBuildable( self->s.modelindex );
+ if( team == BIT_ALIENS )
+ {
+ if( !level.alienRecoverBuildPoints )
+ level.alienRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer;
+ level.alienRecoverBuildPoints += value;
+ }
+ else if( team == BIT_HUMANS )
+ {
+ if( !level.humanRecoverBuildPoints )
+ level.humanRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer;
+ level.humanRecoverBuildPoints += value;
+ }
+
+ self->killedBy = ENTITYNUM_NONE;
+}
+
+static void G_RecoverSetKiller( gentity_t *self, gentity_t *attacker )
+{
+ if( g_buildPointsRecoverRate.integer < 1 )
+ return;
+
+ // note attacker if teamkill
+ if( attacker && attacker->client &&
+ attacker->client->ps.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( self->s.modelindex ) )
+ self->killedBy = attacker - g_entities;
+}
+
//==================================================================================
@@ -531,6 +579,7 @@ void A_CreepRecede( gentity_t *self )
if( !( self->s.eFlags & EF_DEAD ) )
{
self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
G_AddEvent( self, EV_BUILD_DESTROY, 0 );
if( self->spawned )
@@ -573,6 +622,7 @@ void ASpawn_Melt( gentity_t *self )
if( !( self->s.eFlags & EF_DEAD ) )
{
self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
G_AddEvent( self, EV_BUILD_DESTROY, 0 );
if( self->spawned )
@@ -631,6 +681,7 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -658,6 +709,8 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
@@ -796,6 +849,18 @@ void AOvermind_Think( gentity_t *self )
vec3_t mins, maxs;
int i, num;
gentity_t *enemy;
+ float om_range = OVERMIND_ATTACK_RANGE;
+ float om_damage = self->splashDamage;
+ float om_splash = self->splashRadius;
+
+ if( g_modMainStrength.integer > 0 )
+ {
+ om_range = om_range * g_modMainStrength.value / 100;
+ range[0] = range[1] = range[2] = om_range;
+
+ om_damage = om_damage * g_modMainStrength.value / 100;
+ om_splash = om_splash * g_modMainStrength.value / 100;
+ }
VectorAdd( self->s.origin, range, maxs );
VectorSubtract( self->s.origin, range, mins );
@@ -814,8 +879,8 @@ void AOvermind_Think( gentity_t *self )
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_SelectiveRadiusDamage( self->s.pos.trBase, self, om_damage,
+ om_splash, self, MOD_OVERMIND, PTE_ALIENS );
G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
}
}
@@ -940,6 +1005,7 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -966,6 +1032,8 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
else
self->nextthink = level.time; //blast immediately
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -1031,13 +1099,18 @@ void AAcidTube_Damage( gentity_t *self )
{
if( self->spawned )
{
+ int repeatRate = ACIDTUBE_REPEAT;
+
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 )
+ if( g_modBuildableSpeed.integer > 0 )
+ repeatRate = repeatRate * 100 / g_modBuildableSpeed.integer;
+
+ if( ( self->timestamp + repeatRate ) > level.time )
self->think = AAcidTube_Damage;
else
{
@@ -1096,7 +1169,8 @@ void AAcidTube_Think( gentity_t *self )
if( !G_Visible( self, enemy ) )
continue;
- if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( enemy->client &&
+ ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS || enemy->client->pers.bleeder ) )
{
if( level.paused || enemy->client->pers.paused )
continue;
@@ -1389,6 +1463,7 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -1447,6 +1522,8 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -1502,6 +1579,18 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
if( client && client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
return;
+ if( client->pers.bleeder )
+ {
+ if( !(client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) )
+ {
+ client->ps.stats[ STAT_STATE ] |= SS_POISONCLOUDED;
+ client->lastPoisonCloudedTime = level.time;
+ trap_SendServerCommand( client->ps.clientNum, "poisoncloud" );
+ trap_SendServerCommand( client->ps.clientNum, "print \"Your booster has poisoned you\n\"" );
+ }
+ return;
+ }
+
//only allow boostage once every 30 seconds
if( client->lastBoostedTime + BOOSTER_INTERVAL > level.time )
return;
@@ -1772,6 +1861,15 @@ void HReactor_Think( gentity_t *self )
vec3_t mins, maxs;
int i, num;
gentity_t *enemy, *tent;
+ float rc_range = REACTOR_ATTACK_RANGE;
+ float rc_damage = REACTOR_ATTACK_DAMAGE;
+
+ if( g_modMainStrength.integer > 0 )
+ {
+ rc_range = rc_range * g_modMainStrength.value / 100;
+ rc_damage = rc_damage * g_modMainStrength.value / 100;
+ range[0] = range[1] = range[2] = rc_range;
+ }
VectorAdd( self->s.origin, range, maxs );
VectorSubtract( self->s.origin, range, mins );
@@ -1792,8 +1890,8 @@ void HReactor_Think( gentity_t *self )
if( level.paused || enemy->client->pers.paused )
continue;
self->timestamp = level.time;
- G_SelectiveRadiusDamage( self->s.pos.trBase, self, REACTOR_ATTACK_DAMAGE,
- REACTOR_ATTACK_RANGE, self, MOD_REACTOR, PTE_HUMANS );
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self, rc_damage,
+ rc_range, self, MOD_REACTOR, PTE_HUMANS );
tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL );
@@ -1943,6 +2041,7 @@ void HMedistat_Think( gentity_t *self )
{
if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
player->client->ps.pm_type != PM_DEAD &&
+ !player->client->pers.bleeder &&
self->enemy == player )
occupied = qtrue;
}
@@ -1960,7 +2059,8 @@ void HMedistat_Think( gentity_t *self )
if( player->flags & FL_NOTARGET )
continue; // notarget cancels even beneficial effects?
- if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
+ !player->client->pers.bleeder )
{
if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
player->client->ps.pm_type != PM_DEAD )
@@ -1980,6 +2080,34 @@ void HMedistat_Think( gentity_t *self )
}
}
+ // bleeding spree retribution
+ if( level.bleeders && !self->enemy )
+ {
+ //look for something to hurt
+ for( i = 0; i < num; i++ )
+ {
+ player = &g_entities[ entityList[ i ] ];
+
+ if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && player->client->pers.bleeder )
+ {
+ if( player->health > 0 &&
+ 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;
+ }
+ }
+ if( BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) )
+ BG_RemoveUpgradeFromInventory( UP_MEDKIT, player->client->ps.stats );
+ }
+ }
+ }
+
//nothing left to heal so go back to idling
if( !self->enemy && self->active )
{
@@ -1990,6 +2118,12 @@ void HMedistat_Think( gentity_t *self )
}
else if( self->enemy ) //heal!
{
+ if( self->enemy->client->pers.bleeder )
+ {
+ G_Damage( self->enemy, NULL, NULL, NULL, NULL, 10, 0, MOD_SLIME );
+ return;
+ }
+
if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED )
self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
@@ -2147,7 +2281,8 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ign
if( !traceEnt->client )
return qfalse;
- if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
+ if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS &&
+ !traceEnt->client->pers.bleeder )
return qfalse;
return qtrue;
@@ -2211,6 +2346,24 @@ void HMGTurret_FindEnemy( gentity_t *self )
}
}
+ // bleeder retribution
+ if( level.bleeders )
+ {
+ for( i = 0; i < num; i++ )
+ {
+ target = &g_entities[ entityList[ i ] ];
+
+ if( target->client && target->client->pers.bleeder )
+ {
+ if( !HMGTurret_CheckTarget( self, target, qfalse ) )
+ continue;
+
+ self->enemy = target;
+ return;
+ }
+ }
+ }
+
//couldn't find a target
self->enemy = NULL;
}
@@ -2239,7 +2392,7 @@ void HMGTurret_Think( gentity_t *self )
//if not powered don't do anything and check again for power next think
if( !( self->powered = G_FindPower( self ) ) )
{
- if( self->spawned )
+ if( g_turretAim.integer && self->spawned )
{
// unpowered turret barrel falls to bottom of range
float droop;
@@ -2254,7 +2407,7 @@ void HMGTurret_Think( gentity_t *self )
return;
}
}
-
+
self->nextthink = level.time + POWER_REFRESH_TIME;
return;
}
@@ -2275,9 +2428,24 @@ void HMGTurret_Think( gentity_t *self )
//if a new target cannot be found don't do anything
if( !self->enemy )
+ {
+ if( g_turretAim.integer &&
+ self->rotatorAngle >= 360.0f &&
+ level.time > self->last_move_time + TURRET_REST_TIME )
+ {
+ float diff;
+
+ diff = AngleSubtract( self->s.angles2[ YAW ], self->rotatorAngle - 360.0f );
+ if( diff < -TURRET_REST_TOLERANCE )
+ self->s.angles2[ YAW ] += TURRET_REST_SPEED;
+ else if ( diff > TURRET_REST_TOLERANCE )
+ self->s.angles2[ YAW ] -= TURRET_REST_SPEED;
+ }
return;
+ }
self->enemy->targeted = self;
+ self->last_move_time = level.time;
//if we are pointing at our target and we can fire shoot it
if( HMGTurret_TrackEnemy( self ) && ( self->count < level.time ) )
@@ -2346,7 +2514,8 @@ void HTeslaGen_Think( gentity_t *self )
if( enemy->flags & FL_NOTARGET )
continue;
- if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
+ if( enemy->client &&
+ ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || enemy->client->pers.bleeder ) &&
enemy->health > 0 &&
!level.paused && !enemy->client->pers.paused &&
Distance( enemy->s.pos.trBase, self->s.pos.trBase ) <= TESLAGEN_RANGE )
@@ -2404,6 +2573,9 @@ void HSpawn_Disappear( gentity_t *self )
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+
+ self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
}
@@ -2436,6 +2608,9 @@ void HSpawn_Blast( gentity_t *self )
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+
+ self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
}
@@ -2451,6 +2626,7 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -2485,6 +2661,8 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->nextthink = level.time; //blast immediately
}
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -2769,6 +2947,50 @@ qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable )
return qfalse;
}
+/*
+===============
+G_BuildableRangeClosest
+
+Find Closest distance to a type of buildable, -1
+===============
+*/
+float G_BuildableRangeClosest( 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;
+ float found = -1.0;
+
+ 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 )
+ {
+ float dist;
+
+ dist = Distance( origin, ent->s.origin );
+ if( found < 0.0 || dist < found )
+ found = dist;
+ }
+ }
+
+ return found;
+}
+
static qboolean G_BoundsIntersect(const vec3_t mins, const vec3_t maxs,
const vec3_t mins2, const vec3_t maxs2)
{
@@ -2851,7 +3073,6 @@ static int G_CompareBuildablesForRemoval( const void *a, const void *b )
buildableA = *(gentity_t **)a;
buildableB = *(gentity_t **)b;
-
// Prefer the one that collides with the thing we're building
aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin,
buildableA->s.modelindex, buildableA->s.origin );
@@ -2909,6 +3130,7 @@ void G_FreeMarkedBuildables( void )
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = -1;
+ new->time = 0;
new->ent = NULL;
Q_strncpyz( new->name, "<markdecon>", 12 );
new->buildable = ent->s.modelindex;
@@ -3035,12 +3257,7 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable,
// Don't allow destruction of hovel with granger inside
if( ent->s.modelindex == BA_A_HOVEL && ent->active )
- {
- if( buildable == BA_A_HOVEL )
- return IBE_HOVEL;
- else
- continue;
- }
+ continue;
// Explicitly disallow replacement of the core buildable with anything
// other than the core buildable
@@ -3163,18 +3380,17 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
{
vec3_t angles;
vec3_t entity_origin, normal;
- vec3_t mins, maxs, nbmins, nbmaxs;
- vec3_t nbVect;
+ vec3_t mins, maxs;
trace_t tr1, tr2, tr3;
int i;
itemBuildError_t reason = IBE_NONE;
+ itemBuildError_t old_reason = IBE_NONE;
gentity_t *tempent;
float minNormal;
qboolean invert;
int contents;
playerState_t *ps = &ent->client->ps;
int buildPoints;
- gentity_t *tmp;
itemBuildError_t tempReason;
// Stop all buildables from interacting with traces
@@ -3200,28 +3416,13 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( tr1.entityNum != ENTITYNUM_WORLD )
reason = IBE_NORMAL;
+ // check nobuild zones
+ if( nobuild_check( entity_origin ) >= 0 )
+ reason = IBE_PERMISSION;
+
contents = trap_PointContents( entity_origin, -1 );
buildPoints = BG_FindBuildPointsForBuildable( buildable );
-
- //check if we are near a nobuild marker, if so, can't build here...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- tmp = &g_entities[ i ];
-
- if( !tmp->noBuild.isNB )
- continue;
-
- nbVect[0] = tmp->noBuild.Area;
- nbVect[1] = tmp->noBuild.Area;
- nbVect[2] = tmp->noBuild.Height;
-
- VectorSubtract( origin, nbVect, nbmins );
- VectorAdd( origin, nbVect, nbmaxs );
-
- if( trap_EntityContact( nbmins, nbmaxs, tmp ) )
- reason = IBE_PERMISSION;
-
- }
+
if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
{
//alien criteria
@@ -3276,6 +3477,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
switch( buildable )
{
case BA_A_OVERMIND:
+ old_reason = reason;
reason = IBE_OVERMIND;
break;
@@ -3359,6 +3561,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( tempent->s.modelindex == BA_H_REACTOR && !tempent->deconstruct )
{
+ old_reason = reason;
reason = IBE_REACTOR;
break;
}
@@ -3369,6 +3572,38 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( ( tempReason = G_SufficientBPAvailable( buildable, origin ) ) != IBE_NONE )
reason = tempReason;
+ if( g_modBuildableCount.integer > 1 && old_reason == IBE_NONE )
+ {
+ if( reason == IBE_REACTOR )
+ {
+ int rcs = 0;
+
+ for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ )
+ {
+ if( tempent->s.eType == ET_BUILDABLE &&
+ tempent->s.modelindex == BA_H_REACTOR &&
+ !tempent->deconstruct )
+ rcs++;
+ }
+ if( rcs < g_modBuildableCount.integer )
+ reason = IBE_NONE;
+ }
+ else if( reason == IBE_OVERMIND )
+ {
+ int oms = 0;
+
+ for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ )
+ {
+ if( tempent->s.eType == ET_BUILDABLE &&
+ tempent->s.modelindex == BA_A_OVERMIND &&
+ !tempent->deconstruct )
+ oms++;
+ }
+ if( oms < g_modBuildableCount.integer )
+ reason = IBE_NONE;
+ }
+ }
+
// Relink buildables
G_SetBuildableLinkState( qtrue );
@@ -3498,8 +3733,10 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
built->buildTime = built->s.time = level.time;
built->spawnBlockTime = 0;
+ built->killedBy = ENTITYNUM_NONE;
+
// build instantly in cheat mode
- if( builder->client && g_cheats.integer )
+ if( builder->client && g_cheats.integer || g_instantBuild.integer )
{
built->health = BG_FindHealthForBuildable( buildable );
built->buildTime = built->s.time =
@@ -3704,6 +3941,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
{
new = level.buildHistory;
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = builder;
new->name[ 0 ] = 0;
new->buildable = buildable;
@@ -3722,39 +3960,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
return built;
}
-static void G_SpawnMarker( vec3_t origin )
-{
- gentity_t *nb;
- int i;
-
- // Make the marker...
- nb = G_Spawn( );
- nb->s.modelindex = 0; //Coder humor is win
- VectorCopy( origin, nb->s.pos.trBase );
- VectorCopy( origin, nb->r.currentOrigin );
- nb->noBuild.isNB = qtrue;
- nb->noBuild.Area = level.nbArea;
- nb->noBuild.Height = level.nbHeight;
- trap_LinkEntity( nb );
-
- // Log markers made...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker != NULL )
- continue;
-
- level.nbMarkers[ i ].Marker = nb;
- VectorCopy( origin, level.nbMarkers[ i ].Origin );
- SnapVector( level.nbMarkers[ i ].Origin );
- break;
- }
-
- // End nobuild mode...
- level.noBuilding = qfalse;
- level.nbArea = 0.0f;
- level.nbHeight = 0.0f;
-}
-
/*
=================
G_BuildIfValid
@@ -3770,15 +3975,6 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )
switch( G_CanBuild( ent, buildable, dist, origin ) )
{
case IBE_NONE:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
-
- G_SpawnMarker( origin );
- return qtrue;
- }
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
@@ -3844,40 +4040,16 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )
return qfalse;
case IBE_SPWNWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_A_SPWNWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
case IBE_TNODEWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_H_TNODEWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
case IBE_RPTWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
@@ -4583,128 +4755,624 @@ void G_BaseSelfDestruct( pTeam_t team )
return "<buildlog entry expired>";
}
+
/*
-============
-G_NobuildLoad
+ * =======
+ * nobuild
+ * =======
+ */
-load the nobuild markers that were previously saved (if there are any).
-============
-*/
-void G_NobuildLoad( void )
+#define MAX_NOBUILD_ZONES 16
+#define NOBUILD_THINK_TIME 200
+
+typedef struct
+{
+ qboolean in_use;
+ vec3_t origin;
+ vec3_t size;
+}
+nobuild_t;
+
+typedef struct
+{
+ vec3_t dir;
+ float yaw;
+}
+nobuild_corner_t;
+
+static nobuild_corner_t nobuildCorner[] = {
+ { { -1, -1, -1 }, 0 },
+ { { -1, 1, -1 }, 270 },
+ { { 1, -1, -1 }, 90 },
+ { { 1, 1, -1 }, 180 },
+
+ { { -1, -1, 1 }, 270 },
+ { { -1, 1, 1 }, 180 },
+ { { 1, -1, 1 }, 0 },
+ { { 1, 1, 1 }, 90 }
+};
+#define MAX_NOBUILD_CORNERS ( sizeof( nobuildCorner ) / sizeof( nobuild_corner_t ) )
+
+typedef enum
+{
+ NB_ORIGIN_X,
+ NB_ORIGIN_Y,
+ NB_ORIGIN_Z,
+ NB_SIZE_X,
+ NB_SIZE_Y,
+ NB_SIZE_Z
+}
+nobuild_sizer_t;
+#define NB_COUNT ( NB_SIZE_Z + 1 )
+
+static nobuild_t noBuildZones[ MAX_NOBUILD_ZONES ];
+
+static qboolean noBuildActive = qfalse;
+static int noBuildEditZone = 0;
+static nobuild_sizer_t noBuildEditType = NB_ORIGIN_X;
+
+void nobuild_init( void )
{
- fileHandle_t f;
- int len;
- char *nobuild;
char map[ MAX_QPATH ];
- vec3_t origin = { 0.0f, 0.0f, 0.0f };
char line[ MAX_STRING_CHARS ];
- int i = 0;
- int i2;
- gentity_t *nb;
- float area;
- float height;
+ char *data, *pool;
+ fileHandle_t f;
+ int len;
+ int lcount = 0;
+ int zonecount = 0;
+ int i;
+ int id;
+ vec3_t origin, size;
+
+ memset( noBuildZones, 0, sizeof( noBuildZones ) );
trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
- len = trap_FS_FOpenFile( va( "nobuild/%s.dat", map ),
- &f, FS_READ );
+ len = trap_FS_FOpenFile( va( "layouts/%s/nobuild.nbd", map ), &f, FS_READ );
if( len < 0 )
{
- // This isn't needed since nobuild is pretty much optional...
- //G_Printf( "ERROR: nobuild for %s could not be opened\n", map );
return;
}
- nobuild = G_Alloc( len + 1 );
- trap_FS_Read( nobuild, len, f );
- *( nobuild + len ) = '\0';
+ data = pool = G_Alloc( len + 1 );
+ trap_FS_Read( data, len, f );
+ *( data + len ) = '\0';
trap_FS_FCloseFile( f );
- while( *nobuild )
+
+ i = 0;
+ while( *data )
{
if( i >= sizeof( line ) - 1 )
{
- return;
+ G_Printf( S_COLOR_RED "ERROR: line overflow in %s before \"%s\"\n",
+ va( "layouts/%s/nobuild.nbd", map ), line );
}
-
- line[ i++ ] = *nobuild;
+ line[ i++ ] = *data;
line[ i ] = '\0';
- if( *nobuild == '\n' )
+ if( *data == '\n' )
{
- i = 0;
- sscanf( line, "%f %f %f %f %f\n",
- &origin[ 0 ], &origin[ 1 ], &origin[ 2 ], &area, &height );
-
- // Make the marker...
- nb = G_Spawn( );
- nb->s.modelindex = 0;
- VectorCopy( origin, nb->s.pos.trBase );
- VectorCopy( origin, nb->r.currentOrigin );
- nb->noBuild.isNB = qtrue;
- nb->noBuild.Area = area;
- nb->noBuild.Height = height;
- trap_LinkEntity( nb );
-
- // Log markers made...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker != NULL )
- continue;
-
- level.nbMarkers[ i ].Marker = nb;
- VectorCopy( origin, level.nbMarkers[ i ].Origin );
- SnapVector( level.nbMarkers[ i ].Origin );
- break;
- }
-
+ i = 0;
+ lcount++;
+ id = -1;
+ if( sscanf( line, "%d %f %f %f %f %f %f\n",
+ &id,
+ &origin[ 0 ], &origin[ 1 ], &origin[ 2 ],
+ &size[ 0 ], &size[ 1 ], &size[ 2 ]) == 7 )
+ {
+ if( zonecount >= MAX_NOBUILD_ZONES )
+ {
+ G_Printf( S_COLOR_YELLOW "WARNING: nobuild zone count exceeded on line %d\n", lcount );
+ break;
+ }
+ noBuildZones[ zonecount ].in_use = qtrue;
+ VectorCopy( origin, noBuildZones[ zonecount ].origin );
+ VectorCopy( size, noBuildZones[ zonecount ].size );
+ zonecount++;
+ }
+ else
+ {
+ G_Printf( S_COLOR_YELLOW "WARNING: nobuild data parse error on line %d\n", lcount );
+ }
}
- nobuild++;
+ data++;
}
+
+ G_Free( pool );
+
+ if( zonecount )
+ G_Printf( "nobuild: loaded %d zones\n", zonecount );
}
-/*
-============
-G_NobuildSave
-Save all currently placed nobuild markers into the "nobuild" folder
-============
-*/
-void G_NobuildSave( void )
+static void nobuild_sizer_think( gentity_t *self )
+{
+ int zone, size;
+ vec3_t origin;
+
+ if( !noBuildActive )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ zone = noBuildEditZone;
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES || !noBuildZones[ zone ].in_use )
+ {
+ if( self->r.linked )
+ trap_UnlinkEntity( self );
+ return;
+ }
+
+ size = self->damage;
+ VectorCopy( noBuildZones[ zone ].origin, origin );
+ switch ( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ case NB_SIZE_X:
+ origin[ 0 ] += size;
+ break;
+ case NB_ORIGIN_Y:
+ case NB_SIZE_Y:
+ origin[ 1 ] += size;
+ break;
+ case NB_ORIGIN_Z:
+ case NB_SIZE_Z:
+ origin[ 2 ] += size;
+ break;
+ }
+
+ VectorCopy( origin, self->s.origin );
+ VectorCopy( origin, self->s.pos.trBase );
+ VectorCopy( origin, self->r.currentOrigin );
+
+ if( self->watertype != noBuildEditType )
+ {
+ self->watertype = noBuildEditType;
+ switch( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ case NB_ORIGIN_Y:
+ case NB_ORIGIN_Z:
+ self->s.modelindex = G_ModelIndex( "models/players/human_base/jetpack.md3" );
+ break;
+ case NB_SIZE_X:
+ case NB_SIZE_Y:
+ case NB_SIZE_Z:
+ self->s.modelindex = G_ModelIndex( "models/players/human_base/battpack.md3" );
+ break;
+ }
+ }
+
+ if( !self->r.linked )
+ trap_LinkEntity( self );
+}
+
+static gentity_t *nobuild_sizer( float distance )
+{
+ gentity_t *self;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_sizer";
+ self->s.eType = ET_GENERAL;
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->r.svFlags = SVF_BROADCAST;
+
+ self->damage = distance;
+ self->watertype = -1;
+ self->think = nobuild_sizer_think;
+
+ // run a think to set positions
+ nobuild_sizer_think( self );
+
+ return self;
+}
+
+static void nobuild_corner_think( gentity_t *self )
+{
+ int zone, corner;
+ vec3_t origin;
+
+ zone = self->count;
+ corner = self->damage;
+
+ if( !noBuildActive || !noBuildZones[ zone ].in_use )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ VectorCopy( noBuildZones[ zone ].origin, origin );
+ origin[ 0 ] += noBuildZones[ zone ].size[ 0 ] * nobuildCorner[ corner ].dir[ 0 ];
+ origin[ 1 ] += noBuildZones[ zone ].size[ 1 ] * nobuildCorner[ corner ].dir[ 1 ];
+ origin[ 2 ] += noBuildZones[ zone ].size[ 2 ] * nobuildCorner[ corner ].dir[ 2 ];
+
+ VectorCopy( origin, self->s.origin );
+ VectorCopy( origin, self->s.pos.trBase );
+ VectorCopy( origin, self->r.currentOrigin );
+}
+
+static gentity_t *nobuild_corner( int zone, int corner )
+{
+ gentity_t *self;
+
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES )
+ return NULL;
+ if( !noBuildZones[ zone ].in_use )
+ return NULL;
+ if( corner < 0 || corner >= MAX_NOBUILD_CORNERS )
+ return NULL;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_corner";
+ self->s.eType = ET_GENERAL;
+ self->s.modelindex = 9999;
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->count = zone;
+ self->damage = corner;
+ self->think = nobuild_corner_think;
+
+ // run a think to set positions
+ nobuild_corner_think( self );
+
+ self->s.apos.trBase[ YAW ] = nobuildCorner[ corner ].yaw;
+ if( nobuildCorner[ corner ].dir[ 2 ] > 0.0f )
+ self->s.apos.trBase[ PITCH ] = 180.0f;
+
+ trap_LinkEntity( self );
+
+ return self;
+}
+
+static void nobuild_think( gentity_t *self )
+{
+ int zone;
+
+ zone = self->count;
+
+ if( !noBuildActive || !noBuildZones[ zone ].in_use )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+ VectorCopy( noBuildZones[ zone ].origin, self->s.origin );
+ VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase );
+ VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin );
+}
+
+static gentity_t *nobuild_spawn( int zone )
+{
+ gentity_t *self;
+ int i;
+
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES )
+ return NULL;
+ if( !noBuildZones[ zone ].in_use )
+ return NULL;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_zone";
+ self->s.eType = ET_GENERAL;
+ self->s.modelindex = G_ModelIndex( "models/buildables/repeater/repeater.md3" );
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->think = nobuild_think;
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ VectorCopy( noBuildZones[ zone ].origin, self->s.origin );
+ VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase );
+ VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin );
+
+ self->s.apos.trBase[ PITCH ] = 180.0f;
+
+ self->count = zone;
+
+ trap_LinkEntity( self );
+
+ for( i = 0; i < MAX_NOBUILD_CORNERS; i++ )
+ nobuild_corner( zone, i );
+
+ return self;
+}
+
+static void nobuild_on( gentity_t *ent )
+{
+ int i;
+
+ if( noBuildActive )
+ return;
+
+ noBuildActive = qtrue;
+ noBuildEditZone = MAX_NOBUILD_ZONES - 1;
+ noBuildEditType = NB_ORIGIN_X;
+
+ nobuild_command( ent, qtrue, qfalse, 0.0f );
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ nobuild_spawn( i );
+ }
+
+ nobuild_sizer( 32.0f );
+ nobuild_sizer( -32.0f );
+
+ trap_SendServerCommand( -1,
+ va( "print \"^3!nobuild ^7edit mode enabled by %s\n\"",
+ ( ent ) ? ent->client->pers.netname : "console " ) );
+}
+
+static void nobuild_off( gentity_t *ent )
+{
+ if( noBuildActive )
+ {
+ noBuildActive = qfalse;
+
+ trap_SendServerCommand( -1,
+ va( "print \"^3!nobuild ^7edit mode disabled by %s\n\"",
+ ( ent ) ? ent->client->pers.netname : "console " ) );
+ }
+}
+
+void nobuild_add( gentity_t *ent )
+{
+ int i;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( !ent )
+ return;
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ if( noBuildZones[ i ].in_use )
+ continue;
+
+ noBuildZones[ i ].in_use = qtrue;
+ VectorCopy( ent->s.origin, noBuildZones[ i ].origin );
+ VectorSet( noBuildZones[ i ].size, 64.0f, 64.0f, 64.0f );
+
+ noBuildEditZone = i;
+ nobuild_spawn( i );
+
+ ADMP( va( "added nobuild zone #%d\n", i ) );
+ return;
+ }
+
+ ADMP( "maximum nobuild zones reached, can not add another zone.\n" );
+}
+
+void nobuild_del( gentity_t *ent )
+{
+ int zone;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES ||
+ !noBuildZones[ noBuildEditZone ].in_use )
+ {
+ ADMP( "a nobuild zone is not selected\n" );
+ return;
+ }
+
+ zone = noBuildEditZone;
+ nobuild_command( ent, qtrue, qfalse, 0.0f );
+ noBuildZones[ zone ].in_use = qfalse;
+ if( noBuildEditZone == zone )
+ noBuildEditZone = -1;
+
+ ADMP( va( "removed nobuild zone #%d\n", zone ) );
+}
+
+#define SIZE_MIN( a, b ) ( a = ( a + b < 32.0 ? a = 32.0 : a + b ) )
+
+void nobuild_command( gentity_t *ent, qboolean next_zone, qboolean next_type, float size )
+{
+ int n;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( next_zone )
+ {
+ n = noBuildEditZone + 1;
+ while( n != noBuildEditZone )
+ {
+ if( n >= MAX_NOBUILD_ZONES )
+ n = 0;
+ if( noBuildZones[ n ].in_use )
+ {
+ noBuildEditZone = n;
+ ADMP( va ( "nobuild zone %d selected\n", n ) );
+ return;
+ }
+ n++;
+ }
+ }
+ else if( next_type )
+ {
+ noBuildEditType++;
+ if ( noBuildEditType >= NB_COUNT )
+ noBuildEditType = 0;
+ }
+ else if( size )
+ {
+ switch( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ noBuildZones[ noBuildEditZone ].origin[ 0 ] += size;
+ break;
+ case NB_ORIGIN_Y:
+ noBuildZones[ noBuildEditZone ].origin[ 1 ] += size;
+ break;
+ case NB_ORIGIN_Z:
+ noBuildZones[ noBuildEditZone ].origin[ 2 ] += size;
+ break;
+ case NB_SIZE_X:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 0 ], size );
+ break;
+ case NB_SIZE_Y:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 1 ], size );
+ break;
+ case NB_SIZE_Z:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 2 ], size );
+ break;
+ }
+ }
+}
+
+void nobuild_go( gentity_t *ent )
+{
+ if( !ent )
+ return;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+ if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES ||
+ !noBuildZones[ noBuildEditZone ].in_use )
+ {
+ ADMP( "a nobuild zone is not selected\n" );
+ return;
+ }
+
+ VectorCopy( noBuildZones[ noBuildEditZone ].origin, ent->client->ps.origin );
+}
+
+void nobuild_set( qboolean enable, gentity_t *ent )
+{
+ if( !ent && enable )
+ {
+ ADMP( "only a connected client can enable nobuild mode\n" );
+ return;
+ }
+ if( enable )
+ {
+ nobuild_on( ent );
+ }
+ else
+ {
+ nobuild_off( ent );
+ }
+}
+
+void nobuild_save( void )
{
char map[ MAX_QPATH ];
char fileName[ MAX_OSPATH ];
fileHandle_t f;
int len;
+ int count = 0;
int i;
- gentity_t *ent;
char *s;
trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
if( !map[ 0 ] )
{
- G_Printf( "NobuildSave( ): no map is loaded\n" );
+ G_Printf( "LayoutSave( ): no map is loaded\n" );
return;
}
- Com_sprintf( fileName, sizeof( fileName ), "nobuild/%s.dat", map );
-
+ Com_sprintf( fileName, sizeof( fileName ), "layouts/%s/nobuild.nbd", map );
len = trap_FS_FOpenFile( fileName, &f, FS_WRITE );
- if( len < 0 )
+ if( len < 0 )
{
- G_Printf( "nobuildsave: could not open %s\n", fileName );
+ G_Printf( "layoutsave: could not open %s\n", fileName );
return;
}
- G_Printf("nobuildsave: saving nobuild to %s\n", fileName );
+ G_Printf("layoutsave: saving layout to %s\n", fileName );
- for( i = 0; i < MAX_GENTITIES; i++ )
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
{
- ent = &level.gentities[ i ];
- if( ent->noBuild.isNB != qtrue )
+ if( !noBuildZones[ i ].in_use )
continue;
- s = va( "%f %f %f %f %f\n",
- ent->r.currentOrigin[ 0 ],
- ent->r.currentOrigin[ 1 ],
- ent->r.currentOrigin[ 2 ],
- ent->noBuild.Area,
- ent->noBuild.Height );
+ s = va( "%d %f %f %f %f %f %f\n",
+ count,
+ noBuildZones[ i ].origin[ 0 ],
+ noBuildZones[ i ].origin[ 1 ],
+ noBuildZones[ i ].origin[ 2 ],
+ noBuildZones[ i ].size[ 0 ],
+ noBuildZones[ i ].size[ 1 ],
+ noBuildZones[ i ].size[ 2 ] );
trap_FS_Write( s, strlen( s ), f );
+ count++;
}
+
trap_FS_FCloseFile( f );
+ G_Printf( "saved %d nobuild zones\n", count );
}
+
+void nobuild_list( gentity_t *ent )
+{
+ int count = 0;
+ int i;
+
+ ADMBP_begin();
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ if( !noBuildZones[ i ].in_use )
+ continue;
+
+ ADMBP( va( "%2d @ x: %6.2f, y: %6.2f, z: %6.2f - %4.2f x %4.2f x %4.2f\n",
+ i,
+ noBuildZones[ i ].origin[ 0 ],
+ noBuildZones[ i ].origin[ 1 ],
+ noBuildZones[ i ].origin[ 2 ],
+ noBuildZones[ i ].size[ 0 ],
+ noBuildZones[ i ].size[ 1 ],
+ noBuildZones[ i ].size[ 2 ] ) );
+ count++;
+ }
+
+ ADMBP( va( "nobuild contains %d zones\n", count ) );
+
+ ADMBP_end();
+}
+
+int nobuild_check( vec3_t origin )
+{
+ int i;
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i ++ )
+ {
+ if( !noBuildZones[ i ].in_use )
+ continue;
+
+ if( origin[ 0 ] > noBuildZones[ i ].origin[ 0 ] - noBuildZones[ i ].size[ 0 ] &&
+ origin[ 0 ] < noBuildZones[ i ].origin[ 0 ] + noBuildZones[ i ].size[ 0 ] &&
+ origin[ 1 ] > noBuildZones[ i ].origin[ 1 ] - noBuildZones[ i ].size[ 1 ] &&
+ origin[ 1 ] < noBuildZones[ i ].origin[ 1 ] + noBuildZones[ i ].size[ 1 ] &&
+ origin[ 2 ] > noBuildZones[ i ].origin[ 2 ] - noBuildZones[ i ].size[ 2 ] &&
+ origin[ 2 ] < noBuildZones[ i ].origin[ 2 ] + noBuildZones[ i ].size[ 2 ] )
+ return i;
+ }
+
+ return -1;
+}
+