OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4MapScript.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) 2013-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 scripted map creation */
19 
20 #include "C4Include.h"
21 #include "landscape/C4MapScript.h"
22 #include "script/C4AulDefFunc.h"
23 #include "landscape/C4Landscape.h"
24 #include "landscape/C4Texture.h"
25 #include "lib/C4Random.h"
26 #include "game/C4GameScript.h"
27 
29 
30 static const char *DrawFn_Transparent_Name = "Transparent";
31 static const char *DrawFn_Sky_Name = "Sky";
32 static const char *DrawFn_Background_Name = "Background";
33 static const char *DrawFn_Liquid_Name = "Liquid";
34 static const char *DrawFn_Solid_Name = "Solid";
35 
36 bool TexColSingle(const char *mattex, uint8_t& col)
37 {
38  if (SEqual(mattex, DrawFn_Transparent_Name)) { col = 0; return true; }
39  if (SEqual(mattex, DrawFn_Sky_Name)) { col = C4M_MaxTexIndex; return true; }
40 
41  col = ::MapScript.pTexMap->GetIndexMatTex(mattex);
42  if (col == 0) return false;
43 
44  return true;
45 }
46 
47 bool FnParTexCol(C4String *mattex, uint8_t& fg, uint8_t& bg)
48 {
49  // Return index of material-texture definition for a single color
50  // Defaults to underground (tunnel background) color. Prefix material with ^ to get overground (sky background) color,
51  // or specify as mattex1:mattex2 for foreground-background pair.
52  if (!mattex || !mattex->GetCStr()) return false;
53 
54  int sep_pos = SCharPos(':', mattex->GetCStr());
55  if (sep_pos == -1)
56  {
57  const char *cmattex = mattex->GetCStr();
58  bool ift = true;
59  if (*cmattex == '^') { ift=false; ++cmattex; }
60 
61  uint8_t col;
62  if (!TexColSingle(cmattex, col)) return false;
63 
64  fg = col;
65  if (ift)
67  else
68  bg = C4M_MaxTexIndex; // sky
69 
70  return true;
71  }
72  else
73  {
74  const char *cmattex = mattex->GetCStr();
75  std::string fg_mattex(cmattex, cmattex + sep_pos);
76  std::string bg_mattex(cmattex + sep_pos + 1);
77 
78  uint8_t fg_col, bg_col;
79  if (!TexColSingle(fg_mattex.c_str(), fg_col)) return false;
80  if (!TexColSingle(bg_mattex.c_str(), bg_col)) return false;
81 
82  fg = fg_col; bg = bg_col;
83  return true;
84  }
85 }
86 
87 void C4MapScriptMatTexMask::UnmaskSpec(C4String *spec)
88 {
89  // Mask all indices of material-texture definitions
90  // Possible definitions:
91  // Material-Texture - Given material-texture combination (both sky and tunnel background)
92  // Material - All defined default textures of given material
93  // * - All materials including sky
94  // Sky - Index C4M_MaxTexIndex
95  // Transparent - Index 0
96  // Background - All tunnel materials plus sky
97  // Liquid - All liquid materials
98  // Solid - All solid materials
99  // Possible modifiers:
100  // ^Material - Given material only with sky background
101  // &Material - Given material only with tunnel background
102  // ~Material - Inverse of given definition; i.e. everything except Material
103  if (!spec || !spec->GetCStr()) return;
104  const char *cspec = spec->GetCStr();
105  bool invert=false, bgsky=false, bgtunnel=false, prefix_done=false;
106  while (*cspec)
107  {
108  switch (*cspec)
109  {
110  case '~': invert=!invert; break;
111  case '^': bgsky=true; break;
112  case '&': bgtunnel=true; break;
113  default: prefix_done=true; break;
114  }
115  if (prefix_done) break;
116  ++cspec;
117  }
118  std::vector<bool> mat_mask(C4M_MaxTexIndex+1, false);
119  if (SEqual(cspec, DrawFn_Transparent_Name))
120  {
121  // "Transparent" is zero index. Force to non-IFT
122  mat_mask[0] = true;
123  }
124  else if (SEqual(cspec, DrawFn_Sky_Name))
125  {
126  // Sky material
127  mat_mask[C4M_MaxTexIndex] = true;
128  }
129  else if (SEqual(cspec, DrawFn_Background_Name))
130  {
131  // All background materials
132  for (int32_t i=1; i<C4M_MaxTexIndex; ++i) if (!DensitySemiSolid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
133  // Background includes sky
134  mat_mask[C4M_MaxTexIndex] = true;
135  }
136  else if (SEqual(cspec, DrawFn_Liquid_Name))
137  {
138  // All liquid materials
139  for (int32_t i=1; i<C4M_MaxTexIndex; ++i) if (DensityLiquid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
140  }
141  else if (SEqual(cspec, DrawFn_Solid_Name))
142  {
143  // All solid materials
144  for (int32_t i=1; i<C4M_MaxTexIndex; ++i) if (DensitySolid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
145  }
146  else if (SEqual(cspec, "*"))
147  {
148  // All materials
149  for (int32_t i=1; i<C4M_MaxTexIndex; ++i) mat_mask[i] = true;
150  // Including sky
151  mat_mask[C4M_MaxTexIndex] = true;
152  }
153  else
154  {
155  // Specified material
156  if (SCharCount('-', cspec))
157  {
158  // Material+Texture
159  int32_t col = ::MapScript.pTexMap->GetIndexMatTex(cspec, nullptr, false);
160  if (col) mat_mask[col] = true;
161  }
162  else
163  {
164  // Only material: Mask all textures of this material
165  int32_t mat = ::MapScript.pMatMap->Get(cspec);
166  if (mat!=MNone)
167  {
168  const char *tex_name;
169  int32_t col;
170  for (int32_t itex=0; (tex_name=::MapScript.pTexMap->GetTexture(itex)); itex++)
171  if ((col = ::MapScript.pTexMap->GetIndex(cspec,tex_name,false)))
172  mat_mask[col] = true;
173  }
174  }
175  }
176 
177  // 'OR' spec onto this->sky_mask and this->tunnel_mask. Apply bgsky, bgtunnel and invert.
178  for (int32_t i=0; i<C4M_MaxTexIndex + 1; ++i)
179  {
180  if ((mat_mask[i] && (bgsky || !bgtunnel)) != invert)
181  sky_mask[i] = true;
182  if ((mat_mask[i] && (!bgsky || bgtunnel)) != invert)
183  tunnel_mask[i] = true;
184  }
185 }
186 
188 {
189  // Mask may be initialized by a simple string or by an array of strings, of which the effects are OR'ed
190  const C4ValueArray *arr = spec.getArray();
191  if (arr)
192  {
193  // Init by array
194  for (int32_t i=0; i<arr->GetSize(); ++i)
195  {
196  C4String *smask = arr->GetItem(i).getStr();
197  if (!smask) throw C4AulExecError(FormatString("MatTexMask expected string as %dth element in array.", (int)i).getData());
198  UnmaskSpec(smask);
199  }
200  }
201  else
202  {
203  // Init by string
204  C4String *smask = spec.getStr();
205  if (smask)
206  UnmaskSpec(smask);
207  else
208  {
209  if (spec) throw C4AulExecError("MatTexMask expected string or array of strings.");
210  // nil defaults to everything except index zero unmasked
211  sky_mask = std::vector<bool>(256, true);
212  tunnel_mask = std::vector<bool>(256, true);
213  sky_mask[0] = false;
214  tunnel_mask[0] = false;
215  }
216  }
217 }
218 
219 
220 bool FnParRect(C4MapScriptLayer *layer, C4ValueArray *rect, C4Rect *rc_bounds)
221 {
222  // Convert rect parameter passed to script function to C4Rect structure
223  // and makes sure it is completely contained in bounding rectangle of layer
224  // rect==nullptr defaults to bounding rectangle of layer
225  *rc_bounds = layer->GetBounds();
226  if (!rect) return true; // nil is OK for rect parameter. Defaults to bounds rectangle
227  if (rect->GetSize() != 4) return false;
228  rc_bounds->Intersect(C4Rect(rect->GetItem(0).getInt(), rect->GetItem(1).getInt(), rect->GetItem(2).getInt(), rect->GetItem(3).getInt()));
229  return true;
230 }
231 
232 static bool FnLayerDraw(C4PropList * _this, C4String *mattex, C4PropList *mask_algo, C4ValueArray *rect)
233 {
234  // Layer script function: Draw material mattex in shape of mask_algo in _this layer within bounds given by rect
235  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
236  uint8_t fg, bg;
237  if (!layer || !FnParTexCol(mattex, fg, bg)) return false;
238  C4Rect rcBounds;
239  if (!FnParRect(layer, rect, &rcBounds)) return false;
240  std::unique_ptr<C4MapScriptAlgo> algo(FnParAlgo(mask_algo));
241  return layer->Fill(fg, bg, rcBounds, algo.get());
242 }
243 
244 static bool FnLayerBlit(C4PropList * _this, C4PropList *mask_algo, C4ValueArray *rect)
245 {
246  // Layer script function: Blit mask_algo onto surface of _this within bounds given by rect
247  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
248  if (!layer) return false;
249  C4Rect rcBounds;
250  if (!FnParRect(layer, rect, &rcBounds)) return false;
251  std::unique_ptr<C4MapScriptAlgo> algo(FnParAlgo(mask_algo));
252  if (!algo.get()) return false;
253  return layer->Blit(rcBounds, algo.get());
254 }
255 
256 static C4PropList *FnCreateLayer(C4PropList * _this, C4String *mattex_fill, int32_t width, int32_t height)
257 {
258  // Layer script function: Create new layer filled by mattex_fill of size width,height as sub layer of _this map
259  // Size defaults to _this layer size
260  uint8_t fg = 0, bg = 0;
261  if (mattex_fill && mattex_fill->GetCStr())
262  if (!FnParTexCol(mattex_fill, fg, bg))
263  throw C4AulExecError(FormatString("CreateLayer: Invalid fill material.").getData());
264 
265  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
266  if (!layer) return nullptr;
267  if (!width && !height)
268  {
269  width = layer->GetWdt();
270  height = layer->GetHgt();
271  }
272  if (width<=0 || height<=0) throw C4AulExecError(FormatString("CreateLayer: Invalid size (%d*%d).", (int)width, (int)height).getData());
273  C4MapScriptMap *map = layer->GetMap();
274  if (!map) return nullptr;
275  layer = map->CreateLayer(width, height);
276  if (fg != 0 || bg != 0) layer->Fill(fg, bg, layer->GetBounds(), nullptr);
277  return layer;
278 }
279 
280 static C4PropList *FnLayerDuplicate(C4PropList * _this, const C4Value &mask_spec, C4ValueArray *rect)
281 {
282  // Layer script function: Create a copy of _this layer within bounds. If mask_spec is specified, copy only materials selected by mask spec
283  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
284  if (!layer) return nullptr;
285  C4MapScriptMap *map = layer->GetMap();
286  if (!map) return nullptr;
287  C4MapScriptMatTexMask mat_mask(mask_spec);
288  C4Rect src_rect;
289  if (!FnParRect(layer, rect, &src_rect)) return nullptr;
290  if (!src_rect.Wdt || !src_rect.Hgt) return nullptr;
291  C4MapScriptLayer *new_layer = map->CreateLayer(src_rect.Wdt, src_rect.Hgt);
292  new_layer->Blit(layer, src_rect, mat_mask, 0,0);
293  return new_layer;
294 }
295 
296 static int32_t FnLayerGetMaterialTextureIndex(C4PropList * _this, C4String* mattex)
297 {
298  if (!mattex) return -1;
299 
300  uint8_t col;
301  if (!TexColSingle(mattex->GetCStr(), col))
302  return -1;
303 
304  return col;
305 }
306 
307 static int32_t FnLayerGetDefaultBackgroundIndex(C4PropList * _this, const C4Value &value)
308 {
309  uint8_t fg;
310  C4String* str;
311 
312  if ((str = value.getStr()))
313  {
314  if (!TexColSingle(str->GetCStr(), fg))
315  return -1;
316  }
317  else
318  {
319  if (!Inside(value.getInt(), 0, 255))
320  return -1;
321  fg = value.getInt();
322  }
323 
325 }
326 
327 static int32_t FnLayerGetPixel(C4PropList * _this, int32_t x, int32_t y)
328 {
329  // Layer script function: Query pixel at position x,y from _this layer
330  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
331  if (!layer) return 0;
332  return layer->GetPix(x,y,0);
333 }
334 
335 static int32_t FnLayerGetBackPixel(C4PropList * _this, int32_t x, int32_t y)
336 {
337  // Layer script function: Query pixel at position x,y from _this layer
338  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
339  if (!layer) return 0;
340  return layer->GetBackPix(x,y,0);
341 }
342 
343 static bool FnLayerSetPixel(C4PropList * _this, int32_t x, int32_t y, const C4Value& fg_value_c4v, const C4Value& bg_value_c4v)
344 {
345  // Layer script function: Set pixel at position x,y to to_value in _this layer
346  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
347  if (!layer) return false;
348  uint8_t fg, bg;
349 
350  if (fg_value_c4v.GetType() == C4V_Nil)
351  {
352  fg = layer->GetPix(x,y,0);
353  }
354  else
355  {
356  const C4Value& val = fg_value_c4v;
357  C4String *str = val.getStr();
358  if (str != nullptr)
359  {
360  if (!TexColSingle(str->GetCStr(), fg))
361  throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
362  }
363  else
364  {
365  if (!Inside(val.getInt(), 0, 255))
366  throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
367  fg = val.getInt();
368  }
369  }
370 
371  if (bg_value_c4v.GetType() == C4V_Nil)
372  {
373  bg = layer->GetBackPix(x,y,0);
374  }
375  else
376  {
377  const C4Value& val = bg_value_c4v;
378  C4String *str = val.getStr();
379  if (str != nullptr)
380  {
381  if (!TexColSingle(str->GetCStr(), bg))
382  throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
383  }
384  else
385  {
386  if (!Inside(val.getInt(), 0, 255))
387  throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
388  bg = val.getInt();
389  }
390  }
391 
392  return layer->SetPix(x,y,fg,bg);
393 }
394 
395 static int32_t FnLayerGetPixelCount(C4PropList * _this, const C4Value &mask_spec, C4ValueArray *rect)
396 {
397  // Layer script function: Count all pixels within rect that match mask_spec specification
398  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
399  if (!layer) return -1;
400  C4MapScriptMatTexMask mat_mask(mask_spec);
401  C4Rect check_rect;
402  if (!FnParRect(layer, rect, &check_rect)) return -1;
403  return layer->GetPixCount(check_rect, mask_spec);
404 }
405 
406 static bool FnLayerResize(C4PropList * _this, int32_t new_wdt, int32_t new_hgt)
407 {
408  // Layer script function: Recreate layer in new size. Resulting layer is empty (color 0)
409  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
410  // safety
411  if (!layer || new_wdt<=0 || new_hgt<=0) return false;
412  // recreate surface in new size
413  layer->ClearSurface();
414  return layer->CreateSurface(new_wdt, new_hgt);
415 }
416 
417 static bool FnLayerFindPosition(C4PropList * _this, C4PropList *out_pos, const C4Value &mask_spec, C4ValueArray *rect, int32_t max_tries)
418 {
419  // Layer script function: Find a position (x,y) that has a color matching mask_spec. Set resulting position as X,Y properties in out_pos prop list
420  C4MapScriptLayer *layer = _this->GetMapScriptLayer();
421  if (!layer) return false;
422  C4MapScriptMatTexMask mat_mask(mask_spec);
423  C4Rect search_rect;
424  if (!FnParRect(layer, rect, &search_rect)) return false;
425  int32_t x,y; bool result;
426  if (!max_tries) max_tries = 500;
427  if ((result = layer->FindPos(search_rect, mat_mask, &x, &y, max_tries)))
428  {
429  if (out_pos && !out_pos->IsFrozen())
430  {
431  out_pos->SetProperty(P_X, C4VInt(x));
432  out_pos->SetProperty(P_Y, C4VInt(y));
433  }
434  }
435  return result;
436 }
437 
438 static C4ValueArray *FnLayerCreateMatTexMask(C4PropList * _this, const C4Value &mask_spec)
439 {
440  // layer script function: Generate an array 256 bools representing the given mask_spec
441  C4MapScriptMatTexMask mat_mask(mask_spec);
442  C4ValueArray *result = new C4ValueArray(C4M_MaxTexIndex + 1);
443  for (int32_t i=0; i < C4M_MaxTexIndex + 1; ++i)
444  {
445  result->SetItem(i, C4VBool(mat_mask(uint8_t(i), C4M_MaxTexIndex)));
446  }
447  return result;
448 }
449 
450 // TODO: CreateBackMatTexMask? Or Return 512 bools?
451 
453 {
454  // It seems like numbered PropLists need a number. I don't know why.
455  AcquireNumber();
456 }
457 
458 bool C4MapScriptLayer::CreateSurface(int32_t wdt, int32_t hgt)
459 {
460  // Create new surface of given size. Surface is filled with color 0
461  ClearSurface();
462  if (wdt<=0 || hgt<=0) return false;
463  fg_surface = std::make_unique<CSurface8>();
464  bg_surface = std::make_unique<CSurface8>();
465  if (!fg_surface->Create(wdt, hgt) || !bg_surface->Create(wdt, hgt))
466  {
467  ClearSurface();
468  return false;
469  }
471  return true;
472 }
473 
475 {
476  fg_surface.reset(); bg_surface.reset();
477  // if there is no surface, width and height parameters are undefined. no need to update them.
478 }
479 
481 {
482  // Called when surface size changes: Update internal property values
483  if (fg_surface)
484  {
485  SetProperty(P_Wdt, C4VInt(fg_surface->Wdt));
486  SetProperty(P_Hgt, C4VInt(fg_surface->Hgt));
487  }
488 }
489 
491 {
492  // Convert all sky (color==C4M_MaxTexIndex) pixels to transparent (color==0)
493  // Needed because C4Landscape map zoom assumes sky to be 0
494  if (!HasSurface()) return;
495  for (int32_t y=0; y<fg_surface->Hgt; ++y)
496  {
497  for (int32_t x=0; x<fg_surface->Wdt; ++x)
498  {
499  if (fg_surface->_GetPix(x,y) == C4M_MaxTexIndex)
500  fg_surface->_SetPix(x,y, 0);
501 
502  if (bg_surface->_GetPix(x,y) == C4M_MaxTexIndex)
503  bg_surface->_SetPix(x,y, 0);
504  }
505  }
506 }
507 
509 {
510  // Return bounding rectangle of surface. Surface always starts at 0,0.
511  return fg_surface ? C4Rect(0,0,fg_surface->Wdt,fg_surface->Hgt) : C4Rect();
512 }
513 
514 bool C4MapScriptLayer::Fill(uint8_t fg, uint8_t bg, const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
515 {
516  // safety
517  uint8_t temp_fg, temp_bg;
518  if (!HasSurface()) return false;
519  assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=fg_surface->Wdt && rcBounds.y+rcBounds.Hgt<=fg_surface->Hgt);
520  // set all non-masked pixels within bounds that fulfill algo
521  for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
522  for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
523  if (!algo || (*algo)(x,y,temp_fg,temp_bg))
524  {
525  fg_surface->_SetPix(x,y,fg);
526  bg_surface->_SetPix(x,y,bg);
527  }
528 
529  return true;
530 }
531 
532 bool C4MapScriptLayer::Blit(const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
533 {
534  // safety
535  if (!HasSurface()) return false;
536  assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=fg_surface->Wdt && rcBounds.y+rcBounds.Hgt<=fg_surface->Hgt);
537  assert(algo);
538  // set all pixels within bounds by algo, if algo is not transparent
539  uint8_t fg, bg;
540  for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
541  for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
542  if (((*algo)(x,y,fg,bg)))
543  {
544  if (fg) fg_surface->_SetPix(x,y,fg);
545  if (bg) bg_surface->_SetPix(x,y,bg);
546  }
547  return true;
548 }
549 
550 bool C4MapScriptLayer::Blit(const C4MapScriptLayer *src, const C4Rect &src_rect, const C4MapScriptMatTexMask &col_mask, int32_t tx, int32_t ty)
551 {
552  // safety
553  assert(src);
554  if (!HasSurface() || !src->HasSurface()) return false;
555  // cannot assert this, because C4Rect::Contains(C4Rect &) has an off-by-one-error which I don't dare to fix right now
556  // TODO: Fix C4Rect::Contains and check if the sector code still works
557  // assert(src->GetBounds().Contains(src_rect));
558  // copy all pixels that aren't masked
559  uint8_t fg, bg;
560  for (int32_t y=src_rect.y; y<src_rect.y+src_rect.Hgt; ++y)
561  for (int32_t x=src_rect.x; x<src_rect.x+src_rect.Wdt; ++x)
562  {
563  fg = src->fg_surface->_GetPix(x, y);
564  bg = src->bg_surface->_GetPix(x, y);
565 
566  if (col_mask(fg, bg))
567  {
568  fg_surface->_SetPix(x-src_rect.x+tx,y-src_rect.y+ty,fg);
569  bg_surface->_SetPix(x-src_rect.x+tx,y-src_rect.y+ty,bg);
570  }
571  }
572  return true;
573 }
574 
575 int32_t C4MapScriptLayer::GetPixCount(const C4Rect &rcBounds, const C4MapScriptMatTexMask &col_mask)
576 {
577  // safety
578  if (!HasSurface()) return 0;
579  assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=fg_surface->Wdt && rcBounds.y+rcBounds.Hgt<=fg_surface->Hgt);
580  // count matching pixels in rect
581  int32_t count = 0;
582  for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
583  for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
584  count += col_mask(fg_surface->_GetPix(x,y), bg_surface->_GetPix(x, y));
585  return count;
586 }
587 
588 bool C4MapScriptLayer::FindPos(const C4Rect &search_rect, const C4MapScriptMatTexMask &col_mask, int32_t *out_x, int32_t *out_y, int32_t max_tries)
589 {
590  // safety
591  if (!HasSurface() || search_rect.Wdt<=0 || search_rect.Hgt<=0) return false;
592  // Search random positions
593  for (int32_t i=0; i<max_tries; ++i)
594  {
595  int32_t x=search_rect.x + Random(search_rect.Wdt);
596  int32_t y=search_rect.y + Random(search_rect.Hgt);
597  if (col_mask(fg_surface->_GetPix(x,y), bg_surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
598  }
599  // Nothing found yet: Start at a random position and search systemically
600  // (this guantuess to find a pixel if there is one, but favours border pixels)
601  int32_t sx=search_rect.x + Random(search_rect.Wdt);
602  int32_t sy=search_rect.y + Random(search_rect.Hgt);
603  for (int32_t x = sx; x < search_rect.x + search_rect.Wdt; ++x)
604  if (col_mask(fg_surface->_GetPix(x,sy), bg_surface->_GetPix(x,sy))) { *out_x=x; *out_y=sy; return true; }
605  for (int32_t y = sy + 1; y<search_rect.y + search_rect.Hgt; ++y)
606  for (int32_t x = search_rect.x; x < search_rect.x + search_rect.Wdt; ++x)
607  if (col_mask(fg_surface->_GetPix(x,y), bg_surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
608  for (int32_t y = search_rect.y; y<sy; ++y)
609  for (int32_t x = search_rect.x; x < search_rect.x + search_rect.Wdt; ++x)
610  if (col_mask(fg_surface->_GetPix(x,y), bg_surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
611  for (int32_t x = search_rect.x; x<sx; ++x)
612  if (col_mask(fg_surface->_GetPix(x,sy), bg_surface->_GetPix(x,sy))) { *out_x=x; *out_y=sy; return true; }
613  // Nothing found
614  return false;
615 }
616 
618 {
619  // Layers are owned by map. Free them.
620  for (std::list<C4MapScriptLayer *>::iterator i=layers.begin(); i!=layers.end(); ++i) delete *i;
621  layers.clear();
622 }
623 
625 {
626  // Create layer and register to map. Layer's created by a map are freed when the map is freed.
628  layers.push_back(new_layer); // push before CreateSurface for exception safety
629  if (!new_layer->CreateSurface(wdt, hgt))
630  {
631  layers.remove(new_layer);
632  delete new_layer;
633  return nullptr;
634  }
635  return new_layer;
636 }
637 
638 C4MapScriptHost::C4MapScriptHost(): LayerPrototype(nullptr), MapPrototype(nullptr), pTexMap(nullptr), pMatMap(nullptr) { }
639 
641 
643 {
644  // Register script host. Add Map and MapLayer prototypes, related constants and engine functions
645  assert(pEngine && pEngine->GetPropList());
646  Clear();
647  LayerPrototype = new C4PropListStaticMember(nullptr, nullptr, ::Strings.RegString("MapLayer"));
648  MapPrototype = new C4PropListStaticMember(LayerPrototype, nullptr, ::Strings.RegString("Map"));
649  LayerPrototype->SetName("MapLayer");
650  MapPrototype->SetName("Map");
651  ::ScriptEngine.RegisterGlobalConstant("MapLayer", C4VPropList(LayerPrototype));
652  ::ScriptEngine.RegisterGlobalConstant("Map", C4VPropList(MapPrototype));
669  Reg2List(pEngine);
671 }
672 
674 {
675  // adds all engine functions to the MapLayer context
677  ::AddFunc(p, "Draw", FnLayerDraw);
678  ::AddFunc(p, "Blit", FnLayerBlit);
679  ::AddFunc(p, "CreateLayer", FnCreateLayer);
680  ::AddFunc(p, "Duplicate", FnLayerDuplicate);
681  ::AddFunc(p, "GetMaterialTextureIndex", FnLayerGetMaterialTextureIndex);
682  ::AddFunc(p, "GetDefaultBackgroundIndex", FnLayerGetDefaultBackgroundIndex);
683  ::AddFunc(p, "GetPixel", FnLayerGetPixel);
684  ::AddFunc(p, "GetBackPixel", FnLayerGetBackPixel);
685  ::AddFunc(p, "SetPixel", FnLayerSetPixel);
686  ::AddFunc(p, "GetPixelCount", FnLayerGetPixelCount);
687  ::AddFunc(p, "Resize", FnLayerResize);
688  ::AddFunc(p, "FindPosition", FnLayerFindPosition);
689  ::AddFunc(p, "CreateMatTexMask", FnLayerCreateMatTexMask);
690 }
691 
692 bool C4MapScriptHost::Load(C4Group & g, const char * f, const char * l, C4LangStringTable * t)
693 {
694  assert(LayerPrototype && MapPrototype);
695  return C4ScriptHost::Load(g, f, l, t);
696 }
697 
698 bool C4MapScriptHost::LoadData(const char * f, const char * d, C4LangStringTable * t)
699 {
700  assert(LayerPrototype && MapPrototype);
701  return C4ScriptHost::LoadData(f, d, t);
702 }
703 
705 {
707  delete LayerPrototype; delete MapPrototype;
708  LayerPrototype = MapPrototype = nullptr;
709 }
710 
712 {
713  // Scripts are compiled in the MapLayer context so it's possible to use all map drawing functions directly without "map->" prefix
714  return LayerPrototype;
715 }
716 
717 C4MapScriptMap *C4MapScriptHost::CreateMap()
718 {
719  return new C4MapScriptMap(MapPrototype);
720 }
721 
722 bool C4MapScriptHost::InitializeMap(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, uint32_t iPlayerCount, std::unique_ptr<CSurface8> *pmap_fg_surface, std::unique_ptr <CSurface8>* pmap_bg_surface)
723 {
724  // Init scripted map by calling InitializeMap in the proper scripts. If *pmap_surface is given, it will pass the existing map to be modified by script.
725  assert(pmap_fg_surface);
726  assert(pmap_bg_surface);
727 
728  this->pTexMap = pTexMap;
729  this->pMatMap = pMatMap;
730  // Don't bother creating surfaces if the functions aren't defined
731  if (!LayerPrototype->GetFunc(PSF_InitializeMap))
732  {
734  if (!scen_proplist || !scen_proplist->GetFunc(PSF_InitializeMap)) return false;
735  }
736  // Create proplist as script context
737  std::unique_ptr<C4MapScriptMap> map(CreateMap());
738 
739  // Drawing on existing map or create new?
740  if (*pmap_fg_surface && *pmap_bg_surface)
741  {
742  // Existing map
743  map->SetSurfaces(std::move(*pmap_fg_surface), std::move(*pmap_bg_surface));
744  }
745  else
746  {
747  assert(!*pmap_fg_surface && !*pmap_bg_surface);
748  // No existing map. Create new.
749  int32_t map_wdt,map_hgt;
750  pLandscape->GetMapSize(map_wdt, map_hgt, iPlayerCount);
751  if (!map->CreateSurface(map_wdt, map_hgt)) return false;
752  }
753  C4AulParSet Pars(C4VPropList(map.get()));
754  C4Value result = map->Call(PSF_InitializeMap, &Pars);
755  if (!result) result = ::GameScript.Call(PSF_InitializeMap, &Pars);
756  // Map creation done.
757  if (result)
758  {
759  map->ConvertSkyToTransparent();
760  }
761  std::tie(*pmap_fg_surface, *pmap_bg_surface) = map->ReleaseSurfaces();
762  return !!result;
763 }
764 
const char * getData() const
Definition: StdBuf.h:450
void RegisterGlobalConstant(const char *szName, const C4Value &rValue)
void SetItem(int32_t iElemNr, const C4Value &Value)
uint32_t Random()
Definition: C4Random.cpp:43
C4PropListStatic * GetPropList()
Definition: C4Aul.h:153
bool Fill(uint8_t fg, uint8_t bg, const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
virtual void SetName(const char *NewName=0)
Definition: C4PropList.cpp:611
C4String * getStr() const
Definition: C4Value.h:117
C4GameScriptHost GameScript
bool HasSurface() const
Definition: C4MapScript.h:296
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
BYTE DefaultBkgMatTex(BYTE fg) const
Definition: C4Texture.cpp:513
C4AulFunc * GetFunc(C4PropertyName k) const
Definition: C4PropList.h:107
void GetMapSize(int32_t &rWdt, int32_t &rHgt, int32_t iPlayerNum)
Definition: C4Scenario.cpp:317
int32_t GetPixCount(const C4Rect &rcBounds, const C4MapScriptMatTexMask &mask)
const char * GetCStr() const
Definition: C4StringTable.h:49
C4String * RegString(StdStrBuf String)
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
bool TexColSingle(const char *mattex, uint8_t &col)
Definition: C4MapScript.cpp:36
void SetProperty(C4PropertyName k, const C4Value &to)
Definition: C4PropList.h:122
C4Value Call(const char *szFunction, C4AulParSet *pPars=0, bool fPassError=false)
virtual void AddEngineFunctions()
Definition: C4Rect.h:29
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)
C4Rect GetBounds() const
bool DensitySemiSolid(int32_t dens)
Definition: C4Landscape.h:204
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:97
unsigned int SCharCount(char cTarget, const char *szInStr, const char *cpUntil)
Definition: Standard.cpp:290
void Intersect(const C4Rect &r2)
Definition: C4Rect.cpp:100
void InitFunctionMap(C4AulScriptEngine *pEngine)
bool SetPix(int32_t x, int32_t y, uint8_t fg, uint8_t bg) const
Definition: C4MapScript.h:307
int32_t GetIndex(const char *szMaterial, const char *szTexture, bool fAddIfNotExist=true, const char *szErrorIfFailed=nullptr)
Definition: C4Texture.cpp:423
int32_t GetHgt() const
Definition: C4MapScript.h:302
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
void Reg2List(C4AulScriptEngine *pEngine)
bool FindPos(const C4Rect &search_rect, const C4MapScriptMatTexMask &mask, int32_t *out_x, int32_t *out_y, int32_t max_tries)
C4MaterialMap * pMatMap
Definition: C4MapScript.h:354
int32_t Wdt
Definition: C4Rect.h:32
int32_t GetWdt() const
Definition: C4MapScript.h:301
C4MapScriptHost MapScript
const int32_t MNone
Definition: C4Constants.h:178
void ConvertSkyToTransparent()
C4TextureMap * pTexMap
Definition: C4MapScript.h:353
class C4MapScriptMap * GetMap()
Definition: C4MapScript.h:281
virtual C4PropListStatic * GetPropList()
int32_t y
Definition: C4Rect.h:32
C4V_Type GetType() const
Definition: C4Value.h:161
C4StringTable Strings
Definition: C4Globals.cpp:42
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:245
bool DensityLiquid(int32_t dens)
Definition: C4Landscape.h:209
C4Landscape Landscape
const int C4M_MaxTexIndex
Definition: C4Constants.h:51
C4Value ScenPropList
Definition: C4ScriptHost.h:163
C4MapScriptLayer(C4PropList *prototype, C4MapScriptMap *map)
bool FnParRect(C4MapScriptLayer *layer, C4ValueArray *rect, C4Rect *rc_bounds)
C4MapScriptLayer * CreateLayer(int32_t wdt, int32_t hgt)
bool FnParTexCol(C4String *mattex, uint8_t &fg, uint8_t &bg)
Definition: C4MapScript.cpp:47
void UpdateSurfaceSize()
bool CreateSurface(int32_t wdt, int32_t hgt)
virtual bool LoadData(const char *szFilename, const char *szData, class C4LangStringTable *pLocalTable)
uint8_t GetBackPix(int32_t x, int32_t y, uint8_t outside_col) const
Definition: C4MapScript.h:306
C4ValueArray * getArray() const
Definition: C4Value.h:118
bool IsFrozen() const
Definition: C4PropList.h:132
bool Blit(const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
int32_t GetSize() const
Definition: C4ValueArray.h:36
int32_t getInt() const
Definition: C4Value.h:112
C4Value C4VBool(bool b)
Definition: C4Value.h:243
int32_t x
Definition: C4Rect.h:32
#define PSF_InitializeMap
Definition: C4GameScript.h:44
int32_t Get(const char *szMaterial)
Definition: C4Material.cpp:362
virtual class C4MapScriptLayer * GetMapScriptLayer()
Definition: C4PropList.cpp:646
const char * GetTexture(int32_t iIndex)
Definition: C4Texture.cpp:503
int32_t GetPixDensity(BYTE byPix) const
virtual bool LoadData(const char *, const char *, C4LangStringTable *)
virtual bool Load(C4Group &, const char *, const char *, C4LangStringTable *)
C4MapScriptAlgo * FnParAlgo(C4PropList *algo_par)
void Init(const C4Value &spec)
int SCharPos(char cTarget, const char *szInStr, int iIndex)
Definition: Standard.cpp:203
int32_t Hgt
Definition: C4Rect.h:32
C4PropList * _getPropList() const
Definition: C4Value.h:129
int32_t GetIndexMatTex(const char *szMaterialTexture, const char *szDefaultTexture=nullptr, bool fAddIfNotExist=true, const char *szErrorIfFailed=nullptr)
Definition: C4Texture.cpp:450
virtual bool Load(C4Group &hGroup, const char *szFilename, const char *szLanguage, C4LangStringTable *pLocalTable)
void AddFunc(C4PropListStatic *Parent, const char *Name, RType(*pFunc)(ThisType *, ParTypes...), bool Public=true)
Definition: C4AulDefFunc.h:261
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:45
C4PropListStatic * GetLayerPrototype()
Definition: C4MapScript.h:351
bool DensitySolid(int32_t dens)
Definition: C4Landscape.h:199
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
uint8_t GetPix(int32_t x, int32_t y, uint8_t outside_col) const
Definition: C4MapScript.h:305