1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 1998-2000, Matthes Bender
5 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18 /* Object definition */
19
20 #include "C4Include.h"
21 #include "C4ForbidLibraryCompilation.h"
22 #include "object/C4Def.h"
23
24 #include "c4group/C4Components.h"
25 #include "graphics/C4Draw.h"
26 #include "graphics/C4DrawGL.h"
27 #include "graphics/C4GraphicsResource.h"
28 #include "graphics/CSurface8.h"
29 #include "landscape/C4Particles.h"
30 #include "landscape/C4SolidMask.h"
31 #include "lib/StdColors.h"
32 #include "lib/StdMeshLoader.h"
33 #include "object/C4Object.h"
34 #include "platform/C4FileMonitor.h"
35 #include "platform/C4SoundSystem.h"
36 #include "player/C4RankSystem.h"
37
38 // Helper class to load additional resources required for meshes from
39 // a C4Group.
40 class C4DefAdditionalResourcesLoader: public StdMeshMaterialLoader
41 {
42 public:
C4DefAdditionalResourcesLoader(C4Group & hGroup)43 C4DefAdditionalResourcesLoader(C4Group& hGroup): Group(hGroup) {}
44
LoadTexture(const char * filename)45 C4Surface* LoadTexture(const char* filename) override
46 {
47 if (!Group.AccessEntry(filename)) return nullptr;
48 C4Surface* surface = new C4Surface;
49 // Suppress error message here, StdMeshMaterial loader
50 // will show one.
51 if (!surface->Read(Group, GetExtension(filename), C4SF_MipMap))
52 { delete surface; surface = nullptr; }
53 return surface;
54 }
55
LoadShaderCode(const char * filename)56 StdStrBuf LoadShaderCode(const char* filename) override
57 {
58 StdStrBuf ret;
59 if (!Group.LoadEntryString(filename, &ret)) return StdStrBuf();
60 return ret;
61 }
62
AddShaderSlices(C4Shader & shader,int ssc)63 void AddShaderSlices(C4Shader& shader, int ssc) override
64 {
65 #ifndef USE_CONSOLE
66 // Add mesh-independent slices
67 shader.AddDefine("OPENCLONK");
68 shader.AddDefine("OC_MESH");
69
70 if (ssc & C4SSC_MOD2) shader.AddDefine("OC_CLRMOD_MOD2");
71 if (ssc & C4SSC_LIGHT) shader.AddDefine("OC_DYNAMIC_LIGHT");
72
73 // Note these are never set for meshes at the moment:
74 if (ssc & C4SSC_BASE) shader.AddDefine("OC_HAVE_BASE");
75 if (ssc & C4SSC_OVERLAY) shader.AddDefine("OC_HAVE_OVERLAY");
76
77 shader.LoadFragmentSlices(&::GraphicsResource.Files, "CommonShader.glsl");
78 shader.LoadFragmentSlices(&::GraphicsResource.Files, "ObjectShader.glsl");
79
80 // Categories for script shaders.
81 shader.SetScriptCategories({"Common", "Object"});
82 #endif
83 }
84
85 private:
86 C4Group& Group;
87 };
88
89
90
DefaultDefCore()91 void C4Def::DefaultDefCore()
92 {
93 rC4XVer[0]=rC4XVer[1]=0;
94 RequireDef.Clear();
95 Shape.Default();
96 Entrance.Default();
97 Collection.Default();
98 PictureRect.Default();
99 SolidMask.Default();
100 TopFace.Default();
101 BurnTurnTo=C4ID::None;
102 GrowthType=0;
103 CrewMember=0;
104 NativeCrew=0;
105 Mass=0;
106 Value=0;
107 Exclusive=0;
108 Category=0;
109 Constructable=0;
110 Rotateable=0;
111 RotatedEntrance=0;
112 Float=0;
113 ColorByOwner=0;
114 NoHorizontalMove=0;
115 LiftTop=0;
116 GrabPutGet=0;
117 UprightAttach=0;
118 Line=0;
119 LineIntersect=0;
120 IncompleteActivity=0;
121 Oversize=0;
122 Fragile=0;
123 NoPushEnter=0;
124 Projectile=0;
125 VehicleControl=0;
126 Pathfinder=0;
127 NoMassFromContents=0;
128 MoveToRange=0;
129 NoStabilize=0;
130 ClosedContainer=0;
131 SilentCommands=0;
132 TemporaryCrew=0;
133 BlitMode=C4D_Blit_Normal;
134 NoBreath=0;
135 ConSizeOff=0;
136 NoGet=0;
137 NoTransferZones=0;
138 HideInCreator = false;
139 }
140
LoadDefCore(C4Group & hGroup)141 bool C4Def::LoadDefCore(C4Group &hGroup)
142 {
143 StdStrBuf Source;
144 if (hGroup.LoadEntryString(C4CFN_DefCore,&Source))
145 {
146 StdStrBuf Name = hGroup.GetFullName() + (const StdStrBuf &)FormatString("%cDefCore.txt", DirectorySeparator);
147 if (!Compile(Source.getData(), Name.getData()))
148 return false;
149 Source.Clear();
150
151 // Check mass
152 if (Mass < 0)
153 {
154 DebugLogF("WARNING: Def %s (%s) at %s has invalid mass!", GetName(), id.ToString(), hGroup.GetFullName().getData());
155 Mass = 0;
156 }
157
158 return true;
159 }
160 return false;
161 }
162
Save(C4Group & hGroup)163 bool C4Def::Save(C4Group &hGroup)
164 {
165 StdStrBuf Out;
166 if (! Decompile(&Out, FormatString("%s::DefCore.txt", id.ToString()).getData()) )
167 return false;
168 return hGroup.Add(C4CFN_DefCore,Out,false,true);
169 }
170
Compile(const char * szSource,const char * szName)171 bool C4Def::Compile(const char *szSource, const char *szName)
172 {
173 return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "DefCore"), StdStrBuf(szSource), szName);
174 }
175
Decompile(StdStrBuf * pOut,const char * szName)176 bool C4Def::Decompile(StdStrBuf *pOut, const char *szName)
177 {
178 return DecompileToBuf_Log<StdCompilerINIWrite>(mkNamingAdapt(*this, "DefCore"), pOut, szName);
179 }
180
CompileFunc(StdCompiler * pComp)181 void C4Def::CompileFunc(StdCompiler *pComp)
182 {
183
184 pComp->Value(mkNamingAdapt(id, "id", C4ID::None ));
185 pComp->Value(mkNamingAdapt(toC4CArr(rC4XVer), "Version" ));
186 pComp->Value(mkNamingAdapt(mkParAdapt(RequireDef, false), "RequireDef", C4IDList() ));
187
188 const StdBitfieldEntry<int32_t> Categories[] =
189 {
190
191 { "C4D_None", C4D_None },
192 { "C4D_StaticBack", C4D_StaticBack },
193 { "C4D_Structure", C4D_Structure },
194 { "C4D_Vehicle", C4D_Vehicle },
195 { "C4D_Living", C4D_Living },
196 { "C4D_Object", C4D_Object },
197
198 { "C4D_Goal", C4D_Goal },
199 { "C4D_Rule", C4D_Rule },
200 { "C4D_Environment", C4D_Environment },
201
202 { "C4D_Background", C4D_Background },
203 { "C4D_Parallax", C4D_Parallax },
204 { "C4D_MouseSelect", C4D_MouseSelect },
205 { "C4D_Foreground", C4D_Foreground },
206 { "C4D_MouseIgnore", C4D_MouseIgnore },
207 { "C4D_IgnoreFoW", C4D_IgnoreFoW },
208
209 { nullptr, 0 }
210 };
211
212 pComp->Value(mkNamingAdapt(mkBitfieldAdapt<int32_t>(Category, Categories),
213 "Category", 0 ));
214
215 pComp->Value(mkParAdapt(Shape, static_cast<C4Shape*>(nullptr)));
216 pComp->Value(mkNamingAdapt(Value, "Value", 0 ));
217 pComp->Value(mkNamingAdapt(Mass, "Mass", 0 ));
218 pComp->Value(mkNamingAdapt(SolidMask, "SolidMask", TargetRect0 ));
219 pComp->Value(mkNamingAdapt(TopFace, "TopFace", TargetRect0 ));
220 pComp->Value(mkNamingAdapt(PictureRect, "Picture", Rect0 ));
221 pComp->Value(mkNamingAdapt(Entrance, "Entrance", Rect0 ));
222 pComp->Value(mkNamingAdapt(Collection, "Collection", Rect0 ));
223 pComp->Value(mkNamingAdapt(Exclusive, "Exclusive", 0 ));
224 pComp->Value(mkNamingAdapt(Line, "Line", 0 ));
225 // <Newton> undocumented, but obsolete? I don't understand the sense of this value.
226 pComp->Value(mkNamingAdapt(LineIntersect, "LineIntersect", 0 ));
227 pComp->Value(mkNamingAdapt(CrewMember, "CrewMember", 0 ));
228 pComp->Value(mkNamingAdapt(NativeCrew, "NoStandardCrew", 0 ));
229 pComp->Value(mkNamingAdapt(Constructable, "Construction", 0 ));
230
231 const StdBitfieldEntry<int32_t> GrabPutGetTypes[] =
232 {
233
234 { "C4D_GrabGet" ,C4D_Grab_Get},
235 { "C4D_GrabPut" ,C4D_Grab_Put},
236
237 { nullptr, 0}
238 };
239
240 pComp->Value(mkNamingAdapt(mkBitfieldAdapt(GrabPutGet, GrabPutGetTypes),
241 "GrabPutGet", 0 ));
242
243 pComp->Value(mkNamingAdapt(Rotateable, "Rotate", 0 ));
244 pComp->Value(mkNamingAdapt(RotatedEntrance, "RotatedEntrance", 0 ));
245 pComp->Value(mkNamingAdapt(Float, "Float", 0 ));
246 pComp->Value(mkNamingAdapt(ColorByOwner, "ColorByOwner", 0 ));
247 pComp->Value(mkNamingAdapt(NoHorizontalMove, "HorizontalFix", 0 ));
248 pComp->Value(mkNamingAdapt(LiftTop, "LiftTop", 0 ));
249 pComp->Value(mkNamingAdapt(UprightAttach, "UprightAttach", 0 ));
250 pComp->Value(mkNamingAdapt(GrowthType, "StretchGrowth", 0 ));
251 pComp->Value(mkNamingAdapt(IncompleteActivity, "IncompleteActivity", 0 ));
252 pComp->Value(mkNamingAdapt(Oversize, "Oversize", 0 ));
253 // <Newton> Fragile and Projectile are kinda obsolete.
254 // Only used at one point in the command system. Should rather be solved with properties if at all
255 pComp->Value(mkNamingAdapt(Fragile, "Fragile", 0 ));
256 pComp->Value(mkNamingAdapt(Projectile, "Projectile", 0 ));
257
258 pComp->Value(mkNamingAdapt(NoPushEnter, "NoPushEnter", 0 ));
259 pComp->Value(mkNamingAdapt(VehicleControl, "VehicleControl", 0 ));
260 pComp->Value(mkNamingAdapt(Pathfinder, "Pathfinder", 0 ));
261 pComp->Value(mkNamingAdapt(MoveToRange, "MoveToRange", 0 ));
262 pComp->Value(mkNamingAdapt(NoMassFromContents, "NoMassFromContents", 0 ));
263 pComp->Value(mkNamingAdapt(NoStabilize, "NoStabilize", 0 ));
264 pComp->Value(mkNamingAdapt(ClosedContainer, "ClosedContainer", 0 ));
265 pComp->Value(mkNamingAdapt(SilentCommands, "SilentCommands", 0 ));
266 pComp->Value(mkNamingAdapt(TemporaryCrew, "TemporaryCrew", 0 ));
267 pComp->Value(mkNamingAdapt(BlitMode, "BlitMode", C4D_Blit_Normal ));
268 pComp->Value(mkNamingAdapt(NoBreath, "NoBreath", 0 ));
269 pComp->Value(mkNamingAdapt(ConSizeOff, "ConSizeOff", 0 ));
270 pComp->Value(mkNamingAdapt(NoGet, "NoGet", 0 ));
271 pComp->Value(mkNamingAdapt(NoTransferZones, "NoTransferZones", 0 ));
272
273 const StdBitfieldEntry<int32_t> AllowPictureStackModes[] =
274 {
275
276 { "APS_Color", APS_Color },
277 { "APS_Graphics", APS_Graphics },
278 { "APS_Name", APS_Name },
279 { "APS_Overlay", APS_Overlay },
280 { nullptr, 0 }
281 };
282
283 pComp->Value(mkNamingAdapt(mkBitfieldAdapt<int32_t>(AllowPictureStack, AllowPictureStackModes), //undocumented
284 "AllowPictureStack", 0 ));
285 pComp->Value(mkNamingAdapt(HideInCreator, "HideInCreator", false));
286 }
287
288 //-------------------------------- C4Def -------------------------------------------------------
289
C4Def()290 C4Def::C4Def(): Script(), C4PropListStatic(ScriptEngine.GetPropList(), nullptr, nullptr)
291 {
292 Script.SetDef(this);
293 assert(ScriptEngine.GetPropList());
294 Graphics.pDef = this;
295 Default();
296 }
297
Default()298 void C4Def::Default()
299 {
300 DefaultDefCore();
301 Next=nullptr;
302 Temporary=false;
303 Filename[0]=0;
304 Creation=0;
305 Count=0;
306 MainFace.Set(nullptr,0,0,0,0);
307 Script.Clear();
308 StringTable.Clear();
309 pClonkNames=nullptr;
310 pRankNames=nullptr;
311 pRankSymbols=nullptr;
312 fClonkNamesOwned = fRankNamesOwned = fRankSymbolsOwned = false;
313 iNumRankSymbols=1;
314 pSolidMask = nullptr;
315 }
316
~C4Def()317 C4Def::~C4Def()
318 {
319 Clear();
320 }
321
Clear()322 void C4Def::Clear()
323 {
324 Script.Clear();
325 C4PropList::Clear();
326
327 Graphics.Clear();
328
329 StringTable.Clear();
330 if (pClonkNames && fClonkNamesOwned) delete pClonkNames; pClonkNames=nullptr;
331 if (pRankNames && fRankNamesOwned) delete pRankNames; pRankNames=nullptr;
332 if (pRankSymbols && fRankSymbolsOwned) delete pRankSymbols; pRankSymbols=nullptr;
333 fClonkNamesOwned = fRankNamesOwned = fRankSymbolsOwned = false;
334 delete pSolidMask; pSolidMask = nullptr;
335 }
336
Load(C4Group & hGroup,StdMeshSkeletonLoader & loader,DWORD dwLoadWhat,const char * szLanguage,C4SoundSystem * pSoundSystem,C4DefGraphicsPtrBackup * gfx_backup)337 bool C4Def::Load(C4Group &hGroup,
338 StdMeshSkeletonLoader &loader,
339 DWORD dwLoadWhat,
340 const char *szLanguage,
341 C4SoundSystem *pSoundSystem,
342 C4DefGraphicsPtrBackup *gfx_backup
343 )
344 {
345 bool AddFileMonitoring = false;
346 if (Game.pFileMonitor && !SEqual(hGroup.GetFullName().getData(),Filename) && !hGroup.IsPacked())
347 AddFileMonitoring = true;
348
349 // Store filename
350 SCopy(hGroup.GetFullName().getData(),Filename);
351
352 // Verbose log filename
353 if (Config.Graphics.VerboseObjectLoading>=3)
354 Log(hGroup.GetFullName().getData());
355
356 if (AddFileMonitoring) Game.pFileMonitor->AddDirectory(Filename);
357
358 // Pre-read all images and shader stuff because they ar eaccessed in unpredictable order during loading
359 hGroup.PreCacheEntries(C4CFN_ShaderFiles);
360 hGroup.PreCacheEntries(C4CFN_ImageFiles);
361
362 LoadMeshMaterials(hGroup, gfx_backup);
363 bool fSuccess = LoadParticleDef(hGroup);
364
365 // Read DefCore
366 if (fSuccess) fSuccess = LoadDefCore(hGroup);
367
368 // Skip def: don't even read sounds!
369 if (fSuccess && Game.C4S.Definitions.SkipDefs.GetIDCount(id, 1)) return false;
370
371 // Read sounds, even if not a valid def (for pure ocd sound folders)
372 if (dwLoadWhat & C4D_Load_Sounds) LoadSounds(hGroup, pSoundSystem);
373
374 // cancel if not a valid definition
375 if (!fSuccess) return false;
376
377 // Read and parse SolidMask bitmap
378 if (!LoadSolidMask(hGroup)) return false;
379
380 // Read surface bitmap, meshes, skeletons
381 if ((dwLoadWhat & C4D_Load_Bitmap) && !LoadGraphics(hGroup, loader)) return false;
382
383 // Read string table
384 C4Language::LoadComponentHost(&StringTable, hGroup, C4CFN_ScriptStringTbl, szLanguage);
385
386 // Register ID with script engine
387 ::ScriptEngine.RegisterGlobalConstant(id.ToString(), C4VPropList(this));
388 ParentKeyName = ::Strings.RegString(id.ToString());
389
390 // Read script
391 if (dwLoadWhat & C4D_Load_Script) LoadScript(hGroup, szLanguage);
392
393 // Read clonknames
394 if (dwLoadWhat & C4D_Load_ClonkNames) LoadClonkNames(hGroup, pClonkNames, szLanguage);
395
396 // Read clonkranks
397 if (dwLoadWhat & C4D_Load_RankNames) LoadRankNames(hGroup, szLanguage);
398
399 // Read rankfaces
400 if (dwLoadWhat & C4D_Load_RankFaces) LoadRankFaces(hGroup);
401
402 // Temporary flag
403 if (dwLoadWhat & C4D_Load_Temporary) Temporary=true;
404
405 return true;
406 }
407
LoadMeshMaterials(C4Group & hGroup,C4DefGraphicsPtrBackup * gfx_backup)408 void C4Def::LoadMeshMaterials(C4Group &hGroup, C4DefGraphicsPtrBackup *gfx_backup)
409 {
410 // Load all mesh materials from this folder
411 C4DefAdditionalResourcesLoader loader(hGroup);
412 hGroup.ResetSearch();
413 char MaterialFilename[_MAX_PATH + 1]; *MaterialFilename = 0;
414
415 for (const auto &mat : mesh_materials)
416 {
417 ::MeshMaterialManager.Remove(mat, &gfx_backup->GetUpdater());
418 }
419 mesh_materials.clear();
420 while (hGroup.FindNextEntry(C4CFN_DefMaterials, MaterialFilename, nullptr, !!*MaterialFilename))
421 {
422 StdStrBuf material;
423 if (hGroup.LoadEntryString(MaterialFilename, &material))
424 {
425 try
426 {
427 StdStrBuf buf;
428 buf.Copy(hGroup.GetName());
429 buf.Append("/"); buf.Append(MaterialFilename);
430 auto new_materials = ::MeshMaterialManager.Parse(material.getData(), buf.getData(), loader);
431 mesh_materials.insert(new_materials.begin(), new_materials.end());
432 }
433 catch (const StdMeshMaterialError& ex)
434 {
435 DebugLogF("Failed to read material script: %s", ex.what());
436 }
437 }
438 }
439 }
440
LoadParticleDef(C4Group & hGroup)441 bool C4Def::LoadParticleDef(C4Group &hGroup)
442 {
443 bool fSuccess = true;
444 // particle def?
445 if (hGroup.AccessEntry(C4CFN_ParticleCore))
446 {
447 // def loading not successful; abort after reading sounds
448 fSuccess = false;
449 // create new particle def
450 C4ParticleDef *pParticleDef = new C4ParticleDef();
451 // load it
452 if (!pParticleDef->Load(hGroup))
453 {
454 // not successful :( - destroy it again
455 delete pParticleDef;
456 }
457 // done
458 }
459 return fSuccess;
460 }
461
LoadSolidMask(C4Group & hGroup)462 bool C4Def::LoadSolidMask(C4Group &hGroup)
463 {
464 if (hGroup.FindEntry(C4CFN_SolidMask))
465 {
466 pSolidMask = C4SolidMask::LoadMaskFromFile(hGroup, C4CFN_SolidMask);
467 if (!pSolidMask)
468 {
469 DebugLogF(" Error loading SolidMask of %s (%s)", hGroup.GetFullName().getData(), id.ToString());
470 return false;
471 }
472 // check SolidMask size
473 if (SolidMask.x<0 || SolidMask.y<0 || SolidMask.x + SolidMask.Wdt>pSolidMask->Wdt || SolidMask.y + SolidMask.Hgt>pSolidMask->Hgt) SolidMask.Default();
474 }
475 else if (SolidMask.Wdt)
476 {
477 // Warning in case someone wants to define SolidMasks the old way (in the main graphics file)
478 DebugLogF("WARNING: Definition %s (%s) defines SolidMask in DefCore but has no SolidMask file!", hGroup.GetFullName().getData(), id.ToString());
479 SolidMask.Default();
480 }
481
482 return true;
483 }
484
LoadGraphics(C4Group & hGroup,StdMeshSkeletonLoader & loader)485 bool C4Def::LoadGraphics(C4Group &hGroup, StdMeshSkeletonLoader &loader)
486 {
487 // Try to load graphics
488 // No fail on error - just have an object without graphics.
489 Graphics.Load(hGroup, loader, !!ColorByOwner);
490
491 if (Graphics.Type == C4DefGraphics::TYPE_Bitmap)
492 {
493 // Bitmap post-load settings
494 if (Graphics.GetBitmap())
495 {
496 // Set MainFace (unassigned bitmap: will be set by GetMainFace())
497 MainFace.Set(nullptr, 0, 0, Shape.Wdt, Shape.Hgt);
498 }
499
500 // Adjust picture rect
501 if ((PictureRect.Wdt == 0) || (PictureRect.Hgt == 0))
502 PictureRect.Set(0, 0, Shape.Wdt*Graphics.Bmp.Bitmap->Scale, Shape.Hgt*Graphics.Bmp.Bitmap->Scale);
503
504 // validate TopFace
505 if (TopFace.x<0 || TopFace.y<0 || TopFace.x + TopFace.Wdt>Graphics.Bmp.Bitmap->Wdt || TopFace.y + TopFace.Hgt>Graphics.Bmp.Bitmap->Hgt)
506 {
507 TopFace.Default();
508 // warn in debug mode
509 DebugLogF("invalid TopFace in %s (%s)", GetName(), id.ToString());
510 }
511 }
512 else
513 {
514 TopFace.Default();
515 }
516
517 return true;
518 }
519
LoadScript(C4Group & hGroup,const char * szLanguage)520 void C4Def::LoadScript(C4Group &hGroup, const char* szLanguage)
521 {
522 // reg script to engine
523 Script.Reg2List(&::ScriptEngine);
524 // Load script
525 Script.Load(hGroup, C4CFN_Script, szLanguage, &StringTable);
526 }
527
LoadClonkNames(C4Group & hGroup,C4ComponentHost * pClonkNames,const char * szLanguage)528 void C4Def::LoadClonkNames(C4Group &hGroup, C4ComponentHost* pClonkNames, const char* szLanguage)
529 {
530 // clear any previous
531 if (pClonkNames) delete pClonkNames; pClonkNames = nullptr;
532 if (hGroup.FindEntry(C4CFN_ClonkNameFiles))
533 {
534 // create new
535 pClonkNames = new C4ComponentHost();
536 if (!C4Language::LoadComponentHost(pClonkNames, hGroup, C4CFN_ClonkNames, szLanguage))
537 {
538 delete pClonkNames; pClonkNames = nullptr;
539 }
540 else
541 fClonkNamesOwned = true;
542 }
543 }
544
LoadRankNames(C4Group & hGroup,const char * szLanguage)545 void C4Def::LoadRankNames(C4Group &hGroup, const char* szLanguage)
546 {
547 // clear any previous
548 if (pRankNames) delete pRankNames; pRankNames = nullptr;
549 if (hGroup.FindEntry(C4CFN_RankNameFiles))
550 {
551 // create new
552 pRankNames = new C4RankSystem();
553 // load from group
554 if (!pRankNames->Load(hGroup, C4CFN_RankNames, 1000, szLanguage))
555 {
556 delete pRankNames; pRankNames = nullptr;
557 }
558 else
559 fRankNamesOwned = true;
560 }
561 }
562
LoadRankFaces(C4Group & hGroup)563 void C4Def::LoadRankFaces(C4Group &hGroup)
564 {
565 // clear any previous
566 if (pRankSymbols) delete pRankSymbols; pRankSymbols = nullptr;
567 // load new
568 if (hGroup.AccessEntry(C4CFN_RankFacesPNG))
569 {
570 pRankSymbols = new C4FacetSurface();
571 if (!pRankSymbols->GetFace().ReadPNG(hGroup, false)) { delete pRankSymbols; pRankSymbols = nullptr; }
572 }
573 // set size
574 if (pRankSymbols)
575 {
576 pRankSymbols->Set(&pRankSymbols->GetFace(), 0, 0, pRankSymbols->GetFace().Hgt, pRankSymbols->GetFace().Hgt);
577 int32_t Q; pRankSymbols->GetPhaseNum(iNumRankSymbols, Q);
578 if (!iNumRankSymbols) { delete pRankSymbols; pRankSymbols = nullptr; }
579 else
580 {
581 if (pRankNames)
582 {
583 // if extended rank names are defined, subtract those from the symbol count. The last symbols are used as overlay
584 iNumRankSymbols = std::max<int32_t>(1, iNumRankSymbols - pRankNames->GetExtendedRankNum());
585 }
586 fRankSymbolsOwned = true;
587 }
588 }
589 }
590
LoadSounds(C4Group & hGroup,C4SoundSystem * pSoundSystem)591 void C4Def::LoadSounds(C4Group &hGroup, C4SoundSystem* pSoundSystem)
592 {
593 if (pSoundSystem)
594 pSoundSystem->LoadEffects(hGroup, (id == C4ID::None) ? nullptr : id.ToString(), true);
595 }
596
Draw(C4Facet & cgo,bool fSelected,DWORD iColor,C4Object * pObj,int32_t iPhaseX,int32_t iPhaseY,C4DrawTransform * trans,const char * graphicsName)597 void C4Def::Draw(C4Facet &cgo, bool fSelected, DWORD iColor, C4Object *pObj, int32_t iPhaseX, int32_t iPhaseY, C4DrawTransform* trans, const char *graphicsName)
598 {
599 if(fSelected)
600 pDraw->DrawBoxDw(cgo.Surface, cgo.X, cgo.Y, cgo.X + cgo.Wdt - 1, cgo.Y + cgo.Hgt - 1, C4RGB(0xca, 0, 0));
601
602 C4DefGraphics* graphics = pObj ? pObj->GetGraphics() : &Graphics;
603 if (graphicsName)
604 {
605 C4DefGraphics *other = graphics->Get(graphicsName);
606 if (other) graphics = other;
607 }
608 graphics->Draw(cgo, iColor, pObj, iPhaseX, iPhaseY, trans);
609 }
610
GetValue(C4Object * pInBase,int32_t iBuyPlayer)611 int32_t C4Def::GetValue(C4Object *pInBase, int32_t iBuyPlayer)
612 {
613 C4Value r = Call(PSF_CalcDefValue, &C4AulParSet(pInBase, iBuyPlayer));
614 int32_t iValue = Value;
615 if (r != C4VNull)
616 iValue = r.getInt();
617 // do any adjustments based on where the item is bought
618 if (pInBase)
619 {
620 r = pInBase->Call(PSF_CalcBuyValue, &C4AulParSet(this, iValue));
621 if (r != C4VNull)
622 iValue = r.getInt();
623 }
624 return iValue;
625 }
626
Synchronize()627 void C4Def::Synchronize()
628 {
629 }
630
IncludeDefinition(C4Def * pIncludeDef)631 void C4Def::IncludeDefinition(C4Def *pIncludeDef)
632 {
633 // inherited rank infos and clonk names, if this definition doesn't have its own
634 if (!fClonkNamesOwned) pClonkNames = pIncludeDef->pClonkNames;
635 if (!fRankNamesOwned) pRankNames = pIncludeDef->pRankNames;
636 if (!fRankSymbolsOwned) { pRankSymbols = pIncludeDef->pRankSymbols; iNumRankSymbols = pIncludeDef->iNumRankSymbols; }
637 }
638
ResetIncludeDependencies()639 void C4Def::ResetIncludeDependencies()
640 {
641 // clear all pointers into foreign defs
642 if (!fClonkNamesOwned) pClonkNames = nullptr;
643 if (!fRankNamesOwned) pRankNames = nullptr;
644 if (!fRankSymbolsOwned) { pRankSymbols = nullptr; iNumRankSymbols = 0; }
645 }
646
GetActionByName(const char * actname)647 C4PropList *C4Def::GetActionByName(const char *actname)
648 {
649 if (!actname) return nullptr;
650 C4String * actname_str = Strings.RegString(actname);
651 actname_str->IncRef();
652 C4PropList *r = GetActionByName(actname_str);
653 actname_str->DecRef();
654 return r;
655 }
656
GetActionByName(C4String * actname)657 C4PropList *C4Def::GetActionByName(C4String *actname)
658 {
659 assert(actname);
660 // If we get the null string or ActIdle by name, return nullptr action
661 if (!actname || actname == &Strings.P[P_Idle]) return nullptr;
662 // otherwise, query actmap
663 C4Value ActMap; GetProperty(P_ActMap, &ActMap);
664 if (!ActMap.getPropList()) return nullptr;
665 C4Value Action; ActMap.getPropList()->GetPropertyByS(actname, &Action);
666 if (!Action.getPropList()) return nullptr;
667 return Action.getPropList();
668 }
669