//////////////////////////////////////////////////////////////////////
// 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 "ground_brush.h"
#include "items.h"
#include "basemap.h"
uint32_t GroundBrush::border_types[256];
int AutoBorder::edgeNameToID(const std::string &edgename) {
if (edgename == "n") {
return NORTH_HORIZONTAL;
} else if (edgename == "w") {
return WEST_HORIZONTAL;
} else if (edgename == "s") {
return SOUTH_HORIZONTAL;
} else if (edgename == "e") {
return EAST_HORIZONTAL;
} else if (edgename == "cnw") {
return NORTHWEST_CORNER;
} else if (edgename == "cne") {
return NORTHEAST_CORNER;
} else if (edgename == "csw") {
return SOUTHWEST_CORNER;
} else if (edgename == "cse") {
return SOUTHEAST_CORNER;
} else if (edgename == "dnw") {
return NORTHWEST_DIAGONAL;
} else if (edgename == "dne") {
return NORTHEAST_DIAGONAL;
} else if (edgename == "dsw") {
return SOUTHWEST_DIAGONAL;
} else if (edgename == "dse") {
return SOUTHEAST_DIAGONAL;
}
return BORDER_NONE;
}
bool AutoBorder::load(pugi::xml_node node, wxArrayString &warnings, GroundBrush* owner, uint16_t ground_equivalent) {
ASSERT(ground ? ground_equivalent != 0 : true);
pugi::xml_attribute attribute;
bool optionalBorder = false;
if ((attribute = node.attribute("type"))) {
if (std::string(attribute.as_string()) == "optional") {
optionalBorder = true;
}
}
if ((attribute = node.attribute("group"))) {
group = attribute.as_uint();
}
for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
if (!(attribute = childNode.attribute("item"))) {
continue;
}
uint16_t itemid = attribute.as_uint();
if (!(attribute = childNode.attribute("edge"))) {
continue;
}
const std::string &orientation = attribute.as_string();
auto type = g_items.getRawItemType(itemid);
if (!type) {
warnings.push_back("Invalid item ID " + std::to_string(itemid) + " for border " + std::to_string(id));
continue;
}
if (ground) { // We are a ground border
type->group = ITEM_GROUP_NONE;
type->ground_equivalent = ground_equivalent;
type->brush = owner;
auto type2 = g_items.getRawItemType(ground_equivalent);
type2->has_equivalent = type2->id != 0;
}
type->alwaysOnBottom = true; // Never-ever place other items under this, will confuse the user something awful.
type->isBorder = true;
type->isOptionalBorder = type->isOptionalBorder ? true : optionalBorder;
if (group && !type->border_group) {
type->border_group = group;
}
int32_t edge_id = edgeNameToID(orientation);
if (edge_id != BORDER_NONE) {
tiles[edge_id] = itemid;
if (type->border_alignment == BORDER_NONE) {
type->border_alignment = ::BorderType(edge_id);
}
}
}
return true;
}
GroundBrush::GroundBrush() :
z_order(0),
has_zilch_outer_border(false),
has_zilch_inner_border(false),
has_outer_border(false),
has_inner_border(false),
optional_border(nullptr),
use_only_optional(false),
randomize(true),
total_chance(0) {
////
}
GroundBrush::~GroundBrush() {
for (BorderBlock* borderBlock : borders) {
if (borderBlock->autoborder) {
for (SpecificCaseBlock* specificCaseBlock : borderBlock->specific_cases) {
delete specificCaseBlock;
}
if (borderBlock->autoborder->ground) {
delete borderBlock->autoborder;
}
}
delete borderBlock;
}
borders.clear();
}
bool GroundBrush::load(pugi::xml_node node, wxArrayString &warnings) {
pugi::xml_attribute attribute;
if ((attribute = node.attribute("lookid"))) {
look_id = attribute.as_uint();
}
if ((attribute = node.attribute("z-order"))) {
z_order = attribute.as_int();
}
if ((attribute = node.attribute("solo_optional"))) {
use_only_optional = attribute.as_bool();
}
if ((attribute = node.attribute("randomize"))) {
randomize = attribute.as_bool();
}
for (pugi::xml_node childNode = node.first_child(); childNode; childNode = childNode.next_sibling()) {
const std::string &childName = as_lower_str(childNode.name());
if (childName == "item") {
uint16_t itemId = childNode.attribute("id").as_uint();
int32_t chance = childNode.attribute("chance").as_int();
auto type = g_items.getRawItemType(itemId);
if (!type) {
warnings.push_back("\nInvalid item id " + std::to_string(itemId));
return false;
}
if (!type->isGroundTile()) {
warnings.push_back("\nItem " + std::to_string(itemId) + " is not ground item.");
return false;
}
if (type->brush && type->brush != this) {
warnings.push_back("\nItem " + std::to_string(itemId) + " can not be member of two brushes");
return false;
}
type->brush = this;
total_chance += chance;
ItemChanceBlock ci;
ci.id = itemId;
ci.chance = total_chance;
border_items.push_back(ci);
} else if (childName == "optional") {
// Mountain border!
if (optional_border) {
warnings.push_back("\nDuplicate optional borders!");
continue;
}
if ((attribute = childNode.attribute("ground_equivalent"))) {
uint16_t ground_equivalent = attribute.as_uint();
// Load from inline definition
const ItemType &type = g_items.getItemType(ground_equivalent);
if (type.id == 0) {
warnings.push_back("Invalid id of ground dependency equivalent item.\n");
continue;
} else if (!type.isGroundTile()) {
warnings.push_back("Ground dependency equivalent is not a ground item.\n");
continue;
} else if (type.brush && type.brush != this) {
warnings.push_back("Ground dependency equivalent does not use the same brush as ground border.\n");
continue;
}
AutoBorder* autoBorder = newd AutoBorder(0); // Empty id basically
autoBorder->load(childNode, warnings, this, ground_equivalent);
optional_border = autoBorder;
} else {
// Load from ID
if (!(attribute = childNode.attribute("id"))) {
warnings.push_back("\nMissing tag id for border node");
continue;
}
uint16_t id = attribute.as_uint();
auto it = g_brushes.borders.find(id);
if (it == g_brushes.borders.end() || !it->second) {
warnings.push_back("\nCould not find border id " + std::to_string(id));
continue;
}
optional_border = it->second;
}
} else if (childName == "border") {
AutoBorder* autoBorder;
if (!(attribute = childNode.attribute("id"))) {
if (!(attribute = childNode.attribute("ground_equivalent"))) {
continue;
}
uint16_t ground_equivalent = attribute.as_uint();
const ItemType &it = g_items.getItemType(ground_equivalent);
if (it.id == 0) {
warnings.push_back("Invalid id of ground dependency equivalent item.\n");
}
if (!it.isGroundTile()) {
warnings.push_back("Ground id " + std::to_string(ground_equivalent) + " dependency equivalent is not a ground item.\n");
}
if (it.brush && it.brush != this) {
warnings.push_back("Ground dependency equivalent does not use the same brush as ground border.\n");
}
autoBorder = newd AutoBorder(0); // Empty id basically
autoBorder->load(childNode, warnings, this, ground_equivalent);
} else {
int32_t id = attribute.as_int();
if (id == 0) {
autoBorder = nullptr;
} else {
auto it = g_brushes.borders.find(id);
if (it == g_brushes.borders.end() || !it->second) {
warnings.push_back("\nCould not find border id " + std::to_string(id));
continue;
}
autoBorder = it->second;
}
}
BorderBlock* borderBlock = newd BorderBlock;
borderBlock->super = false;
borderBlock->autoborder = autoBorder;
if ((attribute = childNode.attribute("to"))) {
const std::string &value = attribute.as_string();
if (value == "all") {
borderBlock->to = 0xFFFFFFFF;
} else if (value == "none") {
borderBlock->to = 0;
} else {
Brush* tobrush = g_brushes.getBrush(value);
if (!tobrush) {
warnings.push_back("To brush " + wxstr(value) + " doesn't exist.");
delete borderBlock;
continue;
}
borderBlock->to = tobrush->getID();
}
} else {
borderBlock->to = 0xFFFFFFFF;
}
if ((attribute = childNode.attribute("super")) && attribute.as_bool()) {
borderBlock->super = true;
}
if ((attribute = childNode.attribute("align"))) {
const std::string &value = attribute.as_string();
if (value == "outer") {
borderBlock->outer = true;
} else if (value == "inner") {
borderBlock->outer = false;
} else {
borderBlock->outer = true;
}
}
if (borderBlock->outer) {
if (borderBlock->to == 0) {
has_zilch_outer_border = true;
} else {
has_outer_border = true;
}
} else {
if (borderBlock->to == 0) {
has_zilch_inner_border = true;
} else {
has_inner_border = true;
}
}
for (pugi::xml_node subChildNode = childNode.first_child(); subChildNode; subChildNode = subChildNode.next_sibling()) {
if (as_lower_str(subChildNode.name()) != "specific") {
continue;
}
auto specificCaseBlock = std::make_unique();
for (pugi::xml_node superChildNode = subChildNode.first_child(); superChildNode; superChildNode = superChildNode.next_sibling()) {
const std::string &superChildName = as_lower_str(superChildNode.name());
if (superChildName == "conditions") {
for (pugi::xml_node conditionChild = superChildNode.first_child(); conditionChild; conditionChild = conditionChild.next_sibling()) {
const std::string &conditionName = as_lower_str(conditionChild.name());
if (conditionName == "match_border") {
if (!(attribute = conditionChild.attribute("id"))) {
continue;
}
int32_t border_id = attribute.as_int();
if (!(attribute = conditionChild.attribute("edge"))) {
continue;
}
int32_t edge_id = AutoBorder::edgeNameToID(attribute.as_string());
auto it = g_brushes.borders.find(border_id);
if (it == g_brushes.borders.end()) {
warnings.push_back("Unknown border id in specific case match block " + std::to_string(border_id));
continue;
}
AutoBorder* autoBorder = it->second;
ASSERT(autoBorder != nullptr);
uint32_t match_itemid = autoBorder->tiles[edge_id];
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->items_to_match.push_back(match_itemid);
} else if (conditionName == "match_group") {
if (!(attribute = conditionChild.attribute("group"))) {
continue;
}
uint16_t group = attribute.as_uint();
if (!(attribute = conditionChild.attribute("edge"))) {
continue;
}
int32_t edge_id = AutoBorder::edgeNameToID(attribute.as_string());
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->match_group = group;
specificCaseBlock->group_match_alignment = ::BorderType(edge_id);
specificCaseBlock->items_to_match.push_back(group);
} else if (conditionName == "match_item") {
if (!(attribute = conditionChild.attribute("id"))) {
continue;
}
int32_t match_itemid = attribute.as_int();
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->match_group = 0;
specificCaseBlock->items_to_match.push_back(match_itemid);
}
}
} else if (superChildName == "actions") {
for (pugi::xml_node actionChild = superChildNode.first_child(); actionChild; actionChild = actionChild.next_sibling()) {
const std::string &actionName = as_lower_str(actionChild.name());
if (actionName == "replace_border") {
if (!(attribute = actionChild.attribute("id"))) {
continue;
}
int32_t border_id = attribute.as_int();
if (!(attribute = actionChild.attribute("edge"))) {
continue;
}
int32_t edge_id = AutoBorder::edgeNameToID(attribute.as_string());
if (!(attribute = actionChild.attribute("with"))) {
continue;
}
int32_t with_id = attribute.as_int();
auto itt = g_brushes.borders.find(border_id);
if (itt == g_brushes.borders.end()) {
warnings.push_back("Unknown border id in specific case match block " + std::to_string(border_id));
continue;
}
AutoBorder* autoBorder = itt->second;
ASSERT(autoBorder != nullptr);
auto type = g_items.getRawItemType(with_id);
if (!type) {
return false;
}
type->isBorder = true;
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->to_replace_id = autoBorder->tiles[edge_id];
specificCaseBlock->with_id = with_id;
} else if (actionName == "replace_item") {
if (!(attribute = actionChild.attribute("id"))) {
continue;
}
int32_t to_replace_id = attribute.as_int();
if (!(attribute = actionChild.attribute("with"))) {
continue;
}
int32_t with_id = attribute.as_int();
auto type = g_items.getRawItemType(with_id);
if (!type) {
return false;
}
type->isBorder = true;
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->to_replace_id = to_replace_id;
specificCaseBlock->with_id = with_id;
} else if (actionName == "delete_borders") {
if (!specificCaseBlock) {
specificCaseBlock = std::make_unique();
}
specificCaseBlock->delete_all = true;
}
}
}
}
}
borders.push_back(borderBlock);
} else if (childName == "friend") {
const std::string &name = childNode.attribute("name").as_string();
if (!name.empty()) {
if (name == "all") {
friends.push_back(0xFFFFFFFF);
} else {
Brush* brush = g_brushes.getBrush(name);
if (brush) {
friends.push_back(brush->getID());
} else {
warnings.push_back("Brush '" + wxstr(name) + "' is not defined.");
}
}
}
hate_friends = false;
} else if (childName == "enemy") {
const std::string &name = childNode.attribute("name").as_string();
if (!name.empty()) {
if (name == "all") {
friends.push_back(0xFFFFFFFF);
} else {
Brush* brush = g_brushes.getBrush(name);
if (brush) {
friends.push_back(brush->getID());
} else {
warnings.push_back("Brush '" + wxstr(name) + "' is not defined.");
}
}
}
hate_friends = true;
} else if (childName == "clear_borders") {
for (std::vector::iterator it = borders.begin();
it != borders.end();
++it) {
BorderBlock* bb = *it;
if (bb->autoborder) {
for (std::vector::iterator specific_iter = bb->specific_cases.begin(); specific_iter != bb->specific_cases.end(); ++specific_iter) {
delete *specific_iter;
}
if (bb->autoborder->ground) {
delete bb->autoborder;
}
}
delete bb;
}
borders.clear();
} else if (childName == "clear_friends") {
friends.clear();
hate_friends = false;
}
}
if (total_chance == 0) {
randomize = false;
}
return true;
}
void GroundBrush::undraw(BaseMap* map, Tile* tile) {
ASSERT(tile);
if (tile->hasGround() && tile->ground->getGroundBrush() == this) {
delete tile->ground;
tile->ground = nullptr;
}
}
void GroundBrush::draw(BaseMap* map, Tile* tile, void* parameter) {
ASSERT(tile);
if (border_items.empty()) {
return;
}
if (parameter != nullptr) {
std::pair ¶m = *reinterpret_cast*>(parameter);
GroundBrush* other = tile->getGroundBrush();
if (param.first) { // Volatile? :)
if (other != nullptr) {
return;
}
} else if (other != param.second) {
return;
}
}
int chance = random(1, total_chance);
uint16_t id = 0;
for (std::vector::const_iterator it = border_items.begin(); it != border_items.end(); ++it) {
if (chance < it->chance) {
id = it->id;
break;
}
}
if (id == 0) {
id = border_items.front().id;
}
tile->addItem(Item::Create(id));
}
const GroundBrush::BorderBlock* GroundBrush::getBrushTo(GroundBrush* first, GroundBrush* second) {
// printf("Border from %s to %s : ", first->getName().c_str(), second->getName().c_str());
if (first) {
if (second) {
if (first->getZ() < second->getZ() && second->hasOuterBorder()) {
if (first->hasInnerBorder()) {
for (std::vector::iterator it = first->borders.begin(); it != first->borders.end(); ++it) {
BorderBlock* bb = *it;
if (bb->outer) {
continue;
} else if (bb->to == second->getID() || bb->to == 0xFFFFFFFF) {
// printf("%d\n", bb->autoborder);
return bb;
}
}
}
for (std::vector::iterator it = second->borders.begin(); it != second->borders.end(); ++it) {
BorderBlock* bb = *it;
if (!bb->outer) {
continue;
} else if (bb->to == first->getID()) {
// printf("%d\n", bb->autoborder);
return bb;
} else if (bb->to == 0xFFFFFFFF) {
// printf("%d\n", bb->autoborder);
return bb;
}
}
} else if (first->hasInnerBorder()) {
for (std::vector::iterator it = first->borders.begin(); it != first->borders.end(); ++it) {
BorderBlock* bb = *it;
if (bb->outer) {
continue;
} else if (bb->to == second->getID()) {
// printf("%d\n", bb->autoborder);
return bb;
} else if (bb->to == 0xFFFFFFFF) {
// printf("%d\n", bb->autoborder);
return bb;
}
}
}
} else if (first->hasInnerZilchBorder()) {
for (std::vector::iterator it = first->borders.begin(); it != first->borders.end(); ++it) {
BorderBlock* bb = *it;
if (bb->outer) {
continue;
} else if (bb->to == 0) {
// printf("%d\n", bb->autoborder);
return bb;
}
}
}
} else if (second && second->hasOuterZilchBorder()) {
for (std::vector::iterator it = second->borders.begin(); it != second->borders.end(); ++it) {
BorderBlock* bb = *it;
if (!bb->outer) {
continue;
} else if (bb->to == 0) {
// printf("%d\n", bb->autoborder);
return bb;
}
}
}
// printf("None\n");
return nullptr;
}
void GroundBrush::doBorders(BaseMap* map, Tile* tile) {
static const auto extractGroundBrushFromTile = [](BaseMap* map, uint32_t x, uint32_t y, uint32_t z) -> GroundBrush* {
Tile* tile = map->getTile(x, y, z);
if (tile) {
return tile->getGroundBrush();
}
return nullptr;
};
ASSERT(tile);
GroundBrush* borderBrush;
if (tile->ground) {
borderBrush = tile->ground->getGroundBrush();
} else {
borderBrush = nullptr;
}
const Position &position = tile->getPosition();
uint32_t x = position.x;
uint32_t y = position.y;
uint32_t z = position.z;
// Pair of visited / what border type
std::pair neighbours[8];
if (x == 0) {
if (y == 0) {
neighbours[0] = { false, nullptr };
neighbours[1] = { false, nullptr };
neighbours[2] = { false, nullptr };
neighbours[3] = { false, nullptr };
neighbours[4] = { false, extractGroundBrushFromTile(map, x + 1, y, z) };
neighbours[5] = { false, nullptr };
neighbours[6] = { false, extractGroundBrushFromTile(map, x, y + 1, z) };
neighbours[7] = { false, extractGroundBrushFromTile(map, x + 1, y + 1, z) };
} else {
neighbours[0] = { false, nullptr };
neighbours[1] = { false, extractGroundBrushFromTile(map, x, y - 1, z) };
neighbours[2] = { false, extractGroundBrushFromTile(map, x + 1, y - 1, z) };
neighbours[3] = { false, nullptr };
neighbours[4] = { false, extractGroundBrushFromTile(map, x + 1, y, z) };
neighbours[5] = { false, nullptr };
neighbours[6] = { false, extractGroundBrushFromTile(map, x, y + 1, z) };
neighbours[7] = { false, extractGroundBrushFromTile(map, x + 1, y + 1, z) };
}
} else if (y == 0) {
neighbours[0] = { false, nullptr };
neighbours[1] = { false, nullptr };
neighbours[2] = { false, nullptr };
neighbours[3] = { false, extractGroundBrushFromTile(map, x - 1, y, z) };
neighbours[4] = { false, extractGroundBrushFromTile(map, x + 1, y, z) };
neighbours[5] = { false, extractGroundBrushFromTile(map, x - 1, y + 1, z) };
neighbours[6] = { false, extractGroundBrushFromTile(map, x, y + 1, z) };
neighbours[7] = { false, extractGroundBrushFromTile(map, x + 1, y + 1, z) };
} else {
neighbours[0] = { false, extractGroundBrushFromTile(map, x - 1, y - 1, z) };
neighbours[1] = { false, extractGroundBrushFromTile(map, x, y - 1, z) };
neighbours[2] = { false, extractGroundBrushFromTile(map, x + 1, y - 1, z) };
neighbours[3] = { false, extractGroundBrushFromTile(map, x - 1, y, z) };
neighbours[4] = { false, extractGroundBrushFromTile(map, x + 1, y, z) };
neighbours[5] = { false, extractGroundBrushFromTile(map, x - 1, y + 1, z) };
neighbours[6] = { false, extractGroundBrushFromTile(map, x, y + 1, z) };
neighbours[7] = { false, extractGroundBrushFromTile(map, x + 1, y + 1, z) };
}
static std::vector specificList;
specificList.clear();
std::vector borderList;
for (int32_t i = 0; i < 8; ++i) {
auto &neighbourPair = neighbours[i];
if (neighbourPair.first) {
continue;
}
// printf("Checking neighbour #%d\n", i);
// printf("\tNeighbour not checked before\n");
GroundBrush* other = neighbourPair.second;
if (borderBrush) {
if (other) {
// printf("\tNeighbour has brush\n");
if (other->getID() == borderBrush->getID()) {
// printf("\tNeighbour has same brush as we\n");
continue;
}
if (other->hasOuterBorder() || borderBrush->hasInnerBorder()) {
bool only_mountain = false;
if (/*!borderBrush->hasInnerBorder() && */ (other->friendOf(borderBrush) || borderBrush->friendOf(other))) {
if (!other->hasOptionalBorder()) {
continue;
}
only_mountain = true;
}
uint32_t tiledata = 0;
for (int32_t j = i; j < 8; ++j) {
auto &otherPair = neighbours[j];
if (!otherPair.first && otherPair.second && otherPair.second->getID() == other->getID()) {
otherPair.first = true;
tiledata |= 1 << j;
}
}
if (tiledata != 0) {
// Add mountain if appropriate!
if (other->hasOptionalBorder() && tile->hasOptionalBorder()) {
BorderCluster borderCluster;
borderCluster.alignment = tiledata;
borderCluster.z = 0x7FFFFFFF; // Above all other borders
borderCluster.border = other->optional_border;
borderList.push_back(borderCluster);
if (other->useSoloOptionalBorder()) {
only_mountain = true;
}
}
if (!only_mountain) {
const BorderBlock* borderBlock = getBrushTo(borderBrush, other);
if (borderBlock) {
bool found = false;
for (BorderCluster &borderCluster : borderList) {
if (borderCluster.border == borderBlock->autoborder) {
borderCluster.alignment |= tiledata;
if (borderCluster.z < other->getZ()) {
borderCluster.z = other->getZ();
}
if (!borderBlock->specific_cases.empty()) {
if (std::find(specificList.begin(), specificList.end(), borderBlock) == specificList.end()) {
specificList.push_back(borderBlock);
}
}
found = true;
break;
}
}
if (!found) {
BorderCluster borderCluster;
borderCluster.alignment = tiledata;
borderCluster.z = other->getZ();
borderCluster.border = borderBlock->autoborder;
borderList.push_back(borderCluster);
if (!borderBlock->specific_cases.empty()) {
if (std::find(specificList.begin(), specificList.end(), borderBlock) == specificList.end()) {
specificList.push_back(borderBlock);
}
}
}
}
}
}
}
} else if (borderBrush->hasInnerZilchBorder()) {
// Border against nothing (or undefined tile)
uint32_t tiledata = 0;
for (int32_t j = i; j < 8; ++j) {
auto &otherPair = neighbours[j];
if (!otherPair.first && !otherPair.second) {
otherPair.first = true;
tiledata |= 1 << j;
}
}
if (tiledata != 0) {
const BorderBlock* borderBlock = getBrushTo(borderBrush, nullptr);
if (!borderBlock) {
continue;
}
if (borderBlock->autoborder) {
BorderCluster borderCluster;
borderCluster.alignment = tiledata;
borderCluster.z = 5000;
borderCluster.border = borderBlock->autoborder;
borderList.push_back(borderCluster);
}
if (!borderBlock->specific_cases.empty()) {
if (std::find(specificList.begin(), specificList.end(), borderBlock) == specificList.end()) {
specificList.push_back(borderBlock);
}
}
}
continue;
}
} else if (other && other->hasOuterZilchBorder()) {
// Border against nothing (or undefined tile)
uint32_t tiledata = 0;
for (int32_t j = i; j < 8; ++j) {
auto &otherPair = neighbours[j];
if (!otherPair.first && otherPair.second && otherPair.second->getID() == other->getID()) {
otherPair.first = true;
tiledata |= 1 << j;
}
}
if (tiledata != 0) {
const BorderBlock* borderBlock = getBrushTo(nullptr, other);
if (borderBlock) {
if (borderBlock->autoborder) {
BorderCluster borderCluster;
borderCluster.alignment = tiledata;
borderCluster.z = other->getZ();
borderCluster.border = borderBlock->autoborder;
borderList.push_back(borderCluster);
}
if (!borderBlock->specific_cases.empty()) {
if (std::find(specificList.begin(), specificList.end(), borderBlock) == specificList.end()) {
specificList.push_back(borderBlock);
}
}
}
// Add mountain if appropriate!
if (other->hasOptionalBorder() && tile->hasOptionalBorder()) {
BorderCluster borderCluster;
borderCluster.alignment = tiledata;
borderCluster.z = 0x7FFFFFFF; // Above other zilch borders
borderCluster.border = other->optional_border;
borderList.push_back(borderCluster);
} else {
tile->setOptionalBorder(false);
}
}
}
// Check tile as done
neighbourPair.first = true;
}
std::sort(borderList.begin(), borderList.end());
tile->cleanBorders();
while (!borderList.empty()) {
BorderCluster &borderCluster = borderList.back();
if (!borderCluster.border) {
borderList.pop_back();
continue;
}
BorderType directions[4] = {
static_cast((border_types[borderCluster.alignment] & 0x000000FF) >> 0),
static_cast((border_types[borderCluster.alignment] & 0x0000FF00) >> 8),
static_cast((border_types[borderCluster.alignment] & 0x00FF0000) >> 16),
static_cast((border_types[borderCluster.alignment] & 0xFF000000) >> 24)
};
for (int32_t i = 0; i < 4; ++i) {
BorderType direction = directions[i];
if (direction == BORDER_NONE) {
break;
}
if (borderCluster.border->tiles[direction]) {
tile->addBorderItem(Item::Create(borderCluster.border->tiles[direction]));
} else {
if (direction == NORTHWEST_DIAGONAL) {
tile->addBorderItem(Item::Create(borderCluster.border->tiles[WEST_HORIZONTAL]));
tile->addBorderItem(Item::Create(borderCluster.border->tiles[NORTH_HORIZONTAL]));
} else if (direction == NORTHEAST_DIAGONAL) {
tile->addBorderItem(Item::Create(borderCluster.border->tiles[EAST_HORIZONTAL]));
tile->addBorderItem(Item::Create(borderCluster.border->tiles[NORTH_HORIZONTAL]));
} else if (direction == SOUTHWEST_DIAGONAL) {
tile->addBorderItem(Item::Create(borderCluster.border->tiles[SOUTH_HORIZONTAL]));
tile->addBorderItem(Item::Create(borderCluster.border->tiles[WEST_HORIZONTAL]));
} else if (direction == SOUTHEAST_DIAGONAL) {
tile->addBorderItem(Item::Create(borderCluster.border->tiles[SOUTH_HORIZONTAL]));
tile->addBorderItem(Item::Create(borderCluster.border->tiles[EAST_HORIZONTAL]));
}
}
}
borderList.pop_back();
}
for (const BorderBlock* borderBlock : specificList) {
for (const SpecificCaseBlock* specificCaseBlock : borderBlock->specific_cases) {
/*
printf("New round\n");
if(specificCaseBlock->to_replace_id == 0) {
continue;
}
if(specificCaseBlock->with_id == 0) {
continue;
}
*/
uint32_t matches = 0;
for (Item* item : tile->items) {
if (!item->isBorder()) {
break;
}
if (specificCaseBlock->match_group > 0) {
// printf("Matching %d == %d : %d == %d\n", item->getBorderGroup(), specificCaseBlock->match_group, item->getBorderAlignment(), specificCaseBlock->group_match_alignment);
if (item->getBorderGroup() == specificCaseBlock->match_group && item->getBorderAlignment() == specificCaseBlock->group_match_alignment) {
// printf("Successfully matched %d == %d : %d == %d\n", item->getBorderGroup(), specificCaseBlock->match_group, item->getBorderAlignment(), specificCaseBlock->group_match_alignment);
++matches;
continue;
}
}
// printf("\tInvestigating first item id:%d\n", item->getID());
for (uint16_t matchId : specificCaseBlock->items_to_match) {
if (item->getID() == matchId) {
// printf("\t\tMatched item id %d\n", item->getID());
++matches;
}
}
}
// printf("\t\t%d matches of %d\n", matches, scb->items_to_match.size());
if (matches == specificCaseBlock->items_to_match.size()) {
auto &tileItems = tile->items;
auto it = tileItems.begin();
if (specificCaseBlock->delete_all) {
// Delete all matching borders
while (it != tileItems.end()) {
Item* item = *it;
if (!item->isBorder()) {
break;
}
bool inc = true;
for (uint16_t matchId : specificCaseBlock->items_to_match) {
if (item->getID() == matchId) {
delete item;
it = tileItems.erase(it);
inc = false;
break;
}
}
if (inc) {
++it;
}
}
} else {
// All matched, replace!
while (it != tileItems.end()) {
Item* item = *it;
if (!item->isBorder()) {
return;
}
if (item->getID() == specificCaseBlock->to_replace_id) {
item->setID(specificCaseBlock->with_id);
return;
}
++it;
}
}
}
}
}
}