table_brush.cpp

main 270 lines · 7.8 KB Raw
Alex Alex Commit Map Editor 18/10/2026 23:40
1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19#include "gui.h"
20
21#include "table_brush.h"
22
23#include "items.h"
24#include "basemap.h"
25
26uint32_t TableBrush::table_types[256];
27
28//=============================================================================
29// Table brush
30
31TableBrush::TableBrush() :
32 look_id(0) {
33 ////
34}
35
36TableBrush::~TableBrush() {
37 ////
38}
39
40bool TableBrush::load(pugi::xml_node node, wxArrayString& warnings) {
41 if (const pugi::xml_attribute attribute = node.attribute("server_lookid")) {
42 look_id = g_items[attribute.as_ushort()].clientID;
43 }
44
45 if (look_id == 0) {
46 look_id = node.attribute("lookid").as_ushort();
47 }
48
49 for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
50 if (as_lower_str(childNode.name()) != "table") {
51 continue;
52 }
53
54 const std::string& alignString = childNode.attribute("align").as_string();
55 if (alignString.empty()) {
56 warnings.push_back("Could not read type tag of table node\n");
57 continue;
58 }
59
60 uint32_t alignment;
61 if (alignString == "vertical") {
62 alignment = TABLE_VERTICAL;
63 } else if (alignString == "horizontal") {
64 alignment = TABLE_HORIZONTAL;
65 } else if (alignString == "south") {
66 alignment = TABLE_SOUTH_END;
67 } else if (alignString == "east") {
68 alignment = TABLE_EAST_END;
69 } else if (alignString == "north") {
70 alignment = TABLE_NORTH_END;
71 } else if (alignString == "west") {
72 alignment = TABLE_WEST_END;
73 } else if (alignString == "alone") {
74 alignment = TABLE_ALONE;
75 } else {
76 warnings.push_back("Unknown table alignment '" + wxstr(alignString) + "'\n");
77 continue;
78 }
79
80 for (pugi::xml_node subChildNode = childNode.first_child(); subChildNode; subChildNode = subChildNode.next_sibling()) {
81 if (as_lower_str(subChildNode.name()) != "item") {
82 continue;
83 }
84
85 uint16_t id = subChildNode.attribute("id").as_ushort();
86 if (id == 0) {
87 warnings.push_back("Could not read id tag of item node\n");
88 break;
89 }
90
91 ItemType& it = g_items[id];
92 if (it.id == 0) {
93 warnings.push_back("There is no itemtype with id " + std::to_string(id));
94 return false;
95 } else if (it.brush && it.brush != this) {
96 warnings.push_back("Itemtype id " + std::to_string(id) + " already has a brush");
97 return false;
98 }
99
100 it.isTable = true;
101 it.brush = this;
102
103 TableType tt;
104 tt.item_id = id;
105 tt.chance = subChildNode.attribute("chance").as_int();
106
107 table_items[alignment].total_chance += tt.chance;
108 table_items[alignment].items.push_back(tt);
109 }
110 }
111 return true;
112}
113
114bool TableBrush::canDraw(BaseMap* map, const Position& position) const {
115 return true;
116}
117
118void TableBrush::undraw(BaseMap* map, Tile* t) {
119 ItemVector::iterator it = t->items.begin();
120 while (it != t->items.end()) {
121 if ((*it)->isTable()) {
122 TableBrush* tb = (*it)->getTableBrush();
123 if (tb == this) {
124 delete *it;
125 it = t->items.erase(it);
126 } else {
127 ++it;
128 }
129 } else {
130 ++it;
131 }
132 }
133}
134
135void TableBrush::draw(BaseMap* map, Tile* tile, void* parameter) {
136 undraw(map, tile); // Remove old
137
138 TableNode& tn = table_items[0];
139 if (tn.total_chance <= 0) {
140 return;
141 }
142 int chance = random(1, tn.total_chance);
143 uint16_t type = 0;
144
145 for (std::vector<TableType>::const_iterator table_iter = tn.items.begin(); table_iter != tn.items.end(); ++table_iter) {
146 if (chance <= table_iter->chance) {
147 type = table_iter->item_id;
148 break;
149 }
150 chance -= table_iter->chance;
151 }
152
153 if (type != 0) {
154 Item* new_item = Item::Create(type);
155 if (new_item && g_gui.IsCurrentActionIDEnabled()) {
156 new_item->setActionID(g_gui.GetCurrentActionID());
157 }
158 tile->addItem(new_item);
159 }
160}
161
162bool hasMatchingTableBrushAtTile(BaseMap* map, TableBrush* table_brush, uint32_t x, uint32_t y, uint32_t z) {
163 Tile* t = map->getTile(x, y, z);
164 if (!t) {
165 return false;
166 }
167
168 ItemVector::const_iterator it = t->items.begin();
169 for (; it != t->items.end(); ++it) {
170 TableBrush* tb = (*it)->getTableBrush();
171 if (tb == table_brush) {
172 return true;
173 }
174 }
175
176 return false;
177}
178
179void TableBrush::doTables(BaseMap* map, Tile* tile) {
180 ASSERT(tile);
181 if (!tile->hasTable()) {
182 return;
183 }
184
185 const Position& position = tile->getPosition();
186
187 int32_t x = position.x;
188 int32_t y = position.y;
189 int32_t z = position.z;
190
191 for (Item* item : tile->items) {
192 ASSERT(item);
193
194 TableBrush* table_brush = item->getTableBrush();
195 if (!table_brush) {
196 continue;
197 }
198
199 bool neighbours[8];
200 if (x == 0) {
201 if (y == 0) {
202 neighbours[0] = false;
203 neighbours[1] = false;
204 neighbours[2] = false;
205 neighbours[3] = false;
206 neighbours[4] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y, z);
207 neighbours[5] = false;
208 neighbours[6] = hasMatchingTableBrushAtTile(map, table_brush, x, y + 1, z);
209 neighbours[7] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y + 1, z);
210 } else {
211 neighbours[0] = false;
212 neighbours[1] = hasMatchingTableBrushAtTile(map, table_brush, x, y - 1, z);
213 neighbours[2] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y - 1, z);
214 neighbours[3] = false;
215 neighbours[4] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y, z);
216 neighbours[5] = false;
217 neighbours[6] = hasMatchingTableBrushAtTile(map, table_brush, x, y + 1, z);
218 neighbours[7] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y + 1, z);
219 }
220 } else if (y == 0) {
221 neighbours[0] = false;
222 neighbours[1] = false;
223 neighbours[2] = false;
224 neighbours[3] = hasMatchingTableBrushAtTile(map, table_brush, x - 1, y, z);
225 neighbours[4] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y, z);
226 neighbours[5] = hasMatchingTableBrushAtTile(map, table_brush, x - 1, y + 1, z);
227 neighbours[6] = hasMatchingTableBrushAtTile(map, table_brush, x, y + 1, z);
228 neighbours[7] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y + 1, z);
229 } else {
230 neighbours[0] = hasMatchingTableBrushAtTile(map, table_brush, x - 1, y - 1, z);
231 neighbours[1] = hasMatchingTableBrushAtTile(map, table_brush, x, y - 1, z);
232 neighbours[2] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y - 1, z);
233 neighbours[3] = hasMatchingTableBrushAtTile(map, table_brush, x - 1, y, z);
234 neighbours[4] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y, z);
235 neighbours[5] = hasMatchingTableBrushAtTile(map, table_brush, x - 1, y + 1, z);
236 neighbours[6] = hasMatchingTableBrushAtTile(map, table_brush, x, y + 1, z);
237 neighbours[7] = hasMatchingTableBrushAtTile(map, table_brush, x + 1, y + 1, z);
238 }
239
240 uint32_t tiledata = 0;
241 for (int32_t i = 0; i < 8; ++i) {
242 if (neighbours[i]) {
243 // Same table as this one, calculate what border
244 tiledata |= 1 << i;
245 }
246 }
247
248 BorderType bt = static_cast<BorderType>(table_types[tiledata]);
249 TableNode& tn = table_brush->table_items[static_cast<int32_t>(bt)];
250 if (tn.total_chance == 0) {
251 return;
252 }
253
254 int32_t chance = random(1, tn.total_chance);
255 uint16_t id = 0;
256
257 for (const TableType& tableType : tn.items) {
258 if (chance <= tableType.chance) {
259 id = tableType.item_id;
260 break;
261 }
262 chance -= tableType.chance;
263 }
264
265 if (id != 0) {
266 item->setID(id);
267 }
268 }
269}
270
Top