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 /* Material definitions used by the landscape */
19
20 #include "C4Include.h"
21 #include "landscape/C4Material.h"
22
23 #include "c4group/C4Components.h"
24 #include "c4group/C4Group.h"
25 #include "editor/C4ToolsDlg.h" // For C4TLS_MatSky...
26 #include "game/C4Physics.h" // For GravAccel
27 #include "landscape/C4PXS.h"
28 #include "landscape/C4Texture.h"
29 #include "landscape/C4Landscape.h"
30 #include "lib/C4Random.h"
31 #include "platform/C4SoundSystem.h"
32 #include "script/C4Aul.h"
33 #include "script/C4Effect.h"
34
35
36 int32_t MVehic=MNone,MHalfVehic=MNone,MTunnel=MNone,MWater=MNone,MEarth=MNone;
37 BYTE MCVehic=0;
38 BYTE MCHalfVehic=0;
39 // -------------------------------------- C4MaterialReaction
40
41
42 struct ReactionFuncMapEntry { const char *szRFName; C4MaterialReactionFunc pFunc; };
43
44 const ReactionFuncMapEntry ReactionFuncMap[] =
45 {
46 { "Script", &C4MaterialMap::mrfScript },
47 { "Convert", &C4MaterialMap::mrfConvert},
48 { "Poof", &C4MaterialMap::mrfPoof },
49 { "Corrode", &C4MaterialMap::mrfCorrode },
50 { "Insert", &C4MaterialMap::mrfInsert },
51 { nullptr, &C4MaterialReaction::NoReaction }
52 };
53
54
CompileFunc(StdCompiler * pComp)55 void C4MaterialReaction::CompileFunc(StdCompiler *pComp)
56 {
57 if (pComp->isDeserializer()) pScriptFunc = nullptr;
58 // compile reaction func ptr
59 StdStrBuf sReactionFuncName;
60 int32_t i=0; while (ReactionFuncMap[i].szRFName && (ReactionFuncMap[i].pFunc != pFunc)) ++i;
61 sReactionFuncName = ReactionFuncMap[i].szRFName;
62 pComp->Value(mkNamingAdapt(mkParAdapt(sReactionFuncName, StdCompiler::RCT_IdtfAllowEmpty), "Type", StdCopyStrBuf() ));
63 i=0; while (ReactionFuncMap[i].szRFName && !SEqual(ReactionFuncMap[i].szRFName, sReactionFuncName.getData())) ++i;
64 pFunc = ReactionFuncMap[i].pFunc;
65 // compile the rest
66 pComp->Value(mkNamingAdapt(mkParAdapt(TargetSpec, StdCompiler::RCT_All), "TargetSpec", StdCopyStrBuf() ));
67 pComp->Value(mkNamingAdapt(mkParAdapt(ScriptFunc, StdCompiler::RCT_IdtfAllowEmpty), "ScriptFunc", StdCopyStrBuf() ));
68 pComp->Value(mkNamingAdapt(iExecMask, "ExecMask", ~0u ));
69 pComp->Value(mkNamingAdapt(fReverse, "Reverse", false ));
70 pComp->Value(mkNamingAdapt(fInverseSpec, "InverseSpec", false ));
71 pComp->Value(mkNamingAdapt(fInsertionCheck, "CheckSlide", true ));
72 pComp->Value(mkNamingAdapt(iDepth, "Depth", 0 ));
73 pComp->Value(mkNamingAdapt(mkParAdapt(sConvertMat, StdCompiler::RCT_IdtfAllowEmpty), "ConvertMat", StdCopyStrBuf() ));
74 pComp->Value(mkNamingAdapt(iCorrosionRate, "CorrosionRate", 100 ));
75 }
76
77
ResolveScriptFuncs(const char * szMatName)78 void C4MaterialReaction::ResolveScriptFuncs(const char *szMatName)
79 {
80 // get script func for script-defined behaviour
81 if (pFunc == &C4MaterialMap::mrfScript)
82 {
83 pScriptFunc = ::ScriptEngine.GetPropList()->GetFunc(this->ScriptFunc.getData());
84 if (!pScriptFunc)
85 DebugLogF(R"(Error getting function "%s" for Material reaction of "%s")", this->ScriptFunc.getData(), szMatName);
86 }
87 else
88 pScriptFunc = nullptr;
89 }
90
91 // -------------------------------------- C4MaterialCore
92
C4MaterialCore()93 C4MaterialCore::C4MaterialCore()
94 {
95 Clear();
96 }
97
Clear()98 void C4MaterialCore::Clear()
99 {
100 CustomReactionList.clear();
101 sTextureOverlay.Clear();
102 sPXSGfx.Clear();
103 sBlastShiftTo.Clear();
104 sInMatConvert.Clear();
105 sInMatConvertTo.Clear();
106 sBelowTempConvertTo.Clear();
107 sAboveTempConvertTo.Clear();
108 *Name='\0';
109 MapChunkType = C4M_Flat;
110 Density = 0;
111 Friction = 0;
112 DigFree = 0;
113 BlastFree = 0;
114 Dig2Object = C4ID::None;
115 Dig2ObjectRatio = 0;
116 Dig2ObjectCollect = 0;
117 Blast2Object = C4ID::None;
118 Blast2ObjectRatio = 0;
119 Blast2PXSRatio = 0;
120 Instable = 0;
121 MaxAirSpeed = 0;
122 MaxSlide = 0;
123 WindDrift = 0;
124 Inflammable = 0;
125 Incendiary = 0;
126 Extinguisher = 0;
127 Corrosive = 0;
128 Corrode = 0;
129 Soil = 0;
130 Placement = 0;
131 Light = 0;
132 OverlayType = 0;
133 PXSGfxRt.Default();
134 PXSGfxSize = 0;
135 InMatConvertDepth = 0;
136 BelowTempConvert = 0;
137 BelowTempConvertDir = 0;
138 AboveTempConvert = 0;
139 AboveTempConvertDir = 0;
140 TempConvStrength = 0;
141 MinHeightCount = 0;
142 SplashRate=10;
143 KeepSinglePixels=false;
144 AnimationSpeed = 20;
145 LightAngle = 255;
146 for (int i = 0; i < 3; i++) {
147 LightEmit[i] = 0;
148 LightSpot[i] = 16;
149 }
150 MinShapeOverlap = 25;
151 }
152
Default()153 void C4MaterialCore::Default()
154 {
155 Clear();
156 }
157
Load(C4Group & hGroup,const char * szEntryName)158 bool C4MaterialCore::Load(C4Group &hGroup,
159 const char *szEntryName)
160 {
161 StdStrBuf Source;
162 if (!hGroup.LoadEntryString(szEntryName,&Source))
163 return false;
164 StdStrBuf Name = hGroup.GetFullName() + DirSep + szEntryName;
165 if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Source, Name.getData()))
166 return false;
167 // adjust placement, if not specified
168 if (!Placement)
169 {
170 if (DensitySolid(Density))
171 {
172 Placement=30;
173 if (!DigFree) Placement+=20;
174 if (!BlastFree) Placement+=10;
175 }
176 else if (DensityLiquid(Density))
177 Placement=10;
178 else Placement=5;
179 }
180 return true;
181 }
182
CompileFunc(StdCompiler * pComp)183 void C4MaterialCore::CompileFunc(StdCompiler *pComp)
184 {
185 assert(pComp->hasNaming());
186 if (pComp->isDeserializer()) Clear();
187 pComp->Name("Material");
188 pComp->Value(mkNamingAdapt(toC4CStr(Name), "Name", ""));
189
190 const StdEnumEntry<C4MaterialCoreShape> Shapes[] =
191 {
192 { "Flat", C4M_Flat },
193 { "TopFlat", C4M_TopFlat },
194 { "Smooth", C4M_Smooth },
195 { "Rough", C4M_Rough },
196 { "Octagon", C4M_Octagon },
197 { "Smoother", C4M_Smoother },
198 { nullptr, C4M_Flat }
199 };
200 pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(MapChunkType, Shapes),
201 "Shape", C4M_Flat));
202 pComp->Value(mkNamingAdapt(Density, "Density", 0));
203 pComp->Value(mkNamingAdapt(Friction, "Friction", 0));
204 pComp->Value(mkNamingAdapt(DigFree, "DigFree", 0));
205 pComp->Value(mkNamingAdapt(BlastFree, "BlastFree", 0));
206 pComp->Value(mkNamingAdapt(Blast2Object, "Blast2Object", C4ID::None));
207 pComp->Value(mkNamingAdapt(Dig2Object, "Dig2Object", C4ID::None));
208 pComp->Value(mkNamingAdapt(Dig2ObjectRatio, "Dig2ObjectRatio", 0));
209 pComp->Value(mkNamingAdapt(Dig2ObjectCollect, "Dig2ObjectCollect", 0));
210 pComp->Value(mkNamingAdapt(Blast2ObjectRatio, "Blast2ObjectRatio", 0));
211 pComp->Value(mkNamingAdapt(Blast2PXSRatio, "Blast2PXSRatio", 0));
212 pComp->Value(mkNamingAdapt(Instable, "Instable", 0));
213 pComp->Value(mkNamingAdapt(MaxAirSpeed, "MaxAirSpeed", 0));
214 pComp->Value(mkNamingAdapt(MaxSlide, "MaxSlide", 0));
215 pComp->Value(mkNamingAdapt(WindDrift, "WindDrift", 0));
216 pComp->Value(mkNamingAdapt(Inflammable, "Inflammable", 0));
217 if (pComp->isDeserializer())
218 {
219 // The value used to have a wrong spelling ("Incindiary"). If there's
220 // no "Incendiary" value, use the wrong spelling instead
221 try
222 {
223 pComp->Value(mkNamingAdapt(Incendiary, "Incendiary"));
224 }
225 catch (StdCompiler::NotFoundException *ex)
226 {
227 delete ex;
228 pComp->Value(mkNamingAdapt(Incendiary, "Incindiary", 0));
229 }
230 }
231 else
232 {
233 // When serializing, write both spellings because some script might be
234 // calling GetMaterialVal with the wrong one
235 pComp->Value(mkNamingAdapt(Incendiary, "Incendiary"));
236 pComp->Value(mkNamingAdapt(Incendiary, "Incindiary"));
237 }
238 pComp->Value(mkNamingAdapt(Corrode, "Corrode", 0));
239 pComp->Value(mkNamingAdapt(Corrosive, "Corrosive", 0));
240 pComp->Value(mkNamingAdapt(Extinguisher, "Extinguisher", 0));
241 pComp->Value(mkNamingAdapt(Soil, "Soil", 0));
242 pComp->Value(mkNamingAdapt(Placement, "Placement", 0));
243 pComp->Value(mkNamingAdapt(Light, "Light", 0));
244 pComp->Value(mkNamingAdapt(mkParAdapt(sTextureOverlay, StdCompiler::RCT_IdtfAllowEmpty),
245 "TextureOverlay", ""));
246 pComp->Value(mkNamingAdapt(OverlayType, "OverlayType", 0));
247 pComp->Value(mkNamingAdapt(mkParAdapt(sPXSGfx, StdCompiler::RCT_IdtfAllowEmpty),
248 "PXSGfx", ""));
249 pComp->Value(mkNamingAdapt(PXSGfxRt, "PXSGfxRt", TargetRect0));
250 pComp->Value(mkNamingAdapt(PXSGfxSize, "PXSGfxSize", PXSGfxRt.Wdt));
251 pComp->Value(mkNamingAdapt(TempConvStrength, "TempConvStrength", 0));
252 pComp->Value(mkNamingAdapt(mkParAdapt(sBlastShiftTo, StdCompiler::RCT_IdtfAllowEmpty),
253 "BlastShiftTo", ""));
254 pComp->Value(mkNamingAdapt(mkParAdapt(sInMatConvert, StdCompiler::RCT_IdtfAllowEmpty),
255 "InMatConvert", ""));
256 pComp->Value(mkNamingAdapt(mkParAdapt(sInMatConvertTo, StdCompiler::RCT_IdtfAllowEmpty),
257 "InMatConvertTo", ""));
258 pComp->Value(mkNamingAdapt(InMatConvertDepth, "InMatConvertDepth", 0));
259 pComp->Value(mkNamingAdapt(AboveTempConvert, "AboveTempConvert", 0));
260 pComp->Value(mkNamingAdapt(AboveTempConvertDir, "AboveTempConvertDir", 0));
261 pComp->Value(mkNamingAdapt(mkParAdapt(sAboveTempConvertTo, StdCompiler::RCT_IdtfAllowEmpty),
262 "AboveTempConvertTo", ""));
263 pComp->Value(mkNamingAdapt(BelowTempConvert, "BelowTempConvert", 0));
264 pComp->Value(mkNamingAdapt(BelowTempConvertDir, "BelowTempConvertDir", 0));
265 pComp->Value(mkNamingAdapt(mkParAdapt(sBelowTempConvertTo, StdCompiler::RCT_IdtfAllowEmpty),
266 "BelowTempConvertTo", ""));
267 pComp->Value(mkNamingAdapt(MinHeightCount, "MinHeightCount", 0));
268 pComp->Value(mkNamingAdapt(SplashRate, "SplashRate", 10));
269 pComp->Value(mkNamingAdapt(KeepSinglePixels, "KeepSinglePixels", false));
270 pComp->Value(mkNamingAdapt(AnimationSpeed, "AnimationSpeed", 100));
271 pComp->Value(mkNamingAdapt(LightAngle, "LightAngle", 255));
272 pComp->Value(mkNamingAdapt(mkArrayAdaptDM(LightEmit, 0), "LightEmit"));
273 pComp->Value(mkNamingAdapt(mkArrayAdaptDM(LightSpot, 16),"LightSpot"));
274 pComp->Value(mkNamingAdapt(MinShapeOverlap, "MinShapeOverlap", 25));
275 pComp->NameEnd();
276 // material reactions
277 pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(CustomReactionList),
278 "Reaction", std::vector<C4MaterialReaction>()));
279 }
280
281
282 // -------------------------------------- C4Material
283
C4Material()284 C4Material::C4Material()
285 {
286 BlastShiftTo=0;
287 InMatConvertTo=MNone;
288 BelowTempConvertTo=0;
289 AboveTempConvertTo=0;
290 }
291
UpdateScriptPointers()292 void C4Material::UpdateScriptPointers()
293 {
294 for (auto & i : CustomReactionList)
295 i.ResolveScriptFuncs(Name);
296 }
297
298
299 // -------------------------------------- C4MaterialMap
300
301
C4MaterialMap()302 C4MaterialMap::C4MaterialMap() : DefReactConvert(&mrfConvert), DefReactPoof(&mrfPoof), DefReactCorrode(&mrfCorrode), DefReactIncinerate(&mrfIncinerate), DefReactInsert(&mrfInsert)
303 {
304 Default();
305 }
306
307
~C4MaterialMap()308 C4MaterialMap::~C4MaterialMap()
309 {
310 Clear();
311 }
312
Clear()313 void C4MaterialMap::Clear()
314 {
315 if (Map) delete [] Map; Map=nullptr; Num=0;
316 delete [] ppReactionMap; ppReactionMap = nullptr;
317 }
318
Load(C4Group & hGroup)319 int32_t C4MaterialMap::Load(C4Group &hGroup)
320 {
321 char entryname[256+1];
322
323 // Determine number of materials in files
324 int32_t mat_num=hGroup.EntryCount(C4CFN_MaterialFiles);
325
326 // Allocate new map
327 C4Material *pNewMap = new C4Material [mat_num + Num];
328 if (!pNewMap) return 0;
329
330 // Load material cores to map
331 hGroup.ResetSearch(); int32_t cnt=0;
332 while (hGroup.FindNextEntry(C4CFN_MaterialFiles,entryname))
333 {
334 if (cnt >= mat_num) {
335 Log("Internal Error: More materials loaded than expected. Make sure your material file names are unique (ignoring case).");
336 break;
337 }
338 // Load mat
339 if (!pNewMap[cnt].Load(hGroup,entryname))
340 { delete [] pNewMap; return 0; }
341 // A new material?
342 if (Get(pNewMap[cnt].Name) == MNone)
343 cnt++;
344 }
345
346 // Take over old materials.
347 for (int32_t i = 0; i < Num; i++)
348 {
349 pNewMap[cnt+i] = Map[i];
350 }
351 delete [] Map;
352 Map = pNewMap;
353
354 // set material number
355 Num+=cnt;
356
357 return cnt;
358 }
359
HasMaterials(C4Group & hGroup) const360 bool C4MaterialMap::HasMaterials(C4Group &hGroup) const
361 {
362 return !!hGroup.EntryCount(C4CFN_MaterialFiles);
363 }
364
Get(const char * szMaterial)365 int32_t C4MaterialMap::Get(const char *szMaterial)
366 {
367 int32_t cnt;
368 for (cnt=0; cnt<Num; cnt++)
369 if (SEqualNoCase(szMaterial,Map[cnt].Name))
370 return cnt;
371 return MNone;
372 }
373
374
CrossMapMaterials(const char * szEarthMaterial)375 bool C4MaterialMap::CrossMapMaterials(const char* szEarthMaterial) // Called after load
376 {
377 // Check material number
378 if (::MaterialMap.Num>C4MaxMaterial)
379 { LogFatal(LoadResStr("IDS_PRC_TOOMANYMATS")); return false; }
380 // build reaction function map
381 delete [] ppReactionMap;
382 typedef C4MaterialReaction * C4MaterialReactionPtr;
383 ppReactionMap = new C4MaterialReactionPtr[(Num+1)*(Num+1)];
384 for (int32_t iMatPXS=-1; iMatPXS<Num; iMatPXS++)
385 {
386 C4Material *pMatPXS = (iMatPXS+1) ? Map+iMatPXS : nullptr;
387 for (int32_t iMatLS=-1; iMatLS<Num; iMatLS++)
388 {
389 C4MaterialReaction *pReaction = nullptr;
390 C4Material *pMatLS = ( iMatLS+1) ? Map+ iMatLS : nullptr;
391 // natural stuff: material conversion here?
392 if (pMatPXS && pMatPXS->sInMatConvert.getLength() && SEqualNoCase(pMatPXS->sInMatConvert.getData(), pMatLS ? pMatLS->Name : C4TLS_MatSky))
393 pReaction = &DefReactConvert;
394 // non-sky reactions
395 else if (pMatPXS && pMatLS)
396 {
397 // incindiary vs extinguisher
398 if ((pMatPXS->Incendiary && pMatLS->Extinguisher) || (pMatPXS->Extinguisher && pMatLS->Incendiary))
399 pReaction = &DefReactPoof;
400 // incindiary vs inflammable
401 else if ((pMatPXS->Incendiary && pMatLS->Inflammable) || (pMatPXS->Inflammable && pMatLS->Incendiary))
402 pReaction = &DefReactIncinerate;
403 // corrosive vs corrode
404 else if (pMatPXS->Corrosive && pMatLS->Corrode)
405 pReaction = &DefReactCorrode;
406 // liquid hitting liquid or solid: Material insertion
407 else if (DensityLiquid(MatDensity(iMatPXS)) && DensitySemiSolid(MatDensity(iMatLS)))
408 pReaction = &DefReactInsert;
409 // solid hitting solid: Material insertion
410 else if (DensitySolid(MatDensity(iMatPXS)) && DensitySolid(MatDensity(iMatLS)))
411 pReaction = &DefReactInsert;
412 }
413 // assign the function; or nullptr for no reaction
414 SetMatReaction(iMatPXS, iMatLS, pReaction);
415 }
416 }
417 // reset max shape size
418 max_shape_width=max_shape_height=0;
419 // material-specific initialization
420 int32_t cnt;
421 for (cnt=0; cnt<Num; cnt++)
422 {
423 C4Material *pMat = Map+cnt;
424 const char *szTextureOverlay = nullptr;
425 // newgfx: init pattern
426 if (Map[cnt].sTextureOverlay.getLength())
427 if (::TextureMap.GetTexture(Map[cnt].sTextureOverlay.getLength()))
428 {
429 szTextureOverlay = Map[cnt].sTextureOverlay.getData();
430 // backwards compatibility: if a pattern was specified although the no-pattern flag was set, overwrite that flag
431 if (Map[cnt].OverlayType & C4MatOv_None)
432 {
433 DebugLogF("Error in overlay of material %s: Flag C4MatOv_None ignored because a custom overlay (%s) was specified!", Map[cnt].Name, szTextureOverlay);
434 Map[cnt].OverlayType &= ~C4MatOv_None;
435 }
436 }
437 // default to first texture in texture map
438 int iTexMapIx;
439 if (!szTextureOverlay && (iTexMapIx = ::TextureMap.GetIndex(Map[cnt].Name, nullptr, false)))
440 szTextureOverlay = TextureMap.GetEntry(iTexMapIx)->GetTextureName();
441 // default to smooth
442 if (!szTextureOverlay)
443 szTextureOverlay = "none";
444 // search/create entry in texmap
445 Map[cnt].DefaultMatTex = ::TextureMap.GetIndex(Map[cnt].Name, szTextureOverlay, true,
446 FormatString("DefaultMatTex of mat %s", Map[cnt].Name).getData());
447 // init PXS facet
448 C4Surface * sfcTexture;
449 C4Texture * Texture;
450 if (Map[cnt].sPXSGfx.getLength())
451 if ((Texture=::TextureMap.GetTexture(Map[cnt].sPXSGfx.getData())))
452 if ((sfcTexture=Texture->Surface32))
453 Map[cnt].PXSFace.Set(sfcTexture, Map[cnt].PXSGfxRt.x, Map[cnt].PXSGfxRt.y, Map[cnt].PXSGfxRt.Wdt, Map[cnt].PXSGfxRt.Hgt);
454 // evaluate reactions for that material
455 for (auto & iRCnt : pMat->CustomReactionList)
456 {
457 C4MaterialReaction *pReact = &iRCnt;
458 if (pReact->sConvertMat.getLength()) pReact->iConvertMat = Get(pReact->sConvertMat.getData()); else pReact->iConvertMat = -1;
459 // evaluate target spec
460 int32_t tmat;
461 if (MatValid(tmat=Get(pReact->TargetSpec.getData())))
462 {
463 // single material target
464 if (pReact->fInverseSpec)
465 for (int32_t cnt2=-1; cnt2<Num; cnt2++) {
466 if (cnt2!=tmat)
467 SetMatReaction(cnt, cnt2, pReact);
468 else
469 SetMatReaction(cnt, tmat, pReact);
470 }
471 }
472 else if (SEqualNoCase(pReact->TargetSpec.getData(), "All"))
473 {
474 // add to all materials, including sky
475 if (!pReact->fInverseSpec) for (int32_t cnt2=-1; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact);
476 }
477 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Solid"))
478 {
479 // add to all solid materials
480 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
481 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
482 }
483 else if (SEqualNoCase(pReact->TargetSpec.getData(), "SemiSolid"))
484 {
485 // add to all semisolid materials
486 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
487 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySemiSolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
488 }
489 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Background"))
490 {
491 // add to all BG materials, including sky
492 if (!pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
493 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Density != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
494 }
495 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Sky"))
496 {
497 // add to sky
498 if (!pReact->fInverseSpec)
499 SetMatReaction(cnt, -1, pReact);
500 else
501 for (int32_t cnt2=0; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact);
502 }
503 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Incendiary") || SEqualNoCase(pReact->TargetSpec.getData(), "Incindiary"))
504 {
505 // add to all incendiary materials
506 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
507 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Incendiary == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
508 }
509 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Extinguisher"))
510 {
511 // add to all incendiary materials
512 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
513 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Extinguisher == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
514 }
515 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Inflammable"))
516 {
517 // add to all incendiary materials
518 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
519 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Inflammable == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
520 }
521 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrosive"))
522 {
523 // add to all incendiary materials
524 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
525 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrosive == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
526 }
527 else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrode"))
528 {
529 // add to all incendiary materials
530 if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
531 for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrode == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
532 }
533 }
534 }
535 // second loop (DefaultMatTex is needed by GetIndexMatTex)
536 for (cnt=0; cnt<Num; cnt++)
537 {
538 if (Map[cnt].sBlastShiftTo.getLength())
539 Map[cnt].BlastShiftTo=::TextureMap.GetIndexMatTex(Map[cnt].sBlastShiftTo.getData(), nullptr, true, FormatString("BlastShiftTo of mat %s", Map[cnt].Name).getData());
540 if (Map[cnt].sInMatConvertTo.getLength())
541 Map[cnt].InMatConvertTo=Get(Map[cnt].sInMatConvertTo.getData());
542 if (Map[cnt].sBelowTempConvertTo.getLength())
543 Map[cnt].BelowTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sBelowTempConvertTo.getData(), nullptr, true, FormatString("BelowTempConvertTo of mat %s", Map[cnt].Name).getData());
544 if (Map[cnt].sAboveTempConvertTo.getLength())
545 Map[cnt].AboveTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sAboveTempConvertTo.getData(), nullptr, true, FormatString("AboveTempConvertTo of mat %s", Map[cnt].Name).getData());
546 }
547
548 // Get hardcoded system material indices
549 const C4TexMapEntry* earth_entry = ::TextureMap.GetEntry(::TextureMap.GetIndexMatTex(szEarthMaterial));
550 if(!earth_entry)
551 { LogFatal(FormatString(R"(Earth material "%s" not found!)", szEarthMaterial).getData()); return false; }
552
553 MVehic = Get("Vehicle"); MCVehic = Mat2PixColDefault(MVehic);
554 MHalfVehic = Get("HalfVehicle"); MCHalfVehic = Mat2PixColDefault(MHalfVehic);
555 MTunnel = Get("Tunnel");
556 MWater = Get("Water");
557 MEarth = Get(earth_entry->GetMaterialName());
558
559 if ((MVehic==MNone) || (MTunnel==MNone))
560 { LogFatal(LoadResStr("IDS_PRC_NOSYSMATS")); return false; }
561
562 return true;
563 }
564
565
SetMatReaction(int32_t iPXSMat,int32_t iLSMat,C4MaterialReaction * pReact)566 void C4MaterialMap::SetMatReaction(int32_t iPXSMat, int32_t iLSMat, C4MaterialReaction *pReact)
567 {
568 // evaluate reaction swap
569 if (pReact && pReact->fReverse) std::swap(iPXSMat, iLSMat);
570 // set it
571 ppReactionMap[(iLSMat+1)*(Num+1) + iPXSMat+1] = pReact;
572 }
573
SaveEnumeration(C4Group & hGroup)574 bool C4MaterialMap::SaveEnumeration(C4Group &hGroup)
575 {
576 char *mapbuf = new char [1000];
577 mapbuf[0]=0;
578 SAppend("[Enumeration]",mapbuf); SAppend(LineFeed,mapbuf);
579 for (int32_t cnt=0; cnt<Num; cnt++)
580 {
581 SAppend(Map[cnt].Name,mapbuf);
582 SAppend(LineFeed,mapbuf);
583 }
584 return hGroup.Add(C4CFN_MatMap,mapbuf,SLen(mapbuf),false,true);
585 }
586
LoadEnumeration(C4Group & hGroup)587 bool C4MaterialMap::LoadEnumeration(C4Group &hGroup)
588 {
589 // Load enumeration map (from savegame), succeed if not present
590 StdStrBuf mapbuf;
591 if (!hGroup.LoadEntryString(C4CFN_MatMap, &mapbuf)) return true;
592
593 // Sort material array by enumeration map, fail if some missing
594 const char *csearch;
595 char cmatname[C4M_MaxName+1];
596 int32_t cmat=0;
597 if (!(csearch = SSearch(mapbuf.getData(),"[Enumeration]"))) { return false; }
598 csearch=SAdvanceSpace(csearch);
599 while (IsIdentifier(*csearch))
600 {
601 SCopyIdentifier(csearch,cmatname,C4M_MaxName);
602 if (!SortEnumeration(cmat,cmatname))
603 {
604 // Output error message!
605 return false;
606 }
607 cmat++;
608 csearch+=SLen(cmatname);
609 csearch=SAdvanceSpace(csearch);
610 }
611
612 return true;
613 }
614
SortEnumeration(int32_t iMat,const char * szMatName)615 bool C4MaterialMap::SortEnumeration(int32_t iMat, const char *szMatName)
616 {
617
618 // Not enough materials loaded
619 if (iMat>=Num) return false;
620
621 // Find requested mat
622 int32_t cmat;
623 for (cmat=iMat; cmat<Num; cmat++)
624 if (SEqual(szMatName,Map[cmat].Name))
625 break;
626 // Not found
627 if (cmat>=Num) return false;
628
629 // already the same?
630 if (cmat == iMat) return true;
631
632 // Move requested mat to indexed position
633 C4Material mswap;
634 mswap = Map[iMat];
635 Map[iMat] = Map[cmat];
636 Map[cmat] = mswap;
637
638 return true;
639 }
640
Default()641 void C4MaterialMap::Default()
642 {
643 Num=0;
644 Map=nullptr;
645 ppReactionMap=nullptr;
646 max_shape_width=max_shape_height=0;
647 }
648
GetReaction(int32_t iPXSMat,int32_t iLandscapeMat)649 C4MaterialReaction *C4MaterialMap::GetReaction(int32_t iPXSMat, int32_t iLandscapeMat)
650 {
651 // safety
652 if (!ppReactionMap) return nullptr;
653 if (!Inside<int32_t>(iPXSMat, -1, Num-1)) return nullptr;
654 if (!Inside<int32_t>(iLandscapeMat, -1, Num-1)) return nullptr;
655 // values OK; get func!
656 return GetReactionUnsafe(iPXSMat, iLandscapeMat);
657 }
658
Smoke(int32_t tx,int32_t ty,int32_t level)659 static void Smoke(int32_t tx, int32_t ty, int32_t level)
660 {
661 // Use scripted function (global func Smoke) to create smoke
662 // Caution: This makes engine internal smoking a synced call.
663 C4AulParSet pars(tx, ty, level);
664 ::ScriptEngine.GetPropList()->Call(P_Smoke, &pars);
665 }
666
mrfInsertCheck(int32_t & iX,int32_t & iY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,bool * pfPosChanged,bool no_slide=false)667 bool mrfInsertCheck(int32_t &iX, int32_t &iY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, bool *pfPosChanged, bool no_slide = false)
668 {
669 // always manipulating pos/speed here
670 if (pfPosChanged) *pfPosChanged = true;
671
672 // Move up by up to 3px to account for moving SolidMasks, other material insertions, etc.
673 int32_t mdens = std::min(::MaterialMap.Map[iPxsMat].Density, C4M_Solid);
674 int32_t max_upwards = 3;
675 bool was_pushed_upwards = false;
676 while (max_upwards-- && (::Landscape.GetDensity(iX, iY) >= mdens))
677 {
678 --iY;
679 was_pushed_upwards = true;
680 }
681
682 // Rough contact? May splash
683 if (fYDir > itofix(1))
684 if (::MaterialMap.Map[iPxsMat].SplashRate && !Random(::MaterialMap.Map[iPxsMat].SplashRate))
685 {
686 fYDir = -fYDir/8;
687 fXDir = fXDir/8 + C4REAL100(Random(200) - 100);
688 if (fYDir) return false;
689 }
690
691 // Contact: Stop
692 fYDir = -GravAccel;
693
694 // Incendiary mats smoke on contact even before doing their slide
695 if (::MaterialMap.Map[iPxsMat].Incendiary)
696 if (!Random(25))
697 {
698 Smoke(iX, iY, 4 + Random(3));
699 }
700
701 // Move by mat path/slide
702 int32_t iSlideX = iX, iSlideY = iY;
703
704 if (!no_slide && ::Landscape.FindMatSlide(iSlideX,iSlideY,Sign(GravAccel),mdens,::MaterialMap.Map[iPxsMat].MaxSlide))
705 {
706 // Sliding on equal material: Move directly to optimize insertion of rain onto lakes
707 // Also move directly when shifted upwards to ensure movement on permamently moving SolidMask
708 if (iPxsMat == iLsMat || was_pushed_upwards)
709 {
710 iX = iSlideX;
711 iY = iSlideY;
712 fXDir = 0;
713 if (was_pushed_upwards)
714 {
715 // When pushed upwards and slide was found into a target position, insert directly to allow additional PXS at same location to solidify in next position in same frame
716 if (::Landscape.GetDensity(iX, iY + Sign(GravAccel)) >= mdens)
717 {
718 return true;
719 }
720 }
721 // Continue existing (and fall down next frame)
722 return false;
723 }
724 // Otherwise, just move using xdir/ydir for nice visuals when rain is moving over landscape
725 // Accelerate into the direction
726 fXDir = (fXDir * 10 + Sign(iSlideX - iX)) / 11 + C4REAL10(Random(5)-2);
727 // Slide target in range? Move there directly.
728 if (Abs(iX - iSlideX) <= Abs(fixtoi(fXDir)))
729 {
730 iX = iSlideX;
731 iY = iSlideY;
732 if (fYDir <= 0) fXDir = 0;
733 }
734 // Continue existance
735 return false;
736 }
737 // insertion OK
738 return true;
739 }
740
mrfUserCheck(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)741 bool mrfUserCheck(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
742 {
743 // check execution mask
744 if ((1<<evEvent) & ~pReaction->iExecMask) return false;
745 // do splash/slide check, if desired
746 if (pReaction->fInsertionCheck && evEvent == meePXSMove)
747 if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
748 return false;
749 // checks OK; reaction may be applied
750 return true;
751 }
752
mrfConvert(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)753 bool C4MaterialMap::mrfConvert(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
754 {
755 if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
756 switch (evEvent)
757 {
758 case meePXSMove: // PXS movement
759 // for hardcoded stuff: only InMatConvert is Snow in Water, which does not have any collision proc
760 if (!pReaction->fUserDefined) break;
761 // user-defined conversions may also convert upon hitting materials
762
763 case meePXSPos: // PXS check before movement
764 {
765 // Check depth
766 int32_t iDepth = pReaction->fUserDefined ? pReaction->iDepth : ::MaterialMap.Map[iPxsMat].InMatConvertDepth;
767 if (!iDepth || GBackMat(iX, iY - iDepth) == iLsMat)
768 {
769 // Convert
770 iPxsMat = pReaction->fUserDefined ? pReaction->iConvertMat : ::MaterialMap.Map[iPxsMat].InMatConvertTo;
771 if (!MatValid(iPxsMat))
772 // Convert failure (target mat not be loaded, or target may be C4TLS_MatSky): Kill Pix
773 return true;
774 // stop movement after conversion
775 fXDir = fYDir = 0;
776 if (pfPosChanged) *pfPosChanged = true;
777 }
778 }
779 break;
780
781 case meeMassMove: // MassMover-movement
782 // Conversion-transfer to PXS
783 ::PXS.Create(iPxsMat,itofix(iX),itofix(iY));
784 return true;
785 }
786 // not handled
787 return false;
788 }
789
mrfPoof(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)790 bool C4MaterialMap::mrfPoof(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
791 {
792 if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
793 switch (evEvent)
794 {
795 case meeMassMove: // MassMover-movement
796 case meePXSPos: // PXS check before movement: Kill both landscape and PXS mat
797 ::Landscape.ExtractMaterial(iLSPosX,iLSPosY,false);
798 if (!Random(3)) Smoke(iX,iY,3);
799 if (!Random(3)) StartSoundEffectAt("Liquids::Pshshsh", iX, iY);
800 return true;
801
802 case meePXSMove: // PXS movement
803 // incindiary/extinguisher/corrosives are always same density proc; so do insertion check first
804 // Do not allow sliding though (e.g. water on lava).
805 if (!pReaction->fUserDefined)
806 if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged, true))
807 // either splash or slide prevented interaction
808 return false;
809 // Always kill both landscape and PXS mat
810 ::Landscape.ExtractMaterial(iLSPosX,iLSPosY,false);
811 if (!Random(3)) Smoke(iX,iY,3);
812 if (!Random(3)) StartSoundEffectAt("Liquids::Pshshsh", iX, iY);
813 return true;
814 }
815 // not handled
816 return false;
817 }
818
mrfCorrode(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)819 bool C4MaterialMap::mrfCorrode(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
820 {
821 if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
822 switch (evEvent)
823 {
824 case meePXSPos: // PXS check before movement
825 // No corrosion - it would make acid incredibly effective
826 break;
827 case meeMassMove: // MassMover-movement
828 {
829 // evaluate corrosion percentage
830 bool fDoCorrode; int d100 = Random(100);
831 if (pReaction->fUserDefined)
832 fDoCorrode = (d100 < pReaction->iCorrosionRate);
833 else
834 fDoCorrode = (d100 < ::MaterialMap.Map[iPxsMat].Corrosive) && (d100 < ::MaterialMap.Map[iLsMat].Corrode);
835 if (fDoCorrode)
836 {
837 ::Landscape.ClearPix(iLSPosX,iLSPosY);
838 //::Landscape.CheckInstabilityRange(iLSPosX,iLSPosY); - more correct, but makes acid too effective as well
839 if (!Random(5))
840 {
841 Smoke(iX, iY, 3 + Random(3));
842 }
843 if (!Random(20)) StartSoundEffectAt("Liquids::Corrode", iX, iY);
844 return true;
845 }
846 }
847 break;
848
849 case meePXSMove: // PXS movement
850 {
851 // corrodes to corrosives are always same density proc; so do insertion check first
852 if (!pReaction->fUserDefined)
853 if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
854 // either splash or slide prevented interaction
855 return false;
856 // evaluate corrosion percentage
857 bool fDoCorrode; int d100 = Random(100);
858 if (pReaction->fUserDefined)
859 fDoCorrode = (d100 < pReaction->iCorrosionRate);
860 else
861 fDoCorrode = (d100 < ::MaterialMap.Map[iPxsMat].Corrosive) && (d100 < ::MaterialMap.Map[iLsMat].Corrode);
862 if (fDoCorrode)
863 {
864 ::Landscape.ClearPix(iLSPosX,iLSPosY);
865 ::Landscape.CheckInstabilityRange(iLSPosX,iLSPosY);
866 if (!Random(5))
867 {
868 Smoke(iX,iY,3+Random(3));
869 }
870 if (!Random(20)) StartSoundEffectAt("Liquids::Corrode", iX, iY);
871 return true;
872 }
873 // Else: dead. Insert material here
874 ::Landscape.InsertMaterial(iPxsMat,&iX,&iY);
875 return true;
876 }
877 }
878 // not handled
879 return false;
880 }
881
mrfIncinerate(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)882 bool C4MaterialMap::mrfIncinerate(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
883 {
884 // not available as user reaction
885 assert(!pReaction->fUserDefined);
886 switch (evEvent)
887 {
888 case meeMassMove: // MassMover-movement
889 case meePXSPos: // PXS check before movement
890 if (::Landscape.Incinerate(iX, iY, NO_OWNER)) return true;
891 break;
892
893 case meePXSMove: // PXS movement
894 // incinerate to inflammables are always same density proc; so do insertion check first
895 if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
896 // either splash or slide prevented interaction
897 return false;
898 // evaluate inflammation (should always succeed)
899 if (::Landscape.Incinerate(iX, iY, NO_OWNER)) return true;
900 // Else: dead. Insert material here
901 ::Landscape.InsertMaterial(iPxsMat,&iX,&iY);
902 return true;
903 }
904 // not handled
905 return false;
906 }
907
mrfInsert(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)908 bool C4MaterialMap::mrfInsert(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
909 {
910 if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
911 switch (evEvent)
912 {
913 case meePXSPos: // PXS check before movement
914 break;
915
916 case meePXSMove: // PXS movement
917 {
918 // check for bounce/slide
919 if (!pReaction->fUserDefined)
920 if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
921 // continue existing
922 return false;
923 // Else: dead. Insert material here
924 ::Landscape.InsertMaterial(iPxsMat,&iX,&iY);
925 return true;
926 }
927
928 case meeMassMove: // MassMover-movement
929 break;
930 }
931 // not handled
932 return false;
933 }
934
mrfScript(C4MaterialReaction * pReaction,int32_t & iX,int32_t & iY,int32_t iLSPosX,int32_t iLSPosY,C4Real & fXDir,C4Real & fYDir,int32_t & iPxsMat,int32_t iLsMat,MaterialInteractionEvent evEvent,bool * pfPosChanged)935 bool C4MaterialMap::mrfScript(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, C4Real &fXDir, C4Real &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
936 {
937 // do generic checks for user-defined reactions
938 if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged))
939 return false;
940
941 // check script func
942 if (!pReaction->pScriptFunc) return false;
943 // OK - let's call it!
944 // 0 1 2 3 4 5 6 7 8
945 int32_t iXDir1, iYDir1, iXDir2, iYDir2;
946 C4AulParSet pars(iX, iY, iLSPosX, iLSPosY, iXDir1 = fixtoi(fXDir, 100), iYDir1 = fixtoi(fYDir, 100), iPxsMat, iLsMat, int(evEvent));
947 if (!!pReaction->pScriptFunc->Exec(nullptr, &pars, false))
948 {
949 // PXS shall be killed!
950 return true;
951 }
952 // PXS shall exist further: write back parameters
953 iPxsMat = pars[6].getInt();
954 int32_t iX2 = pars[0].getInt(), iY2 = pars[1].getInt();
955 iXDir2 = pars[4].getInt(); iYDir2 = pars[5].getInt();
956 if (iX!=iX2 || iY!=iY2 || iXDir1!=iXDir2 || iYDir1!=iYDir2)
957 {
958 // changes to pos/speed detected
959 if (pfPosChanged) *pfPosChanged = true;
960 iX=iX2; iY=iY2;
961 fXDir = C4REAL100(iXDir2);
962 fYDir = C4REAL100(iYDir2);
963 }
964 // OK; done
965 return false;
966 }
967
UpdateScriptPointers()968 void C4MaterialMap::UpdateScriptPointers()
969 {
970 // update in all materials
971 for (int32_t i=0; i<Num; ++i) Map[i].UpdateScriptPointers();
972 }
973
974 C4MaterialMap MaterialMap;
975