You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
15 KiB
447 lines
15 KiB
|
3 years ago
|
/**
|
||
|
|
* @file playerdat.cpp
|
||
|
|
*
|
||
|
|
* Implementation of all player data.
|
||
|
|
*/
|
||
|
|
|
||
|
2 months ago
|
#include "tables/playerdat.hpp"
|
||
|
3 years ago
|
|
||
|
3 years ago
|
#include <algorithm>
|
||
|
|
#include <array>
|
||
|
3 years ago
|
#include <bitset>
|
||
|
|
#include <charconv>
|
||
|
3 years ago
|
#include <cstdint>
|
||
|
3 years ago
|
#include <vector>
|
||
|
3 years ago
|
|
||
|
3 years ago
|
#include <expected.hpp>
|
||
|
|
#include <fmt/format.h>
|
||
|
6 months ago
|
#include <magic_enum/magic_enum_utility.hpp>
|
||
|
3 years ago
|
|
||
|
|
#include "data/file.hpp"
|
||
|
6 months ago
|
#include "data/record_reader.hpp"
|
||
|
|
#include "data/value_reader.hpp"
|
||
|
3 years ago
|
#include "items.h"
|
||
|
|
#include "player.h"
|
||
|
2 months ago
|
#include "tables/textdat.h"
|
||
|
3 years ago
|
#include "utils/language.h"
|
||
|
3 years ago
|
#include "utils/static_vector.hpp"
|
||
|
3 years ago
|
#include "utils/str_cat.hpp"
|
||
|
3 years ago
|
|
||
|
|
namespace devilution {
|
||
|
|
|
||
|
3 years ago
|
namespace {
|
||
|
3 years ago
|
|
||
|
|
class ExperienceData {
|
||
|
|
/** Specifies the experience point limit of each level. */
|
||
|
|
std::vector<uint32_t> levelThresholds;
|
||
|
|
|
||
|
|
public:
|
||
|
|
uint8_t getMaxLevel() const
|
||
|
|
{
|
||
|
|
return static_cast<uint8_t>(std::min<size_t>(levelThresholds.size(), std::numeric_limits<uint8_t>::max()));
|
||
|
|
}
|
||
|
|
|
||
|
|
DVL_REINITIALIZES void clear()
|
||
|
|
{
|
||
|
|
levelThresholds.clear();
|
||
|
|
}
|
||
|
|
|
||
|
|
[[nodiscard]] uint32_t getThresholdForLevel(unsigned level) const
|
||
|
|
{
|
||
|
|
if (level > 0)
|
||
|
3 years ago
|
return levelThresholds[std::min<unsigned>(level - 1, getMaxLevel())];
|
||
|
3 years ago
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void setThresholdForLevel(unsigned level, uint32_t experience)
|
||
|
|
{
|
||
|
|
if (level > 0) {
|
||
|
|
if (level > levelThresholds.size()) {
|
||
|
|
// To avoid ValidatePlayer() resetting players to 0 experience we need to use the maximum possible value here
|
||
|
|
// As long as the file has no gaps it'll get initialised properly.
|
||
|
|
levelThresholds.resize(level, std::numeric_limits<uint32_t>::max());
|
||
|
|
}
|
||
|
|
|
||
|
|
levelThresholds[static_cast<size_t>(level - 1)] = experience;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} ExperienceData;
|
||
|
|
|
||
|
3 years ago
|
enum class ExperienceColumn {
|
||
|
|
Level,
|
||
|
|
Experience,
|
||
|
|
LAST = Experience
|
||
|
|
};
|
||
|
3 years ago
|
|
||
|
3 years ago
|
tl::expected<ExperienceColumn, ColumnDefinition::Error> mapExperienceColumnFromName(std::string_view name)
|
||
|
|
{
|
||
|
|
if (name == "Level") {
|
||
|
|
return ExperienceColumn::Level;
|
||
|
3 years ago
|
}
|
||
|
3 years ago
|
if (name == "Experience") {
|
||
|
|
return ExperienceColumn::Experience;
|
||
|
3 years ago
|
}
|
||
|
3 years ago
|
return tl::unexpected { ColumnDefinition::Error::UnknownColumn };
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
|
void ReloadExperienceData()
|
||
|
|
{
|
||
|
|
constexpr std::string_view filename = "txtdata\\Experience.tsv";
|
||
|
|
auto dataFileResult = DataFile::load(filename);
|
||
|
|
if (!dataFileResult.has_value()) {
|
||
|
3 years ago
|
DataFile::reportFatalError(dataFileResult.error(), filename);
|
||
|
3 years ago
|
}
|
||
|
3 years ago
|
DataFile &dataFile = dataFileResult.value();
|
||
|
3 years ago
|
|
||
|
3 years ago
|
constexpr unsigned ExpectedColumnCount = enum_size<ExperienceColumn>::value;
|
||
|
|
|
||
|
|
std::array<ColumnDefinition, ExpectedColumnCount> columns;
|
||
|
|
auto parseHeaderResult = dataFile.parseHeader<ExperienceColumn>(columns.data(), columns.data() + columns.size(), mapExperienceColumnFromName);
|
||
|
3 years ago
|
|
||
|
3 years ago
|
if (!parseHeaderResult.has_value()) {
|
||
|
|
DataFile::reportFatalError(parseHeaderResult.error(), filename);
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
|
ExperienceData.clear();
|
||
|
3 years ago
|
for (DataFileRecord record : dataFile) {
|
||
|
3 years ago
|
uint8_t level = 0;
|
||
|
|
uint32_t experience = 0;
|
||
|
|
bool skipRecord = false;
|
||
|
|
|
||
|
3 years ago
|
FieldIterator fieldIt = record.begin();
|
||
|
7 months ago
|
const FieldIterator endField = record.end();
|
||
|
3 years ago
|
for (auto &column : columns) {
|
||
|
3 years ago
|
fieldIt += column.skipLength;
|
||
|
3 years ago
|
|
||
|
3 years ago
|
if (fieldIt == endField) {
|
||
|
|
DataFile::reportFatalError(DataFile::Error::NotEnoughColumns, filename);
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
3 years ago
|
DataFileField field = *fieldIt;
|
||
|
|
|
||
|
|
switch (static_cast<ExperienceColumn>(column)) {
|
||
|
|
case ExperienceColumn::Level: {
|
||
|
3 years ago
|
auto parseIntResult = field.parseInt(level);
|
||
|
3 years ago
|
|
||
|
3 years ago
|
if (!parseIntResult.has_value()) {
|
||
|
3 years ago
|
if (*field == "MaxLevel") {
|
||
|
3 years ago
|
skipRecord = true;
|
||
|
|
} else {
|
||
|
3 years ago
|
DataFile::reportFatalFieldError(parseIntResult.error(), filename, "Level", field);
|
||
|
3 years ago
|
}
|
||
|
|
}
|
||
|
|
} break;
|
||
|
|
|
||
|
3 years ago
|
case ExperienceColumn::Experience: {
|
||
|
3 years ago
|
auto parseIntResult = field.parseInt(experience);
|
||
|
3 years ago
|
|
||
|
3 years ago
|
if (!parseIntResult.has_value()) {
|
||
|
|
DataFile::reportFatalFieldError(parseIntResult.error(), filename, "Experience", field);
|
||
|
3 years ago
|
}
|
||
|
|
} break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (skipRecord)
|
||
|
|
break;
|
||
|
|
|
||
|
3 years ago
|
++fieldIt;
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
|
if (!skipRecord)
|
||
|
|
ExperienceData.setThresholdForLevel(level, experience);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
6 months ago
|
tl::expected<PlayerClassFlag, std::string> ParsePlayerClassFlag(std::string_view value)
|
||
|
|
{
|
||
|
|
const std::optional<PlayerClassFlag> enumValueOpt = magic_enum::enum_cast<PlayerClassFlag>(value);
|
||
|
|
if (enumValueOpt.has_value()) {
|
||
|
|
return enumValueOpt.value();
|
||
|
|
}
|
||
|
|
return tl::make_unexpected("Unknown enum value");
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
void LoadClassData(std::string_view classPath, ClassAttributes &attributes, PlayerCombatData &combat)
|
||
|
3 years ago
|
{
|
||
|
|
const std::string filename = StrCat("txtdata\\classes\\", classPath, "\\attributes.tsv");
|
||
|
6 months ago
|
tl::expected<DataFile, DataFile::Error> dataFileResult = DataFile::loadOrDie(filename);
|
||
|
3 years ago
|
DataFile &dataFile = dataFileResult.value();
|
||
|
6 months ago
|
dataFile.skipHeaderOrDie(filename);
|
||
|
|
|
||
|
|
ValueReader reader { dataFile, filename };
|
||
|
|
|
||
|
6 months ago
|
reader.readEnumList("classFlags", attributes.classFlags, ParsePlayerClassFlag);
|
||
|
6 months ago
|
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);
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
6 months ago
|
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);
|
||
|
3 years ago
|
}
|
||
|
6 months ago
|
reader.readInt("gold", startingLoadoutData.gold);
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
6 months ago
|
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);
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
6 months ago
|
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);
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
6 months ago
|
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);
|
||
|
3 years ago
|
|
||
|
6 months ago
|
ValueReader reader { dataFile, filename };
|
||
|
3 years ago
|
|
||
|
6 months ago
|
magic_enum::enum_for_each<HeroSpeech>([&](const HeroSpeech speech) {
|
||
|
|
reader.read(magic_enum::enum_name(speech), sounds[speech], ParseSfxId);
|
||
|
3 years ago
|
});
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
6 months ago
|
/** Contains the data related to each player class. */
|
||
|
|
std::vector<PlayerData> PlayersData;
|
||
|
|
|
||
|
3 years ago
|
std::vector<ClassAttributes> ClassAttributesPerClass;
|
||
|
|
|
||
|
3 years ago
|
std::vector<PlayerCombatData> PlayersCombatData;
|
||
|
|
|
||
|
6 months ago
|
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)
|
||
|
|
{
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
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();
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
void LoadClassesAttributes()
|
||
|
|
{
|
||
|
|
ClassAttributesPerClass.clear();
|
||
|
6 months ago
|
ClassAttributesPerClass.reserve(PlayersData.size());
|
||
|
3 years ago
|
PlayersCombatData.clear();
|
||
|
6 months ago
|
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());
|
||
|
3 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
} // namespace
|
||
|
|
|
||
|
3 years ago
|
const ClassAttributes &GetClassAttributes(HeroClass playerClass)
|
||
|
|
{
|
||
|
|
return ClassAttributesPerClass[static_cast<size_t>(playerClass)];
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
void LoadPlayerDataFiles()
|
||
|
|
{
|
||
|
|
ReloadExperienceData();
|
||
|
6 months ago
|
LoadClassDat();
|
||
|
3 years ago
|
LoadClassesAttributes();
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
6 months ago
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
uint32_t GetNextExperienceThresholdForLevel(unsigned level)
|
||
|
3 years ago
|
{
|
||
|
3 years ago
|
return ExperienceData.getThresholdForLevel(level);
|
||
|
3 years ago
|
}
|
||
|
3 years ago
|
|
||
|
3 years ago
|
uint8_t GetMaximumCharacterLevel()
|
||
|
|
{
|
||
|
3 years ago
|
return ExperienceData.getMaxLevel();
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
6 months ago
|
size_t GetNumPlayerClasses()
|
||
|
3 years ago
|
{
|
||
|
6 months ago
|
return PlayersData.size();
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
6 months ago
|
const PlayerData &GetPlayerDataForClass(HeroClass playerClass)
|
||
|
|
{
|
||
|
|
const size_t playerClassIndex = static_cast<size_t>(playerClass);
|
||
|
|
assert(playerClassIndex < PlayersData.size());
|
||
|
|
return PlayersData[playerClassIndex];
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
2 years ago
|
const PlayerCombatData &GetPlayerCombatDataForClass(HeroClass pClass)
|
||
|
3 years ago
|
{
|
||
|
6 months ago
|
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||
|
|
assert(playerClassIndex < PlayersCombatData.size());
|
||
|
|
return PlayersCombatData[playerClassIndex];
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
2 years ago
|
const PlayerStartingLoadoutData &GetPlayerStartingLoadoutForClass(HeroClass pClass)
|
||
|
3 years ago
|
{
|
||
|
6 months ago
|
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||
|
|
assert(playerClassIndex < PlayersStartingLoadoutData.size());
|
||
|
|
return PlayersStartingLoadoutData[playerClassIndex];
|
||
|
3 years ago
|
}
|
||
|
|
|
||
|
6 months ago
|
const PlayerSpriteData &GetPlayerSpriteDataForClass(HeroClass pClass)
|
||
|
|
{
|
||
|
|
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||
|
|
assert(playerClassIndex < PlayersSpriteData.size());
|
||
|
|
return PlayersSpriteData[playerClassIndex];
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
6 months ago
|
const PlayerAnimData &GetPlayerAnimDataForClass(HeroClass pClass)
|
||
|
|
{
|
||
|
|
const size_t playerClassIndex = static_cast<size_t>(pClass);
|
||
|
|
assert(playerClassIndex < PlayersAnimData.size());
|
||
|
|
return PlayersAnimData[playerClassIndex];
|
||
|
|
}
|
||
|
3 years ago
|
|
||
|
|
} // namespace devilution
|