diff --git a/data/js/magic/clumsy.js b/data/js/magic/clumsy.js index ada3d365..0460b360 100644 --- a/data/js/magic/clumsy.js +++ b/data/js/magic/clumsy.js @@ -106,22 +106,38 @@ function onSpellCast( mSock, mChar, directCast, spellNum ) // The following loop checks to see if any item is currently equipped (if not a GM) if( mChar.commandlevel < 2 ) { - if( spellType != 2 ) + if( spellType != 2 ) { var itemRHand = mChar.FindItemLayer( 0x01 ); var itemLHand = mChar.FindItemLayer( 0x02 ); - if(( itemLHand && itemLHand.type != 119 ) || ( itemRHand && ( itemRHand.type != 9 || itemRHand.type != 119 ))) // Spellbook + var lHandBlocks = false; + var rHandBlocks = false; + + // Evaluate blocking for left and right hand items + if( isSpellCastingAllowed( itemRHand ) || isSpellCastingAllowed( itemLHand )) + { + var result = handleItem( itemLHand, itemRHand, mChar ); + lHandBlocks = result.lHandBlocks; + rHandBlocks = result.rHandBlocks; + } + + if( lHandBlocks || rHandBlocks ) { if( mSock != null ) { mSock.SysMessage( GetDictionaryEntry( 708, mSock.language )); // You cannot cast with a weapon equipped. } - mChar.SetTimer( Timer.SPELLTIME, 0 ); - mChar.isCasting = false; - mChar.spellCast = -1; + + if( !mChar.isCasting ) + { + mChar.SetTimer( Timer.SPELLTIME, 0 ); + mChar.isCasting = false; + mChar.spellCast = -1; + } return true; } } + return false; } if( mChar.visible == 1 || mChar.visible == 2 ) @@ -389,4 +405,30 @@ function onSpellSuccess( mSock, mChar, ourTarg ) DoTempEffect( 0, sourceChar, ourTarg, 3, Math.round( mChar.skills.magery / 100 ), 0, 0 ); } +// Function to check if an equipped item allows casting +function isSpellCastingAllowed( item ) +{ + return item != null && ( item.type == 9 || item.type == 119 ); // Assuming type 9 is spellbook, and type 119 is spell channeling item +} + +// Function to handle items +function handleItem( itemLHand, itemRHand, mChar ) +{ + const UnEquipEnabled = GetServerSetting("AutoUnequippedCasting"); + var lHandBlocks = false; // Default to false + var rHandBlocks = false; // Default to false + if( UnEquipEnabled && itemLHand != null && !isSpellCastingAllowed( itemLHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemLHand.container = mChar.pack; + lHandBlocks = true; // Set to true if item is blocking + } + + if( UnEquipEnabled && itemRHand != null && !isSpellCastingAllowed( itemRHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemRHand.container = mChar.pack; + rHandBlocks = true; // Set to true if item is blocking + } + return { lHandBlocks: lHandBlocks, rHandBlocks: rHandBlocks }; +} + function _restorecontext_() {} diff --git a/data/js/magic/createfood.js b/data/js/magic/createfood.js index 0349dfa2..8952e094 100644 --- a/data/js/magic/createfood.js +++ b/data/js/magic/createfood.js @@ -83,18 +83,34 @@ function onSpellCast( mSock, mChar, directCast, spellNum ) { var itemRHand = mChar.FindItemLayer( 0x01 ); var itemLHand = mChar.FindItemLayer( 0x02 ); - if(( itemLHand && itemLHand.type != 119 ) || ( itemRHand && ( itemRHand.type != 9 || itemRHand.type != 119 ))) // Spellbook + var lHandBlocks = false; + var rHandBlocks = false; + + // Evaluate blocking for left and right hand items + if( isSpellCastingAllowed( itemRHand ) || isSpellCastingAllowed( itemLHand )) { - if( mSock ) + var result = handleItem( itemLHand, itemRHand, mChar ); + lHandBlocks = result.lHandBlocks; + rHandBlocks = result.rHandBlocks; + } + + if( lHandBlocks || rHandBlocks ) + { + if( mSock != null ) { - mSock.SysMessage( GetDictionaryEntry( 708, mSock.language )); // You cannot cast with a weapon equipped. + mSock.SysMessage(GetDictionaryEntry( 708, mSock.language )); // You cannot cast with a weapon equipped. + } + + if( !mChar.isCasting ) + { + mChar.SetTimer( Timer.SPELLTIME, 0 ); + mChar.isCasting = false; + mChar.spellCast = -1; } - mChar.SetTimer( Timer.SPELLTIME, 0 ); - mChar.isCasting = false; - mChar.spellCast = -1; return true; } } + return false; } // Turn character visible @@ -304,4 +320,30 @@ function onSpellSuccess( mSock, mChar, ourTarg ) } } +// Function to check if an equipped item allows casting +function isSpellCastingAllowed( item ) +{ + return item != null && ( item.type == 9 || item.type == 119 ); // Assuming type 9 is spellbook, and type 119 is spell channeling item +} + +// Function to handle items +function handleItem( itemLHand, itemRHand, mChar ) +{ + const UnEquipEnabled = GetServerSetting( "AutoUnequippedCasting" ); + var lHandBlocks = false; // Default to false + var rHandBlocks = false; // Default to false + if(UnEquipEnabled && itemLHand != null && !isSpellCastingAllowed( itemLHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemLHand.container = mChar.pack; + lHandBlocks = true; // Set to true if item is blocking + } + + if( UnEquipEnabled && itemRHand != null && !isSpellCastingAllowed( itemRHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemRHand.container = mChar.pack; + rHandBlocks = true; // Set to true if item is blocking + } + return { lHandBlocks: lHandBlocks, rHandBlocks: rHandBlocks }; +} + function _restorecontext_() {} diff --git a/data/js/magic/level1targ.js b/data/js/magic/level1targ.js index 1d35bd6e..83d825df 100644 --- a/data/js/magic/level1targ.js +++ b/data/js/magic/level1targ.js @@ -67,20 +67,35 @@ function ItemInHandCheck( mChar, mSock, spellType ) { var itemRHand = mChar.FindItemLayer( 0x01 ); var itemLHand = mChar.FindItemLayer( 0x02 ); - if(( itemLHand && itemLHand.type != 119 ) || ( itemRHand && ( itemRHand.type != 9 || itemRHand.type != 119 ))) // Spellbook + var lHandBlocks = false; + var rHandBlocks = false; + + // Evaluate blocking for left and right hand items + if( isSpellCastingAllowed( itemRHand ) || isSpellCastingAllowed( itemLHand )) { - if( mSock ) + var result = handleItem( itemLHand, itemRHand, mChar ); + lHandBlocks = result.lHandBlocks; + rHandBlocks = result.rHandBlocks; + } + + if( lHandBlocks || rHandBlocks ) + { + if( mSock != null ) { mSock.SysMessage( GetDictionaryEntry( 708, mSock.language )); // You cannot cast with a weapon equipped. } - mChar.SetTimer( Timer.SPELLTIME, 0 ); - mChar.isCasting = false; - mChar.spellCast = -1; + + if( !mChar.isCasting ) + { + mChar.SetTimer( Timer.SPELLTIME, 0 ); + mChar.isCasting = false; + mChar.spellCast = -1; + } return false; } } + return true; } - return true; } function onSpellCast( mSock, mChar, directCast, spellNum ) @@ -635,4 +650,30 @@ function DispatchSpell( spellNum, mSpell, sourceChar, ourTarg, caster ) } } +// Function to check if an equipped item allows casting +function isSpellCastingAllowed( item ) +{ + return item != null && ( item.type == 9 || item.type == 119 ); // Assuming type 9 is spellbook, and type 119 is spell channeling item +} + +// Function to handle items +function handleItem( itemLHand, itemRHand, mChar ) +{ + const UnEquipEnabled = GetServerSetting( "AutoUnequippedCasting" ); + var lHandBlocks = false; // Default to false + var rHandBlocks = false; // Default to false + if( UnEquipEnabled && itemLHand != null && !isSpellCastingAllowed( itemLHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemLHand.container = mChar.pack; + lHandBlocks = true; // Set to true if item is blocking + } + + if( UnEquipEnabled && itemRHand != null && !isSpellCastingAllowed( itemRHand )) + { // Allow casting if item is spell channeling or type 9 spell book + itemRHand.container = mChar.pack; + rHandBlocks = true; // Set to true if item is blocking + } + return { lHandBlocks: lHandBlocks, rHandBlocks: rHandBlocks }; +} + function _restorecontext_() {} diff --git a/source/Changelog.txt b/source/Changelog.txt index 61552660..6be6f964 100644 --- a/source/Changelog.txt +++ b/source/Changelog.txt @@ -1,3 +1,6 @@ +20/04/2024 - Dragon Slayer/Xuri + Introduced a new INI setting, AutoUnequippedCasting, enhancing customization and control for users. + 18/04/2024 - Dragon Slayer Added Focus Skill to Stam Regain and Mana Regan. diff --git a/source/SEFunctions.cpp b/source/SEFunctions.cpp index 17ffe76a..bb54f037 100644 --- a/source/SEFunctions.cpp +++ b/source/SEFunctions.cpp @@ -5080,6 +5080,9 @@ JSBool SE_GetServerSetting( JSContext *cx, [[maybe_unused]] JSObject *obj, uintN case 347: // MOONGATESFACETS *rval = INT_TO_JSVAL( static_cast( cwmWorldState->ServerData()->GetMoongateFacetStatus() )); break; + case 348: // AUTOUNEQUIPPEDCASTING + *rval = BOOLEAN_TO_JSVAL( cwmWorldState->ServerData()->AutoUnequippedCasting() ); + break; default: ScriptError( cx, "GetServerSetting: Invalid server setting name provided" ); return false; diff --git a/source/cServerData.cpp b/source/cServerData.cpp index 62d46868..9b73d99f 100644 --- a/source/cServerData.cpp +++ b/source/cServerData.cpp @@ -367,7 +367,8 @@ const std::map CServerData::uox3IniCaseValue {"YOUNGPLAYERSYSTEM"s, 344}, {"YOUNGLOCATION"s, 345}, {"SECRETSHARDKEY"s, 346}, - {"MOONGATEFACETS"s, 347} + {"MOONGATEFACETS"s, 347}, + {"AUTOUNEQUIPPEDCASTING"s, 348} }; constexpr auto MAX_TRACKINGTARGETS = 128; @@ -478,6 +479,7 @@ constexpr auto BIT_ENABLENPCGUILDDISCOUNTS = UI32( 100 ); constexpr auto BIT_ENABLENPCGUILDPREMIUMS = UI32( 101 ); constexpr auto BIT_SNOOPAWARENESS = UI32( 102 ); constexpr auto BIT_YOUNGPLAYERSYSTEM = UI32( 103 ); +constexpr auto BIT_AUTOUNEQUIPPEDCASTING = UI32( 104 ); // New uox3.ini format lookup @@ -803,6 +805,7 @@ auto CServerData::ResetDefaults() -> void TravelSpellsBetweenWorlds( false ); TravelSpellsWhileAggressor( false ); CastSpellsWhileMoving( false ); + AutoUnequippedCasting( false ); MaxControlSlots( 0 ); // Default to 0, which is equal to off MaxFollowers( 5 ); MaxPetOwners( 10 ); @@ -3868,6 +3871,20 @@ auto CServerData::TravelSpellsWhileAggressor( bool newVal ) -> void boolVals.set( BIT_TRAVELSPELLSWHILEAGGRESSOR, newVal ); } +//o------------------------------------------------------------------------------------------------o +//| Function - CServerData::AutoUnequippedCasting() +//o------------------------------------------------------------------------------------------------o +//| Purpose - Gets/Sets whether spells will auto unequipe the hands that is not a spellbook or spellchanneling type. +//o------------------------------------------------------------------------------------------------o +auto CServerData::AutoUnequippedCasting() const -> bool +{ + return boolVals.test( BIT_AUTOUNEQUIPPEDCASTING ); +} +auto CServerData::AutoUnequippedCasting( bool newVal ) -> void +{ + boolVals.set( BIT_AUTOUNEQUIPPEDCASTING, newVal ); +} + //o------------------------------------------------------------------------------------------------o //| Function - CServerData::MaxControlSlots() //o------------------------------------------------------------------------------------------------o @@ -5173,6 +5190,7 @@ auto CServerData::SaveIni( const std::string &filename ) -> bool ofsOutput << "TRAVELSPELLSWHILEAGGRESSOR=" << ( TravelSpellsWhileAggressor() ? 1 : 0 ) << '\n'; ofsOutput << "HIDESTATSFORUNKNOWNMAGICITEMS=" << HideStatsForUnknownMagicItems() << '\n'; ofsOutput << "CASTSPELLSWHILEMOVING=" << ( CastSpellsWhileMoving() ? 1 : 0 ) << '\n'; + ofsOutput << "AUTOUNEQUIPPEDCASTING=" << ( AutoUnequippedCasting() ? 1 : 0 ) << '\n'; ofsOutput << "}" << '\n'; ofsOutput << '\n' << "[start locations]" << '\n' << "{" << '\n'; @@ -6524,6 +6542,9 @@ auto CServerData::HandleLine( const std::string& tag, const std::string& value ) case 347: // MOONGATEFACETS SetMoongateFacetStatus( static_cast( std::stoul( value, nullptr, 0 ))); break; + case 348: // AUTOUNEQUIPPEDCASTING + AutoUnequippedCasting(( static_cast( std::stoul( value, nullptr, 0 )) >= 1 ? true : false )); + break; default: rValue = false; break; diff --git a/source/cServerData.h b/source/cServerData.h index 5e8f0952..1bc7cddf 100644 --- a/source/cServerData.h +++ b/source/cServerData.h @@ -212,7 +212,7 @@ class CServerData // Once over 62, bitsets are costly. std::vector has a special exception in the c++ specificaiton, to minimize wasted space for bools // These should be updated - std::bitset<104> boolVals; // Many values stored this way, rather than using bools. + std::bitset<105> boolVals; // Many values stored this way, rather than using bools. std::bitset<64> spawnRegionsFacets; // Used to determine which facets to enable spawn regions for, set in UOX>INI std::bitset<64> moongateFacets; // Used to determine which facets to enable moongates for, set in UOX>INI @@ -924,6 +924,9 @@ class CServerData auto TravelSpellsWhileAggressor( bool value ) -> void; auto TravelSpellsWhileAggressor() const -> bool; + auto AutoUnequippedCasting( bool value ) -> void; + auto AutoUnequippedCasting() const -> bool; + auto ToolUseLimit( bool value ) -> void; auto ToolUseLimit() const -> bool; diff --git a/source/magic.cpp b/source/magic.cpp index daf0bf3e..d04ab3f9 100644 --- a/source/magic.cpp +++ b/source/magic.cpp @@ -4108,20 +4108,50 @@ bool CMagic::SelectSpell( CSocket *mSock, SI32 num ) } // The following loop checks to see if any item is currently equipped (if not a GM) - if( !mChar->IsGM() ) - { - if( type != 2 ) - { - CItem *itemRHand = mChar->GetItemAtLayer( IL_RIGHTHAND ); - CItem *itemLHand = mChar->GetItemAtLayer( IL_LEFTHAND ); - if(( itemLHand != nullptr && itemLHand->GetType() != IT_SPELLCHANNELING ) || ( itemRHand != nullptr && itemRHand->GetType() != IT_SPELLBOOK && itemRHand->GetType() != IT_SPELLCHANNELING )) - { - mSock->SysMessage( 708 ); // You cannot cast with a weapon equipped. - mChar->StopSpell(); - return false; - } - } - } + if( !mChar->IsGM() && type != 2 ) + { + bool autoUnequipEnabled = cwmWorldState->ServerData()->AutoUnequippedCasting(); + + CItem *itemRHand = mChar->GetItemAtLayer( IL_RIGHTHAND ); + CItem *itemLHand = mChar->GetItemAtLayer( IL_LEFTHAND ); + auto mCharPack = mChar->GetPackItem(); + + // Function to check and possibly unequip an item if it blocks spell casting + auto handleItem = [&]( CItem* item, auto itemCheck, bool& blockFlag ) + { + if( item && itemCheck( item )) + { + // If auto-unequip is enabled, make sure pack can hold another item before unequipping + if( autoUnequipEnabled && ValidateObject( mCharPack ) && mCharPack->GetContainsList()->Num() < mCharPack->GetMaxItems() ) + { + item->SetCont( mCharPack ); + blockFlag = false; + } + else + { + blockFlag = true; + } + } + else + { + blockFlag = false; + } + }; + + bool lHandBlocks = true; + bool rHandBlocks = true; + + // Evaluate blocking for left and right hand items + handleItem( itemLHand, []( CItem* item ) { return item->GetType() != IT_SPELLCHANNELING; }, lHandBlocks ); + handleItem( itemRHand, []( CItem* item ) { return item->GetType() != IT_SPELLBOOK && item->GetType() != IT_SPELLCHANNELING; }, rHandBlocks ); + + if( lHandBlocks || rHandBlocks ) + { + mSock->SysMessage( 708 ); // You cannot cast with a weapon equipped. + mChar->StopSpell(); + return false; + } + } if( mChar->GetVisible() == VT_TEMPHIDDEN || mChar->GetVisible() == VT_INVISIBLE ) {