Skip to content

Commit

Permalink
feat: monsters weight (#122)
Browse files Browse the repository at this point in the history
This adds the possibility to add stacked monsters and set weight into the stacked monsters to randomize their spawn.
  • Loading branch information
phacUFPE authored Oct 2, 2024
1 parent aa6ea40 commit 45acebc
Show file tree
Hide file tree
Showing 26 changed files with 428 additions and 239 deletions.
14 changes: 7 additions & 7 deletions source/common_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ struct MapConversionContext {
NpcMap npcType;

void operator()(Map &map, Tile* tile, long long done) {
if (tile->monster) {
MonsterMap::iterator f = monsterType.find(tile->monster->getName());
if (f == monsterType.end()) {
MonsterInfo info = {
tile->monster->getName(),
tile->monster->getLookType()
for (const auto monster : tile->monsters) {
const auto it = monsterType.find(monster->getName());
if (it == monsterType.end()) {
MonsterInfo monsterInfo = {
monster->getName(),
monster->getLookType()
};
monsterType[tile->monster->getName()] = info;
monsterType[monster->getName()] = monsterInfo;
}
}
if (tile->npc) {
Expand Down
44 changes: 32 additions & 12 deletions source/copybuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void CopyBuffer::copy(Editor &editor, int floor) {

int tile_count = 0;
int item_count = 0;
int monsterCount = 0;
copyPos = Position(0xFFFF, 0xFFFF, floor);

for (Tile* tile : editor.getSelection()) {
Expand All @@ -83,9 +84,12 @@ void CopyBuffer::copy(Editor &editor, int floor) {
}

// Monster
if (tile->monster && tile->monster->isSelected()) {
copied_tile->monster = tile->monster->deepCopy();
}
const auto monstersSelection = tile->getSelectedMonsters();
std::ranges::for_each(monstersSelection, [&](const auto monster) {
++monsterCount;
copied_tile->addMonster(monster->deepCopy());
});

if (tile->spawnMonster && tile->spawnMonster->isSelected()) {
copied_tile->spawnMonster = tile->spawnMonster->deepCopy();
}
Expand All @@ -108,9 +112,16 @@ void CopyBuffer::copy(Editor &editor, int floor) {
}
}

std::ostringstream ss;
ss << "Copied " << tile_count << " tile" << (tile_count > 1 ? "s" : "") << " (" << item_count << " item" << (item_count > 1 ? "s" : "") << ")";
g_gui.SetStatusText(wxstr(ss.str()));
fmt::dynamic_format_arg_store<fmt::format_context> store;

store.push_back(tile_count);
tile_count > 1 ? store.push_back("s") : store.push_back("");
store.push_back(item_count);
item_count > 1 ? store.push_back("s") : store.push_back("");
store.push_back(monsterCount);
monsterCount > 1 ? store.push_back("s") : store.push_back("");

g_gui.SetStatusText(fmt::vformat("Copied {} tile{}, {} item{} and {} monster{}", store));
}

void CopyBuffer::cut(Editor &editor, int floor) {
Expand All @@ -125,6 +136,7 @@ void CopyBuffer::cut(Editor &editor, int floor) {
Map &map = editor.getMap();
int tile_count = 0;
int item_count = 0;
int monsterCount = 0;
copyPos = Position(0xFFFF, 0xFFFF, floor);

BatchAction* batch = editor.createBatch(ACTION_CUT_TILES);
Expand Down Expand Up @@ -153,9 +165,10 @@ void CopyBuffer::cut(Editor &editor, int floor) {
}

// Monster
if (newtile->monster && newtile->monster->isSelected()) {
copied_tile->monster = newtile->monster;
newtile->monster = nullptr;
const auto monsterSelection = newtile->popSelectedMonsters();
for (auto monsterIt = monsterSelection.begin(); monsterIt != monsterSelection.end(); ++monsterIt) {
++monsterCount;
copied_tile->monsters.emplace_back(*monsterIt);
}

if (newtile->spawnMonster && newtile->spawnMonster->isSelected()) {
Expand Down Expand Up @@ -226,9 +239,16 @@ void CopyBuffer::cut(Editor &editor, int floor) {
editor.addBatch(batch);
editor.updateActions();

std::stringstream ss;
ss << "Cut out " << tile_count << " tile" << (tile_count > 1 ? "s" : "") << " (" << item_count << " item" << (item_count > 1 ? "s" : "") << ")";
g_gui.SetStatusText(wxstr(ss.str()));
fmt::dynamic_format_arg_store<fmt::format_context> store;

store.push_back(tile_count);
tile_count > 1 ? store.push_back("s") : store.push_back("");
store.push_back(item_count);
item_count > 1 ? store.push_back("s") : store.push_back("");
store.push_back(monsterCount);
monsterCount > 1 ? store.push_back("s") : store.push_back("");

g_gui.SetStatusText(fmt::vformat("Cut out {} tile{}, {} item{} and {} monster{}", store));
}

void CopyBuffer::paste(Editor &editor, const Position &toPosition) {
Expand Down
28 changes: 20 additions & 8 deletions source/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1100,10 +1100,10 @@ void Editor::moveSelection(const Position &offset) {
new_tile->spawnMonster = nullptr;
}
// Move monster
if (new_tile->monster && new_tile->monster->isSelected()) {
storage_tile->monster = new_tile->monster;
new_tile->monster = nullptr;
}
const auto monstersSelection = new_tile->popSelectedMonsters();
std::ranges::for_each(monstersSelection, [&](const auto monster) {
storage_tile->addMonster(monster);
});
// Move npc
if (new_tile->npc && new_tile->npc->isSelected()) {
storage_tile->npc = new_tile->npc;
Expand Down Expand Up @@ -1325,6 +1325,7 @@ void Editor::destroySelection() {
} else {
int tile_count = 0;
int item_count = 0;
int monsterCount = 0;
PositionList tilestoborder;

BatchAction* batch = actionQueue->createBatch(ACTION_DELETE_TILES);
Expand All @@ -1342,11 +1343,22 @@ void Editor::destroySelection() {
// Delete the items from the tile
delete *iit;
}
// Monster
if (newtile->monster && newtile->monster->isSelected()) {
delete newtile->monster;
newtile->monster = nullptr;

auto monstersSelection = newtile->popSelectedMonsters();
std::ranges::for_each(monstersSelection, [&](auto monster) {
++monsterCount;
delete monster;
});
// Clear the vector to avoid being used anywhere else in this block with nullptrs
monstersSelection.clear();

/*
for (auto monsterIt = monstersSelection.begin(); monsterIt != monstersSelection.end(); ++monsterIt) {
++monsterCount;
// Delete the monsters from the tile
delete *monsterIt;
}
*/

if (newtile->spawnMonster && newtile->spawnMonster->isSelected()) {
delete newtile->spawnMonster;
Expand Down
1 change: 1 addition & 0 deletions source/gui_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ enum EditorActionID {
PALETTE_MONSTER_SPAWN_TIME,
PALETTE_MONSTER_SPAWN_SIZE,
PALETTE_MONSTER_SPAWN_DENSITY,
PALETTE_MONSTER_DEFAULT_WEIGHT,

PALETTE_NPC_TILESET_CHOICE,
PALETTE_NPC_LISTBOX,
Expand Down
70 changes: 40 additions & 30 deletions source/iomap_otbm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,11 @@ bool IOMapOTBM::loadSpawnsMonster(Map &map, pugi::xml_document &doc) {
spawntime = g_settings.getInteger(Config::DEFAULT_SPAWN_MONSTER_TIME);
}

auto weight = static_cast<uint8_t>(monsterNode.attribute("weight").as_uint());
if (weight == 0) {
weight = static_cast<uint8_t>(g_settings.getInteger(Config::MONSTER_DEFAULT_WEIGHT));
}

Direction direction = NORTH;
int dir = monsterNode.attribute("direction").as_int(-1);
if (dir >= DIRECTION_FIRST && dir <= DIRECTION_LAST) {
Expand Down Expand Up @@ -1090,16 +1095,14 @@ bool IOMapOTBM::loadSpawnsMonster(Map &map, pugi::xml_document &doc) {
}

if (!monsterTile) {
wxString err;
err << "Discarding monster \"" << name << "\" at " << monsterPosition.x << ":" << monsterPosition.y << ":" << monsterPosition.z << " due to invalid position.";
warnings.Add(err);
const auto error = fmt::format("Discarding monster \"{}\" at {}:{}:{} due to invalid position", name, monsterPosition.x, monsterPosition.y, monsterPosition.z);
warnings.Add(error);
break;
}

if (monsterTile->monster) {
wxString err;
err << "Duplicate monster \"" << name << "\" at " << monsterPosition.x << ":" << monsterPosition.y << ":" << monsterPosition.z << " was discarded.";
warnings.Add(err);
if (monsterTile->isMonsterRepeated(name)) {
const auto error = fmt::format("Duplicate monster \"{}\" at {}:{}:{} was discarded.", name, monsterPosition.x, monsterPosition.y, monsterPosition.z);
warnings.Add(error);
break;
}

Expand All @@ -1111,7 +1114,8 @@ bool IOMapOTBM::loadSpawnsMonster(Map &map, pugi::xml_document &doc) {
Monster* monster = newd Monster(type);
monster->setDirection(direction);
monster->setSpawnMonsterTime(spawntime);
monsterTile->monster = monster;
monster->setWeight(weight);
monsterTile->monsters.emplace_back(monster);

if (monsterTile->getLocation()->getSpawnMonsterCount() == 0) {
// No monster spawn, create a newd one
Expand Down Expand Up @@ -1762,30 +1766,36 @@ bool IOMapOTBM::saveSpawns(Map &map, pugi::xml_document &doc) {
int32_t radius = spawnMonster->getSize();
spawnNode.append_attribute("radius") = radius;

for (int32_t y = -radius; y <= radius; ++y) {
for (int32_t x = -radius; x <= radius; ++x) {
Tile* monster_tile = map.getTile(spawnPosition + Position(x, y, 0));
if (monster_tile) {
Monster* monster = monster_tile->monster;
if (monster && !monster->isSaved()) {
pugi::xml_node monsterNode = spawnNode.append_child("monster");
monsterNode.append_attribute("name") = monster->getName().c_str();
monsterNode.append_attribute("x") = x;
monsterNode.append_attribute("y") = y;
monsterNode.append_attribute("z") = spawnPosition.z;
auto monsterSpawnTime = monster->getSpawnMonsterTime();
if (monsterSpawnTime > std::numeric_limits<uint32_t>::max() || monsterSpawnTime < std::numeric_limits<uint32_t>::min()) {
monsterSpawnTime = 60;
}
for (auto y = -radius; y <= radius; ++y) {
for (auto x = -radius; x <= radius; ++x) {
const auto monsterTile = map.getTile(spawnPosition + Position(x, y, 0));
if (monsterTile) {
for (const auto monster : monsterTile->monsters) {
if (monster && !monster->isSaved()) {
pugi::xml_node monsterNode = spawnNode.append_child("monster");
monsterNode.append_attribute("name") = monster->getName().c_str();
monsterNode.append_attribute("x") = x;
monsterNode.append_attribute("y") = y;
monsterNode.append_attribute("z") = spawnPosition.z;
auto monsterSpawnTime = monster->getSpawnMonsterTime();
if (monsterSpawnTime > std::numeric_limits<uint32_t>::max() || monsterSpawnTime < std::numeric_limits<uint32_t>::min()) {
monsterSpawnTime = 60;
}

monsterNode.append_attribute("spawntime") = monsterSpawnTime;
if (monster->getDirection() != NORTH) {
monsterNode.append_attribute("direction") = monster->getDirection();
}
monsterNode.append_attribute("spawntime") = monsterSpawnTime;
if (monster->getDirection() != NORTH) {
monsterNode.append_attribute("direction") = monster->getDirection();
}

// Mark as saved
monster->save();
monsterList.push_back(monster);
if (monsterTile->monsters.size() > 1) {
const auto weight = monster->getWeight();
monsterNode.append_attribute("weight") = weight > 0 ? weight : g_settings.getInteger(Config::MONSTER_DEFAULT_WEIGHT);
}

// Mark as saved
monster->save();
monsterList.push_back(monster);
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions source/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ _Ret_bytecap_(_Size) inline void* __CRTDECL operator new[](size_t _Size, const c

#include <asio.hpp>
#include <fmt/core.h>
#include <fmt/format.h>
#include <fmt/args.h>
#include <nlohmann/json.hpp>

#include "definitions.h"
Expand Down
31 changes: 19 additions & 12 deletions source/main_menubar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,7 @@ void MainMenuBar::OnMapRemoveEmptyMonsterSpawns(wxCommandEvent &WXUNUSED(event))
g_gui.CreateLoadBar("Searching map for empty monsters spawns to remove...");

Map &map = g_gui.GetCurrentMap();
MonsterVector monster;
MonsterVector monsters;
TileVector toDeleteSpawns;
for (const auto &spawnPosition : map.spawnsMonster) {
Tile* tile = map.getTile(spawnPosition);
Expand All @@ -1551,13 +1551,22 @@ void MainMenuBar::OnMapRemoveEmptyMonsterSpawns(wxCommandEvent &WXUNUSED(event))
const int32_t radius = tile->spawnMonster->getSize();

bool empty = true;
for (int32_t y = -radius; y <= radius; ++y) {
for (int32_t x = -radius; x <= radius; ++x) {
Tile* creature_tile = map.getTile(spawnPosition + Position(x, y, 0));
if (creature_tile && creature_tile->monster && !creature_tile->monster->isSaved()) {
creature_tile->monster->save();
monster.push_back(creature_tile->monster);
empty = false;
for (auto y = -radius; y <= radius; ++y) {
for (auto x = -radius; x <= radius; ++x) {
const auto creatureTile = map.getTile(spawnPosition + Position(x, y, 0));
if (creatureTile) {
for (const auto monster : creatureTile->monsters) {
if (empty) {
empty = false;
}

if (monster->isSaved()) {
continue;
}

monster->save();
monsters.push_back(monster);
}
}
}
}
Expand All @@ -1567,7 +1576,7 @@ void MainMenuBar::OnMapRemoveEmptyMonsterSpawns(wxCommandEvent &WXUNUSED(event))
}
}

for (Monster* monster : monster) {
for (const auto monster : monsters) {
monster->reset();
}

Expand Down Expand Up @@ -1847,9 +1856,7 @@ void MainMenuBar::OnMapStatistics(wxCommandEvent &WXUNUSED(event)) {
spawn_npc_count += 1;
}

if (tile->monster) {
monster_count += 1;
}
monster_count += tile->monsters.size();

if (tile->npc) {
npc_count += 1;
Expand Down
13 changes: 7 additions & 6 deletions source/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,12 +893,13 @@ int64_t RemoveMonstersOnMap(Map &map, bool selectedOnly) {
++it;
continue;
}
if (tile->monster) {
delete tile->monster;
tile->monster = nullptr;
for (auto monster : tile->monsters) {
delete monster;
++removed;
}

tile->monsters.clear();

++it;
}
return removed;
Expand All @@ -919,10 +920,10 @@ std::pair<int64_t, std::unordered_map<std::string, int64_t>> CountMonstersOnMap(
++it;
continue;
}
if (tile->monster) {

for (const auto monster : tile->monsters) {
++total;
std::string monsterName = tile->monster->getName();
++monsterCount[monsterName];
++monsterCount[monster->getName()];
}

++it;
Expand Down
Loading

0 comments on commit 45acebc

Please sign in to comment.