Skip to content

Commit

Permalink
Merge pull request OpenEnroth#1325 from captainurist/macos_fixes_389
Browse files Browse the repository at this point in the history
More fixes in monster enums
  • Loading branch information
captainurist authored Oct 9, 2023
2 parents 3c0581f + 1113281 commit e56071d
Show file tree
Hide file tree
Showing 31 changed files with 504 additions and 370 deletions.
1 change: 1 addition & 0 deletions src/Bin/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(BIN_CODEGEN_SOURCES
set(BIN_CODEGEN_HEADERS
CodeGenOptions.h
CodeGenEnums.h
CodeGenFunctions.h
CodeGenMap.h)

if(NOT BUILD_PLATFORM STREQUAL "android")
Expand Down
141 changes: 97 additions & 44 deletions src/Bin/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <utility>

Expand All @@ -14,7 +15,10 @@
#include "Engine/GameResourceManager.h"
#include "Engine/MapInfo.h"

#include "GUI/UI/Houses/TownHall.h"

#include "Library/Lod/LodReader.h"
#include "Library/Random/Random.h"
#include "Library/Serialization/EnumSerialization.h"

#include "Utility/Format.h"
Expand All @@ -30,19 +34,6 @@ static auto contains = [](const std::string &haystack, const std::string &needle
return haystack.find(needle) != std::string::npos;
};

static std::string toUpperCaseEnum(const std::string &string) {
std::string result;
for (char c : trim(string)) {
if (isalnum(c)) {
result += static_cast<char>(toupper(c));
} else if (isspace(c) || c == '/' || c == '-') {
if (!result.ends_with('_'))
result += '_';
}
}
return result;
}

int runItemIdCodeGen(CodeGenOptions options, GameResourceManager *resourceManager) {
ItemTable itemTable;
itemTable.Initialize(resourceManager);
Expand Down Expand Up @@ -256,9 +247,26 @@ int runHouseIdCodeGen(CodeGenOptions options, GameResourceManager *resourceManag
return 0;
}

std::string cleanupMonsterEnumName(std::string enumName) {
if (enumName.starts_with("ZBLASTERGUY") || enumName.starts_with("ZULTRA_DRAGON"))
enumName = enumName.substr(1);
MonsterStats loadMonsterStats(GameResourceManager *resourceManager) {
TriBlob dmonBlobs;
dmonBlobs.mm7 = resourceManager->getEventsFile("dmonlist.bin");

pMonsterList = new MonsterList;
deserialize(dmonBlobs, pMonsterList);

MonsterStats result;
result.Initialize(resourceManager->getEventsFile("monsters.txt"));
return result;
}

std::string cleanupMonsterIdEnumName(std::string enumName) {
for (const char *prefix : {"ZBLASTERGUY", "ZULTRA_DRAGON", })
if (enumName.starts_with(prefix))
enumName = enumName.substr(1);

for (const char *prefix : {"ZCAT", "ZCHICKEN", "ZDOG", "ZRAT"})
if (enumName.starts_with(prefix))
enumName = "UNUSED_" + enumName.substr(1);

enumName = replaceAll(enumName, "MALEA", "MALE_A");
enumName = replaceAll(enumName, "MALEB", "MALE_B");
Expand All @@ -268,23 +276,17 @@ std::string cleanupMonsterEnumName(std::string enumName) {
}

int runMonsterIdCodeGen(CodeGenOptions options, GameResourceManager *resourceManager) {
TriBlob dmonBlobs;
dmonBlobs.mm7 = resourceManager->getEventsFile("dmonlist.bin");

pMonsterList = new MonsterList;
deserialize(dmonBlobs, pMonsterList);

MonsterStats monsterStats;
monsterStats.Initialize(resourceManager->getEventsFile("monsters.txt"));
MonsterStats monsterStats = loadMonsterStats(resourceManager);

CodeGenMap map;
map.insert(MONSTER_INVALID, "INVALID", "");

for (const MonsterId i : monsterStats.pInfos.indices()) {
const MonsterInfo &desc = monsterStats.pInfos[i];
std::string enumName = cleanupMonsterEnumName(toUpperCaseEnum(desc.pPictureName));
for (const MonsterId i : allMonsters()) {
const MonsterDesc &desc = pMonsterList->pMonsters[i];
const MonsterInfo &info = monsterStats.pInfos[i];
std::string enumName = cleanupMonsterIdEnumName(toUpperCaseEnum(desc.pMonsterName));

std::string comment = desc.pName;
std::string comment = info.pName;
if (comment == "peasant")
comment = "Peasant";
if (!comment.empty())
Expand All @@ -297,32 +299,31 @@ int runMonsterIdCodeGen(CodeGenOptions options, GameResourceManager *resourceMan
return 0;
}

int runMonsterTypeCodeGen(CodeGenOptions options, GameResourceManager *resourceManager) {
TriBlob dmonBlobs;
dmonBlobs.mm7 = resourceManager->getEventsFile("dmonlist.bin");
std::string cleanupMonsterTypeEnumName(std::string enumName) {
enumName = cleanupMonsterIdEnumName(enumName);

pMonsterList = new MonsterList;
deserialize(dmonBlobs, pMonsterList);
if (enumName.ends_with("_A")) {
enumName.resize(enumName.size() - 2);
} else if (!enumName.empty()) {
throw Exception("Invalid monster id name");
}

return enumName;
}

MonsterStats monsterStats;
monsterStats.Initialize(resourceManager->getEventsFile("monsters.txt"));
int runMonsterTypeCodeGen(CodeGenOptions options, GameResourceManager *resourceManager) {
MonsterStats monsterStats = loadMonsterStats(resourceManager);

CodeGenMap map;
map.insert(MONSTER_TYPE_INVALID, "INVALID", "");

int counter = 0;
for (const MonsterId i : monsterStats.pInfos.indices()) {
for (const MonsterId i : allMonsters()) {
if (++counter % 3 != 1)
continue;

const MonsterInfo &desc = monsterStats.pInfos[i];
std::string enumName = cleanupMonsterEnumName(toUpperCaseEnum(desc.pPictureName));

if (enumName.ends_with("_A")) {
enumName.resize(enumName.size() - 2);
} else if (!enumName.empty()) {
throw Exception("Invalid monster id name");
}
const MonsterDesc &desc = pMonsterList->pMonsters[i];
std::string enumName = cleanupMonsterTypeEnumName(toUpperCaseEnum(desc.pMonsterName));

map.insert(monsterTypeForMonsterId(i), enumName, "");
}
Expand All @@ -331,6 +332,57 @@ int runMonsterTypeCodeGen(CodeGenOptions options, GameResourceManager *resourceM
return 0;
}

int runBountyHuntCodeGen(CodeGenOptions options, GameResourceManager *resourceManager) {
// Fill bounty hunt map.
grng = RandomEngine::create(RANDOM_ENGINE_SEQUENTIAL);
IndexedArray<std::unordered_set<MonsterId>, HOUSE_FIRST_TOWN_HALL, HOUSE_LAST_TOWN_HALL> monstersByTownHall;
for (const HouseId townHall : allTownhallHouses()) {
grng->seed(0);
while(true) {
MonsterId monsterId = GUIWindow_TownHall::randomMonsterForHunting(townHall);
if (!monstersByTownHall[townHall].insert(monsterId).second)
break;
}
}

// Invert the map.
std::unordered_map<MonsterId, std::unordered_set<HouseId>> townHallsByMonster;
for (const HouseId townHall : allTownhallHouses())
for (const MonsterId monsterId : monstersByTownHall[townHall])
townHallsByMonster[monsterId].insert(townHall);

// Reduce the map to monster types & check that it's actually reducible.
std::unordered_map<MonsterType, std::unordered_set<HouseId>> townHallsByMonsterType;
for (const MonsterId monsterId : allMonsters()) {
MonsterType monsterType = monsterTypeForMonsterId(monsterId);
if (townHallsByMonsterType.contains(monsterType) && townHallsByMonsterType[monsterType] != townHallsByMonster[monsterId])
throw Exception("Invalid bounty hunt record");

townHallsByMonsterType[monsterType] = townHallsByMonster[monsterId];
}

// Prepare output table.
std::vector<std::array<std::string, 8>> table;
for (const MonsterType monsterType : allMonsterTypes()) {
auto &line = table.emplace_back();
line[0] = fmt::format("{{{}, ", toString(monsterType));
line[1] = "{";
for (const HouseId townHall : townHallsByMonsterType[monsterType])
line[2 + std::to_underlying(townHall) - std::to_underlying(HOUSE_FIRST_TOWN_HALL)] = toString(townHall) + ", ";
for (size_t i = 6; i >= 2; i--) {
if (!line[i].empty()) {
line[i].resize(line[i].size() - 2); // Drop the last ", ".
break;
}
}
line[7] = "}},";
}

// Dump!
dumpAligned(stdout, " ", table);
return 0;
}

int platformMain(int argc, char **argv) {
try {
CodeGenOptions options = CodeGenOptions::parse(argc, argv);
Expand All @@ -349,6 +401,7 @@ int platformMain(int argc, char **argv) {
case CodeGenOptions::SUBCOMMAND_HOUSE_ID: return runHouseIdCodeGen(std::move(options), &resourceManager);
case CodeGenOptions::SUBCOMMAND_MONSTER_ID: return runMonsterIdCodeGen(std::move(options), &resourceManager);
case CodeGenOptions::SUBCOMMAND_MONSTER_TYPE: return runMonsterTypeCodeGen(std::move(options), &resourceManager);
case CodeGenOptions::SUBCOMMAND_BOUNTY_HUNT: return runBountyHuntCodeGen(std::move(options), &resourceManager);
default:
assert(false);
return 1;
Expand Down
3 changes: 3 additions & 0 deletions src/Bin/CodeGen/CodeGenEnums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ MM_DEFINE_ENUM_SERIALIZATION_FUNCTIONS(BuildingType, CASE_INSENSITIVE, {
{BUILDING_SHADOW_GUILD, "SHADOW_GUILD"},
{BUILDING_ADVENTURERS_INN, "ADVENTURERS_INN"},
})

MM_DEFINE_ENUM_MAGIC_SERIALIZATION_FUNCTIONS(HouseId)
MM_DEFINE_ENUM_MAGIC_SERIALIZATION_FUNCTIONS(MonsterType)
3 changes: 3 additions & 0 deletions src/Bin/CodeGen/CodeGenEnums.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#pragma once

#include "Engine/Tables/BuildingTable.h"
#include "Engine/Objects/MonsterEnums.h"

#include "Library/Serialization/SerializationFwd.h"

MM_DECLARE_SERIALIZATION_FUNCTIONS(BuildingType)
MM_DECLARE_SERIALIZATION_FUNCTIONS(HouseId)
MM_DECLARE_SERIALIZATION_FUNCTIONS(MonsterType)
45 changes: 45 additions & 0 deletions src/Bin/CodeGen/CodeGenFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <cstdio>
#include <cctype>
#include <vector>
#include <array>
#include <string_view>
#include <string>
#include <algorithm>

#include "Utility/Format.h"

inline std::string toUpperCaseEnum(const std::string &string) {
std::string result;
for (char c : trim(string)) {
if (std::isalnum(c)) {
result += static_cast<char>(toupper(c));
} else if (std::isspace(c) || c == '/' || c == '-') {
if (!result.ends_with('_'))
result += '_';
}
}
return result;
}

template<size_t N>
void dumpAligned(FILE *file, std::string_view prefix, const std::vector<std::array<std::string, N>> &table) {
std::array<size_t, N> maxLengths;
maxLengths.fill(0);

for (const auto &line : table)
for (size_t i = 0; i < line.size(); i++)
maxLengths[i] = std::max(maxLengths[i], line[i].size());

for (const auto &line : table) {
std::string output = std::string(prefix);
for (size_t i = 0; i < line.size(); i++) {
output += line[i];
output += std::string(maxLengths[i] - line[i].size(), ' ');
}
while (output.ends_with(' ')) // Note that we don't trim front.
output.pop_back();
fmt::println(file, "{}", output);
}
}
19 changes: 6 additions & 13 deletions src/Bin/CodeGen/CodeGenMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "Utility/Workaround/ToUnderlying.h"
#include "Utility/Format.h"

#include "CodeGenFunctions.h"

class CodeGenMap {
public:
template<class Enum>
Expand Down Expand Up @@ -40,25 +42,16 @@ class CodeGenMap {
}

void dump(FILE *file, const std::string &prefix) {
std::vector<std::pair<std::string, std::string>> linesAndComments;
std::vector<std::array<std::string, 2>> linesAndComments;
for (const auto &[value, name] : _nameByValue) {
std::string comment = _commentByValue[value];
if (!comment.empty())
comment = " // " + comment;
comment = "// " + comment;

linesAndComments.emplace_back(fmt::format("{}{} = {}", prefix, name, value), comment);
linesAndComments.push_back({fmt::format("{}{} = {}, ", prefix, name, value), comment});
}

size_t maxLineLen = 0;
for (const auto &[line, _] : linesAndComments)
maxLineLen = std::max(maxLineLen, line.size());

for (const auto &[line, comment] : linesAndComments) {
std::string padding;
if (!comment.empty())
padding = std::string(maxLineLen - line.size(), ' ');
fmt::println(file, " {},{}{}", line, padding, comment);
}
dumpAligned(file, " ", linesAndComments);
}

private:
Expand Down
7 changes: 6 additions & 1 deletion src/Bin/CodeGen/CodeGenOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,21 @@ CodeGenOptions CodeGenOptions::parse(int argc, char **argv) {
CLI::App *monsterTypes = app->add_subcommand("monster_types", "Generate monster types enum")->fallthrough();
monsterTypes->callback([&] { result.subcommand = SUBCOMMAND_MONSTER_TYPE; });

CLI::App *bountyHunt = app->add_subcommand("bounty_hunt", "Generate monster type / town hall table for bounty hunts")->fallthrough();
bountyHunt->callback([&] { result.subcommand = SUBCOMMAND_BOUNTY_HUNT; });

try {
app->parse(argc, argv);
} catch (const CLI::ParseError &e) {
// TODO(captainurist): this is getting out of hand.
bool isHelp =
app->get_help_ptr()->as<bool>() ||
items->get_help_ptr()->as<bool>() ||
maps->get_help_ptr()->as<bool>() ||
beacons->get_help_ptr()->as<bool>() ||
monsters->get_help_ptr()->as<bool>() ||
monsterTypes->get_help_ptr()->as<bool>();
monsterTypes->get_help_ptr()->as<bool>() ||
bountyHunt->get_help_ptr()->as<bool>();
if (isHelp) {
app->exit(e);
result.helpPrinted = true;
Expand Down
1 change: 1 addition & 0 deletions src/Bin/CodeGen/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct CodeGenOptions : GameStarterOptions {
SUBCOMMAND_HOUSE_ID,
SUBCOMMAND_MONSTER_ID,
SUBCOMMAND_MONSTER_TYPE,
SUBCOMMAND_BOUNTY_HUNT,
};
using enum Subcommand;

Expand Down
5 changes: 1 addition & 4 deletions src/Engine/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -997,10 +997,7 @@ void Engine::_461103_load_level_sub() {
//{
// v3 = pActors[i].pMonsterInfo.uID;
v17 = 0;
if (pActors[i].monsterInfo.uID >= MONSTER_PEASANT_DWARF_FEMALE_A_A &&
pActors[i].monsterInfo.uID <= MONSTER_PEASANT_HUMAN2_FEMALE_C_C ||
pActors[i].monsterInfo.uID >= MONSTER_PEASANT_GOBLIN_FEMALE_A_A &&
pActors[i].monsterInfo.uID <= MONSTER_PEASANT_GOBLIN_MALE_C_C)
if (isPeasant(pActors[i].monsterInfo.uID))
v17 = 1;
// v1 = 0;
v4 = (std::to_underlying(pActors[i].monsterInfo.uID) - 1) % 3; // TODO(captainurist): encapsulate monster tier calculation.
Expand Down
2 changes: 1 addition & 1 deletion src/Engine/Graphics/Outdoor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2317,7 +2317,7 @@ void UpdateActors_ODM() {
pActors[Actor_ITR].aiState == Summoned || !pActors[Actor_ITR].moveSpeed)
continue;

bool Water_Walk = MonsterStats::BelongsToSupertype(pActors[Actor_ITR].monsterInfo.uID, MONSTER_SUPERTYPE_WATER_ELEMENTAL);
bool Water_Walk = supertypeForMonsterId(pActors[Actor_ITR].monsterInfo.uID) == MONSTER_SUPERTYPE_WATER_ELEMENTAL;

pActors[Actor_ITR].sectorId = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/Engine/Graphics/Vis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ bool Vis::isBillboardPartOfSelection(int billboardId, Vis_SelectionFilter *filte
return false;

auto only_target_undead = filter->select_flags & TargetUndead;
auto target_not_undead = MonsterStats::BelongsToSupertype(pActors[object_idx].monsterInfo.uID, MONSTER_SUPERTYPE_UNDEAD) == 0;
auto target_not_undead = supertypeForMonsterId(pActors[object_idx].monsterInfo.uID) != MONSTER_SUPERTYPE_UNDEAD;

if (only_target_undead && target_not_undead)
return false;
Expand Down
Loading

0 comments on commit e56071d

Please sign in to comment.