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