From d54cd08560e54aaffa3cacab0ceab5b1dec7481c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 14:41:08 -0400 Subject: [PATCH] [Spells] Adds a rule to allow right-click memorize from spell scrolls. (#1377) * [Spells] Adds a rule to allow right-click memorize from spell scrolls. * Typo. --- common/ruletypes.h | 1 + zone/client.cpp | 2 +- zone/client.h | 1 + zone/client_packet.cpp | 7 +++- zone/effects.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index da2078434..0bec71b80 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -373,6 +373,7 @@ RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") +RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/client.cpp b/zone/client.cpp index 10c58a265..614917da9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10102,7 +10102,7 @@ std::vector Client::GetScribedSpells() { if (IsValidSpell(m_pp.spell_book[index])) { scribed_spells.push_back(m_pp.spell_book[index]); } - } + } return scribed_spells; } diff --git a/zone/client.h b/zone/client.h index 2c8dc2e2b..c48ea2237 100644 --- a/zone/client.h +++ b/zone/client.h @@ -982,6 +982,7 @@ public: void ResetTrade(); void DropInst(const EQ::ItemInstance* inst); bool TrainDiscipline(uint32 itemid); + bool MemorizeSpellFromItem(uint32 item_id); void TrainDiscBySpellID(int32 spell_id); uint32 GetDisciplineTimer(uint32 timer_id); int GetDiscSlotBySpellID(int32 spellid); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6d277b140..ea7ad932a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8876,7 +8876,12 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } else if (item->ItemType == EQ::item::ItemTypeSpell) { - return; + if (RuleB(Spells, AllowSpellMemorizeFromItem)) { + DeleteItemInInventory(slot_id, 1, true); + MemorizeSpellFromItem(item->ID); + } else { + return; + } } else if ((item->Click.Type == EQ::item::ItemEffectClick) || (item->Click.Type == EQ::item::ItemEffectExpendable) || (item->Click.Type == EQ::item::ItemEffectEquipClick) || (item->Click.Type == EQ::item::ItemEffectClick2)) { diff --git a/zone/effects.cpp b/zone/effects.cpp index da3608416..b68887ad9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -509,6 +509,87 @@ bool Client::TrainDiscipline(uint32 itemid) { return(false); } +bool Client::MemorizeSpellFromItem(uint32 item_id) { + const EQ::ItemData *item = database.GetItem(item_id); + if(item == nullptr) { + Message(Chat::Red, "Unable to find the scroll!"); + LogError("Unable to find scroll id [{}]\n", (unsigned long)item_id); + return false; + } + + if (!item->IsClassCommon() || item->ItemType != EQ::item::ItemTypeSpell) { + Message(Chat::Red, "Invalid item type, you cannot learn from this item."); + SummonItem(item_id); + return false; + } + + if(!( + item->Name[0] == 'S' && + item->Name[1] == 'p' && + item->Name[2] == 'e' && + item->Name[3] == 'l' && + item->Name[4] == 'l' && + item->Name[5] == ':' && + item->Name[6] == ' ' + )) { + Message(Chat::Red, "This item is not a scroll."); + SummonItem(item_id); + return false; + } + int player_class = GetClass(); + uint32 cbit = 1 << (player_class - 1); + if(!(item->Classes & cbit)) { + Message(Chat::Red, "Your class cannot learn from this scroll."); + SummonItem(item_id); + return false; + } + + uint32 spell_id = item->Scroll.Effect; + if(!IsValidSpell(spell_id)) { + Message(Chat::Red, "This scroll contains invalid knowledge."); + return false; + } + + const SPDat_Spell_Struct &spell = spells[spell_id]; + uint8 level_to_use = spell.classes[player_class - 1]; + if(level_to_use == 255) { + Message(Chat::Red, "Your class cannot learn from this scroll."); + SummonItem(item_id); + return false; + } + + if(level_to_use > GetLevel()) { + Message(Chat::Red, "You must be at least level %d to learn this spell.", level_to_use); + SummonItem(item_id); + return false; + } + + for(int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) { + if (!HasSpellScribed(spell_id)) { + auto next_slot = GetNextAvailableSpellBookSlot(); + if (next_slot != -1) { + ScribeSpell(spell_id, next_slot); + return true; + } else { + Message( + Chat::Red, + "Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.", + ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), + spell_id + ); + SummonItem(item_id); + return false; + } + } else { + Message(Chat::Red, "You already know this spell."); + SummonItem(item_id); + return false; + } + } + Message(Chat::Red, "You have learned too many spells and can learn no more."); + return false; +} + void Client::TrainDiscBySpellID(int32 spell_id) { int i;