You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
7.5 KiB
304 lines
7.5 KiB
/** |
|
* @file objdat.cpp |
|
* |
|
* Implementation of all object data. |
|
*/ |
|
#include "tables/objdat.h" |
|
|
|
#include <string> |
|
#include <string_view> |
|
#include <vector> |
|
|
|
#include <ankerl/unordered_dense.h> |
|
#include <expected.hpp> |
|
|
|
#include "cursor.h" |
|
#include "data/file.hpp" |
|
#include "data/iterators.hpp" |
|
#include "data/record_reader.hpp" |
|
|
|
namespace devilution { |
|
|
|
/** Maps from dun_object_id to object_id. */ |
|
const _object_id ObjTypeConv[] = { |
|
OBJ_NULL, |
|
OBJ_LEVER, |
|
OBJ_CRUX1, |
|
OBJ_CRUX2, |
|
OBJ_CRUX3, |
|
OBJ_ANGEL, |
|
OBJ_BANNERL, |
|
OBJ_BANNERM, |
|
OBJ_BANNERR, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_BOOK2L, |
|
OBJ_BOOK2R, |
|
OBJ_BCROSS, |
|
OBJ_NULL, |
|
OBJ_CANDLE1, |
|
OBJ_CANDLE2, |
|
OBJ_CANDLEO, |
|
OBJ_CAULDRON, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_FLAMEHOLE, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_MCIRCLE1, |
|
OBJ_MCIRCLE2, |
|
OBJ_SKFIRE, |
|
OBJ_SKPILE, |
|
OBJ_SKSTICK1, |
|
OBJ_SKSTICK2, |
|
OBJ_SKSTICK3, |
|
OBJ_SKSTICK4, |
|
OBJ_SKSTICK5, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_SWITCHSKL, |
|
OBJ_NULL, |
|
OBJ_TRAPL, |
|
OBJ_TRAPR, |
|
OBJ_TORTURE1, |
|
OBJ_TORTURE2, |
|
OBJ_TORTURE3, |
|
OBJ_TORTURE4, |
|
OBJ_TORTURE5, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NUDEW2R, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_TNUDEM1, |
|
OBJ_TNUDEM2, |
|
OBJ_TNUDEM3, |
|
OBJ_TNUDEM4, |
|
OBJ_TNUDEW1, |
|
OBJ_TNUDEW2, |
|
OBJ_TNUDEW3, |
|
OBJ_CHEST1, |
|
OBJ_CHEST1, |
|
OBJ_CHEST1, |
|
OBJ_CHEST2, |
|
OBJ_CHEST2, |
|
OBJ_CHEST2, |
|
OBJ_CHEST3, |
|
OBJ_CHEST3, |
|
OBJ_CHEST3, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_PEDESTAL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_ALTBOY, |
|
OBJ_NULL, |
|
OBJ_NULL, |
|
OBJ_WARARMOR, |
|
OBJ_WARWEAP, |
|
OBJ_TORCHR2, |
|
OBJ_TORCHL2, |
|
OBJ_MUSHPATCH, |
|
OBJ_STAND, |
|
OBJ_TORCHL, |
|
OBJ_TORCHR, |
|
OBJ_FLAMELVR, |
|
OBJ_SARC, |
|
OBJ_BARREL, |
|
OBJ_BARRELEX, |
|
OBJ_BOOKSHELF, |
|
OBJ_BOOKCASEL, |
|
OBJ_BOOKCASER, |
|
OBJ_ARMORSTANDN, |
|
OBJ_WEAPONRACKN, |
|
OBJ_BLOODFTN, |
|
OBJ_PURIFYINGFTN, |
|
OBJ_SHRINEL, |
|
OBJ_SHRINER, |
|
OBJ_GOATSHRINE, |
|
OBJ_MURKYFTN, |
|
OBJ_TEARFTN, |
|
OBJ_DECAP, |
|
OBJ_TCHEST1, |
|
OBJ_TCHEST2, |
|
OBJ_TCHEST3, |
|
OBJ_LAZSTAND, |
|
OBJ_BOOKSTAND, |
|
OBJ_BOOKSHELFR, |
|
OBJ_POD, |
|
OBJ_PODEX, |
|
OBJ_URN, |
|
OBJ_URNEX, |
|
OBJ_L5BOOKS, |
|
OBJ_L5CANDLE, |
|
OBJ_L5LEVER, |
|
OBJ_L5SARC, |
|
}; |
|
|
|
/** Contains the data related to each object ID. */ |
|
std::vector<ObjectData> AllObjects; |
|
|
|
/** Maps from object_graphic_id to object CEL name. */ |
|
std::vector<std::string> ObjMasterLoadList; |
|
|
|
namespace { |
|
|
|
tl::expected<theme_id, std::string> ParseTheme(std::string_view value) |
|
{ |
|
if (value.empty()) return THEME_NONE; |
|
if (value == "THEME_BARREL") return THEME_BARREL; |
|
if (value == "THEME_SHRINE") return THEME_SHRINE; |
|
if (value == "THEME_MONSTPIT") return THEME_MONSTPIT; |
|
if (value == "THEME_SKELROOM") return THEME_SKELROOM; |
|
if (value == "THEME_TREASURE") return THEME_TREASURE; |
|
if (value == "THEME_LIBRARY") return THEME_LIBRARY; |
|
if (value == "THEME_TORTURE") return THEME_TORTURE; |
|
if (value == "THEME_BLOODFOUNTAIN") return THEME_BLOODFOUNTAIN; |
|
if (value == "THEME_DECAPITATED") return THEME_DECAPITATED; |
|
if (value == "THEME_PURIFYINGFOUNTAIN") return THEME_PURIFYINGFOUNTAIN; |
|
if (value == "THEME_ARMORSTAND") return THEME_ARMORSTAND; |
|
if (value == "THEME_GOATSHRINE") return THEME_GOATSHRINE; |
|
if (value == "THEME_CAULDRON") return THEME_CAULDRON; |
|
if (value == "THEME_MURKYFOUNTAIN") return THEME_MURKYFOUNTAIN; |
|
if (value == "THEME_TEARFOUNTAIN") return THEME_TEARFOUNTAIN; |
|
if (value == "THEME_BRNCROSS") return THEME_BRNCROSS; |
|
if (value == "THEME_WEAPONRACK") return THEME_WEAPONRACK; |
|
return tl::make_unexpected("Unknown enum value"); |
|
} |
|
|
|
tl::expected<quest_id, std::string> ParseQuest(std::string_view value) |
|
{ |
|
if (value.empty()) return Q_INVALID; |
|
if (value == "Q_ROCK") return Q_ROCK; |
|
if (value == "Q_MUSHROOM") return Q_MUSHROOM; |
|
if (value == "Q_GARBUD") return Q_GARBUD; |
|
if (value == "Q_ZHAR") return Q_ZHAR; |
|
if (value == "Q_VEIL") return Q_VEIL; |
|
if (value == "Q_DIABLO") return Q_DIABLO; |
|
if (value == "Q_BUTCHER") return Q_BUTCHER; |
|
if (value == "Q_LTBANNER") return Q_LTBANNER; |
|
if (value == "Q_BLIND") return Q_BLIND; |
|
if (value == "Q_BLOOD") return Q_BLOOD; |
|
if (value == "Q_ANVIL") return Q_ANVIL; |
|
if (value == "Q_WARLORD") return Q_WARLORD; |
|
if (value == "Q_SKELKING") return Q_SKELKING; |
|
if (value == "Q_PWATER") return Q_PWATER; |
|
if (value == "Q_SCHAMB") return Q_SCHAMB; |
|
if (value == "Q_BETRAYER") return Q_BETRAYER; |
|
if (value == "Q_GRAVE") return Q_GRAVE; |
|
if (value == "Q_FARMER") return Q_FARMER; |
|
if (value == "Q_GIRL") return Q_GIRL; |
|
if (value == "Q_TRADER") return Q_TRADER; |
|
if (value == "Q_DEFILER") return Q_DEFILER; |
|
if (value == "Q_NAKRUL") return Q_NAKRUL; |
|
if (value == "Q_CORNSTN") return Q_CORNSTN; |
|
if (value == "Q_JERSEY") return Q_JERSEY; |
|
return tl::make_unexpected("Unknown enum value"); |
|
} |
|
|
|
tl::expected<ObjectDataFlags, std::string> ParseObjectDataFlags(std::string_view value) |
|
{ |
|
if (value.empty()) return ObjectDataFlags::None; |
|
if (value == "Animated") return ObjectDataFlags::Animated; |
|
if (value == "Solid") return ObjectDataFlags::Solid; |
|
if (value == "MissilesPassThrough") return ObjectDataFlags::MissilesPassThrough; |
|
if (value == "Light") return ObjectDataFlags::Light; |
|
if (value == "Trap") return ObjectDataFlags::Trap; |
|
if (value == "Breakable") return ObjectDataFlags::Breakable; |
|
return tl::make_unexpected("Unknown enum value"); |
|
} |
|
|
|
tl::expected<SelectionRegion, std::string> ParseSelectionRegion(std::string_view value) |
|
{ |
|
if (value.empty()) return SelectionRegion::None; |
|
if (value == "Bottom") return SelectionRegion::Bottom; |
|
if (value == "Middle") return SelectionRegion::Middle; |
|
if (value == "Top") return SelectionRegion::Top; |
|
return tl::make_unexpected("Unknown enum value"); |
|
} |
|
|
|
} // namespace |
|
|
|
void LoadObjectData() |
|
{ |
|
const std::string_view filename = "txtdata\\objects\\objdat.tsv"; |
|
DataFile dataFile = DataFile::loadOrDie(filename); |
|
dataFile.skipHeaderOrDie(filename); |
|
|
|
AllObjects.clear(); |
|
ObjMasterLoadList.clear(); |
|
|
|
ankerl::unordered_dense::map<std::string, uint8_t> filenameToId; |
|
|
|
for (DataFileRecord record : dataFile) { |
|
RecordReader reader { record, filename }; |
|
ObjectData &item = AllObjects.emplace_back(); |
|
|
|
reader.advance(); // skip id |
|
|
|
std::string objFilename; |
|
reader.readString("file", objFilename); |
|
if (const auto it = filenameToId.find(objFilename); it != filenameToId.end()) { |
|
item.ofindex = it->second; |
|
} else { |
|
const auto id = static_cast<uint8_t>(ObjMasterLoadList.size()); |
|
ObjMasterLoadList.push_back(objFilename); |
|
filenameToId.emplace(std::move(objFilename), id); |
|
item.ofindex = id; |
|
} |
|
|
|
reader.readInt("minLevel", item.minlvl); |
|
reader.readInt("maxLevel", item.maxlvl); |
|
reader.read("levelType", item.olvltype, ParseDungeonType); |
|
reader.read("theme", item.otheme, ParseTheme); |
|
reader.read("quest", item.oquest, ParseQuest); |
|
reader.readEnumList("flags", item.flags, ParseObjectDataFlags); |
|
reader.readInt("animDelay", item.animDelay); |
|
reader.readInt("animLen", item.animLen); |
|
reader.readInt("animWidth", item.animWidth); |
|
reader.readEnumList("selectionRegion", item.selectionRegion, ParseSelectionRegion); |
|
} |
|
|
|
// Sanity check because we do not actually parse the IDs yet. |
|
assert(static_cast<size_t>(OBJ_LAST) + 1 == AllObjects.size()); |
|
|
|
AllObjects.shrink_to_fit(); |
|
ObjMasterLoadList.shrink_to_fit(); |
|
} |
|
|
|
} // namespace devilution
|
|
|