Implement skeleton for Enchant Confirm Window. Make original ability functions static for player class ability retrieval. Add private static access internal functions. Remove check for non-existing animations for player (would just not change the animation if it doesn't exist). Release Build 11598.

master
sigonasr2 3 months ago
parent 0d7759b230
commit 1e7345d7b5
  1. 40
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 2
      Adventures in Lestoria Tests/GameHelper.h
  3. 24
      Adventures in Lestoria Tests/ItemTests.cpp
  4. 20
      Adventures in Lestoria Tests/PlayerTests.cpp
  5. 3
      Adventures in Lestoria/AdventuresInLestoria.cpp
  6. 34
      Adventures in Lestoria/ArtificerEnchantConfirmWindow.cpp
  7. 50
      Adventures in Lestoria/ArtificerEnchantWindow.cpp
  8. 2
      Adventures in Lestoria/ArtificerRefineWindow.cpp
  9. 1
      Adventures in Lestoria/ArtificerWindow.cpp
  10. 2
      Adventures in Lestoria/CraftItemWindow.cpp
  11. 4
      Adventures in Lestoria/DEFINES.h
  12. 19
      Adventures in Lestoria/Item.cpp
  13. 8
      Adventures in Lestoria/Item.h
  14. 4
      Adventures in Lestoria/Merchant.cpp
  15. 14
      Adventures in Lestoria/Player.cpp
  16. 90
      Adventures in Lestoria/Player.h
  17. 2
      Adventures in Lestoria/Version.h
  18. 12
      Adventures in Lestoria/assets/config/audio/events.txt
  19. BIN
      Adventures in Lestoria/assets/sounds/enchant_item.ogg
  20. BIN
      Adventures in Lestoria/assets/sounds/refine_item.ogg
  21. BIN
      x64/Release/Adventures in Lestoria.exe

@ -118,13 +118,13 @@ namespace EnchantTests
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Aura of the Beast")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Aura of the Beast")};
std::unordered_map<int,uint32_t>statDistribution; std::unordered_map<int,uint32_t>statDistribution;
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Health Boost"); nullRing.lock()->_EnchantItem("Health Boost");
statDistribution[player->GetMaxHealth()]++; statDistribution[player->GetMaxHealth()]++;
} }
Assert::AreEqual(size_t(3),statDistribution.size(),L"There should be three entries generated. If not, then the RNG picking is likely not working!"); Assert::AreEqual(size_t(3),statDistribution.size(),L"There should be three entries generated. If not, then the RNG picking is likely not working!");
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Aura of the Beast",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Aura of the Beast",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Health Boost"); nullRing2.lock()->_EnchantItem("Health Boost");
Test::InRange(player->GetMaxHealth(),{106,110},L"Max Health not in expected range with two rings equipped."); Test::InRange(player->GetMaxHealth(),{106,110},L"Max Health not in expected range with two rings equipped.");
} }
} }
@ -133,12 +133,12 @@ namespace EnchantTests
Assert::AreEqual(100,player->GetAttack(),L"Player starts with 100 attack."); Assert::AreEqual(100,player->GetAttack(),L"Player starts with 100 attack.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Attack Boost")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Attack Boost")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Attack Boost"); nullRing.lock()->_EnchantItem("Attack Boost");
Test::InRange(player->GetAttack(),{103,105},L"Attack not in expected range."); Test::InRange(player->GetAttack(),{103,105},L"Attack not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Attack Boost",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Attack Boost",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Attack Boost"); nullRing2.lock()->_EnchantItem("Attack Boost");
Test::InRange(player->GetAttack(),{106,110},L"Attack not in expected range with two rings equipped."); Test::InRange(player->GetAttack(),{106,110},L"Attack not in expected range with two rings equipped.");
} }
} }
@ -146,12 +146,12 @@ namespace EnchantTests
Assert::AreEqual(100.0_Pct,player->GetMoveSpdMult(),L"Player starts with 100% Movespd."); Assert::AreEqual(100.0_Pct,player->GetMoveSpdMult(),L"Player starts with 100% Movespd.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Movement Boost")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Movement Boost")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Movement Boost"); nullRing.lock()->_EnchantItem("Movement Boost");
Test::InRange(player->GetMoveSpdMult(),{103.0_Pct,105.0_Pct},L"Move Speed not in expected range."); Test::InRange(player->GetMoveSpdMult(),{103.0_Pct,105.0_Pct},L"Move Speed not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Movement Boost",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Movement Boost",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Movement Boost"); nullRing2.lock()->_EnchantItem("Movement Boost");
Test::InRange(player->GetMoveSpdMult(),{106.0_Pct,110.0_Pct},L"Move Speed not in expected range with two rings equipped."); Test::InRange(player->GetMoveSpdMult(),{106.0_Pct,110.0_Pct},L"Move Speed not in expected range with two rings equipped.");
} }
} }
@ -159,12 +159,12 @@ namespace EnchantTests
Assert::AreEqual(0.0_Pct,player->GetCooldownReductionPct(),L"Player starts with 0% CDR."); Assert::AreEqual(0.0_Pct,player->GetCooldownReductionPct(),L"Player starts with 0% CDR.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Ability Haste")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Ability Haste")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Ability Haste"); nullRing.lock()->_EnchantItem("Ability Haste");
Test::InRange(player->GetCooldownReductionPct(),{3.0_Pct,5.0_Pct},L"CDR not in expected range."); Test::InRange(player->GetCooldownReductionPct(),{3.0_Pct,5.0_Pct},L"CDR not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Ability Haste",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Ability Haste",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Ability Haste"); nullRing2.lock()->_EnchantItem("Ability Haste");
Test::InRange(player->GetCooldownReductionPct(),{6.0_Pct,10.0_Pct},L"CDR not in expected range with two rings."); Test::InRange(player->GetCooldownReductionPct(),{6.0_Pct,10.0_Pct},L"CDR not in expected range with two rings.");
} }
} }
@ -172,12 +172,12 @@ namespace EnchantTests
Assert::AreEqual(0.0_Pct,player->GetCritRatePct(),L"Player starts with 0% Crit Rate."); Assert::AreEqual(0.0_Pct,player->GetCritRatePct(),L"Player starts with 0% Crit Rate.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Crit Rate")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Crit Rate")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Crit Rate"); nullRing.lock()->_EnchantItem("Crit Rate");
Test::InRange(player->GetCritRatePct(),{3.0_Pct,5.0_Pct},L"Crit Rate not in expected range."); Test::InRange(player->GetCritRatePct(),{3.0_Pct,5.0_Pct},L"Crit Rate not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Crit Rate",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Crit Rate",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Crit Rate"); nullRing2.lock()->_EnchantItem("Crit Rate");
Test::InRange(player->GetCritRatePct(),{6.0_Pct,10.0_Pct},L"Crit Rate not in expected range with two rings."); Test::InRange(player->GetCritRatePct(),{6.0_Pct,10.0_Pct},L"Crit Rate not in expected range with two rings.");
} }
} }
@ -185,12 +185,12 @@ namespace EnchantTests
Assert::AreEqual(50.0_Pct,player->GetCritDmgPct(),L"Player starts with 50% Crit Damage."); Assert::AreEqual(50.0_Pct,player->GetCritDmgPct(),L"Player starts with 50% Crit Damage.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Crit Damage")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Crit Damage")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Crit Damage"); nullRing.lock()->_EnchantItem("Crit Damage");
Test::InRange(player->GetCritDmgPct(),{57.0_Pct,60.0_Pct},L"Crit Damage not in expected range."); Test::InRange(player->GetCritDmgPct(),{57.0_Pct,60.0_Pct},L"Crit Damage not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Crit Damage",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Crit Damage",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Crit Damage"); nullRing2.lock()->_EnchantItem("Crit Damage");
Test::InRange(player->GetCritDmgPct(),{64.0_Pct,70.0_Pct},L"Crit Damage not in expected range with two rings."); Test::InRange(player->GetCritDmgPct(),{64.0_Pct,70.0_Pct},L"Crit Damage not in expected range with two rings.");
} }
} }
@ -198,12 +198,12 @@ namespace EnchantTests
Assert::AreEqual(0.0_Pct,player->GetDamageReductionFromBuffs(),L"Player starts with 0% Damage Reduction."); Assert::AreEqual(0.0_Pct,player->GetDamageReductionFromBuffs(),L"Player starts with 0% Damage Reduction.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Stoneskin")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Stoneskin")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Stoneskin"); nullRing.lock()->_EnchantItem("Stoneskin");
Test::InRange(player->GetDamageReductionFromBuffs(),{3.0_Pct,5.0_Pct},L"Damage Reduction not in expected range."); Test::InRange(player->GetDamageReductionFromBuffs(),{3.0_Pct,5.0_Pct},L"Damage Reduction not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Stoneskin",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Stoneskin",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Stoneskin"); nullRing2.lock()->_EnchantItem("Stoneskin");
Test::InRange(player->GetDamageReductionFromBuffs(),{6.0_Pct,10.0_Pct},L"Damage Reduction not in expected range with two rings."); Test::InRange(player->GetDamageReductionFromBuffs(),{6.0_Pct,10.0_Pct},L"Damage Reduction not in expected range with two rings.");
} }
} }
@ -211,12 +211,12 @@ namespace EnchantTests
Assert::AreEqual(100,player->GetMaxMana(),L"Player starts with 100 mana."); Assert::AreEqual(100,player->GetMaxMana(),L"Player starts with 100 mana.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Mana Pool")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Mana Pool")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Mana Pool"); nullRing.lock()->_EnchantItem("Mana Pool");
Test::InRange(player->GetMaxMana(),{107,112},L"Mana Pool not in expected range."); Test::InRange(player->GetMaxMana(),{107,112},L"Mana Pool not in expected range.");
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Mana Pool",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Mana Pool",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Mana Pool"); nullRing2.lock()->_EnchantItem("Mana Pool");
Test::InRange(player->GetMaxMana(),{114,124},L"Mana Pool not in expected range with two rings."); Test::InRange(player->GetMaxMana(),{114,124},L"Mana Pool not in expected range with two rings.");
} }
} }
@ -227,7 +227,7 @@ namespace EnchantTests
Assert::AreEqual(0.0_Pct,player->GetHP6RecoveryPct(),L"Player starts with 0% HP/6 recovery."); Assert::AreEqual(0.0_Pct,player->GetHP6RecoveryPct(),L"Player starts with 0% HP/6 recovery.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Magical Protection")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Magical Protection")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Magical Protection"); nullRing.lock()->_EnchantItem("Magical Protection");
Test::InRange(player->GetMaxHealth(),{102,103},L"Max Health not in expected range."); Test::InRange(player->GetMaxHealth(),{102,103},L"Max Health not in expected range.");
Test::InRange(player->GetDamageReductionFromBuffs(),{2.0_Pct,3.0_Pct},L"Damage Reduction not in expected range."); Test::InRange(player->GetDamageReductionFromBuffs(),{2.0_Pct,3.0_Pct},L"Damage Reduction not in expected range.");
Test::InRange(player->GetMoveSpdMult(),{102.0_Pct,103.0_Pct},L"Move Speed % not in expected range."); Test::InRange(player->GetMoveSpdMult(),{102.0_Pct,103.0_Pct},L"Move Speed % not in expected range.");
@ -235,7 +235,7 @@ namespace EnchantTests
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Magical Protection",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Magical Protection",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Magical Protection"); nullRing2.lock()->_EnchantItem("Magical Protection");
Test::InRange(player->GetMaxHealth(),{102,103},L"Max Health not in expected range with two rings."); Test::InRange(player->GetMaxHealth(),{102,103},L"Max Health not in expected range with two rings.");
Test::InRange(player->GetDamageReductionFromBuffs(),{2.0_Pct,3.0_Pct},L"Damage Reduction not in expected range with two rings."); Test::InRange(player->GetDamageReductionFromBuffs(),{2.0_Pct,3.0_Pct},L"Damage Reduction not in expected range with two rings.");
Test::InRange(player->GetMoveSpdMult(),{102.0_Pct,103.0_Pct},L"Move Speed % not in expected range with two rings."); Test::InRange(player->GetMoveSpdMult(),{102.0_Pct,103.0_Pct},L"Move Speed % not in expected range with two rings.");
@ -250,7 +250,7 @@ namespace EnchantTests
Assert::AreEqual(50.0_Pct,player->GetCritDmgPct(),L"Player starts with 50% crit rate."); Assert::AreEqual(50.0_Pct,player->GetCritDmgPct(),L"Player starts with 50% crit rate.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Aura of the Beast")}; std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Aura of the Beast")};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing.lock()->EnchantItem("Aura of the Beast"); nullRing.lock()->_EnchantItem("Aura of the Beast");
Test::InRange(player->GetAttack(),{102,103},L"Attack not in expected range."); Test::InRange(player->GetAttack(),{102,103},L"Attack not in expected range.");
Test::InRange(player->GetCritRatePct(),{2.0_Pct,3.0_Pct},L"Crit Rate not in expected range."); Test::InRange(player->GetCritRatePct(),{2.0_Pct,3.0_Pct},L"Crit Rate not in expected range.");
Test::InRange(player->GetCooldownReductionPct(),{2.0_Pct,3.0_Pct},L"Cooldown Reduction % not in expected range."); Test::InRange(player->GetCooldownReductionPct(),{2.0_Pct,3.0_Pct},L"Cooldown Reduction % not in expected range.");
@ -258,7 +258,7 @@ namespace EnchantTests
} }
std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Aura of the Beast",EquipSlot::RING2)}; std::weak_ptr<Item>nullRing2{Game::GiveAndEquipEnchantedRing("Aura of the Beast",EquipSlot::RING2)};
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
nullRing2.lock()->EnchantItem("Aura of the Beast"); nullRing2.lock()->_EnchantItem("Aura of the Beast");
Test::InRange(player->GetAttack(),{102,103},L"Attack not in expected range with two rings."); Test::InRange(player->GetAttack(),{102,103},L"Attack not in expected range with two rings.");
Test::InRange(player->GetCritRatePct(),{2.0_Pct,3.0_Pct},L"Crit Rate not in expected range with two rings."); Test::InRange(player->GetCritRatePct(),{2.0_Pct,3.0_Pct},L"Crit Rate not in expected range with two rings.");
Test::InRange(player->GetCooldownReductionPct(),{2.0_Pct,3.0_Pct},L"Cooldown Reduction % not in expected range with two rings."); Test::InRange(player->GetCooldownReductionPct(),{2.0_Pct,3.0_Pct},L"Cooldown Reduction % not in expected range with two rings.");

@ -66,7 +66,7 @@ namespace Game{
inline std::weak_ptr<Item>GiveAndEquipEnchantedRing(const std::string_view enchantName,const EquipSlot slot=EquipSlot::RING1){ inline std::weak_ptr<Item>GiveAndEquipEnchantedRing(const std::string_view enchantName,const EquipSlot slot=EquipSlot::RING1){
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)}; std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,slot); Inventory::EquipItem(nullRing,slot);
nullRing.lock()->EnchantItem(enchantName); nullRing.lock()->_EnchantItem(enchantName);
return nullRing; return nullRing;
} }
} }

@ -251,14 +251,14 @@ namespace ItemTests
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)}; std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
Assert::IsFalse(slimeKingRing.lock()->HasEnchant()); Assert::IsFalse(slimeKingRing.lock()->HasEnchant());
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
slimeKingRing.lock()->EnchantItem(ItemEnchant::RollRandomEnchant()); slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant());
Assert::IsTrue(slimeKingRing.lock()->HasEnchant()); Assert::IsTrue(slimeKingRing.lock()->HasEnchant());
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability. if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
} }
testGame->ChangePlayerClass(WIZARD); testGame->ChangePlayerClass(WIZARD);
player=testGame->GetPlayer(); //The player pointer has been reassigned... player=testGame->GetPlayer(); //The player pointer has been reassigned...
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
slimeKingRing.lock()->EnchantItem(ItemEnchant::RollRandomEnchant()); slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant());
Assert::IsTrue(slimeKingRing.lock()->HasEnchant()); Assert::IsTrue(slimeKingRing.lock()->HasEnchant());
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability. if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
} }
@ -277,6 +277,8 @@ namespace ItemTests
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)}; std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,uint32_t>enchantCounts; std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,uint32_t>enchantCounts;
for(int i:std::ranges::iota_view(0,1000)){ for(int i:std::ranges::iota_view(0,1000)){
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
player->AddMoney("Fragment Enchant Cost"_i[1]);
ItemEnchantInfo resultEnchant{extraRing.lock()->ApplyRandomEnchant()}; ItemEnchantInfo resultEnchant{extraRing.lock()->ApplyRandomEnchant()};
if(resultEnchant.GetClass().has_value())Assert::AreEqual(int(resultEnchant.GetClass().value()),int(player->GetClass()),L"Player's class matches the class of the enchant."); if(resultEnchant.GetClass().has_value())Assert::AreEqual(int(resultEnchant.GetClass().value()),int(player->GetClass()),L"Player's class matches the class of the enchant.");
enchantCounts[resultEnchant.Category()]++; enchantCounts[resultEnchant.Category()]++;
@ -296,5 +298,23 @@ namespace ItemTests
Assert::AreEqual("Item Enchants.Class Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Quickdraw").DisplayCol().n,L"Expecting a class enchant to have the class enchant pixel display color."); Assert::AreEqual("Item Enchants.Class Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Quickdraw").DisplayCol().n,L"Expecting a class enchant to have the class enchant pixel display color.");
Assert::AreEqual("Item Enchants.Unique Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Magical Protection").DisplayCol().n,L"Expecting a unique enchant to have the unique enchant pixel display color."); Assert::AreEqual("Item Enchants.Unique Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Magical Protection").DisplayCol().n,L"Expecting a unique enchant to have the unique enchant pixel display color.");
} }
TEST_METHOD(CanBeEnchantedTest){
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the money nor required fragments to enchant this item.");
Assert::AreEqual(false,testArmor.lock()->CanBeEnchanted(),L"We can't enchant armor.");
player->SetMoney(2000U);
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the required fragments to enchant this item.");
player->SetMoney(0U);
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the required money to enchant this item.");
player->SetMoney(2000U);
Assert::AreEqual(true,extraRing.lock()->CanBeEnchanted(),L"We don't have the required money to enchant this item.");
extraRing.lock()->ApplyRandomEnchant();
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"Ring cannot be enchanted again due to consumption of fragments.");
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
Assert::AreEqual(true,extraRing.lock()->CanBeEnchanted(),L"Ring can be enchanted again with the right amount of fragments.");
Assert::AreEqual(uint32_t(2000-"Fragment Enchant Cost"_i[1]),player->GetMoney(),util::wformat("Lost {} money due to enchanting ring.","Fragment Enchant Cost"_i[1]).c_str());
}
}; };
} }

@ -629,17 +629,17 @@ namespace PlayerTests
TEST_METHOD(HasEnchantCheck){ TEST_METHOD(HasEnchantCheck){
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)}; std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1); Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
slimeKingRing.lock()->EnchantItem("Emergency Recovery"); slimeKingRing.lock()->_EnchantItem("Emergency Recovery");
Assert::IsTrue(player->HasEnchant("Emergency Recovery")); Assert::IsTrue(player->HasEnchant("Emergency Recovery"));
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2); Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
Assert::IsTrue(player->HasEnchant("Emergency Recovery")); Assert::IsTrue(player->HasEnchant("Emergency Recovery"));
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1); Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
slimeKingRing.lock()->EnchantItem("Reaper of Souls"); slimeKingRing.lock()->_EnchantItem("Reaper of Souls");
Assert::IsTrue(player->HasEnchant("Reaper of Souls")); Assert::IsTrue(player->HasEnchant("Reaper of Souls"));
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2); Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
Assert::IsTrue(player->HasEnchant("Reaper of Souls")); Assert::IsTrue(player->HasEnchant("Reaper of Souls"));
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1); Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
slimeKingRing.lock()->EnchantItem("Attack Boost"); slimeKingRing.lock()->_EnchantItem("Attack Boost");
Assert::IsTrue(player->HasEnchant("Attack Boost")); Assert::IsTrue(player->HasEnchant("Attack Boost"));
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2); Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
Assert::IsTrue(player->HasEnchant("Attack Boost")); Assert::IsTrue(player->HasEnchant("Attack Boost"));
@ -658,18 +658,18 @@ namespace PlayerTests
Inventory::EquipItem(leatherShoes,EquipSlot::SHOES); Inventory::EquipItem(leatherShoes,EquipSlot::SHOES);
Inventory::EquipItem(woodenSword,EquipSlot::WEAPON); Inventory::EquipItem(woodenSword,EquipSlot::WEAPON);
leatherHelmet.lock()->EnchantItem("Wizard's Soul"); leatherHelmet.lock()->_EnchantItem("Wizard's Soul");
Assert::IsTrue(player->HasEnchant("Wizard's Soul")); Assert::IsTrue(player->HasEnchant("Wizard's Soul"));
leatherArmor.lock()->EnchantItem("Ability Haste"); leatherArmor.lock()->_EnchantItem("Ability Haste");
Assert::IsTrue(player->HasEnchant("Ability Haste")); Assert::IsTrue(player->HasEnchant("Ability Haste"));
leatherPants.lock()->EnchantItem("Improved Ground Slam"); leatherPants.lock()->_EnchantItem("Improved Ground Slam");
Assert::IsTrue(player->HasEnchant("Improved Ground Slam")); Assert::IsTrue(player->HasEnchant("Improved Ground Slam"));
leatherGloves.lock()->EnchantItem("Battle Shout"); leatherGloves.lock()->_EnchantItem("Battle Shout");
Assert::IsTrue(player->HasEnchant("Battle Shout")); Assert::IsTrue(player->HasEnchant("Battle Shout"));
leatherShoes.lock()->EnchantItem("Attack Boost"); leatherShoes.lock()->_EnchantItem("Attack Boost");
Inventory::UnequipItem(EquipSlot::RING2); Inventory::UnequipItem(EquipSlot::RING2);
Assert::IsTrue(player->HasEnchant("Attack Boost")); Assert::IsTrue(player->HasEnchant("Attack Boost"));
woodenSword.lock()->EnchantItem("Mana Pool"); woodenSword.lock()->_EnchantItem("Mana Pool");
Assert::IsTrue(player->HasEnchant("Mana Pool")); Assert::IsTrue(player->HasEnchant("Mana Pool"));
Inventory::UnequipItem(EquipSlot::HELMET); Inventory::UnequipItem(EquipSlot::HELMET);
@ -744,7 +744,7 @@ namespace PlayerTests
player->GetAbility3().cooldown=player->GetAbility3().GetCooldownTime(); player->GetAbility3().cooldown=player->GetAbility3().GetCooldownTime();
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)}; std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1); Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Multi-Multishot"); nullRing.lock()->_EnchantItem("Multi-Multishot");
testKey->bHeld=true; //Force the key to be held down for testing purposes. testKey->bHeld=true; //Force the key to be held down for testing purposes.
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),oldCooldownTime-oldCooldownTime*"Multi-Multishot"_ENC["COOLDOWN REDUCTION PCT"]/100.f,L"Old cooldown time with multishot cooldown reduction pct matches."); Assert::AreEqual(player->GetAbility3().GetCooldownTime(),oldCooldownTime-oldCooldownTime*"Multi-Multishot"_ENC["COOLDOWN REDUCTION PCT"]/100.f,L"Old cooldown time with multishot cooldown reduction pct matches.");
testGame->SetElapsedTime(0.f); testGame->SetElapsedTime(0.f);

@ -436,7 +436,7 @@ bool AiL::OnConsoleCommand(const std::string& sCommand){
if(args[0]=="/accessory"){ if(args[0]=="/accessory"){
if(args.size()<2)ConsoleOut()<<"Usage: /accessory <Accessory Name> [Enchant Name]"<<std::endl; if(args.size()<2)ConsoleOut()<<"Usage: /accessory <Accessory Name> [Enchant Name]"<<std::endl;
std::weak_ptr<Item>accessory{Inventory::AddItem(args[1])}; std::weak_ptr<Item>accessory{Inventory::AddItem(args[1])};
if(args.size()>=3)accessory.lock()->EnchantItem(args[2]); if(args.size()>=3)accessory.lock()->_EnchantItem(args[2]);
ConsoleOut()<<"Added "<<args[1]<<" to player's inventory."<<std::endl; ConsoleOut()<<"Added "<<args[1]<<" to player's inventory."<<std::endl;
}else{ }else{
ConsoleOut()<<"Invalid command! Use /help to see available commands."<<std::endl; ConsoleOut()<<"Invalid command! Use /help to see available commands."<<std::endl;
@ -2988,6 +2988,7 @@ void AiL::ChangePlayerClass(Class cl){
player->OnLevelStart(); player->OnLevelStart();
player->timers=oldTimers; player->timers=oldTimers;
player->shield=previousShield; player->shield=previousShield;
player->SetAbility4(Thief::GetOriginalAbility2());
} }
void AiL::InitializeClasses(){ void AiL::InitializeClasses(){

@ -38,11 +38,41 @@ All rights reserved.
#include "Menu.h" #include "Menu.h"
#include "AdventuresInLestoria.h" #include "AdventuresInLestoria.h"
#include "MenuLabel.h"
#include "MenuIconButton.h"
INCLUDE_game INCLUDE_game
void Menu::InitializeArtificerEnchantConfirmWindow(){ void Menu::InitializeArtificerEnchantConfirmWindow(){
Menu*artificerEnchantConfirmWindow=CreateMenu(ARTIFICER_ENCHANT_CONFIRM,CENTERED,vi2d{144,144}); Menu*artificerEnchantConfirmWindow=CreateMenu(ARTIFICER_ENCHANT_CONFIRM,CENTERED,vi2d{240,144});
auto enchantSuccessLabel{artificerEnchantConfirmWindow->ADD("Enchant Success Label",MenuLabel)(geom2d::rect<float>{{0.f,-14.f},vf2d{artificerEnchantConfirmWindow->size.x,12.f}},"Enchantment Success!",1.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
auto chooseResultLabel{artificerEnchantConfirmWindow->ADD("Choose Result Label",MenuLabel)(geom2d::rect<float>{vf2d{0.f,0.f},vf2d{artificerEnchantConfirmWindow->size.x,12.f}},"Choose a Result",1.f,ComponentAttr::SHADOW)END};
const float oldLabelTextWidth{game->GetTextSize("OLD").x*2.f};
auto oldLabel{artificerEnchantConfirmWindow->ADD("Old Item Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-oldLabelTextWidth/2-4.f,14.f},vf2d{oldLabelTextWidth+8.f,24.f}},"OLD",2.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
const float newLabelTextWidth{game->GetTextSize("NEW").x*2.f};
auto newLabel{artificerEnchantConfirmWindow->ADD("New Item Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+artificerEnchantConfirmWindow->size.x/4.f-oldLabelTextWidth/2+2.f,14.f},vf2d{oldLabelTextWidth+8.f,24.f}},"NEW",2.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
auto oldIcon{artificerEnchantConfirmWindow->ADD("Old Item Icon",MenuIconButton)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-12.f,42.f},{24,24}},nullptr,DO_NOTHING,IconButtonAttr::NOT_SELECTABLE)END};
auto newIcon{artificerEnchantConfirmWindow->ADD("New Item Icon",MenuIconButton)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+artificerEnchantConfirmWindow->size.x/4.f-12.f+4.f,42.f},{24,24}},nullptr,DO_NOTHING,IconButtonAttr::NOT_SELECTABLE)END};
auto oldItemDescription{artificerEnchantConfirmWindow->ADD("Old Item Description",MenuLabel)(geom2d::rect<float>{{0.f,74.f},{artificerEnchantConfirmWindow->size.x/2.f-8.f,60.f}},"",0.5f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
auto newItemDescription{artificerEnchantConfirmWindow->ADD("New Item Description",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+8.f,74.f},{artificerEnchantConfirmWindow->size.x/2.f-8.f,60.f}},"",0.5f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
const float takeOldTextWidth{float(game->GetTextSize("Take Old").x)*2.f};
auto takeOldButton{artificerEnchantConfirmWindow->ADD("Take Old Button",MenuComponent)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-takeOldTextWidth/2.f,138.f},{takeOldTextWidth,20.f}},"Take Old",[](MenuFuncData data){
onClick:
return true;
},vf2d{2.f,2.f})END};
const float takeNewTextWidth{float(game->GetTextSize("Take New").x)*2.f};
auto takeNewButton{artificerEnchantConfirmWindow->ADD("Take New Button",MenuComponent)(geom2d::rect<float>{vf2d{artificerEnchantConfirmWindow->size.x/2.f+8.f+artificerEnchantConfirmWindow->size.x/4.f-takeNewTextWidth/2.f,138.f},{takeNewTextWidth,20.f}},"Take New",[](MenuFuncData data){
onClick:
return true;
},vf2d{2.f,2.f})END};
artificerEnchantConfirmWindow->SetupKeyboardNavigation( artificerEnchantConfirmWindow->SetupKeyboardNavigation(
[](MenuType type,Data&returnData){ //On Open [](MenuType type,Data&returnData){ //On Open
@ -51,7 +81,7 @@ void Menu::InitializeArtificerEnchantConfirmWindow(){
{ //Button Key { //Button Key
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}}, {game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
{game->KEY_BACK,{"Stay",[](MenuType type){ {game->KEY_BACK,{"Stay",[](MenuType type){
Menu::CloseMenu();
}}}, }}},
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}}, {game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
} }

@ -43,6 +43,7 @@ All rights reserved.
#include "MenuLabel.h" #include "MenuLabel.h"
#include "MenuDecal.h" #include "MenuDecal.h"
#include "PlayerMoneyLabel.h" #include "PlayerMoneyLabel.h"
#include "SoundEffect.h"
INCLUDE_game INCLUDE_game
@ -59,18 +60,43 @@ void Menu::InitializeArtificerEnchantWindow(){
auto accessoryDescription{artificerEnchantWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+56.f,16.f},{artificerEnchantWindow->size.x/2-40.f,72.f}},"",0.5f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END}; auto accessoryDescription{artificerEnchantWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+56.f,16.f},{artificerEnchantWindow->size.x/2-40.f,72.f}},"",0.5f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
const auto ResetRefineDisplay{[artificerEnchantWindow](){ const auto ResetEnchantDisplay{[artificerEnchantWindow](){
MenuType menuType{artificerEnchantWindow->GetType()}; MenuType menuType{artificerEnchantWindow->GetType()};
Component<MenuItemItemButton>(menuType,"Item Icon")->SetItem(Item::BLANK); Component<MenuItemItemButton>(menuType,"Item Icon")->SetItem(Item::BLANK);
Component<ScrollableWindowComponent>(menuType,"Enchant Container")->Disable();
Component<MenuLabel>(menuType,"Enchant List Header")->Disable();
Component<MenuLabel>(menuType,"Enchant Cost Label")->Disable();
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Disable();
Component<MenuLabel>(menuType,"Fragment Label")->Disable();
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Disable();
Component<MenuComponent>(menuType,"Fragment Enchant Button")->Disable();
}}; }};
const auto EnableRefineDisplay{[artificerEnchantWindow](){ const auto EnableEnchantDisplay{[artificerEnchantWindow](){
MenuType menuType{artificerEnchantWindow->GetType()}; MenuType menuType{artificerEnchantWindow->GetType()};
const std::weak_ptr<Item>&selectedItem{Component<MenuItemItemButton>(menuType,"Item Icon")->GetItem()}; const std::weak_ptr<Item>&selectedItem{Component<MenuItemItemButton>(menuType,"Item Icon")->GetItem()};
std::vector<ItemEnchantInfo>availableEnchants{ItemEnchant::GetAvailableEnchants()}; std::vector<ItemEnchantInfo>availableEnchants{ItemEnchant::GetAvailableEnchants()};
std::sort(availableEnchants.begin(),availableEnchants.end(),[](const ItemEnchantInfo&enchant1,const ItemEnchantInfo&enchant2){
return enchant1.Category()!=enchant2.Category()?int(enchant1.Category())<int(enchant2.Category()):enchant1.Name()<enchant2.Name();
});
std::string enchantList{std::accumulate(availableEnchants.begin(),availableEnchants.end(),""s,[](const std::string&acc,const ItemEnchantInfo&enchant){ std::string enchantList{std::accumulate(availableEnchants.begin(),availableEnchants.end(),""s,[](const std::string&acc,const ItemEnchantInfo&enchant){
return std::format("{}{}{}#FFFFFF\n",acc,enchant.DisplayCol().toHTMLColorCode(),enchant.Name()); return std::format("{}{}{}#FFFFFF\n",acc,enchant.DisplayCol().toHTMLColorCode(),enchant.Name());
})}; })};
Component<ScrollableWindowComponent>(menuType,"Enchant Container")->Enable();
Component<MenuLabel>(menuType,"Enchant List")->SetLabel(enchantList); Component<MenuLabel>(menuType,"Enchant List")->SetLabel(enchantList);
Component<MenuLabel>(menuType,"Enchant List Header")->Enable();
Component<MenuLabel>(menuType,"Enchant Cost Label")->Enable();
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Enable();
Component<MenuDecal>(menuType,"Fragment Cost Icon")->SetImage(GFX.at(selectedItem.lock()->FragmentIcon().value()));
Component<MenuLabel>(menuType,"Fragment Label")->Enable();
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Enable();
Component<MenuComponent>(menuType,"Fragment Enchant Button")->Enable();
Component<MenuComponent>(menuType,"Fragment Enchant Button")->SetGrayedOut(!selectedItem.lock()->CanBeEnchanted());
const std::string_view fragmentName{selectedItem.lock()->FragmentName()};
const Pixel fragmentItemDisplayCol{Inventory::GetItemCount(fragmentName)>="Fragment Enchant Cost"_i[0]?WHITE:RED};
const Pixel moneyCostDisplayCol{game->GetPlayer()->GetMoney()>="Fragment Enchant Cost"_i[1]?WHITE:RED};
Component<MenuLabel>(menuType,"Fragment Label")->SetLabel(std::format("{}{} x{} ({})",fragmentItemDisplayCol.toHTMLColorCode(),fragmentName,"Fragment Enchant Cost"_i[0],Inventory::GetItemCount(fragmentName)));
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->SetLabel(std::format("{}{} gold",moneyCostDisplayCol.toHTMLColorCode(),"Fragment Enchant Cost"_i[1]));
}}; }};
auto inventoryDisplay{artificerEnchantWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,28.f},{artificerEnchantWindow->size.x/2-4.f,artificerEnchantWindow->size.y-44}},"","",[](MenuFuncData data){ auto inventoryDisplay{artificerEnchantWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,28.f},{artificerEnchantWindow->size.x/2-4.f,artificerEnchantWindow->size.y-44}},"","",[](MenuFuncData data){
@ -78,18 +104,18 @@ void Menu::InitializeArtificerEnchantWindow(){
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)}; RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->SelectChild(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)); DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->SelectChild(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component));
return true; return true;
},[EnableRefineDisplay](MenuFuncData data){OnHover: },[EnableEnchantDisplay](MenuFuncData data){OnHover:
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)}; RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem()); Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
EnableRefineDisplay(); EnableEnchantDisplay();
return true; return true;
},[ResetRefineDisplay,EnableRefineDisplay](MenuFuncData data){OnMouseOut: },[ResetEnchantDisplay,EnableEnchantDisplay](MenuFuncData data){OnMouseOut:
ResetRefineDisplay(); ResetEnchantDisplay();
auto childComponent{DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->GetSelectedChild()}; auto childComponent{DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->GetSelectedChild()};
if(childComponent){ if(childComponent){
RowItemDisplay&item{childComponent.value().get()}; RowItemDisplay&item{childComponent.value().get()};
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem()); Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
EnableRefineDisplay(); EnableEnchantDisplay();
} }
return true; return true;
}, },
@ -107,9 +133,15 @@ void Menu::InitializeArtificerEnchantWindow(){
auto fragmentDisplayLabel{artificerEnchantWindow->ADD("Fragment Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,152.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END}; auto fragmentDisplayLabel{artificerEnchantWindow->ADD("Fragment Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,152.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END};
auto fragmentMoneyCostLabel{artificerEnchantWindow->ADD("Fragment Money Cost Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,164.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END}; auto fragmentMoneyCostLabel{artificerEnchantWindow->ADD("Fragment Money Cost Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,164.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
auto fragmentEnchantButton{artificerEnchantWindow->ADD("Fragment Enchant Button",MenuComponent)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+96.f,180.f},{artificerEnchantWindow->size.x/2-80.f,12.f}},"Enchant",[EnableRefineDisplay](MenuFuncData data){ auto fragmentEnchantButton{artificerEnchantWindow->ADD("Fragment Enchant Button",MenuComponent)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+96.f,180.f},{artificerEnchantWindow->size.x/2-80.f,12.f}},"Enchant",[EnableEnchantDisplay](MenuFuncData data){
onClick: onClick:
EnableRefineDisplay(); //Refresh the current display contents. EnableEnchantDisplay(); //Refresh the current display contents.
SoundEffect::PlaySFX("Enchant Item",SoundEffect::CENTERED);
std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
std::optional<ItemEnchant>previousEnchant{item.lock()->GetEnchant()};
item.lock()->ApplyRandomEnchant();
const ItemEnchant&newEnchant{item.lock()->GetEnchant().value()};
//TODO: Do stuff with this info.
Menu::OpenMenu(ARTIFICER_ENCHANT_CONFIRM,true); Menu::OpenMenu(ARTIFICER_ENCHANT_CONFIRM,true);
return true; return true;
})END}; })END};

@ -124,7 +124,7 @@ void Menu::InitializeArtificerRefineWindow(){
auto fragmentRefineButton{artificerRefineWindow->ADD("Fragment Refine Button",MenuComponent)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+96.f,180.f},{artificerRefineWindow->size.x/2-80.f,12.f}},"Refine",[EnableRefineDisplay](MenuFuncData data){ auto fragmentRefineButton{artificerRefineWindow->ADD("Fragment Refine Button",MenuComponent)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+96.f,180.f},{artificerRefineWindow->size.x/2-80.f,12.f}},"Refine",[EnableRefineDisplay](MenuFuncData data){
onClick: onClick:
SoundEffect::PlaySFX("Sprint",SoundEffect::CENTERED); SoundEffect::PlaySFX("Refine Item",SoundEffect::CENTERED);
std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()}; std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
RefineResult result{item.lock()->Refine()}; RefineResult result{item.lock()->Refine()};
Component<MenuItemItemButton>(ARTIFICER_REFINE_RESULT,"Item Icon")->SetItem(item); Component<MenuItemItemButton>(ARTIFICER_REFINE_RESULT,"Item Icon")->SetItem(item);

@ -62,6 +62,7 @@ void Menu::InitializeArtificerWindow(){
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; },vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END;
artificerWindow->ADD("Enchant Button",MenuComponent)(geom2d::rect<float>{{0.f,60.f},{144.f,24.f}},"Enchant",[](MenuFuncData data){ artificerWindow->ADD("Enchant Button",MenuComponent)(geom2d::rect<float>{{0.f,60.f},{144.f,24.f}},"Enchant",[](MenuFuncData data){
Menu::OpenMenu(MenuType::ARTIFICER_ENCHANT); Menu::OpenMenu(MenuType::ARTIFICER_ENCHANT);
Component<RowInventoryScrollableWindowComponent>(MenuType::ARTIFICER_ENCHANT,"Accessory List")->ClearSelectedChild();
return true; return true;
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; },vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END;
artificerWindow->ADD("Help Button",MenuComponent)(geom2d::rect<float>{{0.f,88.f},{144.f,24.f}},"Help",[](MenuFuncData data){ artificerWindow->ADD("Help Button",MenuComponent)(geom2d::rect<float>{{0.f,88.f},{144.f,24.f}},"Help",[](MenuFuncData data){

@ -70,7 +70,7 @@ void Menu::InitializeCraftItemWindow(){
for(const auto&[name,amt]:consumedResources.GetItems()){ for(const auto&[name,amt]:consumedResources.GetItems()){
Inventory::RemoveItem(Inventory::GetItem(name)[0],amt); Inventory::RemoveItem(Inventory::GetItem(name)[0],amt);
} }
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-consumedResources.GetCost()); game->GetPlayer()->RemoveMoney(consumedResources.GetCost());
Inventory::UpdateBlacksmithInventoryLists(); Inventory::UpdateBlacksmithInventoryLists();
SoundEffect::PlaySFX("Craft Equip",SoundEffect::CENTERED); SoundEffect::PlaySFX("Craft Equip",SoundEffect::CENTERED);

@ -121,6 +121,10 @@ Ability&class::GetAbility1(){return ability1;}; \
Ability&class::GetAbility2(){return ability2;}; \ Ability&class::GetAbility2(){return ability2;}; \
Ability&class::GetAbility3(){return ability3;}; \ Ability&class::GetAbility3(){return ability3;}; \
Ability&class::GetAbility4(){return ability4;}; \ Ability&class::GetAbility4(){return ability4;}; \
Ability&class::_GetOriginalAbility1(){return GetOriginalAbility1();}; \
Ability&class::_GetOriginalAbility2(){return GetOriginalAbility2();}; \
Ability&class::_GetOriginalAbility3(){return GetOriginalAbility2();}; \
Ability&class::_GetOriginalRightClickAbility(){return GetOriginalRightClickAbility();}; \
Ability&class::GetOriginalAbility1(){return original_ability1;}; \ Ability&class::GetOriginalAbility1(){return original_ability1;}; \
Ability&class::GetOriginalAbility2(){return original_ability2;}; \ Ability&class::GetOriginalAbility2(){return original_ability2;}; \
Ability&class::GetOriginalAbility3(){return original_ability3;}; \ Ability&class::GetOriginalAbility3(){return original_ability3;}; \

@ -1034,7 +1034,7 @@ void Item::EnhanceItem(uint8_t qty){
for(const auto&[name,amt]:consumedResources.GetItems()){ for(const auto&[name,amt]:consumedResources.GetItems()){
Inventory::RemoveItem(Inventory::GetItem(name)[0],amt); Inventory::RemoveItem(Inventory::GetItem(name)[0],amt);
} }
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-consumedResources.GetCost()); game->GetPlayer()->RemoveMoney(consumedResources.GetCost());
SoundEffect::PlaySFX("Enhance Item",SoundEffect::CENTERED); SoundEffect::PlaySFX("Enhance Item",SoundEffect::CENTERED);
@ -1047,7 +1047,7 @@ void Item::EnhanceItem(uint8_t qty){
for(const auto&[name,amt]:consumedResources.GetItems()){ for(const auto&[name,amt]:consumedResources.GetItems()){
Inventory::RemoveItem(Inventory::GetItem(name)[0],amt); Inventory::RemoveItem(Inventory::GetItem(name)[0],amt);
} }
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-consumedResources.GetCost()); game->GetPlayer()->RemoveMoney(consumedResources.GetCost());
} }
} }
SoundEffect::PlaySFX("Craft Item",SoundEffect::CENTERED); SoundEffect::PlaySFX("Craft Item",SoundEffect::CENTERED);
@ -1441,10 +1441,14 @@ const bool Item::CanBeRefined()const{
return false; //Maxed out, so cannot be refined. return false; //Maxed out, so cannot be refined.
} }
const bool Item::CanBeEnchanted()const{
return IsAccessory()&&Inventory::GetItemCount(FragmentName())>="Fragment Enchant Cost"_i[0]&&game->GetPlayer()->GetMoney()>="Fragment Enchant Cost"_i[1];
}
RefineResult Item::Refine(){ RefineResult Item::Refine(){
if(!CanBeRefined())ERR("WARNING! Trying to refine an item that cannot be refined! Make sure you are checking with CanBeRefined() before calling this function!"); if(!CanBeRefined())ERR("WARNING! Trying to refine an item that cannot be refined! Make sure you are checking with CanBeRefined() before calling this function!");
Inventory::RemoveItem(FragmentName(),"Fragment Refine Cost"_i[0]); Inventory::RemoveItem(FragmentName(),"Fragment Refine Cost"_i[0]);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-"Fragment Refine Cost"_i[1]); game->GetPlayer()->RemoveMoney("Fragment Refine Cost"_i[1]);
std::vector<ItemAttribute>statsAvailableForRefining; std::vector<ItemAttribute>statsAvailableForRefining;
for(const auto&[attr,val]:randomizedStats){ for(const auto&[attr,val]:randomizedStats){
float maxVal{ITEM_DATA[ActualName()].GetMaxStats().A_Read(attr)}; float maxVal{ITEM_DATA[ActualName()].GetMaxStats().A_Read(attr)};
@ -1461,13 +1465,13 @@ RefineResult Item::Refine(){
return RefineResult{chosenAttr,newAmt-oldAmt}; return RefineResult{chosenAttr,newAmt-oldAmt};
} }
void Item::EnchantItem(const std::string_view enchantName){ void Item::_EnchantItem(const std::string_view enchantName){
enchant=ItemEnchant{enchantName}; enchant=ItemEnchant{enchantName};
game->GetPlayer()->RecalculateEquipStats(); game->GetPlayer()->RecalculateEquipStats();
} }
std::optional<ItemEnchant>Item::GetEnchant()const{ const std::optional<ItemEnchant>&Item::GetEnchant()const{
return enchant; return enchant;
} }
const bool Item::HasEnchant()const{ const bool Item::HasEnchant()const{
@ -1524,7 +1528,10 @@ const std::optional<std::string>&Item::FragmentIcon()const{
} }
const ItemEnchantInfo Item::ApplyRandomEnchant(){ const ItemEnchantInfo Item::ApplyRandomEnchant(){
if(!CanBeEnchanted())ERR("WARNING! Trying to enchant an item that cannot be enchanted! Make sure you are checking with CanBeEnchanted() before calling this function!");
EnchantName randomEnchantName{ItemEnchant::RollRandomEnchant()}; EnchantName randomEnchantName{ItemEnchant::RollRandomEnchant()};
EnchantItem(randomEnchantName); _EnchantItem(randomEnchantName);
Inventory::RemoveItem(FragmentName(),"Fragment Enchant Cost"_i[0]);
game->GetPlayer()->RemoveMoney("Fragment Enchant Cost"_i[1]);
return ItemEnchantInfo::GetEnchant(randomEnchantName); return ItemEnchantInfo::GetEnchant(randomEnchantName);
} }

@ -254,11 +254,15 @@ public:
//Assumes an item can be refined. CHECK WITH CanBeRefined() First!!! //Assumes an item can be refined. CHECK WITH CanBeRefined() First!!!
//Refines a random stat by the parameters defined in the Accessories.txt configuration file. Also takes away the costs required to refine the item. //Refines a random stat by the parameters defined in the Accessories.txt configuration file. Also takes away the costs required to refine the item.
RefineResult Refine(); RefineResult Refine();
const bool CanBeEnchanted()const;
void Lock(); void Lock();
void Unlock(); void Unlock();
void EnchantItem(const std::string_view enchantName); void _EnchantItem(const std::string_view enchantName);
//Applies a random valid enchant to an item and consumes the materials in the process.
//Assumes an item can be enchanted. CHECK WITH CanBeEnchanted() First!!!
//Enchants the item based on its class parameters defined in the ItemEnchants.txt configuration file. Also takes away the costs required to refine the item (Located in Accessories.txt).
const ItemEnchantInfo ApplyRandomEnchant(); const ItemEnchantInfo ApplyRandomEnchant();
std::optional<ItemEnchant>GetEnchant()const; const std::optional<ItemEnchant>&GetEnchant()const;
const bool HasEnchant()const; const bool HasEnchant()const;
friend const bool operator==(std::shared_ptr<Item>lhs,std::shared_ptr<Item>rhs){return lhs->it==rhs->it&&lhs->randomizedStats==rhs->randomizedStats;}; friend const bool operator==(std::shared_ptr<Item>lhs,std::shared_ptr<Item>rhs){return lhs->it==rhs->it&&lhs->randomizedStats==rhs->randomizedStats;};
friend const bool operator==(std::shared_ptr<Item>lhs,const IT&rhs){return lhs->ActualName()==const_cast<IT&>(rhs);}; friend const bool operator==(std::shared_ptr<Item>lhs,const IT&rhs){return lhs->ActualName()==const_cast<IT&>(rhs);};

@ -184,7 +184,7 @@ void Merchant::PurchaseItem(IT item,uint32_t amt){
} }
Inventory::AddItem(item,amt); Inventory::AddItem(item,amt);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost); game->GetPlayer()->RemoveMoney(totalCost);
} }
void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){ void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){
sellFunctionPrimed.Validate(item.lock()->ActualName(),amt); sellFunctionPrimed.Validate(item.lock()->ActualName(),amt);
@ -222,7 +222,7 @@ void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){
std::string itemName=item.lock()->ActualName(); //We need the item name since our reference to the item is about to be deleted. std::string itemName=item.lock()->ActualName(); //We need the item name since our reference to the item is about to be deleted.
Inventory::RemoveItem(item,amt); Inventory::RemoveItem(item,amt);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()+totalCost); game->GetPlayer()->AddMoney(totalCost);
//If we still have some in our inventory, we'll add them back in. //If we still have some in our inventory, we'll add them back in.
if(foundLoadoutSlot!=-1&&Inventory::GetItemCount(itemName)>0){ if(foundLoadoutSlot!=-1&&Inventory::GetItemCount(itemName)>0){

@ -1288,7 +1288,6 @@ void Player::SetAnimationBasedOnTargetingDirection(const std::string_view animat
std::string newAnimState{std::format("{}_{}_{}",Capitalize(GetClassName()),animation_name,facingChar)}; std::string newAnimState{std::format("{}_{}_{}",Capitalize(GetClassName()),animation_name,facingChar)};
if(animation.HasState(newAnimState))UpdateAnimation(newAnimState); if(animation.HasState(newAnimState))UpdateAnimation(newAnimState);
else ERR(std::format("WARNING! Animation {} does not exist on the player! THIS SHOULD NOT BE HAPPENING!",newAnimState));
} }
void Player::ApplyIframes(float duration){ void Player::ApplyIframes(float duration){
@ -1539,12 +1538,12 @@ void Player::RecalculateEquipStats(){
if(GetClass()&WIZARD){ if(GetClass()&WIZARD){
if(HasEnchant("Blink Portal"))GetRightClickAbility().COOLDOWN_TIME="Blink Portal"_ENC["COOLDOWN"]; if(HasEnchant("Blink Portal"))GetRightClickAbility().COOLDOWN_TIME="Blink Portal"_ENC["COOLDOWN"];
if(HasEnchant("Summon Comet")){ if(HasEnchant("Summon Comet")){
float castTimeDiff{GetOriginalAbility3().precastInfo.castTime-"Summon Comet"_ENC["METEOR CAST TIME"]}; float castTimeDiff{_GetOriginalAbility3().precastInfo.castTime-"Summon Comet"_ENC["METEOR CAST TIME"]};
GetAbility3().precastInfo.castTime-=castTimeDiff; GetAbility3().precastInfo.castTime-=castTimeDiff;
GetAbility3().COOLDOWN_TIME+="Summon Comet"_ENC["COOLDOWN REDUCTION"]; //This is not a typo, we add because the cooldown reduction in the config is NEGATIVE! GetAbility3().COOLDOWN_TIME+="Summon Comet"_ENC["COOLDOWN REDUCTION"]; //This is not a typo, we add because the cooldown reduction in the config is NEGATIVE!
} }
if(HasEnchant("Solar Flare")){ if(HasEnchant("Solar Flare")){
float castTimeDiff{GetOriginalAbility3().precastInfo.castTime-"Solar Flare"_ENC["METEOR CAST TIME"]}; float castTimeDiff{_GetOriginalAbility3().precastInfo.castTime-"Solar Flare"_ENC["METEOR CAST TIME"]};
GetAbility3().precastInfo.castTime-=castTimeDiff; GetAbility3().precastInfo.castTime-=castTimeDiff;
GetAbility3().COOLDOWN_TIME+="Solar Flare"_ENC["COOLDOWN INCREASE"]; GetAbility3().COOLDOWN_TIME+="Solar Flare"_ENC["COOLDOWN INCREASE"];
} }
@ -2291,3 +2290,12 @@ void Player::SetTestScreenAimingLocation(vf2d forcedAimingLoc){
void Player::SetAbility4(const Ability&ability){ void Player::SetAbility4(const Ability&ability){
} }
void Player::RemoveMoney(const uint32_t moneyCost){
if(moneyCost>GetMoney())ERR(std::format("WARNING! Trying to spend more money than we actually have! THIS SHOULD NOT BE HAPPENING! Current Money: {} Trying to spend: {}",GetMoney(),moneyCost));
SetMoney(GetMoney()-moneyCost);
}
void Player::AddMoney(const uint32_t moneyCost){
SetMoney(GetMoney()+moneyCost);
}

@ -231,10 +231,6 @@ public:
virtual Ability&GetAbility3()=0; virtual Ability&GetAbility3()=0;
virtual Ability&GetAbility4()=0; virtual Ability&GetAbility4()=0;
virtual void SetAbility4(const Ability&originalAbility)=0; //NOTE: Make sure to provide the original ability and not a current ability! virtual void SetAbility4(const Ability&originalAbility)=0; //NOTE: Make sure to provide the original ability and not a current ability!
virtual Ability&GetOriginalAbility1()=0;
virtual Ability&GetOriginalAbility2()=0;
virtual Ability&GetOriginalAbility3()=0;
virtual Ability&GetOriginalRightClickAbility()=0;
virtual std::string&GetWalkNAnimation()=0; virtual std::string&GetWalkNAnimation()=0;
virtual std::string&GetWalkEAnimation()=0; virtual std::string&GetWalkEAnimation()=0;
virtual std::string&GetWalkSAnimation()=0; virtual std::string&GetWalkSAnimation()=0;
@ -268,6 +264,8 @@ public:
static void AddMoneyListener(std::weak_ptr<MenuComponent>component); static void AddMoneyListener(std::weak_ptr<MenuComponent>component);
uint32_t GetMoney()const; uint32_t GetMoney()const;
void SetMoney(uint32_t newMoney); void SetMoney(uint32_t newMoney);
void AddMoney(const uint32_t moneyCost);
void RemoveMoney(const uint32_t moneyCost);
void AddXP(const uint64_t xpGain); void AddXP(const uint64_t xpGain);
void OnLevelUp(); void OnLevelUp();
const uint8_t LevelCap()const; const uint8_t LevelCap()const;
@ -485,6 +483,10 @@ protected:
vf2d leapStartingPos{}; vf2d leapStartingPos{};
float poisonArrowReadyTimer{}; float poisonArrowReadyTimer{};
void ActivateCatForm(); void ActivateCatForm();
virtual Ability&_GetOriginalAbility1()=0;
virtual Ability&_GetOriginalAbility2()=0;
virtual Ability&_GetOriginalAbility3()=0;
virtual Ability&_GetOriginalRightClickAbility()=0;
}; };
#pragma region Warrior #pragma region Warrior
@ -507,10 +509,14 @@ struct Warrior:Player{
Ability&GetAbility2()override; Ability&GetAbility2()override;
Ability&GetAbility3()override; Ability&GetAbility3()override;
Ability&GetAbility4()override; Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;
@ -547,10 +553,14 @@ struct Thief:Player{
Ability&GetAbility2()override; Ability&GetAbility2()override;
Ability&GetAbility3()override; Ability&GetAbility3()override;
Ability&GetAbility4()override; Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;
@ -587,10 +597,14 @@ struct Ranger:Player{
Ability&GetAbility2()override; Ability&GetAbility2()override;
Ability&GetAbility3()override; Ability&GetAbility3()override;
Ability&GetAbility4()override; Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;
@ -627,10 +641,14 @@ struct Trapper:Player{
Ability&GetAbility2()override; Ability&GetAbility2()override;
Ability&GetAbility3()override; Ability&GetAbility3()override;
Ability&GetAbility4()override; Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;
@ -663,14 +681,18 @@ struct Wizard:Player{
static void InitializeClassAbilities(); static void InitializeClassAbilities();
const std::string&GetClassName()override; const std::string&GetClassName()override;
Ability&GetRightClickAbility()override; Ability&GetRightClickAbility()override;
Ability&GetAbility1()override; Ability&GetAbility1();
Ability&GetAbility2()override; Ability&GetAbility2();
Ability&GetAbility3()override; Ability&GetAbility3();
Ability&GetAbility4()override; Ability&GetAbility4();
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;
@ -707,10 +729,14 @@ struct Witch:Player{
Ability&GetAbility2()override; Ability&GetAbility2()override;
Ability&GetAbility3()override; Ability&GetAbility3()override;
Ability&GetAbility4()override; Ability&GetAbility4()override;
Ability&GetOriginalAbility1()override; virtual Ability&_GetOriginalAbility1()override;
Ability&GetOriginalAbility2()override; virtual Ability&_GetOriginalAbility2()override;
Ability&GetOriginalAbility3()override; virtual Ability&_GetOriginalAbility3()override;
Ability&GetOriginalRightClickAbility()override; virtual Ability&_GetOriginalRightClickAbility()override;
static Ability&GetOriginalAbility1();
static Ability&GetOriginalAbility2();
static Ability&GetOriginalAbility3();
static Ability&GetOriginalRightClickAbility();
void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability! void SetAbility4(const Ability&originalAbility)override; //NOTE: Make sure to provide the original ability and not a current ability!
std::string&GetWalkNAnimation()override; std::string&GetWalkNAnimation()override;
std::string&GetWalkEAnimation()override; std::string&GetWalkEAnimation()override;

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 3 #define VERSION_MINOR 3
#define VERSION_PATCH 0 #define VERSION_PATCH 0
#define VERSION_BUILD 11564 #define VERSION_BUILD 11598
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -137,6 +137,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = disassemble.ogg, 70% File[0] = disassemble.ogg, 70%
} }
Enchant Item
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = enchant_item.ogg, 100%
}
Equip Armor Equip Armor
{ {
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
@ -323,6 +329,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = ranger_charged_shot.ogg, 70% File[0] = ranger_charged_shot.ogg, 70%
} }
Refine Item
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = refine_item.ogg, 60%, 110%, 130%
}
Rise Rise
{ {
CombatSound = True CombatSound = True

Loading…
Cancel
Save