item.cpp

main 563 lines · 11.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
20#include "brush.h"
21#include "graphics.h"
22#include "gui.h"
23#include "tile.h"
24#include "complexitem.h"
25#include "iomap.h"
26#include "item.h"
27
28#include "ground_brush.h"
29#include "carpet_brush.h"
30#include "table_brush.h"
31#include "wall_brush.h"
32
33Item* Item::Create(uint16_t _type, uint16_t _subtype /*= 0xFFFF*/) {
34 if (_type == 0) {
35 return nullptr;
36 }
37 Item* newItem = nullptr;
38
39 const ItemType& it = g_items[_type];
40
41 if (it.id != 0) {
42 if (it.isDepot()) {
43 newItem = newd Depot(_type);
44 } else if (it.isContainer()) {
45 newItem = newd Container(_type);
46 } else if (it.isTeleport()) {
47 newItem = newd Teleport(_type);
48 } else if (it.isDoor()) {
49 newItem = newd Door(_type);
50 } else if (it.isPodium()) {
51 newItem = newd Podium(_type);
52 } else if (_subtype == 0xFFFF) {
53 if (it.isFluidContainer()) {
54 newItem = newd Item(_type, LIQUID_NONE);
55 } else if (it.isSplash()) {
56 newItem = newd Item(_type, LIQUID_WATER);
57 } else if (it.charges > 0) {
58 newItem = newd Item(_type, it.charges);
59 } else {
60 newItem = newd Item(_type, 1);
61 }
62 } else {
63 newItem = newd Item(_type, _subtype);
64 }
65 } else {
66 newItem = newd Item(_type, _subtype);
67 }
68
69 return newItem;
70}
71
72Item::Item(unsigned short _type, unsigned short _count) :
73 id(_type),
74 subtype(1),
75 selected(false),
76 frame(0) {
77 if (hasSubtype()) {
78 subtype = _count;
79 }
80}
81
82Item::~Item() {
83 ////
84}
85
86Item* Item::deepCopy() const {
87 Item* copy = Create(id, subtype);
88 if (copy) {
89 copy->selected = selected;
90 if (attributes) {
91 copy->attributes = newd ItemAttributeMap(*attributes);
92 }
93 }
94 return copy;
95}
96
97Item* transformItem(Item* old_item, uint16_t new_id, Tile* parent) {
98 if (old_item == nullptr) {
99 return nullptr;
100 }
101
102 old_item->setID(new_id);
103 // Through the magic of deepCopy, this will now be a pointer to an item of the correct type.
104 Item* new_item = old_item->deepCopy();
105 if (parent) {
106 // Find the old item and remove it from the tile, insert this one instead!
107 if (old_item == parent->ground) {
108 delete old_item;
109 parent->ground = new_item;
110 return new_item;
111 }
112
113 std::queue<Container*> containers;
114 for (ItemVector::iterator item_iter = parent->items.begin(); item_iter != parent->items.end(); ++item_iter) {
115 if (*item_iter == old_item) {
116 delete old_item;
117 item_iter = parent->items.erase(item_iter);
118 parent->items.insert(item_iter, new_item);
119 return new_item;
120 }
121
122 Container* c = dynamic_cast<Container*>(*item_iter);
123 if (c) {
124 containers.push(c);
125 }
126 }
127
128 while (containers.size() != 0) {
129 Container* container = containers.front();
130 ItemVector& v = container->getVector();
131 for (ItemVector::iterator item_iter = v.begin(); item_iter != v.end(); ++item_iter) {
132 Item* i = *item_iter;
133 Container* c = dynamic_cast<Container*>(i);
134 if (c) {
135 containers.push(c);
136 }
137
138 if (i == old_item) {
139 // Found it!
140 item_iter = v.erase(item_iter);
141 v.insert(item_iter, new_item);
142 return new_item;
143 }
144 }
145 containers.pop();
146 }
147 }
148
149 delete new_item;
150 return nullptr;
151}
152
153uint32_t Item::memsize() const {
154 uint32_t mem = sizeof(*this);
155 return mem;
156}
157
158void Item::setID(uint16_t newid) {
159 id = newid;
160}
161
162void Item::setSubtype(uint16_t n) {
163 subtype = n;
164}
165
166bool Item::hasSubtype() const {
167 const ItemType& it = g_items[id];
168 return (it.isFluidContainer() || it.isSplash() || isCharged() || it.stackable || it.charges != 0);
169}
170
171uint16_t Item::getSubtype() const {
172 if (hasSubtype()) {
173 return subtype;
174 }
175 return 0;
176}
177
178bool Item::hasProperty(enum ITEMPROPERTY prop) const {
179 const ItemType& it = g_items[id];
180 switch (prop) {
181 case BLOCKSOLID:
182 if (it.unpassable) {
183 return true;
184 }
185 break;
186
187 case MOVEABLE:
188 if (it.moveable && getUniqueID() == 0) {
189 return true;
190 }
191 break;
192 /*
193 case HASHEIGHT:
194 if(it.height != 0 )
195 return true;
196 break;
197 */
198 case BLOCKPROJECTILE:
199 if (it.blockMissiles) {
200 return true;
201 }
202 break;
203
204 case BLOCKPATHFIND:
205 if (it.blockPathfinder) {
206 return true;
207 }
208 break;
209
210 case HOOK_SOUTH:
211 if (it.hookSouth) {
212 return true;
213 }
214 break;
215
216 case HOOK_EAST:
217 if (it.hookEast) {
218 return true;
219 }
220 break;
221
222 case BLOCKINGANDNOTMOVEABLE:
223 if (it.unpassable && (!it.moveable || getUniqueID() != 0)) {
224 return true;
225 }
226 break;
227
228 case HASLIGHT:
229 if (hasLight()) {
230 return true;
231 }
232 break;
233
234 default:
235 return false;
236 }
237 return false;
238}
239
240std::pair<int, int> Item::getDrawOffset() const {
241 ItemType& it = g_items[id];
242 if (it.sprite != nullptr) {
243 return it.sprite->getDrawOffset();
244 }
245 return std::make_pair(0, 0);
246}
247
248bool Item::hasLight() const {
249 const ItemType& type = g_items.getItemType(id);
250 if (type.sprite) {
251 return type.sprite->hasLight();
252 }
253 return false;
254}
255
256SpriteLight Item::getLight() const {
257 const ItemType& type = g_items.getItemType(id);
258 if (type.sprite) {
259 return type.sprite->getLight();
260 }
261 return SpriteLight { 0, 0 };
262}
263
264double Item::getWeight() const {
265 ItemType& it = g_items[id];
266 if (it.stackable) {
267 return it.weight * std::max(1, (int)subtype);
268 }
269
270 return it.weight;
271}
272
273void Item::setUniqueID(unsigned short n) {
274 setAttribute("uid", n);
275}
276
277void Item::setActionID(unsigned short n) {
278 setAttribute("aid", n);
279}
280
281void Item::setText(const std::string& str) {
282 setAttribute("text", str);
283}
284
285void Item::setDescription(const std::string& str) {
286 setAttribute("desc", str);
287}
288
289void Item::setTier(unsigned short n) {
290 setAttribute("tier", n);
291}
292
293double Item::getWeight() {
294 ItemType& it = g_items[id];
295 if (it.isStackable()) {
296 return it.weight * subtype;
297 }
298 return it.weight;
299}
300
301bool Item::canHoldText() const {
302 return isReadable() || canWriteText();
303}
304
305bool Item::canHoldDescription() const {
306 return g_items[id].allowDistRead;
307}
308
309uint8_t Item::getMiniMapColor() const {
310 GameSprite* spr = g_items[id].sprite;
311 if (spr) {
312 return spr->getMiniMapColor();
313 }
314 return 0;
315}
316
317GroundBrush* Item::getGroundBrush() const {
318 ItemType& item_type = g_items.getItemType(id);
319 if (item_type.isGroundTile() && item_type.brush && item_type.brush->isGround()) {
320 return item_type.brush->asGround();
321 }
322 return nullptr;
323}
324
325TableBrush* Item::getTableBrush() const {
326 ItemType& item_type = g_items.getItemType(id);
327 if (item_type.isTable && item_type.brush && item_type.brush->isTable()) {
328 return item_type.brush->asTable();
329 }
330 return nullptr;
331}
332
333CarpetBrush* Item::getCarpetBrush() const {
334 ItemType& item_type = g_items.getItemType(id);
335 if (item_type.isCarpet && item_type.brush && item_type.brush->isCarpet()) {
336 return item_type.brush->asCarpet();
337 }
338 return nullptr;
339}
340
341DoorBrush* Item::getDoorBrush() const {
342 ItemType& item_type = g_items.getItemType(id);
343 if (!item_type.isWall || !item_type.isBrushDoor || !item_type.brush || !item_type.brush->isWall()) {
344 return nullptr;
345 }
346
347 DoorType door_type = item_type.brush->asWall()->getDoorTypeFromID(id);
348 DoorBrush* door_brush = nullptr;
349 // Quite a horrible dependency on a global here, meh.
350 switch (door_type) {
351 case WALL_DOOR_NORMAL: {
352 door_brush = g_gui.normal_door_brush;
353 break;
354 }
355 case WALL_DOOR_LOCKED: {
356 door_brush = g_gui.locked_door_brush;
357 break;
358 }
359 case WALL_DOOR_QUEST: {
360 door_brush = g_gui.quest_door_brush;
361 break;
362 }
363 case WALL_DOOR_MAGIC: {
364 door_brush = g_gui.magic_door_brush;
365 break;
366 }
367 case WALL_DOOR_NORMAL_ALT: {
368 door_brush = g_gui.normal_door_alt_brush;
369 break;
370 }
371 case WALL_ARCHWAY: {
372 door_brush = g_gui.archway_door_brush;
373 break;
374 }
375 case WALL_WINDOW: {
376 door_brush = g_gui.window_door_brush;
377 break;
378 }
379 case WALL_HATCH_WINDOW: {
380 door_brush = g_gui.hatch_door_brush;
381 break;
382 }
383 default: {
384 break;
385 }
386 }
387 return door_brush;
388}
389
390WallBrush* Item::getWallBrush() const {
391 ItemType& item_type = g_items.getItemType(id);
392 if (item_type.isWall && item_type.brush && item_type.brush->isWall()) {
393 return item_type.brush->asWall();
394 }
395 return nullptr;
396}
397
398BorderType Item::getWallAlignment() const {
399 ItemType& it = g_items[id];
400 if (!it.isWall) {
401 return BORDER_NONE;
402 }
403 return it.border_alignment;
404}
405
406BorderType Item::getBorderAlignment() const {
407 ItemType& it = g_items[id];
408 return it.border_alignment;
409}
410
411void Item::animate() {
412 ItemType& type = g_items[id];
413 GameSprite* sprite = type.sprite;
414 if (!sprite || !sprite->animator) {
415 return;
416 }
417
418 frame = sprite->animator->getFrame();
419}
420
421// ============================================================================
422// Static conversions
423
424std::string Item::LiquidID2Name(uint16_t id) {
425 switch (id) {
426 case LIQUID_NONE:
427 return "None";
428 case LIQUID_WATER:
429 return "Water";
430 case LIQUID_BLOOD:
431 return "Blood";
432 case LIQUID_BEER:
433 return "Beer";
434 case LIQUID_SLIME:
435 return "Slime";
436 case LIQUID_LEMONADE:
437 return "Lemonade";
438 case LIQUID_MILK:
439 return "Milk";
440 case LIQUID_MANAFLUID:
441 return "Manafluid";
442 case LIQUID_WATER2:
443 return "Water";
444 case LIQUID_LIFEFLUID:
445 return "Lifefluid";
446 case LIQUID_OIL:
447 return "Oil";
448 case LIQUID_SLIME2:
449 return "Slime";
450 case LIQUID_URINE:
451 return "Urine";
452 case LIQUID_COCONUT_MILK:
453 return "Coconut Milk";
454 case LIQUID_WINE:
455 return "Wine";
456 case LIQUID_MUD:
457 return "Mud";
458 case LIQUID_FRUIT_JUICE:
459 return "Fruit Juice";
460 case LIQUID_LAVA:
461 return "Lava";
462 case LIQUID_RUM:
463 return "Rum";
464 case LIQUID_SWAMP:
465 return "Swamp";
466 case LIQUID_INK:
467 return "Ink";
468 case LIQUID_TEA:
469 return "Tea";
470 case LIQUID_MEAD:
471 return "Mead";
472 default:
473 return "Unknown";
474 }
475}
476
477uint16_t Item::LiquidName2ID(std::string liquid) {
478 to_lower_str(liquid);
479 if (liquid == "none") {
480 return LIQUID_NONE;
481 }
482 if (liquid == "water") {
483 return LIQUID_WATER;
484 }
485 if (liquid == "blood") {
486 return LIQUID_BLOOD;
487 }
488 if (liquid == "beer") {
489 return LIQUID_BEER;
490 }
491 if (liquid == "slime") {
492 return LIQUID_SLIME;
493 }
494 if (liquid == "lemonade") {
495 return LIQUID_LEMONADE;
496 }
497 if (liquid == "milk") {
498 return LIQUID_MILK;
499 }
500 if (liquid == "manafluid") {
501 return LIQUID_MANAFLUID;
502 }
503 if (liquid == "lifefluid") {
504 return LIQUID_LIFEFLUID;
505 }
506 if (liquid == "oil") {
507 return LIQUID_OIL;
508 }
509 if (liquid == "urine") {
510 return LIQUID_URINE;
511 }
512 if (liquid == "coconut milk") {
513 return LIQUID_COCONUT_MILK;
514 }
515 if (liquid == "wine") {
516 return LIQUID_WINE;
517 }
518 if (liquid == "mud") {
519 return LIQUID_MUD;
520 }
521 if (liquid == "fruit juice") {
522 return LIQUID_FRUIT_JUICE;
523 }
524 if (liquid == "lava") {
525 return LIQUID_LAVA;
526 }
527 if (liquid == "rum") {
528 return LIQUID_RUM;
529 }
530 if (liquid == "swamp") {
531 return LIQUID_SWAMP;
532 }
533 if (liquid == "ink") {
534 return LIQUID_INK;
535 }
536 if (liquid == "tea") {
537 return LIQUID_TEA;
538 }
539 if (liquid == "mead") {
540 return LIQUID_MEAD;
541 }
542 return LIQUID_NONE;
543}
544
545// ============================================================================
546// XML Saving & loading
547
548Item* Item::Create(pugi::xml_node xml) {
549 pugi::xml_attribute attribute;
550
551 int16_t id = 0;
552 if ((attribute = xml.attribute("id"))) {
553 id = attribute.as_ushort();
554 }
555
556 int16_t count = 1;
557 if ((attribute = xml.attribute("count")) || (attribute = xml.attribute("subtype"))) {
558 count = attribute.as_ushort();
559 }
560
561 return Create(id, count);
562}
563
Top