mirror of
https://github.com/diasurgical/DevilutionX.git
synced 2026-05-21 05:40:35 +00:00
Make Player Class Data Data-Driven (#8167)
This commit is contained in:
@@ -158,12 +158,37 @@ set(devilutionx_assets
|
||||
plrgfx/warrior/whu/whulm.trn
|
||||
plrgfx/warrior/whu/whuqm.trn
|
||||
txtdata/Experience.tsv
|
||||
txtdata/classes/barbarian/animations.tsv
|
||||
txtdata/classes/barbarian/attributes.tsv
|
||||
txtdata/classes/barbarian/sounds.tsv
|
||||
txtdata/classes/barbarian/sprites.tsv
|
||||
txtdata/classes/barbarian/starting_loadout.tsv
|
||||
txtdata/classes/bard/animations.tsv
|
||||
txtdata/classes/bard/attributes.tsv
|
||||
txtdata/classes/bard/sounds.tsv
|
||||
txtdata/classes/bard/sprites.tsv
|
||||
txtdata/classes/bard/starting_loadout.tsv
|
||||
txtdata/classes/monk/animations.tsv
|
||||
txtdata/classes/monk/attributes.tsv
|
||||
txtdata/classes/monk/sounds.tsv
|
||||
txtdata/classes/monk/sprites.tsv
|
||||
txtdata/classes/monk/starting_loadout.tsv
|
||||
txtdata/classes/rogue/animations.tsv
|
||||
txtdata/classes/rogue/attributes.tsv
|
||||
txtdata/classes/rogue/sounds.tsv
|
||||
txtdata/classes/rogue/sprites.tsv
|
||||
txtdata/classes/rogue/starting_loadout.tsv
|
||||
txtdata/classes/sorcerer/animations.tsv
|
||||
txtdata/classes/sorcerer/attributes.tsv
|
||||
txtdata/classes/sorcerer/sounds.tsv
|
||||
txtdata/classes/sorcerer/sprites.tsv
|
||||
txtdata/classes/sorcerer/starting_loadout.tsv
|
||||
txtdata/classes/warrior/animations.tsv
|
||||
txtdata/classes/warrior/attributes.tsv
|
||||
txtdata/classes/warrior/sounds.tsv
|
||||
txtdata/classes/warrior/sprites.tsv
|
||||
txtdata/classes/warrior/starting_loadout.tsv
|
||||
txtdata/classes/classdat.tsv
|
||||
txtdata/items/item_prefixes.tsv
|
||||
txtdata/items/item_suffixes.tsv
|
||||
txtdata/items/itemdat.tsv
|
||||
|
||||
@@ -8,6 +8,8 @@ set(hellfire_mod
|
||||
nlevels/cutl6w.clx
|
||||
nlevels/l5data/cornerstone.dun
|
||||
nlevels/l5data/uberroom.dun
|
||||
txtdata/classes/sorcerer/starting_loadout.tsv
|
||||
txtdata/classes/classdat.tsv
|
||||
txtdata/items/item_prefixes.tsv
|
||||
txtdata/items/item_suffixes.tsv
|
||||
txtdata/items/unique_itemdat.tsv
|
||||
|
||||
@@ -709,6 +709,7 @@ add_devilutionx_object_library(libdevilutionx_txtdata
|
||||
data/file.cpp
|
||||
data/parser.cpp
|
||||
data/record_reader.cpp
|
||||
data/value_reader.cpp
|
||||
)
|
||||
target_link_dependencies(libdevilutionx_txtdata PUBLIC
|
||||
fmt::fmt
|
||||
|
||||
@@ -82,8 +82,8 @@ std::size_t SelectedItem = 0;
|
||||
namespace {
|
||||
|
||||
OptionalOwnedClxSpriteList ArtHero;
|
||||
std::array<uint8_t, enum_size<HeroClass>::value + 1> ArtHeroPortraitOrder;
|
||||
std::array<OptionalOwnedClxSpriteList, enum_size<HeroClass>::value + 1> ArtHeroOverrides;
|
||||
std::vector<uint8_t> ArtHeroPortraitOrder;
|
||||
std::vector<OptionalOwnedClxSpriteList> ArtHeroOverrides;
|
||||
|
||||
std::size_t SelectedItemMax;
|
||||
std::size_t ListViewportSize = 1;
|
||||
@@ -581,17 +581,18 @@ void LoadHeros()
|
||||
return;
|
||||
const uint16_t numPortraits = ClxSpriteList { *ArtHero }.numSprites();
|
||||
|
||||
ArtHeroPortraitOrder = { 0, 1, 2, 2, 1, 0, 3 };
|
||||
if (numPortraits >= 6) {
|
||||
ArtHeroPortraitOrder[static_cast<std::size_t>(HeroClass::Monk)] = 3;
|
||||
ArtHeroPortraitOrder[static_cast<std::size_t>(HeroClass::Bard)] = 4;
|
||||
ArtHeroPortraitOrder[enum_size<HeroClass>::value] = 5;
|
||||
ArtHeroPortraitOrder.resize(GetNumPlayerClasses() + 1);
|
||||
for (size_t i = 0; i < GetNumPlayerClasses(); ++i) {
|
||||
const PlayerData &playerClassData = GetPlayerDataForClass(static_cast<HeroClass>(i));
|
||||
ArtHeroPortraitOrder[i] = playerClassData.portrait;
|
||||
}
|
||||
if (numPortraits >= 7) {
|
||||
ArtHeroPortraitOrder[static_cast<std::size_t>(HeroClass::Barbarian)] = 6;
|
||||
ArtHeroPortraitOrder.back() = 3;
|
||||
if (numPortraits >= 6) {
|
||||
ArtHeroPortraitOrder.back() = 5;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i <= enum_size<HeroClass>::value; ++i) {
|
||||
ArtHeroOverrides.resize(GetNumPlayerClasses() + 1);
|
||||
for (size_t i = 0; i <= GetNumPlayerClasses(); ++i) {
|
||||
char portraitPath[18];
|
||||
*BufCopy(portraitPath, "ui_art\\hero", i) = '\0';
|
||||
ArtHeroOverrides[i] = LoadPcx(portraitPath, /*transparentColor=*/std::nullopt, /*outPalette=*/nullptr, /*logError=*/false);
|
||||
|
||||
@@ -143,7 +143,7 @@ void SelheroListFocus(size_t value)
|
||||
return;
|
||||
}
|
||||
|
||||
SELHERO_DIALOG_HERO_IMG->setSprite(UiGetHeroDialogSprite(enum_size<HeroClass>::value));
|
||||
SELHERO_DIALOG_HERO_IMG->setSprite(UiGetHeroDialogSprite(GetNumPlayerClasses()));
|
||||
for (char *textStat : textStats)
|
||||
strcpy(textStat, "--");
|
||||
SELLIST_DIALOG_DELETE_BUTTON->SetFlags(baseFlags | UiFlags::ColorUiSilver | UiFlags::ElementDisabled);
|
||||
@@ -169,22 +169,31 @@ void SelheroListSelect(size_t value)
|
||||
|
||||
vecSelHeroDlgItems.clear();
|
||||
int itemH = 33;
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Warrior"), static_cast<int>(HeroClass::Warrior)));
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Rogue"), static_cast<int>(HeroClass::Rogue)));
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Sorcerer"), static_cast<int>(HeroClass::Sorcerer)));
|
||||
if (gbIsHellfire) {
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Monk"), static_cast<int>(HeroClass::Monk)));
|
||||
if (HaveBardAssets() || *GetOptions().Gameplay.testBard) {
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Bard"), static_cast<int>(HeroClass::Bard)));
|
||||
for (size_t i = 0; i < GetNumPlayerClasses(); ++i) {
|
||||
const HeroClass heroClass = static_cast<HeroClass>(i);
|
||||
|
||||
if (heroClass == HeroClass::Monk && !gbIsHellfire) {
|
||||
continue;
|
||||
}
|
||||
if (HaveBarbarianAssets() || *GetOptions().Gameplay.testBarbarian) {
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Barbarian"), static_cast<int>(HeroClass::Barbarian)));
|
||||
|
||||
if (heroClass == HeroClass::Bard && !HaveBardAssets() && !(*GetOptions().Gameplay.testBard)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (heroClass == HeroClass::Barbarian && !HaveBarbarianAssets() && !(*GetOptions().Gameplay.testBarbarian)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const PlayerData &playerData = GetPlayerDataForClass(heroClass);
|
||||
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(std::string_view(playerData.className), static_cast<int>(heroClass)));
|
||||
}
|
||||
if (vecSelHeroDlgItems.size() > 4)
|
||||
itemH = 26;
|
||||
const int itemY = static_cast<int>(246 + (176 - vecSelHeroDlgItems.size() * itemH) / 2);
|
||||
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, vecSelHeroDlgItems.size(), uiPosition.x + 264, (uiPosition.y + itemY), 320, itemH, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||||
const int itemY = static_cast<int>(246 + (176 - std::min<size_t>(vecSelHeroDlgItems.size(), 6) * itemH) / 2);
|
||||
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, std::min<size_t>(vecSelHeroDlgItems.size(), 6), uiPosition.x + 264, (uiPosition.y + itemY), 320, itemH, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||||
|
||||
const SDL_Rect rectScrollBar = { (Sint16)(uiPosition.x + 585), (Sint16)(uiPosition.y + 244), 25, 178 };
|
||||
vecSelDlgItems.push_back(std::make_unique<UiScrollbar>((*ArtScrollBarBackground)[0], (*ArtScrollBarThumb)[0], *ArtScrollBarArrow, rectScrollBar));
|
||||
|
||||
const SDL_Rect rect2 = { (Sint16)(uiPosition.x + 279), (Sint16)(uiPosition.y + 429), 140, 35 };
|
||||
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect2, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "data/value_reader.hpp"
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "appfat.h"
|
||||
|
||||
namespace devilution {
|
||||
|
||||
ValueReader::ValueReader(DataFile &dataFile, std::string_view filename)
|
||||
: it_(dataFile.begin())
|
||||
, end_(dataFile.end())
|
||||
, filename_(filename)
|
||||
{
|
||||
}
|
||||
|
||||
DataFileField ValueReader::getValueField(std::string_view expectedKey)
|
||||
{
|
||||
if (it_ == end_) {
|
||||
app_fatal(fmt::format("Missing field {} in {}", expectedKey, filename_));
|
||||
}
|
||||
DataFileRecord record = *it_;
|
||||
FieldIterator fieldIt = record.begin();
|
||||
const FieldIterator endField = record.end();
|
||||
|
||||
const std::string_view key = (*fieldIt).value();
|
||||
if (key != expectedKey) {
|
||||
app_fatal(fmt::format("Unexpected field in {}: got {}, expected {}", filename_, key, expectedKey));
|
||||
}
|
||||
|
||||
++fieldIt;
|
||||
if (fieldIt == endField) {
|
||||
DataFile::reportFatalError(DataFile::Error::NotEnoughColumns, filename_);
|
||||
}
|
||||
return *fieldIt;
|
||||
}
|
||||
|
||||
} // namespace devilution
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include <expected.hpp>
|
||||
#include <function_ref.hpp>
|
||||
|
||||
#include "data/file.hpp"
|
||||
#include "data/iterators.hpp"
|
||||
|
||||
namespace devilution {
|
||||
|
||||
/**
|
||||
* @brief A value reader.
|
||||
*/
|
||||
class ValueReader {
|
||||
public:
|
||||
ValueReader(DataFile &dataFile, std::string_view filename);
|
||||
|
||||
DataFileField getValueField(std::string_view expectedKey);
|
||||
|
||||
template <typename T, typename F>
|
||||
void readValue(std::string_view expectedKey, T &outValue, F &&readFn)
|
||||
{
|
||||
DataFileField valueField = getValueField(expectedKey);
|
||||
if (const tl::expected<void, devilution::DataFileField::Error> result = readFn(valueField, outValue);
|
||||
!result.has_value()) {
|
||||
DataFile::reportFatalFieldError(result.error(), filename_, "Value", valueField);
|
||||
}
|
||||
++it_;
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
void read(std::string_view expectedKey, T &outValue, F &&parseFn)
|
||||
{
|
||||
readValue(expectedKey, outValue, [&parseFn](DataFileField &valueField, T &outValue) -> tl::expected<void, devilution::DataFileField::Error> {
|
||||
const auto result = parseFn(valueField.value());
|
||||
if (!result.has_value()) {
|
||||
return tl::make_unexpected(devilution::DataFileField::Error::InvalidValue);
|
||||
}
|
||||
|
||||
outValue = result.value();
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if_t<std::is_integral_v<T>, void>
|
||||
readInt(std::string_view expectedKey, T &outValue)
|
||||
{
|
||||
readValue(expectedKey, outValue, [](DataFileField &valueField, T &outValue) {
|
||||
return valueField.parseInt(outValue);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if_t<std::is_integral_v<T>, void>
|
||||
readDecimal(std::string_view expectedKey, T &outValue)
|
||||
{
|
||||
readValue(expectedKey, outValue, [](DataFileField &valueField, T &outValue) {
|
||||
return valueField.parseFixed6(outValue);
|
||||
});
|
||||
}
|
||||
|
||||
void readString(std::string_view expectedKey, std::string &outValue)
|
||||
{
|
||||
readValue(expectedKey, outValue, [](DataFileField &valueField, std::string &outValue) {
|
||||
outValue = valueField.value();
|
||||
return tl::expected<void, devilution::DataFileField::Error> {};
|
||||
});
|
||||
}
|
||||
|
||||
void readChar(std::string_view expectedKey, char &outValue)
|
||||
{
|
||||
readValue(expectedKey, outValue, [](DataFileField &valueField, char &outValue) -> tl::expected<void, devilution::DataFileField::Error> {
|
||||
if (valueField.value().size() != 1) {
|
||||
return tl::make_unexpected(devilution::DataFileField::Error::InvalidValue);
|
||||
}
|
||||
|
||||
outValue = valueField.value().at(0);
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
RecordIterator it_;
|
||||
const RecordIterator end_;
|
||||
std::string_view filename_;
|
||||
};
|
||||
|
||||
} // namespace devilution
|
||||
@@ -124,7 +124,7 @@ std::string GetTooltipString()
|
||||
std::string GetPlayerAssetString()
|
||||
{
|
||||
char chars[5] {
|
||||
CharChar[static_cast<int>(MyPlayer->_pClass)],
|
||||
GetPlayerSpriteDataForClass(MyPlayer->_pClass).classChar,
|
||||
ArmourChar[tracked_data.playerGfx >> 4],
|
||||
WepChar[tracked_data.playerGfx & 0xF],
|
||||
'a',
|
||||
|
||||
+17
-1
@@ -278,7 +278,14 @@ void sound_init()
|
||||
mask |= sfx_MONK;
|
||||
break;
|
||||
default:
|
||||
app_fatal("effects:1");
|
||||
if (static_cast<size_t>(MyPlayer->_pClass) < GetNumPlayerClasses()) {
|
||||
// this is a custom class, so we need to add init sounds, since we can't determine which ones will be used by it
|
||||
mask |= (sfx_WARRIOR | sfx_MONK);
|
||||
if (!gbIsSpawn)
|
||||
mask |= (sfx_ROGUE | sfx_SORCERER);
|
||||
} else {
|
||||
app_fatal("effects:1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +318,15 @@ int GetSFXLength(SfxID nSFX)
|
||||
return sfx.pSnd->DSB.GetLength();
|
||||
}
|
||||
|
||||
tl::expected<HeroSpeech, std::string> ParseHeroSpeech(std::string_view value)
|
||||
{
|
||||
const std::optional<HeroSpeech> enumValueOpt = magic_enum::enum_cast<HeroSpeech>(value);
|
||||
if (enumValueOpt.has_value()) {
|
||||
return enumValueOpt.value();
|
||||
}
|
||||
return tl::make_unexpected("Unknown enum value.");
|
||||
}
|
||||
|
||||
tl::expected<SfxID, std::string> ParseSfxId(std::string_view value)
|
||||
{
|
||||
const std::optional<SfxID> enumValueOpt = magic_enum::enum_cast<SfxID>(value);
|
||||
|
||||
@@ -37,6 +37,7 @@ void ui_sound_init();
|
||||
void effects_play_sound(SfxID);
|
||||
int GetSFXLength(SfxID nSFX);
|
||||
|
||||
tl::expected<HeroSpeech, std::string> ParseHeroSpeech(std::string_view value);
|
||||
tl::expected<SfxID, std::string> ParseSfxId(std::string_view value);
|
||||
|
||||
} // namespace devilution
|
||||
|
||||
@@ -50,6 +50,15 @@ void ui_sound_init() { }
|
||||
void effects_play_sound(SfxID id) { }
|
||||
int GetSFXLength(SfxID nSFX) { return 0; }
|
||||
|
||||
tl::expected<HeroSpeech, std::string> ParseHeroSpeech(std::string_view value)
|
||||
{
|
||||
const std::optional<HeroSpeech> enumValueOpt = magic_enum::enum_cast<HeroSpeech>(value);
|
||||
if (enumValueOpt.has_value()) {
|
||||
return enumValueOpt.value();
|
||||
}
|
||||
return tl::make_unexpected("Unknown enum value.");
|
||||
}
|
||||
|
||||
tl::expected<SfxID, std::string> ParseSfxId(std::string_view value)
|
||||
{
|
||||
const std::optional<SfxID> enumValueOpt = magic_enum::enum_cast<SfxID>(value);
|
||||
|
||||
+5
-22
@@ -9,6 +9,7 @@
|
||||
#endif
|
||||
#include "engine/load_file.hpp"
|
||||
#include "lighting.h"
|
||||
#include "utils/str_cat.hpp"
|
||||
|
||||
namespace devilution {
|
||||
|
||||
@@ -30,32 +31,14 @@ uint8_t *GetPauseTRN()
|
||||
std::optional<std::array<uint8_t, 256>> GetClassTRN(Player &player)
|
||||
{
|
||||
std::array<uint8_t, 256> trn;
|
||||
const char *path;
|
||||
char path[64];
|
||||
|
||||
switch (player._pClass) {
|
||||
case HeroClass::Warrior:
|
||||
path = "plrgfx\\warrior.trn";
|
||||
break;
|
||||
case HeroClass::Rogue:
|
||||
path = "plrgfx\\rogue.trn";
|
||||
break;
|
||||
case HeroClass::Sorcerer:
|
||||
path = "plrgfx\\sorcerer.trn";
|
||||
break;
|
||||
case HeroClass::Monk:
|
||||
path = "plrgfx\\monk.trn";
|
||||
break;
|
||||
case HeroClass::Bard:
|
||||
path = "plrgfx\\bard.trn";
|
||||
break;
|
||||
case HeroClass::Barbarian:
|
||||
path = "plrgfx\\barbarian.trn";
|
||||
break;
|
||||
}
|
||||
const PlayerSpriteData &spriteData = GetPlayerSpriteDataForClass(player._pClass);
|
||||
*BufCopy(path, "plrgfx\\", spriteData.trn, ".trn") = '\0';
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (!debugTRN.empty()) {
|
||||
path = debugTRN.c_str();
|
||||
*BufCopy(path, debugTRN.c_str()) = '\0';
|
||||
}
|
||||
#endif
|
||||
if (LoadOptionalFileInMem(path, &trn[0], 256)) {
|
||||
|
||||
+5
-15
@@ -1158,22 +1158,12 @@ void FreeInvGFX()
|
||||
|
||||
void InitInv()
|
||||
{
|
||||
switch (MyPlayer->_pClass) {
|
||||
case HeroClass::Warrior:
|
||||
case HeroClass::Barbarian:
|
||||
pInvCels = LoadCel("data\\inv\\inv", static_cast<uint16_t>(SidePanelSize.width));
|
||||
break;
|
||||
case HeroClass::Rogue:
|
||||
case HeroClass::Bard:
|
||||
pInvCels = LoadCel("data\\inv\\inv_rog", static_cast<uint16_t>(SidePanelSize.width));
|
||||
break;
|
||||
case HeroClass::Sorcerer:
|
||||
pInvCels = LoadCel("data\\inv\\inv_sor", static_cast<uint16_t>(SidePanelSize.width));
|
||||
break;
|
||||
case HeroClass::Monk:
|
||||
pInvCels = LoadCel(!gbIsSpawn ? "data\\inv\\inv_sor" : "data\\inv\\inv", static_cast<uint16_t>(SidePanelSize.width));
|
||||
break;
|
||||
const PlayerData &playerClassData = GetPlayerDataForClass(MyPlayer->_pClass);
|
||||
const char *invName = playerClassData.inv.c_str();
|
||||
if (gbIsSpawn && (playerClassData.inv == "inv_rog" || playerClassData.inv == "inv_sor")) {
|
||||
invName = "inv";
|
||||
}
|
||||
pInvCels = LoadCel(StrCat("data\\inv\\", invName).c_str(), static_cast<uint16_t>(SidePanelSize.width));
|
||||
}
|
||||
|
||||
void DrawInv(const Surface &out)
|
||||
|
||||
+9
-1
@@ -9,7 +9,6 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <expected.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "data/file.hpp"
|
||||
@@ -42,6 +41,15 @@ std::vector<PLStruct> ItemPrefixes;
|
||||
/** Contains the data related to each item suffix. */
|
||||
std::vector<PLStruct> ItemSuffixes;
|
||||
|
||||
tl::expected<_item_indexes, std::string> ParseItemId(std::string_view value)
|
||||
{
|
||||
const std::optional<_item_indexes> enumValueOpt = magic_enum::enum_cast<_item_indexes>(value);
|
||||
if (enumValueOpt.has_value()) {
|
||||
return enumValueOpt.value();
|
||||
}
|
||||
return tl::make_unexpected("Unknown enum value");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
tl::expected<item_class, std::string> ParseItemClass(std::string_view value)
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <expected.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "objdat.h"
|
||||
#include "spelldat.h"
|
||||
@@ -654,8 +656,15 @@ extern std::vector<PLStruct> ItemSuffixes;
|
||||
extern DVL_API_FOR_TEST std::vector<UniqueItem> UniqueItems;
|
||||
extern ankerl::unordered_dense::map<int32_t, int32_t> UniqueItemMappingIdsToIndices;
|
||||
|
||||
tl::expected<_item_indexes, std::string> ParseItemId(std::string_view value);
|
||||
void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename, int32_t baseMappingId);
|
||||
void LoadUniqueItemDatFromFile(DataFile &dataFile, std::string_view filename, int32_t baseMappingId);
|
||||
void LoadItemData();
|
||||
|
||||
} // namespace devilution
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<devilution::_item_indexes> {
|
||||
static constexpr int min = devilution::_item_indexes::IDI_NONE;
|
||||
static constexpr int max = devilution::_item_indexes::IDI_NUM_DEFAULT_ITEMS;
|
||||
};
|
||||
|
||||
+1
-2
@@ -3012,8 +3012,7 @@ void CreatePlrItems(Player &player)
|
||||
}
|
||||
|
||||
InitCursor();
|
||||
for (const auto &itemChoice : loadout.items) {
|
||||
const _item_indexes itemData = gbIsHellfire && itemChoice.hellfire != _item_indexes::IDI_NONE ? itemChoice.hellfire : itemChoice.diablo;
|
||||
for (const _item_indexes itemData : loadout.items) {
|
||||
if (itemData != _item_indexes::IDI_NONE)
|
||||
CreateStartingItem(player, itemData);
|
||||
}
|
||||
|
||||
+7
-8
@@ -136,28 +136,27 @@ void InitQuestText()
|
||||
void InitQTextMsg(_speech_id m)
|
||||
{
|
||||
SfxID sfxnr = Speeches[m].sfxnr;
|
||||
const SfxID *classSounds = herosounds[static_cast<size_t>(MyPlayer->_pClass)];
|
||||
switch (sfxnr) {
|
||||
case SfxID::Warrior1:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::ChamberOfBoneLore)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::ChamberOfBoneLore);
|
||||
break;
|
||||
case SfxID::Warrior10:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::ValorLore)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::ValorLore);
|
||||
break;
|
||||
case SfxID::Warrior11:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::HallsOfTheBlindLore)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::HallsOfTheBlindLore);
|
||||
break;
|
||||
case SfxID::Warrior12:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::WarlordOfBloodLore)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::WarlordOfBloodLore);
|
||||
break;
|
||||
case SfxID::Warrior54:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::InSpirituSanctum)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::InSpirituSanctum);
|
||||
break;
|
||||
case SfxID::Warrior55:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::PraedictumOtium)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::PraedictumOtium);
|
||||
break;
|
||||
case SfxID::Warrior56:
|
||||
sfxnr = classSounds[static_cast<size_t>(HeroSpeech::EfficioObitusUtInimicus)];
|
||||
sfxnr = GetHeroSound(MyPlayer->_pClass, HeroSpeech::EfficioObitusUtInimicus);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ bool IsNetPlayerValid(const Player &player)
|
||||
{
|
||||
// we no longer check character level here, players with out-of-range clevels are not allowed to join the game and we don't observe change clevel messages that would set it out of range
|
||||
// (there's no code path that would result in _pLevel containing an out of range value in the DevilutionX code)
|
||||
return static_cast<uint8_t>(player._pClass) < enum_size<HeroClass>::value
|
||||
return static_cast<uint8_t>(player._pClass) < GetNumPlayerClasses()
|
||||
&& player.plrlevel < NUMLEVELS
|
||||
&& InDungeonBounds(player.position.tile)
|
||||
&& !std::string_view(player._pName).empty();
|
||||
|
||||
+2
-2
@@ -359,7 +359,7 @@ void UnPackPlayer(const PlayerPack &packed, Player &player)
|
||||
player.position.future = position;
|
||||
player.setLevel(std::clamp<int8_t>(packed.plrlevel, 0, NUMLEVELS));
|
||||
|
||||
player._pClass = static_cast<HeroClass>(std::clamp<uint8_t>(packed.pClass, 0, enum_size<HeroClass>::value - 1));
|
||||
player._pClass = static_cast<HeroClass>(std::clamp<uint8_t>(packed.pClass, 0, static_cast<uint8_t>(GetNumPlayerClasses() - 1)));
|
||||
|
||||
ClrPlrPath(player);
|
||||
player.destAction = ACTION_NONE;
|
||||
@@ -464,7 +464,7 @@ bool UnPackNetPlayer(const PlayerNetPack &packed, Player &player)
|
||||
{
|
||||
CopyUtf8(player._pName, packed.pName, sizeof(player._pName));
|
||||
|
||||
ValidateField(packed.pClass, packed.pClass < enum_size<HeroClass>::value);
|
||||
ValidateField(packed.pClass, packed.pClass < GetNumPlayerClasses());
|
||||
player._pClass = static_cast<HeroClass>(packed.pClass);
|
||||
|
||||
const Point position { packed.px, packed.py };
|
||||
|
||||
+11
-9
@@ -1486,7 +1486,7 @@ PlayerWeaponGraphic GetPlayerWeaponGraphic(player_graphic graphic, PlayerWeaponG
|
||||
|
||||
uint16_t GetPlayerSpriteWidth(HeroClass cls, player_graphic graphic, PlayerWeaponGraphic weaponGraphic)
|
||||
{
|
||||
const PlayerSpriteData spriteData = PlayersSpriteData[static_cast<size_t>(cls)];
|
||||
const PlayerSpriteData spriteData = GetPlayerSpriteDataForClass(cls);
|
||||
|
||||
switch (graphic) {
|
||||
case player_graphic::Stand:
|
||||
@@ -1683,7 +1683,7 @@ int Player::GetPositionPathIndex(Point pos)
|
||||
|
||||
void Player::Say(HeroSpeech speechId) const
|
||||
{
|
||||
const SfxID soundEffect = herosounds[static_cast<size_t>(_pClass)][static_cast<size_t>(speechId)];
|
||||
const SfxID soundEffect = GetHeroSound(_pClass, speechId);
|
||||
|
||||
if (soundEffect == SfxID::None)
|
||||
return;
|
||||
@@ -1693,7 +1693,7 @@ void Player::Say(HeroSpeech speechId) const
|
||||
|
||||
void Player::SaySpecific(HeroSpeech speechId) const
|
||||
{
|
||||
const SfxID soundEffect = herosounds[static_cast<size_t>(_pClass)][static_cast<size_t>(speechId)];
|
||||
const SfxID soundEffect = GetHeroSound(_pClass, speechId);
|
||||
|
||||
if (soundEffect == SfxID::None || effect_is_playing(soundEffect))
|
||||
return;
|
||||
@@ -1704,7 +1704,7 @@ void Player::SaySpecific(HeroSpeech speechId) const
|
||||
void Player::Say(HeroSpeech speechId, int delay) const
|
||||
{
|
||||
sfxdelay = delay;
|
||||
sfxdnum = herosounds[static_cast<size_t>(_pClass)][static_cast<size_t>(speechId)];
|
||||
sfxdnum = GetHeroSound(_pClass, speechId);
|
||||
}
|
||||
|
||||
void Player::Stop()
|
||||
@@ -2048,7 +2048,8 @@ ClxSprite GetPlayerPortraitSprite(Player &player)
|
||||
const HeroClass cls = GetPlayerSpriteClass(player._pClass);
|
||||
const PlayerWeaponGraphic animWeaponId = GetPlayerWeaponGraphic(player_graphic::Stand, static_cast<PlayerWeaponGraphic>(player._pgfxnum & 0xF));
|
||||
|
||||
const char *path = PlayersSpriteData[static_cast<std::size_t>(cls)].classPath;
|
||||
const PlayerSpriteData &spriteData = GetPlayerSpriteDataForClass(cls);
|
||||
const char *path = spriteData.classPath.c_str();
|
||||
|
||||
const char *szCel;
|
||||
if (!inDungeon)
|
||||
@@ -2064,7 +2065,7 @@ ClxSprite GetPlayerPortraitSprite(Player &player)
|
||||
}
|
||||
}
|
||||
|
||||
char prefix[3] = { CharChar[static_cast<std::size_t>(cls)], ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
|
||||
char prefix[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
|
||||
char pszName[256];
|
||||
*fmt::format_to(pszName, R"(plrgfx\{0}\{1}\{1}{2})", path, std::string_view(prefix, 3), szCel) = 0;
|
||||
|
||||
@@ -2103,7 +2104,8 @@ void LoadPlrGFX(Player &player, player_graphic graphic)
|
||||
const HeroClass cls = GetPlayerSpriteClass(player._pClass);
|
||||
const PlayerWeaponGraphic animWeaponId = GetPlayerWeaponGraphic(graphic, static_cast<PlayerWeaponGraphic>(player._pgfxnum & 0xF));
|
||||
|
||||
const char *path = PlayersSpriteData[static_cast<std::size_t>(cls)].classPath;
|
||||
const PlayerSpriteData &spriteData = GetPlayerSpriteDataForClass(cls);
|
||||
const char *path = spriteData.classPath.c_str();
|
||||
|
||||
const char *szCel;
|
||||
switch (graphic) {
|
||||
@@ -2152,7 +2154,7 @@ void LoadPlrGFX(Player &player, player_graphic graphic)
|
||||
app_fatal("PLR:2");
|
||||
}
|
||||
|
||||
char prefix[3] = { CharChar[static_cast<std::size_t>(cls)], ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
|
||||
char prefix[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
|
||||
char pszName[256];
|
||||
*fmt::format_to(pszName, R"(plrgfx\{0}\{1}\{1}{2})", path, std::string_view(prefix, 3), szCel) = 0;
|
||||
const uint16_t animationWidth = GetPlayerSpriteWidth(cls, graphic, animWeaponId);
|
||||
@@ -2224,7 +2226,7 @@ void NewPlrAnim(Player &player, player_graphic graphic, Direction dir, Animation
|
||||
void SetPlrAnims(Player &player)
|
||||
{
|
||||
const HeroClass pc = player._pClass;
|
||||
const PlayerAnimData plrAtkAnimData = PlayersAnimData[static_cast<uint8_t>(pc)];
|
||||
const PlayerAnimData &plrAtkAnimData = GetPlayerAnimDataForClass(pc);
|
||||
auto gn = static_cast<PlayerWeaponGraphic>(player._pgfxnum & 0xFU);
|
||||
|
||||
if (leveltype == DTYPE_TOWN) {
|
||||
|
||||
@@ -183,16 +183,6 @@ constexpr std::array<char, 9> WepChar = {
|
||||
't', // staff
|
||||
};
|
||||
|
||||
/** Maps from player class to letter used in graphic files. */
|
||||
constexpr std::array<char, 6> CharChar = {
|
||||
'w', // warrior
|
||||
'r', // rogue
|
||||
's', // sorcerer
|
||||
'm', // monk
|
||||
'b', // bard
|
||||
'c', // barbarian
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Contains Data (CelSprites) for a player graphic (player_graphic)
|
||||
*/
|
||||
|
||||
+225
-140
@@ -15,8 +15,11 @@
|
||||
|
||||
#include <expected.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <magic_enum/magic_enum_utility.hpp>
|
||||
|
||||
#include "data/file.hpp"
|
||||
#include "data/record_reader.hpp"
|
||||
#include "data/value_reader.hpp"
|
||||
#include "items.h"
|
||||
#include "player.h"
|
||||
#include "textdat.h"
|
||||
@@ -156,122 +159,203 @@ void ReloadExperienceData()
|
||||
void LoadClassData(std::string_view classPath, ClassAttributes &attributes, PlayerCombatData &combat)
|
||||
{
|
||||
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\attributes.tsv");
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::load(filename);
|
||||
if (!dataFileResult.has_value()) {
|
||||
DataFile::reportFatalError(dataFileResult.error(), filename);
|
||||
}
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||||
DataFile &dataFile = dataFileResult.value();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
if (tl::expected<void, DataFile::Error> result = dataFile.skipHeader();
|
||||
!result.has_value()) {
|
||||
DataFile::reportFatalError(result.error(), filename);
|
||||
}
|
||||
ValueReader reader { dataFile, filename };
|
||||
|
||||
auto recordIt = dataFile.begin();
|
||||
const auto recordEnd = dataFile.end();
|
||||
|
||||
const auto getValueField = [&](std::string_view expectedKey) {
|
||||
if (recordIt == recordEnd) {
|
||||
app_fatal(fmt::format("Missing field {} in {}", expectedKey, filename));
|
||||
}
|
||||
DataFileRecord record = *recordIt;
|
||||
FieldIterator fieldIt = record.begin();
|
||||
const FieldIterator endField = record.end();
|
||||
|
||||
const std::string_view key = (*fieldIt).value();
|
||||
if (key != expectedKey) {
|
||||
app_fatal(fmt::format("Unexpected field in {}: got {}, expected {}", filename, key, expectedKey));
|
||||
}
|
||||
|
||||
++fieldIt;
|
||||
if (fieldIt == endField) {
|
||||
DataFile::reportFatalError(DataFile::Error::NotEnoughColumns, filename);
|
||||
}
|
||||
return *fieldIt;
|
||||
};
|
||||
|
||||
const auto valueReader = [&](auto &&readFn) {
|
||||
return [&](std::string_view expectedKey, auto &outValue) {
|
||||
DataFileField valueField = getValueField(expectedKey);
|
||||
if (const tl::expected<void, devilution::DataFileField::Error> result = readFn(valueField, outValue);
|
||||
!result.has_value()) {
|
||||
DataFile::reportFatalFieldError(result.error(), filename, "Value", valueField);
|
||||
}
|
||||
++recordIt;
|
||||
};
|
||||
};
|
||||
|
||||
const auto readInt = valueReader([](DataFileField &valueField, auto &outValue) {
|
||||
return valueField.parseInt(outValue);
|
||||
});
|
||||
const auto readDecimal = valueReader([](DataFileField &valueField, auto &outValue) {
|
||||
return valueField.parseFixed6(outValue);
|
||||
});
|
||||
|
||||
readInt("baseStr", attributes.baseStr);
|
||||
readInt("baseMag", attributes.baseMag);
|
||||
readInt("baseDex", attributes.baseDex);
|
||||
readInt("baseVit", attributes.baseVit);
|
||||
readInt("maxStr", attributes.maxStr);
|
||||
readInt("maxMag", attributes.maxMag);
|
||||
readInt("maxDex", attributes.maxDex);
|
||||
readInt("maxVit", attributes.maxVit);
|
||||
readInt("blockBonus", combat.baseToBlock);
|
||||
readDecimal("adjLife", attributes.adjLife);
|
||||
readDecimal("adjMana", attributes.adjMana);
|
||||
readDecimal("lvlLife", attributes.lvlLife);
|
||||
readDecimal("lvlMana", attributes.lvlMana);
|
||||
readDecimal("chrLife", attributes.chrLife);
|
||||
readDecimal("chrMana", attributes.chrMana);
|
||||
readDecimal("itmLife", attributes.itmLife);
|
||||
readDecimal("itmMana", attributes.itmMana);
|
||||
readInt("baseMagicToHit", combat.baseMagicToHit);
|
||||
readInt("baseMeleeToHit", combat.baseMeleeToHit);
|
||||
readInt("baseRangedToHit", combat.baseRangedToHit);
|
||||
reader.readInt("baseStr", attributes.baseStr);
|
||||
reader.readInt("baseMag", attributes.baseMag);
|
||||
reader.readInt("baseDex", attributes.baseDex);
|
||||
reader.readInt("baseVit", attributes.baseVit);
|
||||
reader.readInt("maxStr", attributes.maxStr);
|
||||
reader.readInt("maxMag", attributes.maxMag);
|
||||
reader.readInt("maxDex", attributes.maxDex);
|
||||
reader.readInt("maxVit", attributes.maxVit);
|
||||
reader.readInt("blockBonus", combat.baseToBlock);
|
||||
reader.readDecimal("adjLife", attributes.adjLife);
|
||||
reader.readDecimal("adjMana", attributes.adjMana);
|
||||
reader.readDecimal("lvlLife", attributes.lvlLife);
|
||||
reader.readDecimal("lvlMana", attributes.lvlMana);
|
||||
reader.readDecimal("chrLife", attributes.chrLife);
|
||||
reader.readDecimal("chrMana", attributes.chrMana);
|
||||
reader.readDecimal("itmLife", attributes.itmLife);
|
||||
reader.readDecimal("itmMana", attributes.itmMana);
|
||||
reader.readInt("baseMagicToHit", combat.baseMagicToHit);
|
||||
reader.readInt("baseMeleeToHit", combat.baseMeleeToHit);
|
||||
reader.readInt("baseRangedToHit", combat.baseRangedToHit);
|
||||
}
|
||||
|
||||
void LoadClassStartingLoadoutData(std::string_view classPath, PlayerStartingLoadoutData &startingLoadoutData)
|
||||
{
|
||||
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\starting_loadout.tsv");
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||||
DataFile &dataFile = dataFileResult.value();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
ValueReader reader { dataFile, filename };
|
||||
|
||||
reader.read("skill", startingLoadoutData.skill, ParseSpellId);
|
||||
reader.read("spell", startingLoadoutData.spell, ParseSpellId);
|
||||
reader.readInt("spellLevel", startingLoadoutData.spellLevel);
|
||||
for (size_t i = 0; i < startingLoadoutData.items.size(); ++i) {
|
||||
reader.read(StrCat("item", i), startingLoadoutData.items[i], ParseItemId);
|
||||
}
|
||||
reader.readInt("gold", startingLoadoutData.gold);
|
||||
}
|
||||
|
||||
void LoadClassSpriteData(std::string_view classPath, PlayerSpriteData &spriteData)
|
||||
{
|
||||
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\sprites.tsv");
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||||
DataFile &dataFile = dataFileResult.value();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
ValueReader reader { dataFile, filename };
|
||||
|
||||
reader.readString("classPath", spriteData.classPath);
|
||||
reader.readChar("classChar", spriteData.classChar);
|
||||
reader.readString("trn", spriteData.trn);
|
||||
reader.readInt("stand", spriteData.stand);
|
||||
reader.readInt("walk", spriteData.walk);
|
||||
reader.readInt("attack", spriteData.attack);
|
||||
reader.readInt("bow", spriteData.bow);
|
||||
reader.readInt("swHit", spriteData.swHit);
|
||||
reader.readInt("block", spriteData.block);
|
||||
reader.readInt("lightning", spriteData.lightning);
|
||||
reader.readInt("fire", spriteData.fire);
|
||||
reader.readInt("magic", spriteData.magic);
|
||||
reader.readInt("death", spriteData.death);
|
||||
}
|
||||
|
||||
void LoadClassAnimData(std::string_view classPath, PlayerAnimData &animData)
|
||||
{
|
||||
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\animations.tsv");
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||||
DataFile &dataFile = dataFileResult.value();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
ValueReader reader { dataFile, filename };
|
||||
|
||||
reader.readInt("unarmedFrames", animData.unarmedFrames);
|
||||
reader.readInt("unarmedActionFrame", animData.unarmedActionFrame);
|
||||
reader.readInt("unarmedShieldFrames", animData.unarmedShieldFrames);
|
||||
reader.readInt("unarmedShieldActionFrame", animData.unarmedShieldActionFrame);
|
||||
reader.readInt("swordFrames", animData.swordFrames);
|
||||
reader.readInt("swordActionFrame", animData.swordActionFrame);
|
||||
reader.readInt("swordShieldFrames", animData.swordShieldFrames);
|
||||
reader.readInt("swordShieldActionFrame", animData.swordShieldActionFrame);
|
||||
reader.readInt("bowFrames", animData.bowFrames);
|
||||
reader.readInt("bowActionFrame", animData.bowActionFrame);
|
||||
reader.readInt("axeFrames", animData.axeFrames);
|
||||
reader.readInt("axeActionFrame", animData.axeActionFrame);
|
||||
reader.readInt("maceFrames", animData.maceFrames);
|
||||
reader.readInt("maceActionFrame", animData.maceActionFrame);
|
||||
reader.readInt("maceShieldFrames", animData.maceShieldFrames);
|
||||
reader.readInt("maceShieldActionFrame", animData.maceShieldActionFrame);
|
||||
reader.readInt("staffFrames", animData.staffFrames);
|
||||
reader.readInt("staffActionFrame", animData.staffActionFrame);
|
||||
reader.readInt("idleFrames", animData.idleFrames);
|
||||
reader.readInt("walkingFrames", animData.walkingFrames);
|
||||
reader.readInt("blockingFrames", animData.blockingFrames);
|
||||
reader.readInt("deathFrames", animData.deathFrames);
|
||||
reader.readInt("castingFrames", animData.castingFrames);
|
||||
reader.readInt("recoveryFrames", animData.recoveryFrames);
|
||||
reader.readInt("townIdleFrames", animData.townIdleFrames);
|
||||
reader.readInt("townWalkingFrames", animData.townWalkingFrames);
|
||||
reader.readInt("castingActionFrame", animData.castingActionFrame);
|
||||
}
|
||||
|
||||
void LoadClassSounds(std::string_view classPath, ankerl::unordered_dense::map<HeroSpeech, SfxID> &sounds)
|
||||
{
|
||||
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\sounds.tsv");
|
||||
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||||
DataFile &dataFile = dataFileResult.value();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
ValueReader reader { dataFile, filename };
|
||||
|
||||
magic_enum::enum_for_each<HeroSpeech>([&](const HeroSpeech speech) {
|
||||
reader.read(magic_enum::enum_name(speech), sounds[speech], ParseSfxId);
|
||||
});
|
||||
}
|
||||
|
||||
/** Contains the data related to each player class. */
|
||||
std::vector<PlayerData> PlayersData;
|
||||
|
||||
std::vector<ClassAttributes> ClassAttributesPerClass;
|
||||
|
||||
std::vector<PlayerCombatData> PlayersCombatData;
|
||||
|
||||
void LoadClassesAttributes()
|
||||
std::vector<PlayerStartingLoadoutData> PlayersStartingLoadoutData;
|
||||
|
||||
/** Contains the data related to each player class. */
|
||||
std::vector<PlayerSpriteData> PlayersSpriteData;
|
||||
|
||||
std::vector<PlayerAnimData> PlayersAnimData;
|
||||
|
||||
std::vector<ankerl::unordered_dense::map<HeroSpeech, SfxID>> herosounds;
|
||||
|
||||
} // namespace
|
||||
|
||||
void LoadClassDatFromFile(DataFile &dataFile, const std::string_view filename)
|
||||
{
|
||||
const std::array classPaths { "warrior", "rogue", "sorcerer", "monk", "bard", "barbarian" };
|
||||
ClassAttributesPerClass.clear();
|
||||
ClassAttributesPerClass.reserve(classPaths.size());
|
||||
PlayersCombatData.clear();
|
||||
PlayersCombatData.reserve(classPaths.size());
|
||||
for (const std::string_view path : classPaths) {
|
||||
LoadClassData(path, ClassAttributesPerClass.emplace_back(), PlayersCombatData.emplace_back());
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
|
||||
PlayersData.reserve(PlayersData.size() + dataFile.numRecords());
|
||||
|
||||
for (DataFileRecord record : dataFile) {
|
||||
if (PlayersData.size() >= static_cast<size_t>(HeroClass::NUM_MAX_CLASSES)) {
|
||||
DisplayFatalErrorAndExit(_("Loading Class Data Failed"), fmt::format(fmt::runtime(_("Could not add a class, since the maximum class number of {} has already been reached.")), static_cast<size_t>(HeroClass::NUM_MAX_CLASSES)));
|
||||
}
|
||||
|
||||
RecordReader reader { record, filename };
|
||||
|
||||
PlayerData &playerData = PlayersData.emplace_back();
|
||||
|
||||
reader.readString("className", playerData.className);
|
||||
reader.readString("folderName", playerData.folderName);
|
||||
reader.readInt("portrait", playerData.portrait);
|
||||
reader.readString("inv", playerData.inv);
|
||||
}
|
||||
}
|
||||
|
||||
/** Contains the data related to each player class. */
|
||||
const PlayerData PlayersData[] = {
|
||||
// clang-format off
|
||||
// HeroClass className
|
||||
// TRANSLATORS: Player Block start
|
||||
/* HeroClass::Warrior */ { N_("Warrior"), },
|
||||
/* HeroClass::Rogue */ { N_("Rogue"), },
|
||||
/* HeroClass::Sorcerer */ { N_("Sorcerer"), },
|
||||
/* HeroClass::Monk */ { N_("Monk"), },
|
||||
/* HeroClass::Bard */ { N_("Bard"), },
|
||||
// TRANSLATORS: Player Block end
|
||||
/* HeroClass::Barbarian */ { N_("Barbarian"), },
|
||||
// clang-format on
|
||||
};
|
||||
namespace {
|
||||
|
||||
const std::array<PlayerStartingLoadoutData, enum_size<HeroClass>::value> PlayersStartingLoadoutData { {
|
||||
// clang-format off
|
||||
// HeroClass skill, spell, spellLevel, items[0].diablo, items[0].hellfire, items[1].diablo, items[1].hellfire, items[2].diablo, items[2].hellfire, items[3].diablo, items[3].hellfire, items[4].diablo, items[4].hellfire, gold,
|
||||
/* HeroClass::Warrior */ { SpellID::ItemRepair, SpellID::Null, 0, { { { IDI_WARRIOR, IDI_WARRIOR, }, { IDI_WARRSHLD, IDI_WARRSHLD, }, { IDI_WARRCLUB, IDI_WARRCLUB, }, { IDI_HEAL, IDI_HEAL, }, { IDI_HEAL, IDI_HEAL, }, }, }, 100, },
|
||||
/* HeroClass::Rogue */ { SpellID::TrapDisarm, SpellID::Null, 0, { { { IDI_ROGUE, IDI_ROGUE, }, { IDI_HEAL, IDI_HEAL, }, { IDI_HEAL, IDI_HEAL, }, { IDI_NONE, IDI_NONE, }, { IDI_NONE, IDI_NONE, }, }, }, 100, },
|
||||
/* HeroClass::Sorcerer */ { SpellID::StaffRecharge, SpellID::Firebolt, 2, { { { IDI_SORCERER_DIABLO, IDI_SORCERER, }, { IDI_MANA, IDI_HEAL, }, { IDI_MANA, IDI_HEAL, }, { IDI_NONE, IDI_NONE, }, { IDI_NONE, IDI_NONE, }, }, }, 100, },
|
||||
/* HeroClass::Monk */ { SpellID::Search, SpellID::Null, 0, { { { IDI_SHORTSTAFF, IDI_SHORTSTAFF, }, { IDI_HEAL, IDI_HEAL, }, { IDI_HEAL, IDI_HEAL, }, { IDI_NONE, IDI_NONE, }, { IDI_NONE, IDI_NONE, }, }, }, 100, },
|
||||
/* HeroClass::Bard */ { SpellID::Identify, SpellID::Null, 0, { { { IDI_BARDSWORD, IDI_BARDSWORD, }, { IDI_BARDDAGGER, IDI_BARDDAGGER, }, { IDI_HEAL, IDI_HEAL, }, { IDI_HEAL, IDI_HEAL, }, { IDI_NONE, IDI_NONE, }, }, }, 100, },
|
||||
/* HeroClass::Barbarian */ { SpellID::Rage, SpellID::Null, 0, { { { IDI_BARBARIAN, IDI_BARBARIAN, }, { IDI_WARRSHLD, IDI_WARRSHLD, }, { IDI_HEAL, IDI_HEAL, }, { IDI_HEAL, IDI_HEAL, }, { IDI_NONE, IDI_NONE, }, }, }, 100, }
|
||||
// clang-format on
|
||||
} };
|
||||
void LoadClassDat()
|
||||
{
|
||||
const std::string_view filename = "txtdata\\classes\\classdat.tsv";
|
||||
DataFile dataFile = DataFile::loadOrDie(filename);
|
||||
PlayersData.clear();
|
||||
LoadClassDatFromFile(dataFile, filename);
|
||||
|
||||
PlayersData.shrink_to_fit();
|
||||
}
|
||||
|
||||
void LoadClassesAttributes()
|
||||
{
|
||||
ClassAttributesPerClass.clear();
|
||||
ClassAttributesPerClass.reserve(PlayersData.size());
|
||||
PlayersCombatData.clear();
|
||||
PlayersCombatData.reserve(PlayersData.size());
|
||||
PlayersStartingLoadoutData.clear();
|
||||
PlayersStartingLoadoutData.reserve(PlayersData.size());
|
||||
PlayersSpriteData.clear();
|
||||
PlayersSpriteData.reserve(PlayersData.size());
|
||||
PlayersAnimData.clear();
|
||||
PlayersAnimData.reserve(PlayersData.size());
|
||||
herosounds.clear();
|
||||
herosounds.reserve(PlayersData.size());
|
||||
|
||||
for (const PlayerData &playerData : PlayersData) {
|
||||
LoadClassData(playerData.folderName, ClassAttributesPerClass.emplace_back(), PlayersCombatData.emplace_back());
|
||||
LoadClassStartingLoadoutData(playerData.folderName, PlayersStartingLoadoutData.emplace_back());
|
||||
LoadClassSpriteData(playerData.folderName, PlayersSpriteData.emplace_back());
|
||||
LoadClassAnimData(playerData.folderName, PlayersAnimData.emplace_back());
|
||||
LoadClassSounds(playerData.folderName, herosounds.emplace_back());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -283,9 +367,22 @@ const ClassAttributes &GetClassAttributes(HeroClass playerClass)
|
||||
void LoadPlayerDataFiles()
|
||||
{
|
||||
ReloadExperienceData();
|
||||
LoadClassDat();
|
||||
LoadClassesAttributes();
|
||||
}
|
||||
|
||||
SfxID GetHeroSound(HeroClass clazz, HeroSpeech speech)
|
||||
{
|
||||
const size_t playerClassIndex = static_cast<size_t>(clazz);
|
||||
assert(playerClassIndex < herosounds.size());
|
||||
const auto findIt = herosounds[playerClassIndex].find(speech);
|
||||
if (findIt != herosounds[playerClassIndex].end()) {
|
||||
return findIt->second;
|
||||
}
|
||||
|
||||
return SfxID::None;
|
||||
}
|
||||
|
||||
uint32_t GetNextExperienceThresholdForLevel(unsigned level)
|
||||
{
|
||||
return ExperienceData.getThresholdForLevel(level);
|
||||
@@ -296,56 +393,44 @@ uint8_t GetMaximumCharacterLevel()
|
||||
return ExperienceData.getMaxLevel();
|
||||
}
|
||||
|
||||
const PlayerData &GetPlayerDataForClass(HeroClass playerClass)
|
||||
size_t GetNumPlayerClasses()
|
||||
{
|
||||
return PlayersData[static_cast<size_t>(playerClass)];
|
||||
return PlayersData.size();
|
||||
}
|
||||
|
||||
const SfxID herosounds[enum_size<HeroClass>::value][enum_size<HeroSpeech>::value] = {
|
||||
// clang-format off
|
||||
{ SfxID::Warrior1, SfxID::Warrior2, SfxID::Warrior3, SfxID::Warrior4, SfxID::Warrior5, SfxID::Warrior6, SfxID::Warrior7, SfxID::Warrior8, SfxID::Warrior9, SfxID::Warrior10, SfxID::Warrior11, SfxID::Warrior12, SfxID::Warrior13, SfxID::Warrior14, SfxID::Warrior15, SfxID::Warrior16, SfxID::Warrior17, SfxID::Warrior18, SfxID::Warrior19, SfxID::Warrior20, SfxID::Warrior21, SfxID::Warrior22, SfxID::Warrior23, SfxID::Warrior24, SfxID::Warrior25, SfxID::Warrior26, SfxID::Warrior27, SfxID::Warrior28, SfxID::Warrior29, SfxID::Warrior30, SfxID::Warrior31, SfxID::Warrior32, SfxID::Warrior33, SfxID::Warrior34, SfxID::Warrior35, SfxID::Warrior36, SfxID::Warrior37, SfxID::Warrior38, SfxID::Warrior39, SfxID::Warrior40, SfxID::Warrior41, SfxID::Warrior42, SfxID::Warrior43, SfxID::Warrior44, SfxID::Warrior45, SfxID::Warrior46, SfxID::Warrior47, SfxID::Warrior48, SfxID::Warrior49, SfxID::Warrior50, SfxID::Warrior51, SfxID::Warrior52, SfxID::Warrior53, SfxID::Warrior54, SfxID::Warrior55, SfxID::Warrior56, SfxID::Warrior57, SfxID::Warrior58, SfxID::Warrior59, SfxID::Warrior60, SfxID::Warrior61, SfxID::Warrior62, SfxID::Warrior63, SfxID::Warrior64, SfxID::Warrior65, SfxID::Warrior66, SfxID::Warrior67, SfxID::Warrior68, SfxID::Warrior69, SfxID::Warrior70, SfxID::Warrior71, SfxID::Warrior72, SfxID::Warrior73, SfxID::Warrior74, SfxID::Warrior75, SfxID::Warrior76, SfxID::Warrior77, SfxID::Warrior78, SfxID::Warrior79, SfxID::Warrior80, SfxID::Warrior81, SfxID::Warrior82, SfxID::Warrior83, SfxID::Warrior84, SfxID::Warrior85, SfxID::Warrior86, SfxID::Warrior87, SfxID::Warrior88, SfxID::Warrior89, SfxID::Warrior90, SfxID::Warrior91, SfxID::Warrior92, SfxID::Warrior93, SfxID::Warrior94, SfxID::Warrior95, SfxID::Warrior96b, SfxID::Warrior97, SfxID::Warrior98, SfxID::Warrior99, SfxID::Warrior100, SfxID::Warrior101, SfxID::Warrior102, SfxID::WarriorDeath },
|
||||
{ SfxID::Rogue1, SfxID::Rogue2, SfxID::Rogue3, SfxID::Rogue4, SfxID::Rogue5, SfxID::Rogue6, SfxID::Rogue7, SfxID::Rogue8, SfxID::Rogue9, SfxID::Rogue10, SfxID::Rogue11, SfxID::Rogue12, SfxID::Rogue13, SfxID::Rogue14, SfxID::Rogue15, SfxID::Rogue16, SfxID::Rogue17, SfxID::Rogue18, SfxID::Rogue19, SfxID::Rogue20, SfxID::Rogue21, SfxID::Rogue22, SfxID::Rogue23, SfxID::Rogue24, SfxID::Rogue25, SfxID::Rogue26, SfxID::Rogue27, SfxID::Rogue28, SfxID::Rogue29, SfxID::Rogue30, SfxID::Rogue31, SfxID::Rogue32, SfxID::Rogue33, SfxID::Rogue34, SfxID::Rogue35, SfxID::Rogue36, SfxID::Rogue37, SfxID::Rogue38, SfxID::Rogue39, SfxID::Rogue40, SfxID::Rogue41, SfxID::Rogue42, SfxID::Rogue43, SfxID::Rogue44, SfxID::Rogue45, SfxID::Rogue46, SfxID::Rogue47, SfxID::Rogue48, SfxID::Rogue49, SfxID::Rogue50, SfxID::Rogue51, SfxID::Rogue52, SfxID::Rogue53, SfxID::Rogue54, SfxID::Rogue55, SfxID::Rogue56, SfxID::Rogue57, SfxID::Rogue58, SfxID::Rogue59, SfxID::Rogue60, SfxID::Rogue61, SfxID::Rogue62, SfxID::Rogue63, SfxID::Rogue64, SfxID::Rogue65, SfxID::Rogue66, SfxID::Rogue67, SfxID::Rogue68, SfxID::Rogue69, SfxID::Rogue70, SfxID::Rogue71, SfxID::Rogue72, SfxID::Rogue73, SfxID::Rogue74, SfxID::Rogue75, SfxID::Rogue76, SfxID::Rogue77, SfxID::Rogue78, SfxID::Rogue79, SfxID::Rogue80, SfxID::Rogue81, SfxID::Rogue82, SfxID::Rogue83, SfxID::Rogue84, SfxID::Rogue85, SfxID::Rogue86, SfxID::Rogue87, SfxID::Rogue88, SfxID::Rogue89, SfxID::Rogue90, SfxID::Rogue91, SfxID::Rogue92, SfxID::Rogue93, SfxID::Rogue94, SfxID::Rogue95, SfxID::Rogue96, SfxID::Rogue97, SfxID::Rogue98, SfxID::Rogue99, SfxID::Rogue100, SfxID::Rogue101, SfxID::Rogue102, SfxID::Rogue71 },
|
||||
{ SfxID::Sorceror1, SfxID::Sorceror2, SfxID::Sorceror3, SfxID::Sorceror4, SfxID::Sorceror5, SfxID::Sorceror6, SfxID::Sorceror7, SfxID::Sorceror8, SfxID::Sorceror9, SfxID::Sorceror10, SfxID::Sorceror11, SfxID::Sorceror12, SfxID::Sorceror13, SfxID::Sorceror14, SfxID::Sorceror15, SfxID::Sorceror16, SfxID::Sorceror17, SfxID::Sorceror18, SfxID::Sorceror19, SfxID::Sorceror20, SfxID::Sorceror21, SfxID::Sorceror22, SfxID::Sorceror23, SfxID::Sorceror24, SfxID::Sorceror25, SfxID::Sorceror26, SfxID::Sorceror27, SfxID::Sorceror28, SfxID::Sorceror29, SfxID::Sorceror30, SfxID::Sorceror31, SfxID::Sorceror32, SfxID::Sorceror33, SfxID::Sorceror34, SfxID::Sorceror35, SfxID::Sorceror36, SfxID::Sorceror37, SfxID::Sorceror38, SfxID::Sorceror39, SfxID::Sorceror40, SfxID::Sorceror41, SfxID::Sorceror42, SfxID::Sorceror43, SfxID::Sorceror44, SfxID::Sorceror45, SfxID::Sorceror46, SfxID::Sorceror47, SfxID::Sorceror48, SfxID::Sorceror49, SfxID::Sorceror50, SfxID::Sorceror51, SfxID::Sorceror52, SfxID::Sorceror53, SfxID::Sorceror54, SfxID::Sorceror55, SfxID::Sorceror56, SfxID::Sorceror57, SfxID::Sorceror58, SfxID::Sorceror59, SfxID::Sorceror60, SfxID::Sorceror61, SfxID::Sorceror62, SfxID::Sorceror63, SfxID::Sorceror64, SfxID::Sorceror65, SfxID::Sorceror66, SfxID::Sorceror67, SfxID::Sorceror68, SfxID::Sorceror69, SfxID::Sorceror70, SfxID::Sorceror71, SfxID::Sorceror72, SfxID::Sorceror73, SfxID::Sorceror74, SfxID::Sorceror75, SfxID::Sorceror76, SfxID::Sorceror77, SfxID::Sorceror78, SfxID::Sorceror79, SfxID::Sorceror80, SfxID::Sorceror81, SfxID::Sorceror82, SfxID::Sorceror83, SfxID::Sorceror84, SfxID::Sorceror85, SfxID::Sorceror86, SfxID::Sorceror87, SfxID::Sorceror88, SfxID::Sorceror89, SfxID::Sorceror90, SfxID::Sorceror91, SfxID::Sorceror92, SfxID::Sorceror93, SfxID::Sorceror94, SfxID::Sorceror95, SfxID::Sorceror96, SfxID::Sorceror97, SfxID::Sorceror98, SfxID::Sorceror99, SfxID::Sorceror100, SfxID::Sorceror101, SfxID::Sorceror102, SfxID::Sorceror71 },
|
||||
{ SfxID::Monk1, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk8, SfxID::Monk9, SfxID::Monk10, SfxID::Monk11, SfxID::Monk12, SfxID::Monk13, SfxID::Monk14, SfxID::Monk15, SfxID::Monk16, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk24, SfxID::None, SfxID::None, SfxID::Monk27, SfxID::None, SfxID::Monk29, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk34, SfxID::Monk35, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk43, SfxID::None, SfxID::None, SfxID::Monk46, SfxID::None, SfxID::None, SfxID::Monk49, SfxID::Monk50, SfxID::None, SfxID::Monk52, SfxID::None, SfxID::Monk54, SfxID::Monk55, SfxID::Monk56, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk61, SfxID::Monk62, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk68, SfxID::Monk69, SfxID::Monk70, SfxID::Monk71, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk79, SfxID::Monk80, SfxID::None, SfxID::Monk82, SfxID::Monk83, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk87, SfxID::Monk88, SfxID::Monk89, SfxID::None, SfxID::Monk91, SfxID::Monk92, SfxID::None, SfxID::Monk94, SfxID::Monk95, SfxID::Monk96, SfxID::Monk97, SfxID::Monk98, SfxID::Monk99, SfxID::None, SfxID::None, SfxID::None, SfxID::Monk71 },
|
||||
{ SfxID::Rogue1, SfxID::Rogue2, SfxID::Rogue3, SfxID::Rogue4, SfxID::Rogue5, SfxID::Rogue6, SfxID::Rogue7, SfxID::Rogue8, SfxID::Rogue9, SfxID::Rogue10, SfxID::Rogue11, SfxID::Rogue12, SfxID::Rogue13, SfxID::Rogue14, SfxID::Rogue15, SfxID::Rogue16, SfxID::Rogue17, SfxID::Rogue18, SfxID::Rogue19, SfxID::Rogue20, SfxID::Rogue21, SfxID::Rogue22, SfxID::Rogue23, SfxID::Rogue24, SfxID::Rogue25, SfxID::Rogue26, SfxID::Rogue27, SfxID::Rogue28, SfxID::Rogue29, SfxID::Rogue30, SfxID::Rogue31, SfxID::Rogue32, SfxID::Rogue33, SfxID::Rogue34, SfxID::Rogue35, SfxID::Rogue36, SfxID::Rogue37, SfxID::Rogue38, SfxID::Rogue39, SfxID::Rogue40, SfxID::Rogue41, SfxID::Rogue42, SfxID::Rogue43, SfxID::Rogue44, SfxID::Rogue45, SfxID::Rogue46, SfxID::Rogue47, SfxID::Rogue48, SfxID::Rogue49, SfxID::Rogue50, SfxID::Rogue51, SfxID::Rogue52, SfxID::Rogue53, SfxID::Rogue54, SfxID::Rogue55, SfxID::Rogue56, SfxID::Rogue57, SfxID::Rogue58, SfxID::Rogue59, SfxID::Rogue60, SfxID::Rogue61, SfxID::Rogue62, SfxID::Rogue63, SfxID::Rogue64, SfxID::Rogue65, SfxID::Rogue66, SfxID::Rogue67, SfxID::Rogue68, SfxID::Rogue69, SfxID::Rogue70, SfxID::Rogue71, SfxID::Rogue72, SfxID::Rogue73, SfxID::Rogue74, SfxID::Rogue75, SfxID::Rogue76, SfxID::Rogue77, SfxID::Rogue78, SfxID::Rogue79, SfxID::Rogue80, SfxID::Rogue81, SfxID::Rogue82, SfxID::Rogue83, SfxID::Rogue84, SfxID::Rogue85, SfxID::Rogue86, SfxID::Rogue87, SfxID::Rogue88, SfxID::Rogue89, SfxID::Rogue90, SfxID::Rogue91, SfxID::Rogue92, SfxID::Rogue93, SfxID::Rogue94, SfxID::Rogue95, SfxID::Rogue96, SfxID::Rogue97, SfxID::Rogue98, SfxID::Rogue99, SfxID::Rogue100, SfxID::Rogue101, SfxID::Rogue102, SfxID::Rogue71 },
|
||||
{ SfxID::Warrior1, SfxID::Warrior2, SfxID::Warrior3, SfxID::Warrior4, SfxID::Warrior5, SfxID::Warrior6, SfxID::Warrior7, SfxID::Warrior8, SfxID::Warrior9, SfxID::Warrior10, SfxID::Warrior11, SfxID::Warrior12, SfxID::Warrior13, SfxID::Warrior14, SfxID::Warrior15, SfxID::Warrior16, SfxID::Warrior17, SfxID::Warrior18, SfxID::Warrior19, SfxID::Warrior20, SfxID::Warrior21, SfxID::Warrior22, SfxID::Warrior23, SfxID::Warrior24, SfxID::Warrior25, SfxID::Warrior26, SfxID::Warrior27, SfxID::Warrior28, SfxID::Warrior29, SfxID::Warrior30, SfxID::Warrior31, SfxID::Warrior32, SfxID::Warrior33, SfxID::Warrior34, SfxID::Warrior35, SfxID::Warrior36, SfxID::Warrior37, SfxID::Warrior38, SfxID::Warrior39, SfxID::Warrior40, SfxID::Warrior41, SfxID::Warrior42, SfxID::Warrior43, SfxID::Warrior44, SfxID::Warrior45, SfxID::Warrior46, SfxID::Warrior47, SfxID::Warrior48, SfxID::Warrior49, SfxID::Warrior50, SfxID::Warrior51, SfxID::Warrior52, SfxID::Warrior53, SfxID::Warrior54, SfxID::Warrior55, SfxID::Warrior56, SfxID::Warrior57, SfxID::Warrior58, SfxID::Warrior59, SfxID::Warrior60, SfxID::Warrior61, SfxID::Warrior62, SfxID::Warrior63, SfxID::Warrior64, SfxID::Warrior65, SfxID::Warrior66, SfxID::Warrior67, SfxID::Warrior68, SfxID::Warrior69, SfxID::Warrior70, SfxID::Warrior71, SfxID::Warrior72, SfxID::Warrior73, SfxID::Warrior74, SfxID::Warrior75, SfxID::Warrior76, SfxID::Warrior77, SfxID::Warrior78, SfxID::Warrior79, SfxID::Warrior80, SfxID::Warrior81, SfxID::Warrior82, SfxID::Warrior83, SfxID::Warrior84, SfxID::Warrior85, SfxID::Warrior86, SfxID::Warrior87, SfxID::Warrior88, SfxID::Warrior89, SfxID::Warrior90, SfxID::Warrior91, SfxID::Warrior92, SfxID::Warrior93, SfxID::Warrior94, SfxID::Warrior95, SfxID::Warrior96b, SfxID::Warrior97, SfxID::Warrior98, SfxID::Warrior99, SfxID::Warrior100, SfxID::Warrior101, SfxID::Warrior102, SfxID::Warrior71 },
|
||||
// clang-format on
|
||||
};
|
||||
const PlayerData &GetPlayerDataForClass(HeroClass playerClass)
|
||||
{
|
||||
const size_t playerClassIndex = static_cast<size_t>(playerClass);
|
||||
assert(playerClassIndex < PlayersData.size());
|
||||
return PlayersData[playerClassIndex];
|
||||
}
|
||||
|
||||
const PlayerCombatData &GetPlayerCombatDataForClass(HeroClass pClass)
|
||||
{
|
||||
return PlayersCombatData[static_cast<size_t>(pClass)];
|
||||
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||||
assert(playerClassIndex < PlayersCombatData.size());
|
||||
return PlayersCombatData[playerClassIndex];
|
||||
}
|
||||
|
||||
const PlayerStartingLoadoutData &GetPlayerStartingLoadoutForClass(HeroClass pClass)
|
||||
{
|
||||
return PlayersStartingLoadoutData[static_cast<size_t>(pClass)];
|
||||
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||||
assert(playerClassIndex < PlayersStartingLoadoutData.size());
|
||||
return PlayersStartingLoadoutData[playerClassIndex];
|
||||
}
|
||||
|
||||
/** Contains the data related to each player class. */
|
||||
const PlayerSpriteData PlayersSpriteData[] = {
|
||||
// clang-format off
|
||||
// HeroClass classPath, stand, walk, attack, bow, swHit, block, lightning, fire, magic, death
|
||||
const PlayerSpriteData &GetPlayerSpriteDataForClass(HeroClass pClass)
|
||||
{
|
||||
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||||
assert(playerClassIndex < PlayersSpriteData.size());
|
||||
return PlayersSpriteData[playerClassIndex];
|
||||
}
|
||||
|
||||
/* HeroClass::Warrior */ { "warrior", 96, 96, 128, 96, 96, 96, 96, 96, 96, 128 },
|
||||
/* HeroClass::Rogue */ { "rogue", 96, 96, 128, 128, 96, 96, 96, 96, 96, 128 },
|
||||
/* HeroClass::Sorcerer */ { "sorceror", 96, 96, 128, 128, 96, 96, 128, 128, 128, 128 },
|
||||
/* HeroClass::Monk */ { "monk", 112, 112, 130, 130, 98, 98, 114, 114, 114, 160 },
|
||||
/* HeroClass::Bard */ { "rogue", 96, 96, 128, 128, 96, 96, 96, 96, 96, 128 },
|
||||
/* HeroClass::Barbarian */ { "warrior", 96, 96, 128, 96, 96, 96, 96, 96, 96, 128 },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
const PlayerAnimData PlayersAnimData[] = {
|
||||
// clang-format off
|
||||
// HeroClass unarmedFrames, unarmedActionFrame, unarmedShieldFrames, unarmedShieldActionFrame, swordFrames, swordActionFrame, swordShieldFrames, swordShieldActionFrame, bowFrames, bowActionFrame, axeFrames, axeActionFrame, maceFrames, maceActionFrame, maceShieldFrames, maceShieldActionFrame, staffFrames, staffActionFrame, idleFrames, walkingFrames, blockingFrames, deathFrames, castingFrames, recoveryFrames, townIdleFrames, townWalkingFrames, castingActionFrame
|
||||
/* HeroClass::Warrior */ { 16, 9, 16, 9, 16, 9, 16, 9, 16, 11, 20, 10, 16, 9, 16, 9, 16, 11, 10, 8, 2, 20, 20, 6, 20, 8, 14 },
|
||||
/* HeroClass::Rogue */ { 18, 10, 18, 10, 18, 10, 18, 10, 12, 7, 22, 13, 18, 10, 18, 10, 16, 11, 8, 8, 4, 20, 16, 7, 20, 8, 12 },
|
||||
/* HeroClass::Sorcerer */ { 20, 12, 16, 9, 16, 12, 16, 12, 20, 16, 24, 16, 16, 12, 16, 12, 16, 12, 8, 8, 6, 20, 12, 8, 20, 8, 8 },
|
||||
/* HeroClass::Monk */ { 12, 7, 12, 7, 16, 12, 16, 12, 20, 14, 23, 14, 16, 12, 16, 12, 13, 8, 8, 8, 3, 20, 18, 6, 20, 8, 13 },
|
||||
/* HeroClass::Bard */ { 18, 10, 18, 10, 18, 10, 18, 10, 12, 11, 22, 13, 18, 10, 18, 10, 16, 11, 8, 8, 4, 20, 16, 7, 20, 8, 12 },
|
||||
/* HeroClass::Barbarian */ { 16, 9, 16, 9, 16, 9, 16, 9, 16, 11, 20, 8, 16, 8, 16, 8, 16, 11, 10, 8, 2, 20, 20, 6, 20, 8, 14 },
|
||||
// clang-format on
|
||||
};
|
||||
const PlayerAnimData &GetPlayerAnimDataForClass(HeroClass pClass)
|
||||
{
|
||||
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||||
assert(playerClassIndex < PlayersAnimData.size());
|
||||
return PlayersAnimData[playerClassIndex];
|
||||
}
|
||||
|
||||
} // namespace devilution
|
||||
|
||||
+22
-15
@@ -22,14 +22,20 @@ enum class HeroClass : uint8_t {
|
||||
Bard,
|
||||
Barbarian,
|
||||
|
||||
LAST = Barbarian
|
||||
NUM_MAX_CLASSES = std::numeric_limits<uint8_t>::max(),
|
||||
|
||||
LAST = Barbarian,
|
||||
};
|
||||
|
||||
struct PlayerData {
|
||||
/* Class Name */
|
||||
const char *className;
|
||||
/* Class Skill */
|
||||
SpellID skill = SpellID::Null;
|
||||
std::string className;
|
||||
/* Class Folder Name */
|
||||
std::string folderName;
|
||||
/* Class Portrait Index */
|
||||
uint8_t portrait;
|
||||
/* Class Inventory UI File */
|
||||
std::string inv;
|
||||
};
|
||||
|
||||
struct ClassAttributes {
|
||||
@@ -98,12 +104,7 @@ struct PlayerStartingLoadoutData {
|
||||
/* Initial level of the starting spell */
|
||||
uint8_t spellLevel;
|
||||
|
||||
struct ItemType {
|
||||
_item_indexes diablo;
|
||||
_item_indexes hellfire;
|
||||
};
|
||||
|
||||
std::array<ItemType, 5> items;
|
||||
std::array<_item_indexes, 5> items;
|
||||
|
||||
/* Initial gold amount, up to a single stack (5000 gold) */
|
||||
uint16_t gold;
|
||||
@@ -111,7 +112,11 @@ struct PlayerStartingLoadoutData {
|
||||
|
||||
struct PlayerSpriteData {
|
||||
/* Class Directory Path */
|
||||
const char *classPath;
|
||||
std::string classPath;
|
||||
/* Class letter used in graphic files */
|
||||
char classChar;
|
||||
/* Class TRN file */
|
||||
std::string trn;
|
||||
/* Sprite width: Stand */
|
||||
uint8_t stand;
|
||||
/* Sprite width: Walk */
|
||||
@@ -192,17 +197,19 @@ struct PlayerAnimData {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Attempts to load data values from external files, currently only Experience.tsv is supported.
|
||||
* @brief Attempts to load data values from external files.
|
||||
*/
|
||||
void LoadClassDatFromFile(DataFile &dataFile, const std::string_view filename);
|
||||
void LoadPlayerDataFiles();
|
||||
|
||||
extern const SfxID herosounds[enum_size<HeroClass>::value][enum_size<HeroSpeech>::value];
|
||||
SfxID GetHeroSound(HeroClass clazz, HeroSpeech speech);
|
||||
uint32_t GetNextExperienceThresholdForLevel(unsigned level);
|
||||
uint8_t GetMaximumCharacterLevel();
|
||||
size_t GetNumPlayerClasses();
|
||||
const PlayerData &GetPlayerDataForClass(HeroClass clazz);
|
||||
const PlayerCombatData &GetPlayerCombatDataForClass(HeroClass clazz);
|
||||
const PlayerStartingLoadoutData &GetPlayerStartingLoadoutForClass(HeroClass clazz);
|
||||
extern const PlayerSpriteData PlayersSpriteData[];
|
||||
extern const PlayerAnimData PlayersAnimData[];
|
||||
const PlayerSpriteData &GetPlayerSpriteDataForClass(HeroClass clazz);
|
||||
const PlayerAnimData &GetPlayerAnimDataForClass(HeroClass clazz);
|
||||
|
||||
} // namespace devilution
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 16
|
||||
unarmedActionFrame 9
|
||||
unarmedShieldFrames 16
|
||||
unarmedShieldActionFrame 9
|
||||
swordFrames 16
|
||||
swordActionFrame 9
|
||||
swordShieldFrames 16
|
||||
swordShieldActionFrame 9
|
||||
bowFrames 16
|
||||
bowActionFrame 11
|
||||
axeFrames 20
|
||||
axeActionFrame 8
|
||||
maceFrames 16
|
||||
maceActionFrame 8
|
||||
maceShieldFrames 16
|
||||
maceShieldActionFrame 8
|
||||
staffFrames 16
|
||||
staffActionFrame 11
|
||||
idleFrames 10
|
||||
walkingFrames 8
|
||||
blockingFrames 2
|
||||
deathFrames 20
|
||||
castingFrames 20
|
||||
recoveryFrames 6
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 14
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Warrior1
|
||||
HorazonsSanctumLore Warrior2
|
||||
GolemSpellLore Warrior3
|
||||
HorazonsCreatureOfFlameLore Warrior4
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton Warrior5
|
||||
GrimspikeLieutenantOfBelialLore Warrior6
|
||||
HorazonsJournal Warrior7
|
||||
YourDeathWillBeAvenged Warrior8
|
||||
RestInPeaceMyFriend Warrior9
|
||||
ValorLore Warrior10
|
||||
HallsOfTheBlindLore Warrior11
|
||||
WarlordOfBloodLore Warrior12
|
||||
ICantUseThisYet Warrior13
|
||||
ICantCarryAnymore Warrior14
|
||||
IHaveNoRoom Warrior15
|
||||
WhereWouldIPutThis Warrior16
|
||||
NoWay Warrior17
|
||||
NotAChance Warrior18
|
||||
IdNeverUseThis Warrior19
|
||||
IdHaveToEquipThat Warrior20
|
||||
ICantMoveThis Warrior21
|
||||
ICantMoveThisYet Warrior22
|
||||
ICantOpenThis Warrior23
|
||||
ICantOpenThisYet Warrior24
|
||||
ICantLiftThis Warrior25
|
||||
ICantLiftThisYet Warrior26
|
||||
ICantCastThatHere Warrior27
|
||||
ICantCastThatYet Warrior28
|
||||
ThatDidntDoAnything Warrior29
|
||||
ICanAlreadyDoThat Warrior30
|
||||
IDontNeedThat Warrior31
|
||||
IDontNeedToDoThat Warrior32
|
||||
IDontWantThat Warrior33
|
||||
IDontHaveASpellReady Warrior34
|
||||
NotEnoughMana Warrior35
|
||||
ThatWouldKillMe Warrior36
|
||||
ICantDoThat Warrior37
|
||||
No Warrior38
|
||||
Yes Warrior39
|
||||
ThatWontWork Warrior40
|
||||
ThatWontWorkHere Warrior41
|
||||
ThatWontWorkYet Warrior42
|
||||
ICantGetThereFromHere Warrior43
|
||||
ItsTooHeavy Warrior44
|
||||
ItsTooBig Warrior45
|
||||
JustWhatIWasLookingFor Warrior46
|
||||
IveGotABadFeelingAboutThis Warrior47
|
||||
GotMilk Warrior48
|
||||
ImNotThirsty Warrior49
|
||||
ImNoMilkmaid Warrior50
|
||||
ICouldBlowUpTheWholeVillage Warrior51
|
||||
YepThatsACowAlright Warrior52
|
||||
TooUghHeavy Warrior53
|
||||
InSpirituSanctum Warrior54
|
||||
PraedictumOtium Warrior55
|
||||
EfficioObitusUtInimicus Warrior56
|
||||
TheEnchantmentIsGone Warrior57
|
||||
OhTooEasy Warrior58
|
||||
BackToTheGrave Warrior59
|
||||
TimeToDie Warrior60
|
||||
ImNotImpressed Warrior61
|
||||
ImSorryDidIBreakYourConcentration Warrior62
|
||||
VengeanceIsMine Warrior63
|
||||
Die Warrior64
|
||||
Yeah Warrior65
|
||||
Ah Warrior66
|
||||
Phew Warrior67
|
||||
Argh Warrior68
|
||||
ArghClang Warrior69
|
||||
Aaaaargh Warrior70
|
||||
OofAh Warrior71
|
||||
HeavyBreathing Warrior72
|
||||
Oh Warrior73
|
||||
Wow Warrior74
|
||||
ThankTheLight Warrior75
|
||||
WhatWasThat Warrior76
|
||||
MmHmm Warrior77
|
||||
Hmm Warrior78
|
||||
UhHuh Warrior79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Warrior80
|
||||
TheTownIsSafeFromTheseFoulSpawn Warrior81
|
||||
RestWellLeoricIllFindYourSon Warrior82
|
||||
YourMadnessEndsHereBetrayer Warrior83
|
||||
YoullLureNoMoreMenToTheirDeaths Warrior84
|
||||
ReturnToHeavenWarriorOfLight Warrior85
|
||||
ICanSeeWhyTheyFearThisWeapon Warrior86
|
||||
ThisMustBeWhatGriswoldWanted Warrior87
|
||||
INeedToGetThisToLachdanan Warrior88
|
||||
INeedToGetThisToGriswold Warrior89
|
||||
IveNeverBeenHereBefore Warrior90
|
||||
MayTheSpiritOfArkaineProtectMe Warrior91
|
||||
ThisIsAPlaceOfGreatPower Warrior92
|
||||
ThisBladeMustBeDestroyed Warrior93
|
||||
YourReignOfPainHasEnded Warrior94
|
||||
NowThatsOneBigMushroom Warrior95
|
||||
TheSmellOfDeathSurroundsMe Warrior96b
|
||||
TheSanctityOfThisPlaceHasBeenFouled Warrior97
|
||||
ItsHotDownHere Warrior98
|
||||
IMustBeGettingClose Warrior99
|
||||
MaybeItsLockedFromTheInside Warrior100
|
||||
LooksLikeItsRustedShut Warrior101
|
||||
MaybeTheresAnotherWay Warrior102
|
||||
AuughUh Warrior71
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath warrior
|
||||
classChar c
|
||||
trn barbarian
|
||||
stand 96
|
||||
walk 96
|
||||
attack 128
|
||||
bow 96
|
||||
swHit 96
|
||||
block 96
|
||||
lightning 96
|
||||
fire 96
|
||||
magic 96
|
||||
death 128
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill Rage
|
||||
spell Null
|
||||
spellLevel 0
|
||||
item0 IDI_BARBARIAN
|
||||
item1 IDI_WARRSHLD
|
||||
item2 IDI_HEAL
|
||||
item3 IDI_HEAL
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 18
|
||||
unarmedActionFrame 10
|
||||
unarmedShieldFrames 18
|
||||
unarmedShieldActionFrame 10
|
||||
swordFrames 18
|
||||
swordActionFrame 10
|
||||
swordShieldFrames 18
|
||||
swordShieldActionFrame 10
|
||||
bowFrames 12
|
||||
bowActionFrame 11
|
||||
axeFrames 22
|
||||
axeActionFrame 13
|
||||
maceFrames 18
|
||||
maceActionFrame 10
|
||||
maceShieldFrames 18
|
||||
maceShieldActionFrame 10
|
||||
staffFrames 16
|
||||
staffActionFrame 11
|
||||
idleFrames 8
|
||||
walkingFrames 8
|
||||
blockingFrames 4
|
||||
deathFrames 20
|
||||
castingFrames 16
|
||||
recoveryFrames 7
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 12
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Rogue1
|
||||
HorazonsSanctumLore Rogue2
|
||||
GolemSpellLore Rogue3
|
||||
HorazonsCreatureOfFlameLore Rogue4
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton Rogue5
|
||||
GrimspikeLieutenantOfBelialLore Rogue6
|
||||
HorazonsJournal Rogue7
|
||||
YourDeathWillBeAvenged Rogue8
|
||||
RestInPeaceMyFriend Rogue9
|
||||
ValorLore Rogue10
|
||||
HallsOfTheBlindLore Rogue11
|
||||
WarlordOfBloodLore Rogue12
|
||||
ICantUseThisYet Rogue13
|
||||
ICantCarryAnymore Rogue14
|
||||
IHaveNoRoom Rogue15
|
||||
WhereWouldIPutThis Rogue16
|
||||
NoWay Rogue17
|
||||
NotAChance Rogue18
|
||||
IdNeverUseThis Rogue19
|
||||
IdHaveToEquipThat Rogue20
|
||||
ICantMoveThis Rogue21
|
||||
ICantMoveThisYet Rogue22
|
||||
ICantOpenThis Rogue23
|
||||
ICantOpenThisYet Rogue24
|
||||
ICantLiftThis Rogue25
|
||||
ICantLiftThisYet Rogue26
|
||||
ICantCastThatHere Rogue27
|
||||
ICantCastThatYet Rogue28
|
||||
ThatDidntDoAnything Rogue29
|
||||
ICanAlreadyDoThat Rogue30
|
||||
IDontNeedThat Rogue31
|
||||
IDontNeedToDoThat Rogue32
|
||||
IDontWantThat Rogue33
|
||||
IDontHaveASpellReady Rogue34
|
||||
NotEnoughMana Rogue35
|
||||
ThatWouldKillMe Rogue36
|
||||
ICantDoThat Rogue37
|
||||
No Rogue38
|
||||
Yes Rogue39
|
||||
ThatWontWork Rogue40
|
||||
ThatWontWorkHere Rogue41
|
||||
ThatWontWorkYet Rogue42
|
||||
ICantGetThereFromHere Rogue43
|
||||
ItsTooHeavy Rogue44
|
||||
ItsTooBig Rogue45
|
||||
JustWhatIWasLookingFor Rogue46
|
||||
IveGotABadFeelingAboutThis Rogue47
|
||||
GotMilk Rogue48
|
||||
ImNotThirsty Rogue49
|
||||
ImNoMilkmaid Rogue50
|
||||
ICouldBlowUpTheWholeVillage Rogue51
|
||||
YepThatsACowAlright Rogue52
|
||||
TooUghHeavy Rogue53
|
||||
InSpirituSanctum Rogue54
|
||||
PraedictumOtium Rogue55
|
||||
EfficioObitusUtInimicus Rogue56
|
||||
TheEnchantmentIsGone Rogue57
|
||||
OhTooEasy Rogue58
|
||||
BackToTheGrave Rogue59
|
||||
TimeToDie Rogue60
|
||||
ImNotImpressed Rogue61
|
||||
ImSorryDidIBreakYourConcentration Rogue62
|
||||
VengeanceIsMine Rogue63
|
||||
Die Rogue64
|
||||
Yeah Rogue65
|
||||
Ah Rogue66
|
||||
Phew Rogue67
|
||||
Argh Rogue68
|
||||
ArghClang Rogue69
|
||||
Aaaaargh Rogue70
|
||||
OofAh Rogue71
|
||||
HeavyBreathing Rogue72
|
||||
Oh Rogue73
|
||||
Wow Rogue74
|
||||
ThankTheLight Rogue75
|
||||
WhatWasThat Rogue76
|
||||
MmHmm Rogue77
|
||||
Hmm Rogue78
|
||||
UhHuh Rogue79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Rogue80
|
||||
TheTownIsSafeFromTheseFoulSpawn Rogue81
|
||||
RestWellLeoricIllFindYourSon Rogue82
|
||||
YourMadnessEndsHereBetrayer Rogue83
|
||||
YoullLureNoMoreMenToTheirDeaths Rogue84
|
||||
ReturnToHeavenWarriorOfLight Rogue85
|
||||
ICanSeeWhyTheyFearThisWeapon Rogue86
|
||||
ThisMustBeWhatGriswoldWanted Rogue87
|
||||
INeedToGetThisToLachdanan Rogue88
|
||||
INeedToGetThisToGriswold Rogue89
|
||||
IveNeverBeenHereBefore Rogue90
|
||||
MayTheSpiritOfArkaineProtectMe Rogue91
|
||||
ThisIsAPlaceOfGreatPower Rogue92
|
||||
ThisBladeMustBeDestroyed Rogue93
|
||||
YourReignOfPainHasEnded Rogue94
|
||||
NowThatsOneBigMushroom Rogue95
|
||||
TheSmellOfDeathSurroundsMe Rogue96
|
||||
TheSanctityOfThisPlaceHasBeenFouled Rogue97
|
||||
ItsHotDownHere Rogue98
|
||||
IMustBeGettingClose Rogue99
|
||||
MaybeItsLockedFromTheInside Rogue100
|
||||
LooksLikeItsRustedShut Rogue101
|
||||
MaybeTheresAnotherWay Rogue102
|
||||
AuughUh Rogue71
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath rogue
|
||||
classChar b
|
||||
trn bard
|
||||
stand 96
|
||||
walk 96
|
||||
attack 128
|
||||
bow 128
|
||||
swHit 96
|
||||
block 96
|
||||
lightning 96
|
||||
fire 96
|
||||
magic 96
|
||||
death 128
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill Identify
|
||||
spell Null
|
||||
spellLevel 0
|
||||
item0 IDI_BARDSWORD
|
||||
item1 IDI_BARDDAGGER
|
||||
item2 IDI_HEAL
|
||||
item3 IDI_HEAL
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -0,0 +1,7 @@
|
||||
className folderName portrait inv
|
||||
Warrior warrior 0 inv
|
||||
Rogue rogue 1 inv_rog
|
||||
Sorcerer sorcerer 2 inv_sor
|
||||
Monk monk 2 inv_sor
|
||||
Bard bard 1 inv_rog
|
||||
Barbarian barbarian 0 inv
|
||||
|
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 12
|
||||
unarmedActionFrame 7
|
||||
unarmedShieldFrames 12
|
||||
unarmedShieldActionFrame 7
|
||||
swordFrames 16
|
||||
swordActionFrame 12
|
||||
swordShieldFrames 16
|
||||
swordShieldActionFrame 12
|
||||
bowFrames 20
|
||||
bowActionFrame 14
|
||||
axeFrames 23
|
||||
axeActionFrame 14
|
||||
maceFrames 16
|
||||
maceActionFrame 12
|
||||
maceShieldFrames 16
|
||||
maceShieldActionFrame 12
|
||||
staffFrames 13
|
||||
staffActionFrame 8
|
||||
idleFrames 8
|
||||
walkingFrames 8
|
||||
blockingFrames 3
|
||||
deathFrames 20
|
||||
castingFrames 18
|
||||
recoveryFrames 6
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 13
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Monk1
|
||||
HorazonsSanctumLore None
|
||||
GolemSpellLore None
|
||||
HorazonsCreatureOfFlameLore None
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton None
|
||||
GrimspikeLieutenantOfBelialLore None
|
||||
HorazonsJournal None
|
||||
YourDeathWillBeAvenged Monk8
|
||||
RestInPeaceMyFriend Monk9
|
||||
ValorLore Monk10
|
||||
HallsOfTheBlindLore Monk11
|
||||
WarlordOfBloodLore Monk12
|
||||
ICantUseThisYet Monk13
|
||||
ICantCarryAnymore Monk14
|
||||
IHaveNoRoom Monk15
|
||||
WhereWouldIPutThis Monk16
|
||||
NoWay None
|
||||
NotAChance None
|
||||
IdNeverUseThis None
|
||||
IdHaveToEquipThat None
|
||||
ICantMoveThis None
|
||||
ICantMoveThisYet None
|
||||
ICantOpenThis None
|
||||
ICantOpenThisYet Monk24
|
||||
ICantLiftThis None
|
||||
ICantLiftThisYet None
|
||||
ICantCastThatHere Monk27
|
||||
ICantCastThatYet None
|
||||
ThatDidntDoAnything Monk29
|
||||
ICanAlreadyDoThat None
|
||||
IDontNeedThat None
|
||||
IDontNeedToDoThat None
|
||||
IDontWantThat None
|
||||
IDontHaveASpellReady Monk34
|
||||
NotEnoughMana Monk35
|
||||
ThatWouldKillMe None
|
||||
ICantDoThat None
|
||||
No None
|
||||
Yes None
|
||||
ThatWontWork None
|
||||
ThatWontWorkHere None
|
||||
ThatWontWorkYet None
|
||||
ICantGetThereFromHere Monk43
|
||||
ItsTooHeavy None
|
||||
ItsTooBig None
|
||||
JustWhatIWasLookingFor Monk46
|
||||
IveGotABadFeelingAboutThis None
|
||||
GotMilk None
|
||||
ImNotThirsty Monk49
|
||||
ImNoMilkmaid Monk50
|
||||
ICouldBlowUpTheWholeVillage None
|
||||
YepThatsACowAlright Monk52
|
||||
TooUghHeavy None
|
||||
InSpirituSanctum Monk54
|
||||
PraedictumOtium Monk55
|
||||
EfficioObitusUtInimicus Monk56
|
||||
TheEnchantmentIsGone None
|
||||
OhTooEasy None
|
||||
BackToTheGrave None
|
||||
TimeToDie None
|
||||
ImNotImpressed Monk61
|
||||
ImSorryDidIBreakYourConcentration Monk62
|
||||
VengeanceIsMine None
|
||||
Die None
|
||||
Yeah None
|
||||
Ah None
|
||||
Phew None
|
||||
Argh Monk68
|
||||
ArghClang Monk69
|
||||
Aaaaargh Monk70
|
||||
OofAh Monk71
|
||||
HeavyBreathing None
|
||||
Oh None
|
||||
Wow None
|
||||
ThankTheLight None
|
||||
WhatWasThat None
|
||||
MmHmm None
|
||||
Hmm None
|
||||
UhHuh Monk79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Monk80
|
||||
TheTownIsSafeFromTheseFoulSpawn None
|
||||
RestWellLeoricIllFindYourSon Monk82
|
||||
YourMadnessEndsHereBetrayer Monk83
|
||||
YoullLureNoMoreMenToTheirDeaths None
|
||||
ReturnToHeavenWarriorOfLight None
|
||||
ICanSeeWhyTheyFearThisWeapon None
|
||||
ThisMustBeWhatGriswoldWanted Monk87
|
||||
INeedToGetThisToLachdanan Monk88
|
||||
INeedToGetThisToGriswold Monk89
|
||||
IveNeverBeenHereBefore None
|
||||
MayTheSpiritOfArkaineProtectMe Monk91
|
||||
ThisIsAPlaceOfGreatPower Monk92
|
||||
ThisBladeMustBeDestroyed None
|
||||
YourReignOfPainHasEnded Monk94
|
||||
NowThatsOneBigMushroom Monk95
|
||||
TheSmellOfDeathSurroundsMe Monk96
|
||||
TheSanctityOfThisPlaceHasBeenFouled Monk97
|
||||
ItsHotDownHere Monk98
|
||||
IMustBeGettingClose Monk99
|
||||
MaybeItsLockedFromTheInside None
|
||||
LooksLikeItsRustedShut None
|
||||
MaybeTheresAnotherWay None
|
||||
AuughUh Monk71
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath monk
|
||||
classChar m
|
||||
trn monk
|
||||
stand 112
|
||||
walk 112
|
||||
attack 130
|
||||
bow 130
|
||||
swHit 98
|
||||
block 98
|
||||
lightning 114
|
||||
fire 114
|
||||
magic 114
|
||||
death 160
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill Search
|
||||
spell Null
|
||||
spellLevel 0
|
||||
item0 IDI_SHORTSTAFF
|
||||
item1 IDI_HEAL
|
||||
item2 IDI_HEAL
|
||||
item3 IDI_NONE
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 18
|
||||
unarmedActionFrame 10
|
||||
unarmedShieldFrames 18
|
||||
unarmedShieldActionFrame 10
|
||||
swordFrames 18
|
||||
swordActionFrame 10
|
||||
swordShieldFrames 18
|
||||
swordShieldActionFrame 10
|
||||
bowFrames 12
|
||||
bowActionFrame 7
|
||||
axeFrames 22
|
||||
axeActionFrame 13
|
||||
maceFrames 18
|
||||
maceActionFrame 10
|
||||
maceShieldFrames 18
|
||||
maceShieldActionFrame 10
|
||||
staffFrames 16
|
||||
staffActionFrame 11
|
||||
idleFrames 8
|
||||
walkingFrames 8
|
||||
blockingFrames 4
|
||||
deathFrames 20
|
||||
castingFrames 16
|
||||
recoveryFrames 7
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 12
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Rogue1
|
||||
HorazonsSanctumLore Rogue2
|
||||
GolemSpellLore Rogue3
|
||||
HorazonsCreatureOfFlameLore Rogue4
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton Rogue5
|
||||
GrimspikeLieutenantOfBelialLore Rogue6
|
||||
HorazonsJournal Rogue7
|
||||
YourDeathWillBeAvenged Rogue8
|
||||
RestInPeaceMyFriend Rogue9
|
||||
ValorLore Rogue10
|
||||
HallsOfTheBlindLore Rogue11
|
||||
WarlordOfBloodLore Rogue12
|
||||
ICantUseThisYet Rogue13
|
||||
ICantCarryAnymore Rogue14
|
||||
IHaveNoRoom Rogue15
|
||||
WhereWouldIPutThis Rogue16
|
||||
NoWay Rogue17
|
||||
NotAChance Rogue18
|
||||
IdNeverUseThis Rogue19
|
||||
IdHaveToEquipThat Rogue20
|
||||
ICantMoveThis Rogue21
|
||||
ICantMoveThisYet Rogue22
|
||||
ICantOpenThis Rogue23
|
||||
ICantOpenThisYet Rogue24
|
||||
ICantLiftThis Rogue25
|
||||
ICantLiftThisYet Rogue26
|
||||
ICantCastThatHere Rogue27
|
||||
ICantCastThatYet Rogue28
|
||||
ThatDidntDoAnything Rogue29
|
||||
ICanAlreadyDoThat Rogue30
|
||||
IDontNeedThat Rogue31
|
||||
IDontNeedToDoThat Rogue32
|
||||
IDontWantThat Rogue33
|
||||
IDontHaveASpellReady Rogue34
|
||||
NotEnoughMana Rogue35
|
||||
ThatWouldKillMe Rogue36
|
||||
ICantDoThat Rogue37
|
||||
No Rogue38
|
||||
Yes Rogue39
|
||||
ThatWontWork Rogue40
|
||||
ThatWontWorkHere Rogue41
|
||||
ThatWontWorkYet Rogue42
|
||||
ICantGetThereFromHere Rogue43
|
||||
ItsTooHeavy Rogue44
|
||||
ItsTooBig Rogue45
|
||||
JustWhatIWasLookingFor Rogue46
|
||||
IveGotABadFeelingAboutThis Rogue47
|
||||
GotMilk Rogue48
|
||||
ImNotThirsty Rogue49
|
||||
ImNoMilkmaid Rogue50
|
||||
ICouldBlowUpTheWholeVillage Rogue51
|
||||
YepThatsACowAlright Rogue52
|
||||
TooUghHeavy Rogue53
|
||||
InSpirituSanctum Rogue54
|
||||
PraedictumOtium Rogue55
|
||||
EfficioObitusUtInimicus Rogue56
|
||||
TheEnchantmentIsGone Rogue57
|
||||
OhTooEasy Rogue58
|
||||
BackToTheGrave Rogue59
|
||||
TimeToDie Rogue60
|
||||
ImNotImpressed Rogue61
|
||||
ImSorryDidIBreakYourConcentration Rogue62
|
||||
VengeanceIsMine Rogue63
|
||||
Die Rogue64
|
||||
Yeah Rogue65
|
||||
Ah Rogue66
|
||||
Phew Rogue67
|
||||
Argh Rogue68
|
||||
ArghClang Rogue69
|
||||
Aaaaargh Rogue70
|
||||
OofAh Rogue71
|
||||
HeavyBreathing Rogue72
|
||||
Oh Rogue73
|
||||
Wow Rogue74
|
||||
ThankTheLight Rogue75
|
||||
WhatWasThat Rogue76
|
||||
MmHmm Rogue77
|
||||
Hmm Rogue78
|
||||
UhHuh Rogue79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Rogue80
|
||||
TheTownIsSafeFromTheseFoulSpawn Rogue81
|
||||
RestWellLeoricIllFindYourSon Rogue82
|
||||
YourMadnessEndsHereBetrayer Rogue83
|
||||
YoullLureNoMoreMenToTheirDeaths Rogue84
|
||||
ReturnToHeavenWarriorOfLight Rogue85
|
||||
ICanSeeWhyTheyFearThisWeapon Rogue86
|
||||
ThisMustBeWhatGriswoldWanted Rogue87
|
||||
INeedToGetThisToLachdanan Rogue88
|
||||
INeedToGetThisToGriswold Rogue89
|
||||
IveNeverBeenHereBefore Rogue90
|
||||
MayTheSpiritOfArkaineProtectMe Rogue91
|
||||
ThisIsAPlaceOfGreatPower Rogue92
|
||||
ThisBladeMustBeDestroyed Rogue93
|
||||
YourReignOfPainHasEnded Rogue94
|
||||
NowThatsOneBigMushroom Rogue95
|
||||
TheSmellOfDeathSurroundsMe Rogue96
|
||||
TheSanctityOfThisPlaceHasBeenFouled Rogue97
|
||||
ItsHotDownHere Rogue98
|
||||
IMustBeGettingClose Rogue99
|
||||
MaybeItsLockedFromTheInside Rogue100
|
||||
LooksLikeItsRustedShut Rogue101
|
||||
MaybeTheresAnotherWay Rogue102
|
||||
AuughUh Rogue71
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath rogue
|
||||
classChar r
|
||||
trn rogue
|
||||
stand 96
|
||||
walk 96
|
||||
attack 128
|
||||
bow 128
|
||||
swHit 96
|
||||
block 96
|
||||
lightning 96
|
||||
fire 96
|
||||
magic 96
|
||||
death 128
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill TrapDisarm
|
||||
spell Null
|
||||
spellLevel 0
|
||||
item0 IDI_ROGUE
|
||||
item1 IDI_HEAL
|
||||
item2 IDI_HEAL
|
||||
item3 IDI_NONE
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 20
|
||||
unarmedActionFrame 12
|
||||
unarmedShieldFrames 16
|
||||
unarmedShieldActionFrame 9
|
||||
swordFrames 16
|
||||
swordActionFrame 12
|
||||
swordShieldFrames 16
|
||||
swordShieldActionFrame 12
|
||||
bowFrames 20
|
||||
bowActionFrame 16
|
||||
axeFrames 24
|
||||
axeActionFrame 16
|
||||
maceFrames 16
|
||||
maceActionFrame 12
|
||||
maceShieldFrames 16
|
||||
maceShieldActionFrame 12
|
||||
staffFrames 16
|
||||
staffActionFrame 12
|
||||
idleFrames 8
|
||||
walkingFrames 8
|
||||
blockingFrames 6
|
||||
deathFrames 20
|
||||
castingFrames 12
|
||||
recoveryFrames 8
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 8
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Sorceror1
|
||||
HorazonsSanctumLore Sorceror2
|
||||
GolemSpellLore Sorceror3
|
||||
HorazonsCreatureOfFlameLore Sorceror4
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton Sorceror5
|
||||
GrimspikeLieutenantOfBelialLore Sorceror6
|
||||
HorazonsJournal Sorceror7
|
||||
YourDeathWillBeAvenged Sorceror8
|
||||
RestInPeaceMyFriend Sorceror9
|
||||
ValorLore Sorceror10
|
||||
HallsOfTheBlindLore Sorceror11
|
||||
WarlordOfBloodLore Sorceror12
|
||||
ICantUseThisYet Sorceror13
|
||||
ICantCarryAnymore Sorceror14
|
||||
IHaveNoRoom Sorceror15
|
||||
WhereWouldIPutThis Sorceror16
|
||||
NoWay Sorceror17
|
||||
NotAChance Sorceror18
|
||||
IdNeverUseThis Sorceror19
|
||||
IdHaveToEquipThat Sorceror20
|
||||
ICantMoveThis Sorceror21
|
||||
ICantMoveThisYet Sorceror22
|
||||
ICantOpenThis Sorceror23
|
||||
ICantOpenThisYet Sorceror24
|
||||
ICantLiftThis Sorceror25
|
||||
ICantLiftThisYet Sorceror26
|
||||
ICantCastThatHere Sorceror27
|
||||
ICantCastThatYet Sorceror28
|
||||
ThatDidntDoAnything Sorceror29
|
||||
ICanAlreadyDoThat Sorceror30
|
||||
IDontNeedThat Sorceror31
|
||||
IDontNeedToDoThat Sorceror32
|
||||
IDontWantThat Sorceror33
|
||||
IDontHaveASpellReady Sorceror34
|
||||
NotEnoughMana Sorceror35
|
||||
ThatWouldKillMe Sorceror36
|
||||
ICantDoThat Sorceror37
|
||||
No Sorceror38
|
||||
Yes Sorceror39
|
||||
ThatWontWork Sorceror40
|
||||
ThatWontWorkHere Sorceror41
|
||||
ThatWontWorkYet Sorceror42
|
||||
ICantGetThereFromHere Sorceror43
|
||||
ItsTooHeavy Sorceror44
|
||||
ItsTooBig Sorceror45
|
||||
JustWhatIWasLookingFor Sorceror46
|
||||
IveGotABadFeelingAboutThis Sorceror47
|
||||
GotMilk Sorceror48
|
||||
ImNotThirsty Sorceror49
|
||||
ImNoMilkmaid Sorceror50
|
||||
ICouldBlowUpTheWholeVillage Sorceror51
|
||||
YepThatsACowAlright Sorceror52
|
||||
TooUghHeavy Sorceror53
|
||||
InSpirituSanctum Sorceror54
|
||||
PraedictumOtium Sorceror55
|
||||
EfficioObitusUtInimicus Sorceror56
|
||||
TheEnchantmentIsGone Sorceror57
|
||||
OhTooEasy Sorceror58
|
||||
BackToTheGrave Sorceror59
|
||||
TimeToDie Sorceror60
|
||||
ImNotImpressed Sorceror61
|
||||
ImSorryDidIBreakYourConcentration Sorceror62
|
||||
VengeanceIsMine Sorceror63
|
||||
Die Sorceror64
|
||||
Yeah Sorceror65
|
||||
Ah Sorceror66
|
||||
Phew Sorceror67
|
||||
Argh Sorceror68
|
||||
ArghClang Sorceror69
|
||||
Aaaaargh Sorceror70
|
||||
OofAh Sorceror71
|
||||
HeavyBreathing Sorceror72
|
||||
Oh Sorceror73
|
||||
Wow Sorceror74
|
||||
ThankTheLight Sorceror75
|
||||
WhatWasThat Sorceror76
|
||||
MmHmm Sorceror77
|
||||
Hmm Sorceror78
|
||||
UhHuh Sorceror79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Sorceror80
|
||||
TheTownIsSafeFromTheseFoulSpawn Sorceror81
|
||||
RestWellLeoricIllFindYourSon Sorceror82
|
||||
YourMadnessEndsHereBetrayer Sorceror83
|
||||
YoullLureNoMoreMenToTheirDeaths Sorceror84
|
||||
ReturnToHeavenWarriorOfLight Sorceror85
|
||||
ICanSeeWhyTheyFearThisWeapon Sorceror86
|
||||
ThisMustBeWhatGriswoldWanted Sorceror87
|
||||
INeedToGetThisToLachdanan Sorceror88
|
||||
INeedToGetThisToGriswold Sorceror89
|
||||
IveNeverBeenHereBefore Sorceror90
|
||||
MayTheSpiritOfArkaineProtectMe Sorceror91
|
||||
ThisIsAPlaceOfGreatPower Sorceror92
|
||||
ThisBladeMustBeDestroyed Sorceror93
|
||||
YourReignOfPainHasEnded Sorceror94
|
||||
NowThatsOneBigMushroom Sorceror95
|
||||
TheSmellOfDeathSurroundsMe Sorceror96
|
||||
TheSanctityOfThisPlaceHasBeenFouled Sorceror97
|
||||
ItsHotDownHere Sorceror98
|
||||
IMustBeGettingClose Sorceror99
|
||||
MaybeItsLockedFromTheInside Sorceror100
|
||||
LooksLikeItsRustedShut Sorceror101
|
||||
MaybeTheresAnotherWay Sorceror102
|
||||
AuughUh Sorceror71
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath sorceror
|
||||
classChar s
|
||||
trn sorcerer
|
||||
stand 96
|
||||
walk 96
|
||||
attack 128
|
||||
bow 128
|
||||
swHit 96
|
||||
block 96
|
||||
lightning 128
|
||||
fire 128
|
||||
magic 128
|
||||
death 128
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill StaffRecharge
|
||||
spell Firebolt
|
||||
spellLevel 2
|
||||
item0 IDI_SORCERER_DIABLO
|
||||
item1 IDI_MANA
|
||||
item2 IDI_MANA
|
||||
item3 IDI_NONE
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -0,0 +1,28 @@
|
||||
Variable Value
|
||||
unarmedFrames 16
|
||||
unarmedActionFrame 9
|
||||
unarmedShieldFrames 16
|
||||
unarmedShieldActionFrame 9
|
||||
swordFrames 16
|
||||
swordActionFrame 9
|
||||
swordShieldFrames 16
|
||||
swordShieldActionFrame 9
|
||||
bowFrames 16
|
||||
bowActionFrame 11
|
||||
axeFrames 20
|
||||
axeActionFrame 10
|
||||
maceFrames 16
|
||||
maceActionFrame 9
|
||||
maceShieldFrames 16
|
||||
maceShieldActionFrame 9
|
||||
staffFrames 16
|
||||
staffActionFrame 11
|
||||
idleFrames 10
|
||||
walkingFrames 8
|
||||
blockingFrames 2
|
||||
deathFrames 20
|
||||
castingFrames 20
|
||||
recoveryFrames 6
|
||||
townIdleFrames 20
|
||||
townWalkingFrames 8
|
||||
castingActionFrame 14
|
||||
|
@@ -0,0 +1,104 @@
|
||||
speech sfx
|
||||
ChamberOfBoneLore Warrior1
|
||||
HorazonsSanctumLore Warrior2
|
||||
GolemSpellLore Warrior3
|
||||
HorazonsCreatureOfFlameLore Warrior4
|
||||
MortaVespaGaieaInnuminoEvegeenJatanLuaGraton Warrior5
|
||||
GrimspikeLieutenantOfBelialLore Warrior6
|
||||
HorazonsJournal Warrior7
|
||||
YourDeathWillBeAvenged Warrior8
|
||||
RestInPeaceMyFriend Warrior9
|
||||
ValorLore Warrior10
|
||||
HallsOfTheBlindLore Warrior11
|
||||
WarlordOfBloodLore Warrior12
|
||||
ICantUseThisYet Warrior13
|
||||
ICantCarryAnymore Warrior14
|
||||
IHaveNoRoom Warrior15
|
||||
WhereWouldIPutThis Warrior16
|
||||
NoWay Warrior17
|
||||
NotAChance Warrior18
|
||||
IdNeverUseThis Warrior19
|
||||
IdHaveToEquipThat Warrior20
|
||||
ICantMoveThis Warrior21
|
||||
ICantMoveThisYet Warrior22
|
||||
ICantOpenThis Warrior23
|
||||
ICantOpenThisYet Warrior24
|
||||
ICantLiftThis Warrior25
|
||||
ICantLiftThisYet Warrior26
|
||||
ICantCastThatHere Warrior27
|
||||
ICantCastThatYet Warrior28
|
||||
ThatDidntDoAnything Warrior29
|
||||
ICanAlreadyDoThat Warrior30
|
||||
IDontNeedThat Warrior31
|
||||
IDontNeedToDoThat Warrior32
|
||||
IDontWantThat Warrior33
|
||||
IDontHaveASpellReady Warrior34
|
||||
NotEnoughMana Warrior35
|
||||
ThatWouldKillMe Warrior36
|
||||
ICantDoThat Warrior37
|
||||
No Warrior38
|
||||
Yes Warrior39
|
||||
ThatWontWork Warrior40
|
||||
ThatWontWorkHere Warrior41
|
||||
ThatWontWorkYet Warrior42
|
||||
ICantGetThereFromHere Warrior43
|
||||
ItsTooHeavy Warrior44
|
||||
ItsTooBig Warrior45
|
||||
JustWhatIWasLookingFor Warrior46
|
||||
IveGotABadFeelingAboutThis Warrior47
|
||||
GotMilk Warrior48
|
||||
ImNotThirsty Warrior49
|
||||
ImNoMilkmaid Warrior50
|
||||
ICouldBlowUpTheWholeVillage Warrior51
|
||||
YepThatsACowAlright Warrior52
|
||||
TooUghHeavy Warrior53
|
||||
InSpirituSanctum Warrior54
|
||||
PraedictumOtium Warrior55
|
||||
EfficioObitusUtInimicus Warrior56
|
||||
TheEnchantmentIsGone Warrior57
|
||||
OhTooEasy Warrior58
|
||||
BackToTheGrave Warrior59
|
||||
TimeToDie Warrior60
|
||||
ImNotImpressed Warrior61
|
||||
ImSorryDidIBreakYourConcentration Warrior62
|
||||
VengeanceIsMine Warrior63
|
||||
Die Warrior64
|
||||
Yeah Warrior65
|
||||
Ah Warrior66
|
||||
Phew Warrior67
|
||||
Argh Warrior68
|
||||
ArghClang Warrior69
|
||||
Aaaaargh Warrior70
|
||||
OofAh Warrior71
|
||||
HeavyBreathing Warrior72
|
||||
Oh Warrior73
|
||||
Wow Warrior74
|
||||
ThankTheLight Warrior75
|
||||
WhatWasThat Warrior76
|
||||
MmHmm Warrior77
|
||||
Hmm Warrior78
|
||||
UhHuh Warrior79
|
||||
TheSpiritsOfTheDeadAreNowAvenged Warrior80
|
||||
TheTownIsSafeFromTheseFoulSpawn Warrior81
|
||||
RestWellLeoricIllFindYourSon Warrior82
|
||||
YourMadnessEndsHereBetrayer Warrior83
|
||||
YoullLureNoMoreMenToTheirDeaths Warrior84
|
||||
ReturnToHeavenWarriorOfLight Warrior85
|
||||
ICanSeeWhyTheyFearThisWeapon Warrior86
|
||||
ThisMustBeWhatGriswoldWanted Warrior87
|
||||
INeedToGetThisToLachdanan Warrior88
|
||||
INeedToGetThisToGriswold Warrior89
|
||||
IveNeverBeenHereBefore Warrior90
|
||||
MayTheSpiritOfArkaineProtectMe Warrior91
|
||||
ThisIsAPlaceOfGreatPower Warrior92
|
||||
ThisBladeMustBeDestroyed Warrior93
|
||||
YourReignOfPainHasEnded Warrior94
|
||||
NowThatsOneBigMushroom Warrior95
|
||||
TheSmellOfDeathSurroundsMe Warrior96b
|
||||
TheSanctityOfThisPlaceHasBeenFouled Warrior97
|
||||
ItsHotDownHere Warrior98
|
||||
IMustBeGettingClose Warrior99
|
||||
MaybeItsLockedFromTheInside Warrior100
|
||||
LooksLikeItsRustedShut Warrior101
|
||||
MaybeTheresAnotherWay Warrior102
|
||||
AuughUh WarriorDeath
|
||||
|
@@ -0,0 +1,14 @@
|
||||
Variable Value
|
||||
classPath warrior
|
||||
classChar w
|
||||
trn warrior
|
||||
stand 96
|
||||
walk 96
|
||||
attack 128
|
||||
bow 96
|
||||
swHit 96
|
||||
block 96
|
||||
lightning 96
|
||||
fire 96
|
||||
magic 96
|
||||
death 128
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill ItemRepair
|
||||
spell Null
|
||||
spellLevel 0
|
||||
item0 IDI_WARRIOR
|
||||
item1 IDI_WARRSHLD
|
||||
item2 IDI_WARRCLUB
|
||||
item3 IDI_HEAL
|
||||
item4 IDI_HEAL
|
||||
gold 100
|
||||
|
@@ -0,0 +1,7 @@
|
||||
className folderName portrait inv
|
||||
Warrior warrior 0 inv
|
||||
Rogue rogue 1 inv_rog
|
||||
Sorcerer sorcerer 2 inv_sor
|
||||
Monk monk 3 inv_sor
|
||||
Bard bard 4 inv_rog
|
||||
Barbarian barbarian 0 inv
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Variable Value
|
||||
skill StaffRecharge
|
||||
spell Firebolt
|
||||
spellLevel 2
|
||||
item0 IDI_SORCERER
|
||||
item1 IDI_HEAL
|
||||
item2 IDI_HEAL
|
||||
item3 IDI_NONE
|
||||
item4 IDI_NONE
|
||||
gold 100
|
||||
|
@@ -84,6 +84,11 @@ BlockTestCase BlockData[] = {
|
||||
|
||||
TEST(Player, PM_DoGotHit)
|
||||
{
|
||||
LoadCoreArchives();
|
||||
LoadGameArchives();
|
||||
ASSERT_TRUE(HaveMainData());
|
||||
LoadPlayerDataFiles();
|
||||
|
||||
Players.resize(1);
|
||||
MyPlayer = &Players[0];
|
||||
for (size_t i = 0; i < sizeof(BlockData) / sizeof(*BlockData); i++) {
|
||||
|
||||
@@ -6,6 +6,7 @@ root = pathlib.Path(__file__).resolve().parent.parent
|
||||
translation_dummy_path = root.joinpath("Source/translation_dummy.cpp")
|
||||
|
||||
base_paths = {
|
||||
"classdat": root.joinpath("assets/txtdata/classes/classdat.tsv"),
|
||||
"monstdat": root.joinpath("assets/txtdata/monsters/monstdat.tsv"),
|
||||
"unique_monstdat": root.joinpath("assets/txtdata/monsters/unique_monstdat.tsv"),
|
||||
"itemdat": root.joinpath("assets/txtdata/items/itemdat.tsv"),
|
||||
@@ -40,6 +41,14 @@ def write_entry(temp_source, var_name, context, string_value, use_p):
|
||||
temp_source.write(f'const char *{var_name} = N_("{string_value}");\n')
|
||||
|
||||
def process_files(paths, temp_source):
|
||||
# Classes
|
||||
if "classdat" in paths:
|
||||
with open(paths["classdat"], 'r') as tsv:
|
||||
reader = csv.DictReader(tsv, delimiter='\t')
|
||||
for i, row in enumerate(reader):
|
||||
var_name = 'CLASS_' + row['className'].upper().replace(' ', '_').replace('-', '_')
|
||||
write_entry(temp_source, f'{var_name}_NAME', "default", row['className'], False)
|
||||
|
||||
# Monsters
|
||||
with open(paths["monstdat"], 'r') as tsv:
|
||||
reader = csv.DictReader(tsv, delimiter='\t')
|
||||
|
||||
Reference in New Issue
Block a user