载入中...
搜索中...
未找到
BuildingDef.cpp
浏览该文件的文档.
2#include "common/Json.h"
3
4#include <algorithm>
5#include <cmath>
6
7namespace eve::building {
8
10
11namespace {
12bool validConvexPolygon(const std::vector<float> &vertices);
13}
14
15bool BuildingDefinition::hasTag(const std::string &tag) const {
16 return std::find(tags.begin(), tags.end(), tag) != tags.end();
17}
18
19std::string BuildingDefinition::getExtra(const std::string &key,
20 const std::string &fallback) const {
21 auto it = extra.find(key);
22 return it == extra.end() ? fallback : it->second;
23}
24
25std::string BuildingDefinition::getVisual2d(const std::string &key,
26 const std::string &fallback) const {
27 auto it = visual2d.find(key);
28 return it == visual2d.end() ? fallback : it->second;
29}
30
31std::string BuildingDefinition::getVisual3d(const std::string &key,
32 const std::string &fallback) const {
33 auto it = visual3d.find(key);
34 return it == visual3d.end() ? fallback : it->second;
35}
36
37int BuildingDefinition::getCost(const std::string &resource, int fallback) const {
38 auto it = cost.find(resource);
39 return it == cost.end() ? fallback : it->second;
40}
41
42bool BuildingDefinition::maskAt(int localX, int localY) const {
43 if (localX < 0 || localY < 0 || localX >= footprintW || localY >= footprintH) return false;
44 if (footprintMask.empty()) return true;
45 const size_t idx = size_t(localY) * size_t(footprintW) + size_t(localX);
46 if (idx >= footprintMask.size()) return false;
47 return footprintMask[idx] != 0;
48}
49
50bool PlacedBuilding::hasTag(const std::string &tag) const {
51 return std::find(tags.begin(), tags.end(), tag) != tags.end();
52}
53
54std::string PlacedBuilding::getProp(const std::string &key, const std::string &fallback) const {
55 auto it = props.find(key);
56 return it == props.end() ? fallback : it->second;
57}
58
59void PlacedBuilding::setProp(const std::string &key, const std::string &value) {
60 props[key] = value;
61}
62
63std::unordered_map<std::string, BuildingDefinition> &BuildingRegistry::table() {
64 static std::unordered_map<std::string, BuildingDefinition> t;
65 return t;
66}
67
69 if (def.id.empty()) return;
70 BuildingDefinition copy = def;
71 if (copy.footprintW <= 0) copy.footprintW = 1;
72 if (copy.footprintH <= 0) copy.footprintH = 1;
73 if (copy.displayName.empty()) copy.displayName = copy.id;
74 if (copy.snapMode.empty()) copy.snapMode = "grid";
75 if (copy.rotationMode.empty()) copy.rotationMode = "cardinal";
76 if (copy.validateRule.empty()) copy.validateRule = "default";
77 if (copy.placementKind != "edge" && copy.placementKind != "corner" &&
78 copy.placementKind != "free")
79 copy.placementKind = "cell";
80 if (!std::isfinite(copy.freeRadiusCells) || copy.freeRadiusCells <= 0.f)
81 copy.freeRadiusCells = 0.25f;
82 if (!std::isfinite(copy.freeFootprintWidthCells) || copy.freeFootprintWidthCells <= 0.f ||
83 !std::isfinite(copy.freeFootprintHeightCells) || copy.freeFootprintHeightCells <= 0.f) {
84 copy.freeFootprintWidthCells = 0.f;
85 copy.freeFootprintHeightCells = 0.f;
86 }
87 if (!copy.freeFootprintVertices.empty() && !validConvexPolygon(copy.freeFootprintVertices))
88 copy.freeFootprintVertices.clear();
89 if (copy.supportMode != "cell_below" && copy.supportMode != "corner_below")
90 copy.supportMode = "none";
91 copy.maxSurfaceSlopeDegrees = std::clamp(copy.maxSurfaceSlopeDegrees, 0.f, 180.f);
92 const size_t expected = size_t(copy.footprintW) * size_t(copy.footprintH);
93 if (!copy.footprintMask.empty() && copy.footprintMask.size() != expected) {
94 copy.footprintMask.clear(); // 长度不匹配时回退实心矩形
95 }
96 table()[copy.id] = std::move(copy);
97}
98
99const BuildingDefinition *BuildingRegistry::find(const std::string &id) {
100 auto &t = table();
101 auto it = t.find(id);
102 return it == t.end() ? nullptr : &it->second;
103}
104
105bool BuildingRegistry::remove(const std::string &id) { return table().erase(id) > 0; }
106
107void BuildingRegistry::clear() { table().clear(); }
108
109int BuildingRegistry::count() { return int(table().size()); }
110
111namespace {
112
113bool validConvexPolygon(const std::vector<float> &vertices) {
114 if (vertices.size() < 6 || vertices.size() % 2 != 0 || vertices.size() > 128) return false;
115 float winding = 0.f;
116 const size_t count = vertices.size() / 2;
117 for (size_t i = 0; i < count; ++i) {
118 const size_t next = (i + 1) % count;
119 const size_t after = (i + 2) % count;
120 const float ax = vertices[next * 2] - vertices[i * 2];
121 const float ay = vertices[next * 2 + 1] - vertices[i * 2 + 1];
122 const float bx = vertices[after * 2] - vertices[next * 2];
123 const float by = vertices[after * 2 + 1] - vertices[next * 2 + 1];
124 if (!std::isfinite(vertices[i * 2]) || !std::isfinite(vertices[i * 2 + 1])) return false;
125 const float cross = ax * by - ay * bx;
126 if (std::fabs(cross) <= 1e-5f) continue;
127 if (winding == 0.f)
128 winding = cross;
129 else if (cross * winding < 0.f)
130 return false;
131 }
132 return winding != 0.f;
133}
134
135BuildingDefinition parseBuildingObject(Value o) {
136 BuildingDefinition def;
137 if (!o.isObject()) return def;
138 def.id = o.getString("id");
139 def.displayName = o.getString("displayName", def.id);
140 def.category = o.getString("category");
141 def.channel = o.getString("channel");
142 def.renderMode = o.getString("renderMode");
143 def.placementKind = o.getString("placementKind", "cell");
144 def.freeRadiusCells = o.getFloat("freeRadiusCells", 0.25f);
145 def.freeFootprintWidthCells = o.getFloat("freeFootprintWidthCells", 0.f);
146 def.freeFootprintHeightCells = o.getFloat("freeFootprintHeightCells", 0.f);
147 def.freeFootprintVertices = o.getFloatArray("freeFootprintVertices");
148 def.connectionGroup = o.getString("connectionGroup");
149 def.structuralRole = o.getString("structuralRole");
150 def.supportMode = o.getString("supportMode", "none");
151 def.supportTag = o.getString("supportTag");
152 def.footprintW = o.getInt("footprintW", 1);
153 def.footprintH = o.getInt("footprintH", 1);
154 def.snapMode = o.getString("snapMode", "grid");
155 def.rotationMode = o.getString("rotationMode", "cardinal");
156 def.validateRule = o.getString("validateRule", "default");
157 def.maxSurfaceSlopeDegrees = o.getFloat("maxSurfaceSlopeDegrees", 180.f);
158 def.maxSurfaceHeightDelta = o.getFloat("maxSurfaceHeightDelta", -1.f);
159 def.tags = o.getStringArray("tags");
160 def.requireTerrain = o.getIntArray("requireTerrain");
161 def.forbidTerrain = o.getIntArray("forbidTerrain");
162 def.requireAdjacentTag = o.getString("requireAdjacentTag");
163 def.requireAdjacentTerrain = o.getInt("requireAdjacentTerrain", -1);
164 def.cost = o.getIntMap("cost");
165 def.extra = o.getStringMap("extra");
166 def.visual2d = o.getStringMap("visual2d");
167 def.visual3d = o.getStringMap("visual3d");
168
169 const Value mask = o.get("footprintMask");
170 def.footprintMask.reserve(mask.size());
171 for (size_t i = 0; i < mask.size(); ++i)
172 def.footprintMask.push_back(uint8_t(mask.at(i).asInt(0) != 0 ? 1 : 0));
173 return def;
174}
175
176} // namespace
177
178int BuildingRegistry::loadFromJson(const std::string &json, std::string *error) {
179 std::string err;
180 const eve::json::Document doc = eve::json::Document::parse(json, &err);
181 if (!doc.valid()) {
182 if (error) *error = err.empty() ? "invalid json" : err;
183 return 0;
184 }
185
186 const Value root = doc.root();
187 int n = 0;
188 if (root.isArray()) {
189 for (size_t i = 0; i < root.size(); ++i) {
190 BuildingDefinition def = parseBuildingObject(root.at(i));
191 if (def.id.empty()) continue;
192 registerBuilding(def);
193 ++n;
194 }
195 } else if (root.isObject()) {
196 BuildingDefinition def = parseBuildingObject(root);
197 if (!def.id.empty()) {
198 registerBuilding(def);
199 ++n;
200 }
201 }
202 return n;
203}
204
205} // namespace eve::building
double value
int root
Definition AnimSmr.cpp:119
int mask
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
int by
Definition CaveMesh.cpp:114
std::uint32_t key
glm::vec3 n
Definition Grass.cpp:63
const std::string * tag
std::string error
Definition Package.cpp:60
int idx
std::vector< Point > vertices
float t
std::string resource
std::uint32_t count
float size
Definition TreeMesh.cpp:156
static void clear()
Clears .
static int count()
Returns the number of .
static void registerBuilding(const BuildingDefinition &def)
Registers building.
static int loadFromJson(const std::string &json, std::string *error=nullptr)
从 JSON 数组或单对象批量注册,返回成功数量。 元素形如: { "id": "house.wood", "displayName": "木屋", "category": "housing",...
static const BuildingDefinition * find(const std::string &id)
Finds .
static bool remove(const std::string &id)
Removes .
EVENGINE_API_FOUNDATION public API.
Definition Json.h:136
bool valid() const
Valid.
Definition Json.h:157
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
Definition Json.cpp:581
Value root() const
Root.
Definition Json.h:159
EVENGINE_API_FOUNDATION public API.
Definition Json.h:38
建筑放置模块入口:定义 / 放置世界 / 鬼影 / 变更事件的脚本绑定点。 设计文档:docs/dev/建筑放置系统设计.md
Definition Building.cpp:7
建筑模板(进程级注册表中的定义)。
std::string placementKind
Placement domain: cell (default), edge, corner, or free.
std::unordered_map< std::string, std::string > visual2d
std::vector< float > freeFootprintVertices
Optional convex local polygon as x/y pairs in grid-cell units, centered on the free anchor.
std::unordered_map< std::string, int > cost
std::unordered_map< std::string, std::string > visual3d
std::string supportMode
Structural support policy: none, cell_below, or corner_below.
float freeFootprintHeightCells
Optional oriented-box height in grid-cell units; positive width and height select OBB collision.
bool maskAt(int localX, int localY) const
占地掩码在局部坐标 (localX, localY) 处是否占用。
std::unordered_map< std::string, std::string > extra
std::string getVisual2d(const std::string &key, const std::string &fallback={}) const
Returns the visual 2 d.
std::string rotationMode
none | cardinal | free。
std::string validateRule
default | boundsOnly | overlapOk | 自定义。
int getCost(const std::string &resource, int fallback=0) const
Returns the cost.
std::string getExtra(const std::string &key, const std::string &fallback={}) const
Returns the extra.
float maxSurfaceSlopeDegrees
Maximum angle between a footprint normal and the grid-plane up axis.
std::string snapMode
grid | cell | free | 自定义。
std::vector< uint8_t > footprintMask
可选占地掩码,长度 footprintW*footprintH;空表示实心矩形。行主序、原点在最小 x/y。
bool hasTag(const std::string &tag) const
查询辅助。
float freeRadiusCells
Circular free-domain collision radius measured in grid-cell units.
std::vector< std::string > tags
float freeFootprintWidthCells
Optional oriented-box width in grid-cell units; positive width and height select OBB collision.
std::string getVisual3d(const std::string &key, const std::string &fallback={}) const
Returns the visual 3 d.
std::vector< std::string > tags
void setProp(const std::string &key, const std::string &value)
Sets the prop.
std::string getProp(const std::string &key, const std::string &fallback={}) const
Returns the prop.
std::unordered_map< std::string, std::string > props
bool hasTag(const std::string &tag) const
True when tag.