From 63f2430a3e9197b1e3abc639fad57e7e66b9853b Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Thu, 11 Jan 2024 17:49:47 -0800 Subject: [PATCH 01/10] Add checks for vampire-lord-ness and werewolf form If the vampire lord check returns true, we do not respond to key presses. There is no visual change in the HUD. (We could draw it in some dimmed red mode, perhaps?) --- src/game/player.cpp | 22 ++++++++++++++++++++++ src/game/player.h | 2 ++ src/plugin/sinks.cpp | 2 ++ src/util/helpers.cpp | 3 +++ 4 files changed, 29 insertions(+) diff --git a/src/game/player.cpp b/src/game/player.cpp index 1e934868..66746a6c 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -16,6 +16,28 @@ namespace player bool weaponsAreDrawn() { return RE::PlayerCharacter::GetSingleton()->AsActorState()->IsWeaponDrawn(); } + bool isVampireLord() + { + // DLC1VampireBeastRace; form id 0x0200283a + const auto* race = RE::PlayerCharacter::GetSingleton()->GetRace(); + if (!race) { return false; } + if (race->GetFormID() == 0x0200283a) { return true; } + const auto* editorID = race->GetFormEditorID(); + if (editorID && std::strcmp(editorID, "DLC1VampireBeastRace")) { return true; } + return false; + } + + bool isWerewolf() + { + // WerewolfBeastRace; form id 0x000cdd84 + const auto* race = RE::PlayerCharacter::GetSingleton()->GetRace(); + if (!race) { return false; } + if (race->GetFormID() == 0x000cdd84) { return true; } + const auto* editorID = race->GetFormEditorID(); + if (editorID && std::strcmp(editorID, "WerewolfBeastRace")) { return true; } + return false; + } + bool useCGOAltGrip() { bool useAltGrip = false; diff --git a/src/game/player.h b/src/game/player.h index 926c17d8..0f0c9ece 100644 --- a/src/game/player.h +++ b/src/game/player.h @@ -28,6 +28,8 @@ namespace player bool isInCombat(); bool weaponsAreDrawn(); bool hasRangedEquipped(); + bool isVampireLord(); + bool isWerewolf(); void unequipSlot(Action slot); void unequipShout(); diff --git a/src/plugin/sinks.cpp b/src/plugin/sinks.cpp index a6fcbcc9..89de11b9 100644 --- a/src/plugin/sinks.cpp +++ b/src/plugin/sinks.cpp @@ -198,6 +198,8 @@ RE::BSEventNotifyControl TheListener::ProcessEvent(const RE::TESMagicEffectApply return RE::BSEventNotifyControl::kContinue; } + // We are specifically looking for VampireLordEffect on the player. + rlog::info("Effect status change: '{}' {} put \"{}\" ({}) on '{}' {}", casterName, event->caster ? rlog::formatAsHex(event->caster->GetFormID()) : "", diff --git a/src/util/helpers.cpp b/src/util/helpers.cpp index a59df552..60957751 100644 --- a/src/util/helpers.cpp +++ b/src/util/helpers.cpp @@ -113,6 +113,9 @@ namespace helpers const auto* control_map = RE::ControlMap::GetSingleton(); if (!control_map || !control_map->AreControlsEnabled(requiredControlFlags)) { return true; } + // Lock out the hud if the player is a vampire lord. issue #100 + if (player::isVampireLord()) { return true; } + return false; // FOR NOW } From 4f2559e5e159caad61d1abec922763d8914aae16 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Thu, 11 Jan 2024 19:41:54 -0800 Subject: [PATCH 02/10] 2 new icons: a bat icon, and a new vampire icon. --- installer/icon-pack-soulsy/spell_bat.svg | 9 +++++++++ installer/icon-pack-soulsy/spell_vampire.svg | 7 ++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 installer/icon-pack-soulsy/spell_bat.svg diff --git a/installer/icon-pack-soulsy/spell_bat.svg b/installer/icon-pack-soulsy/spell_bat.svg new file mode 100644 index 00000000..f9503b8e --- /dev/null +++ b/installer/icon-pack-soulsy/spell_bat.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/installer/icon-pack-soulsy/spell_vampire.svg b/installer/icon-pack-soulsy/spell_vampire.svg index 7c06c49d..e5216fb7 100644 --- a/installer/icon-pack-soulsy/spell_vampire.svg +++ b/installer/icon-pack-soulsy/spell_vampire.svg @@ -1,10 +1,7 @@ - - - - - + + From 300270dd9330e6df7763fc559e88f375bb0fc3a3 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Thu, 11 Jan 2024 21:15:48 -0800 Subject: [PATCH 03/10] Moved some spell icons to power icons. Also pulled the color-determining function into the keywords file and re-used it. --- installer/core/SoulsyHUD_KID.ini | 12 ++++++- src/controller/control.rs | 12 ++++++- src/data/keywords.rs | 57 +++++++++++++++++++++++++++++++- src/data/power.rs | 12 +++++-- src/data/shout.rs | 4 +-- src/data/spell.rs | 44 ++++-------------------- src/game/equippable.cpp | 20 +++++------ src/images/icons.rs | 10 ++++-- src/lib.rs | 5 +++ src/renderer/ui_renderer.cpp | 2 +- 10 files changed, 120 insertions(+), 58 deletions(-) diff --git a/installer/core/SoulsyHUD_KID.ini b/installer/core/SoulsyHUD_KID.ini index 207f446c..ab7fdca3 100644 --- a/installer/core/SoulsyHUD_KID.ini +++ b/installer/core/SoulsyHUD_KID.ini @@ -8,7 +8,7 @@ Keyword = Soulsy_Archetype_Guide|Magic Effect|Guide Keyword = Soulsy_Archetype_Light|Magic Effect|Light Keyword = Soulsy_Archetype_NightEye|Magic Effect|NightEye ;Keyword = Soulsy_Archetype_Protect|Magic Effect| -Keyword = Soulsy_Archetype_Reanimate|Magic Effect|*Reanimate +;Keyword = Soulsy_Archetype_Reanimate|Magic Effect|*Reanimate Keyword = Soulsy_Archetype_Reflect|Magic Effect|ReflectDamage Keyword = Soulsy_Archetype_Resist|Magic Effect|*Resist Keyword = Soulsy_Archetype_Root|Magic Effect|*Root @@ -39,6 +39,7 @@ Keyword = Soulsy_BoundGreatword|Magic Effect|*BoundGreatsword ; for misc mods not yet covered by OCF that I use. ; mcCampingLight.esp Keyword = OCF_MgefPowerAction_PitchTent|Magic Effect|mcguff_camp_buildcamp + ; EldenLantern.esp Keyword = OCF_ToolLantern|Armor|EldenRingLantern.esp Keyword = OCF_InvColorBlue|Armor|0x80e~EldenRingLantern.esp @@ -47,6 +48,15 @@ Keyword = OCF_InvColorRed|Armor|0x809~EldenRingLantern.esp Keyword = OCF_InvColorSun|Armor|0xD63~EldenRingLantern.esp Keyword = OCF_InvColorWhite|Armor|0x805~EldenRingLantern.esp +; Effects granted by some minor powers and spells +; Bats vampire lord spell "Bats Effect" [MGEF:0200E654] +Keyword = Soulsy_Power_Bats|Magic Effect|DLC1VQ08BatsEffect +Keyword = Soulsy_Power_Bats|Spell|DLC1VQ08Bats +Keyword = Soulsy_Power_Bats|Spell|DLC1VampireBats +Keyword = Soulsy_Power_Vampire|Spell|DLC1VampireChange +Keyword = Soulsy_Power_Vampire|Magic Effect|DLC1VampireChangeEffect +Keyword = Soulsy_Power_RevertForm|Spell|DLC1RevertForm + ; Spells associated with shouts. ; We can't assign keywords to shouts directly, so we use their spells. diff --git a/src/controller/control.rs b/src/controller/control.rs index dd2888a4..2fce3a42 100644 --- a/src/controller/control.rs +++ b/src/controller/control.rs @@ -1103,7 +1103,11 @@ impl Controller { } else { "Item equipped" }; - log::info!("{prefix}: name='{}'; form_spec='{form_spec}';", item.name()); + log::info!( + "{prefix}: name='{}'; icon={}; form_spec='{form_spec}'", + item.name(), + item.icon() + ); if item.is_ammo() { if let Some(visible) = self.visible.get(&HudElement::Ammo) { @@ -1209,6 +1213,12 @@ impl Controller { self.update_slot(HudElement::Left, &item); } + // If the player is now a werewolf or a vampire, we do not + // attempt to equip anything ourselves. + if isVampireLord() || isWerewolf() { + return false; // I forget what this means. do we even use it? + } + if !switching { // We are not switching from two-hander to one-hander. Phew. return left_unexpected || right_unexpected; diff --git a/src/data/keywords.rs b/src/data/keywords.rs index 33e55199..011c1eff 100644 --- a/src/data/keywords.rs +++ b/src/data/keywords.rs @@ -7,6 +7,8 @@ use enumset::{enum_set, EnumSet, EnumSetType}; use strum::{Display, EnumIter, IntoEnumIterator}; +use super::color::InvColor; + impl TryFrom<&str> for SpellKeywords { type Error = eyre::Error; @@ -93,6 +95,7 @@ pub enum SpellKeywords { MagicSummonUndead, MagicTelekinesis, MagicTurnUndead, + MagicVampireDrain, MagicWard, MagicWeaponSpeed, @@ -104,6 +107,12 @@ pub enum SpellKeywords { IconMagicWater, DAR_SummonAstralWyrm, + // Vampire and werewolf icons. + Power_Bats, + Power_RevertForm, + Power_Vampire, + Spell_Blood, + // vanilla shouts Shout_AnimalAllegiance, Shout_AuraWhisper, @@ -645,7 +654,11 @@ pub const ICON_SUMMON: EnumSet = enum_set!( | SpellKeywords::SpellSummon_Spirit ); -pub const ICON_VAMPIRE: EnumSet = enum_set!(SpellKeywords::ClassVampire); +pub const ICON_VAMPIRE: EnumSet = + enum_set!(SpellKeywords::ClassVampire | SpellKeywords::MagicVampireDrain); + +pub const ICON_BLOOD: EnumSet = + enum_set!(SpellKeywords::Spell_Blood | SpellKeywords::MagicVampireDrain); pub const ICON_VISION: EnumSet = enum_set!( SpellKeywords::SpellEnhance_Sight @@ -823,3 +836,45 @@ pub const COLOR_WIND: EnumSet = enum_set!( | SpellKeywords::SpellDamage_WindCloak | SpellKeywords::SpellDamage_Sonic ); + +pub fn color_for_tagset(tagset: &EnumSet) -> Option { + if !tagset.is_disjoint(DARENII_ARCLIGHT) { + Some(InvColor::ShockArc) + } else if !tagset.is_disjoint(COLOR_ASH) { + Some(InvColor::Ash) + } else if !tagset.is_disjoint(COLOR_BLOOD) { + Some(InvColor::Blood) + } else if !tagset.is_disjoint(COLOR_BOUND_ITEMS) { + Some(InvColor::Bound) + } else if !tagset.is_disjoint(COLOR_EARTH) { + Some(InvColor::Brown) + } else if !tagset.is_disjoint(COLOR_ELDRITCH) { + Some(InvColor::Eldritch) + } else if !tagset.is_disjoint(COLOR_HOLY) { + Some(InvColor::Holy) + } else if !tagset.is_disjoint(DARENII_LUNARIS) { + Some(InvColor::Lunar) + } else if !tagset.is_disjoint(COLOR_NECROTIC) { + Some(InvColor::Necrotic) + } else if !tagset.is_disjoint(COLOR_POISON) { + Some(InvColor::Poison) + } else if !tagset.is_disjoint(COLOR_SHADOW) { + Some(InvColor::Shadow) + } else if !tagset.is_disjoint(COLOR_SUN) { + Some(InvColor::Sun) + } else if !tagset.is_disjoint(COLOR_WATER) { + Some(InvColor::Water) + } else if !tagset.is_disjoint(COLOR_WIND) { + Some(InvColor::Gray) + } else if !tagset.is_disjoint(ICON_HEALING) { + Some(InvColor::Green) + } else if !tagset.is_disjoint(COLOR_FIRE) { + Some(InvColor::Fire) + } else if !tagset.is_disjoint(COLOR_FROST) { + Some(InvColor::Frost) + } else if !tagset.is_disjoint(COLOR_SHOCK) { + Some(InvColor::Shock) + } else { + None + } +} diff --git a/src/data/power.rs b/src/data/power.rs index 24016f85..ae76ec53 100644 --- a/src/data/power.rs +++ b/src/data/power.rs @@ -16,8 +16,14 @@ impl PowerType { let icon = if kywds.contains(SpellKeywords::SpellShapechange_Werebeast) { Icon::PowerWerewolf - } else if kywds.contains(SpellKeywords::SpellShapechange) { - Icon::PowerWerebear + } else if kywds.contains(SpellKeywords::Power_Vampire) + || kywds.contains(SpellKeywords::SpellShapechange_Vampire) + { + Icon::PowerVampire + } else if kywds.contains(SpellKeywords::Power_Bats) { + Icon::PowerBats + } else if kywds.contains(SpellKeywords::Power_RevertForm) { + Icon::PowerRevertForm } else if kywds.contains(SpellKeywords::PowerAction_Bag) { Icon::ArmorBackpack } else if kywds.contains(SpellKeywords::PowerAction_Bard) { @@ -78,7 +84,7 @@ impl PowerType { PowerType { icon, - color: InvColor::default(), + color: color_for_tagset(&kywds).unwrap_or_default(), } } } diff --git a/src/data/shout.rs b/src/data/shout.rs index 3e4d8333..d25daa47 100644 --- a/src/data/shout.rs +++ b/src/data/shout.rs @@ -61,7 +61,7 @@ impl ShoutType { ShoutVariant::KynesPeace => InvColor::Green, ShoutVariant::MarkedForDeath => InvColor::Poison, ShoutVariant::Stormcall => InvColor::Shock, - _ => InvColor::White, + _ => color_for_tagset(&keywords).unwrap_or_default(), }; Self { @@ -147,7 +147,7 @@ impl ShoutType { ShoutVariant::WailOfTheBanshee => "Faaz-zah-frul", ShoutVariant::Wanderlust => "Od-vah-koor", ShoutVariant::Warcry => "Zun-kaal-zoor", - ShoutVariant::Unclassified => "This shout is new to me!", + ShoutVariant::Unclassified => "This shout is new to me.", } } } diff --git a/src/data/spell.rs b/src/data/spell.rs index 74ee6304..7c74afcf 100644 --- a/src/data/spell.rs +++ b/src/data/spell.rs @@ -82,7 +82,7 @@ impl SpellType { } else if !tagset.is_disjoint(ICON_STORM) { Icon::SpellLightningBlast } else if !tagset.is_disjoint(ICON_VAMPIRE) { - Icon::SpellVampire + Icon::PowerVampire } else if !tagset.is_disjoint(ICON_DRUID) { Icon::SpellLeaves } else if !tagset.is_disjoint(ICON_ROOT) { @@ -130,6 +130,10 @@ impl SpellType { Icon::SpellSummon } else if tagset.contains(SpellKeywords::MagicSummonUndead) { Icon::SpellReanimate + } else if tagset.contains(SpellKeywords::Power_Bats) { + Icon::PowerBats + } else if tagset.contains(SpellKeywords::Spell_Blood) { + Icon::SpellBlood } else if tagset.contains(SpellKeywords::SpellShapechange_Werebeast) || tagset.contains(SpellKeywords::SpellShapechange) { @@ -170,42 +174,8 @@ impl SpellType { let color_kwds = strings_to_keywords::(&tags); let color = if let Some(assigned) = color_kwds.first() { assigned.clone() - } else if !tagset.is_disjoint(DARENII_ARCLIGHT) { - InvColor::ShockArc - } else if !tagset.is_disjoint(COLOR_ASH) { - InvColor::Ash - } else if !tagset.is_disjoint(COLOR_BLOOD) { - InvColor::Blood - } else if !tagset.is_disjoint(COLOR_BOUND_ITEMS) { - InvColor::Bound - } else if !tagset.is_disjoint(COLOR_EARTH) { - InvColor::Brown - } else if !tagset.is_disjoint(COLOR_ELDRITCH) { - InvColor::Eldritch - } else if !tagset.is_disjoint(COLOR_HOLY) { - InvColor::Holy - } else if !tagset.is_disjoint(DARENII_LUNARIS) { - InvColor::Lunar - } else if !tagset.is_disjoint(COLOR_NECROTIC) { - InvColor::Necrotic - } else if !tagset.is_disjoint(COLOR_POISON) { - InvColor::Poison - } else if !tagset.is_disjoint(COLOR_SHADOW) { - InvColor::Shadow - } else if !tagset.is_disjoint(COLOR_SUN) { - InvColor::Sun - } else if !tagset.is_disjoint(COLOR_WATER) { - InvColor::Water - } else if !tagset.is_disjoint(COLOR_WIND) { - InvColor::Gray - } else if !tagset.is_disjoint(ICON_HEALING) { - InvColor::Green - } else if !tagset.is_disjoint(COLOR_FIRE) { - InvColor::Fire - } else if !tagset.is_disjoint(COLOR_FROST) { - InvColor::Frost - } else if !tagset.is_disjoint(COLOR_SHOCK) { - InvColor::Shock + } else if let Some(color) = color_for_tagset(&tagset) { + color } else { match data.school { // TODO identify common colors for magical schools diff --git a/src/game/equippable.cpp b/src/game/equippable.cpp index 1527cbbc..6863d62d 100644 --- a/src/game/equippable.cpp +++ b/src/game/equippable.cpp @@ -88,7 +88,7 @@ namespace equippable if (form->Is(RE::FormType::Shout)) { - rlog::trace("making HudItem for shout: '{}'"sv, safename); + rlog::debug("making HudItem for shout: '{}'"sv, safename); auto* shout = form->As(); if (!shout) return simple_from_formdata(ItemCategory::Shout, std::move(safename), formSpec); @@ -108,7 +108,7 @@ namespace equippable if (spell_type == RE::MagicSystem::SpellType::kLesserPower || spell_type == RE::MagicSystem::SpellType::kPower) { - rlog::trace("making HudItem for power: '{}'"sv, safename); + rlog::debug("making HudItem for power: '{}'"sv, safename); const auto* costliest = spell->GetCostliestEffectItem(); if (costliest) { @@ -125,7 +125,7 @@ namespace equippable } // Regular spells. - rlog::trace("making HudItem for spell: '{}'"sv, safename); + rlog::debug("making HudItem for spell: '{}'"sv, safename); const auto* costliest = spell->GetCostliestEffectItem(); if (costliest) { @@ -145,7 +145,7 @@ namespace equippable if (form->Is(RE::FormType::Ammo)) { - rlog::trace("making HudItem for ammo: '{}'"sv, safename); + rlog::debug("making HudItem for ammo: '{}'"sv, safename); const auto* ammo = form->As()->AsKeywordForm(); ammo->ForEachKeyword(KeywordAccumulator::collect); auto& keywords = KeywordAccumulator::mKeywords; @@ -160,7 +160,7 @@ namespace equippable const auto* weapon = form->As(); if (weapon) { - rlog::trace("making HudItem for weapon: '{}'"sv, safename); + rlog::debug("making HudItem for weapon: '{}'"sv, safename); weapon->ForEachKeyword(KeywordAccumulator::collect); auto& keywords = KeywordAccumulator::mKeywords; if (weapon->IsBound()) { keywords->push_back(std::string("OCF_InvColorBound")); } @@ -173,7 +173,7 @@ namespace equippable if (form->IsArmor()) { - rlog::trace("making HudItem for armor: '{}'"sv, safename); + rlog::debug("making HudItem for armor: '{}'"sv, safename); const auto* armor = form->As(); armor->ForEachKeyword(KeywordAccumulator::collect); auto& keywords = KeywordAccumulator::mKeywords; @@ -190,7 +190,7 @@ namespace equippable if (form->Is(RE::FormType::Light)) { // This form type does not have keywords. This presents a problem. Cough. - rlog::trace("making HudItem for light: '{}';"sv, safename); + rlog::debug("making HudItem for light: '{}';"sv, safename); const auto name = std::string(form->GetName()); // this use of GetName() is okay if (name.find("Lantern") != std::string::npos) // yes, very limited in effectiveness; TODO { @@ -204,7 +204,7 @@ namespace equippable if (form->Is(RE::FormType::Scroll)) { - rlog::trace("making HudItem for scroll: '{}'"sv, safename); + rlog::debug("making HudItem for scroll: '{}'"sv, safename); auto* scroll = form->As(); if (scroll->GetCostliestEffectItem() && scroll->GetCostliestEffectItem()->baseEffect) { @@ -226,7 +226,7 @@ namespace equippable if (alchemy_potion->IsFood()) { - rlog::trace("making HudItem for food: '{}'"sv, safename); + rlog::debug("making HudItem for food: '{}'"sv, safename); alchemy_potion->ForEachKeyword(KeywordAccumulator::collect); auto& keywords = KeywordAccumulator::mKeywords; rust::Box item = @@ -235,7 +235,7 @@ namespace equippable } else { - rlog::trace("making HudItem for potion: '{}'"sv, safename); + rlog::debug("making HudItem for potion: '{}'"sv, safename); const auto* effect = alchemy_potion->GetCostliestEffectItem()->baseEffect; auto actor_value = effect->data.primaryAV; rust::Box item = potion_from_formdata(alchemy_potion->IsPoison(), diff --git a/src/images/icons.rs b/src/images/icons.rs index 7160dad8..84a796e4 100644 --- a/src/images/icons.rs +++ b/src/images/icons.rs @@ -97,11 +97,14 @@ pub enum Icon { PotionSkooma, PotionStamina, Power, + PowerBats, PowerCraft, PowerFillBottles, PowerHorse, PowerPeek, PowerPray, + PowerRevertForm, + PowerVampire, PowerWash, PowerWerebear, PowerWerewolf, @@ -176,6 +179,7 @@ pub enum Icon { SpellArclight, // SpellBlast, // not yet used SpellBleed, + SpellBlood, // SpellBolt, // not yet used // SpellChainLightning, // not yet used SpellCircle, @@ -228,7 +232,6 @@ pub enum Icon { SpellSun, SpellTeleport, SpellTime, - SpellVampire, SpellWard, SpellWater, // SpellWave, // not yet used @@ -361,11 +364,14 @@ impl Icon { Icon::PotionStamina => Icon::PotionDefault, Icon::Power => Icon::Power, + Icon::PowerBats => Icon::Power, Icon::PowerCraft => Icon::Power, Icon::PowerFillBottles => Icon::Power, Icon::PowerHorse => Icon::Power, Icon::PowerPeek => Icon::Power, Icon::PowerPray => Icon::Power, + Icon::PowerRevertForm => Icon::Power, + Icon::PowerVampire => Icon::Power, Icon::PowerWash => Icon::Power, Icon::PowerWerebear => Icon::Power, Icon::PowerWerewolf => Icon::Power, @@ -443,6 +449,7 @@ impl Icon { // Icon::SpellArcane => Icon::Destruction, Icon::SpellArclight => Icon::Destruction, // Icon::SpellBlast => Icon::Destruction, + Icon::SpellBlood => Icon::Destruction, Icon::SpellBleed => Icon::Destruction, // Icon::SpellBolt => Icon::Destruction, // Icon::SpellChainLightning => Icon::Destruction, @@ -496,7 +503,6 @@ impl Icon { Icon::SpellSun => Icon::Restoration, Icon::SpellTeleport => Icon::Alteration, Icon::SpellTime => Icon::Alteration, - Icon::SpellVampire => Icon::Destruction, Icon::SpellWard => Icon::Restoration, Icon::SpellWater => Icon::Destruction, // Icon::SpellWave => Icon::Destruction, diff --git a/src/lib.rs b/src/lib.rs index 0c8f2a0c..808c6bda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -624,5 +624,10 @@ pub mod plugin { fn itemCount(form_spec: &CxxString) -> u32; /// Is the player using CGO's alt-grip mode? (Always false if not using CGO or compatible mod.) fn useCGOAltGrip() -> bool; + /// Is the player a vampire lord? + fn isVampireLord() -> bool; + /// Is the player a werewolf? + fn isWerewolf() -> bool; + } } diff --git a/src/renderer/ui_renderer.cpp b/src/renderer/ui_renderer.cpp index 12125fa7..b8d48ad8 100644 --- a/src/renderer/ui_renderer.cpp +++ b/src/renderer/ui_renderer.cpp @@ -542,7 +542,7 @@ namespace ui auto entry_name = std::string(entry->name()); const auto hotkey = settings->hotkey_for(slotLayout.element); const auto slot_center = ImVec2(slotLayout.center.x, slotLayout.center.y); - const bool skipItem = entry_name.empty() && entry->icon_key().empty(); + const bool skipItem = (entry_name.empty() && entry->icon_key().empty()) || entry->form_string().empty(); const auto slotbg = std::string(slotLayout.bg_image); if (slotLayout.bg_color.a > 0 && ui_renderer::lazyLoadHudImage(slotbg)) From d47aeaa4759c99b157fa15906a62a77016537d00 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Thu, 11 Jan 2024 22:05:51 -0800 Subject: [PATCH 04/10] Rename some icons. --- .../{spell_bat.svg => power_bats.svg} | 0 .../{spell_vampire.svg => power_vampire.svg} | 0 installer/icon-pack-soulsy/shout_slowtime.svg | 11 +++++++++++ 3 files changed, 11 insertions(+) rename installer/icon-pack-soulsy/{spell_bat.svg => power_bats.svg} (100%) rename installer/icon-pack-soulsy/{spell_vampire.svg => power_vampire.svg} (100%) create mode 100644 installer/icon-pack-soulsy/shout_slowtime.svg diff --git a/installer/icon-pack-soulsy/spell_bat.svg b/installer/icon-pack-soulsy/power_bats.svg similarity index 100% rename from installer/icon-pack-soulsy/spell_bat.svg rename to installer/icon-pack-soulsy/power_bats.svg diff --git a/installer/icon-pack-soulsy/spell_vampire.svg b/installer/icon-pack-soulsy/power_vampire.svg similarity index 100% rename from installer/icon-pack-soulsy/spell_vampire.svg rename to installer/icon-pack-soulsy/power_vampire.svg diff --git a/installer/icon-pack-soulsy/shout_slowtime.svg b/installer/icon-pack-soulsy/shout_slowtime.svg new file mode 100644 index 00000000..cd358008 --- /dev/null +++ b/installer/icon-pack-soulsy/shout_slowtime.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + From 7345d80a64de26c466c2c8dc60b941dcd1318284 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Thu, 11 Jan 2024 23:18:10 -0800 Subject: [PATCH 05/10] Consolidate icon selection into the keywords file. --- src/controller/control.rs | 2 +- src/data/keywords.rs | 192 +++++++++++++++++++++++++++++++++++++- src/data/power.rs | 66 +------------ src/data/spell.rs | 124 +----------------------- src/game/player.cpp | 4 +- 5 files changed, 198 insertions(+), 190 deletions(-) diff --git a/src/controller/control.rs b/src/controller/control.rs index 2fce3a42..d3650b1e 100644 --- a/src/controller/control.rs +++ b/src/controller/control.rs @@ -1104,7 +1104,7 @@ impl Controller { "Item equipped" }; log::info!( - "{prefix}: name='{}'; icon={}; form_spec='{form_spec}'", + "{prefix}: name='{}'; icon='{}'; form_spec='{form_spec}'", item.name(), item.icon() ); diff --git a/src/data/keywords.rs b/src/data/keywords.rs index 011c1eff..8b6c2ac3 100644 --- a/src/data/keywords.rs +++ b/src/data/keywords.rs @@ -7,6 +7,8 @@ use enumset::{enum_set, EnumSet, EnumSetType}; use strum::{Display, EnumIter, IntoEnumIterator}; +use crate::images::Icon; + use super::color::InvColor; impl TryFrom<&str> for SpellKeywords { @@ -654,8 +656,11 @@ pub const ICON_SUMMON: EnumSet = enum_set!( | SpellKeywords::SpellSummon_Spirit ); -pub const ICON_VAMPIRE: EnumSet = - enum_set!(SpellKeywords::ClassVampire | SpellKeywords::MagicVampireDrain); +pub const ICON_VAMPIRE: EnumSet = enum_set!( + SpellKeywords::ClassVampire + | SpellKeywords::MagicVampireDrain + | SpellKeywords::SpellShapechange_Vampire +); pub const ICON_BLOOD: EnumSet = enum_set!(SpellKeywords::Spell_Blood | SpellKeywords::MagicVampireDrain); @@ -837,6 +842,189 @@ pub const COLOR_WIND: EnumSet = enum_set!( | SpellKeywords::SpellDamage_Sonic ); +pub fn icon_for_tagset(tagset: &EnumSet) -> Option { + if tagset.contains(SpellKeywords::Power_Bats) { + Some(Icon::PowerBats) + } else if tagset.contains(SpellKeywords::SpellShapechange_Werebeast) { + Some(Icon::PowerWerewolf) + } else if tagset.contains(SpellKeywords::Power_RevertForm) { + Some(Icon::PowerRevertForm) + } else if tagset.contains(SpellKeywords::PowerAction_Bag) { + Some(Icon::ArmorBackpack) + } else if tagset.contains(SpellKeywords::PowerAction_Bard) { + Some(Icon::MiscLute) + } else if tagset.contains(SpellKeywords::PowerAction_Bathe) { + // I have no joke here; I just like saying power wash. + Some(Icon::PowerWash) + } else if tagset.contains(SpellKeywords::PowerAction_Bless) { + Some(Icon::ArmorBackpack) // TODO bless icon + } else if tagset.contains(SpellKeywords::PowerAction_BuryCorpse) { + Some(Icon::ToolShovel) + } else if tagset.contains(SpellKeywords::PowerAction_Campfire) { + Some(Icon::MiscCampfire) + // } else if tagset.contains(SpellKeywords::PowerAction_Coin) { + // Some(Icon::MiscCoin) + // } else if tagset.contains(SpellKeywords::PowerAction_CommandFollower) { + // Some(Icon::ArmorBackpack) // TODO command icon + // } else if tagset.contains(SpellKeywords::PowerAction_Craft) { + // Some(Icon::ArmorBackpack) // TODO craft icon + } else if tagset.contains(SpellKeywords::PowerAction_FillWater) { + Some(Icon::PowerFillBottles) + } else if tagset.contains(SpellKeywords::PowerAction_HarvestCorpse) { + Some(Icon::ToolShovel) // TODO wrong! + } else if tagset.contains(SpellKeywords::PowerAction_HarvestGather) { + Some(Icon::ToolSickle) + } else if tagset.contains(SpellKeywords::PowerAction_HarvestWood) { + Some(Icon::WeaponWoodAxe) + } else if tagset.contains(SpellKeywords::PowerAction_Horse) { + Some(Icon::PowerHorse) + } else if tagset.contains(SpellKeywords::PowerAction_Lantern) { + Some(Icon::MiscLantern) + } else if tagset.contains(SpellKeywords::PowerAction_PitchTent) { + Some(Icon::MiscTent) + } else if tagset.contains(SpellKeywords::PowerAction_PeekKeyhole) { + Some(Icon::PowerPeek) + } else if tagset.contains(SpellKeywords::PowerAction_Potion) { + Some(Icon::PotionDefault) + } else if tagset.contains(SpellKeywords::PowerAction_Pray) { + Some(Icon::PowerPray) + } else if tagset.contains(SpellKeywords::PowerAction_Relax) { + Some(Icon::PowerPeek) + // } else if tagset.contains(SpellKeywords::PowerAction_Speech) { + // Some(Icon::PowerPeek) + // } else if tagset.contains(SpellKeywords::PowerAction_StatusFrostfall) { + // Some(Icon::PowerPeek) + // } else if tagset.contains(SpellKeywords::PowerAction_StatusSunhelm) { + // Some(Icon::PowerPeek) + } else if tagset.contains(SpellKeywords::PowerAction_TameAnimal) { + Some(Icon::ShoutAnimalAllegiance) + // } else if tagset.contains(SpellKeywords::PowerAction_Train) { + // Some(Icon::PowerPeek) + // } else if tagset.contains(SpellKeywords::PowerAction_WeaponGrip) { + // Some(Icon::WeaponGrip) + } else if !tagset.is_disjoint(ICON_CLOAK) { + Some(Icon::ArmorCloak) + } else if !tagset.is_disjoint(ICON_BUFF) { + Some(Icon::SpellStamina) + } else if !tagset.is_disjoint(ICON_CONTROL) { + Some(Icon::SpellControl) + } else if !tagset.is_disjoint(ICON_FEAR) { + Some(Icon::SpellFear) + } else if !tagset.is_disjoint(ICON_LIGHT) { + Some(Icon::SpellLight) + } else if !tagset.is_disjoint(ICON_SUMMON) { + Some(Icon::SpellSummon) + } else if !tagset.is_disjoint(ICON_PARALYZE) { + Some(Icon::SpellParalyze) + } else if !tagset.is_disjoint(ICON_VISION) { + Some(Icon::SpellEagleEye) + // bound weapons + } else if tagset.contains(SpellKeywords::SpellBound_Weapon) { + if tagset.contains(SpellKeywords::BoundBattleAxe) { + Some(Icon::WeaponAxeTwoHanded) + } else if tagset.contains(SpellKeywords::BoundBow) { + Some(Icon::WeaponBow) + } else if tagset.contains(SpellKeywords::BoundDagger) { + Some(Icon::WeaponDagger) + } else if tagset.contains(SpellKeywords::BoundGreatsword) { + Some(Icon::WeaponSwordTwoHanded) + } else if tagset.contains(SpellKeywords::BoundHammer) { + Some(Icon::WeaponHammer) + } else if tagset.contains(SpellKeywords::BoundMace) { + Some(Icon::WeaponMace) + } else if tagset.contains(SpellKeywords::BoundShield) { + Some(Icon::ArmorShieldHeavy) + } else if tagset.contains(SpellKeywords::BoundSword) { + Some(Icon::WeaponSwordOneHanded) + } else if tagset.contains(SpellKeywords::BoundWarAxe) { + Some(Icon::WeaponAxeOneHanded) + } else { + Some(Icon::WeaponSwordOneHanded) + } + } else if tagset.contains(SpellKeywords::SpellBound_Armor) { + Some(Icon::ArmorShieldHeavy) + } else if !tagset.is_disjoint(ICON_HEALING) { + Some(Icon::SpellHeal) + } else if !tagset.is_disjoint(ICON_EARTH) { + Some(Icon::SpellEarth) + } else if !tagset.is_disjoint(ICON_STORM) { + Some(Icon::SpellLightningBlast) + } else if !tagset.is_disjoint(ICON_VAMPIRE) { + Some(Icon::PowerVampire) + } else if !tagset.is_disjoint(ICON_DRUID) { + Some(Icon::SpellLeaves) + } else if !tagset.is_disjoint(ICON_ROOT) { + Some(Icon::SpellRoot) + } else if !tagset.is_disjoint(ICON_CIRCLE) { + Some(Icon::SpellCircle) + } else if !tagset.is_disjoint(ICON_HOLY) { + Some(Icon::SpellSun) + // next one-off vanilla spells + } else if tagset.contains(SpellKeywords::Archetype_Teleport) { + Some(Icon::SpellTeleport) + } else if tagset.contains(SpellKeywords::SpellTime) { + Some(Icon::SpellTime) + } else if tagset.contains(SpellKeywords::Archetype_Detect) { + Some(Icon::SpellDetect) + } else if tagset.contains(SpellKeywords::Archetype_WeaponBuff) { + Some(Icon::SpellSharpen) + } else if tagset.contains(SpellKeywords::Archetype_Guide) { + Some(Icon::SpellWisp) + } else if tagset.contains(SpellKeywords::Archetype_CarryWeight) { + Some(Icon::SpellFeather) + } else if tagset.contains(SpellKeywords::Archetype_Cure) { + Some(Icon::SpellCure) + } else if tagset.contains(SpellKeywords::SpellReanimate) { + Some(Icon::SpellReanimate) + } else if tagset.contains(SpellKeywords::Archetype_Reflect) { + Some(Icon::SpellReflect) + } else if tagset.contains(SpellKeywords::MagicRune) { + Some(Icon::SpellRune) + } else if tagset.contains(SpellKeywords::Archetype_Silence) { + Some(Icon::SpellSilence) + } else if tagset.contains(SpellKeywords::SpellSoulTrap) { + Some(Icon::SpellSoultrap) + } else if tagset.contains(SpellKeywords::MagicSlow) { + Some(Icon::SpellSlow) + } else if tagset.contains(SpellKeywords::MagicNightEye) { + Some(Icon::SpellDetect) + } else if tagset.contains(SpellKeywords::MagicTurnUndead) { + Some(Icon::SpellSun) + } else if tagset.contains(SpellKeywords::MagicWard) { + Some(Icon::SpellWard) + } else if tagset.contains(SpellKeywords::MagicWeaponSpeed) { + Some(Icon::ShoutElementalFury) + } else if tagset.contains(SpellKeywords::MagicSummonFamiliar) { + Some(Icon::SpellSummon) + } else if tagset.contains(SpellKeywords::MagicSummonUndead) { + Some(Icon::SpellReanimate) + } else if tagset.contains(SpellKeywords::Spell_Blood) { + Some(Icon::SpellBlood) + } else if tagset.contains(SpellKeywords::SpellShapechange_Werebeast) { + Some(Icon::PowerWerewolf) + // next icon packs + } else if !tagset.is_disjoint(DARENII_ARCLIGHT) { + Some(Icon::SpellArclight) + } else if !tagset.is_disjoint(DARENII_DESECRATION) { + Some(Icon::SpellDesecration) + } else if !tagset.is_disjoint(DARENII_STELLARIS) { + Some(Icon::SpellStars) + } else if !tagset.is_disjoint(DARENII_LUNARIS) { + Some(Icon::SpellMoon) + } else if !tagset.is_disjoint(CONSTELLATION_SPELLS) { + Some(Icon::SpellConstellation) + // now really generic damage spells + } else if !tagset.is_disjoint(ICON_FIRE) { + Some(Icon::SpellFire) + } else if !tagset.is_disjoint(ICON_SHOCK) { + Some(Icon::SpellShock) + } else if !tagset.is_disjoint(ICON_FROST) { + Some(Icon::SpellFrost) + } else { + None + } +} + pub fn color_for_tagset(tagset: &EnumSet) -> Option { if !tagset.is_disjoint(DARENII_ARCLIGHT) { Some(InvColor::ShockArc) diff --git a/src/data/power.rs b/src/data/power.rs index ae76ec53..edd9ede2 100644 --- a/src/data/power.rs +++ b/src/data/power.rs @@ -12,71 +12,11 @@ pub struct PowerType { impl PowerType { pub fn new(name: &str, tags: Vec) -> Self { + log::info!("{tags:?}"); let kywds = strings_to_enumset::(&tags); - let icon = if kywds.contains(SpellKeywords::SpellShapechange_Werebeast) { - Icon::PowerWerewolf - } else if kywds.contains(SpellKeywords::Power_Vampire) - || kywds.contains(SpellKeywords::SpellShapechange_Vampire) - { - Icon::PowerVampire - } else if kywds.contains(SpellKeywords::Power_Bats) { - Icon::PowerBats - } else if kywds.contains(SpellKeywords::Power_RevertForm) { - Icon::PowerRevertForm - } else if kywds.contains(SpellKeywords::PowerAction_Bag) { - Icon::ArmorBackpack - } else if kywds.contains(SpellKeywords::PowerAction_Bard) { - Icon::MiscLute - } else if kywds.contains(SpellKeywords::PowerAction_Bathe) { - // I have no joke here; I just like saying power wash. - Icon::PowerWash - } else if kywds.contains(SpellKeywords::PowerAction_Bless) { - Icon::ArmorBackpack // TODO bless icon - } else if kywds.contains(SpellKeywords::PowerAction_BuryCorpse) { - Icon::ToolShovel - } else if kywds.contains(SpellKeywords::PowerAction_Campfire) { - Icon::MiscCampfire - // } else if kywds.contains(SpellKeywords::PowerAction_Coin) { - // Icon::MiscCoin - // } else if kywds.contains(SpellKeywords::PowerAction_CommandFollower) { - // Icon::ArmorBackpack // TODO command icon - // } else if kywds.contains(SpellKeywords::PowerAction_Craft) { - // Icon::ArmorBackpack // TODO craft icon - } else if kywds.contains(SpellKeywords::PowerAction_FillWater) { - Icon::PowerFillBottles - } else if kywds.contains(SpellKeywords::PowerAction_HarvestCorpse) { - Icon::ToolShovel // TODO wrong! - } else if kywds.contains(SpellKeywords::PowerAction_HarvestGather) { - Icon::ToolSickle - } else if kywds.contains(SpellKeywords::PowerAction_HarvestWood) { - Icon::WeaponWoodAxe - } else if kywds.contains(SpellKeywords::PowerAction_Horse) { - Icon::PowerHorse - } else if kywds.contains(SpellKeywords::PowerAction_Lantern) { - Icon::MiscLantern - } else if kywds.contains(SpellKeywords::PowerAction_PitchTent) { - Icon::MiscTent - } else if kywds.contains(SpellKeywords::PowerAction_PeekKeyhole) { - Icon::PowerPeek - } else if kywds.contains(SpellKeywords::PowerAction_Potion) { - Icon::PotionDefault - } else if kywds.contains(SpellKeywords::PowerAction_Pray) { - Icon::PowerPray - } else if kywds.contains(SpellKeywords::PowerAction_Relax) { - Icon::PowerPeek - // } else if kywds.contains(SpellKeywords::PowerAction_Speech) { - // Icon::PowerPeek - // } else if kywds.contains(SpellKeywords::PowerAction_StatusFrostfall) { - // Icon::PowerPeek - // } else if kywds.contains(SpellKeywords::PowerAction_StatusSunhelm) { - // Icon::PowerPeek - } else if kywds.contains(SpellKeywords::PowerAction_TameAnimal) { - Icon::ShoutAnimalAllegiance - // } else if kywds.contains(SpellKeywords::PowerAction_Train) { - // Icon::PowerPeek - // } else if kywds.contains(SpellKeywords::PowerAction_WeaponGrip) { - // Icon::WeaponGrip + let icon = if let Some(found) = icon_for_tagset(&kywds) { + found } else { log::debug!("Falling back to default icon for power; name='{name}'; keywords={tags:?}"); Icon::Power diff --git a/src/data/spell.rs b/src/data/spell.rs index 7c74afcf..ef71867b 100644 --- a/src/data/spell.rs +++ b/src/data/spell.rs @@ -34,128 +34,8 @@ impl SpellType { // a spell type, e.g. cloak spells, we use that. We then try to use an // icon for a mod spell pack, e.g., constellation. If all else fails, // we use the icon for the magic school. - let icon = if !tagset.is_disjoint(ICON_CLOAK) { - Icon::ArmorCloak - } else if !tagset.is_disjoint(ICON_BUFF) { - Icon::SpellStamina - } else if !tagset.is_disjoint(ICON_CONTROL) { - Icon::SpellControl - } else if !tagset.is_disjoint(ICON_FEAR) { - Icon::SpellFear - } else if !tagset.is_disjoint(ICON_LIGHT) { - Icon::SpellLight - } else if !tagset.is_disjoint(ICON_SUMMON) { - Icon::SpellSummon - } else if !tagset.is_disjoint(ICON_PARALYZE) { - Icon::SpellParalyze - } else if !tagset.is_disjoint(ICON_VISION) { - Icon::SpellEagleEye - // bound weapons - } else if tagset.contains(SpellKeywords::SpellBound_Weapon) { - if tagset.contains(SpellKeywords::BoundBattleAxe) { - Icon::WeaponAxeTwoHanded - } else if tagset.contains(SpellKeywords::BoundBow) { - Icon::WeaponBow - } else if tagset.contains(SpellKeywords::BoundDagger) { - Icon::WeaponDagger - } else if tagset.contains(SpellKeywords::BoundGreatsword) { - Icon::WeaponSwordTwoHanded - } else if tagset.contains(SpellKeywords::BoundHammer) { - Icon::WeaponHammer - } else if tagset.contains(SpellKeywords::BoundMace) { - Icon::WeaponMace - } else if tagset.contains(SpellKeywords::BoundShield) { - Icon::ArmorShieldHeavy - } else if tagset.contains(SpellKeywords::BoundSword) { - Icon::WeaponSwordOneHanded - } else if tagset.contains(SpellKeywords::BoundWarAxe) { - Icon::WeaponAxeOneHanded - } else { - Icon::WeaponSwordOneHanded - } - } else if tagset.contains(SpellKeywords::SpellBound_Armor) { - Icon::ArmorShieldHeavy - } else if !tagset.is_disjoint(ICON_HEALING) { - Icon::SpellHeal - } else if !tagset.is_disjoint(ICON_EARTH) { - Icon::SpellEarth - } else if !tagset.is_disjoint(ICON_STORM) { - Icon::SpellLightningBlast - } else if !tagset.is_disjoint(ICON_VAMPIRE) { - Icon::PowerVampire - } else if !tagset.is_disjoint(ICON_DRUID) { - Icon::SpellLeaves - } else if !tagset.is_disjoint(ICON_ROOT) { - Icon::SpellRoot - } else if !tagset.is_disjoint(ICON_CIRCLE) { - Icon::SpellCircle - } else if !tagset.is_disjoint(ICON_HOLY) { - Icon::SpellSun - // next one-off vanilla spells - } else if tagset.contains(SpellKeywords::Archetype_Teleport) { - Icon::SpellTeleport - } else if tagset.contains(SpellKeywords::SpellTime) { - Icon::SpellTime - } else if tagset.contains(SpellKeywords::Archetype_Detect) { - Icon::SpellDetect - } else if tagset.contains(SpellKeywords::Archetype_WeaponBuff) { - Icon::SpellSharpen - } else if tagset.contains(SpellKeywords::Archetype_Guide) { - Icon::SpellWisp - } else if tagset.contains(SpellKeywords::Archetype_CarryWeight) { - Icon::SpellFeather - } else if tagset.contains(SpellKeywords::Archetype_Cure) { - Icon::SpellCure - } else if tagset.contains(SpellKeywords::SpellReanimate) { - Icon::SpellReanimate - } else if tagset.contains(SpellKeywords::Archetype_Reflect) { - Icon::SpellReflect - } else if tagset.contains(SpellKeywords::MagicRune) { - Icon::SpellRune - } else if tagset.contains(SpellKeywords::Archetype_Silence) { - Icon::SpellSilence - } else if tagset.contains(SpellKeywords::SpellSoulTrap) { - Icon::SpellSoultrap - } else if tagset.contains(SpellKeywords::MagicSlow) { - Icon::SpellSlow - } else if tagset.contains(SpellKeywords::MagicNightEye) { - Icon::SpellDetect - } else if tagset.contains(SpellKeywords::MagicTurnUndead) { - Icon::SpellSun - } else if tagset.contains(SpellKeywords::MagicWard) { - Icon::SpellWard - } else if tagset.contains(SpellKeywords::MagicWeaponSpeed) { - Icon::ShoutElementalFury - } else if tagset.contains(SpellKeywords::MagicSummonFamiliar) { - Icon::SpellSummon - } else if tagset.contains(SpellKeywords::MagicSummonUndead) { - Icon::SpellReanimate - } else if tagset.contains(SpellKeywords::Power_Bats) { - Icon::PowerBats - } else if tagset.contains(SpellKeywords::Spell_Blood) { - Icon::SpellBlood - } else if tagset.contains(SpellKeywords::SpellShapechange_Werebeast) - || tagset.contains(SpellKeywords::SpellShapechange) - { - Icon::PowerWerewolf - // next icon packs - } else if !tagset.is_disjoint(DARENII_ARCLIGHT) { - Icon::SpellArclight - } else if !tagset.is_disjoint(DARENII_DESECRATION) { - Icon::SpellDesecration - } else if !tagset.is_disjoint(DARENII_STELLARIS) { - Icon::SpellStars - } else if !tagset.is_disjoint(DARENII_LUNARIS) { - Icon::SpellMoon - } else if !tagset.is_disjoint(CONSTELLATION_SPELLS) { - Icon::SpellConstellation - // now really generic damage spells - } else if !tagset.is_disjoint(ICON_FIRE) { - Icon::SpellFire - } else if !tagset.is_disjoint(ICON_SHOCK) { - Icon::SpellShock - } else if !tagset.is_disjoint(ICON_FROST) { - Icon::SpellFrost + let icon = if let Some(icon) = icon_for_tagset(&tagset) { + icon } else { log::debug!("Falling back to magic school for spell; data: {data:?}"); log::debug!(" keywords: {tags:?}"); diff --git a/src/game/player.cpp b/src/game/player.cpp index 66746a6c..835ffb05 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -23,7 +23,7 @@ namespace player if (!race) { return false; } if (race->GetFormID() == 0x0200283a) { return true; } const auto* editorID = race->GetFormEditorID(); - if (editorID && std::strcmp(editorID, "DLC1VampireBeastRace")) { return true; } + if (editorID && std::strcmp(editorID, "DLC1VampireBeastRace") == 0) { return true; } return false; } @@ -34,7 +34,7 @@ namespace player if (!race) { return false; } if (race->GetFormID() == 0x000cdd84) { return true; } const auto* editorID = race->GetFormEditorID(); - if (editorID && std::strcmp(editorID, "WerewolfBeastRace")) { return true; } + if (editorID && std::strcmp(editorID, "WerewolfBeastRace") == 0) { return true; } return false; } From b086bbec57bad0ee1ac630e429b47023a06f4aaf Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Fri, 12 Jan 2024 15:39:51 -0800 Subject: [PATCH 06/10] A new icon: book. --- installer/icon-pack-soulsy/misc_book.svg | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 installer/icon-pack-soulsy/misc_book.svg diff --git a/installer/icon-pack-soulsy/misc_book.svg b/installer/icon-pack-soulsy/misc_book.svg new file mode 100644 index 00000000..a8f39ac4 --- /dev/null +++ b/installer/icon-pack-soulsy/misc_book.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + From b235ca82ad9847a0711a51529fcbb739606e6a66 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Fri, 12 Jan 2024 15:45:04 -0800 Subject: [PATCH 07/10] Handle equipping books for spell learning reworks. A new item category, a new icon, a new clause in the huditem maker. This might need some tweaking. --- src/data/base.rs | 8 ++++++++ src/data/mod.rs | 1 + src/game/equippable.cpp | 7 +++++++ src/images/icons.rs | 4 +++- src/lib.rs | 4 +++- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/data/base.rs b/src/data/base.rs index 6e54d8b3..d0aa05cb 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -21,6 +21,7 @@ pub enum BaseType { Empty, Ammo(AmmoType), Armor(ArmorType), + Book, Food(FoodType), HandToHand, Light(LightType), @@ -91,6 +92,7 @@ impl BaseType { ItemCategory::Armor => { Self::Armor(ArmorType::classify(name, keywords.clone(), twohanded)) } + ItemCategory::Book => Self::Book, ItemCategory::Food => Self::Food(FoodType::classify(name, keywords.clone(), twohanded)), ItemCategory::HandToHand => Self::HandToHand, ItemCategory::Lantern => Self::Light(LightType::Lantern), @@ -112,6 +114,7 @@ impl BaseType { BaseType::Empty => false, BaseType::Ammo(_) => true, BaseType::Armor(_) => true, + BaseType::Book => true, BaseType::Equipset(_) => false, BaseType::Food(_) => true, BaseType::HandToHand => false, @@ -155,6 +158,7 @@ impl BaseType { BaseType::Empty => false, BaseType::Ammo(_) => false, BaseType::Armor(t) => t.is_utility(), + BaseType::Book => true, BaseType::Equipset(_) => false, BaseType::Food(_) => true, BaseType::HandToHand => false, @@ -195,6 +199,7 @@ impl BaseType { BaseType::Empty => false, BaseType::Ammo(_) => false, BaseType::Armor(t) => !t.is_utility(), + BaseType::Book => false, BaseType::Equipset(_) => false, BaseType::Food(_) => false, BaseType::HandToHand => true, @@ -214,6 +219,7 @@ impl BaseType { BaseType::Empty => false, BaseType::Ammo(_) => false, BaseType::Armor(_) => false, + BaseType::Book => false, BaseType::Equipset(_) => false, BaseType::Food(_) => false, BaseType::HandToHand => true, @@ -235,6 +241,7 @@ impl HasIcon for BaseType { BaseType::Empty => Color::default(), BaseType::Ammo(t) => t.color(), BaseType::Armor(t) => t.color(), + BaseType::Book => Color::default(), // for now BaseType::Equipset(_) => Color::default(), BaseType::Food(t) => t.color(), BaseType::HandToHand => Color::default(), @@ -254,6 +261,7 @@ impl HasIcon for BaseType { BaseType::Empty => &Icon::IconDefault, BaseType::Ammo(t) => t.icon(), BaseType::Armor(t) => t.icon(), + BaseType::Book => &Icon::MiscBook, BaseType::Equipset(t) => t, BaseType::Food(t) => t.icon(), BaseType::HandToHand => &Icon::HandToHand, diff --git a/src/data/mod.rs b/src/data/mod.rs index 1fe0620b..f6488d1b 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -100,6 +100,7 @@ pub fn categorize_shout( pub fn simple_from_formdata(kind: ItemCategory, name: String, form_string: String) -> Box { let classification = match kind { + ItemCategory::Book => BaseType::Book, ItemCategory::HandToHand => BaseType::HandToHand, ItemCategory::Lantern => BaseType::Light(base::LightType::Lantern), ItemCategory::Torch => BaseType::Light(base::LightType::Torch), diff --git a/src/game/equippable.cpp b/src/game/equippable.cpp index 6863d62d..ee0b059a 100644 --- a/src/game/equippable.cpp +++ b/src/game/equippable.cpp @@ -247,6 +247,13 @@ namespace equippable } } + if (form->Is(RE::FormType::Book)) + { + rlog::debug("making HudItem for boook: '{}';"sv, safename); + rust::Box item = simple_from_formdata(ItemCategory::Book, std::move(safename), formSpec); + return item; + } + const auto formtype = form->GetFormType(); const auto formtypestr = RE::FormTypeToString(formtype); rlog::debug("hudItemFromForm() fell all the way through; type={}; name='{}'; formspec='{}';", diff --git a/src/images/icons.rs b/src/images/icons.rs index 84a796e4..77678dfb 100644 --- a/src/images/icons.rs +++ b/src/images/icons.rs @@ -82,8 +82,9 @@ pub enum Icon { #[default] IconDefault, Illusion, - MiscLantern, + MiscBook, MiscCampfire, + MiscLantern, MiscLute, MiscTent, PotionDefault, @@ -342,6 +343,7 @@ impl Icon { Icon::HandToHand => Icon::HandToHand, Icon::IconDefault => Icon::IconDefault, + Icon::MiscBook => Icon::IconDefault, Icon::MiscCampfire => Icon::IconDefault, Icon::MiscLantern => Icon::MiscLantern, Icon::MiscLute => Icon::IconDefault, diff --git a/src/lib.rs b/src/lib.rs index 808c6bda..bf15bfe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,11 +213,13 @@ pub mod plugin { } /// A high-level item category, used to jump-start item categorization via keywords & form data. - /// These categories make sense to the HUD and do not have to map to form types. + /// These categories make sense to the HUD and do not have to map to form types. They are, however, + /// pretty related. #[derive(Debug, Clone, Hash)] enum ItemCategory { Ammo, Armor, + Book, Food, HandToHand, Lantern, From f9574a96084f951032be035ac9773a5e3cd1ff8d Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Fri, 12 Jan 2024 15:59:57 -0800 Subject: [PATCH 08/10] KID tweaks. --- installer/core/SoulsyHUD_KID.ini | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/installer/core/SoulsyHUD_KID.ini b/installer/core/SoulsyHUD_KID.ini index ab7fdca3..4038e8ea 100644 --- a/installer/core/SoulsyHUD_KID.ini +++ b/installer/core/SoulsyHUD_KID.ini @@ -1,5 +1,5 @@ ; These keywords were ones I did before integrating with OCF. -; I should see if I can remove them. +; I comment them out as I find OCF tags that can do the same work. Keyword = Soulsy_Archetype_CarryWeight|Magic Effect|CarryWeight Keyword = Soulsy_Archetype_Detect|Magic Effect|*Detect Keyword = Soulsy_Archetype_Detect|Magic Effect|DetectLife @@ -50,12 +50,13 @@ Keyword = OCF_InvColorWhite|Armor|0x805~EldenRingLantern.esp ; Effects granted by some minor powers and spells ; Bats vampire lord spell "Bats Effect" [MGEF:0200E654] -Keyword = Soulsy_Power_Bats|Magic Effect|DLC1VQ08BatsEffect -Keyword = Soulsy_Power_Bats|Spell|DLC1VQ08Bats -Keyword = Soulsy_Power_Bats|Spell|DLC1VampireBats -Keyword = Soulsy_Power_Vampire|Spell|DLC1VampireChange -Keyword = Soulsy_Power_Vampire|Magic Effect|DLC1VampireChangeEffect -Keyword = Soulsy_Power_RevertForm|Spell|DLC1RevertForm +Keyword = Soulsy_Power_Bats|Magic Effect|DLC1VQ08BatsEffect|NONE|100 +Keyword = Soulsy_Power_Bats|Spell|DLC1VQ08Bats|NONE|100 +Keyword = Soulsy_Power_Bats|Spell|DLC1VampireBats|NONE|100 +Keyword = Soulsy_Power_Bats|Magic Effect|0x0200E654|NONE|100 +Keyword = Soulsy_Power_Vampire|Spell|DLC1VampireChange|NONE|100 +Keyword = Soulsy_Power_Vampire|Magic Effect|DLC1VampireChangeEffect|NONE|100 +Keyword = Soulsy_Power_RevertForm|Spell|DLC1RevertForm|NONE|100 ; Spells associated with shouts. ; We can't assign keywords to shouts directly, so we use their spells. From afe21451a74acedd3b4259327759f42d86282b9c Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Fri, 12 Jan 2024 17:07:40 -0800 Subject: [PATCH 09/10] A better bats icon. --- installer/icon-pack-soulsy/power_bats.svg | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/installer/icon-pack-soulsy/power_bats.svg b/installer/icon-pack-soulsy/power_bats.svg index f9503b8e..d17ba2d6 100644 --- a/installer/icon-pack-soulsy/power_bats.svg +++ b/installer/icon-pack-soulsy/power_bats.svg @@ -1,9 +1,11 @@ - - - - - - - - + + + + + + + + + + From 937d25ed021c68ba9f61c897acdd7bc6b687d4f1 Mon Sep 17 00:00:00 2001 From: C J Silverio Date: Fri, 12 Jan 2024 18:06:53 -0800 Subject: [PATCH 10/10] KID improvements, color cleanup, dead code pruning. --- installer/core/SoulsyHUD_KID.ini | 11 ++++----- src/data/mod.rs | 39 +++++++++----------------------- src/data/power.rs | 12 ++++++---- src/data/spell.rs | 13 +++++------ 4 files changed, 29 insertions(+), 46 deletions(-) diff --git a/installer/core/SoulsyHUD_KID.ini b/installer/core/SoulsyHUD_KID.ini index 4038e8ea..5dfb1775 100644 --- a/installer/core/SoulsyHUD_KID.ini +++ b/installer/core/SoulsyHUD_KID.ini @@ -50,13 +50,10 @@ Keyword = OCF_InvColorWhite|Armor|0x805~EldenRingLantern.esp ; Effects granted by some minor powers and spells ; Bats vampire lord spell "Bats Effect" [MGEF:0200E654] -Keyword = Soulsy_Power_Bats|Magic Effect|DLC1VQ08BatsEffect|NONE|100 -Keyword = Soulsy_Power_Bats|Spell|DLC1VQ08Bats|NONE|100 -Keyword = Soulsy_Power_Bats|Spell|DLC1VampireBats|NONE|100 -Keyword = Soulsy_Power_Bats|Magic Effect|0x0200E654|NONE|100 -Keyword = Soulsy_Power_Vampire|Spell|DLC1VampireChange|NONE|100 -Keyword = Soulsy_Power_Vampire|Magic Effect|DLC1VampireChangeEffect|NONE|100 -Keyword = Soulsy_Power_RevertForm|Spell|DLC1RevertForm|NONE|100 +Keyword = Soulsy_Power_Bats|Magic Effect|DLC1BatsEffect +;Keyword = Soulsy_Power_Vampire|Spell|DLC1VampireChange|NONE|100 +;Keyword = Soulsy_Power_Vampire|Magic Effect|DLC1VampireChangeEffect|NONE|100 +;Keyword = Soulsy_Power_RevertForm|Spell|DLC1RevertForm|NONE|100 ; Spells associated with shouts. ; We can't assign keywords to shouts directly, so we use their spells. diff --git a/src/data/mod.rs b/src/data/mod.rs index f6488d1b..b6d1f570 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -54,6 +54,17 @@ pub fn hud_item_from_keywords( Box::new(result) } +pub fn categorize_shout( + keywords_ffi: &CxxVector, + name: String, + form_string: String, +) -> Box { + let keywords: Vec = keywords_ffi.iter().map(|xs| xs.to_string()).collect(); + let kind = BaseType::Shout(ShoutType::new(keywords)); + let result = HudItem::preclassified(name, form_string, 1, kind); + Box::new(result) +} + pub fn fill_out_spell_data( hostile: bool, resist: i32, @@ -87,17 +98,6 @@ pub fn magic_from_spelldata( Box::new(result) } -pub fn categorize_shout( - keywords_ffi: &CxxVector, - name: String, - form_string: String, -) -> Box { - let keywords: Vec = keywords_ffi.iter().map(|xs| xs.to_string()).collect(); - let kind = BaseType::Shout(ShoutType::new(keywords)); - let result = HudItem::preclassified(name, form_string, 1, kind); - Box::new(result) -} - pub fn simple_from_formdata(kind: ItemCategory, name: String, form_string: String) -> Box { let classification = match kind { ItemCategory::Book => BaseType::Book, @@ -175,23 +175,6 @@ pub trait HasKeywords { fn classify(name: &str, keywords: Vec, twohanded: bool) -> Self; } -// A generic convert keywords to enum variants function. -pub fn strings_to_keywords TryFrom<&'a str>>(tags: &[String]) -> Vec { - let keywords: Vec = tags - .iter() - .filter_map(|xs| { - if let Ok(subtype) = T::try_from(xs.as_str()) { - Some(subtype) - } else { - log::trace!("Unknown keyword: '{xs}';"); - - None - } - }) - .collect(); - keywords -} - // Generic convert keywords to an enum set. pub fn strings_to_enumset TryFrom<&'a str>>( tags: &[String], diff --git a/src/data/power.rs b/src/data/power.rs index edd9ede2..09dee79c 100644 --- a/src/data/power.rs +++ b/src/data/power.rs @@ -1,6 +1,7 @@ use super::color::InvColor; use super::keywords::*; use super::{strings_to_enumset, HasIcon}; +use crate::data::color::color_from_keywords; use crate::images::Icon; use crate::plugin::Color; @@ -22,10 +23,13 @@ impl PowerType { Icon::Power }; - PowerType { - icon, - color: color_for_tagset(&kywds).unwrap_or_default(), - } + let color = if let Some(c) = color_from_keywords(&tags) { + c + } else { + color_for_tagset(&kywds).unwrap_or_default() + }; + + PowerType { icon, color } } } diff --git a/src/data/spell.rs b/src/data/spell.rs index ef71867b..27c2c004 100644 --- a/src/data/spell.rs +++ b/src/data/spell.rs @@ -11,10 +11,10 @@ use enumset::EnumSet; -use super::color::InvColor; +use super::color::{color_from_keywords, InvColor}; use super::keywords::*; use super::magic::{School, SpellData}; -use super::{strings_to_enumset, strings_to_keywords, HasIcon}; +use super::{strings_to_enumset, HasIcon}; use crate::images::icons::Icon; use crate::plugin::Color; @@ -51,11 +51,10 @@ impl SpellType { // Colors. We base this on damage type, mostly, but first we look to see // if we have a color keyword. - let color_kwds = strings_to_keywords::(&tags); - let color = if let Some(assigned) = color_kwds.first() { - assigned.clone() - } else if let Some(color) = color_for_tagset(&tagset) { - color + let color = if let Some(c) = color_from_keywords(&tags) { + c + } else if let Some(c) = color_for_tagset(&tagset) { + c } else { match data.school { // TODO identify common colors for magical schools