1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2020, The LegacyClonk 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// complex dynamic landscape creator
19
20#pragma once
21
22#include "C4ForwardDeclarations.h"
23#include <C4Group.h>
24#include <C4Scenario.h>
25#include <C4Surface.h>
26
27#include <format>
28
29#define C4MC_SizeRes 100 // positions in percent
30#define C4MC_ZoomRes 100 // zoom resolution (-100 to +99)
31
32// string consts
33#define C4MC_Overlay "overlay" // overlay node
34#define C4MC_Point "point" // polygon point
35#define C4MC_Map "map" // map node
36
37#define C4MC_DefAlgo "solid" // default overlay algorithm
38
39// error messages
40#define C4MCErr_404 "file not found"
41#define C4MCErr_NoGroup "internal error: no group"
42
43#define C4MCErr_EOF "unexpected end of file"
44#define C4MCErr_NoDirGlobal "can't use directives in local scope"
45#define C4MCErr_UnknownDir "unknown directive: {}"
46#define C4MCErr_MapNoGlobal "can't declare map in local scope"
47#define C4MCErr_OpTypeErr "operator type mismatch"
48#define C4MCErr_IdtfExp "identifier expected"
49#define C4MCErr_UnnamedNoGlbl "unnamed objects not allowed in global scope"
50#define C4MCErr_BlOpenExp "'{{' expected"
51#define C4MCErr_OpsNoGlobal "operators not allowed in global scope"
52#define C4MCErr_SColonOrOpExp "';' or operator expected"
53#define C4MCErr_Obj2Exp "second operand expected"
54#define C4MCErr_ReinstNoGlobal "can't reinstanciate object '{}' in global scope"
55#define C4MCErr_UnknownObj "unknown object: {}"
56#define C4MCErr_ReinstUnknown "can't reinstanciate '{}'; object type is unknown"
57#define C4MCErr_EqSColonBlOpenExp "'=', ';' or '{{' expected"
58#define C4MCErr_FieldConstExp "constant for field '{}' expected"
59#define C4MCErr_SColonExp "';' expected"
60#define C4MCErr_Field404 "field '{}' not found"
61#define C4MCErr_FieldValInvalid "'{}' is not a valid value for this field"
62#define C4MCErr_MatNotFound "material '{}' not found"
63#define C4MCErr_TexNotFound "texture '{}' not found"
64#define C4MCErr_AlgoNotFound "algorithm '{}' not found"
65#define C4MCErr_SFuncNotFound "script func '{}' not found in scenario script"
66#define C4MCErr_PointOnlyOvl "point only allowed in overlays"
67
68// predef
69class C4MCCallbackArray;
70class C4MCCallbackArrayList;
71class C4MCNode;
72class C4MCOverlay;
73class C4MCPoint;
74class C4MCMap;
75class C4MapCreatorS2;
76class C4MCParserErr;
77class C4MCParser;
78
79struct C4MCAlgorithm
80{
81 char Identifier[C4MaxName];
82 bool(*Function)(C4MCOverlay *, int32_t, int32_t);
83};
84
85extern C4MCAlgorithm C4MCAlgoMap[];
86
87// node type enum
88enum C4MCNodeType { MCN_Node, MCN_Overlay, MCN_Point, MCN_Map };
89
90// one token type
91enum C4MCTokenType
92{
93 MCT_NONE, // nothing
94 MCT_DIR, // directive (stored in CurrTokenIdtf)
95 MCT_IDTF, // identifier (stored in CurrTokenIdtf)
96 MCT_INT, // integer constant (stored in CurrTokenVal)
97 MCT_EQ, // =
98 MCT_BLOPEN, // {
99 MCT_BLCLOSE, // }
100 MCT_SCOLON, // ;
101 MCT_AND, // &
102 MCT_OR, // |
103 MCT_XOR, // ^
104 MCT_RANGE, // -
105 MCT_PERCENT, // integer constant (stored in CurrTokenVal) + %
106 MCT_PX, // integer constant (stored in CurrTokenVal) + px
107 MCT_EOF // end of file
108};
109
110// a callback array
111// contains a script func, and a map to call the func for
112class C4MCCallbackArray
113{
114public:
115 C4MCCallbackArray(C4AulFunc *pSFunc, C4MapCreatorS2 *pMapCreator);
116 ~C4MCCallbackArray();
117
118protected:
119 C4MapCreatorS2 *pMapCreator; // map creator class to query current map of
120 uint8_t *pMap; // bitmap whether or not to call the function for a map pixel
121 int32_t iWdt, iHgt; // size of the bitmap, when created
122 C4AulFunc *pSF; // script func to be called
123
124 C4MCCallbackArray *pNext; // next array in linked list
125
126public:
127 void EnablePixel(int32_t iX, int32_t iY); // enable pixel in map; create map if necessary
128 void Execute(int32_t iMapZoom); // evaluate the array
129
130 friend class C4MCCallbackArrayList;
131};
132
133// callback array list: contains all callbacks
134class C4MCCallbackArrayList
135{
136public:
137 C4MCCallbackArrayList() { pFirst = nullptr; }
138 ~C4MCCallbackArrayList() { Clear(); }
139
140protected:
141 C4MCCallbackArray *pFirst; // first array in list
142
143public:
144 void Add(C4MCCallbackArray *pNewArray); // add given array to list
145 void Clear(); // clear the list
146 void Execute(int32_t iMapZoom); // execute all arrays
147};
148
149// generic map creator tree node
150// the code has been STL-free so far, so keep the line
151class C4MCNode
152{
153public:
154 C4MCNode *Owner, *Child0, *ChildL, *Prev, *Next; // tree structure
155 C4MapCreatorS2 *MapCreator; // owning map creator
156 char Name[C4MaxName]; // name, if named
157
158public:
159 C4MCNode(C4MCNode *pOwner = nullptr);
160 C4MCNode(C4MCNode *pOwner, C4MCNode &rTemplate, bool fClone); // constructor using template
161 virtual ~C4MCNode();
162
163 virtual C4MCNode *clone(C4MCNode *pToNode) { return new C4MCNode(pToNode, *this, true); }
164
165 void Clear(); // clear all child nodes
166 void Reg2Owner(C4MCNode *pOwner); // register into list
167
168protected:
169 virtual bool GlobalScope() { return false; } // whether node is a global scope
170 virtual bool SetOp(C4MCTokenType eOp) { return false; } // set following operator
171 C4MCNode *GetNodeByName(const char *szName); // search node by name
172
173 virtual bool SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType); // set field
174 int32_t IntPar(C4MCParser *pParser, const char *szSVal, int32_t iVal, C4MCTokenType ValType); // ensure par is int32_t
175 const char *StrPar(C4MCParser *pParser, const char *szSVal, int32_t iVal, C4MCTokenType ValType); // ensure par is string
176
177 virtual void Evaluate() {} // called when all fields are initialized
178 void ReEvaluate(); // evaluate everything again
179
180 // For Percents and Pixels
181 class int_bool
182 {
183 public:
184 int32_t Evaluate(int32_t relative_to)
185 {
186 if (percent) return value * relative_to / C4MC_SizeRes; else return value;
187 }
188
189 void Set(int32_t value, bool percent)
190 {
191 this->value = value; this->percent = percent;
192 }
193
194 private:
195 int32_t value;
196 bool percent;
197 };
198
199public:
200 virtual C4MCNodeType Type() { return MCN_Node; } // get node type
201 virtual C4MCOverlay *Overlay() { return nullptr; } // return overlay, if this is one
202 C4MCOverlay *OwnerOverlay(); // return an owner who is an overlay
203
204 friend class C4MCParser;
205 friend struct C4MCNodeAttr;
206};
207
208// node attribute entry for SetField search
209enum C4MCValueType
210{
211 C4MCV_None,
212 C4MCV_Integer,
213 C4MCV_Percent,
214 C4MCV_Pixels,
215 C4MCV_Material,
216 C4MCV_Texture,
217 C4MCV_Algorithm,
218 C4MCV_Boolean,
219 C4MCV_Zoom,
220 C4MCV_ScriptFunc,
221};
222
223struct C4MCNodeAttr
224{
225 const char *Name; // name of field
226 C4MCValueType Type; // type of field
227 union
228 {
229 int32_t C4MCOverlay:: *integer;
230 C4MCNode::int_bool C4MCOverlay:: *intBool;
231 char (C4MCOverlay:: *texture)[C4M_MaxName + 1];
232 C4MCAlgorithm * C4MCOverlay:: *algorithm;
233 bool C4MCOverlay:: *boolean;
234 C4MCCallbackArray * C4MCOverlay:: *scriptFunc;
235 }; // offset of field in overlay MCOverlay-class
236
237 C4MCNodeAttr() : Name(""), Type(C4MCV_None), integer(nullptr) {}
238 C4MCNodeAttr(const char *Name, int32_t C4MCOverlay:: *integer, C4MCValueType Type = C4MCV_Integer) : Name(Name), Type(Type), integer(integer) {}
239 C4MCNodeAttr(const char *Name, C4MCNode::int_bool C4MCOverlay:: *intBool, C4MCValueType Type = C4MCV_Percent) : Name(Name), Type(Type), intBool(intBool) {}
240 C4MCNodeAttr(const char *Name, char (C4MCOverlay:: *texture)[C4M_MaxName + 1]) : Name(Name), Type(C4MCV_Texture), texture(texture) {}
241 C4MCNodeAttr(const char *Name, C4MCAlgorithm * C4MCOverlay:: *algorithm) : Name(Name), Type(C4MCV_Algorithm), algorithm(algorithm) {}
242 C4MCNodeAttr(const char *Name, bool C4MCOverlay:: *boolean) : Name(Name), Type(C4MCV_Boolean), boolean(boolean) {}
243 C4MCNodeAttr(const char *Name, C4MCCallbackArray * C4MCOverlay:: *scriptFunc) : Name(Name), Type(C4MCV_ScriptFunc), scriptFunc(scriptFunc) {}
244};
245extern C4MCNodeAttr C4MCOvrlMap[];
246
247// overlay node
248class C4MCOverlay : public C4MCNode
249{
250public:
251 C4MCOverlay(C4MCNode *pOwner = nullptr);
252 C4MCOverlay(C4MCNode *pOwner, C4MCOverlay &rTemplate, bool fClone); // construct of template
253
254 C4MCNode *clone(C4MCNode *pToNode) override { return new C4MCOverlay(pToNode, *this, true); }
255
256protected:
257 void Default(); // set default values for default presets
258
259public:
260 int32_t Seed; // random seed
261 int32_t FixedSeed; // fixed random seed set in def
262 int32_t X, Y, Wdt, Hgt, OffX, OffY; // extends/offset
263 int_bool RX, RY, RWdt, RHgt, ROffX, ROffY; // extends/offset relatively to owner
264 int32_t Material; // material index
265 bool Sub; // tunnel bg?
266 char Texture[C4M_MaxName + 1]; // texture name
267 uint8_t MatClr; // resolved mat-tex color
268 C4MCTokenType Op; // following operator
269 C4MCAlgorithm *Algorithm; // algorithm to calc whether filled or not
270 int32_t Turbulence, Lambda, Rotate; // turbulence factors; rotation angle
271 int_bool Alpha, Beta; // extra params
272 int32_t ZoomX, ZoomY; // zoom factor for algorithm
273 bool Invert, LooseBounds, Group, Mask; // extra algo behaviour
274 C4MCCallbackArray *pEvaluateFunc; // function called for nodes being evaluated and fulfilled
275 C4MCCallbackArray *pDrawFunc; // function called when this node is drawn - pass drawcolor as first param, return color to be actually used
276
277 bool SetOp(C4MCTokenType eOp) override { Op = eOp; return true; } // set following operator
278
279 C4MCAlgorithm *GetAlgo(const char *szName);
280
281 bool SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType) override; // set field
282
283 void Evaluate() override; // called when all fields are initialized
284
285 C4MCOverlay *Overlay() override { return this; } // this is an overlay
286 C4MCOverlay *FirstOfChain(); // go backwards in op chain until first overlay of chain
287
288 bool CheckMask(int32_t iX, int32_t iY); // check whether algorithms succeeds at iX/iY
289 bool RenderPix(int32_t iX, int32_t iY, uint8_t &rPix, C4MCTokenType eLastOp = MCT_NONE, bool fLastSet = false, bool fDraw = true, C4MCOverlay **ppPixelSetOverlay = nullptr); // render this pixel
290 bool PeekPix(int32_t iX, int32_t iY); // check mask; regard operator chain
291 bool InBounds(int32_t iX, int32_t iY) { return iX >= X && iY >= Y && iX < X + Wdt && iY < Y + Hgt; } // return whether point iX/iY is inside bounds
292
293public:
294 C4MCNodeType Type() override { return MCN_Overlay; } // get node type
295
296 friend class C4MapCreatorS2;
297 friend class C4MCParser;
298};
299
300// point of polygon node
301class C4MCPoint : public C4MCNode
302{
303public:
304 C4MCPoint(C4MCNode *pOwner = nullptr);
305 C4MCPoint(C4MCNode *pOwner, C4MCPoint &rTemplate, bool fClone); // construct of template
306
307 C4MCNode *clone(C4MCNode *pToNode) override { return new C4MCPoint(pToNode, *this, true); }
308
309protected:
310 void Default(); // set default values for default presets
311
312public:
313 int32_t X, Y;
314 int_bool RX, RY;
315
316 virtual void Evaluate() override; // called when all fields are initialized
317 bool SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType) override; // set field
318
319public:
320 C4MCNodeType Type() override { return MCN_Point; } // get node type
321
322 friend class C4MapCreatorS2;
323 friend class C4MCParser;
324};
325
326// simply an overlay that can be rendered
327class C4MCMap : public C4MCOverlay
328{
329public:
330 C4MCMap(C4MCNode *pOwner = nullptr);
331 C4MCMap(C4MCNode *pOwner, C4MCMap &rTemplate, bool fClone); // construct of template
332
333 C4MCNode *clone(C4MCNode *pToNode) override { return new C4MCMap(pToNode, *this, true); }
334
335protected:
336 void Default(); // set default values for default presets
337
338public:
339 bool RenderTo(uint8_t *pToBuf, int32_t iPitch); // render to buffer
340 void SetSize(int32_t iWdt, int32_t iHgt);
341
342public:
343 C4MCNodeType Type() override { return MCN_Map; } // get node type
344
345 friend class C4MapCreatorS2;
346 friend class C4MCParser;
347};
348
349// main map creator class
350class C4MapCreatorS2 : public C4MCNode
351{
352public:
353 C4MapCreatorS2(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, int iPlayerCount);
354 C4MapCreatorS2(C4MapCreatorS2 &rTemplate, C4SLandscape *pLandscape); // construct of template
355 ~C4MapCreatorS2();
356
357
358 void Default(); // set default data
359 void Clear(); // clear any data
360 bool ReadFile(const char *szFilename, C4Group *pGrp); // read defs of file
361 bool ReadScript(const char *szScript); // reads def directly from mem
362
363public:
364 C4MCMap *GetMap(const char *szMapName); // get map by name
365
366public:
367 CSurface8 *Render(const char *szMapName); // create map surface
368
369protected:
370 C4SLandscape *Landscape; // landsape presets
371 C4TextureMap *TexMap; // texture map
372 C4MaterialMap *MatMap; // material map
373 C4MCMap DefaultMap; // default template: landscape
374 C4MCOverlay DefaultOverlay; // default template: overlay
375 C4MCPoint DefaultPoint; // default template: point
376 C4MCMap *pCurrentMap; // map currently rendered
377 C4MCCallbackArrayList CallbackArrays; // list of callback arrays
378 int PlayerCount; // player count for MapPlayerExtend
379
380 bool GlobalScope() override { return true; } // it's the global node
381
382public:
383 void ExecuteCallbacks(int32_t iMapZoom) { CallbackArrays.Execute(iMapZoom); }
384
385 friend class C4MCOverlay;
386 friend class C4MCMap;
387 friend class C4MCParser;
388 friend class C4MCCallbackArray;
389};
390
391// file parser for map creator
392
393// parser error
394class C4MCParserErr
395{
396public:
397 std::string Msg; // message string
398
399 C4MCParserErr(C4MCParser *pParser, std::string_view msg); // construct setting error msg
400
401 template<typename... Args>
402 C4MCParserErr(C4MCParser *pParser, std::format_string<Args...> fmt, Args &&...args) // construct setting error msg
403 : C4MCParserErr{pParser, std::format(fmt, std::forward<Args>(args)...)}
404 {
405 }
406
407 void show() const; // log error
408};
409
410// the parser
411class C4MCParser
412{
413private:
414 C4MapCreatorS2 *MapCreator; // map creator parsing into
415 char *Code; // loaded code
416 const char *CPos; // current parser pos in code
417 C4MCTokenType CurrToken; // last token read
418 char CurrTokenIdtf[C4MaxName]; // current token string
419 int32_t CurrTokenVal; // current token value
420 char Filename[C4MaxName]; // filename
421
422 bool AdvanceSpaces(); // advance to next token char; return whether EOF is reached
423 bool GetNextToken(); // get token, store in fields and advance to next; return whether not EOF
424 void ParseTo(C4MCNode *pToNode); // parse stuff into
425 void ParseValue(C4MCNode *pToNode, const char *szFieldName); // Set Field
426
427public:
428 C4MCParser(C4MapCreatorS2 *pMapCreator);
429 ~C4MCParser();
430
431 void Clear(); // clear stuff
432
433 void ParseFile(const char *szFilename, C4Group *pGrp); // load and parse file
434 void Parse(const char *szScript); // load and parse from mem
435
436 friend class C4MCParserErr;
437};
438