3373 lines
86 KiB
C
3373 lines
86 KiB
C
/*
|
|
This file is part of Warzone 2100.
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
|
Copyright (C) 2005-2010 Warzone 2100 Project
|
|
|
|
Warzone 2100 is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Warzone 2100 is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Warzone 2100; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/**
|
|
* @file stats.c
|
|
*
|
|
* Store common stats for weapons, components, brains, etc.
|
|
*
|
|
*/
|
|
#include <string.h>
|
|
|
|
#include "lib/framework/frame.h"
|
|
#include "lib/framework/strres.h"
|
|
#include "lib/framework/frameresource.h"
|
|
#include "lib/gamelib/gtime.h"
|
|
#include "objects.h"
|
|
#include "stats.h"
|
|
#include "map.h"
|
|
#include "main.h"
|
|
#include "lib/sound/audio_id.h"
|
|
#include "projectile.h"
|
|
#include "text.h"
|
|
|
|
#define WEAPON_TIME 100
|
|
|
|
/* The stores for the different stats */
|
|
BODY_STATS *asBodyStats;
|
|
BRAIN_STATS *asBrainStats;
|
|
PROPULSION_STATS *asPropulsionStats;
|
|
SENSOR_STATS *asSensorStats;
|
|
ECM_STATS *asECMStats;
|
|
REPAIR_STATS *asRepairStats;
|
|
WEAPON_STATS *asWeaponStats;
|
|
CONSTRUCT_STATS *asConstructStats;
|
|
PROPULSION_TYPES *asPropulsionTypes;
|
|
static TERRAIN_TABLE *asTerrainTable;
|
|
static SPECIAL_ABILITY *asSpecialAbility;
|
|
|
|
//used to hold the modifiers cross refd by weapon effect and propulsion type
|
|
WEAPON_MODIFIER asWeaponModifier[WE_NUMEFFECTS][PROPULSION_TYPE_NUM];
|
|
|
|
//used to hold the current upgrade level per player per weapon subclass
|
|
WEAPON_UPGRADE asWeaponUpgrade[MAX_PLAYERS][WSC_NUM_WEAPON_SUBCLASSES];
|
|
SENSOR_UPGRADE asSensorUpgrade[MAX_PLAYERS];
|
|
ECM_UPGRADE asECMUpgrade[MAX_PLAYERS];
|
|
REPAIR_UPGRADE asRepairUpgrade[MAX_PLAYERS];
|
|
CONSTRUCTOR_UPGRADE asConstUpgrade[MAX_PLAYERS];
|
|
BODY_UPGRADE asBodyUpgrade[MAX_PLAYERS][BODY_TYPE];
|
|
|
|
/* The number of different stats stored */
|
|
UDWORD numBodyStats;
|
|
UDWORD numBrainStats;
|
|
UDWORD numPropulsionStats;
|
|
UDWORD numSensorStats;
|
|
UDWORD numECMStats;
|
|
UDWORD numRepairStats;
|
|
UDWORD numWeaponStats;
|
|
UDWORD numConstructStats;
|
|
static UDWORD numSpecialAbility;
|
|
|
|
//the max values of the stats used in the design screen
|
|
static UDWORD maxComponentWeight;
|
|
static UDWORD maxBodyArmour;
|
|
static UDWORD maxBodyPower;
|
|
static UDWORD maxBodyPoints;
|
|
static UDWORD maxSensorRange;
|
|
static UDWORD maxSensorPower;
|
|
static UDWORD maxECMPower;
|
|
static UDWORD maxECMRange;
|
|
static UDWORD maxConstPoints;
|
|
static UDWORD maxRepairPoints;
|
|
static UDWORD maxWeaponRange;
|
|
static UDWORD maxWeaponDamage;
|
|
static UDWORD maxWeaponROF;
|
|
static UDWORD maxPropulsionSpeed;
|
|
|
|
//stores for each players component states - can be either UNAVAILABLE, REDUNDANT, FOUND or AVAILABLE
|
|
UBYTE *apCompLists[MAX_PLAYERS][COMP_NUMCOMPONENTS];
|
|
|
|
//store for each players Structure states
|
|
UBYTE *apStructTypeLists[MAX_PLAYERS];
|
|
|
|
static BOOL compareYes(const char *strToCompare, const char *strOwner);
|
|
static bool getMovementModel(const char* movementModel, MOVEMENT_MODEL* model);
|
|
|
|
//Access functions for the max values to be used in the Design Screen
|
|
static void setMaxComponentWeight(UDWORD weight);
|
|
static void setMaxBodyArmour(UDWORD armour);
|
|
static void setMaxBodyPower(UDWORD power);
|
|
static void setMaxBodyPoints(UDWORD points);
|
|
static void setMaxSensorRange(UDWORD range);
|
|
static void setMaxSensorPower(UDWORD power);
|
|
static void setMaxECMRange(UDWORD power);
|
|
static void setMaxECMPower(UDWORD power);
|
|
static void setMaxConstPoints(UDWORD points);
|
|
static void setMaxRepairPoints(UDWORD repair);
|
|
static void setMaxWeaponRange(UDWORD range);
|
|
static void setMaxWeaponDamage(UDWORD damage);
|
|
static void setMaxWeaponROF(UDWORD rof);
|
|
static void setMaxPropulsionSpeed(UDWORD speed);
|
|
|
|
//determine the effect this upgrade would have on the max values
|
|
static void updateMaxWeaponStats(UWORD maxValue);
|
|
static void updateMaxSensorStats(UWORD maxRange, UWORD maxPower);
|
|
static void updateMaxRepairStats(UWORD maxValue);
|
|
static void updateMaxECMStats(UWORD maxValue);
|
|
static void updateMaxBodyStats(UWORD maxBody, UWORD maxPower, UWORD maxArmour);
|
|
static void updateMaxConstStats(UWORD maxValue);
|
|
|
|
/*******************************************************************************
|
|
* Generic stats macros/functions
|
|
*******************************************************************************/
|
|
|
|
/* Macro to allocate memory for a set of stats */
|
|
#define ALLOC_STATS(numEntries, list, listSize, type) \
|
|
ASSERT( (numEntries) < REF_RANGE, \
|
|
"allocStats: number of stats entries too large for " #type );\
|
|
if ((list)) free((list)); \
|
|
(list) = (type *)malloc(sizeof(type) * (numEntries)); \
|
|
if ((list) == NULL) \
|
|
{ \
|
|
debug( LOG_FATAL, "Out of memory" ); \
|
|
abort(); \
|
|
return false; \
|
|
} \
|
|
(listSize) = (numEntries); \
|
|
return true
|
|
|
|
|
|
/*Macro to Deallocate stats*/
|
|
#define STATS_DEALLOC(list, listSize, type) \
|
|
statsDealloc((COMPONENT_STATS*)(list), (listSize), sizeof(type)); \
|
|
(list) = NULL
|
|
|
|
|
|
void statsInitVars(void)
|
|
{
|
|
/* The number of different stats stored */
|
|
numBodyStats = 0;
|
|
numBrainStats = 0;
|
|
numPropulsionStats = 0;
|
|
numSensorStats = 0;
|
|
numECMStats = 0;
|
|
numRepairStats = 0;
|
|
numWeaponStats = 0;
|
|
numConstructStats = 0;
|
|
numSpecialAbility = 0;
|
|
|
|
//initialise the upgrade structures
|
|
memset(asWeaponUpgrade, 0, sizeof(asWeaponUpgrade));
|
|
memset(asSensorUpgrade, 0, sizeof(asSensorUpgrade));
|
|
memset(asECMUpgrade, 0, sizeof(asECMUpgrade));
|
|
memset(asRepairUpgrade, 0, sizeof(asRepairUpgrade));
|
|
memset(asBodyUpgrade, 0, sizeof(asBodyUpgrade));
|
|
|
|
// init the max values
|
|
maxComponentWeight = maxBodyArmour = maxBodyPower =
|
|
maxBodyPoints = maxSensorRange = maxSensorPower = maxECMPower = maxECMRange =
|
|
maxConstPoints = maxRepairPoints = maxWeaponRange = maxWeaponDamage =
|
|
maxPropulsionSpeed = 0;
|
|
}
|
|
|
|
|
|
/*Deallocate all the stats assigned from input data*/
|
|
void statsDealloc(COMPONENT_STATS* pStats, UDWORD listSize, UDWORD structureSize)
|
|
{
|
|
if (pStats)
|
|
{
|
|
free(pStats);
|
|
}
|
|
}
|
|
|
|
|
|
static BOOL allocateStatName(BASE_STATS* pStat, const char *Name)
|
|
{
|
|
pStat->pName = allocateName(Name);
|
|
|
|
return pStat != NULL;
|
|
}
|
|
|
|
|
|
/* body stats need the extra list removing */
|
|
static void deallocBodyStats(void)
|
|
{
|
|
BODY_STATS *psStat;
|
|
UDWORD inc;
|
|
|
|
for (inc = 0; inc < numBodyStats; inc++)
|
|
{
|
|
psStat = &asBodyStats[inc];
|
|
free(psStat->ppIMDList);
|
|
}
|
|
free(asBodyStats);
|
|
asBodyStats = NULL;
|
|
}
|
|
|
|
/*Deallocate all the stats assigned from input data*/
|
|
BOOL statsShutDown(void)
|
|
{
|
|
STATS_DEALLOC(asWeaponStats, numWeaponStats, WEAPON_STATS);
|
|
//STATS_DEALLOC(asBodyStats, numBodyStats, BODY_STATS);
|
|
deallocBodyStats();
|
|
STATS_DEALLOC(asBrainStats, numBrainStats, BRAIN_STATS);
|
|
STATS_DEALLOC(asPropulsionStats, numPropulsionStats, PROPULSION_STATS);
|
|
STATS_DEALLOC(asSensorStats, numSensorStats, SENSOR_STATS);
|
|
STATS_DEALLOC(asECMStats, numECMStats, ECM_STATS);
|
|
STATS_DEALLOC(asRepairStats, numRepairStats, REPAIR_STATS);
|
|
STATS_DEALLOC(asConstructStats, numConstructStats, CONSTRUCT_STATS);
|
|
deallocPropulsionTypes();
|
|
deallocTerrainTable();
|
|
deallocSpecialAbility();
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Macro to set the stats for a particular ref
|
|
* The macro uses the ref number in the stats structure to
|
|
* index the correct array entry
|
|
*/
|
|
#define SET_STATS(stats, list, index, type, refStart) \
|
|
ASSERT( ((stats)->ref >= (refStart)) && ((stats)->ref < (refStart) + REF_RANGE), \
|
|
"setStats: Invalid " #type " ref number" ); \
|
|
memcpy((list) + (index), (stats), sizeof(type))
|
|
|
|
|
|
/* Return the number of newlines in a file buffer */
|
|
UDWORD numCR(const char *pFileBuffer, UDWORD fileSize)
|
|
{
|
|
UDWORD lines=0;
|
|
|
|
while (fileSize-- > 0)
|
|
{
|
|
if (*pFileBuffer++ == '\n')
|
|
{
|
|
lines++;
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Allocate stats functions
|
|
*******************************************************************************/
|
|
/* Allocate Weapon stats */
|
|
BOOL statsAllocWeapons(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asWeaponStats, numWeaponStats, WEAPON_STATS);
|
|
}
|
|
/* Allocate Body Stats */
|
|
BOOL statsAllocBody(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asBodyStats, numBodyStats, BODY_STATS);
|
|
}
|
|
/* Allocate Brain Stats */
|
|
BOOL statsAllocBrain(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asBrainStats, numBrainStats, BRAIN_STATS);
|
|
}
|
|
/* Allocate Propulsion Stats */
|
|
BOOL statsAllocPropulsion(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asPropulsionStats, numPropulsionStats, PROPULSION_STATS);
|
|
}
|
|
/* Allocate Sensor Stats */
|
|
BOOL statsAllocSensor(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asSensorStats, numSensorStats, SENSOR_STATS);
|
|
}
|
|
/* Allocate Ecm Stats */
|
|
BOOL statsAllocECM(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asECMStats, numECMStats, ECM_STATS);
|
|
}
|
|
|
|
/* Allocate Repair Stats */
|
|
BOOL statsAllocRepair(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asRepairStats, numRepairStats, REPAIR_STATS);
|
|
}
|
|
|
|
/* Allocate Construct Stats */
|
|
BOOL statsAllocConstruct(UDWORD numStats)
|
|
{
|
|
ALLOC_STATS(numStats, asConstructStats, numConstructStats, CONSTRUCT_STATS);
|
|
}
|
|
|
|
const char *getStatName(const void * Stat)
|
|
{
|
|
const BASE_STATS * const psStats= (const BASE_STATS*)Stat;
|
|
|
|
return getName(psStats->pName);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* Load stats functions
|
|
*******************************************************************************/
|
|
|
|
/*Load the weapon stats from the file exported from Access*/
|
|
BOOL loadWeaponStats(const char *pWeaponData, UDWORD bufferSize)
|
|
{
|
|
unsigned int NumWeapons = numCR(pWeaponData, bufferSize);
|
|
WEAPON_STATS sStats, * const psStats = &sStats;
|
|
UDWORD i, rotate, maxElevation, surfaceToAir;
|
|
SDWORD minElevation;
|
|
char WeaponName[MAX_STR_LENGTH], GfxFile[MAX_STR_LENGTH];
|
|
char mountGfx[MAX_STR_LENGTH], flightGfx[MAX_STR_LENGTH],
|
|
hitGfx[MAX_STR_LENGTH], missGfx[MAX_STR_LENGTH],
|
|
waterGfx[MAX_STR_LENGTH], muzzleGfx[MAX_STR_LENGTH],
|
|
trailGfx[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH];
|
|
char fireOnMove[10], weaponClass[15], weaponSubClass[15],
|
|
weaponEffect[16], movement[15], facePlayer[5], //weaponEffect[15] caused stack corruption. --Qamly
|
|
faceInFlight[5],lightWorld[5];
|
|
UDWORD longRange, effectSize, numAttackRuns, designable;
|
|
UDWORD numRounds;
|
|
|
|
char *StatsName;
|
|
UDWORD penetrate;
|
|
UDWORD dummyVal;
|
|
|
|
// Skip descriptive header
|
|
if (strncmp(pWeaponData,"Weapon ",7)==0)
|
|
{
|
|
pWeaponData = strchr(pWeaponData,'\n') + 1;
|
|
NumWeapons--;
|
|
}
|
|
|
|
if (!statsAllocWeapons(NumWeapons))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i=0; i < NumWeapons; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(WEAPON_STATS));
|
|
|
|
WeaponName[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
mountGfx[0] = '\0';
|
|
muzzleGfx[0] = '\0';
|
|
flightGfx[0] = '\0';
|
|
hitGfx[0] = '\0';
|
|
missGfx[0] = '\0';
|
|
waterGfx[0] = '\0';
|
|
trailGfx[0] = '\0';
|
|
fireOnMove[0] = '\0';
|
|
weaponClass[0] = '\0';
|
|
weaponSubClass[0] = '\0';
|
|
movement[0] = '\0';
|
|
weaponEffect[0] = '\0';
|
|
facePlayer[0] = '\0';
|
|
faceInFlight[0] = '\0';
|
|
|
|
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pWeaponData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%d,\
|
|
%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%[^','],%[^','],%[^','],%d,%d,%d,%[^','],%[^','],%d,%d,\
|
|
%[^','],%d,%d,%d,%d,%d",
|
|
(char *)&WeaponName, (char *)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&psStats->body, (char *)&GfxFile, (char *)&mountGfx, (char *)&muzzleGfx, (char *)&flightGfx,
|
|
(char *)&hitGfx, (char *)&missGfx, (char *)&waterGfx, (char *)&trailGfx, &psStats->shortRange,
|
|
&psStats->longRange,&psStats->shortHit, &psStats->longHit,
|
|
&psStats->firePause, &psStats->numExplosions, &numRounds,
|
|
&psStats->reloadTime, &psStats->damage, &psStats->radius,
|
|
&psStats->radiusHit, &psStats->radiusDamage, &psStats->incenTime,
|
|
&psStats->incenDamage, &psStats->incenRadius, &psStats->directLife,
|
|
&psStats->radiusLife, &psStats->flightSpeed, &psStats->indirectHeight,
|
|
(char *)&fireOnMove, (char *)&weaponClass, (char *)&weaponSubClass, (char *)&movement, (char *)&weaponEffect,
|
|
&rotate, &maxElevation, &minElevation, (char *)&facePlayer, (char *)&faceInFlight,
|
|
&psStats->recoilValue, &psStats->minRange, (char *)&lightWorld,
|
|
&effectSize, &surfaceToAir, &numAttackRuns, &designable, &penetrate);
|
|
|
|
psStats->numRounds = (UBYTE)numRounds;
|
|
|
|
//#ifdef DEBUG
|
|
// Hack to get the current stats working... a zero flight speed value will cause an assert in projectile.c line 957
|
|
// I'm not sure if this should be on debug only...
|
|
// ... the last thing we want is for a zero value to get through on release (with no asserts!)
|
|
//
|
|
// Anyway if anyone has a problem with this, take it up with Tim ... we have a frank and open discussion about it.
|
|
#define DEFAULT_FLIGHTSPEED (500)
|
|
if (psStats->flightSpeed==0)
|
|
{
|
|
debug( LOG_NEVER, "STATS: Zero Flightspeed for %s - using default of %d\n", WeaponName, DEFAULT_FLIGHTSPEED );
|
|
psStats->flightSpeed=DEFAULT_FLIGHTSPEED;
|
|
}
|
|
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, WeaponName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_WEAPON_START + i;
|
|
|
|
//multiply time stats
|
|
psStats->firePause *= WEAPON_TIME;
|
|
psStats->incenTime *= WEAPON_TIME;
|
|
psStats->directLife *= WEAPON_TIME;
|
|
psStats->radiusLife *= WEAPON_TIME;
|
|
psStats->reloadTime *= WEAPON_TIME;
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
|
|
debug( LOG_FATAL, "Cannot find the weapon PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
//get the rest of the imd's
|
|
if (strcmp(mountGfx, "0"))
|
|
{
|
|
psStats->pMountGraphic = (iIMDShape *) resGetData("IMD", mountGfx);
|
|
if (psStats->pMountGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the mount PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pMountGraphic = NULL;
|
|
}
|
|
|
|
if(GetGameMode() == GS_NORMAL)
|
|
{
|
|
psStats->pMuzzleGraphic = (iIMDShape *) resGetData("IMD", muzzleGfx);
|
|
if (psStats->pMuzzleGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the muzzle PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
|
|
psStats->pInFlightGraphic = (iIMDShape *) resGetData("IMD", flightGfx);
|
|
if (psStats->pInFlightGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the flight PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
psStats->pTargetHitGraphic = (iIMDShape *) resGetData("IMD", hitGfx);
|
|
if (psStats->pTargetHitGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the target hit PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
psStats->pTargetMissGraphic = (iIMDShape *) resGetData("IMD", missGfx);
|
|
if (psStats->pTargetMissGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the target miss PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
psStats->pWaterHitGraphic = (iIMDShape *) resGetData("IMD", waterGfx);
|
|
if (psStats->pWaterHitGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the water hit PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
//trail graphic can be null
|
|
if (strcmp(trailGfx, "0"))
|
|
{
|
|
psStats->pTrailGraphic = (iIMDShape *) resGetData("IMD", trailGfx);
|
|
if (psStats->pTrailGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the trail PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pTrailGraphic = NULL;
|
|
}
|
|
}
|
|
|
|
//set the fireOnMove
|
|
if (!strcmp(fireOnMove, "NO"))
|
|
{
|
|
psStats->fireOnMove = FOM_NO;
|
|
}
|
|
else if (!strcmp(fireOnMove,"PARTIAL"))
|
|
{
|
|
psStats->fireOnMove = FOM_PARTIAL;
|
|
}
|
|
else if (!strcmp(fireOnMove,"YES"))
|
|
{
|
|
psStats->fireOnMove = FOM_YES;
|
|
}
|
|
else
|
|
{
|
|
debug( LOG_ERROR, "Invalid fire on move flag for weapon %s - assuming YES", getStatName(psStats) );
|
|
psStats->fireOnMove = FOM_YES;
|
|
}
|
|
|
|
//set the weapon class
|
|
if (!strcmp(weaponClass, "KINETIC"))
|
|
{
|
|
psStats->weaponClass = WC_KINETIC;
|
|
}
|
|
else if (!strcmp(weaponClass,"EXPLOSIVE"))
|
|
{
|
|
//psStats->weaponClass = WC_EXPLOSIVE;
|
|
psStats->weaponClass = WC_KINETIC; // explosives were removed from release version of Warzone
|
|
}
|
|
else if (!strcmp(weaponClass,"HEAT"))
|
|
{
|
|
psStats->weaponClass = WC_HEAT;
|
|
}
|
|
else if (!strcmp(weaponClass,"MISC"))
|
|
{
|
|
//psStats->weaponClass = WC_MISC;
|
|
psStats->weaponClass = WC_HEAT; // removed from release version of Warzone
|
|
}
|
|
else
|
|
{
|
|
debug( LOG_ERROR, "Invalid weapon class for weapon %s - assuming KINETIC", getStatName(psStats) );
|
|
psStats->weaponClass = WC_KINETIC;
|
|
}
|
|
|
|
//set the subClass
|
|
if (!getWeaponSubClass(weaponSubClass, &psStats->weaponSubClass))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//set the movement model
|
|
if (!getMovementModel(movement, &psStats->movementModel))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//set the weapon effect
|
|
if (!getWeaponEffect(weaponEffect, &psStats->weaponEffect))
|
|
{
|
|
debug( LOG_FATAL, "loadWepaonStats: Invalid weapon effect for weapon %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
StatsName=psStats->pName;
|
|
|
|
//set the face Player value
|
|
if (compareYes(facePlayer, StatsName))
|
|
{
|
|
psStats->facePlayer = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->facePlayer = false;
|
|
}
|
|
|
|
//set the In flight face Player value
|
|
if (compareYes(faceInFlight, StatsName))
|
|
{
|
|
psStats->faceInFlight = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->faceInFlight = false;
|
|
}
|
|
|
|
//set the light world value
|
|
if (compareYes(lightWorld, StatsName))
|
|
{
|
|
psStats->lightWorld = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->lightWorld = false;
|
|
}
|
|
|
|
//set the effect size
|
|
if (effectSize > UBYTE_MAX)
|
|
{
|
|
ASSERT( false,"loadWeaponStats: effectSize is greater than 255 for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
psStats->effectSize = (UBYTE)effectSize;
|
|
|
|
//set the rotate angle
|
|
if (rotate > UBYTE_MAX)
|
|
{
|
|
ASSERT( false,"loadWeaponStats: rotate is greater than 255 for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
psStats->rotate = (UBYTE)rotate;
|
|
|
|
//set the minElevation
|
|
if (minElevation > SBYTE_MAX || minElevation < SBYTE_MIN)
|
|
{
|
|
ASSERT( false,"loadWeaponStats: minElevation is outside of limits for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
psStats->minElevation = (SBYTE)minElevation;
|
|
|
|
//set the maxElevation
|
|
if (maxElevation > UBYTE_MAX)
|
|
{
|
|
ASSERT( false,"loadWeaponStats: maxElevation is outside of limits for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
psStats->maxElevation = (UBYTE)maxElevation;
|
|
|
|
//set the surfaceAir
|
|
if (surfaceToAir > UBYTE_MAX)
|
|
{
|
|
ASSERT( false, "loadWeaponStats: Surface to Air is outside of limits for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
if (surfaceToAir == 0)
|
|
{
|
|
psStats->surfaceToAir = (UBYTE)SHOOT_ON_GROUND;
|
|
}
|
|
else if (surfaceToAir <= 50)
|
|
{
|
|
psStats->surfaceToAir = (UBYTE)SHOOT_IN_AIR;
|
|
}
|
|
else
|
|
{
|
|
psStats->surfaceToAir = (UBYTE)(SHOOT_ON_GROUND | SHOOT_IN_AIR);
|
|
}
|
|
|
|
//set the attackRuns for VTOLs
|
|
if (numAttackRuns > UBYTE_MAX)
|
|
{
|
|
ASSERT( false, "loadWeaponStats: num of attack runs is outside of limits for weapon %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
psStats->vtolAttackRuns = (UBYTE)numAttackRuns;
|
|
|
|
//set design flag
|
|
if (designable)
|
|
{
|
|
psStats->designable = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->designable = false;
|
|
}
|
|
|
|
//set penetrate flag
|
|
if (penetrate)
|
|
{
|
|
psStats->penetrate = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->penetrate = false;
|
|
}
|
|
|
|
// error check the ranges
|
|
if (psStats->flightSpeed > 0 && !proj_Direct(psStats))
|
|
{
|
|
longRange = (UDWORD)proj_GetLongRange(psStats);
|
|
}
|
|
else
|
|
{
|
|
longRange = UDWORD_MAX;
|
|
}
|
|
if (psStats->shortRange > longRange)
|
|
{
|
|
debug( LOG_NEVER, "%s, flight speed is too low to reach short range (max range %d)\n", WeaponName, longRange );
|
|
}
|
|
else if (psStats->longRange > longRange)
|
|
{
|
|
debug( LOG_NEVER, "%s, flight speed is too low to reach long range (max range %d)\n", WeaponName, longRange );
|
|
}
|
|
|
|
//set the weapon sounds to default value
|
|
psStats->iAudioFireID = NO_SOUND;
|
|
psStats->iAudioImpactID = NO_SOUND;
|
|
|
|
//save the stats
|
|
statsSetWeapon(psStats, i);
|
|
|
|
// Set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxWeaponRange(psStats->longRange);
|
|
setMaxWeaponDamage(psStats->damage);
|
|
setMaxWeaponROF(weaponROF(psStats, -1));
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pWeaponData = strchr(pWeaponData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Body stats from the file exported from Access*/
|
|
BOOL loadBodyStats(const char *pBodyData, UDWORD bufferSize)
|
|
{
|
|
BODY_STATS sStats, * const psStats = &sStats;
|
|
unsigned int NumBody = numCR(pBodyData, bufferSize);
|
|
unsigned int i, designable;
|
|
char BodyName[MAX_STR_LENGTH], size[MAX_STR_LENGTH],
|
|
GfxFile[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH],
|
|
flameIMD[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
// Skip descriptive header
|
|
if (strncmp(pBodyData,"Body ",5)==0)
|
|
{
|
|
pBodyData = strchr(pBodyData,'\n') + 1;
|
|
NumBody--;
|
|
}
|
|
|
|
if (!statsAllocBody(NumBody))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < NumBody; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(BODY_STATS));
|
|
|
|
BodyName[0] = '\0';
|
|
size[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
flameIMD[0] = '\0';
|
|
|
|
//Watermelon:added 10 %d to store FRONT,REAR,LEFT,RIGHT,TOP,BOTTOM armour values
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pBodyData,"%[^','],%[^','],%[^','],%d,%d,%d,%d,%[^','],\
|
|
%d,%d,%d, \
|
|
%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%[^','],%d",
|
|
(char*)&BodyName, (char*)&dummy, (char*)&size, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &psStats->body, (char*)&GfxFile, &dummyVal,
|
|
&psStats->weaponSlots, &psStats->powerOutput,
|
|
(int*)&psStats->armourValue[HIT_SIDE_FRONT][WC_KINETIC], (int*)&psStats->armourValue[HIT_SIDE_FRONT][WC_HEAT],
|
|
(int*)&psStats->armourValue[HIT_SIDE_REAR][WC_KINETIC], (int*)&psStats->armourValue[HIT_SIDE_REAR][WC_HEAT],
|
|
(int*)&psStats->armourValue[HIT_SIDE_LEFT][WC_KINETIC], (int*)&psStats->armourValue[HIT_SIDE_LEFT][WC_HEAT],
|
|
(int*)&psStats->armourValue[HIT_SIDE_RIGHT][WC_KINETIC], (int*)&psStats->armourValue[HIT_SIDE_RIGHT][WC_HEAT],
|
|
(int*)&psStats->armourValue[HIT_SIDE_TOP][WC_KINETIC], (int*)&psStats->armourValue[HIT_SIDE_TOP][WC_HEAT],
|
|
(int*)&psStats->armourValue[HIT_SIDE_BOTTOM][WC_KINETIC],(int*)&psStats->armourValue[HIT_SIDE_BOTTOM][WC_HEAT],
|
|
(char*)&flameIMD, &designable);//, &psStats->armourValue[WC_EXPLOSIVE],
|
|
//&psStats->armourValue[WC_MISC]);
|
|
|
|
//allocate storage for the name
|
|
if (!allocateStatName((BASE_STATS *)psStats, BodyName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_BODY_START + i;
|
|
|
|
if (!getBodySize(size, &psStats->size))
|
|
{
|
|
ASSERT( false, "loadBodyStats: unknown body size for %s",
|
|
getStatName(psStats) );
|
|
return false;
|
|
}
|
|
|
|
//set design flag
|
|
psStats->designable = (designable != 0);
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the body PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
//get the flame graphic
|
|
if (strcmp(flameIMD, "0"))
|
|
{
|
|
psStats->pFlameIMD = (iIMDShape *) resGetData("IMD", flameIMD);
|
|
if (psStats->pFlameIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the flame PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pFlameIMD = NULL;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetBody(psStats, i);
|
|
|
|
//set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
//use front armour value to prevent bodyStats corrupt problems
|
|
setMaxBodyArmour(psStats->armourValue[HIT_SIDE_FRONT][WC_KINETIC]);
|
|
setMaxBodyArmour(psStats->armourValue[HIT_SIDE_FRONT][WC_HEAT]);
|
|
setMaxBodyPower(psStats->powerOutput);
|
|
setMaxBodyPoints(psStats->body);
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pBodyData = strchr(pBodyData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Brain stats from the file exported from Access*/
|
|
BOOL loadBrainStats(const char *pBrainData, UDWORD bufferSize)
|
|
{
|
|
BRAIN_STATS sStats, * const psStats = &sStats;
|
|
const unsigned int NumBrain = numCR(pBrainData, bufferSize);
|
|
unsigned int i = 0, weapon = 0;
|
|
char BrainName[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH],
|
|
weaponName[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
if (!statsAllocBrain(NumBrain))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < NumBrain; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(BRAIN_STATS));
|
|
|
|
BrainName[0] = '\0';
|
|
weaponName[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pBrainData,"%[^','],%[^','],%d,%d,%d,%d,%d,%[^','],%d",
|
|
(char*)&BrainName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
(char*)&weaponName, &psStats->progCap); //, &psStats->AICap, &psStats->AISpeed);
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, BrainName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_BRAIN_START + i;
|
|
|
|
//check weapon attached
|
|
if (!strcmp(weaponName, "0"))
|
|
{
|
|
psStats->psWeaponStat = NULL;
|
|
}
|
|
else
|
|
{
|
|
weapon = getCompFromName(COMP_WEAPON, weaponName);
|
|
|
|
//if weapon not found - error
|
|
if (weapon == -1)
|
|
{
|
|
debug( LOG_FATAL, "Unable to find Weapon %s for brain %s", weaponName, BrainName );
|
|
abort();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//Weapon found, alloc this to the brain
|
|
psStats->psWeaponStat = asWeaponStats + weapon;
|
|
}
|
|
}
|
|
|
|
// All brains except ZNULLBRAIN available in design screen
|
|
if ( strcmp( BrainName, "ZNULLBRAIN" ) == 0 )
|
|
{
|
|
psStats->designable = false;
|
|
}
|
|
else
|
|
{
|
|
psStats->designable = true;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetBrain(psStats, i);
|
|
|
|
//increment the pointer to the start of the next record
|
|
pBrainData = strchr(pBrainData, '\n') + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*returns the propulsion type based on the string name passed in */
|
|
bool getPropulsionType(const char* typeName, PROPULSION_TYPE* type)
|
|
{
|
|
if (strcmp(typeName, "Wheeled") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_WHEELED;
|
|
}
|
|
else if (strcmp(typeName, "Tracked") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_TRACKED;
|
|
}
|
|
else if (strcmp(typeName, "Legged") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_LEGGED;
|
|
}
|
|
else if (strcmp(typeName, "Hover") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_HOVER;
|
|
}
|
|
else if (strcmp(typeName, "Ski") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_SKI;
|
|
}
|
|
else if (strcmp(typeName, "Lift") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_LIFT;
|
|
}
|
|
else if (strcmp(typeName, "Propellor") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_PROPELLOR;
|
|
}
|
|
else if (strcmp(typeName, "Half-Tracked") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_HALF_TRACKED;
|
|
}
|
|
else if (strcmp(typeName, "Jump") == 0)
|
|
{
|
|
*type = PROPULSION_TYPE_JUMP;
|
|
}
|
|
else
|
|
{
|
|
debug( LOG_ERROR, "getPropulsionType: Invalid Propulsion type %s - assuming Hover", typeName );
|
|
*type = PROPULSION_TYPE_HOVER;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Propulsion stats from the file exported from Access*/
|
|
BOOL loadPropulsionStats(const char *pPropulsionData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumPropulsion = numCR(pPropulsionData, bufferSize);
|
|
PROPULSION_STATS sStats, * const psStats = &sStats;
|
|
unsigned int i = 0, designable;
|
|
char PropulsionName[MAX_STR_LENGTH], imdName[MAX_STR_LENGTH],
|
|
dummy[MAX_STR_LENGTH], type[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
if (!statsAllocPropulsion(NumPropulsion))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < NumPropulsion; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(PROPULSION_STATS));
|
|
|
|
PropulsionName[0] = '\0';
|
|
imdName[0] = '\0';
|
|
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pPropulsionData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%d,%d",
|
|
(char*)&PropulsionName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&psStats->body, (char*)&imdName, (char*)&type, &psStats->maxSpeed, &designable);
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, PropulsionName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_PROPULSION_START + i;
|
|
|
|
//set design flag
|
|
psStats->designable = (designable != 0);
|
|
|
|
//deal with imd - stored so that got something to see in Design Screen!
|
|
if (strcmp(imdName, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", imdName);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the propulsion PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
//set up the stats type
|
|
if (!getPropulsionType(type, &psStats->propulsionType))
|
|
{
|
|
debug( LOG_FATAL, "loadPropulsionStats: Invalid Propulsion type for %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetPropulsion(psStats, i);
|
|
|
|
//set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxPropulsionSpeed(psStats->maxSpeed);
|
|
//setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pPropulsionData = strchr(pPropulsionData,'\n') + 1;
|
|
}
|
|
|
|
/*since propulsion weight is a multiple of body weight we may need to
|
|
adjust the max component weight value*/
|
|
//check we've loaded them both in
|
|
if (asBodyStats && asPropulsionStats)
|
|
{
|
|
//check against each body stat
|
|
for (i = 0; i < numBodyStats; i++)
|
|
{
|
|
//check stat is designable
|
|
if (asBodyStats[i].designable)
|
|
{
|
|
unsigned int j = 0;
|
|
//check against each propulsion stat
|
|
for (j = 0; j < numPropulsionStats; j++)
|
|
{
|
|
//check stat is designable
|
|
if (asPropulsionStats[j].designable)
|
|
{
|
|
setMaxComponentWeight(asPropulsionStats[j].weight * asBodyStats[i].weight / 100);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Sensor stats from the file exported from Access*/
|
|
BOOL loadSensorStats(const char *pSensorData, UDWORD bufferSize)
|
|
{
|
|
unsigned int NumSensor = numCR(pSensorData, bufferSize);
|
|
SENSOR_STATS sStats, * const psStats = &sStats;
|
|
unsigned int i = 0, designable;
|
|
char SensorName[MAX_STR_LENGTH], location[MAX_STR_LENGTH],
|
|
GfxFile[MAX_STR_LENGTH],type[MAX_STR_LENGTH];
|
|
char mountGfx[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
// Skip descriptive header
|
|
if (strncmp(pSensorData,"Sensor ",7)==0)
|
|
{
|
|
pSensorData = strchr(pSensorData,'\n') + 1;
|
|
NumSensor--;
|
|
}
|
|
|
|
if (!statsAllocSensor(NumSensor))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < NumSensor; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(SENSOR_STATS));
|
|
|
|
SensorName[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
mountGfx[0] = '\0';
|
|
location[0] = '\0';
|
|
type[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pSensorData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%d,%[^','],%[^','],%d,%d,%d",
|
|
(char*)&SensorName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&psStats->body, (char*)&GfxFile,(char*)&mountGfx,
|
|
&psStats->range, (char*)&location, (char*)&type, &psStats->time, &psStats->power, &designable);
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, SensorName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_SENSOR_START + i;
|
|
|
|
if (!strcmp(location,"DEFAULT"))
|
|
{
|
|
psStats->location = LOC_DEFAULT;
|
|
}
|
|
else if(!strcmp(location, "TURRET"))
|
|
{
|
|
psStats->location = LOC_TURRET;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( false, "Invalid Sensor location" );
|
|
}
|
|
|
|
if (!strcmp(type,"STANDARD"))
|
|
{
|
|
psStats->type = STANDARD_SENSOR;
|
|
}
|
|
else if(!strcmp(type, "INDIRECT CB"))
|
|
{
|
|
psStats->type = INDIRECT_CB_SENSOR;
|
|
}
|
|
else if(!strcmp(type, "VTOL CB"))
|
|
{
|
|
psStats->type = VTOL_CB_SENSOR;
|
|
}
|
|
else if(!strcmp(type, "VTOL INTERCEPT"))
|
|
{
|
|
psStats->type = VTOL_INTERCEPT_SENSOR;
|
|
}
|
|
else if(!strcmp(type, "SUPER"))
|
|
{
|
|
psStats->type = SUPER_SENSOR;
|
|
}
|
|
else if (!strcmp(type, "RADAR DETECTOR"))
|
|
{
|
|
psStats->type = RADAR_DETECTOR_SENSOR;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( false, "Invalid Sensor type" );
|
|
}
|
|
|
|
//multiply time stats
|
|
psStats->time *= WEAPON_TIME;
|
|
|
|
//set design flag
|
|
psStats->designable = (designable != 0);
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the sensor PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
if (strcmp(mountGfx, "0"))
|
|
{
|
|
psStats->pMountGraphic = (iIMDShape *) resGetData("IMD", mountGfx);
|
|
if (psStats->pMountGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the mount PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pMountGraphic = NULL;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetSensor(psStats, i);
|
|
|
|
// set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxSensorRange(psStats->range);
|
|
setMaxSensorPower(psStats->power);
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pSensorData = strchr(pSensorData,'\n') + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*Load the ECM stats from the file exported from Access*/
|
|
BOOL loadECMStats(const char *pECMData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumECM = numCR(pECMData, bufferSize);
|
|
ECM_STATS sStats, * const psStats = &sStats;
|
|
unsigned int i = 0, designable;
|
|
char ECMName[MAX_STR_LENGTH], location[MAX_STR_LENGTH],
|
|
GfxFile[MAX_STR_LENGTH];
|
|
char mountGfx[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
if (!statsAllocECM(NumECM))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i=0; i < NumECM; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(ECM_STATS));
|
|
|
|
ECMName[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
mountGfx[0] = '\0';
|
|
location[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pECMData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],%[^','],\
|
|
%[^','],%d,%d,%d",
|
|
(char*)&ECMName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&psStats->body, (char*)&GfxFile, (char*)&mountGfx, (char*)&location, &psStats->power,
|
|
&psStats->range, &designable);
|
|
|
|
// set a default ECM range for now
|
|
psStats->range = TILE_UNITS * 8;
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, ECMName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_ECM_START + i;
|
|
|
|
if (!strcmp(location,"DEFAULT"))
|
|
{
|
|
psStats->location = LOC_DEFAULT;
|
|
}
|
|
else if(!strcmp(location, "TURRET"))
|
|
{
|
|
psStats->location = LOC_TURRET;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( false, "Invalid ECM location" );
|
|
}
|
|
|
|
//set design flag
|
|
psStats->designable = (designable != 0);
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the ECM PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
if (strcmp(mountGfx, "0"))
|
|
{
|
|
psStats->pMountGraphic = (iIMDShape *) resGetData("IMD", mountGfx);
|
|
if (psStats->pMountGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the mount PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//set to NULL
|
|
psStats->pMountGraphic = NULL;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetECM(psStats, i);
|
|
|
|
// Set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxECMPower(psStats->power);
|
|
setMaxECMRange(psStats->range);
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pECMData = strchr(pECMData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Repair stats from the file exported from Access*/
|
|
BOOL loadRepairStats(const char *pRepairData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumRepair = numCR(pRepairData, bufferSize);
|
|
REPAIR_STATS sStats, * const psStats = &sStats;
|
|
unsigned int i = 0, designable, repairArmour;
|
|
char RepairName[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH],
|
|
GfxFile[MAX_STR_LENGTH], mountGfx[MAX_STR_LENGTH],
|
|
location[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
if (!statsAllocRepair(NumRepair))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i=0; i < NumRepair; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(REPAIR_STATS));
|
|
|
|
RepairName[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
mountGfx[0] = '\0';
|
|
location[0] = '\0';
|
|
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pRepairData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%[^','],%d,%d,%d",
|
|
(char*)&RepairName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&repairArmour, (char*)&location, (char*)&GfxFile, (char*)&mountGfx,
|
|
&psStats->repairPoints, &psStats->time,&designable);
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, RepairName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_REPAIR_START + i;
|
|
|
|
if (!strcmp(location,"DEFAULT"))
|
|
{
|
|
psStats->location = LOC_DEFAULT;
|
|
}
|
|
else if(!strcmp(location, "TURRET"))
|
|
{
|
|
psStats->location = LOC_TURRET;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( false, "Invalid Repair location" );
|
|
}
|
|
|
|
//multiply time stats
|
|
psStats->time *= WEAPON_TIME;
|
|
|
|
//check its not 0 since we will be dividing by it at a later stage
|
|
if (psStats->time == 0)
|
|
{
|
|
|
|
ASSERT( false, "loadRepairStats: the delay time cannot be zero for %s",
|
|
psStats->pName );
|
|
|
|
psStats->time = 1;
|
|
}
|
|
|
|
psStats->repairArmour = (repairArmour != 0);
|
|
|
|
//set design flag
|
|
psStats->designable = (designable != 0);
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the Repair PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
if (strcmp(mountGfx, "0"))
|
|
{
|
|
psStats->pMountGraphic = (iIMDShape *) resGetData("IMD", mountGfx);
|
|
if (psStats->pMountGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the Repair mount PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//set to NULL
|
|
psStats->pMountGraphic = NULL;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetRepair(psStats, i);
|
|
|
|
//set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxRepairPoints(psStats->repairPoints);
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pRepairData = strchr(pRepairData,'\n') + 1;
|
|
}
|
|
// free(pData);
|
|
// free(psStats);
|
|
return true;
|
|
}
|
|
|
|
/*Load the Construct stats from the file exported from Access*/
|
|
BOOL loadConstructStats(const char *pConstructData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumConstruct = numCR(pConstructData, bufferSize);
|
|
CONSTRUCT_STATS sStats, * const psStats = &sStats;
|
|
unsigned int i = 0, designable;
|
|
char ConstructName[MAX_STR_LENGTH], GfxFile[MAX_STR_LENGTH];
|
|
char mountGfx[MAX_STR_LENGTH], dummy[MAX_STR_LENGTH];
|
|
UDWORD dummyVal;
|
|
|
|
if (!statsAllocConstruct(NumConstruct))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (i=0; i < NumConstruct; i++)
|
|
{
|
|
memset(psStats, 0, sizeof(CONSTRUCT_STATS));
|
|
|
|
ConstructName[0] = '\0';
|
|
GfxFile[0] = '\0';
|
|
mountGfx[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pConstructData,"%[^','],%[^','],%d,%d,%d,%d,%d,%d,%[^','],\
|
|
%[^','],%d,%d",
|
|
(char*)&ConstructName, (char*)&dummy, &psStats->buildPower,&psStats->buildPoints,
|
|
&psStats->weight, &dummyVal, &dummyVal,
|
|
&psStats->body, (char*)&GfxFile, (char*)&mountGfx,
|
|
&psStats->constructPoints,&designable);
|
|
|
|
if (!allocateStatName((BASE_STATS *)psStats, ConstructName))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psStats->ref = REF_CONSTRUCT_START + i;
|
|
|
|
//set design flag
|
|
if (designable)
|
|
{
|
|
psStats->designable = true;
|
|
}
|
|
else
|
|
{
|
|
psStats->designable = false;
|
|
}
|
|
|
|
//get the IMD for the component
|
|
if (strcmp(GfxFile, "0"))
|
|
{
|
|
psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
|
|
if (psStats->pIMD == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the constructor PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psStats->pIMD = NULL;
|
|
}
|
|
|
|
if (strcmp(mountGfx, "0"))
|
|
{
|
|
psStats->pMountGraphic = (iIMDShape *) resGetData("IMD", mountGfx);
|
|
if (psStats->pMountGraphic == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the mount PIE for record %s", getStatName(psStats) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//set to NULL
|
|
psStats->pMountGraphic = NULL;
|
|
}
|
|
|
|
//save the stats
|
|
statsSetConstruct(psStats, i);
|
|
|
|
// Set the max stat values for the design screen
|
|
if (psStats->designable)
|
|
{
|
|
setMaxConstPoints(psStats->constructPoints);
|
|
setMaxComponentWeight(psStats->weight);
|
|
}
|
|
|
|
//increment the pointer to the start of the next record
|
|
pConstructData = strchr(pConstructData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*Load the Propulsion Types from the file exported from Access*/
|
|
BOOL loadPropulsionTypes(const char *pPropTypeData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumTypes = PROPULSION_TYPE_NUM;
|
|
PROPULSION_TYPES *pPropType;
|
|
unsigned int i, multiplier;
|
|
PROPULSION_TYPE type;
|
|
char PropulsionName[MAX_STR_LENGTH], flightName[MAX_STR_LENGTH];
|
|
|
|
//allocate storage for the stats
|
|
asPropulsionTypes = (PROPULSION_TYPES *)malloc(sizeof(PROPULSION_TYPES)*NumTypes);
|
|
if (asPropulsionTypes == NULL)
|
|
{
|
|
debug( LOG_FATAL, "PropulsionTypes - Out of memory" );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
memset(asPropulsionTypes, 0, (sizeof(PROPULSION_TYPES)*NumTypes));
|
|
|
|
for (i=0; i < NumTypes; i++)
|
|
{
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pPropTypeData,"%[^','],%[^','],%d",
|
|
(char*)&PropulsionName, (char*)&flightName, &multiplier);
|
|
|
|
//set the pointer for this record based on the name
|
|
if (!getPropulsionType(PropulsionName, &type))
|
|
{
|
|
debug( LOG_FATAL, "loadPropulsionTypes: Invalid Propulsion type - %s", PropulsionName );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
pPropType = asPropulsionTypes + type;
|
|
|
|
if (!strcmp(flightName, "GROUND"))
|
|
{
|
|
pPropType->travel = GROUND;
|
|
}
|
|
else if (!strcmp(flightName, "AIR"))
|
|
{
|
|
pPropType->travel = AIR;
|
|
}
|
|
else
|
|
{
|
|
ASSERT( false, "Invalid travel type for Propulsion" );
|
|
}
|
|
|
|
//don't care about this anymore! AB FRIDAY 13/11/98
|
|
//want it back again! AB 27/11/98
|
|
if (multiplier > UWORD_MAX)
|
|
{
|
|
ASSERT( false, "loadPropulsionTypes: power Ratio multiplier too high" );
|
|
//set to a default value since not life threatening!
|
|
multiplier = 100;
|
|
}
|
|
pPropType->powerRatioMult = (UWORD)multiplier;
|
|
|
|
//initialise all the sound variables
|
|
pPropType->startID = NO_SOUND;
|
|
pPropType->idleID = NO_SOUND;
|
|
pPropType->moveOffID = NO_SOUND;
|
|
pPropType->moveID = NO_SOUND;
|
|
pPropType->hissID = NO_SOUND;
|
|
pPropType->shutDownID = NO_SOUND;
|
|
|
|
//increment the pointer to the start of the next record
|
|
pPropTypeData = strchr(pPropTypeData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*Load the Terrain Table from the file exported from Access*/
|
|
BOOL loadTerrainTable(const char *pTerrainTableData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumEntries = numCR(pTerrainTableData, bufferSize);
|
|
unsigned int i;
|
|
UDWORD terrainType, propulsionType, speedFactor;
|
|
|
|
//allocate storage for the stats
|
|
asTerrainTable = (TERRAIN_TABLE *)malloc(sizeof(*asTerrainTable) * PROPULSION_TYPE_NUM * TER_MAX);
|
|
|
|
if (asTerrainTable == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Terrain Types - Out of memory" );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
//initialise the storage to 100
|
|
for (i = 0; i < TER_MAX; ++i)
|
|
{
|
|
unsigned int j;
|
|
for (j = 0; j < PROPULSION_TYPE_NUM; j++)
|
|
{
|
|
TERRAIN_TABLE * const pTerrainTable = &asTerrainTable[i * PROPULSION_TYPE_NUM + j];
|
|
pTerrainTable->speedFactor = 100;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NumEntries; ++i)
|
|
{
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pTerrainTableData,"%d,%d,%d",
|
|
&terrainType, &propulsionType, &speedFactor);
|
|
//store the speed factor at the correct location from the start
|
|
storeSpeedFactor(terrainType, propulsionType, speedFactor);
|
|
//increment the pointer to the start of the next record
|
|
pTerrainTableData = strchr(pTerrainTableData,'\n') + 1;
|
|
}
|
|
|
|
//check that none of the entries are 0 otherwise this will stop a droid dead in its tracks
|
|
//and it will not be able to move again!
|
|
for (i = 0; i < TER_MAX; ++i)
|
|
{
|
|
unsigned int j;
|
|
for (j = 0; j < PROPULSION_TYPE_NUM; ++j)
|
|
{
|
|
TERRAIN_TABLE * const pTerrainTable = asTerrainTable + (i * PROPULSION_TYPE_NUM + j);
|
|
if (pTerrainTable->speedFactor == 0)
|
|
{
|
|
debug( LOG_FATAL, "loadTerrainTable: Invalid propulsion/terrain table entry" );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Special Ability stats from the file exported from Access*/
|
|
BOOL loadSpecialAbility(const char *pSAbilityData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumTypes = numCR(pSAbilityData, bufferSize);
|
|
SPECIAL_ABILITY *pSAbility;
|
|
unsigned int i, accessID;
|
|
char SAbilityName[MAX_STR_LENGTH];
|
|
|
|
//allocate storage for the stats
|
|
asSpecialAbility = (SPECIAL_ABILITY *)malloc(sizeof(SPECIAL_ABILITY)*NumTypes);
|
|
|
|
if (asSpecialAbility == NULL)
|
|
{
|
|
debug( LOG_FATAL, "SpecialAbility - Out of memory" );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
numSpecialAbility = NumTypes;
|
|
|
|
memset(asSpecialAbility, 0, (sizeof(SPECIAL_ABILITY)*NumTypes));
|
|
|
|
//copy the start location
|
|
pSAbility = asSpecialAbility;
|
|
|
|
for (i=0; i < NumTypes; i++)
|
|
{
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pSAbilityData,"%[^','],%d",(char*)&SAbilityName, &accessID);
|
|
//check that the data is ordered in the way it will be stored
|
|
if (accessID != i)
|
|
{
|
|
debug( LOG_FATAL, "The Special Ability sequence is invalid" );
|
|
abort();
|
|
return false;
|
|
}
|
|
//allocate storage for the name
|
|
asSpecialAbility->pName = (char *)malloc((strlen(SAbilityName))+1);
|
|
if (asSpecialAbility->pName == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Special Ability Name - Out of memory" );
|
|
abort();
|
|
return false;
|
|
}
|
|
strcpy(asSpecialAbility->pName,SAbilityName);
|
|
|
|
//increment the pointer to the start of the next record
|
|
pSAbilityData = strchr(pSAbilityData,'\n') + 1;
|
|
asSpecialAbility++;
|
|
}
|
|
|
|
//reset the pointer to the start of the special ability stats
|
|
asSpecialAbility = pSAbility;
|
|
return true;
|
|
}
|
|
|
|
/* load the IMDs to use for each body-propulsion combination */
|
|
BOOL loadBodyPropulsionIMDs(const char *pData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumTypes = numCR(pData, bufferSize);
|
|
BODY_STATS *psBodyStat = asBodyStats;
|
|
PROPULSION_STATS *psPropulsionStat = asPropulsionStats;
|
|
unsigned int i, numStats;
|
|
char bodyName[MAX_STR_LENGTH], propulsionName[MAX_STR_LENGTH],
|
|
leftIMD[MAX_STR_LENGTH], rightIMD[MAX_STR_LENGTH];
|
|
iIMDShape **startIMDs;
|
|
BOOL found;
|
|
|
|
//check that the body and propulsion stats have already been read in
|
|
|
|
ASSERT( asBodyStats != NULL, "Body Stats have not been set up" );
|
|
ASSERT( asPropulsionStats != NULL, "Propulsion Stats have not been set up" );
|
|
|
|
//allocate space
|
|
for (numStats = 0; numStats < numBodyStats; numStats++)
|
|
{
|
|
psBodyStat = &asBodyStats[numStats];
|
|
psBodyStat->ppIMDList = (iIMDShape **) malloc(numPropulsionStats * NUM_PROP_SIDES * sizeof(iIMDShape *));
|
|
if (psBodyStat->ppIMDList == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Out of memory" );
|
|
abort();
|
|
}
|
|
//initialise the pointer space
|
|
memset(psBodyStat->ppIMDList, 0, (numPropulsionStats *
|
|
NUM_PROP_SIDES * sizeof(iIMDShape *)));
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i < NumTypes; i++)
|
|
{
|
|
bodyName[0] = '\0';
|
|
propulsionName[0] = '\0';
|
|
leftIMD[0] = '\0';
|
|
rightIMD[0] = '\0';
|
|
|
|
/*read the data into the storage - the data is delimited using comma's
|
|
not interested in the last number - needed for sscanf*/
|
|
sscanf(pData,"%[^','],%[^','],%[^','],%[^','],%*d",(char*)&bodyName,
|
|
(char*)&propulsionName, (char*)&leftIMD, (char*)&rightIMD);
|
|
|
|
//get the body stats
|
|
found = false;
|
|
|
|
for (numStats = 0; numStats < numBodyStats; numStats++)
|
|
{
|
|
psBodyStat = &asBodyStats[numStats];
|
|
if (!strcmp(psBodyStat->pName, bodyName))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
debug( LOG_FATAL, "loadBodyPropulsionPIEs: Invalid body name %s", bodyName );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
//get the propulsion stats
|
|
found = false;
|
|
|
|
for (numStats = 0; numStats < numPropulsionStats; numStats++)
|
|
{
|
|
psPropulsionStat = &asPropulsionStats[numStats];
|
|
if (!strcmp(psPropulsionStat->pName, propulsionName))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
debug( LOG_FATAL, "Invalid propulsion name %s", propulsionName );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
//allocate the left and right propulsion IMDs
|
|
startIMDs = psBodyStat->ppIMDList;
|
|
psBodyStat->ppIMDList += (numStats * NUM_PROP_SIDES);
|
|
if (strcmp(leftIMD, "0"))
|
|
{
|
|
*psBodyStat->ppIMDList = (iIMDShape *) resGetData("IMD", leftIMD);
|
|
if (*psBodyStat->ppIMDList == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the left propulsion PIE for body %s", bodyName );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*psBodyStat->ppIMDList = NULL;
|
|
}
|
|
|
|
psBodyStat->ppIMDList++;
|
|
//right IMD might not be there
|
|
if (strcmp(rightIMD, "0"))
|
|
{
|
|
*psBodyStat->ppIMDList = (iIMDShape *) resGetData("IMD", rightIMD);
|
|
if (*psBodyStat->ppIMDList == NULL)
|
|
{
|
|
debug( LOG_FATAL, "Cannot find the right propulsion PIE for body %s", bodyName );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*psBodyStat->ppIMDList = NULL;
|
|
}
|
|
|
|
//reset the IMDList pointer
|
|
psBodyStat->ppIMDList = startIMDs;
|
|
|
|
//increment the pointer to the start of the next record
|
|
pData = strchr(pData,'\n') + 1;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
|
|
|
|
static BOOL
|
|
statsGetAudioIDFromString( char *szStatName, char *szWavName, SDWORD *piWavID )
|
|
{
|
|
if ( strcmp( szWavName, "-1" ) == 0 )
|
|
{
|
|
*piWavID = NO_SOUND;
|
|
}
|
|
else
|
|
{
|
|
if ( (*piWavID = audio_GetIDFromStr(szWavName)) == NO_SOUND )
|
|
{
|
|
debug( LOG_FATAL, "statsGetAudioIDFromString: couldn't get ID %d for sound %s", *piWavID, szWavName );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
if ((*piWavID < 0
|
|
|| *piWavID > ID_MAX_SOUND)
|
|
&& *piWavID != NO_SOUND)
|
|
{
|
|
debug( LOG_FATAL, "statsGetAudioIDFromString: Invalid ID - %d for sound %s", *piWavID, szStatName );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*Load the weapon sounds from the file exported from Access*/
|
|
BOOL loadWeaponSounds(const char *pSoundData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumRecords = numCR(pSoundData, bufferSize);
|
|
SDWORD i, weaponSoundID, explosionSoundID, inc, iDum;
|
|
char WeaponName[MAX_STR_LENGTH];
|
|
char szWeaponWav[MAX_STR_LENGTH], szExplosionWav[MAX_STR_LENGTH];
|
|
|
|
ASSERT( asWeaponStats != NULL, "loadWeaponSounds: Weapon stats not loaded" );
|
|
|
|
for (i=0; i < NumRecords; i++)
|
|
{
|
|
WeaponName[0] = '\0';
|
|
szWeaponWav[0] = '\0';
|
|
szExplosionWav[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pSoundData,"%[^','],%[^','],%[^','],%d",
|
|
WeaponName, szWeaponWav, szExplosionWav, &iDum);
|
|
|
|
if ( statsGetAudioIDFromString( WeaponName, szWeaponWav, &weaponSoundID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( WeaponName, szExplosionWav, &explosionSoundID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (inc = 0; inc < (SDWORD)numWeaponStats; inc++)
|
|
{
|
|
if (!strcmp(asWeaponStats[inc].pName, WeaponName))
|
|
{
|
|
asWeaponStats[inc].iAudioFireID = weaponSoundID;
|
|
asWeaponStats[inc].iAudioImpactID = explosionSoundID;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT_OR_RETURN(false, inc != (SDWORD)numWeaponStats, "Weapon stat not found - %s", WeaponName);
|
|
|
|
//increment the pointer to the start of the next record
|
|
pSoundData = strchr(pSoundData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the Weapon Effect Modifiers from the file exported from Access*/
|
|
BOOL loadWeaponModifiers(const char *pWeapModData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumRecords = numCR(pWeapModData, bufferSize);
|
|
PROPULSION_TYPE propInc;
|
|
WEAPON_EFFECT effectInc;
|
|
UDWORD i, j, modifier;
|
|
char weaponEffectName[MAX_STR_LENGTH], propulsionName[MAX_STR_LENGTH];
|
|
|
|
//initialise to 100%
|
|
for (i=0; i < WE_NUMEFFECTS; i++)
|
|
{
|
|
for (j=0; j < PROPULSION_TYPE_NUM; j++)
|
|
{
|
|
asWeaponModifier[i][j] = 100;
|
|
}
|
|
}
|
|
|
|
for (i=0; i < NumRecords; i++)
|
|
{
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pWeapModData,"%[^','],%[^','],%d",
|
|
(char*)&weaponEffectName, (char*)&propulsionName, &modifier);
|
|
|
|
//get the weapon effect inc
|
|
if (!getWeaponEffect(weaponEffectName, &effectInc))
|
|
{
|
|
debug( LOG_FATAL, "loadWeaponModifiers: Invalid Weapon Effect - %s", weaponEffectName );
|
|
abort();
|
|
return false;
|
|
}
|
|
//get the propulsion inc
|
|
if (!getPropulsionType(propulsionName, &propInc))
|
|
{
|
|
debug( LOG_FATAL, "loadWeaponModifiers: Invalid Propulsion type - %s", propulsionName );
|
|
abort();
|
|
return false;
|
|
}
|
|
|
|
if (modifier > UWORD_MAX)
|
|
{
|
|
debug( LOG_FATAL, "loadWeaponModifiers: modifier for effect %s, prop type %s is too large", weaponEffectName, propulsionName );
|
|
abort();
|
|
return false;
|
|
}
|
|
//store in the appropriate index
|
|
asWeaponModifier[effectInc][propInc] = (UWORD)modifier;
|
|
|
|
//increment the pointer to the start of the next record
|
|
pWeapModData = strchr(pWeapModData,'\n') + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*Load the propulsion type sounds from the file exported from Access*/
|
|
BOOL loadPropulsionSounds(const char *pPropSoundData, UDWORD bufferSize)
|
|
{
|
|
const unsigned int NumRecords = numCR(pPropSoundData, bufferSize);
|
|
SDWORD i, startID, idleID, moveOffID, moveID,
|
|
hissID, shutDownID, iDum;
|
|
char propulsionName[MAX_STR_LENGTH], szStart[MAX_STR_LENGTH],
|
|
szIdle[MAX_STR_LENGTH], szMoveOff[MAX_STR_LENGTH],
|
|
szMove[MAX_STR_LENGTH], szHiss[MAX_STR_LENGTH],
|
|
szShutDown[MAX_STR_LENGTH];
|
|
PROPULSION_TYPE type;
|
|
PROPULSION_TYPES *pPropType;
|
|
|
|
|
|
ASSERT( asPropulsionTypes != NULL,
|
|
"loadPropulsionSounds: Propulsion type stats not loaded" );
|
|
|
|
for (i=0; i < NumRecords; i++)
|
|
{
|
|
|
|
propulsionName[0] = '\0';
|
|
//read the data into the storage - the data is delimeted using comma's
|
|
sscanf(pPropSoundData,"%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%d",
|
|
propulsionName, szStart, szIdle, szMoveOff, szMove, szHiss, szShutDown, &iDum);
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szStart, &startID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szIdle, &idleID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szMoveOff, &moveOffID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szMove, &moveID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szHiss, &hissID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( statsGetAudioIDFromString( propulsionName, szShutDown, &shutDownID ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!getPropulsionType(propulsionName, &type))
|
|
{
|
|
debug( LOG_FATAL, "loadPropulsionSounds: Invalid Propulsion type - %s", propulsionName );
|
|
abort();
|
|
return false;
|
|
}
|
|
pPropType = asPropulsionTypes + type;
|
|
pPropType->startID = (SWORD)startID;
|
|
pPropType->idleID = (SWORD)idleID;
|
|
pPropType->moveOffID = (SWORD)moveOffID;
|
|
pPropType->moveID = (SWORD)moveID;
|
|
pPropType->hissID = (SWORD)hissID;
|
|
pPropType->shutDownID = (SWORD)shutDownID;
|
|
|
|
//increment the pointer to the start of the next record
|
|
pPropSoundData = strchr(pPropSoundData,'\n') + 1;
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Set stats functions
|
|
*******************************************************************************/
|
|
/* Set the stats for a particular weapon type */
|
|
void statsSetWeapon(WEAPON_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asWeaponStats, index, WEAPON_STATS, REF_WEAPON_START);
|
|
}
|
|
/* Set the stats for a particular body type */
|
|
void statsSetBody(BODY_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asBodyStats, index, BODY_STATS, REF_BODY_START);
|
|
}
|
|
/* Set the stats for a particular brain type */
|
|
void statsSetBrain(BRAIN_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asBrainStats, index, BRAIN_STATS, REF_BRAIN_START);
|
|
}
|
|
/* Set the stats for a particular power type */
|
|
void statsSetPropulsion(PROPULSION_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asPropulsionStats, index, PROPULSION_STATS,
|
|
REF_PROPULSION_START);
|
|
}
|
|
/* Set the stats for a particular sensor type */
|
|
void statsSetSensor(SENSOR_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asSensorStats, index, SENSOR_STATS, REF_SENSOR_START);
|
|
}
|
|
/* Set the stats for a particular ecm type */
|
|
void statsSetECM(ECM_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asECMStats, index, ECM_STATS, REF_ECM_START);
|
|
}
|
|
/* Set the stats for a particular repair type */
|
|
void statsSetRepair(REPAIR_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asRepairStats, index, REPAIR_STATS, REF_REPAIR_START);
|
|
}
|
|
/* Set the stats for a particular construct type */
|
|
void statsSetConstruct(CONSTRUCT_STATS *psStats, UDWORD index)
|
|
{
|
|
SET_STATS(psStats, asConstructStats, index, CONSTRUCT_STATS, REF_CONSTRUCT_START);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Get stats functions
|
|
*******************************************************************************/
|
|
WEAPON_STATS *statsGetWeapon(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_WEAPON_START) && (ref < REF_WEAPON_START + REF_RANGE),
|
|
"statsGetWeapon: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numWeaponStats; index++)
|
|
{
|
|
if (asWeaponStats[index].ref == ref)
|
|
{
|
|
return &asWeaponStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetWeapon: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
BODY_STATS *statsGetBody(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_BODY_START) && (ref < REF_BODY_START + REF_RANGE),
|
|
"statsGetBody: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numBodyStats; index++)
|
|
{
|
|
if (asBodyStats[index].ref == ref)
|
|
{
|
|
return &asBodyStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetBody: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
BRAIN_STATS *statsGetBrain(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_BRAIN_START) && (ref < REF_BRAIN_START + REF_RANGE),
|
|
"statsGetBrain: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numBrainStats; index++)
|
|
{
|
|
if (asBrainStats[index].ref == ref)
|
|
{
|
|
return &asBrainStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetBrain: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
PROPULSION_STATS *statsGetPropulsion(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_PROPULSION_START) && (ref < REF_PROPULSION_START +
|
|
REF_RANGE),
|
|
"statsGetPropulsion: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numPropulsionStats; index++)
|
|
{
|
|
if (asPropulsionStats[index].ref == ref)
|
|
{
|
|
return &asPropulsionStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetPropulsion: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
SENSOR_STATS *statsGetSensor(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_SENSOR_START) && (ref < REF_SENSOR_START + REF_RANGE),
|
|
"statsGetSensor: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numSensorStats; index++)
|
|
{
|
|
if (asSensorStats[index].ref == ref)
|
|
{
|
|
return &asSensorStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetSensor: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
ECM_STATS *statsGetECM(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_ECM_START) && (ref < REF_ECM_START + REF_RANGE),
|
|
"statsGetECM: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numECMStats; index++)
|
|
{
|
|
if (asECMStats[index].ref == ref)
|
|
{
|
|
return &asECMStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetECM: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
REPAIR_STATS *statsGetRepair(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_REPAIR_START) && (ref < REF_REPAIR_START + REF_RANGE),
|
|
"statsGetRepair: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numRepairStats; index++)
|
|
{
|
|
if (asRepairStats[index].ref == ref)
|
|
{
|
|
return &asRepairStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetRepair: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
CONSTRUCT_STATS *statsGetConstruct(UDWORD ref)
|
|
{
|
|
UDWORD index;
|
|
ASSERT( (ref >= REF_CONSTRUCT_START) && (ref < REF_CONSTRUCT_START + REF_RANGE),
|
|
"statsGetConstruct: Invalid reference number: %x", ref );
|
|
|
|
for (index = 0; index < numConstructStats; index++)
|
|
{
|
|
if (asConstructStats[index].ref == ref)
|
|
{
|
|
return &asConstructStats[index];
|
|
}
|
|
}
|
|
ASSERT( false, "statsGetConstruct: Reference number not found in list: %x", ref );
|
|
return NULL; // should never get here, but this stops the compiler complaining.
|
|
}
|
|
|
|
/***********************************************************************************
|
|
* Dealloc the extra storage tables
|
|
***********************************************************************************/
|
|
//Deallocate the storage assigned for the Propulsion Types table
|
|
void deallocPropulsionTypes(void)
|
|
{
|
|
free(asPropulsionTypes);
|
|
asPropulsionTypes = NULL;
|
|
}
|
|
|
|
//dealloc the storage assigned for the terrain table
|
|
void deallocTerrainTable(void)
|
|
{
|
|
free(asTerrainTable);
|
|
asTerrainTable = NULL;
|
|
}
|
|
|
|
//dealloc the storage assigned for the Special Ability stats
|
|
void deallocSpecialAbility(void)
|
|
{
|
|
UBYTE inc;
|
|
SPECIAL_ABILITY* pList = asSpecialAbility;
|
|
|
|
for (inc=0; inc < numSpecialAbility; inc++, pList++)
|
|
{
|
|
free(pList->pName);
|
|
}
|
|
free(asSpecialAbility);
|
|
asSpecialAbility = NULL;
|
|
}
|
|
|
|
//store the speed Factor in the terrain table
|
|
void storeSpeedFactor(UDWORD terrainType, UDWORD propulsionType, UDWORD speedFactor)
|
|
{
|
|
TERRAIN_TABLE *pTerrainTable = asTerrainTable;
|
|
|
|
ASSERT( propulsionType < PROPULSION_TYPE_NUM,
|
|
"The propulsion type is too large" );
|
|
|
|
pTerrainTable += (terrainType * PROPULSION_TYPE_NUM + propulsionType);
|
|
pTerrainTable->speedFactor = speedFactor;
|
|
}
|
|
|
|
//get the speed factor for a given terrain type and propulsion type
|
|
UDWORD getSpeedFactor(UDWORD type, UDWORD propulsionType)
|
|
{
|
|
TERRAIN_TABLE *pTerrainTable = asTerrainTable;
|
|
|
|
ASSERT( propulsionType < PROPULSION_TYPE_NUM,
|
|
"The propulsion type is too large" );
|
|
|
|
pTerrainTable += (type * PROPULSION_TYPE_NUM + propulsionType);
|
|
|
|
return pTerrainTable->speedFactor;
|
|
}
|
|
|
|
//return the type of stat this stat is!
|
|
UDWORD statType(UDWORD ref)
|
|
{
|
|
if (ref >=REF_BODY_START && ref < REF_BODY_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_BODY;
|
|
}
|
|
if (ref >=REF_BRAIN_START && ref < REF_BRAIN_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_BRAIN;
|
|
}
|
|
if (ref >=REF_PROPULSION_START && ref <
|
|
REF_PROPULSION_START + REF_RANGE)
|
|
{
|
|
return COMP_PROPULSION;
|
|
}
|
|
if (ref >=REF_SENSOR_START && ref < REF_SENSOR_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_SENSOR;
|
|
}
|
|
if (ref >=REF_ECM_START && ref < REF_ECM_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_ECM;
|
|
}
|
|
if (ref >=REF_REPAIR_START && ref < REF_REPAIR_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_REPAIRUNIT;
|
|
}
|
|
if (ref >=REF_WEAPON_START && ref < REF_WEAPON_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_WEAPON;
|
|
}
|
|
if (ref >=REF_CONSTRUCT_START && ref < REF_CONSTRUCT_START +
|
|
REF_RANGE)
|
|
{
|
|
return COMP_CONSTRUCT;
|
|
}
|
|
//else
|
|
ASSERT( false, "Invalid stat pointer - cannot determine Stat Type" );
|
|
return COMP_UNKNOWN;
|
|
}
|
|
|
|
//return the REF_START value of this type of stat
|
|
UDWORD statRefStart(UDWORD stat)
|
|
{
|
|
UDWORD start;
|
|
|
|
switch (stat)
|
|
{
|
|
case COMP_BODY:
|
|
{
|
|
start = REF_BODY_START;
|
|
break;
|
|
}
|
|
case COMP_BRAIN:
|
|
{
|
|
start = REF_BRAIN_START;
|
|
break;
|
|
}
|
|
case COMP_PROPULSION:
|
|
{
|
|
start = REF_PROPULSION_START;
|
|
break;
|
|
}
|
|
case COMP_SENSOR:
|
|
{
|
|
start = REF_SENSOR_START;
|
|
break;
|
|
}
|
|
case COMP_ECM:
|
|
{
|
|
start = REF_ECM_START;
|
|
break;
|
|
}
|
|
case COMP_REPAIRUNIT:
|
|
{
|
|
start = REF_REPAIR_START;
|
|
break;
|
|
}
|
|
case COMP_WEAPON:
|
|
{
|
|
start = REF_WEAPON_START;
|
|
break;
|
|
}
|
|
case COMP_CONSTRUCT:
|
|
{
|
|
start = REF_CONSTRUCT_START;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//COMP_UNKNOWN should be an error
|
|
debug( LOG_FATAL, "Invalid stat type" );
|
|
abort();
|
|
start = 0;
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
|
|
/*Returns the component type based on the string - used for reading in data */
|
|
unsigned int componentType(const char* pType)
|
|
{
|
|
if (!strcmp(pType, "BODY"))
|
|
{
|
|
return COMP_BODY;
|
|
}
|
|
if (!strcmp(pType, "PROPULSION"))
|
|
{
|
|
return COMP_PROPULSION;
|
|
}
|
|
if (!strcmp(pType, "BRAIN"))
|
|
{
|
|
return COMP_BRAIN;
|
|
}
|
|
if (!strcmp(pType, "REPAIR"))
|
|
{
|
|
return COMP_REPAIRUNIT;
|
|
}
|
|
if (!strcmp(pType, "ECM"))
|
|
{
|
|
return COMP_ECM;
|
|
}
|
|
if (!strcmp(pType, "SENSOR"))
|
|
{
|
|
return COMP_SENSOR;
|
|
}
|
|
if (!strcmp(pType, "WEAPON"))
|
|
{
|
|
return COMP_WEAPON;
|
|
}
|
|
if (!strcmp(pType, "CONSTRUCT"))
|
|
{
|
|
return COMP_CONSTRUCT;
|
|
}
|
|
|
|
ASSERT( false, "Unknown Component Type" );
|
|
return 0; // Should never get here.
|
|
}
|
|
|
|
//function to compare a value with yes/no - if neither warns player!
|
|
BOOL compareYes(const char* strToCompare, const char* strOwner)
|
|
{
|
|
if (!strcmp(strToCompare, "YES"))
|
|
{
|
|
return true;
|
|
}
|
|
else if (!strcmp(strToCompare, "NO"))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//set default to false but continue
|
|
//DBERROR(("Invalid yes/no for record %s", strOwner));
|
|
debug( LOG_FATAL, "Invalid yes/no for record %s", getName(strOwner) );
|
|
abort();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//get the component Inc for a stat based on the Resource name and type
|
|
//returns -1 if record not found
|
|
//used in Scripts
|
|
SDWORD getCompFromResName(UDWORD compType, const char *pName)
|
|
{
|
|
return getCompFromName(compType, pName);
|
|
}
|
|
|
|
static void getStatsDetails(UDWORD compType, BASE_STATS **ppsStats, UDWORD *pnumStats, UDWORD *pstatSize)
|
|
{
|
|
|
|
switch (compType)
|
|
{
|
|
case COMP_BODY:
|
|
*ppsStats = (BASE_STATS*)asBodyStats;
|
|
*pnumStats = numBodyStats;
|
|
*pstatSize = sizeof(BODY_STATS);
|
|
break;
|
|
case COMP_BRAIN:
|
|
*ppsStats = (BASE_STATS*)asBrainStats;
|
|
*pnumStats = numBrainStats;
|
|
*pstatSize = sizeof(BRAIN_STATS);
|
|
break;
|
|
case COMP_PROPULSION:
|
|
*ppsStats = (BASE_STATS*)asPropulsionStats;
|
|
*pnumStats = numPropulsionStats;
|
|
*pstatSize = sizeof(PROPULSION_STATS);
|
|
break;
|
|
case COMP_REPAIRUNIT:
|
|
*ppsStats = (BASE_STATS*)asRepairStats;
|
|
*pnumStats = numRepairStats;
|
|
*pstatSize = sizeof(REPAIR_STATS);
|
|
break;
|
|
case COMP_ECM:
|
|
*ppsStats = (BASE_STATS*)asECMStats;
|
|
*pnumStats = numECMStats;
|
|
*pstatSize = sizeof(ECM_STATS);
|
|
break;
|
|
case COMP_SENSOR:
|
|
*ppsStats = (BASE_STATS*)asSensorStats;
|
|
*pnumStats = numSensorStats;
|
|
*pstatSize = sizeof(SENSOR_STATS);
|
|
break;
|
|
case COMP_CONSTRUCT:
|
|
*ppsStats = (BASE_STATS*)asConstructStats;
|
|
*pnumStats = numConstructStats;
|
|
*pstatSize = sizeof(CONSTRUCT_STATS);
|
|
break;
|
|
case COMP_WEAPON:
|
|
*ppsStats = (BASE_STATS*)asWeaponStats;
|
|
*pnumStats = numWeaponStats;
|
|
*pstatSize = sizeof(WEAPON_STATS);
|
|
break;
|
|
default:
|
|
//COMP_UNKNOWN should be an error
|
|
debug( LOG_FATAL, "Invalid component type - game.c" );
|
|
abort();
|
|
}
|
|
}
|
|
|
|
|
|
//get the component Inc for a stat based on the name and type
|
|
//returns -1 if record not found
|
|
SDWORD getCompFromName(UDWORD compType, const char *pName)
|
|
{
|
|
BASE_STATS *psStats = NULL;
|
|
UDWORD numStats = 0, count, statSize = 0;
|
|
|
|
getStatsDetails(compType, &psStats,&numStats,&statSize);
|
|
|
|
//find the stat with the same name
|
|
for(count = 0; count < numStats; count++)
|
|
{
|
|
if (!strcmp(pName, psStats->pName))
|
|
{
|
|
return count;
|
|
}
|
|
psStats = (BASE_STATS*)((char*)psStats + statSize);
|
|
}
|
|
|
|
//return -1 if record not found or an invalid component type is passed in
|
|
return -1;
|
|
}
|
|
|
|
/*return the name to display for the interface - valid for OBJECTS and STATS*/
|
|
const char* getName(const char *pNameID)
|
|
{
|
|
/* See if the name has a string resource associated with it by trying
|
|
* to get the string resource.
|
|
*/
|
|
const char * const name = strresGetString(psStringRes, pNameID);
|
|
if (!name)
|
|
{
|
|
debug( LOG_ERROR, "Unable to find string resource for %s", pNameID );
|
|
return "Name Unknown";
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/*sets the store to the body size based on the name passed in - returns false
|
|
if doesn't compare with any*/
|
|
BOOL getBodySize(const char *pSize, UBYTE *pStore)
|
|
{
|
|
if (!strcmp(pSize, "LIGHT"))
|
|
{
|
|
*pStore = SIZE_LIGHT;
|
|
return true;
|
|
}
|
|
else if (!strcmp(pSize, "MEDIUM"))
|
|
{
|
|
*pStore = SIZE_MEDIUM;
|
|
return true;
|
|
}
|
|
else if (!strcmp(pSize, "HEAVY"))
|
|
{
|
|
*pStore = SIZE_HEAVY;
|
|
return true;
|
|
}
|
|
else if (!strcmp(pSize, "SUPER HEAVY"))
|
|
{
|
|
*pStore = SIZE_SUPER_HEAVY;
|
|
return true;
|
|
}
|
|
|
|
ASSERT( false, "Invalid size - %s", pSize );
|
|
return false;
|
|
}
|
|
|
|
/*returns the weapon sub class based on the string name passed in */
|
|
bool getWeaponSubClass(const char* subClass, WEAPON_SUBCLASS* wclass)
|
|
{
|
|
if (strcmp(subClass, "CANNON") == 0)
|
|
{
|
|
*wclass = WSC_CANNON;
|
|
}
|
|
else if (strcmp(subClass, "MORTARS") == 0)
|
|
{
|
|
*wclass = WSC_MORTARS;
|
|
}
|
|
else if (strcmp(subClass, "MISSILE") == 0)
|
|
{
|
|
*wclass = WSC_MISSILE;
|
|
}
|
|
else if (strcmp(subClass, "ROCKET") == 0)
|
|
{
|
|
*wclass = WSC_ROCKET;
|
|
}
|
|
else if (strcmp(subClass, "ENERGY") == 0)
|
|
{
|
|
*wclass = WSC_ENERGY;
|
|
}
|
|
else if (strcmp(subClass, "GAUSS") == 0)
|
|
{
|
|
*wclass = WSC_GAUSS;
|
|
}
|
|
else if (strcmp(subClass, "FLAME") == 0)
|
|
{
|
|
*wclass = WSC_FLAME;
|
|
}
|
|
else if (strcmp(subClass, "HOWITZERS") == 0)
|
|
{
|
|
*wclass = WSC_HOWITZERS;
|
|
}
|
|
else if (strcmp(subClass, "MACHINE GUN") == 0)
|
|
{
|
|
*wclass = WSC_MGUN;
|
|
}
|
|
else if (strcmp(subClass, "ELECTRONIC") == 0)
|
|
{
|
|
*wclass = WSC_ELECTRONIC;
|
|
}
|
|
else if (strcmp(subClass, "A-A GUN") == 0)
|
|
{
|
|
*wclass = WSC_AAGUN;
|
|
}
|
|
else if (strcmp(subClass, "SLOW MISSILE") == 0)
|
|
{
|
|
*wclass = WSC_SLOWMISSILE;
|
|
}
|
|
else if (strcmp(subClass, "SLOW ROCKET") == 0)
|
|
{
|
|
*wclass = WSC_SLOWROCKET;
|
|
}
|
|
else if (strcmp(subClass, "LAS_SAT") == 0)
|
|
{
|
|
*wclass = WSC_LAS_SAT;
|
|
}
|
|
else if (strcmp(subClass, "BOMB") == 0)
|
|
{
|
|
*wclass = WSC_BOMB;
|
|
}
|
|
else if (strcmp(subClass, "COMMAND") == 0)
|
|
{
|
|
*wclass = WSC_COMMAND;
|
|
}
|
|
else if (strcmp(subClass, "EMP") == 0)
|
|
{
|
|
*wclass = WSC_EMP;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!"Invalid weapon sub class", "Invalid weapon sub class: %s", subClass);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*returns the movement model based on the string name passed in */
|
|
bool getMovementModel(const char* movementModel, MOVEMENT_MODEL* model)
|
|
{
|
|
if (strcmp(movementModel,"DIRECT") == 0)
|
|
{
|
|
*model = MM_DIRECT;
|
|
}
|
|
else if (strcmp(movementModel,"INDIRECT") == 0)
|
|
{
|
|
*model = MM_INDIRECT;
|
|
}
|
|
else if (strcmp(movementModel,"HOMING-DIRECT") == 0)
|
|
{
|
|
*model = MM_HOMINGDIRECT;
|
|
}
|
|
else if (strcmp(movementModel,"HOMING-INDIRECT") == 0)
|
|
{
|
|
*model = MM_HOMINGINDIRECT;
|
|
}
|
|
else if (strcmp(movementModel,"ERRATIC-DIRECT") == 0)
|
|
{
|
|
*model = MM_ERRATICDIRECT;
|
|
}
|
|
else if (strcmp(movementModel,"SWEEP") == 0)
|
|
{
|
|
*model = MM_SWEEP;
|
|
}
|
|
else
|
|
{
|
|
// We've got problem if we got here
|
|
ASSERT(!"Invalid movement model", "Invalid movement model: %s", movementModel);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool getWeaponEffect(const char* weaponEffect, WEAPON_EFFECT* effect)
|
|
{
|
|
if (strcmp(weaponEffect, "ANTI PERSONNEL") == 0)
|
|
{
|
|
*effect = WE_ANTI_PERSONNEL;
|
|
}
|
|
else if (strcmp(weaponEffect, "ANTI TANK") == 0)
|
|
{
|
|
*effect = WE_ANTI_TANK;
|
|
}
|
|
else if (strcmp(weaponEffect, "BUNKER BUSTER") == 0)
|
|
{
|
|
*effect = WE_BUNKER_BUSTER;
|
|
}
|
|
else if (strcmp(weaponEffect, "ARTILLERY ROUND") == 0)
|
|
{
|
|
*effect = WE_ARTILLERY_ROUND;
|
|
}
|
|
else if (strcmp(weaponEffect, "FLAMER") == 0)
|
|
{
|
|
*effect = WE_FLAMER;
|
|
}
|
|
else if (strcmp(weaponEffect, "ANTI AIRCRAFT") == 0 || strcmp(weaponEffect, "ALL ROUNDER") == 0)
|
|
{
|
|
*effect = WE_ANTI_AIRCRAFT;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!"Invalid weapon effect", "Invalid weapon effect: %s", weaponEffect);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
looks up the name to get the resource associated with it - or allocates space
|
|
and stores the name. Eventually ALL names will be 'resourced' for translation
|
|
*/
|
|
char* allocateName(const char* name)
|
|
{
|
|
char * storeName;
|
|
|
|
/* Check whether the given string has a string resource associated with
|
|
* it.
|
|
*/
|
|
if (!strresGetString(psStringRes, name))
|
|
{
|
|
debug(LOG_FATAL, "Unable to find string resource for %s", name);
|
|
abort();
|
|
return NULL;
|
|
}
|
|
|
|
storeName = strdup(name);
|
|
if (!storeName)
|
|
{
|
|
debug(LOG_FATAL, "Out of memory");
|
|
abort();
|
|
return NULL;
|
|
}
|
|
|
|
return storeName;
|
|
}
|
|
|
|
|
|
/*Access functions for the upgradeable stats of a weapon*/
|
|
UDWORD weaponFirePause(const WEAPON_STATS* psStats, UBYTE player)
|
|
{
|
|
if(psStats->reloadTime == 0)
|
|
{
|
|
return (psStats->firePause - (psStats->firePause * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].firePause)/100);
|
|
}
|
|
else
|
|
{
|
|
return psStats->firePause; //fire pause is neglectable for weapons with reload time
|
|
}
|
|
}
|
|
|
|
/* Reload time is reduced for weapons with salvo fire */
|
|
UDWORD weaponReloadTime(WEAPON_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->reloadTime - (psStats->reloadTime * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].firePause)/100);
|
|
}
|
|
|
|
UDWORD weaponShortHit(const WEAPON_STATS* psStats, UBYTE player)
|
|
{
|
|
return (psStats->shortHit + (psStats->shortHit * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].shortHit)/100);
|
|
}
|
|
|
|
UDWORD weaponLongHit(const WEAPON_STATS* psStats, UBYTE player)
|
|
{
|
|
return (psStats->longHit + (psStats->longHit * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].longHit)/100);
|
|
}
|
|
|
|
UDWORD weaponDamage(const WEAPON_STATS* psStats, UBYTE player)
|
|
{
|
|
return (psStats->damage + (psStats->damage * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].damage)/100);
|
|
}
|
|
|
|
UDWORD weaponRadDamage(WEAPON_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->radiusDamage + (psStats->radiusDamage * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].radiusDamage)/100);
|
|
}
|
|
|
|
UDWORD weaponIncenDamage(WEAPON_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->incenDamage + (psStats->incenDamage * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].incenDamage)/100);
|
|
}
|
|
|
|
UDWORD weaponRadiusHit(WEAPON_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->radiusHit + (psStats->radiusHit * asWeaponUpgrade[player][
|
|
psStats->weaponSubClass].radiusHit)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a sensor*/
|
|
UDWORD sensorPower(SENSOR_STATS *psStats, UBYTE player)
|
|
{
|
|
return (UWORD)(psStats->power + (psStats->power * asSensorUpgrade[player].
|
|
power)/100);
|
|
}
|
|
|
|
UDWORD sensorRange(SENSOR_STATS *psStats, UBYTE player)
|
|
{
|
|
return (UWORD)(psStats->range + (psStats->range * asSensorUpgrade[player].
|
|
range)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a ECM*/
|
|
UDWORD ecmPower(ECM_STATS *psStats, UBYTE player)
|
|
{
|
|
return (UWORD)(psStats->power + (psStats->power * asECMUpgrade[player].power)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a ECM*/
|
|
UDWORD ecmRange(ECM_STATS *psStats, UBYTE player)
|
|
{
|
|
return (UWORD)(psStats->range + (psStats->range * asECMUpgrade[player].range)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a repair*/
|
|
UDWORD repairPoints(REPAIR_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->repairPoints + (psStats->repairPoints *
|
|
asRepairUpgrade[player].repairPoints)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a constructor*/
|
|
UDWORD constructorPoints(CONSTRUCT_STATS *psStats, UBYTE player)
|
|
{
|
|
return (psStats->constructPoints + (psStats->constructPoints *
|
|
asConstUpgrade[player].constructPoints)/100);
|
|
}
|
|
|
|
/*Access functions for the upgradeable stats of a body*/
|
|
UDWORD bodyPower(BODY_STATS *psStats, UBYTE player, UBYTE bodyType)
|
|
{
|
|
return (psStats->powerOutput + (psStats->powerOutput *
|
|
asBodyUpgrade[player][bodyType].powerOutput)/100);
|
|
}
|
|
|
|
UDWORD bodyArmour(BODY_STATS *psStats, UBYTE player, UBYTE bodyType,
|
|
WEAPON_CLASS weaponClass, int side)
|
|
{
|
|
switch (weaponClass)
|
|
{
|
|
case WC_KINETIC:
|
|
//case WC_EXPLOSIVE:
|
|
return (psStats->armourValue[side][WC_KINETIC] + (psStats->
|
|
armourValue[side][WC_KINETIC] * asBodyUpgrade[player][bodyType].
|
|
armourValue[WC_KINETIC])/100);
|
|
break;
|
|
case WC_HEAT:
|
|
//case WC_MISC:
|
|
return (psStats->armourValue[side][WC_HEAT] + (psStats->
|
|
armourValue[side][WC_HEAT] * asBodyUpgrade[player][bodyType].
|
|
armourValue[WC_HEAT])/100);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ASSERT( false,"bodyArmour() : Unknown weapon class" );
|
|
return 0; // Should never get here.
|
|
}
|
|
|
|
//calculates the weapons ROF based on the fire pause and the salvos
|
|
UWORD weaponROF(WEAPON_STATS *psStat, SBYTE player)
|
|
{
|
|
UWORD rof = 0;
|
|
|
|
//if there are salvos
|
|
if (psStat->numRounds)
|
|
{
|
|
if (psStat->reloadTime != 0)
|
|
{
|
|
// Rounds per salvo multiplied with the number of salvos per minute
|
|
rof = (UWORD)(psStat->numRounds * 60 * GAME_TICKS_PER_SEC /
|
|
(player >= 0 ? weaponReloadTime(psStat, player) : psStat->reloadTime) );
|
|
}
|
|
}
|
|
if (rof == 0)
|
|
{
|
|
rof = (UWORD)weaponFirePause(psStat, (UBYTE)selectedPlayer);
|
|
if (rof != 0)
|
|
{
|
|
rof = (UWORD)(60 * GAME_TICKS_PER_SEC / rof);
|
|
}
|
|
//else leave it at 0
|
|
}
|
|
return rof;
|
|
}
|
|
|
|
//Access functions for the max values to be used in the Design Screen
|
|
void setMaxComponentWeight(UDWORD weight)
|
|
{
|
|
if (weight > maxComponentWeight)
|
|
{
|
|
maxComponentWeight = weight;
|
|
}
|
|
}
|
|
UDWORD getMaxComponentWeight(void)
|
|
{
|
|
return maxComponentWeight;
|
|
}
|
|
|
|
void setMaxBodyArmour(UDWORD armour)
|
|
{
|
|
if (armour > maxBodyArmour)
|
|
{
|
|
maxBodyArmour = armour;
|
|
}
|
|
}
|
|
UDWORD getMaxBodyArmour(void)
|
|
{
|
|
return maxBodyArmour;
|
|
}
|
|
|
|
void setMaxBodyPower(UDWORD power)
|
|
{
|
|
if (power > maxBodyPower)
|
|
{
|
|
maxBodyPower = power;
|
|
}
|
|
}
|
|
UDWORD getMaxBodyPower(void)
|
|
{
|
|
return maxBodyPower;
|
|
}
|
|
|
|
void setMaxBodyPoints(UDWORD points)
|
|
{
|
|
if (points > maxBodyPoints)
|
|
{
|
|
maxBodyPoints = points;
|
|
}
|
|
}
|
|
UDWORD getMaxBodyPoints(void)
|
|
{
|
|
return maxBodyPoints;
|
|
}
|
|
|
|
void setMaxSensorRange(UDWORD range)
|
|
{
|
|
if (range > maxSensorRange)
|
|
{
|
|
maxSensorRange = range;
|
|
}
|
|
}
|
|
UDWORD getMaxSensorRange(void)
|
|
{
|
|
return maxSensorRange;
|
|
}
|
|
|
|
void setMaxSensorPower(UDWORD power)
|
|
{
|
|
if (power > maxSensorPower)
|
|
{
|
|
maxSensorPower = power;
|
|
}
|
|
}
|
|
UDWORD getMaxSensorPower(void)
|
|
{
|
|
return maxSensorPower;
|
|
}
|
|
|
|
void setMaxECMPower(UDWORD power)
|
|
{
|
|
if (power > maxECMPower)
|
|
{
|
|
maxECMPower = power;
|
|
}
|
|
}
|
|
UDWORD getMaxECMPower(void)
|
|
{
|
|
return maxECMPower;
|
|
}
|
|
|
|
void setMaxECMRange(UDWORD power)
|
|
{
|
|
if (power > maxECMRange)
|
|
{
|
|
maxECMPower = power;
|
|
}
|
|
}
|
|
UDWORD getMaxECMRange(void)
|
|
{
|
|
return maxECMRange;
|
|
}
|
|
|
|
void setMaxConstPoints(UDWORD points)
|
|
{
|
|
if (points > maxConstPoints)
|
|
{
|
|
maxConstPoints = points;
|
|
}
|
|
}
|
|
UDWORD getMaxConstPoints(void)
|
|
{
|
|
return maxConstPoints;
|
|
}
|
|
|
|
void setMaxRepairPoints(UDWORD repair)
|
|
{
|
|
if (repair > maxRepairPoints)
|
|
{
|
|
maxRepairPoints = repair;
|
|
}
|
|
}
|
|
UDWORD getMaxRepairPoints(void)
|
|
{
|
|
return maxRepairPoints;
|
|
}
|
|
|
|
void setMaxWeaponRange(UDWORD range)
|
|
{
|
|
if (range > maxWeaponRange)
|
|
{
|
|
maxWeaponRange = range;
|
|
}
|
|
}
|
|
UDWORD getMaxWeaponRange(void)
|
|
{
|
|
return maxWeaponRange;
|
|
}
|
|
|
|
void setMaxWeaponDamage(UDWORD damage)
|
|
{
|
|
if (damage > maxWeaponDamage)
|
|
{
|
|
maxWeaponDamage = damage;
|
|
}
|
|
}
|
|
UDWORD getMaxWeaponDamage(void)
|
|
{
|
|
return maxWeaponDamage;
|
|
}
|
|
|
|
void setMaxWeaponROF(UDWORD rof)
|
|
{
|
|
if (rof > maxWeaponROF)
|
|
{
|
|
maxWeaponROF = rof;
|
|
}
|
|
}
|
|
UDWORD getMaxWeaponROF(void)
|
|
{
|
|
return maxWeaponROF;
|
|
}
|
|
|
|
void setMaxPropulsionSpeed(UDWORD speed)
|
|
{
|
|
if (speed > maxPropulsionSpeed)
|
|
{
|
|
maxPropulsionSpeed = speed;
|
|
}
|
|
}
|
|
UDWORD getMaxPropulsionSpeed(void)
|
|
{
|
|
return maxPropulsionSpeed;
|
|
}
|
|
|
|
//determine the effect this upgrade would have on the max values
|
|
void updateMaxWeaponStats(UWORD maxValue)
|
|
{
|
|
UDWORD currentMaxValue = getMaxWeaponDamage();
|
|
|
|
if (currentMaxValue < (currentMaxValue + maxValue / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxValue / 100;
|
|
setMaxWeaponDamage(currentMaxValue);
|
|
}
|
|
|
|
//the fire pause is dealt with differently
|
|
}
|
|
|
|
void updateMaxSensorStats(UWORD maxRange, UWORD maxPower)
|
|
{
|
|
UDWORD currentMaxValue = getMaxSensorRange();
|
|
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxRange / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxRange / 100;
|
|
setMaxSensorRange(currentMaxValue);
|
|
}
|
|
|
|
currentMaxValue = getMaxSensorPower();
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxPower / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxPower / 100;
|
|
setMaxSensorPower(currentMaxValue);
|
|
}
|
|
}
|
|
|
|
void updateMaxRepairStats(UWORD maxValue)
|
|
{
|
|
UDWORD currentMaxValue = getMaxRepairPoints();
|
|
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxValue / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxValue / 100;
|
|
setMaxRepairPoints(currentMaxValue);
|
|
}
|
|
}
|
|
|
|
void updateMaxECMStats(UWORD maxValue)
|
|
{
|
|
UDWORD currentMaxValue = getMaxECMPower();
|
|
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxValue / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxValue / 100;
|
|
setMaxECMPower(currentMaxValue);
|
|
}
|
|
}
|
|
|
|
void updateMaxBodyStats(UWORD maxBody, UWORD maxPower, UWORD maxArmour)
|
|
{
|
|
UDWORD currentMaxValue = getMaxBodyPoints();
|
|
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxBody / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxBody / 100;
|
|
setMaxBodyPoints(currentMaxValue);
|
|
}
|
|
|
|
currentMaxValue = getMaxBodyPower();
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxPower / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxPower / 100;
|
|
setMaxBodyPower(currentMaxValue);
|
|
}
|
|
|
|
currentMaxValue = getMaxBodyArmour();
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxArmour / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxArmour / 100;
|
|
setMaxBodyArmour(currentMaxValue);
|
|
}
|
|
}
|
|
|
|
void updateMaxConstStats(UWORD maxValue)
|
|
{
|
|
UDWORD currentMaxValue = getMaxConstPoints();
|
|
|
|
if (currentMaxValue < (currentMaxValue + currentMaxValue * maxValue / 100))
|
|
{
|
|
currentMaxValue += currentMaxValue * maxValue / 100;
|
|
setMaxConstPoints(currentMaxValue);
|
|
}
|
|
}
|
|
|
|
//propulsion stats are not upgradeable
|
|
|
|
void adjustMaxDesignStats(void)
|
|
{
|
|
UWORD weaponDamage, sensorRange, sensorPower, repairPoints,
|
|
ecmPower, constPoints, bodyPoints, bodyPower, bodyArmour, inc;
|
|
|
|
//init all the values
|
|
weaponDamage = sensorRange = sensorPower = repairPoints =
|
|
ecmPower = constPoints = bodyPoints = bodyPower = bodyArmour = (UWORD)0;
|
|
|
|
//go thru' all the functions getting the max upgrade values for the stats
|
|
for (inc = 0; inc < numFunctions; inc++)
|
|
{
|
|
switch(asFunctions[inc]->type)
|
|
{
|
|
case DROIDREPAIR_UPGRADE_TYPE:
|
|
if (repairPoints < ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints)
|
|
{
|
|
repairPoints = ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints;
|
|
}
|
|
break;
|
|
case DROIDECM_UPGRADE_TYPE:
|
|
if (ecmPower < ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints)
|
|
{
|
|
ecmPower = ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints;
|
|
}
|
|
break;
|
|
case DROIDBODY_UPGRADE_TYPE:
|
|
if (bodyPoints < ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->body)
|
|
{
|
|
bodyPoints = ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->body;
|
|
}
|
|
if (bodyPower < ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints)
|
|
{
|
|
bodyPower = ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints;
|
|
}
|
|
if (bodyArmour < ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->armourValue[WC_KINETIC])
|
|
{
|
|
bodyArmour = ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->armourValue[WC_KINETIC];
|
|
}
|
|
if (bodyArmour < ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->armourValue[WC_HEAT])
|
|
{
|
|
bodyArmour = ((DROIDBODY_UPGRADE_FUNCTION *)asFunctions[inc])->armourValue[WC_HEAT];
|
|
}
|
|
break;
|
|
case DROIDSENSOR_UPGRADE_TYPE:
|
|
if (sensorRange < ((DROIDSENSOR_UPGRADE_FUNCTION *)asFunctions[inc])->range)
|
|
{
|
|
sensorRange = ((DROIDSENSOR_UPGRADE_FUNCTION *)asFunctions[inc])->range;
|
|
}
|
|
if (sensorPower < ((DROIDSENSOR_UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints)
|
|
{
|
|
sensorPower = ((DROIDSENSOR_UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints;
|
|
}
|
|
break;
|
|
case DROIDCONST_UPGRADE_TYPE:
|
|
if (constPoints < ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints)
|
|
{
|
|
constPoints = ((UPGRADE_FUNCTION *)asFunctions[inc])->upgradePoints;
|
|
}
|
|
break;
|
|
case WEAPON_UPGRADE_TYPE:
|
|
if (weaponDamage < ((WEAPON_UPGRADE_FUNCTION *)asFunctions[inc])->damage)
|
|
{
|
|
weaponDamage = ((WEAPON_UPGRADE_FUNCTION *)asFunctions[inc])->damage;
|
|
}
|
|
break;
|
|
default:
|
|
//not interested in other function types
|
|
break;
|
|
}
|
|
}
|
|
|
|
//determine the effect on the max values for the stats
|
|
updateMaxWeaponStats(weaponDamage);
|
|
updateMaxSensorStats(sensorRange, sensorPower);
|
|
updateMaxRepairStats(repairPoints);
|
|
updateMaxECMStats(ecmPower);
|
|
updateMaxBodyStats(bodyPoints, bodyPower, bodyArmour);
|
|
updateMaxConstStats(constPoints);
|
|
}
|
|
|
|
/* Check if an object has a weapon */
|
|
BOOL objHasWeapon(BASE_OBJECT *psObj)
|
|
{
|
|
|
|
//check if valid type
|
|
if(psObj->type == OBJ_DROID)
|
|
{
|
|
if ( ((DROID *)psObj)->numWeaps > 0 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if(psObj->type == OBJ_STRUCTURE)
|
|
{
|
|
if ( ((STRUCTURE *)psObj)->numWeaps > 0 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
SENSOR_STATS *objActiveRadar(BASE_OBJECT *psObj)
|
|
{
|
|
SENSOR_STATS *psStats = NULL;
|
|
int compIndex;
|
|
|
|
switch (psObj->type)
|
|
{
|
|
case OBJ_DROID:
|
|
if (((DROID *)psObj)->droidType != DROID_SENSOR && ((DROID *)psObj)->droidType != DROID_COMMAND)
|
|
{
|
|
return NULL;
|
|
}
|
|
compIndex = ((DROID *)psObj)->asBits[COMP_SENSOR].nStat;
|
|
ASSERT_OR_RETURN( NULL, compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
|
|
psStats = asSensorStats + compIndex;
|
|
break;
|
|
case OBJ_STRUCTURE:
|
|
psStats = ((STRUCTURE *)psObj)->pStructureType->pSensor;
|
|
if (psStats == NULL || psStats->location != LOC_TURRET || ((STRUCTURE *)psObj)->status != SS_BUILT)
|
|
{
|
|
return NULL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return psStats;
|
|
}
|
|
|
|
bool objRadarDetector(BASE_OBJECT *psObj)
|
|
{
|
|
if (psObj->type == OBJ_STRUCTURE)
|
|
{
|
|
STRUCTURE *psStruct = (STRUCTURE *)psObj;
|
|
|
|
return (psStruct->status == SS_BUILT && psStruct->pStructureType->pSensor && psStruct->pStructureType->pSensor->type == RADAR_DETECTOR_SENSOR);
|
|
}
|
|
else if (psObj->type == OBJ_DROID)
|
|
{
|
|
DROID *psDroid = (DROID *)psObj;
|
|
SENSOR_STATS *psSensor = getSensorStats(psDroid);
|
|
|
|
return (psSensor && psSensor->type == RADAR_DETECTOR_SENSOR);
|
|
}
|
|
return false;
|
|
}
|