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