| 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 |
| 69 | class C4MCCallbackArray; |
| 70 | class C4MCCallbackArrayList; |
| 71 | class C4MCNode; |
| 72 | class C4MCOverlay; |
| 73 | class C4MCPoint; |
| 74 | class C4MCMap; |
| 75 | class C4MapCreatorS2; |
| 76 | class C4MCParserErr; |
| 77 | class C4MCParser; |
| 78 | |
| 79 | struct C4MCAlgorithm |
| 80 | { |
| 81 | char Identifier[C4MaxName]; |
| 82 | bool(*Function)(C4MCOverlay *, int32_t, int32_t); |
| 83 | }; |
| 84 | |
| 85 | extern C4MCAlgorithm C4MCAlgoMap[]; |
| 86 | |
| 87 | // node type enum |
| 88 | enum C4MCNodeType { MCN_Node, MCN_Overlay, MCN_Point, MCN_Map }; |
| 89 | |
| 90 | // one token type |
| 91 | enum 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 |
| 112 | class C4MCCallbackArray |
| 113 | { |
| 114 | public: |
| 115 | C4MCCallbackArray(C4AulFunc *pSFunc, C4MapCreatorS2 *pMapCreator); |
| 116 | ~C4MCCallbackArray(); |
| 117 | |
| 118 | protected: |
| 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 | |
| 126 | public: |
| 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 |
| 134 | class C4MCCallbackArrayList |
| 135 | { |
| 136 | public: |
| 137 | C4MCCallbackArrayList() { pFirst = nullptr; } |
| 138 | ~C4MCCallbackArrayList() { Clear(); } |
| 139 | |
| 140 | protected: |
| 141 | C4MCCallbackArray *pFirst; // first array in list |
| 142 | |
| 143 | public: |
| 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 |
| 151 | class C4MCNode |
| 152 | { |
| 153 | public: |
| 154 | C4MCNode *Owner, *Child0, *ChildL, *Prev, *Next; // tree structure |
| 155 | C4MapCreatorS2 *MapCreator; // owning map creator |
| 156 | char Name[C4MaxName]; // name, if named |
| 157 | |
| 158 | public: |
| 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 | |
| 168 | protected: |
| 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 | |
| 199 | public: |
| 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 |
| 209 | enum 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 | |
| 223 | struct 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 | }; |
| 245 | extern C4MCNodeAttr C4MCOvrlMap[]; |
| 246 | |
| 247 | // overlay node |
| 248 | class C4MCOverlay : public C4MCNode |
| 249 | { |
| 250 | public: |
| 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 | |
| 256 | protected: |
| 257 | void Default(); // set default values for default presets |
| 258 | |
| 259 | public: |
| 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 | |
| 293 | public: |
| 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 |
| 301 | class C4MCPoint : public C4MCNode |
| 302 | { |
| 303 | public: |
| 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 | |
| 309 | protected: |
| 310 | void Default(); // set default values for default presets |
| 311 | |
| 312 | public: |
| 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 | |
| 319 | public: |
| 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 |
| 327 | class C4MCMap : public C4MCOverlay |
| 328 | { |
| 329 | public: |
| 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 | |
| 335 | protected: |
| 336 | void Default(); // set default values for default presets |
| 337 | |
| 338 | public: |
| 339 | bool RenderTo(uint8_t *pToBuf, int32_t iPitch); // render to buffer |
| 340 | void SetSize(int32_t iWdt, int32_t iHgt); |
| 341 | |
| 342 | public: |
| 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 |
| 350 | class C4MapCreatorS2 : public C4MCNode |
| 351 | { |
| 352 | public: |
| 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 | |
| 363 | public: |
| 364 | C4MCMap *GetMap(const char *szMapName); // get map by name |
| 365 | |
| 366 | public: |
| 367 | CSurface8 *Render(const char *szMapName); // create map surface |
| 368 | |
| 369 | protected: |
| 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 | |
| 382 | public: |
| 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 |
| 394 | class C4MCParserErr |
| 395 | { |
| 396 | public: |
| 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 |
| 411 | class C4MCParser |
| 412 | { |
| 413 | private: |
| 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 | |
| 427 | public: |
| 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 | |