| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: ThingTemplate.cpp ////////////////////////////////////////////////////////////////////////
- // Created: Colin Day, April 2001
- // Desc: Thing templates
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_POWER_NAMES // for PowerNames[]
- #define DEFINE_SHADOW_NAMES // for TheShadowNames[]
- #define DEFINE_GEOMETRY_NAMES // for GeometryNames[]
- #define DEFINE_BUILD_COMPLETION_NAMES // for BuildCompletionNames[]
- #define DEFINE_EDITOR_SORTING_NAMES // for EditorSortingNames[]
- #define DEFINE_RADAR_PRIORITY_NAMES // for RadarPriorityNames[]
- #define DEFINE_BUILDABLE_STATUS_NAMES // for BuildableStatusNames[]
- #include "Common/DamageFX.h"
- #include "Common/GameAudio.h"
- #include "Common/GameCommon.h"
- #include "Common/GlobalData.h"
- #include "Common/INI.h"
- #include "Common/MessageStream.h"
- #include "Common/Module.h"
- #include "Common/ModuleFactory.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/ProductionPrerequisite.h"
- #include "Common/Radar.h"
- #include "Common/RandomValue.h"
- #include "Common/Science.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingSort.h"
- #include "Common/BitFlagsIO.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/FXList.h"
- #include "GameClient/GameText.h"
- #include "GameClient/Image.h"
- #include "GameClient/Shadow.h"
- #include "GameLogic/Armor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/SpecialPowerModule.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Powers.h"
- #include "GameLogic/Weapon.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- const Int USE_EXP_VALUE_FOR_SKILL_VALUE = -999;
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- AudioEventRTS ThingTemplate::s_audioEventNoSound;
- /*
- NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem!
- "s_objectReskinFieldParseTable" is intended to be used for parsing the "ObjectReskin" keyword, and
- should only allow you to specify things that affect the appearance of a template.
-
- The idea is that some units are functionally the same, but different visually; rather than replicate
- all the settings, you can specify it as "this one is like that one, but looks different".
- Thus, currently, the only things in the reskin table are:
-
- -- DrawModules
- -- DrawModuleData
- -- Geometry
-
- So: if you add/remove/modify any settings that deal with visual appearance, you *may* want to
- add 'em to the reskin table... but do so VERY CAUTIOUSLY and with careful deliberation (and
- after checking around for other opinions).
- */
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- const FieldParse ThingTemplate::s_objectFieldParseTable[] =
- {
- { "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ThingTemplate, m_displayName ) },
- { "RadarPriority", INI::parseByteSizedIndexList, RadarPriorityNames, offsetof( ThingTemplate, m_radarPriority ) },
- { "TransportSlotCount", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_transportSlotCount ) },
- { "FenceWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceWidth ) },
- { "FenceXOffset", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceXOffset ) },
- { "IsBridge", INI::parseBool, NULL, offsetof( ThingTemplate, m_isBridge ) },
- { "ArmorSet", ThingTemplate::parseArmorTemplateSet, NULL, 0},
- { "WeaponSet", ThingTemplate::parseWeaponTemplateSet,NULL, 0},
- { "VisionRange", INI::parseReal, NULL, offsetof( ThingTemplate, m_visionRange ) },
- { "ShroudClearingRange", INI::parseReal, NULL, offsetof( ThingTemplate, m_shroudClearingRange ) },
- { "PlacementViewAngle", INI::parseAngleReal, NULL, offsetof( ThingTemplate, m_placementViewAngle ) },
- { "FactoryExitWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_factoryExitWidth ) },
- { "FactoryExtraBibWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_factoryExtraBibWidth ) },
-
- { "SkillPointValue", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_skillPointValues ) },
- { "ExperienceValue", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_experienceValues ) },
- { "ExperienceRequired", ThingTemplate::parseIntList, (void*)LEVEL_COUNT, offsetof( ThingTemplate, m_experienceRequired ) },
- { "IsTrainable", INI::parseBool, NULL, offsetof( ThingTemplate, m_isTrainable ) },
- { "Side", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_defaultOwningSide ) },
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- { "Prerequisites", ThingTemplate::parsePrerequisites, 0, 0 },
- { "Buildable", INI::parseByteSizedIndexList, BuildableStatusNames, offsetof( ThingTemplate, m_buildable) },
- { "BuildCost", INI::parseUnsignedShort, NULL, offsetof( ThingTemplate, m_buildCost ) },
- { "BuildTime", INI::parseReal, NULL, offsetof( ThingTemplate, m_buildTime ) },
- { "RefundValue", INI::parseUnsignedShort, NULL, offsetof( ThingTemplate, m_refundValue ) },
- { "BuildCompletion", INI::parseByteSizedIndexList, BuildCompletionNames, offsetof( ThingTemplate, m_buildCompletion ) },
- { "EnergyProduction", INI::parseInt, NULL, offsetof( ThingTemplate, m_energyProduction ) },
- { "EnergyBonus", INI::parseInt, NULL, offsetof( ThingTemplate, m_energyBonus ) },
- { "IsForbidden", INI::parseBool, NULL, offsetof( ThingTemplate, m_isForbidden ) },
- { "IsPrerequisite", INI::parseBool, NULL, offsetof( ThingTemplate, m_isPrerequisite ) },
- { "DisplayColor", INI::parseColorInt, NULL, offsetof( ThingTemplate, m_displayColor ) },
- { "EditorSorting", INI::parseByteSizedIndexList, EditorSortingNames, offsetof( ThingTemplate, m_editorSorting ) },
- { "KindOf", KindOfMaskType::parseFromINI, NULL, offsetof( ThingTemplate, m_kindof ) },
- { "CommandSet", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_commandSetString ) },
- { "BuildVariations", INI::parseAsciiStringVector, NULL, offsetof( ThingTemplate, m_buildVariations ) },
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- { "Behavior", ThingTemplate::parseModuleName, (const void*)MODULETYPE_BEHAVIOR, offsetof(ThingTemplate, m_behaviorModuleInfo) },
- { "Body", ThingTemplate::parseModuleName, (const void*)999, offsetof(ThingTemplate, m_behaviorModuleInfo) },
- { "Draw", ThingTemplate::parseModuleName, (const void*)MODULETYPE_DRAW, offsetof(ThingTemplate, m_drawModuleInfo) },
- { "ClientUpdate", ThingTemplate::parseModuleName, (const void*)MODULETYPE_CLIENT_UPDATE, offsetof(ThingTemplate, m_clientUpdateModuleInfo) },
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- { "SelectPortrait", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_selectedPortraitImageName ) },
- { "ButtonImage", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_buttonImageName ) },
-
- //Code renderer handles these states now.
- //{ "InventoryImageEnabled", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_ENABLED ] ) },
- //{ "InventoryImageDisabled", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_DISABLED ] ) },
- //{ "InventoryImageHilite", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_HILITE ] ) },
- //{ "InventoryImagePushed", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_inventoryImage[ INV_IMAGE_PUSHED ] ) },
-
- { "UpgradeCameo1", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 0 ] ) },
- { "UpgradeCameo2", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 1 ] ) },
- { "UpgradeCameo3", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 2 ] ) },
- { "UpgradeCameo4", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 3 ] ) },
- { "UpgradeCameo5", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_upgradeCameoUpgradeNames[ 4 ] ) },
-
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- { "VoiceSelect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSelect]) },
- { "VoiceGroupSelect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGroupSelect]) },
- { "VoiceMove", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceMove]) },
- { "VoiceAttack", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttack]) },
- { "VoiceEnter", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceEnter ]) },
- { "VoiceFear", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceFear ]) },
- { "VoiceSelectElite", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSelectElite ]) },
- { "VoiceCreated", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceCreated]) },
- { "VoiceTaskUnable", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceTaskUnable ]) },
- { "VoiceTaskComplete", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceTaskComplete ]) },
- { "VoiceMeetEnemy", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceMeetEnemy]) },
- { "VoiceGarrison", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGarrison]) },
- #ifdef ALLOW_SURRENDER
- { "VoiceSurrender", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceSurrender]) },
- #endif
- { "VoiceDefect", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceDefect]) },
- { "VoiceAttackSpecial", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttackSpecial ]) },
- { "VoiceAttackAir", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceAttackAir ]) },
- { "VoiceGuard", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_voiceGuard ]) },
- { "SoundMoveStart", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveStart]) },
- { "SoundMoveStartDamaged",INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveStartDamaged]) },
- { "SoundMoveLoop", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveLoop]) },
- { "SoundMoveLoopDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundMoveLoopDamaged]) },
- { "SoundDie", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundDie]) },
- { "SoundCrush", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundCrush ]) },
- { "SoundAmbient", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbient ]) },
- { "SoundAmbientDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientDamaged ]) },
- { "SoundAmbientReallyDamaged",INI::parseDynamicAudioEventRTS, NULL,offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientReallyDamaged ]) },
- { "SoundAmbientRubble", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundAmbientRubble]) },
- { "SoundStealthOn", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundStealthOn ]) },
- { "SoundStealthOff", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundStealthOff ]) },
- { "SoundCreated", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundCreated ]) },
- { "SoundOnDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundOnDamaged ]) },
- { "SoundOnReallyDamaged", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundOnReallyDamaged ]) },
- { "SoundDieFire", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundDieFire ]) },
- { "SoundDieToxin", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundDieToxin ]) },
- { "SoundEnter", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundEnter ]) },
- { "SoundExit", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundExit ]) },
- { "SoundPromotedVeteran", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedVeteran ]) },
- { "SoundPromotedElite", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedElite ]) },
- { "SoundPromotedHero", INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundPromotedHero ]) },
- { "SoundFallingFromPlane",INI::parseDynamicAudioEventRTS, NULL, offsetof( ThingTemplate, m_audioarray.m_audio[TTAUDIO_soundFalling ]) },
- { "UnitSpecificSounds", ThingTemplate::parsePerUnitSounds, NULL, offsetof(ThingTemplate, m_perUnitSounds) },
- { "UnitSpecificFX", ThingTemplate::parsePerUnitFX, NULL, offsetof(ThingTemplate, m_perUnitFX) },
- { "Scale", INI::parseReal, NULL, offsetof( ThingTemplate, m_assetScale ) },
- { "Geometry", GeometryInfo::parseGeometryType, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryMajorRadius", GeometryInfo::parseGeometryMajorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryMinorRadius", GeometryInfo::parseGeometryMinorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryHeight", GeometryInfo::parseGeometryHeight, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryIsSmall", GeometryInfo::parseGeometryIsSmall, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "Shadow", INI::parseBitString8, TheShadowNames, offsetof( ThingTemplate, m_shadowType ) },
- { "ShadowSizeX", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowSizeX ) },
- { "ShadowSizeY", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowSizeY ) },
- { "ShadowOffsetX", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowOffsetX ) },
- { "ShadowOffsetY", INI::parseReal, NULL, offsetof( ThingTemplate, m_shadowOffsetY ) },
- { "ShadowTexture", INI::parseAsciiString, NULL, offsetof( ThingTemplate, m_shadowTextureName ) },
- { "OcclusionDelay", INI::parseDurationUnsignedInt, NULL, offsetof( ThingTemplate, m_occlusionDelay ) },
- { "AddModule", ThingTemplate::parseAddModule, NULL, 0 },
- { "RemoveModule", ThingTemplate::parseRemoveModule, NULL, 0 },
- { "ReplaceModule", ThingTemplate::parseReplaceModule, NULL, 0 },
- { "InheritableModule", ThingTemplate::parseInheritableModule, NULL, 0 },
- { "Locomotor", AIUpdateModuleData::parseLocomotorSet, NULL, 0 },
- { "InstanceScaleFuzziness", INI::parseReal, NULL, offsetof(ThingTemplate, m_instanceScaleFuzziness ) },
- { "StructureRubbleHeight", INI::parseUnsignedByte, NULL, offsetof(ThingTemplate, m_structureRubbleHeight ) },
- { "ThreatValue", INI::parseUnsignedShort, NULL, offsetof(ThingTemplate, m_threatValue ) },
- { "MaxSimultaneousOfType", INI::parseUnsignedShort, NULL, offsetof(ThingTemplate, m_maxSimultaneousOfType ) },
- { "CrusherLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crusherLevel ) },
- { "CrushableLevel", INI::parseUnsignedByte, NULL, offsetof( ThingTemplate, m_crushableLevel ) },
- { 0, 0, 0, 0 } // keep this last
- };
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- const FieldParse ThingTemplate::s_objectReskinFieldParseTable[] =
- {
- { "Draw", ThingTemplate::parseModuleName, (const void*)MODULETYPE_DRAW, offsetof(ThingTemplate, m_drawModuleInfo) },
- { "Geometry", GeometryInfo::parseGeometryType, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryMajorRadius", GeometryInfo::parseGeometryMajorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryMinorRadius", GeometryInfo::parseGeometryMinorRadius, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryHeight", GeometryInfo::parseGeometryHeight, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "GeometryIsSmall", GeometryInfo::parseGeometryIsSmall, NULL, offsetof( ThingTemplate, m_geometryInfo ) },
- { "FenceWidth", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceWidth ) },
- { "FenceXOffset", INI::parseReal, NULL, offsetof( ThingTemplate, m_fenceXOffset ) },
- { 0, 0, 0, 0 } // keep this last
- };
- // NOTE NOTE NOTE -- s_objectFieldParseTable and s_objectReskinFieldParseTable must be updated in tandem -- see comment above
- // ------------------------------------------------------------------------------------------------
- /** See if the tag string is present in any of the module info entries here */
- // ------------------------------------------------------------------------------------------------
- const ModuleInfo::Nugget *ModuleInfo::getNuggetWithTag( const AsciiString& tag ) const
- {
- std::vector< Nugget >::const_iterator it;
- for( it = m_info.begin(); it != m_info.end(); ++it )
- if( (*it).m_moduleTag == tag )
- return &(*it);
- // no match
- return NULL;
- } // end isTagPresent
- // ------------------------------------------------------------------------------------------------
- /** Add this module info to the thing template */
- // ------------------------------------------------------------------------------------------------
- void ModuleInfo::addModuleInfo(ThingTemplate *thingTemplate,
- const AsciiString& name,
- const AsciiString& moduleTag,
- const ModuleData* data,
- Int interfaceMask,
- Bool inheritable)
- {
- //
- // there must be a module tag present, and it must be unique across all module infos
- // for this thing template
- //
- #if defined(_DEBUG) || defined(_INTERNAL)
- // get module info
- const Nugget *nugget;
-
- nugget = thingTemplate->getBehaviorModuleInfo().getNuggetWithTag( moduleTag );
- if( nugget != NULL )
- {
- // compare this nugget tag against the tag for the new data we're going to submit
- DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
- ("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",
- name.str(),
- thingTemplate->getName().str(),
- name.str(),
- moduleTag.str(),
- moduleTag.str(),
- nugget->first.str()) );
- // srj sez: prevent people from ignoring this.
- throw INI_INVALID_DATA;
- } // end if
- nugget = thingTemplate->getDrawModuleInfo().getNuggetWithTag( moduleTag );
- if( nugget != NULL )
- {
- // compare this nugget tag against the tag for the new data we're going to submit
- DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
- ("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",
- name.str(),
- thingTemplate->getName().str(),
- name.str(),
- moduleTag.str(),
- moduleTag.str(),
- nugget->first.str()) );
- // srj sez: prevent people from ignoring this.
- throw INI_INVALID_DATA;
- } // end if
- nugget = thingTemplate->getClientUpdateModuleInfo().getNuggetWithTag( moduleTag );
- if( nugget != NULL )
- {
- // compare this nugget tag against the tag for the new data we're going to submit
- DEBUG_ASSERTCRASH( nugget->m_moduleTag != moduleTag,
- ("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",
- name.str(),
- thingTemplate->getName().str(),
- name.str(),
- moduleTag.str(),
- moduleTag.str(),
- nugget->first.str()) );
- // srj sez: prevent people from ignoring this.
- throw INI_INVALID_DATA;
- } // end if
- #endif
- m_info.push_back(Nugget(name, moduleTag, data, interfaceMask, inheritable));
- }
- //-------------------------------------------------------------------------------------------------
- Bool ModuleInfo::clearModuleDataWithTag(const AsciiString& tagToClear, AsciiString& clearedModuleNameOut)
- {
- Bool cleared = false;
- // do NOT clear... we only want to modify this if we return true.
- // if we return false, we should leave this unmodified.
- //clearedModuleNameOut.clear();
- for (std::vector<Nugget>::iterator it = m_info.begin(); it != m_info.end(); /* empty */ )
- {
- if (it->m_moduleTag == tagToClear)
- {
- DEBUG_ASSERTCRASH(!cleared, ("Hmm, multiple clears in ModuleInfo::clearModuleDataWithTag, should this be possible?"));
- clearedModuleNameOut = it->first;
- it = m_info.erase(it);
- cleared = true;
- }
- else
- {
- ++it;
- }
- }
- return cleared;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ModuleInfo::clearCopiedFromDefaultEntries(Int interfaceMask)
- {
- Bool ret = false;
- std::vector<Nugget>::iterator it = m_info.begin();
- while( it != m_info.end() )
- {
- if( (it->interfaceMask & interfaceMask) != 0 && it->copiedFromDefault && !it->inheritable )
- {
- it = m_info.erase( it );
- ret = true;
- }
- else
- {
- ++it;
- }
- }
- return ret;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ModuleInfo::clearAiModuleInfo()
- {
- Bool ret = false;
- std::vector<Nugget>::iterator it = m_info.begin();
- while( it != m_info.end() )
- {
- if (it->second->isAiModuleData() )
- {
- it = m_info.erase( it );
- ret = true;
- }
- else
- {
- ++it;
- }
- }
- return ret;
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseModuleName(INI* ini, void *instance, void* store, const void* userData)
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- ModuleInfo* mi = (ModuleInfo*)store;
- ModuleType type = (ModuleType)(UnsignedInt)userData;
- const char* token = ini->getNextToken();
- AsciiString tokenStr = token;
- // get the tag string (it is now required)
- AsciiString moduleTagStr;
- try
- {
- moduleTagStr = ini->getNextToken();
- }
- catch( ... )
- {
- 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",
- ini->getLineNum(), ini->getFilename().str(),
- tokenStr.str(), self->getName().str() ));
- throw;
-
- }
- Int interfaceMask;
- // ugh -- special case for "Body".
- if (type == 999)
- {
- type = MODULETYPE_BEHAVIOR;
- // what interface(s) does this module support?
- interfaceMask = TheModuleFactory->findModuleInterfaceMask(tokenStr, type);
- if ((interfaceMask & (MODULEINTERFACE_BODY)) == 0)
- {
- DEBUG_CRASH(("Only Body allowed here"));
- throw INI_INVALID_DATA;
- }
- }
- else
- {
- interfaceMask = TheModuleFactory->findModuleInterfaceMask(tokenStr, type);
- if ((interfaceMask & (MODULEINTERFACE_BODY)) != 0)
- {
- DEBUG_CRASH(("No Body allowed here"));
- throw INI_INVALID_DATA;
- }
- }
-
- // if we're overriding, we can totally skip over this block
- if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
- {
- if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE)
- {
- // do nothing, just fall thru
- }
- else
- {
- DEBUG_CRASH(("[LINE: %d - FILE: '%s'] You must use AddModule to add modules in override INI files.\n",
- ini->getLineNum(), ini->getFilename().str(), self->getName().str()));
- throw INI_INVALID_DATA;
- }
- }
- else
- {
- self->m_behaviorModuleInfo.clearCopiedFromDefaultEntries(interfaceMask);
- self->m_drawModuleInfo.clearCopiedFromDefaultEntries(interfaceMask);
- self->m_clientUpdateModuleInfo.clearCopiedFromDefaultEntries(interfaceMask);
- }
- if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE
- && self->m_moduleBeingReplacedName.isNotEmpty()
- && self->m_moduleBeingReplacedName != tokenStr)
- {
- 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",
- ini->getLineNum(), ini->getFilename().str(), self->m_moduleBeingReplacedName.str(), tokenStr.str(), self->getName().str()));
- throw INI_INVALID_DATA;
- }
- if (self->m_moduleParsingMode == MODULEPARSE_ADD_REMOVE_REPLACE
- && self->m_moduleBeingReplacedTag.isNotEmpty()
- && self->m_moduleBeingReplacedTag == moduleTagStr)
- {
- 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",
- ini->getLineNum(), ini->getFilename().str(), moduleTagStr.str(), self->m_moduleBeingReplacedName.str(), self->getName().str()));
- throw INI_INVALID_DATA;
- }
- ModuleData* data = TheModuleFactory->newModuleDataFromINI(ini, tokenStr, type, moduleTagStr);
- if (data->isAiModuleData())
- {
- Bool replaced = mi->clearAiModuleInfo();
- if (replaced)
- {
- DEBUG_LOG(("replaced an AI for %s!\n",self->getName().str()));
- }
- }
- Bool inheritable = (self->m_moduleParsingMode == MODULEPARSE_INHERITABLE);
- mi->addModuleInfo(self, tokenStr, moduleTagStr, data, interfaceMask, inheritable);
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseIntList(INI* ini, void *instance, void* store, const void* userData)
- {
- Int numberEntries = (Int)userData;
- Int *intList = (Int*)store;
- for( Int intIndex = 0; intIndex < numberEntries; intIndex ++ )
- {
- const char *token = ini->getNextToken();
- intList[intIndex] = ini->scanInt(token);
- }
- }
- //-------------------------------------------------------------------------------------------------
- static void parsePrerequisiteUnit( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- std::vector<ProductionPrerequisite>* v = (std::vector<ProductionPrerequisite>*)instance;
- ProductionPrerequisite prereq;
- Bool orUnitWithPrevious = FALSE;
- for (const char *token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
- {
- prereq.addUnitPrereq( AsciiString( token ), orUnitWithPrevious );
- orUnitWithPrevious = TRUE;
- }
- v->push_back(prereq);
- }
- //-------------------------------------------------------------------------------------------------
- static void parsePrerequisiteScience( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- std::vector<ProductionPrerequisite>* v = (std::vector<ProductionPrerequisite>*)instance;
- ProductionPrerequisite prereq;
- prereq.addSciencePrereq(INI::scanScience(ini->getNextToken()));
- v->push_back(prereq);
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parsePrerequisites( INI* ini, void *instance, void *store, const void* userData )
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- static const FieldParse myFieldParse[] =
- {
- { "Object", parsePrerequisiteUnit, 0, 0 },
- { "Science", parsePrerequisiteScience, 0, 0 },
- { 0, 0, 0, 0 }
- };
- if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
- {
- self->m_prereqInfo.clear();
- }
- ini->initFromINI(&self->m_prereqInfo, myFieldParse);
- }
- //-------------------------------------------------------------------------------------------Static
- static void parseArbitraryFXIntoMap( INI* ini, void *instance, void* /* store */, const void* userData )
- {
- PerUnitFXMap* mapFX = (PerUnitFXMap*)instance;
- const char* name = (const char*)userData;
- const char* token = ini->getNextToken();
- const FXList* fxl = TheFXListStore->findFXList(token); // could be null!
- DEBUG_ASSERTCRASH(fxl != NULL || stricmp(token, "None") == 0, ("FXList %s not found!\n",token));
- mapFX->insert(std::make_pair(AsciiString(name), fxl));
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parsePerUnitFX( INI* ini, void *instance, void *store, const void *userData )
- {
- PerUnitFXMap* fxmap = (PerUnitFXMap*)store;
- fxmap->clear();
- static const FieldParse myFieldParse[] =
- {
- { 0, parseArbitraryFXIntoMap, NULL, 0 },
- { 0, 0, 0, 0 }
- };
- ini->initFromINI(fxmap, myFieldParse);
- }
- //-------------------------------------------------------------------------------------------Static
- static void parseArbitrarySoundsIntoMap( INI* ini, void *instance, void* /* store */, const void* userData )
- {
- PerUnitSoundMap *mapSounds = (PerUnitSoundMap*) instance;
- const char* name = (const char*)userData;
- const char* token = ini->getNextToken();
-
- AudioEventRTS a;
- if (token)
- a.setEventName(token);
- mapSounds->insert(std::make_pair(AsciiString(name), a));
- }
- //-------------------------------------------------------------------------------------------------
- /** Parse Additional per unit sounds such as TankTurretMove and TankTurretMoveLoop. */
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parsePerUnitSounds( INI* ini, void *instance, void *store, const void *userData )
- {
- PerUnitSoundMap *mapSounds = (PerUnitSoundMap*)store;
- mapSounds->clear();
- static const FieldParse myFieldParse[] =
- {
- { 0, parseArbitrarySoundsIntoMap, NULL, 0 },
- { 0, 0, 0, 0 }
- };
- ini->initFromINI(mapSounds, myFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- /** Parse modules to add to the existing set of modules. */
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseAddModule(INI *ini, void *instance, void *store, const void *userData)
- {
- // don't care about the result.
- ThingTemplate* self = (ThingTemplate*)instance;
- ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
- if (oldMode != MODULEPARSE_NORMAL)
- throw INI_INVALID_DATA;
- self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
- ini->initFromINI(self, self->getFieldParse());
- self->m_moduleParsingMode = oldMode;
- }
- //-------------------------------------------------------------------------------------------------
- /** Parse modules to remove from the existing set of modules. */
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseRemoveModule(INI *ini, void *instance, void *store, const void *userData)
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
- if (oldMode != MODULEPARSE_NORMAL)
- throw INI_INVALID_DATA;
- self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
- const char *modToRemove = ini->getNextToken();
- AsciiString removedModuleName;
- Bool removed = self->removeModuleInfo(modToRemove, removedModuleName);
- if (!removed)
- {
- DEBUG_ASSERTCRASH(removed, ("RemoveModule %s was not found for %s.\n",modToRemove, self->getName().str()));
- throw INI_INVALID_DATA;
- }
- self->m_moduleParsingMode = oldMode;
- }
- //-------------------------------------------------------------------------------------------------
- /** Replace the existing tagged modules with the new modules. */
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseReplaceModule(INI *ini, void *instance, void *store, const void *userData)
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
- if (oldMode != MODULEPARSE_NORMAL)
- throw INI_INVALID_DATA;
- self->m_moduleParsingMode = MODULEPARSE_ADD_REMOVE_REPLACE;
- const char *modToRemove = ini->getNextToken();
- AsciiString removedModuleName;
- Bool removed = self->removeModuleInfo(modToRemove, removedModuleName);
- if (!removed)
- {
- DEBUG_CRASH(("[LINE: %d - FILE: '%s'] ReplaceModule %s was not found for %s; cannot continue.\n",
- ini->getLineNum(), ini->getFilename().str(), modToRemove, self->getName().str()));
- throw INI_INVALID_DATA;
- }
- self->m_moduleBeingReplacedName = removedModuleName;
- self->m_moduleBeingReplacedTag = modToRemove;
- ini->initFromINI(self, self->getFieldParse());
- self->m_moduleBeingReplacedName.clear();
- self->m_moduleBeingReplacedTag.clear();
- self->m_moduleParsingMode = oldMode;
- }
- //-------------------------------------------------------------------------------------------------
- /** mark the module(s) as being "Inheritable". */
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseInheritableModule(INI *ini, void *instance, void *store, const void *userData)
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- ModuleParseMode oldMode = (ModuleParseMode)self->m_moduleParsingMode;
- if (oldMode != MODULEPARSE_NORMAL)
- throw INI_INVALID_DATA;
- self->m_moduleParsingMode = MODULEPARSE_INHERITABLE;
- ini->initFromINI(self, self->getFieldParse());
- self->m_moduleParsingMode = oldMode;
- }
- //-------------------------------------------------------------------------------------------------
- /** Remove the module whose tag matches moduleToRemove. */
- //-------------------------------------------------------------------------------------------------
- Bool ThingTemplate::removeModuleInfo(const AsciiString& moduleToRemove, AsciiString& clearedModuleNameOut)
- {
- Bool removed = false;
- // do NOT clear... we only want to modify this if we return true.
- // if we return false, we should leave this unmodified.
- //clearedModuleNameOut.clear();
- if (m_behaviorModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
- {
- DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
- removed = true;
- }
- if (m_drawModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
- {
- DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
- removed = true;
- }
- if (m_clientUpdateModuleInfo.clearModuleDataWithTag(moduleToRemove, clearedModuleNameOut))
- {
- DEBUG_ASSERTCRASH(!removed, ("Hmm, multiple removed in ThingTemplate::removeModuleInfo, should this be possible?"));
- removed = true;
- }
- return removed;
- }
- //-------------------------------------------------------------------------------------------------
- /// @todo srj -- move this to another file
- void ArmorTemplateSet::parseArmorTemplateSet( INI* ini )
- {
- static const FieldParse myFieldParse[] =
- {
- { "Conditions", ArmorSetFlags::parseFromINI, NULL, offsetof( ArmorTemplateSet, m_types ) },
- { "Armor", INI::parseArmorTemplate, NULL, offsetof( ArmorTemplateSet, m_template ) },
- { "DamageFX", INI::parseDamageFX, NULL, offsetof( ArmorTemplateSet, m_fx ) },
- { 0, 0, 0, 0 }
- };
- ini->initFromINI(this, myFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseArmorTemplateSet( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- if (self->m_armorCopiedFromDefault == TRUE)
- {
- self->m_armorCopiedFromDefault = FALSE;
- self->m_armorTemplateSets.clear();
- }
- ArmorTemplateSet ws;
- ws.parseArmorTemplateSet(ini);
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES)
- {
- for (ArmorTemplateSetVector::const_iterator it = self->m_armorTemplateSets.begin(); it != self->m_armorTemplateSets.end(); ++it)
- {
- if (it->getNthConditionsYes(0) == ws.getNthConditionsYes(0))
- {
- DEBUG_CRASH(("dup armorset condition in %s\n",self->getName().str()));
- }
- }
- }
- #endif
- self->m_armorTemplateSets.push_back(ws);
- self->m_armorTemplateSetFinder.clear();
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::parseWeaponTemplateSet( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- ThingTemplate* self = (ThingTemplate*)instance;
- if (self->m_weaponsCopiedFromDefault == TRUE)
- {
- self->m_weaponsCopiedFromDefault = FALSE;
- self->m_weaponTemplateSets.clear();
- }
- WeaponTemplateSet ws;
- ws.parseWeaponTemplateSet(ini, self);
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES)
- {
- for (WeaponTemplateSetVector::const_iterator it = self->m_weaponTemplateSets.begin(); it != self->m_weaponTemplateSets.end(); ++it)
- {
- if (it->getNthConditionsYes(0) == ws.getNthConditionsYes(0))
- {
- DEBUG_CRASH(("dup weaponset condition in %s\n",self->getName().str()));
- }
- }
- }
- #endif
- self->m_weaponTemplateSets.push_back(ws);
- self->m_weaponTemplateSetFinder.clear();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ThingTemplate::ThingTemplate() :
- m_geometryInfo(GEOMETRY_SPHERE, FALSE, 1, 1, 1)
- {
- m_moduleParsingMode = MODULEPARSE_NORMAL;
- m_reskinnedFrom = NULL;
- m_radarPriority = RADAR_PRIORITY_INVALID;
- m_nextThingTemplate = NULL;
- m_transportSlotCount = 0;
- m_fenceWidth = 0;
- m_fenceXOffset = 0;
- m_visionRange = 0.0f;
- m_shroudClearingRange = -1.0f;
- m_buildCost = 0;
- m_buildTime = 1;
- m_refundValue = 0;
- m_energyProduction = 0;
- m_energyBonus = 0;
- m_buildCompletion = BC_APPEARS_AT_RALLY_POINT;
- for( Int levelIndex = 0; levelIndex < LEVEL_COUNT; levelIndex++ )
- {
- m_experienceValues[levelIndex] = 0;
- m_experienceRequired[levelIndex] = 0;
- // -1 means "same value as experienceValues for that level"
- m_skillPointValues[levelIndex] = USE_EXP_VALUE_FOR_SKILL_VALUE;
- }
- m_isTrainable = FALSE;
- m_templateID = 0;
- m_kindof = KINDOFMASK_NONE;
- //m_defaultOwningSide = ""; // unnecessary
- m_isBuildFacility = FALSE;
- m_isPrerequisite = FALSE;
- m_placementViewAngle = 0.0f;
- m_factoryExitWidth = 0.0f;
- m_factoryExtraBibWidth = 0.0f;
- m_selectedPortraitImage = NULL;
- m_buttonImage = NULL;
- m_shadowType = SHADOW_NONE;
- m_shadowSizeX = 0.0f;
- m_shadowSizeY = 0.0f;
- m_shadowOffsetX = 0.0f;
- m_shadowOffsetY = 0.0f;
- m_occlusionDelay = TheGlobalData->m_defaultOcclusionDelay;
- m_structureRubbleHeight = 0;
- m_instanceScaleFuzziness = 0;
- m_threatValue = 0;
- m_maxSimultaneousOfType = 0; // unlimited
- m_crusherLevel = 0; //Unspecified, this object is unable to crush anything!
- m_crushableLevel = 255; //Unspecified, this object is unable to be crushed by anything!
- }
- //-------------------------------------------------------------------------------------------------
- AIUpdateModuleData *ThingTemplate::friend_getAIModuleInfo(void)
- {
- Int numModInfos = m_behaviorModuleInfo.getCount();
- for (int j = 0; j < numModInfos; ++j)
- {
- if (m_behaviorModuleInfo.getNthData(j) && m_behaviorModuleInfo.getNthData(j)->isAiModuleData())
- {
- return (AIUpdateModuleData *)m_behaviorModuleInfo.friend_getNthData(j);
- }
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::validateAudio()
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- #define AUDIO_TEST(y) \
- if (!get##y()->getEventName().isEmpty() && get##y()->getEventName().compareNoCase("NoSound") != 0) { \
- DEBUG_ASSERTLOG(TheAudio->isValidAudioEvent(get##y()), ("Invalid Sound '%s' in Object '%s'. (%s?)\n", #y, getName().str(), get##y()->getEventName().str())); \
- }
- AUDIO_TEST(VoiceSelect)
- AUDIO_TEST(VoiceGroupSelect)
- AUDIO_TEST(VoiceMove)
- AUDIO_TEST(VoiceAttack)
- AUDIO_TEST(VoiceEnter)
- AUDIO_TEST(VoiceFear)
- AUDIO_TEST(VoiceSelectElite)
- AUDIO_TEST(VoiceCreated)
- AUDIO_TEST(VoiceNearEnemy)
- AUDIO_TEST(VoiceTaskUnable)
- AUDIO_TEST(VoiceTaskComplete)
- AUDIO_TEST(VoiceMeetEnemy)
- AUDIO_TEST(VoiceGarrison)
- #ifdef ALLOW_SURRENDER
- AUDIO_TEST(VoiceSurrender)
- #endif
- AUDIO_TEST(VoiceDefect)
- AUDIO_TEST(VoiceAttackSpecial)
- AUDIO_TEST(VoiceAttackAir)
- AUDIO_TEST(VoiceGuard)
- AUDIO_TEST(SoundMoveStart)
- AUDIO_TEST(SoundMoveStartDamaged)
- AUDIO_TEST(SoundMoveLoop)
- AUDIO_TEST(SoundMoveLoopDamaged)
- AUDIO_TEST(SoundDie)
- AUDIO_TEST(SoundCrush)
- AUDIO_TEST(SoundAmbient)
- AUDIO_TEST(SoundAmbientDamaged)
- AUDIO_TEST(SoundAmbientReallyDamaged)
- AUDIO_TEST(SoundAmbientRubble)
- AUDIO_TEST(SoundStealthOn)
- AUDIO_TEST(SoundStealthOff)
- AUDIO_TEST(SoundCreated)
- AUDIO_TEST(SoundOnDamaged)
- AUDIO_TEST(SoundOnReallyDamaged)
- AUDIO_TEST(SoundDieFire)
- AUDIO_TEST(SoundDieToxin)
- AUDIO_TEST(SoundEnter)
- AUDIO_TEST(SoundExit)
- AUDIO_TEST(SoundPromotedVeteran)
- AUDIO_TEST(SoundPromotedElite)
- AUDIO_TEST(SoundPromotedHero)
-
- #undef AUDIO_TEST
- const PerUnitSoundMap *perUnitSounds = getAllPerUnitSounds();
- if (!perUnitSounds)
- {
- return;
- }
- for (PerUnitSoundMap::const_iterator it = perUnitSounds->begin(); it != perUnitSounds->end(); ++it)
- {
- if (!it->second.getEventName().isEmpty() && it->second.getEventName().compareNoCase("NoSound") != 0)
- {
- DEBUG_ASSERTCRASH(TheAudio->isValidAudioEvent(&it->second),
- ("Invalid UnitSpecificSound '%s' in Object '%s'. (%s?)",
- it->first.str(),
- getName().str(),
- it->second.getEventName().str()));
- }
- }
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::validate()
- {
- if (m_shadowTextureName.isEmpty())
- {
- // no texture given, pick a default
- switch (getTemplateGeometryInfo().getGeomType())
- {
- case GEOMETRY_SPHERE:
- case GEOMETRY_CYLINDER:
- m_shadowTextureName = "shadow";
- break;
- case GEOMETRY_BOX:
- m_shadowTextureName = "shadows";
- break;
- }
- }
- validateAudio();
- #if defined(_DEBUG) || defined(_INTERNAL)
-
- if (getName() == "DefaultThingTemplate")
- return;
- // neutron missile is an old special case....
- if (getName() == "NeutronMissile")
- return;
- // another cheesy special case....
- if (getName() == "FlamethrowerProjectileStream")
- return;
- // drawable-only templates are exempt from further checks.
- if (isKindOf(KINDOF_DRAWABLE_ONLY))
- return;
- // build-variation templates are exempt from further checks.
- if (!m_buildVariations.empty())
- return;
- Bool isImmobile = isKindOf(KINDOF_IMMOBILE);
-
- if (isKindOf(KINDOF_SHRUBBERY) && !isImmobile)
- {
- DEBUG_CRASH(("SHRUBBERY %s must be marked IMMOBILE!",getName().str()));
- }
- if (isKindOf(KINDOF_STRUCTURE) && !isImmobile)
- {
- 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()));
- }
- if (isKindOf(KINDOF_STICK_TO_TERRAIN_SLOPE) && !isImmobile)
- {
- DEBUG_CRASH(("item %s is marked STICK_TO_TERRAIN_SLOPE but not IMMOBILE -- please fix it.\n",getName().str()));
- }
- if (isKindOf(KINDOF_STRUCTURE))
- {
- if (m_armorTemplateSets.empty() || (m_armorTemplateSets.size() == 1 && m_armorTemplateSets[0].getArmorTemplate() == NULL))
- {
- DEBUG_CRASH(("Structure %s has no armor, but probably should (StructureArmor) -- please fix it.)\n",getName().str()));
- }
- for (ArmorTemplateSetVector::const_iterator it = m_armorTemplateSets.begin(); it != m_armorTemplateSets.end(); ++it)
- {
- if (it->getDamageFX() == NULL)
- {
- DEBUG_CRASH(("Structure %s has no ArmorDamageFX, and really should.\n",getName().str()));
- }
- }
- }
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- // copy the guts of that into this, but preserve this' name, id, and list-links.
- void ThingTemplate::copyFrom(const ThingTemplate* that)
- {
- if (!that)
- return;
- ThingTemplate* next = this->m_nextThingTemplate;
- UnsignedShort id = this->m_templateID;
- AsciiString name = this->m_nameString;
- *this = *that;
- this->m_nextThingTemplate = next;
- this->m_templateID = id;
- this->m_nameString = name;
- }
- //-------------------------------------------------------------------------------------------------
- void ThingTemplate::setCopiedFromDefault()
- {
- m_armorCopiedFromDefault = true;
- m_weaponsCopiedFromDefault = true;
- m_behaviorModuleInfo.setCopiedFromDefault(true);
- m_drawModuleInfo.setCopiedFromDefault(true);
- m_clientUpdateModuleInfo.setCopiedFromDefault(true);
- }
- //-------------------------------------------------------------------------------------------------
- ThingTemplate::~ThingTemplate()
- {
- // note, we don't need to take any special action for Armor/WeaponSets...
- // though it is just a list of 'raw' pointers, we don't have ownership of 'em,
- // and so we MUST NOT delete them
- }
- //=============================================================================
- void ThingTemplate::resolveNames()
- {
- Int i, j;
- for (i = 0; i < m_prereqInfo.size(); i++)
- {
- m_prereqInfo[i].resolveNames();
- }
- const Int MAX_BF = 32;
- const ThingTemplate* tmpls[MAX_BF];
- for (i = 0; i < m_prereqInfo.size(); i++)
- {
- Int count = m_prereqInfo[i].getAllPossibleBuildFacilityTemplates(tmpls, MAX_BF);
- for (j = 0; j < count; j++)
- {
- // casting const away is a little evil, but justified in this case:
- // PropductionPrerequisite should only be allowed 'const' access,
- // but ThingTemplate can muck with stuff with gleeful abandon. (srj)
- if( tmpls[ j ] )
- const_cast<ThingTemplate*>(tmpls[j])->m_isBuildFacility = true;
- // DEBUG_LOG(("BF: %s is a buildfacility for %s\n",tmpls[j]->m_nameString.str(),this->m_nameString.str()));
- }
- }
-
- if (isKindOf(KINDOF_COMMANDCENTER)) {
- // Command centers are considered factories. jba.
- m_isBuildFacility = true;
- }
- // keep a pointer to portrait and button image if present for speed later
- if( TheMappedImageCollection )
- {
- if( m_selectedPortraitImageName.isNotEmpty() )
- {
- m_selectedPortraitImage = TheMappedImageCollection->findImageByName( m_selectedPortraitImageName );
- DEBUG_ASSERTCRASH( m_selectedPortraitImage, ("%s is looking for Portrait %s but can't find it. Skipping...", getName().str(), m_buttonImageName.str() ) );
- m_selectedPortraitImageName.clear(); // we're done with this, so nuke it
- }
- if( m_buttonImageName.isNotEmpty() )
- {
- m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName );
- DEBUG_ASSERTCRASH( m_buttonImage, ("%s is looking for ButtonImage %s but can't find it. Skipping...", getName().str(), m_buttonImageName.str() ) );
- m_buttonImageName.clear(); // we're done with this, so nuke it
- }
- }
- }
- //=============================================================================
- #ifdef LOAD_TEST_ASSETS
- void ThingTemplate::initForLTA(const AsciiString& name)
- {
- m_nameString = name;
- char buffer[1024];
- strncpy(buffer, name.str(), sizeof(buffer));
- for (int i=0; buffer[i]; i++) {
- if (buffer[i] == '/') {
- i++;
- break;
- }
- }
- m_LTAName = AsciiString(buffer+i);
- m_behaviorModuleInfo.clear();
- m_drawModuleInfo.clear();
- m_clientUpdateModuleInfo.clear();
- AsciiString moduleTag;
- moduleTag.format( "LTA_%sDestroyDie", m_LTAName.str() );
- m_behaviorModuleInfo.addModuleInfo(this, "DestroyDie", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "DestroyDie", MODULETYPE_BEHAVIOR, moduleTag), (MODULEINTERFACE_DIE), false);
- moduleTag.format( "LTA_%sInactiveBody", m_LTAName.str() );
- m_behaviorModuleInfo.addModuleInfo(this, "InactiveBody", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "InactiveBody", MODULETYPE_BEHAVIOR, moduleTag), (MODULEINTERFACE_BODY), false);
- moduleTag.format( "LTA_%sW3DDefaultDraw", m_LTAName.str() );
- m_drawModuleInfo.addModuleInfo(this, "W3DDefaultDraw", moduleTag, TheModuleFactory->newModuleDataFromINI(NULL, "W3DDefaultDraw", MODULETYPE_DRAW, moduleTag), (MODULEINTERFACE_DRAW), false);
- m_armorCopiedFromDefault = false;
- m_weaponsCopiedFromDefault = false;
- m_kindof = KINDOFMASK_NONE;
- m_assetScale = 1.0f;
- m_instanceScaleFuzziness = 0.0f; ///< tolerance to randomly vary scale per instance
- m_structureRubbleHeight = 0.0f; // zero means "use global default"
- m_displayName.translate( name );
- m_shadowType = SHADOW_VOLUME;
- m_geometryInfo.set(GEOMETRY_SPHERE, false, 10.0, 10.0, 10.0);
-
- }
- #endif
- //=============================================================================
- const ArmorTemplateSet* ThingTemplate::findArmorTemplateSet(const ArmorSetFlags& t) const
- {
- return m_armorTemplateSetFinder.findBestInfo(m_armorTemplateSets, t);
- }
- //=============================================================================
- const WeaponTemplateSet* ThingTemplate::findWeaponTemplateSet(const WeaponSetFlags& t) const
- {
- return m_weaponTemplateSetFinder.findBestInfo(m_weaponTemplateSets, t);
- }
- //-----------------------------------------------------------------------------
- // returns true iff we have at least one weaponset that contains a weapon.
- // returns false if we have no weaponsets, or they are all empty.
- Bool ThingTemplate::canPossiblyHaveAnyWeapon() const
- {
- for (WeaponTemplateSetVector::const_iterator it = m_weaponTemplateSets.begin();
- it != m_weaponTemplateSets.end();
- ++it)
- {
- if (it->hasAnyWeapons())
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- Int ThingTemplate::getSkillPointValue(Int level) const
- {
- Int value = m_skillPointValues[level];
-
- if (value == USE_EXP_VALUE_FOR_SKILL_VALUE)
- value = getExperienceValue(level);
-
- return value;
- }
- //-----------------------------------------------------------------------------
- const ThingTemplate *ThingTemplate::getBuildFacilityTemplate( const Player *player ) const
- {
- if (getPrereqCount() > 0)
- {
- return m_prereqInfo[0].getExistingBuildFacilityTemplate(player); // might return null
- }
- else
- {
- return NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- BuildableStatus ThingTemplate::getBuildable() const
- {
- BuildableStatus bs;
- if (TheGameLogic && TheGameLogic->findBuildableStatusOverride(this, bs))
- return bs;
- return (BuildableStatus)m_buildable;
- }
- //-------------------------------------------------------------------------------------------------
- const FXList *ThingTemplate::getPerUnitFX(const AsciiString& fxName) const
- {
- if (fxName.isEmpty())
- {
- return NULL;
- }
-
- PerUnitFXMap::const_iterator it = m_perUnitFX.find(fxName);
- if (it == m_perUnitFX.end())
- {
- DEBUG_CRASH(("Unknown FX name (%s) asked for in ThingTemplate (%s). ", fxName.str(), m_nameString.str()));
- return NULL;
- }
- return (it->second);
- }
- //-------------------------------------------------------------------------------------------------
- const AudioEventRTS *ThingTemplate::getPerUnitSound(const AsciiString& soundName) const
- {
- if (soundName.isEmpty())
- {
- return &s_audioEventNoSound;
- }
-
- PerUnitSoundMap::const_iterator it = m_perUnitSounds.find(soundName);
- if (it == m_perUnitSounds.end())
- {
- DEBUG_LOG(("Unknown Audio name (%s) asked for in ThingTemplate (%s).\n", soundName.str(), m_nameString.str()));
- return &s_audioEventNoSound;
- }
- return &(it->second);
- }
- //-------------------------------------------------------------------------------------------------
- Bool ThingTemplate::isEquivalentTo(const ThingTemplate* tt) const
- {
- // sanity
- if (!(this && tt))
- return false;
- // sanity
- if (this == tt)
- return true;
-
- if (this->getFinalOverride() == tt->getFinalOverride())
- return true;
- // This reskinned from that?
- if (this->m_reskinnedFrom == tt)
- return true;
- // That reskinned from this?
- if (this == tt->m_reskinnedFrom)
- return true;
- // This reskinned from that reskinned from?
- // Kris: added case (chassis 2 compared to chassis 3 -- NULL possible if not reskinned)
- if( this->m_reskinnedFrom && this->m_reskinnedFrom == tt->m_reskinnedFrom )
- return true;
- // Is this thing a build variation of that thing or vice versa
- Int i;
- Int numVariations = m_buildVariations.size();
- for (i = 0; i < numVariations; ++i)
- if (m_buildVariations[i].compareNoCase(tt->getName()) == 0)
- return true;
-
- numVariations = tt->m_buildVariations.size();
- for (i = 0; i < numVariations; ++i)
- if (tt->m_buildVariations[i].compareNoCase(getName()) == 0)
- return true;
- // Guess we're not equivalent.
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ThingTemplate::isBuildableItem(void) const
- {
- return (getBuildCost() != 0);
- }
- //-------------------------------------------------------------------------------------------------
- /** NOTE that we're not paying attention to m_override here, instead the portions
- * that retrieve template data values use the get() wrappers, which *DO* pay
- * attention to the override values */
- //-------------------------------------------------------------------------------------------------
- Int ThingTemplate::calcCostToBuild( const Player* player) const
- {
- if (!player)
- return 0;
- // changePercent format is "-.2 equals 20% cheaper"
- Real factionModifier = 1 + player->getProductionCostChangePercent( getName() );
- factionModifier *= player->getProductionCostChangeBasedOnKindOf( m_kindof );
- return getBuildCost() * factionModifier * player->getHandicap()->getHandicap(Handicap::BUILDCOST, this);
- }
- //-------------------------------------------------------------------------------------------------
- /** NOTE that we're not paying attention to m_override here, instead the portions
- * that retrieve template data values use the get() wrappers, which *DO* pay
- * attention to the override values */
- //-------------------------------------------------------------------------------------------------
- Int ThingTemplate::calcTimeToBuild( const Player* player) const
- {
- Int buildTime = getBuildTime() * LOGICFRAMES_PER_SECOND;
- buildTime *= player->getHandicap()->getHandicap(Handicap::BUILDTIME, this);
- Real factionModifier = 1 + player->getProductionTimeChangePercent( getName() );
- buildTime *= factionModifier;
- #if defined (_DEBUG) || defined (_INTERNAL)
- if( player->buildsInstantly() )
- {
- buildTime = 1;
- }
- #endif
- // Adjust build time based on energy supply.
- Real EnergyPercent = player->getEnergy()->getEnergySupplyRatio(); //I'm at 80% Energy
- if (EnergyPercent > 1.0f)
- EnergyPercent = 1.0f; // getEnergySupplyRatio() returns a true ratio, but we don't care about excess.
- Real EnergyShort = 1.0f - EnergyPercent; //so I am 20% short
- EnergyShort *= TheGlobalData->m_LowEnergyPenaltyModifier; //which is a 40% penalty, or a 10% penalty
- Real penaltyRate = 1.0f - EnergyShort;
- penaltyRate = max(penaltyRate, TheGlobalData->m_MinLowEnergyProductionSpeed); //bind so 0% does not dead stop you
- if( EnergyPercent < 1.0f ) //and make 99% look like 80% (eg) since most of the time you are down only a little
- penaltyRate = min(penaltyRate, TheGlobalData->m_MaxLowEnergyProductionSpeed);
- if (penaltyRate <= 0.0f)
- penaltyRate = 0.01f; // Design won't make the minimum 0, they promise
- buildTime /= penaltyRate;//and voila. Makes sense, designers have control, all is happy.
- // Multiple build facilities can have an added production bonus.
- if (getBuildCompletion() == BC_APPEARS_AT_RALLY_POINT)
- {
- const ThingTemplate *tmpl = getBuildFacilityTemplate(player); // could be null if none exist
- Int count = 0;
- if (tmpl)
- {
- player->countObjectsByThingTemplate(1, &tmpl, false, &count);
- Real factoryMult = TheGlobalData->m_MultipleFactory;
- if (factoryMult > 0.0f)
- {
- for(int i=0; i < count - 1; i++)
- buildTime *= factoryMult;
- }
- }
- }
- return(buildTime);
- }
- //---------------------------------------------------------------------------------------ModuleInfo
- //-------------------------------------------------------------------------------------------------
- ModuleData* ModuleInfo::friend_getNthData(Int i)
- {
- if (i >= 0 && i < m_info.size())
- {
- // This is kinda naughty, but its necessary.
- return const_cast<ModuleData*>(m_info[i].second);
- }
- return NULL;
- }
|