载入中...
搜索中...
未找到
TileConfig.cpp
浏览该文件的文档.
1#include "map/TileConfig.h"
3
4#include "common/Module.h"
5#include "data/DataModule.h"
6#include "data/JsonDocument.h"
10#include "graphics/Graphics.h"
11
12#include <Poco/Dynamic/Var.h>
13#include <Poco/JSON/Array.h>
14#include <Poco/JSON/Object.h>
15
16#include <algorithm>
17#include <memory>
18
19namespace eve::map {
20namespace {
21
22float asFloat(const Poco::Dynamic::Var &v, float fallback) {
23 try {
24 if (v.isEmpty()) return fallback;
25 return static_cast<float>(v.convert<double>());
26 } catch (...) {
27 return fallback;
28 }
29}
30
31int asInt(const Poco::Dynamic::Var &v, int fallback) {
32 try {
33 if (v.isEmpty()) return fallback;
34 return v.convert<int>();
35 } catch (...) {
36 return fallback;
37 }
38}
39
40bool asBool(const Poco::Dynamic::Var &v, bool fallback) {
41 try {
42 if (v.isEmpty()) return fallback;
43 return v.convert<bool>();
44 } catch (...) {
45 return fallback;
46 }
47}
48
49std::string asString(const Poco::Dynamic::Var &v) {
50 try {
51 if (v.isEmpty()) return {};
52 return v.convert<std::string>();
53 } catch (...) {
54 return {};
55 }
56}
57
58bool readVec2(Poco::JSON::Object::Ptr o, const char *key, float &a, float &b) {
59 if (!o || !o->has(key)) return false;
60 Poco::JSON::Array::Ptr arr;
61 try {
62 arr = o->getArray(key);
63 } catch (...) {
64 return false;
65 }
66 if (!arr || arr->size() < 2) return false;
67 a = asFloat(arr->get(0), a);
68 b = asFloat(arr->get(1), b);
69 return true;
70}
71
72bool readVec4(Poco::JSON::Object::Ptr o, const char *key, float &a, float &b, float &c, float &d) {
73 if (!o || !o->has(key)) return false;
74 Poco::JSON::Array::Ptr arr;
75 try {
76 arr = o->getArray(key);
77 } catch (...) {
78 return false;
79 }
80 if (!arr || arr->size() < 4) return false;
81 a = asFloat(arr->get(0), a);
82 b = asFloat(arr->get(1), b);
83 c = asFloat(arr->get(2), c);
84 d = asFloat(arr->get(3), d);
85 return true;
86}
87
88int64_t fileModtime(const std::string &path) {
89 auto *fs = eve::ModuleManager::getInstance<eve::filesystem::Filesystem>("Filesystem");
90 if (!fs) fs = eve::filesystem::Filesystem::create();
92 if (!fs->getInfo(path, info)) return -1;
93 return info.modtime;
94}
95
96graphics::Texture *tryLoadTexture(const std::string &path) {
97 if (path.empty()) return nullptr;
98 auto *gfx = eve::ModuleManager::getInstance<eve::graphics::Graphics>("Graphics");
99 if (!gfx) return nullptr;
100 try {
101 graphics::Texture *tex = gfx->newTextureFromFile(path);
102 if (auto *hot = eve::ModuleManager::getInstance<eve::filesystem::HotReload>("HotReload"))
103 hot->bind(path, "texture");
104 return tex;
105 } catch (...) {
106 return nullptr;
107 }
108}
109
110struct TilesetInfo {
111 std::string image;
112 int firstGid = 1;
113 int columns = 1;
114 int tileW = 32;
115 int tileH = 32;
116 int margin = 0;
117 int spacing = 0;
118};
119
120TilesetInfo readTilesetObject(Poco::JSON::Object::Ptr o) {
121 TilesetInfo info;
122 if (!o) return info;
123 if (o->has("image")) info.image = asString(o->get("image"));
124 else if (o->has("texture")) info.image = asString(o->get("texture"));
125 if (o->has("firstgid")) info.firstGid = asInt(o->get("firstgid"), 1);
126 else if (o->has("firstGid")) info.firstGid = asInt(o->get("firstGid"), 1);
127 if (o->has("columns")) info.columns = asInt(o->get("columns"), 1);
128 if (o->has("tilewidth")) info.tileW = asInt(o->get("tilewidth"), 32);
129 else if (o->has("tileWidth")) info.tileW = asInt(o->get("tileWidth"), 32);
130 if (o->has("tileheight")) info.tileH = asInt(o->get("tileheight"), 32);
131 else if (o->has("tileHeight")) info.tileH = asInt(o->get("tileHeight"), 32);
132 if (o->has("margin")) info.margin = asInt(o->get("margin"), 0);
133 if (o->has("spacing")) info.spacing = asInt(o->get("spacing"), 0);
134 if (info.columns <= 0 && o->has("imagewidth") && info.tileW > 0) {
135 const int iw = asInt(o->get("imagewidth"), 0);
136 if (iw > 0) info.columns = std::max(1, (iw - info.margin) / (info.tileW + info.spacing));
137 }
138 return info;
139}
140
141void applyTileset(TileLayer *layer, const TilesetInfo &info) {
142 if (!layer) return;
143 layer->setTilesetTileSize(info.tileW, info.tileH);
144 graphics::Texture *tex = tryLoadTexture(info.image);
145 layer->resource()->texturePath = info.image;
146 layer->setTileset(tex, info.firstGid, info.columns, info.margin, info.spacing);
147}
148
149bool decodeLayerData(Poco::JSON::Object::Ptr layerObj, size_t expectedCount,
150 std::vector<uint32_t> &out, std::string *error) {
151 if (!layerObj || !layerObj->has("data")) {
152 if (error) *error = "missing data";
153 return false;
154 }
155
156 try {
157 if (layerObj->isArray("data")) {
158 auto arr = layerObj->getArray("data");
159 if (!arr) {
160 if (error) *error = "invalid data array";
161 return false;
162 }
163 out.resize(arr->size());
164 for (size_t i = 0; i < arr->size(); ++i) out[i] = uint32_t(asInt(arr->get(i), 0));
165 if (expectedCount > 0 && out.size() != expectedCount) {
166 if (error) *error = "gid count mismatch";
167 return false;
168 }
169 return true;
170 }
171 } catch (...) {
172 }
173
174 std::string encoding = layerObj->has("encoding") ? asString(layerObj->get("encoding")) : "";
175 std::string compression =
176 layerObj->has("compression") ? asString(layerObj->get("compression")) : "";
177 if (encoding != "base64") {
178 if (error) *error = "unsupported encoding";
179 return false;
180 }
181 if (compression == "zstd") {
182 if (error) *error = "zstd not supported; export as zlib";
183 return false;
184 }
185
186 const std::string b64 = asString(layerObj->get("data"));
187 size_t decodedLen = 0;
188 std::unique_ptr<char[]> decoded(eve::data::decode("base64", b64.data(), b64.size(), decodedLen));
189 if (!decoded) {
190 if (error) *error = "base64 decode failed";
191 return false;
192 }
193
194 const char *bytes = decoded.get();
195 size_t nbytes = decodedLen;
196 std::unique_ptr<char[]> inflated;
197 if (!compression.empty()) {
198 if (compression != "zlib" && compression != "gzip") {
199 if (error) *error = "unsupported compression";
200 return false;
201 }
202 size_t rawsize = expectedCount * 4;
203 try {
204 inflated.reset(eve::data::decompress(compression, bytes, nbytes, rawsize));
205 } catch (...) {
206 if (error) *error = "decompress failed";
207 return false;
208 }
209 if (!inflated) {
210 if (error) *error = "decompress failed";
211 return false;
212 }
213 bytes = inflated.get();
214 nbytes = rawsize;
215 }
216
217 if (expectedCount > 0 && nbytes != expectedCount * 4) {
218 if (error) *error = "gid byte length mismatch";
219 return false;
220 }
221 const size_t count = nbytes / 4;
222 out.resize(count);
223 for (size_t i = 0; i < count; ++i) {
224 const unsigned char *p = reinterpret_cast<const unsigned char *>(bytes) + i * 4;
225 out[i] = uint32_t(p[0]) | (uint32_t(p[1]) << 8) | (uint32_t(p[2]) << 16) |
226 (uint32_t(p[3]) << 24);
227 }
228 return true;
229}
230
231void applyLayerDraw(TileLayer *layer, Poco::JSON::Object::Ptr layerObj) {
232 if (!layer || !layerObj) return;
233 if (layerObj->has("visible"))
234 layer->setVisible(asBool(layerObj->get("visible"), true));
235 if (layerObj->has("layer"))
236 layer->setLayer(asInt(layerObj->get("layer"), layer->getLayer()));
237 else if (layerObj->has("id"))
238 layer->setLayer(asInt(layerObj->get("id"), layer->getLayer()));
239 float r = 1, g = 1, b = 1, a = 1;
240 if (readVec4(layerObj, "tint", r, g, b, a))
241 layer->setTint(r, g, b, a);
242 else if (layerObj->has("opacity")) {
243 a = asFloat(layerObj->get("opacity"), 1.f);
244 layer->setTint(1.f, 1.f, 1.f, a);
245 }
246 float ox = layer->getX(), oy = layer->getY();
247 if (layerObj->has("x")) ox = asFloat(layerObj->get("x"), ox);
248 if (layerObj->has("offsetx")) ox += asFloat(layerObj->get("offsetx"), 0.f);
249 if (layerObj->has("y")) oy = asFloat(layerObj->get("y"), oy);
250 if (layerObj->has("offsety")) oy += asFloat(layerObj->get("offsety"), 0.f);
251 if (layerObj->has("x") || layerObj->has("y") || layerObj->has("offsetx") ||
252 layerObj->has("offsety"))
253 layer->setOrigin(ox, oy);
254}
255
256bool isTileLayerObject(Poco::JSON::Object::Ptr o) {
257 if (!o) return false;
258 if (o->has("type")) {
259 const std::string t = asString(o->get("type"));
260 if (!t.empty() && t != "tilelayer") return false;
261 }
262 return o->has("data");
263}
264
265bool isObjectGroup(Poco::JSON::Object::Ptr o) {
266 if (!o) return false;
267 if (!o->has("type")) return false;
268 return asString(o->get("type")) == "objectgroup";
269}
270
271bool parseOrientation(Poco::JSON::Object::Ptr root, TileLayer::Config *cfg, std::string *error) {
272 if (!root || !cfg) return false;
273 if (!root->has("orientation")) return true;
274 const std::string o = asString(root->get("orientation"));
275 if (o.empty() || o == "orthogonal")
276 cfg->orientation = MapOrientation::Orthogonal;
277 else if (o == "isometric")
278 cfg->orientation = MapOrientation::Isometric;
279 else if (o == "staggered")
280 cfg->orientation = MapOrientation::Staggered;
281 else if (o == "hexagonal")
282 cfg->orientation = MapOrientation::Hexagonal;
283 else {
284 if (error) *error = "unknown orientation: " + o;
285 return false;
286 }
287 if (root->has("staggeraxis")) {
288 const std::string a = asString(root->get("staggeraxis"));
289 cfg->staggerAxis = (a == "x") ? StaggerAxis::X : StaggerAxis::Y;
290 }
291 if (root->has("staggerindex")) {
292 const std::string i = asString(root->get("staggerindex"));
293 cfg->staggerIndex = (i == "even") ? StaggerIndex::Even : StaggerIndex::Odd;
294 }
295 if (root->has("hexsidelength"))
296 cfg->hexSideLength = asFloat(root->get("hexsidelength"), 0.f);
297 return true;
298}
299
300bool applyMapGlobals(TileLayer *layer, Poco::JSON::Object::Ptr root, std::string *error) {
301 if (!layer || !root) return false;
302
303 int mapW = layer->getMapWidth();
304 int mapH = layer->getMapHeight();
305 float tileW = layer->getTileWidth();
306 float tileH = layer->getTileHeight();
307
308 if (root->has("width")) mapW = asInt(root->get("width"), mapW);
309 if (root->has("height")) mapH = asInt(root->get("height"), mapH);
310 if (root->has("tilewidth")) tileW = asFloat(root->get("tilewidth"), tileW);
311 else if (root->has("tileWidth")) tileW = asFloat(root->get("tileWidth"), tileW);
312 if (root->has("tileheight")) tileH = asFloat(root->get("tileheight"), tileH);
313 else if (root->has("tileHeight")) tileH = asFloat(root->get("tileHeight"), tileH);
314
315 if (mapW != layer->getMapWidth() || mapH != layer->getMapHeight()) layer->resize(mapW, mapH);
317
318 if (!parseOrientation(root, &(*layer->config()), error)) return false;
319
320 float ox = layer->getX(), oy = layer->getY();
321 if (readVec2(root, "origin", ox, oy))
322 layer->setOrigin(ox, oy);
323 else {
324 if (root->has("x")) ox = asFloat(root->get("x"), ox);
325 if (root->has("y")) oy = asFloat(root->get("y"), oy);
326 if (root->has("x") || root->has("y")) layer->setOrigin(ox, oy);
327 }
328
329 if (root->has("layer"))
330 layer->setLayer(asInt(root->get("layer"), layer->getLayer()));
331 if (root->has("visible"))
332 layer->setVisible(asBool(root->get("visible"), layer->isVisible()));
333 if (root->has("autoReload"))
334 layer->resource()->autoReload = asBool(root->get("autoReload"), true);
335
336 float r = 1, g = 1, b = 1, a = 1;
337 if (readVec4(root, "tint", r, g, b, a)) layer->setTint(r, g, b, a);
338
339 if (root->has("tileset") && !root->isArray("tileset")) {
340 try {
341 auto o = root->getObject("tileset");
342 if (o && !o->has("source")) applyTileset(layer, readTilesetObject(o));
343 } catch (...) {
344 }
345 } else if (root->has("tilesets")) {
346 try {
347 auto arr = root->getArray("tilesets");
348 if (arr) {
349 for (size_t i = 0; i < arr->size(); ++i) {
350 try {
351 auto o = arr->getObject(i);
352 if (o && o->has("source")) continue;
353 applyTileset(layer, readTilesetObject(o));
354 break;
355 } catch (...) {
356 }
357 }
358 }
359 } catch (...) {
360 }
361 } else if (root->has("image") || root->has("texture")) {
362 applyTileset(layer, readTilesetObject(root));
363 }
364 return true;
365}
366
367bool applyFlatLayerData(TileLayer *layer, Poco::JSON::Object::Ptr root, std::string *error) {
368 const int mapW = layer->getMapWidth();
369 const int mapH = layer->getMapHeight();
370 const size_t need = size_t(std::max(0, mapW) * std::max(0, mapH));
371 if (need == 0) {
372 if (error) *error = "empty map size";
373 return false;
374 }
375 std::vector<uint32_t> data;
376 if (!decodeLayerData(root, need, data, error)) return false;
377 auto &gids = layer->tiles()->gids;
378 gids.assign(need, 0u);
379 const size_t n = std::min(need, data.size());
380 for (size_t i = 0; i < n; ++i) gids[i] = data[i];
381 return true;
382}
383
384bool applyOneLayerObject(TileLayer *layer, Poco::JSON::Object::Ptr layerObj, int mapW, int mapH,
385 std::string *error) {
386 if (!layer || !layerObj) return false;
387 int w = mapW, h = mapH;
388 if (layerObj->has("width")) w = asInt(layerObj->get("width"), w);
389 if (layerObj->has("height")) h = asInt(layerObj->get("height"), h);
390 if (w != layer->getMapWidth() || h != layer->getMapHeight()) layer->resize(w, h);
391
392 const size_t need =
393 size_t(std::max(0, layer->getMapWidth()) * std::max(0, layer->getMapHeight()));
394 std::vector<uint32_t> data;
395 if (!decodeLayerData(layerObj, need, data, error)) return false;
396 auto &gids = layer->tiles()->gids;
397 gids.assign(need, 0u);
398 const size_t n = std::min(need, data.size());
399 for (size_t i = 0; i < n; ++i) gids[i] = data[i];
400
401 applyLayerDraw(layer, layerObj);
402 return true;
403}
404
405void parseObjectGroup(Poco::JSON::Object::Ptr group, std::vector<MapObject> &out) {
406 if (!group || !group->has("objects")) return;
407 Poco::JSON::Array::Ptr arr;
408 try {
409 arr = group->getArray("objects");
410 } catch (...) {
411 return;
412 }
413 if (!arr) return;
414 for (size_t i = 0; i < arr->size(); ++i) {
415 Poco::JSON::Object::Ptr o;
416 try {
417 o = arr->getObject(i);
418 } catch (...) {
419 continue;
420 }
421 if (!o) continue;
422 MapObject mo;
423 if (o->has("name")) mo.name = asString(o->get("name"));
424 if (o->has("type")) mo.type = asString(o->get("type"));
425 else if (o->has("class")) mo.type = asString(o->get("class"));
426 mo.x = o->has("x") ? asFloat(o->get("x"), 0.f) : 0.f;
427 mo.y = o->has("y") ? asFloat(o->get("y"), 0.f) : 0.f;
428 mo.width = o->has("width") ? asFloat(o->get("width"), 0.f) : 0.f;
429 mo.height = o->has("height") ? asFloat(o->get("height"), 0.f) : 0.f;
430 if (o->has("gid")) mo.gid = uint32_t(asInt(o->get("gid"), 0));
431 out.push_back(std::move(mo));
432 }
433}
434
435void abandonLayers(std::vector<TileLayer *> &layers) {
436 for (TileLayer *layer : layers) {
437 if (!layer) continue;
438 layer->clear();
439 layer->setVisible(false);
440 }
441 layers.clear();
442}
443
444TilesetInfo readDefaultTileset(Poco::JSON::Object::Ptr root) {
445 TilesetInfo defaultTs;
446 if (root->has("tilesets")) {
447 try {
448 auto arr = root->getArray("tilesets");
449 if (arr) {
450 for (size_t i = 0; i < arr->size(); ++i) {
451 try {
452 auto o = arr->getObject(i);
453 if (o && o->has("source")) continue;
454 return readTilesetObject(o);
455 } catch (...) {
456 }
457 }
458 }
459 } catch (...) {
460 }
461 } else if (root->has("tileset") && !root->isArray("tileset")) {
462 try {
463 auto o = root->getObject("tileset");
464 if (o && !o->has("source")) return readTilesetObject(o);
465 } catch (...) {
466 }
467 } else if (root->has("image") || root->has("texture")) {
468 return readTilesetObject(root);
469 }
470 return defaultTs;
471}
472
473std::vector<TileLayer *> loadMapObject(Poco::JSON::Object::Ptr root, const std::string &path,
475 std::vector<MapObject> *objects, std::string *error) {
476 std::vector<TileLayer *> out;
477 if (!root) {
478 if (error) *error = "config root must be object";
479 return out;
480 }
481
482 int mapW = 10, mapH = 10;
483 float tileW = 32.f, tileH = 32.f;
484 if (root->has("width")) mapW = asInt(root->get("width"), mapW);
485 if (root->has("height")) mapH = asInt(root->get("height"), mapH);
486 if (root->has("tilewidth")) tileW = asFloat(root->get("tilewidth"), tileW);
487 else if (root->has("tileWidth")) tileW = asFloat(root->get("tileWidth"), tileW);
488 if (root->has("tileheight")) tileH = asFloat(root->get("tileheight"), tileH);
489 else if (root->has("tileHeight")) tileH = asFloat(root->get("tileHeight"), tileH);
490
491 // Validate orientation early on a temp config.
492 TileLayer::Config orientCheck;
493 if (!parseOrientation(root, &orientCheck, error)) return out;
494
495 TilesetInfo defaultTs = readDefaultTileset(root);
496 if (objects) objects->clear();
497
498 auto bindResource = [&](TileLayer *layer) {
499 auto res = layer->resource();
500 res->path = path;
501 if (!path.empty()) {
502 res->modtime = fileModtime(path);
503 if (fs) {
504 fs->watch(path);
505 if (auto *hot =
506 eve::ModuleManager::getInstance<eve::filesystem::HotReload>("HotReload"))
507 hot->bind(path, "tilemap");
508 }
509 }
510 if (root->has("autoReload"))
511 res->autoReload = asBool(root->get("autoReload"), true);
512 };
513
514 if (root->has("layers")) {
515 try {
516 auto arr = root->getArray("layers");
517 if (arr) {
518 int sort = 0;
519 for (size_t i = 0; i < arr->size(); ++i) {
520 Poco::JSON::Object::Ptr lo;
521 try {
522 lo = arr->getObject(i);
523 } catch (...) {
524 continue;
525 }
526 if (objects && isObjectGroup(lo)) {
527 parseObjectGroup(lo, *objects);
528 continue;
529 }
530 if (!isTileLayerObject(lo)) continue;
531 TileLayer *layer = TileLayer::createLayer(mapW, mapH, tileW, tileH);
532 if (!applyMapGlobals(layer, root, error)) {
533 abandonLayers(out);
534 layer->clear();
535 layer->setVisible(false);
536 return {};
537 }
538 applyTileset(layer, defaultTs);
539 if (!applyOneLayerObject(layer, lo, mapW, mapH, error)) {
540 abandonLayers(out);
541 layer->clear();
542 layer->setVisible(false);
543 return {};
544 }
545 if (!lo->has("layer") && !lo->has("id")) layer->setLayer(sort);
546 ++sort;
547 bindResource(layer);
548 out.push_back(layer);
549 }
550 }
551 } catch (...) {
552 }
553 if (!out.empty()) return out;
554 }
555
556 TileLayer *layer = TileLayer::createLayer(mapW, mapH, tileW, tileH);
557 if (!applyMapGlobals(layer, root, error)) {
558 layer->clear();
559 layer->setVisible(false);
560 return {};
561 }
562 applyTileset(layer, defaultTs);
563 if (root->has("data")) {
564 if (!applyFlatLayerData(layer, root, error)) {
565 layer->clear();
566 layer->setVisible(false);
567 return {};
568 }
569 }
570 bindResource(layer);
571 out.push_back(layer);
572 return out;
573}
574
575} // namespace
576
578 if (!layer || !doc || !doc->isObject()) return false;
579 auto root = doc->object();
580 if (!root) return false;
581
582 std::string err;
583 if (!applyMapGlobals(layer, root, &err)) return false;
584
585 if (root->has("layers")) {
586 try {
587 auto arr = root->getArray("layers");
588 if (arr) {
589 for (size_t i = 0; i < arr->size(); ++i) {
590 Poco::JSON::Object::Ptr lo;
591 try {
592 lo = arr->getObject(i);
593 } catch (...) {
594 continue;
595 }
596 if (!isTileLayerObject(lo)) continue;
597 return applyOneLayerObject(layer, lo, layer->getMapWidth(),
598 layer->getMapHeight(), nullptr);
599 }
600 }
601 } catch (...) {
602 }
603 }
604
605 if (root->has("data")) return applyFlatLayerData(layer, root, nullptr);
606 return true;
607}
608
609bool applyConfigText(TileLayer *layer, const std::string &json, std::string *error) {
610 auto *dm = eve::data::DataModule::create();
611 std::string err;
612 std::unique_ptr<data::JsonDocument> doc(dm->decodeJson(json, &err));
613 if (!doc) {
614 if (error) *error = err.empty() ? "invalid json" : err;
615 return false;
616 }
617 if (!doc->isObject()) {
618 if (error) *error = "config root must be object";
619 return false;
620 }
621 auto root = doc->object();
622 if (!root) {
623 if (error) *error = "config root must be object";
624 return false;
625 }
626 if (!applyMapGlobals(layer, root, error)) return false;
627 if (root->has("layers")) {
628 try {
629 auto arr = root->getArray("layers");
630 if (arr) {
631 for (size_t i = 0; i < arr->size(); ++i) {
632 Poco::JSON::Object::Ptr lo;
633 try {
634 lo = arr->getObject(i);
635 } catch (...) {
636 continue;
637 }
638 if (!isTileLayerObject(lo)) continue;
639 return applyOneLayerObject(layer, lo, layer->getMapWidth(),
641 }
642 }
643 } catch (...) {
644 }
645 }
646 if (root->has("data")) return applyFlatLayerData(layer, root, error);
647 return true;
648}
649
650bool loadConfigFile(TileLayer *layer, const std::string &path, std::string *error) {
651 if (!layer || path.empty()) {
652 if (error) *error = "empty path";
653 return false;
654 }
655 auto *fs = eve::ModuleManager::getInstance<eve::filesystem::Filesystem>("Filesystem");
656 if (!fs) fs = eve::filesystem::Filesystem::create();
657
658 std::unique_ptr<eve::filesystem::FileData> data;
659 try {
660 data.reset(fs->read(path));
661 } catch (...) {
662 if (error) *error = "read failed: " + path;
663 return false;
664 }
665 if (!data || data->getSize() == 0) {
666 if (error) *error = "empty file: " + path;
667 return false;
668 }
669
670 std::string text(static_cast<const char *>(data->getData()), data->getSize());
671 if (!applyConfigText(layer, text, error)) return false;
672
673 auto res = layer->resource();
674 res->path = path;
675 res->modtime = fileModtime(path);
676 fs->watch(path);
677 if (auto *hot = eve::ModuleManager::getInstance<eve::filesystem::HotReload>("HotReload"))
678 hot->bind(path, "tilemap");
679 return true;
680}
681
682bool reloadConfigFile(TileLayer *layer, std::string *error) {
683 if (!layer) return false;
684 const std::string &path = layer->resource()->path;
685 if (path.empty()) {
686 if (error) *error = "no config path";
687 return false;
688 }
689 return loadConfigFile(layer, path, error);
690}
691
692std::vector<TileLayer *> loadMapText(const std::string &json, std::vector<MapObject> *objects,
693 std::string *error) {
694 auto *dm = eve::data::DataModule::create();
695 std::string err;
696 std::unique_ptr<data::JsonDocument> doc(dm->decodeJson(json, &err));
697 if (!doc || !doc->isObject()) {
698 if (error) *error = err.empty() ? "invalid json" : err;
699 return {};
700 }
701 return loadMapObject(doc->object(), {}, nullptr, objects, error);
702}
703
704std::vector<TileLayer *> loadMapFile(const std::string &path, std::vector<MapObject> *objects,
705 std::string *error) {
706 if (path.empty()) {
707 if (error) *error = "empty path";
708 return {};
709 }
710
711 auto *fs = eve::ModuleManager::getInstance<eve::filesystem::Filesystem>("Filesystem");
712 if (!fs) fs = eve::filesystem::Filesystem::create();
713
714 std::unique_ptr<eve::filesystem::FileData> data;
715 try {
716 data.reset(fs->read(path));
717 } catch (...) {
718 if (error) *error = "read failed: " + path;
719 return {};
720 }
721 if (!data || data->getSize() == 0) {
722 if (error) *error = "empty file: " + path;
723 return {};
724 }
725
726 std::string text(static_cast<const char *>(data->getData()), data->getSize());
727 auto *dm = eve::data::DataModule::create();
728 std::string err;
729 std::unique_ptr<data::JsonDocument> doc(dm->decodeJson(text, &err));
730 if (!doc || !doc->isObject()) {
731 if (error) *error = err.empty() ? "invalid json" : err;
732 return {};
733 }
734 return loadMapObject(doc->object(), path, fs, objects, error);
735}
736
737std::vector<TileLayer *> loadMapFile(const std::string &path, std::string *error) {
738 return loadMapFile(path, nullptr, error);
739}
740
741} // namespace eve::map
float u
Definition Grass.cpp:234
glm::vec3 n
Definition Grass.cpp:64
int h
int w
std::string error
const FusedGroup & group
uint32_t a
uint32_t b
uint32_t c
TileLayer * layer
glm::vec4 p[6]
Light2D::Data * data
int d
int v
int spacing
int firstGid
int columns
int tileW
int margin
int tileH
std::string image
Thin RAII wrapper over a Poco JSON value (object/array/scalar).
bool isObject() const
Root type predicates.
Poco::JSON::Object::Ptr object()
Root as JSON object/array (may be null when the type differs).
virtual FileData * read(std::string filename, int64_t size=File::ALL) const =0
Reads data from a file.
virtual bool watch(std::string path)=0
Watch a virtual or absolute OS path (file or directory). File watches monitor the parent directory an...
ECS tile layer entity. Script mutates tile GIDs / tileset / draw; TileRenderSystem batch-draws atlas ...
Definition TileLayer.h:30
void clear()
Clears the layer to empty.
Definition TileLayer.cpp:69
float getTileWidth()
Tile size in pixels.
Definition TileLayer.cpp:35
void setOrigin(float x, float y)
World origin of the layer (pixels).
Definition TileLayer.cpp:25
void setTileset(graphics::Texture *texture, int firstGid, int columns, int margin=0, int spacing=0)
Binds the atlas texture and tile table.
Definition TileLayer.cpp:71
void resize(int mapW, int mapH)
Resizes the tile grid (existing GIDs preserved where possible).
Definition TileLayer.cpp:44
void setTilesetTileSize(int tileW, int tileH)
Tile size in pixels of the bound tileset.
Definition TileLayer.cpp:86
void setLayer(int layer)
Draw order layer / visibility / tint.
Definition TileLayer.cpp:99
static TileLayer * createLayer(int mapW, int mapH, float tileW=32.f, float tileH=32.f)
Definition TileLayer.cpp:12
void setTileSize(float tileW, float tileH)
Definition TileLayer.cpp:38
void setVisible(bool visible)
int getMapWidth()
Map dimensions in tiles.
Definition TileLayer.cpp:33
void setTint(float r, float g, float b, float a=1.f)
char * decompress(CompressedData *data, size_t &decompressedsize)
Decompresses existing compressed data into raw bytes.
char * decode(std::string format, const char *src, size_t srclen, size_t &dstlen)
放置世界:格子占用(多通道)+ 地形语义 + 已放置建筑实例。 行为由 PlacementSystem 提供;本类暴露便于脚本绑定的薄封装方法。 坐标换算统一走 eve::grid(支持 rectang...
bool reloadConfigFile(TileLayer *layer, std::string *error)
std::vector< TileLayer * > loadMapText(const std::string &json, std::vector< MapObject > *objects, std::string *error)
Parse map JSON text (no filesystem). Same semantics as loadMapFile.
bool applyConfigDocument(TileLayer *layer, data::JsonDocument *doc)
Apply map/layer JSON onto an existing TileLayer (Config + Tiles + Tileset + Draw)....
bool applyConfigText(TileLayer *layer, const std::string &json, std::string *error)
Parse JSON text and apply onto one layer.
StaggerIndex
Which rows/columns are offset by half a tile.
std::vector< TileLayer * > loadMapFile(const std::string &path, std::vector< MapObject > *objects, std::string *error)
StaggerAxis
Stagger axis for staggered/hex maps (shift every other row/column).
bool loadConfigFile(TileLayer *layer, const std::string &path, std::string *error)
Read path via Filesystem, apply onto layer, bind Resource.path + modtime for hot reload....