diff --git a/TemplePlus/critter.cpp b/TemplePlus/critter.cpp index ac1a2e956..19322e1cd 100644 --- a/TemplePlus/critter.cpp +++ b/TemplePlus/critter.cpp @@ -248,8 +248,8 @@ uint32_t LegacyCritterSystem::HasMet(objHndl critter, objHndl otherCritter) { return addresses.HasMet(critter, otherCritter); } -uint32_t LegacyCritterSystem::AddFollower(objHndl npc, objHndl pc, int unkFlag, bool asAiFollower) { - return addresses.AddFollower(npc, pc, unkFlag, asAiFollower); +uint32_t LegacyCritterSystem::AddFollower(objHndl npc, objHndl pc, int forcedFollower, bool asAiFollower) { + return addresses.AddFollower(npc, pc, forcedFollower, asAiFollower); } bool LegacyCritterSystem::FollowerAtMax(){ diff --git a/TemplePlus/fixes/generalfixes.cpp b/TemplePlus/fixes/generalfixes.cpp index 0c09cd6df..4b34982f9 100644 --- a/TemplePlus/fixes/generalfixes.cpp +++ b/TemplePlus/fixes/generalfixes.cpp @@ -23,14 +23,19 @@ struct TigTextStyle; -// fixes size_colossal (was misspelled as size_clossal) -class SizeColossalFix : public TempleFix { + +class HexFixes : public TempleFix { public: void apply() override { + + // fixes size_colossal (was misspelled as size_clossal) writeHex(0x10278078, "73 69 7A 65 5F 63 6F 6C 6F 73 73 61 6C"); + + // fixes Orb of Golden Death infinite Earth Elementals + writeHex(0x101034D9, "C1 E0 10"); // was 08, a wrong shift (looks like a typo error, all the others are shifted by 0x10: radMenuEntry.d20ActionData1 = (invIdxFromCondArg2 << 16) | [spellIdx] } -} sizeColossalFix; +} hexFixes; // Makes Kukri Proficiency Martial class KukriFix : public TempleFix { diff --git a/TemplePlus/inventory.cpp b/TemplePlus/inventory.cpp index ab7fb4263..b1eaf2b91 100644 --- a/TemplePlus/inventory.cpp +++ b/TemplePlus/inventory.cpp @@ -411,6 +411,7 @@ objHndl InventorySystem::FindMatchingStackableItem(objHndl receiver, objHndl ite auto itemSpell = itemObj->GetSpell(obj_f_item_spell_idx, 0); auto invenItemSpell = invenItemObj->GetSpell(obj_f_item_spell_idx, 0); if (itemSpell.spellLevel != invenItemSpell.spellLevel + || itemSpell.spellEnum != invenItemSpell.spellEnum // Temple+: added this for automated scribed scrolls (who all use the same proto) || (itemObj->type == obj_t_scroll && spellSys.IsArcaneSpellClass(itemSpell.classCode) != spellSys.IsArcaneSpellClass(invenItemSpell.classCode) )) diff --git a/TemplePlus/spell.cpp b/TemplePlus/spell.cpp index 783b7d763..bfcd8dcb2 100644 --- a/TemplePlus/spell.cpp +++ b/TemplePlus/spell.cpp @@ -867,7 +867,7 @@ uint32_t LegacySpellSystem::spellRegistryCopy(uint32_t spellEnum, SpellEntry* sp return spellEntryRegistry.copy(spellEnum, spellEntry); } -void LegacySpellSystem::DoForSpellEntries(void(*cb)(SpellEntry & spellEntry)){ +void LegacySpellSystem::DoForSpellEntries( const std::function &cb){ for (auto it : spellEntryRegistry) { cb(*it.data); } @@ -882,10 +882,8 @@ int LegacySpellSystem::CopyLearnableSpells(objHndl& handle, int spellClass, std: for (auto it : spellEntryRegistry) { auto spEntry = it.data; - // new spells from supplemental materials are generally added above enum 802 in Temple+ - if (spEntry->spellEnum > SPELL_ENUM_MAX_VANILLA) { - if (!config.nonCoreMaterials) - continue; + if (IsNonCore(spEntry->spellEnum) && !config.nonCoreMaterials) { + continue; } if (GetSpellLevelBySpellClass(spEntry->spellEnum, spellClass) >= 0) { entries.push_back(*spEntry); @@ -1414,7 +1412,7 @@ void LegacySpellSystem::SpellPacketSetCasterLevel(SpellPacketBody* spellPkt) con } // item spell - else if (spellPkt->invIdx != 255 && (spellPkt->spellEnum < NORMAL_SPELL_RANGE || spellPkt->spellEnum > SPELL_LIKE_ABILITY_RANGE)){ + else if (spellPkt->invIdx != 255 && !IsMonsterSpell(spellPkt->spellEnum)){ spellPkt->casterLevel = 0; logger->info("Critter {} is casting item spell {} at base caster_level {}.", casterName, spellName, 0); } @@ -1436,7 +1434,7 @@ void LegacySpellSystem::SpellPacketSetCasterLevel(SpellPacketBody* spellPkt) con } else{ // domain spell if (spellPkt->spellClass == Domain_Special){ // domain special (usually used for monsters) - if (spellPkt->invIdx != 255 && (spellPkt->spellEnum < NORMAL_SPELL_RANGE || spellPkt->spellEnum > SPELL_LIKE_ABILITY_RANGE)) { + if (spellPkt->invIdx != 255 && !IsMonsterSpell(spellPkt->spellEnum)) { spellPkt->casterLevel = 0; logger->info("Critter {} is casting item spell {} at base caster_level {}.", casterName, spellName, 0); } @@ -2902,6 +2900,12 @@ int LegacySpellSystem::GetSpellSchool(int spellEnum){ return spEntry.spellSchoolEnum; } +bool LegacySpellSystem::IsMonsterSpell(int spellEnum) +{ + return spellEnum >= NORMAL_SPELL_RANGE + && spellEnum <= SPELL_LIKE_ABILITY_RANGE; +} + bool LegacySpellSystem::IsSpellLike(int spellEnum){ return (spellEnum >= NORMAL_SPELL_RANGE && spellEnum <= SPELL_LIKE_ABILITY_RANGE) || spellEnum >= CLASS_SPELL_LIKE_ABILITY_START; @@ -2922,6 +2926,12 @@ bool LegacySpellSystem::IsNewSlotDesignator(int spellEnum) return false; } +// Non-Core spells will be added in the expanded range +bool LegacySpellSystem::IsNonCore(int spellEnum) +{ + return (spellEnum > SPELL_ENUM_MAX_VANILLA); +} + int LegacySpellSystem::GetSpellLevelBySpellClass(int spellEnum, int spellClass, objHndl handle){ if (IsLabel(spellEnum)) diff --git a/TemplePlus/spell.h b/TemplePlus/spell.h index 68e15c787..944c89485 100644 --- a/TemplePlus/spell.h +++ b/TemplePlus/spell.h @@ -170,7 +170,7 @@ struct LegacySpellSystem : temple::AddressTable uint32_t spellRegistryCopy(uint32_t spellEnum, SpellEntry* spellEntry); - void DoForSpellEntries( void(__cdecl*cb)(SpellEntry & spellEntry)); + void DoForSpellEntries( const std::function &cb); int CopyLearnableSpells(objHndl & handle, int spellClass, std::vector & entries); uint32_t ConfigSpellTargetting(PickerArgs* pickerArgs, SpellPacketBody* spellPacketBody); @@ -224,10 +224,11 @@ struct LegacySpellSystem : temple::AddressTable These are not interruptible via Ready vs. Spell, and do not automatically show up in the console as [ACTOR] casts [SPELL]! */ + static bool IsMonsterSpell(int spellEnum); static bool IsSpellLike(int spellEnum); static bool IsLabel(int spellEnum); // check if it is a hardcoded "label" enum (used in the GUI etc) static bool IsNewSlotDesignator(int spellEnum); // check if it is a hardcoded "new slot" designator (used for sorting) enums 1605-1614 - + static bool IsNonCore(int spellEnum); int GetSpellLevelBySpellClass(int spellEnum, int spellClass, objHndl handle = objHndl::null); // returns -1 if not available for spell class bool SpellHasMultiSelection(int spellEnum); diff --git a/TemplePlus/spell_condition.cpp b/TemplePlus/spell_condition.cpp index 13b09df6f..41b0be5bb 100644 --- a/TemplePlus/spell_condition.cpp +++ b/TemplePlus/spell_condition.cpp @@ -20,6 +20,9 @@ #include "float_line.h" #include "action_sequence.h" #include "ai.h" +#include +#include +#include void PyPerformTouchAttack_PatchedCallToHitProcessing(D20Actn * pd20A, D20Actn d20A, uint32_t savedesi, uint32_t retaddr, PyObject * pyObjCaller, PyObject * pyTupleArgs); @@ -76,8 +79,13 @@ class SpellConditionFixes : public TempleFix { static int SpellResistance_SpellResistanceMod(DispatcherCallbackArgs args); + static int SuggestionOnAdd(DispatcherCallbackArgs args); + void apply() override { + // Fix for when summoned Balor from skull casts suggestion + replaceFunction(0x100D01D0, SuggestionOnAdd); + // Magic Circle Taking Damage - didn't check that attacker is not null replaceFunction(0x100C8D60, MagicCirclePreventDamage); @@ -1468,3 +1476,31 @@ int SpellConditionFixes::SpellResistance_SpellResistanceMod(DispatcherCallbackAr return 0; } +int SpellConditionFixes::SuggestionOnAdd(DispatcherCallbackArgs args) +{ + auto spellId = args.GetCondArg(0); + auto duration = args.GetCondArg(1); + auto arg2 = args.GetCondArg(2); // is 0... + + if (!conds.AddTo(args.objHndCaller, "Charmed", { spellId, duration, arg2 })) { + logger->error("d20_mods_spells.c / _begin_spell_suggestion(): unable to add condition"); + } + floatSys.FloatSpellLine(args.objHndCaller, 20018, FloatLineColor::Red); // Charmed! + auto partyLeader = party.GetConsciousPartyLeader(); + SpellPacketBody pkt(spellId); + if (party.IsInParty(pkt.caster)) { + if (party.ObjIsAIFollower(pkt.caster)) { + critterSys.AddFollower(args.objHndCaller, /*partyLeader*/ pkt.caster, 1, 1); + } + else { + critterSys.AddFollower(args.objHndCaller, pkt.caster, 1, 0); + uiSystems->GetParty().Update(); + } + } + else { + critterSys.AddFollower(args.objHndCaller, pkt.caster, 1, 1); + } + args.SetCondArg(2, 1); + return 0; +} + diff --git a/TemplePlus/ui/ui_item_creation.cpp b/TemplePlus/ui/ui_item_creation.cpp index 3ad381d11..75cf05d53 100644 --- a/TemplePlus/ui/ui_item_creation.cpp +++ b/TemplePlus/ui/ui_item_creation.cpp @@ -259,6 +259,8 @@ UiItemCreation::UiItemCreation(const UiSystemConf &config) { memset(craftedItemHandles, 0, sizeof(craftedItemHandles)); craftedItemNamePos = 0; craftingWidgetId = -1; + scribeScrollSpells.clear(); + mScribedScrollSpell = 0; LoadMaaSpecs(); @@ -408,7 +410,7 @@ int UiItemCreation::CraftedWandCasterLevel(objHndl item) return casterLvl; } -bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem){ +bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem, int spellEnum){ bool canCraft = 1; bool xpCheck = 0; auto insuffXp = itemCreationAddresses.craftInsufficientXP; @@ -416,10 +418,10 @@ bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem auto insuffSkill = itemCreationAddresses.craftSkillReqNotMet; auto insuffPrereqs = itemCreationAddresses.insuffPrereqs; auto surplusXP = d20LevelSys.GetSurplusXp(crafter); - uint32_t craftingCostCP; + uint32_t craftingCostCP = 0; auto partyMoney = party.GetMoney(); - auto itemObj = objSystem->GetObject(objHndItem); + *insuffXp = 0; *insuffCp = 0; @@ -427,28 +429,31 @@ bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem *insuffPrereqs = 0; // Check GP Section - int itemWorth = itemObj->GetInt32(obj_f_item_worth); - - // Scrolls + int itemWorth = 0; if (itemCreationType == ItemCreationType::ScribeScroll){ - itemWorth = ScribedScrollWorth(objHndItem, ScribedScrollCasterLevel(objHndItem)); - craftingCostCP = itemWorth / 2; - } - // MAA - else if (itemCreationType == ItemCreationType::CraftMagicArmsAndArmor){ - craftingCostCP = MaaCpCost( CRAFT_EFFECT_INVALID ); - } - // Wands & Potions - else if (itemCreationType == ItemCreationType::CraftWand){ - itemWorth = CraftedWandWorth(objHndItem, CraftedWandCasterLevel(objHndItem)); //ItemWorthAdjustedForCasterLevel(objHndItem, CraftedWandCasterLevel(objHndItem)); + itemWorth = ScribedScrollWorth(spellEnum, ScribedScrollCasterLevel(spellEnum)); craftingCostCP = itemWorth / 2; } - // Potions else { - // current method for crafting stuff: - craftingCostCP = itemWorth / 2; - }; + auto itemObj = objSystem->GetObject(objHndItem); + itemWorth = itemObj->GetInt32(obj_f_item_worth); + // MAA + if (itemCreationType == ItemCreationType::CraftMagicArmsAndArmor) { + craftingCostCP = MaaCpCost(CRAFT_EFFECT_INVALID); + } + // Wands & Potions + else if (itemCreationType == ItemCreationType::CraftWand) { + itemWorth = CraftedWandWorth(objHndItem, CraftedWandCasterLevel(objHndItem)); //ItemWorthAdjustedForCasterLevel(objHndItem, CraftedWandCasterLevel(objHndItem)); + craftingCostCP = itemWorth / 2; + } + // Potions + else { + // current method for crafting stuff: + craftingCostCP = itemWorth / 2; + }; + } + if ( ( (uint32_t)partyMoney ) < craftingCostCP){ *insuffCp = 1; canCraft = 0; @@ -458,63 +463,27 @@ bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem // Check XP & prerequisites section // Scrolls, Wands and Potions: - if ( itemCreationType != CraftMagicArmsAndArmor){ - // check requirements from rules\\item_creation.mes - if (!ItemCreationParseMesfileEntry(crafter, objHndItem)){ + if (itemCreationType == ItemCreationType::ScribeScroll) { + if (!ScribeScrollCheck(crafter, spellEnum)) { *insuffPrereqs = 1; canCraft = 0; } - - // scrolls - ensure caster can also actually cast the spell - else if (itemCreationType == ItemCreationType::ScribeScroll ) { - - auto spData = itemObj->GetSpell(obj_f_item_spell_idx, 0); // scrolls should only have a single spell... - auto spEnum = spData.spellEnum; - - std::vector spellClasses, spellLevels; - - auto isLevelOk = false; - - if (!spellSys.SpellKnownQueryGetData(crafter, spEnum, spellClasses, spellLevels)) - *insuffPrereqs = 1; - - for (auto i=0u; i < spellLevels.size(); i++){ - auto maxSpellLvl = -1; - auto spClass = spellClasses[i]; - if (spellSys.isDomainSpell(spClass)) - { - maxSpellLvl = spellSys.GetMaxSpellLevel(crafter, stat_level_cleric); - - } else - { - maxSpellLvl = spellSys.GetMaxSpellLevel(crafter, spellSys.GetCastingClass(spClass)); - } - - - if (maxSpellLvl >= spellLevels[i]) - { - isLevelOk = true; - break; - } - } - - if (!isLevelOk){ - *insuffPrereqs = 1; - canCraft = 0; - } - - } - - // check XP + // check XP int itemXPCost = itemWorth / 2500; xpCheck = surplusXP >= itemXPCost; - } - // MAA - else { + } + else if (itemCreationType == CraftMagicArmsAndArmor) { int magicArmsAndArmorXPCost = MaaXpCost(CRAFT_EFFECT_INVALID); xpCheck = surplusXP >= magicArmsAndArmorXPCost; } + else { + // check requirements from rules\\item_creation.mes + if (!ItemCreationParseMesfileEntry(crafter, objHndItem)){ + *insuffPrereqs = 1; + canCraft = 0; + } + } if (xpCheck){ return canCraft; @@ -526,6 +495,30 @@ bool UiItemCreation::CreateItemResourceCheck(objHndl crafter, objHndl objHndItem } +bool UiItemCreation::ScribeScrollCheck(objHndl crafter, int spEnum) +{ + std::vector spellClasses, spellLevels; + if (!spellSys.SpellKnownQueryGetData(crafter, spEnum, spellClasses, spellLevels)) + return false; + + for (auto i = 0u; i < spellLevels.size(); i++) { + auto maxSpellLvl = -1; + auto spClass = spellClasses[i]; + if (spellSys.isDomainSpell(spClass)) { + maxSpellLvl = spellSys.GetMaxSpellLevel(crafter, stat_level_cleric); + } + else { + maxSpellLvl = spellSys.GetMaxSpellLevel(crafter, spellSys.GetCastingClass(spClass)); + } + + + if (maxSpellLvl >= spellLevels[i]) { + return true; + } + } + return false; +} + const char* UiItemCreation::GetItemCreationMesLine(int lineId){ MesLine line; line.key = lineId; @@ -953,15 +946,35 @@ void UiItemCreation::CraftScrollWandPotionSetItemSpellData(objHndl objHndItem, o return; } + if (itemCreationType == ScribeScroll){ auto scrollSpell = obj->GetSpell(obj_f_item_spell_idx, 0); - ScribedScrollSpellGet(objHndItem, scrollSpell); + ScribedScrollSpellGet(mScribedScrollSpell, scrollSpell); obj->SetSpell(obj_f_item_spell_idx, 0, scrollSpell); + auto invAid = GetScrollInventoryIconId(mScribedScrollSpell); + obj->SetInt32(obj_f_item_inv_aid, invAid); + + + auto baseDescr = description.GetDescriptionString(obj->GetInt32(obj_f_description) ); + auto spellName = spellSys.GetSpellName(mScribedScrollSpell); + int SPELL_ENUM_AID = 1; + auto aidSpellName = spellSys.GetSpellName(SPELL_ENUM_AID); + auto pos = std::strstr(baseDescr, aidSpellName); + auto endPos = pos + std::strlen(aidSpellName); + char newName[1024] = {0,}; + auto idx = 0; + for (idx = 0; baseDescr + idx < pos; ++idx) { + newName[idx] = baseDescr[idx]; + } + for (auto i=0; i < strlen(spellName); ++i) { + newName[idx+i] = spellName[i]; + } - /*int casterLevelFinal = scrollSpell.spellLevel * 2 - 1; - if (casterLevelFinal < 1) - casterLevelFinal = 1;*/ - + for (auto i = 0; endPos + i < baseDescr + strlen(baseDescr); ++i) { + newName[idx + strlen(spellName) + i] = endPos[i]; + } + auto newNameId = description.CustomNameNew(newName); + obj->SetInt32(obj_f_description, newNameId); return; }; @@ -1013,7 +1026,7 @@ void UiItemCreation::CraftScrollWandPotionSetItemSpellData(objHndl objHndItem, o }; -void UiItemCreation::CreateItemDebitXPGP(objHndl crafter, objHndl objHndItem){ +void UiItemCreation::CreateItemDebitXPGP(objHndl crafter, objHndl objHndItem, int spellEnum){ uint32_t crafterXP = objects.getInt32(crafter, obj_f_critter_experience); uint32_t craftingCostCP = 0; uint32_t craftingCostXP = 0; @@ -1028,7 +1041,7 @@ void UiItemCreation::CreateItemDebitXPGP(objHndl crafter, objHndl objHndItem){ if (itemCreationType == ItemCreationType::CraftWand) itemWorth = CraftedWandWorth(objHndItem, CraftedWandCasterLevel(objHndItem)); else if (itemCreationType == ItemCreationType::ScribeScroll){ - itemWorth = ScribedScrollWorth(objHndItem, ScribedScrollCasterLevel(objHndItem)); + itemWorth = ScribedScrollWorth(spellEnum, ScribedScrollCasterLevel(spellEnum)); } else itemWorth = objects.getInt32(objHndItem, obj_f_item_worth); @@ -1121,7 +1134,7 @@ bool UiItemCreation::CraftedWandSpellGet(objHndl item, SpellStoreData & spellDat } -void UiItemCreation::ItemCreationCraftingCostTexts(int widgetId, objHndl objHndItem){ +void UiItemCreation::ItemCreationCraftingCostTexts(int widgetId, objHndl objHndItem, int spellEnum){ // prolog int32_t * insuffXp; int32_t * insuffCp; @@ -1132,19 +1145,23 @@ void UiItemCreation::ItemCreationCraftingCostTexts(int widgetId, objHndl objHndI TigRect rect(212 + 108 * mUseCo8Ui, 157, 159, 10); char * prereqString; - int casterLevelNew = -1; // h4x! - auto obj = objSystem->GetObject(objHndItem); - auto itemWorth = obj->GetInt32(obj_f_item_worth); + int casterLevelNew = -1; + auto itemWorth = 0; - if (itemCreationType == CraftWand){ + if (itemCreationType == ScribeScroll) { + casterLevelNew = ScribedScrollCasterLevel(spellEnum); + itemWorth = ScribedScrollWorth(spellEnum, casterLevelNew); + } + else if (itemCreationType == CraftWand){ casterLevelNew = CraftedWandCasterLevel(objHndItem); itemWorth = CraftedWandWorth(objHndItem, casterLevelNew); } - else if (itemCreationType == ScribeScroll){ - casterLevelNew = ScribedScrollCasterLevel(objHndItem); - itemWorth = ScribedScrollWorth(objHndItem, casterLevelNew); + else { + auto obj = objSystem->GetObject(objHndItem); + itemWorth = obj->GetInt32(obj_f_item_worth); } + insuffXp = itemCreationAddresses.craftInsufficientXP; insuffCp = itemCreationAddresses.craftInsufficientFunds; @@ -1205,11 +1222,58 @@ void UiItemCreation::ItemCreationCraftingCostTexts(int widgetId, objHndl objHndI rect.y = 200; rect.width = 150; rect.height = 105; - prereqString = temple::GetRef(0x101525B0)(itemCreationCrafter, objHndItem); - if (prereqString){ - UiRenderer::DrawTextInWidget(widgetId, prereqString, rect, *itemCreationAddresses.itemCreationTextStyle); + if (GetItemCreationType() == ItemCreationType::ScribeScroll) { + std::string tmp; + + auto prereqStr = PrintPrereqToken("S"); + auto prereqMet = spellSys.IsSpellKnown(itemCreationCrafter, spellEnum); + + auto minCasterLevel = 0; + auto clPrereqMet = false; + if (prereqMet) { + SpellStoreData spellData; + ScribedScrollSpellGet(spellEnum, spellData); + + auto castingClass = !spellSys.isDomainSpell(spellData.classCode) ? spellSys.GetCastingClass(spellData.classCode) : stat_level_cleric; + minCasterLevel = d20ClassSys.GetMinCasterLevelForSpellLevel(castingClass, spellData.spellLevel); + clPrereqMet = critterSys.GetCasterLevelForClass(itemCreationCrafter, castingClass) >= minCasterLevel; + + } + + auto clPrereqStr = PrintPrereqToken( fmt::format("C{}", minCasterLevel).c_str() ); + + + static auto prereqFieldLabel = temple::GetRef(0x10BED98C); + if (*itemCreationAddresses.craftInsufficientXP + || *itemCreationAddresses.craftInsufficientFunds + || *itemCreationAddresses.craftSkillReqNotMet + || *itemCreationAddresses.insuffPrereqs) { + + //_snprintf(tmp, 2000, "@0%s @%d%s", prereqFieldLabel, prereqMet ? 1 : 2, prereqStr); + tmp.append(fmt::format("@0{} @{:d}{}\n", prereqFieldLabel, prereqMet ? 1 : 2, prereqStr)); + if (prereqMet) { + tmp.append(fmt::format("@0{} @{:d}{}\n", prereqFieldLabel, clPrereqMet ? 1 : 2, clPrereqStr)); + } + } + else { + //_snprintf(tmp, 2000, "@0%s @3%s", prereqFieldLabel, prereqStr); + tmp.append(fmt::format("@0{} @3{}\n", prereqFieldLabel, prereqStr)); + if (prereqMet) { + tmp.append(fmt::format("@0{} @3{}\n", prereqFieldLabel, clPrereqStr)); + } + } + if (tmp.size()) { + UiRenderer::DrawTextInWidget(widgetId, tmp, rect, *itemCreationAddresses.itemCreationTextStyle); + } + } + else { + prereqString = temple::GetRef(0x101525B0)(itemCreationCrafter, objHndItem); + if (prereqString) { + UiRenderer::DrawTextInWidget(widgetId, prereqString, rect, *itemCreationAddresses.itemCreationTextStyle); + } } + if (!*insuffPrereq && (itemCreationType == ItemCreationType::CraftWand || itemCreationType == ItemCreationType::ScribeScroll)) { @@ -1252,7 +1316,11 @@ BOOL UiItemCreation::ItemCreationEntryMsg(int widId, TigMsg* msg){ craftingItemIdx = itemIdx; auto itemHandle = craftedItemHandles[itemCreationType][itemIdx]; - if (CreateItemResourceCheck(itemCreationCrafter, itemHandle)) { + mScribedScrollSpell = 0; + if (itemCreationType == ItemCreationType::ScribeScroll) { + mScribedScrollSpell = scribeScrollSpells[itemIdx]; + } + if (CreateItemResourceCheck(itemCreationCrafter, itemHandle, mScribedScrollSpell)) { uiManager->SetButtonState(mItemCreationCreateBtnId, LgcyButtonState::Normal); } else { @@ -1576,15 +1644,11 @@ uint32_t UiItemCreation::CraftedWandWorth(objHndl item, int casterLevelNew){ } -bool UiItemCreation::ScribedScrollSpellGet(objHndl item, SpellStoreData & spellDataOut, int * spellLevelBaseOut){ - if (!item) - return false; - auto obj = objSystem->GetObject(item); - if (!obj->GetSpellArray(obj_f_item_spell_idx).GetSize()) - return false; - +bool UiItemCreation::ScribedScrollSpellGet(int spellEnum, SpellStoreData & spellDataOut, int * spellLevelBaseOut){ + // get spell - auto spellData = obj->GetSpell(obj_f_item_spell_idx, 0); + SpellStoreData spellData; + spellData.spellEnum = spellEnum; // default values (shouldn't really be used...) int spellLevelBasic = 999; @@ -1655,15 +1719,15 @@ bool UiItemCreation::ScribedScrollSpellGet(objHndl item, SpellStoreData & spellD return true; } -int UiItemCreation::ScribedScrollSpellLevel(objHndl item) +int UiItemCreation::ScribedScrollSpellLevel(int spellEnum) { SpellStoreData scrollSpell; - if (!ScribedScrollSpellGet(item, scrollSpell)) + if (!ScribedScrollSpellGet(spellEnum, scrollSpell)) scrollSpell.spellLevel = -1; return scrollSpell.spellLevel; } -int UiItemCreation::ScribedScrollCasterLevel(objHndl item) +int UiItemCreation::ScribedScrollCasterLevel(int spellEnum) { // int result = ScribedScrollSpellLevel(item); /*if (result <= 1) @@ -1672,7 +1736,7 @@ int UiItemCreation::ScribedScrollCasterLevel(objHndl item) */ SpellStoreData scrollSpell; - if (!ScribedScrollSpellGet(item, scrollSpell)) + if (!ScribedScrollSpellGet(spellEnum, scrollSpell)) scrollSpell.spellLevel = -1; if (scrollSpell.spellLevel <= 1) return 1; @@ -1691,21 +1755,18 @@ int UiItemCreation::ScribedScrollCasterLevel(objHndl item) return casterLvl; } -uint32_t UiItemCreation::ScribedScrollWorth(objHndl item, int casterLevelNew) +uint32_t UiItemCreation::ScribedScrollWorth(int spellEnum, int casterLevelNew) { auto baseWorth = 25; - auto obj = objSystem->GetObject(item); - - // which spell? - auto spellData = obj->GetSpell(obj_f_item_spell_idx, 0); - // Calculate cost - SpellEntry spEntry(spellData.spellEnum); + // Calculate cost + SpellEntry spEntry(spellEnum); int materialCost = spEntry.costGp; // retrieve Spell Known data (e.g. for Bards) and caster level (as modified by user selection) - int spellLevelBase = spellData.spellLevel; // default value - ScribedScrollSpellGet(item, spellData, &spellLevelBase); + int spellLevelBase = 0; // default value + SpellStoreData spellData; + ScribedScrollSpellGet(spellEnum, spellData, &spellLevelBase); auto casterLevelBase = max(1,spellLevelBase * 2 - 1); auto casterClass = (Stat)spellSys.GetCastingClass(spellData.classCode); auto minCasterLevel = (int)d20ClassSys.GetMinCasterLevelForSpellLevel(casterClass, spellLevelBase); @@ -1822,7 +1883,7 @@ UiItemCreation::UiItemCreation(){ memset(craftedItemHandles, 0, sizeof(craftedItemHandles)); craftedItemNamePos = 0; craftingWidgetId = -1; - + mScribedScrollSpell = 0; } int UiItemCreation::GetItemCreationType(){ @@ -1998,8 +2059,14 @@ std::string UiItemCreation::PrintPrereqToken(const char * reqTxt) result = fmt::format("{}", d20Stats.GetRaceName(d20RaceSys.GetRaceEnum(reqTxt + 1)) ); break; case 'S': + { + if (itemCreationType == ItemCreationType::ScribeScroll) { + result = spellSys.GetSpellName(mScribedScrollSpell); + return result; + } result = fmt::format("{}", spellSys.GetSpellName(spellSys.GetSpellEnum(reqTxt + 1))); break; + } case 'O': { StringTokenizer tok(reqTxt + 1); @@ -2025,6 +2092,35 @@ std::string UiItemCreation::PrintPrereqToken(const char * reqTxt) return result; } +int UiItemCreation::GetScrollInventoryIconId(int spellEnum) +{ + SpellEntry entry(mScribedScrollSpell); + if (!entry.spellEnum) { + return 0; + } + + switch ((SpellSchools)entry.spellSchoolEnum) { + case SpellSchools::School_Abjuration: + return 384; + case SpellSchools::School_Conjuration: + return 379; + case SpellSchools::School_Divination: + return 386; + case SpellSchools::School_Enchantment: + return 383; + case SpellSchools::School_Evocation: + return 154; + case SpellSchools::School_Illusion: + return 381; + case SpellSchools::School_Necromancy: + return 380; + case SpellSchools::School_Transmutation: + return 382; + default: + return 154; + } +} + ItemEnhancementSpec::ItemEnhancementSpec(const std::string &condName, uint32_t Flags, int EffcBonus, int enhBonus) :condName(condName),flags(Flags),effectiveBonus(EffcBonus){ @@ -2196,18 +2292,27 @@ void UiItemCreation::ItemCreationWndRender(int widId){ // draw info pertaining to selected item if (craftingItemIdx >= 0 && (uint32_t) craftingItemIdx < numItemsCrafting[itemCreationType]) { - auto itemHandle = craftedItemHandles[itemCreationType][craftingItemIdx]; - + objHndl itemHandle = craftedItemHandles[itemCreationType][craftingItemIdx]; + const char* itemName = nullptr; + auto invAid = 0; + if (itemCreationType == ItemCreationType::ScribeScroll) { + itemName = spellSys.GetSpellName(mScribedScrollSpell); + invAid = GetScrollInventoryIconId(mScribedScrollSpell); + } + else { + itemName = ItemCreationGetItemName(itemHandle); + invAid = gameSystems->GetObj().GetObject(itemHandle)->GetInt32(obj_f_item_inv_aid); + } + // draw icon - auto invAid = (UiGenericAsset)gameSystems->GetObj().GetObject(itemHandle)->GetInt32(obj_f_item_inv_aid); int textureId; - uiAssets->GetAsset(UiAssetType::Inventory, invAid, textureId); + uiAssets->GetAsset(UiAssetType::Inventory, (UiGenericAsset)invAid, textureId); rect = TigRect(temple::GetRef(0x102FAEC4)); rect.x += 108 * mUseCo8Ui; UiRenderer::DrawTexture(textureId, rect); - auto itemName = ItemCreationGetItemName(itemHandle); + measText = UiRenderer::MeasureTextSize(itemName, temple::GetRef(0x10BED938)); if (measText.width > 161) { measText.width = 161; @@ -2215,7 +2320,7 @@ void UiItemCreation::ItemCreationWndRender(int widId){ rect = TigRect((161 - measText.width )/2 + 208 + 108 * mUseCo8Ui, 132, 161, 24); UiRenderer::DrawTextInWidget(widId, itemName, rect, temple::GetRef(0x10BEDFE8)); - ItemCreationCraftingCostTexts(widId, itemHandle); + ItemCreationCraftingCostTexts(widId, itemHandle, mScribedScrollSpell); } @@ -2243,21 +2348,30 @@ void UiItemCreation::ItemCreationEntryRender(int widId){ UiRenderer::PushFont(PredefinedFont::ARIAL_10); - auto itemHandle = craftedItemHandles[itemCreationType][itemIdx]; - auto itemName = ItemCreationGetItemName(itemHandle); + // special case for scrolls + const char* text = nullptr; + if (itemCreationType == ItemCreationType::ScribeScroll) { + auto spellEnum = scribeScrollSpells[itemIdx]; + text = spellSys.GetSpellName(spellEnum); + } + else { + auto itemHandle = craftedItemHandles[itemCreationType][itemIdx]; + text = ItemCreationGetItemName(itemHandle); + } + TigRect rect(32, 12 * widIdx + 55, 155 + mUseCo8Ui * 108, 12); auto checkRes = itemCreationResourceCheckResults[itemIdx]; if (itemIdx == craftingItemIdx) { if (checkRes) - UiRenderer::DrawTextInWidget(mItemCreationWndId, itemName, rect, temple::GetRef(0x10BEDFE8)); + UiRenderer::DrawTextInWidget(mItemCreationWndId, text, rect, temple::GetRef(0x10BEDFE8)); else - UiRenderer::DrawTextInWidget(mItemCreationWndId, itemName, rect, temple::GetRef(0x10BECE90)); + UiRenderer::DrawTextInWidget(mItemCreationWndId, text, rect, temple::GetRef(0x10BECE90)); } else { if (checkRes) - UiRenderer::DrawTextInWidget(mItemCreationWndId, itemName, rect, temple::GetRef(0x10BED938)); + UiRenderer::DrawTextInWidget(mItemCreationWndId, text, rect, temple::GetRef(0x10BED938)); else - UiRenderer::DrawTextInWidget(mItemCreationWndId, itemName, rect, temple::GetRef(0x10BED6D8)); + UiRenderer::DrawTextInWidget(mItemCreationWndId, text, rect, temple::GetRef(0x10BED6D8)); } UiRenderer::PopFont(); @@ -2492,23 +2606,38 @@ void UiItemCreation::MaaEnhBonusUpRender(int widId){ void UiItemCreation::ButtonStateInit(int wndId){ uiManager->SetHidden(wndId, false); *mItemCreationWnd = *uiManager->GetWindow(wndId); - itemCreationResourceCheckResults = new bool[numItemsCrafting[itemCreationType]]; - for (int i = 0; i < (int)numItemsCrafting[itemCreationType];i++) - { - auto itemHandle = craftedItemHandles[itemCreationType][i]; - if (itemHandle) { - itemCreationResourceCheckResults[i] = CreateItemResourceCheck(itemCreationCrafter, itemHandle); + + auto itemCount = numItemsCrafting[itemCreationType]; + + itemCreationResourceCheckResults = new bool[itemCount]; + if (itemCreationType == ItemCreationType::ScribeScroll) { + for (int i = 0u; i < itemCount; ++i) { + auto spellEnum = scribeScrollSpells[i]; + itemCreationResourceCheckResults[i] = CreateItemResourceCheck(itemCreationCrafter, objHndl::null, spellEnum); } } - - if (craftingItemIdx >= 0 && craftingItemIdx < (int)numItemsCrafting[itemCreationType]){ - if (CreateItemResourceCheck(itemCreationCrafter, craftedItemHandles[itemCreationType][craftingItemIdx])) + else { + for (int i = 0u; i < itemCount; i++) + { + auto itemHandle = craftedItemHandles[itemCreationType][i]; + if (itemHandle) { + itemCreationResourceCheckResults[i] = CreateItemResourceCheck(itemCreationCrafter, itemHandle); + } + } + } + + mScribedScrollSpell = 0; + if (craftingItemIdx >= 0 && craftingItemIdx < itemCount){ + if (itemCreationType == ItemCreationType::ScribeScroll) { + mScribedScrollSpell = scribeScrollSpells[craftingItemIdx]; + } + if (CreateItemResourceCheck(itemCreationCrafter, craftedItemHandles[itemCreationType][craftingItemIdx], mScribedScrollSpell)) uiManager->SetButtonState(mItemCreationCreateBtnId, LgcyButtonState::Normal); else uiManager->SetButtonState(mItemCreationCreateBtnId, LgcyButtonState::Disabled); } - uiManager->ScrollbarSetYmax(mItemCreationScrollbarId, numItemsCrafting[itemCreationType] - NUM_DISPLAYED_CRAFTABLE_ITEMS_MAX < 0 ? 0 : numItemsCrafting[itemCreationType] - NUM_DISPLAYED_CRAFTABLE_ITEMS_MAX); + uiManager->ScrollbarSetYmax(mItemCreationScrollbarId, itemCount - NUM_DISPLAYED_CRAFTABLE_ITEMS_MAX < 0 ? 0 : itemCount - NUM_DISPLAYED_CRAFTABLE_ITEMS_MAX); uiManager->ScrollbarSetY(mItemCreationScrollbarId, 0); mItemCreationScrollbarY = 0; uiManager->BringToFront(wndId); @@ -2661,9 +2790,9 @@ BOOL UiItemCreation::CreateBtnMsg(int widId, TigMsg* msg) itemHandle = craftedItemHandles[itemCreationType][craftingItemIdx]; } - if ( CreateItemResourceCheck(itemCreationCrafter, itemHandle) ) + if ( CreateItemResourceCheck(itemCreationCrafter, itemHandle, mScribedScrollSpell) ) { - CreateItemDebitXPGP(itemCreationCrafter, itemHandle); + CreateItemDebitXPGP(itemCreationCrafter, itemHandle, mScribedScrollSpell); CreateItemFinalize(itemCreationCrafter, itemHandle); } } @@ -2849,28 +2978,32 @@ void UiItemCreation::CreateItemFinalize(objHndl crafter, objHndl item){ //infrastructure::gKeyboard.Update(); // if ALT is pressed, keep the window open for more crafting! - //auto& itemCreationResourceCheckResults = temple::GetRef(0x10BEE330); if (altPressed) { // refresh the resource checks - auto numItemsCrafting = temple::GetRef(0x11E76C7C); // array containing number of protos - static auto craftingHandles = temple::GetRef(0x11E76C3C); // proto handles - - for (int i = 0; i < numItemsCrafting[itemCreationType]; i++) { - auto protoHandle = craftingHandles[itemCreationType][i]; - if (protoHandle) - itemCreationResourceCheckResults[i] = CreateItemResourceCheck(crafter, protoHandle); + if (itemCreationType == ItemCreationType::ScribeScroll) { + for (int i = 0; i < numItemsCrafting[itemCreationType]; i++) { + auto protoHandle = craftedItemHandles[itemCreationType][i]; + auto spellEnum = scribeScrollSpells[i]; + if (protoHandle) { + itemCreationResourceCheckResults[i] = CreateItemResourceCheck(crafter, protoHandle, spellEnum); + } + } } - - auto icItemIdx = temple::GetRef(0x10BEE398); - if (icItemIdx >= 0 && icItemIdx < numItemsCrafting[itemCreationType]) { + else { + for (int i = 0; i < numItemsCrafting[itemCreationType]; i++) { + auto protoHandle = craftedItemHandles[itemCreationType][i]; + if (protoHandle) + itemCreationResourceCheckResults[i] = CreateItemResourceCheck(crafter, protoHandle); + } + } + + if (craftingItemIdx >= 0 && craftingItemIdx < numItemsCrafting[itemCreationType]) { auto createBtnId = mItemCreationCreateBtnId; //temple::GetRef(0x10BED8B0); - if (CreateItemResourceCheck(crafter, item)) - { + if (CreateItemResourceCheck(crafter, item, mScribedScrollSpell)) { uiManager->SetButtonState(createBtnId, LgcyButtonState::Normal); } - else - { + else { uiManager->SetButtonState(createBtnId, LgcyButtonState::Disabled); } } @@ -3825,43 +3958,109 @@ bool UiItemCreation::InitItemCreationRules(){ for (auto i = (int)ItemCreationType::IC_Alchemy; i < ItemCreationType::Inactive; i++){ if (i == ItemCreationType::CraftMagicArmsAndArmor){ mMaaCraftableItemList.clear(); + continue; } - else + if (i == ItemCreationType::ScribeScroll) { // special handling now + continue; + } + + MesLine line(i); + mesFuncs.GetLine_Safe(icrules, &line); + numItemsCrafting[i] = 0; { - MesLine line(i); - mesFuncs.GetLine_Safe(icrules, &line); - numItemsCrafting[i] = 0; - { - StringTokenizer craftableTok(line.value); - while (craftableTok.next()) { - if (craftableTok.token().type == StringTokenType::Number) - ++numItemsCrafting[i]; - } + StringTokenizer craftableTok(line.value); + while (craftableTok.next()) { + if (craftableTok.token().type == StringTokenType::Number) + ++numItemsCrafting[i]; } - craftedItemHandles[i] = new objHndl[numItemsCrafting[i]+1]; - memset(craftedItemHandles[i], 0, sizeof(objHndl) *(numItemsCrafting[i] + 1)); - if (numItemsCrafting[i] > 0){ - int j = 0; - StringTokenizer craftableTok(line.value); - while (craftableTok.next()) { - if (craftableTok.token().type == StringTokenType::Number){ - auto protoHandle = gameSystems->GetObj().GetProtoHandle(craftableTok.token().numberInt); - if (protoHandle) - craftedItemHandles[i][j++] = protoHandle; - } - + } + craftedItemHandles[i] = new objHndl[numItemsCrafting[i]+1]; + memset(craftedItemHandles[i], 0, sizeof(objHndl) *(numItemsCrafting[i] + 1)); + if (numItemsCrafting[i] > 0){ + int j = 0; + StringTokenizer craftableTok(line.value); + while (craftableTok.next()) { + if (craftableTok.token().type == StringTokenType::Number){ + auto protoHandle = gameSystems->GetObj().GetProtoHandle(craftableTok.token().numberInt); + if (protoHandle) + craftedItemHandles[i][j++] = protoHandle; } - numItemsCrafting[i] = j; // in case there are invalid protos - std::sort(craftedItemHandles[i], &craftedItemHandles[i][numItemsCrafting[i]], - [this](objHndl first, objHndl second){ - auto firstName = ItemCreationGetItemName(first); - auto secondName = ItemCreationGetItemName(second); - auto comRes = _stricmp(firstName, secondName); - return comRes < 0; - }); + } - + numItemsCrafting[i] = j; // in case there are invalid protos + std::sort(craftedItemHandles[i], &craftedItemHandles[i][numItemsCrafting[i]], + [this](objHndl first, objHndl second){ + auto firstName = ItemCreationGetItemName(first); + auto secondName = ItemCreationGetItemName(second); + auto comRes = _stricmp(firstName, secondName); + return comRes < 0; + }); + } + + } + // Automated scribe scroll entries: + { + int i = (int)ItemCreationType::ScribeScroll; + numItemsCrafting[i] = 0; + spellSys.DoForSpellEntries( [&](SpellEntry& entry)->void { + auto spellEnum = entry.spellEnum; + if (spellSys.IsNonCore(spellEnum) && !config.nonCoreMaterials) { + return; + } + if (spellSys.IsMonsterSpell(spellEnum) || spellSys.IsSpellLike(spellEnum)) { + return; + } + // Todo: warlock... + if (spellEnum >= 2300 && spellEnum <= 2400) { + return; + } + if (!entry.spellLvlsNum) { // some non-spells in the 700 range are like that + return; + } + if (spellEnum >= 700 && spellEnum <= 802) { // special case some snowflakes grrr + // 733 Scorching Ray, 740 Ray of Clumsiness, 775 Resonance, 794, 795, 796, 797, 798, 799 - vigor spells, scintillating sphere, etc + switch (spellEnum) { + case 733: + case 740: + case 775: + case 794: + case 795: + case 796: + case 797: + case 798: + case 799: + break; + default: + return; + } + } + scribeScrollSpells.push_back(spellEnum); + numItemsCrafting[i]++; + }); + std::sort(scribeScrollSpells.begin(), scribeScrollSpells.end(), + [this](int first, int second) { + auto firstName = spellSys.GetSpellName(first); + auto secondName = spellSys.GetSpellName(second); + auto comRes = _stricmp(firstName, secondName); + return comRes < 0; + }); + craftedItemHandles[i] = new objHndl[numItemsCrafting[i] + 1]; + memset(craftedItemHandles[i], 0, sizeof(objHndl) * (numItemsCrafting[i] + 1)); + + // Use Aid scroll as prototype + auto SCROLL_OF_AID_PROTO = 9002, SPELL_AID_ENUM = 1; + auto scrollProtoHandle = gameSystems->GetObj().GetProtoHandle(SCROLL_OF_AID_PROTO); + if (!scrollProtoHandle) { + throw TempleException("InitItemCreationRules: Cannot Find aid scroll proto!"); + } + auto &spell = objSystem->GetObject(scrollProtoHandle)->GetSpell(obj_f_item_spell_idx, 0); + if (spell.spellEnum != SPELL_AID_ENUM) { + throw TempleException("InitItemCreationRules: Unexpected scroll proto - expected aid spell scroll!"); + } + // just fill it with the same handle + for (auto j = 0u; j < numItemsCrafting[i]; ++j) { + craftedItemHandles[i][j] = scrollProtoHandle; } } diff --git a/TemplePlus/ui/ui_item_creation.h b/TemplePlus/ui/ui_item_creation.h index 6bca5202d..62d39edc5 100644 --- a/TemplePlus/ui/ui_item_creation.h +++ b/TemplePlus/ui/ui_item_creation.h @@ -93,7 +93,7 @@ class UiItemCreation : public UiSystem { void ItemCreationWndRender(int widId); void ItemCreationEntryRender(int widId); - void ItemCreationCraftingCostTexts(int widId, objHndl objHndItem); + void ItemCreationCraftingCostTexts(int widId, objHndl objHndItem, int spellEnum); BOOL ItemCreationEntryMsg(int widId, TigMsg* msg); void ItemCreationCreateBtnRender(int widId) const; void ItemCreationCancelBtnRender(int widId) const; @@ -156,21 +156,22 @@ class UiItemCreation : public UiSystem { int MaaCpCost(int appliedEffectIdx); int MaaXpCost(int effIdx); // calculates XP cost for crafting effects in MAA - bool CreateItemResourceCheck(objHndl crafter, objHndl item); + bool CreateItemResourceCheck(objHndl crafter, objHndl item, int spellEnum = 0); + bool ScribeScrollCheck(objHndl crafter, int spellEnum); const char* GetItemCreationMesLine(int lineId); char const* ItemCreationGetItemName(objHndl itemHandle) const; objHndl MaaGetItemHandle(); - void CreateItemDebitXPGP(objHndl crafter, objHndl item); + void CreateItemDebitXPGP(objHndl crafter, objHndl item, int spellEnum = 0); bool CraftedWandSpellGet(objHndl item, SpellStoreData& spellData, int * spellLevelBase = nullptr); int CraftedWandSpellLevel(objHndl item); int CraftedWandCasterLevel(objHndl item); uint32_t ItemWorthAdjustedForCasterLevel(objHndl objHndItem, uint32_t casterLevelNew); uint32_t CraftedWandWorth(objHndl item, int casterLevelNew); - bool ScribedScrollSpellGet(objHndl item, SpellStoreData& spellData, int * spellLevelBase = nullptr); - int ScribedScrollSpellLevel(objHndl item); - int ScribedScrollCasterLevel(objHndl item); - uint32_t ScribedScrollWorth(objHndl item, int casterLevelNew); + bool ScribedScrollSpellGet(int spellEnum, SpellStoreData& spellData, int * spellLevelBase = nullptr); + int ScribedScrollSpellLevel(int spellEnum); + int ScribedScrollCasterLevel(int spellEnum); + uint32_t ScribedScrollWorth(int spellEnum, int casterLevelNew); int GetMaxSpellLevelFromCasterLevel(int cl); bool IsWeaponBonus(int effIdx); @@ -211,9 +212,12 @@ class UiItemCreation : public UiSystem { bool ItemCreationRulesParseReqText(objHndl crafter, const char* reqTxt); std::string PrintPrereqToken(const char* str); + int GetScrollInventoryIconId(int spellEnum); + int mItemCreationType = 9; objHndl mItemCreationCrafter; int mCraftingItemIdx = -1; + int mScribedScrollSpell = 0; int craftingWidgetId; // denotes which item creation widget is currently active @@ -270,7 +274,7 @@ class UiItemCreation : public UiSystem { int& maaSelectedEffIdx = mMaaSelectedEffIdx; // currently selected craftable effect - bool* itemCreationResourceCheckResults = nullptr; + bool* itemCreationResourceCheckResults = nullptr; // 0x10BEE330 std::vector mMaaCraftableItemList; // handles to enchantable inventory items std::vector appliedBonusIndices; @@ -280,8 +284,10 @@ class UiItemCreation : public UiSystem { int craftedItemExtraGold; // stores the crafted item existing (pre-crafting) extra gold cost int& craftingItemIdx = mCraftingItemIdx; //temple::GetRef(0x10BEE398); - uint32_t numItemsCrafting[8]; // for each Item Creation type - objHndl* craftedItemHandles[8]; // proto handles for new items, and item to modifyt for MAA + uint32_t numItemsCrafting[8]; // for each Item Creation type (0x11E76C7C) + objHndl* craftedItemHandles[8]; // proto handles for new items, and item to modify for MAA (0x11E76C7C) + std::vector scribeScrollSpells; // New: special casing for automated scroll scribing + int& itemCreationType = mItemCreationType; objHndl& itemCreationCrafter = mItemCreationCrafter;//temple::GetRef(0x10BECEE0); std::string craftedItemName; diff --git a/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab b/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab index e7d7f078c..9fd44ae70 100644 --- a/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab +++ b/tpdatasrc/tpgamefiles/mes/help/spell_compendium_help.tab @@ -1,5 +1,5 @@ TAG_WEAPON_SHIELD_PROFICIENCY TAG_COMBAT Weapon, Armor, and Shield Proficiency A character who uses a weapon with which he or she is not proficient takes a -4 penalty on ~attack rolls~[TAG_ATTACK_ROLL]. A character who wears armor and/or uses a shield with which he or she is not proficient takes the armor's (and/or shield's) armor check penalty on attack rolls and on all Strength-based and Dexterity-based ability and skill checks. The penalty for nonproficiency with armor stacks with the penalty for nonproficiency with shields. Weapon, armor, or shield proficiency may be granted by the character's race, class or by the following feats: ~Armor Proficiency (Light)~[TAG_ARMOR_LIGHT] ~Armor Proficiency (Medium)~[TAG_ARMOR_MEDIUM] ~Armor Proficiency (Heavy)~[TAG_ARMOR_HEAVY] ~Exotic Weapon Proficiency~[TAG_EXOTIC_PROF] ~Martial Weapon Proficiency~[TAG_MARTIAL_PROF] ~Simple Weapon Proficiency~[TAG_SIMPLE_PROF] ~Shield Proficiency~[TAG_SHIELD_PROF] Note: I am unsure about the state of simple weapon prof and shield prof and need to verify the help text as they differ from actual D&D. -TAG_SPELLS_ACID_FOG TAG_SPELLS TAG_SORCERER_ TAG_WIZARD_6 TAG_WATER_D Acid Fog ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Creation)[Acid] Level: ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 6, ~Domain: Water~[TAG_WATER_D] 7 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Medium (100 ft. + 10 ft./level) Area: 20-ft. radius Duration: 1 round/level Saving Throw: None Spell Resistance: No Acid fog creates a billowing mass of misty vapors similar to that produced by a ~solid fog~[TAG_SPELLS_SOLID_FOG] spell. In addition to slowing creatures down and obscuring sight, this spell's vapors are highly acidic. Each round on your turn, starting when you cast the spell, the fog deals 2d6 points of acid damage to each creature and object within it. +TAG_SPELLS_ACID_FOG TAG_SPELLS TAG_SORCERER_6 TAG_WIZARD_6 TAG_WATER_D Acid Fog ~Conjuration~[TAG_MAGIC_SCHOOLS_CONJURATION](Creation)[Acid] Level: ~Sorcerer~[TAG_SORCERERS]/~Wizard~[TAG_WIZARDS] 6, ~Domain: Water~[TAG_WATER_D] 7 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Medium (100 ft. + 10 ft./level) Area: 20-ft. radius Duration: 1 round/level Saving Throw: None Spell Resistance: No Acid fog creates a billowing mass of misty vapors similar to that produced by a ~solid fog~[TAG_SPELLS_SOLID_FOG] spell. In addition to slowing creatures down and obscuring sight, this spell's vapors are highly acidic. Each round on your turn, starting when you cast the spell, the fog deals 2d6 points of acid damage to each creature and object within it. TAG_SPELLS_AID_MASS TAG_SPELLS TAG_CLERIC_3 Aid, Mass ~Enchantment~[TAG_MAGIC_SCHOOLS_ENCHANTMENT](Compulsion)[Mind-Affecting] Level: ~Cleric~[TAG_CLERICS] 3 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Close (25 ft. + 5 ft./2 levels) Target: One creature/level no two more than 30 ft. apart Duration: 1 min./level Saving Throw: None Spell Resistance: No You hold your holy symbol aloft and cast the spell. A silvery radiance dances from your hands, leaping over all the nearby party members and strengthening them. Aid, Mass grants the targets a +1 ~morale~[TAG_MODIFIER_MORALE] bonus on ~attack rolls~[TAG_ATTACK_ROLL] and saves against ~fear~[TAG_FEAR] effects, plus ~temporary hit points~[TAG_TEMPORARY_HIT_POINTS] equal to 1d8 + ~caster level~[TAG_CASTER_LEVEL] (to a maximum of 1d8+15 temporary hit points). TAG_SPELLS_ALIGN_WEAPON_MASS TAG_SPELLS TAG_CLERIC_3 Align Weapon, Mass ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION](see text) Level: ~Cleric~[TAG_CLERICS] 3 Components: V, S Casting Time: 1 ~standard action~[TAG_STANDARD_ACTION] Range: Close (25 ft. + 5 ft./2 levels) Target: One creature/level no two more than 30 ft. apart Duration: 1 min./level Saving Throw: None Spell Resistance: No You hold your holy symbol high and speak old words of power. Your party's weapons take on a pale blue radiance. This spell functions like ~align weapon~[TAG_SPELLS_ALIGN_WEAPON], except that it affects multiple weapons or projectiles at a distance. Note: The projectiles part is not implemented. Warning: as a cleric you can't cast a spell that does have an opposed Alignment Descriptor. I can't change the descriptor on the fly, which means you can actually try to cast the spell but it will fizzle and you loose the spell if you try to cast it with the wrong alignment! TAG_SPELLS_ALLEGRO TAG_SPELLS TAG_BARD_3 Allegro ~Transmutation~[TAG_MAGIC_SCHOOLS_TRANSMUTATION] ~Bard~[TAG_BARDS] 3 Components: V, S Casting Time: 1 ~free action~[TAG_FREE_ACTION] Range: 20 ft. Area: 20 ft. radius burst centered on you Duration: 1 min/level With a quick wiggle of your fingers and a few arcane words, you release the feather in your hand to complete the spell. Suddenly, translucent blue motes burst outward from you and collect on yourself and your nearby allies before fading away. Each creature within the spell's area gains a 30-foot ~enhancement~[TAG_ENHANCEMENT_BONUS] bonus to its land ~speed~[TAG_MOVEMENT_RATE], up to a maximum of double the creature's land speed. Affected creatures retain these effects for the duration of the spell, even if they leave the original area. diff --git a/tpdatasrc/tpgamefiles/mes/help/stormlord_help.tab b/tpdatasrc/tpgamefiles/mes/help/stormlord_help.tab index b2df729c8..3e1dcb577 100644 --- a/tpdatasrc/tpgamefiles/mes/help/stormlord_help.tab +++ b/tpdatasrc/tpgamefiles/mes/help/stormlord_help.tab @@ -6,5 +6,5 @@ TAG_CLASS_FEATURES_STORMLORD_ENHANCED_JAVELINS TAG_STORMLORDS Stormlord Enhanc TAG_CLASS_FEATURES_STORMLORD_RESISTANCE_TO_ELECTRICITY TAG_STORMLORDS Stormlord Resistance to Electricity As a stormlord gains levels in this prestige class, he becomes increasingly ~resistant~[TAG_SPECIAL_ABILITIES_RESISTANCE_TO_ENERGY] to electrical energy, starting at 1st level with resistance to electricity 5. This bonus increases at 4th and 7th level by an additional 5. TAG_CLASS_FEATURES_STORMLORD_SHOCK_WEAPONS TAG_STORMLORDS Stormlord Shock Weapons Any spear or javelin used by a stormlord of 2nd level or higher is treated as a shock weapon (dealing an extra 1d6 points of electricity damage). The weapon loses this ability when leaving the hand of the stormlord. For a stormlord of 8th level or higher, any spear or javelin he uses is instead treated as a shocking burst weapon. TAG_CLASS_FEATURES_STORMLORD_THUNDERING_WEAPONS TAG_STORMLORDS Stormlords Thundering Weapons For a stormlord of 5th level or higher, any spear or javelin he uses is treated as a thundering weapon. The weapon loses this ability when leaving the hand of the stormlord. This effect stacks with that of the stormlord's shock weapon ability. -TAG_CLASS_FEATURES_STORMLORD_IMMUNITY_TO_ELECTRICITY_TAG_STORMLORDS Stormlords Immunity to Electricity At 9th level, a stormlord gains immunity to electricity. +TAG_CLASS_FEATURES_STORMLORD_IMMUNITY_TO_ELECTRICITY TAG_STORMLORDS Stormlords Immunity to Electricity At 9th level, a stormlord gains immunity to electricity. TAG_CLASS_FEATURES_STORMLORD_STORM_OF_ELEMENTAL_FURY TAG_STORMLORDS Stormlords Storm of Elemental Fury At 10th level, a stormlord can summon a storm of great magnitude and power. Once per day, a stormlord can use storm of elemental fury as if he were a 17th-level cleric. Deactivated until Spell Compendium is merged to temple+ diff --git a/tpdatasrc/tpgamefiles/rules/d20_ai/targeting.py b/tpdatasrc/tpgamefiles/rules/d20_ai/targeting.py index 423b8a65b..0383a6a4b 100644 --- a/tpdatasrc/tpgamefiles/rules/d20_ai/targeting.py +++ b/tpdatasrc/tpgamefiles/rules/d20_ai/targeting.py @@ -254,7 +254,7 @@ def will_kos(attacker, target, aiSearchingTgt): return 0 - if target.type == obj_t_npc: + if target.type != obj_t_npc: return 0 taifs = AiFightStatus(target)