/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection 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 #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, 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 );\ (list) = (type *)malloc(sizeof(type) * (numEntries)); \ if ((list) == NULL) \ { \ debug( LOG_ERROR, "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)) void statsInitVars(void) { int i,j; asBodyStats = NULL; asBrainStats = NULL; asPropulsionStats = NULL; asSensorStats = NULL; asECMStats = NULL; asRepairStats = NULL; asWeaponStats = NULL; asConstructStats = NULL; asPropulsionTypes = NULL; asTerrainTable = NULL; asSpecialAbility = NULL; /* The number of different stats stored */ numBodyStats = 0; numBrainStats = 0; numPropulsionStats = 0; numSensorStats = 0; numECMStats = 0; numRepairStats = 0; numWeaponStats = 0; numConstructStats = 0; numSpecialAbility = 0; //stores for each players component states - can be either UNAVAILABLE, FOUND or AVAILABLE for(i=0; ipName = 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 *******************************************************************************/ // Include the definitions of the database loading functions here (they need the // static functions from this file as well). #include "stats-db.c" /*Load the weapon stats from the file exported from Access*/ BOOL loadWeaponStats(const char *pWeaponData, UDWORD bufferSize) { const 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; 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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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", getStatName(psStats) ); abort(); return false; } //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", getStatName(psStats) ); abort(); return false; } //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_ERROR, "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; const 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; 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_ERROR, "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_ERROR, "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 { //get the weapon stat if (!getResourceName(weaponName)) { return false; } weapon = getCompFromName(COMP_WEAPON, weaponName); //if weapon not found - error if (weapon == -1) { debug( LOG_ERROR, "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 { return false; } 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_ERROR, "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_ERROR, "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) { const 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; 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 { 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(mountGfx, "0")) { psStats->pIMD = (iIMDShape *) resGetData("IMD", GfxFile); if (psStats->pIMD == NULL) { debug( LOG_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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; if (!getResourceName(bodyName)) { return false; } for (numStats = 0; numStats < numBodyStats; numStats++) { psBodyStat = &asBodyStats[numStats]; if (!strcmp(psBodyStat->pName, bodyName)) { found = true; break; } } if (!found) { debug( LOG_ERROR, "loadBodyPropulsionPIEs: Invalid body name %s", bodyName ); abort(); return false; } //get the propulsion stats found = false; if (!getResourceName(propulsionName)) { return false; } for (numStats = 0; numStats < numPropulsionStats; numStats++) { psPropulsionStat = &asPropulsionStats[numStats]; if (!strcmp(psPropulsionStat->pName, propulsionName)) { found = true; break; } } if (!found) { debug( LOG_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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]; BOOL Ok = true; 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; } //find the weapon stat if (!getResourceName(WeaponName)) { return false; } for (inc = 0; inc < (SDWORD)numWeaponStats; inc++) { if (!strcmp(asWeaponStats[inc].pName, WeaponName)) { asWeaponStats[inc].iAudioFireID = weaponSoundID; asWeaponStats[inc].iAudioImpactID = explosionSoundID; break; } } if (inc == (SDWORD)numWeaponStats) { debug( LOG_ERROR, "loadWeaponSounds: Weapon stat not found - %s", WeaponName ); abort(); Ok = false; // return false; } //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_ERROR, "loadWeaponModifiers: Invalid Weapon Effect - %s", weaponEffectName ); abort(); return false; } //get the propulsion inc if (!getPropulsionType(propulsionName, &propInc)) { debug( LOG_ERROR, "loadWeaponModifiers: Invalid Propulsion type - %s", propulsionName ); abort(); return false; } if (modifier > UWORD_MAX) { debug( LOG_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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_ERROR, "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; } //converts the name read in from Access into the name which is used in the Stat lists BOOL getResourceName(const char *pName) { return true; } /*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 ); abort(); 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) { *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_ERROR, "Unable to find string resource for %s", name); abort(); return NULL; } storeName = strdup(name); if (!storeName) { debug(LOG_ERROR, "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; }