载入中...
搜索中...
未找到
PlacementWorld.cpp
浏览该文件的文档.
2#include "building/Ghost.h"
4#include "data/DataModule.h"
5#include "data/JsonDocument.h"
7#include "map/TileLayer.h"
8
9#include <Poco/JSON/Object.h>
10
11#include <algorithm>
12#include <cmath>
13#include <memory>
14
15namespace eve::building {
16
17PlacementWorld::PlacementWorld(int width, int height, float cellSize) {
18 width_ = width > 0 ? width : 1;
19 height_ = height > 0 ? height : 1;
20 grid_.cellW = cellSize > 0.f ? cellSize : 1.f;
21 grid_.cellH = grid_.cellW;
22 occupancy_.assign(size_t(width_) * size_t(height_), 0);
23 terrain_.assign(size_t(width_) * size_t(height_), 0);
24 terrainOverrides_.assign(size_t(width_) * size_t(height_), 0);
26}
27
28void PlacementWorld::destroy() { delete this; }
29
31 if (s > 0.f) {
32 grid_.cellW = s;
33 grid_.cellH = s;
34 }
35}
36
37void PlacementWorld::setOrigin(float x, float y) {
38 grid_.originX = x;
39 grid_.originY = y;
40}
41
42void PlacementWorld::setExtra(const std::string &key, const std::string &value) {
43 extra_[key] = value;
44}
45
46std::string PlacementWorld::getExtra(const std::string &key, const std::string &fallback) const {
47 auto it = extra_.find(key);
48 return it == extra_.end() ? fallback : it->second;
49}
50
51// ---- Grid 配置 ----
52
56
59}
60
61void PlacementWorld::setGridPlane(const std::string &plane) {
63}
64
67}
68
69void PlacementWorld::setCellGap(float gapX, float gapY) {
70 grid_.cellGapX = gapX;
71 grid_.cellGapY = gapY;
72}
73
75
76void PlacementWorld::setStagger(const std::string &axis, const std::string &index) {
77 grid_.staggerAxis = (axis == "x" || axis == "X") ? grid::StaggerAxis::X
79 grid_.staggerIndex = (index == "even" || index == "Even") ? grid::StaggerIndex::Even
81}
82
84 if (!layer) return;
85 const auto &c = *layer->config();
86 switch (c.orientation) {
89 break;
92 break;
95 break;
98 break;
99 }
100 grid_.cellW = c.tileW;
101 grid_.cellH = c.tileH;
102 grid_.originX = c.originX;
103 grid_.originY = c.originY;
108 grid_.hexSideLength = c.hexSideLength;
109}
110
111// ---- 坐标换算 ----
112
113int PlacementWorld::worldToCellX(float worldX) const {
114 int cx = 0, cy = 0;
115 grid::worldToCell(grid_, worldX, 0.f, cx, cy, width_, height_);
116 return cx;
117}
118
119int PlacementWorld::worldToCellY(float worldY) const {
120 int cx = 0, cy = 0;
121 grid::worldToCell(grid_, 0.f, worldY, cx, cy, width_, height_);
122 return cy;
123}
124
125float PlacementWorld::cellToWorldX(int cellX) const {
126 float px = 0.f, py = 0.f;
127 grid::cellToWorld(grid_, cellX, 0, px, py);
128 return px;
129}
130
131float PlacementWorld::cellToWorldY(int cellY) const {
132 float px = 0.f, py = 0.f;
133 grid::cellToWorld(grid_, 0, cellY, px, py);
134 return py;
135}
136
137void PlacementWorld::cellToWorldPlane(int cellX, int cellY, float &px, float &py) const {
138 grid::cellToWorld(grid_, cellX, cellY, px, py);
139}
140
141void PlacementWorld::cellToWorld3D(int cellX, int cellY, float elevation, float &worldX,
142 float &worldY, float &worldZ) const {
143 float px = 0.f, py = 0.f;
144 grid::cellToWorld(grid_, cellX, cellY, px, py);
145 if (grid_.plane == grid::GridPlane::XZ) {
146 worldX = px;
147 worldY = elevation;
148 worldZ = py;
149 } else {
150 worldX = px;
151 worldY = py;
152 worldZ = elevation;
153 }
154}
155
156float PlacementWorld::cellToWorld3DX(int cellX, int cellY, float elevation) const {
157 float wx = 0.f, wy = 0.f, wz = 0.f;
158 cellToWorld3D(cellX, cellY, elevation, wx, wy, wz);
159 return wx;
160}
161
162float PlacementWorld::cellToWorld3DY(int cellX, int cellY, float elevation) const {
163 float wx = 0.f, wy = 0.f, wz = 0.f;
164 cellToWorld3D(cellX, cellY, elevation, wx, wy, wz);
165 return wy;
166}
167
168float PlacementWorld::cellToWorld3DZ(int cellX, int cellY, float elevation) const {
169 float wx = 0.f, wy = 0.f, wz = 0.f;
170 cellToWorld3D(cellX, cellY, elevation, wx, wy, wz);
171 return wz;
172}
173
174void PlacementWorld::worldToCell3D(float worldX, float worldY, float worldZ, int &cellX,
175 int &cellY) const {
176 const float px = worldX;
177 const float py = (grid_.plane == grid::GridPlane::XZ) ? worldZ : worldY;
178 grid::worldToCell(grid_, px, py, cellX, cellY, width_, height_);
179}
180
181int PlacementWorld::worldToCell3DX(float worldX, float worldY, float worldZ) const {
182 int cx = 0, cy = 0;
183 worldToCell3D(worldX, worldY, worldZ, cx, cy);
184 return cx;
185}
186
187int PlacementWorld::worldToCell3DY(float worldX, float worldY, float worldZ) const {
188 int cx = 0, cy = 0;
189 worldToCell3D(worldX, worldY, worldZ, cx, cy);
190 return cy;
191}
192
193// ---- 地形 ----
194
196 std::fill(terrain_.begin(), terrain_.end(), semantic);
197 std::fill(terrainOverrides_.begin(), terrainOverrides_.end(), 1);
198}
199
200void PlacementWorld::setTerrain(int cellX, int cellY, int semantic) {
201 if (!inBounds(cellX, cellY)) return;
202 const size_t idx = size_t(cellY) * size_t(width_) + size_t(cellX);
203 terrain_[idx] = semantic;
204 terrainOverrides_[idx] = 1;
205}
206
207int PlacementWorld::getTerrain(int cellX, int cellY) const {
208 if (!inBounds(cellX, cellY)) return 0;
209 const size_t idx = size_t(cellY) * size_t(width_) + size_t(cellX);
210 if (terrainOverrides_[idx]) return terrain_[idx];
211 if (terrainBound_) return terrainFromGid(cellX, cellY);
212 return terrain_[idx];
213}
214
215int PlacementWorld::terrainFromGid(int cellX, int cellY) const {
216 if (!tileLayer_) return 0;
217 const int gid = tileLayer_->getTile(cellX, cellY);
218 auto it = terrainGidMap_.find(gid);
219 return it == terrainGidMap_.end() ? 0 : it->second;
220}
221
222bool PlacementWorld::inBounds(int cellX, int cellY) const {
223 return cellX >= 0 && cellY >= 0 && cellX < width_ && cellY < height_;
224}
225
226// ---- Tilemap 绑定 ----
227
229 tileLayer_ = layer;
230 terrainBound_ = (layer != nullptr);
232 // 绑定后地形由 GID 懒解析;清掉旧手动地形,避免旧值泄漏。
233 std::fill(terrain_.begin(), terrain_.end(), 0);
234 std::fill(terrainOverrides_.begin(), terrainOverrides_.end(), 0);
235}
236
238 tileLayer_ = nullptr;
239 terrainBound_ = false;
240 std::fill(terrain_.begin(), terrain_.end(), 0);
241 std::fill(terrainOverrides_.begin(), terrainOverrides_.end(), 0);
242}
243
244void PlacementWorld::setTerrainGidMapJson(const std::string &json) {
245 auto *dm = eve::data::DataModule::create();
246 std::string err;
247 std::unique_ptr<data::JsonDocument> doc(dm->decodeJson(json, &err));
248 if (!doc || !doc->isObject()) return;
249 auto obj = doc->object();
250 if (!obj) return;
251 std::vector<std::string> names;
252 obj->getNames(names);
253 for (const auto &n : names) {
254 try {
255 const int gid = std::atoi(n.c_str());
256 const int semantic = obj->getValue<int>(n);
257 if (gid >= 0) terrainGidMap_[gid] = semantic;
258 } catch (...) {
259 }
260 }
261}
262
264 if (gid >= 0) terrainGidMap_[gid] = semantic;
265}
266
267void PlacementWorld::clearTerrainGidMap() { terrainGidMap_.clear(); }
268
269// ---- 占用查询 ----
270
271std::vector<int> &PlacementWorld::channelOccupancy(const std::string &channel) {
272 if (channel.empty()) return occupancy_;
273 auto &ch = allChannels_[channel];
274 if (ch.size() != occupancy_.size()) ch.assign(occupancy_.size(), 0);
275 return ch;
276}
277
278int PlacementWorld::getOccupant(int cellX, int cellY) const {
279 return getOccupantInChannel(std::string{}, cellX, cellY);
280}
281
282int PlacementWorld::getOccupantInChannel(const std::string &channel, int cellX, int cellY) const {
283 if (!inBounds(cellX, cellY)) return 0;
284 const size_t idx = size_t(cellY) * size_t(width_) + size_t(cellX);
285 if (channel.empty()) return occupancy_[idx];
286 auto it = allChannels_.find(channel);
287 if (it == allChannels_.end() || idx >= it->second.size()) return 0;
288 return it->second[idx];
289}
290
291int PlacementWorld::getAnyOccupant(int cellX, int cellY) const {
292 const int occ = getOccupantInChannel(std::string{}, cellX, cellY);
293 if (occ != 0) return occ;
294 for (const auto &kv : allChannels_) {
295 const int v = getOccupantInChannel(kv.first, cellX, cellY);
296 if (v != 0) return v;
297 }
298 return 0;
299}
300
301bool PlacementWorld::isCellEmpty(int cellX, int cellY) const {
302 return getOccupant(cellX, cellY) == 0;
303}
304
305bool PlacementWorld::isCellEmptyInChannel(const std::string &channel, int cellX, int cellY) const {
306 return getOccupantInChannel(channel, cellX, cellY) == 0;
307}
308
309int PlacementWorld::getBuildingCount() const { return int(instanceOrder_.size()); }
310
311bool PlacementWorld::hasBuilding(int instanceId) const {
312 return buildings_.count(instanceId) > 0;
313}
314
315std::string PlacementWorld::getBuildingId(int instanceId) const {
316 auto it = buildings_.find(instanceId);
317 return it == buildings_.end() ? std::string{} : it->second.buildingId;
318}
319
320int PlacementWorld::getBuildingCellX(int instanceId) const {
321 auto it = buildings_.find(instanceId);
322 return it == buildings_.end() ? 0 : it->second.originCellX;
323}
324
325int PlacementWorld::getBuildingCellY(int instanceId) const {
326 auto it = buildings_.find(instanceId);
327 return it == buildings_.end() ? 0 : it->second.originCellY;
328}
329
330float PlacementWorld::getBuildingWorldX(int instanceId) const {
331 auto it = buildings_.find(instanceId);
332 return it == buildings_.end() ? 0.f : it->second.worldX;
333}
334
335float PlacementWorld::getBuildingWorldY(int instanceId) const {
336 auto it = buildings_.find(instanceId);
337 if (it == buildings_.end()) return 0.f;
338 const PlacedBuilding &pb = it->second;
339 // 真实世界 Y:XZ 平面 = 高度;XY 平面 = 平面第二轴。
340 return grid_.plane == grid::GridPlane::XZ ? pb.elevation : pb.worldY;
341}
342
343float PlacementWorld::getBuildingWorldZ(int instanceId) const {
344 auto it = buildings_.find(instanceId);
345 if (it == buildings_.end()) return 0.f;
346 const PlacedBuilding &pb = it->second;
347 return grid_.plane == grid::GridPlane::XZ ? pb.worldY : pb.elevation;
348}
349
350float PlacementWorld::getBuildingElevation(int instanceId) const {
351 auto it = buildings_.find(instanceId);
352 return it == buildings_.end() ? 0.f : it->second.elevation;
353}
354
355std::string PlacementWorld::getBuildingChannel(int instanceId) const {
356 auto it = buildings_.find(instanceId);
357 return it == buildings_.end() ? std::string{} : it->second.channel;
358}
359
360float PlacementWorld::getBuildingRotation(int instanceId) const {
361 auto it = buildings_.find(instanceId);
362 return it == buildings_.end() ? 0.f : it->second.rotationDeg;
363}
364
365std::string PlacementWorld::getBuildingProp(int instanceId, const std::string &key,
366 const std::string &fallback) const {
367 auto it = buildings_.find(instanceId);
368 return it == buildings_.end() ? fallback : it->second.getProp(key, fallback);
369}
370
371void PlacementWorld::setBuildingProp(int instanceId, const std::string &key,
372 const std::string &value) {
373 auto it = buildings_.find(instanceId);
374 if (it == buildings_.end()) return;
375 it->second.setProp(key, value);
376}
377
378bool PlacementWorld::buildingHasTag(int instanceId, const std::string &tag) const {
379 auto it = buildings_.find(instanceId);
380 return it == buildings_.end() ? false : it->second.hasTag(tag);
381}
382
384 if (index < 0 || index >= int(instanceOrder_.size())) return 0;
385 return instanceOrder_[size_t(index)];
386}
387
388// ---- 便捷操作 ----
389
390bool PlacementWorld::canPlace(const std::string &buildingId, int cellX, int cellY,
391 float rotationDeg) {
392 return PlacementSystem::canPlace(this, buildingId, cellX, cellY, rotationDeg, 0, nullptr);
393}
394
395std::string PlacementWorld::canPlaceReason(const std::string &buildingId, int cellX, int cellY,
396 float rotationDeg) {
397 std::string reason;
398 PlacementSystem::canPlace(this, buildingId, cellX, cellY, rotationDeg, 0, &reason);
399 return reason;
400}
401
402int PlacementWorld::placeAt(const std::string &buildingId, int cellX, int cellY,
403 float rotationDeg) {
404 return PlacementSystem::placeAt(this, buildingId, cellX, cellY, rotationDeg);
405}
406
407int PlacementWorld::placeAtWorld(const std::string &buildingId, float worldX, float worldY,
408 float rotationDeg) {
409 return PlacementSystem::placeAtWorld(this, buildingId, worldX, worldY, rotationDeg);
410}
411
412int PlacementWorld::placeAtWorld3D(const std::string &buildingId, float worldX, float worldY,
413 float worldZ, float rotationDeg) {
414 return PlacementSystem::placeAtWorld3D(this, buildingId, worldX, worldY, worldZ, rotationDeg);
415}
416
418 return PlacementSystem::placeGhost(this, ghost);
419}
420
421bool PlacementWorld::removeBuilding(int instanceId) {
422 return PlacementSystem::removeBuilding(this, instanceId);
423}
424
425bool PlacementWorld::moveBuilding(int instanceId, int cellX, int cellY, float rotationDeg) {
426 return PlacementSystem::moveBuilding(this, instanceId, cellX, cellY, rotationDeg);
427}
428
430
431} // namespace eve::building
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
std::string value
std::string layout
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
std::vector< Colorf > px
uint32_t c
int width
TileLayer * layer
int idx
int v
uint32_t s
Definition Weather.cpp:28
uint32_t semantic
Definition WfcSimple.cpp:15
static int placeAt(PlacementWorld *world, const std::string &buildingId, int cellX, int cellY, float rotationDeg=0.f)
成功返回 instanceId;失败返回 0。
static int placeAtWorld3D(PlacementWorld *world, const std::string &buildingId, float worldX, float worldY, float worldZ, float rotationDeg=0.f)
static void clearBuildings(PlacementWorld *world)
static void ensureBuiltins()
确保内置规则已注册(模块首次使用时自动调用)。
static bool removeBuilding(PlacementWorld *world, int instanceId)
static bool moveBuilding(PlacementWorld *world, int instanceId, int cellX, int cellY, float rotationDeg=-1.f)
static int placeAtWorld(PlacementWorld *world, const std::string &buildingId, float worldX, float worldY, float rotationDeg=0.f)
先吸附再放置;free/自定义 snap 会保留吸附后的世界坐标。
static int placeGhost(PlacementWorld *world, Ghost *ghost)
static bool canPlace(PlacementWorld *world, const std::string &buildingId, int cellX, int cellY, float rotationDeg=0.f, int excludeInstanceId=0, std::string *reason=nullptr)
std::string canPlaceReason(const std::string &buildingId, int cellX, int cellY, float rotationDeg=0.f)
PlacementWorld(int width, int height, float cellSize=32.f)
创建 width×height 的格子世界,cellSize 为像素/格。
bool inBounds(int cellX, int cellY) const
int worldToCellX(float worldX) const
世界像素 ↔ 格子坐标。
void destroy()
释放资源并使其失效。
int getTerrain(int cellX, int cellY) const
std::string getBuildingProp(int instanceId, const std::string &key, const std::string &fallback={}) const
float getBuildingWorldY(int instanceId) const
int getBuildingCellY(int instanceId) const
void setGridFromLayer(map::TileLayer *layer)
float getBuildingWorldX(int instanceId) const
bool moveBuilding(int instanceId, int cellX, int cellY, float rotationDeg=-1.f)
std::string getGridLayoutName() const
float getBuildingElevation(int instanceId) const
int getOccupant(int cellX, int cellY) const
占用查询:格子上建筑实例 / 是否为空。
bool isCellEmptyInChannel(const std::string &channel, int cellX, int cellY) const
已放置建筑实例查询。
bool hasBuilding(int instanceId) const
void setOrigin(float x, float y)
int getBuildingInstanceAt(int index) const
按插入顺序取实例 id。
int getBuildingCellX(int instanceId) const
float cellToWorld3DX(int cellX, int cellY, float elevation) const
bool buildingHasTag(int instanceId, const std::string &tag) const
int placeAt(const std::string &buildingId, int cellX, int cellY, float rotationDeg=0.f)
void setGridLayout(const std::string &layout)
void setExtra(const std::string &key, const std::string &value)
世界级额外属性(键值)。
void setCellGap(float gapX, float gapY)
bool isCellEmpty(int cellX, int cellY) const
void setTerrain(int cellX, int cellY, int semantic)
void setTerrainGid(int gid, int semantic)
bool removeBuilding(int instanceId)
float getBuildingRotation(int instanceId) const
int worldToCell3DX(float worldX, float worldY, float worldZ) const
float cellToWorldX(int cellX) const
int placeAtWorld3D(const std::string &buildingId, float worldX, float worldY, float worldZ, float rotationDeg=0.f)
std::string getGridPlaneName() const
bool canPlace(const std::string &buildingId, int cellX, int cellY, float rotationDeg=0.f)
便捷操作(转发 PlacementSystem):放置 / 移除 / 移动。
float cellToWorldY(int cellY) const
int worldToCellY(float worldY) const
void cellToWorldPlane(int cellX, int cellY, float &px, float &py) const
void setBuildingProp(int instanceId, const std::string &key, const std::string &value)
std::string getBuildingId(int instanceId) const
void setGridPlane(const std::string &plane)
void bindTileLayer(map::TileLayer *layer)
std::string getExtra(const std::string &key, const std::string &fallback={}) const
int worldToCell3DY(float worldX, float worldY, float worldZ) const
void setStagger(const std::string &axis, const std::string &index)
int placeAtWorld(const std::string &buildingId, float worldX, float worldY, float rotationDeg=0.f)
int getAnyOccupant(int cellX, int cellY) const
void worldToCell3D(float worldX, float worldY, float worldZ, int &cellX, int &cellY) const
float cellToWorld3DY(int cellX, int cellY, float elevation) const
std::string getBuildingChannel(int instanceId) const
void setTerrainGidMapJson(const std::string &json)
float getBuildingWorldZ(int instanceId) const
int getOccupantInChannel(const std::string &channel, int cellX, int cellY) const
float cellToWorld3DZ(int cellX, int cellY, float elevation) const
std::vector< int > & channelOccupancy(const std::string &channel)
void fillTerrain(int semantic)
地形语义(占用检查用)。
void cellToWorld3D(int cellX, int cellY, float elevation, float &worldX, float &worldY, float &worldZ) const
ECS tile layer entity. Script mutates tile GIDs / tileset / draw; TileRenderSystem batch-draws atlas ...
Definition TileLayer.h:30
int getTile(int tx, int ty)
Definition TileLayer.cpp:58
建筑放置模块入口:定义 / 放置世界 / 鬼影 / 变更事件的脚本绑定点。 设计文档:docs/dev/建筑放置系统设计.md
Definition Building.cpp:7
void cellToWorld(const GridConfig &cfg, int cx, int cy, float &px, float &py)
void worldToCell(const GridConfig &cfg, float px, float py, int &cx, int &cy, int mapW, int mapH)
已放置的建筑实例。
static const char * layoutName(GridLayout l)
Definition GridConfig.cpp:5
static const char * planeName(GridPlane p)
static GridPlane planeFromName(const std::string &name)
StaggerAxis staggerAxis
Definition GridConfig.h:32
StaggerIndex staggerIndex
Definition GridConfig.h:33
static GridLayout layoutFromName(const std::string &name)