/src/game/landscape/C4Material.cpp

https://bitbucket.org/randrian/openclonk2 · C++ · 890 lines · 674 code · 83 blank · 133 comment · 222 complexity · 3d55cb5e28e579a7e4f85baf3e6246a9 MD5 · raw file

  1. /*
  2. * OpenClonk, http://www.openclonk.org
  3. *
  4. * Copyright (c) 1998-2000, 2007 Matthes Bender
  5. * Copyright (c) 2002, 2005-2007 Sven Eberhardt
  6. * Copyright (c) 2006-2007 Peter Wortmann
  7. * Copyright (c) 2006-2007 G?nther Brammer
  8. * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
  9. *
  10. * Portions might be copyrighted by other authors who have contributed
  11. * to OpenClonk.
  12. *
  13. * Permission to use, copy, modify, and/or distribute this software for any
  14. * purpose with or without fee is hereby granted, provided that the above
  15. * copyright notice and this permission notice appear in all copies.
  16. * See isc_license.txt for full license and disclaimer.
  17. *
  18. * "Clonk" is a registered trademark of Matthes Bender.
  19. * See clonk_trademark_license.txt for full license.
  20. */
  21. /* Material definitions used by the landscape */
  22. #include <C4Include.h>
  23. #include <C4Material.h>
  24. #include <C4Components.h>
  25. #ifndef BIG_C4INCLUDE
  26. #include <C4Group.h>
  27. #include <C4PXS.h>
  28. #include <C4Random.h>
  29. #include <C4ToolsDlg.h> // For C4TLS_MatSky...
  30. #include <C4Texture.h>
  31. #include <C4Aul.h>
  32. #include <C4Landscape.h>
  33. #include <C4SoundSystem.h>
  34. #include <C4Effects.h>
  35. #include <C4Game.h>
  36. #include <C4Log.h>
  37. #include <C4Physics.h> // For GravAccel
  38. #endif
  39. int32_t MVehic=MNone,MTunnel=MNone,MWater=MNone,MSnow=MNone,MEarth=MNone,MGranite=MNone,MFlyAshes=MNone;
  40. BYTE MCVehic=0;
  41. // -------------------------------------- C4MaterialReaction
  42. struct ReactionFuncMapEntry { const char *szRFName; C4MaterialReactionFunc pFunc; };
  43. const ReactionFuncMapEntry ReactionFuncMap[] = {
  44. { "Script", &C4MaterialMap::mrfScript },
  45. { "Convert", &C4MaterialMap::mrfConvert},
  46. { "Poof", &C4MaterialMap::mrfPoof },
  47. { "Corrode", &C4MaterialMap::mrfCorrode },
  48. { "Insert", &C4MaterialMap::mrfInsert },
  49. { NULL, &C4MaterialReaction::NoReaction } };
  50. void C4MaterialReaction::CompileFunc(StdCompiler *pComp)
  51. {
  52. if (pComp->isCompiler()) pScriptFunc = NULL;
  53. // compile reaction func ptr
  54. StdStrBuf sReactionFuncName;
  55. int32_t i=0; while (ReactionFuncMap[i].szRFName && (ReactionFuncMap[i].pFunc != pFunc)) ++i;
  56. sReactionFuncName = ReactionFuncMap[i].szRFName;
  57. pComp->Value(mkNamingAdapt(sReactionFuncName, "Type", StdStrBuf() ));
  58. i=0; while (ReactionFuncMap[i].szRFName && !SEqual(ReactionFuncMap[i].szRFName, sReactionFuncName.getData())) ++i;
  59. pFunc = ReactionFuncMap[i].pFunc;
  60. // compile the rest
  61. pComp->Value(mkNamingAdapt(TargetSpec, "TargetSpec", StdCopyStrBuf() ));
  62. pComp->Value(mkNamingAdapt(ScriptFunc, "ScriptFunc", StdCopyStrBuf() ));
  63. pComp->Value(mkNamingAdapt(iExecMask, "ExecMask", ~0u ));
  64. pComp->Value(mkNamingAdapt(fReverse, "Reverse", false ));
  65. pComp->Value(mkNamingAdapt(fInverseSpec, "InverseSpec", false ));
  66. pComp->Value(mkNamingAdapt(fInsertionCheck, "CheckSlide", true ));
  67. pComp->Value(mkNamingAdapt(iDepth, "Depth", 0 ));
  68. pComp->Value(mkNamingAdapt(sConvertMat, "ConvertMat", StdCopyStrBuf() ));
  69. pComp->Value(mkNamingAdapt(iCorrosionRate, "CorrosionRate", 100 ));
  70. }
  71. void C4MaterialReaction::ResolveScriptFuncs(const char *szMatName)
  72. {
  73. // get script func for script-defined behaviour
  74. if (pFunc == &C4MaterialMap::mrfScript)
  75. pScriptFunc = ::ScriptEngine.GetSFuncWarn(this->ScriptFunc.getData(), AA_PROTECTED, FormatString("Material reaction of \"%s\"", szMatName).getData());
  76. else
  77. pScriptFunc = NULL;
  78. }
  79. // -------------------------------------- C4MaterialCore
  80. C4MaterialCore::C4MaterialCore()
  81. {
  82. Clear();
  83. }
  84. void C4MaterialCore::Clear()
  85. {
  86. CustomReactionList.clear();
  87. sTextureOverlay.Clear();
  88. sPXSGfx.Clear();
  89. sBlastShiftTo.Clear();
  90. sInMatConvert.Clear();
  91. sInMatConvertTo.Clear();
  92. sBelowTempConvertTo.Clear();
  93. sAboveTempConvertTo.Clear();
  94. *Name='\0';
  95. MapChunkType = 0;
  96. Density = 0;
  97. Friction = 0;
  98. DigFree = 0;
  99. BlastFree = 0;
  100. Dig2Object = 0;
  101. Dig2ObjectRatio = 0;
  102. Dig2ObjectOnRequestOnly = 0;
  103. Blast2Object = 0;
  104. Blast2ObjectRatio = 0;
  105. Blast2PXSRatio = 0;
  106. Instable = 0;
  107. MaxAirSpeed = 0;
  108. MaxSlide = 0;
  109. WindDrift = 0;
  110. Inflammable = 0;
  111. Incindiary = 0;
  112. Extinguisher = 0;
  113. Corrosive = 0;
  114. Corrode = 0;
  115. Soil = 0;
  116. Placement = 0;
  117. OverlayType = 0;
  118. PXSGfxRt.Default();
  119. PXSGfxSize = 0;
  120. InMatConvertDepth = 0;
  121. BelowTempConvert = 0;
  122. BelowTempConvertDir = 0;
  123. AboveTempConvert = 0;
  124. AboveTempConvertDir = 0;
  125. ColorAnimation = 0;
  126. TempConvStrength = 0;
  127. MinHeightCount = 0;
  128. SplashRate=10;
  129. }
  130. void C4MaterialCore::Default()
  131. {
  132. Clear();
  133. }
  134. bool C4MaterialCore::Load(C4Group &hGroup,
  135. const char *szEntryName)
  136. {
  137. StdStrBuf Source;
  138. if (!hGroup.LoadEntryString(szEntryName,Source))
  139. return false;
  140. StdStrBuf Name = hGroup.GetFullName() + DirSep + szEntryName;
  141. if(!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Source, Name.getData()))
  142. return false;
  143. // adjust placement, if not specified
  144. if (!Placement)
  145. {
  146. if (DensitySolid(Density))
  147. {
  148. Placement=30;
  149. if (!DigFree) Placement+=20;
  150. if (!BlastFree) Placement+=10;
  151. if (!Dig2ObjectOnRequestOnly) Placement+=10;
  152. }
  153. else if (DensityLiquid(Density))
  154. Placement=10;
  155. else Placement=5;
  156. }
  157. return true;
  158. }
  159. void C4MaterialCore::CompileFunc(StdCompiler *pComp)
  160. {
  161. if (pComp->isCompiler()) Clear();
  162. pComp->Name("Material");
  163. pComp->Value(mkNamingAdapt(toC4CStr(Name), "Name", "" ));
  164. pComp->Value(mkNamingAdapt(ColorAnimation, "ColorAnimation", 0 ));
  165. pComp->Value(mkNamingAdapt(MapChunkType, "Shape", 0 ));
  166. pComp->Value(mkNamingAdapt(Density, "Density", 0 ));
  167. pComp->Value(mkNamingAdapt(Friction, "Friction", 0 ));
  168. pComp->Value(mkNamingAdapt(DigFree, "DigFree", 0 ));
  169. pComp->Value(mkNamingAdapt(BlastFree, "BlastFree", 0 ));
  170. pComp->Value(mkNamingAdapt(mkC4IDAdapt(Blast2Object),"Blast2Object", 0 ));
  171. pComp->Value(mkNamingAdapt(mkC4IDAdapt(Dig2Object), "Dig2Object", 0 ));
  172. pComp->Value(mkNamingAdapt(Dig2ObjectRatio, "Dig2ObjectRatio", 0 ));
  173. pComp->Value(mkNamingAdapt(Dig2ObjectOnRequestOnly, "Dig2ObjectRequest", 0 ));
  174. pComp->Value(mkNamingAdapt(Blast2ObjectRatio, "Blast2ObjectRatio", 0 ));
  175. pComp->Value(mkNamingAdapt(Blast2PXSRatio, "Blast2PXSRatio", 0 ));
  176. pComp->Value(mkNamingAdapt(Instable, "Instable", 0 ));
  177. pComp->Value(mkNamingAdapt(MaxAirSpeed, "MaxAirSpeed", 0 ));
  178. pComp->Value(mkNamingAdapt(MaxSlide, "MaxSlide", 0 ));
  179. pComp->Value(mkNamingAdapt(WindDrift, "WindDrift", 0 ));
  180. pComp->Value(mkNamingAdapt(Inflammable, "Inflammable", 0 ));
  181. pComp->Value(mkNamingAdapt(Incindiary, "Incindiary", 0 ));
  182. pComp->Value(mkNamingAdapt(Corrode, "Corrode", 0 ));
  183. pComp->Value(mkNamingAdapt(Corrosive, "Corrosive", 0 ));
  184. pComp->Value(mkNamingAdapt(Extinguisher, "Extinguisher", 0 ));
  185. pComp->Value(mkNamingAdapt(Soil, "Soil", 0 ));
  186. pComp->Value(mkNamingAdapt(Placement, "Placement", 0 ));
  187. pComp->Value(mkNamingAdapt(mkParAdapt(sTextureOverlay, StdCompiler::RCT_IdtfAllowEmpty),"TextureOverlay", "" ));
  188. pComp->Value(mkNamingAdapt(OverlayType, "OverlayType", 0 ));
  189. pComp->Value(mkNamingAdapt(mkParAdapt(sPXSGfx, StdCompiler::RCT_IdtfAllowEmpty), "PXSGfx", "" ));
  190. pComp->Value(mkNamingAdapt(PXSGfxRt, "PXSGfxRt", TargetRect0 ));
  191. pComp->Value(mkNamingAdapt(PXSGfxSize, "PXSGfxSize", PXSGfxRt.Wdt ));
  192. pComp->Value(mkNamingAdapt(TempConvStrength, "TempConvStrength", 0 ));
  193. pComp->Value(mkNamingAdapt(mkParAdapt(sBlastShiftTo, StdCompiler::RCT_IdtfAllowEmpty),"BlastShiftTo", "" ));
  194. pComp->Value(mkNamingAdapt(mkParAdapt(sInMatConvert, StdCompiler::RCT_IdtfAllowEmpty),"InMatConvert", "" ));
  195. pComp->Value(mkNamingAdapt(mkParAdapt(sInMatConvertTo, StdCompiler::RCT_IdtfAllowEmpty),"InMatConvertTo", "" ));
  196. pComp->Value(mkNamingAdapt(InMatConvertDepth, "InMatConvertDepth", 0 ));
  197. pComp->Value(mkNamingAdapt(AboveTempConvert, "AboveTempConvert", 0 ));
  198. pComp->Value(mkNamingAdapt(AboveTempConvertDir, "AboveTempConvertDir",0 ));
  199. pComp->Value(mkNamingAdapt(mkParAdapt(sAboveTempConvertTo, StdCompiler::RCT_IdtfAllowEmpty),"AboveTempConvertTo", "" ));
  200. pComp->Value(mkNamingAdapt(BelowTempConvert, "BelowTempConvert", 0 ));
  201. pComp->Value(mkNamingAdapt(BelowTempConvertDir, "BelowTempConvertDir",0 ));
  202. pComp->Value(mkNamingAdapt(mkParAdapt(sBelowTempConvertTo, StdCompiler::RCT_IdtfAllowEmpty),"BelowTempConvertTo", "" ));
  203. pComp->Value(mkNamingAdapt(MinHeightCount, "MinHeightCount", 0 ));
  204. pComp->Value(mkNamingAdapt(SplashRate, "SplashRate", 10 ));
  205. pComp->NameEnd();
  206. // material reactions
  207. pComp->Value(mkNamingAdapt(
  208. mkSTLContainerAdapt(CustomReactionList),
  209. "Reaction", std::vector<C4MaterialReaction>()));
  210. }
  211. // -------------------------------------- C4Material
  212. C4Material::C4Material()
  213. {
  214. BlastShiftTo=0;
  215. InMatConvertTo=MNone;
  216. BelowTempConvertTo=0;
  217. AboveTempConvertTo=0;
  218. }
  219. void C4Material::UpdateScriptPointers()
  220. {
  221. for (uint32_t i = 0; i < CustomReactionList.size(); ++i)
  222. CustomReactionList[i].ResolveScriptFuncs(Name);
  223. }
  224. // -------------------------------------- C4MaterialMap
  225. C4MaterialMap::C4MaterialMap() : DefReactConvert(&mrfConvert), DefReactPoof(&mrfPoof), DefReactCorrode(&mrfCorrode), DefReactIncinerate(&mrfIncinerate), DefReactInsert(&mrfInsert)
  226. {
  227. Default();
  228. }
  229. C4MaterialMap::~C4MaterialMap()
  230. {
  231. Clear();
  232. }
  233. void C4MaterialMap::Clear()
  234. {
  235. if (Map) delete [] Map; Map=NULL;
  236. delete [] ppReactionMap; ppReactionMap = NULL;
  237. }
  238. int32_t C4MaterialMap::Load(C4Group &hGroup, C4Group* OverloadFile)
  239. {
  240. char entryname[256+1];
  241. // Determine number of materials in files
  242. int32_t mat_num=hGroup.EntryCount(C4CFN_MaterialFiles);
  243. // Allocate new map
  244. C4Material *pNewMap = new C4Material [mat_num + Num];
  245. if(!pNewMap) return 0;
  246. // Load material cores to map
  247. hGroup.ResetSearch(); int32_t cnt=0;
  248. while (hGroup.FindNextEntry(C4CFN_MaterialFiles,entryname))
  249. {
  250. // Load mat
  251. if (!pNewMap[cnt].Load(hGroup,entryname))
  252. { delete [] pNewMap; return 0; }
  253. // A new material?
  254. if(Get(pNewMap[cnt].Name) == MNone)
  255. cnt++;
  256. }
  257. // Take over old materials.
  258. for(int32_t i = 0; i < Num; i++)
  259. {
  260. pNewMap[cnt+i] = Map[i];
  261. }
  262. delete [] Map;
  263. Map = pNewMap;
  264. // set material number
  265. Num+=cnt;
  266. return cnt;
  267. }
  268. bool C4MaterialMap::HasMaterials(C4Group &hGroup) const
  269. {
  270. return !!hGroup.EntryCount(C4CFN_MaterialFiles);
  271. }
  272. int32_t C4MaterialMap::Get(const char *szMaterial)
  273. {
  274. int32_t cnt;
  275. for (cnt=0; cnt<Num; cnt++)
  276. if (SEqualNoCase(szMaterial,Map[cnt].Name))
  277. return cnt;
  278. return MNone;
  279. }
  280. bool C4MaterialMap::CrossMapMaterials() // Called after load
  281. {
  282. // Check material number
  283. if (::MaterialMap.Num>C4MaxMaterial)
  284. { LogFatal(LoadResStr("IDS_PRC_TOOMANYMATS")); return false; }
  285. // build reaction function map
  286. delete [] ppReactionMap;
  287. typedef C4MaterialReaction * C4MaterialReactionPtr;
  288. ppReactionMap = new C4MaterialReactionPtr[(Num+1)*(Num+1)];
  289. for (int32_t iMatPXS=-1; iMatPXS<Num; iMatPXS++)
  290. {
  291. C4Material *pMatPXS = (iMatPXS+1) ? Map+iMatPXS : NULL;
  292. for (int32_t iMatLS=-1; iMatLS<Num; iMatLS++)
  293. {
  294. C4MaterialReaction *pReaction = NULL;
  295. C4Material *pMatLS = ( iMatLS+1) ? Map+ iMatLS : NULL;
  296. // natural stuff: material conversion here?
  297. if (pMatPXS && pMatPXS->sInMatConvert.getLength() && SEqualNoCase(pMatPXS->sInMatConvert.getData(), pMatLS ? pMatLS->Name : C4TLS_MatSky))
  298. pReaction = &DefReactConvert;
  299. // the rest is happening for same/higher densities only
  300. else if ((MatDensity(iMatPXS) <= MatDensity(iMatLS)) && pMatPXS && pMatLS)
  301. {
  302. // incindiary vs extinguisher
  303. if ((pMatPXS->Incindiary && pMatLS->Extinguisher) || (pMatPXS->Extinguisher && pMatLS->Incindiary))
  304. pReaction = &DefReactPoof;
  305. // incindiary vs inflammable
  306. else if ((pMatPXS->Incindiary && pMatLS->Inflammable) || (pMatPXS->Inflammable && pMatLS->Incindiary))
  307. pReaction = &DefReactIncinerate;
  308. // corrosive vs corrode
  309. else if (pMatPXS->Corrosive && pMatLS->Corrode)
  310. pReaction = &DefReactCorrode;
  311. // otherwise, when hitting same or higher density: Material insertion
  312. else
  313. pReaction = &DefReactInsert;
  314. }
  315. // assign the function; or NULL for no reaction
  316. SetMatReaction(iMatPXS, iMatLS, pReaction);
  317. }
  318. }
  319. // material-specific initialization
  320. int32_t cnt;
  321. for (cnt=0; cnt<Num; cnt++)
  322. {
  323. C4Material *pMat = Map+cnt;
  324. const char *szTextureOverlay = NULL;
  325. // newgfx: init pattern
  326. if (Map[cnt].sTextureOverlay.getLength())
  327. if (::TextureMap.GetTexture(Map[cnt].sTextureOverlay.getLength()))
  328. {
  329. szTextureOverlay = Map[cnt].sTextureOverlay.getData();
  330. // backwards compatibility: if a pattern was specified although the no-pattern flag was set, overwrite that flag
  331. if (Map[cnt].OverlayType & C4MatOv_None)
  332. {
  333. DebugLogF("Error in overlay of material %s: Flag C4MatOv_None ignored because a custom overlay (%s) was specified!", Map[cnt].Name, szTextureOverlay);
  334. Map[cnt].OverlayType &= ~C4MatOv_None;
  335. }
  336. }
  337. // default to smooth
  338. if (!szTextureOverlay)
  339. szTextureOverlay = "Smooth";
  340. // search/create entry in texmap
  341. Map[cnt].DefaultMatTex = ::TextureMap.GetIndex(Map[cnt].Name, szTextureOverlay, true,
  342. FormatString("DefaultMatTex of mat %s", Map[cnt].Name).getData());
  343. // init PXS facet
  344. SURFACE sfcTexture;
  345. C4Texture * Texture;
  346. if (Map[cnt].sPXSGfx.getLength())
  347. if (Texture=::TextureMap.GetTexture(Map[cnt].sPXSGfx.getData()))
  348. if (sfcTexture=Texture->Surface32)
  349. Map[cnt].PXSFace.Set(sfcTexture, Map[cnt].PXSGfxRt.x, Map[cnt].PXSGfxRt.y, Map[cnt].PXSGfxRt.Wdt, Map[cnt].PXSGfxRt.Hgt);
  350. // evaluate reactions for that material
  351. for (unsigned int iRCnt = 0; iRCnt < pMat->CustomReactionList.size(); ++iRCnt)
  352. {
  353. C4MaterialReaction *pReact = &(pMat->CustomReactionList[iRCnt]);
  354. if (pReact->sConvertMat.getLength()) pReact->iConvertMat = Get(pReact->sConvertMat.getData()); else pReact->iConvertMat = -1;
  355. // evaluate target spec
  356. int32_t tmat;
  357. if (MatValid(tmat=Get(pReact->TargetSpec.getData())))
  358. {
  359. // single material target
  360. if (pReact->fInverseSpec)
  361. for (int32_t cnt2=-1; cnt2<Num; cnt2++) if (cnt2!=tmat) SetMatReaction(cnt, cnt2, pReact);
  362. else
  363. SetMatReaction(cnt, tmat, pReact);
  364. }
  365. else if (SEqualNoCase(pReact->TargetSpec.getData(), "All"))
  366. {
  367. // add to all materials, including sky
  368. if (!pReact->fInverseSpec) for (int32_t cnt2=-1; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact);
  369. }
  370. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Solid"))
  371. {
  372. // add to all solid materials
  373. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  374. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  375. }
  376. else if (SEqualNoCase(pReact->TargetSpec.getData(), "SemiSolid"))
  377. {
  378. // add to all semisolid materials
  379. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  380. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (DensitySemiSolid(Map[cnt2].Density) != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  381. }
  382. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Background"))
  383. {
  384. // add to all BG materials, including sky
  385. if (!pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  386. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Density != pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  387. }
  388. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Sky"))
  389. {
  390. // add to sky
  391. if (!pReact->fInverseSpec)
  392. SetMatReaction(cnt, -1, pReact);
  393. else
  394. for (int32_t cnt2=0; cnt2<Num; cnt2++) SetMatReaction(cnt, cnt2, pReact);
  395. }
  396. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Incindiary"))
  397. {
  398. // add to all incendiary materials
  399. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  400. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Incindiary == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  401. }
  402. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Extinguisher"))
  403. {
  404. // add to all incendiary materials
  405. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  406. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Extinguisher == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  407. }
  408. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Inflammable"))
  409. {
  410. // add to all incendiary materials
  411. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  412. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Inflammable == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  413. }
  414. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrosive"))
  415. {
  416. // add to all incendiary materials
  417. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  418. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrosive == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  419. }
  420. else if (SEqualNoCase(pReact->TargetSpec.getData(), "Corrode"))
  421. {
  422. // add to all incendiary materials
  423. if (pReact->fInverseSpec) SetMatReaction(cnt, -1, pReact);
  424. for (int32_t cnt2=0; cnt2<Num; cnt2++) if (!Map[cnt2].Corrode == pReact->fInverseSpec) SetMatReaction(cnt, cnt2, pReact);
  425. }
  426. }
  427. }
  428. // second loop (DefaultMatTex is needed by GetIndexMatTex)
  429. for (cnt=0; cnt<Num; cnt++)
  430. {
  431. if (Map[cnt].sBlastShiftTo.getLength())
  432. Map[cnt].BlastShiftTo=::TextureMap.GetIndexMatTex(Map[cnt].sBlastShiftTo.getData(), NULL, true, FormatString("BlastShiftTo of mat %s", Map[cnt].Name).getData());
  433. if (Map[cnt].sInMatConvertTo.getLength())
  434. Map[cnt].InMatConvertTo=Get(Map[cnt].sInMatConvertTo.getData());
  435. if (Map[cnt].sBelowTempConvertTo.getLength())
  436. Map[cnt].BelowTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sBelowTempConvertTo.getData(), NULL, true, FormatString("BelowTempConvertTo of mat %s", Map[cnt].Name).getData());
  437. if (Map[cnt].sAboveTempConvertTo.getLength())
  438. Map[cnt].AboveTempConvertTo=::TextureMap.GetIndexMatTex(Map[cnt].sAboveTempConvertTo.getData(), NULL, true, FormatString("AboveTempConvertTo of mat %s", Map[cnt].Name).getData());
  439. }
  440. #if 0
  441. int32_t i=0;
  442. while (ReactionFuncMap[i].szRFName) {printf("%s: %p\n", ReactionFuncMap[i].szRFName, ReactionFuncMap[i].pFunc);++i;}
  443. for (int32_t cnt=-1; cnt<Num; cnt++)
  444. for (int32_t cnt2=-1; cnt2<Num; cnt2++)
  445. if (ppReactionMap[(cnt2+1)*(Num+1) + cnt+1])
  446. printf("%s -> %s: %p\n", Map[cnt].Name, Map[cnt2].Name, ppReactionMap[(cnt2+1)*(Num+1) + cnt+1]->pFunc);
  447. #endif
  448. // Get hardcoded system material indices
  449. MVehic = Get("Vehicle"); MCVehic = Mat2PixColDefault(MVehic);
  450. MTunnel = Get("Tunnel");
  451. MWater = Get("Water");
  452. MSnow = Get("Snow");
  453. MGranite = Get("Granite");
  454. MFlyAshes= Get("FlyAshes");
  455. MEarth = Get(Game.C4S.Landscape.Material);
  456. if ((MVehic==MNone) || (MTunnel==MNone))
  457. { LogFatal(LoadResStr("IDS_PRC_NOSYSMATS")); return false; }
  458. return true;
  459. }
  460. void C4MaterialMap::SetMatReaction(int32_t iPXSMat, int32_t iLSMat, C4MaterialReaction *pReact)
  461. {
  462. // evaluate reaction swap
  463. if (pReact && pReact->fReverse) Swap(iPXSMat, iLSMat);
  464. // set it
  465. ppReactionMap[(iLSMat+1)*(Num+1) + iPXSMat+1] = pReact;
  466. }
  467. bool C4MaterialMap::SaveEnumeration(C4Group &hGroup)
  468. {
  469. char *mapbuf = new char [1000];
  470. mapbuf[0]=0;
  471. SAppend("[Enumeration]",mapbuf); SAppend(LineFeed,mapbuf);
  472. for (int32_t cnt=0; cnt<Num; cnt++)
  473. {
  474. SAppend(Map[cnt].Name,mapbuf);
  475. SAppend(LineFeed,mapbuf);
  476. }
  477. SAppend(EndOfFile,mapbuf);
  478. return hGroup.Add(C4CFN_MatMap,mapbuf,SLen(mapbuf),false,true);
  479. }
  480. bool C4MaterialMap::LoadEnumeration(C4Group &hGroup)
  481. {
  482. // Load enumeration map (from savegame), succeed if not present
  483. StdStrBuf mapbuf;
  484. if (!hGroup.LoadEntryString(C4CFN_MatMap, mapbuf)) return true;
  485. // Sort material array by enumeration map, fail if some missing
  486. const char *csearch;
  487. char cmatname[C4M_MaxName+1];
  488. int32_t cmat=0;
  489. if (!(csearch = SSearch(mapbuf.getData(),"[Enumeration]"))) { return false; }
  490. csearch=SAdvanceSpace(csearch);
  491. while (IsIdentifier(*csearch))
  492. {
  493. SCopyIdentifier(csearch,cmatname,C4M_MaxName);
  494. if (!SortEnumeration(cmat,cmatname))
  495. {
  496. // Output error message!
  497. return false;
  498. }
  499. cmat++;
  500. csearch+=SLen(cmatname);
  501. csearch=SAdvanceSpace(csearch);
  502. }
  503. return true;
  504. }
  505. bool C4MaterialMap::SortEnumeration(int32_t iMat, const char *szMatName)
  506. {
  507. // Not enough materials loaded
  508. if (iMat>=Num) return false;
  509. // Find requested mat
  510. int32_t cmat;
  511. for (cmat=iMat; cmat<Num; cmat++)
  512. if (SEqual(szMatName,Map[cmat].Name))
  513. break;
  514. // Not found
  515. if (cmat>=Num) return false;
  516. // already the same?
  517. if (cmat == iMat) return true;
  518. // Move requested mat to indexed position
  519. C4Material mswap;
  520. mswap = Map[iMat];
  521. Map[iMat] = Map[cmat];
  522. Map[cmat] = mswap;
  523. return true;
  524. }
  525. void C4MaterialMap::Default()
  526. {
  527. Num=0;
  528. Map=NULL;
  529. ppReactionMap=NULL;
  530. }
  531. C4MaterialReaction *C4MaterialMap::GetReaction(int32_t iPXSMat, int32_t iLandscapeMat)
  532. {
  533. // safety
  534. if (!ppReactionMap) return NULL;
  535. if (!Inside<int32_t>(iPXSMat, -1, Num-1)) return NULL;
  536. if (!Inside<int32_t>(iLandscapeMat, -1, Num-1)) return NULL;
  537. // values OK; get func!
  538. return GetReactionUnsafe(iPXSMat, iLandscapeMat);
  539. }
  540. bool mrfInsertCheck(int32_t &iX, int32_t &iY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, bool *pfPosChanged)
  541. {
  542. // always manipulating pos/speed here
  543. if (pfPosChanged) *pfPosChanged = true;
  544. // Rough contact? May splash
  545. if (fYDir > itofix(1))
  546. if (::MaterialMap.Map[iPxsMat].SplashRate && !Random(::MaterialMap.Map[iPxsMat].SplashRate))
  547. {
  548. fYDir = -fYDir/8;
  549. fXDir = fXDir/8 + FIXED100(Random(200) - 100);
  550. if (fYDir) return false;
  551. }
  552. // Contact: Stop
  553. fYDir = 0;
  554. // Incindiary mats smoke on contact even before doing their slide
  555. if (::MaterialMap.Map[iPxsMat].Incindiary)
  556. if (!Random(25)) Smoke(iX, iY, 4+Rnd3() );
  557. // Move by mat path/slide
  558. int32_t iSlideX = iX, iSlideY = iY;
  559. if (::Landscape.FindMatSlide(iSlideX,iSlideY,Sign(GravAccel),::MaterialMap.Map[iPxsMat].Density,::MaterialMap.Map[iPxsMat].MaxSlide))
  560. {
  561. if(iPxsMat == iLsMat)
  562. { iX = iSlideX; iY = iSlideY; fXDir = 0; return false; }
  563. // Accelerate into the direction
  564. fXDir = (fXDir * 10 + Sign(iSlideX - iX)) / 11 + FIXED10(Random(5)-2);
  565. // Slide target in range? Move there directly.
  566. if(Abs(iX - iSlideX) <= Abs(fixtoi(fXDir)))
  567. {
  568. iX = iSlideX;
  569. iY = iSlideY;
  570. if(fYDir <= 0) fXDir = 0;
  571. }
  572. // Continue existance
  573. return false;
  574. }
  575. // insertion OK
  576. return true;
  577. }
  578. bool mrfUserCheck(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  579. {
  580. // check execution mask
  581. if ((1<<evEvent) & ~pReaction->iExecMask) return false;
  582. // do splash/slide check, if desired
  583. if (pReaction->fInsertionCheck && evEvent == meePXSMove)
  584. if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
  585. return false;
  586. // checks OK; reaction may be applied
  587. return true;
  588. }
  589. bool C4MaterialMap::mrfConvert(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  590. {
  591. if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
  592. switch (evEvent)
  593. {
  594. case meePXSMove: // PXS movement
  595. // for hardcoded stuff: only InMatConvert is Snow in Water, which does not have any collision proc
  596. if (!pReaction->fUserDefined) break;
  597. // user-defined conversions may also convert upon hitting materials
  598. case meePXSPos: // PXS check before movement
  599. {
  600. // Check depth
  601. int32_t iDepth = pReaction->fUserDefined ? pReaction->iDepth : ::MaterialMap.Map[iPxsMat].InMatConvertDepth;
  602. if (!iDepth || GBackMat(iX, iY - iDepth) == iLsMat)
  603. {
  604. // Convert
  605. iPxsMat = pReaction->fUserDefined ? pReaction->iConvertMat : ::MaterialMap.Map[iPxsMat].InMatConvertTo;
  606. if (!MatValid(iPxsMat))
  607. // Convert failure (target mat not be loaded, or target may be C4TLS_MatSky): Kill Pix
  608. return true;
  609. // stop movement after conversion
  610. fXDir = fYDir = 0;
  611. if (pfPosChanged) *pfPosChanged = true;
  612. }
  613. }
  614. break;
  615. case meeMassMove: // MassMover-movement
  616. // Conversion-transfer to PXS
  617. ::PXS.Create(iPxsMat,itofix(iX),itofix(iY));
  618. return true;
  619. }
  620. // not handled
  621. return false;
  622. }
  623. bool C4MaterialMap::mrfPoof(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  624. {
  625. if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
  626. switch (evEvent)
  627. {
  628. case meeMassMove: // MassMover-movement
  629. case meePXSPos: // PXS check before movement: Kill both landscape and PXS mat
  630. ::Landscape.ExtractMaterial(iLSPosX,iLSPosY);
  631. if (!Rnd3()) Smoke(iX,iY,3);
  632. if (!Rnd3()) StartSoundEffectAt("Pshshsh", iX, iY);
  633. return true;
  634. case meePXSMove: // PXS movement
  635. // incindiary/extinguisher/corrosives are always same density proc; so do insertion check first
  636. if (!pReaction->fUserDefined)
  637. if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
  638. // either splash or slide prevented interaction
  639. return false;
  640. // Always kill both landscape and PXS mat
  641. ::Landscape.ExtractMaterial(iLSPosX,iLSPosY);
  642. if (!Rnd3()) Smoke(iX,iY,3);
  643. if (!Rnd3()) StartSoundEffectAt("Pshshsh", iX, iY);
  644. return true;
  645. }
  646. // not handled
  647. return false;
  648. }
  649. bool C4MaterialMap::mrfCorrode(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  650. {
  651. if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
  652. switch (evEvent)
  653. {
  654. case meePXSPos: // PXS check before movement
  655. // No corrosion - it would make acid incredibly effective
  656. break;
  657. case meeMassMove: // MassMover-movement
  658. {
  659. // evaluate corrosion percentage
  660. bool fDoCorrode;
  661. if (pReaction->fUserDefined)
  662. fDoCorrode = (Random(100) < pReaction->iCorrosionRate);
  663. else
  664. fDoCorrode = (Random(100) < ::MaterialMap.Map[iPxsMat].Corrosive) && (Random(100) < ::MaterialMap.Map[iLsMat].Corrode);
  665. if (fDoCorrode)
  666. {
  667. ClearBackPix(iLSPosX,iLSPosY);
  668. //::Landscape.CheckInstabilityRange(iLSPosX,iLSPosY); - more correct, but makes acid too effective as well
  669. if (!Random(5)) Smoke(iX,iY,3+Random(3));
  670. if (!Random(20)) StartSoundEffectAt("Corrode", iX, iY);
  671. return true;
  672. }
  673. }
  674. break;
  675. case meePXSMove: // PXS movement
  676. {
  677. // corrodes to corrosives are always same density proc; so do insertion check first
  678. if (!pReaction->fUserDefined)
  679. if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
  680. // either splash or slide prevented interaction
  681. return false;
  682. // evaluate corrosion percentage
  683. bool fDoCorrode;
  684. if (pReaction->fUserDefined)
  685. fDoCorrode = (Random(100) < pReaction->iCorrosionRate);
  686. else
  687. fDoCorrode = (Random(100) < ::MaterialMap.Map[iPxsMat].Corrosive) && (Random(100) < ::MaterialMap.Map[iLsMat].Corrode);
  688. if (fDoCorrode)
  689. {
  690. ClearBackPix(iLSPosX,iLSPosY);
  691. ::Landscape.CheckInstabilityRange(iLSPosX,iLSPosY);
  692. if (!Random(5)) Smoke(iX,iY,3+Random(3));
  693. if (!Random(20)) StartSoundEffectAt("Corrode", iX, iY);
  694. return true;
  695. }
  696. // Else: dead. Insert material here
  697. ::Landscape.InsertMaterial(iPxsMat,iX,iY);
  698. return true;
  699. }
  700. }
  701. // not handled
  702. return false;
  703. }
  704. bool C4MaterialMap::mrfIncinerate(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  705. {
  706. // not available as user reaction
  707. assert(!pReaction->fUserDefined);
  708. switch (evEvent)
  709. {
  710. case meeMassMove: // MassMover-movement
  711. case meePXSPos: // PXS check before movement
  712. if (::Landscape.Incinerate(iX, iY)) return true;
  713. break;
  714. case meePXSMove: // PXS movement
  715. // incinerate to inflammables are always same density proc; so do insertion check first
  716. if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
  717. // either splash or slide prevented interaction
  718. return false;
  719. // evaluate inflammation (should always succeed)
  720. if (::Landscape.Incinerate(iX, iY)) return true;
  721. // Else: dead. Insert material here
  722. ::Landscape.InsertMaterial(iPxsMat,iX,iY);
  723. return true;
  724. }
  725. // not handled
  726. return false;
  727. }
  728. bool C4MaterialMap::mrfInsert(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  729. {
  730. if (pReaction->fUserDefined) if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged)) return false;
  731. switch (evEvent)
  732. {
  733. case meePXSPos: // PXS check before movement
  734. break;
  735. case meePXSMove: // PXS movement
  736. {
  737. // check for bounce/slide
  738. if (!pReaction->fUserDefined)
  739. if (!mrfInsertCheck(iX, iY, fXDir, fYDir, iPxsMat, iLsMat, pfPosChanged))
  740. // continue existing
  741. return false;
  742. // Else: dead. Insert material here
  743. ::Landscape.InsertMaterial(iPxsMat,iX,iY);
  744. return true;
  745. }
  746. case meeMassMove: // MassMover-movement
  747. break;
  748. }
  749. // not handled
  750. return false;
  751. }
  752. bool C4MaterialMap::mrfScript(C4MaterialReaction *pReaction, int32_t &iX, int32_t &iY, int32_t iLSPosX, int32_t iLSPosY, FIXED &fXDir, FIXED &fYDir, int32_t &iPxsMat, int32_t iLsMat, MaterialInteractionEvent evEvent, bool *pfPosChanged)
  753. {
  754. // do generic checks for user-defined reactions
  755. if (!mrfUserCheck(pReaction, iX, iY, iLSPosX, iLSPosY, fXDir, fYDir, iPxsMat, iLsMat, evEvent, pfPosChanged))
  756. return false;
  757. // check script func
  758. if (!pReaction->pScriptFunc) return false;
  759. // OK - let's call it!
  760. // 0 1 2 3 4 5 6 7 8
  761. int32_t iXDir1, iYDir1, iXDir2, iYDir2;
  762. C4AulParSet pars(C4VInt(iX), C4VInt(iY), C4VInt(iLSPosX), C4VInt(iLSPosY), C4VInt(iXDir1=fixtoi(fXDir, 100)), C4VInt(iYDir1=fixtoi(fYDir, 100)), C4VInt(iPxsMat), C4VInt(iLsMat), C4VInt(evEvent));
  763. if (!!pReaction->pScriptFunc->Exec(NULL, &pars, false))
  764. {
  765. // PXS shall be killed!
  766. return true;
  767. }
  768. // PXS shall exist further: write back parameters
  769. iPxsMat = pars[6].getInt();
  770. int32_t iX2 = pars[0].getInt(), iY2 = pars[1].getInt();
  771. iXDir2 = pars[4].getInt(); iYDir2 = pars[5].getInt();
  772. if (iX!=iX2 || iY!=iY2 || iXDir1!=iXDir2 || iYDir1!=iYDir2)
  773. {
  774. // changes to pos/speed detected
  775. if (pfPosChanged) *pfPosChanged = true;
  776. iX=iX2; iY=iY2;
  777. fXDir = FIXED100(iXDir2);
  778. fYDir = FIXED100(iYDir2);
  779. }
  780. // OK; done
  781. return false;
  782. }
  783. void C4MaterialMap::UpdateScriptPointers()
  784. {
  785. // update in all materials
  786. for (int32_t i=0; i<Num; ++i) Map[i].UpdateScriptPointers();
  787. }
  788. int32_t PixCol2MatOld(BYTE pixc)
  789. {
  790. const int C4M_ColsPerMat = 3;
  791. if (pixc < GBM) return MNone;
  792. pixc &= 63; // Substract GBM, ignore IFT
  793. if (pixc > ::MaterialMap.Num*C4M_ColsPerMat-1) return MNone;
  794. return pixc / C4M_ColsPerMat;
  795. }
  796. int32_t PixCol2MatOld2(BYTE pixc)
  797. {
  798. int32_t iMat = ((int32_t) (pixc&0x7f)) -1;
  799. // if above MVehic, don't forget additional vehicle-colors
  800. if (iMat<=MVehic) return iMat;
  801. // equals middle vehicle-color
  802. if (iMat==MVehic+1) return MVehic;
  803. // above: range check
  804. iMat-=2; if (iMat >= ::MaterialMap.Num) return MNone;
  805. return iMat;
  806. }
  807. C4MaterialMap MaterialMap;