mirror of
https://github.com/libretro/devilutionX.git
synced 2026-11-07 10:25:57 -10:00
Extract spell data to a TSV
This commit is contained in:
1 parent
2da492d203
commit
124d113b57
13 files changed
+368
-77
No files matched your search
@@ -163,6 +163,7 @@ set(devilutionx_assets
|
||||
txtdata/monsters/monstdat.tsv
|
||||
txtdata/monsters/unique_monstdat.tsv
|
||||
txtdata/sound/effects.tsv
|
||||
txtdata/spells/spelldat.tsv
|
||||
ui_art/diablo.pal
|
||||
ui_art/hellfire.pal
|
||||
ui_art/creditsw.clx
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <charconv>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
|
||||
#include <expected.hpp>
|
||||
#include <function_ref.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "utils/parse_int.hpp"
|
||||
#include "utils/str_cat.hpp"
|
||||
#include "utils/str_split.hpp"
|
||||
|
||||
namespace devilution {
|
||||
@@ -157,6 +158,45 @@ public:
|
||||
return parseIntArray<T>(destination.data(), N);
|
||||
}
|
||||
|
||||
template <typename T, typename ParseFn>
|
||||
[[nodiscard]] tl::expected<void, std::string> parseEnumArray(T *destination, size_t n, std::optional<T> fillMissing, ParseFn &&parseFn)
|
||||
{
|
||||
size_t i = 0;
|
||||
const std::string_view str = value();
|
||||
if (!str.empty()) {
|
||||
for (const std::string_view part : SplitByChar(str, ',')) {
|
||||
if (i == n)
|
||||
return tl::make_unexpected(StrCat("Too many values, max: ", n));
|
||||
auto result = parseFn(part);
|
||||
if (!result.has_value()) {
|
||||
return tl::make_unexpected(std::move(result).error());
|
||||
}
|
||||
destination[i++] = *result;
|
||||
}
|
||||
}
|
||||
if (i != n) {
|
||||
if (!fillMissing.has_value()) {
|
||||
return tl::make_unexpected(StrCat("Too few values, expected ", n, " got ", i));
|
||||
}
|
||||
while (i < n) {
|
||||
destination[i++] = *fillMissing;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T, size_t N, typename ParseFn>
|
||||
[[nodiscard]] tl::expected<void, std::string> parseEnumArray(T (&destination)[N], std::optional<T> fillMissing, ParseFn &&parseFn)
|
||||
{
|
||||
return parseEnumArray<T, ParseFn>(destination, N, std::move(fillMissing), std::forward<ParseFn>(parseFn));
|
||||
}
|
||||
|
||||
template <typename T, size_t N, typename ParseFn>
|
||||
[[nodiscard]] tl::expected<void, std::string> parseIntArray(std::array<T, N> &destination, std::optional<T> fillMissing, ParseFn &&parseFn)
|
||||
{
|
||||
return parseEnumArray<T, ParseFn>(destination.data(), N, std::move(fillMissing), std::forward<ParseFn>(parseFn));
|
||||
}
|
||||
|
||||
template <typename T, typename ParseFn>
|
||||
[[nodiscard]] tl::expected<void, std::string> parseEnumList(T &destination, ParseFn &&parseFn)
|
||||
{
|
||||
|
||||
@@ -28,8 +28,7 @@ public:
|
||||
typename std::enable_if_t<std::is_integral_v<T>, void>
|
||||
readInt(std::string_view name, T &out)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseInt(out), name, field);
|
||||
}
|
||||
|
||||
@@ -37,8 +36,7 @@ public:
|
||||
typename std::enable_if_t<std::is_integral_v<T>, void>
|
||||
readOptionalInt(std::string_view name, T &out)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
if (field.value().empty()) return;
|
||||
failOnError(field.parseInt(out), name, field);
|
||||
}
|
||||
@@ -46,16 +44,21 @@ public:
|
||||
template <typename T, size_t N>
|
||||
void readIntArray(std::string_view name, T (&out)[N])
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseIntArray(out), name, field);
|
||||
}
|
||||
|
||||
template <typename T, size_t N, typename F>
|
||||
void readEnumArray(std::string_view name, std::optional<T> fillMissing, T (&out)[N], F &&parseFn)
|
||||
{
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseEnumArray(out, fillMissing, parseFn), name, field, DataFileField::Error::InvalidValue);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
void readIntArray(std::string_view name, std::array<T, N> &out)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseIntArray(out), name, field);
|
||||
}
|
||||
|
||||
@@ -63,15 +66,13 @@ public:
|
||||
typename std::enable_if_t<std::is_integral_v<T>, void>
|
||||
readFixed6(std::string_view name, T &out)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseFixed6(out), name, field);
|
||||
}
|
||||
|
||||
void readBool(std::string_view name, bool &out)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseBool(out), name, field);
|
||||
}
|
||||
|
||||
@@ -84,8 +85,7 @@ public:
|
||||
template <typename T, typename F>
|
||||
void read(std::string_view name, T &out, F &&parseFn)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
tl::expected<T, std::string> result = parseFn(field.value());
|
||||
failOnError(result, name, field, DataFileField::Error::InvalidValue);
|
||||
out = *std::move(result);
|
||||
@@ -94,8 +94,7 @@ public:
|
||||
template <typename T, typename F>
|
||||
void readEnumList(std::string_view name, T &out, F &&parseFn)
|
||||
{
|
||||
advance();
|
||||
DataFileField field = *it_;
|
||||
DataFileField field = nextField();
|
||||
failOnError(field.parseEnumList(out, std::forward<F>(parseFn)),
|
||||
name, field, DataFileField::Error::InvalidValue);
|
||||
}
|
||||
@@ -109,6 +108,12 @@ public:
|
||||
|
||||
void advance();
|
||||
|
||||
DataFileField nextField()
|
||||
{
|
||||
advance();
|
||||
return *it_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void failOnError(const tl::expected<T, DataFileField::Error> &result, std::string_view name, const DataFileField &field)
|
||||
|
||||
@@ -2476,6 +2476,7 @@ int DiabloMain(int argc, char **argv)
|
||||
LoadPlayerDataFiles();
|
||||
|
||||
// TODO: We can probably load this much later (when the game is starting).
|
||||
LoadSpellData();
|
||||
LoadMissileData();
|
||||
LoadMonsterData();
|
||||
LoadItemData();
|
||||
|
||||
+183
-58
@@ -5,11 +5,14 @@
|
||||
*/
|
||||
#include "spelldat.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <expected.hpp>
|
||||
|
||||
#include "utils/language.h"
|
||||
#include "data/file.hpp"
|
||||
#include "data/iterators.hpp"
|
||||
#include "data/record_reader.hpp"
|
||||
|
||||
namespace devilution {
|
||||
|
||||
@@ -19,66 +22,158 @@ const auto Lightning = SpellDataFlags::Lightning;
|
||||
const auto Magic = SpellDataFlags::Magic;
|
||||
const auto Targeted = SpellDataFlags::Targeted;
|
||||
const auto AllowedInTown = SpellDataFlags::AllowedInTown;
|
||||
|
||||
void AddNullSpell()
|
||||
{
|
||||
SpellData &null = SpellsData.emplace_back();
|
||||
null.sSFX = SfxID::None;
|
||||
null.bookCost10 = null.staffCost10 = null.sManaCost = 0;
|
||||
null.flags = SpellDataFlags::Fire;
|
||||
null.sBookLvl = null.sStaffLvl = 0;
|
||||
null.minInt = 0;
|
||||
null.sMissiles[0] = null.sMissiles[1] = MissileID::Null;
|
||||
null.sManaAdj = null.sMinMana = 0;
|
||||
null.sStaffMin = 40;
|
||||
null.sStaffMax = 80;
|
||||
}
|
||||
|
||||
// A temporary solution for parsing soundID until we have a more general one.
|
||||
tl::expected<SfxID, std::string> ParseSpellSoundId(std::string_view value)
|
||||
{
|
||||
if (value == "CastFire") return SfxID::CastFire;
|
||||
if (value == "CastHealing") return SfxID::CastHealing;
|
||||
if (value == "CastLightning") return SfxID::CastLightning;
|
||||
if (value == "CastSkill") return SfxID::CastSkill;
|
||||
return tl::make_unexpected("Unknown enum value (only a few are supported for now)");
|
||||
}
|
||||
|
||||
tl::expected<SpellDataFlags, std::string> ParseSpellDataFlag(std::string_view value)
|
||||
{
|
||||
if (value == "Fire") return SpellDataFlags::Fire;
|
||||
if (value == "Lightning") return SpellDataFlags::Lightning;
|
||||
if (value == "Magic") return SpellDataFlags::Magic;
|
||||
if (value == "Targeted") return SpellDataFlags::Targeted;
|
||||
if (value == "AllowedInTown") return SpellDataFlags::AllowedInTown;
|
||||
return tl::make_unexpected("Unknown enum value");
|
||||
}
|
||||
|
||||
tl::expected<MissileID, std::string> ParseMissileId(std::string_view value)
|
||||
{
|
||||
if (value == "Arrow") return MissileID::Arrow;
|
||||
if (value == "Firebolt") return MissileID::Firebolt;
|
||||
if (value == "Guardian") return MissileID::Guardian;
|
||||
if (value == "Phasing") return MissileID::Phasing;
|
||||
if (value == "NovaBall") return MissileID::NovaBall;
|
||||
if (value == "FireWall") return MissileID::FireWall;
|
||||
if (value == "Fireball") return MissileID::Fireball;
|
||||
if (value == "LightningControl") return MissileID::LightningControl;
|
||||
if (value == "Lightning") return MissileID::Lightning;
|
||||
if (value == "MagmaBallExplosion") return MissileID::MagmaBallExplosion;
|
||||
if (value == "TownPortal") return MissileID::TownPortal;
|
||||
if (value == "FlashBottom") return MissileID::FlashBottom;
|
||||
if (value == "FlashTop") return MissileID::FlashTop;
|
||||
if (value == "ManaShield") return MissileID::ManaShield;
|
||||
if (value == "FlameWave") return MissileID::FlameWave;
|
||||
if (value == "ChainLightning") return MissileID::ChainLightning;
|
||||
if (value == "ChainBall") return MissileID::ChainBall;
|
||||
if (value == "BloodHit") return MissileID::BloodHit;
|
||||
if (value == "BoneHit") return MissileID::BoneHit;
|
||||
if (value == "MetalHit") return MissileID::MetalHit;
|
||||
if (value == "Rhino") return MissileID::Rhino;
|
||||
if (value == "MagmaBall") return MissileID::MagmaBall;
|
||||
if (value == "ThinLightningControl") return MissileID::ThinLightningControl;
|
||||
if (value == "ThinLightning") return MissileID::ThinLightning;
|
||||
if (value == "BloodStar") return MissileID::BloodStar;
|
||||
if (value == "BloodStarExplosion") return MissileID::BloodStarExplosion;
|
||||
if (value == "Teleport") return MissileID::Teleport;
|
||||
if (value == "FireArrow") return MissileID::FireArrow;
|
||||
if (value == "DoomSerpents") return MissileID::DoomSerpents;
|
||||
if (value == "FireOnly") return MissileID::FireOnly;
|
||||
if (value == "StoneCurse") return MissileID::StoneCurse;
|
||||
if (value == "BloodRitual") return MissileID::BloodRitual;
|
||||
if (value == "Invisibility") return MissileID::Invisibility;
|
||||
if (value == "Golem") return MissileID::Golem;
|
||||
if (value == "Etherealize") return MissileID::Etherealize;
|
||||
if (value == "Spurt") return MissileID::Spurt;
|
||||
if (value == "ApocalypseBoom") return MissileID::ApocalypseBoom;
|
||||
if (value == "Healing") return MissileID::Healing;
|
||||
if (value == "FireWallControl") return MissileID::FireWallControl;
|
||||
if (value == "Infravision") return MissileID::Infravision;
|
||||
if (value == "Identify") return MissileID::Identify;
|
||||
if (value == "FlameWaveControl") return MissileID::FlameWaveControl;
|
||||
if (value == "Nova") return MissileID::Nova;
|
||||
if (value == "Rage") return MissileID::Rage;
|
||||
if (value == "Apocalypse") return MissileID::Apocalypse;
|
||||
if (value == "ItemRepair") return MissileID::ItemRepair;
|
||||
if (value == "StaffRecharge") return MissileID::StaffRecharge;
|
||||
if (value == "TrapDisarm") return MissileID::TrapDisarm;
|
||||
if (value == "Inferno") return MissileID::Inferno;
|
||||
if (value == "InfernoControl") return MissileID::InfernoControl;
|
||||
if (value == "FireMan") return MissileID::FireMan;
|
||||
if (value == "Krull") return MissileID::Krull;
|
||||
if (value == "ChargedBolt") return MissileID::ChargedBolt;
|
||||
if (value == "HolyBolt") return MissileID::HolyBolt;
|
||||
if (value == "Resurrect") return MissileID::Resurrect;
|
||||
if (value == "Telekinesis") return MissileID::Telekinesis;
|
||||
if (value == "LightningArrow") return MissileID::LightningArrow;
|
||||
if (value == "Acid") return MissileID::Acid;
|
||||
if (value == "AcidSplat") return MissileID::AcidSplat;
|
||||
if (value == "AcidPuddle") return MissileID::AcidPuddle;
|
||||
if (value == "HealOther") return MissileID::HealOther;
|
||||
if (value == "Elemental") return MissileID::Elemental;
|
||||
if (value == "ResurrectBeam") return MissileID::ResurrectBeam;
|
||||
if (value == "BoneSpirit") return MissileID::BoneSpirit;
|
||||
if (value == "WeaponExplosion") return MissileID::WeaponExplosion;
|
||||
if (value == "RedPortal") return MissileID::RedPortal;
|
||||
if (value == "DiabloApocalypseBoom") return MissileID::DiabloApocalypseBoom;
|
||||
if (value == "DiabloApocalypse") return MissileID::DiabloApocalypse;
|
||||
if (value == "Mana") return MissileID::Mana;
|
||||
if (value == "Magi") return MissileID::Magi;
|
||||
if (value == "LightningWall") return MissileID::LightningWall;
|
||||
if (value == "LightningWallControl") return MissileID::LightningWallControl;
|
||||
if (value == "Immolation") return MissileID::Immolation;
|
||||
if (value == "SpectralArrow") return MissileID::SpectralArrow;
|
||||
if (value == "FireballBow") return MissileID::FireballBow;
|
||||
if (value == "LightningBow") return MissileID::LightningBow;
|
||||
if (value == "ChargedBoltBow") return MissileID::ChargedBoltBow;
|
||||
if (value == "HolyBoltBow") return MissileID::HolyBoltBow;
|
||||
if (value == "Warp") return MissileID::Warp;
|
||||
if (value == "Reflect") return MissileID::Reflect;
|
||||
if (value == "Berserk") return MissileID::Berserk;
|
||||
if (value == "RingOfFire") return MissileID::RingOfFire;
|
||||
if (value == "StealPotions") return MissileID::StealPotions;
|
||||
if (value == "StealMana") return MissileID::StealMana;
|
||||
if (value == "RingOfLightning") return MissileID::RingOfLightning;
|
||||
if (value == "Search") return MissileID::Search;
|
||||
if (value == "Aura") return MissileID::Aura;
|
||||
if (value == "Aura2") return MissileID::Aura2;
|
||||
if (value == "SpiralFireball") return MissileID::SpiralFireball;
|
||||
if (value == "RuneOfFire") return MissileID::RuneOfFire;
|
||||
if (value == "RuneOfLight") return MissileID::RuneOfLight;
|
||||
if (value == "RuneOfNova") return MissileID::RuneOfNova;
|
||||
if (value == "RuneOfImmolation") return MissileID::RuneOfImmolation;
|
||||
if (value == "RuneOfStone") return MissileID::RuneOfStone;
|
||||
if (value == "BigExplosion") return MissileID::BigExplosion;
|
||||
if (value == "HorkSpawn") return MissileID::HorkSpawn;
|
||||
if (value == "Jester") return MissileID::Jester;
|
||||
if (value == "OpenNest") return MissileID::OpenNest;
|
||||
if (value == "OrangeFlare") return MissileID::OrangeFlare;
|
||||
if (value == "BlueFlare") return MissileID::BlueFlare;
|
||||
if (value == "RedFlare") return MissileID::RedFlare;
|
||||
if (value == "YellowFlare") return MissileID::YellowFlare;
|
||||
if (value == "BlueFlare2") return MissileID::BlueFlare2;
|
||||
if (value == "YellowExplosion") return MissileID::YellowExplosion;
|
||||
if (value == "RedExplosion") return MissileID::RedExplosion;
|
||||
if (value == "BlueExplosion") return MissileID::BlueExplosion;
|
||||
if (value == "BlueExplosion2") return MissileID::BlueExplosion2;
|
||||
if (value == "OrangeExplosion") return MissileID::OrangeExplosion;
|
||||
return tl::make_unexpected("Unknown enum value");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/** Data related to each spell ID. */
|
||||
const SpellData SpellsData[] = {
|
||||
// clang-format off
|
||||
// id sNameText, sSFX, bookCost10, staffCost10, sManaCost, flags, sBookLvl, sStaffLvl, minInt, { sMissiles[2] } sManaAdj, sMinMana, sStaffMin, sStaffMax
|
||||
/*SpellID::Null*/ { nullptr, SfxID::None, 0, 0, 0, Fire, 0, 0, 0, { MissileID::Null, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::Firebolt*/ { P_("spell", "Firebolt"), SfxID::CastFire, 100, 5, 6, Fire | Targeted, 1, 1, 15, { MissileID::Firebolt, MissileID::Null, }, 1, 3, 40, 80 },
|
||||
/*SpellID::Healing*/ { P_("spell", "Healing"), SfxID::CastHealing, 100, 5, 5, Magic | AllowedInTown, 1, 1, 17, { MissileID::Healing, MissileID::Null, }, 3, 1, 20, 40 },
|
||||
/*SpellID::Lightning*/ { P_("spell", "Lightning"), SfxID::CastLightning, 300, 15, 10, Lightning | Targeted, 4, 3, 20, { MissileID::LightningControl, MissileID::Null, }, 1, 6, 20, 60 },
|
||||
/*SpellID::Flash*/ { P_("spell", "Flash"), SfxID::CastLightning, 750, 50, 30, Lightning, 5, 4, 33, { MissileID::FlashBottom, MissileID::FlashTop }, 2, 16, 20, 40 },
|
||||
/*SpellID::Identify*/ { P_("spell", "Identify"), SfxID::CastSkill, 0, 10, 13, Magic | AllowedInTown, -1, -1, 23, { MissileID::Identify, MissileID::Null, }, 2, 1, 8, 12 },
|
||||
/*SpellID::FireWall*/ { P_("spell", "Fire Wall"), SfxID::CastFire, 600, 40, 28, Fire | Targeted, 3, 2, 27, { MissileID::FireWallControl, MissileID::Null, }, 2, 16, 8, 16 },
|
||||
/*SpellID::TownPortal*/ { P_("spell", "Town Portal"), SfxID::CastSkill, 300, 20, 35, Magic | Targeted, 3, 3, 20, { MissileID::TownPortal, MissileID::Null, }, 3, 18, 8, 12 },
|
||||
/*SpellID::StoneCurse*/ { P_("spell", "Stone Curse"), SfxID::CastFire, 1200, 80, 60, Magic | Targeted, 6, 5, 51, { MissileID::StoneCurse, MissileID::Null, }, 3, 40, 8, 16 },
|
||||
/*SpellID::Infravision*/ { P_("spell", "Infravision"), SfxID::CastHealing, 0, 60, 40, Magic, -1, -1, 36, { MissileID::Infravision, MissileID::Null, }, 5, 20, 0, 0 },
|
||||
/*SpellID::Phasing*/ { P_("spell", "Phasing"), SfxID::CastFire, 350, 20, 12, Magic, 7, 6, 39, { MissileID::Phasing, MissileID::Null, }, 2, 4, 40, 80 },
|
||||
/*SpellID::ManaShield*/ { P_("spell", "Mana Shield"), SfxID::CastFire, 1600, 120, 33, Magic, 6, 5, 25, { MissileID::ManaShield, MissileID::Null, }, 0, 33, 4, 10 },
|
||||
/*SpellID::Fireball*/ { P_("spell", "Fireball"), SfxID::CastFire, 800, 30, 16, Fire | Targeted, 8, 7, 48, { MissileID::Fireball, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
/*SpellID::Guardian*/ { P_("spell", "Guardian"), SfxID::CastFire, 1400, 95, 50, Fire | Targeted, 9, 8, 61, { MissileID::Guardian, MissileID::Null, }, 2, 30, 16, 32 },
|
||||
/*SpellID::ChainLightning*/ { P_("spell", "Chain Lightning"), SfxID::CastFire, 1100, 75, 30, Lightning, 8, 7, 54, { MissileID::ChainLightning, MissileID::Null, }, 1, 18, 20, 60 },
|
||||
/*SpellID::FlameWave*/ { P_("spell", "Flame Wave"), SfxID::CastFire, 1000, 65, 35, Fire | Targeted, 9, 8, 54, { MissileID::FlameWaveControl, MissileID::Null, }, 3, 20, 20, 40 },
|
||||
/*SpellID::DoomSerpents*/ { P_("spell", "Doom Serpents"), SfxID::CastFire, 0, 0, 0, Lightning, -1, -1, 0, { MissileID::Null, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::BloodRitual*/ { P_("spell", "Blood Ritual"), SfxID::CastFire, 0, 0, 0, Magic, -1, -1, 0, { MissileID::Null, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::Nova*/ { P_("spell", "Nova"), SfxID::CastLightning, 2100, 130, 60, Magic, 14, 10, 87, { MissileID::Nova, MissileID::Null, }, 3, 35, 16, 32 },
|
||||
/*SpellID::Invisibility*/ { P_("spell", "Invisibility"), SfxID::CastFire, 0, 0, 0, Magic, -1, -1, 0, { MissileID::Null, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::Inferno*/ { P_("spell", "Inferno"), SfxID::CastFire, 200, 10, 11, Fire | Targeted, 3, 2, 20, { MissileID::InfernoControl, MissileID::Null, }, 1, 6, 20, 40 },
|
||||
/*SpellID::Golem*/ { P_("spell", "Golem"), SfxID::CastFire, 1800, 110, 100, Fire | Targeted, 11, 9, 81, { MissileID::Golem, MissileID::Null, }, 6, 60, 16, 32 },
|
||||
/*SpellID::Rage*/ { P_("spell", "Rage"), SfxID::CastHealing, 0, 0, 15, Magic, -1, -1, 0, { MissileID::Rage, MissileID::Null, }, 1, 1, 0, 0 },
|
||||
/*SpellID::Teleport*/ { P_("spell", "Teleport"), SfxID::CastSkill, 2000, 125, 35, Magic | Targeted, 14, 12, 105, { MissileID::Teleport, MissileID::Null, }, 3, 15, 16, 32 },
|
||||
/*SpellID::Apocalypse*/ { P_("spell", "Apocalypse"), SfxID::CastFire, 3000, 200, 150, Fire, 19, 15, 149, { MissileID::Apocalypse, MissileID::Null, }, 6, 90, 8, 12 },
|
||||
/*SpellID::Etherealize*/ { P_("spell", "Etherealize"), SfxID::CastFire, 2600, 160, 100, Magic, -1, -1, 93, { MissileID::Etherealize, MissileID::Null, }, 0, 100, 2, 6 },
|
||||
/*SpellID::ItemRepair*/ { P_("spell", "Item Repair"), SfxID::CastSkill, 0, 0, 0, Magic | AllowedInTown, -1, -1, 255, { MissileID::ItemRepair, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::StaffRecharge*/ { P_("spell", "Staff Recharge"), SfxID::CastSkill, 0, 0, 0, Magic | AllowedInTown, -1, -1, 255, { MissileID::StaffRecharge, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::TrapDisarm*/ { P_("spell", "Trap Disarm"), SfxID::CastSkill, 0, 0, 0, Magic, -1, -1, 255, { MissileID::TrapDisarm, MissileID::Null, }, 0, 0, 40, 80 },
|
||||
/*SpellID::Elemental*/ { P_("spell", "Elemental"), SfxID::CastFire, 1050, 70, 35, Fire, 8, 6, 68, { MissileID::Elemental, MissileID::Null, }, 2, 20, 20, 60 },
|
||||
/*SpellID::ChargedBolt*/ { P_("spell", "Charged Bolt"), SfxID::CastFire, 100, 5, 6, Lightning | Targeted, 1, 1, 25, { MissileID::ChargedBolt, MissileID::Null, }, 1, 6, 40, 80 },
|
||||
/*SpellID::HolyBolt*/ { P_("spell", "Holy Bolt"), SfxID::CastFire, 100, 5, 7, Magic | Targeted, 1, 1, 20, { MissileID::HolyBolt, MissileID::Null, }, 1, 3, 40, 80 },
|
||||
/*SpellID::Resurrect*/ { P_("spell", "Resurrect"), SfxID::CastHealing, 400, 25, 20, Magic | AllowedInTown, -1, 5, 30, { MissileID::Resurrect, MissileID::Null, }, 0, 20, 4, 10 },
|
||||
/*SpellID::Telekinesis*/ { P_("spell", "Telekinesis"), SfxID::CastFire, 250, 20, 15, Magic, 2, 2, 33, { MissileID::Telekinesis, MissileID::Null, }, 2, 8, 20, 40 },
|
||||
/*SpellID::HealOther*/ { P_("spell", "Heal Other"), SfxID::CastHealing, 100, 5, 5, Magic | AllowedInTown, 1, 1, 17, { MissileID::HealOther, MissileID::Null, }, 3, 1, 20, 40 },
|
||||
/*SpellID::BloodStar*/ { P_("spell", "Blood Star"), SfxID::CastFire, 2750, 180, 25, Magic, 14, 13, 70, { MissileID::BloodStar, MissileID::Null, }, 2, 14, 20, 60 },
|
||||
/*SpellID::BoneSpirit*/ { P_("spell", "Bone Spirit"), SfxID::CastFire, 1150, 80, 24, Magic, 9, 7, 34, { MissileID::BoneSpirit, MissileID::Null, }, 1, 12, 20, 60 },
|
||||
/*SpellID::Mana*/ { P_("spell", "Mana"), SfxID::CastHealing, 100, 5, 255, Magic | AllowedInTown, -1, 5, 17, { MissileID::Mana, MissileID::Null, }, 3, 1, 12, 24 },
|
||||
/*SpellID::Magi*/ { P_("spell", "the Magi"), SfxID::CastHealing, 10000, 20, 255, Magic | AllowedInTown, -1, 20, 45, { MissileID::Magi, MissileID::Null, }, 3, 1, 15, 30 },
|
||||
/*SpellID::Jester*/ { P_("spell", "the Jester"), SfxID::CastHealing, 10000, 20, 255, Magic | Targeted, -1, 4, 30, { MissileID::Jester, MissileID::Null, }, 3, 1, 15, 30 },
|
||||
/*SpellID::LightningWall*/ { P_("spell", "Lightning Wall"), SfxID::CastLightning, 600, 40, 28, Lightning | Targeted, 3, 2, 27, { MissileID::LightningWallControl, MissileID::Null, }, 2, 16, 8, 16 },
|
||||
/*SpellID::Immolation*/ { P_("spell", "Immolation"), SfxID::CastFire, 2100, 130, 60, Fire, 14, 10, 87, { MissileID::Immolation, MissileID::Null, }, 3, 35, 16, 32 },
|
||||
/*SpellID::Warp*/ { P_("spell", "Warp"), SfxID::CastSkill, 300, 20, 35, Magic, 3, 3, 25, { MissileID::Warp, MissileID::Null, }, 3, 18, 8, 12 },
|
||||
/*SpellID::Reflect*/ { P_("spell", "Reflect"), SfxID::CastSkill, 300, 20, 35, Magic, 3, 3, 25, { MissileID::Reflect, MissileID::Null, }, 3, 15, 8, 12 },
|
||||
/*SpellID::Berserk*/ { P_("spell", "Berserk"), SfxID::CastSkill, 300, 20, 35, Magic | Targeted, 3, 3, 35, { MissileID::Berserk, MissileID::Null, }, 3, 15, 8, 12 },
|
||||
/*SpellID::RingOfFire*/ { P_("spell", "Ring of Fire"), SfxID::CastFire, 600, 40, 28, Fire, 5, 5, 27, { MissileID::RingOfFire, MissileID::Null, }, 2, 16, 8, 16 },
|
||||
/*SpellID::Search*/ { P_("spell", "Search"), SfxID::CastSkill, 300, 20, 15, Magic, 1, 3, 25, { MissileID::Search, MissileID::Null, }, 1, 1, 8, 12 },
|
||||
/*SpellID::RuneOfFire*/ { P_("spell", "Rune of Fire"), SfxID::CastHealing, 800, 30, 255, Magic | Targeted, -1, -1, 48, { MissileID::RuneOfFire, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
/*SpellID::RuneOfLight*/ { P_("spell", "Rune of Light"), SfxID::CastHealing, 800, 30, 255, Magic | Targeted, -1, -1, 48, { MissileID::RuneOfLight, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
/*SpellID::RuneOfNova*/ { P_("spell", "Rune of Nova"), SfxID::CastHealing, 800, 30, 255, Magic | Targeted, -1, -1, 48, { MissileID::RuneOfNova, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
/*SpellID::RuneOfImmolation*/ { P_("spell", "Rune of Immolation"), SfxID::CastHealing, 800, 30, 255, Magic | Targeted, -1, -1, 48, { MissileID::RuneOfImmolation, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
/*SpellID::RuneOfStone*/ { P_("spell", "Rune of Stone"), SfxID::CastHealing, 800, 30, 255, Magic | Targeted, -1, -1, 48, { MissileID::RuneOfStone, MissileID::Null, }, 1, 10, 40, 80 },
|
||||
// clang-format on
|
||||
};
|
||||
std::vector<SpellData> SpellsData;
|
||||
|
||||
tl::expected<SpellID, std::string> ParseSpellId(std::string_view value)
|
||||
{
|
||||
@@ -137,4 +232,34 @@ tl::expected<SpellID, std::string> ParseSpellId(std::string_view value)
|
||||
return tl::make_unexpected("Unknown enum value");
|
||||
}
|
||||
|
||||
void LoadSpellData()
|
||||
{
|
||||
SpellsData.clear();
|
||||
const std::string_view filename = "txtdata\\spells\\spelldat.tsv";
|
||||
DataFile dataFile = DataFile::loadOrDie(filename);
|
||||
SpellsData.reserve(dataFile.numRecords() + 1);
|
||||
AddNullSpell();
|
||||
dataFile.skipHeaderOrDie(filename);
|
||||
for (DataFileRecord record : dataFile) {
|
||||
RecordReader reader { record, filename };
|
||||
SpellData &item = SpellsData.emplace_back();
|
||||
reader.advance(); // skip id
|
||||
reader.readString("name", item.sNameText);
|
||||
reader.read("soundId", item.sSFX, ParseSpellSoundId);
|
||||
reader.readInt("bookCost10", item.bookCost10);
|
||||
reader.readInt("staffCost10", item.staffCost10);
|
||||
reader.readInt("manaCost", item.sManaCost);
|
||||
reader.readEnumList("flags", item.flags, ParseSpellDataFlag);
|
||||
reader.readInt("bookLevel", item.sBookLvl);
|
||||
reader.readInt("staffLevel", item.sStaffLvl);
|
||||
reader.readInt("minIntelligence", item.minInt);
|
||||
reader.readEnumArray("missiles", /*fillMissing=*/std::make_optional(MissileID::Null), item.sMissiles, ParseMissileId);
|
||||
reader.readInt("manaMultiplier", item.sManaAdj);
|
||||
reader.readInt("minMana", item.sMinMana);
|
||||
reader.readInt("staffMin", item.sStaffMin);
|
||||
reader.readInt("staffMax", item.sStaffMax);
|
||||
}
|
||||
SpellsData.shrink_to_fit();
|
||||
}
|
||||
|
||||
} // namespace devilution
|
||||
+6
-2
@@ -6,8 +6,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <expected.hpp>
|
||||
|
||||
@@ -221,7 +223,7 @@ enum class SpellDataFlags : uint8_t {
|
||||
use_enum_as_flags(SpellDataFlags);
|
||||
|
||||
struct SpellData {
|
||||
const char *sNameText;
|
||||
std::string sNameText;
|
||||
SfxID sSFX;
|
||||
uint16_t bookCost10;
|
||||
uint8_t staffCost10;
|
||||
@@ -262,11 +264,13 @@ struct SpellData {
|
||||
}
|
||||
};
|
||||
|
||||
extern const SpellData SpellsData[];
|
||||
extern std::vector<SpellData> SpellsData;
|
||||
|
||||
inline const SpellData &GetSpellData(SpellID spellId)
|
||||
{
|
||||
return SpellsData[static_cast<std::underlying_type<SpellID>::type>(spellId)];
|
||||
}
|
||||
|
||||
void LoadSpellData();
|
||||
|
||||
} // namespace devilution
|
||||
@@ -781,3 +781,54 @@ const char *ITEM_SUFFIX_94_NAME = N_("blocking");
|
||||
const char *ITEM_SUFFIX_95_NAME = N_("devastation");
|
||||
const char *ITEM_SUFFIX_96_NAME = N_("decay");
|
||||
const char *ITEM_SUFFIX_97_NAME = N_("peril");
|
||||
const char *SPELL_FIREBOLT_NAME = P_("spell", "Firebolt");
|
||||
const char *SPELL_HEALING_NAME = P_("spell", "Healing");
|
||||
const char *SPELL_LIGHTNING_NAME = P_("spell", "Lightning");
|
||||
const char *SPELL_FLASH_NAME = P_("spell", "Flash");
|
||||
const char *SPELL_IDENTIFY_NAME = P_("spell", "Identify");
|
||||
const char *SPELL_FIRE_WALL_NAME = P_("spell", "Fire Wall");
|
||||
const char *SPELL_TOWN_PORTAL_NAME = P_("spell", "Town Portal");
|
||||
const char *SPELL_STONE_CURSE_NAME = P_("spell", "Stone Curse");
|
||||
const char *SPELL_INFRAVISION_NAME = P_("spell", "Infravision");
|
||||
const char *SPELL_PHASING_NAME = P_("spell", "Phasing");
|
||||
const char *SPELL_MANA_SHIELD_NAME = P_("spell", "Mana Shield");
|
||||
const char *SPELL_FIREBALL_NAME = P_("spell", "Fireball");
|
||||
const char *SPELL_GUARDIAN_NAME = P_("spell", "Guardian");
|
||||
const char *SPELL_CHAIN_LIGHTNING_NAME = P_("spell", "Chain Lightning");
|
||||
const char *SPELL_FLAME_WAVE_NAME = P_("spell", "Flame Wave");
|
||||
const char *SPELL_DOOM_SERPENTS_NAME = P_("spell", "Doom Serpents");
|
||||
const char *SPELL_BLOOD_RITUAL_NAME = P_("spell", "Blood Ritual");
|
||||
const char *SPELL_NOVA_NAME = P_("spell", "Nova");
|
||||
const char *SPELL_INVISIBILITY_NAME = P_("spell", "Invisibility");
|
||||
const char *SPELL_INFERNO_NAME = P_("spell", "Inferno");
|
||||
const char *SPELL_GOLEM_NAME = P_("spell", "Golem");
|
||||
const char *SPELL_RAGE_NAME = P_("spell", "Rage");
|
||||
const char *SPELL_TELEPORT_NAME = P_("spell", "Teleport");
|
||||
const char *SPELL_APOCALYPSE_NAME = P_("spell", "Apocalypse");
|
||||
const char *SPELL_ETHEREALIZE_NAME = P_("spell", "Etherealize");
|
||||
const char *SPELL_ITEM_REPAIR_NAME = P_("spell", "Item Repair");
|
||||
const char *SPELL_STAFF_RECHARGE_NAME = P_("spell", "Staff Recharge");
|
||||
const char *SPELL_TRAP_DISARM_NAME = P_("spell", "Trap Disarm");
|
||||
const char *SPELL_ELEMENTAL_NAME = P_("spell", "Elemental");
|
||||
const char *SPELL_CHARGED_BOLT_NAME = P_("spell", "Charged Bolt");
|
||||
const char *SPELL_HOLY_BOLT_NAME = P_("spell", "Holy Bolt");
|
||||
const char *SPELL_RESURRECT_NAME = P_("spell", "Resurrect");
|
||||
const char *SPELL_TELEKINESIS_NAME = P_("spell", "Telekinesis");
|
||||
const char *SPELL_HEAL_OTHER_NAME = P_("spell", "Heal Other");
|
||||
const char *SPELL_BLOOD_STAR_NAME = P_("spell", "Blood Star");
|
||||
const char *SPELL_BONE_SPIRIT_NAME = P_("spell", "Bone Spirit");
|
||||
const char *SPELL_MANA_NAME = P_("spell", "Mana");
|
||||
const char *SPELL_THE_MAGI_NAME = P_("spell", "the Magi");
|
||||
const char *SPELL_THE_JESTER_NAME = P_("spell", "the Jester");
|
||||
const char *SPELL_LIGHTNING_WALL_NAME = P_("spell", "Lightning Wall");
|
||||
const char *SPELL_IMMOLATION_NAME = P_("spell", "Immolation");
|
||||
const char *SPELL_WARP_NAME = P_("spell", "Warp");
|
||||
const char *SPELL_REFLECT_NAME = P_("spell", "Reflect");
|
||||
const char *SPELL_BERSERK_NAME = P_("spell", "Berserk");
|
||||
const char *SPELL_RING_OF_FIRE_NAME = P_("spell", "Ring of Fire");
|
||||
const char *SPELL_SEARCH_NAME = P_("spell", "Search");
|
||||
const char *SPELL_RUNE_OF_FIRE_NAME = P_("spell", "Rune of Fire");
|
||||
const char *SPELL_RUNE_OF_LIGHT_NAME = P_("spell", "Rune of Light");
|
||||
const char *SPELL_RUNE_OF_NOVA_NAME = P_("spell", "Rune of Nova");
|
||||
const char *SPELL_RUNE_OF_IMMOLATION_NAME = P_("spell", "Rune of Immolation");
|
||||
const char *SPELL_RUNE_OF_STONE_NAME = P_("spell", "Rune of Stone");
|
||||
@@ -0,0 +1,52 @@
|
||||
id name soundId bookCost10 staffCost10 manaCost flags bookLevel staffLevel minIntelligence missiles manaMultiplier minMana staffMin staffMax
|
||||
Firebolt Firebolt CastFire 100 5 6 Fire,Targeted 1 1 15 Firebolt 1 3 40 80
|
||||
Healing Healing CastHealing 100 5 5 Magic,AllowedInTown 1 1 17 Healing 3 1 20 40
|
||||
Lightning Lightning CastLightning 300 15 10 Lightning,Targeted 4 3 20 LightningControl 1 6 20 60
|
||||
Flash Flash CastLightning 750 50 30 Lightning 5 4 33 FlashBottom,FlashTop 2 16 20 40
|
||||
Identify Identify CastSkill 0 10 13 Magic,AllowedInTown -1 -1 23 Identify 2 1 8 12
|
||||
FireWall Fire Wall CastFire 600 40 28 Fire,Targeted 3 2 27 FireWallControl 2 16 8 16
|
||||
TownPortal Town Portal CastSkill 300 20 35 Magic,Targeted 3 3 20 TownPortal 3 18 8 12
|
||||
StoneCurse Stone Curse CastFire 1200 80 60 Magic,Targeted 6 5 51 StoneCurse 3 40 8 16
|
||||
Infravision Infravision CastHealing 0 60 40 Magic -1 -1 36 Infravision 5 20 0 0
|
||||
Phasing Phasing CastFire 350 20 12 Magic 7 6 39 Phasing 2 4 40 80
|
||||
ManaShield Mana Shield CastFire 1600 120 33 Magic 6 5 25 ManaShield 0 33 4 10
|
||||
Fireball Fireball CastFire 800 30 16 Fire,Targeted 8 7 48 Fireball 1 10 40 80
|
||||
Guardian Guardian CastFire 1400 95 50 Fire,Targeted 9 8 61 Guardian 2 30 16 32
|
||||
ChainLightning Chain Lightning CastFire 1100 75 30 Lightning 8 7 54 ChainLightning 1 18 20 60
|
||||
FlameWave Flame Wave CastFire 1000 65 35 Fire,Targeted 9 8 54 FlameWaveControl 3 20 20 40
|
||||
DoomSerpents Doom Serpents CastFire 0 0 0 Lightning -1 -1 0 0 0 40 80
|
||||
BloodRitual Blood Ritual CastFire 0 0 0 Magic -1 -1 0 0 0 40 80
|
||||
Nova Nova CastLightning 2100 130 60 Magic 14 10 87 Nova 3 35 16 32
|
||||
Invisibility Invisibility CastFire 0 0 0 Magic -1 -1 0 0 0 40 80
|
||||
Inferno Inferno CastFire 200 10 11 Fire,Targeted 3 2 20 InfernoControl 1 6 20 40
|
||||
Golem Golem CastFire 1800 110 100 Fire,Targeted 11 9 81 Golem 6 60 16 32
|
||||
Rage Rage CastHealing 0 0 15 Magic -1 -1 0 Rage 1 1 0 0
|
||||
Teleport Teleport CastSkill 2000 125 35 Magic,Targeted 14 12 105 Teleport 3 15 16 32
|
||||
Apocalypse Apocalypse CastFire 3000 200 150 Fire 19 15 149 Apocalypse 6 90 8 12
|
||||
Etherealize Etherealize CastFire 2600 160 100 Magic -1 -1 93 Etherealize 0 100 2 6
|
||||
ItemRepair Item Repair CastSkill 0 0 0 Magic,AllowedInTown -1 -1 255 ItemRepair 0 0 40 80
|
||||
StaffRecharge Staff Recharge CastSkill 0 0 0 Magic,AllowedInTown -1 -1 255 StaffRecharge 0 0 40 80
|
||||
TrapDisarm Trap Disarm CastSkill 0 0 0 Magic -1 -1 255 TrapDisarm 0 0 40 80
|
||||
Elemental Elemental CastFire 1050 70 35 Fire 8 6 68 Elemental 2 20 20 60
|
||||
ChargedBolt Charged Bolt CastFire 100 5 6 Lightning,Targeted 1 1 25 ChargedBolt 1 6 40 80
|
||||
HolyBolt Holy Bolt CastFire 100 5 7 Magic,Targeted 1 1 20 HolyBolt 1 3 40 80
|
||||
Resurrect Resurrect CastHealing 400 25 20 Magic,AllowedInTown -1 5 30 Resurrect 0 20 4 10
|
||||
Telekinesis Telekinesis CastFire 250 20 15 Magic 2 2 33 Telekinesis 2 8 20 40
|
||||
HealOther Heal Other CastHealing 100 5 5 Magic,AllowedInTown 1 1 17 HealOther 3 1 20 40
|
||||
BloodStar Blood Star CastFire 2750 180 25 Magic 14 13 70 BloodStar 2 14 20 60
|
||||
BoneSpirit Bone Spirit CastFire 1150 80 24 Magic 9 7 34 BoneSpirit 1 12 20 60
|
||||
Mana Mana CastHealing 100 5 255 Magic,AllowedInTown -1 5 17 Mana 3 1 12 24
|
||||
Magi the Magi CastHealing 10000 20 255 Magic,AllowedInTown -1 20 45 Magi 3 1 15 30
|
||||
Jester the Jester CastHealing 10000 20 255 Magic,Targeted -1 4 30 Jester 3 1 15 30
|
||||
LightningWall Lightning Wall CastLightning 600 40 28 Lightning,Targeted 3 2 27 LightningWallControl 2 16 8 16
|
||||
Immolation Immolation CastFire 2100 130 60 Fire 14 10 87 Immolation 3 35 16 32
|
||||
Warp Warp CastSkill 300 20 35 Magic 3 3 25 Warp 3 18 8 12
|
||||
Reflect Reflect CastSkill 300 20 35 Magic 3 3 25 Reflect 3 15 8 12
|
||||
Berserk Berserk CastSkill 300 20 35 Magic,Targeted 3 3 35 Berserk 3 15 8 12
|
||||
RingOfFire Ring of Fire CastFire 600 40 28 Fire 5 5 27 RingOfFire 2 16 8 16
|
||||
Search Search CastSkill 300 20 15 Magic 1 3 25 Search 1 1 8 12
|
||||
RuneOfFire Rune of Fire CastHealing 800 30 255 Magic,Targeted -1 -1 48 RuneOfFire 1 10 40 80
|
||||
RuneOfLight Rune of Light CastHealing 800 30 255 Magic,Targeted -1 -1 48 RuneOfLight 1 10 40 80
|
||||
RuneOfNova Rune of Nova CastHealing 800 30 255 Magic,Targeted -1 -1 48 RuneOfNova 1 10 40 80
|
||||
RuneOfImmolation Rune of Immolation CastHealing 800 30 255 Magic,Targeted -1 -1 48 RuneOfImmolation 1 10 40 80
|
||||
RuneOfStone Rune of Stone CastHealing 800 30 255 Magic,Targeted -1 -1 48 RuneOfStone 1 10 40 80
|
||||
|
@@ -18,6 +18,7 @@ public:
|
||||
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
LoadSpellData();
|
||||
LoadItemData();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -414,6 +414,7 @@ public:
|
||||
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
LoadSpellData();
|
||||
LoadItemData();
|
||||
}
|
||||
};
|
||||
@@ -953,6 +954,7 @@ public:
|
||||
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
LoadSpellData();
|
||||
LoadPlayerDataFiles();
|
||||
LoadMonsterData();
|
||||
LoadItemData();
|
||||
|
||||
@@ -52,6 +52,7 @@ void RunTimedemo(std::string timedemoFolderName)
|
||||
HeadlessMode = true;
|
||||
demo::InitPlayBack(demoNumber, true);
|
||||
|
||||
LoadSpellData();
|
||||
LoadPlayerDataFiles();
|
||||
LoadMissileData();
|
||||
LoadMonsterData();
|
||||
|
||||
@@ -374,6 +374,7 @@ TEST(Writehero, pfile_write_hero)
|
||||
MyPlayerId = 0;
|
||||
MyPlayer = &Players[MyPlayerId];
|
||||
|
||||
LoadSpellData();
|
||||
LoadPlayerDataFiles();
|
||||
LoadItemData();
|
||||
_uiheroinfo info {};
|
||||
|
||||
@@ -10,6 +10,7 @@ itemdat_path = root.joinpath("assets/txtdata/items/itemdat.tsv")
|
||||
unique_itemdat_path = root.joinpath("assets/txtdata/items/unique_itemdat.tsv")
|
||||
item_prefixes_path = root.joinpath("assets/txtdata/items/item_prefixes.tsv")
|
||||
item_suffixes_path = root.joinpath("assets/txtdata/items/item_suffixes.tsv")
|
||||
spelldat_path = root.joinpath("assets/txtdata/spells/spelldat.tsv")
|
||||
|
||||
with open(translation_dummy_path, 'w') as temp_source:
|
||||
temp_source.write(f'/**\n')
|
||||
@@ -62,3 +63,9 @@ with open(translation_dummy_path, 'w') as temp_source:
|
||||
name = row['name']
|
||||
var_name = f'ITEM_SUFFIX_{i}'
|
||||
temp_source.write(f'const char *{var_name}_NAME = N_("{name}");\n')
|
||||
with open(spelldat_path, 'r') as tsv:
|
||||
reader = csv.DictReader(tsv, delimiter='\t')
|
||||
for i, row in enumerate(reader):
|
||||
name = row['name']
|
||||
var_name = 'SPELL_' + name.upper().replace(' ', '_').replace('-', '_')
|
||||
temp_source.write(f'const char *{var_name}_NAME = P_("spell", "{name}");\n')
|
||||
Reference in new issue
Block a user