/* 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 */ /* * Research.c * * Research tree and functions! * */ #include #include "objects.h" #include "lib/gamelib/gtime.h" #include "research.h" #include "message.h" #include "text.h" #include "lib/sound/audio.h" #include "audio_id.h" #include "lib/script/script.h" #include "scripttabs.h" #include "hci.h" #include "console.h" #include "cmddroid.h" #include "power.h" #include "mission.h" #include "frend.h" // frontend ids. #include "intimage.h" #include "multiplay.h" //#include "intfac.h" // Interface image id's. //used to calc the research power #define RESEARCH_FACTOR 32//16 #define RESEARCH_MAX_POWER 450 // The stores for the research stats RESEARCH *asResearch; UDWORD numResearch; //used for Callbacks to say which topic was last researched RESEARCH *psCBLastResearch; //research is now loaded per campaign - this hopefully is the max there will be in any one campaign! //changing above a UBYTE size will require changes throughout research - put the designers off if you can! //PC and PSX are different because of the multiplayer research trees //#define MAX_RESEARCH (216 + 20) //#define MAX_RESEARCH (255) //new levels required for Patch #define MAX_RESEARCH (450) //need to define max's for each of the lists associated with the research - these //values have been chosen based on the current research stats - 21/12/98 //#define MAX_RESEARCH_PR (280 + 20) /*#define MAX_RESEARCH_PR (400) #define MAX_RESEARCH_STRUCT_PR (24 + 5) #define MAX_RESEARCH_FUNC (150 + 25) #define MAX_RESEARCH_STRUCT_RED (10 + 2) #define MAX_RESEARCH_ARTE_RED (20 + 5) #define MAX_RESEARCH_STRUCT_RES (44 + 5) #define MAX_RESEARCH_ARTE_RES (65 + 5)*/ //new levels required for Patches #define MAX_RESEARCH_PR (650) #define MAX_RESEARCH_STRUCT_PR (44 + 5) #define MAX_RESEARCH_FUNC (250 + 25) #define MAX_RESEARCH_STRUCT_RED (30 + 2) #define MAX_RESEARCH_ARTE_RED (40 + 5) #define MAX_RESEARCH_STRUCT_RES (84 + 5) #define MAX_RESEARCH_ARTE_RES (125 + 5) //need corresponding arrays for the above //needs to be a UWORD* for the Patches UWORD *pResearchPR; //UBYTE *pResearchPR; UWORD *pResearchStructPR; FUNCTION **pResearchFunc; UWORD *pResearchStructRed; COMP_BASE_STATS **pResearchArteRed; UWORD *pResearchStructRes; COMP_BASE_STATS **pResearchArteRes; COMP_BASE_STATS **pResearchArteRep; UWORD numResearchPR; UWORD numResearchStructPR; UWORD numResearchFunc; UWORD numResearchStructRed; UBYTE numResearchArteRed; UWORD numResearchStructRes; UBYTE numResearchArteRes; UBYTE numResearchArteRep; //List of pointers to arrays of PLAYER_RESEARCH[numResearch] for each player PLAYER_RESEARCH* asPlayerResList[MAX_PLAYERS]; /* Default level of sensor, Repair and ECM */ UDWORD aDefaultSensor[MAX_PLAYERS]; UDWORD aDefaultECM[MAX_PLAYERS]; UDWORD aDefaultRepair[MAX_PLAYERS]; #ifdef HASH_NAMES static UWORD setIconIDFromHashedName(STRING *pIconName, UDWORD NameHash); #else //set the iconID based on the name read in in the stats static UWORD setIconID(STRING *pIconName, STRING *pName); #endif static COMP_BASE_STATS * getComponentDetails(STRING *pName, STRING *pCompName); static void replaceComponent(COMP_BASE_STATS *pNewComponent, COMP_BASE_STATS *pOldComponent, UBYTE player); static BOOL checkResearchName(RESEARCH *psRes, UDWORD numStats); //static void enableSelfRepair(UBYTE player); char *getResearchName(RESEARCH *pResearch) { #ifdef HASH_NAMES return(strresGetString(NULL,pResearch->NameHash)); #else return(getName(pResearch->pName)); #endif } //flag that indicates whether the player can self repair static UBYTE bSelfRepair[MAX_PLAYERS]; static void replaceDroidComponent(DROID *pList, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc); static void replaceStructureComponent(STRUCTURE *pList, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc, UBYTE player); static void switchComponent(DROID *psDroid,UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc); static void replaceTransDroidComponents(DROID *psTransporter, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc); BOOL researchInitVars(void) { int i; psCBLastResearch = NULL; asResearch = NULL; //research is a pre-defined size now asResearch = (RESEARCH *)MALLOC(sizeof(RESEARCH)* MAX_RESEARCH); if (asResearch == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(asResearch, 0, (MAX_RESEARCH * sizeof(RESEARCH))); //create the PLAYER_RESEARCH arrays for (i=0; i < MAX_PLAYERS; i++) { asPlayerResList[i] = (PLAYER_RESEARCH*)MALLOC(MAX_RESEARCH * sizeof(PLAYER_RESEARCH)); if (asPlayerResList[i] == NULL) { debug( LOG_ERROR, "Out of memory assigning Player_Research" ); abort(); return FALSE; } memset(asPlayerResList[i], 0, (MAX_RESEARCH * sizeof(PLAYER_RESEARCH))); } numResearch = 0; //and deal with all the other arrays for research //needs to be UWORD sized for the Patches pResearchPR = (UWORD *) MALLOC(sizeof(UWORD) * MAX_RESEARCH_PR); //pResearchPR = (UBYTE *) MALLOC(sizeof(UBYTE) * MAX_RESEARCH_PR); if (pResearchPR == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } //needs to be UWORD sized for the Patches memset(pResearchPR, 0, (MAX_RESEARCH_PR * sizeof(UWORD))); //memset(pResearchPR, 0, (MAX_RESEARCH_PR * sizeof(UBYTE))); pResearchStructPR = (UWORD *) MALLOC(sizeof(UWORD) * MAX_RESEARCH_STRUCT_PR); if (pResearchStructPR == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchStructPR, 0, (MAX_RESEARCH_STRUCT_PR * sizeof(UWORD))); pResearchFunc = (FUNCTION **) MALLOC(sizeof(FUNCTION *) * MAX_RESEARCH_FUNC); if (pResearchFunc == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchFunc, 0, (MAX_RESEARCH_FUNC * sizeof(FUNCTION *))); pResearchStructRed = (UWORD *) MALLOC(sizeof(UWORD) * MAX_RESEARCH_STRUCT_RED); if (pResearchStructRed == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchStructRed, 0, (MAX_RESEARCH_STRUCT_RED * sizeof(UWORD))); pResearchArteRed = (COMP_BASE_STATS **) MALLOC(sizeof(COMP_BASE_STATS *) * MAX_RESEARCH_ARTE_RED); if (pResearchArteRed == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchArteRed, 0, (MAX_RESEARCH_ARTE_RED * sizeof(COMP_BASE_STATS *))); pResearchStructRes = (UWORD *) MALLOC(sizeof(UWORD) * MAX_RESEARCH_STRUCT_RES); if (pResearchStructRes == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchStructRes, 0, (MAX_RESEARCH_STRUCT_RES * sizeof(UWORD))); pResearchArteRes = (COMP_BASE_STATS **) MALLOC(sizeof(COMP_BASE_STATS *) * MAX_RESEARCH_ARTE_RES); if (pResearchArteRes == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchArteRes, 0, (MAX_RESEARCH_ARTE_RES * sizeof(COMP_BASE_STATS *))); pResearchArteRep = (COMP_BASE_STATS **) MALLOC(sizeof(COMP_BASE_STATS *) * MAX_RESEARCH_ARTE_RES); if (pResearchArteRep == NULL) { debug( LOG_ERROR, "Research Stats - Out of memory" ); abort(); return FALSE; } memset(pResearchArteRep, 0, (MAX_RESEARCH_ARTE_RES * sizeof(COMP_BASE_STATS *))); for(i=0; iNameHash=HashString(ResearchName); #else if (!allocateName(&pResearch->pName, ResearchName)) { return FALSE; } #endif //check the name hasn't been used already if (!checkResearchName(pResearch, i)) { return FALSE; } pResearchData += (strlen(ResearchName)+1); pResearch->ref = REF_RESEARCH_START + i; //determine the tech level ResearchName[0] = '\0'; sscanf(pResearchData,"%[^','],", ResearchName); if (!setTechLevel((BASE_STATS *)pResearch, ResearchName)) { return FALSE; } pResearchData += (strlen(ResearchName)+1); ResearchName[0] = '\0'; sscanf(pResearchData,"%[^','],", ResearchName); //subGroup value now holds which category the research comes under for yet another icon! // store subgroup. may differ from tech level at some point? /*if(strcmp(ResearchName,"Sub Group One") == 0) { pResearch->subGroup = 1; } else if(strcmp(ResearchName,"Sub Group Two") == 0) { pResearch->subGroup = 2; } else if(strcmp(ResearchName,"Sub Group Three") == 0) { pResearch->subGroup = 3; } // now support for future 'add on packs' (hey I can be hopeful). else if(strcmp(ResearchName,"Sub Group Four") == 0) { pResearch->subGroup = 4; } else if(strcmp(ResearchName,"Sub Group Five") == 0) { pResearch->subGroup = 5; } else if(strcmp(ResearchName,"Sub Group Six") == 0) { pResearch->subGroup = 6; } else if(strcmp(ResearchName,"Sub Group Seven") == 0) { pResearch->subGroup = 7; } else if(strcmp(ResearchName,"Sub Group Eight") == 0) { pResearch->subGroup = 8; } else if(strcmp(ResearchName,"Sub Group Nine") == 0) { pResearch->subGroup = 9; } else { DBERROR(("Unknown Research Subgroup.")); }*/ if (strcmp(ResearchName, "0")) { #ifdef HASH_NAMES pResearch->subGroup = setIconIDFromHashedName(ResearchName, pResearch->NameHash); #else pResearch->subGroup = setIconID(ResearchName, pResearch->pName); #endif } else { pResearch->subGroup = NO_RESEARCH_ICON; } pResearchData += (strlen(ResearchName)+1); iconID[0] = '\0'; imdName[0] = '\0'; imdName2[0] = '\0'; msgName[0] = '\0'; structName[0] = '\0'; compName[0] = '\0'; compType[0] = '\0'; { UDWORD numPRRequired; UDWORD numFunctions; UDWORD numStructures; UDWORD numRedStructs; UDWORD numStructResults; UDWORD numRedArtefacts; UDWORD numArteResults; sscanf(pResearchData,"%d,%[^','],%[^','],%[^','],%[^','],%[^','], \ %[^','],%[^','],%d,%d,%d,%d,%d,%d,%d,%d,%d", &techCode, iconID, imdName, imdName2, msgName, structName, compName, compType, &resPoints, &keyTopic, &numPRRequired, &numFunctions, &numStructures, &numRedStructs, &numStructResults, &numRedArtefacts, &numArteResults); pResearch->numPRRequired=(UBYTE)numPRRequired; pResearch->numFunctions=(UBYTE)numFunctions; pResearch->numStructures=(UBYTE)numStructures; pResearch->numRedStructs=(UBYTE)numRedStructs; pResearch->numStructResults=(UBYTE)numStructResults; pResearch->numRedArtefacts=(UBYTE)numRedArtefacts; pResearch->numArteResults=(UBYTE)numArteResults; } //set keytopic flag if (keyTopic) { pResearch->keyTopic = TRUE; } else { pResearch->keyTopic = FALSE; } //check the tech code is valid if (techCode > 1) //if (pResearch->techCode != TC_MAJOR AND pResearch->techCode != TC_MINOR) { debug( LOG_ERROR, "Invalid tech code for research topic - %s ", getResearchName(pResearch) ); abort(); return FALSE; } if (techCode == 0) { pResearch->techCode = TC_MAJOR; } else { pResearch->techCode = TC_MINOR; } //set the iconID if (strcmp(iconID, "0")) { #ifdef HASH_NAMES pResearch->iconID = setIconIDFromHashedName(iconID, pResearch->NameHash); #else pResearch->iconID = setIconID(iconID, pResearch->pName); #endif } else { pResearch->iconID = NO_RESEARCH_ICON; } //get the IMDs used in the interface if (strcmp(structName, "0")) { //find the structure stat structID = getStructStatFromName(structName); if (structID >= 0) { pResearch->psStat = (BASE_STATS *)(asStructureStats + structID); } else { debug( LOG_ERROR, "Cannot find the structure Stat for Research %s", getResearchName(pResearch) ); abort(); return FALSE; } } else if (strcmp(compName, "0")) { //find the component stat psComp = getComponentDetails(compType, compName); if (psComp != NULL) { pResearch->psStat = (BASE_STATS *)psComp; } else { debug( LOG_ERROR, "Cannot find the component Stat for Research %s", getResearchName(pResearch) ); abort(); return FALSE; } } else { pResearch->psStat = NULL; } if (strcmp(imdName, "0")) { pResearch->pIMD = (iIMDShape *) resGetData("IMD", imdName); if (pResearch->pIMD == NULL) { debug( LOG_ERROR, "Cannot find the research PIE for record %s", getResearchName(pResearch) ); abort(); return FALSE; } } else { pResearch->pIMD = NULL; } if (strcmp(imdName2, "0")) { pResearch->pIMD2 = (iIMDShape *) resGetData("IMD", imdName2); if (pResearch->pIMD2 == NULL) { debug( LOG_ERROR, "Cannot find the 2nd research PIE for record %s", getResearchName(pResearch) ); abort(); return FALSE; } } else { pResearch->pIMD2 = NULL; } //get the message viewdata - if any if (strcmp(msgName, "0")) { //check its a major tech code if (pResearch->techCode != TC_MAJOR) { debug( LOG_ERROR, "This research should not have a message associated with it, %s the message will be ignored!", getResearchName(pResearch) ); abort(); } else { pResearch->pViewData = getViewData(msgName); } } //redundancies - artefacts if (pResearch->numRedArtefacts > 0) { /*pResearch->pRedArtefacts = (COMP_BASE_STATS **) MALLOC(pResearch-> numRedArtefacts*sizeof(COMP_BASE_STATS *)); if (pResearch->pRedArtefacts == NULL) { DBERROR(("Out of memory assigning research artefacts - redundancies")); return FALSE; }*/ if (numResearchArteRed >= MAX_RESEARCH_ARTE_RED) { debug( LOG_ERROR, "Out of memory assigning research artefacts - redundancies" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pRedArtefacts = pResearchArteRed + numResearchArteRed; //pResearchArteRed += pResearch->numRedArtefacts; //keep track on how many are being allocated numResearchArteRed = (UBYTE)(numResearchArteRed + pResearch->numRedArtefacts); } //results if (pResearch->numArteResults > 0) { /*pResearch->pArtefactResults = (COMP_BASE_STATS **) MALLOC(pResearch-> numArteResults*sizeof(COMP_BASE_STATS *)); if (pResearch->pArtefactResults == NULL) { DBERROR(("Out of memory assigning research artefacts - results")); return FALSE; }*/ if (numResearchArteRed >= MAX_RESEARCH_ARTE_RES) { debug( LOG_ERROR, "Out of memory assigning research artefacts - results" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pArtefactResults = pResearchArteRes + numResearchArteRes; //pResearchArteRes += pResearch->numArteResults; //keep track on how many are being allocated numResearchArteRes = (UBYTE)(numResearchArteRes + pResearch->numArteResults); } //replacements if (pResearch->numArteResults > 0) { /*pResearch->pReplacedArtefacts = (COMP_BASE_STATS **) MALLOC(pResearch-> numArteResults*sizeof(COMP_BASE_STATS *)); if (pResearch->pReplacedArtefacts == NULL) { DBERROR(("Out of memory assigning research artefacts - replacements")); return FALSE; }*/ if (numResearchArteRep >= MAX_RESEARCH_ARTE_RES) { debug( LOG_ERROR, "Out of memory assigning research artefacts - replacements" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pReplacedArtefacts = pResearchArteRep + numResearchArteRep; //pResearchArteRep += pResearch->numArteResults; //keep track on how many are being allocated numResearchArteRep = (UBYTE)(numResearchArteRep + pResearch->numArteResults); } //allocate storage for the functions if (pResearch->numFunctions > 0) { /*pResearch->pFunctionList = (FUNCTION**)MALLOC(pResearch-> numFunctions*sizeof(FUNCTION*)); if (pResearch->pFunctionList == NULL) { DBERROR(("Out of memory assigning research functions")); return FALSE; }*/ if (numResearchFunc >= MAX_RESEARCH_FUNC) { debug( LOG_ERROR, "Out of memory assigning research functions" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pFunctionList = pResearchFunc + numResearchFunc; //pResearchFunc += pResearch->numFunctions; //keep track on how many are being allocated numResearchFunc = (numResearchFunc + pResearch->numFunctions); } //allocate storage for the pre-requisities if (pResearch->numPRRequired > 0) { /*pResearch->pPRList = (UDWORD*)MALLOC(pResearch-> numPRRequired*sizeof(UDWORD)); if (pResearch->pPRList == NULL) { DBERROR(("Out of memory assigning research pre-requisities")); return FALSE; }*/ if (numResearchPR >= MAX_RESEARCH_PR) { debug( LOG_ERROR, "Out of memory assigning research pre-requisities" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pPRList = pResearchPR + numResearchPR; //pResearchPR += pResearch->numPRRequired; //keep track on how many are being allocated numResearchPR = (UWORD)(numResearchPR + pResearch->numPRRequired); } //allocate storage for the structures //requirements if (pResearch->numStructures > 0) { /*pResearch->pStructList = (UDWORD *) MALLOC(pResearch-> numStructures*sizeof(UDWORD)); if (pResearch->pStructList == NULL) { DBERROR(("Out of memory assigning research structures - requirements")); return FALSE; }*/ if (numResearchStructPR >= MAX_RESEARCH_STRUCT_PR) { debug( LOG_ERROR, "Out of memory assigning research structures - requirements" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pStructList = pResearchStructPR + numResearchStructPR; //pResearchStructPR += pResearch->numStructures; //keep track on how many are being allocated numResearchStructPR = (UBYTE)(numResearchStructPR + pResearch->numStructures); } //redundancies if (pResearch->numRedStructs > 0) { /*pResearch->pRedStructs = (UDWORD *) MALLOC(pResearch-> numRedStructs*sizeof(UDWORD)); if (pResearch->pRedStructs == NULL) { DBERROR(("Out of memory assigning research structures - redundancies")); return FALSE; }*/ if (numResearchStructRed >= MAX_RESEARCH_STRUCT_RED) { debug( LOG_ERROR, "Out of memory assigning research structures - redundancies" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pRedStructs = pResearchStructRed + numResearchStructRed; //pResearchStructRed += pResearch->numRedStructs; //keep track on how many are being allocated numResearchStructRed = (UBYTE)(numResearchStructRed + pResearch->numRedStructs); } //results if (pResearch->numStructResults > 0) { /*pResearch->pStructureResults = (UDWORD *) MALLOC(pResearch-> numStructResults*sizeof(UDWORD)); if (pResearch->pStructureResults == NULL) { DBERROR(("Out of memory assigning research structures - results")); return FALSE; }*/ if (numResearchStructRes >= MAX_RESEARCH_STRUCT_RES) { debug( LOG_ERROR, "Out of memory assigning research structures - results" ); abort(); return FALSE; } //don't MALLOC - get them from the pre-defined arrays pResearch->pStructureResults = pResearchStructRes + numResearchStructRes; //pResearchStructRes += pResearch->numStructResults; //keep track on how many are being allocated numResearchStructRes = (UBYTE)(numResearchStructRes + pResearch->numStructResults); } //set the researchPoints if (resPoints > UWORD_MAX) { debug( LOG_ERROR, "Research Points too high for research topic - %s ", getResearchName(pResearch) ); abort(); return FALSE; } pResearch->researchPoints = (UWORD)resPoints; //set the research power pResearch->researchPower = pResearch->researchPoints / RESEARCH_FACTOR; if (pResearch->researchPower > RESEARCH_MAX_POWER) { pResearch->researchPower = RESEARCH_MAX_POWER; } //increment the pointer to the start of the next record pResearchData = strchr(pResearchData,'\n') + 1; //increment the list to the start of the next storage block pResearch++; } // FREE(pStartResearchData); //Do this in initResearch now since there is a Max Research //now we know how many research topics there are we can create the //PLAYER_RESEARCH arrays /*for (i=0; i < MAX_PLAYERS; i++) { asPlayerResList[i] = (PLAYER_RESEARCH*)MALLOC(numResearch * sizeof(PLAYER_RESEARCH)); if (asPlayerResList[i] == NULL) { DBERROR(("Out of memory assigning Player_Research")); return FALSE; } memset(asPlayerResList[i], 0, (numResearch * sizeof(PLAYER_RESEARCH))); }*/ return TRUE; } //Load the pre-requisites for a research list BOOL loadResearchPR(char *pPRData, UDWORD bufferSize) { char *pStartPRData; UDWORD NumToAlloc = 0, i; STRING ResearchName[MAX_NAME_SIZE], PRName[MAX_NAME_SIZE]; //UBYTE incR, incPR; UWORD incR, incPR; RESEARCH *pResearch = asResearch, *pPRResearch = asResearch; BOOL recFound; #ifdef HASH_NAMES UDWORD HashedResearchName; UDWORD HashedPRName; #endif pStartPRData = pPRData; NumToAlloc = numCR(pPRData, bufferSize); //check not going to go over max ASSERT( NumToAlloc <= MAX_RESEARCH_PR, "loadResearchPR: too many!" ); numResearchPR = 0; for (i=0; i < NumToAlloc; i++) { recFound = FALSE; //read the data into the storage - the data is delimited using commas ResearchName[0] = '\0'; PRName[0] = '\0'; sscanf(pPRData,"%[^','],%[^','],%*d", ResearchName, PRName); if (!getResourceName(ResearchName)) { return FALSE; } if (!getResourceName(PRName)) { return FALSE; } #ifdef HASH_NAMES HashedResearchName=HashString(ResearchName); HashedPRName=HashString(PRName); #endif //loop through each Research to compare the name for (incR=0; incR < numResearch; incR++) { #ifdef HASH_NAMES if ( pResearch[incR].NameHash == HashedResearchName) #else if (!(strcmp(ResearchName, pResearch[incR].pName))) #endif { //Research found for (incPR=0; incPR < numResearch; incPR++) { #ifdef HASH_NAMES if ( pPRResearch[incPR].NameHash == HashedPRName ) #else if (!(strcmp(PRName, pPRResearch[incPR].pName))) #endif { //check not allocating more than allowed if ((pResearch[incR].storeCount + 1) > (SDWORD)pResearch[incR].numPRRequired) { debug( LOG_ERROR, "Trying to allocate more pre-requisites than allowed for research %s", getResearchName(pResearch) ); abort(); return FALSE; } //PRresearch found alloc this to the current Research pResearch[incR].pPRList[pResearch[incR]. storeCount] = incPR; //keep tab on how many we have loaded in numResearchPR++; pResearch[incR].storeCount++; recFound = TRUE; break; } } //if pre-requisite not found - error if (!recFound) { debug( LOG_ERROR, "Unable to find Pre-requisite %s for research %s", PRName, ResearchName ); abort(); return FALSE; } else { break; } } } //if Research not found - error if (!recFound) { debug( LOG_ERROR, "Unable to find Research %s", ResearchName ); abort(); return FALSE; } //quick check that haven't reached maxPR if (numResearchPR >= MAX_RESEARCH_PR) { //don't load any more since will write over memory! break; } //increment the pointer to the start of the next record pPRData = strchr(pPRData,'\n') + 1; } // FREE(pStartPRData); return TRUE; } //Load the artefacts for a research list BOOL loadResearchArtefacts(char *pArteData, UDWORD bufferSize, UDWORD listNumber) { char *pStartArteData; UDWORD NumToAlloc = 0, i; STRING ResearchName[MAX_NAME_SIZE], ArteName[MAX_NAME_SIZE], TypeName[MAX_NAME_SIZE]; UDWORD incR; RESEARCH *pResearch = asResearch; COMP_BASE_STATS *pArtefact; UDWORD newType; UBYTE maxArtefacts; //initialise the storage flags for (incR = 0; incR < numResearch; incR++) { pResearch[incR].storeCount = 0; } pResearch = asResearch; pStartArteData = pArteData; NumToAlloc = numCR(pArteData, bufferSize); //check not going to go over max switch (listNumber) { case RED_LIST: ASSERT( NumToAlloc <= MAX_RESEARCH_ARTE_RED, "loadResearchArtefacts: too many Redundant Components" ); numResearchArteRed = 0; break; case RES_LIST: ASSERT( NumToAlloc <= MAX_RESEARCH_ARTE_RES, "loadResearchArtefacts: too many Component Results" ); numResearchArteRes = 0; numResearchArteRep = 0; break; } for (i=0; i < NumToAlloc; i++) { //read the data into the storage - the data is delimited using commas ResearchName[0] = '\0'; ArteName[0] = '\0'; TypeName[0] = '\0'; sscanf(pArteData,"%[^','],%[^','],%[^',']", ResearchName, ArteName, TypeName); //increment the data pointer pArteData += (strlen(ResearchName)+1+strlen(ArteName)+1+strlen(TypeName)+1); if (!getResourceName(ResearchName)) { return FALSE; } if (!getResourceName(ArteName)) { return FALSE; } pArtefact = getComponentDetails(TypeName, ArteName); if (pArtefact == NULL) { return FALSE; } //get the type for comparison later newType = statType(pArtefact->ref); pResearch = getResearch(ResearchName, FALSE); if (pResearch == NULL) { return FALSE; } //ArtefactResearch found - alloc the artefact to the current Research topic switch (listNumber) { case RED_LIST: *(pResearch->pRedArtefacts + pResearch->storeCount) = pArtefact; //keep tab on how many we have loaded in numResearchArteRed++; maxArtefacts = pResearch->numRedArtefacts; break; case RES_LIST: *(pResearch->pArtefactResults + pResearch->storeCount) = pArtefact ; //keep tab on how many we have loaded in numResearchArteRes++; maxArtefacts = pResearch->numArteResults; break; default: debug( LOG_ERROR, "Unknown research list" ); abort(); return FALSE; } //deal with extra data switch (listNumber) { case RED_LIST: //ignore the last character sscanf(pArteData,",%*d"); break; case RES_LIST: ArteName[0] = '\0'; TypeName[0] = '\0'; sscanf(pArteData, "%[^','],%[^','],%*d", ArteName, TypeName); if (!strcmp(ArteName, "0")) { *(pResearch->pReplacedArtefacts + pResearch->storeCount) = NULL; } else { if (!getResourceName(ArteName)) { return FALSE; } pArtefact = getComponentDetails(TypeName, ArteName); if (pArtefact == NULL) { return FALSE; } //check the old and new types are the same if (statType(pArtefact->ref) != newType) { debug( LOG_ERROR, "You are trying to replace one type of component with a different type for research %s in ResultComponents.txt", ResearchName ); abort(); return FALSE; } //ArtefactResearch found - alloc the artefact to the current Research topic *(pResearch->pReplacedArtefacts + pResearch->storeCount) = pArtefact; numResearchArteRep++; } break; default: debug( LOG_ERROR, "Unknown research list" ); abort(); return FALSE; } //check not allocating more than allowed if (pResearch->storeCount > maxArtefacts) { debug( LOG_ERROR, "Trying to allocate more artefacts than allowed for research %s", getResearchName(pResearch) ); abort(); return FALSE; } pResearch->storeCount++; //quick check that haven't reached maxArtes if (numResearchArteRed >= MAX_RESEARCH_ARTE_RED OR numResearchArteRes >= MAX_RESEARCH_ARTE_RES OR numResearchArteRep > MAX_RESEARCH_ARTE_RES) { //don't load any more since will write over memory! break; } //increment the pointer to the start of the next record pArteData = strchr(pArteData,'\n') + 1; } // FREE(pStartArteData); return TRUE; } //Load the Structures for a research list BOOL loadResearchStructures(char *pStructData, UDWORD bufferSize,UDWORD listNumber) { char *pStartStructData; UDWORD NumToAlloc = 0, i; STRING ResearchName[MAX_NAME_SIZE], StructureName[MAX_NAME_SIZE]; //UBYTE incR; UWORD incR; UWORD incS; RESEARCH *pResearch = asResearch; STRUCTURE_STATS *pStructure = asStructureStats; BOOL recFound; UDWORD numToFind; #ifdef HASH_NAMES UDWORD HashedResearchName; UDWORD HashedStructureName; #endif //initialise the storage flags for (incR = 0; incR < numResearch; incR++) { pResearch[incR].storeCount = 0; } pResearch = asResearch; pStartStructData = pStructData; NumToAlloc = numCR(pStructData, bufferSize); switch (listNumber) { case REQ_LIST: //check not going to go over max ASSERT( NumToAlloc <= MAX_RESEARCH_STRUCT_PR, "loadResearchStructures: too many Struct PRs" ); numResearchStructPR = 0; break; case RED_LIST: //check not going to go over max ASSERT( NumToAlloc <= MAX_RESEARCH_STRUCT_RED, "loadResearchStructures: too many redundant structure" ); numResearchStructRed = 0; break; case RES_LIST: //check not going to go over max ASSERT( NumToAlloc <= MAX_RESEARCH_STRUCT_RES, "loadResearchStructures: too many structure results" ); numResearchStructRes = 0; break; } for (i=0; i < NumToAlloc; i++) { recFound = FALSE; numToFind = 0; //read the data into the storage - the data is delimited using comma's ResearchName[0] = '\0'; StructureName[0] = '\0'; sscanf(pStructData,"%[^','],%[^','],%*d,%*d", ResearchName, StructureName); if (!getResourceName(ResearchName)) { return FALSE; } if (!getResourceName(StructureName)) { return FALSE; } #ifdef HASH_NAMES HashedResearchName=HashString(ResearchName); HashedStructureName=HashString(StructureName); #endif //loop through each Research to compare the name for (incR=0; incR < numResearch; incR++) { #ifdef HASH_NAMES if (pResearch[incR].NameHash==HashedResearchName) #else if (!(strcmp(ResearchName, pResearch[incR].pName))) #endif { //Research found for (incS=0; incS < numStructureStats; incS++) { #ifdef HASH_NAMES if (pStructure[incS].NameHash==HashedStructureName) #else if (!(strcmp(StructureName, pStructure[incS].pName))) #endif { //Structure found - alloc this to the current Research switch (listNumber) { case REQ_LIST: pResearch[incR].pStructList[pResearch[incR]. storeCount] = incS; //keep tab on how many we have loaded in numResearchStructPR++; numToFind = pResearch[incR].numStructures; break; case RED_LIST: pResearch[incR].pRedStructs[pResearch[incR]. storeCount] = incS; //keep tab on how many we have loaded in numResearchStructRed++; numToFind = pResearch[incR].numRedStructs; break; case RES_LIST: pResearch[incR].pStructureResults[pResearch[incR]. storeCount] = incS; //keep tab on how many we have loaded in numResearchStructRes++; numToFind = pResearch[incR].numStructResults; break; default: /* NO DEFAULT CASE? Alex.... Here ya go - just for you...*/ debug( LOG_ERROR, "Unknown research list" ); abort(); return FALSE; } recFound = TRUE; //check not allocating more than allowed if (pResearch[incR].storeCount > (SDWORD)numToFind) { debug( LOG_ERROR, "Trying to allocate more Structures than allowed for research %s", getResearchName(pResearch) ); abort(); return FALSE; } pResearch[incR].storeCount++; break; } } //if Structure not found - error if (!recFound) { debug( LOG_ERROR, "Unable to find Structure %s for research %s", StructureName, ResearchName ); abort(); return FALSE; } else { break; } } } //if Research not found - error if (!recFound) { debug( LOG_ERROR, "Unable to allocate all Research Structures for %s", ResearchName ); abort(); return FALSE; } //quick check that haven't reached max structs if (numResearchStructPR >= MAX_RESEARCH_STRUCT_PR OR numResearchStructRes >= MAX_RESEARCH_STRUCT_RES OR numResearchStructRed >= MAX_RESEARCH_STRUCT_RED) { //don't load any more since will write over memory! break; } //increment the pointer to the start of the next record pStructData = strchr(pStructData,'\n') + 1; } // FREE(pStartStructData); return TRUE; } //Load the pre-requisites for a research list BOOL loadResearchFunctions(char *pFunctionData, UDWORD bufferSize) { char *pStartFunctionData; UDWORD NumToAlloc = 0, i; STRING ResearchName[MAX_NAME_SIZE], FunctionName[MAX_NAME_SIZE]; UDWORD incR, incF; RESEARCH *pResearch = asResearch; FUNCTION **pFunction = asFunctions; BOOL recFound; #ifdef HASH_NAMES UDWORD HashedResearchName; UDWORD HashedFunctionName; #endif //initialise the storage flags for (incR = 0; incR < numResearch; incR++) { pResearch[incR].storeCount = 0; } pResearch = asResearch; pStartFunctionData = pFunctionData; NumToAlloc = numCR(pFunctionData, bufferSize); //check not going to go over max ASSERT( NumToAlloc <= MAX_RESEARCH_FUNC, "loadResearchFunctions: too many" ); numResearchFunc = 0; for (i=0; i < NumToAlloc; i++) { recFound = FALSE; //read the data into the storage - the data is delimited using comma's ResearchName[0] = '\0'; FunctionName[0] = '\0'; sscanf(pFunctionData,"%[^','],%[^','],%*d", ResearchName, FunctionName); if (!getResourceName(ResearchName)) { return FALSE; } #ifdef HASH_NAMES HashedResearchName=HashString(ResearchName); HashedFunctionName=HashString(FunctionName); #endif //loop through each Research to compare the name for (incR=0; incR < numResearch; incR++) { #ifdef HASH_NAMES if (pResearch[incR].NameHash==HashedResearchName) #else if (!(strcmp(ResearchName, pResearch[incR].pName))) #endif { //Research found for (incF=0; incF < numFunctions; incF++) { #ifdef HASH_NAMES if ((*pFunction[incF]).NameHash==HashedFunctionName) #else if (!(strcmp(FunctionName, (*pFunction[incF]).pName))) #endif { //Function found alloc this to the current Research pResearch[incR].pFunctionList[pResearch[incR]. storeCount] = pFunction[incF]; //keep tab on how many we have loaded in numResearchFunc++; recFound = TRUE; //check not allocating more than allowed if (pResearch[incR].storeCount > (SDWORD)pResearch[incR].numFunctions) { debug( LOG_ERROR, "Trying to allocate more Functions than allowed for research %s", ResearchName ); abort(); return FALSE; } pResearch[incR].storeCount++; break; } } //if Function not found - error if (!recFound) { debug( LOG_ERROR, "Unable to find Function %s for research %s", FunctionName, ResearchName ); abort(); return FALSE; } else { break; } } } //if Research not found - error if (!recFound) { debug( LOG_ERROR, "Unable to allocate all research Functions for %s", ResearchName ); abort(); return FALSE; } //quick check that haven't reached maxPR if (numResearchFunc >= MAX_RESEARCH_FUNC) { //don't load any more since will write over memory! break; } //increment the pointer to the start of the next record pFunctionData = strchr(pFunctionData,'\n') + 1; } // FREE(pStartFunctionData); return TRUE; } /* Function to check what can be researched for a particular player at any one instant. A topic can be researched if the playerRes 'possible' flag has been set (by script) or if the research pre-req topics have been researched. A check is made for any structures that are required to have been built for topics that do not have the 'possible' flag set. **NB** A topic with zero PR's can ONLY be researched once the 'possible' flag has been set. There can only be 'limit' number of entries 'topic' is the currently researched topic */ //needs to be UWORD sized for Patches //UBYTE fillResearchList(UBYTE *plist, UDWORD playerID, UWORD topic, UWORD limit) // NOTE by AJL may 99 - skirmish now has it's own version of this, skTopicAvail. UWORD fillResearchList(UWORD *plist, UDWORD playerID, UWORD topic, UWORD limit) { //UBYTE inc, count=0; UWORD inc, count=0; UDWORD incPR, incS; PLAYER_RESEARCH *pPlayerRes = asPlayerResList[playerID]; BOOL bPRFound, bStructFound; //needs to be UWORD sized for Patches ASSERT( numResearch < UWORD_MAX, "fillResearchList: only using a UWORD for storage - need more!" ); //ASSERT( numResearch < UBYTE_MAX, // "fillResearchList: only using a UBYTE for storage - need more!" ); for (inc=0; inc < numResearch; inc++) { //if the inc matches the 'topic' - automatically add to the list if (inc == topic) { goto add_research; } //if its a cancelled topic - add to list if (IsResearchCancelled(&pPlayerRes[inc])) { goto add_research; } //if the topic is possible and has not already been researched - add to list if ((IsResearchPossible(&pPlayerRes[inc]))) { if ((IsResearchCompleted(&pPlayerRes[inc])==FALSE) && (IsResearchStarted(&pPlayerRes[inc])==FALSE)) { goto add_research; } } //if single player mode and key topic, then ignore cos can't do it! if (!bMultiPlayer) { if (asResearch[inc].keyTopic) { continue; } } // make sure that the research is not completed or started by another researchfac if ((IsResearchCompleted(&pPlayerRes[inc])==FALSE) && (IsResearchStarted(&pPlayerRes[inc])==FALSE)) { // Research is not completed ... also it has not been started by another researchfac //if there aren't any PR's - go to next topic if (!asResearch[inc].numPRRequired) { continue; } //check for pre-requisites bPRFound = TRUE; for (incPR = 0; incPR < asResearch[inc].numPRRequired; incPR++) { if (IsResearchCompleted(&(pPlayerRes[asResearch[inc].pPRList[incPR]]))==0) { //if haven't pre-requisite - quit checking rest bPRFound = FALSE; break; } } if (!bPRFound) { //if haven't pre-requisites, skip the rest of the checks continue; } //check for structure effects bStructFound = TRUE; for (incS = 0; incS < asResearch[inc].numStructures; incS++) { //if (!checkStructureStatus(asStructureStats + asResearch[inc]. // pStructList[incS], playerID, SS_BUILT)) if (!checkSpecificStructExists(asResearch[inc].pStructList[incS], playerID)) { //if not built, quit checking bStructFound = FALSE; break; } } if (!bStructFound) { //if haven't all structs built, skip to next topic continue; } add_research: //if passed all the tests - add it to the list *plist++ = inc; count++; if (count == limit) { return count; } } } return count; } /* process the results of a completed research topic */ void researchResult(UDWORD researchIndex, UBYTE player, BOOL bDisplay) { RESEARCH *pResearch = asResearch + researchIndex; UDWORD type, inc;//, upgrade; STRUCTURE *psCurr; DROID *psDroid; FUNCTION *pFunction; UDWORD compInc; MESSAGE *pMessage; PLAYER_RESEARCH *pPlayerRes = asPlayerResList[player]; ASSERT( researchIndex < numResearch, "researchResult: invalid research index" ); sendReseachStatus(NULL, researchIndex, player, FALSE); MakeResearchCompleted(&pPlayerRes[researchIndex]); //check for structures to be made available for (inc = 0; inc < pResearch->numStructResults; inc++) { apStructTypeLists[player][pResearch->pStructureResults[inc]] = AVAILABLE; } //check for structures to be made redundant for (inc = 0; inc < pResearch->numRedStructs; inc++) { apStructTypeLists[player][pResearch->pRedStructs[inc]] = UNAVAILABLE; } //check for artefacts to be made available for (inc = 0; inc < pResearch->numArteResults; inc++) { //determine the type of artefact type = statType(pResearch->pArtefactResults[inc]->ref); //set the component state to AVAILABLE compInc = pResearch->pArtefactResults[inc]->ref - statRefStart(type); apCompLists[player][type][compInc] = AVAILABLE; //check for default sensor if (type == COMP_SENSOR) { if ((asSensorStats + compInc)->location == LOC_DEFAULT) { aDefaultSensor[player] = compInc; } } //check for default ECM if (type == COMP_ECM) { if ((asECMStats + compInc)->location == LOC_DEFAULT) { aDefaultECM[player] = compInc; } } //check for default Repair if (type == COMP_REPAIRUNIT) { if ((asRepairStats + compInc)->location == LOC_DEFAULT) { aDefaultRepair[player] = compInc; } } //check if this component replaces an 'older' component if (pResearch->pReplacedArtefacts[inc] != NULL) { replaceComponent(pResearch->pArtefactResults[inc], pResearch-> pReplacedArtefacts[inc], player); //set the 'old' component to unavailable type = statType(pResearch->pReplacedArtefacts[inc]->ref); //set the component state to AVAILABLE compInc = pResearch->pReplacedArtefacts[inc]->ref - statRefStart(type); apCompLists[player][type][compInc] = UNAVAILABLE; } //check if the component is a brain if (type == COMP_BRAIN) { cmdDroidAvailable(asBrainStats + compInc, player); } //check if self repair has come on line if (type == COMP_REPAIRUNIT) { if (asRepairStats[compInc].location == LOC_DEFAULT) { enableSelfRepair(player); } } } //check for artefacts to be made redundant for (inc = 0; inc < pResearch->numRedArtefacts; inc++) { //determine the type of artefact type = statType(pResearch->pRedArtefacts[inc]->ref); //set the component state to UNAVAILABLE apCompLists[player][type][pResearch->pRedArtefacts[inc]->ref - statRefStart(type)] = UNAVAILABLE; } //check for technology effects for (inc = 0; inc < pResearch->numFunctions; inc++) { pFunction = pResearch->pFunctionList[inc]; switch (pFunction->type) { case(PRODUCTION_UPGRADE_TYPE): { productionUpgrade(pFunction, player); //search the list of players structures for a Factory for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { /*if (psCurr->pStructureType->type == REF_FACTORY OR psCurr->pStructureType->type == REF_CYBORG_FACTORY OR psCurr->pStructureType->type == REF_VTOL_FACTORY) { //upgrade the Output productionUpgrade(pFunction, psCurr); }*/ if ((psCurr->pStructureType->type == REF_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->factory) OR (psCurr->pStructureType->type == REF_CYBORG_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->cyborgFactory) OR (psCurr->pStructureType->type == REF_VTOL_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->vtolFactory)) { //upgrade the Output for the structure structureProductionUpgrade(psCurr); } //set the function upgrade flag for future factories being built /*for (upgrade = 0; upgrade < numProductionUpgrades; upgrade++) { if (apProductionUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apProductionUpgrades[player][upgrade].available = TRUE; break; } }*/ } //and the mission structures for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if ((psCurr->pStructureType->type == REF_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->factory) OR (psCurr->pStructureType->type == REF_CYBORG_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->cyborgFactory) OR (psCurr->pStructureType->type == REF_VTOL_FACTORY AND ((PRODUCTION_UPGRADE_FUNCTION *)pFunction)->vtolFactory)) { //upgrade the Output for the structure structureProductionUpgrade(psCurr); } } // message/sound in here for production boost break; } case(RESEARCH_UPGRADE_TYPE): { researchUpgrade(pFunction, player); //search the list of players structures for a Research Facility for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_RESEARCH) { //upgrade the research points //researchUpgrade(pFunction, psCurr); structureResearchUpgrade(psCurr); } //set the function upgrade flag for future factories being built /*for (upgrade = 0; upgrade < numResearchUpgrades; upgrade++) { if (apResearchUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apResearchUpgrades[player][upgrade].available = TRUE; break; } }*/ } //and the mission structures for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_RESEARCH) { //upgrade the research points structureResearchUpgrade(psCurr); } } // Stuff a message in here/sound whatever for research boost. break; } case(POWER_UPGRADE_TYPE): { powerUpgrade(pFunction, player); //search the list of players structures for a Power Gens for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_POWER_GEN) { //upgrade the power points structurePowerUpgrade(psCurr); } } //and the mission structure for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_POWER_GEN) { //upgrade the power points structurePowerUpgrade(psCurr); } } break; } case(REARM_UPGRADE_TYPE): { reArmUpgrade(pFunction, player); //search the list of players structures for a ReArm pad for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_REARM_PAD) { //upgrade the rearm points structureReArmUpgrade(psCurr); } } //and the mission structure for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_REARM_PAD) { //upgrade the rearm points structureReArmUpgrade(psCurr); } } break; } case(REPAIR_UPGRADE_TYPE): { repairFacUpgrade(pFunction, player); //search the list of players structures for a Power Gens for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_REPAIR_FACILITY) { //upgrade the repair points structureRepairUpgrade(psCurr); } } //and the mission structure for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_REPAIR_FACILITY) { //upgrade the repair points structureRepairUpgrade(psCurr); } } break; } case(WEAPON_UPGRADE_TYPE): { //for the current player, upgrade the weapon stats weaponUpgrade(pFunction, player); // message/sound for weapon upgrade break; } case(DROIDSENSOR_UPGRADE_TYPE): { //for the current player, upgrade the sensor stats sensorUpgrade(pFunction, player); //for each structure in the player's list, upgrade the sensor stat for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { structureSensorUpgrade(psCurr); } for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { structureSensorUpgrade(psCurr); } //for each droid in the player's list, upgrade the sensor stat for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidSensorUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidSensorUpgrade); } } for (psDroid = mission.apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidSensorUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidSensorUpgrade); } } for (psDroid = apsLimboDroids[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidSensorUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidSensorUpgrade); } } // message/sound for sensor upgrade break; } case(DROIDECM_UPGRADE_TYPE): { //for the current player, upgrade the ecm stats ecmUpgrade(pFunction, player); //for each structure in the player's list, upgrade the ecm stat for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { structureECMUpgrade(psCurr); } for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { structureECMUpgrade(psCurr); } //for each droid in the player's list, upgrade the ecm stat for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidECMUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidECMUpgrade); } } for (psDroid = mission.apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidECMUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidECMUpgrade); } } for (psDroid = apsLimboDroids[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidECMUpgrade(psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidECMUpgrade); } } // message/sound for ecm upgrade break; } case(DROIDREPAIR_UPGRADE_TYPE): { //for the current player, upgrade the repair stats repairUpgrade(pFunction, player); // message/sound for repair upgrade break; } case(DROIDCONST_UPGRADE_TYPE): { //for the current player, upgrade the constructor stats constructorUpgrade(pFunction, player); // message/sound for constructor upgrade break; } case(DROIDBODY_UPGRADE_TYPE): { //for each droid in the player's list, upgrade the body points for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidBodyUpgrade(pFunction, psDroid); } for (psDroid = mission.apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidBodyUpgrade(pFunction, psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidSensorUpgrade); } } for (psDroid = apsLimboDroids[player]; psDroid != NULL; psDroid = psDroid->psNext) { droidBodyUpgrade(pFunction, psDroid); if (psDroid->droidType == DROID_TRANSPORTER) { upgradeTransporterDroids(psDroid, droidSensorUpgrade); } } //DO THIS AFTER so above calculations can use the previous upgrade values //for the current player, upgrade the body stats bodyUpgrade(pFunction, player); // message/sound for body upgrade break; } case(STRUCTURE_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the stats for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { //do this for none wallDefense structs if (!wallDefenceStruct(psCurr->pStructureType)) { structureBodyUpgrade(pFunction, psCurr); structureArmourUpgrade(pFunction, psCurr); structureResistanceUpgrade(pFunction, psCurr); } //defense type can have resistance upgrade now - AB 19/02/99 if (psCurr->pStructureType->type == REF_DEFENSE) { structureResistanceUpgrade(pFunction, psCurr); } } for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { //do this for none wallDefense structs if (!wallDefenceStruct(psCurr->pStructureType)) { structureBodyUpgrade(pFunction, psCurr); structureArmourUpgrade(pFunction, psCurr); structureResistanceUpgrade(pFunction, psCurr); } //defense type can have resistance upgrade now - AB 19/02/99 if (psCurr->pStructureType->type == REF_DEFENSE) { structureResistanceUpgrade(pFunction, psCurr); } } //DO THIS AFTER so above calculations can use the previous upgrade values //for the current player, upgrade the structure stats structureUpgrade(pFunction, player); // message/sound for structure upgrade break; } case(WALLDEFENCE_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the stats for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { //do this for wallDefense structs if (wallDefenceStruct(psCurr->pStructureType)) { structureBodyUpgrade(pFunction, psCurr); structureArmourUpgrade(pFunction, psCurr); } } for (psCurr = mission.apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { //do this for wallDefense structs if (wallDefenceStruct(psCurr->pStructureType)) { structureBodyUpgrade(pFunction, psCurr); structureArmourUpgrade(pFunction, psCurr); } } //DO THIS AFTER so above calculations can use the previous upgrade values //for the current player, upgrade the wall/defence structure stats wallDefenceUpgrade(pFunction, player); // message/sound for wall/defence structure upgrade break; } /*case(ARMOUR_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the armour type for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { armourUpgrade(pFunction, psCurr); } //set the function upgrade flag for future factories being built for (upgrade = 0; upgrade < numArmourUpgrades; upgrade++) { if (apArmourUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apArmourUpgrades[player][upgrade].available = TRUE; break; } } // message/sound in here for armour upgrade break; }*/ /*case(REPAIR_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the armour type for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { repairUpgrade(pFunction, psCurr); } //set the function upgrade flag for future factories being built for (upgrade = 0; upgrade < numRepairUpgrades; upgrade++) { if (apRepairUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apRepairUpgrades[player][upgrade].available = TRUE; break; } } //message/sound in here for repair points upgraded break; }*/ /*case(BODY_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the armour type for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { bodyUpgrade(pFunction, psCurr); } //set the function upgrade flag for future factories being built for (upgrade = 0; upgrade < numBodyUpgrades; upgrade++) { if (apBodyUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apBodyUpgrades[player][upgrade].available = TRUE; break; } } // message/sound in here for body points upgrade break; }*/ /*case(RESISTANCE_UPGRADE_TYPE): { //for each structure in the player's list, upgrade the armour type for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext) { resistanceUpgrade(pFunction, psCurr); } //set the function upgrade flag for future factories being built for (upgrade = 0; upgrade < numResistanceUpgrades; upgrade++) { if (apResistanceUpgrades[player][upgrade].functionInc == pFunction-> ref - REF_FUNCTION_START) { apResistanceUpgrades[player][upgrade].available = TRUE; break; } } // message/sound for resistance upgrade break; }*/ default: { ASSERT( FALSE,"Invalid function type" ); } }//end of switch }//end of function loop //Add message to player's list if Major Topic if ((pResearch->techCode == TC_MAJOR) && bDisplay) { //only play sound if major topic if (player == selectedPlayer) { audio_QueueTrack(ID_SOUND_MAJOR_RESEARCH); //add console text message addConsoleMessage(strresGetString(psStringRes, STR_INT_RESCOMPLETED), LEFT_JUSTIFY); } //check there is viewdata for the research topic - just don't add message if not! if (pResearch->pViewData != NULL) { pMessage = addMessage(MSG_RESEARCH, FALSE, player); if (pMessage != NULL) { pMessage->pViewData = (MSG_VIEWDATA *)pResearch->pViewData; } } } else { if ((player == selectedPlayer) && bDisplay) { audio_QueueTrack(ID_SOUND_RESEARCH_COMPLETED); //add console text message addConsoleMessage(strresGetString(psStringRes, STR_INT_RESCOMPLETED), LEFT_JUSTIFY); } } #ifdef SCRIPTS if ((player == selectedPlayer) && bDisplay) { psCBLastResearch = pResearch; eventFireCallbackTrigger((TRIGGER_TYPE)CALL_RESEARCHCOMPLETED); psCBLastResearch = NULL; } #endif //just a debug check... #ifdef DEBUG /*this just checks that there are not more than 32 weapons now available for the design screen - it'll give us grief in the design screen (which we may HAVE TO fix)!*/ //only check if selectedPlayer has done the research if (player == selectedPlayer) { UDWORD vtolCompInc; compInc = vtolCompInc =0; for (inc = 0; inc < numWeaponStats; inc++) { if (apCompLists[selectedPlayer][COMP_WEAPON][inc] == AVAILABLE AND asWeaponStats[inc].design) { if (asWeaponStats[inc].vtolAttackRuns) { vtolCompInc++; } else { compInc++; } if (compInc >= 32) { ASSERT( FALSE, "researchResult - more than 32 weapons now available" ); //don't bother checking any more break; } if (vtolCompInc >= 32) { ASSERT( FALSE, "researchResult - more than 32 vtol weapons now available" ); //don't bother checking any more break; } } } } #endif } /*This function is called when the research files are reloaded*/ BOOL ResearchShutDown(void) { //we don't malloc these anymore this way - 10/12/98 /*UDWORD inc; RESEARCH* pList = asResearch; for (inc=0; inc < numResearch; inc++) { #if !defined (RESOURCE_NAMES) && !defined (STORE_RESOURCE_ID) FREE(pList->pName); #endif //FREE(pList->pTechnologyName); // FREE(pList->pSubGroupName); if (pList->numRedArtefacts > 0) { FREE(pList->pRedArtefacts); } if (pList->numArteResults > 0) { FREE(pList->pArtefactResults); FREE(pList->pReplacedArtefacts); } if (pList->numPRRequired > 0) { FREE(pList->pPRList); } if (pList->numStructures > 0) { FREE(pList->pStructList); } if (pList->numRedStructs > 0) { FREE(pList->pRedStructs); } if (pList->numStructResults > 0) { FREE(pList->pStructureResults); } if (pList->numFunctions > 0) { FREE(pList->pFunctionList); } pList++; } if(numResearch) { FREE (asResearch); } //free up the lists for each player for (inc = 0; inc < MAX_PLAYERS; inc++) { if(asPlayerResList[inc]) { FREE (asPlayerResList[inc]); } }*/ UBYTE i; //don't allocate the memory now - done in initResearch, so just initialise it memset(asResearch, 0, (MAX_RESEARCH * sizeof(RESEARCH))); for (i=0; i < MAX_PLAYERS; i++) { memset(asPlayerResList[i], 0, (MAX_RESEARCH * sizeof(PLAYER_RESEARCH))); } //and init all the other arrays used //needs to be UWORD sized for the Patches memset(pResearchPR, 0, (MAX_RESEARCH_PR * sizeof(UWORD))); //memset(pResearchPR, 0, (MAX_RESEARCH_PR * sizeof(UBYTE))); memset(pResearchStructPR, 0, (MAX_RESEARCH_STRUCT_PR * sizeof(UWORD))); memset(pResearchFunc, 0, (MAX_RESEARCH_FUNC * sizeof(FUNCTION *))); memset(pResearchStructRed, 0, (MAX_RESEARCH_STRUCT_RED * sizeof(UWORD))); memset(pResearchArteRed, 0, (MAX_RESEARCH_ARTE_RED * sizeof(COMP_BASE_STATS *))); memset(pResearchStructRes, 0, (MAX_RESEARCH_STRUCT_RES * sizeof(UWORD))); memset(pResearchArteRes, 0, (MAX_RESEARCH_ARTE_RES * sizeof(COMP_BASE_STATS *))); memset(pResearchArteRep, 0, (MAX_RESEARCH_ARTE_RES * sizeof(COMP_BASE_STATS *))); return TRUE; } /*This function is called when a game finishes*/ BOOL ResearchRelease(void) { UBYTE i; //free all the pre-defined arrays for research FREE(asResearch); for (i=0; i < MAX_PLAYERS; i++) { FREE(asPlayerResList[i]); } FREE(pResearchPR); FREE(pResearchStructPR); FREE(pResearchFunc); FREE(pResearchStructRed); FREE(pResearchArteRed); FREE(pResearchStructRes); FREE(pResearchArteRes); FREE(pResearchArteRep); return TRUE; } /*puts research facility on hold*/ void holdResearch(STRUCTURE *psBuilding) { RESEARCH_FACILITY *psResFac; ASSERT( psBuilding->pStructureType->type == REF_RESEARCH, "holdResearch: structure not a research facility" ); psResFac = (RESEARCH_FACILITY *)psBuilding->pFunctionality; if (psResFac->psSubject) { //set the time the research facilty was put on hold psResFac->timeStartHold = gameTime; //play audio to indicate on hold if (psBuilding->player == selectedPlayer) { audio_PlayTrack(ID_SOUND_WINDOWCLOSE); } } } /*release a research facility from hold*/ void releaseResearch(STRUCTURE *psBuilding) { RESEARCH_FACILITY *psResFac; ASSERT( psBuilding->pStructureType->type == REF_RESEARCH, "releaseResearch: structure not a research facility" ); psResFac = (RESEARCH_FACILITY *)psBuilding->pFunctionality; if (psResFac->psSubject AND psResFac->timeStartHold) { //adjust the start time for the current subject if (psResFac->timeStarted != ACTION_START_TIME) { psResFac->timeStarted += (gameTime - psResFac->timeStartHold); } psResFac->timeStartHold = 0; } } /* Cancel All Research for player 0 */ void CancelAllResearch(UDWORD pl) { STRUCTURE *psCurr; for (psCurr=apsStructLists[pl]; psCurr != NULL; psCurr = psCurr->psNext) { if (psCurr->pStructureType->type == REF_RESEARCH) { if ( ( ((RESEARCH_FACILITY *)psCurr->pFunctionality)!=NULL ) && ( ((RESEARCH_FACILITY *)psCurr->pFunctionality)->psSubject !=NULL ) ) { debug( LOG_NEVER, "canceling research for %p\n", psCurr ); cancelResearch(psCurr); } } } } /* sets the status of the topic to cancelled and stores the current research points accquired */ void cancelResearch(STRUCTURE *psBuilding) { UDWORD topicInc; PLAYER_RESEARCH *pPlayerRes; RESEARCH_FACILITY *psResFac; ASSERT( psBuilding->pStructureType->type == REF_RESEARCH, "cancelResearch: structure not a research facility" ); psResFac = (RESEARCH_FACILITY *)psBuilding->pFunctionality; topicInc = ((RESEARCH *)psResFac->psSubject) - asResearch; if (topicInc > numResearch) { ASSERT( FALSE, "cancelResearch: invalid research topic" ); return; } pPlayerRes = asPlayerResList[psBuilding->player] + topicInc; if (psBuilding->pStructureType->type == REF_RESEARCH) { //check if waiting to accrue power if (psResFac->timeStarted == ACTION_START_TIME) { //return the power addPower(psBuilding->player, psResFac->powerAccrued); psResFac->powerAccrued = 0; //reset this topic as not having been researched ResetResearchStatus(pPlayerRes); } else { // only PC version saves these /*store the points - need to keep this so can add points after the topic has been cancelled and restarted*/ pPlayerRes->currentPoints += (psResFac->researchPoints * (gameTime - psResFac->timeStarted)) / GAME_TICKS_PER_SEC; //set the researched flag MakeResearchCancelled(pPlayerRes); } sendReseachStatus(psBuilding, topicInc, psBuilding->player, FALSE); //initialise the research facility's subject psResFac->psSubject = NULL; //set the researched flag - only set the flag to cancelled if it got past the accruePower stage //pPlayerRes->researched = CANCELLED_RESEARCH; } } /* For a given view data get the research this is related to */ RESEARCH * getResearchForMsg(VIEWDATA *pViewData) { UDWORD inc; RESEARCH *psResearch; for (inc = 0; inc < numResearch; inc++) { psResearch = asResearch + inc; //compare the pointer if (psResearch->pViewData == pViewData) { return psResearch; } } return NULL; } #ifdef HASH_NAMES static UWORD setIconIDFromHashedName(STRING *pIconName, UDWORD NameHash) { IMAGEDEF *Image; UWORD i; UDWORD IconHash = HashString(pIconName); IMAGEFILE *Images = (IMAGEFILE*)resGetData("IMG","intfac.img"); Image = Images->ImageDefs; for(i=0; iHeader.NumImages; i++) { if(IconHash == Image->HashValue) { // DBPRINTF(("Matched research icon #%d\n",IconHash)); return i; } Image++; } //add more names as images are created // ASSERT( FALSE, "Invalid icon graphic %s for topic %s", pIconName, pName ); debug( LOG_NEVER, "Failed to matched research icon %s #%d\n", pIconName, IconHash ); return 0; // Should never get here. } #else //set the iconID based on the name read in in the stats static UWORD setIconID(STRING *pIconName, STRING *pName) { //compare the names with those created in 'Framer' if (!strcmp(pIconName, "IMAGE_ROCKET")) { return IMAGE_ROCKET; } if (!strcmp(pIconName, "IMAGE_CANNON")) { return IMAGE_CANNON; } if (!strcmp(pIconName, "IMAGE_HOVERCRAFT")) { return IMAGE_HOVERCRAFT; } if (!strcmp(pIconName, "IMAGE_ECM")) { return IMAGE_ECM; } if (!strcmp(pIconName, "IMAGE_PLASCRETE")) { return IMAGE_PLASCRETE; } if (!strcmp(pIconName, "IMAGE_TRACKS")) { return IMAGE_TRACKS; } if (!strcmp(pIconName, "IMAGE_RES_DROIDTECH")) { return IMAGE_RES_DROIDTECH; } if (!strcmp(pIconName,"IMAGE_RES_WEAPONTECH")) { return IMAGE_RES_WEAPONTECH; } if (!strcmp(pIconName,"IMAGE_RES_COMPUTERTECH")) { return IMAGE_RES_COMPUTERTECH; } if (!strcmp(pIconName,"IMAGE_RES_POWERTECH")) { return IMAGE_RES_POWERTECH; } if (!strcmp(pIconName,"IMAGE_RES_SYSTEMTECH")) { return IMAGE_RES_SYSTEMTECH; } if (!strcmp(pIconName,"IMAGE_RES_STRUCTURETECH")) { return IMAGE_RES_STRUCTURETECH; } if (!strcmp(pIconName,"IMAGE_RES_CYBORGTECH")) { return IMAGE_RES_CYBORGTECH; } if (!strcmp(pIconName,"IMAGE_RES_DEFENCE")) { return IMAGE_RES_DEFENCE; } if (!strcmp(pIconName,"IMAGE_RES_QUESTIONMARK")) { return IMAGE_RES_QUESTIONMARK; } if (!strcmp(pIconName,"IMAGE_RES_GRPACC")) { return IMAGE_RES_GRPACC; } if (!strcmp(pIconName,"IMAGE_RES_GRPUPG")) { return IMAGE_RES_GRPUPG; } if (!strcmp(pIconName,"IMAGE_RES_GRPREP")) { return IMAGE_RES_GRPREP; } if (!strcmp(pIconName,"IMAGE_RES_GRPROF")) { return IMAGE_RES_GRPROF; } if (!strcmp(pIconName,"IMAGE_RES_GRPDAM")) { return IMAGE_RES_GRPDAM; } //add more names as images are created ASSERT( FALSE, "Invalid icon graphic %s for topic %s", pIconName, pName ); return 0; // Should never get here. } #endif SDWORD mapRIDToIcon( UDWORD rid ) { switch(rid) { case RID_ROCKET: return(IMAGE_ROCKET); break; case RID_CANNON: return(IMAGE_CANNON); break; case RID_HOVERCRAFT: return(IMAGE_HOVERCRAFT); break; case RID_ECM: return(IMAGE_ECM); break; case RID_PLASCRETE: return(IMAGE_PLASCRETE); break; case RID_TRACKS: return(IMAGE_TRACKS); break; case RID_DROIDTECH: return(IMAGE_RES_DROIDTECH); break; case RID_WEAPONTECH: return(IMAGE_RES_WEAPONTECH); break; case RID_COMPUTERTECH: return(IMAGE_RES_COMPUTERTECH); break; case RID_POWERTECH: return(IMAGE_RES_POWERTECH); break; case RID_SYSTEMTECH: return(IMAGE_RES_SYSTEMTECH); break; case RID_STRUCTURETECH: return(IMAGE_RES_STRUCTURETECH); break; case RID_CYBORGTECH: return(IMAGE_RES_CYBORGTECH); break; case RID_DEFENCE: return(IMAGE_RES_DEFENCE); break; case RID_QUESTIONMARK: return(IMAGE_RES_QUESTIONMARK); break; case RID_GRPACC: return(IMAGE_RES_GRPACC); break; case RID_GRPUPG: return(IMAGE_RES_GRPUPG); break; case RID_GRPREP: return(IMAGE_RES_GRPREP); break; case RID_GRPROF: return(IMAGE_RES_GRPROF); break; case RID_GRPDAM: return(IMAGE_RES_GRPDAM); break; default: ASSERT( FALSE,"Weirdy mapping request for RID to icon" ); return(-1); //pass back a value that can never have been set up break; } } SDWORD mapIconToRID(UDWORD iconID) { switch(iconID) { case IMAGE_ROCKET: return(RID_ROCKET); break; case IMAGE_CANNON: return(RID_CANNON); break; case IMAGE_HOVERCRAFT: return(RID_HOVERCRAFT); break; case IMAGE_ECM: return(RID_ECM); break; case IMAGE_PLASCRETE: return(RID_PLASCRETE); break; case IMAGE_TRACKS: return(RID_TRACKS); break; case IMAGE_RES_DROIDTECH: return(RID_DROIDTECH); break; case IMAGE_RES_WEAPONTECH: return(RID_WEAPONTECH); break; case IMAGE_RES_COMPUTERTECH: return(RID_COMPUTERTECH); break; case IMAGE_RES_POWERTECH: return(RID_POWERTECH); break; case IMAGE_RES_SYSTEMTECH: return(RID_SYSTEMTECH); break; case IMAGE_RES_STRUCTURETECH: return(RID_STRUCTURETECH); break; case IMAGE_RES_CYBORGTECH: return(RID_CYBORGTECH); break; case IMAGE_RES_DEFENCE: return(RID_DEFENCE); break; case IMAGE_RES_QUESTIONMARK: return(RID_QUESTIONMARK); break; case IMAGE_RES_GRPACC: return(RID_GRPACC); break; case IMAGE_RES_GRPUPG: return(RID_GRPUPG); break; case IMAGE_RES_GRPREP: return(RID_GRPREP); break; case IMAGE_RES_GRPROF: return(RID_GRPROF); break; case IMAGE_RES_GRPDAM: return(RID_GRPDAM); break; default: return(-1); //pass back a value that can never have been set up break; } } /* returns a pointer to a component based on the name - used to load in the research */ COMP_BASE_STATS * getComponentDetails(STRING *pName, STRING *pCompName) { UDWORD stat, size, quantity, address, inc; COMP_BASE_STATS *pArtefact; #ifdef HASH_NAMES UDWORD HashedName; #endif stat = componentType(pName); //get the stat list switch (stat) { case COMP_BODY: { pArtefact = (COMP_BASE_STATS*)asBodyStats; size = sizeof(BODY_STATS); quantity = numBodyStats; break; } case COMP_BRAIN: { pArtefact = (COMP_BASE_STATS*)asBrainStats; size = sizeof(BRAIN_STATS); quantity = numBrainStats; break; } case COMP_PROPULSION: { pArtefact = (COMP_BASE_STATS*)asPropulsionStats; size = sizeof(PROPULSION_STATS); quantity = numPropulsionStats; break; } case COMP_REPAIRUNIT: { pArtefact = (COMP_BASE_STATS*)asRepairStats; size = sizeof(REPAIR_STATS); quantity = numRepairStats; break; } case COMP_ECM: { pArtefact = (COMP_BASE_STATS*)asECMStats; size = sizeof(ECM_STATS); quantity = numECMStats; break; } case COMP_SENSOR: { pArtefact = (COMP_BASE_STATS*)asSensorStats; size = sizeof(SENSOR_STATS); quantity = numSensorStats; break; } /*case COMP_PROGRAM: { pArtefact = (COMP_BASE_STATS*)asProgramStats; size = sizeof(PROGRAM_STATS); quantity = numProgramStats; break; }*/ case COMP_WEAPON: { pArtefact = (COMP_BASE_STATS*)asWeaponStats; size = sizeof(WEAPON_STATS); quantity = numWeaponStats; break; } case COMP_CONSTRUCT: { pArtefact = (COMP_BASE_STATS*)asConstructStats; size = sizeof(CONSTRUCT_STATS); quantity = numConstructStats; break; } default: { //COMP_UNKNOWN should be an error debug( LOG_ERROR, "Unknown artefact type - %s", pName ); abort(); return FALSE; } } address = (UDWORD)pArtefact; #ifdef HASH_NAMES HashedName=HashString(pCompName); #endif for (inc = 0; inc < quantity; inc++) { #ifdef HASH_NAMES if (pArtefact->NameHash==HashedName) #else if (!strcmp(pArtefact->pName, pCompName)) #endif { return pArtefact; } address += size; pArtefact = (COMP_BASE_STATS*)address; } debug( LOG_ERROR, "Cannot find component %s", pCompName ); abort(); return NULL; } //return a pointer to a research topic based on the name RESEARCH * getResearch(STRING *pName, BOOL resName) { UDWORD inc; #ifdef HASH_NAMES UDWORD HashedName=HashString(pName); #endif //need to get the in game name if a resource name has been passed in if (resName) { if (!getResourceName(pName)) { debug(LOG_ERROR, "getResearch: resource not found"); return NULL; } } for (inc=0; inc < numResearch; inc++) { #ifdef HASH_NAMES if (asResearch[inc].NameHash==HashedName) #else if (!stricmp(asResearch[inc].pName, pName)) #endif { return &asResearch[inc]; } } for (inc = 0; inc < numResearch; inc++) { debug(LOG_ERROR, " Research %d: %s", inc, asResearch[inc].pName); } debug(LOG_ERROR, "Unknown research - %s", pName); assert(FALSE); return NULL; } /* looks through the players lists of structures and droids to see if any are using the old component - if any then replaces them with the new component */ void replaceComponent(COMP_BASE_STATS *pNewComponent, COMP_BASE_STATS *pOldComponent, UBYTE player) { //DROID *psDroid; DROID_TEMPLATE *psTemplates; //STRUCTURE *psStructure; UDWORD inc, oldType, newType, oldCompInc, newCompInc; //get the type and index of the old component oldType = statType(pOldComponent->ref); oldCompInc = pOldComponent->ref - statRefStart(oldType); //get the type and index of the new component newType = statType(pNewComponent->ref); newCompInc = pNewComponent->ref - statRefStart(newType); //check old and new type are the same if (oldType != newType) { return; } replaceDroidComponent(apsDroidLists[player], oldType, oldCompInc, newCompInc); replaceDroidComponent(mission.apsDroidLists[player], oldType, oldCompInc, newCompInc); replaceDroidComponent(apsLimboDroids[player], oldType, oldCompInc, newCompInc); //check thru the droids /*for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid = psDroid->psNext) { switch(oldType) { case COMP_BODY: case COMP_BRAIN: case COMP_PROPULSION: case COMP_REPAIRUNIT: case COMP_ECM: case COMP_SENSOR: case COMP_CONSTRUCT: if (psDroid->asBits[oldType].nStat == oldCompInc) { psDroid->asBits[oldType].nStat = (UBYTE)newCompInc; } break; //case COMP_PROGRAM: // for (inc=0; inc < psDroid->numProgs; inc++) // { // if ((psDroid->asProgs[inc].psStats->ref - REF_PROGRAM_START) == // oldCompInc) // { // psDroid->asProgs[inc].psStats = (asProgramStats + newCompInc); // } // } // break; case COMP_WEAPON: //can only be one weapon now //for (inc=0; inc < psDroid->numWeaps; inc++) if (psDroid->asWeaps[0].nStat > 0) { //if (psDroid->asWeaps[inc].nStat == oldCompInc) if (psDroid->asWeaps[0].nStat == oldCompInc) { //psDroid->asWeaps[inc].nStat = newCompInc; psDroid->asWeaps[0].nStat = newCompInc; } } break; default: //unknown comp type DBERROR(("Unknown component type - invalid droid")); return; } }*/ //check thru the templates for (psTemplates = apsDroidTemplates[player]; psTemplates != NULL; psTemplates = psTemplates->psNext) { switch(oldType) { case COMP_BODY: case COMP_BRAIN: case COMP_PROPULSION: case COMP_REPAIRUNIT: case COMP_ECM: case COMP_SENSOR: case COMP_CONSTRUCT: if (psTemplates->asParts[oldType] == (SDWORD)oldCompInc) { psTemplates->asParts[oldType] = newCompInc; } break; //case COMP_PROGRAM: // for (inc=0; inc < psTemplates->numProgs; inc++) // { // if (psTemplates->asProgs[inc] == oldCompInc) // { // psTemplates->asProgs[inc] = newCompInc; // } // } // break; case COMP_WEAPON: for (inc=0; inc < psTemplates->numWeaps; inc++) { if (psTemplates->asWeaps[inc] == oldCompInc) { psTemplates->asWeaps[inc] = newCompInc; } } break; default: //unknown comp type debug( LOG_ERROR, "Unknown component type - invalid Template" ); abort(); return; } } replaceStructureComponent(apsStructLists[player], oldType, oldCompInc, newCompInc, player); replaceStructureComponent(mission.apsStructLists[player], oldType, oldCompInc, newCompInc, player); //check thru the structures /*for (psStructure = apsStructLists[player]; psStructure != NULL; psStructure = psStructure->psNext) { switch (oldType) { case COMP_ECM: if (psStructure->pStructureType->pECM == (asECMStats + oldCompInc)) { psStructure->ecmPower = (UWORD)(asECMStats + newCompInc)->power; } break; case COMP_SENSOR: if (psStructure->pStructureType->pSensor == (asSensorStats + oldCompInc)) { //psStructure->sensorPower = (asSensorStats + newCompInc)->power; psStructure->sensorPower = (UWORD)sensorPower(asSensorStats + newCompInc,player); //psStructure->sensorRange = (asSensorStats + newCompInc)->range; psStructure->sensorRange = (UWORD)sensorRange(asSensorStats + newCompInc,player); } break; case COMP_WEAPON: //for (inc=0; inc < psStructure->numWeaps; inc++) //can only be one weapon now if (psStructure->asWeaps[0].nStat > 0) { //if (psStructure->asWeaps[inc].nStat == oldCompInc) //{ // psStructure->asWeaps[inc].nStat = newCompInc; //} if (psStructure->asWeaps[0].nStat == oldCompInc) { psStructure->asWeaps[0].nStat = newCompInc; } } break; default: //ignore all other component types break; } }*/ } /*Looks through all the currently allocated stats to check the name is not a duplicate*/ #ifndef HASH_NAMES static BOOL checkResearchName(RESEARCH *psResearch, UDWORD numStats) { UDWORD inc; char *pName=psResearch->pName; for (inc = 0; inc < numStats; inc++) { if (!strcmp(asResearch[inc].pName, pName)) { //oops! found the name ASSERT( FALSE, "Research name has already been used - %s", pName ); return FALSE; } } return TRUE; } #else static BOOL checkResearchName(RESEARCH *psResearch, UDWORD numStats) { UDWORD inc; for (inc = 0; inc < numStats; inc++) { if (asResearch[inc].NameHash==psResearch->NameHash) { //oops! found the name ASSERT( FALSE, "Research name has already been used - %x", psResearch->NameHash ); return FALSE; } } return TRUE; } #endif /* Sets the 'possible' flag for a player's research so the topic will appear in the research list next time the Research Facilty is selected */ BOOL enableResearch(RESEARCH *psResearch, UDWORD player) { UDWORD inc; PLAYER_RESEARCH *pPlayerRes = asPlayerResList[player]; STRUCTURE *psStruct; BOOL resFree = FALSE; inc = psResearch - asResearch; if (inc > numResearch) { ASSERT( FALSE, "enableResearch: Invalid research topic - %s", getResearchName(psResearch) ); return FALSE; } //found, so set the flag MakeResearchPossible(&pPlayerRes[inc]); if(player == selectedPlayer) { //set the research reticule button to flash if research facility is free for (psStruct = apsStructLists[selectedPlayer]; psStruct != NULL; psStruct=psStruct->psNext) { if (psStruct->pStructureType->type == REF_RESEARCH AND psStruct->status == SS_BUILT AND ((RESEARCH_FACILITY *)psStruct->pFunctionality)->psSubject == NULL) { resFree = TRUE; break; } } if (resFree) { flashReticuleButton(IDRET_RESEARCH); } } return TRUE; } /*find the last research topic of importance that the losing player did and 'give' the results to the reward player*/ void researchReward(UBYTE losingPlayer, UBYTE rewardPlayer) { UDWORD topicIndex, researchPoints, rewardID; STRUCTURE *psStruct; RESEARCH_FACILITY *psFacility; topicIndex = researchPoints = rewardID = 0; //look through the losing players structures to find a research facility for (psStruct = apsStructLists[losingPlayer]; psStruct != NULL; psStruct = psStruct->psNext) { if (psStruct->pStructureType->type == REF_RESEARCH) { psFacility = (RESEARCH_FACILITY *)psStruct->pFunctionality; if (psFacility->psBestTopic) { topicIndex = ((RESEARCH *)psFacility->psBestTopic)->ref - REF_RESEARCH_START; if (topicIndex) { //if it cost more - it is better (or should be) if (researchPoints < asResearch[topicIndex].researchPoints) { //store the 'best' topic researchPoints = asResearch[topicIndex].researchPoints; rewardID = topicIndex; } } } } } //if a topic was found give the reward player the results of that research if (rewardID) { researchResult(rewardID, rewardPlayer, TRUE); if (rewardPlayer == selectedPlayer) { //addConsoleMessage(strresGetString(psStringRes,STR_GAM_RESREWARD), DEFAULT_JUSTIFY); //name the actual reward //addConsoleMessage(asResearch[rewardID].pName, DEFAULT_JUSTIFY); CONPRINTF(ConsoleString,(ConsoleString,"%s :- %s", strresGetString(psStringRes,STR_GAM_RESREWARD), getName(asResearch[rewardID].pName))); } } /* Not worth mentioning if nothing useful to gain? if (rewardPlayer == selectedPlayer) { addConsoleMessage(strresGetString(psStringRes,STR_GAM_RESREWARD), DEFAULT_JUSTIFY); }*/ } #ifdef HASH_NAMES #define getResearchName(res) (strresGetString(NULL,res.NameHash)) #else #define getResearchName(res) (res.pName) #endif /*checks that the research has loaded up as expected - must be done after all research parts have been loaded*/ BOOL checkResearchStats(void) { UDWORD resInc, inc; for (resInc=0; resInc < numResearch; resInc++) { if (asResearch[resInc].numPRRequired == 0) { if (asResearch[resInc].pPRList != NULL) { ASSERT( FALSE, "checkResearchStats: PreReq for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numPRRequired; inc++) { if (asResearch[resInc].pPRList[inc] > numResearch) { ASSERT( FALSE, "checkResearchStats: Invalid PreReq for topic %s", getResearchName(asResearch[resInc]) ); return FALSE; } } } if (asResearch[resInc].numStructures == 0) { if (asResearch[resInc].pStructList != NULL) { ASSERT( FALSE, "checkResearchStats: StructureList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numStructures; inc++) { if (asResearch[resInc].pStructList[inc] > numStructureStats) { ASSERT( FALSE, "checkResearchStats: Invalid Structure for topic %s", getResearchName(asResearch[resInc]) ); return FALSE; } } } if (asResearch[resInc].numFunctions == 0) { if (asResearch[resInc].pFunctionList != NULL) { ASSERT( FALSE, "checkResearchStats: FunctionList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numFunctions; inc++) { if (asResearch[resInc].pFunctionList[inc]->ref - REF_FUNCTION_START > numFunctions) { ASSERT( FALSE, "checkResearchStats: Invalid function for %s", getResearchName(asResearch[resInc]) ); } } } if (asResearch[resInc].numRedStructs == 0) { if (asResearch[resInc].pRedStructs != NULL) { ASSERT( FALSE, "checkResearchStats: Redundant StructList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numRedStructs; inc++) { if (asResearch[resInc].pRedStructs[inc] > numStructureStats) { ASSERT( FALSE, "checkResearchStats: Invalid Redundant Structure for topic %s", getResearchName(asResearch[resInc]) ); return FALSE; } } } if (asResearch[resInc].numStructResults == 0) { if (asResearch[resInc].pStructureResults != NULL) { ASSERT( FALSE, "checkResearchStats: Result StructList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numStructResults; inc++) { if (asResearch[resInc].pStructureResults[inc] > numStructureStats) { ASSERT( FALSE, "checkResearchStats: Invalid Result Structure for topic %s", getResearchName(asResearch[resInc]) ); return FALSE; } } } if (asResearch[resInc].numArteResults == 0) { if (asResearch[resInc].pArtefactResults != NULL) { ASSERT( FALSE, "checkResearchStats: CompResultList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numArteResults; inc++) { ASSERT( PTRVALID(asResearch[resInc].pArtefactResults[inc], sizeof(COMP_BASE_STATS*)), "checkResearchStats: Invalid Comp Result for topic %s", getResearchName(asResearch[resInc]) ); } } if (asResearch[resInc].numRedArtefacts == 0) { if (asResearch[resInc].pRedArtefacts != NULL) { ASSERT( FALSE, "checkResearchStats: RedundantCompList for topic %s should be NULL", getResearchName(asResearch[resInc]) ); return FALSE; } } else { for (inc=0; inc < asResearch[resInc].numRedArtefacts; inc++) { ASSERT( PTRVALID(asResearch[resInc].pRedArtefacts[inc], sizeof(COMP_BASE_STATS*)), "checkResearchStats: Invalid Redundant Comp for topic %s", getResearchName(asResearch[resInc]) ); } } } return TRUE; } /*flag self repair so droids can start when idle*/ void enableSelfRepair(UBYTE player) { bSelfRepair[player] = TRUE; } /*check to see if any research has been completed that enables self repair*/ BOOL selfRepairEnabled(UBYTE player) { if (bSelfRepair[player]) { return TRUE; } else { return FALSE; } } /*checks the stat to see if its of type wall or defence*/ BOOL wallDefenceStruct(STRUCTURE_STATS *psStats) { if (psStats->type == REF_DEFENSE OR psStats->type == REF_WALL OR psStats->type == REF_WALLCORNER OR psStats->type == REF_BLASTDOOR) { return TRUE; } else { return FALSE; } } /*for a given list of droids, replace the old component if exists*/ void replaceDroidComponent(DROID *pList, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc) { DROID *psDroid; //check thru the droids for (psDroid = pList; psDroid != NULL; psDroid = psDroid->psNext) { switchComponent(psDroid, oldType, oldCompInc, newCompInc); //need to replace the units inside the transporter if (psDroid->droidType == DROID_TRANSPORTER) { replaceTransDroidComponents(psDroid, oldType, oldCompInc, newCompInc); } } } /*replaces any components necessary for units that are inside a transporter*/ void replaceTransDroidComponents(DROID *psTransporter, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc) { DROID *psCurr; ASSERT( psTransporter->droidType == DROID_TRANSPORTER, "replaceTransUnitComponents: invalid unit type" ); for (psCurr = psTransporter->psGroup->psList; psCurr != NULL; psCurr = psCurr->psGrpNext) { if (psCurr != psTransporter) { switchComponent(psCurr, oldType, oldCompInc, newCompInc); } } } void replaceStructureComponent(STRUCTURE *pList, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc, UBYTE player) { STRUCTURE *psStructure; //if the type is not one we are interested in, then don't bother checking if (!(oldType == COMP_ECM OR oldType == COMP_SENSOR OR oldType == COMP_WEAPON)) { return; } //check thru the structures for (psStructure = pList; psStructure != NULL; psStructure = psStructure->psNext) { switch (oldType) { case COMP_ECM: if (psStructure->pStructureType->pECM == (asECMStats + oldCompInc)) { psStructure->ecmPower = (UWORD)(asECMStats + newCompInc)->power; } break; case COMP_SENSOR: if (psStructure->pStructureType->pSensor == (asSensorStats + oldCompInc)) { //psStructure->sensorPower = (asSensorStats + newCompInc)->power; psStructure->sensorPower = (UWORD)sensorPower(asSensorStats + newCompInc,player); //psStructure->sensorRange = (asSensorStats + newCompInc)->range; psStructure->sensorRange = (UWORD)sensorRange(asSensorStats + newCompInc,player); } break; case COMP_WEAPON: //for (inc=0; inc < psStructure->numWeaps; inc++) //can only be one weapon now if (psStructure->asWeaps[0].nStat > 0) { /*if (psStructure->asWeaps[inc].nStat == oldCompInc) { psStructure->asWeaps[inc].nStat = newCompInc; }*/ if (psStructure->asWeaps[0].nStat == oldCompInc) { psStructure->asWeaps[0].nStat = newCompInc; } } break; default: //ignore all other component types break; } } } /*swaps the old component for the new one for a specific droid*/ void switchComponent(DROID *psDroid, UDWORD oldType, UDWORD oldCompInc, UDWORD newCompInc) { ASSERT( PTRVALID(psDroid, sizeof(DROID)), "switchComponent:invalid droid pointer" ); switch(oldType) { case COMP_BODY: case COMP_BRAIN: case COMP_PROPULSION: case COMP_REPAIRUNIT: case COMP_ECM: case COMP_SENSOR: case COMP_CONSTRUCT: if (psDroid->asBits[oldType].nStat == oldCompInc) { psDroid->asBits[oldType].nStat = (UBYTE)newCompInc; } break; /*case COMP_PROGRAM: for (inc=0; inc < psDroid->numProgs; inc++) { if ((psDroid->asProgs[inc].psStats->ref - REF_PROGRAM_START) == oldCompInc) { psDroid->asProgs[inc].psStats = (asProgramStats + newCompInc); } } break;*/ case COMP_WEAPON: //can only be one weapon now //for (inc=0; inc < psDroid->numWeaps; inc++) if (psDroid->asWeaps[0].nStat > 0) { //if (psDroid->asWeaps[inc].nStat == oldCompInc) if (psDroid->asWeaps[0].nStat == oldCompInc) { //psDroid->asWeaps[inc].nStat = newCompInc; psDroid->asWeaps[0].nStat = newCompInc; } } break; default: //unknown comp type debug( LOG_ERROR, "Unknown component type - invalid droid" ); abort(); return; } }