OpenClonk
C4Landscape.cpp
Go to the documentation of this file.
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  /* Handles landscape and sky */
19 
20 #include "C4Include.h"
22 #include "landscape/C4Landscape.h"
23 
24 #include "c4group/C4Components.h"
25 #include "control/C4Record.h"
26 #include "editor/C4ToolsDlg.h"
27 #include "game/C4GraphicsSystem.h"
28 #include "game/C4Physics.h"
30 #include "gui/C4GameMessage.h"
32 #include "landscape/C4Map.h"
34 #include "landscape/C4MapScript.h"
35 #include "landscape/C4MassMover.h"
36 #include "landscape/C4Material.h"
38 #include "landscape/C4PXS.h"
39 #include "landscape/C4Sky.h"
40 #include "landscape/C4SolidMask.h"
41 #include "landscape/C4Texture.h"
42 #include "landscape/C4Weather.h"
43 #include "landscape/fow/C4FoW.h"
44 #include "lib/C4Random.h"
45 #include "lib/StdColors.h"
46 #include "object/C4Def.h"
47 #include "object/C4FindObject.h"
48 #include "object/C4GameObjects.h"
49 
50 #include <array>
51 
53 {
54  std::unique_ptr<CSurface8> Surface8;
55  std::unique_ptr<CSurface8> Surface8Bkg; // Background material
56  std::unique_ptr<CSurface8> Map;
57  std::unique_ptr<CSurface8> MapBkg;
58  std::unique_ptr<C4LandscapeRender> pLandscapeRender;
59  // array size of landscape width/height: Filled with 0s for border pixels that are open and MCVehic for pixels that are closed
60  std::vector<uint8_t> TopRowPix, BottomRowPix, LeftColPix, RightColPix;
63  int32_t PixCntPitch = 0;
64  std::vector<uint8_t> PixCnt;
65  std::array<C4Rect, C4LS_MaxRelights> Relights;
66  mutable std::array<std::unique_ptr<uint8_t[]>, C4M_MaxTexIndex> BridgeMatConversion; // NoSave //
67 
69  int32_t Width = 0, Height = 0;
70  int32_t MapWidth = 0, MapHeight = 0, MapZoom = 0;
71  std::array<DWORD, C4MaxMaterial> MatCount{}; // NoSave //
72  std::array<DWORD, C4MaxMaterial> EffectiveMatCount{}; // NoSave //
73 
74  bool NoScan = false; // ExecuteScan() disabled
75  int32_t ScanX = 0, ScanSpeed = 2; // SyncClearance-NoSave //
77  uint32_t Modulation = 0; // landscape blit modulation; 0 means normal
78  int32_t MapSeed = 0; // random seed for MapToLandscape
80  std::unique_ptr<C4MapCreatorS2> pMapCreator; // map creator for script-generated maps
81  bool fMapChanged = false;
82  std::unique_ptr<BYTE[]> pInitial; // Initial landscape after creation - used for diff
83  std::unique_ptr<BYTE[]> pInitialBkg; // Initial bkg landscape after creation - used for diff
84  std::unique_ptr<C4FoW> pFoW;
85 
86  void ClearMatCount();
87 
88  void ExecuteScan(C4Landscape *);
89  int32_t DoScan(C4Landscape *, int32_t x, int32_t y, int32_t mat, int32_t dir);
90  uint32_t ChunkyRandom(uint32_t &iOffset, uint32_t iRange) const; // return static random value, according to offset and MapSeed
91  void DrawChunk(C4Landscape *, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, C4MaterialCoreShape Shape, uint32_t cro);
92  void DrawSmoothOChunk(C4Landscape *, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, int flip, uint32_t cro);
93  void ChunkOZoom(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, uint8_t iTexture, int32_t iOffX = 0, int32_t iOffY = 0);
94  bool TexOZoom(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, DWORD *dwpTextureUsage, int32_t iToX = 0, int32_t iToY = 0);
95  bool MapToSurface(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY);
96  bool MapToLandscape(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iOffsX = 0, int32_t iOffsY = 0, bool noClear = false); // zoom map segment to surface (or sector surfaces)
97  bool InitBorderPix(); // init out-of-landscape pixels for ALL sides
98  bool GetMapColorIndex(const char *szMaterial, const char *szTexture, BYTE &rbyCol) const;
99  //bool SkyToLandscape(int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY);
100  bool CreateMap(CSurface8*& sfcMap, CSurface8*& sfcMapBkg); // create map by landscape attributes
101  bool CreateMapS2(C4Group &ScenFile, CSurface8*& sfcMap, CSurface8*& sfcMapBkg); // create map by def file
102  bool Mat2Pal(); // assign material colors to landscape palette
103  void UpdatePixCnt(const C4Landscape *, const C4Rect &Rect, bool fCheck = false);
104  void UpdateMatCnt(const C4Landscape *, C4Rect Rect, bool fPlus);
105  void PrepareChange(const C4Landscape *d, const C4Rect &BoundingBox);
106  void FinishChange(C4Landscape *d, C4Rect BoundingBox);
107  bool DrawLineLandscape(int32_t iX, int32_t iY, int32_t iGrade, uint8_t line_color, uint8_t line_color_bkg);
108  bool DrawLineMap(int32_t iX, int32_t iY, int32_t iRadius, uint8_t line_color, uint8_t line_color_bkg);
109  uint8_t *GetBridgeMatConversion(const C4Landscape *d, int32_t for_material_col) const;
110  bool SaveInternal(const C4Landscape *d, C4Group &hGroup) const;
111  bool SaveDiffInternal(const C4Landscape *d, C4Group &hGroup, bool fSyncSave) const;
112 
113  int32_t ForPolygon(C4Landscape *d, int *vtcs, int length, const std::function<bool(int32_t, int32_t)> &callback,
114  C4MaterialList *mats_count = nullptr, uint8_t col = 0, uint8_t colBkg = 0, uint8_t *conversion_table = nullptr);
115 
116  std::unique_ptr<CSurface8> CreateDefaultBkgSurface(CSurface8& sfcFg, bool msbAsIft) const;
117  void DigMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, C4Object *pCollect = nullptr);
118  void BlastMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, int32_t caused_by, int32_t str, C4ValueArray *out_objects);
119 
120  bool DigFreePix(C4Landscape *d, int32_t tx, int32_t ty);
121  bool DigFreePixNoInstability(C4Landscape *d, int32_t tx, int32_t ty);
122  bool BlastFreePix(C4Landscape *d, int32_t tx, int32_t ty);
123  bool ShakeFreePix(C4Landscape *d, int32_t tx, int32_t ty);
124 
125  C4ValueArray *PrepareFreeShape(C4Rect &BoundingBox, C4Object *by_object);
126  void PostFreeShape(C4ValueArray *dig_objects, C4Object *by_object);
127  BYTE DefaultBkgMat(BYTE fg) const;
128 };
129 
130 namespace
131 {
132  bool ForLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2,
133  std::function<bool(int32_t, int32_t)> fnCallback,
134  int32_t *lastx = nullptr, int32_t *lasty = nullptr)
135  {
136  int d, dx, dy, aincr, bincr, xincr, yincr, x, y;
137  if (Abs(x2 - x1) < Abs(y2 - y1))
138  {
139  if (y1 > y2) { std::swap(x1, x2); std::swap(y1, y2); }
140  xincr = (x2 > x1) ? 1 : -1;
141  dy = y2 - y1; dx = Abs(x2 - x1);
142  d = 2 * dx - dy; aincr = 2 * (dx - dy); bincr = 2 * dx; x = x1; y = y1;
143  if (!fnCallback(x, y))
144  {
145  if (lastx) *lastx = x; if (lasty) *lasty = y;
146  return false;
147  }
148  for (y = y1 + 1; y <= y2; ++y)
149  {
150  if (d >= 0) { x += xincr; d += aincr; }
151  else d += bincr;
152  if (!fnCallback(x, y))
153  {
154  if (lastx) *lastx = x; if (lasty) *lasty = y;
155  return false;
156  }
157  }
158  }
159  else
160  {
161  if (x1 > x2) { std::swap(x1, x2); std::swap(y1, y2); }
162  yincr = (y2 > y1) ? 1 : -1;
163  dx = x2 - x1; dy = Abs(y2 - y1);
164  d = 2 * dy - dx; aincr = 2 * (dy - dx); bincr = 2 * dy; x = x1; y = y1;
165  if (!fnCallback(x, y))
166  {
167  if (lastx) *lastx = x; if (lasty) *lasty = y;
168  return false;
169  }
170  for (x = x1 + 1; x <= x2; ++x)
171  {
172  if (d >= 0) { y += yincr; d += aincr; }
173  else d += bincr;
174  if (!fnCallback(x, y))
175  {
176  if (lastx) *lastx = x; if (lasty) *lasty = y;
177  return false;
178  }
179  }
180  }
181  return true;
182  }
183 }
184 
186  : p(new P)
187 {
188  Default();
189 }
190 
192 {
193  Clear();
194 }
195 
196 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
197 /* +++++++++++++++++++++++++ Execute and display +++++++++++++++++++++++++++ */
198 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
199 
201 {
202  // Landscape scan
203  if (!p->NoScan)
204  p->ExecuteScan(this);
205  // move sky
206  p->Sky.Execute();
207 
208  // Queued Relights -- note that normally we process them before drawing every frame;
209  // this just makes sure relights don't accumulate over a long period of time if no
210  // viewport is open (developer mode).
211  if (!::Game.iTick35)
212  DoRelights();
213 }
214 
215 
217 {
218  int32_t cy, mat;
219 
220  // Check: Scan needed?
221  const int32_t iTemperature = ::Weather.GetTemperature();
222  for (mat = 0; mat < ::MaterialMap.Num; mat++)
223  if (MatCount[mat])
224  {
225  if (::MaterialMap.Map[mat].BelowTempConvertTo &&
226  iTemperature < ::MaterialMap.Map[mat].BelowTempConvert)
227  break;
228  else if (::MaterialMap.Map[mat].AboveTempConvertTo &&
229  iTemperature > ::MaterialMap.Map[mat].AboveTempConvert)
230  break;
231  }
232  if (mat >= ::MaterialMap.Num)
233  return;
234 
236  AddDbgRec(RCT_MatScan, &ScanX, sizeof(ScanX));
237 
238  for (int32_t cnt = 0; cnt < ScanSpeed; cnt++)
239  {
240 
241  // Scan landscape column: sectors down
242  int32_t last_mat = -1;
243  for (cy = 0; cy < Height; cy++)
244  {
245  mat = d->_GetMat(ScanX, cy);
246  // material change?
247  if (last_mat != mat)
248  {
249  // upwards
250  if (last_mat != -1)
251  DoScan(d, ScanX, cy - 1, last_mat, 1);
252  // downwards
253  if (mat != -1)
254  cy += DoScan(d, ScanX, cy, mat, 0);
255  }
256  last_mat = mat;
257  }
258 
259  // Scan advance & rewind
260  ScanX++;
261  if (ScanX >= Width)
262  ScanX = 0;
263 
264  }
265 
266 }
267 
268 #define PRETTY_TEMP_CONV
269 
270 int32_t C4Landscape::P::DoScan(C4Landscape *d, int32_t cx, int32_t cy, int32_t mat, int32_t dir)
271 {
272  int32_t conv_to_tex = 0;
273  int32_t iTemperature = ::Weather.GetTemperature();
274  // Check below conv
275  if (::MaterialMap.Map[mat].BelowTempConvertDir == dir)
277  if (iTemperature< ::MaterialMap.Map[mat].BelowTempConvert)
278  conv_to_tex = ::MaterialMap.Map[mat].BelowTempConvertTo;
279  // Check above conv
280  if (::MaterialMap.Map[mat].AboveTempConvertDir == dir)
282  if (iTemperature>::MaterialMap.Map[mat].AboveTempConvert)
283  conv_to_tex = ::MaterialMap.Map[mat].AboveTempConvertTo;
284  // nothing to do?
285  if (!conv_to_tex) return 0;
286  // find material
287  int32_t conv_to = ::TextureMap.GetEntry(conv_to_tex)->GetMaterialIndex();
288  // find mat top
289  int32_t mconv = ::MaterialMap.Map[mat].TempConvStrength,
290  mconvs = mconv;
292  {
293  C4RCMatScan rc = { cx, cy, mat, conv_to, dir, mconvs };
294  AddDbgRec(RCT_MatScanDo, &rc, sizeof(C4RCMatScan));
295  }
296  int32_t ydir = (dir == 0 ? +1 : -1), cy2;
297 #ifdef PRETTY_TEMP_CONV
298  // get left pixel
299  int32_t lmat = (cx > 0 ? d->_GetMat(cx - 1, cy) : -1);
300  // left pixel not converted? do nothing
301  if (lmat == mat) return 0;
302  // left pixel converted? suppose it was done by a prior scan and skip check
303  if (lmat != conv_to)
304  {
305  int32_t iSearchRange = std::max<int32_t>(5, mconvs);
306  // search upper/lower bound
307  int32_t cys = cy, cxs = cx;
308  while (cxs < ::Landscape.GetWidth() - 1)
309  {
310  // one step right
311  cxs++;
312  if (d->_GetMat(cxs, cys) == mat)
313  {
314  // search surface
315  cys -= ydir;
316  while (Inside<int32_t>(cys, 0, ::Landscape.GetHeight() - 1) && d->_GetMat(cxs, cys) == mat)
317  {
318  cys -= ydir;
319  if ((mconvs = std::min(mconv - Abs(cys - cy), mconvs)) < 0)
320  return 0;
321  }
322  // out of bounds?
323  if (!Inside<int32_t>(cys, 0, ::Landscape.GetHeight() - 1)) break;
324  // back one step
325  cys += ydir;
326  }
327  else
328  {
329  // search surface
330  cys += ydir;
331  while (Inside<int32_t>(cys, 0, ::Landscape.GetHeight() - 1) && d->_GetMat(cxs, cys) != mat)
332  {
333  cys += ydir;
334  if (Abs(cys - cy) > iSearchRange)
335  break;
336  }
337  // out of bounds?
338  if (!Inside<int32_t>(cys, 0, ::Landscape.GetHeight() - 1)) break;
339  if (Abs(cys - cy) > iSearchRange) break;
340  }
341  }
342  }
343 #endif
344  // Conversion
345  bool conv_to_is_solid = (conv_to > -1) && DensitySolid(::MaterialMap.Map[conv_to].Density);
346  for (cy2 = cy; mconvs >= 0 && Inside<int32_t>(cy2, 0, ::Landscape.GetHeight() - 1); cy2 += ydir, mconvs--)
347  {
348  // material changed?
349  int32_t pix = d->_GetPix(cx, cy2);
350  if (PixCol2Mat(pix) != mat) break;
351 #ifdef PRETTY_TEMP_CONV
352  // get left pixel
353  int32_t lmat = (cx > 0 ? d->_GetMat(cx - 1, cy2) : -1);
354  // left pixel not converted? break
355  if (lmat == mat) break;
356 #endif
357  // set mat (and keep background material)
358  d->SetPix2(cx, cy2, MatTex2PixCol(conv_to_tex), Transparent);
359  if (!conv_to_is_solid) d->CheckInstabilityRange(cx, cy2);
360  }
361  // return pixel converted
362  return Abs(cy2 - cy);
363 }
364 
366 {
367  uint32_t clrMod = 0xffffffff;
368  if (p->Modulation)
369  {
370  pDraw->ActivateBlitModulation(p->Modulation);
371  clrMod = p->Modulation;
372  }
373  // blit landscape
374  if (::GraphicsSystem.Show8BitSurface == 1)
375  pDraw->Blit8Fast(p->Surface8.get(), cgo.TargetX, cgo.TargetY, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt);
376  else if (::GraphicsSystem.Show8BitSurface == 2)
377  pDraw->Blit8Fast(p->Surface8Bkg.get(), cgo.TargetX, cgo.TargetY, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt);
378  else if (p->pLandscapeRender)
379  {
380  DoRelights();
381  p->pLandscapeRender->Draw(cgo, pLight, clrMod);
382  }
383  if (p->Modulation) pDraw->DeactivateBlitModulation();
384 }
385 
387 {
388  if (!p->pLandscapeRender) return true;
389  for (int32_t i = 0; i < C4LS_MaxRelights; i++)
390  {
391  if (!p->Relights[i].Wdt)
392  break;
393  // Remove all solid masks in the (twice!) extended region around the change
394  C4Rect SolidMaskRect = p->pLandscapeRender->GetAffectedRect(p->Relights[i]);
395  C4SolidMask * pSolid;
396  for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
397  pSolid->RemoveTemporary(SolidMaskRect);
398  // Perform the update
399  p->pLandscapeRender->Update(p->Relights[i], this);
400  if (p->pFoW) p->pFoW->Ambient.UpdateFromLandscape(*this, p->Relights[i]);
401  // Restore Solidmasks
402  for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
403  pSolid->PutTemporary(SolidMaskRect);
405  // Clear slot
406  p->Relights[i].Default();
407  }
408  return true;
409 }
410 
411 
412 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
413 /* +++++++++++++++++++++++ Add and destroy landscape++++++++++++++++++++++++ */
414 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
415 static std::vector<int32_t> GetRoundPolygon(int32_t x, int32_t y, int32_t size, int32_t smoothness)
416 {
417  /*
418  So what is this? It's basically a circle with the radius 'size'. The radius
419  is adjusted by two sin/cos waves. The random lies in the phase of the sin/cos
420  and in the factor the wave is added to the normal circle shape. smoothness from
421  0 to 100. 0 gives an exagerated 'explosion' shape while 100 is almost a circle
422  */
423 
424  if (smoothness > 100) smoothness = 100;
425  if (smoothness < 0) smoothness = 0;
426  if (size <= 0) size = 1;
427 
428  // vertex count of the polygon
429  int32_t count = 2 * size / 3 + 6;
430 
431  std::vector<int32_t> vertices;
432  vertices.reserve(count * 2);
433 
434  // varying phase of the sin/cos waves
435  C4Real begin = itofix(360)*(int32_t)Random(100) / 100;
436  C4Real begin2 = itofix(360)*(int32_t)Random(100) / 100;
437 
438  // parameters:
439  // the bigger the factor, the smaller the divergence from a circle
440  C4Real anticircle = itofix(3) + smoothness / 16 * smoothness / 16;
441  // the bigger the factor the more random is the divergence from the circle
442  int random = 80 * (200 - smoothness);
443 
444  for (int i = 0; i < count; ++i)
445  {
446  C4Real angle = itofix(360)*i / count;
447 
448  C4Real currsize = itofix(size);
449  currsize += Sin(angle * 3 + begin + itofix(Random(random)) / 100) * size / anticircle; // +sin
450  currsize += Cos(angle * 5 + begin2 + itofix(Random(random)) / 100) * size / anticircle / 2; // +cos
451 
452  vertices.push_back(x + fixtoi(Sin(angle)*currsize));
453  vertices.push_back(y - fixtoi(Cos(angle)*currsize));
454  }
455 
456  return vertices;
457 }
458 
459 static std::vector<int32_t> GetRectangle(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
460 {
461  std::vector<int32_t> vertices;
462  vertices.resize(8);
463 
464  vertices[0] = tx; vertices[1] = ty;
465  vertices[2] = tx; vertices[3] = ty + hgt;
466  vertices[4] = tx + wdt; vertices[5] = ty + hgt;
467  vertices[6] = tx + wdt; vertices[7] = ty;
468 
469  return vertices;
470 }
471 
472 static C4Rect getBoundingBox(int *vtcs, int length)
473 {
474  C4Rect BoundingBox(vtcs[0], vtcs[1], 1, 1);
475  for (int32_t i = 2; i + 1 < length; i += 2)
476  {
477  BoundingBox.Add(C4Rect(vtcs[i], vtcs[i + 1], 1, 1));
478  }
479  return BoundingBox;
480 }
481 
482 void C4Landscape::ClearFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
483 {
484  std::vector<int32_t> vertices(GetRectangle(tx, ty, wdt, hgt));
485  C4Rect r(tx, ty, wdt, hgt);
486  p->PrepareChange(this, r);
487  p->ForPolygon(this, &vertices[0], vertices.size() / 2, [this](int32_t x, int32_t y) { return ClearPix(x, y); });
488  p->FinishChange(this, r);
489 }
490 
491 int32_t C4Landscape::DigFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, C4Object *by_object, bool no_dig2objects, bool no_instability_check)
492 {
493  std::vector<int32_t> vertices(GetRectangle(tx, ty, wdt, hgt));
494  return DigFreeShape(&vertices[0], vertices.size(), by_object, no_dig2objects, no_instability_check);
495 }
496 
497 int32_t C4Landscape::DigFree(int32_t tx, int32_t ty, int32_t rad, C4Object *by_object, bool no_dig2objects, bool no_instability_check)
498 {
499  std::vector<int32_t> vertices(GetRoundPolygon(tx, ty, rad, 80));
500  return DigFreeShape(&vertices[0], vertices.size(), by_object, no_dig2objects, no_instability_check);
501 }
502 
503 void C4Landscape::BlastFree(int32_t tx, int32_t ty, int32_t rad, int32_t caused_by, C4Object *by_object, int32_t iMaxDensity)
504 {
505  std::vector<int32_t> vertices(GetRoundPolygon(tx, ty, rad, 30));
506  BlastFreeShape(&vertices[0], vertices.size(), by_object, caused_by, iMaxDensity);
507 }
508 
509 void C4Landscape::ShakeFree(int32_t tx, int32_t ty, int32_t rad)
510 {
511  std::vector<int32_t> vertices(GetRoundPolygon(tx, ty, rad, 50));
512  p->ForPolygon(this, &vertices[0], vertices.size() / 2, [this](int32_t x, int32_t y) { return p->ShakeFreePix(this, x, y); });
513 }
514 
516 {
517  // Remember any in-earth objects in area
518  C4FindObjectInRect fo_inrect(BoundingBox);
519  C4FindObjectOCF fo_insolid(OCF_InSolid);
520  C4FindObjectLayer fo_layer(by_object ? by_object->Layer : nullptr);
521  C4FindObject *fo_list[] = { &fo_inrect, &fo_insolid, &fo_layer };
522  C4FindObjectAndStatic fo_srch(3, fo_list);
523  return fo_srch.FindMany(::Objects, ::Objects.Sectors);
524 }
525 
527 {
528  // Do callbacks to digger and dug out objects for objects that are now dug free
529  if (by_object)
530  {
531  for (int32_t i = 0; i < dig_objects->GetSize(); ++i)
532  {
533  C4Object *dig_object = dig_objects->GetItem(i).getObj();
534  if (dig_object && !GBackSolid(dig_object->GetX(), dig_object->GetY()))
535  if (dig_object != by_object)
536  if (!dig_object->Contained && dig_object->Status)
537  {
538  C4AulParSet pars(by_object);
539  dig_object->Call(PSF_OnDugOut, &pars);
540  if (dig_object->Status && by_object->Status)
541  {
542  C4AulParSet pars(dig_object);
543  by_object->Call(PSF_DigOutObject, &pars);
544  }
545  }
546  }
547  }
548 }
549 
550 int32_t C4Landscape::DigFreeShape(int *vtcs, int length, C4Object *by_object, bool no_dig2objects, bool no_instability_check)
551 {
552  using namespace std::placeholders;
553 
554  C4Rect BoundingBox = getBoundingBox(vtcs, length);
555  int32_t amount;
556 
557  // Remember any collectible objects in area
558  std::unique_ptr<C4ValueArray> dig_objects(p->PrepareFreeShape(BoundingBox, by_object));
559 
560  std::function<bool(int32_t, int32_t)> callback;
561  if (no_instability_check)
562  callback = [this](int32_t x, int32_t y) { return p->DigFreePixNoInstability(this, x, y); };
563  else
564  callback = [this](int32_t x, int32_t y) { return p->DigFreePix(this, x, y); };
565 
566  if (by_object)
567  {
568  if (!by_object->MaterialContents)
569  by_object->MaterialContents = new C4MaterialList;
570  amount = p->ForPolygon(this, vtcs, length / 2, callback, by_object->MaterialContents);
571  }
572  else
573  amount = p->ForPolygon(this, vtcs, length / 2, callback, nullptr);
574 
575  // create objects from the material
576  if (!::Game.iTick5)
577  {
578  if (!no_dig2objects)
579  if (by_object)
580  if (by_object->MaterialContents)
581  {
582  int32_t tx = BoundingBox.GetMiddleX(), ty = BoundingBox.GetBottom();
583  p->DigMaterial2Objects(tx, ty, by_object->MaterialContents, by_object);
584  }
585  }
586 
587  // Do callbacks to digger for objects that are now dug free
588  p->PostFreeShape(dig_objects.get(), by_object);
589 
590  return amount;
591 }
592 
593 void C4Landscape::BlastFreeShape(int *vtcs, int length, C4Object *by_object, int32_t by_player, int32_t iMaxDensity)
594 {
595  C4MaterialList *MaterialContents = nullptr;
596 
597  C4Rect BoundingBox = getBoundingBox(vtcs, length);
598 
599  // Remember any collectible objects in area
600  std::unique_ptr<C4ValueArray> dig_objects(p->PrepareFreeShape(BoundingBox, by_object));
601 
602  uint8_t *pblast_tbl = nullptr, blast_tbl[C4M_MaxTexIndex];
603  if (iMaxDensity < C4M_Vehicle)
604  {
605  for (int32_t i = 0; i < C4M_MaxTexIndex; ++i) blast_tbl[i] = (GetPixDensity(i) <= iMaxDensity);
606  pblast_tbl = blast_tbl;
607  }
608 
609  if (by_object)
610  {
611  if (!by_object->MaterialContents)
612  by_object->MaterialContents = new C4MaterialList;
613  p->ForPolygon(this, vtcs, length / 2, [this](int32_t x, int32_t y) { return p->BlastFreePix(this, x, y); }, by_object->MaterialContents, 0, 0, pblast_tbl);
614  }
615  else
616  {
617  MaterialContents = new C4MaterialList;
618  p->ForPolygon(this, vtcs, length / 2, [this](int32_t x, int32_t y) { return p->BlastFreePix(this, x, y); }, MaterialContents, iMaxDensity);
619  }
620 
621  // create objects from the material
622  C4MaterialList *mat_list = nullptr;
623  if (by_object)
624  mat_list = by_object->MaterialContents;
625  else
626  mat_list = MaterialContents;
627 
628  int32_t tx = BoundingBox.GetMiddleX(), ty = BoundingBox.GetMiddleY();
629  p->BlastMaterial2Objects(tx, ty, mat_list, by_player, (BoundingBox.Wdt + BoundingBox.Hgt) / 4, dig_objects.get());
630 
631  if (MaterialContents) delete MaterialContents;
632 
633  // Do callbacks to digger for objects that are now dug free
634  p->PostFreeShape(dig_objects.get(), by_object);
635 }
636 
637 void C4Landscape::P::BlastMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, int32_t caused_by, int32_t str, C4ValueArray *out_objects)
638 {
639  for (int32_t mat = 0; mat < ::MaterialMap.Num; mat++)
640  {
641  if (mat_list->Amount[mat])
642  {
643  int32_t cast_strength = str;
644  int32_t pxsamount = 0, blastamount = 0;
645 
646  if (::MaterialMap.Map[mat].Blast2PXSRatio != 0)
647  {
648  pxsamount = mat_list->Amount[mat] / ::MaterialMap.Map[mat].Blast2PXSRatio;
649  ::PXS.Cast(mat, pxsamount, tx, ty, cast_strength * 2);
650  }
651 
652  if (::MaterialMap.Map[mat].Blast2Object != C4ID::None)
653  {
654  if (::MaterialMap.Map[mat].Blast2ObjectRatio != 0)
655  {
656  blastamount = mat_list->Amount[mat] / ::MaterialMap.Map[mat].Blast2ObjectRatio;
657  Game.CastObjects(::MaterialMap.Map[mat].Blast2Object, nullptr, blastamount, cast_strength, tx, ty, NO_OWNER, caused_by, out_objects);
658  }
659  }
660 
661  mat_list->Amount[mat] -= std::max(blastamount * ::MaterialMap.Map[mat].Blast2ObjectRatio,
662  pxsamount * ::MaterialMap.Map[mat].Blast2PXSRatio);
663  }
664  }
665 }
666 
667 void C4Landscape::P::DigMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, C4Object *pCollect)
668 {
669  C4AulParSet pars(pCollect);
670  for (int32_t mat = 0; mat < ::MaterialMap.Num; mat++)
671  {
672  if (mat_list->Amount[mat])
673  {
674  if (::MaterialMap.Map[mat].Dig2Object != C4ID::None)
675  if (::MaterialMap.Map[mat].Dig2ObjectRatio != 0)
676  while (mat_list->Amount[mat] >= ::MaterialMap.Map[mat].Dig2ObjectRatio)
677  {
678  mat_list->Amount[mat] -= ::MaterialMap.Map[mat].Dig2ObjectRatio;
679  C4Object *pObj = Game.CreateObject(::MaterialMap.Map[mat].Dig2Object, nullptr, NO_OWNER, tx, ty);
680  if (!pObj || !pObj->Status) continue;
681  // Set controller to the controller of the object responsible for digging out
682  if (pCollect && pCollect->Status)
683  pObj->Controller = pCollect->Controller;
684  // Do callbacks to dug object and digger
685  pObj->Call(PSF_OnDugOut, &pars);
686  if (!pObj->Status || !pCollect || !pCollect->Status || pObj->Contained) continue;
687  C4AulParSet pars(C4VObj(pObj));
688  pCollect->Call(PSF_DigOutObject, &pars);
689  if (!pObj->Status || !pCollect->Status || pObj->Contained) continue;
690  // Try to collect object
691  if (::MaterialMap.Map[mat].Dig2ObjectCollect)
692  if (pCollect && pCollect->Status && pObj && pObj->Status)
693  if (!pCollect->Collect(pObj))
694  // Collection forced? Don't generate objects
695  if (::MaterialMap.Map[mat].Dig2ObjectCollect == 2)
696  {
697  pObj->AssignRemoval();
698  // Cap so we never have more than one object worth of material in the store
699  mat_list->Amount[mat] = ::MaterialMap.Map[mat].Dig2ObjectRatio;
700  break;
701  }
702  }
703  }
704  }
705 }
706 
707 bool C4Landscape::P::DigFreePixNoInstability(C4Landscape *d, int32_t tx, int32_t ty)
708 {
709  int32_t mat = d->GetMat(tx, ty);
710  if (MatValid(mat))
711  if (::MaterialMap.Map[mat].DigFree)
712  {
713  d->ClearPix(tx, ty);
714  return true;
715  }
716  return false;
717 }
718 
719 bool C4Landscape::P::DigFreePix(C4Landscape *d, int32_t tx, int32_t ty)
720 {
721  int32_t mat = d->GetMat(tx, ty);
722  if (MatValid(mat))
723  if (::MaterialMap.Map[mat].DigFree)
724  {
725  d->ClearPix(tx, ty);
726  // check for instable materials to start moving by the cleared space
727  d->CheckInstabilityRange(tx, ty);
728  return true;
729  }
730  return false;
731 }
732 
733 bool C4Landscape::P::BlastFreePix(C4Landscape *d, int32_t tx, int32_t ty)
734 {
735  int32_t mat = d->GetMat(tx, ty);
736  if (MatValid(mat))
737  {
738  // for blast, either shift to different material or blast free
739  if (::MaterialMap.Map[mat].BlastFree)
740  {
741  d->ClearPix(tx, ty);
742  // check for instable materials to start moving by the cleared space
743  d->CheckInstabilityRange(tx, ty);
744  return true;
745  }
746  else
747  if (::MaterialMap.Map[mat].BlastShiftTo)
749  }
750  return false;
751 }
752 
753 bool C4Landscape::P::ShakeFreePix(C4Landscape *d, int32_t tx, int32_t ty)
754 {
755  int32_t mat = d->GetMat(tx, ty);
756  if (MatValid(mat))
757  {
758  if (::MaterialMap.Map[mat].DigFree)
759  {
760  d->ClearPix(tx, ty);
761  ::PXS.Create(mat, itofix(tx), itofix(ty));
762  // check for instable materials to start moving by the cleared space
763  d->CheckInstabilityRange(tx, ty);
764  return true;
765  }
766  }
767  return false;
768 }
769 
770 bool C4Landscape::ClearPix(int32_t tx, int32_t ty)
771 {
772  // Replace pixel with background pixel
773  BYTE bkgPix = p->Surface8Bkg->GetPix(tx, ty);
774  return SetPix2(tx, ty, bkgPix, bkgPix);
775 }
776 
778 {
779  if (p->pFoW) p->pFoW->Remove(pObj);
780 }
781 
782 bool C4Landscape::SetPix2(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
783 {
784  // check bounds
785  if (x < 0 || y < 0 || x >= GetWidth() || y >= GetHeight())
786  return false;
787  // no change?
788  if ((fgPix == Transparent || fgPix == _GetPix(x, y)) && (bgPix == Transparent || bgPix == p->Surface8Bkg->_GetPix(x, y)))
789  return true;
790  // set pixel
791  return _SetPix2(x, y, fgPix, bgPix);
792 }
793 
794 bool C4Landscape::_SetPix2(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
795 {
796  if (Config.General.DebugRec)
797  {
798  C4RCSetPix rc;
799  rc.x = x; rc.y = y; rc.clr = fgPix; rc.bgClr = fgPix;
800  AddDbgRec(RCT_SetPix, &rc, sizeof(rc));
801  }
802  assert(x >= 0 && y >= 0 && x < GetWidth() && y < GetHeight());
803  // get pixel and resolve transparency to already existing pixel
804  BYTE opix = _GetPix(x, y);
805  if (fgPix == Transparent) fgPix = opix;
806  if (bgPix == Transparent) bgPix = p->Surface8Bkg->_GetPix(x, y);
807  // check pixel
808  if (fgPix == opix && bgPix == p->Surface8Bkg->_GetPix(x, y)) return true;
809  // count pixels
810  if (p->Pix2Dens[fgPix])
811  {
812  if (!p->Pix2Dens[opix]) p->PixCnt[(y / 15) + (x / 17) * p->PixCntPitch]++;
813  }
814  else
815  {
816  if (p->Pix2Dens[opix]) p->PixCnt[(y / 15) + (x / 17) * p->PixCntPitch]--;
817  }
818  // count material
819  assert(!fgPix || MatValid(p->Pix2Mat[fgPix]));
820  int32_t omat = p->Pix2Mat[opix], nmat = p->Pix2Mat[fgPix];
821  if (opix) p->MatCount[omat]--;
822  if (fgPix) p->MatCount[nmat]++;
823  // count effective material
824  if (omat != nmat)
825  {
826  if (fgPix && ::MaterialMap.Map[nmat].MinHeightCount)
827  {
828  // Check for material above & below
829  int iMinHeight = ::MaterialMap.Map[nmat].MinHeightCount,
830  iBelow = GetMatHeight(x, y + 1, +1, nmat, iMinHeight),
831  iAbove = GetMatHeight(x, y - 1, -1, nmat, iMinHeight);
832  // Will be above treshold?
833  if (iBelow + iAbove + 1 >= iMinHeight)
834  {
835  int iChange = 1;
836  // Check for heights below threshold
837  if (iBelow < iMinHeight) iChange += iBelow;
838  if (iAbove < iMinHeight) iChange += iAbove;
839  // Change
840  p->EffectiveMatCount[nmat] += iChange;
841  }
842  }
843  if (opix && ::MaterialMap.Map[omat].MinHeightCount)
844  {
845  // Check for material above & below
846  int iMinHeight = ::MaterialMap.Map[omat].MinHeightCount,
847  iBelow = GetMatHeight(x, y + 1, +1, omat, iMinHeight),
848  iAbove = GetMatHeight(x, y - 1, -1, omat, iMinHeight);
849  // Not already below threshold?
850  if (iBelow + iAbove + 1 >= iMinHeight)
851  {
852  int iChange = 1;
853  // Check for heights that will get below threshold
854  if (iBelow < iMinHeight) iChange += iBelow;
855  if (iAbove < iMinHeight) iChange += iAbove;
856  // Change
857  p->EffectiveMatCount[omat] -= iChange;
858  }
859  }
860  }
861  // set 8bpp-surface only!
862  p->Surface8->SetPix(x, y, fgPix);
863  p->Surface8Bkg->SetPix(x, y, bgPix);
864  // note for relight
865  if (p->pLandscapeRender)
866  {
867  C4Rect CheckRect = p->pLandscapeRender->GetAffectedRect(C4Rect(x, y, 1, 1));
868  for (int32_t i = 0; i < C4LS_MaxRelights; i++)
869  if (!p->Relights[i].Wdt || p->Relights[i].Overlap(CheckRect) || i + 1 >= C4LS_MaxRelights)
870  {
871  p->Relights[i].Add(CheckRect);
872  break;
873  }
874  // Invalidate FoW
875  if (p->pFoW)
876  p->pFoW->Invalidate(CheckRect);
877  }
878  // success
879  return true;
880 }
881 
882 void C4Landscape::_SetPix2Tmp(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
883 {
884  // set 8bpp-surface only!
885  assert(x >= 0 && y >= 0 && x < GetWidth() && y < GetHeight());
886  if (fgPix != Transparent) p->Surface8->SetPix(x, y, fgPix);
887  if (bgPix != Transparent) p->Surface8Bkg->SetPix(x, y, bgPix);
888 }
889 
890 bool C4Landscape::CheckInstability(int32_t tx, int32_t ty, int32_t recursion_count)
891 {
892  int32_t mat = GetMat(tx, ty);
893  if (MatValid(mat)) {
894  const C4Material &material = MaterialMap.Map[mat];
895  if (material.Instable)
896  return ::MassMover.Create(tx, ty);
897  // Get rid of single pixels
898  else if (DensitySolid(material.Density) && !material.KeepSinglePixels && recursion_count < 10)
899  if ((!::GBackSolid(tx, ty + 1)) + (!::GBackSolid(tx, ty - 1)) + (!::GBackSolid(tx + 1, ty)) + (!::GBackSolid(tx - 1, ty)) >= 3)
900  {
901  if (!ClearPix(tx, ty)) return false;
902  // Diggable material drops; other material just gets removed
903  if (material.DigFree) ::PXS.Create(mat, itofix(tx), itofix(ty));
904  // check other pixels around this
905  // Note this cannot lead to an endless recursion (unless you do funny stuff like e.g. set DigFree=1 in material Tunnel).
906  // Check recursion anyway, because very large strips of single pixel width might cause sufficient recursion to crash
907  CheckInstability(tx + 1, ty, ++recursion_count);
908  CheckInstability(tx - 1, ty, recursion_count);
909  CheckInstability(tx, ty - 1, recursion_count);
910  CheckInstability(tx, ty + 1, recursion_count);
911  return true;
912  }
913  }
914  return false;
915 }
916 
917 void C4Landscape::CheckInstabilityRange(int32_t tx, int32_t ty)
918 {
919  if (!CheckInstability(tx, ty))
920  {
921  CheckInstability(tx, ty - 1);
922  CheckInstability(tx, ty - 2);
923  CheckInstability(tx - 1, ty);
924  CheckInstability(tx + 1, ty);
925  }
926 }
927 void C4Landscape::DrawMaterialRect(int32_t mat, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
928 {
929  int32_t cx, cy;
930  for (cy = ty; cy < ty + hgt; cy++)
931  for (cx = tx; cx < tx + wdt; cx++)
932  if ((MatDensity(mat) >= GetDensity(cx, cy)))
933  SetPix2(cx, cy, Mat2PixColDefault(mat), p->Surface8Bkg->GetPix(cx, cy));
934 }
935 
936 void C4Landscape::RaiseTerrain(int32_t tx, int32_t ty, int32_t wdt)
937 {
938  int32_t cx, cy;
939  BYTE cpix;
940  for (cx = tx; cx < tx + wdt; cx++)
941  {
942  for (cy = ty; (cy + 1 < ::Landscape.GetHeight()) && !GBackSolid(cx, cy + 1); cy++) {}
943  if (cy + 1 < ::Landscape.GetHeight()) if (cy - ty < 20)
944  {
945  cpix = GetPix(cx, cy + 1);
946  if (!MatVehicle(PixCol2Mat(cpix)))
947  while (cy >= ty) { SetPix2(cx, cy, cpix, GetBackPix(cx, cy + 1)); cy--; }
948  }
949  }
950 }
951 
952 
953 int32_t C4Landscape::ExtractMaterial(int32_t fx, int32_t fy, bool distant_first)
954 {
955  int32_t mat = GetMat(fx, fy);
956  if (mat == MNone) return MNone;
957  FindMatTop(mat, fx, fy, distant_first);
958  ClearPix(fx, fy);
959  CheckInstabilityRange(fx, fy);
960  return mat;
961 }
962 
963 bool C4Landscape::InsertMaterialOutsideLandscape(int32_t tx, int32_t ty, int32_t mdens)
964 {
965  // Out-of-bounds insertion considered successful if inserted into same or lower density
966  // This ensures pumping out of map works
967  // Do allow insertion into same density because it covers the case of e.g. pumping water into the upper ocean of an underwater scenario
968  return GetDensity(tx, ty) <= mdens;
969 }
970 
971 bool C4Landscape::InsertMaterial(int32_t mat, int32_t *tx, int32_t *ty, int32_t vx, int32_t vy, bool query_only)
972 {
973  assert(tx); assert(ty);
974  int32_t mdens;
975  if (!MatValid(mat)) return false;
976  mdens = std::min(MatDensity(mat), C4M_Solid);
977  if (!mdens) return true;
978 
979  // Bounds
980  if (!Inside<int32_t>(*tx, 0, GetWidth() - 1) || !Inside<int32_t>(*ty, 0, GetHeight())) return InsertMaterialOutsideLandscape(*tx, *ty, mdens);
981 
983  {
984  // Same or higher density?
985  if (GetDensity(*tx, *ty) >= mdens)
986  // Push
987  if (!FindMatPathPush(*tx, *ty, mdens, ::MaterialMap.Map[mat].MaxSlide, !!::MaterialMap.Map[mat].Instable))
988  // Or die
989  return false;
990  }
991  else
992  {
993  // Move up above same density
994  while (mdens == std::min(GetDensity(*tx, *ty), C4M_Solid))
995  {
996  (*ty)--; if (*ty < 0) return false;
997  // Primitive slide (1)
998  if (GetDensity(*tx - 1, *ty) < mdens) (*tx)--;
999  if (GetDensity(*tx + 1, *ty) < mdens) (*tx)++;
1000  }
1001  // Stuck in higher density
1002  if (GetDensity(*tx, *ty) > mdens) return false;
1003  }
1004 
1005  // Try slide
1006  while (FindMatSlide(*tx, *ty, +1, mdens, ::MaterialMap.Map[mat].MaxSlide))
1007  if (GetDensity(*tx, *ty + 1) < mdens)
1008  {
1009  if (!query_only)
1010  return ::PXS.Create(mat, itofix(*tx), itofix(*ty), C4REAL10(vx), C4REAL10(vy));
1011  return true;
1012  }
1013 
1014  if (query_only)
1015  {
1016  // since tx and ty changed, we need to re-check the bounds here
1017  // if we really inserted it, the check is made again in InsertDeadMaterial
1018  if (!Inside<int32_t>(*tx, 0, GetWidth() - 1) || !Inside<int32_t>(*ty, 0, GetHeight())) return InsertMaterialOutsideLandscape(*tx, *ty, mdens);
1019  return true;
1020  }
1021 
1022  // Try reaction with material below and at insertion position
1023  C4MaterialReaction *pReact; int32_t tmat;
1024  int32_t check_dir = 0;
1025  for (int32_t i = 0; i < 2; ++i)
1026  {
1027  if ((pReact = ::MaterialMap.GetReactionUnsafe(mat, tmat = GetMat(*tx, *ty + check_dir))))
1028  {
1029  C4Real fvx = C4REAL10(vx), fvy = C4REAL10(vy);
1030  if ((*pReact->pFunc)(pReact, *tx, *ty, *tx, *ty + check_dir, fvx, fvy, mat, tmat, meePXSPos, nullptr))
1031  {
1032  // the material to be inserted killed itself in some material reaction below
1033  return true;
1034  }
1035  }
1036  if (!(check_dir = Sign(GravAccel))) break;
1037  }
1038 
1039  // Insert as dead material
1040  return InsertDeadMaterial(mat, *tx, *ty);
1041 }
1042 
1043 bool C4Landscape::InsertDeadMaterial(int32_t mat, int32_t tx, int32_t ty)
1044 {
1045  // Check bounds
1046  if (tx < 0 || ty < 0 || tx >= GetWidth() || ty >= GetHeight())
1047  return InsertMaterialOutsideLandscape(tx, ty, std::min(MatDensity(mat), C4M_Solid));
1048 
1049  // Save back original material so we can insert it later
1050  int omat = 0;
1052  omat = GetMat(tx, ty);
1053 
1054  // Check surroundings for inspiration for texture to use
1055  int n = 0; int pix = -1;
1056  if (tx > 0 && _GetMat(tx - 1, ty) == mat)
1057  if (!Random(++n)) pix = _GetPix(tx - 1, ty);
1058  if (ty > 0 && _GetMat(tx, ty - 1) == mat)
1059  if (!Random(++n)) pix = _GetPix(tx, ty - 1);
1060  if (tx + 1 < GetWidth() && _GetMat(tx + 1, ty) == mat)
1061  if (!Random(++n)) pix = _GetPix(tx + 1, ty);
1062  if (ty + 1 < GetHeight() && _GetMat(tx, ty + 1) == mat)
1063  if (!Random(++n)) pix = _GetPix(tx, ty + 1);
1064  if (pix < 0)
1065  pix = Mat2PixColDefault(mat);
1066 
1067  // Insert dead material
1068  SetPix2(tx, ty, pix, Transparent);
1069 
1070  // Search a position for the old material pixel
1072  {
1073  int32_t tyo = ty - 1;
1074  InsertMaterial(omat, &tx, &tyo);
1075  }
1076 
1077  return true;
1078 }
1079 
1080 bool C4Landscape::Incinerate(int32_t x, int32_t y, int32_t cause_player)
1081 {
1082  int32_t mat = GetMat(x, y);
1083  if (MatValid(mat))
1084  if (::MaterialMap.Map[mat].Inflammable)
1085  {
1086  C4AulParSet pars(C4VInt(x), C4VInt(y), C4VInt(cause_player));
1088  }
1089  return false;
1090 }
1091 
1093 {
1095 }
1096 
1097 std::unique_ptr<CSurface8> C4Landscape::P::CreateDefaultBkgSurface(CSurface8& sfcFg, bool msbAsIft) const
1098 {
1099  auto sfcBg = std::make_unique<CSurface8>();
1100  if (!sfcBg->Create(sfcFg.Wdt, sfcFg.Hgt))
1101  {
1102  return nullptr;
1103  }
1104 
1105  for (int32_t y = 0; y < sfcFg.Hgt; ++y)
1106  {
1107  for (int32_t x = 0; x < sfcFg.Wdt; ++x)
1108  {
1109  BYTE fgPix = sfcFg._GetPix(x, y);
1110  BYTE bgPix;
1111 
1112  // If we treat the most significant bit as the IFT flag
1113  // (compatibility option for pre-7.0 maps), then set
1114  // the background pixel to 0 if the foreground does not
1115  // have IFT set, and remove the IFT flag from the
1116  // foreground pixel.
1117  if (msbAsIft)
1118  {
1119  if (fgPix & 0x80)
1120  {
1121  fgPix &= ~0x80;
1122  sfcFg._SetPix(x, y, fgPix);
1123  bgPix = DefaultBkgMat(fgPix);
1124  }
1125  else
1126  {
1127  bgPix = 0;
1128  }
1129  }
1130  else
1131  {
1132  bgPix = DefaultBkgMat(fgPix);
1133  }
1134 
1135  sfcBg->_SetPix(x, y, bgPix);
1136  }
1137  }
1138 
1139  return sfcBg;
1140 }
1141 
1142 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
1143 /* ++++ Polygon drawing code extracted from ALLEGRO by Shawn Hargreaves ++++ */
1144 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
1145 
1146 struct CPolyEdge // An edge for the polygon drawer
1147 {
1148  int y; // Current (starting at the top) y position
1149  int bottom; // bottom y position of this edge
1150  int x; // Fixed point x position
1151  int dx; // Fixed point x gradient
1152  int w; // Width of line segment
1153  struct CPolyEdge *prev; // Doubly linked list
1154  struct CPolyEdge *next;
1155 };
1156 
1157 #define POLYGON_FIX_SHIFT 16
1158 
1159 static void fill_edge_structure(CPolyEdge *edge, int *i1, int *i2)
1160 {
1161  if (i2[1] < i1[1]) // Swap
1162  {
1163  int *t = i1; i1 = i2; i2 = t;
1164  }
1165  edge->y = i1[1];
1166  edge->bottom = i2[1] - 1;
1167  edge->dx = ((i2[0] - i1[0]) << POLYGON_FIX_SHIFT) / (i2[1] - i1[1]);
1168  edge->x = (i1[0] << POLYGON_FIX_SHIFT) + (1 << (POLYGON_FIX_SHIFT - 1)) - 1;
1169  edge->prev = nullptr;
1170  edge->next = nullptr;
1171  if (edge->dx < 0)
1172  edge->x += std::min<int>(edge->dx + (1 << POLYGON_FIX_SHIFT), 0);
1173  edge->w = std::max<int>(Abs(edge->dx) - (1 << POLYGON_FIX_SHIFT), 0);
1174 }
1175 
1176 static CPolyEdge *add_edge(CPolyEdge *list, CPolyEdge *edge, int sort_by_x)
1177 {
1178  CPolyEdge *pos = list;
1179  CPolyEdge *prev = nullptr;
1180  if (sort_by_x)
1181  {
1182  while ((pos) && (pos->x + pos->w / 2 < edge->x + edge->w / 2))
1183  {
1184  prev = pos; pos = pos->next;
1185  }
1186  }
1187  else
1188  {
1189  while ((pos) && (pos->y < edge->y))
1190  {
1191  prev = pos; pos = pos->next;
1192  }
1193  }
1194  edge->next = pos;
1195  edge->prev = prev;
1196  if (pos) pos->prev = edge;
1197  if (prev) { prev->next = edge; return list; }
1198  else return edge;
1199 }
1200 
1201 static CPolyEdge *remove_edge(CPolyEdge *list, CPolyEdge *edge)
1202 {
1203  if (edge->next) edge->next->prev = edge->prev;
1204  if (edge->prev) { edge->prev->next = edge->next; return list; }
1205  else return edge->next;
1206 }
1207 
1208 // Global polygon quick buffer
1209 const int QuickPolyBufSize = 20;
1211 
1212 int32_t C4Landscape::P::ForPolygon(C4Landscape *d, int *vtcs, int length, const std::function<bool(int32_t, int32_t)> &callback,
1213  C4MaterialList *mats_count, uint8_t col, uint8_t colBkg, uint8_t *conversion_table)
1214 {
1215  // Variables for polygon drawer
1216  int c, x1, x2, y;
1217  int top = INT_MAX;
1218  int bottom = INT_MIN;
1219  int *i1, *i2;
1220  CPolyEdge *edge, *next_edge, *edgebuf;
1221  CPolyEdge *active_edges = nullptr;
1222  CPolyEdge *inactive_edges = nullptr;
1223  bool use_qpb = false;
1224 
1225  // Return value
1226  int32_t amount = 0;
1227  // Poly Buf
1228  if (length <= QuickPolyBufSize)
1229  {
1230  edgebuf = QuickPolyBuf; use_qpb = true;
1231  }
1232  else if (!(edgebuf = new CPolyEdge[length])) { return 0; }
1233 
1234  // Fill the edge table
1235  edge = edgebuf;
1236  i1 = vtcs;
1237  i2 = vtcs + (length - 1) * 2;
1238  for (c = 0; c < length; c++)
1239  {
1240  if (i1[1] != i2[1])
1241  {
1242  fill_edge_structure(edge, i1, i2);
1243  if (edge->bottom >= edge->y)
1244  {
1245  if (edge->y < top) top = edge->y;
1246  if (edge->bottom > bottom) bottom = edge->bottom;
1247  inactive_edges = add_edge(inactive_edges, edge, false);
1248  edge++;
1249  }
1250  }
1251  i2 = i1; i1 += 2;
1252  }
1253 
1254  // For each scanline in the polygon...
1255  for (c = top; c <= bottom; c++)
1256  {
1257  // Check for newly active edges
1258  edge = inactive_edges;
1259  while ((edge) && (edge->y == c))
1260  {
1261  next_edge = edge->next;
1262  inactive_edges = remove_edge(inactive_edges, edge);
1263  active_edges = add_edge(active_edges, edge, true);
1264  edge = next_edge;
1265  }
1266 
1267  // Draw horizontal line segments
1268  edge = active_edges;
1269  while ((edge) && (edge->next))
1270  {
1271  x1 = edge->x >> POLYGON_FIX_SHIFT;
1272  x2 = (edge->next->x + edge->next->w) >> POLYGON_FIX_SHIFT;
1273  y = c;
1274  // Fix coordinates
1275  if (x1 > x2) std::swap(x1, x2);
1276  // Set line
1277  if (callback)
1278  {
1279  for (int xcnt = x2 - x1 - 1; xcnt >= 0; xcnt--)
1280  {
1281  uint8_t pix = d->GetPix(x1 + xcnt, y);
1282  if (!conversion_table || conversion_table[pix])
1283  {
1284  int32_t mat = d->GetPixMat(pix);
1285  if (callback(x1 + xcnt, y))
1286  if (mats_count)
1287  {
1288  mats_count->Add(mat, 1);
1289  amount++;
1290  }
1291  }
1292  }
1293  }
1294  else if (conversion_table)
1295  for (int xcnt = x2 - x1 - 1; xcnt >= 0; xcnt--)
1296  {
1297  const uint8_t pix = conversion_table[uint8_t(d->GetPix(x1 + xcnt, y))];
1298  Surface8->SetPix(x1 + xcnt, y, pix);
1299  if (colBkg != Transparent) Surface8Bkg->SetPix(x1 + xcnt, y, colBkg);
1300  }
1301  else
1302  for (int xcnt = x2 - x1 - 1; xcnt >= 0; xcnt--)
1303  {
1304  if (col != Transparent) Surface8->SetPix(x1 + xcnt, y, col);
1305  if (colBkg != Transparent) Surface8Bkg->SetPix(x1 + xcnt, y, colBkg);
1306  }
1307  edge = edge->next->next;
1308  }
1309 
1310  // Update edges, sorting and removing dead ones
1311  edge = active_edges;
1312  while (edge)
1313  {
1314  next_edge = edge->next;
1315  if (c >= edge->bottom)
1316  {
1317  active_edges = remove_edge(active_edges, edge);
1318  }
1319  else
1320  {
1321  edge->x += edge->dx;
1322  while ((edge->prev) && (edge->x + edge->w / 2 < edge->prev->x + edge->prev->w / 2))
1323  {
1324  if (edge->next) edge->next->prev = edge->prev;
1325  edge->prev->next = edge->next;
1326  edge->next = edge->prev;
1327  edge->prev = edge->prev->prev;
1328  edge->next->prev = edge;
1329  if (edge->prev) edge->prev->next = edge;
1330  else active_edges = edge;
1331  }
1332  }
1333  edge = next_edge;
1334  }
1335  }
1336 
1337  // Clear scratch memory
1338  if (!use_qpb) delete[] edgebuf;
1339 
1340  return amount;
1341 }
1342 
1343 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
1344 /* +++++++++++++++++++++++++ Save, Init and load +++++++++++++++++++++++ */
1345 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
1346 
1348 {
1349  // Gravity
1351 }
1352 
1353 void C4Landscape::Clear(bool fClearMapCreator, bool fClearSky, bool fClearRenderer)
1354 {
1355  if (fClearMapCreator) { p->pMapCreator.reset(); }
1356  // clear sky
1357  if (fClearSky) p->Sky.Clear();
1358  // clear surfaces, if assigned
1359  if (fClearRenderer) { p->pLandscapeRender.reset(); }
1360  p->TopRowPix.clear();
1361  p->BottomRowPix.clear();
1362  p->LeftColPix.clear();
1363  p->RightColPix.clear();
1364  p->Surface8.reset();
1365  p->Surface8Bkg.reset();
1366  p->Map.reset();
1367  p->MapBkg.reset();
1368  // clear initial landscape
1369  p->pInitial.reset();
1370  p->pInitialBkg.reset();
1371  p->pFoW.reset();
1372  // clear relight array
1373  for (auto &relight : p->Relights)
1374  relight.Default();
1375  // clear scan
1376  p->ScanX = 0;
1377  p->mode = LandscapeMode::Undefined;
1378  // clear pixel count
1379  p->PixCnt.clear();
1380  p->PixCntPitch = 0;
1381  // clear bridge material conversion temp buffers
1382  for (auto &conv : p->BridgeMatConversion)
1383  conv.reset();
1384 }
1385 
1387 {
1388  pComp->Value(mkNamingAdapt(p->MapSeed, "MapSeed", 0));
1389  pComp->Value(mkNamingAdapt(mkCastIntAdapt(p->Gravity), "Gravity", DefaultGravAccel));
1390  pComp->Value(mkNamingAdapt(p->Modulation, "MatModulation", 0U));
1391  pComp->Value(mkNamingAdapt(mkCastIntAdapt(p->mode), "Mode", LandscapeMode::Undefined));
1392 
1393  if (pComp->isDeserializer())
1394  {
1395  int32_t ambient_brightness;
1396  pComp->Value(mkNamingAdapt(ambient_brightness, "AmbientBrightness", 255));
1397  if (p->pFoW) p->pFoW->Ambient.SetBrightness(ambient_brightness / static_cast<double>(255));
1398  }
1399  else
1400  {
1401  if (p->pFoW)
1402  {
1403  int32_t ambient_brightness = static_cast<int32_t>(p->pFoW->Ambient.GetBrightness() * 255 + 0.5);
1404  pComp->Value(mkNamingAdapt(ambient_brightness, "AmbientBrightness", 255));
1405  }
1406  }
1407 }
1408 
1409 static std::unique_ptr<CSurface8> GroupReadSurface8(C4Group &hGroup, const char *szWildCard)
1410 {
1411  if (!hGroup.AccessEntry(szWildCard))
1412  return nullptr;
1413  // create surface
1414  auto pSfc = std::make_unique<CSurface8>();
1415  if (!pSfc->Read(hGroup))
1416  {
1417  return nullptr;
1418  }
1419  return pSfc;
1420 }
1421 
1422 bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bool &rfLoaded, bool fSavegame)
1423 {
1424  // set map seed, if not pre-assigned
1425  if (!p->MapSeed) p->MapSeed = Random(3133700);
1426 
1427  // increase max map size, since developers might set a greater one here
1428  Game.C4S.Landscape.MapWdt.Max = 10000;
1429  Game.C4S.Landscape.MapHgt.Max = 10000;
1430 
1431  // map and landscape must be initialized with fixed random, so runtime joining clients may recreate it
1432  // with same seed
1433  // after map/landscape creation, the seed must be fixed again, so there's no difference between clients creating
1434  // and not creating the map
1435  // this, however, would cause syncloss to DebugRecs
1437 
1439 
1440  // map is like it's loaded for regular gamestart
1441  // but it's changed and would have to be saved if a new section is loaded
1442  p->fMapChanged = fOverloadCurrent;
1443 
1444  // don't change landscape mode in runtime joins
1445  bool fLandscapeModeSet = (p->mode != LandscapeMode::Undefined);
1446 
1447  // Make pixel maps
1448  // Pixel maps only depend on loaded materials and textures
1449  // They might be accessed in map scripts, so they should be ready before map creation
1450  UpdatePixMaps();
1451 
1452  Game.SetInitProgress(60);
1453  // create map if necessary
1455  {
1456  std::unique_ptr<CSurface8> sfcMap, sfcMapBkg;
1457 
1458  // Static map from scenario: Old-style Map.bmp with highest bit IFT
1459  if ((sfcMap = GroupReadSurface8(hGroup, C4CFN_Map)))
1460  {
1461  if (!fLandscapeModeSet) p->mode = LandscapeMode::Static;
1462  sfcMapBkg = p->CreateDefaultBkgSurface(*sfcMap, true);
1463  if (!sfcMapBkg) return false;
1464  }
1465 
1466  // Static map from scenario: New-style MapFg.bmp and MapBg.bmp with
1467  // full 255 mat-tex combinations. Background map is optional, if not
1468  // given default will be created with tunnel background for all
1469  // semisolid pixels.
1470  if (!sfcMap)
1471  {
1472  if ((sfcMap = GroupReadSurface8(hGroup, C4CFN_MapFg)))
1473  {
1474  if (!fLandscapeModeSet) p->mode = LandscapeMode::Static;
1475  sfcMapBkg = GroupReadSurface8(hGroup, C4CFN_MapBg);
1476  if (!sfcMapBkg) sfcMapBkg = p->CreateDefaultBkgSurface(*sfcMap, false);
1477  if (!sfcMapBkg) return false;
1478  }
1479  }
1480 
1481  // dynamic map from Landscape.txt
1482  CSurface8 *rendered_map = nullptr, *rendered_bkg = nullptr;
1483  if (!sfcMap)
1484  {
1485  if (p->CreateMapS2(hGroup, rendered_map, rendered_bkg))
1486  {
1487  sfcMap.reset(rendered_map);
1488  sfcMapBkg.reset(rendered_bkg);
1489  if (!fLandscapeModeSet) p->mode = LandscapeMode::Dynamic;
1490  }
1491  }
1492 
1493  // script may create or edit map
1495  {
1496  if (!fLandscapeModeSet) p->mode = LandscapeMode::Dynamic;
1497  }
1498 
1499  // Dynamic map by scenario
1500  if (!sfcMap && !fOverloadCurrent)
1501  {
1502  if (p->CreateMap(rendered_map, rendered_bkg))
1503  {
1504  sfcMap.reset(rendered_map);
1505  sfcMapBkg.reset(rendered_bkg);
1506  // Although this is a dynamic map, it's probably just the empty default map.
1507  // Set the mode to static so people can start drawing directly in the editor.
1508  if (!fLandscapeModeSet) p->mode = LandscapeMode::Static;
1509  }
1510  }
1511 
1512  // No map failure
1513  if (!sfcMap)
1514  {
1515  // no problem if only overloading
1516  if (!fOverloadCurrent) return false;
1517  if (fLoadSky) if (!p->Sky.Init(fSavegame)) return false;
1518  return true;
1519  }
1520 
1521  assert(sfcMapBkg != nullptr);
1522 
1523  if (Config.General.DebugRec)
1524  {
1525  AddDbgRec(RCT_Block, "|---MAP---|", 12);
1526  AddDbgRec(RCT_Map, sfcMap->Bits, sfcMap->Pitch*sfcMap->Hgt);
1527  }
1528 
1529  // Store map size and calculate map zoom
1530  int iWdt, iHgt;
1531  sfcMap->GetSurfaceSize(iWdt, iHgt);
1532  p->MapWidth = iWdt; p->MapHeight = iHgt;
1533  p->MapZoom = Game.C4S.Landscape.MapZoom.Evaluate();
1534 
1535  // Calculate landscape size
1536  p->Width = p->MapZoom * p->MapWidth;
1537  p->Height = p->MapZoom * p->MapHeight;
1538  p->Width = std::max<int32_t>(p->Width, 100);
1539  p->Height = std::max<int32_t>(p->Height, 100);
1540 
1541  // if overloading, clear current landscape (and sections, etc.)
1542  // must clear, of course, before new sky is eventually read
1543  if (fOverloadCurrent) Clear(!Game.C4S.Landscape.KeepMapCreator, fLoadSky, false);
1544 
1545  // assign new map
1546  assert(p->Map == nullptr);
1547  assert(p->MapBkg == nullptr);
1548  p->Map = std::move(sfcMap);
1549  p->MapBkg = std::move(sfcMapBkg);
1550 
1551  // Sky (might need to know landscape height)
1552  if (fLoadSky)
1553  {
1554  Game.SetInitProgress(70);
1555  if (!p->Sky.Init(fSavegame)) return false;
1556  }
1557  }
1558 
1559  // Exact landscape from scenario (no map or exact recreation)
1560  else /* if (Game.C4S.Landscape.ExactLandscape) */
1561  {
1562  C4DebugRecOff DBGRECOFF;
1563  // if overloading, clear current
1564  if (fOverloadCurrent) Clear(!Game.C4S.Landscape.KeepMapCreator, fLoadSky, false);
1565  // load it
1566  if (!fLandscapeModeSet) p->mode = LandscapeMode::Exact;
1567  rfLoaded = true;
1568  if (!Load(hGroup, fLoadSky, fSavegame)) return false;
1569  }
1570 
1571  // progress
1572  Game.SetInitProgress(75);
1573 
1574  // copy noscan-var
1575  p->NoScan = Game.C4S.Landscape.NoScan != 0;
1576 
1577  // Scan settings
1578  p->ScanSpeed = Clamp(GetWidth() / 500, 2, 15);
1579 
1580  // Create pixel count array before any SetPix operations may take place
1581  // Proper pixel counts will be done later, but needs to have the arrays redy to avoid dead pointer access.
1582  // We will use 15x17 blocks so the pixel count can't get over 255.
1583  int32_t PixCntWidth = (GetWidth() + 16) / 17;
1584  p->PixCntPitch = (GetHeight() + 14) / 15;
1585  p->PixCnt.resize(PixCntWidth * p->PixCntPitch);
1586 
1587  // map to big surface and sectionize it
1588  // (not for shaders though - they require continous textures)
1590  {
1591  assert(p->Surface8 == nullptr);
1592  assert(p->Surface8Bkg == nullptr);
1593 
1594  // Create landscape surfaces
1595  {
1596  auto sf8 = std::make_unique<CSurface8>();
1597  auto sfb8 = std::make_unique<CSurface8>();
1598  if (!sf8->Create(GetWidth(), GetHeight()) || !sfb8->Create(GetWidth(), GetHeight()))
1599  return false;
1600  p->Surface8 = std::move(sf8);
1601  p->Surface8Bkg = std::move(sfb8);
1602  if (!p->Mat2Pal())
1603  return false;
1604  }
1605 
1606  // Map to landscape
1607  // Landscape render disabled during initial landscape zoom (will be updated later)
1608  std::unique_ptr<C4LandscapeRender> lsrender_backup;
1609  lsrender_backup.swap(p->pLandscapeRender);
1610  bool map2landscape_success = MapToLandscape();
1611  lsrender_backup.swap(p->pLandscapeRender);
1612  if (!map2landscape_success) return false;
1613  }
1614 
1615  // Init out-of-landscape pixels for bottom
1616  p->InitBorderPix();
1617 
1618  Game.SetInitProgress(80);
1619 
1620  if (Config.General.DebugRec)
1621  {
1622  AddDbgRec(RCT_Block, "|---LANDSCAPE---|", 18);
1623  AddDbgRec(RCT_Map, p->Surface8->Bits, p->Surface8->Pitch * p->Surface8->Hgt);
1624 
1625  AddDbgRec(RCT_Block, "|---LANDSCAPE BKG---|", 22);
1626  AddDbgRec(RCT_Map, p->Surface8Bkg->Bits, p->Surface8Bkg->Pitch * p->Surface8Bkg->Hgt);
1627  }
1628 
1629  // Create FoW
1630  assert(p->pFoW == nullptr);
1631  if (Game.C4S.Game.FoWEnabled)
1632  p->pFoW = std::make_unique<C4FoW>();
1633 
1634  // Create renderer
1635 #ifndef USE_CONSOLE
1636  if (!p->pLandscapeRender)
1637  p->pLandscapeRender = std::make_unique<C4LandscapeRenderGL>();
1638 #endif
1639 
1640  if (p->pLandscapeRender)
1641  {
1642  // Initialize renderer
1643  if (fOverloadCurrent)
1644  {
1645  if (!p->pLandscapeRender->ReInit(GetWidth(), GetHeight()))
1646  return false;
1647  }
1648  else
1649  {
1650  if (!p->pLandscapeRender->Init(GetWidth(), GetHeight(), &::TextureMap, &::GraphicsResource.Files))
1651  return false;
1652  }
1653  }
1654 
1655  // Save initial landscape
1656  if (!SaveInitial())
1657  return false;
1658 
1659  // Load diff, if existant
1660  ApplyDiff(hGroup);
1661 
1662  // Pixel count tracking from landscape zoom is incomplete, so recalculate it.
1663  p->UpdatePixCnt(this, C4Rect(0, 0, GetWidth(), GetHeight()));
1664  p->ClearMatCount();
1665  p->UpdateMatCnt(this, C4Rect(0, 0, GetWidth(), GetHeight()), true);
1666 
1667  // Create initial landscape render data (after applying diff so landscape is complete)
1668  if (p->pLandscapeRender) p->pLandscapeRender->Update(C4Rect(0, 0, GetWidth(), GetHeight()), this);
1669  Game.SetInitProgress(87);
1670 
1671  // after map/landscape creation, the seed must be fixed again, so there's no difference between clients creating
1672  // and not creating the map
1674 
1675  // Create ambient light map after landscape creation
1676  if (p->pFoW) p->pFoW->Ambient.CreateFromLandscape(*this, 10., 50., 0.25);
1677  Game.SetInitProgress(84);
1678 
1679  // Success
1680  rfLoaded = true;
1681  return true;
1682 }
1683 
1685 {
1686  return p->Map != nullptr && p->MapBkg != nullptr;
1687 }
1688 
1689 bool C4Landscape::Save(C4Group &hGroup) const
1690 {
1692  bool r = p->SaveInternal(this, hGroup);
1694  return r;
1695 }
1696 
1697 bool C4Landscape::P::SaveInternal(const C4Landscape *d, C4Group &hGroup) const
1698 {
1699  // Save landscape surface
1700  char szTempLandscape[_MAX_PATH + 1];
1701  SCopy(Config.AtTempPath(C4CFN_TempLandscape), szTempLandscape);
1702  MakeTempFilename(szTempLandscape);
1703  if (!Surface8->Save(szTempLandscape))
1704  return false;
1705 
1706  // Move temp file to group
1707  if (!hGroup.Move(szTempLandscape, C4CFN_LandscapeFg))
1708  return false;
1709 
1710  // Same for background surface
1711  SCopy(Config.AtTempPath(C4CFN_TempLandscapeBkg), szTempLandscape);
1712  MakeTempFilename(szTempLandscape);
1713  if (!Surface8Bkg->Save(szTempLandscape))
1714  return false;
1715  if (!hGroup.Move(szTempLandscape, C4CFN_LandscapeBg))
1716  return false;
1717 
1718  // Save map
1719  if (fMapChanged && Map)
1720  if (!d->SaveMap(hGroup))
1721  return false;
1722 
1723  // save textures (if changed)
1724  if (!d->SaveTextures(hGroup))
1725  return false;
1726 
1727  return true;
1728 }
1729 
1730 bool C4Landscape::SaveDiff(C4Group &hGroup, bool fSyncSave) const
1731 {
1733  bool r = p->SaveDiffInternal(this, hGroup, fSyncSave);
1735  return r;
1736 }
1737 
1738 bool C4Landscape::P::SaveDiffInternal(const C4Landscape *d, C4Group &hGroup, bool fSyncSave) const
1739 {
1740  assert(pInitial && pInitialBkg);
1741  if (!pInitial || !pInitialBkg) return false;
1742 
1743  // If it shouldn't be sync-save: Clear all bytes that have not changed, i.e.
1744  // set them to C4M_MaxTexIndex
1745  bool fChanged = false, fChangedBkg = false;;
1746  if (!fSyncSave)
1747  for (int y = 0; y < Height; y++)
1748  for (int x = 0; x < Width; x++)
1749  {
1750  if (pInitial[y * Width + x] == Surface8->_GetPix(x, y))
1751  Surface8->SetPix(x, y, C4M_MaxTexIndex);
1752  else
1753  fChanged = true;
1754 
1755  if (pInitialBkg[y * Width + x] == Surface8Bkg->_GetPix(x, y))
1756  Surface8Bkg->SetPix(x, y, C4M_MaxTexIndex);
1757  else
1758  fChangedBkg = true;
1759  }
1760 
1761  if (fSyncSave || fChanged)
1762  {
1763  // Save landscape surface
1764  if (!Surface8->Save(Config.AtTempPath(C4CFN_TempLandscape)))
1765  return false;
1766 
1767  // Move temp file to group
1770  return false;
1771  }
1772 
1773  if (fSyncSave || fChangedBkg)
1774  {
1775  // Save landscape surface
1776  if (!Surface8Bkg->Save(Config.AtTempPath(C4CFN_TempLandscapeBkg)))
1777  return false;
1778 
1779  // Move temp file to group
1782  return false;
1783  }
1784 
1785  // Restore landscape pixels
1786  if (!fSyncSave)
1787  for (int y = 0; y < Height; y++)
1788  for (int x = 0; x < Width; x++)
1789  {
1790  if (Surface8->_GetPix(x, y) == C4M_MaxTexIndex)
1791  Surface8->SetPix(x, y, pInitial[y * Width + x]);
1792  if (Surface8Bkg->_GetPix(x, y) == C4M_MaxTexIndex)
1793  Surface8Bkg->SetPix(x, y, pInitialBkg[y * Width + x]);
1794  }
1795 
1796  // Save changed map, too
1797  if (fMapChanged && Map)
1798  if (!d->SaveMap(hGroup)) return false;
1799 
1800  // and textures (if changed)
1801  if (!d->SaveTextures(hGroup)) return false;
1802 
1803  return true;
1804 }
1805 
1807 {
1808 
1809  // Create array
1810  p->pInitial = std::make_unique<BYTE[]>(GetWidth() * GetHeight());
1811  p->pInitialBkg = std::make_unique<BYTE[]>(GetWidth() * GetHeight());
1812 
1813  // Save material data
1814  for (int y = 0; y < GetHeight(); y++)
1815  {
1816  const int pitch = y * GetWidth();
1817  for (int x = 0; x < GetWidth(); x++)
1818  {
1819  p->pInitial[pitch + x] = p->Surface8->_GetPix(x, y);
1820  p->pInitialBkg[pitch + x] = p->Surface8Bkg->_GetPix(x, y);
1821  }
1822  }
1823 
1824  return true;
1825 }
1826 
1827 bool C4Landscape::Load(C4Group &hGroup, bool fLoadSky, bool fSavegame)
1828 {
1829  assert(!p->Surface8 && !p->Surface8Bkg);
1830 
1831  // Load exact landscape from group
1832  if ((p->Surface8 = GroupReadSurface8(hGroup, C4CFN_Landscape)) == nullptr)
1833  {
1834  if ((p->Surface8 = GroupReadSurface8(hGroup, C4CFN_LandscapeFg)) == nullptr) return false;
1835  p->Surface8Bkg = GroupReadSurface8(hGroup, C4CFN_LandscapeBg);
1836 
1837  if (p->Surface8Bkg)
1838  {
1839  if (p->Surface8->Wdt != p->Surface8Bkg->Wdt || p->Surface8->Hgt != p->Surface8Bkg->Hgt)
1840  {
1841  LogFatal(FormatString("Landscape has different dimensions than background landscape (%dx%d vs. %dx%d)", p->Surface8->Wdt, p->Surface8->Hgt, p->Surface8Bkg->Wdt, p->Surface8Bkg->Hgt).getData());
1842  return false;
1843  }
1844  }
1845  else
1846  {
1847  // LandscapeFg.bmp loaded: Assume full 8bit mat-tex values
1848  // when creating background surface.
1849  p->Surface8Bkg = p->CreateDefaultBkgSurface(*p->Surface8, false);
1850  }
1851  }
1852  else
1853  {
1854  // Landscape.bmp loaded: Assume msb is IFT flag when creating
1855  // background surface.
1856  p->Surface8Bkg = p->CreateDefaultBkgSurface(*p->Surface8, true);
1857  }
1858 
1859  int iWidth, iHeight;
1860  p->Surface8->GetSurfaceSize(iWidth, iHeight);
1861  p->Width = iWidth; p->Height = iHeight;
1862 
1863  // adjust pal
1864  if (!p->Mat2Pal()) return false;
1865  // Landscape should be in correct format: Make sure it is!
1866  for (int32_t y = 0; y < GetHeight(); ++y)
1867  for (int32_t x = 0; x < GetWidth(); ++x)
1868  {
1869  BYTE byPix = p->Surface8->_GetPix(x, y);
1870  int32_t iMat = PixCol2Mat(byPix);
1871 
1872  if (byPix && !MatValid(iMat))
1873  {
1874  LogFatal(FormatString("Landscape loading error at (%d/%d): Pixel value %d not a valid material!", (int)x, (int)y, (int)byPix).getData());
1875  return false;
1876  }
1877 
1878  BYTE byPixBkg = p->Surface8Bkg->_GetPix(x, y);
1879  int32_t iMatBkg = PixCol2Mat(byPixBkg);
1880 
1881  if (byPixBkg && !MatValid(iMatBkg))
1882  {
1883  LogFatal(FormatString("Background Landscape loading error at (%d/%d): Pixel value %d not a valid material!", (int)x, (int)y, (int)byPixBkg).getData());
1884  return false;
1885  }
1886  }
1887 
1888  // Init sky
1889  if (fLoadSky)
1890  {
1891  Game.SetInitProgress(70);
1892  if (p->Sky.Init(fSavegame)) return false;
1893  }
1894  // Success
1895  return true;
1896 }
1898 {
1899  std::unique_ptr<CSurface8> pDiff, pDiffBkg;
1900  // Load diff landscape from group
1901  pDiff = GroupReadSurface8(hGroup, C4CFN_DiffLandscape);
1902  pDiffBkg = GroupReadSurface8(hGroup, C4CFN_DiffLandscapeBkg);
1903  if (pDiff == nullptr && pDiffBkg == nullptr) return false;
1904 
1905  // convert all pixels: keep if same material; re-set if different material
1906  BYTE byPix;
1907  for (int32_t y = 0; y < GetHeight(); ++y) for (int32_t x = 0; x < GetWidth(); ++x)
1908  {
1909  if (pDiff && pDiff->GetPix(x, y) != C4M_MaxTexIndex)
1910  if (p->Surface8->_GetPix(x, y) != (byPix = pDiff->_GetPix(x, y)))
1911  // material has changed here: readjust with new texture
1912  p->Surface8->SetPix(x, y, byPix);
1913  if (pDiffBkg && pDiffBkg->GetPix(x, y) != C4M_MaxTexIndex)
1914  if (p->Surface8Bkg->_GetPix(x, y) != (byPix = pDiffBkg->_GetPix(x, y)))
1915  p->Surface8Bkg->_SetPix(x, y, byPix);
1916  }
1917 
1918  // done
1919  return true;
1920 }
1921 
1923 {
1924  p = std::make_unique<P>();
1925 }
1926 
1928 {
1929  std::fill(MatCount.begin(), MatCount.end(), 0);
1930  std::fill(EffectiveMatCount.begin(), EffectiveMatCount.end(), 0);
1931 }
1932 
1934 {
1935  p->ScanX = 0;
1936 }
1937 
1938 
1939 int32_t PixCol2Mat(BYTE pixc)
1940 {
1941  // Get texture
1942  int32_t iTex = PixCol2Tex(pixc);
1943  if (!iTex) return MNone;
1944  // Get material-texture mapping
1945  const C4TexMapEntry *pTex = ::TextureMap.GetEntry(iTex);
1946  // Return material
1947  return pTex ? pTex->GetMaterialIndex() : MNone;
1948 }
1949 
1950 bool C4Landscape::SaveMap(C4Group &hGroup) const
1951 {
1952  // No map
1953  if (!p->Map) return false;
1954  assert(p->MapBkg != nullptr);
1955 
1956  // Create map palette
1957  CStdPalette Palette;
1959 
1960  // Save map surface
1961  if (!p->Map->Save(Config.AtTempPath(C4CFN_TempMapFg), &Palette))
1962  return false;
1963 
1964  // Move temp file to group
1965  if (!hGroup.Move(Config.AtTempPath(C4CFN_TempMapFg),
1966  C4CFN_MapFg))
1967  return false;
1968 
1969  // Save background map surface
1970  if (!p->MapBkg->Save(Config.AtTempPath(C4CFN_TempMapBg), &Palette))
1971  return false;
1972 
1973  // Move temp file to group
1974  if (!hGroup.Move(Config.AtTempPath(C4CFN_TempMapBg),
1975  C4CFN_MapBg))
1976  return false;
1977 
1978  // Success
1979  return true;
1980 }
1981 
1983 {
1984  // if material-texture-combinations have been added, write the texture map
1985  if (::TextureMap.fEntriesAdded)
1986  {
1987  C4Group *pMatGroup = new C4Group();
1988  bool fSuccess = false;
1989  // create local material group
1990  if (!hGroup.FindEntry(C4CFN_Material))
1991  {
1992  // delete previous item at temp path
1994  // create at temp path
1995  if (pMatGroup->Open(Config.AtTempPath(C4CFN_Material), true))
1996  // write to it
1997  if (::TextureMap.SaveMap(*pMatGroup, C4CFN_TexMap))
1998  // close (flush)
1999  if (pMatGroup->Close())
2000  // add it
2002  fSuccess = true;
2003  // temp group must remain for scenario file closure
2004  // it will be deleted when the group is closed
2005  }
2006  else
2007  // simply write it to the local material file
2008  if (pMatGroup->OpenAsChild(&hGroup, C4CFN_Material))
2009  fSuccess = ::TextureMap.SaveMap(*pMatGroup, C4CFN_TexMap);
2010  // close material group again
2011  if (pMatGroup->IsOpen()) pMatGroup->Close();
2012  delete pMatGroup;
2013  // fail if unsuccessful
2014  if (!fSuccess) return false;
2015  }
2016  // done, success
2017  return true;
2018 }
2019 
2021 {
2022  assert(Width > 0);
2023  // Init Top-/BottomRowPix array, which determines if out-of-landscape pixels on top/bottom side of the map are solid or not
2024  // In case of Top-/BottomOpen=2, unit by map and not landscape to avoid runtime join sync losses
2025  if (!Width) return true;
2026  TopRowPix.clear();
2027  BottomRowPix.clear();
2028  LeftColPix.clear();
2029  RightColPix.clear();
2030  // must access Game.C4S here because Landscape.TopOpen / Landscape.BottomOpen may not be initialized yet
2031  // why is there a local copy of that static variable anyway?
2032  int32_t top_open_flag = Game.C4S.Landscape.TopOpen;
2033  int32_t bottom_open_flag = Game.C4S.Landscape.BottomOpen;
2034  if (top_open_flag == 2 && !Map) top_open_flag = 1;
2035  if (bottom_open_flag == 2 && !Map) bottom_open_flag = 1;
2036 
2037  // Init TopRowPix
2038  switch (top_open_flag)
2039  {
2040  // TopOpen=0: Top is closed
2041  case 0: TopRowPix.assign(Width, MCVehic); break;
2042  // TopOpen=2: Top is open when pixel below has sky background
2043  case 2:
2044  TopRowPix.resize(Width);
2045  for (int32_t x = 0; x < Width; ++x)
2046  {
2047  uint8_t map_pix = MapBkg->GetPix(x / MapZoom, 0);
2048  TopRowPix[x] = ((map_pix != 0) ? MCVehic : 0);
2049  }
2050  break;
2051  // TopOpen=1: Top is open
2052  default: TopRowPix.assign(Width, 0); break;
2053  }
2054 
2055  // Init BottomRowPix
2056  switch (bottom_open_flag)
2057  {
2058  // BottomOpen=0: Bottom is closed
2059  case 0: BottomRowPix.assign(Width, MCVehic); break;
2060  // BottomOpen=2: Bottom is open when pixel above has sky background
2061  case 2:
2062  BottomRowPix.resize(Width);
2063  for (int32_t x = 0; x < Width; ++x)
2064  {
2065  uint8_t map_pix = MapBkg->GetPix(x / MapZoom, Map->Hgt - 1);
2066  BottomRowPix[x] = ((map_pix != 0) ? MCVehic : 0);
2067  }
2068  break;
2069  // BottomOpen=1: Bottom is open
2070  default: BottomRowPix.assign(Width, 0); break;
2071  }
2072 
2074  {
2075  // Compatibility: check both foreground and background material per
2076  // default, Top/BottomOpen=2-like behavior with AutoScanSideOpen=2.
2077  bool only_bg = Game.C4S.Landscape.AutoScanSideOpen == 2;
2078  LeftColPix.resize(Height);
2079  RightColPix.resize(Height);
2080  uint8_t map_pix;
2081  for (int32_t y = 0; y < Height; ++y)
2082  {
2083  map_pix = MapBkg->GetPix(0, y / MapZoom);
2084  if (!only_bg) map_pix += Map->GetPix(0, y / MapZoom);
2085  LeftColPix[y] = ((map_pix != 0) ? MCVehic : 0);
2086  map_pix = MapBkg->GetPix(Map->Wdt - 1, y / MapZoom);
2087  if (!only_bg) map_pix += Map->GetPix(Map->Wdt - 1, y / MapZoom);
2088  RightColPix[y] = ((map_pix != 0) ? MCVehic : 0);
2089  }
2090  }
2091  else
2092  {
2093  int32_t LeftOpen = std::min(Height, Game.C4S.Landscape.LeftOpen);
2094  int32_t RightOpen = std::min(Height, Game.C4S.Landscape.RightOpen);
2095  LeftColPix.assign(Height, MCVehic);
2096  RightColPix.assign(Height, MCVehic);
2097  for (int32_t cy = 0; cy < LeftOpen; cy++)
2098  LeftColPix[cy] = 0;
2099  for (int32_t cy = 0; cy < RightOpen; cy++)
2100  RightColPix[cy] = 0;
2101  }
2102 
2103  return true;
2104 }
2105 
2107 {
2108  // zoom map to landscape
2109  return p->MapToLandscape(this, *p->Map, *p->MapBkg, 0, 0, p->MapWidth, p->MapHeight);
2110 }
2111 
2112 
2113 uint32_t C4Landscape::P::ChunkyRandom(uint32_t & iOffset, uint32_t iRange) const
2114 {
2115  if (!iRange) return 0;
2116  iOffset = (iOffset * 16807) % 2147483647;
2117  return (iOffset ^ MapSeed) % iRange;
2118 }
2119 
2120 void C4Landscape::P::DrawChunk(C4Landscape *d, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, C4MaterialCoreShape Shape, uint32_t cro)
2121 {
2122  unsigned int top_rough = 0, side_rough = 0, bottom_rough = 0;
2123  // what to do?
2124  switch (Shape)
2125  {
2126  case C4M_Flat: case C4M_Octagon:
2127  if (mcol != Transparent) Surface8->Box(tx, ty, tx + wdt, ty + hgt, mcol);
2128  if (mcolBkg != Transparent) Surface8Bkg->Box(tx, ty, tx + wdt, ty + hgt, mcolBkg);
2129  return;
2130  case C4M_TopFlat:
2131  top_rough = 0; side_rough = 2; bottom_rough = 4;
2132  break;
2133  case C4M_Smooth:
2134  top_rough = 2; side_rough = 2; bottom_rough = 2;
2135  break;
2136  case C4M_Rough:
2137  top_rough = 4; side_rough = 4; bottom_rough = 4;
2138  break;
2139  case C4M_Smoother:
2140  top_rough = 1; side_rough = 1; bottom_rough = 1;
2141  break;
2142  }
2143  int vtcs[16];
2144  unsigned int rx = std::max(wdt / 2, 1);
2145 
2146  vtcs[0] = tx - ChunkyRandom(cro, rx * side_rough / 4); vtcs[1] = ty - ChunkyRandom(cro, rx * top_rough / 4);
2147  vtcs[2] = tx - ChunkyRandom(cro, rx * side_rough / 2); vtcs[3] = ty + hgt / 2;
2148  vtcs[4] = tx - ChunkyRandom(cro, rx * side_rough / 4); vtcs[5] = ty + hgt + ChunkyRandom(cro, rx * bottom_rough / 4);
2149  vtcs[6] = tx + wdt / 2; vtcs[7] = ty + hgt + ChunkyRandom(cro, rx * bottom_rough / 2);
2150  vtcs[8] = tx + wdt + ChunkyRandom(cro, rx * side_rough / 4); vtcs[9] = ty + hgt + ChunkyRandom(cro, rx * bottom_rough / 4);
2151  vtcs[10] = tx + wdt + ChunkyRandom(cro, rx * side_rough / 2); vtcs[11] = ty + hgt / 2;
2152  vtcs[12] = tx + wdt + ChunkyRandom(cro, rx * side_rough / 4); vtcs[13] = ty - ChunkyRandom(cro, rx * top_rough / 4);
2153  vtcs[14] = tx + wdt / 2; vtcs[15] = ty - ChunkyRandom(cro, rx * top_rough / 2);
2154 
2155  ForPolygon(d, vtcs, 8, nullptr, nullptr, mcol, mcolBkg);
2156 }
2157 
2158 void C4Landscape::P::DrawSmoothOChunk(C4Landscape *d, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, int flip, uint32_t cro)
2159 {
2160  int vtcs[8];
2161  unsigned int rx = std::max(wdt / 2, 1);
2162 
2163  vtcs[0] = tx; vtcs[1] = ty;
2164  vtcs[2] = tx; vtcs[3] = ty + hgt;
2165  vtcs[4] = tx + wdt; vtcs[5] = ty + hgt;
2166  vtcs[6] = tx + wdt; vtcs[7] = ty;
2167 
2168  switch (flip)
2169  {
2170  case 0: vtcs[0] = tx + wdt / 2; vtcs[1] += hgt / 3; vtcs[7] -= ChunkyRandom(cro, rx / 2); break;
2171  case 1: vtcs[2] = tx + wdt / 2; vtcs[3] -= hgt / 3; vtcs[5] += ChunkyRandom(cro, rx / 2); break;
2172  case 2: vtcs[4] = tx + wdt / 2; vtcs[5] -= hgt / 3; vtcs[3] += ChunkyRandom(cro, rx / 2); break;
2173  case 3: vtcs[6] = tx + wdt / 2; vtcs[7] += hgt / 3; vtcs[1] -= ChunkyRandom(cro, rx / 2); break;
2174  case 4: vtcs[0] = tx + wdt / 2; vtcs[1] += hgt / 2; break;
2175  case 5: vtcs[2] = tx + wdt / 2; vtcs[3] -= hgt / 2; break;
2176  case 6: vtcs[4] = tx + wdt / 2; vtcs[5] -= hgt / 2; break;
2177  case 7: vtcs[6] = tx + wdt / 2; vtcs[7] += hgt / 2; break;
2178  }
2179 
2180  ForPolygon(d, vtcs, 4, nullptr, nullptr, mcol, mcolBkg);
2181 }
2182 
2183 void C4Landscape::P::ChunkOZoom(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, uint8_t iTexture, int32_t iOffX, int32_t iOffY)
2184 {
2185  const C4TexMapEntry *entry = ::TextureMap.GetEntry(iTexture);
2186  C4Material *pMaterial = entry->GetMaterial();
2187  if (!pMaterial) return;
2188  const char *texture_name = entry->GetTextureName();
2189  C4Texture *texture = ::TextureMap.GetTexture(texture_name);
2190  C4TextureShape *shape = texture ? texture->GetMaterialShape() : nullptr;
2191  // Chunk type by material
2193  // Get map & landscape size
2194  int iMapWidth, iMapHeight;
2195  sfcMap.GetSurfaceSize(iMapWidth, iMapHeight);
2196  // Clip desired map segment to map size
2197  iMapX = Clamp<int32_t>(iMapX, 0, iMapWidth - 1);
2198  iMapY = Clamp<int32_t>(iMapY, 0, iMapHeight - 1);
2199  iMapWdt = Clamp<int32_t>(iMapWdt, 0, iMapWidth - iMapX);
2200  iMapHgt = Clamp<int32_t>(iMapHgt, 0, iMapHeight - iMapY);
2201  // get chunk size
2202  int iChunkWidth = MapZoom, iChunkHeight = MapZoom;
2203  // Scan map lines
2204  for (int iY = iMapY; iY < iMapY + iMapHgt; iY++)
2205  {
2206  // Landscape target coordinate vertical
2207  int iToY = iY * iChunkHeight + iOffY;
2208  // Scan map line
2209  for (int iX = iMapX; iX < iMapX + iMapWdt; iX++)
2210  {
2211  // Map scan line start
2212  uint8_t MapPixel = sfcMap._GetPix(iX, iY);
2213  uint8_t MapPixelBkg = sfcMapBkg._GetPix(iX, iY);
2214  // Landscape target coordinate horizontal
2215  int iToX = iX * iChunkWidth + iOffX;
2216  // Here's a chunk of the texture-material to zoom
2217  if (MapPixel == iTexture)
2218  {
2219  // Draw chunk
2220  DrawChunk(d, iToX, iToY, iChunkWidth, iChunkHeight, MapPixel, MapPixelBkg, iChunkType, (iX << 16) + iY);
2221  }
2222  // Other chunk, check for slope smoothers
2223  else if (iChunkType == C4M_Smooth || iChunkType == C4M_Smoother || iChunkType == C4M_Octagon)
2224  {
2225  // Map scan line pixel below
2226  uint8_t below = sfcMap.GetPix(iX, iY + 1);
2227  uint8_t above = sfcMap.GetPix(iX, iY - 1);
2228  uint8_t left = sfcMap.GetPix(iX - 1, iY);
2229  uint8_t right = sfcMap.GetPix(iX + 1, iY);
2230  uint8_t leftBkg = sfcMapBkg.GetPix(iX - 1, iY);
2231  uint8_t rightBkg = sfcMapBkg.GetPix(iX + 1, iY);
2232  // do not fill a tiny hole
2233  if (below == iTexture && above == iTexture && left == iTexture && right == iTexture)
2234  continue;
2235  int flat = iChunkType == C4M_Octagon ? 4 : 0;
2236  // Smooth chunk & same texture-material below
2237  if (iY < iMapHeight - 1 && below == iTexture)
2238  {
2239  // Same texture-material on left
2240  if (iX > 0 && left == iTexture)
2241  {
2242  // Draw smoother
2243  DrawSmoothOChunk(d, iToX, iToY, iChunkWidth, iChunkHeight, left, leftBkg, 3 + flat, (iX << 16) + iY);
2244  }
2245  // Same texture-material on right
2246  if (iX < iMapWidth - 1 && right == iTexture)
2247  {
2248  // Draw smoother
2249  DrawSmoothOChunk(d, iToX, iToY, iChunkWidth, iChunkHeight, right, rightBkg, 0 + flat, (iX << 16) + iY);
2250  }
2251  }
2252  // Smooth chunk & same texture-material above
2253  if (iY > 0 && above == iTexture)
2254  {
2255  // Same texture-material on left
2256  if (iX > 0 && left == iTexture)
2257  {
2258  // Draw smoother
2259  DrawSmoothOChunk(d, iToX, iToY, iChunkWidth, iChunkHeight, left, leftBkg, 2 + flat, (iX << 16) + iY);
2260  }
2261  // Same texture-material on right
2262  if (iX < iMapWidth - 1 && right == iTexture)
2263  {
2264  // Draw smoother
2265  DrawSmoothOChunk(d, iToX, iToY, iChunkWidth, iChunkHeight, right, rightBkg, 1 + flat, (iX << 16) + iY);
2266  }
2267  }
2268  }
2269  }
2270  }
2271  // Draw custom shapes on top of regular materials
2272  if (shape && !::Game.C4S.Landscape.FlatChunkShapes) shape->Draw(sfcMap, sfcMapBkg, iMapX, iMapY, iMapWdt, iMapHgt, iTexture, iOffX, iOffY, MapZoom, pMaterial->MinShapeOverlap);
2273 }
2274 
2275 static bool GetTexUsage(const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, DWORD *dwpTextureUsage)
2276 {
2277  int iX, iY;
2278  // No good parameters
2279  if (!dwpTextureUsage) return false;
2280  // Clip desired map segment to map size
2281  iMapX = Clamp<int32_t>(iMapX, 0, sfcMap.Wdt - 1); iMapY = Clamp<int32_t>(iMapY, 0, sfcMap.Hgt - 1);
2282  iMapWdt = Clamp<int32_t>(iMapWdt, 0, sfcMap.Wdt - iMapX); iMapHgt = Clamp<int32_t>(iMapHgt, 0, sfcMap.Hgt - iMapY);
2283  // Zero texture usage list
2284  for (int32_t cnt = 0; cnt < C4M_MaxTexIndex; cnt++) dwpTextureUsage[cnt] = 0;
2285  // Scan map pixels
2286  for (iY = iMapY; iY < iMapY + iMapHgt; iY++)
2287  for (iX = iMapX; iX < iMapX + iMapWdt; iX++)
2288  {
2289  // Count texture map index
2290  const int32_t tex = sfcMap.GetPix(iX, iY);
2291  assert(tex < C4M_MaxTexIndex);
2292 
2293  if (!dwpTextureUsage[tex]++) if (tex)
2294  {
2295  // Check if texture actually exists
2296  if (!::TextureMap.GetEntry(tex)->GetMaterial())
2297  LogF("Map2Landscape error: Texture index %d at (%d/%d) in map not defined in texture map!", (int)tex, (int)iX, (int)iY);
2298  // No error. Landscape is usually fine but might contain some holes where material should be
2299  }
2300 
2301  // Ignore background texture for now -- this is only used for ChunkOZoom,
2302  // for which only the foreground texture is relevant.
2303 
2304  /*
2305  // Count texture map index
2306  const int32_t texBkg = sfcMapBkg.GetPix(iX, iY);
2307  if (!dwpTextureUsage[texBkg]++) if (texBkg)
2308  {
2309  // Check if texture actually exists
2310  if (!::TextureMap.GetEntry(texBkg)->GetMaterial())
2311  LogF("Map2Landscape error: Texture index %d at (%d/%d) in background map not defined in texture map!", (int) texBkg, (int) iX, (int) iY);
2312  // No error. Landscape is usually fine but might contain some holes where material should be
2313  }
2314  */
2315 
2316  }
2317  // Done
2318  return true;
2319 }
2320 
2321 bool C4Landscape::P::TexOZoom(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, DWORD *dwpTextureUsage, int32_t iToX, int32_t iToY)
2322 {
2323  // ChunkOZoom all used textures
2324  for (auto iIndex : ::TextureMap.Order)
2325  {
2326  if (dwpTextureUsage[iIndex] > 0)
2327  {
2328  // ChunkOZoom map to landscape
2329  ChunkOZoom(d, sfcMap, sfcMapBkg, iMapX, iMapY, iMapWdt, iMapHgt, iIndex, iToX, iToY);
2330  }
2331  }
2332 
2333  // Done
2334  return true;
2335 }
2336 
2337 bool C4Landscape::P::MapToSurface(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY)
2338 {
2339 
2340  // assign clipper
2341  Surface8->Clip(iToX, iToY, iToX + iToWdt - 1, iToY + iToHgt - 1);
2342  Surface8Bkg->Clip(iToX, iToY, iToX + iToWdt - 1, iToY + iToHgt - 1);
2344 
2345  // Enlarge map segment for chunky rim
2346  iMapX -= 2 + MaterialMap.max_shape_width / MapZoom;
2347  iMapY -= 2 + MaterialMap.max_shape_height / MapZoom;
2348  iMapWdt += 4 + MaterialMap.max_shape_width / MapZoom * 2;
2349  iMapHgt += 4 + MaterialMap.max_shape_height / MapZoom * 2;
2350 
2351  // Determine texture usage in map segment
2352  DWORD dwTexUsage[C4M_MaxTexIndex];
2353  if (!GetTexUsage(sfcMap, sfcMapBkg, iMapX, iMapY, iMapWdt, iMapHgt, dwTexUsage)) return false;
2354  // Texture zoom map to landscape
2355  if (!TexOZoom(d, sfcMap, sfcMapBkg, iMapX, iMapY, iMapWdt, iMapHgt, dwTexUsage, iOffX, iOffY)) return false;
2356 
2357  // remove clipper
2358  Surface8->NoClip();
2359  Surface8Bkg->NoClip();
2360 
2361  // success
2362  return true;
2363 }
2364 
2365 bool C4Landscape::P::MapToLandscape(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iOffsX, int32_t iOffsY, bool noClear)
2366 {
2367  assert(Surface8 && Surface8Bkg);
2368  // Clip to map/landscape segment
2369  int iMapWidth, iMapHeight, iLandscapeWidth, iLandscapeHeight;
2370  // Get map & landscape size
2371  sfcMap.GetSurfaceSize(iMapWidth, iMapHeight);
2372  Surface8->GetSurfaceSize(iLandscapeWidth, iLandscapeHeight);
2373  // Clip map segment to map size
2374  iMapX = Clamp<int32_t>(iMapX, 0, iMapWidth - 1); iMapY = Clamp<int32_t>(iMapY, 0, iMapHeight - 1);
2375  iMapWdt = Clamp<int32_t>(iMapWdt, 0, iMapWidth - iMapX); iMapHgt = Clamp<int32_t>(iMapHgt, 0, iMapHeight - iMapY);
2376  // No segment
2377  if (!iMapWdt || !iMapHgt) return true;
2378 
2379  // Get affected landscape rect
2380  C4Rect To;
2381  To.x = iMapX*MapZoom + iOffsX;
2382  To.y = iMapY*MapZoom + iOffsY;
2383  To.Wdt = iMapWdt*MapZoom;
2384  To.Hgt = iMapHgt*MapZoom;
2385 
2386  PrepareChange(d, To);
2387 
2388  // clear the old landscape if not supressed
2389  if (!noClear)
2390  {
2391  Surface8->ClearBox8Only(To.x, To.y, To.Wdt, To.Hgt);
2392  Surface8Bkg->ClearBox8Only(To.x, To.y, To.Wdt, To.Hgt);
2393  }
2394 
2395  MapToSurface(d, sfcMap, sfcMapBkg, iMapX, iMapY, iMapWdt, iMapHgt, To.x, To.y, To.Wdt, To.Hgt, iOffsX, iOffsY);
2396  FinishChange(d, To);
2397  return true;
2398 }
2399 
2400 bool C4Landscape::P::CreateMap(CSurface8*& sfcMap, CSurface8*& sfcMapBkg)
2401 {
2402  int32_t iWidth = 0, iHeight = 0;
2403 
2404  // Create map surface
2405  Game.C4S.Landscape.GetMapSize(iWidth, iHeight, Game.StartupPlayerCount);
2406  auto fg_map = std::make_unique<CSurface8>(iWidth, iHeight);
2407 
2408  // Fill sfcMap
2409  C4MapCreator MapCreator;
2410  MapCreator.Create(fg_map.get(),
2413 
2414  auto bg_map = CreateDefaultBkgSurface(*fg_map, false);
2415  if (!bg_map)
2416  return false;
2417 
2418  sfcMap = fg_map.release();
2419  sfcMapBkg = bg_map.release();
2420  return true;
2421 }
2422 
2423 bool C4Landscape::P::CreateMapS2(C4Group &ScenFile, CSurface8*& sfcMap, CSurface8*& sfcMapBkg)
2424 {
2425  // file present?
2426  if (!ScenFile.AccessEntry(C4CFN_DynLandscape)) return false;
2427 
2428  // create map creator
2429  if (!pMapCreator)
2430  pMapCreator = std::make_unique<C4MapCreatorS2>(&Game.C4S.Landscape, &::TextureMap, &::MaterialMap, Game.StartupPlayerCount);
2431 
2432  // read file
2433  pMapCreator->ReadFile(C4CFN_DynLandscape, &ScenFile);
2434  // render landscape
2435  if (!pMapCreator->Render(nullptr, sfcMap, sfcMapBkg))
2436  return false;
2437 
2438  // keep map creator until script callbacks have been done
2439  return true;
2440 }
2441 
2443 {
2444  // map creator present?
2445  if (!p->pMapCreator) return true;
2446  // call scripts
2447  p->pMapCreator->ExecuteCallbacks(p->MapZoom);
2448  // destroy map creator, if not needed later
2449  if (!Game.C4S.Landscape.KeepMapCreator) { p->pMapCreator.reset(); }
2450  // done, success
2451  return true;
2452 }
2453 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
2454 /* +++++++++++++++ Searching for features in the landscape +++++++++++++++++ */
2455 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
2456 
2457 int32_t C4Landscape::GetMatHeight(int32_t x, int32_t y, int32_t iYDir, int32_t iMat, int32_t iMax) const
2458 {
2459  if (iYDir > 0)
2460  {
2461  iMax = std::min<int32_t>(iMax, GetHeight() - y);
2462  for (int32_t i = 0; i < iMax; i++)
2463  if (_GetMat(x, y + i) != iMat)
2464  return i;
2465  }
2466  else
2467  {
2468  iMax = std::min<int32_t>(iMax, y + 1);
2469  for (int32_t i = 0; i < iMax; i++)
2470  if (_GetMat(x, y - i) != iMat)
2471  return i;
2472  }
2473  return iMax;
2474 }
2475 // Nearest free above semi solid
2476 bool AboveSemiSolid(int32_t &rx, int32_t &ry)
2477 {
2478  int32_t cy1 = ry, cy2 = ry;
2479  bool UseUpwardsNextFree = false, UseDownwardsNextSolid = false;
2480 
2481  while ((cy1 >= 0) || (cy2 < ::Landscape.GetHeight()))
2482  {
2483  // Check upwards
2484  if (cy1 >= 0)
2485  {
2486  if (GBackSemiSolid(rx, cy1)) UseUpwardsNextFree = true;
2487  else if (UseUpwardsNextFree) { ry = cy1; return true; }
2488  }
2489  // Check downwards
2490  if (cy2 < ::Landscape.GetHeight())
2491  {
2492  if (!GBackSemiSolid(rx, cy2)) UseDownwardsNextSolid = true;
2493  else if (UseDownwardsNextSolid) { ry = cy2; return true; }
2494  }
2495  // Advance
2496  cy1--; cy2++;
2497  }
2498 
2499  return false;
2500 }
2501 
2502 // Nearest free directly above solid
2503 bool AboveSolid(int32_t &rx, int32_t &ry)
2504 {
2505  int32_t cy1 = ry, cy2 = ry;
2506 
2507  while ((cy1 >= 0) || (cy2 < ::Landscape.GetHeight()))
2508  {
2509  // Check upwards
2510  if (cy1 >= 0)
2511  if (!GBackSemiSolid(rx, cy1))
2512  if (GBackSolid(rx, cy1 + 1))
2513  {
2514  ry = cy1; return true;
2515  }
2516  // Check downwards
2517  if (cy2 + 1 < ::Landscape.GetHeight())
2518  if (!GBackSemiSolid(rx, cy2))
2519  if (GBackSolid(rx, cy2 + 1))
2520  {
2521  ry = cy2; return true;
2522  }
2523  // Advance
2524  cy1--; cy2++;
2525  }
2526 
2527  return false;
2528 }
2529 
2530 // Nearest free/semi above solid
2531 bool SemiAboveSolid(int32_t &rx, int32_t &ry)
2532 {
2533  int32_t cy1 = ry, cy2 = ry;
2534 
2535  while ((cy1 >= 0) || (cy2 < ::Landscape.GetHeight()))
2536  {
2537  // Check upwards
2538  if (cy1 >= 0)
2539  if (!GBackSolid(rx, cy1))
2540  if (GBackSolid(rx, cy1 + 1))
2541  {
2542  ry = cy1; return true;
2543  }
2544  // Check downwards
2545  if (cy2 + 1 < ::Landscape.GetHeight())
2546  if (!GBackSolid(rx, cy2))
2547  if (GBackSolid(rx, cy2 + 1))
2548  {
2549  ry = cy2; return true;
2550  }
2551  // Advance
2552  cy1--; cy2++;
2553  }
2554 
2555  return false;
2556 }
2557 
2558 bool FindLiquidHeight(int32_t cx, int32_t &ry, int32_t hgt)
2559 {
2560  int32_t cy1 = ry, cy2 = ry, rl1 = 0, rl2 = 0;
2561 
2562  while ((cy1 >= 0) || (cy2 < ::Landscape.GetHeight()))
2563  {
2564  // Check upwards
2565  if (cy1 >= 0)
2566  {
2567  if (GBackLiquid(cx, cy1))
2568  {
2569  rl1++; if (rl1 >= hgt) { ry = cy1 + hgt / 2; return true; }
2570  }
2571  else rl1 = 0;
2572  }
2573  // Check downwards
2574  if (cy2 + 1 < ::Landscape.GetHeight())
2575  {
2576  if (GBackLiquid(cx, cy2))
2577  {
2578  rl2++; if (rl2 >= hgt) { ry = cy2 - hgt / 2; return true; }
2579  }
2580  else rl2 = 0;
2581  }
2582  // Advance
2583  cy1--; cy2++;
2584  }
2585 
2586  return false;
2587 }
2588 
2589 bool FindTunnelHeight(int32_t cx, int32_t &ry, int32_t hgt)
2590 {
2591  int32_t cy1 = ry, cy2 = ry, rl1 = 0, rl2 = 0;
2592 
2593  while ((cy1 >= 0) || (cy2 < ::Landscape.GetHeight()))
2594  {
2595  // Check upwards
2596  if (cy1 >= 0)
2597  {
2598  if (Landscape.GetBackPix(cx, cy1) != 0 && MatDensity(GBackMat(cx, cy1)) < C4M_Liquid)
2599  {
2600  rl1++; if (rl1 >= hgt) { ry = cy1 + hgt / 2; return true; }
2601  }
2602  else rl1 = 0;
2603  }
2604  // Check downwards
2605  if (cy2 + 1 < ::Landscape.GetHeight())
2606  {
2607  if (Landscape.GetBackPix(cx, cy2) != 0 && MatDensity(GBackMat(cx, cy2)) < C4M_Liquid)
2608  {
2609  rl2++; if (rl2 >= hgt) { ry = cy2 - hgt / 2; return true; }
2610  }
2611  else rl2 = 0;
2612  }
2613  // Advance
2614  cy1--; cy2++;
2615  }
2616 
2617  return false;
2618 }
2619 
2620 // Starting from rx/ry, searches for a width of solid ground.
2621 // Returns bottom center of surface space found.
2622 bool FindSolidGround(int32_t &rx, int32_t &ry, int32_t width)
2623 {
2624  bool fFound = false;
2625 
2626  int32_t cx1, cx2, cy1, cy2, rl1 = 0, rl2 = 0;
2627 
2628  for (cx1 = cx2 = rx, cy1 = cy2 = ry; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2629  {
2630  // Left search
2631  if (cx1 >= 0) // Still going
2632  {
2633  if (AboveSolid(cx1, cy1)) rl1++; // Run okay
2634  else rl1 = 0; // No run
2635  }
2636  // Right search
2637  if (cx2 < ::Landscape.GetWidth()) // Still going
2638  {
2639  if (AboveSolid(cx2, cy2)) rl2++; // Run okay
2640  else rl2 = 0; // No run
2641  }
2642  // Check runs
2643  if (rl1 >= width) { rx = cx1 + rl1 / 2; ry = cy1; fFound = true; break; }
2644  if (rl2 >= width) { rx = cx2 - rl2 / 2; ry = cy2; fFound = true; break; }
2645  }
2646 
2647  if (fFound) AboveSemiSolid(rx, ry);
2648 
2649  return fFound;
2650 }
2651 
2652 bool FindSurfaceLiquid(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
2653 {
2654  bool fFound = false;
2655 
2656  int32_t cx1, cx2, cy1, cy2, rl1 = 0, rl2 = 0, cnt;
2657  bool lokay;
2658  for (cx1 = cx2 = rx, cy1 = cy2 = ry; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2659  {
2660  // Left search
2661  if (cx1 > 0) // Still going
2662  {
2663  if (!AboveSemiSolid(cx1, cy1)) cx1 = -1; // Abort left
2664  else
2665  {
2666  for (lokay = true, cnt = 0; cnt < height; cnt++) if (!GBackLiquid(cx1, cy1 + 1 + cnt)) lokay = false;
2667  if (lokay) rl1++; // Run okay
2668  else rl1 = 0; // No run
2669  }
2670  }
2671  // Right search
2672  if (cx2 < ::Landscape.GetWidth()) // Still going
2673  {
2674  if (!AboveSemiSolid(cx2, cy2)) cx2 = ::Landscape.GetWidth(); // Abort right
2675  else
2676  {
2677  for (lokay = true, cnt = 0; cnt < height; cnt++) if (!GBackLiquid(cx2, cy2 + 1 + cnt)) lokay = false;
2678  if (lokay) rl2++; // Run okay
2679  else rl2 = 0; // No run
2680  }
2681  }
2682  // Check runs
2683  if (rl1 >= width) { rx = cx1 + rl1 / 2; ry = cy1; fFound = true; break; }
2684  if (rl2 >= width) { rx = cx2 - rl2 / 2; ry = cy2; fFound = true; break; }
2685  }
2686 
2687  if (fFound) AboveSemiSolid(rx, ry);
2688 
2689  return fFound;
2690 }
2691 
2692 bool FindLiquid(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
2693 {
2694  int32_t cx1, cx2, cy1, cy2, rl1 = 0, rl2 = 0;
2695 
2696  for (cx1 = cx2 = rx, cy1 = cy2 = ry; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2697  {
2698  // Left search
2699  if (cx1 > 0)
2700  {
2701  if (FindLiquidHeight(cx1, cy1, height)) rl1++;
2702  else rl1 = 0;
2703  }
2704  // Right search
2705  if (cx2 < ::Landscape.GetWidth())
2706  {
2707  if (FindLiquidHeight(cx2, cy2, height)) rl2++;
2708  else rl2 = 0;
2709  }
2710  // Check runs
2711  if (rl1 >= width) { rx = cx1 + rl1 / 2; ry = cy1; return true; }
2712  if (rl2 >= width) { rx = cx2 - rl2 / 2; ry = cy2; return true; }
2713  }
2714 
2715  return false;
2716 }
2717 
2718 // Starting from rx/ry, searches for tunnel background
2719 // Tunnel background == no sky && no semi/solid material (density < 25)
2720 bool FindTunnel(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
2721 {
2722  int32_t cx1, cx2, cy1, cy2, rl1 = 0, rl2 = 0;
2723 
2724  for (cx1 = cx2 = rx, cy1 = cy2 = ry; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2725  {
2726  // Left search
2727  if (cx1 > 0)
2728  {
2729  if (FindTunnelHeight(cx1, cy1, height)) rl1++;
2730  else rl1 = 0;
2731  }
2732  // Right search
2733  if (cx2 < ::Landscape.GetWidth())
2734  {
2735  if (FindTunnelHeight(cx2, cy2, height)) rl2++;
2736  else rl2 = 0;
2737  }
2738  // Check runs
2739  if (rl1 >= width) { rx = cx1 + rl1 / 2; ry = cy1; return true; }
2740  if (rl2 >= width) { rx = cx2 - rl2 / 2; ry = cy2; return true; }
2741  }
2742 
2743  return false;
2744 }
2745 
2746 // Starting from rx/ry, searches for a width of solid ground. Extreme distances
2747 // may not exceed hrange. Returns bottom center of surface found.
2748 bool FindLevelGround(int32_t &rx, int32_t &ry, int32_t width, int32_t hrange)
2749 {
2750  bool fFound = false;
2751 
2752  int32_t cx1, cx2, cy1, cy2, rh1, rh2, rl1, rl2;
2753 
2754  cx1 = cx2 = rx; cy1 = cy2 = ry;
2755  rh1 = cy1; rh2 = cy2;
2756  rl1 = rl2 = 0;
2757 
2758  for (cx1--, cx2++; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2759  {
2760  // Left search
2761  if (cx1 > 0) // Still going
2762  {
2763  if (!AboveSemiSolid(cx1, cy1)) cx1 = -1; // Abort left
2764  else
2765  {
2766  if (GBackSolid(cx1, cy1 + 1) && (Abs(cy1 - rh1) < hrange))
2767  rl1++; // Run okay
2768  else
2769  {
2770  rl1 = 0; rh1 = cy1;
2771  } // No run
2772  }
2773  }
2774 
2775  // Right search
2776  if (cx2 < ::Landscape.GetWidth()) // Still going
2777  {
2778  if (!AboveSemiSolid(cx2, cy2)) cx2 = ::Landscape.GetWidth(); // Abort right
2779  else
2780  {
2781  if (GBackSolid(cx2, cy2 + 1) && (Abs(cy2 - rh2) < hrange))
2782  rl2++; // Run okay
2783  else
2784  {
2785  rl2 = 0; rh2 = cy2;
2786  } // No run
2787  }
2788  }
2789 
2790  // Check runs
2791  if (rl1 >= width) { rx = cx1 + rl1 / 2; ry = cy1; fFound = true; break; }
2792  if (rl2 >= width) { rx = cx2 - rl2 / 2; ry = cy2; fFound = true; break; }
2793  }
2794 
2795  if (fFound) AboveSemiSolid(rx, ry);
2796 
2797  return fFound;
2798 }
2799 
2800 // Starting from rx/ry, searches for a width of solid level ground with
2801 // structure clearance (category). Returns bottom center of surface found.
2802 bool FindConSiteSpot(int32_t &rx, int32_t &ry, int32_t wdt, int32_t hgt, int32_t hrange)
2803 {
2804  bool fFound = false;
2805 
2806  // No hrange limit, use standard smooth surface limit
2807  if (hrange == -1) hrange = std::max(wdt / 4, 5);
2808 
2809  int32_t cx1, cx2, cy1, cy2, rh1, rh2, rl1, rl2;
2810 
2811  // Left offset starting position
2812  cx1 = std::min(rx + wdt / 2, ::Landscape.GetWidth() - 1); cy1 = ry;
2813  // No good: use centered starting position
2814  if (!AboveSemiSolid(cx1, cy1)) { cx1 = std::min<int32_t>(rx, ::Landscape.GetWidth() - 1); cy1 = ry; }
2815  // Right offset starting position
2816  cx2 = std::max(rx - wdt / 2, 0); cy2 = ry;
2817  // No good: use centered starting position
2818  if (!AboveSemiSolid(cx2, cy2)) { cx2 = std::min<int32_t>(rx, ::Landscape.GetWidth() - 1); cy2 = ry; }
2819 
2820  rh1 = cy1; rh2 = cy2; rl1 = rl2 = 0;
2821 
2822  for (cx1--, cx2++; (cx1 > 0) || (cx2 < ::Landscape.GetWidth()); cx1--, cx2++)
2823  {
2824  // Left search
2825  if (cx1 > 0) // Still going
2826  {
2827  if (!AboveSemiSolid(cx1, cy1))
2828  cx1 = -1; // Abort left
2829  else
2830  {
2831  if (GBackSolid(cx1, cy1 + 1) && (Abs(cy1 - rh1) < hrange))
2832  rl1++; // Run okay
2833  else
2834  {
2835  rl1 = 0; rh1 = cy1;
2836  } // No run
2837  }
2838  }
2839 
2840  // Right search
2841  if (cx2 < ::Landscape.GetWidth()) // Still going
2842  {
2843  if (!AboveSemiSolid(cx2, cy2))
2844  cx2 = ::Landscape.GetWidth(); // Abort right
2845  else
2846  {
2847  if (GBackSolid(cx2, cy2 + 1) && (Abs(cy2 - rh2) < hrange))
2848  rl2++; // Run okay
2849  else
2850  {
2851  rl2 = 0; rh2 = cy2;
2852  } // No run
2853  }
2854  }
2855 
2856  // Check runs & object overlap
2857  if (rl1 >= wdt) if (cx1 > 0)
2858  if (!Game.FindConstuctionSiteBlock(cx1, cy1 - hgt - 10, wdt, hgt + 40))
2859  {
2860  rx = cx1 + wdt / 2; ry = cy1; fFound = true; break;
2861  }
2862  if (rl2 >= wdt) if (cx2 < ::Landscape.GetWidth())
2863  if (!Game.FindConstuctionSiteBlock(cx2 - wdt, cy2 - hgt - 10, wdt, hgt + 40))
2864  {
2865  rx = cx2 - wdt / 2; ry = cy2; fFound = true; break;
2866  }
2867  }
2868 
2869  if (fFound) AboveSemiSolid(rx, ry);
2870 
2871  return fFound;
2872 }
2873 
2874 // Returns false on any solid pix in path.
2875 bool PathFreePix(int32_t x, int32_t y)
2876 {
2877  return !GBackSolid(x, y);
2878 }
2879 
2880 bool PathFree(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
2881 {
2882  return ForLine(x1, y1, x2, y2, &PathFreePix);
2883 }
2884 
2885 bool PathFree(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t *ix, int32_t *iy)
2886 {
2887  // use the standard Bresenham algorithm and just adjust it to behave correctly in the inversed case
2888  bool reverse = false;
2889  bool steep = Abs(y2 - y1) > Abs(x2 - x1);
2890 
2891  if (steep)
2892  {
2893  std::swap(x1, y1);
2894  std::swap(x2, y2);
2895  }
2896 
2897  if (x1 > x2)
2898  {
2899  std::swap(x1, x2);
2900  std::swap(y1, y2);
2901  reverse = true;
2902  }
2903 
2904  if (!reverse)
2905  {
2906  int32_t deltax = x2 - x1;
2907  int32_t deltay = Abs(y2 - y1);
2908  int32_t error = 0;
2909  int32_t ystep = (y1 < y2) ? 1 : -1;
2910  int32_t y = y1;
2911 
2912  for (int32_t x = x1; x <= x2; x++)
2913  {
2914  if (steep)
2915  {
2916  if (GBackSolid(y, x))
2917  {
2918  if (ix) { *ix = y; *iy = x; }
2919  return false;
2920  }
2921  }
2922  else
2923  {
2924  if (GBackSolid(x, y))
2925  {
2926  if (ix) { *ix = x; *iy = y; }
2927  return false;
2928  }
2929  }
2930 
2931  error += deltay;
2932  if (2 * error >= deltax)
2933  {
2934  y += ystep;
2935  error -= deltax;
2936  }
2937  }
2938  }
2939  else // reverse
2940  {
2941  int32_t deltax = x2 - x1;
2942  int32_t deltay = Abs(y2 - y1);
2943  int32_t error = 0;
2944  int32_t ystep = (y1 < y2) ? 1 : -1;
2945  int32_t y = y2;
2946 
2947  // normal (inverse) routine
2948  for (int32_t x = x2; x >= x1; x--)
2949  {
2950  if (steep)
2951  {
2952  if (GBackSolid(y, x))
2953  {
2954  if (ix) { *ix = y; *iy = x; }
2955  return false;
2956  }
2957  }
2958  else
2959  {
2960  if (GBackSolid(x, y))
2961  {
2962  if (ix) { *ix = x; *iy = y; }
2963  return false;
2964  }
2965  }
2966 
2967  error -= deltay;
2968  if (2 * error <= -deltax)
2969  {
2970  y -= ystep;
2971  error += deltax;
2972  }
2973 
2974  }
2975  }
2976  // no solid material encountered: path free!
2977  return true;
2978 }
2979 
2980 bool PathFreeIgnoreVehiclePix(int32_t x, int32_t y)
2981 {
2982  BYTE byPix = ::Landscape.GetPix(x, y);
2983  return !byPix || !DensitySolid(::Landscape.GetPixMat(byPix)) || ::Landscape.GetPixMat(byPix) == MVehic;
2984 }
2985 
2986 bool PathFreeIgnoreVehicle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t *ix, int32_t *iy)
2987 {
2988  return ForLine(x1, y1, x2, y2, &PathFreeIgnoreVehiclePix, ix, iy);
2989 }
2990 
2991 int32_t TrajectoryDistance(int32_t iFx, int32_t iFy, C4Real iXDir, C4Real iYDir, int32_t iTx, int32_t iTy)
2992 {
2993  int32_t iClosest = Distance(iFx, iFy, iTx, iTy);
2994  // Follow free trajectory, take closest point distance
2995  C4Real cx = itofix(iFx), cy = itofix(iFy);
2996  int32_t cdis;
2997  while (Inside(fixtoi(cx), 0, ::Landscape.GetWidth() - 1) && Inside(fixtoi(cy), 0, ::Landscape.GetHeight() - 1) && !GBackSolid(fixtoi(cx), fixtoi(cy)))
2998  {
2999  cdis = Distance(fixtoi(cx), fixtoi(cy), iTx, iTy);
3000  if (cdis < iClosest) iClosest = cdis;
3001  cx += iXDir; cy += iYDir; iYDir += GravAccel;
3002  }
3003  return iClosest;
3004 }
3005 
3006 static constexpr int32_t
3007 Throwing_MaxVertical = 50,
3008 Throwing_MaxHorizontal = 60;
3009 
3010 bool FindThrowingPosition(int32_t iTx, int32_t iTy, C4Real fXDir, C4Real fYDir, int32_t iHeight, int32_t &rX, int32_t &rY)
3011 {
3012 
3013  // Start underneath throwing target
3014  rX = iTx; rY = iTy; // improve: check from overhanging cliff
3015  if (!SemiAboveSolid(rX, rY)) return false;
3016 
3017  // Target too far above surface
3018  if (!Inside(rY - iTy, -Throwing_MaxVertical, +Throwing_MaxVertical)) return false;
3019 
3020  // Search in direction according to launch fXDir
3021  int32_t iDir = +1; if (fXDir > 0) iDir = -1;
3022 
3023  // Move along surface
3024  for (int32_t cnt = 0; Inside<int32_t>(rX, 0, ::Landscape.GetWidth() - 1) && (cnt <= Throwing_MaxHorizontal); rX += iDir, cnt++)
3025  {
3026  // Adjust to surface
3027  if (!SemiAboveSolid(rX, rY)) return false;
3028 
3029  // Check trajectory distance
3030  int32_t itjd = TrajectoryDistance(rX, rY - iHeight, fXDir, fYDir, iTx, iTy);
3031 
3032  // Hitting range: success
3033  if (itjd <= 2) return true;
3034  }
3035 
3036  // Failure
3037  return false;
3038 
3039 }
3040 
3041 static constexpr int32_t
3042  Closest_MaxRange = 200,
3043  Closest_Step = 10;
3044 
3045 bool FindClosestFree(int32_t &rX, int32_t &rY, int32_t iAngle1, int32_t iAngle2,
3046  int32_t iExcludeAngle1, int32_t iExcludeAngle2)
3047 {
3048  int32_t iX, iY;
3049  for (int32_t iR = Closest_Step; iR < Closest_MaxRange; iR += Closest_Step)
3050  for (int32_t iAngle = iAngle1; iAngle < iAngle2; iAngle += Closest_Step)
3051  if (!Inside(iAngle, iExcludeAngle1, iExcludeAngle2))
3052  {
3053  iX = rX + fixtoi(Sin(itofix(iAngle))*iR);
3054  iY = rY - fixtoi(Cos(itofix(iAngle))*iR);
3055  if (Inside<int32_t>(iX, 0, ::Landscape.GetWidth() - 1))
3056  if (Inside<int32_t>(iY, 0, ::Landscape.GetHeight() - 1))
3057  if (!GBackSemiSolid(iX, iY))
3058  {
3059  rX = iX; rY = iY; return true;
3060  }
3061  }
3062  return false;
3063 }
3064 
3065 bool ConstructionCheck(C4PropList * PropList, int32_t iX, int32_t iY, C4Object *pByObj)
3066 {
3067  C4Def *ndef;
3068  // Check def
3069  if (!(ndef = PropList->GetDef()))
3070  {
3071  if (pByObj) GameMsgObjectError(FormatString(LoadResStr("IDS_OBJ_UNDEF"), PropList->GetName()).getData(), pByObj);
3072  return false;
3073  }
3074  // Constructable?
3075  if (!ndef->Constructable)
3076  {
3077  if (pByObj) GameMsgObjectError(FormatString(LoadResStr("IDS_OBJ_NOCON"), ndef->GetName()).getData(), pByObj);
3078  return false;
3079  }
3080  // Check area
3081  int32_t rtx, rty, wdt, hgt;
3082  wdt = ndef->Shape.Wdt; hgt = ndef->Shape.Hgt - ndef->ConSizeOff;
3083  rtx = iX - wdt / 2; rty = iY - hgt;
3084  if (::Landscape.AreaSolidCount(rtx, rty, wdt, hgt) > (wdt*hgt / 20))
3085  {
3086  if (pByObj) GameMsgObjectError(LoadResStr("IDS_OBJ_NOROOM"), pByObj);
3087  return false;
3088  }
3089  if (::Landscape.AreaSolidCount(rtx, rty + hgt, wdt, 5) < (wdt * 2))
3090  {
3091  if (pByObj) GameMsgObjectError(LoadResStr("IDS_OBJ_NOLEVEL"), pByObj);
3092  return false;
3093  }
3094  // Check other structures
3095  C4Object *other;
3096  if ((other = Game.FindConstuctionSiteBlock(rtx, rty, wdt, hgt)))
3097  {
3098  if (pByObj) GameMsgObjectError(FormatString(LoadResStr("IDS_OBJ_NOOTHER"), other->GetName()).getData(), pByObj);
3099  return false;
3100  }
3101  return true;
3102 }
3103 
3104 // Finds the next pixel position moving to desired slide.
3105 bool C4Landscape::FindMatPath(int32_t &fx, int32_t &fy, int32_t ydir, int32_t mdens, int32_t mslide) const
3106 {
3107  assert(mdens <= C4M_Solid); // mdens normalized in InsertMaterial
3108 
3109  int32_t cslide;
3110  bool fLeft = true, fRight = true;
3111 
3112  // One downwards
3113  if (GetDensity(fx, fy + ydir) < mdens) { fy += ydir; return true; }
3114 
3115  // Find downwards slide path
3116  for (cslide = 1; (cslide <= mslide) && (fLeft || fRight); cslide++)
3117  {
3118  // Check left
3119  if (fLeft)
3120  {
3121  if (GetDensity(fx - cslide, fy) >= mdens) // Left clogged
3122  fLeft = false;
3123  else if (GetDensity(fx - cslide, fy + ydir) < mdens) // Left slide okay
3124  {
3125  fx--; return true;
3126  }
3127  }
3128  // Check right
3129  if (fRight)
3130  {
3131  if (GetDensity(fx + cslide, fy) >= mdens) // Right clogged
3132  fRight = false;
3133  else if (GetDensity(fx + cslide, fy + ydir) < mdens) // Right slide okay
3134  {
3135  fx++; return true;
3136  }
3137  }
3138  }
3139 
3140  return false;
3141 }
3142 
3143 // Finds the closest immediate slide position.
3144 bool C4Landscape::FindMatSlide(int32_t &fx, int32_t &fy, int32_t ydir, int32_t mdens, int32_t mslide) const
3145 {
3146  assert(mdens <= C4M_Solid); // mdens normalized in InsertMaterial and mrfInsertCheck
3147  int32_t cslide;
3148  bool fLeft = true, fRight = true;
3149 
3150  // One downwards
3151  if (GetDensity(fx, fy + ydir) < mdens) { fy += ydir; return true; }
3152 
3153  // Find downwards slide path
3154  for (cslide = 1; (cslide <= mslide) && (fLeft || fRight); cslide++)
3155  {
3156  // Check left
3157  if (fLeft)
3158  {
3159  if (GetDensity(fx - cslide, fy) >= mdens && GetDensity(fx - cslide, fy + ydir) >= mdens) // Left clogged
3160  fLeft = false;
3161  else if (GetDensity(fx - cslide, fy + ydir) < mdens) // Left slide okay
3162  {
3163  fx -= cslide; fy += ydir; return true;
3164  }
3165  }
3166  // Check right
3167  if (fRight)
3168  {
3169  if (GetDensity(fx + cslide, fy) >= mdens && GetDensity(fx + cslide, fy + ydir) >= mdens) // Right clogged
3170  fRight = false;
3171  else if (GetDensity(fx + cslide, fy + ydir) < mdens) // Right slide okay
3172  {
3173  fx += cslide; fy += ydir; return true;
3174  }
3175  }
3176  }
3177 
3178  return false;
3179 }
3180 
3181 // Find closest point with density below mdens. Note this may return a point outside of the landscape,
3182 // Assumption: There are no holes with smaller density inside of material with greater
3183 // density.
3184 bool C4Landscape::FindMatPathPush(int32_t &fx, int32_t &fy, int32_t mdens, int32_t mslide, bool liquid) const
3185 {
3186  // Startpoint must be inside landscape
3187  fx = Clamp<int32_t>(fx, 0, GetWidth() - 1);
3188  fy = Clamp<int32_t>(fy, 0, GetHeight() - 1);
3189  // Range to search, calculate bounds
3190  const int32_t iPushRange = 500;
3191  int32_t left = std::max<int32_t>(0, fx - iPushRange), right = std::min<int32_t>(GetWidth() - 1, fx + iPushRange),
3192  top = std::max<int32_t>(0, fy - iPushRange), bottom = std::min<int32_t>(GetHeight() - 1, fy + iPushRange);
3193  // Direction constants
3194  const int8_t R = 0, D = 1, L = 2, U = 3;
3195  int8_t dir = 0;
3196  int32_t x = fx, y = fy;
3197  // Get startpoint density
3198  int32_t dens = GetDensity(fx, fy);
3199  // Smaller density? We're done.
3200  if (dens < mdens)
3201  return true;
3202  // Right density?
3203  else if (dens == mdens)
3204  {
3205  // Find start point for border search
3206  for (int32_t i = 0; ; i++)
3207  if (x - i - 1 < left || GetDensity(x - i - 1, y) != mdens)
3208  {
3209  x -= i; dir = L; break;
3210  }
3211  else if (y - i - 1 < top || GetDensity(x, y - i - 1) != mdens)
3212  {
3213  y -= i; dir = U; break;
3214  }
3215  else if (x + i + 1 > right || GetDensity(x + i + 1, y) != mdens)
3216  {
3217  x += i; dir = R; break;
3218  }
3219  else if (y + i + 1 > bottom || GetDensity(x, y + i + 1) != mdens)
3220  {
3221  y += i; dir = D; break;
3222  }
3223  }
3224  // Greater density
3225  else
3226  {
3227  // Try to find a way out
3228  int i = 1;
3229  for (; i < iPushRange; i++)
3230  if (GetDensity(x - i, y) <= mdens)
3231  {
3232  x -= i; dir = R; break;
3233  }
3234  else if (GetDensity(x, y - i) <= mdens)
3235  {
3236  y -= i; dir = D; break;
3237  }
3238  else if (GetDensity(x + i, y) <= mdens)
3239  {
3240  x += i; dir = L; break;
3241  }
3242  else if (GetDensity(x, y + i) <= mdens)
3243  {
3244  y += i; dir = U; break;
3245  }
3246  // Not found?
3247  if (i >= iPushRange) return false;
3248  // Done?
3249  if (GetDensity(x, y) < mdens)
3250  {
3251  fx = x; fy = y;
3252  return true;
3253  }
3254  }
3255  // Save startpoint of search
3256  int32_t sx = x, sy = y, sdir = dir;
3257  // Best point so far
3258  bool fGotBest = false; int32_t bx = 0, by = 0, bdist = 0;
3259  // Start searching
3260  do
3261  {
3262  // We should always be in a material of same density
3263  assert(x >= left && y >= top && x <= right && y <= bottom && GetDensity(x, y) == mdens);
3264  // Calc new position
3265  int nx = x, ny = y;
3266  switch (dir)
3267  {
3268  case R: nx++; break;
3269  case D: ny++; break;
3270  case L: nx--; break;
3271  case U: ny--; break;
3272  default: assert(false);
3273  }
3274  // In bounds?
3275  bool fInBounds = (nx >= left && ny >= top && nx <= right && ny <= bottom);
3276  // Get density. Not this performs an SideOpen-check if outside landscape bounds.
3277  int32_t dens = GetDensity(nx, ny);
3278  // Flow possible?
3279  if (dens < mdens)
3280  {
3281  // Calculate "distance".
3282  int32_t dist = Abs(nx - fx) + mslide * (liquid ? fy - ny : Abs(fy - ny));
3283  // New best point?
3284  if (!fGotBest || dist < bdist)
3285  {
3286  // Save it
3287  bx = nx; by = ny; bdist = dist; fGotBest = true;
3288  // Adjust borders: We can obviously safely ignore anything at greater distance
3289  top = std::max<int32_t>(top, fy - dist / mslide - 1);
3290  if (!liquid)
3291  {
3292  bottom = std::min<int32_t>(bottom, fy + dist / mslide + 1);
3293  left = std::max<int32_t>(left, fx - dist - 1);
3294  right = std::min<int32_t>(right, fx + dist + 1);
3295  }
3296  // Set new startpoint
3297  sx = x; sy = y; sdir = dir;
3298  }
3299  }
3300  // Step?
3301  if (fInBounds && dens == mdens)
3302  {
3303  // New point
3304  x = nx; y = ny;
3305  // Turn left
3306  (dir += 3) %= 4;
3307  }
3308  // Otherwise: Turn right
3309  else
3310  {
3311  ++dir;
3312  dir %= 4;
3313  }
3314  } while (x != sx || y != sy || dir != sdir);
3315  // Nothing found?
3316  if (!fGotBest) return false;
3317  // Return it
3318  fx = bx; fy = by;
3319  return true;
3320 }
3321 
3322 int32_t C4Landscape::AreaSolidCount(int32_t x, int32_t y, int32_t wdt, int32_t hgt) const
3323 {
3324  int32_t cx, cy, ascnt = 0;
3325  for (cy = y; cy < y + hgt; cy++)
3326  for (cx = x; cx < x + wdt; cx++)
3327  if (GBackSolid(cx, cy))
3328  ascnt++;
3329  return ascnt;
3330 }
3331 
3332 void C4Landscape::FindMatTop(int32_t mat, int32_t &x, int32_t &y, bool distant_first) const
3333 {
3334  int32_t mslide, cslide, tslide, distant_x = 0;
3335  bool fLeft, fRight;
3336 
3337  if (!MatValid(mat)) return;
3338  mslide = ::MaterialMap.Map[mat].MaxSlide;
3339 
3340  do
3341  {
3342  // Catch most common case: Walk upwards until material changes
3343  while (GetMat(x, y - 1) == mat) --y;
3344 
3345  // Find upwards slide
3346  fLeft = true; fRight = true; tslide = 0; distant_x = x;
3347  for (cslide = 1; (cslide <= mslide) && (fLeft || fRight); cslide++)
3348  {
3349  // Left
3350  if (fLeft)
3351  {
3352  if (GetMat(x - cslide, y) != mat) fLeft = false;
3353  else
3354  {
3355  distant_x = x - cslide;
3356  if (GetMat(distant_x, y - 1) == mat) { tslide = -cslide; break; }
3357  }
3358  }
3359  // Right
3360  if (fRight)
3361  {
3362  if (GetMat(x + cslide, y) != mat) fRight = false;
3363  else
3364  {
3365  distant_x = x + cslide;
3366  if (GetMat(distant_x, y - 1) == mat) { tslide = +cslide; break; }
3367  }
3368  }
3369  }
3370 
3371  // Slide
3372  if (tslide) { x += tslide; y--; }
3373 
3374  } while (tslide);
3375 
3376  // return top pixel max slide away from center if desired
3377  if (distant_first) x = distant_x;
3378 
3379 }
3380 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
3381 /* ++++++++++++++ Editor mode (draw landscape with brush)+++++++++++++++++++ */
3382 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
3383 
3385 {
3386  p->mode = mode;
3387 }
3388 
3390 {
3391  return p->mode;
3392 }
3393 
3394 bool C4Landscape::P::GetMapColorIndex(const char *szMaterial, const char *szTexture, BYTE & rbyCol) const
3395 {
3396  // Sky
3397  if (SEqual(szMaterial, C4TLS_MatSky))
3398  rbyCol = 0;
3399  // Material-Texture
3400  else
3401  {
3402  rbyCol = ::TextureMap.GetIndex(szMaterial, szTexture);
3403  if (!rbyCol) return false;
3404  }
3405  // Found
3406  return true;
3407 }
3408 
3409 bool C4Landscape::DrawBrush(int32_t iX, int32_t iY, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
3410 {
3411  BYTE byCol, byColBkg;
3412  // Get map color index by material-texture
3413  if (!p->GetMapColorIndex(szMaterial, szTexture, byCol)) return false;
3414  if (!p->GetMapColorIndex(szBackMaterial, szBackTexture, byColBkg)) return false;
3415  // Get material shape size
3416  C4Texture *texture = ::TextureMap.GetTexture(szTexture);
3417  int32_t shape_wdt = 0, shape_hgt = 0;
3418  if (texture && texture->GetMaterialShape())
3419  {
3420  shape_wdt = texture->GetMaterialShape()->GetMaxPolyWidth() / p->MapZoom;
3421  shape_hgt = texture->GetMaterialShape()->GetMaxPolyHeight() / p->MapZoom;
3422  }
3423  // Draw
3424  switch (p->mode)
3425  {
3426  // Dynamic: ignore
3428  break;
3429  // Static: draw to map by material-texture-index, chunk-o-zoom to landscape
3430  case LandscapeMode::Static:
3431  {
3432  // Draw to map
3433  int32_t iRadius = std::max<int32_t>(2 * iGrade / p->MapZoom, 1);
3434  if (iRadius == 1)
3435  {
3436  p->Map->SetPix(iX / p->MapZoom, iY / p->MapZoom, byCol);
3437  p->MapBkg->SetPix(iX / p->MapZoom, iY / p->MapZoom, byColBkg);
3438  }
3439  else
3440  {
3441  p->Map->Circle(iX / p->MapZoom, iY / p->MapZoom, iRadius, byCol);
3442  p->MapBkg->Circle(iX / p->MapZoom, iY / p->MapZoom, iRadius, byColBkg);
3443  }
3444  // Update landscape
3445  p->MapToLandscape(this, *p->Map, *p->MapBkg, iX / p->MapZoom - iRadius - 1 - shape_wdt, iY / p->MapZoom - iRadius - 1 - shape_hgt, 2 * iRadius + 2 + shape_wdt * 2, 2 * iRadius + 2 + shape_hgt * 2);
3446  SetMapChanged();
3447  }
3448  break;
3449  // Exact: draw directly to landscape by color & pattern
3450  case LandscapeMode::Exact:
3451  {
3452  C4Rect BoundingBox(iX - iGrade - 1, iY - iGrade - 1, iGrade * 2 + 2, iGrade * 2 + 2);
3453  // Draw to landscape
3454  p->PrepareChange(this, BoundingBox);
3455  p->Surface8->Circle(iX, iY, iGrade, byCol);
3456  p->Surface8Bkg->Circle(iX, iY, iGrade, byColBkg);
3457  p->FinishChange(this, BoundingBox);
3458  break;
3459  }
3460  case LandscapeMode::Undefined: assert(false); break;
3461  }
3462  return true;
3463 }
3464 
3465 bool C4Landscape::P::DrawLineLandscape(int32_t iX, int32_t iY, int32_t iGrade, uint8_t line_color, uint8_t line_color_bkg)
3466 {
3467  Surface8->Circle(iX, iY, iGrade, line_color);
3468  Surface8Bkg->Circle(iX, iY, iGrade, line_color_bkg);
3469  return true;
3470 }
3471 
3472 bool C4Landscape::P::DrawLineMap(int32_t iX, int32_t iY, int32_t iRadius, uint8_t line_color, uint8_t line_color_bkg)
3473 {
3474  if (!Map) return false;
3475  if (iRadius == 1)
3476  {
3477  Map->SetPix(iX, iY, line_color); MapBkg->SetPix(iX, iY, line_color_bkg);
3478  }
3479  else
3480  {
3481  Map->Circle(iX, iY, iRadius, line_color); MapBkg->Circle(iX, iY, iRadius, line_color_bkg);
3482  }
3483  return true;
3484 }
3485 
3486 bool C4Landscape::DrawLine(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
3487 {
3488  // Get map color index by material-texture
3489  uint8_t line_color, line_color_bkg;
3490  if (!p->GetMapColorIndex(szMaterial, szTexture, line_color)) return false;
3491  if (!p->GetMapColorIndex(szBackMaterial, szBackTexture, line_color_bkg)) return false;
3492  // Get material shape size
3493  C4Texture *texture = ::TextureMap.GetTexture(szTexture);
3494  int32_t shape_wdt = 0, shape_hgt = 0;
3495  if (texture && texture->GetMaterialShape())
3496  {
3497  shape_wdt = texture->GetMaterialShape()->GetMaxPolyWidth() / p->MapZoom;
3498  shape_hgt = texture->GetMaterialShape()->GetMaxPolyHeight() / p->MapZoom;
3499  }
3500  // Draw
3501  switch (p->mode)
3502  {
3503  // Dynamic: ignore
3505  break;
3506  // Static: draw to map by material-texture-index, chunk-o-zoom to landscape
3507  case LandscapeMode::Static:
3508  {
3509  // Draw to map
3510  int32_t iRadius = std::max<int32_t>(2 * iGrade / p->MapZoom, 1);
3511  iX1 /= p->MapZoom; iY1 /= p->MapZoom; iX2 /= p->MapZoom; iY2 /= p->MapZoom;
3512  ForLine(iX1, iY1, iX2, iY2, [this, line_color, line_color_bkg, iRadius](int32_t x, int32_t y) { return p->DrawLineMap(x, y, iRadius, line_color, line_color_bkg); });
3513  // Update landscape
3514  int iUpX = std::min(iX1, iX2) - iRadius - 1;
3515  int iUpY = std::min(iY1, iY2) - iRadius - 1;
3516  int iUpWdt = Abs(iX2 - iX1) + 2 * iRadius + 2;
3517  int iUpHgt = Abs(iY2 - iY1) + 2 * iRadius + 2;
3518  p->MapToLandscape(this, *p->Map, *p->MapBkg, iUpX - shape_wdt, iUpY - shape_hgt, iUpWdt + shape_wdt * 2, iUpHgt + shape_hgt * 2);
3519  SetMapChanged();
3520  }
3521  break;
3522  // Exact: draw directly to landscape by color & pattern
3523  case LandscapeMode::Exact:
3524  {
3525  // Set texture pattern & get material color
3526  C4Rect BoundingBox(iX1 - iGrade, iY1 - iGrade, iGrade * 2 + 1, iGrade * 2 + 1);
3527  BoundingBox.Add(C4Rect(iX2 - iGrade, iY2 - iGrade, iGrade * 2 + 1, iGrade * 2 + 1));
3528  // Draw to landscape
3529  p->PrepareChange(this, BoundingBox);
3530  ForLine(iX1, iY1, iX2, iY2, [this, line_color, line_color_bkg, iGrade](int32_t x, int32_t y) { return p->DrawLineLandscape(x, y, iGrade, line_color, line_color_bkg); });
3531  p->FinishChange(this, BoundingBox);
3532  break;
3533  }
3534  case LandscapeMode::Undefined: assert(false); break;
3535  }
3536  return true;
3537 }
3538 
3539 bool C4Landscape::DrawBox(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
3540 {
3541  // get upper-left/lower-right - corners
3542  int32_t iX0 = std::min(iX1, iX2); int32_t iY0 = std::min(iY1, iY2);
3543  iX2 = std::max(iX1, iX2); iY2 = std::max(iY1, iY2); iX1 = iX0; iY1 = iY0;
3544  BYTE byCol, byColBkg;
3545  // Get map color index by material-texture
3546  if (!p->GetMapColorIndex(szMaterial, szTexture, byCol)) return false;
3547  if (!p->GetMapColorIndex(szBackMaterial, szBackTexture, byColBkg)) return false;
3548  // Get material shape size
3549  C4Texture *texture = ::TextureMap.GetTexture(szTexture);
3550  int32_t shape_wdt = 0, shape_hgt = 0;
3551  if (texture && texture->GetMaterialShape())
3552  {
3553  shape_wdt = texture->GetMaterialShape()->GetMaxPolyWidth() / p->MapZoom;
3554  shape_hgt = texture->GetMaterialShape()->GetMaxPolyHeight() / p->MapZoom;
3555  }
3556  // Draw
3557  switch (p->mode)
3558  {
3559  // Dynamic: ignore
3561  break;
3562  // Static: draw to map by material-texture-index, chunk-o-zoom to landscape
3563  case LandscapeMode::Static:
3564  // Draw to map
3565  iX1 /= p->MapZoom; iY1 /= p->MapZoom; iX2 /= p->MapZoom; iY2 /= p->MapZoom;
3566  p->Map->Box(iX1, iY1, iX2, iY2, byCol);
3567  p->MapBkg->Box(iX1, iY1, iX2, iY2, byColBkg);
3568  // Update landscape
3569  p->MapToLandscape(this, *p->Map, *p->MapBkg, iX1 - 1 - shape_wdt, iY1 - 1 - shape_hgt, iX2 - iX1 + 3 + shape_wdt * 2, iY2 - iY1 + 3 + shape_hgt * 2);
3570  SetMapChanged();
3571  break;
3572  // Exact: draw directly to landscape by color & pattern
3573  case LandscapeMode::Exact:
3574  {
3575  C4Rect BoundingBox(iX1, iY1, iX2 - iX1 + 1, iY2 - iY1 + 1);
3576  // Draw to landscape
3577  p->PrepareChange(this, BoundingBox);
3578  p->Surface8->Box(iX1, iY1, iX2, iY2, byCol);
3579  p->Surface8Bkg->Box(iX1, iY1, iX2, iY2, byColBkg);
3580  p->FinishChange(this, BoundingBox);
3581  break;
3582  }
3583  case LandscapeMode::Undefined: assert(false); break;
3584  }
3585  return true;
3586 }
3587 
3588 bool C4Landscape::DrawChunks(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, int32_t icntx, int32_t icnty, const char *szMaterial, const char *szTexture, bool bIFT)
3589 {
3590  BYTE byColor;
3591  if (!p->GetMapColorIndex(szMaterial, szTexture, byColor)) return false;
3592 
3593  int32_t iMaterial = ::MaterialMap.Get(szMaterial);
3594  if (!MatValid(iMaterial))
3595  return false;
3596 
3598 
3599  C4Rect BoundingBox(tx - 5, ty - 5, wdt + 10, hgt + 10);
3600  p->PrepareChange(this, BoundingBox);
3601 
3602  // assign clipper
3603  p->Surface8->Clip(BoundingBox.x, BoundingBox.y, BoundingBox.x + BoundingBox.Wdt, BoundingBox.y + BoundingBox.Hgt);
3604  p->Surface8Bkg->Clip(BoundingBox.x, BoundingBox.y, BoundingBox.x + BoundingBox.Wdt, BoundingBox.y + BoundingBox.Hgt);
3606 
3607  // draw all chunks
3608  int32_t x, y;
3609  for (x = 0; x < icntx; x++)
3610  for (y = 0; y < icnty; y++)
3611  p->DrawChunk(this, tx + wdt*x / icntx, ty + hgt*y / icnty, wdt / icntx, hgt / icnty, byColor, bIFT ? p->DefaultBkgMat(byColor) : 0, shape, Random(1000));
3612 
3613  // remove clipper
3614  p->Surface8->NoClip();
3615  p->Surface8Bkg->NoClip();
3616 
3617  p->FinishChange(this, BoundingBox);
3618 
3619  // success
3620  return true;
3621 }
3622 
3623 bool C4Landscape::DrawPolygon(int *vtcs, int length, const char *szMaterial, const char* szBackMaterial, bool fDrawBridge)
3624 {
3625  if (length < 6) return false;
3626  if (length % 2 == 1) return false;
3627  // get texture
3628  int32_t iMatTex = ::TextureMap.GetIndexMatTex(szMaterial);
3629  if (!iMatTex) return false;
3630  uint8_t mcol = MatTex2PixCol(iMatTex);
3631  // get background texture
3632  uint8_t mcolBkg = 0;
3633  if (szBackMaterial != nullptr)
3634  {
3635  const int32_t iBackMatTex = ::TextureMap.GetIndexMatTex(szBackMaterial);
3636  if (!iBackMatTex) return false;
3637  mcolBkg = MatTex2PixCol(iBackMatTex);
3638  }
3639  // do bridging?
3640  uint8_t *conversion_map = nullptr;
3641  if (fDrawBridge)
3642  {
3643  conversion_map = p->GetBridgeMatConversion(this, MatTex2PixCol(iMatTex));
3644  mcolBkg = Transparent;
3645  }
3646  // prepare pixel count update
3647  C4Rect BoundingBox = getBoundingBox(vtcs, length);
3648  // draw polygon
3649  p->PrepareChange(this, BoundingBox);
3650  p->ForPolygon(this, vtcs, length / 2, nullptr, nullptr, mcol, mcolBkg, conversion_map);
3651  p->FinishChange(this, BoundingBox);
3652  return true;
3653 }
3654 
3656 {
3657  return p->Surface8 ? p->Surface8->pPal : nullptr;
3658 }
3659 
3660 int32_t C4Landscape::GetWidth() const
3661 {
3662  return p->Width;
3663 }
3664 
3665 int32_t C4Landscape::GetHeight() const
3666 {
3667  return p->Height;
3668 }
3669 
3671 {
3672  return p->MapZoom;
3673 }
3674 
3676 {
3677  return p->Gravity;
3678 }
3679 
3681 {
3682  p->Gravity = g;
3683 }
3684 
3685 BYTE C4Landscape::_GetPix(int32_t x, int32_t y) const
3686 {
3687 #ifdef _DEBUG
3688  if (x < 0 || y < 0 || x >= p->Width || y >= p->Height) { BREAKPOINT_HERE; }
3689 #endif
3690  return p->Surface8->_GetPix(x, y);
3691 }
3692 
3693 BYTE C4Landscape::GetPix(int32_t x, int32_t y) const // get landscape pixel (bounds checked)
3694 {
3695  extern BYTE MCVehic;
3696  // Border checks
3697  if (x < 0)
3698  {
3699  return p->LeftColPix[Clamp(y, 0, GetHeight()-1)];
3700  }
3701  if (static_cast<uint32_t>(x) >= static_cast<uint32_t>(p->Width))
3702  {
3703  return p->RightColPix[Clamp(y, 0, GetHeight()-1)];
3704  }
3705  if (y < 0)
3706  {
3707  return p->TopRowPix[x];
3708  }
3709  if (static_cast<uint32_t>(y) >= static_cast<uint32_t>(p->Height))
3710  {
3711  return p->BottomRowPix[x];
3712  }
3713  return p->Surface8->_GetPix(x, y);
3714 }
3715 
3716 int32_t C4Landscape::_GetMat(int32_t x, int32_t y) const
3717 {
3718  return p->Pix2Mat[_GetPix(x, y)];
3719 }
3720 
3721 int32_t C4Landscape::_GetDensity(int32_t x, int32_t y) const // get landscape density (bounds not checked)
3722 {
3723  return p->Pix2Dens[_GetPix(x, y)];
3724 }
3725 
3726 int32_t C4Landscape::_GetPlacement(int32_t x, int32_t y) const // get landscape material placement (bounds not checked)
3727 {
3728  return p->Pix2Place[_GetPix(x, y)];
3729 }
3730 
3731 int32_t C4Landscape::GetMat(int32_t x, int32_t y) const // get landscape material (bounds checked)
3732 {
3733  return p->Pix2Mat[GetPix(x, y)];
3734 }
3735 
3736 int32_t C4Landscape::GetDensity(int32_t x, int32_t y) const // get landscape density (bounds checked)
3737 {
3738  return p->Pix2Dens[GetPix(x, y)];
3739 }
3740 
3741 int32_t C4Landscape::GetPlacement(int32_t x, int32_t y) const // get landscape material placement (bounds checked)
3742 {
3743  return p->Pix2Place[GetPix(x, y)];
3744 }
3745 
3746 BYTE C4Landscape::_GetBackPix(int32_t x, int32_t y) const // get landscape pixel (bounds not checked)
3747 {
3748 #ifdef _DEBUG
3749  if (x < 0 || y < 0 || x >= p->Width || y >= p->Height) { BREAKPOINT_HERE; }
3750 #endif
3751  return p->Surface8Bkg->_GetPix(x, y);
3752 }
3753 
3754 BYTE C4Landscape::GetBackPix(int32_t x, int32_t y) const // get landscape pixel (bounds checked)
3755 {
3756  // Border checks
3757  if (x < 0)
3758  {
3759  return p->DefaultBkgMat(p->LeftColPix[Clamp(y, 0, GetHeight()-1)]);
3760  }
3761  if (static_cast<uint32_t>(x) >= static_cast<uint32_t>(GetWidth()))
3762  {
3763  return p->DefaultBkgMat(p->RightColPix[Clamp(y, 0, GetHeight()-1)]);
3764  }
3765  if (y < 0)
3766  {
3767  return p->DefaultBkgMat(p->TopRowPix[x]);
3768  }
3769  if (static_cast<uint32_t>(y) >= static_cast<uint32_t>(GetHeight()))
3770  {
3771  return p->DefaultBkgMat(p->BottomRowPix[x]);
3772  }
3773 
3774  return p->Surface8Bkg->_GetPix(x, y);
3775 }
3776 
3777 int32_t C4Landscape::_GetBackMat(int32_t x, int32_t y) const // get landscape material (bounds not checked)
3778 {
3779  return p->Pix2Mat[_GetBackPix(x, y)];
3780 }
3781 
3782 int32_t C4Landscape::_GetBackDensity(int32_t x, int32_t y) const // get landscape density (bounds not checked)
3783 {
3784  return p->Pix2Dens[_GetBackPix(x, y)];
3785 }
3786 
3787 int32_t C4Landscape::_GetBackPlacement(int32_t x, int32_t y) const // get landscape material placement (bounds not checked)
3788 {
3789  return p->Pix2Place[_GetBackPix(x, y)];
3790 }
3791 
3792 int32_t C4Landscape::GetBackMat(int32_t x, int32_t y) const // get landscape material (bounds checked)
3793 {
3794  return p->Pix2Mat[GetBackPix(x, y)];
3795 }
3796 
3797 int32_t C4Landscape::GetBackDensity(int32_t x, int32_t y) const // get landscape density (bounds checked)
3798 {
3799  return p->Pix2Dens[GetBackPix(x, y)];
3800 }
3801 
3802 int32_t C4Landscape::GetBackPlacement(int32_t x, int32_t y) const // get landscape material placement (bounds checked)
3803 {
3804  return p->Pix2Place[GetBackPix(x, y)];
3805 }
3806 
3807 bool C4Landscape::GetLight(int32_t x, int32_t y)
3808 {
3809  return GetBackPix(x, y) == 0 || p->Pix2Light[GetPix(x, y)];
3810 }
3811 
3812 bool C4Landscape::_GetLight(int32_t x, int32_t y)
3813 {
3814  return _GetBackPix(x, y) == 0 || p->Pix2Light[_GetPix(x, y)];
3815 }
3816 
3817 bool C4Landscape::_FastSolidCheck(int32_t x, int32_t y) const // checks whether there *might* be something solid at the point
3818 {
3819  return p->PixCnt[(x / 17) * p->PixCntPitch + (y / 15)] > 0;
3820 }
3821 
3823 {
3824  return (x / 17) * 17 + 17;
3825 }
3826 
3827 int32_t C4Landscape::GetPixMat(BYTE byPix) const { return p->Pix2Mat[byPix]; }
3828 
3829 int32_t C4Landscape::GetPixDensity(BYTE byPix) const { return p->Pix2Dens[byPix]; }
3830 
3831 bool C4Landscape::_PathFree(int32_t x, int32_t y, int32_t x2, int32_t y2) const
3832 {
3833  x /= 17; y /= 15; x2 /= 17; y2 /= 15;
3834  while (x != x2 && y != y2)
3835  {
3836  if (p->PixCnt[x * p->PixCntPitch + y])
3837  return false;
3838  if (x > x2) x--; else x++;
3839  if (y > y2) y--; else y++;
3840  }
3841  if (x != x2)
3842  do
3843  {
3844  if (p->PixCnt[x * p->PixCntPitch + y])
3845  return false;
3846  if (x > x2) x--; else x++;
3847  } while (x != x2);
3848  else
3849  while (y != y2)
3850  {
3851  if (p->PixCnt[x * p->PixCntPitch + y])
3852  return false;
3853  if (y > y2) y--; else y++;
3854  }
3855  return !p->PixCnt[x * p->PixCntPitch + y];
3856 }
3857 
3858 uint8_t *C4Landscape::P::GetBridgeMatConversion(const C4Landscape *d, int32_t for_material_col) const
3859 {
3860  // safety
3861  int32_t for_material = d->GetPixMat(for_material_col);
3862  if (for_material < 0 || for_material >= MaterialMap.Num) return nullptr;
3863  // query map. create if not done yet
3864  if (!BridgeMatConversion[for_material_col])
3865  {
3866  auto conv_map = std::make_unique<uint8_t[]>(C4M_MaxTexIndex);
3867  for (int32_t i = 0; i < C4M_MaxTexIndex; ++i)
3868  {
3869  if ((MatDensity(for_material) >= d->GetPixDensity(i)))
3870  {
3871  // bridge pixel OK here. change pixel.
3872  conv_map[i] = for_material_col;
3873  }
3874  else
3875  {
3876  // bridge pixel not OK - keep current pixel
3877  conv_map[i] = i;
3878  }
3879  }
3880  BridgeMatConversion[for_material_col] = std::move(conv_map);
3881  }
3882  return BridgeMatConversion[for_material_col].get();
3883 }
3884 
3885 bool C4Landscape::DrawQuad(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iX3, int32_t iY3, int32_t iX4, int32_t iY4, const char *szMaterial, const char *szBackMaterial, bool fDrawBridge)
3886 {
3887  // set vertices
3888  int32_t vtcs[8];
3889  vtcs[0] = iX1; vtcs[1] = iY1;
3890  vtcs[2] = iX2; vtcs[3] = iY2;
3891  vtcs[4] = iX3; vtcs[5] = iY3;
3892  vtcs[6] = iX4; vtcs[7] = iY4;
3893  return DrawPolygon(vtcs, 8, szMaterial, szBackMaterial, fDrawBridge);
3894 }
3895 
3896 BYTE C4Landscape::GetMapIndex(int32_t iX, int32_t iY) const
3897 {
3898  if (!p->Map) return 0;
3899  return p->Map->GetPix(iX, iY);
3900 }
3901 
3902 BYTE C4Landscape::GetBackMapIndex(int32_t iX, int32_t iY) const
3903 {
3904  if (!p->MapBkg) return 0;
3905  return p->MapBkg->GetPix(iX, iY);
3906 }
3907 
3908 void C4Landscape::P::PrepareChange(const C4Landscape *d, const C4Rect &BoundingBox)
3909 {
3910  // move solidmasks out of the way
3911  C4Rect SolidMaskRect = BoundingBox;
3912  if (pLandscapeRender)
3913  SolidMaskRect = pLandscapeRender->GetAffectedRect(pLandscapeRender->GetAffectedRect(SolidMaskRect));
3914  for (C4SolidMask * pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
3915  {
3916  pSolid->RemoveTemporary(SolidMaskRect);
3917  }
3918  UpdateMatCnt(d, BoundingBox, false);
3919 }
3920 
3922 {
3923  // Intersect bounding box with landscape
3924  BoundingBox.Intersect(C4Rect(0, 0, Width, Height));
3925  if (!BoundingBox.Wdt || !BoundingBox.Hgt) return;
3926  // update render
3927  if (pLandscapeRender)
3928  pLandscapeRender->Update(BoundingBox, d);
3929  UpdateMatCnt(d, BoundingBox, true);
3930  // Restore Solidmasks
3931  C4Rect SolidMaskRect = BoundingBox;
3932  if (pLandscapeRender)
3933  SolidMaskRect = pLandscapeRender->GetAffectedRect(pLandscapeRender->GetAffectedRect(SolidMaskRect));
3934  for (C4SolidMask * pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
3935  {
3936  pSolid->Repair(SolidMaskRect);
3937  }
3939  UpdatePixCnt(d, BoundingBox);
3940  // update FoW
3941  if (pFoW)
3942  {
3943  pFoW->Invalidate(BoundingBox);
3944  pFoW->Ambient.UpdateFromLandscape(*d, BoundingBox);
3945  }
3946 }
3947 
3948 
3949 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
3950 /* ++++++++++++++++++ Functions for Script interface +++++++++++++++++++++++ */
3951 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
3952 
3953 bool C4Landscape::DrawMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky)
3954 {
3955  // safety
3956  if (!szMapDef) return false;
3957  // clip to landscape size
3958  if (!ClipRect(iX, iY, iWdt, iHgt)) return false;
3959  // get needed map size
3960  int32_t iMapWdt = (iWdt - 1) / p->MapZoom + 1;
3961  int32_t iMapHgt = (iHgt - 1) / p->MapZoom + 1;
3962  C4SLandscape FakeLS = Game.C4S.Landscape;
3963  FakeLS.MapWdt.Set(iMapWdt, 0, iMapWdt, iMapWdt);
3964  FakeLS.MapHgt.Set(iMapHgt, 0, iMapHgt, iMapHgt);
3965  // create map creator
3966  C4MapCreatorS2 MapCreator(&FakeLS, &::TextureMap, &::MaterialMap, Game.StartupPlayerCount);
3967  // read file
3968  MapCreator.ReadScript(szMapDef);
3969  // render map
3970  CSurface8* sfcMap = nullptr;
3971  CSurface8* sfcMapBkg = nullptr;
3972  if (!MapCreator.Render(nullptr, sfcMap, sfcMapBkg))
3973  return false;
3974  // map it to the landscape
3975  bool fSuccess = p->MapToLandscape(this, *sfcMap, *sfcMapBkg, 0, 0, iMapWdt, iMapHgt, iX, iY, ignoreSky);
3976  // cleanup
3977  delete sfcMap;
3978  delete sfcMapBkg;
3979  // return whether successful
3980  return fSuccess;
3981 }
3982 
3983 bool C4Landscape::DrawDefMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky)
3984 {
3985  // safety
3986  if (!szMapDef || !p->pMapCreator) return false;
3987  // clip to landscape size
3988  if (!ClipRect(iX, iY, iWdt, iHgt)) return false;
3989  // get needed map size
3990  int32_t iMapWdt = (iWdt - 1) / p->MapZoom + 1;
3991  int32_t iMapHgt = (iHgt - 1) / p->MapZoom + 1;
3992  bool fSuccess = false;
3993  // render map
3994  C4MCMap *pMap = p->pMapCreator->GetMap(szMapDef);
3995  if (!pMap) return false;
3996  pMap->SetSize(iMapWdt, iMapHgt);
3997  CSurface8* sfcMap = nullptr;
3998  CSurface8* sfcMapBkg = nullptr;
3999  if (p->pMapCreator->Render(szMapDef, sfcMap, sfcMapBkg))
4000  {
4001  // map to landscape
4002  fSuccess = p->MapToLandscape(this, *sfcMap, *sfcMapBkg, 0, 0, iMapWdt, iMapHgt, iX, iY, ignoreSky);
4003  // cleanup
4004  delete sfcMap;
4005  delete sfcMapBkg;
4006  }
4007  // done
4008  return fSuccess;
4009 }
4010 
4011 // creates and draws a map section using MapCreatorS2 and a map from the loaded Landscape.txt
4012 
4013 bool C4Landscape::SetModulation(DWORD dwWithClr) // adjust the way the landscape is blitted
4014 {
4015  p->Modulation = dwWithClr;
4016  return true;
4017 }
4018 
4019 DWORD C4Landscape::GetModulation() const { return p->Modulation; }
4020 
4021 bool C4Landscape::ClipRect(int32_t &rX, int32_t &rY, int32_t &rWdt, int32_t &rHgt) const
4022 {
4023  // clip by bounds
4024  if (rX < 0) { rWdt += rX; rX = 0; }
4025  if (rY < 0) { rHgt += rY; rY = 0; }
4026  int32_t iOver;
4027 
4028  iOver = rX + rWdt - GetWidth();
4029  if (iOver > 0)
4030  rWdt -= iOver;
4031 
4032  iOver = rY + rHgt - GetHeight();
4033  if (iOver > 0)
4034  rHgt -= iOver;
4035 
4036  // anything left inside the bounds?
4037  return rWdt > 0 && rHgt > 0;
4038 }
4039 
4040 bool C4Landscape::ReplaceMapColor(BYTE iOldIndex, BYTE iNewIndex)
4041 {
4042  // find every occurance of iOldIndex in map; replace it by new index
4043  if (!p->Map) return false;
4044  int iPitch, iMapWdt, iMapHgt;
4045  BYTE *pMap = p->Map->Bits;
4046  iMapWdt = p->Map->Wdt;
4047  iMapHgt = p->Map->Hgt;
4048  iPitch = p->Map->Pitch;
4049  if (!pMap) return false;
4050  for (int32_t y = 0; y < iMapHgt; ++y)
4051  {
4052  for (int32_t x = 0; x < iMapWdt; ++x)
4053  {
4054  if (*pMap == iOldIndex)
4055  *pMap = iNewIndex;
4056  ++pMap;
4057  }
4058  pMap += iPitch - iMapWdt;
4059  }
4060  return true;
4061 }
4062 
4063 bool C4Landscape::SetTextureIndex(const char *szMatTex, BYTE iNewIndex, bool fInsert)
4064 {
4065  if (((!szMatTex || !*szMatTex) && !fInsert) || !Inside<int>(iNewIndex, 1, C4M_MaxTexIndex - 1))
4066  {
4067  DebugLogF("Cannot insert new texture %s to index %d: Invalid parameters.", (const char *)szMatTex, (int)iNewIndex);
4068  return false;
4069  }
4070  // get last mat index - returns zero for not found (valid for insertion mode)
4071  StdStrBuf Material, Texture;
4072  Material.CopyUntil(szMatTex, '-'); Texture.Copy(SSearch(szMatTex, "-"));
4073  BYTE iOldIndex = (szMatTex && *szMatTex) ? ::TextureMap.GetIndex(Material.getData(), Texture.getData(), false) : 0;
4074  // insertion mode?
4075  if (fInsert)
4076  {
4077  // there must be room to move up to
4078  BYTE byLastMoveIndex = C4M_MaxTexIndex - 1;
4079  while (::TextureMap.GetEntry(byLastMoveIndex))
4080  if (--byLastMoveIndex == iNewIndex)
4081  {
4082  DebugLogF("Cannot insert new texture %s to index %d: No room for insertion.", (const char *)szMatTex, (int)iNewIndex);
4083  return false;
4084  }
4085  // then move up all other textures first
4086  // could do this in one loop, but it's just a developement call anyway, so move one index at a time
4087  while (--byLastMoveIndex >= iNewIndex)
4088  if (::TextureMap.GetEntry(byLastMoveIndex))
4089  {
4090  ReplaceMapColor(byLastMoveIndex, byLastMoveIndex + 1);
4091  ::TextureMap.MoveIndex(byLastMoveIndex, byLastMoveIndex + 1);
4092  }
4093  // new insertion desired?
4094  if (szMatTex && *szMatTex)
4095  {
4096  // move from old or create new
4097  if (iOldIndex)
4098  {
4099  ReplaceMapColor(iOldIndex, iNewIndex);
4100  ::TextureMap.MoveIndex(iOldIndex, iNewIndex);
4101  }
4102  else
4103  {
4104  StdStrBuf Material, Texture;
4105  Material.CopyUntil(szMatTex, '-'); Texture.Copy(SSearch(szMatTex, "-"));
4106  // new insertion
4107  if (!::TextureMap.AddEntry(iNewIndex, Material.getData(), Texture.getData()))
4108  {
4109  LogF("Cannot insert new texture %s to index %d: Texture map entry error", (const char *)szMatTex, (int)iNewIndex);
4110  return false;
4111  }
4112  }
4113  }
4114  // done, success
4115  return true;
4116  }
4117  else
4118  {
4119  // new index must not be occupied
4120  const C4TexMapEntry *pOld;
4121  if ((pOld = ::TextureMap.GetEntry(iNewIndex)) && !pOld->isNull())
4122  {
4123  DebugLogF("Cannot move texture %s to index %d: Index occupied by %s-%s.", (const char *)szMatTex, (int)iNewIndex, pOld->GetMaterialName(), pOld->GetTextureName());
4124  return false;
4125  }
4126  // must only move existing textures
4127  if (!iOldIndex)
4128  {
4129  DebugLogF("Cannot move texture %s to index %d: Texture not found.", (const char *)szMatTex, (int)iNewIndex);
4130  return false;
4131  }
4132  // update map
4133  ReplaceMapColor(iOldIndex, iNewIndex);
4134  // change to new index in texmap
4135  ::TextureMap.MoveIndex(iOldIndex, iNewIndex);
4136  // done, success
4137  return true;
4138  }
4139 }
4140 
4141 // change color index of map texture, or insert a new one
4142 
4143 void C4Landscape::SetMapChanged() { p->fMapChanged = true; }
4144 
4146 {
4147  // check usage in landscape
4148  bool fTexUsage[C4M_MaxTexIndex];
4149  int32_t iMatTex;
4150  for (iMatTex = 0; iMatTex < C4M_MaxTexIndex; ++iMatTex)
4151  fTexUsage[iMatTex] = false;
4152  for (int32_t y = 0; y < GetHeight(); ++y)
4153  for (int32_t x = 0; x < GetWidth(); ++x)
4154  {
4155  const BYTE pix = p->Surface8->GetPix(x, y);
4156  const BYTE backPix = p->Surface8Bkg->GetPix(x, y);
4157  assert(pix < C4M_MaxTexIndex);
4158  assert(backPix < C4M_MaxTexIndex);
4159 
4160  fTexUsage[pix] = true;
4161  fTexUsage[backPix] = true;
4162  }
4163 
4164  // check usage by materials
4165  for (int32_t iMat = 0; iMat < ::MaterialMap.Num; ++iMat)
4166  {
4167  C4Material *pMat = ::MaterialMap.Map + iMat;
4168  if (pMat->BlastShiftTo >= 0) fTexUsage[pMat->BlastShiftTo] = true;
4169  if (pMat->BelowTempConvertTo >= 0) fTexUsage[pMat->BelowTempConvertTo] = true;
4170  if (pMat->AboveTempConvertTo >= 0) fTexUsage[pMat->AboveTempConvertTo] = true;
4171  if (pMat->DefaultMatTex >= 0) fTexUsage[pMat->DefaultMatTex] = true;
4172  }
4173  // remove unused
4174  for (iMatTex = 1; iMatTex < C4M_MaxTexIndex; ++iMatTex)
4175  if (!fTexUsage[iMatTex])
4176  ::TextureMap.RemoveEntry(iMatTex);
4177  // flag rewrite
4178  ::TextureMap.fEntriesAdded = true;
4179 }
4180 
4182 {
4183  return p->Sky;
4184 }
4185 
4187 {
4188  return p->pFoW != nullptr;
4189 }
4190 
4192 {
4193  return p->pFoW.get();
4194 }
4195 
4196 int32_t C4Landscape::GetMatCount(int material) const
4197 {
4198  assert(material >= 0 && (unsigned) material < p->MatCount.size());
4199  return p->MatCount[material];
4200 }
4201 
4202 int32_t C4Landscape::GetEffectiveMatCount(int material) const
4203 {
4204  assert(material >= 0 && (unsigned) material < p->EffectiveMatCount.size());
4205  return p->EffectiveMatCount[material];
4206 }
4207 
4208 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
4209 /* +++++++++++++++++++++++++++ Update functions ++++++++++++++++++++++++++++ */
4210 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
4211 
4213 {
4214  // Pixel maps must be update
4215  UpdatePixMaps();
4216  // Update landscape palette
4217  p->Mat2Pal();
4218 }
4219 
4221 {
4222  int32_t i;
4223  for (i = 0; i < C4M_MaxTexIndex; i++) p->Pix2Mat[i] = PixCol2Mat(i);
4224  for (i = 0; i < C4M_MaxTexIndex; i++) p->Pix2Dens[i] = MatDensity(p->Pix2Mat[i]);
4225  for (i = 0; i < C4M_MaxTexIndex; i++) p->Pix2Place[i] = MatValid(p->Pix2Mat[i]) ? ::MaterialMap.Map[p->Pix2Mat[i]].Placement : 0;
4226  for (i = 0; i < C4M_MaxTexIndex; i++) p->Pix2Light[i] = MatValid(p->Pix2Mat[i]) && (::MaterialMap.Map[p->Pix2Mat[i]].Light>0);
4227  p->Pix2Place[0] = 0;
4228  // clear bridge mat conversion buffers
4229  std::fill(p->BridgeMatConversion.begin(), p->BridgeMatConversion.end(), nullptr);
4230 }
4231 
4233 {
4234  if (!Surface8 || !Surface8Bkg) return false;
4235  // set landscape pal
4236  int32_t tex;
4237  for (tex = 0; tex < C4M_MaxTexIndex; tex++)
4238  {
4239  const C4TexMapEntry *pTex = ::TextureMap.GetEntry(tex);
4240  if (!pTex || pTex->isNull())
4241  continue;
4242  // colors
4243  DWORD dwPix = pTex->GetPattern().PatternClr(0, 0);
4244  Surface8->pPal->Colors[MatTex2PixCol(tex)] = dwPix;
4245  Surface8Bkg->pPal->Colors[MatTex2PixCol(tex)] = dwPix;
4246  }
4247  // success
4248  return true;
4249 }
4250 
4251 
4252 void C4Landscape::P::UpdatePixCnt(const C4Landscape *d, const C4Rect &Rect, bool fCheck)
4253 {
4254  int32_t PixCntWidth = (Width + 16) / 17;
4255  for (int32_t y = std::max<int32_t>(0, Rect.y / 15); y < std::min<int32_t>(PixCntPitch, (Rect.y + Rect.Hgt + 14) / 15); y++)
4256  for (int32_t x = std::max<int32_t>(0, Rect.x / 17); x < std::min<int32_t>(PixCntWidth, (Rect.x + Rect.Wdt + 16) / 17); x++)
4257  {
4258  int iCnt = 0;
4259  for (int32_t x2 = x * 17; x2 < std::min<int32_t>(x * 17 + 17, Width); x2++)
4260  for (int32_t y2 = y * 15; y2 < std::min<int32_t>(y * 15 + 15, Height); y2++)
4261  if (d->_GetDensity(x2, y2))
4262  iCnt++;
4263  if (fCheck)
4264  assert(iCnt == PixCnt[x * PixCntPitch + y]);
4265  PixCnt[x * PixCntPitch + y] = iCnt;
4266  }
4267 }
4268 
4269 void C4Landscape::P::UpdateMatCnt(const C4Landscape *d, C4Rect Rect, bool fPlus)
4270 {
4271  Rect.Intersect(C4Rect(0, 0, Width, Height));
4272  if (!Rect.Hgt || !Rect.Wdt) return;
4273  // Multiplicator for changes
4274  const int32_t iMul = fPlus ? +1 : -1;
4275  // Count pixels
4276  for (int32_t x = 0; x < Rect.Wdt; x++)
4277  {
4278  int iHgt = 0;
4279  int32_t y;
4280  for (y = 1; y < Rect.Hgt; y++)
4281  {
4282  int32_t iMat = d->_GetMat(Rect.x + x, Rect.y + y - 1);
4283  // Same material? Count it.
4284  if (iMat == d->_GetMat(Rect.x + x, Rect.y + y))
4285  iHgt++;
4286  else
4287  {
4288  if (iMat >= 0)
4289  {
4290  // Normal material counting
4291  MatCount[iMat] += iMul * (iHgt + 1);
4292  // Effective material counting enabled?
4293  if (int32_t iMinHgt = ::MaterialMap.Map[iMat].MinHeightCount)
4294  {
4295  // First chunk? Add any material above when checking chunk height
4296  int iAddedHeight = 0;
4297  if (Rect.y && iHgt + 1 == y)
4298  iAddedHeight = d->GetMatHeight(Rect.x + x, Rect.y - 1, -1, iMat, iMinHgt);
4299  // Check the chunk height
4300  if (iHgt + 1 + iAddedHeight >= iMinHgt)
4301  {
4302  EffectiveMatCount[iMat] += iMul * (iHgt + 1);
4303  if (iAddedHeight < iMinHgt)
4304  EffectiveMatCount[iMat] += iMul * iAddedHeight;
4305  }
4306  }
4307  }
4308  // Next chunk of material
4309  iHgt = 0;
4310  }
4311  }
4312  // Check last pixel
4313  int32_t iMat = d->_GetMat(Rect.x + x, Rect.y + Rect.Hgt - 1);
4314  if (iMat >= 0)
4315  {
4316  // Normal material counting
4317  MatCount[iMat] += iMul * (iHgt + 1);
4318  // Minimum height counting?
4319  if (int32_t iMinHgt = ::MaterialMap.Map[iMat].MinHeightCount)
4320  {
4321  int iAddedHeight1 = 0, iAddedHeight2 = 0;
4322  // Add any material above for chunk size check
4323  if (Rect.y && iHgt + 1 == Rect.Hgt)
4324  iAddedHeight1 = d->GetMatHeight(Rect.x + x, Rect.y - 1, -1, iMat, iMinHgt);
4325  // Add any material below for chunk size check
4326  if (Rect.y + y < Height)
4327  iAddedHeight2 = d->GetMatHeight(Rect.x + x, Rect.y + Rect.Hgt, 1, iMat, iMinHgt);
4328  // Chunk tall enough?
4329  if (iHgt + 1 + iAddedHeight1 + iAddedHeight2 >= ::MaterialMap.Map[iMat].MinHeightCount)
4330  {
4331  EffectiveMatCount[iMat] += iMul * (iHgt + 1);
4332  if (iAddedHeight1 < iMinHgt)
4333  EffectiveMatCount[iMat] += iMul * iAddedHeight1;
4334  if (iAddedHeight2 < iMinHgt)
4335  EffectiveMatCount[iMat] += iMul * iAddedHeight2;
4336  }
4337  }
4338  }
4339  }
4340 }
4341 
4342 
4343 
uint8_t * GetBridgeMatConversion(const C4Landscape *d, int32_t for_material_col) const
#define C4CFN_DiffLandscapeBkg
Definition: C4Components.h:64
void SetInitProgress(float fToProgress)
Definition: C4Game.cpp:3386
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
C4Object * getObj() const
Definition: C4Value.cpp:68
bool HasMap() const
bool FindMatPathPush(int32_t &fx, int32_t &fy, int32_t mdens, int32_t mslide, bool liquid) const
C4MaterialReaction * GetReactionUnsafe(int32_t iPXSMat, int32_t iLandscapeMat)
Definition: C4Material.h:191
std::array< DWORD, C4MaxMaterial > MatCount
Definition: C4Landscape.cpp:71
class C4TextureShape * GetMaterialShape() const
Definition: C4Texture.h:40
int32_t BlastShiftTo
Definition: C4Material.h:151
constexpr bool DEBUGREC_MATSCAN
Definition: C4Include.h:33
int32_t Max
Definition: C4Scenario.h:31
bool DrawBox(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
int32_t GetMatHeight(int32_t x, int32_t y, int32_t iYDir, int32_t iMat, int32_t iMax) const
bool FindLiquidHeight(int32_t cx, int32_t &ry, int32_t hgt)
int32_t DoScan(C4Landscape *, int32_t x, int32_t y, int32_t mat, int32_t dir)
void Blit8Fast(CSurface8 *sfcSource, int fx, int fy, C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt)
Definition: C4Draw.cpp:251
static int32_t FastSolidCheckNextX(int32_t x)
int32_t DefaultMatTex
Definition: C4Material.h:155
BYTE GetMapIndex(int32_t iX, int32_t iY) const
int32_t RandomSeed
Definition: C4Game.h:135
int32_t GetX() const
Definition: C4Object.h:287
C4Config Config
Definition: C4Config.cpp:833
const int32_t C4M_Vehicle
Definition: C4Constants.h:171
bool CreateMapS2(C4Group &ScenFile, CSurface8 *&sfcMap, CSurface8 *&sfcMapBkg)
void Cast(int32_t mat, int32_t num, int32_t tx, int32_t ty, int32_t level)
Definition: C4PXS.cpp:309
bool Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bool &rfLoaded, bool fSavegame)
BYTE GetBackMapIndex(int32_t iX, int32_t iY) const
float Y
Definition: C4Facet.h:118
void Synchronize()
bool _FastSolidCheck(int32_t x, int32_t y) const
int32_t _GetBackPlacement(int32_t x, int32_t y) const
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
int32_t GetMiddleY() const
Definition: C4Rect.h:57
bool SaveDiff(C4Group &hGroup, bool fSyncSave) const
static bool CheckConsistency()
uint32_t Random()
Definition: C4Random.cpp:43
int32_t iTick5
Definition: C4Game.h:130
std::unique_ptr< CSurface8 > Surface8
Definition: C4Landscape.cpp:54
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
bool Incinerate(int32_t x, int32_t y, int32_t cause_player)
int Hgt
Definition: CSurface8.h:28
bool SemiAboveSolid(int32_t &rx, int32_t &ry)
bool GetMapColorIndex(const char *szMaterial, const char *szTexture, BYTE &rbyCol) const
int32_t Pix2Dens[C4M_MaxTexIndex]
Definition: C4Landscape.cpp:61
int32_t MaxSlide
Definition: C4Material.h:104
int32_t GetDensity(int32_t x, int32_t y) const
bool ApplyDiff(C4Group &hGroup)
C4Game Game
Definition: C4Globals.cpp:52
void Draw(const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, uint8_t iTexture, int32_t iOffX, int32_t iOffY, int32_t MapZoom, int32_t min_overlap_ratio)
int32_t GetBottom() const
Definition: C4Rect.h:58
bool LandscapePushPull
Definition: C4Scenario.h:110
int32_t iTick35
Definition: C4Game.h:130
bool BlastFreePix(C4Landscape *d, int32_t tx, int32_t ty)
C4Material * GetMaterial() const
Definition: C4Texture.h:63
int32_t Pix2Place[C4M_MaxTexIndex]
Definition: C4Landscape.cpp:61
bool AccessEntry(const char *szWildCard, size_t *iSize=nullptr, char *sFileName=nullptr, bool NeedsToBeAGroup=false)
Definition: C4Group.cpp:1695
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4Scenario C4S
Definition: C4Game.h:74
static void RemoveSolidMasks()
int32_t DigFree(int32_t tx, int32_t ty, int32_t rad, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
C4ConfigGeneral General
Definition: C4Config.h:251
const int32_t C4LS_MaxRelights
Definition: C4Landscape.h:27
#define C4CFN_DynLandscape
Definition: C4Components.h:115
void HandleTexMapUpdate()
#define C4CFN_Material
Definition: C4Components.h:25
int32_t GetPixDensity(BYTE byPix) const
void ExecuteScan(C4Landscape *)
void DigMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, C4Object *pCollect=nullptr)
void BlastMaterial2Objects(int32_t tx, int32_t ty, C4MaterialList *mat_list, int32_t caused_by, int32_t str, C4ValueArray *out_objects)
bool InsertDeadMaterial(int32_t mat, int32_t tx, int32_t ty)
bool SetTextureIndex(const char *szMatTex, BYTE iNewIndex, bool fInsert)
bool AddEntry(BYTE byIndex, const char *szMaterial, const char *szTexture)
Definition: C4Texture.cpp:103
int32_t Instable
Definition: C4Material.h:102
bool MatVehicle(int32_t iMat)
Definition: C4Material.h:215
bool MapToSurface(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY)
BYTE _GetBackPix(int32_t x, int32_t y) const
void GetMapSize(int32_t &rWdt, int32_t &rHgt, int32_t iPlayerNum)
Definition: C4Scenario.cpp:319
#define C4CFN_MapFg
Definition: C4Components.h:58
#define C4CFN_Map
Definition: C4Components.h:57
BYTE GetBackPix(int32_t x, int32_t y) const
const char * SSearch(const char *szString, const char *szIndex)
Definition: Standard.cpp:363
std::vector< uint8_t > RightColPix
Definition: C4Landscape.cpp:60
CStdPalette * GetPal() const
C4MaterialList * MaterialContents
Definition: C4Object.h:154
C4SLandscape Landscape
Definition: C4Scenario.h:236
void Execute()
void PutTemporary(C4Rect where)
C4Material * Map
Definition: C4Material.h:169
bool DrawLine(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
bool PostInitMap()
LandscapeMode mode
Definition: C4Landscape.cpp:68
bool HasFoW() const
void Set(int32_t std=0, int32_t rnd=0, int32_t min=0, int32_t max=100)
Definition: C4Scenario.cpp:36
C4MaterialCoreShape
Definition: C4Material.h:71
void _SetPix2Tmp(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
bool CheckInstability(int32_t tx, int32_t ty, int32_t recursion_count=0)
const uint32_t OCF_InSolid
Definition: C4Constants.h:99
std::unique_ptr< C4FoW > pFoW
Definition: C4Landscape.cpp:84
void ClearPointers(C4Object *pObj)
#define GravAccel
Definition: C4Physics.h:27
void RaiseTerrain(int32_t tx, int32_t ty, int32_t wdt)
std::vector< uint8_t > PixCnt
Definition: C4Landscape.cpp:64
C4SVal Gravity
Definition: C4Scenario.h:176
int32_t DigFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
Definition: C4Rect.h:27
int32_t max_shape_height
Definition: C4Material.h:171
class C4FoW * GetFoW()
bool InsertMaterialOutsideLandscape(int32_t tx, int32_t ty, int32_t mdens)
C4Real C4REAL100(int x)
Definition: C4Real.h:267
uint8_t BYTE
bool SetModulation(DWORD dwWithClr)
bool DrawLineMap(int32_t iX, int32_t iY, int32_t iRadius, uint8_t line_color, uint8_t line_color_bkg)
int32_t PixCol2Mat(BYTE pixc)
C4SVal MapWdt
Definition: C4Scenario.h:178
C4TextureMap TextureMap
Definition: C4Texture.cpp:576
void BlastFreeShape(int *vtcs, int length, C4Object *by_object=nullptr, int32_t by_player=NO_OWNER, int32_t iMaxDensity=C4M_Vehicle)
bool InitializeMap(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, uint32_t iPlayerCount, std::unique_ptr< CSurface8 > *pmap_fg_surface, std::unique_ptr< CSurface8 > *pmap_bg_surface)
int32_t BlastFree
Definition: C4Material.h:95
int32_t DigFreeShape(int *vtcs, int length, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
bool FindTunnel(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
int Sign(T val)
Definition: Standard.h:45
int32_t MinShapeOverlap
Definition: C4Material.h:137
bool InsertMaterial(int32_t mat, int32_t *tx, int32_t *ty, int32_t vx=0, int32_t vy=0, bool query_only=false)
#define _MAX_PATH
std::unique_ptr< CSurface8 > CreateDefaultBkgSurface(CSurface8 &sfcFg, bool msbAsIft) const
bool Create(int32_t mat, C4Real ix, C4Real iy, C4Real ixdir=Fix0, C4Real iydir=Fix0)
Definition: C4PXS.cpp:175
int32_t _GetBackMat(int32_t x, int32_t y) const
C4ValueArray * PrepareFreeShape(C4Rect &BoundingBox, C4Object *by_object)
void GameMsgObjectError(const char *szText, C4Object *pTarget, bool Red)
void UpdateMatCnt(const C4Landscape *, C4Rect Rect, bool fPlus)
Definition: C4Texture.h:48
void ScenarioInit()
void CastObjects(C4ID id, C4Object *pCreator, int32_t num, int32_t level, int32_t tx, int32_t ty, int32_t iOwner=NO_OWNER, int32_t iController=NO_OWNER, C4ValueArray *out_objects=nullptr)
Definition: C4Game.cpp:1411
C4GraphicsResource GraphicsResource
#define C4CFN_DiffLandscape
Definition: C4Components.h:63
uint32_t Modulation
Definition: C4Landscape.cpp:77
#define C4CFN_Landscape
Definition: C4Components.h:60
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
StdCastAdapt< T, int32_t > mkCastIntAdapt(T &rValue)
Definition: StdAdaptors.h:273
C4ObjectPtr Layer
Definition: C4Object.h:136
std::unique_ptr< CSurface8 > Map
Definition: C4Landscape.cpp:56
int32_t TopOpen
Definition: C4Scenario.h:170
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:88
bool ClipRect(int32_t &rX, int32_t &rY, int32_t &rWdt, int32_t &rHgt) const
void Intersect(const C4Rect &r2)
Definition: C4Rect.cpp:100
Definition: C4Sky.h:28
bool SaveMap(C4Group &hGroup, const char *szEntryName)
Definition: C4Texture.cpp:309
const C4TexMapEntry * GetEntry(int32_t iIndex) const
Definition: C4Texture.h:85
int32_t Dig2ObjectCollect
Definition: C4Material.h:98
bool ExactLandscape
Definition: C4Scenario.h:165
bool DigFreePixNoInstability(C4Landscape *d, int32_t tx, int32_t ty)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool DrawBrush(int32_t iX, int32_t iY, int32_t iGrade, const char *szMaterial, const char *szTexture, const char *szBackMaterial, const char *szBackTexture)
const char * GetName() const override
Definition: C4PropList.cpp:267
int32_t GetIndex(const char *szMaterial, const char *szTexture, bool fAddIfNotExist=true, const char *szErrorIfFailed=nullptr)
Definition: C4Texture.cpp:414
Definition: C4Real.h:58
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
std::unique_ptr< BYTE[]> pInitialBkg
Definition: C4Landscape.cpp:83
C4MaterialReactionFunc pFunc
Definition: C4Material.h:47
bool TexOZoom(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, DWORD *dwpTextureUsage, int32_t iToX=0, int32_t iToY=0)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
bool Save(C4Group &hGroup) const
int32_t Distance(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2)
Definition: Standard.cpp:25
bool Load(C4Group &hGroup, bool fLoadSky, bool fSavegame)
int32_t GetPlacement(int32_t x, int32_t y) const
int32_t AutoScanSideOpen
Definition: C4Scenario.h:172
int32_t Wdt
Definition: C4Rect.h:30
void PrepareChange(const C4Landscape *d, const C4Rect &BoundingBox)
struct CPolyEdge * prev
void RemoveEntry(int32_t iIndex)
Definition: C4Texture.cpp:525
void FixRandom(uint64_t iSeed)
Definition: C4Game.cpp:3093
C4ValueArray * FindMany(const C4ObjectList &Objs)
bool Create(int32_t x, int32_t y, bool fExecute=false)
Definition: C4MassMover.cpp:70
C4MapScriptHost MapScript
void RemoveTemporary(C4Rect where)
bool ReadScript(const char *szScript)
int32_t MatDensity(int32_t mat)
Definition: C4Material.h:240
const int32_t MNone
Definition: C4Constants.h:177
CPolyEdge QuickPolyBuf[QuickPolyBufSize]
C4Shape Shape
Definition: C4Def.h:104
C4SVal MapZoom
Definition: C4Scenario.h:178
int32_t GetMaxPolyHeight() const
struct CPolyEdge * next
bool FindClosestFree(int32_t &rX, int32_t &rY, int32_t iAngle1, int32_t iAngle2, int32_t iExcludeAngle1, int32_t iExcludeAngle2)
C4Real C4REAL10(int x)
Definition: C4Real.h:269
bool FindConSiteSpot(int32_t &rx, int32_t &ry, int32_t wdt, int32_t hgt, int32_t hrange)
int32_t y
Definition: C4Rect.h:30
const char * GetTextureName() const
Definition: C4Texture.h:61
const char * GetMaterialName() const
Definition: C4Texture.h:60
int32_t Light
Definition: C4Material.h:113
int32_t AboveTempConvert
Definition: C4Material.h:126
void Create(CSurface8 *sfcMap, C4SLandscape &rLScape, C4TextureMap &rTexMap, int32_t iPlayerNum=1)
Definition: C4Map.cpp:78
int iCnt
Definition: TstC4NetIO.cpp:32
int32_t PixCntPitch
Definition: C4Landscape.cpp:63
bool MapToLandscape()
void ShakeFree(int32_t tx, int32_t ty, int32_t rad)
const C4Real DefaultGravAccel
Definition: C4Movement.cpp:40
C4Landscape Landscape
bool CreateMap(CSurface8 *&sfcMap, CSurface8 *&sfcMapBkg)
void CheckInstabilityRange(int32_t tx, int32_t ty)
int32_t MVehic
Definition: C4Material.cpp:36
const int C4M_MaxTexIndex
Definition: C4Constants.h:51
BYTE MCVehic
Definition: C4Material.cpp:37
bool Move(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1325
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
int32_t BottomOpen
Definition: C4Scenario.h:170
#define C4CFN_TexMap
Definition: C4Components.h:80
int32_t AboveTempConvertDir
Definition: C4Material.h:127
int32_t Constructable
Definition: C4Def.h:118
void SetMode(LandscapeMode iMode)
static const C4ID None
Definition: C4Id.h:39
#define C4CFN_TempLandscapeBkg
Definition: C4Components.h:157
int32_t Blast2PXSRatio
Definition: C4Material.h:101
void RemoveUnusedTexMapEntries()
DWORD PatternClr(unsigned int iX, unsigned int iY) const
Definition: C4Draw.cpp:159
bool PathFreeIgnoreVehicle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t *ix, int32_t *iy)
#define C4CFN_LandscapeFg
Definition: C4Components.h:61
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
void MoveIndex(BYTE byOldIndex, BYTE byNewIndex)
Definition: C4Texture.cpp:403
int32_t Blast2ObjectRatio
Definition: C4Material.h:100
bool DrawDefMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky=false)
std::vector< uint8_t > TopRowPix
Definition: C4Landscape.cpp:60
int32_t LeftOpen
Definition: C4Scenario.h:171
int32_t GetTemperature()
Definition: C4Weather.cpp:100
class C4Sky & GetSky()
bool DrawChunks(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, int32_t icntx, int32_t icnty, const char *szMaterial, const char *szTexture, bool bIFT)
void Add(const C4Rect &r2)
Definition: C4Rect.cpp:144
C4SRealism Realism
Definition: C4Scenario.h:127
std::vector< int32_t > Order
Definition: C4Texture.h:77
void AssignRemoval(bool fExitContents=false)
Definition: C4Object.cpp:255
int32_t AboveTempConvertTo
Definition: C4Material.h:154
void AddDbgRec(C4RecordChunkType eType, const void *pData, int iSize)
Definition: C4Record.cpp:32
void UpdatePixMaps()
Definition: C4Def.h:98
const int NO_OWNER
Definition: C4Constants.h:137
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
bool SaveDiffInternal(const C4Landscape *d, C4Group &hGroup, bool fSyncSave) const
bool MapToLandscape(C4Landscape *d, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iOffsX=0, int32_t iOffsY=0, bool noClear=false)
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4Object * FindConstuctionSiteBlock(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
Definition: C4Game.cpp:1142
BYTE DefaultBkgMat(BYTE fg) const
#define BREAKPOINT_HERE
#define C4CFN_TempMapFg
Definition: C4Components.h:154
bool DrawPolygon(int *vtcs, int length, const char *szMaterial, const char *szBackMaterial, bool fDrawBridge)
int32_t GetMaxPolyWidth() const
C4Weather Weather
Definition: C4Weather.cpp:206
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:237
C4PXSSystem PXS
Definition: C4PXS.cpp:423
BYTE DefaultBkgMatTex(BYTE fg) const
Definition: C4Texture.cpp:504
std::array< DWORD, C4MaxMaterial > EffectiveMatCount
Definition: C4Landscape.cpp:72
int32_t GetMat(int32_t x, int32_t y) const
int32_t Num
Definition: C4Material.h:168
int32_t Status
Definition: C4PropList.h:168
bool DrawLineLandscape(int32_t iX, int32_t iY, int32_t iGrade, uint8_t line_color, uint8_t line_color_bkg)
int32_t MinHeightCount
Definition: C4Material.h:130
int32_t _GetPlacement(int32_t x, int32_t y) const
int32_t ForPolygon(C4Landscape *d, int *vtcs, int length, const std::function< bool(int32_t, int32_t)> &callback, C4MaterialList *mats_count=nullptr, uint8_t col=0, uint8_t colBkg=0, uint8_t *conversion_table=nullptr)
int32_t GetSize() const
Definition: C4ValueArray.h:36
bool FindMatPath(int32_t &fx, int32_t &fy, int32_t ydir, int32_t mdens, int32_t mslide) const
int32_t GetY() const
Definition: C4Object.h:288
bool Collect(C4Object *pObj)
Definition: C4Object.cpp:4174
int32_t GetBackMat(int32_t x, int32_t y) const
bool SetPix2(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
const int32_t C4M_Solid
Definition: C4Constants.h:172
BYTE GetPix(int iX, int iY) const
Definition: CSurface8.h:49
const char * getData() const
Definition: StdBuf.h:442
bool Render(const char *szMapName, CSurface8 *&sfcMap, CSurface8 *&sfcMapBkg)
std::unique_ptr< BYTE[]> pInitial
Definition: C4Landscape.cpp:82
C4ID Blast2Object
Definition: C4Material.h:99
void CopyUntil(const char *szString, char cUntil)
Definition: StdBuf.h:613
bool DrawMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky=false)
bool FindTunnelHeight(int32_t cx, int32_t &ry, int32_t hgt)
bool PathFree(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
bool FindSurfaceLiquid(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
int32_t Dig2ObjectRatio
Definition: C4Material.h:97
C4LSectors Sectors
Definition: C4GameObjects.h:42
void _SetPix(int iX, int iY, BYTE byCol)
Definition: CSurface8.h:44
int32_t Density
Definition: C4Material.h:92
virtual bool isDeserializer()
Definition: StdCompiler.h:53
int32_t x
Definition: C4Rect.h:30
int32_t GetBackDensity(int32_t x, int32_t y) const
#define C4CFN_MapBg
Definition: C4Components.h:59
void StoreMapPalette(CStdPalette *, C4MaterialMap &rMaterials)
Definition: C4Texture.cpp:537
void DrawChunk(C4Landscape *, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, C4MaterialCoreShape Shape, uint32_t cro)
bool FlatChunkShapes
Definition: C4Scenario.h:188
C4MassMoverSet MassMover
int32_t MapWidth
Definition: C4Landscape.cpp:70
int32_t ExtractMaterial(int32_t fx, int32_t fy, bool distant_first)
int32_t GetHeight() const
void DrawMaterialRect(int32_t mat, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
Definition: C4FoW.h:101
bool DrawQuad(int32_t iX1, int32_t iY1, int32_t iX2, int32_t iY2, int32_t iX3, int32_t iY3, int32_t iX4, int32_t iY4, const char *szMaterial, const char *szBackMaterial, bool fDrawBridge)
std::vector< uint8_t > LeftColPix
Definition: C4Landscape.cpp:60
bool FindLiquid(int32_t &rx, int32_t &ry, int32_t width, int32_t height)
bool LandscapeInsertThrust
Definition: C4Scenario.h:111
LandscapeMode
Definition: C4Landscape.h:29
float TargetX
Definition: C4Facet.h:165
int32_t StartupPlayerCount
Definition: C4Game.h:109
int32_t Get(const char *szMaterial)
Definition: C4Material.cpp:365
const char * GetTexture(int32_t iIndex)
Definition: C4Texture.cpp:494
bool Pix2Light[C4M_MaxTexIndex]
Definition: C4Landscape.cpp:62
void PostFreeShape(C4ValueArray *dig_objects, C4Object *by_object)
bool _PathFree(int32_t x, int32_t y, int32_t x2, int32_t y2) const
int32_t BelowTempConvertTo
Definition: C4Material.h:153
int32_t GetMapZoom() const
int32_t BelowTempConvert
Definition: C4Material.h:123
int Wdt
Definition: CSurface8.h:28
virtual const char * GetName() const
Definition: C4PropList.cpp:649
bool GBackSemiSolid(int32_t x, int32_t y)
Definition: C4Landscape.h:234
float Hgt
Definition: C4Facet.h:118
bool ShakeFreePix(C4Landscape *d, int32_t tx, int32_t ty)
bool SaveInitial()
static C4SolidMask * First
Definition: C4SolidMask.h:72
C4SVal MapHgt
Definition: C4Scenario.h:178
virtual C4Def const * GetDef() const
Definition: C4PropList.cpp:685
int32_t GetEffectiveMatCount(int material) const
bool SaveMap(C4Group &hGroup) const
C4MaterialCoreShape MapChunkType
Definition: C4Material.h:91
int32_t Placement
Definition: C4Material.h:112
std::unique_ptr< C4LandscapeRender > pLandscapeRender
Definition: C4Landscape.cpp:58
bool EraseItem(const char *szItemName)
Definition: StdFile.cpp:809
std::unique_ptr< C4MapCreatorS2 > pMapCreator
Definition: C4Landscape.cpp:80
int32_t RightOpen
Definition: C4Scenario.h:171
bool KeepSinglePixels
Definition: C4Material.h:132
void Draw(C4TargetFacet &cgo, class C4FoWRegion *pLight=nullptr)
int32_t Amount[C4MaxMaterial]
int32_t GetWidth() const
std::unique_ptr< CSurface8 > Surface8Bkg
Definition: C4Landscape.cpp:55
void ClearFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt)
C4MaterialMap MaterialMap
Definition: C4Material.cpp:974
C4SGame Game
Definition: C4Scenario.h:234
BYTE Mat2PixColDefault(int32_t mat)
Definition: C4Material.h:235
void Clear(bool fClearMapCreator=true, bool fClearSky=true, bool fClearRenderer=true)
int32_t GetMiddleX() const
Definition: C4Rect.h:56
int32_t GetPixMat(BYTE byPix) const
C4Real GetGravity() const
bool ConstructionCheck(C4PropList *PropList, int32_t iX, int32_t iY, C4Object *pByObj)
bool isNull() const
Definition: C4Texture.h:59
BYTE bgClr
Definition: C4Record.h:126
bool ClearPix(int32_t tx, int32_t ty)
void DeactivateBlitModulation()
Definition: C4Draw.h:189
void SetGravity(C4Real g)
int32_t max_shape_width
Definition: C4Material.h:171
const C4Pattern & GetPattern() const
Definition: C4Texture.h:64
int32_t _GetDensity(int32_t x, int32_t y) const
int32_t GetMatCount(int material) const
bool PathFreePix(int32_t x, int32_t y)
BYTE GetPix(int32_t x, int32_t y) const
BYTE _GetPix(int32_t x, int32_t y) const
void CompileFunc(StdCompiler *pComp)
int32_t DebugRec
Definition: C4Config.h:60
bool DoRelights()
int32_t _GetBackDensity(int32_t x, int32_t y) const
#define C4TLS_MatSky
Definition: C4ToolsDlg.h:39
BYTE _GetPix(int x, int y) const
Definition: CSurface8.h:54
std::array< C4Rect, C4LS_MaxRelights > Relights
Definition: C4Landscape.cpp:65
bool DigFreePix(C4Landscape *d, int32_t tx, int32_t ty)
C4ObjectPtr Contained
Definition: C4Object.h:144
int32_t TempConvStrength
Definition: C4Material.h:129
LandscapeMode GetMode() const
const int QuickPolyBufSize
const char * AtTempPath(const char *szFilename)
Definition: C4Config.cpp:540
void ActivateBlitModulation(DWORD dwWithClr)
Definition: C4Draw.h:188
int32_t Hgt
Definition: C4Rect.h:30
T Abs(T val)
Definition: Standard.h:42
int32_t _GetMat(int32_t x, int32_t y) const
C4Real Sin(const C4Real &fAngle)
Definition: C4Real.h:265
#define C4CFN_TempMapBg
Definition: C4Components.h:155
void MakeTempFilename(char *szFilename)
Definition: StdFile.cpp:323
bool ReplaceMapColor(BYTE iOldIndex, BYTE iNewIndex)
void ChunkOZoom(C4Landscape *, const CSurface8 &sfcMap, const CSurface8 &sfcMapBkg, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, uint8_t iTexture, int32_t iOffX=0, int32_t iOffY=0)
#define POLYGON_FIX_SHIFT
bool NoPrimaryClipper()
Definition: C4Draw.cpp:237
BYTE MatTex2PixCol(int32_t tex)
Definition: C4Material.h:230
C4Surface * Surface
Definition: C4Facet.h:117
int32_t AreaSolidCount(int32_t x, int32_t y, int32_t wdt, int32_t hgt) const
const int32_t C4M_Liquid
Definition: C4Constants.h:174
float TargetY
Definition: C4Facet.h:165
bool SaveTextures(C4Group &hGroup) const
C4SolidMask * Prev
Definition: C4SolidMask.h:74
bool KeepMapCreator
Definition: C4Scenario.h:185
std::array< std::unique_ptr< uint8_t[]>, C4M_MaxTexIndex > BridgeMatConversion
Definition: C4Landscape.cpp:66
bool FindSolidGround(int32_t &rx, int32_t &ry, int32_t width)
void DrawSmoothOChunk(C4Landscape *, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, uint8_t mcol, uint8_t mcolBkg, int flip, uint32_t cro)
bool FindMatSlide(int32_t &fx, int32_t &fy, int32_t ydir, int32_t mdens, int32_t mslide) const
uint32_t DWORD
bool FindThrowingPosition(int32_t iTx, int32_t iTy, C4Real fXDir, C4Real fYDir, int32_t iHeight, int32_t &rX, int32_t &rY)
int32_t GetBackPlacement(int32_t x, int32_t y) const
int32_t GetIndexMatTex(const char *szMaterialTexture, const char *szDefaultTexture=nullptr, bool fAddIfNotExist=true, const char *szErrorIfFailed=nullptr)
Definition: C4Texture.cpp:441
static const uint8_t Transparent
Definition: C4Landscape.h:50
int fixtoi(const C4Fixed &x)
Definition: C4Real.h:259
bool MatValid(int32_t mat)
Definition: C4Material.h:210
bool AboveSolid(int32_t &rx, int32_t &ry)
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:260
bool fEntriesAdded
Definition: C4Texture.h:83
int32_t ConSizeOff
Definition: C4Def.h:143
#define C4CFN_LandscapeBg
Definition: C4Components.h:62
bool IsOpen() const
Definition: C4Group.cpp:1891
int32_t Controller
Definition: C4Object.h:111
void Copy()
Definition: StdBuf.h:467
int32_t ScanSpeed
Definition: C4Landscape.cpp:75
float Wdt
Definition: C4Facet.h:118
bool GetLight(int32_t x, int32_t y)
void FinishChange(C4Landscape *d, C4Rect BoundingBox)
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
bool DebugLogF(const char *strMessage ...)
Definition: C4Log.cpp:288
static void PutSolidMasks()
bool AboveSemiSolid(int32_t &rx, int32_t &ry)
#define PSF_OnDugOut
Definition: C4GameScript.h:100
float X
Definition: C4Facet.h:118
bool _GetLight(int32_t x, int32_t y)
#define PSF_DigOutObject
Definition: C4GameScript.h:99
void GetSurfaceSize(int &irX, int &irY) const
Definition: CSurface8.cpp:195
int32_t Evaluate()
Definition: C4Scenario.cpp:50
void SetMapChanged()
static C4SolidMask * Last
Definition: C4SolidMask.h:73
int32_t DigFree
Definition: C4Material.h:94
std::vector< uint8_t > BottomRowPix
Definition: C4Landscape.cpp:60
bool DensitySolid(int32_t dens)
Definition: C4Landscape.h:196
C4Real Cos(const C4Real &fAngle)
Definition: C4Real.h:266
int32_t GBackMat(int32_t x, int32_t y)
Definition: C4Landscape.h:219
bool FindLevelGround(int32_t &rx, int32_t &ry, int32_t width, int32_t hrange)
std::unique_ptr< CSurface8 > MapBkg
Definition: C4Landscape.cpp:57
void SetSize(int32_t iWdt, int32_t iHgt)
void UpdatePixCnt(const C4Landscape *, const C4Rect &Rect, bool fCheck=false)
BYTE clr
Definition: C4Record.h:125
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
bool GBackLiquid(int32_t x, int32_t y)
Definition: C4Landscape.h:239
int32_t MapHeight
Definition: C4Landscape.cpp:70
int32_t Pix2Mat[C4M_MaxTexIndex]
Definition: C4Landscape.cpp:61
bool FoWEnabled
Definition: C4Scenario.h:125
C4Object * CreateObject(C4PropList *type, C4Object *pCreator, int32_t owner=NO_OWNER, int32_t x=50, int32_t y=50, int32_t r=0, bool grow_from_center=false, C4Real xdir=Fix0, C4Real ydir=Fix0, C4Real rdir=Fix0, int32_t iController=NO_OWNER)
Definition: C4Game.cpp:1072
int32_t PixCol2Tex(BYTE pixc)
Definition: C4Landscape.h:211
C4SolidMask * Next
Definition: C4SolidMask.h:75
void FindMatTop(int32_t mat, int32_t &x, int32_t &y, bool distant_first) const
int32_t GetMaterialIndex() const
Definition: C4Texture.h:62
int32_t BelowTempConvertDir
Definition: C4Material.h:124
uint32_t ChunkyRandom(uint32_t &iOffset, uint32_t iRange) const
C4GameObjects Objects
Definition: C4Globals.cpp:48
DWORD GetModulation() const
bool PathFreeIgnoreVehiclePix(int32_t x, int32_t y)
bool GBackSolid(int32_t x, int32_t y)
Definition: C4Landscape.h:229
void BlastFree(int32_t tx, int32_t ty, int32_t rad, int32_t caused_by=NO_OWNER, C4Object *by_object=nullptr, int32_t iMaxDensity=C4M_Vehicle)
void Add(int32_t iMaterial, int32_t iAmount)
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool _SetPix2(int32_t x, int32_t y, BYTE fgPix, BYTE bgPix)
#define C4CFN_TempLandscape
Definition: C4Components.h:156
int32_t Inflammable
Definition: C4Material.h:106
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:110
bool SaveInternal(const C4Landscape *d, C4Group &hGroup) const
int32_t TrajectoryDistance(int32_t iFx, int32_t iFy, C4Real iXDir, C4Real iYDir, int32_t iTx, int32_t iTy)