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