summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
authorenneract <trem.redman@gmail.com>2014-02-25 13:03:43 +0100
committerenneract <trem.redman@gmail.com>2014-02-25 13:03:43 +0100
commitdac3d3127fc94231bdde0c0822bb12de01e9e836 (patch)
tree17829bc1a1b0ddb2d49421c5ea0114b4c2eff436 /src/game
parentcd9f8731a13a29d51a401f67ec2aa0b8962e01c8 (diff)
0.1.7
Diffstat (limited to 'src/game')
-rw-r--r--src/game/bg_misc.c244
-rw-r--r--src/game/bg_public.h41
-rw-r--r--src/game/g_active.c123
-rw-r--r--src/game/g_buildable.c2285
-rw-r--r--src/game/g_client.c8
-rw-r--r--src/game/g_cmds.c108
-rw-r--r--src/game/g_combat.c26
-rw-r--r--src/game/g_local.h64
-rw-r--r--src/game/g_main.c141
-rw-r--r--src/game/g_weapon.c4
-rw-r--r--src/game/tremulous.h89
11 files changed, 1521 insertions, 1612 deletions
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index a416ecb..ca443f2 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -69,7 +69,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
ASPAWN_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_OVERMIND, //int buildNum;
@@ -105,7 +106,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qtrue, //qboolean uniqueTest;
OVERMIND_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_BARRICADE, //int buildNum;
@@ -141,7 +143,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
BARRICADE_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_ACIDTUBE, //int buildNum;
@@ -177,7 +180,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
ACIDTUBE_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_TRAPPER, //int buildNum;
@@ -213,7 +217,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
TRAPPER_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_BOOSTER, //int buildNum;
@@ -250,7 +255,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
BOOSTER_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_HIVE, //int buildNum;
@@ -285,7 +291,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
HIVE_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_H_SPAWN, //int buildNum;
@@ -321,7 +328,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
HSPAWN_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_H_MGTURRET, //int buildNum;
@@ -357,7 +365,12 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
MGTURRET_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ MGTURRET_R_IDLE, //float resistance;
+ MGTURRET_R_ACTIVE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_TESLAGEN, //int buildNum;
@@ -393,7 +406,12 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
TESLAGEN_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ TESLAGEN_R_IDLE, //float resistance;
+ TESLAGEN_R_ACTIVE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_ARMOURY, //int buildNum;
@@ -429,7 +447,12 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
ARMOURY_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ ARMOURY_RESISTANCE, //float resistance;
+ ARMOURY_RESISTANCE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_DCC, //int buildNum;
@@ -465,7 +488,12 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
DC_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ DC_R_IDLE, //float resistance;
+ DC_R_ACTIVE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_MEDISTAT, //int buildNum;
@@ -502,20 +530,24 @@ static const buildableAttributes_t bg_buildableList[ ] =
qtrue, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
MEDISTAT_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ MEDISTAT_R_IDLE, //float resistance;
+ MEDISTAT_R_ACTIVE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_REACTOR, //int buildNum;
"reactor", //char *buildName;
"Reactor", //char *humanName;
- "All structures except the telenode rely on a reactor to operate. "
- "The reactor provides power for all the human structures either "
- "directly or via repeaters. Only one reactor can be built at a time.",
- "team_human_reactor", //char *entityName;
+ "A large nuclear generator, able to power substantial amounts of "
+ "structures.",
+ "team_human_reactor_big", //char *entityName;
TR_GRAVITY, //trType_t traj;
0.0, //float bounce;
REACTOR_BP, //int buildPoints;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ ( 1 << S3 ), //int stages
REACTOR_HEALTH, //int health;
0, //int regenRate;
REACTOR_SPLASHDAMAGE, //int splashDamage;
@@ -536,9 +568,14 @@ static const buildableAttributes_t bg_buildableList[ ] =
0, //int creepSize;
qfalse, //qboolean dccTest;
qfalse, //qboolean transparentTest;
- qtrue, //qboolean uniqueTest;
+ qfalse, //qboolean uniqueTest;
REACTOR_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ qtrue, //qboolean isPowerSource;
+ qfalse, //qboolean requiresPower;
+ REACTOR_RESISTANCE, //float resistance;
+ REACTOR_RESISTANCE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
},
{
BA_H_REPEATER, //int buildNum;
@@ -574,7 +611,135 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
REPEATER_VALUE, //int value;
- qfalse //qboolean cuboid;
+ qfalse, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
+ },
+ {
+ BA_H_CAPBANK, //int buildNum;
+ "capbank", //char *buildName;
+ "Capacitor Bank", //char *humanName;
+ "A bank of capacitors able to compensate for "
+ "fluctations in the power network or even power "
+ "the network in the absence of a Reactor.",
+ "team_human_capbank", //char *entityName;
+ TR_GRAVITY, //trType_t traj;
+ 0.0, //float bounce;
+ CAPBANK_BP, //int buildPoints;
+ ( 1 << S2 )|( 1 << S3 ), //int stages
+ CAPBANK_HEALTH, //int health;
+ 0, //int regenRate;
+ CAPBANK_SPLASHDAMAGE, //int splashDamage;
+ CAPBANK_SPLASHRADIUS, //int splashRadius;
+ MOD_HSPAWN, //int meansOfDeath;
+ TEAM_HUMANS, //int team;
+ ( 1 << WP_HBUILD ), //weapon_t buildWeapon;
+ BANIM_IDLE1, //int idleAnim;
+ 100, //int nextthink;
+ CAPBANK_BT, //int buildTime;
+ qtrue, //qboolean usable;
+ 0, //int turretRange;
+ 0, //int turretFireSpeed;
+ WP_NONE, //weapon_t turretProjType;
+ 0.95f, //float minNormal;
+ qfalse, //qboolean invertNormal;
+ qfalse, //qboolean creepTest;
+ 0, //int creepSize;
+ qfalse, //qboolean dccTest;
+ qfalse, //qboolean transparentTest;
+ qfalse, //qboolean uniqueTest;
+ CAPBANK_VALUE, //int value;
+ qfalse, //qboolean cuboid;
+ qtrue, //qboolean isPowerSource;
+ qfalse, //qboolean requiresPower;
+ CAPBANK_RESISTANCE, //float resistance;
+ CAPBANK_RESISTANCE, //float surgeResistance;
+ qfalse //qboolean hasStorage;
+ },
+ {
+ BA_H_RTG, //int buildNum;
+ "rtg", //char *buildName;
+ "RTG Unit", //char *humanName;
+ "A portable all-in-one device, featuring a "
+ "radioisotope thermoelectric generator and a "
+ "miniaturized version of the Refinery. Does not "
+ "require power to be built. ^2First RTG Unit does "
+ "not require build points!",
+ "team_human_reactor", //char *entityName;
+ TR_GRAVITY, //trType_t traj;
+ 0.0, //float bounce;
+ RTG_BP, //int buildPoints;
+ ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ RTG_HEALTH, //int health;
+ 0, //int regenRate;
+ RTG_SPLASHDAMAGE, //int splashDamage;
+ RTG_SPLASHRADIUS, //int splashRadius;
+ MOD_HSPAWN, //int meansOfDeath;
+ TEAM_HUMANS, //int team;
+ ( 1 << WP_HBUILD ), //weapon_t buildWeapon;
+ BANIM_IDLE1, //int idleAnim;
+ 500, //int nextthink;
+ RTG_BT, //int buildTime;
+ qtrue, //qboolean usable;
+ 0, //int turretRange;
+ 0, //int turretFireSpeed;
+ WP_NONE, //weapon_t turretProjType;
+ 0.95f, //float minNormal;
+ qfalse, //qboolean invertNormal;
+ qfalse, //qboolean creepTest;
+ 0, //int creepSize;
+ qfalse, //qboolean dccTest;
+ qfalse, //qboolean transparentTest;
+ qfalse, //qboolean uniqueTest;
+ RTG_VALUE, //int value;
+ qfalse, //qboolean cuboid;
+ qtrue, //qboolean isPowerSource;
+ qfalse, //qboolean requiresPower;
+ RTG_RESISTANCE, //float resistance;
+ RTG_RESISTANCE, //float surgeResistance;
+ qtrue //qboolean hasStorage;
+ },
+ {
+ BA_H_REFINERY, //int buildNum;
+ "refinery", //char *buildName;
+ "Refinery", //char *humanName;
+ "A portable all-in-one device, featuring a "
+ "radioisotope thermoelectric generator and a "
+ "miniaturized version of the Refinery. Does not "
+ "require power to be built. ^1This is the first "
+ "buildable to be built.",
+ "team_human_refinery", //char *entityName;
+ TR_GRAVITY, //trType_t traj;
+ 0.0, //float bounce;
+ REFINERY_BP, //int buildPoints;
+ ( 1 << S2 )|( 1 << S3 ), //int stages
+ REFINERY_HEALTH, //int health;
+ 0, //int regenRate;
+ REFINERY_SPLASHDAMAGE, //int splashDamage;
+ REFINERY_SPLASHRADIUS, //int splashRadius;
+ MOD_HSPAWN, //int meansOfDeath;
+ TEAM_HUMANS, //int team;
+ ( 1 << WP_HBUILD ), //weapon_t buildWeapon;
+ BANIM_IDLE1, //int idleAnim;
+ 500, //int nextthink;
+ REFINERY_BT, //int buildTime;
+ qtrue, //qboolean usable;
+ 0, //int turretRange;
+ 0, //int turretFireSpeed;
+ WP_NONE, //weapon_t turretProjType;
+ 0.95f, //float minNormal;
+ qfalse, //qboolean invertNormal;
+ qfalse, //qboolean creepTest;
+ 0, //int creepSize;
+ qfalse, //qboolean dccTest;
+ qfalse, //qboolean transparentTest;
+ qfalse, //qboolean uniqueTest;
+ REFINERY_VALUE, //int value;
+ qfalse, //qboolean cuboid;
+ qfalse, //qboolean isPowerSource;
+ qtrue, //qboolean requiresPower;
+ REFINERY_R_IDLE, //float resistance;
+ REFINERY_R_ACTIVE, //float surgeResistance;
+ qtrue //qboolean hasStorage;
},
{
BA_H_CUBOID1, //int buildNum;
@@ -610,7 +775,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
0, //int value;
- qtrue //qboolean cuboid;
+ qtrue, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_H_CUBOID2, //int buildNum;
@@ -618,13 +784,13 @@ static const buildableAttributes_t bg_buildableList[ ] =
"Glass", //char *humanName;
"A cuboid made of a transparent chemical compound. "
"Its durability is low compared to other glass-like "
- "materials. However it has shown increased strenght "
+ "materials. However it has shown increased strength "
"when formed into a thin pane shape.",
"team_human_hcuboid2", //char *entityName;
TR_GRAVITY, //trType_t traj;
0.0, //float bounce;
0, //int buildPoints;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ ( 1 << S2 )|( 1 << S3 ), //int stages
0, //int health;
0, //int regenRate;
0, //int splashDamage;
@@ -647,7 +813,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
0, //int value;
- qtrue //qboolean cuboid;
+ qtrue, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_H_CUBOID3, //int buildNum;
@@ -661,7 +828,7 @@ static const buildableAttributes_t bg_buildableList[ ] =
TR_GRAVITY, //trType_t traj;
0.0, //float bounce;
0, //int buildPoints;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ ( 1 << S3 ), //int stages
0, //int health;
0, //int regenRate;
0, //int splashDamage;
@@ -684,7 +851,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
0, //int value;
- qtrue //qboolean cuboid;
+ qtrue, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_CUBOID1, //int buildNum;
@@ -720,7 +888,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
0, //int value;
- qtrue //qboolean cuboid;
+ qtrue, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
},
{
BA_A_CUBOID2, //int buildNum;
@@ -735,7 +904,7 @@ static const buildableAttributes_t bg_buildableList[ ] =
TR_GRAVITY, //trType_t traj;
0.0, //float bounce;
0, //int buildPoints;
- ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages
+ ( 1 << S2 )|( 1 << S3 ), //int stages
0, //int health;
0, //int regenRate;
0, //int splashDamage;
@@ -758,7 +927,8 @@ static const buildableAttributes_t bg_buildableList[ ] =
qfalse, //qboolean transparentTest;
qfalse, //qboolean uniqueTest;
0, //int value;
- qtrue //qboolean cuboid;
+ qtrue, //qboolean cuboid;
+ DEFAULT_POWER_SETTINGS
}
};
@@ -1283,7 +1453,7 @@ static const classAttributes_t bg_classList[ ] =
80, //int steptime;
LEVEL2_SPEED, //float speed;
10.0f, //float acceleration;
- 3.0f, //float airAcceleration;
+ 2.0f, //float airAcceleration;
6.0f, //float friction;
100.0f, //float stopSpeed;
380.0f, //float jumpMagnitude;
@@ -1310,7 +1480,7 @@ static const classAttributes_t bg_classList[ ] =
80, //int steptime;
LEVEL2_UPG_SPEED, //float speed;
10.0f, //float acceleration;
- 3.0f, //float airAcceleration;
+ 2.0f, //float airAcceleration;
6.0f, //float friction;
100.0f, //float stopSpeed;
380.0f, //float jumpMagnitude;
@@ -1419,9 +1589,9 @@ static const classAttributes_t bg_classList[ ] =
0.002f, //float bob;
1.0f, //float bobCycle;
100, //int steptime;
- 1.0f, //float speed;
+ 1.1f, //float speed;
10.0f, //float acceleration;
- 1.0f, //float airAcceleration;
+ 1.5f, //float airAcceleration;
6.0f, //float friction;
100.0f, //float stopSpeed;
220.0f, //float jumpMagnitude;
@@ -4301,7 +4471,7 @@ const cuboidAttributes_t BG_CuboidTypes [] =
80, // float hppv
CBHPT_PLAIN, // int hpt
3.0, // float bppv
- 20, // int buildrate
+ 30, // int buildrate
5000, // int minbt
qfalse, // qboolean regen;
0, // int regenspeed
@@ -4325,7 +4495,7 @@ const cuboidAttributes_t BG_CuboidTypes [] =
52, // float hppv
CBHPT_PANES, // int hpt
1.5, // float bppv
- 15, // int buildrate
+ 25, // int buildrate
6500, // int minbt
qfalse, // qboolean regen;
0, // int regenspeed
@@ -4349,7 +4519,7 @@ const cuboidAttributes_t BG_CuboidTypes [] =
50, // float hppv
CBHPT_PLAIN, // int hpt
4.0, // float bppv
- 10, // int buildrate
+ 15, // int buildrate
8000, // int minbt
qfalse, // qboolean regen;
0, // int regenspeed
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index e06015d..cbaf90a 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -280,12 +280,11 @@ typedef enum
PERS_CREDIT, // human credit
PERS_QUEUEPOS, // position in the spawn queue
PERS_NEWWEAPON, // weapon to switch to
- PERS_BP,
- PERS_MARKEDBP,
+ PERS_BUILDPOINTS,
//zdrytchx: no space in stats, use persistant. This meanas we risk doing a double jump upon spawning but death animations are 1700 msecs long, so technically it's impossible anyway
PERS_JUMPTIME,
PERS_SPAWNS_IMPLANTED
- // netcode has space for 1 more
+ // netcode has space for 2 more
} persEnum_t;
#define PS_WALLCLIMBINGFOLLOW 0x00000001
@@ -293,6 +292,21 @@ typedef enum
#define PS_NONSEGMODEL 0x00000004
#define PS_SPRINTTOGGLE 0x00000008
+// player_state->misc[] indexes
+typedef enum
+{
+ MISC_INFOHEAD,
+ MISC_INFO1,
+ MISC_INFO2,
+ MISC_INFO3,
+
+ MISC_CUBOID_X,
+ MISC_CUBOID_Y,
+ MISC_CUBOID_Z
+
+ // netcode has space for 10 more
+} miscEnum_t;
+
// entityState_t->eFlags
// notice that some flags are overlapped, so their meaning depends on context
#define EF_DEAD 0x0001 // don't draw a foe marker over players with EF_DEAD
@@ -307,7 +321,7 @@ typedef enum
// buildable flags:
#define EF_B_SPAWNED 0x0008
#define EF_B_POWERED 0x0010
-#define EF_B_MARKED 0x0020
+#define EF_B_SURGE 0x0020 // note: enabled/disabled for Repeaters
#define EF_WARN_CHARGE 0x0020 // Lucifer Cannon is about to overcharge
#define EF_WALLCLIMB 0x0040 // wall walking
@@ -438,6 +452,10 @@ typedef enum
BA_H_REACTOR,
BA_H_REPEATER,
+ BA_H_CAPBANK,
+ BA_H_RTG,
+
+ BA_H_REFINERY,
//cuboids must stay in a block
#define CUBOID_FIRST BA_H_CUBOID1
@@ -581,7 +599,10 @@ typedef enum
EV_ALIEN_HATCH_FAILURE, // when it doesns't work
EV_JETPACK_DEACTIVATE,
- EV_JETPACK_REFUEL
+ EV_JETPACK_REFUEL,
+
+ EV_POWER_SWITCH,
+ EV_POWER_ZAP
} entity_event_t;
@@ -1068,9 +1089,17 @@ typedef struct
qboolean transparentTest;
qboolean uniqueTest;
- int value;
+ int value;
qboolean cuboid;
+
+ //power grid features
+ qboolean isPowerSource;
+ qboolean requiresPower;
+ float resistance;
+ float surgeResistance;
+
+ qboolean hasStorage;
} buildableAttributes_t;
typedef struct
diff --git a/src/game/g_active.c b/src/game/g_active.c
index 1fa0caa..95f00d6 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -682,20 +682,6 @@ void ClientTimerActions( gentity_t *ent, int msec )
client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT;
else
client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT;
-
- // Let the client know which buildables will be removed by building
- for( i = 0; i < MAX_MISC; i++ )
- {
- if( i < level.numBuildablesForRemoval )
- client->ps.misc[ i ] = level.markedBuildables[ i ]->s.number;
- else
- client->ps.misc[ i ] = 0;
- }
- }
- else
- {
- for( i = 0; i < MAX_MISC; i++ )
- client->ps.misc[ i ] = 0;
}
break;
@@ -780,6 +766,55 @@ void ClientTimerActions( gentity_t *ent, int msec )
ent->client->ps.stats[ STAT_SHAKE ] *= 0.77f;
if( ent->client->ps.stats[ STAT_SHAKE ] < 0 )
ent->client->ps.stats[ STAT_SHAKE ] = 0;
+
+ // update the currents and voltages
+ if( BG_GetPlayerWeapon( &client->ps ) == WP_HBUILD )
+ {
+ trace_t tr;
+ vec3_t viewOrigin, forward, end;
+ gentity_t *traceEnt;
+ int head_network = 0, head_entity = 1023;
+
+ BG_GetClientViewOrigin( &client->ps, viewOrigin );
+ AngleVectors( client->ps.viewangles, forward, NULL, NULL );
+ VectorMA( viewOrigin, 200, forward, end );
+
+ trap_Trace( &tr, viewOrigin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID );
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ head_network = ent->powerNetwork;
+
+ if( tr.fraction < 0.99f )
+ {
+ if( traceEnt->client )
+ {
+ if( traceEnt->client->pers.teamSelection == TEAM_HUMANS &&
+ traceEnt->client->ps.stats[ STAT_HEALTH ] >= 0 )
+ {
+ head_entity = traceEnt->s.number;
+ client->ps.misc[ MISC_INFO1 ] = traceEnt->client->ps.stats[ STAT_HEALTH ];
+
+ if( BG_GetPlayerWeapon( &traceEnt->client->ps ) == WP_HBUILD )
+ client->ps.misc[ MISC_INFO2 ] = traceEnt->client->ps.persistant[ PERS_BUILDPOINTS ];
+ else
+ client->ps.misc[ MISC_INFO2 ] = -1;
+ }
+ }
+ else if( traceEnt->s.eType == ET_BUILDABLE &&
+ traceEnt->health > 0 && traceEnt->buildableTeam == TEAM_HUMANS &&
+ ( traceEnt->isPowerSource || traceEnt->requiresPower ) )
+ {
+ head_network = traceEnt->powerNetwork;
+ head_entity = traceEnt->s.number;
+ client->ps.misc[ MISC_INFO1 ] = traceEnt->current * 1000;
+ client->ps.misc[ MISC_INFO2 ] = traceEnt->voltage * 1000;
+ client->ps.misc[ MISC_INFO3 ] = traceEnt->storedBP * 10;
+ }
+ }
+
+ client->ps.misc[ MISC_INFOHEAD ] = ( head_network & 1023 ) | ( head_entity & 1023 ) << 10;
+
+ }
}
while( client->time1000 >= 1000 )
@@ -1769,16 +1804,11 @@ void ClientThink_real( gentity_t *ent )
vec3_t eyes, view, point;
gentity_t *traceEnt;
-#define USE_OBJECT_RANGE 64
-
- int entityList[ MAX_GENTITIES ];
- vec3_t range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE };
- vec3_t mins, maxs;
- int i, num;
+#define USE_OBJECT_RANGE 100
// look for object infront of player
AngleVectors( client->ps.viewangles, view, NULL, NULL );
- BG_GetClientViewOrigin( &client->ps, eyes ); // !@#CUBOID
+ BG_GetClientViewOrigin( &client->ps, eyes );
VectorMA( eyes, USE_OBJECT_RANGE, view, point );
trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT );
@@ -1786,50 +1816,29 @@ void ClientThink_real( gentity_t *ent )
if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use )
traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
- else
+ else if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
- //no entity in front of player - do a small area search
-
- VectorAdd( client->ps.origin, range, maxs );
- VectorSubtract( client->ps.origin, range, mins );
-
- num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- for( i = 0; i < num; i++ )
+ if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ],
+ client->pers.credit,
+ g_alienStage.integer ) )
{
- traceEnt = &g_entities[ entityList[ i ] ];
-
- if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use )
- {
- traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
- break;
- }
+ //no nearby objects and alien - show class menu
+ G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST );
}
-
- if( i == num && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ else
{
- if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ],
- client->pers.credit,
- g_alienStage.integer ) )
- {
- //no nearby objects and alien - show class menu
- G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST );
- }
- else
- {
- //flash frags
- G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 );
- }
+ //flash frags
+ G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 );
}
}
}
- client->ps.persistant[ PERS_BP ] = G_GetBuildPoints( client->ps.origin,
- client->ps.stats[ STAT_TEAM ] );
- client->ps.persistant[ PERS_MARKEDBP ] = G_GetMarkedBuildPoints( client->ps.origin,
- client->ps.stats[ STAT_TEAM ] );
-
- if( client->ps.persistant[ PERS_BP ] < 0 )
- client->ps.persistant[ PERS_BP ] = 0;
+ if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
+ client->ps.persistant[ PERS_BUILDPOINTS ] = G_GetBuildPoints( client->ps.origin, client->ps.stats[ STAT_TEAM ] );
+ if( client->ps.persistant[ PERS_BUILDPOINTS ] < 0 )
+ client->ps.persistant[ PERS_BUILDPOINTS ] = 0;
+ }
// perform once-a-second actions
ClientTimerActions( ent, msec );
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index 0039ccd..8447e84 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -111,223 +111,6 @@ gentity_t *G_CheckSpawnPoint( int spawnNum, const vec3_t origin,
return NULL;
}
-#define POWER_REFRESH_TIME 2000
-
-/*
-================
-G_FindPower
-
-attempt to find power for self, return qtrue if successful
-================
-*/
-qboolean G_FindPower( gentity_t *self, qboolean searchUnspawned )
-{
- int i, j;
- gentity_t *ent, *ent2;
- gentity_t *closestPower = NULL;
- int distance = 0;
- int minDistance = REPEATER_BASESIZE + 1;
- vec3_t temp_v;
-
- if( self->buildableTeam != TEAM_HUMANS )
- return qfalse;
-
- // Reactor is always powered
- if( self->s.modelindex == BA_H_REACTOR )
- {
- self->parentNode = self;
-
- return qtrue;
- }
-
- // Handle repeaters
- if( self->s.modelindex == BA_H_REPEATER )
- {
- self->parentNode = G_Reactor( );
-
- return self->parentNode != NULL;
- }
-
- // 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( ( ent->s.modelindex == BA_H_REACTOR || ent->s.modelindex == BA_H_REPEATER ) &&
- ( searchUnspawned || ent->spawned ) && ent->powered && ent->health > 0 )
- {
- VectorSubtract( self->s.origin, ent->s.origin, temp_v );
- distance = VectorLength( temp_v );
-
- // Always prefer a reactor if there is one in range
- if( ent->s.modelindex == BA_H_REACTOR && distance <= REACTOR_BASESIZE )
- {
- // Only power as much BP as the reactor can hold
- if( self->s.modelindex != BA_NONE )
- {
- int buildPoints = g_humanBuildPoints.integer;
-
- // Scan the buildables in the reactor zone
- for( j = MAX_CLIENTS, ent2 = g_entities + j; j < level.num_entities; j++, ent2++ )
- {
- gentity_t *powerEntity;
-
- if( ent2->s.eType != ET_BUILDABLE )
- continue;
-
- if( ent2 == self )
- continue;
-
- powerEntity = ent2->parentNode;
-
- if( powerEntity && powerEntity->s.modelindex == BA_H_REACTOR && ( powerEntity == ent ) )
- {
- buildPoints -= BG_Buildable( ent2->s.modelindex, ent2->cuboidSize )->buildPoints;
- }
- }
-
- buildPoints -= level.humanBuildPointQueue;
-
- buildPoints -= BG_Buildable( self->s.modelindex, self->cuboidSize )->buildPoints;
-
- if( buildPoints >= 0 )
- {
- self->parentNode = ent;
- return qtrue;
- }
- else
- {
- // a buildable can still be built if it shares BP from two zones
-
- // TODO: handle combined power zones here
- }
- }
-
- // Dummy buildables don't need to look for zones
- else
- {
- self->parentNode = ent;
- return qtrue;
- }
- }
- else if( distance < minDistance )
- {
- // It's a repeater, so check that enough BP will be available to power
- // the buildable but only if self is a real buildable
-
- if( self->s.modelindex != BA_NONE )
- {
- 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( ent2->s.eType != ET_BUILDABLE )
- continue;
-
- if( ent2 == self )
- continue;
-
- powerEntity = ent2->parentNode;
-
- if( powerEntity && powerEntity->s.modelindex == BA_H_REPEATER && ( powerEntity == ent ) )
- {
- buildPoints -= BG_Buildable( ent2->s.modelindex, ent->cuboidSize )->buildPoints;
- }
- }
-
- if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active )
- buildPoints -= level.buildPointZones[ ent->buildPointZone ].queuedBuildPoints;
-
- buildPoints -= BG_Buildable( self->s.modelindex, self->cuboidSize )->buildPoints;
-
- if( buildPoints >= 0 )
- {
- closestPower = ent;
- minDistance = distance;
- }
- else
- {
- // a buildable can still be built if it shares BP from two zones
-
- // TODO: handle combined power zones here
- }
- }
- else
- {
- // Dummy buildables don't need to look for zones
- closestPower = ent;
- minDistance = distance;
- }
- }
- }
- }
-
- self->parentNode = closestPower;
- return self->parentNode != NULL;
-}
-
-/*
-================
-G_PowerEntityForPoint
-
-Simple wrapper to G_FindPower to find the entity providing
-power for the specified point
-================
-*/
-gentity_t *G_PowerEntityForPoint( const vec3_t origin )
-{
- gentity_t dummy;
-
- dummy.parentNode = NULL;
- dummy.buildableTeam = TEAM_HUMANS;
- dummy.s.modelindex = BA_NONE;
- VectorCopy( origin, dummy.s.origin );
-
- if( G_FindPower( &dummy, qfalse ) )
- return dummy.parentNode;
- else
- return NULL;
-}
-
-/*
-================
-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
-that is providing it
-================
-*/
-buildable_t G_IsPowered( vec3_t origin )
-{
- gentity_t *ent = G_PowerEntityForPoint( origin );
-
- if( ent )
- return ent->s.modelindex;
- else
- return BA_NONE;
-}
-
-
/*
==================
G_GetBuildPoints
@@ -347,95 +130,36 @@ int G_GetBuildPoints( const vec3_t pos, team_t team )
}
else if( team == TEAM_HUMANS )
{
- gentity_t *powerPoint = G_PowerEntityForPoint( pos );
-
- if( powerPoint && powerPoint->s.modelindex == BA_H_REACTOR )
- return level.humanBuildPoints;
-
- if( powerPoint && powerPoint->s.modelindex == BA_H_REPEATER &&
- powerPoint->usesBuildPointZone && level.buildPointZones[ powerPoint->buildPointZone ].active )
- {
- return level.buildPointZones[ powerPoint->buildPointZone ].totalBuildPoints -
- level.buildPointZones[ powerPoint->buildPointZone ].queuedBuildPoints;
- }
-
- // Return the BP of the main zone by default
- return level.humanBuildPoints;
+ return 0xDEADBEE; //humans use the material system
}
return 0;
}
/*
-==================
-G_GetMarkedBuildPoints
+================
+G_IsDCCBuilt
-Get the number of marked build points from a position
-==================
+See if any powered DCC exists
+================
*/
-int G_GetMarkedBuildPoints( const vec3_t pos, team_t team )
+qboolean G_IsDCCBuilt( void )
{
- gentity_t *ent;
int i;
- int sum = 0;
-
- if( G_TimeTilSuddenDeath( ) <= 0 )
- return 0;
-
- if( !g_markDeconstruct.integer )
- return 0;
-
- for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
- {
- if( ent->s.eType != ET_BUILDABLE )
- continue;
-
- if( team == TEAM_HUMANS &&
- ent->s.modelindex != BA_H_REACTOR &&
- ent->s.modelindex != BA_H_REPEATER &&
- ent->parentNode != G_PowerEntityForPoint( pos ) )
- continue;
-
- if( !ent->inuse )
- continue;
-
- if( ent->health <= 0 )
- continue;
-
- if( ent->buildableTeam != team )
- continue;
-
- if( ent->deconstruct )
- sum += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints;
- }
-
- return sum;
-}
-
-/*
-==================
-G_InPowerZone
-
-See if a buildable is inside of another power zone.
-Return pointer to provider if so.
-It's different from G_FindPower because FindPower for
-providers will find themselves.
-(This doesn't check if power zones overlap)
-==================
-*/
-gentity_t *G_InPowerZone( gentity_t *self )
-{
- int i;
- gentity_t *ent;
- int distance;
- vec3_t temp_v;
+ gentity_t *ent;
+ static gentity_t *cache = NULL;
+
+ if( cache && cache->inuse && cache->s.eType == ET_BUILDABLE &&
+ cache->s.modelindex == BA_H_DCC && cache->spawned &&
+ cache->powered && cache->health >= 0 )
+ return qtrue;
for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
{
if( ent->s.eType != ET_BUILDABLE )
continue;
- if( ent == self )
+ if( ent->s.modelindex != BA_H_DCC )
continue;
if( !ent->spawned )
@@ -443,81 +167,46 @@ gentity_t *G_InPowerZone( gentity_t *self )
if( ent->health <= 0 )
continue;
+
+ if( !ent->powered )
+ 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 );
+ cache = ent;
- if( ent->s.modelindex == BA_H_REACTOR && distance <= REACTOR_BASESIZE )
- return ent;
- else if( ent->s.modelindex == BA_H_REPEATER && distance <= REPEATER_BASESIZE )
- return ent;
- }
+ return qtrue;
}
- return NULL;
+ return qfalse;
}
-/*
-================
-G_FindDCC
-
-attempt to find a controlling DCC for self, return number found
-================
-*/
-int G_FindDCC( gentity_t *self )
-{
- int i;
- gentity_t *ent;
- int distance = 0;
- vec3_t temp_v;
- int foundDCC = 0;
-
- if( self->buildableTeam != TEAM_HUMANS )
- return 0;
-
- //iterate through entities
- for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities && foundDCC < MAX_DCS_PER_BUILDABLE; 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 < DC_RANGE && ent->powered )
- {
- foundDCC++;
- }
- }
- }
-
- return foundDCC;
-}
/*
================
-G_IsDCCBuilt
+G_IsRTGBuilt
-See if any powered DCC exists
+See if any RTG exists
================
*/
-qboolean G_IsDCCBuilt( void )
+qboolean G_IsRTGBuilt( void )
{
int i;
gentity_t *ent;
+ static gentity_t *cache = NULL;
+
+ if( cache && cache->inuse && cache->s.eType == ET_BUILDABLE &&
+ cache->s.modelindex == BA_H_DCC && cache->spawned &&
+ cache->health >= 0 )
+ return qtrue;
for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
{
+ if( !ent->r.linked )
+ continue;
+
if( ent->s.eType != ET_BUILDABLE )
continue;
- if( ent->s.modelindex != BA_H_DCC )
+ if( ent->s.modelindex != BA_H_RTG )
continue;
if( !ent->spawned )
@@ -526,6 +215,8 @@ qboolean G_IsDCCBuilt( void )
if( ent->health <= 0 )
continue;
+ cache = ent;
+
return qtrue;
}
@@ -534,33 +225,17 @@ qboolean G_IsDCCBuilt( void )
/*
================
-G_Reactor
G_Overmind
-Since there's only one of these and we quite often want to find them, cache the
-results, but check them for validity each time
+Since there's only one overmind and we quite often want to find it, cache the
+results, but check it for validity each time
-The code here will break if more than one reactor or overmind is allowed, even
+The code here will break if more than one overmind is allowed, even
if one of them is dead/unspawned
================
*/
static gentity_t *G_FindBuildable( buildable_t buildable );
-gentity_t *G_Reactor( void )
-{
- static gentity_t *rc;
-
- // If cache becomes invalid renew it
- if( !rc || rc->s.eType != ET_BUILDABLE || rc->s.modelindex != BA_H_REACTOR )
- rc = G_FindBuildable( BA_H_REACTOR );
-
- // If we found it and it's alive, return it
- if( rc && rc->spawned && rc->health > 0 )
- return rc;
-
- return NULL;
-}
-
gentity_t *G_Overmind( void )
{
static gentity_t *om;
@@ -698,6 +373,457 @@ static void G_CreepSlow( gentity_t *self )
/*
================
+G_ScanPowerGrid
+
+Recursively finds all power entities reachable from the specified entity
+================
+*/
+static struct
+{
+ int networkID;
+
+ gentity_t *load[ MAX_GENTITIES ];
+ int loadCount;
+
+ gentity_t *sources[ MAX_GENTITIES ];
+ int sourceCount;
+
+ qboolean visited[ MAX_GENTITIES ];
+} grid;
+
+void G_ScanPowerGrid( gentity_t *this )
+{
+ int i;
+ int nextList[ MAX_GENTITIES ], nextCount;
+ gentity_t *next;
+ vec3_t mins, maxs;
+ float range;
+
+ switch( this->s.modelindex )
+ {
+ case BA_H_REACTOR:
+ case BA_H_CAPBANK:
+ case BA_H_RTG:
+ range = REACTOR_BASESIZE;
+ break;
+ case BA_H_REPEATER:
+ range = REPEATER_BASESIZE;
+ break;
+ }
+
+ for( i = 0; i < 3; i++ )
+ {
+ mins[ i ] = this->s.origin[ i ] - range;
+ maxs[ i ] = this->s.origin[ i ] + range;
+ }
+
+ nextCount = trap_EntitiesInBox( mins, maxs, nextList, MAX_GENTITIES );
+
+ for( i = 0; i < nextCount; i++ )
+ {
+ if( grid.visited[ nextList[ i ] ] )
+ continue;
+ grid.visited[ nextList[ i ] ] = qtrue;
+
+ next = g_entities + nextList[ i ];
+
+ if( next->s.eType != ET_BUILDABLE )
+ {
+ //let ckits know in which network's range they're in
+ if( next->client &&
+ next->health >= 0 &&
+ next->s.weapon == WP_HBUILD )
+ next->powerNetwork = grid.networkID;
+ continue;
+ }
+
+ if( next->health <= 0 )
+ continue;
+ if( next->buildableTeam != TEAM_HUMANS )
+ continue;
+
+ //repeater just extends the power grid
+ //it does not provide or consume power
+ if( next->spawned && next->s.modelindex == BA_H_REPEATER )
+ {
+ //switched off
+ if( !next->active )
+ continue;
+
+ next->powerNetwork = grid.networkID;
+ G_ScanPowerGrid( next );
+ continue;
+ }
+
+ if( !next->isPowerSource && !next->requiresPower )
+ continue;
+
+ next->powerNetwork = grid.networkID;
+ if( next->isPowerSource )
+ {
+ //switched off
+ if( !next->active )
+ continue;
+ grid.sources[ grid.sourceCount++ ] = next;
+ }
+ else
+ grid.load[ grid.loadCount++ ] = next;
+ }
+}
+
+
+/*
+================
+G_CalculatePowerGrid
+
+This function takes the listed power sources (with voltages V and
+internal resistances Rs) and load (with resistances R), constructs the
+following electrical circuit and solves it:
+
+ Legend:
+ -(-+)-- voltage source
+ -/\/\/- resistor
+ |- ground
+
+ V1 Rs1 I1 R1
+ +--(-+)--/\/\/-->-+ +--/\/\/--+
+ | V2 Rs2 I2| I | R2 |
+ |--+--(-+)--/\/\/-->-+-->--+--/\/\/--+--|
+ | | | |
+ ... ... ... ...
+ | Vn Rsn In| | Rn |
+ +--(-+)--/\/\/-->-+ +--/\/\/--+
+
+================
+*/
+void G_CalculatePowerGrid( void )
+{
+ int i, j;
+ gentity_t *ent;
+
+ float nGl, nGs, nG, nR;
+ float URs, U2, Is;
+
+ // the net load conductance (resistors in parallel)
+ for( nGl = 0.0f, i = 0; i < grid.loadCount; i++ )
+ nGl += 1.0f / grid.load[ i ]->resistance;
+
+ // the net source conductance (resistors in parallel)
+ for( nGs = 0.0f, i = 0; i < grid.sourceCount; i++ )
+ nGs += 1.0f / grid.sources[ i ]->resistance;
+
+ // solve the circuit using the superposition theorem
+ for( i = 0; i < grid.sourceCount; i++ )
+ {
+ ent = grid.sources[ i ];
+
+ // net load + other sources conductance
+ nG = nGl + nGs - 1.0f / ent->resistance;
+
+ // net resistance
+ nR = 1.0f / nG + ent->resistance;
+
+ // current flowing through the source
+ Is = ent->voltage / nR;
+ ent->current += ent->voltage / nR;
+
+ // voltage drop on source's internal resistance
+ URs = Is * ent->resistance;
+
+ // voltage drop on other sources or the entire load
+ U2 = ent->voltage - URs;
+
+ // current flowing through other sources
+ for( j = 0; j < grid.sourceCount; j++ )
+ if( i != j )
+ grid.sources[ j ]->current -= U2 / grid.sources[ j ]->resistance;
+
+ // current flowing through parts of the load
+ for( j = 0; j < grid.loadCount; j++ )
+ grid.load[ j ]->current += U2 / grid.load[ j ]->resistance;
+ }
+}
+
+/*
+================
+G_UpdatePowerGrid
+
+Recalculate the entire power grid
+================
+*/
+void G_UpdatePowerGrid( float dt )
+{
+ int i;
+ gentity_t *ent;
+
+ // reset all ckits
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ g_entities[ i ].powerNetwork = 0;
+
+ // reset all power entities
+ for( i = MAX_CLIENTS; i < level.num_entities; i++ )
+ {
+ ent = g_entities + i;
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+ if( ent->buildableTeam != TEAM_HUMANS )
+ continue;
+
+ ent->powerNetwork = 0;
+ ent->current = 0.0f;
+
+ if( !ent->spawned )
+ {
+ ent->resistance = PREBUILD_RESISTANCE;
+ continue;
+ }
+
+ if( ent->s.modelindex == BA_H_REACTOR ||
+ ent->s.modelindex == BA_H_RTG )
+ ent->voltage = POWER_VOLTAGE * g_voltageModifier.value;
+
+ if( !ent->requiresPower || ent->isPowerSource )
+ continue;
+
+ if( ent->surge )
+ ent->resistance = BG_Buildable( ent->s.modelindex, NULL )->surgeResistance;
+ else
+ ent->resistance = BG_Buildable( ent->s.modelindex, NULL )->resistance;
+ }
+
+ // this table will be used throughout the following loop and its recursive calls
+ memset( grid.visited, 0, sizeof( grid.visited ) );
+
+ // find an unvisited power source and make a list of all power sources
+ // and receivers reachable from it for G_CalculatePowerGrid
+ for( i = MAX_CLIENTS; i < level.num_entities; i++ )
+ {
+ if( grid.visited[ i ] )
+ continue;
+
+ ent = g_entities + i;
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+ if( !ent->spawned )
+ continue;
+ if( ent->health <= 0 )
+ continue;
+ if( ent->buildableTeam != TEAM_HUMANS )
+ continue;
+ if( !ent->isPowerSource )
+ continue;
+ if( !ent->active )
+ continue;
+
+ // unique network id
+ grid.networkID = ent->s.number;
+ ent->powerNetwork = grid.networkID;
+
+ // traverse the world and find all reachable power entities
+ grid.loadCount = 0;
+ grid.sourceCount = 0;
+
+ // add this source to the list
+ grid.visited[ i ] = qtrue;
+ grid.sources[ grid.sourceCount++ ] = ent;
+
+ // scan recursively
+ G_ScanPowerGrid( ent );
+
+ // calculate the power grid
+ G_CalculatePowerGrid( );
+ }
+
+ // calculate voltages, power levels, etc.
+ for( i = MAX_CLIENTS; i < level.num_entities; i++ )
+ {
+ ent = g_entities + i;
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+ if( ent->buildableTeam != TEAM_HUMANS )
+ continue;
+
+ if( ent->isPowerSource )
+ {
+ if( ent->active && ent->s.modelindex == BA_H_CAPBANK )
+ ent->voltage -= ent->current * dt / CAPBANK_CAPACITY;
+
+ //zapping effect
+ #define MIN_ZAP_CURRENT 2.0f
+ #define ZAP_CHANCE_FACTOR 0.01f
+ if( ent->current > MIN_ZAP_CURRENT )
+ {
+ float chance;
+
+ chance = ( ent->current - MIN_ZAP_CURRENT ) * ZAP_CHANCE_FACTOR;
+ if( chance > 0.2f )
+ chance = 0.2f;
+
+ chance = 1.0f - chance;
+ if( random() > chance )
+ G_AddEvent( ent, EV_POWER_ZAP, 0 );
+ }
+ }
+ else
+ ent->voltage = ent->current * ent->resistance;
+ }
+}
+
+/*
+================
+G_SetupPowerEntity
+
+Called when a Human buildable finishes spawning and needs power grid
+related variables to be set accordingly
+================
+*/
+void G_SetupPowerEntity( gentity_t *built )
+{
+ built->requiresPower = BG_Buildable( built->s.modelindex, NULL )->requiresPower;
+ built->isPowerSource = BG_Buildable( built->s.modelindex, NULL )->isPowerSource;
+ built->resistance = BG_Buildable( built->s.modelindex, NULL )->resistance;
+
+ if( built->isPowerSource )
+ {
+ switch( built->s.modelindex )
+ {
+ case BA_H_REACTOR:
+ built->resistance = REACTOR_RESISTANCE;
+ break;
+ case BA_H_CAPBANK:
+ built->voltage = 0.0f; //spawn discharged
+ built->resistance = CAPBANK_RESISTANCE;
+ case BA_H_RTG:
+ built->resistance = RTG_RESISTANCE;
+ break;
+ }
+ }
+}
+
+/*
+================
+G_PowerForPoint
+
+Returns to which network ID this point belongs
+================
+*/
+
+int G_PowerForPoint( vec3_t point )
+{
+ int i;
+ int list[ MAX_GENTITIES ], count;
+ gentity_t *ent;
+ vec3_t mins, maxs;
+ float range;
+/*
+ switch( this->s.modelindex )
+ {
+
+ }*/
+ range = REACTOR_BASESIZE;
+
+ for( i = 0; i < 3; i++ )
+ {
+ mins[ i ] = point[ i ] - range;
+ maxs[ i ] = point[ i ] + range;
+ }
+
+ count = trap_EntitiesInBox( mins, maxs, list, MAX_GENTITIES );
+
+ for( i = 0; i < count; i++ )
+ {
+ ent = g_entities + list[ i ];
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+ if( ent->buildableTeam != TEAM_HUMANS )
+ continue;
+ if( ent->health <= 0 )
+ continue;
+ if( !ent->spawned )
+ continue;
+ if( !ent->powerNetwork )
+ continue;
+
+ switch( ent->s.modelindex )
+ {
+ case BA_H_REACTOR:
+ case BA_H_CAPBANK:
+ case BA_H_RTG:
+ range = REACTOR_BASESIZE;
+ break;
+ case BA_H_REPEATER:
+ range = REPEATER_BASESIZE;
+ break;
+ default:
+ continue;
+ }
+
+ if( Distance( ent->s.origin, point ) > range )
+ continue;
+
+ return ent->powerNetwork;
+ }
+ return 0;
+}
+
+/*
+================
+NOTES TO G_CheckPower AND G_Surge
+
+Make sure that a buildable's resistance NEVER depends on its power
+state. Failure to ensure that will result in feedback loops in the
+power grid and general weirdness.
+================
+*/
+
+/*
+================
+G_CheckPower
+
+(Helper for Human buildable think functions)
+Checks if there's enough power for the buildable to idle
+================
+*/
+qboolean G_CheckPower( gentity_t *self, float min_current )
+{
+ self->surge = qfalse;
+ self->surgePowered = qfalse;
+ self->powered = qtrue;
+
+ if( self->current < min_current )
+ {
+ self->powered = qfalse;
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+================
+G_Surge
+
+(Helper for Human buildable think functions)
+Checks if there's enough power for the buildable to perform its action
+================
+*/
+qboolean G_Surge( gentity_t *self, float surge_current )
+{
+ self->surge = qtrue;
+
+ if( self->current < surge_current )
+ return qfalse;
+
+ self->surgePowered = qtrue;
+ return qtrue;
+}
+
+/*
+================
nullDieFunction
hack to prevent compilers complaining about function pointer -> NULL conversion
@@ -1536,42 +1662,6 @@ void ATrapper_Think( gentity_t *self )
//==================================================================================
-
-
-
-/*
-================
-G_SuicideIfNoPower
-
-Destroy human structures that have been unpowered too long
-================
-*/
-static qboolean G_SuicideIfNoPower( gentity_t *self )
-{
- if( self->buildableTeam != TEAM_HUMANS )
- return qfalse;
-
- if( !self->powered )
- {
- // if the power hasn't reached this buildable for some time, then destroy the buildable
- if( self->count == 0 )
- self->count = level.time;
- else if( ( level.time - self->count ) >= HUMAN_BUILDABLE_INACTIVE_TIME )
- {
- if( self->parentNode )
- G_Damage( self, NULL, g_entities + self->parentNode->killedBy,
- NULL, NULL, self->health, 0, MOD_NOCREEP );
- else
- G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_NOCREEP );
- return qtrue;
- }
- }
- else
- self->count = 0;
-
- return qfalse;
-}
-
/*
================
G_IdlePowerState
@@ -1594,13 +1684,9 @@ static void G_IdlePowerState( gentity_t *self )
}
-
-
//==================================================================================
-
-
/*
================
HSpawn_Disappear
@@ -1666,7 +1752,6 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->die = nullDieFunction;
self->killedBy = attacker - g_entities;
- self->powered = qfalse; //free up power
self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
if( self->spawned )
@@ -1694,36 +1779,30 @@ void HSpawn_Think( gentity_t *self )
{
gentity_t *ent;
- // set parentNode
- self->powered = G_FindPower( self, qfalse );
-
- if( G_SuicideIfNoPower( self ) )
+ if( !self->spawned || self->health <= 0 )
return;
-
- if( self->spawned )
+
+ //only suicide if at rest
+ if( self->s.groundEntityNum )
{
- //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 = G_CheckSpawnPoint( self->s.number, self->s.origin,
- self->s.origin2, BA_H_SPAWN, NULL ) ) != NULL )
+ // If the thing blocking the spawn is a buildable, kill it.
+ // If it's part of the map, kill self.
+ if( ent->s.eType == ET_BUILDABLE )
{
- // If the thing blocking the spawn is a buildable, kill it.
- // If it's part of the map, kill self.
- if( ent->s.eType == ET_BUILDABLE )
- {
- G_Damage( ent, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE );
- G_SetBuildableAnim( self, BANIM_SPAWN1, qtrue );
- }
- else if( ent->s.number == ENTITYNUM_WORLD || ent->s.eType == ET_MOVER )
- {
- G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE );
- return;
- }
-
- if( ent->s.eType == ET_CORPSE )
- G_FreeEntity( ent ); //quietly remove
+ G_Damage( ent, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE );
+ G_SetBuildableAnim( self, BANIM_SPAWN1, qtrue );
+ }
+ else if( ent->s.number == ENTITYNUM_WORLD || ent->s.eType == ET_MOVER )
+ {
+ G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE );
+ return;
}
+
+ if( ent->s.eType == ET_CORPSE )
+ G_FreeEntity( ent ); //quietly remove
}
}
@@ -1735,7 +1814,25 @@ void HSpawn_Think( gentity_t *self )
//==================================================================================
+/*
+================
+HRepeater_Think
+
+Think function for Human Repeater
+================
+*/
+void HRepeater_Think( gentity_t *self )
+{
+ self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
+
+ if( !self->spawned || self->health <= 0 )
+ return;
+
+ self->powered = self->active && ( self->powerNetwork != 0 );
+
+ G_IdlePowerState( self );
+}
/*
@@ -1752,7 +1849,6 @@ static void HRepeater_Die( gentity_t *self, gentity_t *inflictor, gentity_t *att
self->die = nullDieFunction;
self->killedBy = attacker - g_entities;
- self->powered = qfalse; //free up power
self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
if( self->spawned )
@@ -1767,83 +1863,34 @@ static void HRepeater_Die( gentity_t *self, gentity_t *inflictor, gentity_t *att
}
G_LogDestruction( self, attacker, mod );
-
- if( self->usesBuildPointZone )
- {
- buildPointZone_t *zone = &level.buildPointZones[self->buildPointZone];
-
- zone->active = qfalse;
- self->usesBuildPointZone = qfalse;
- }
}
/*
================
-HRepeater_Think
+HSwitchable_Use
-Think for human power repeater
+Use for switchable buildings
================
*/
-void HRepeater_Think( gentity_t *self )
+void HSwitchable_Use( gentity_t *self, gentity_t *other, gentity_t *activator )
{
- int i;
- gentity_t *powerEnt;
- buildPointZone_t *zone;
-
- self->powered = G_FindPower( self, qfalse );
-
- powerEnt = G_InPowerZone( self );
- if( powerEnt != NULL )
- {
- // If the repeater is inside of another power zone then suicide
- // Attribute death to whoever built the reactor if that's a human,
- // which will ensure that it does not queue the BP
- if( powerEnt->builtBy >= 0 )
- G_Damage( self, NULL, g_entities + powerEnt->builtBy, NULL, NULL, self->health, 0, MOD_SUICIDE );
- else
- G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE );
+ if( !self->spawned || self->health <= 0 )
return;
- }
-
- G_IdlePowerState( self );
- // Initialise the zone once the repeater has spawned
- if( self->spawned && ( !self->usesBuildPointZone || !level.buildPointZones[ self->buildPointZone ].active ) )
+ if( !other || !other->client )
+ return;
+
+ // ckits and blasters switch the building
+ if( other->s.weapon == WP_HBUILD ||
+ other->s.weapon == WP_BLASTER )
{
- // See if a free zone exists
- for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ )
- {
- zone = &level.buildPointZones[ i ];
-
- if( !zone->active )
- {
- // Initialise the BP queue with no BP queued
- zone->queuedBuildPoints = 0;
- zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer;
- zone->nextQueueTime = level.time;
- zone->active = qtrue;
-
- self->buildPointZone = zone - level.buildPointZones;
- self->usesBuildPointZone = qtrue;
-
- break;
- }
- }
+ self->active ^= 1;
+ G_AddEvent( self, EV_POWER_SWITCH, 0 );
+ return;
}
- 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 || !self->spawned )
+ // not powered
+ if( !self->powerNetwork )
return;
if( other && other->client )
@@ -1869,8 +1916,16 @@ void HReactor_Think( gentity_t *self )
vec3_t mins, maxs;
int i, num;
gentity_t *enemy, *tent;
+ qboolean fired = qfalse;
+
+ if( !self->spawned || self->health <= 0 )
+ return;
- if( self->dcc )
+ self->powered = self->active;
+
+ G_IdlePowerState( self );
+
+ if( G_IsDCCBuilt( ) )
{
VectorAdd( self->s.origin, dccrange, maxs );
VectorSubtract( self->s.origin, dccrange, mins );
@@ -1881,54 +1936,62 @@ void HReactor_Think( gentity_t *self )
VectorSubtract( self->s.origin, range, mins );
}
- if( self->spawned && ( self->health > 0 ) )
+ // Creates a tesla trail for every target
+ num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ for( i = 0; i < num; i++ )
{
- qboolean fired = qfalse;
-
- // Creates a tesla trail for every target
- 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_TEAM ] != TEAM_ALIENS )
- continue;
- if( enemy->flags & FL_NOTARGET )
- continue;
+ enemy = &g_entities[ entityList[ i ] ];
+ if( !enemy->client ||
+ enemy->client->ps.stats[ STAT_TEAM ] != TEAM_ALIENS )
+ continue;
+ if( enemy->flags & FL_NOTARGET )
+ continue;
- tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL );
- tent->s.generic1 = self->s.number; //src
- tent->s.clientNum = enemy->s.number; //dest
- VectorCopy( self->s.pos.trBase, tent->s.origin2 );
- fired = qtrue;
- }
+ tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL );
+ tent->s.generic1 = self->s.number; //src
+ tent->s.clientNum = enemy->s.number; //dest
+ VectorCopy( self->s.pos.trBase, tent->s.origin2 );
+ fired = qtrue;
+ }
- // Actual damage is done by radius
- if( fired )
- {
- self->timestamp = level.time;
- if( self->dcc )
- G_SelectiveRadiusDamage( self->s.pos.trBase, self,
- REACTOR_ATTACK_DCC_DAMAGE,
- REACTOR_ATTACK_DCC_RANGE, self,
- MOD_REACTOR, TEAM_HUMANS );
- else
- G_SelectiveRadiusDamage( self->s.pos.trBase, self,
- REACTOR_ATTACK_DAMAGE,
- REACTOR_ATTACK_RANGE, self,
- MOD_REACTOR, TEAM_HUMANS );
- }
+ // Actual damage is done by radius
+ if( fired )
+ {
+ self->timestamp = level.time;
+ if( G_IsDCCBuilt( ) )
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self,
+ REACTOR_ATTACK_DCC_DAMAGE,
+ REACTOR_ATTACK_DCC_RANGE, self,
+ MOD_REACTOR, TEAM_HUMANS );
+ else
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self,
+ REACTOR_ATTACK_DAMAGE,
+ REACTOR_ATTACK_RANGE, self,
+ MOD_REACTOR, TEAM_HUMANS );
}
- if( self->dcc )
- self->nextthink = level.time + REACTOR_ATTACK_DCC_REPEAT;
- else
- self->nextthink = level.time + REACTOR_ATTACK_REPEAT;
+ if( G_IsDCCBuilt( ) )
+ self->nextthink -= REACTOR_ATTACK_DCC_REPEAT - REACTOR_ATTACK_REPEAT;
}
//==================================================================================
+/*
+================
+HArmoury_Think
+*
+Think function for Human Armoury
+================
+*/
+void HArmoury_Think( gentity_t *self )
+{
+ self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
+
+ if( !self->spawned )
+ return;
+ G_CheckPower( self, ARMOURY_CURRENT );
+}
/*
================
@@ -1953,57 +2016,9 @@ void HArmoury_Activate( gentity_t *self, gentity_t *other, gentity_t *activator
}
}
-/*
-================
-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 = G_FindPower( self, qfalse );
-
- G_SuicideIfNoPower( 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 = G_FindPower( self, qfalse );
-
- G_SuicideIfNoPower( self );
-}
-
-
-
-
//==================================================================================
-
-
/*
================
HMedistat_Die
@@ -2037,10 +2052,12 @@ void HMedistat_Think( gentity_t *self )
qboolean occupied = qfalse;
self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
-
- self->powered = G_FindPower( self, qfalse );
- if( G_SuicideIfNoPower( self ) )
+
+ if( !self->spawned || self->health <= 0 )
return;
+
+ G_CheckPower( self, MEDISTAT_I_IDLE );
+
G_IdlePowerState( self );
//clear target's healing flag
@@ -2055,105 +2072,105 @@ void HMedistat_Think( gentity_t *self )
self->active = qfalse;
self->enemy = NULL;
}
-
- 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++ )
{
- VectorAdd( self->s.origin, self->r.maxs, maxs );
- VectorAdd( self->s.origin, self->r.mins, mins );
+ player = &g_entities[ entityList[ i ] ];
- mins[ 2 ] += fabs( self->r.mins[ 2 ] ) + self->r.maxs[ 2 ];
- maxs[ 2 ] += 60; //player height
+ if( player->flags & FL_NOTARGET )
+ continue; // notarget cancels even beneficial effects?
+
+ //remove poison from everyone, not just the healed player
+ if( player->client && player->client->ps.stats[ STAT_STATE ] & SS_POISONED )
+ player->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
+
+ if( self->enemy == player && player->client &&
+ ( ( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) &&
+ player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
+ PM_Live( player->client->ps.pm_type ) ) )
+ {
+ if( !G_Surge( self, MEDISTAT_I_ACTIVE ) )
+ return;
- //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 );
+ occupied = qtrue;
+ player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE;
+ }
+ }
+
+ if( !occupied )
+ {
+ self->enemy = NULL;
+
+ //look for something to heal
for( i = 0; i < num; i++ )
{
player = &g_entities[ entityList[ i ] ];
if( player->flags & FL_NOTARGET )
continue; // notarget cancels even beneficial effects?
-
- //remove poison from everyone, not just the healed player
- if( player->client && player->client->ps.stats[ STAT_STATE ] & SS_POISONED )
- player->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
-
- if( self->enemy == player && player->client &&
- ( (player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) && // DIFF NOTE: remove this change from diffs ASAP
- player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
- PM_Live( player->client->ps.pm_type ) ) )
- {
- occupied = qtrue;
- player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE;
- }
- }
-
- if( !occupied )
- {
- self->enemy = NULL;
- //look for something to heal
- for( i = 0; i < num; i++ )
+ if( player->client && player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
- player = &g_entities[ entityList[ i ] ];
-
- if( player->flags & FL_NOTARGET )
- continue; // notarget cancels even beneficial effects?
-
- if( player->client && player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ if( ( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] ||
+ player->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) &&
+ PM_Live( player->client->ps.pm_type ) )
{
- if( ( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] ||
- player->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) &&
- PM_Live( player->client->ps.pm_type ) )
+ if( !G_Surge( self, MEDISTAT_I_ACTIVE ) )
+ return;
+
+ self->enemy = player;
+
+ //start the heal anim
+ if( !self->active && self->surge && self->surgePowered )
{
- self->enemy = player;
-
- //start the heal anim
- if( !self->active )
- {
- G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
- self->active = qtrue;
- player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE;
- }
+ G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
+ self->active = qtrue;
+ player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE;
}
- else if( !BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) )
- BG_AddUpgradeToInventory( UP_MEDKIT, player->client->ps.stats );
}
+ 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 );
+ //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 && self->enemy->client ) //heal!
- {
- if( self->enemy->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX )
- self->enemy->client->ps.stats[ STAT_STAMINA ] += STAMINA_MEDISTAT_RESTORE;
+ self->active = qfalse;
+ }
+ else if( self->enemy && self->enemy->client ) //heal!
+ {
+ if( self->enemy->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX )
+ self->enemy->client->ps.stats[ STAT_STAMINA ] += STAMINA_MEDISTAT_RESTORE;
- if( self->enemy->client->ps.stats[ STAT_STAMINA ] > STAMINA_MAX )
- self->enemy->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
+ if( self->enemy->client->ps.stats[ STAT_STAMINA ] > STAMINA_MAX )
+ self->enemy->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
- self->enemy->health++;
+ self->enemy->health++;
- //if they're completely healed, give them a medkit
- if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] )
- {
- self->enemy->health = self->enemy->client->ps.stats[ STAT_MAX_HEALTH ];
- if( !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) )
- BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats );
- }
+ //if they're completely healed, give them a medkit
+ if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] )
+ {
+ self->enemy->health = self->enemy->client->ps.stats[ STAT_MAX_HEALTH ];
+ if( !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) )
+ BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats );
}
}
}
@@ -2179,8 +2196,8 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target,
trace_t tr;
vec3_t dir, end;
- if( !target || target->health <= 0 || !target->client ||
- target->client->pers.teamSelection != TEAM_ALIENS )
+ if( !target || target->health <= 0 || !target->client /*||
+ target->client->pers.teamSelection != TEAM_ALIENS */) //debug: target humans too for now
return qfalse;
if( target->flags & FL_NOTARGET )
@@ -2368,6 +2385,7 @@ HMGTurret_Think
Think function for MG turret
================
*/
+
void HMGTurret_Think( gentity_t *self )
{
self->nextthink = level.time +
@@ -2375,10 +2393,12 @@ void HMGTurret_Think( gentity_t *self )
// Turn off client side muzzle flashes
self->s.eFlags &= ~EF_FIRING;
-
- self->powered = G_FindPower( self, qfalse );
- if( G_SuicideIfNoPower( self ) )
+
+ if( !self->spawned || self->health <= 0 )
return;
+
+ G_CheckPower( self, MGTURRET_I_IDLE );
+
G_IdlePowerState( self );
// If not powered or spawned don't do anything
@@ -2386,10 +2406,9 @@ void HMGTurret_Think( gentity_t *self )
{
// if power loss drop turret
if( self->spawned &&
- HMGTurret_State( self, MGT_STATE_INACTIVE ) )
+ HMGTurret_State( self, MGT_STATE_INACTIVE ) );
return;
- self->nextthink = level.time + POWER_REFRESH_TIME;
return;
}
if( !self->spawned )
@@ -2428,6 +2447,9 @@ void HMGTurret_Think( gentity_t *self )
if( !self->active || self->turretSpinupTime > level.time )
return;
+ if( !G_Surge( self, MGTURRET_I_ACTIVE ) )
+ return;
+
// Fire repeat delay
if( self->timestamp > level.time )
return;
@@ -2456,62 +2478,174 @@ Think function for Tesla Generator
*/
void HTeslaGen_Think( gentity_t *self )
{
+ vec3_t origin, range, mins, maxs;
+ int entityList[ MAX_GENTITIES ], i, num;
+
self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
- self->powered = G_FindPower( self, qfalse );
- if( G_SuicideIfNoPower( self ) )
+ if( !self->spawned || self->health <= 0 )
return;
+
+ G_CheckPower( self, TESLAGEN_I_IDLE );
+
G_IdlePowerState( self );
//if not powered don't do anything and check again for power next think
if( !self->powered )
{
self->s.eFlags &= ~EF_FIRING;
- self->nextthink = level.time + POWER_REFRESH_TIME;
return;
}
- if( self->spawned && self->timestamp < level.time )
- {
- vec3_t origin, range, mins, maxs;
- int entityList[ MAX_GENTITIES ], i, num;
+ if( !self->spawned )
+ return;
- // Communicates firing state to client
- self->s.eFlags &= ~EF_FIRING;
+ // Communicates firing state to client
+ self->s.eFlags &= ~EF_FIRING;
- // Move the muzzle from the entity origin up a bit to fire over turrets
- VectorMA( self->s.origin, self->r.maxs[ 2 ], self->s.origin2, origin );
+ // Move the muzzle from the entity origin up a bit to fire over turrets
+ VectorMA( self->s.origin, self->r.maxs[ 2 ], self->s.origin2, origin );
- VectorSet( range, TESLAGEN_RANGE, TESLAGEN_RANGE, TESLAGEN_RANGE );
- VectorAdd( origin, range, maxs );
- VectorSubtract( origin, range, mins );
+ VectorSet( range, TESLAGEN_RANGE, TESLAGEN_RANGE, TESLAGEN_RANGE );
+ VectorAdd( origin, range, maxs );
+ VectorSubtract( origin, range, mins );
- // Attack nearby Aliens
- num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- for( i = 0; i < num; i++ )
- {
- self->enemy = &g_entities[ entityList[ i ] ];
+ // Attack nearby Aliens
+ num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ for( i = 0; i < num; i++ )
+ {
+ self->enemy = &g_entities[ entityList[ i ] ];
- if( self->enemy->flags & FL_NOTARGET )
- continue;
+ if( self->enemy->flags & FL_NOTARGET )
+ continue;
+
+ if( self->enemy->client && self->enemy->health > 0 &&
+ /*self->enemy->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS &&*/
+ Distance( origin, self->enemy->s.pos.trBase ) <= TESLAGEN_RANGE )
+ {
+ if( !G_Surge( self, TESLAGEN_I_ACTIVE ) )
+ break;
- if( self->enemy->client && self->enemy->health > 0 &&
- self->enemy->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS &&
- Distance( origin, self->enemy->s.pos.trBase ) <= TESLAGEN_RANGE )
- FireWeapon( self );
+ FireWeapon( self );
}
- self->enemy = NULL;
+ }
+ self->enemy = NULL;
- if( self->s.eFlags & EF_FIRING )
- {
- G_AddEvent( self, EV_FIRE_WEAPON, 0 );
+ 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 );
+ //doesn't really need an anim
+ //G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
- self->timestamp = level.time + TESLAGEN_REPEAT;
- }
+ self->timestamp = level.time + TESLAGEN_REPEAT;
+ }
+}
+
+//==================================================================================
+
+void HDCC_Think( gentity_t *self )
+{
+ int i, count, list[ MAX_GENTITIES ];
+ gentity_t *ent;
+ vec3_t mins, maxs;
+
+ self->nextthink = level.time + DC_HEALRATE;
+
+ if( !self->spawned || self->health <= 0 )
+ return;
+
+ G_CheckPower( self, DC_I_IDLE );
+ G_IdlePowerState( self );
+ if( !self->powered )
+ return;
+
+ for( i = 0; i < 3; i++ )
+ mins[ i ] = self->s.origin[ i ] - DC_RANGE,
+ maxs[ i ] = self->s.origin[ i ] + DC_RANGE;
+
+ count = trap_EntitiesInBox( mins, maxs, list, MAX_GENTITIES );
+
+ for( i = 0; i < count; i++ )
+ {
+ ent = g_entities + list[ i ];
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+ if( ent->buildableTeam != TEAM_HUMANS )
+ continue;
+ if( ent->health >= BG_Buildable( ent->s.modelindex, ent->cuboidSize )->health )
+ continue;
+ if( ent->lastDamageTime + HUMAN_REGEN_DAMAGE_TIME >= level.time )
+ continue;
+ if( BG_Buildable( ent->s.modelindex, NULL )->cuboid &&
+ !BG_CuboidAttributes( ent->s.modelindex )->repairable )
+ continue;
+
+ if( !G_Surge( self, DC_I_ACTIVE ) )
+ return;
+
+ ent->health++;
+ }
+}
+
+//==================================================================================
+
+void HCapbank_Think( gentity_t *self)
+{
+ //nothing to do here
+}
+
+//==================================================================================
+
+void HRTG_Think( gentity_t *self)
+{
+ self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
+
+ if( !self->spawned || self->health <= 0 )
+ return;
+
+ if( G_TimeTilSuddenDeath( ) <= 0 )
+ {
+ self->storedBP = 0;
+ return;
}
+
+ self->storedBP += RTG_YIELD * g_massYieldModifier.value;
+
+ if( self->storedBP > RTG_STORAGE )
+ self->storedBP = RTG_STORAGE;
+}
+
+//==================================================================================
+
+void HRefinery_Think( gentity_t *self)
+{
+ self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink;
+
+ if( !self->spawned || self->health <= 0 )
+ return;
+
+ G_CheckPower( self, REFINERY_I_IDLE );
+ G_IdlePowerState( self );
+
+ if( G_TimeTilSuddenDeath( ) <= 0 )
+ {
+ self->storedBP = 0;
+ return;
+ }
+
+ //nothing to do
+ if( self->storedBP >= REFINERY_STORAGE - 0.001f )
+ return;
+
+ if( !G_Surge( self, REFINERY_I_ACTIVE ) )
+ return;
+
+ self->storedBP += REFINERY_YIELD * g_massYieldModifier.value;
+
+ if( self->storedBP > REFINERY_STORAGE )
+ self->storedBP = REFINERY_STORAGE;
}
//==================================================================================
@@ -2534,7 +2668,6 @@ void Cuboid_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
G_SetBuildableAnim( self, BANIM_DESTROY1, qtrue ); // just for sound
self->die = nullDieFunction;
self->killedBy = attacker - g_entities;
- self->powered = qfalse;
self->s.eFlags &= ~EF_FIRING;
G_LogDestruction( self, attacker, mod );
dir[ 0 ] =
@@ -2632,48 +2765,6 @@ void G_QueueBuildPoints( gentity_t *self )
level.alienBuildPointQueue += queuePoints;
break;
-
- case TEAM_HUMANS:
- powerEntity = G_PowerEntityForEntity( self );
-
- if( powerEntity )
- {
- int nqt;
- switch( powerEntity->s.modelindex )
- {
- case BA_H_REACTOR:
- nqt = G_NextQueueTime( level.humanBuildPointQueue,
- g_humanBuildPoints.integer,
- g_humanBuildQueueTime.integer );
- if( !level.humanBuildPointQueue ||
- level.time + nqt < level.humanNextQueueTime )
- level.humanNextQueueTime = level.time + nqt;
-
- level.humanBuildPointQueue += queuePoints;
- break;
-
- case BA_H_REPEATER:
- if( powerEntity->usesBuildPointZone &&
- level.buildPointZones[ powerEntity->buildPointZone ].active )
- {
- buildPointZone_t *zone = &level.buildPointZones[ powerEntity->buildPointZone ];
-
- nqt = G_NextQueueTime( zone->queuedBuildPoints,
- zone->totalBuildPoints,
- g_humanRepeaterBuildQueueTime.integer );
-
- if( !zone->queuedBuildPoints ||
- level.time + nqt < zone->nextQueueTime )
- zone->nextQueueTime = level.time + nqt;
-
- zone->queuedBuildPoints += queuePoints;
- }
- break;
-
- default:
- break;
- }
- }
}
}
@@ -2774,6 +2865,7 @@ void G_BuildableThink( gentity_t *ent, int msec )
{
G_TeamCommand( TEAM_ALIENS, "cp \"The Overmind has awakened!\"" );
}
+ G_SetupPowerEntity( ent );
}
// Timer actions
@@ -2784,9 +2876,18 @@ void G_BuildableThink( gentity_t *ent, int msec )
if( !ent->spawned )
{
- buildRate=(int)(ceil((float)maxHealth/(float)buildTime*1e3f));
- ent->health=MIN(ent->health+buildRate,maxHealth);
- ent->healthLeft=MAX(ent->healthLeft-buildRate,0);
+ buildRate = (int)( ceil( (float)maxHealth / (float)buildTime * 1.0e3f ) );
+
+ if( ent->buildableTeam == TEAM_HUMANS &&
+ ent->s.modelindex != BA_H_RTG )
+ {
+ buildRate *= ent->current / PREBUILD_CURRENT;
+ if( buildRate < 0 )
+ buildRate = 0;
+ ent->powered = ( buildRate > 0 );
+ }
+ ent->health = MIN( ent->health + buildRate, maxHealth );
+ ent->healthLeft = MAX( ent->healthLeft - buildRate, 0 );
}
else if( ent->health > 0 && ent->health < maxHealth )
{
@@ -2795,13 +2896,6 @@ void G_BuildableThink( gentity_t *ent, int msec )
{
ent->health += regenRate;
}
- else if( ent->buildableTeam == TEAM_HUMANS && ent->dcc &&
- ( ent->lastDamageTime + HUMAN_REGEN_DAMAGE_TIME ) < level.time &&
- ( !BG_Buildable( ent->s.modelindex, NULL )->cuboid ||
- BG_CuboidAttributes( ent->s.modelindex )->repairable ) )
- {
- ent->health += DC_HEALRATE * ent->dcc;
- }
}
if( ent->health >= maxHealth )
@@ -2819,8 +2913,6 @@ void G_BuildableThink( gentity_t *ent, int msec )
if( ent->clientSpawnTime < 0 )
ent->clientSpawnTime = 0;
- ent->dcc = ( ent->buildableTeam != TEAM_HUMANS ) ? 0 : G_FindDCC( ent );
-
// Set health
ent->s.generic1 = MIN( MAX( ent->health, 0 ), 999 );
@@ -2828,16 +2920,47 @@ void G_BuildableThink( gentity_t *ent, int msec )
BG_CuboidPackHealthSafe( ent->s.modelindex, &ent->s, ent->health );
// Set flags
- ent->s.eFlags &= ~( EF_B_POWERED | EF_B_SPAWNED | EF_B_MARKED );
- if( ent->powered )
- ent->s.eFlags |= EF_B_POWERED;
+ ent->s.eFlags &= ~( EF_B_POWERED | EF_B_SPAWNED | EF_B_SURGE );
+
+ if( ent->buildableTeam == TEAM_HUMANS )
+ {
+ if( ent->s.modelindex == BA_H_REPEATER )
+ {
+ if( ent->powered )
+ ent->s.eFlags |= EF_B_POWERED;
+ if( ent->active )
+ ent->s.eFlags |= EF_B_SURGE;
+ }
+ else if( ent->isPowerSource )
+ {
+ ent->s.eFlags |= EF_B_POWERED;
+ if( ent->active )
+ ent->s.eFlags |= EF_B_SURGE;
+ }
+ else
+ {
+ if( ent->powered )
+ {
+ if( ent->surge )
+ {
+ ent->s.eFlags |= EF_B_SURGE;
+ if( ent->surgePowered )
+ ent->s.eFlags |= EF_B_POWERED;
+ }
+ else
+ ent->s.eFlags |= EF_B_POWERED;
+ }
+ }
+ }
+ else
+ {
+ if( ent->powered )
+ ent->s.eFlags |= EF_B_POWERED;
+ }
if( ent->spawned )
ent->s.eFlags |= EF_B_SPAWNED;
- if( ent->deconstruct )
- ent->s.eFlags |= EF_B_MARKED;
-
// Check if this buildable is touching any triggers
G_BuildableTouchTriggers( ent );
@@ -2940,450 +3063,61 @@ static qboolean G_BuildablesIntersect( buildable_t a, vec3_t originA, vec3_t cub
/*
===============
-G_CompareBuildablesForRemoval
-
-qsort comparison function for a buildable removal list
-===============
-*/
-static buildable_t cmpBuildable;
-static vec3_t cmpOrigin;
-static vec3_t cmpCuboid;
-static int G_CompareBuildablesForRemoval( const void *a, const void *b )
-{
- int precedence[ ] =
- {
- BA_NONE,
-
- BA_A_BARRICADE,
- BA_A_ACIDTUBE,
- BA_A_TRAPPER,
- BA_A_HIVE,
- BA_A_BOOSTER,
- BA_A_SPAWN,
- BA_A_CUBOID1,
- BA_A_OVERMIND,
-
- BA_H_MGTURRET,
- BA_H_TESLAGEN,
- BA_H_DCC,
- BA_H_MEDISTAT,
- BA_H_ARMOURY,
- BA_H_SPAWN,
- BA_H_REPEATER,
- BA_H_CUBOID1,
- BA_H_CUBOID2,
- BA_H_REACTOR
- };
-
- gentity_t *buildableA, *buildableB;
- int i;
- int aPrecedence = 0, bPrecedence = 0;
- qboolean aMatches = qfalse, bMatches = qfalse;
-
- buildableA = *(gentity_t **)a;
- buildableB = *(gentity_t **)b;
-
- // Prefer the one that collides with the thing we're building
- aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, cmpCuboid,
- buildableA->s.modelindex, buildableA->s.origin, buildableA->cuboidSize );
- bMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, cmpCuboid,
- buildableB->s.modelindex, buildableB->s.origin, buildableB->cuboidSize );
- if( aMatches && !bMatches )
- return -1;
- else if( !aMatches && bMatches )
- return 1;
-
- // If the only spawn is marked, prefer it last
- if( cmpBuildable == BA_A_SPAWN || cmpBuildable == BA_H_SPAWN )
- {
- if( ( buildableA->s.modelindex == BA_A_SPAWN && level.numAlienSpawns == 1 ) ||
- ( buildableA->s.modelindex == BA_H_SPAWN && level.numHumanSpawns == 1 ) )
- return 1;
-
- if( ( buildableB->s.modelindex == BA_A_SPAWN && level.numAlienSpawns == 1 ) ||
- ( buildableB->s.modelindex == BA_H_SPAWN && level.numHumanSpawns == 1 ) )
- return -1;
- }
-
- // If one matches the thing we're building, prefer it
- aMatches = ( buildableA->s.modelindex == cmpBuildable );
- bMatches = ( buildableB->s.modelindex == cmpBuildable );
- if( aMatches && !bMatches )
- return -1;
- else if( !aMatches && bMatches )
- return 1;
-
- // They're the same type
- if( buildableA->s.modelindex == buildableB->s.modelindex )
- {
- gentity_t *powerEntity = G_PowerEntityForPoint( cmpOrigin );
-
- // Prefer the entity that is providing power for this point
- aMatches = ( powerEntity == buildableA );
- bMatches = ( powerEntity == buildableB );
- if( aMatches && !bMatches )
- return -1;
- else if( !aMatches && bMatches )
- return 1;
-
- // Pick the one marked earliest
- return buildableA->deconstructTime - buildableB->deconstructTime;
- }
-
- // Resort to preference list
- for( i = 0; i < sizeof( precedence ) / sizeof( precedence[ 0 ] ); i++ )
- {
- if( buildableA->s.modelindex == precedence[ i ] )
- aPrecedence = i;
-
- if( buildableB->s.modelindex == precedence[ i ] )
- bPrecedence = i;
- }
-
- return aPrecedence - bPrecedence;
-}
-
-/*
-===============
-G_ClearDeconMarks
-
-Remove decon mark from all buildables
-===============
-*/
-void G_ClearDeconMarks( void )
-{
- int i;
- gentity_t *ent;
-
- for( i = MAX_CLIENTS, ent = g_entities + i ; i < level.num_entities ; i++, ent++ )
- {
- if( !ent->inuse )
- continue;
-
- if( ent->s.eType != ET_BUILDABLE )
- continue;
-
- ent->deconstruct = qfalse;
- }
-}
-
-/*
-===============
-G_FreeMarkedBuildables
-
-Free up build points for a team by deconstructing marked buildables
-===============
-*/
-void G_FreeMarkedBuildables( gentity_t *deconner, char *readable, int rsize,
- char *nums, int nsize )
-{
- int i;
- int bNum;
- int listItems = 0;
- int totalListItems = 0;
- gentity_t *ent;
- int removalCounts[ BA_NUM_BUILDABLES ] = {0};
-
- if( readable && rsize )
- readable[ 0 ] = '\0';
- if( nums && nsize )
- nums[ 0 ] = '\0';
-
- if( !g_markDeconstruct.integer )
- return; // Not enabled, can't deconstruct anything
-
- for( i = 0; i < level.numBuildablesForRemoval; i++ )
- {
- ent = level.markedBuildables[ i ];
- bNum = BG_Buildable( ent->s.modelindex, NULL )->number;
-
- if( removalCounts[ bNum ] == 0 )
- totalListItems++;
-
- G_Damage( ent, NULL, deconner, NULL, NULL, ent->health, 0, MOD_REPLACE );
-
- removalCounts[ bNum ]++;
-
- if( nums )
- Q_strcat( nums, nsize, va( " %d", ent - g_entities ) );
-
- G_FreeEntity( ent );
- }
-
- if( !readable )
- return;
-
- for( i = 0; i < BA_NUM_BUILDABLES; i++ )
- {
- if( removalCounts[ i ] )
- {
- if( listItems )
- {
- if( listItems == ( totalListItems - 1 ) )
- Q_strcat( readable, rsize, va( "%s and ",
- ( totalListItems > 2 ) ? "," : "" ) );
- else
- Q_strcat( readable, rsize, ", " );
- }
- Q_strcat( readable, rsize, va( "%s", BG_Buildable( i, NULL )->humanName ) );
- if( removalCounts[ i ] > 1 )
- Q_strcat( readable, rsize, va( " (%dx)", removalCounts[ i ] ) );
- listItems++;
- }
- }
-}
-
-/*
-===============
G_SufficientBPAvailable
Determine if enough build points can be released for the buildable
and list the buildables that must be destroyed if this is the case
===============
*/
-static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable,
+static itemBuildError_t G_SufficientBPAvailable( gclient_t *builder,
+ buildable_t buildable,
vec3_t origin,
- vec3_t cuboidSize )
+ vec3_t cuboidSize )
{
- int i;
- int numBuildables = 0;
- int numRequired = 0;
- int pointsYielded = 0;
- gentity_t *ent;
- team_t team = BG_Buildable( buildable, NULL )->team;
- int buildPoints = BG_Buildable( buildable, cuboidSize )->buildPoints;
- int remainingBP, remainingSpawns;
- qboolean collision = qfalse;
- int collisionCount = 0;
- qboolean repeaterInRange = qfalse;
- int repeaterInRangeCount = 0;
- itemBuildError_t bpError;
- buildable_t spawn;
- buildable_t core;
- int spawnCount = 0;
- qboolean changed = qtrue;
-
- level.numBuildablesForRemoval = 0;
-
- if( team == TEAM_ALIENS )
- {
- remainingBP = G_GetBuildPoints( origin, team );
- remainingSpawns = level.numAlienSpawns;
- bpError = IBE_NOALIENBP;
- spawn = BA_A_SPAWN;
- core = BA_A_OVERMIND;
- }
- else if( team == TEAM_HUMANS )
- {
- if( buildable == BA_H_REACTOR || buildable == BA_H_REPEATER )
- remainingBP = level.humanBuildPoints;
- else
- remainingBP = G_GetBuildPoints( origin, team );
-
- remainingSpawns = level.numHumanSpawns;
- bpError = IBE_NOHUMANBP;
- spawn = BA_H_SPAWN;
- core = BA_H_REACTOR;
- }
- else
- {
- Com_Error( ERR_FATAL, "team is %d\n", team );
- return IBE_NONE;
- }
-
- // Simple non-marking case
- if( !g_markDeconstruct.integer )
+ int required;
+ int available;
+ int error;
+
+ required = BG_Buildable( buildable, cuboidSize )->buildPoints;
+
+ switch( BG_Buildable( buildable, NULL )->team )
{
- if( remainingBP - buildPoints < 0 )
- return bpError;
-
- // Check for buildable<->buildable collisions
- for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
- {
- if( ent->s.eType != ET_BUILDABLE )
- continue;
-
- if( G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize ) )
- return IBE_NOROOM;
-
- }
-
- return IBE_NONE;
+ case TEAM_ALIENS:
+ error = IBE_NOALIENBP;
+ available = G_GetBuildPoints( origin, TEAM_ALIENS );
+ break;
+
+ case TEAM_HUMANS:
+ error = IBE_NOHUMANBP;
+ if( !builder )
+ available = required; //always enough
+ else
+ //first RTG is free
+ if( buildable == BA_H_RTG && !G_IsRTGBuilt() )
+ available = INFINITE;
+ else
+ available = builder->ps.persistant[ PERS_BUILDPOINTS ];
+ break;
}
+
+ if( required > available )
+ return error;
+
+ return IBE_NONE;
- // Set buildPoints to the number extra that are required
- buildPoints -= remainingBP;
- // Build a list of buildable entities
+#if 0
+ // Check for buildable<->buildable collisions
+ // FIXME: is this check even needed?
for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
{
if( ent->s.eType != ET_BUILDABLE )
continue;
- collision = G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize );
-
- if( collision )
- {
- // Don't allow replacements at all
- if( g_markDeconstruct.integer == 1 )
- return IBE_NOROOM;
-
- // Only allow replacements of the same type
- if( g_markDeconstruct.integer == 2 && ent->s.modelindex != buildable )
- return IBE_NOROOM;
-
- // Any other setting means anything goes
-
- collisionCount++;
- }
-
- // Check if this is a repeater and it's in range
- if( buildable == BA_H_REPEATER &&
- buildable == ent->s.modelindex &&
- Distance( ent->s.origin, origin ) < REPEATER_BASESIZE )
- {
- repeaterInRange = qtrue;
- repeaterInRangeCount++;
- }
- else
- repeaterInRange = qfalse;
-
- // Don't allow marked buildables to be replaced in another zone,
- // unless the marked buildable isn't in a zone (and thus unpowered)
- if( team == TEAM_HUMANS &&
- buildable != BA_H_REACTOR &&
- buildable != BA_H_REPEATER &&
- ent->parentNode != G_PowerEntityForPoint( origin ) )
- continue;
-
- if( !ent->inuse )
- continue;
-
- if( ent->health <= 0 )
- continue;
-
- if( ent->buildableTeam != team )
- continue;
-
- // Explicitly disallow replacement of the core buildable with anything
- // other than the core buildable
- if( ent->s.modelindex == core && buildable != core )
- continue;
-
- // Don't allow a power source to be replaced by a dependant
- if( team == TEAM_HUMANS &&
- G_PowerEntityForPoint( origin ) == ent &&
- buildable != BA_H_REPEATER &&
- buildable != core )
- continue;
-
- // Don't include unpowered buildables
- if( !collision && !ent->powered )
- continue;
-
- if( ent->deconstruct )
- {
- level.markedBuildables[ numBuildables++ ] = ent;
-
- // Buildables that are marked here will always end up at the front of the
- // removal list, so just incrementing numBuildablesForRemoval is sufficient
- if( collision || repeaterInRange )
- {
- // Collided with something, so we definitely have to remove it or
- // it's a repeater that intersects the new repeater's power area,
- // so it must be removed
-
- if( collision )
- collisionCount--;
-
- if( repeaterInRange )
- repeaterInRangeCount--;
-
- if( ent->powered )
- pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints;
- level.numBuildablesForRemoval++;
- }
- else if( BG_Buildable( ent->s.modelindex, NULL )->uniqueTest &&
- ent->s.modelindex == buildable )
- {
- // If it's a unique buildable, it must be replaced by the same type
- if( ent->powered )
- pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints;
- level.numBuildablesForRemoval++;
- }
- }
- }
-
- numRequired = level.numBuildablesForRemoval;
-
- // We still need build points, but have no candidates for removal
- if( buildPoints > 0 && numBuildables == 0 )
- return bpError;
-
- // Collided with something we can't remove
- if( collisionCount > 0 )
- return IBE_NOROOM;
-
- // There are one or more repeaters we can't remove
- if( repeaterInRangeCount > 0 )
- return IBE_RPTPOWERHERE;
-
- // Sort the list
- cmpBuildable = buildable;
- VectorCopy( origin, cmpOrigin );
- VectorCopy( cuboidSize, cmpCuboid );
- qsort( level.markedBuildables, numBuildables, sizeof( level.markedBuildables[ 0 ] ),
- G_CompareBuildablesForRemoval );
-
- // Determine if there are enough markees to yield the required BP
- for( ; pointsYielded < buildPoints && level.numBuildablesForRemoval < numBuildables;
- level.numBuildablesForRemoval++ )
- {
- ent = level.markedBuildables[ level.numBuildablesForRemoval ];
- if( ent->powered )
- pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints;
+ if( G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize ) )
+ return IBE_NOROOM;
}
-
- // Do another pass to see if we can meet quota with fewer buildables
- // than we have now due to mismatches between priority and BP amounts
- // by repeatedly testing if we can chop off the first thing that isn't
- // required by rules of collision/uniqueness, which are always at the head
- while( changed && level.numBuildablesForRemoval > 1 &&
- level.numBuildablesForRemoval > numRequired )
- {
- int pointsUnYielded = 0;
- changed = qfalse;
- ent = level.markedBuildables[ numRequired ];
- if( ent->powered )
- pointsUnYielded = BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints;
-
- if( pointsYielded - pointsUnYielded >= buildPoints )
- {
- pointsYielded -= pointsUnYielded;
- memmove( &level.markedBuildables[ numRequired ],
- &level.markedBuildables[ numRequired + 1 ],
- ( level.numBuildablesForRemoval - numRequired )
- * sizeof( gentity_t * ) );
- level.numBuildablesForRemoval--;
- changed = qtrue;
- }
- }
-
- for( i = 0; i < level.numBuildablesForRemoval; i++ )
- {
- if( level.markedBuildables[ i ]->s.modelindex == spawn )
- spawnCount++;
- }
-
- // Make sure we're not removing the last spawn
- if( !g_cheats.integer && remainingSpawns > 0 && ( remainingSpawns - spawnCount ) < 1 )
- return IBE_LASTSPAWN;
-
- // Not enough points yielded
- if( pointsYielded < buildPoints )
- return bpError;
- else
- return IBE_NONE;
+#endif
}
/*
@@ -3410,23 +3144,6 @@ static void G_SetBuildableLinkState( qboolean link )
}
}
-static void G_SetBuildableMarkedLinkState( qboolean link )
-{
- int i;
- gentity_t *ent;
-
- for( i = 0; i < level.numBuildablesForRemoval; i++ )
- {
- ent = level.markedBuildables[ i ];
- if( link )
- trap_LinkEntity( ent );
- else
- trap_UnlinkEntity( ent );
- }
-}
-
-
-
/*
================
G_CanBuild
@@ -3482,7 +3199,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
contents = trap_PointContents( entity_origin, -1 );
- if( ( tempReason = G_SufficientBPAvailable( buildable, origin, cuboidSize ) ) != IBE_NONE )
+ if( ( tempReason = G_SufficientBPAvailable( ent->client, buildable, origin, cuboidSize ) ) != IBE_NONE )
reason = tempReason;
if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
@@ -3510,32 +3227,14 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
else if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
//human criteria
-
- // Check for power
- if( G_IsPowered( entity_origin ) == BA_NONE )
- {
- //tell player to build a repeater to provide power
- if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER )
- reason = IBE_NOPOWERHERE;
- }
+ if( !G_PowerForPoint( origin ) &&
+ buildable != BA_H_RTG )
+ reason = IBE_NOPOWERHERE;
//this buildable requires a DCC
if( BG_Buildable( buildable, NULL )->dccTest && !G_IsDCCBuilt( ) )
reason = IBE_NODCC;
- //check that there is a parent reactor when building a repeater
- if( buildable == BA_H_REPEATER )
- {
- tempent = G_Reactor( );
-
- if( tempent == NULL ) // No reactor
- 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;
- }
-
// Check permission to build here
if( tr1.surfaceFlags & SURF_NOHUMANBUILD || contents & CONTENTS_NOHUMANBUILD )
reason = IBE_PERMISSION;
@@ -3549,7 +3248,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( BG_Buildable( buildable, NULL )->uniqueTest )
{
tempent = G_FindBuildable( buildable );
- if( tempent && !tempent->deconstruct )
+ if( tempent )
{
switch( buildable )
{
@@ -3570,18 +3269,12 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
//check there is enough room to spawn from (presuming this is a spawn)
if( reason == IBE_NONE )
- {
- G_SetBuildableMarkedLinkState( qfalse );
if( G_CheckSpawnPoint( ENTITYNUM_NONE, origin, normal, buildable, NULL ) != NULL )
reason = IBE_NORMAL;
- G_SetBuildableMarkedLinkState( qtrue );
- }
//this item does not fit here
if( reason == IBE_NONE && ( tr2.startsolid || !COMPARE_FLOAT_EPSILON(tr3.fraction,1.0f) ) )
reason = IBE_NOROOM;
- if( reason != IBE_NONE )
- level.numBuildablesForRemoval = 0;
if( g_buildableDensityLimit.integer > 0 )
{
@@ -3661,12 +3354,11 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
{
gentity_t *built;
vec3_t localOrigin;
- char readable[ MAX_STRING_CHARS ];
char buildnums[ MAX_STRING_CHARS ];
buildLog_t *log;
qboolean cuboid;
- cuboid=BG_Buildable(buildable,NULL)->cuboid;
+ cuboid = BG_Buildable(buildable,NULL)->cuboid;
VectorCopy( origin, localOrigin );
@@ -3675,10 +3367,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
else
log = NULL;
- // Free existing buildables
- G_FreeMarkedBuildables( builder, readable, sizeof( readable ),
- buildnums, sizeof( buildnums ) );
-
// Spawn the buildable
built = G_Spawn();
built->s.eType = ET_BUILDABLE;
@@ -3692,13 +3380,13 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
if( !builder->client )
VectorMA( localOrigin, 1.0f, normal, localOrigin );
- if(cuboid)
+ if( cuboid )
{
- BG_CuboidBBox(cuboidSize,built->r.mins,built->r.maxs);
- VectorCopy(cuboidSize,&built->cuboidSize);
+ BG_CuboidBBox( cuboidSize, built->r.mins, built->r.maxs );
+ VectorCopy( cuboidSize, &built->cuboidSize );
}
else
- BG_BuildableBoundingBox(buildable,built->r.mins,built->r.maxs);
+ BG_BuildableBoundingBox( buildable, built->r.mins, built->r.maxs );
built->health = 1;
built->healthLeft = BG_Buildable( buildable, cuboidSize )->health-1;
@@ -3713,8 +3401,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
built->spawned = qfalse;
built->buildTime = built->s.time = level.time;
- // build instantly in cheat mode
- if( builder->client && (g_cheats.integer || g_instantBuild.integer) )
+ if( builder->client && g_instantBuild.integer )
{
built->health = BG_Buildable( buildable, cuboidSize )->health;
built->buildTime = built->s.time =
@@ -3804,16 +3491,34 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
case BA_H_REACTOR:
built->think = HReactor_Think;
built->die = HSpawn_Die;
- built->use = HRepeater_Use;
+ built->use = HSwitchable_Use;
built->powered = built->active = qtrue;
break;
case BA_H_REPEATER:
built->think = HRepeater_Think;
built->die = HRepeater_Die;
- built->use = HRepeater_Use;
+ built->use = HSwitchable_Use;
built->count = -1;
break;
+
+ case BA_H_CAPBANK:
+ built->think = HCapbank_Think;
+ built->die = HSpawn_Die;
+ built->use = HSwitchable_Use;
+ break;
+
+ case BA_H_RTG:
+ built->think = HRTG_Think;
+ built->die = HSpawn_Die;
+ built->use = HSwitchable_Use;
+ break;
+
+ case BA_H_REFINERY:
+ built->think = HRefinery_Think;
+ built->die = HSpawn_Die;
+ //built->use = HSwitchable_Use;
+ break;
default:
//erk
@@ -3839,15 +3544,14 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
G_SetOrigin( built, localOrigin );
- // roughly nudge the buildable onto the surface D:<
VectorScale( normal, -512.0f, built->s.pos.trDelta );
- if(BG_Buildable(buildable, NULL)->cuboid)
- VectorCopy(cuboidSize,built->s.angles);
+ if( BG_Buildable( buildable, NULL )->cuboid )
+ VectorCopy( cuboidSize,built->s.angles );
else
{
- VectorCopy( angles, built->s.angles );
- built->s.angles[ PITCH ] = 0.0f;
+ VectorCopy( angles, built->s.angles );
+ built->s.angles[ PITCH ] = 0.0f;
}
built->s.pos.trType = BG_Buildable( buildable, NULL )->traj;
@@ -3859,13 +3563,8 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
built->s.angles2[ YAW ] = angles[ YAW ];
built->s.angles2[ PITCH ] = MGTURRET_VERTICALCAP;
- if( BG_Buildable( buildable, NULL )->team == TEAM_ALIENS )
- {
- built->powered = qtrue;
- built->s.eFlags |= EF_B_POWERED;
- }
- else if( ( built->powered = G_FindPower( built, qfalse ) ) )
- built->s.eFlags |= EF_B_POWERED;
+ built->powered = qtrue;
+ built->s.eFlags |= EF_B_POWERED;
built->s.eFlags &= ~EF_B_SPAWNED;
@@ -3878,32 +3577,47 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable,
if( built->builtBy >= 0 )
G_SetBuildableAnim( built, BANIM_CONSTRUCT1, qtrue );
+ if( BG_Buildable( buildable, NULL )->team == TEAM_HUMANS )
+ {
+ //special case for the RTG unit that can be built w/o power
+ if( buildable == BA_H_RTG )
+ built->requiresPower = qfalse;
+ else
+ built->requiresPower = qtrue;
+
+ built->isPowerSource = qfalse;
+ built->resistance = PREBUILD_RESISTANCE;
+ if( BG_Buildable( buildable, NULL )->isPowerSource ||
+ buildable == BA_H_REPEATER )
+ built->active = qtrue; //spawn enabled
+ }
+
+ // subtract build points
+ if( buildable != BA_H_RTG || G_IsRTGBuilt( ) ) //first RTG is free
+ if( builder && builder->client )
+ builder->client->ps.persistant[ PERS_BUILDPOINTS ] -= BG_Buildable( buildable, cuboidSize )->buildPoints;
+
+ BG_CuboidPackHealthSafe( built->s.modelindex, &built->s, built->health );
+
trap_LinkEntity( built );
if( builder && builder->client )
{
G_TeamCommand( builder->client->ps.stats[ STAT_TEAM ],
- va( "print \"%s ^2built^7 by %s%s%s\n\"",
- G_CuboidName(built->s.modelindex,cuboidSize,qfalse),
- builder->client->pers.netname,
- ( readable[ 0 ] ) ? "^7, ^3replacing^7 " : "",
- readable ) );
- G_LogPrintf( "Construct: %d %d %s%s: %s" S_COLOR_WHITE " is building "
- "%s%s%s\n",
+ va( "print \"%s ^2built^7 by %s\n\"",
+ G_CuboidName( built->s.modelindex, cuboidSize, qfalse ),
+ builder->client->pers.netname ) );
+ G_LogPrintf( "Construct: %d %d %s%s: %s" S_COLOR_WHITE " is building %s\n",
builder - g_entities,
built - g_entities,
BG_Buildable( built->s.modelindex, NULL )->name,
buildnums,
builder->client->pers.netname,
- G_CuboidName(built->s.modelindex,built->cuboidSize,qtrue),
- readable[ 0 ] ? ", replacing " : "",
- readable );
+ G_CuboidName( built->s.modelindex, built->cuboidSize, qtrue ) );
}
if( log )
G_BuildLogSet( log, built );
-
- BG_CuboidPackHealthSafe(built->s.modelindex,&built->s,built->health);
return built;
}
@@ -4026,6 +3740,8 @@ static gentity_t *G_FinishSpawningBuildable( gentity_t *ent, qboolean force )
built->health = BG_Buildable( buildable, built->cuboidSize )->health;
built->s.eFlags |= EF_B_SPAWNED;
+ G_SetupPowerEntity( built );
+
// drop towards normal surface
VectorScale( built->s.origin2, -4096.0f, dest );
VectorAdd( dest, built->s.origin, dest );
@@ -4407,8 +4123,6 @@ buildLog_t *G_BuildLogNew( gentity_t *actor, buildFate_t fate )
void G_BuildLogSet( buildLog_t *log, gentity_t *ent )
{
log->modelindex = ent->s.modelindex;
- log->deconstruct = log->deconstruct;
- log->deconstructTime = ent->deconstructTime;
VectorCopy( ent->s.pos.trBase, log->origin );
VectorCopy( ent->s.angles, log->angles );
VectorCopy( ent->s.origin2, log->origin2 );
@@ -4464,8 +4178,6 @@ void G_BuildLogRevertThink( gentity_t *ent )
}
built = G_FinishSpawningBuildable( ent, qtrue );
- if( ( built->deconstruct = ent->deconstruct ) )
- built->deconstructTime = ent->deconstructTime;
built->buildTime = built->s.time = 0;
G_KillBox( built );
@@ -4482,8 +4194,6 @@ void G_BuildLogRevert( int id )
int i;
vec3_t dist;
- level.numBuildablesForRemoval = 0;
-
level.numBuildLogs -= level.buildId - id;
while( level.buildId > id )
{
@@ -4521,8 +4231,6 @@ void G_BuildLogRevert( int id )
VectorCopy( log->origin2, builder->s.origin2 );
VectorCopy( log->angles2, builder->s.angles2 );
builder->s.modelindex = log->modelindex;
- builder->deconstruct = log->deconstruct;
- builder->deconstructTime = log->deconstructTime;
builder->think = G_BuildLogRevertThink;
builder->nextthink = level.time + FRAMETIME;
@@ -4539,27 +4247,6 @@ void G_BuildLogRevert( int id )
level.alienBuildPointQueue =
MAX( 0, level.alienBuildPointQueue - value );
}
- else
- {
- if( log->powerSource == BA_H_REACTOR )
- {
- level.humanBuildPointQueue =
- MAX( 0, level.humanBuildPointQueue - value );
- }
- else if( log->powerSource == BA_H_REPEATER )
- {
- gentity_t *source;
- buildPointZone_t *zone;
-
- source = G_PowerEntityForPoint( log->origin );
- if( source && source->usesBuildPointZone )
- {
- zone = &level.buildPointZones[ source->buildPointZone ];
- zone->queuedBuildPoints =
- MAX( 0, zone->queuedBuildPoints - value );
- }
- }
- }
}
}
}
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 7596ea1..a3c7a67 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -1482,6 +1482,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
for( i = 0; i < MAX_PERSISTANT; i++ )
persistant[ i ] = client->ps.persistant[ i ];
+ persistant[ PERS_BUILDPOINTS ] = 0; // clear buildpoints
eventSequence = client->ps.eventSequence;
memset( client, 0, sizeof( *client ) );
@@ -1795,11 +1796,12 @@ void ClientDisconnect( int clientNum )
G_RelayCuboidToSpectators
Called everytime a player changes his cuboid size.
-A server command is issued to everyone spectating him
-so that their clients can know the cuboid size as well.
============
*/
-void G_RelayCuboidToSpectators(gentity_t *self)
+void G_RelayCuboidToSpectators( gentity_t *self )
{
+ self->client->ps.misc[ MISC_CUBOID_X ] = self->client->cuboidSelection[ 0 ] * 10;
+ self->client->ps.misc[ MISC_CUBOID_Y ] = self->client->cuboidSelection[ 1 ] * 10;
+ self->client->ps.misc[ MISC_CUBOID_Z ] = self->client->cuboidSelection[ 2 ] * 10;
}
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 2c15638..c1da4e0 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -1363,7 +1363,7 @@ void Cmd_CallVote_f( gentity_t *ent )
}
else if( !Q_stricmp( vote, "map_restart" ) )
{
- if( level.time / 60000 >= g_restartVoteTimelimit.integer )
+ if( g_restartVoteTimelimit.integer && ( level.time - level.startTime ) / 60000 >= g_restartVoteTimelimit.integer )
{
trap_SendServerCommand( ent-g_entities,
va( "print \"%s: It's not allowed to call a restart vote after %i minute%s.\n\"",
@@ -1377,7 +1377,7 @@ void Cmd_CallVote_f( gentity_t *ent )
}
else if( !Q_stricmp( vote, "map" ) )
{
- if( level.time / 60000 >= g_mapVoteTimelimit.integer )
+ if( g_mapVoteTimelimit.integer && ( level.time - level.startTime ) / 60000 >= g_mapVoteTimelimit.integer )
{
trap_SendServerCommand( ent-g_entities,
va( "print \"%s: It's not allowed to call a map vote after %i minute%s. Call a ^1nextmap^7 vote instead\n\"",
@@ -1434,7 +1434,7 @@ void Cmd_CallVote_f( gentity_t *ent )
}
else if( !Q_stricmp( vote, "sudden_death" ) )
{
- if(!g_suddenDeathVotePercent.integer)
+ if( !g_suddenDeathVotePercent.integer )
{
trap_SendServerCommand( ent-g_entities,
"print \"Sudden Death votes have been disabled\n\"" );
@@ -1918,6 +1918,7 @@ void Cmd_Destroy_f( gentity_t *ent )
char cmd[ 12 ];
qboolean deconstruct = qtrue;
qboolean lastSpawn = qfalse;
+ int bp;
if( ent->client->pers.namelog->denyBuild )
{
@@ -1942,22 +1943,21 @@ void Cmd_Destroy_f( gentity_t *ent )
( ( ent->client->ps.weapon >= WP_ABUILD ) &&
( ent->client->ps.weapon <= WP_HBUILD ) ) )
{
+ //give some BP back: 20% for a dead buildable, 80% for a new one
+ bp = BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildPoints;
+ bp *= MAX( (float)traceEnt->health, 0.0f ) /
+ BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->health * 0.6f + 0.2f;
+
// Always let the builder prevent the explosion
if( traceEnt->health <= 0 )
{
+ ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp;
G_QueueBuildPoints( traceEnt );
G_RewardAttackers( traceEnt );
G_FreeEntity( traceEnt );
return;
}
- // Cancel deconstruction (unmark)
- if( deconstruct && g_markDeconstruct.integer && traceEnt->deconstruct )
- {
- traceEnt->deconstruct = qfalse;
- return;
- }
-
// Prevent destruction of the last spawn
if( ent->client->pers.teamSelection == TEAM_ALIENS &&
traceEnt->s.modelindex == BA_A_SPAWN )
@@ -1972,8 +1972,7 @@ void Cmd_Destroy_f( gentity_t *ent )
lastSpawn = qtrue;
}
- if( lastSpawn && !g_cheats.integer &&
- !g_markDeconstruct.integer )
+ if( lastSpawn && !g_cheats.integer )
{
G_TriggerMenu( ent->client->ps.clientNum, MN_B_LASTSPAWN );
return;
@@ -1986,9 +1985,7 @@ void Cmd_Destroy_f( gentity_t *ent )
return;
}
- if( !g_markDeconstruct.integer ||
- ( ent->client->pers.teamSelection == TEAM_HUMANS &&
- !G_FindPower( traceEnt, qtrue ) ) )
+ if( ent->client->pers.teamSelection == TEAM_HUMANS )
{
if( ent->client->buildTimer )
{
@@ -2004,21 +2001,18 @@ void Cmd_Destroy_f( gentity_t *ent )
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
traceEnt->health, 0, MOD_SUICIDE );
}
- else if( g_markDeconstruct.integer &&
- ( ent->client->pers.teamSelection != TEAM_HUMANS ||
- G_FindPower( traceEnt , qtrue ) || lastSpawn ) )
- {
- traceEnt->deconstruct = qtrue; // Mark buildable for deconstruction
- traceEnt->deconstructTime = level.time;
- }
else
{
if( !g_cheats.integer && !g_instantBuild.integer ) // add a bit to the build timer
{
- ent->client->buildTimer +=
- BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildTime / 4;
- G_RecalcBuildTimer(ent->client);
+ ent->client->buildTimer += BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildTime / 4;
+ G_RecalcBuildTimer(ent->client);
}
+
+ ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp;
+ if( ent->client->ps.persistant[ PERS_BUILDPOINTS ] > CKIT_STORAGE )
+ ent->client->ps.persistant[ PERS_BUILDPOINTS ] = CKIT_STORAGE;
+
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
traceEnt->health, 0, MOD_DECONSTRUCT );
G_FreeEntity( traceEnt );
@@ -2782,6 +2776,69 @@ void Cmd_Reload_f( gentity_t *ent )
playerState_t *ps = &ent->client->ps;
int ammo;
+ // reload transfers mass in case of CKit
+ if( ps->weapon == WP_HBUILD )
+ {
+ trace_t trace;
+ vec3_t eyes, view, point;
+ gentity_t *other;
+
+ #define USE_OBJECT_RANGE 100
+
+ // look for object infront of player
+ AngleVectors( ent->client->ps.viewangles, view, NULL, NULL );
+ BG_GetClientViewOrigin( &ent->client->ps, eyes );
+ VectorMA( eyes, USE_OBJECT_RANGE, view, point );
+
+ trap_Trace( &trace, ent->client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT );
+ other = &g_entities[ trace.entityNum ];
+
+ // transfer FROM buildable
+ if( other->s.eType == ET_BUILDABLE )
+ {
+ if( other->buildableTeam == TEAM_HUMANS &&
+ BG_Buildable( other->s.modelindex, NULL )->hasStorage &&
+ other->spawned && other->health >= 0 &&
+ other->storedBP > 0 )
+ {
+ float bp;
+
+ bp = floor( MIN( MIN( other->storedBP, 4 ),
+ CKIT_STORAGE - ent->client->ps.persistant[ PERS_BUILDPOINTS ] ) );
+
+ other->storedBP -= bp;
+ ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp;
+
+ if( bp )
+ G_AddEvent( ent, EV_RPTUSE_SOUND, 0 );
+ else
+ G_AddEvent( ent, EV_BUILD_REPAIRED, 0 );
+ }
+ }
+ //transfer TO another player
+ else if( other->client )
+ {
+ if( BG_GetPlayerWeapon( &other->client->ps ) == WP_HBUILD &&
+ other->client->ps.stats[ STAT_HEALTH ] >= 0 )
+ {
+ int bp;
+
+ bp = MIN( MIN( ent->client->ps.persistant[ PERS_BUILDPOINTS ], 4 ),
+ CKIT_STORAGE - other->client->ps.persistant[ PERS_BUILDPOINTS ] );
+
+ ent->client->ps.persistant[ PERS_BUILDPOINTS ] -= bp;
+ other->client->ps.persistant[ PERS_BUILDPOINTS ] += bp;
+
+ if( bp )
+ G_AddEvent( ent, EV_RPTUSE_SOUND, 0 );
+ else
+ G_AddEvent( ent, EV_BUILD_REPAIRED, 0 );
+ }
+ }
+
+ return;
+ }
+
// weapon doesn't ever need reloading
if( BG_Weapon( ps->weapon )->infiniteAmmo )
return;
@@ -3305,7 +3362,6 @@ Cmd_Debug1_f
*/
void Cmd_Debug1_f( gentity_t *other )
{
- other->client->ps.stats[ STAT_SHAKE ] += 70;
}
/*
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index fe5d607..6acf6b7 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -195,17 +195,18 @@ float G_RewardAttackers( gentity_t *self )
AddScore( player, stageValue );
- // killing buildables earns score, but not credits
- if( self->s.eType != ET_BUILDABLE )
- {
- G_AddCreditToClient( player->client, stageValue, qtrue );
+ if( self->s.eType == ET_BUILDABLE )
+ stageValue *= g_buildableValueModifier.value;
+ else
+ stageValue *= g_playerValueModifier.value;
+
+ G_AddCreditToClient( player->client, stageValue, qtrue );
- // add to stage counters
- if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- alienCredits += stageValue;
- else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- humanCredits += stageValue;
- }
+ // add to stage counters
+ if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ alienCredits += stageValue;
+ else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ humanCredits += stageValue;
}
self->credits[ i ] = 0;
}
@@ -1069,7 +1070,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
// base is under attack warning if DCC'd
- if( targ->buildableTeam == TEAM_HUMANS && G_FindDCC( targ ) &&
+ if( targ->buildableTeam == TEAM_HUMANS && G_IsDCCBuilt( ) &&
level.time > level.humanBaseAttackTimer )
{
level.humanBaseAttackTimer = level.time + DC_ATTACK_PERIOD;
@@ -1085,7 +1086,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
// add to the attacker's hit counter
if( attacker->client && targ != attacker && targ->health > 0
&& targ->s.eType != ET_MISSILE
- && targ->s.eType != ET_GENERAL )
+ && targ->s.eType != ET_GENERAL
+ && mod != MOD_DECONSTRUCT )
{
if( OnSameTeam( targ, attacker ) )
attacker->client->ps.persistant[ PERS_HITS ]--;
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 001c7d9..944140f 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -198,16 +198,12 @@ struct gentity_s
gentity_t *parentNode; // for creep and defence/spawn dependencies
qboolean active; // for power repeater, but could be useful elsewhere
qboolean locked; // used for turret tracking
- qboolean powered; // for human buildables
int builtBy; // clientNum of person that built this
- int dcc; // number of controlling dccs
qboolean spawned; // whether or not this buildable has finished spawning
int shrunkTime; // time when a barricade shrunk or zero
int buildTime; // when this buildable was built
int animTime; // last animation change
int time1000; // timer evaluated every second
- qboolean deconstruct; // deconstruct if no BP left
- int deconstructTime; // time at which structure marked
int overmindAttackTimer;
int overmindDyingTimer;
int overmindSpawnsTimer;
@@ -243,10 +239,24 @@ struct gentity_s
qboolean pointAgainstWorld; // don't use the bbox for map collisions
- int buildPointZone; // index for zone
- int usesBuildPointZone; // does it use a zone?
+ vec3_t cuboidSize;
- vec3_t cuboidSize;
+ //power grid
+ qboolean requiresPower; // false for telenodes, cuboids, etc.
+ qboolean isPowerSource; // true for all active elements (including the capbank)
+
+ int powerNetwork; // which network is it in (0 is no network)
+ qboolean powered; // is this buildable powered?
+ qboolean surge; // true if the buildable requests high amount of power (resistance=surgeResistance)
+ // IMPORTANT NOTE: it should NOT depend on anything power-related to avoid
+ // unstable network states
+ qboolean surgePowered; // is surge powered?
+ float voltage; // voltage drop (load) or gain (source)
+ float current; // current flow
+ float resistance; // resistance (for sources it's their internal resistance)
+
+ //mass
+ float storedBP;
};
typedef enum
@@ -485,16 +495,6 @@ void G_PrintSpawnQueue( spawnQueue_t *sq );
#define MAX_DAMAGE_REGION_TEXT 8192
#define MAX_DAMAGE_REGIONS 16
-// build point zone
-typedef struct
-{
- int active;
-
- int totalBuildPoints;
- int queuedBuildPoints;
- int nextQueueTime;
-} buildPointZone_t;
-
// store locational damage regions
typedef struct damageRegion_s
{
@@ -641,14 +641,6 @@ typedef struct
int alienBuildPoints;
int alienBuildPointQueue;
int alienNextQueueTime;
- int humanBuildPoints;
- int humanBuildPointQueue;
- int humanNextQueueTime;
-
- buildPointZone_t *buildPointZones;
-
- gentity_t *markedBuildables[ MAX_GENTITIES ];
- int numBuildablesForRemoval;
int alienKills;
int humanKills;
@@ -801,8 +793,8 @@ gentity_t *G_CheckSpawnPoint( int spawnNum, const vec3_t origin,
buildable_t G_IsPowered( vec3_t origin );
qboolean G_IsDCCBuilt( void );
-int G_FindDCC( gentity_t *self );
-gentity_t *G_Reactor( void );
+qboolean G_IsRTGBuilt( void );
+qboolean G_FindDCC( gentity_t *self );
gentity_t *G_Overmind( void );
qboolean G_FindCreep( gentity_t *self );
@@ -823,12 +815,7 @@ void G_BaseSelfDestruct( team_t team );
int G_NextQueueTime( int queuedBP, int totalBP, int queueBaseRate );
void G_QueueBuildPoints( gentity_t *self );
int G_GetBuildPoints( const vec3_t pos, team_t team );
-int G_GetMarkedBuildPoints( const vec3_t pos, team_t team );
qboolean G_FindPower( gentity_t *self, qboolean searchUnspawned );
-gentity_t *G_PowerEntityForPoint( const vec3_t origin );
-gentity_t *G_PowerEntityForEntity( gentity_t *ent );
-gentity_t *G_RepeaterEntityForPoint( vec3_t origin );
-gentity_t *G_InPowerZone( gentity_t *self );
buildLog_t *G_BuildLogNew( gentity_t *actor, buildFate_t fate );
void G_BuildLogSet( buildLog_t *log, gentity_t *ent );
void G_BuildLogAuto( gentity_t *actor, gentity_t *buildable, buildFate_t fate );
@@ -836,6 +823,7 @@ void G_BuildLogRevert( int id );
const char *G_CuboidName(buildable_t buildable, const vec3_t cuboidSize, qboolean verbose);
void G_LayoutBuildItem( buildable_t buildable, vec3_t origin, vec3_t angles, vec3_t origin2, vec3_t angles2 );
void G_RemoveUnbuiltBuildables( gentity_t *self );
+void G_UpdatePowerGrid( float dt );
//
// g_utils.c
@@ -1158,11 +1146,6 @@ extern vmCvar_t pmove_msec;
extern vmCvar_t g_alienBuildPoints;
extern vmCvar_t g_alienBuildQueueTime;
-extern vmCvar_t g_humanBuildPoints;
-extern vmCvar_t g_humanBuildQueueTime;
-extern vmCvar_t g_humanRepeaterBuildPoints;
-extern vmCvar_t g_humanRepeaterBuildQueueTime;
-extern vmCvar_t g_humanRepeaterMaxZones;
extern vmCvar_t g_humanStage;
extern vmCvar_t g_humanCredits;
extern vmCvar_t g_humanMaxStage;
@@ -1182,8 +1165,6 @@ extern vmCvar_t g_disabledEquipment;
extern vmCvar_t g_disabledClasses;
extern vmCvar_t g_disabledBuildables;
-extern vmCvar_t g_markDeconstruct;
-
extern vmCvar_t g_debugMapRotation;
extern vmCvar_t g_currentMapRotation;
extern vmCvar_t g_mapRotationNodes;
@@ -1227,6 +1208,11 @@ extern vmCvar_t g_cuboidHealthLimit;
extern vmCvar_t g_buildableDensityLimit;
extern vmCvar_t g_buildableDensityLimitRange;
+extern vmCvar_t g_buildableValueModifier;
+extern vmCvar_t g_playerValueModifier;
+extern vmCvar_t g_massYieldModifier;
+extern vmCvar_t g_voltageModifier;
+
void trap_Print( const char *fmt );
void trap_Error( const char *fmt );
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 0f9c31c..1cbf897 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -86,11 +86,6 @@ vmCvar_t g_maxNameChanges;
vmCvar_t g_alienBuildPoints;
vmCvar_t g_alienBuildQueueTime;
-vmCvar_t g_humanBuildPoints;
-vmCvar_t g_humanBuildQueueTime;
-vmCvar_t g_humanRepeaterBuildPoints;
-vmCvar_t g_humanRepeaterBuildQueueTime;
-vmCvar_t g_humanRepeaterMaxZones;
vmCvar_t g_humanStage;
vmCvar_t g_humanCredits;
vmCvar_t g_humanMaxStage;
@@ -110,8 +105,6 @@ vmCvar_t g_disabledEquipment;
vmCvar_t g_disabledClasses;
vmCvar_t g_disabledBuildables;
-vmCvar_t g_markDeconstruct;
-
vmCvar_t g_debugMapRotation;
vmCvar_t g_currentMapRotation;
vmCvar_t g_mapRotationNodes;
@@ -158,6 +151,11 @@ vmCvar_t g_cuboidMode;
vmCvar_t g_buildableDensityLimit;
vmCvar_t g_buildableDensityLimitRange;
+vmCvar_t g_buildableValueModifier;
+vmCvar_t g_playerValueModifier;
+vmCvar_t g_massYieldModifier;
+vmCvar_t g_voltageModifier;
+
// copy cvars that can be set in worldspawn so they can be restored later
static char cv_gravity[ MAX_CVAR_VALUE_STRING ];
static char cv_humanMaxStage[ MAX_CVAR_VALUE_STRING ];
@@ -232,11 +230,6 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_alienBuildPoints, "g_alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, 0, 0, qfalse },
{ &g_alienBuildQueueTime, "g_alienBuildQueueTime", DEFAULT_ALIEN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanBuildPoints, "g_humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, 0, 0, qfalse },
- { &g_humanBuildQueueTime, "g_humanBuildQueueTime", DEFAULT_HUMAN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterBuildPoints, "g_humanRepeaterBuildPoints", DEFAULT_HUMAN_REPEATER_BUILDPOINTS, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterMaxZones, "g_humanRepeaterMaxZones", DEFAULT_HUMAN_REPEATER_MAX_ZONES, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterBuildQueueTime, "g_humanRepeaterBuildQueueTime", DEFAULT_HUMAN_REPEATER_QUEUE_TIME, CVAR_ARCHIVE, 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, cv_humanMaxStage },
@@ -261,8 +254,6 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse },
{ &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse },
- { &g_markDeconstruct, "g_markDeconstruct", "3", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
-
{ &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse },
{ &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING
{ &g_mapRotationNodes, "g_mapRotationNodes", "", CVAR_ROM, 0, qfalse },
@@ -301,7 +292,12 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_cuboidMode, "g_cuboidMode", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_buildableDensityLimit, "g_buildableDensityLimit", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
- { &g_buildableDensityLimitRange, "g_buildableDensityLimitRange", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }
+ { &g_buildableDensityLimitRange, "g_buildableDensityLimitRange", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
+
+ { &g_playerValueModifier, "g_playerValueModifier", "0.5", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
+ { &g_buildableValueModifier, "g_buildableValueModifier", "0.16", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
+ { &g_massYieldModifier, "g_massYieldModifier", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
+ { &g_voltageModifier, "g_voltageModifier", "1.0", CVAR_ARCHIVE | CVAR_SERVERINFO }
};
static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] );
@@ -1197,7 +1193,6 @@ void G_CalculateBuildPoints( void )
{
int i;
buildable_t buildable;
- buildPointZone_t *zone;
// BP queue updates
while( level.alienBuildPointQueue > 0 &&
@@ -1209,15 +1204,6 @@ void G_CalculateBuildPoints( void )
g_alienBuildQueueTime.integer );
}
- while( level.humanBuildPointQueue > 0 &&
- level.humanNextQueueTime < level.time )
- {
- level.humanBuildPointQueue--;
- level.humanNextQueueTime += G_NextQueueTime( level.humanBuildPointQueue,
- g_humanBuildPoints.integer,
- g_humanBuildQueueTime.integer );
- }
-
// Sudden Death checks
if( G_TimeTilSuddenDeath( ) <= 0 && level.suddenDeathWarning < TW_PASSED )
{
@@ -1226,7 +1212,6 @@ void G_CalculateBuildPoints( void )
trap_SendServerCommand( -1, "print \"Beginning Sudden Death.\n\"" );
trap_SendServerCommand( -1, "announce suddendeath" );
level.suddenDeathWarning = TW_PASSED;
- G_ClearDeconMarks( );
// Clear blueprints, or else structs that cost 0 BP can still be built after SD
for( i = 0; i < level.maxclients; i++ )
@@ -1246,101 +1231,26 @@ void G_CalculateBuildPoints( void )
level.suddenDeathWarning = TW_IMMINENT;
}
- level.humanBuildPoints = g_humanBuildPoints.integer - level.humanBuildPointQueue;
level.alienBuildPoints = g_alienBuildPoints.integer - level.alienBuildPointQueue;
- // Reset buildPointZones
- for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ )
- {
- buildPointZone_t *zone = &level.buildPointZones[ i ];
-
- zone->active = qfalse;
- zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer;
- }
-
// Iterate through entities
for( i = MAX_CLIENTS; i < level.num_entities; i++ )
{
gentity_t *ent = &g_entities[ i ];
- buildPointZone_t *zone;
buildable_t buildable;
int cost;
if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD )
continue;
- // mark a zone as active
- if( ent->usesBuildPointZone )
- {
- assert( ent->buildPointZone >= 0 && ent->buildPointZone < g_humanRepeaterMaxZones.integer );
-
- zone = &level.buildPointZones[ ent->buildPointZone ];
- zone->active = qtrue;
- }
-
// Subtract the BP from the appropriate pool
buildable = ent->s.modelindex;
cost = BG_Buildable( buildable, ent->cuboidSize )->buildPoints;
if( ent->buildableTeam == TEAM_ALIENS )
level.alienBuildPoints -= cost;
- if( buildable == BA_H_REPEATER )
- level.humanBuildPoints -= cost;
- else if( buildable != BA_H_REACTOR )
- {
- gentity_t *power = G_PowerEntityForEntity( ent );
-
- if( power )
- {
- if( power->s.modelindex == BA_H_REACTOR )
- level.humanBuildPoints -= cost;
- else if( power->s.modelindex == BA_H_REPEATER && power->usesBuildPointZone )
- level.buildPointZones[ power->buildPointZone ].totalBuildPoints -= cost;
- }
- }
}
- // Finally, update repeater zones and their queues
- // note that this has to be done after the used BP is calculated
- for( i = MAX_CLIENTS; i < level.num_entities; i++ )
- {
- gentity_t *ent = &g_entities[ i ];
-
- if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD ||
- ent->buildableTeam != TEAM_HUMANS )
- continue;
-
- buildable = ent->s.modelindex;
-
- if( buildable != BA_H_REPEATER )
- continue;
-
- if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active )
- {
- zone = &level.buildPointZones[ ent->buildPointZone ];
-
- if( G_TimeTilSuddenDeath( ) > 0 )
- {
- // BP queue updates
- while( zone->queuedBuildPoints > 0 &&
- zone->nextQueueTime < level.time )
- {
- zone->queuedBuildPoints--;
- zone->nextQueueTime += G_NextQueueTime( zone->queuedBuildPoints,
- zone->totalBuildPoints,
- g_humanRepeaterBuildQueueTime.integer );
- }
- }
- else
- {
- zone->totalBuildPoints = zone->queuedBuildPoints = 0;
- }
- }
- }
-
- if( level.humanBuildPoints < 0 )
- level.humanBuildPoints = 0;
-
if( level.alienBuildPoints < 0 )
level.alienBuildPoints = 0;
}
@@ -2338,14 +2248,6 @@ void CheckCvars( void )
trap_Cvar_Set( "g_needpass", "0" );
}
- // Unmark any structures for deconstruction when
- // the server setting is changed
- if( g_markDeconstruct.modificationCount != lastMarkDeconModCount )
- {
- lastMarkDeconModCount = g_markDeconstruct.modificationCount;
- G_ClearDeconMarks( );
- }
-
// If we change g_suddenDeathTime during a map, we need to update
// when sd will begin
if( g_suddenDeathTime.modificationCount != lastSDTimeModCount )
@@ -2354,24 +2256,6 @@ void CheckCvars( void )
level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000;
}
- // If the number of zones changes, we need a new array
- if( g_humanRepeaterMaxZones.integer != lastNumZones )
- {
- buildPointZone_t *newZones;
- size_t newsize = g_humanRepeaterMaxZones.integer * sizeof( buildPointZone_t );
- size_t oldsize = lastNumZones * sizeof( buildPointZone_t );
-
- newZones = BG_Alloc( newsize );
- if( level.buildPointZones )
- {
- Com_Memcpy( newZones, level.buildPointZones, MIN( oldsize, newsize ) );
- BG_Free( level.buildPointZones );
- }
-
- level.buildPointZones = newZones;
- lastNumZones = g_humanRepeaterMaxZones.integer;
- }
-
level.frameMsec = trap_Milliseconds( );
}
@@ -2586,6 +2470,9 @@ void G_RunFrame( int levelTime )
G_SpawnClients( TEAM_HUMANS );
G_CalculateAvgPlayers( );
G_UpdateZaps( msec );
+
+ // update the power grid
+ G_UpdatePowerGrid( 0.001f * msec );
// see if it is time to end the level
CheckExitRules( );
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 4a28083..e4399a4 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -768,8 +768,8 @@ void CheckCkitRepair( gentity_t *ent )
if( tr.fraction < 1.0f && traceEnt->spawned && traceEnt->health > 0 &&
traceEnt->s.eType == ET_BUILDABLE && traceEnt->buildableTeam == TEAM_HUMANS )
{
- if(BG_Buildable(traceEnt->s.modelindex,NULL)->cuboid)
- if(!BG_CuboidAttributes(traceEnt->s.modelindex)->repairable)
+ if( BG_Buildable( traceEnt->s.modelindex, NULL )->cuboid )
+ if( !BG_CuboidAttributes( traceEnt->s.modelindex )->repairable )
return;
bHealth = BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->health;
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index bf60cd0..71d848e 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -600,10 +600,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define DC_SPLASHDAMAGE 50
#define DC_SPLASHRADIUS 100
#define DC_ATTACK_PERIOD 10000 // how often to spam "under attack"
-#define DC_HEALRATE 4
+#define DC_HEALRATE 250 // +1 HP every this amount of time
#define DC_RANGE 1000
#define DC_VALUE HBVM(DC_BP)
-#define MAX_DCS_PER_BUILDABLE 2
#define ARMOURY_BP 10
#define ARMOURY_BT 15000
@@ -612,7 +611,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ARMOURY_SPLASHRADIUS 100
#define ARMOURY_VALUE HBVM(ARMOURY_BP)
-#define REACTOR_BP 0
+#define REACTOR_BP 36
#define REACTOR_BT 30000
#define REACTOR_HEALTH HBHM(930)
#define REACTOR_SPLASHDAMAGE 200
@@ -623,7 +622,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define REACTOR_ATTACK_DCC_REPEAT 1000
#define REACTOR_ATTACK_DCC_RANGE 150.0f
#define REACTOR_ATTACK_DCC_DAMAGE 40
-#define REACTOR_VALUE HBVM(30)
+#define REACTOR_VALUE HBVM(REACTOR_BP)
#define REPEATER_BP 4
#define REPEATER_BT 15000
@@ -632,6 +631,86 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define REPEATER_SPLASHRADIUS 100
#define REPEATER_VALUE HBVM(REPEATER_BP)
+#define CAPBANK_BP 10
+#define CAPBANK_BT 20000
+#define CAPBANK_HEALTH HBHM(310)
+#define CAPBANK_SPLASHDAMAGE 70
+#define CAPBANK_SPLASHRADIUS 140
+#define CAPBANK_VALUE HBVM(CAPBANK_BP)
+
+#define RTG_BP 14
+#define RTG_BT 20000
+#define RTG_HEALTH HBHM(460)
+#define RTG_SPLASHDAMAGE 120
+#define RTG_SPLASHRADIUS 150
+#define RTG_VALUE HBVM(15)
+#define RTG_YIELD 0.15
+#define RTG_STORAGE 15
+
+#define REFINERY_BP 16
+#define REFINERY_BT 20000
+#define REFINERY_HEALTH HBHM(310)
+#define REFINERY_SPLASHDAMAGE 100
+#define REFINERY_SPLASHRADIUS 150
+#define REFINERY_VALUE HBVM(REFINERY_BP)
+#define REFINERY_YIELD 0.35 // at 2Hz
+#define REFINERY_STORAGE 30
+
+/*
+ * POWER GRID settings
+ *
+ * All units are SI:
+ * resistance (R) - ohms
+ * voltage (V) - volts
+ * current (I) - amperes
+ * capacity (C) - farads
+ */
+
+//settings for buildables that are not a part of the power grid
+#define DEFAULT_POWER_SETTINGS qfalse, qfalse, 0.0f, 0.0f, qfalse
+
+#define RESISTANCE(i,pc) (POWER_VOLTAGE/(i)*(pc))
+
+#define POWER_VOLTAGE 100.0f
+
+#define RTG_RESISTANCE 5.0f
+
+#define REACTOR_RESISTANCE 1.0f
+
+#define CAPBANK_RESISTANCE 0.05f
+#define CAPBANK_CAPACITY 2.0f
+
+#define PREBUILD_CURRENT 7.0f
+#define PREBUILD_RESISTANCE RESISTANCE(PREBUILD_CURRENT,0.8f)
+
+#define MEDISTAT_I_IDLE 0.25f
+#define MEDISTAT_R_IDLE RESISTANCE(MEDISTAT_I_IDLE,0.35f)
+#define MEDISTAT_I_ACTIVE 1.0f
+#define MEDISTAT_R_ACTIVE RESISTANCE(MEDISTAT_I_ACTIVE,0.75f)
+
+#define ARMOURY_CURRENT 0.1f
+#define ARMOURY_RESISTANCE RESISTANCE(ARMOURY_CURRENT,0.25f)
+
+#define TESLAGEN_I_IDLE 0.6f
+#define TESLAGEN_R_IDLE RESISTANCE(TESLAGEN_I_IDLE,0.52f)
+#define TESLAGEN_I_ACTIVE 5.0f
+#define TESLAGEN_R_ACTIVE RESISTANCE(TESLAGEN_I_ACTIVE,0.75f)
+
+#define MGTURRET_I_IDLE 0.3f
+#define MGTURRET_R_IDLE RESISTANCE(MGTURRET_I_IDLE,0.6f)
+#define MGTURRET_I_ACTIVE 2.0f
+#define MGTURRET_R_ACTIVE RESISTANCE(MGTURRET_I_ACTIVE,0.75f)
+
+#define DC_I_IDLE 0.4f
+#define DC_R_IDLE RESISTANCE(DC_I_IDLE,0.5f)
+#define DC_I_ACTIVE 5.0f
+#define DC_R_ACTIVE RESISTANCE(DC_I_ACTIVE,0.75f)
+
+#define REFINERY_I_IDLE 0.25f
+#define REFINERY_R_IDLE RESISTANCE(REFINERY_I_IDLE,0.55f)
+#define REFINERY_I_ACTIVE 4.0f
+#define REFINERY_R_ACTIVE RESISTANCE(REFINERY_I_ACTIVE,0.75f)
+
/*
* HUMAN misc
*/
@@ -664,6 +743,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define HUMAN_BUILDABLE_INACTIVE_TIME 90000
+#define CKIT_STORAGE 36
+
/*
* Misc
*/