////////////////////////////////////////////////////////////////////// // This file is part of Remere's Map Editor ////////////////////////////////////////////////////////////////////// // Remere's Map Editor is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Remere's Map Editor is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ////////////////////////////////////////////////////////////////////// #include "main.h" #include "brush.h" #include "tile.h" #include "monster.h" #include "house.h" #include "basemap.h" #include "spawn_monster.h" #include "ground_brush.h" #include "wall_brush.h" #include "carpet_brush.h" #include "table_brush.h" #include "npc.h" #include "spawn_npc.h" Tile::Tile(int x, int y, int z) : location(nullptr), ground(nullptr), monster(nullptr), spawnMonster(nullptr), npc(nullptr), spawnNpc(nullptr), house_id(0), mapflags(0), statflags(0), minimapColor(INVALID_MINIMAP_COLOR) { //// } Tile::Tile(TileLocation& loc) : location(&loc), ground(nullptr), monster(nullptr), spawnMonster(nullptr), npc(nullptr), spawnNpc(nullptr), house_id(0), mapflags(0), statflags(0), minimapColor(INVALID_MINIMAP_COLOR) { //// } Tile::~Tile() { while(!items.empty()) { delete items.back(); items.pop_back(); } delete monster; //printf("%d,%d,%d,%p\n", tilePos.x, tilePos.y, tilePos.z, ground); delete ground; delete spawnMonster; delete npc; delete spawnNpc; } Tile* Tile::deepCopy(BaseMap& map) { Tile* copy = map.allocator.allocateTile(location); copy->flags = flags; copy->house_id = house_id; if(spawnMonster) copy->spawnMonster = spawnMonster->deepCopy(); if(spawnNpc) copy->spawnNpc = spawnNpc->deepCopy(); if(monster) copy->monster = monster->deepCopy(); if(npc) copy->npc = npc->deepCopy(); // Spawncount & exits are not transferred on copy! if(ground) copy->ground = ground->deepCopy(); ItemVector::iterator it; it = items.begin(); while(it != items.end()) { copy->items.push_back((*it)->deepCopy()); ++it; } return copy; } uint32_t Tile::memsize() const { uint32_t mem = sizeof(*this); if(ground) mem += ground->memsize(); ItemVector::const_iterator it; it = items.begin(); while(it != items.end()) { mem += (*it)->memsize(); ++it; } mem += sizeof(Item*) * items.capacity(); return mem; } int Tile::size() const { int sz = 0; if(ground) ++sz; sz += items.size(); if(monster) ++sz; if(spawnMonster) ++sz; if(npc) ++sz; if(spawnNpc) ++sz; if(location) { if(location->getHouseExits()) ++sz; if(location->getSpawnMonsterCount()) ++sz; if(location->getSpawnNpcCount()) ++sz; if(location->getWaypointCount()) ++ sz; } return sz; } void Tile::merge(Tile* other) { if(other->isPZ()) setPZ(true); if(other->house_id) { house_id = other->house_id; } if(other->ground) { delete ground; ground = other->ground; other->ground = nullptr; } if(other->monster) { delete monster; monster = other->monster; other->monster = nullptr; } if(other->spawnMonster) { delete spawnMonster; spawnMonster = other->spawnMonster; other->spawnMonster = nullptr; } if(other->npc) { delete npc; npc = other->npc; other->npc = nullptr; } if(other->spawnNpc) { delete spawnNpc; spawnNpc = other->spawnNpc; other->spawnNpc = nullptr; } if(other->monster) { delete monster; monster = other->monster; other->monster = nullptr; } if(other->npc) { delete npc; npc = other->npc; other->npc = nullptr; } ItemVector::iterator it; it = other->items.begin(); while(it != other->items.end()) { addItem(*it); ++it; } other->items.clear(); } bool Tile::hasProperty(enum ITEMPROPERTY prop) const { if(prop == PROTECTIONZONE && isPZ()) return true; if(ground && ground->hasProperty(prop)){ return true; } ItemVector::const_iterator iit; for(iit = items.begin(); iit != items.end(); ++iit){ if((*iit)->hasProperty(prop)) return true; } return false; } int Tile::getIndexOf(Item* item) const { if(!item) return wxNOT_FOUND; int index = 0; if(ground) { if(ground == item) return index; index++; } if(!items.empty()) { auto it = std::find(items.begin(), items.end(), item); if(it != items.end()) { index += (it - items.begin()); return index; } } return wxNOT_FOUND; } Item* Tile::getTopItem() const { if(!items.empty() && !items.back()->isMetaItem()) return items.back(); if(ground && !ground->isMetaItem()) { return ground; } return nullptr; } Item* Tile::getItemAt(int index) const { if(index < 0) return nullptr; if(ground) { if(index == 0) return ground; index--; } if(index >= 0 && index < static_cast(items.size())) { return items[index]; } return nullptr; } void Tile::addItem(Item* item) { if(!item) return; if(item->isGroundTile()) { //printf("ADDING GROUND\n"); delete ground; ground = item; return; } ItemVector::iterator it; uint16_t gid = item->getGroundEquivalent(); if(gid != 0) { delete ground; ground = Item::Create(gid); // At the very bottom! it = items.begin(); } else { if(item->isAlwaysOnBottom()) { it = items.begin(); while(true) { if(it == items.end()) { break; } else if((*it)->isAlwaysOnBottom()) { if(item->getTopOrder() < (*it)->getTopOrder()) { break; } } else { // Always on top break; } ++it; } } else { it = items.end(); } } items.insert(it, item); if(item->isSelected()) { statflags |= TILESTATE_SELECTED; } } void Tile::select() { if(size() == 0) return; if(ground) ground->select(); if(spawnMonster) spawnMonster->select(); if(spawnNpc) spawnNpc->select(); if(monster) monster->select(); if(npc) npc->select(); ItemVector::iterator it; it = items.begin(); while(it != items.end()) { (*it)->select(); ++it; } statflags |= TILESTATE_SELECTED; } void Tile::deselect() { if(ground) ground->deselect(); if(spawnMonster) spawnMonster->deselect(); if(spawnNpc) spawnNpc->deselect(); if(monster) monster->deselect(); if(npc) npc->deselect(); ItemVector::iterator it; it = items.begin(); while(it != items.end()) { (*it)->deselect(); ++it; } statflags &= ~TILESTATE_SELECTED; } Item* Tile::getTopSelectedItem() { for(ItemVector::reverse_iterator iter = items.rbegin(); iter != items.rend(); ++iter) { if((*iter)->isSelected() && !(*iter)->isMetaItem()) { return *iter; } } if(ground && ground->isSelected() && !ground->isMetaItem()) { return ground; } return nullptr; } ItemVector Tile::popSelectedItems(bool ignoreTileSelected) { ItemVector pop_items; if(!ignoreTileSelected && !isSelected()) return pop_items; if(ground && ground->isSelected()) { pop_items.push_back(ground); ground = nullptr; } ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isSelected()) { pop_items.push_back(*it); it = items.erase(it); } else { ++it; } } statflags &= ~TILESTATE_SELECTED; return pop_items; } ItemVector Tile::getSelectedItems() { ItemVector selected_items; if(!isSelected()) return selected_items; if(ground && ground->isSelected()) { selected_items.push_back(ground); } ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isSelected()) { selected_items.push_back(*it); } it++; } return selected_items; } uint8_t Tile::getMiniMapColor() const { if(minimapColor != INVALID_MINIMAP_COLOR) return minimapColor; for(ItemVector::const_reverse_iterator item_iter = items.rbegin(); item_iter != items.rend(); ++item_iter) { if((*item_iter)->getMiniMapColor()) { return (*item_iter)->getMiniMapColor(); break; } } // check ground too if(hasGround()) { return ground->getMiniMapColor(); } return 0; } bool tilePositionLessThan(const Tile* a, const Tile* b) { return a->getPosition() < b->getPosition(); } bool tilePositionVisualLessThan(const Tile* a, const Tile* b) { Position pa = a->getPosition(); Position pb = b->getPosition(); if(pa.z > pb.z) return true; if(pa.z < pb.z) return false; if(pa.y < pb.y) return true; if(pa.y > pb.y) return false; if(pa.x < pb.x) return true; return false; } void Tile::update() { statflags &= TILESTATE_MODIFIED; if(spawnMonster && spawnMonster->isSelected()) { statflags |= TILESTATE_SELECTED; } if(spawnNpc && spawnNpc->isSelected()) { statflags |= TILESTATE_SELECTED; } if(monster && monster->isSelected()) { statflags |= TILESTATE_SELECTED; } if(npc && npc->isSelected()) { statflags |= TILESTATE_SELECTED; } if(ground) { if(ground->isSelected()) { statflags |= TILESTATE_SELECTED; } if(ground->isBlocking()) { statflags |= TILESTATE_BLOCKING; } if(ground->getUniqueID() != 0) { statflags |= TILESTATE_UNIQUE; } if(ground->getMiniMapColor() != 0) { minimapColor = ground->getMiniMapColor(); } } ItemVector::const_iterator iter = items.begin(); while(iter != items.end()) { Item* i = *iter; if(i->isSelected()) { statflags |= TILESTATE_SELECTED; } if(i->getUniqueID() != 0) { statflags |= TILESTATE_UNIQUE; } if(i->getMiniMapColor() != 0) { minimapColor = i->getMiniMapColor(); } ItemType& it = g_items[i->getID()]; if(it.unpassable) { statflags |= TILESTATE_BLOCKING; } if(it.isOptionalBorder) { statflags |= TILESTATE_OP_BORDER; } if(it.isTable) { statflags |= TILESTATE_HAS_TABLE; } if(it.isCarpet) { statflags |= TILESTATE_HAS_CARPET; } ++iter; } if((statflags & TILESTATE_BLOCKING) == 0) { if(ground == nullptr && items.size() == 0) { statflags |= TILESTATE_BLOCKING; } } } void Tile::borderize(BaseMap* parent) { GroundBrush::doBorders(parent, this); } void Tile::addBorderItem(Item* item) { if(!item) return; ASSERT(item->isBorder()); items.insert(items.begin(), item); } GroundBrush* Tile::getGroundBrush() const { if(ground) { if(ground->getGroundBrush()) { return ground->getGroundBrush(); } } return nullptr; } void Tile::cleanBorders() { ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isBorder()) { delete *it; it = items.erase(it); } else { // Borders should only be on the bottom, we can ignore the rest of the items return; } } } void Tile::wallize(BaseMap* parent) { WallBrush::doWalls(parent, this); } Item* Tile::getWall() const { ItemVector::const_iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isWall()) { return *it; } ++it; } return nullptr; } Item* Tile::getCarpet() const { ItemVector::const_iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isCarpet()) { return *it; } ++it; } return nullptr; } Item* Tile::getTable() const { ItemVector::const_iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isTable()) { return *it; } ++it; } return nullptr; } void Tile::addWallItem(Item* item) { if(!item) { return; } ASSERT(item->isWall()); addItem(item); } void Tile::cleanWalls(bool dontdelete) { ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isWall()) { if(!dontdelete) { delete *it; } it = items.erase(it); } else ++it; } } void Tile::cleanWalls(WallBrush* wb) { ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isWall() && wb->hasWall(*it)) { delete *it; it = items.erase(it); } else ++it; } } void Tile::cleanTables(bool dontdelete) { ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isTable()) { if(!dontdelete) { delete *it; } it = items.erase(it); } else ++it; } } void Tile::tableize(BaseMap* parent) { TableBrush::doTables(parent, this); } void Tile::carpetize(BaseMap* parent) { CarpetBrush::doCarpets(parent, this); } void Tile::selectGround() { bool selected_ = false; if(ground) { ground->select(); selected_ = true; } ItemVector::iterator it; it = items.begin(); while(it != items.end()) { if((*it)->isBorder()) { (*it)->select(); selected_ = true; } else { break; } ++it; } if(selected_) statflags |= TILESTATE_SELECTED; } void Tile::deselectGround() { if(ground) { ground->deselect(); } ItemVector::iterator it = items.begin(); while(it != items.end()) { if((*it)->isBorder()) { (*it)->deselect(); } else { break; } ++it; } } void Tile::setHouse(House* _house) { house_id = (_house? _house->id : 0); } void Tile::addHouseExit(House* h) { if(!h) return; HouseExitList* house_exits = location->createHouseExits(); house_exits->push_back(h->id); } void Tile::removeHouseExit(House* h) { if(!h) return; HouseExitList* house_exits = location->getHouseExits(); if(!house_exits) return; for(std::vector::iterator it = house_exits->begin(); it != house_exits->end(); ++it) { if(*it == h->id) { house_exits->erase(it); return; } } }