ThingFactory.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: ThingFactory.cpp /////////////////////////////////////////////////////////////////////////
  24. // Created: Colin Day, April 2001
  25. // Desc: This is how we go and make our things, we make our things, we make our things!
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/ThingFactory.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/FileSystem.h"
  32. #include "Common/GameAudio.h"
  33. #include "Common/MapObject.h"
  34. #include "Common/ModuleFactory.h"
  35. #include "Common/RandomValue.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Object.h"
  38. #include "Common/Player.h"
  39. #include "Common/PlayerList.h"
  40. #include "GameLogic/PartitionManager.h"
  41. #include "GameLogic/Module/CreateModule.h"
  42. #include "Common/ProductionPrerequisite.h"
  43. #include "GameClient/GameClient.h"
  44. #include "GameClient/Drawable.h"
  45. #include "Common/INI.h"
  46. #ifdef _INTERNAL
  47. // for occasional debugging...
  48. //#pragma optimize("", off)
  49. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  50. #endif
  51. enum { TEMPLATE_HASH_SIZE = 12288 };
  52. // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
  53. ThingFactory *TheThingFactory = NULL; ///< Thing manager singleton declaration
  54. // STATIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  55. ///////////////////////////////////////////////////////////////////////////////////////////////////
  56. // PRIVATE METHODS
  57. ///////////////////////////////////////////////////////////////////////////////////////////////////
  58. //-------------------------------------------------------------------------------------------------
  59. /** Free all data loaded into this template database */
  60. //-------------------------------------------------------------------------------------------------
  61. void ThingFactory::freeDatabase( void )
  62. {
  63. while (m_firstTemplate)
  64. {
  65. ThingTemplate* tmpl = m_firstTemplate;
  66. m_firstTemplate = m_firstTemplate->friend_getNextTemplate();
  67. tmpl->deleteInstance();
  68. }
  69. m_templateHashMap.clear();
  70. } // end freeDatabase
  71. //-------------------------------------------------------------------------------------------------
  72. /** add the thing template passed in, into the databse */
  73. //-------------------------------------------------------------------------------------------------
  74. void ThingFactory::addTemplate( ThingTemplate *tmplate )
  75. {
  76. ThingTemplateHashMapIt tIt = m_templateHashMap.find(tmplate->getName());
  77. if (tIt != m_templateHashMap.end()) {
  78. DEBUG_CRASH(("Duplicate Thing Template name found: %s\n", tmplate->getName().str()));
  79. }
  80. // Link it to the list
  81. tmplate->friend_setNextTemplate(m_firstTemplate);
  82. m_firstTemplate = tmplate;
  83. // Add it to the hash table.
  84. m_templateHashMap[tmplate->getName()] = tmplate;
  85. } // end addTemplate
  86. ///////////////////////////////////////////////////////////////////////////////////////////////////
  87. // PUBLIC METHODS
  88. ///////////////////////////////////////////////////////////////////////////////////////////////////
  89. //-------------------------------------------------------------------------------------------------
  90. //-------------------------------------------------------------------------------------------------
  91. ThingFactory::ThingFactory()
  92. {
  93. m_firstTemplate = NULL;
  94. m_nextTemplateID = 1; // not zero!
  95. m_templateHashMap.resize( TEMPLATE_HASH_SIZE );
  96. } // end ThingFactory
  97. //-------------------------------------------------------------------------------------------------
  98. //-------------------------------------------------------------------------------------------------
  99. ThingFactory::~ThingFactory()
  100. {
  101. // free all the template data
  102. freeDatabase();
  103. } // end ~ThingFactory
  104. //-------------------------------------------------------------------------------------------------
  105. /** Create a new template with name 'name' and add to our template list */
  106. //-------------------------------------------------------------------------------------------------
  107. ThingTemplate *ThingFactory::newTemplate( const AsciiString& name )
  108. {
  109. ThingTemplate *newTemplate;
  110. // allocate template
  111. newTemplate = newInstance(ThingTemplate);
  112. // if the default template is present, get it and copy over any data to the new template
  113. const ThingTemplate *defaultT = findTemplate( AsciiString( "DefaultThingTemplate" ), FALSE );
  114. if( defaultT )
  115. {
  116. // copy over static data
  117. *newTemplate = *defaultT;
  118. newTemplate->setCopiedFromDefault();
  119. } // end if
  120. // give template a unique identifier
  121. newTemplate->friend_setTemplateID( m_nextTemplateID++ );
  122. DEBUG_ASSERTCRASH( m_nextTemplateID != 0, ("m_nextTemplateID wrapped to zero") );
  123. // assign name
  124. newTemplate->friend_setTemplateName( name );
  125. // add to list
  126. addTemplate( newTemplate );
  127. // return the newly created template
  128. return newTemplate;
  129. }
  130. //-------------------------------------------------------------------------------------------------
  131. /** Create newTemplate, copy data from final override of 'thingTemplate' to the newly created one,
  132. * and add newTemplate as the m_override of that final override. NOTE that newTemplate
  133. * is *NOT* added to master template list, it is a hidden place to store
  134. * override values for 'thingTemplate' */
  135. //-------------------------------------------------------------------------------------------------
  136. ThingTemplate* ThingFactory::newOverride( ThingTemplate *thingTemplate )
  137. {
  138. // sanity
  139. DEBUG_ASSERTCRASH( thingTemplate, ("newOverride(): NULL 'parent' thing template\n") );
  140. // sanity just for debuging, the weapon must be in the master list to do overrides
  141. DEBUG_ASSERTCRASH( findTemplate( thingTemplate->getName() ) != NULL,
  142. ("newOverride(): Thing template '%s' not in master list\n",
  143. thingTemplate->getName().str()) );
  144. // find final override of the 'parent' template
  145. ThingTemplate *child = (ThingTemplate*) thingTemplate->friend_getFinalOverride();
  146. // allocate new template
  147. ThingTemplate *newTemplate = newInstance(ThingTemplate);
  148. // copy data from final override to 'newTemplate' as a set of initial default values
  149. *newTemplate = *child;
  150. newTemplate->setCopiedFromDefault();
  151. newTemplate->markAsOverride();
  152. child->setNextOverride(newTemplate);
  153. // return the newly created override for us to set values with etc
  154. return newTemplate;
  155. } // end newOverride
  156. //-------------------------------------------------------------------------------------------------
  157. /** Init */
  158. //-------------------------------------------------------------------------------------------------
  159. void ThingFactory::init( void )
  160. {
  161. } // end init
  162. //-------------------------------------------------------------------------------------------------
  163. /** Reset */
  164. //-------------------------------------------------------------------------------------------------
  165. void ThingFactory::reset( void )
  166. {
  167. ThingTemplate *t;
  168. // go through all templates and delete any overrides
  169. for( t = m_firstTemplate; t; /* empty */ )
  170. {
  171. Bool possibleAdjustment = FALSE;
  172. // t itself can be deleted if it is something created for this map only. Therefore,
  173. // we need to store what the next item is so that we don't orphan a bunch of templates.
  174. ThingTemplate *nextT = t->friend_getNextTemplate();
  175. if (t == m_firstTemplate) {
  176. possibleAdjustment = TRUE;
  177. }
  178. // if stillValid is NULL after we delete the overrides, then this template was created for
  179. // this map only. If it also happens to be m_firstTemplate, then we need to update m_firstTemplate
  180. // as well. Finally, if it was only created for this map, we need to remove the name from the
  181. // hash map, to prevent any crashes.
  182. AsciiString templateName = t->getName();
  183. Overridable *stillValid = t->deleteOverrides();
  184. if (stillValid == NULL && possibleAdjustment) {
  185. m_firstTemplate = nextT;
  186. }
  187. if (stillValid == NULL) {
  188. // Also needs to be removed from the Hash map.
  189. m_templateHashMap.erase(templateName);
  190. }
  191. t = nextT;
  192. }
  193. } // end reset
  194. //-------------------------------------------------------------------------------------------------
  195. /** Update */
  196. //-------------------------------------------------------------------------------------------------
  197. void ThingFactory::update( void )
  198. {
  199. } // end update
  200. //-------------------------------------------------------------------------------------------------
  201. /** Return the template with the matching database name */
  202. //-------------------------------------------------------------------------------------------------
  203. const ThingTemplate *ThingFactory::findByTemplateID( UnsignedShort id )
  204. {
  205. for (ThingTemplate *tmpl = m_firstTemplate; tmpl; tmpl = tmpl->friend_getNextTemplate())
  206. {
  207. if (tmpl->getTemplateID() == id)
  208. return tmpl;
  209. }
  210. DEBUG_CRASH(("template %d not found\n",(Int)id));
  211. return NULL;
  212. }
  213. //-------------------------------------------------------------------------------------------------
  214. /** Return the template with the matching database name */
  215. //-------------------------------------------------------------------------------------------------
  216. ThingTemplate *ThingFactory::findTemplateInternal( const AsciiString& name, Bool check )
  217. {
  218. ThingTemplateHashMapIt tIt = m_templateHashMap.find(name);
  219. if (tIt != m_templateHashMap.end()) {
  220. return tIt->second;
  221. }
  222. #ifdef LOAD_TEST_ASSETS
  223. if (!strncmp(name.str(), TEST_STRING, strlen(TEST_STRING)))
  224. {
  225. ThingTemplate *tmplate = newTemplate( AsciiString( "Un-namedTemplate" ) );
  226. // load the values
  227. tmplate->initForLTA( name );
  228. // Kinda lame, but necessary.
  229. m_templateHashMap.erase("Un-namedTemplate");
  230. m_templateHashMap[name] = tmplate;
  231. // add tmplate template to the database
  232. return findTemplateInternal( name );
  233. }
  234. #endif
  235. if( check && name.isNotEmpty() )
  236. {
  237. DEBUG_CRASH( ("Failed to find thing template %s (case sensitive) This issue has a chance of crashing after you ignore it!", name.str() ) );
  238. }
  239. return NULL;
  240. } // end getTemplate
  241. //=============================================================================
  242. Object *ThingFactory::newObject( const ThingTemplate *tmplate, Team *team, ObjectStatusMaskType statusBits )
  243. {
  244. if (tmplate == NULL)
  245. throw ERROR_BAD_ARG;
  246. const std::vector<AsciiString>& asv = tmplate->getBuildVariations();
  247. if (!asv.empty())
  248. {
  249. Int which = GameLogicRandomValue(0, asv.size()-1);
  250. const ThingTemplate* tmp = findTemplate( asv[which] );
  251. if (tmp != NULL)
  252. tmplate = tmp;
  253. }
  254. DEBUG_ASSERTCRASH(!tmplate->isKindOf(KINDOF_DRAWABLE_ONLY), ("You may not create Objects with the template %s, only Drawables\n",tmplate->getName().str()));
  255. // have the game logic create an object of the correct type.
  256. // (this will throw an exception on failure.)
  257. //Added ability to pass in optional statusBits. This is needed to be set prior to
  258. //the onCreate() calls... in the case of constructing.
  259. Object *obj = TheGameLogic->friend_createObject( tmplate, statusBits, team );
  260. // run the create function for the thing
  261. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  262. {
  263. CreateModuleInterface* create = (*m)->getCreate();
  264. if (!create)
  265. continue;
  266. create->onCreate();
  267. }
  268. //
  269. // all objects are part of the partition manager system, add it to that
  270. // system now
  271. //
  272. ThePartitionManager->registerObject( obj );
  273. obj->initObject();
  274. return obj;
  275. }
  276. //=============================================================================
  277. Drawable *ThingFactory::newDrawable(const ThingTemplate *tmplate, DrawableStatus statusBits)
  278. {
  279. if (tmplate == NULL)
  280. throw ERROR_BAD_ARG;
  281. Drawable *draw = TheGameClient->friend_createDrawable( tmplate, statusBits );
  282. /** @todo we should keep track of all the drawables we've allocated here
  283. but we'll wait until we have an drawable storage to do that cause it will
  284. all be tied together */
  285. return draw;
  286. } // end newDrawableByType
  287. #if defined(_DEBUG) || defined(_INTERNAL)
  288. AsciiString TheThingTemplateBeingParsedName;
  289. #endif
  290. //-------------------------------------------------------------------------------------------------
  291. /** Parse Object entry */
  292. //-------------------------------------------------------------------------------------------------
  293. /*static*/ void ThingFactory::parseObjectDefinition( INI* ini, const AsciiString& name, const AsciiString& reskinFrom )
  294. {
  295. #if defined(_DEBUG) || defined(_INTERNAL)
  296. TheThingTemplateBeingParsedName = name;
  297. #endif
  298. // find existing item if present
  299. ThingTemplate *thingTemplate = TheThingFactory->findTemplateInternal( name, FALSE );
  300. if( !thingTemplate )
  301. {
  302. // no item is present, create a new one
  303. thingTemplate = TheThingFactory->newTemplate( name );
  304. if ( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
  305. {
  306. // This ThingTemplate is actually an override, so we will mark it as such so that it properly
  307. // gets deleted on ::reset().
  308. thingTemplate->markAsOverride();
  309. }
  310. }
  311. else if( ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES )
  312. {
  313. //Holy crap, this sucks to debug!!!
  314. //If you have two different objects, the previous code would simply
  315. //allow you to define multiple objects with the same name, and just
  316. //nuke the old one with the new one. So, I (KM) have added this
  317. //assert to notify in case of two same-name objects.
  318. DEBUG_CRASH(( "[LINE: %d in '%s'] Duplicate factionunit %s found!", ini->getLineNum(), ini->getFilename().str(), name.str() ));
  319. }
  320. else
  321. {
  322. thingTemplate = TheThingFactory->newOverride( thingTemplate );
  323. }
  324. if (reskinFrom.isNotEmpty())
  325. {
  326. const ThingTemplate* reskinTmpl = TheThingFactory->findTemplate(reskinFrom);
  327. if (reskinTmpl)
  328. {
  329. thingTemplate->copyFrom(reskinTmpl);
  330. thingTemplate->setCopiedFromDefault();
  331. thingTemplate->setReskinnedFrom(reskinTmpl);
  332. ini->initFromINI( thingTemplate, thingTemplate->getReskinFieldParse() );
  333. }
  334. else
  335. {
  336. DEBUG_CRASH(("ObjectReskin must come after the original Object (%s, %s).\n",reskinFrom.str(),name.str()));
  337. throw INI_INVALID_DATA;
  338. }
  339. }
  340. else
  341. {
  342. ini->initFromINI( thingTemplate, thingTemplate->getFieldParse() );
  343. }
  344. thingTemplate->validate();
  345. if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
  346. {
  347. thingTemplate->resolveNames();
  348. }
  349. #if defined(_DEBUG) || defined(_INTERNAL)
  350. TheThingTemplateBeingParsedName.clear();
  351. #endif
  352. }
  353. //#define CHECK_THING_NAMES
  354. #ifdef CHECK_THING_NAMES
  355. #include "Common/STLTypedefs.h"
  356. const char *outFilenameINI = "thing.txt";
  357. const char *outFilenameStringFile = "thingString.txt";
  358. void resetReportFile( void )
  359. {
  360. FILE *fp = fopen(outFilenameINI, "w");
  361. if (fp)
  362. {
  363. fprintf(fp, "-- ThingTemplate INI Report --\n\n");
  364. fclose(fp);
  365. }
  366. fp = fopen(outFilenameStringFile, "w");
  367. if (fp)
  368. {
  369. fprintf(fp, "-- ThingTemplate String File Report --\n\n");
  370. fclose(fp);
  371. }
  372. }
  373. AsciiStringList missingStrings;
  374. void reportMissingNameInStringFile( AsciiString templateName )
  375. {
  376. // see if we've seen it before
  377. AsciiStringListConstIterator cit = std::find(missingStrings.begin(), missingStrings.end(), templateName);
  378. if (cit != missingStrings.end())
  379. return;
  380. missingStrings.push_back(templateName);
  381. }
  382. void dumpMissingStringNames( void )
  383. {
  384. missingStrings.sort();
  385. FILE *fp = fopen(outFilenameStringFile, "w");
  386. if (fp)
  387. {
  388. fprintf(fp, "-- ThingTemplate String File Report --\n\n");
  389. for (AsciiStringListConstIterator cit = missingStrings.begin(); cit!=missingStrings.end(); cit++)
  390. {
  391. fprintf(fp, "OBJECT:%s\n\"%s\"\nEND\n\n", cit->str(), cit->str());
  392. }
  393. fclose(fp);
  394. }
  395. }
  396. AsciiStringList missingNames;
  397. void reportMissingNameInTemplate( AsciiString templateName )
  398. {
  399. // see if we've seen it before
  400. AsciiStringListConstIterator cit = std::find(missingNames.begin(), missingNames.end(), templateName);
  401. if (cit != missingNames.end())
  402. return;
  403. missingNames.push_back(templateName);
  404. FILE *fp = fopen(outFilenameINI, "a+");
  405. if (fp)
  406. {
  407. fprintf(fp, " DisplayName = OBJECT:%s\n", templateName.str());
  408. fclose(fp);
  409. }
  410. //reportMissingNameInStringFile( templateName );
  411. }
  412. #endif
  413. //-------------------------------------------------------------------------------------------------
  414. /** Post process phase after loading the database files */
  415. //-------------------------------------------------------------------------------------------------
  416. void ThingFactory::postProcessLoad()
  417. {
  418. #ifdef CHECK_THING_NAMES
  419. //resetReportFile();
  420. #endif
  421. // go through all thing templates
  422. for( ThingTemplate *thingTemplate = m_firstTemplate;
  423. thingTemplate;
  424. thingTemplate = thingTemplate->friend_getNextTemplate() )
  425. {
  426. // resolve the prerequisite names
  427. thingTemplate->resolveNames();
  428. #ifdef CHECK_THING_NAMES
  429. if (thingTemplate->getDisplayName().isEmpty())
  430. {
  431. reportMissingNameInTemplate( thingTemplate->getName() );
  432. }
  433. else if (wcsstr(thingTemplate->getDisplayName().str(), L"MISSING:"))
  434. {
  435. AsciiString asciiName;
  436. asciiName.translate(thingTemplate->getDisplayName());
  437. asciiName.removeLastChar();
  438. asciiName = asciiName.str() + 17;
  439. reportMissingNameInStringFile( asciiName );
  440. }
  441. #endif
  442. } // end for
  443. #ifdef CHECK_THING_NAMES
  444. dumpMissingStringNames();
  445. exit(0);
  446. #endif
  447. } // end postProcess