ThingTemplate.cpp 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613
  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: ThingTemplate.cpp ////////////////////////////////////////////////////////////////////////
  24. // Created: Colin Day, April 2001
  25. // Desc: Thing templates
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_POWER_NAMES // for PowerNames[]
  30. #define DEFINE_SHADOW_NAMES // for TheShadowNames[]
  31. #define DEFINE_GEOMETRY_NAMES // for GeometryNames[]
  32. #define DEFINE_BUILD_COMPLETION_NAMES // for BuildCompletionNames[]
  33. #define DEFINE_EDITOR_SORTING_NAMES // for EditorSortingNames[]
  34. #define DEFINE_RADAR_PRIORITY_NAMES // for RadarPriorityNames[]
  35. #define DEFINE_BUILDABLE_STATUS_NAMES // for BuildableStatusNames[]
  36. #include "Common/DamageFX.h"
  37. #include "Common/GameAudio.h"
  38. #include "Common/GameCommon.h"
  39. #include "Common/GlobalData.h"
  40. #include "Common/INI.h"
  41. #include "Common/MessageStream.h"
  42. #include "Common/Module.h"
  43. #include "Common/ModuleFactory.h"
  44. #include "Common/Player.h"
  45. #include "Common/PlayerList.h"
  46. #include "Common/ProductionPrerequisite.h"
  47. #include "Common/Radar.h"
  48. #include "Common/RandomValue.h"
  49. #include "Common/Science.h"
  50. #include "Common/ThingTemplate.h"
  51. #include "Common/ThingFactory.h"
  52. #include "Common/ThingSort.h"
  53. #include "Common/BitFlagsIO.h"
  54. #include "GameClient/Drawable.h"
  55. #include "GameClient/FXList.h"
  56. #include "GameClient/GameText.h"
  57. #include "GameClient/Image.h"
  58. #include "GameClient/Shadow.h"
  59. #include "GameLogic/Armor.h"
  60. #include "GameLogic/Module/AIUpdate.h"
  61. #include "GameLogic/Module/SpecialPowerModule.h"
  62. #include "GameLogic/Object.h"
  63. #include "GameLogic/Powers.h"
  64. #include "GameLogic/Weapon.h"
  65. #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
  66. #ifdef _INTERNAL
  67. // for occasional debugging...
  68. //#pragma optimize("", off)
  69. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  70. #endif
  71. //-------------------------------------------------------------------------------------------------
  72. //-------------------------------------------------------------------------------------------------
  73. const Int USE_EXP_VALUE_FOR_SKILL_VALUE = -999;
  74. ///////////////////////////////////////////////////////////////////////////////////////////////////
  75. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  76. ///////////////////////////////////////////////////////////////////////////////////////////////////
  77. AudioEventRTS ThingTemplate::s_audioEventNoSound;
  78. /*
  79. NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem!
  80. "s_objectReskinFieldParseTable" is intended to be used for parsing the "ObjectReskin" keyword, and
  81. should only allow you to specify things that affect the appearance of a template.
  82. The idea is that some units are functionally the same, but different visually; rather than replicate
  83. all the settings, you can specify it as "this one is like that one, but looks different".
  84. Thus, currently, the only things in the reskin table are:
  85. -- DrawModules
  86. -- DrawModuleData
  87. -- Geometry
  88. So: if you add/remove/modify any settings that deal with visual appearance, you *may* want to
  89. add 'em to the reskin table... but do so VERY CAUTIOUSLY and with careful deliberation (and
  90. after checking around for other opinions).
  91. */
  92. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  93. const FieldParse ThingTemplate::s_objectFieldParseTable[] =
  94. {
  95. { "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ThingTemplate, m_displayName ) },
  96. { "RadarPriority", INI::parseByteSizedIndexList, RadarPriorityNames, offsetof( ThingTemplate, m_radarPriority ) },
  97. { "TransportSlotCount", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_transportSlotCount ) },
  98. { "FenceWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceWidth ) },
  99. { "FenceXOffset", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceXOffset ) },
  100. { "IsBridge", INI::parseBool, NULL, offsetof( ThingTemplate, m_isBridge ) },
  101. { "ArmorSet", ThingTemplate::parseArmorTemplateSet, NULL, 0},
  102. { "WeaponSet", ThingTemplate::parseWeaponTemplateSet,NULL, 0},
  103. { "VisionRange", INI::parseReal, NULL, offsetof( ThingTemplate, m_visionRange ) },
  104. { "ShroudClearingRange", INI::parseReal, NULL, offsetof( ThingTemplate, m_shroudClearingRange ) },
  105. { "ShroudRevealToAllRange", INI::parseReal, NULL, offsetof( ThingTemplate, m_shroudRevealToAllRange ) },
  106. { "PlacementViewAngle", INI::parseAngleReal, NULL, offsetof( ThingTemplate, m_placementViewAngle ) },
  107. { "FactoryExitWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_factoryExitWidth ) },
  108. { "FactoryExtraBibWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_factoryExtraBibWidth ) },
  109. { "SkillPointValue", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_skillPointValues ) },
  110. { "ExperienceValue", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_experienceValues ) },
  111. { "ExperienceRequired", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_experienceRequired ) },
  112. { "IsTrainable", INI::parseBool, NULL, offsetof( ThingTemplate, m_isTrainable ) },
  113. { "EnterGuard", INI::parseBool, NULL, offsetof( ThingTemplate, m_enterGuard ) },
  114. { "HijackGuard", INI::parseBool, NULL, offsetof( ThingTemplate, m_hijackGuard ) },
  115. { "Side", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_defaultOwningSide ) },
  116. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  117. { "Prerequisites", ThingTemplate::parsePrerequisites, 0, 0 },
  118. { "Buildable", INI::parseByteSizedIndexList, BuildableStatusNames, offsetof( ThingTemplate, m_buildable) },
  119. { "BuildCost", INI::parseUnsignedShort, NULL, offsetof( ThingTemplate, m_buildCost ) },
  120. { "BuildTime", INI::parseReal, NULL, offsetof( ThingTemplate, m_buildTime ) },
  121. { "RefundValue", INI::parseUnsignedShort, NULL, offsetof( ThingTemplate, m_refundValue ) },
  122. { "BuildCompletion", INI::parseByteSizedIndexList, BuildCompletionNames, offsetof( ThingTemplate, m_buildCompletion ) },
  123. { "EnergyProduction", INI::parseInt, NULL, offsetof( ThingTemplate, m_energyProduction ) },
  124. { "EnergyBonus", INI::parseInt, NULL, offsetof( ThingTemplate, m_energyBonus ) },
  125. { "IsForbidden", INI::parseBool, NULL, offsetof( ThingTemplate, m_isForbidden ) },
  126. { "IsPrerequisite", INI::parseBool, NULL, offsetof( ThingTemplate, m_isPrerequisite ) },
  127. { "DisplayColor", INI::parseColorInt, NULL, offsetof( ThingTemplate, m_displayColor ) },
  128. { "EditorSorting", INI::parseByteSizedIndexList, EditorSortingNames, offsetof( ThingTemplate, m_editorSorting ) },
  129. { "KindOf", KindOfMaskType::parseFromINI, NULL, offsetof( ThingTemplate, m_kindof ) },
  130. { "CommandSet", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_commandSetString ) },
  131. { "BuildVariations", INI::parseAsciiStringVector, NULL, offsetof( ThingTemplate, m_buildVariations ) },
  132. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  133. { "Behavior", ThingTemplate::parseModuleName, (const void*)MODULETYPE_BEHAVIOR, offsetof(ThingTemplate, m_behaviorModuleInfo) },
  134. { "Body", ThingTemplate::parseModuleName, (const void*)999, offsetof(ThingTemplate, m_behaviorModuleInfo) },
  135. { "Draw", ThingTemplate::parseModuleName, (const void*)MODULETYPE_DRAW, offsetof(ThingTemplate, m_drawModuleInfo) },
  136. { "ClientUpdate", ThingTemplate::parseModuleName, (const void*)MODULETYPE_CLIENT_UPDATE, offsetof(ThingTemplate, m_clientUpdateModuleInfo) },
  137. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  138. { "SelectPortrait", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_selectedPortraitImageName ) },
  139. { "ButtonImage", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_buttonImageName ) },
  140. //Code renderer handles these states now.
  141. //{ "InventoryImageEnabled", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_ENABLED ] ) },
  142. //{ "InventoryImageDisabled", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_DISABLED ] ) },
  143. //{ "InventoryImageHilite", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_HILITE ] ) },
  144. //{ "InventoryImagePushed", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_PUSHED ] ) },
  145. { "UpgradeCameo1", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 0 ] ) },
  146. { "UpgradeCameo2", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 1 ] ) },
  147. { "UpgradeCameo3", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 2 ] ) },
  148. { "UpgradeCameo4", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 3 ] ) },
  149. { "UpgradeCameo5", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 4 ] ) },
  150. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  151. { "VoiceSelect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSelect]) },
  152. { "VoiceGroupSelect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGroupSelect]) },
  153. { "VoiceMove", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceMove]) },
  154. { "VoiceAttack", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttack]) },
  155. { "VoiceEnter", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceEnter ]) },
  156. { "VoiceFear", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceFear ]) },
  157. { "VoiceSelectElite", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSelectElite ]) },
  158. { "VoiceCreated", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceCreated]) },
  159. { "VoiceTaskUnable", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceTaskUnable ]) },
  160. { "VoiceTaskComplete", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceTaskComplete ]) },
  161. { "VoiceMeetEnemy", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceMeetEnemy]) },
  162. { "VoiceGarrison", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGarrison]) },
  163. #ifdef ALLOW_SURRENDER
  164. { "VoiceSurrender", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSurrender]) },
  165. #endif
  166. { "VoiceDefect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceDefect]) },
  167. { "VoiceAttackSpecial", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttackSpecial ]) },
  168. { "VoiceAttackAir", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttackAir ]) },
  169. { "VoiceGuard", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGuard ]) },
  170. { "SoundMoveStart", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveStart]) },
  171. { "SoundMoveStartDamaged",INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveStartDamaged]) },
  172. { "SoundMoveLoop", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveLoop]) },
  173. { "SoundMoveLoopDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveLoopDamaged]) },
  174. { "SoundAmbient", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbient ]) },
  175. { "SoundAmbientDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientDamaged ]) },
  176. { "SoundAmbientReallyDamaged",INI::parseDynamicAudioEventRTS, NULL,offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientReallyDamaged ]) },
  177. { "SoundAmbientRubble", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientRubble]) },
  178. { "SoundStealthOn", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundStealthOn ]) },
  179. { "SoundStealthOff", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundStealthOff ]) },
  180. { "SoundCreated", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundCreated ]) },
  181. { "SoundOnDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundOnDamaged ]) },
  182. { "SoundOnReallyDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundOnReallyDamaged ]) },
  183. { "SoundEnter", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundEnter ]) },
  184. { "SoundExit", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundExit ]) },
  185. { "SoundPromotedVeteran", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedVeteran ]) },
  186. { "SoundPromotedElite", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedElite ]) },
  187. { "SoundPromotedHero", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedHero ]) },
  188. { "SoundFallingFromPlane",INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundFalling ]) },
  189. { "UnitSpecificSounds", ThingTemplate::parsePerUnitSounds, NULL, offsetof(ThingTemplate, m_perUnitSounds) },
  190. { "UnitSpecificFX", ThingTemplate::parsePerUnitFX, NULL, offsetof(ThingTemplate, m_perUnitFX) },
  191. { "Scale", INI::parseReal, NULL, offsetof( ThingTemplate, m_assetScale ) },
  192. { "Geometry", GeometryInfo::parseGeometryType, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  193. { "GeometryMajorRadius", GeometryInfo::parseGeometryMajorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  194. { "GeometryMinorRadius", GeometryInfo::parseGeometryMinorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  195. { "GeometryHeight", GeometryInfo::parseGeometryHeight, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  196. { "GeometryIsSmall", GeometryInfo::parseGeometryIsSmall, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  197. { "Shadow", INI::parseBitString8, TheShadowNames, offsetof( ThingTemplate, m_shadowType ) },
  198. { "ShadowSizeX", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowSizeX ) },
  199. { "ShadowSizeY", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowSizeY ) },
  200. { "ShadowOffsetX", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowOffsetX ) },
  201. { "ShadowOffsetY", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowOffsetY ) },
  202. { "ShadowTexture", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_shadowTextureName ) },
  203. { "OcclusionDelay", INI::parseDurationUnsignedInt, NULL, offsetof( ThingTemplate, m_occlusionDelay ) },
  204. { "AddModule", ThingTemplate::parseAddModule, NULL, 0 },
  205. { "RemoveModule", ThingTemplate::parseRemoveModule, NULL, 0 },
  206. { "ReplaceModule", ThingTemplate::parseReplaceModule, NULL, 0 },
  207. { "InheritableModule", ThingTemplate::parseInheritableModule, NULL, 0 },
  208. { "OverrideableByLikeKind", ThingTemplate::OverrideableByLikeKind, NULL, 0 },
  209. { "Locomotor", AIUpdateModuleData::parseLocomotorSet, NULL, 0 },
  210. { "InstanceScaleFuzziness", INI::parseReal, NULL, offsetof(ThingTemplate, m_instanceScaleFuzziness ) },
  211. { "StructureRubbleHeight", INI::parseUnsignedByte, NULL, offsetof(ThingTemplate, m_structureRubbleHeight ) },
  212. { "ThreatValue", INI::parseUnsignedShort, NULL, offsetof(ThingTemplate, m_threatValue ) },
  213. { "MaxSimultaneousOfType", ThingTemplate::parseMaxSimultaneous, NULL, offsetof(ThingTemplate, m_maxSimultaneousOfType ) },
  214. { "MaxSimultaneousLinkKey", NameKeyGenerator::parseStringAsNameKeyType, NULL, offsetof(ThingTemplate, m_maxSimultaneousLinkKey ) },
  215. { "CrusherLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crusherLevel ) },
  216. { "CrushableLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crushableLevel ) },
  217. { 0, 0, 0, 0 } // keep this last
  218. };
  219. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  220. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  221. const FieldParse ThingTemplate::s_objectReskinFieldParseTable[] =
  222. {
  223. { "Draw", ThingTemplate::parseModuleName, (const void*)MODULETYPE_DRAW, offsetof(ThingTemplate, m_drawModuleInfo) },
  224. { "Geometry", GeometryInfo::parseGeometryType, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  225. { "GeometryMajorRadius", GeometryInfo::parseGeometryMajorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  226. { "GeometryMinorRadius", GeometryInfo::parseGeometryMinorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  227. { "GeometryHeight", GeometryInfo::parseGeometryHeight, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  228. { "GeometryIsSmall", GeometryInfo::parseGeometryIsSmall, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
  229. { "FenceWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceWidth ) },
  230. { "FenceXOffset", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceXOffset ) },
  231. // Needed to avoid some cheats with the scud storm rebuild hole
  232. { "MaxSimultaneousOfType", ThingTemplate::parseMaxSimultaneous, NULL, offsetof(ThingTemplate, m_maxSimultaneousOfType ) },
  233. { "MaxSimultaneousLinkKey", NameKeyGenerator::parseStringAsNameKeyType, NULL, offsetof(ThingTemplate, m_maxSimultaneousLinkKey ) },
  234. { 0, 0, 0, 0 } // keep this last
  235. };
  236. // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
  237. // ------------------------------------------------------------------------------------------------
  238. /** See if the tag string is present in any of the module info entries here */
  239. // ------------------------------------------------------------------------------------------------
  240. const ModuleInfo::Nugget *ModuleInfo::getNuggetWithTag( const AsciiString& tag ) const
  241. {
  242. std::vector< Nugget >::const_iterator it;
  243. for( it = m_info.begin(); it != m_info.end(); ++it )
  244. if( (*it).m_moduleTag == tag )
  245. return &(*it);
  246. // no match
  247. return NULL;
  248. } // end isTagPresent
  249. // ------------------------------------------------------------------------------------------------
  250. /** Add this module info to the thing template */
  251. // ------------------------------------------------------------------------------------------------
  252. void ModuleInfo::addModuleInfo(ThingTemplate *thingTemplate,
  253. const AsciiString& name,
  254. const AsciiString& moduleTag,
  255. const ModuleData* data,
  256. Int interfaceMask,
  257. Bool inheritable,
  258. Bool overrideableByLikeKind)
  259. {
  260. //
  261. // there must be a module tag present, and it must be unique across all module infos
  262. // for this thing template
  263. //
  264. #if defined(_DEBUG) || defined(_INTERNAL)
  265. // get module info
  266. const Nugget *nugget;
  267. nugget = thingTemplate->getBehaviorModuleInfo().getNuggetWithTag( moduleTag );
  268. if( nugget != NULL )
  269. {
  270. // compare this nugget tag against the tag for the new data we're going to submit
  271. DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
  272. ("addModuleInfo - ERROR defining module '%s' on thing template '%s'. The module '%s' has the tag '%s' which must be unique among all modules for this object, but the tag '%s' is also already on module '%s' within this object.\n\nPlease make unique tag names within an object definition\n",
  273. name.str(),
  274. thingTemplate->getName().str(),
  275. name.str(),
  276. moduleTag.str(),
  277. moduleTag.str(),
  278. nugget->first.str()) );
  279. // srj sez: prevent people from ignoring this.
  280. throw INI_INVALID_DATA;
  281. } // end if
  282. nugget = thingTemplate->getDrawModuleInfo().getNuggetWithTag( moduleTag );
  283. if( nugget != NULL )
  284. {
  285. // compare this nugget tag against the tag for the new data we're going to submit
  286. DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
  287. ("addModuleInfo - ERROR defining module '%s' on thing template '%s'. The module '%s' has the tag '%s' which must be unique among all modules for this object, but the tag '%s' is also already on module '%s' within this object.\n\nPlease make unique tag names within an object definition\n",
  288. name.str(),
  289. thingTemplate->getName().str(),
  290. name.str(),
  291. moduleTag.str(),
  292. moduleTag.str(),
  293. nugget->first.str()) );
  294. // srj sez: prevent people from ignoring this.
  295. throw INI_INVALID_DATA;
  296. } // end if
  297. nugget = thingTemplate->getClientUpdateModuleInfo().getNuggetWithTag( moduleTag );
  298. if( nugget != NULL )
  299. {
  300. // compare this nugget tag against the tag for the new data we're going to submit
  301. DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
  302. ("addModuleInfo - ERROR defining module '%s' on thing template '%s'. The module '%s' has the tag '%s' which must be unique among all modules for this object, but the tag '%s' is also already on module '%s' within this object.\n\nPlease make unique tag names within an object definition\n",
  303. name.str(),
  304. thingTemplate->getName().str(),
  305. name.str(),
  306. moduleTag.str(),
  307. moduleTag.str(),
  308. nugget->first.str()) );
  309. // srj sez: prevent people from ignoring this.
  310. throw INI_INVALID_DATA;
  311. } // end if
  312. #endif
  313. m_info.push_back(Nugget(name, moduleTag, data, interfaceMask, inheritable, overrideableByLikeKind));
  314. }
  315. //-------------------------------------------------------------------------------------------------
  316. Bool ModuleInfo::clearModuleDataWithTag(const AsciiString& tagToClear, AsciiString& clearedModuleNameOut)
  317. {
  318. Bool cleared = false;
  319. // do NOT clear... we only want to modify this if we return true.
  320. // if we return false, we should leave this unmodified.
  321. //clearedModuleNameOut.clear();
  322. for (std::vector<Nugget>::iterator it = m_info.begin(); it != m_info.end(); /* empty */ )
  323. {
  324. if (it->m_moduleTag == tagToClear)
  325. {
  326. DEBUG_ASSERTCRASH(!cleared, ("Hmm, multiple clears in ModuleInfo::clearModuleDataWithTag, should this be possible?"));
  327. clearedModuleNameOut = it->first;
  328. it = m_info.erase(it);
  329. cleared = true;
  330. }
  331. else
  332. {
  333. ++it;
  334. }
  335. }
  336. return cleared;
  337. }
  338. //-------------------------------------------------------------------------------------------------
  339. Bool ModuleInfo::clearCopiedFromDefaultEntries(Int interfaceMask, const AsciiString &newName, const ThingTemplate *fullTemplate )
  340. {
  341. static KindOfMaskType ImmuneToGPSScramblerMask;
  342. KindOfMaskType &m = ImmuneToGPSScramblerMask;
  343. m.set(KINDOF_AIRCRAFT);// NO PLANES or helicopters
  344. m.set(KINDOF_SHRUBBERY);// NO trees or bushes
  345. m.set(KINDOF_OPTIMIZED_TREE);
  346. m.set(KINDOF_STRUCTURE);// NO buildings
  347. m.set(KINDOF_DRAWABLE_ONLY);
  348. m.set(KINDOF_MOB_NEXUS);
  349. m.set(KINDOF_IGNORED_IN_GUI);
  350. m.set(KINDOF_CLEARED_BY_BUILD);
  351. m.set(KINDOF_DEFENSIVE_WALL);
  352. m.set(KINDOF_BALLISTIC_MISSILE);
  353. m.set(KINDOF_SUPPLY_SOURCE);
  354. m.set(KINDOF_BOAT);
  355. m.set(KINDOF_INERT);
  356. m.set(KINDOF_BRIDGE);
  357. m.set(KINDOF_LANDMARK_BRIDGE);
  358. m.set(KINDOF_BRIDGE_TOWER);
  359. Bool disallowed = fullTemplate->isAnyKindOf( ImmuneToGPSScramblerMask );
  360. static KindOfMaskType CandidateForGPSScramblerMask;
  361. CandidateForGPSScramblerMask.set(KINDOF_SCORE);
  362. CandidateForGPSScramblerMask.set(KINDOF_VEHICLE);
  363. CandidateForGPSScramblerMask.set(KINDOF_INFANTRY);
  364. CandidateForGPSScramblerMask.set(KINDOF_PORTABLE_STRUCTURE);
  365. Bool candidate = fullTemplate->isAnyKindOf( CandidateForGPSScramblerMask );
  366. Bool ret = false;
  367. std::vector<Nugget>::iterator it = m_info.begin();
  368. while( it != m_info.end() )
  369. {
  370. if( (it->interfaceMask & interfaceMask) != 0 && it->copiedFromDefault )
  371. {
  372. if ( it->inheritable )
  373. {
  374. if( it->m_moduleTag.compare("ModuleTag_DefaultAutoHealBehavior") == 0 && !fullTemplate->isTrainable() )
  375. {
  376. // Don't inherit this module if it is entirely useless to us.
  377. it = m_info.erase( it );
  378. ret = true;
  379. }
  380. else
  381. {
  382. ++it;//skip to the next nugget, 'cause we inherit this one
  383. }
  384. }
  385. else if ( it->overrideableByLikeKind)
  386. {
  387. AsciiString oldName = it->first;
  388. if ( oldName == newName //we will dump this instance, since the INI author requested a specific one of the same class
  389. || disallowed // or, we just do not Add these special overrideables to these kinds of templates, so just dump it
  390. || candidate == FALSE )
  391. {
  392. it = m_info.erase( it );
  393. ret = true;
  394. }
  395. else
  396. ++it;//no match, preserve the default instnace of this Module for now
  397. }
  398. else // just dump this instance of this Module, since one of the same interface mask has been added by caller
  399. {
  400. it = m_info.erase( it );
  401. ret = true;
  402. }
  403. }
  404. else
  405. ++it;
  406. }
  407. return ret;
  408. }
  409. //-------------------------------------------------------------------------------------------------
  410. Bool ModuleInfo::clearAiModuleInfo()
  411. {
  412. Bool ret = false;
  413. std::vector<Nugget>::iterator it = m_info.begin();
  414. while( it != m_info.end() )
  415. {
  416. if (it->second->isAiModuleData() )
  417. {
  418. it = m_info.erase( it );
  419. ret = true;
  420. }
  421. else
  422. {
  423. ++it;
  424. }
  425. }
  426. return ret;
  427. }
  428. //-------------------------------------------------------------------------------------------------
  429. void ThingTemplate::parseModuleName(INI* ini, void *instance, void* store, const void* userData)
  430. {
  431. ThingTemplate* self = (ThingTemplate*)instance;
  432. ModuleInfo* mi = (ModuleInfo*)store;
  433. ModuleType type = (ModuleType)(UnsignedInt)userData;
  434. const char* token = ini->getNextToken();
  435. AsciiString tokenStr = token;
  436. // get the tag string (it is now required)
  437. AsciiString moduleTagStr;
  438. try
  439. {
  440. moduleTagStr = ini->getNextToken();
  441. }
  442. catch( ... )
  443. {
  444. DEBUG_CRASH(( "[LINE: %d - FILE: '%s'] Module tag not found for module '%s' on thing template '%s'. Module tags are required and must be unique for all modules within an object definition\n",
  445. ini->getLineNum(), ini->getFilename().str(),
  446. tokenStr.str(), self->getName().str() ));
  447. throw;
  448. }
  449. Int interfaceMask;
  450. // ugh -- special case for "Body".
  451. if (type == 999)
  452. {
  453. type = MODULETYPE_BEHAVIOR;
  454. // what interface(s) does this module support?
  455. interfaceMask = TheModuleFactory->findModuleInterfaceMask(tokenStr, type);
  456. if ((interfaceMask & (MODULEINTERFACE_BODY)) == 0)
  457. {
  458. DEBUG_CRASH(("Only Body allowed here"));
  459. throw INI_INVALID_DATA;
  460. }
  461. }
  462. else
  463. {
  464. interfaceMask = TheModuleFactory->findModuleInterfaceMask(tokenStr, type);
  465. if ((interfaceMask & (MODULEINTERFACE_BODY)) != 0)
  466. {
  467. DEBUG_CRASH(("No Body allowed here"));
  468. throw INI_INVALID_DATA;
  469. }
  470. }
  471. // if we're overriding, we can totally skip over this block
  472. if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
  473. {
  474. if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE)
  475. {
  476. // do nothing, just fall thru
  477. }
  478. else
  479. {
  480. DEBUG_CRASH(("[LINE: %d - FILE: '%s'] You must use AddModule to add modules in override INI files.\n",
  481. ini->getLineNum(), ini->getFilename().str(), self->getName().str()));
  482. throw INI_INVALID_DATA;
  483. }
  484. }
  485. else
  486. {
  487. // if (self->getName().compare("GLAVehicleQuadCannon"))
  488. // DEBUG_ASSERTCRASH( FALSE, ("WE ARE CLEARING DEFAULT MODULES FROM A QUAD CANNON.") );
  489. self->m_behaviorModuleInfo.clearCopiedFromDefaultEntries(interfaceMask, tokenStr, self );
  490. self->m_drawModuleInfo.clearCopiedFromDefaultEntries(interfaceMask, tokenStr, self );
  491. self->m_clientUpdateModuleInfo.clearCopiedFromDefaultEntries(interfaceMask, tokenStr, self );
  492. }
  493. if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE
  494. && self->m_moduleBeingReplacedName.isNotEmpty()
  495. && self->m_moduleBeingReplacedName != tokenStr)
  496. {
  497. DEBUG_CRASH(("[LINE: %d - FILE: '%s'] ReplaceModule must replace modules with another module of the same type, but you are attempting to replace a %s with a %s for Object %s.\n",
  498. ini->getLineNum(), ini->getFilename().str(), self->m_moduleBeingReplacedName.str(), tokenStr.str(), self->getName().str()));
  499. throw INI_INVALID_DATA;
  500. }
  501. if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE
  502. && self->m_moduleBeingReplacedTag.isNotEmpty()
  503. && self->m_moduleBeingReplacedTag == moduleTagStr)
  504. {
  505. DEBUG_CRASH(("[LINE: %d - FILE: '%s'] ReplaceModule must specify a new, unique tag for the replaced module, but you are not doing so for %s (%s) for Object %s.\n",
  506. ini->getLineNum(), ini->getFilename().str(), moduleTagStr.str(), self->m_moduleBeingReplacedName.str(), self->getName().str()));
  507. throw INI_INVALID_DATA;
  508. }
  509. ModuleData* data = TheModuleFactory->newModuleDataFromINI(ini, tokenStr, type, moduleTagStr);
  510. if (data->isAiModuleData())
  511. {
  512. Bool replaced = mi->clearAiModuleInfo();
  513. if (replaced)
  514. {
  515. //Kris: Commented this out for SPAM reasons. Do we really need this?
  516. //DEBUG_LOG(("replaced an AI for %s!\n",self->getName().str()));
  517. }
  518. }
  519. Bool inheritable = (self->m_moduleParsingMode == MODULEPARSE_INHERITABLE);
  520. Bool overrideableByLikeKind = (self->m_moduleParsingMode == MODULEPARSE_OVERRIDEABLE_BY_LIKE_KIND);
  521. mi->addModuleInfo(self, tokenStr, moduleTagStr, data, interfaceMask, inheritable, overrideableByLikeKind);
  522. }
  523. //-------------------------------------------------------------------------------------------------
  524. void ThingTemplate::parseIntList(INI* ini, void *instance, void* store, const void* userData)
  525. {
  526. Int numberEntries = (Int)userData;
  527. Int *intList = (Int*)store;
  528. for( Int intIndex = 0; intIndex < numberEntries; intIndex ++ )
  529. {
  530. const char *token = ini->getNextToken();
  531. intList[intIndex] = ini->scanInt(token);
  532. }
  533. }
  534. //-------------------------------------------------------------------------------------------------
  535. static void parsePrerequisiteUnit( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  536. {
  537. std::vector<ProductionPrerequisite>* v = (std::vector<ProductionPrerequisite>*)instance;
  538. ProductionPrerequisite prereq;
  539. Bool orUnitWithPrevious = FALSE;
  540. for (const char *token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
  541. {
  542. prereq.addUnitPrereq( AsciiString( token ), orUnitWithPrevious );
  543. orUnitWithPrevious = TRUE;
  544. }
  545. v->push_back(prereq);
  546. }
  547. //-------------------------------------------------------------------------------------------------
  548. static void parsePrerequisiteScience( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  549. {
  550. std::vector<ProductionPrerequisite>* v = (std::vector<ProductionPrerequisite>*)instance;
  551. ProductionPrerequisite prereq;
  552. prereq.addSciencePrereq(INI::scanScience(ini->getNextToken()));
  553. v->push_back(prereq);
  554. }
  555. //-------------------------------------------------------------------------------------------------
  556. void ThingTemplate::parsePrerequisites( INI* ini, void *instance, void *store, const void* userData )
  557. {
  558. ThingTemplate* self = (ThingTemplate*)instance;
  559. static const FieldParse myFieldParse[] =
  560. {
  561. { "Object", parsePrerequisiteUnit, 0, 0 },
  562. { "Science", parsePrerequisiteScience, 0, 0 },
  563. { 0, 0, 0, 0 }
  564. };
  565. if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
  566. {
  567. self->m_prereqInfo.clear();
  568. }
  569. ini->initFromINI(&self->m_prereqInfo, myFieldParse);
  570. }
  571. //-------------------------------------------------------------------------------------------Static
  572. static void parseArbitraryFXIntoMap( INI* ini, void *instance, void* /* store */, const void* userData )
  573. {
  574. PerUnitFXMap* mapFX = (PerUnitFXMap*)instance;
  575. const char* name = (const char*)userData;
  576. const char* token = ini->getNextToken();
  577. const FXList* fxl = TheFXListStore->findFXList(token); // could be null!
  578. DEBUG_ASSERTCRASH(fxl != NULL || stricmp(token, "None") == 0, ("FXList %s not found!\n",token));
  579. mapFX->insert(std::make_pair(AsciiString(name), fxl));
  580. }
  581. //-------------------------------------------------------------------------------------------------
  582. //-------------------------------------------------------------------------------------------------
  583. void ThingTemplate::parsePerUnitFX( INI* ini, void *instance, void *store, const void *userData )
  584. {
  585. PerUnitFXMap* fxmap = (PerUnitFXMap*)store;
  586. fxmap->clear();
  587. static const FieldParse myFieldParse[] =
  588. {
  589. { 0, parseArbitraryFXIntoMap, NULL, 0 },
  590. { 0, 0, 0, 0 }
  591. };
  592. ini->initFromINI(fxmap, myFieldParse);
  593. }
  594. //-------------------------------------------------------------------------------------------Static
  595. static void parseArbitrarySoundsIntoMap( INI* ini, void *instance, void* /* store */, const void* userData )
  596. {
  597. PerUnitSoundMap *mapSounds = (PerUnitSoundMap*) instance;
  598. const char* name = (const char*)userData;
  599. const char* token = ini->getNextToken();
  600. AudioEventRTS a;
  601. if (token)
  602. a.setEventName(token);
  603. mapSounds->insert(std::make_pair(AsciiString(name), a));
  604. }
  605. //-------------------------------------------------------------------------------------------------
  606. /** Parse Additional per unit sounds such as TankTurretMove and TankTurretMoveLoop. */
  607. //-------------------------------------------------------------------------------------------------
  608. void ThingTemplate::parsePerUnitSounds( INI* ini, void *instance, void *store, const void *userData )
  609. {
  610. PerUnitSoundMap *mapSounds = (PerUnitSoundMap*)store;
  611. mapSounds->clear();
  612. static const FieldParse myFieldParse[] =
  613. {
  614. { 0, parseArbitrarySoundsIntoMap, NULL, 0 },
  615. { 0, 0, 0, 0 }
  616. };
  617. ini->initFromINI(mapSounds, myFieldParse);
  618. }
  619. //-------------------------------------------------------------------------------------------------
  620. /** Parse modules to add to the existing set of modules. */
  621. //-------------------------------------------------------------------------------------------------
  622. void ThingTemplate::parseAddModule(INI *ini, void *instance, void *store, const void *userData)
  623. {
  624. // don't care about the result.
  625. ThingTemplate* self = (ThingTemplate*)instance;
  626. ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
  627. if (oldMode != MODULEPARSE_NORMAL)
  628. throw INI_INVALID_DATA;
  629. self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
  630. ini->initFromINI(self, self->getFieldParse());
  631. self->m_moduleParsingMode = oldMode;
  632. }
  633. //-------------------------------------------------------------------------------------------------
  634. /** Parse modules to remove from the existing set of modules. */
  635. //-------------------------------------------------------------------------------------------------
  636. void ThingTemplate::parseRemoveModule(INI *ini, void *instance, void *store, const void *userData)
  637. {
  638. ThingTemplate* self = (ThingTemplate*)instance;
  639. ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
  640. if (oldMode != MODULEPARSE_NORMAL)
  641. throw INI_INVALID_DATA;
  642. self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
  643. const char *modToRemove = ini->getNextToken();
  644. AsciiString removedModuleName;
  645. Bool removed = self->removeModuleInfo(modToRemove, removedModuleName);
  646. if (!removed)
  647. {
  648. DEBUG_ASSERTCRASH(removed, ("RemoveModule %s was not found for %s. The game will crash now!\n",modToRemove, self->getName().str()));
  649. throw INI_INVALID_DATA;
  650. }
  651. self->m_moduleParsingMode = oldMode;
  652. }
  653. //-------------------------------------------------------------------------------------------------
  654. /** Replace the existing tagged modules with the new modules. */
  655. //-------------------------------------------------------------------------------------------------
  656. void ThingTemplate::parseReplaceModule(INI *ini, void *instance, void *store, const void *userData)
  657. {
  658. ThingTemplate* self = (ThingTemplate*)instance;
  659. ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
  660. if (oldMode != MODULEPARSE_NORMAL)
  661. throw INI_INVALID_DATA;
  662. self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
  663. const char *modToRemove = ini->getNextToken();
  664. AsciiString removedModuleName;
  665. Bool removed = self->removeModuleInfo(modToRemove, removedModuleName);
  666. if (!removed)
  667. {
  668. DEBUG_CRASH(("[LINE: %d - FILE: '%s'] ReplaceModule %s was not found for %s; cannot continue.\n",
  669. ini->getLineNum(), ini->getFilename().str(), modToRemove, self->getName().str()));
  670. throw INI_INVALID_DATA;
  671. }
  672. self->m_moduleBeingReplacedName = removedModuleName;
  673. self->m_moduleBeingReplacedTag = modToRemove;
  674. ini->initFromINI(self, self->getFieldParse());
  675. self->m_moduleBeingReplacedName.clear();
  676. self->m_moduleBeingReplacedTag.clear();
  677. self->m_moduleParsingMode = oldMode;
  678. }
  679. //-------------------------------------------------------------------------------------------------
  680. /** mark the module(s) as being "Inheritable". */
  681. //-------------------------------------------------------------------------------------------------
  682. void ThingTemplate::parseInheritableModule(INI *ini, void *instance, void *store, const void *userData)
  683. {
  684. ThingTemplate* self = (ThingTemplate*)instance;
  685. ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
  686. if (oldMode != MODULEPARSE_NORMAL)
  687. throw INI_INVALID_DATA;
  688. self->m_moduleParsingMode = MODULEPARSE_INHERITABLE;
  689. ini->initFromINI(self, self->getFieldParse());
  690. self->m_moduleParsingMode = oldMode;
  691. }
  692. //-------------------------------------------------------------------------------------------------
  693. /** mark the module(s) as being "VverrideableByLikeKind". default module will be replaced by any of the exact same class */
  694. //-------------------------------------------------------------------------------------------------
  695. void ThingTemplate::OverrideableByLikeKind(INI *ini, void *instance, void *store, const void *userData)
  696. {
  697. ThingTemplate* self = (ThingTemplate*)instance;
  698. ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
  699. if (oldMode != MODULEPARSE_NORMAL)
  700. throw INI_INVALID_DATA;
  701. self->m_moduleParsingMode = MODULEPARSE_OVERRIDEABLE_BY_LIKE_KIND;
  702. ini->initFromINI(self, self->getFieldParse());
  703. self->m_moduleParsingMode = oldMode;
  704. }
  705. //-------------------------------------------------------------------------------------------------
  706. /** Remove the module whose tag matches moduleToRemove. */
  707. //-------------------------------------------------------------------------------------------------
  708. Bool ThingTemplate::removeModuleInfo(const AsciiString& moduleToRemove, AsciiString& clearedModuleNameOut)
  709. {
  710. Bool removed = false;
  711. // do NOT clear... we only want to modify this if we return true.
  712. // if we return false, we should leave this unmodified.
  713. //clearedModuleNameOut.clear();
  714. if (m_behaviorModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
  715. {
  716. DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
  717. removed = true;
  718. }
  719. if (m_drawModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
  720. {
  721. DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
  722. removed = true;
  723. }
  724. if (m_clientUpdateModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
  725. {
  726. DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
  727. removed = true;
  728. }
  729. return removed;
  730. }
  731. //-------------------------------------------------------------------------------------------------
  732. /// @todo srj -- move this to another file
  733. void ArmorTemplateSet::parseArmorTemplateSet( INI* ini )
  734. {
  735. static const FieldParse myFieldParse[] =
  736. {
  737. { "Conditions", ArmorSetFlags::parseFromINI, NULL, offsetof( ArmorTemplateSet, m_types ) },
  738. { "Armor", INI::parseArmorTemplate, NULL, offsetof( ArmorTemplateSet, m_template ) },
  739. { "DamageFX", INI::parseDamageFX, NULL, offsetof( ArmorTemplateSet, m_fx ) },
  740. { 0, 0, 0, 0 }
  741. };
  742. ini->initFromINI(this, myFieldParse);
  743. }
  744. //-------------------------------------------------------------------------------------------------
  745. void ThingTemplate::parseArmorTemplateSet( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  746. {
  747. ThingTemplate* self = (ThingTemplate*)instance;
  748. if (self->m_armorCopiedFromDefault == TRUE)
  749. {
  750. self->m_armorCopiedFromDefault = FALSE;
  751. self->m_armorTemplateSets.clear();
  752. }
  753. ArmorTemplateSet ws;
  754. ws.parseArmorTemplateSet(ini);
  755. #if defined(_DEBUG) || defined(_INTERNAL)
  756. if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES)
  757. {
  758. for (ArmorTemplateSetVector::const_iterator it = self->m_armorTemplateSets.begin(); it != self->m_armorTemplateSets.end(); ++it)
  759. {
  760. if (it->getNthConditionsYes(0) == ws.getNthConditionsYes(0))
  761. {
  762. DEBUG_CRASH(("dup armorset condition in %s\n",self->getName().str()));
  763. }
  764. }
  765. }
  766. #endif
  767. self->m_armorTemplateSets.push_back(ws);
  768. self->m_armorTemplateSetFinder.clear();
  769. }
  770. //-------------------------------------------------------------------------------------------------
  771. void ThingTemplate::parseWeaponTemplateSet( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  772. {
  773. ThingTemplate* self = (ThingTemplate*)instance;
  774. if (self->m_weaponsCopiedFromDefault == TRUE)
  775. {
  776. self->m_weaponsCopiedFromDefault = FALSE;
  777. self->m_weaponTemplateSets.clear();
  778. }
  779. WeaponTemplateSet ws;
  780. ws.parseWeaponTemplateSet(ini, self);
  781. #if defined(_DEBUG) || defined(_INTERNAL)
  782. if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES)
  783. {
  784. for (WeaponTemplateSetVector::const_iterator it = self->m_weaponTemplateSets.begin(); it != self->m_weaponTemplateSets.end(); ++it)
  785. {
  786. if (it->getNthConditionsYes(0) == ws.getNthConditionsYes(0))
  787. {
  788. DEBUG_CRASH(("dup weaponset condition in %s\n",self->getName().str()));
  789. }
  790. }
  791. }
  792. #endif
  793. self->m_weaponTemplateSets.push_back(ws);
  794. self->m_weaponTemplateSetFinder.clear();
  795. }
  796. //-------------------------------------------------------------------------------------------------
  797. // Parse the "maxSimultaneousOfType" keyword
  798. void ThingTemplate::parseMaxSimultaneous(INI *ini, void *instance, void *store, const void *userData)
  799. {
  800. // Most of the time, this is an UnsignedShort, but sometimes this is the keyword
  801. // "DeterminedBySuperweaponRestriction"
  802. const char DETERMINED_BY_SUPERWEAPON_KEYWORD[] = "DeterminedBySuperweaponRestriction";
  803. ThingTemplate *myTemplate = (ThingTemplate *)instance;
  804. DEBUG_ASSERTCRASH ( &myTemplate->m_maxSimultaneousOfType == store, ("Bad store passed to parseMaxSimultaneous" ) );
  805. const char * token = ini->getNextToken();
  806. if ( stricmp( token, DETERMINED_BY_SUPERWEAPON_KEYWORD ) == 0 )
  807. {
  808. myTemplate->m_maxSimultaneousDeterminedBySuperweaponRestriction = true;
  809. *(UnsignedShort *)store = 0;
  810. }
  811. else
  812. {
  813. // Copied from parseUnsignedShort
  814. Int value = INI::scanInt(token);
  815. if (value < 0 || value > 65535)
  816. {
  817. DEBUG_CRASH(("Bad value parseMaxSimultaneous"));
  818. throw ERROR_BUG;
  819. }
  820. *(UnsignedShort *)store = (UnsignedShort)value;
  821. myTemplate->m_maxSimultaneousDeterminedBySuperweaponRestriction = false;
  822. }
  823. }
  824. //-------------------------------------------------------------------------------------------------
  825. //-------------------------------------------------------------------------------------------------
  826. ThingTemplate::ThingTemplate() :
  827. m_geometryInfo(GEOMETRY_SPHERE, FALSE, 1, 1, 1)
  828. {
  829. m_moduleParsingMode = MODULEPARSE_NORMAL;
  830. m_reskinnedFrom = NULL;
  831. m_radarPriority = RADAR_PRIORITY_INVALID;
  832. m_nextThingTemplate = NULL;
  833. m_transportSlotCount = 0;
  834. m_fenceWidth = 0;
  835. m_fenceXOffset = 0;
  836. m_visionRange = 0.0f;
  837. m_shroudClearingRange = -1.0f;
  838. m_shroudRevealToAllRange = -1.0f;
  839. m_buildCost = 0;
  840. m_buildTime = 1;
  841. m_refundValue = 0;
  842. m_energyProduction = 0;
  843. m_energyBonus = 0;
  844. m_buildCompletion = BC_APPEARS_AT_RALLY_POINT;
  845. for( Int levelIndex = 0; levelIndex < LEVEL_COUNT; levelIndex++ )
  846. {
  847. m_experienceValues[levelIndex] = 0;
  848. m_experienceRequired[levelIndex] = 0;
  849. // -1 means "same value as experienceValues for that level"
  850. m_skillPointValues[levelIndex] = USE_EXP_VALUE_FOR_SKILL_VALUE;
  851. }
  852. m_isTrainable = FALSE;
  853. m_enterGuard = FALSE;
  854. m_hijackGuard = FALSE;
  855. m_templateID = 0;
  856. m_kindof = KINDOFMASK_NONE;
  857. //m_defaultOwningSide = ""; // unnecessary
  858. m_isBuildFacility = FALSE;
  859. m_isPrerequisite = FALSE;
  860. m_placementViewAngle = 0.0f;
  861. m_factoryExitWidth = 0.0f;
  862. m_factoryExtraBibWidth = 0.0f;
  863. m_selectedPortraitImage = NULL;
  864. m_buttonImage = NULL;
  865. m_shadowType = SHADOW_NONE;
  866. m_shadowSizeX = 0.0f;
  867. m_shadowSizeY = 0.0f;
  868. m_shadowOffsetX = 0.0f;
  869. m_shadowOffsetY = 0.0f;
  870. m_occlusionDelay = TheGlobalData->m_defaultOcclusionDelay;
  871. m_structureRubbleHeight = 0;
  872. m_instanceScaleFuzziness = 0;
  873. m_threatValue = 0;
  874. m_maxSimultaneousOfType = 0; // unlimited
  875. m_maxSimultaneousLinkKey = NAMEKEY_INVALID; // Not linked
  876. m_maxSimultaneousDeterminedBySuperweaponRestriction = false;
  877. m_crusherLevel = 0; //Unspecified, this object is unable to crush anything!
  878. m_crushableLevel = 255; //Unspecified, this object is unable to be crushed by anything!
  879. }
  880. //-------------------------------------------------------------------------------------------------
  881. AIUpdateModuleData *ThingTemplate::friend_getAIModuleInfo(void)
  882. {
  883. Int numModInfos = m_behaviorModuleInfo.getCount();
  884. for (int j = 0; j < numModInfos; ++j)
  885. {
  886. if (m_behaviorModuleInfo.getNthData(j) && m_behaviorModuleInfo.getNthData(j)->isAiModuleData())
  887. {
  888. return (AIUpdateModuleData *)m_behaviorModuleInfo.friend_getNthData(j);
  889. }
  890. }
  891. return NULL;
  892. }
  893. //-------------------------------------------------------------------------------------------------
  894. //-------------------------------------------------------------------------------------------------
  895. void ThingTemplate::validateAudio()
  896. {
  897. #if defined(_DEBUG) || defined(_INTERNAL)
  898. #define AUDIO_TEST(y) \
  899. if (!get##y()->getEventName().isEmpty() && get##y()->getEventName().compareNoCase("NoSound") != 0) { \
  900. DEBUG_ASSERTLOG(TheAudio->isValidAudioEvent(get##y()), ("Invalid Sound '%s' in Object '%s'. (%s?)\n", #y, getName().str(), get##y()->getEventName().str())); \
  901. }
  902. AUDIO_TEST(VoiceSelect)
  903. AUDIO_TEST(VoiceGroupSelect)
  904. AUDIO_TEST(VoiceMove)
  905. AUDIO_TEST(VoiceAttack)
  906. AUDIO_TEST(VoiceEnter)
  907. AUDIO_TEST(VoiceFear)
  908. AUDIO_TEST(VoiceSelectElite)
  909. AUDIO_TEST(VoiceCreated)
  910. AUDIO_TEST(VoiceNearEnemy)
  911. AUDIO_TEST(VoiceTaskUnable)
  912. AUDIO_TEST(VoiceTaskComplete)
  913. AUDIO_TEST(VoiceMeetEnemy)
  914. AUDIO_TEST(VoiceGarrison)
  915. #ifdef ALLOW_SURRENDER
  916. AUDIO_TEST(VoiceSurrender)
  917. #endif
  918. AUDIO_TEST(VoiceDefect)
  919. AUDIO_TEST(VoiceAttackSpecial)
  920. AUDIO_TEST(VoiceAttackAir)
  921. AUDIO_TEST(VoiceGuard)
  922. AUDIO_TEST(SoundMoveStart)
  923. AUDIO_TEST(SoundMoveStartDamaged)
  924. AUDIO_TEST(SoundMoveLoop)
  925. AUDIO_TEST(SoundMoveLoopDamaged)
  926. AUDIO_TEST(SoundAmbient)
  927. AUDIO_TEST(SoundAmbientDamaged)
  928. AUDIO_TEST(SoundAmbientReallyDamaged)
  929. AUDIO_TEST(SoundAmbientRubble)
  930. AUDIO_TEST(SoundStealthOn)
  931. AUDIO_TEST(SoundStealthOff)
  932. AUDIO_TEST(SoundCreated)
  933. AUDIO_TEST(SoundOnDamaged)
  934. AUDIO_TEST(SoundOnReallyDamaged)
  935. AUDIO_TEST(SoundEnter)
  936. AUDIO_TEST(SoundExit)
  937. AUDIO_TEST(SoundPromotedVeteran)
  938. AUDIO_TEST(SoundPromotedElite)
  939. AUDIO_TEST(SoundPromotedHero)
  940. #undef AUDIO_TEST
  941. const PerUnitSoundMap *perUnitSounds = getAllPerUnitSounds();
  942. if (!perUnitSounds)
  943. {
  944. return;
  945. }
  946. for (PerUnitSoundMap::const_iterator it = perUnitSounds->begin(); it != perUnitSounds->end(); ++it)
  947. {
  948. if (!it->second.getEventName().isEmpty() && it->second.getEventName().compareNoCase("NoSound") != 0)
  949. {
  950. DEBUG_ASSERTCRASH(TheAudio->isValidAudioEvent(&it->second),
  951. ("Invalid UnitSpecificSound '%s' in Object '%s'. (%s?)",
  952. it->first.str(),
  953. getName().str(),
  954. it->second.getEventName().str()));
  955. }
  956. }
  957. #endif
  958. }
  959. //-------------------------------------------------------------------------------------------------
  960. void ThingTemplate::validate()
  961. {
  962. if (m_shadowTextureName.isEmpty())
  963. {
  964. // no texture given, pick a default
  965. switch (getTemplateGeometryInfo().getGeomType())
  966. {
  967. case GEOMETRY_SPHERE:
  968. case GEOMETRY_CYLINDER:
  969. m_shadowTextureName = "shadow";
  970. break;
  971. case GEOMETRY_BOX:
  972. m_shadowTextureName = "shadows";
  973. break;
  974. }
  975. }
  976. validateAudio();
  977. #if defined(_DEBUG) || defined(_INTERNAL)
  978. if (getName() == "DefaultThingTemplate")
  979. return;
  980. // neutron missile is an old special case....
  981. if (getName() == "NeutronMissile")
  982. return;
  983. // another cheesy special case....
  984. if (getName() == "FlamethrowerProjectileStream")
  985. return;
  986. // drawable-only templates are exempt from further checks.
  987. if (isKindOf(KINDOF_DRAWABLE_ONLY))
  988. return;
  989. // build-variation templates are exempt from further checks.
  990. if (!m_buildVariations.empty())
  991. return;
  992. Bool isImmobile = isKindOf(KINDOF_IMMOBILE);
  993. if (isKindOf(KINDOF_SHRUBBERY) && !isImmobile)
  994. {
  995. DEBUG_CRASH(("SHRUBBERY %s must be marked IMMOBILE!",getName().str()));
  996. }
  997. if (isKindOf(KINDOF_STRUCTURE) && !isImmobile)
  998. {
  999. DEBUG_CRASH(("Structure %s is not marked immobile, but probably should be -- please fix it. (If we ever add mobile structures, this debug sniffer will need to be revised.)\n",getName().str()));
  1000. }
  1001. if (isKindOf(KINDOF_STICK_TO_TERRAIN_SLOPE) && !isImmobile)
  1002. {
  1003. DEBUG_CRASH(("item %s is marked STICK_TO_TERRAIN_SLOPE but not IMMOBILE -- please fix it.\n",getName().str()));
  1004. }
  1005. if (isKindOf(KINDOF_STRUCTURE))
  1006. {
  1007. if (m_armorTemplateSets.empty() || (m_armorTemplateSets.size() == 1 && m_armorTemplateSets[0].getArmorTemplate() == NULL))
  1008. {
  1009. DEBUG_CRASH(("Structure %s has no armor, but probably should (StructureArmor) -- please fix it.)\n",getName().str()));
  1010. }
  1011. for (ArmorTemplateSetVector::const_iterator it = m_armorTemplateSets.begin(); it != m_armorTemplateSets.end(); ++it)
  1012. {
  1013. if (it->getDamageFX() == NULL)
  1014. {
  1015. DEBUG_CRASH(("Structure %s has no ArmorDamageFX, and really should.\n",getName().str()));
  1016. }
  1017. }
  1018. }
  1019. #endif
  1020. }
  1021. //-------------------------------------------------------------------------------------------------
  1022. // copy the guts of that into this, but preserve this' name, id, and list-links.
  1023. void ThingTemplate::copyFrom(const ThingTemplate* that)
  1024. {
  1025. if (!that)
  1026. return;
  1027. ThingTemplate* next = this->m_nextThingTemplate;
  1028. UnsignedShort id = this->m_templateID;
  1029. AsciiString name = this->m_nameString;
  1030. *this = *that;
  1031. this->m_nextThingTemplate = next;
  1032. this->m_templateID = id;
  1033. this->m_nameString = name;
  1034. }
  1035. //-------------------------------------------------------------------------------------------------
  1036. void ThingTemplate::setCopiedFromDefault()
  1037. {
  1038. m_armorCopiedFromDefault = true;
  1039. m_weaponsCopiedFromDefault = true;
  1040. m_behaviorModuleInfo.setCopiedFromDefault(true);
  1041. m_drawModuleInfo.setCopiedFromDefault(true);
  1042. m_clientUpdateModuleInfo.setCopiedFromDefault(true);
  1043. }
  1044. //-------------------------------------------------------------------------------------------------
  1045. ThingTemplate::~ThingTemplate()
  1046. {
  1047. // note, we don't need to take any special action for Armor/WeaponSets...
  1048. // though it is just a list of 'raw' pointers, we don't have ownership of 'em,
  1049. // and so we MUST NOT delete them
  1050. }
  1051. //=============================================================================
  1052. void ThingTemplate::resolveNames()
  1053. {
  1054. Int i, j;
  1055. //Kris: July 31, 2003
  1056. //NOTE: Make sure that all code in this function supports caching properly. For example,
  1057. // templates can be partially overridden by map.ini files. When this happens, strings
  1058. // that have been parsed are looked up, cached, then cleared. The problem is if a string
  1059. // gets cached, but not overridden, it will be clear the next time we call this function.
  1060. // so we will want to make sure we don't NULL out cached data if the string is empty. A
  1061. // concrete example is overriding an object with prerequisites. We just override the portrait.
  1062. // So the 1st time we call this function, we get the standard template data. During this first
  1063. // call, the strings are looked up, cached, and cleared. Then we override the portrait in the
  1064. // map.ini. The next time we call this function, we look up all the strings again. The prereq
  1065. // names didn't used to check for empty strings so they would NULL out all the previous prereqs
  1066. // the object had. So be sure to make sure all string lookups don't blindly lookup things -- check
  1067. // if the string isNotEmpty first!
  1068. for (i = 0; i < m_prereqInfo.size(); i++)
  1069. {
  1070. m_prereqInfo[i].resolveNames();
  1071. }
  1072. const Int MAX_BF = 32;
  1073. const ThingTemplate* tmpls[MAX_BF];
  1074. for (i = 0; i < m_prereqInfo.size(); i++)
  1075. {
  1076. Int count = m_prereqInfo[i].getAllPossibleBuildFacilityTemplates(tmpls, MAX_BF);
  1077. for (j = 0; j < count; j++)
  1078. {
  1079. // casting const away is a little evil, but justified in this case:
  1080. // PropductionPrerequisite should only be allowed 'const' access,
  1081. // but ThingTemplate can muck with stuff with gleeful abandon. (srj)
  1082. if( tmpls[ j ] )
  1083. const_cast<ThingTemplate*>(tmpls[j])->m_isBuildFacility = true;
  1084. // DEBUG_LOG(("BF: %s is a buildfacility for %s\n",tmpls[j]->m_nameString.str(),this->m_nameString.str()));
  1085. }
  1086. }
  1087. if (isKindOf(KINDOF_COMMANDCENTER)) {
  1088. // Command centers are considered factories. jba.
  1089. m_isBuildFacility = true;
  1090. }
  1091. // keep a pointer to portrait and button image if present for speed later
  1092. if( TheMappedImageCollection )
  1093. {
  1094. if( m_selectedPortraitImageName.isNotEmpty() )
  1095. {
  1096. m_selectedPortraitImage = TheMappedImageCollection->findImageByName( m_selectedPortraitImageName );
  1097. DEBUG_ASSERTCRASH( m_selectedPortraitImage, ("%s is looking for Portrait %s but can't find it. Skipping...", getName().str(), m_buttonImageName.str() ) );
  1098. m_selectedPortraitImageName.clear(); // we're done with this, so nuke it
  1099. }
  1100. if( m_buttonImageName.isNotEmpty() )
  1101. {
  1102. m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName );
  1103. DEBUG_ASSERTCRASH( m_buttonImage, ("%s is looking for ButtonImage %s but can't find it. Skipping...", getName().str(), m_buttonImageName.str() ) );
  1104. m_buttonImageName.clear(); // we're done with this, so nuke it
  1105. }
  1106. }
  1107. }
  1108. //=============================================================================
  1109. #ifdef LOAD_TEST_ASSETS
  1110. void ThingTemplate::initForLTA(const AsciiString& name)
  1111. {
  1112. m_nameString = name;
  1113. char buffer[1024];
  1114. strncpy(buffer, name.str(), sizeof(buffer));
  1115. for (int i=0; buffer[i]; i++) {
  1116. if (buffer[i] == '/') {
  1117. i++;
  1118. break;
  1119. }
  1120. }
  1121. m_LTAName = AsciiString(buffer+i);
  1122. m_behaviorModuleInfo.clear();
  1123. m_drawModuleInfo.clear();
  1124. m_clientUpdateModuleInfo.clear();
  1125. AsciiString moduleTag;
  1126. moduleTag.format( "LTA_%sDestroyDie", m_LTAName.str() );
  1127. m_behaviorModuleInfo.addModuleInfo(this, "DestroyDie", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "DestroyDie", MODULETYPE_BEHAVIOR, moduleTag), (MODULEINTERFACE_DIE), false);
  1128. moduleTag.format( "LTA_%sInactiveBody", m_LTAName.str() );
  1129. m_behaviorModuleInfo.addModuleInfo(this, "InactiveBody", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "InactiveBody", MODULETYPE_BEHAVIOR, moduleTag), (MODULEINTERFACE_BODY), false);
  1130. moduleTag.format( "LTA_%sW3DDefaultDraw", m_LTAName.str() );
  1131. m_drawModuleInfo.addModuleInfo(this, "W3DDefaultDraw", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "W3DDefaultDraw", MODULETYPE_DRAW, moduleTag), (MODULEINTERFACE_DRAW), false);
  1132. m_armorCopiedFromDefault = false;
  1133. m_weaponsCopiedFromDefault = false;
  1134. m_kindof = KINDOFMASK_NONE;
  1135. m_assetScale = 1.0f;
  1136. m_instanceScaleFuzziness = 0.0f; ///< tolerance to randomly vary scale per instance
  1137. m_structureRubbleHeight = 0.0f; // zero means "use global default"
  1138. m_displayName.translate( name );
  1139. m_shadowType = SHADOW_VOLUME;
  1140. m_geometryInfo.set(GEOMETRY_SPHERE, false, 10.0, 10.0, 10.0);
  1141. }
  1142. #endif
  1143. //=============================================================================
  1144. const ArmorTemplateSet* ThingTemplate::findArmorTemplateSet(const ArmorSetFlags& t) const
  1145. {
  1146. return m_armorTemplateSetFinder.findBestInfo(m_armorTemplateSets, t);
  1147. }
  1148. //=============================================================================
  1149. const WeaponTemplateSet* ThingTemplate::findWeaponTemplateSet(const WeaponSetFlags& t) const
  1150. {
  1151. return m_weaponTemplateSetFinder.findBestInfo(m_weaponTemplateSets, t);
  1152. }
  1153. //-----------------------------------------------------------------------------
  1154. // returns true iff we have at least one weaponset that contains a weapon.
  1155. // returns false if we have no weaponsets, or they are all empty.
  1156. Bool ThingTemplate::canPossiblyHaveAnyWeapon() const
  1157. {
  1158. for (WeaponTemplateSetVector::const_iterator it = m_weaponTemplateSets.begin();
  1159. it != m_weaponTemplateSets.end();
  1160. ++it)
  1161. {
  1162. if (it->hasAnyWeapons())
  1163. return true;
  1164. }
  1165. return false;
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. Int ThingTemplate::getSkillPointValue(Int level) const
  1169. {
  1170. Int value = m_skillPointValues[level];
  1171. if (value == USE_EXP_VALUE_FOR_SKILL_VALUE)
  1172. value = getExperienceValue(level);
  1173. return value;
  1174. }
  1175. //-----------------------------------------------------------------------------
  1176. const ThingTemplate *ThingTemplate::getBuildFacilityTemplate( const Player *player ) const
  1177. {
  1178. if (getPrereqCount() > 0)
  1179. {
  1180. return m_prereqInfo[0].getExistingBuildFacilityTemplate(player); // might return null
  1181. }
  1182. else
  1183. {
  1184. return NULL;
  1185. }
  1186. }
  1187. //-------------------------------------------------------------------------------------------------
  1188. BuildableStatus ThingTemplate::getBuildable() const
  1189. {
  1190. BuildableStatus bs;
  1191. if (TheGameLogic && TheGameLogic->findBuildableStatusOverride(this, bs))
  1192. return bs;
  1193. return (BuildableStatus)m_buildable;
  1194. }
  1195. //-------------------------------------------------------------------------------------------------
  1196. const FXList *ThingTemplate::getPerUnitFX(const AsciiString& fxName) const
  1197. {
  1198. if (fxName.isEmpty())
  1199. {
  1200. return NULL;
  1201. }
  1202. PerUnitFXMap::const_iterator it = m_perUnitFX.find(fxName);
  1203. if (it == m_perUnitFX.end())
  1204. {
  1205. DEBUG_CRASH(("Unknown FX name (%s) asked for in ThingTemplate (%s). ", fxName.str(), m_nameString.str()));
  1206. return NULL;
  1207. }
  1208. return (it->second);
  1209. }
  1210. //-------------------------------------------------------------------------------------------------
  1211. const AudioEventRTS *ThingTemplate::getPerUnitSound(const AsciiString& soundName) const
  1212. {
  1213. if (soundName.isEmpty())
  1214. {
  1215. return &s_audioEventNoSound;
  1216. }
  1217. PerUnitSoundMap::const_iterator it = m_perUnitSounds.find(soundName);
  1218. if (it == m_perUnitSounds.end())
  1219. {
  1220. #ifndef DO_UNIT_TIMINGS
  1221. DEBUG_LOG(("Unknown Audio name (%s) asked for in ThingTemplate (%s).\n", soundName.str(), m_nameString.str()));
  1222. #endif
  1223. return &s_audioEventNoSound;
  1224. }
  1225. return &(it->second);
  1226. }
  1227. //-------------------------------------------------------------------------------------------------
  1228. UnsignedInt ThingTemplate::getMaxSimultaneousOfType() const
  1229. {
  1230. if ( m_maxSimultaneousDeterminedBySuperweaponRestriction && TheGameLogic )
  1231. {
  1232. return TheGameLogic->getSuperweaponRestriction();
  1233. }
  1234. return m_maxSimultaneousOfType;
  1235. }
  1236. //-------------------------------------------------------------------------------------------------
  1237. Bool ThingTemplate::isEquivalentTo(const ThingTemplate* tt) const
  1238. {
  1239. // sanity
  1240. if (!(this && tt))
  1241. return false;
  1242. // sanity
  1243. if (this == tt)
  1244. return true;
  1245. if (this->getFinalOverride() == tt->getFinalOverride())
  1246. return true;
  1247. // This reskinned from that?
  1248. if (this->m_reskinnedFrom == tt)
  1249. return true;
  1250. // That reskinned from this?
  1251. if (this == tt->m_reskinnedFrom)
  1252. return true;
  1253. // This reskinned from that reskinned from?
  1254. // Kris: added case (chassis 2 compared to chassis 3 -- NULL possible if not reskinned)
  1255. if( this->m_reskinnedFrom && this->m_reskinnedFrom == tt->m_reskinnedFrom )
  1256. return true;
  1257. // Is this thing a build variation of that thing or vice versa
  1258. Int i;
  1259. Int numVariations = m_buildVariations.size();
  1260. for (i = 0; i < numVariations; ++i)
  1261. if (m_buildVariations[i].compareNoCase(tt->getName()) == 0)
  1262. return true;
  1263. numVariations = tt->m_buildVariations.size();
  1264. for (i = 0; i < numVariations; ++i)
  1265. if (tt->m_buildVariations[i].compareNoCase(getName()) == 0)
  1266. return true;
  1267. // Guess we're not equivalent.
  1268. return false;
  1269. }
  1270. //-------------------------------------------------------------------------------------------------
  1271. Bool ThingTemplate::isBuildableItem(void) const
  1272. {
  1273. return (getBuildCost() != 0);
  1274. }
  1275. //-------------------------------------------------------------------------------------------------
  1276. /** NOTE that we're not paying attention to m_override here, instead the portions
  1277. * that retrieve template data values use the get() wrappers, which *DO* pay
  1278. * attention to the override values */
  1279. //-------------------------------------------------------------------------------------------------
  1280. Int ThingTemplate::calcCostToBuild( const Player* player) const
  1281. {
  1282. if (!player)
  1283. return 0;
  1284. // changePercent format is "-.2 equals 20% cheaper"
  1285. Real factionModifier = 1 + player->getProductionCostChangePercent( getName() );
  1286. factionModifier *= player->getProductionCostChangeBasedOnKindOf( m_kindof );
  1287. return getBuildCost() * factionModifier * player->getHandicap()->getHandicap(Handicap::BUILDCOST, this);
  1288. }
  1289. //-------------------------------------------------------------------------------------------------
  1290. /** NOTE that we're not paying attention to m_override here, instead the portions
  1291. * that retrieve template data values use the get() wrappers, which *DO* pay
  1292. * attention to the override values */
  1293. //-------------------------------------------------------------------------------------------------
  1294. Int ThingTemplate::calcTimeToBuild( const Player* player) const
  1295. {
  1296. Int buildTime = getBuildTime() * LOGICFRAMES_PER_SECOND;
  1297. buildTime *= player->getHandicap()->getHandicap(Handicap::BUILDTIME, this);
  1298. Real factionModifier = 1 + player->getProductionTimeChangePercent( getName() );
  1299. buildTime *= factionModifier;
  1300. #if defined (_DEBUG) || defined (_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  1301. if( player->buildsInstantly() )
  1302. {
  1303. buildTime = 1;
  1304. }
  1305. #endif
  1306. // Adjust build time based on energy supply.
  1307. Real EnergyPercent = player->getEnergy()->getEnergySupplyRatio(); //I'm at 80% Energy
  1308. if (EnergyPercent > 1.0f)
  1309. EnergyPercent = 1.0f; // getEnergySupplyRatio() returns a true ratio, but we don't care about excess.
  1310. Real EnergyShort = 1.0f - EnergyPercent; //so I am 20% short
  1311. EnergyShort *= TheGlobalData->m_LowEnergyPenaltyModifier; //which is a 40% penalty, or a 10% penalty
  1312. Real penaltyRate = 1.0f - EnergyShort;
  1313. penaltyRate = max(penaltyRate, TheGlobalData->m_MinLowEnergyProductionSpeed); //bind so 0% does not dead stop you
  1314. if( EnergyPercent < 1.0f ) //and make 99% look like 80% (eg) since most of the time you are down only a little
  1315. penaltyRate = min(penaltyRate, TheGlobalData->m_MaxLowEnergyProductionSpeed);
  1316. if (penaltyRate <= 0.0f)
  1317. penaltyRate = 0.01f; // Design won't make the minimum 0, they promise
  1318. buildTime /= penaltyRate;//and voila. Makes sense, designers have control, all is happy.
  1319. // Multiple build facilities can have an added production bonus.
  1320. if (getBuildCompletion() == BC_APPEARS_AT_RALLY_POINT)
  1321. {
  1322. const ThingTemplate *tmpl = getBuildFacilityTemplate(player); // could be null if none exist
  1323. Int count = 0;
  1324. if (tmpl)
  1325. {
  1326. player->countObjectsByThingTemplate(1, &tmpl, false, &count);
  1327. Real factoryMult = TheGlobalData->m_MultipleFactory;
  1328. if (factoryMult > 0.0f)
  1329. {
  1330. for(int i=0; i < count - 1; i++)
  1331. buildTime *= factoryMult;
  1332. }
  1333. }
  1334. }
  1335. return(buildTime);
  1336. }
  1337. //---------------------------------------------------------------------------------------ModuleInfo
  1338. //-------------------------------------------------------------------------------------------------
  1339. ModuleData* ModuleInfo::friend_getNthData(Int i)
  1340. {
  1341. if (i >= 0 && i < m_info.size())
  1342. {
  1343. // This is kinda naughty, but its necessary.
  1344. return const_cast<ModuleData*>(m_info[i].second);
  1345. }
  1346. return NULL;
  1347. }