diff --git a/Changelog.txt b/Changelog.txt index 1478c854a..e6756cbe4 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -3350,4 +3350,8 @@ Additionally, the problem of zig-zag issue following in the South direction has - Changed: Optimisation how f_onchar_delete is launch and avoid dual launch when delete char on login screen. 26-09-2023, Drk84 -- Fixed: Ship plank disappearing when closed before its timer expires or after a save is performed. \ No newline at end of file +- Fixed: Ship plank disappearing when closed before its timer expires or after a save is performed. + +07-10-2023, Nolok +- Added: FUNC keyword to item templates and template-triggers with special parsing (@Create, @CreateLoot, @NPCRestock). It allows to call a function with arguments on the last created ITEM. + Default object: the item. SRC: the character holding it. diff --git a/src/common/common.h b/src/common/common.h index a50a83155..d01e32beb 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -221,6 +221,23 @@ auto i32_narrow16(const T a) noexcept return static_cast(a & umask); } +template +auto i16_narrow8(const T a) noexcept +{ + static_assert(std::is_arithmetic_v, "Input variable is not an arithmetic type."); + static_assert(std::is_integral_v, "Only integral types are supported by this function."); + static_assert(sizeof(T) == 2, "Input variable is not a 16 bit number."); + + // Since the narrowing can be implementation specific, here we decide that we take only the lower 16 bytes and discard the upper ones. + constexpr uint16 umask = 0x00FF; + if constexpr (std::is_signed_v) + { + return static_cast(a & umask); + } + else + return static_cast(a & umask); +} + //#define IMulDiv(a,b,c) (((((int)(a)*(int)(b)) + (int)(c / 2)) / (int)(c)) - (IsNegative((int)(a)*(int)(b)))) inline int IMulDiv(const int a, const int b, const int c) noexcept diff --git a/src/game/chars/CChar.cpp b/src/game/chars/CChar.cpp index c35983cb5..4901f30cc 100644 --- a/src/game/chars/CChar.cpp +++ b/src/game/chars/CChar.cpp @@ -1278,17 +1278,41 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) { ADDTOCALLSTACK("CChar::ReadScriptReduced"); bool fFullInterp = false; - bool fBlockItemAttr = false; //Set a temporary boolean to block item attributes to set on Character. + bool fBlockItemAttr = false; // Set a temporary boolean to block item attributes to set on Character. CItem * pItem = nullptr; while ( s.ReadKeyParse() ) { if ( s.IsKeyHead("ON", 2) ) break; + bool fItemCreated = false; // With the current keyword, have i created an item? int iCmd = FindTableSorted(s.GetKey(), CItem::sm_szTemplateTable, ARRAY_COUNT(CItem::sm_szTemplateTable)-1); - bool fItemCreation = false; - if ( fVendor ) + if (iCmd == ITC_FUNC) + { + if (!pItem || fBlockItemAttr) + continue; + + lptstr ptcFunctionName = s.GetArgRaw(); + std::unique_ptr pScriptArgs; + // Locate arguments for the called function + tchar* ptcArgs = strchr(ptcFunctionName, ' '); + if (ptcArgs) + { + *ptcArgs = 0; + ++ptcArgs; + GETNONWHITESPACE(ptcArgs); + pScriptArgs = std::make_unique(ptcArgs); + } + pItem->r_Call(ptcFunctionName, this, pScriptArgs.get()); + if (pItem->IsDeleted()) + { + pItem = nullptr; + //g_Log.EventDebug("FUNC deleted the item.\n"); + } + continue; + } + else if ( fVendor ) { if (iCmd != -1) { @@ -1298,7 +1322,7 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) case ITC_SELL: { fBlockItemAttr = false; //Make sure we reset the value, if the last input is not a ITEM(NEWBIE) or CONTAINER. - CItemContainer * pCont = GetBank((iCmd == ITC_SELL) ? LAYER_VENDOR_STOCK : LAYER_VENDOR_BUYS ); + CItemContainer * pCont = GetBank((iCmd == ITC_SELL) ? LAYER_VENDOR_STOCK : LAYER_VENDOR_BUYS); if ( pCont ) { pItem = CItem::CreateHeader(s.GetArgRaw(), pCont, false); @@ -1308,6 +1332,7 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) pItem = nullptr; continue; } + //case ITC_BREAK: // I don't find a use case for that... case ITC_ITEM: case ITC_CONTAINER: case ITC_ITEMNEWBIE: @@ -1323,66 +1348,67 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) } else { - switch ( iCmd ) - { + switch (iCmd) + { case ITC_FULLINTERP: - { - lpctstr pszArgs = s.GetArgStr(); - GETNONWHITESPACE(pszArgs); - fFullInterp = ( *pszArgs == '\0' ) ? true : ( s.GetArgVal() != 0); + { + lpctstr pszArgs = s.GetArgStr(); + GETNONWHITESPACE(pszArgs); + fFullInterp = (*pszArgs == '\0') ? true : (s.GetArgVal() != 0); + continue; + } + case ITC_NEWBIESWAP: + { + if (!pItem) continue; + + if (pItem->IsAttr(ATTR_NEWBIE)) + { + if (Calc_GetRandVal(s.GetArgVal()) == 0) + pItem->ClrAttr(ATTR_NEWBIE); } - case ITC_NEWBIESWAP: + else { - if ( !pItem ) - continue; - - if ( pItem->IsAttr( ATTR_NEWBIE ) ) - { - if ( Calc_GetRandVal( s.GetArgVal() ) == 0 ) - pItem->ClrAttr(ATTR_NEWBIE); - } - else - { - if ( Calc_GetRandVal( s.GetArgVal() ) == 0 ) - pItem->SetAttr(ATTR_NEWBIE); - } - continue; + if (Calc_GetRandVal(s.GetArgVal()) == 0) + pItem->SetAttr(ATTR_NEWBIE); } + continue; + } case ITC_ITEM: case ITC_CONTAINER: case ITC_ITEMNEWBIE: - { - fItemCreation = true; + { + fBlockItemAttr = false; + fItemCreated = true; - if ( IsStatFlag( STATF_CONJURED ) && iCmd != ITC_ITEMNEWBIE ) // This check is not needed. - break; // conjured creates have no loot. + if (IsStatFlag(STATF_CONJURED) && iCmd != ITC_ITEMNEWBIE) // This check is not needed (sure?). + break; // conjured creates have no loot. - pItem = CItem::CreateHeader( s.GetArgRaw(), this, iCmd == ITC_ITEMNEWBIE ); - if ( pItem == nullptr ) - { - m_UIDLastNewItem = GetUID(); // Setting m_UIDLastNewItem to CChar's UID to prevent calling any following functions meant to be called on that item - continue; - } - m_UIDLastNewItem.InitUID(); //Clearing the attr for the next cycle + pItem = CItem::CreateHeader(s.GetArgRaw(), this, iCmd == ITC_ITEMNEWBIE); + if (pItem == nullptr) + { + m_UIDLastNewItem = GetUID(); // Setting m_UIDLastNewItem to CChar's UID to prevent calling any following functions meant to be called on that item + continue; + } + m_UIDLastNewItem.InitUID(); //Clearing the attr for the next cycle - pItem->_iCreatedResScriptIdx = s.m_iResourceFileIndex; - pItem->_iCreatedResScriptLine = s.m_iLineNum; + pItem->_iCreatedResScriptIdx = s.m_iResourceFileIndex; + pItem->_iCreatedResScriptLine = s.m_iLineNum; - if ( iCmd == ITC_ITEMNEWBIE ) - pItem->SetAttr(ATTR_NEWBIE); + if (iCmd == ITC_ITEMNEWBIE) + pItem->SetAttr(ATTR_NEWBIE); - if ( !pItem->IsItemInContainer() && !pItem->IsItemEquipped()) - pItem = nullptr; - continue; - } + if (!pItem->IsItemInContainer() && !pItem->IsItemEquipped()) + pItem = nullptr; + continue; + } case ITC_BREAK: case ITC_BUY: case ITC_SELL: pItem = nullptr; continue; - } + } } @@ -1398,10 +1424,11 @@ bool CChar::ReadScriptReduced(CResourceLock &s, bool fVendor) else pItem->r_LoadVal( s ); } - else if (!fItemCreation) + else if (!fItemCreated) { + // I'm setting an attribute to myself, not the item (e.g. @Create trigger). Run that script line. TRIGRET_TYPE tRet = OnTriggerRun( s, TRIGRUN_SINGLE_EXEC, &g_Serv, nullptr, nullptr ); - if ( (tRet == TRIGRET_RET_FALSE) && fFullInterp ) + if ((tRet == TRIGRET_RET_FALSE) && fFullInterp) ; else if ( tRet != TRIGRET_RET_DEFAULT ) { diff --git a/src/game/items/CItem.cpp b/src/game/items/CItem.cpp index dd4c5e9e2..77a541ff1 100644 --- a/src/game/items/CItem.cpp +++ b/src/game/items/CItem.cpp @@ -527,6 +527,7 @@ lpctstr const CItem::sm_szTemplateTable[ITC_QTY+1] = "BUY", "CONTAINER", "FULLINTERP", + "FUNC", "ITEM", "ITEMNEWBIE", "NEWBIESWAP", @@ -581,7 +582,6 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static } } - bool fItemAttrib = false; CItem * pNewTopCont = nullptr; CItem * pItem = nullptr; while ( s.ReadKeyParse()) @@ -589,27 +589,24 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static if ( s.IsKeyHead( "ON", 2 )) break; - int index = FindTableSorted( s.GetKey(), sm_szTemplateTable, ARRAY_COUNT( sm_szTemplateTable )-1 ); - switch (index) + int iCmd = FindTableSorted( s.GetKey(), sm_szTemplateTable, ARRAY_COUNT( sm_szTemplateTable )-1 ); + switch (iCmd) { case ITC_BUY: // "BUY" case ITC_SELL: // "SELL" - fItemAttrib = false; if (pVendorBuy != nullptr) { - pItem = CItem::CreateHeader( s.GetArgRaw(), (index==ITC_SELL)?pVendorSell:pVendorBuy, false ); + pItem = CItem::CreateHeader(s.GetArgRaw(), (iCmd == ITC_SELL) ? pVendorSell : pVendorBuy, false); if ( pItem == nullptr ) continue; if ( pItem->IsItemInContainer()) { - fItemAttrib = true; - pItem->SetContainedLayer( (char)(pItem->GetAmount())); // set the Restock amount. + pItem->SetContainedLayer(i16_narrow8(pItem->GetAmount())); // set the Restock amount. } } continue; case ITC_CONTAINER: - fItemAttrib = false; { pItem = CItem::CreateHeader( s.GetArgRaw(), pCont, false, pVendor ); if ( pItem == nullptr ) @@ -619,7 +616,6 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static DEBUG_ERR(( "CreateTemplate: CContainer %s is not a container\n", pItem->GetResourceName() )); else { - fItemAttrib = true; if ( ! pNewTopCont ) pNewTopCont = pItem; } @@ -628,16 +624,40 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static case ITC_ITEM: case ITC_ITEMNEWBIE: - fItemAttrib = false; if ( pCont == nullptr && pItem != nullptr ) - continue; // Don't create anymore items til we have some place to put them ! + continue; // Don't create anymore items until we have some place to put them ! pItem = CItem::CreateHeader( s.GetArgRaw(), pCont, false, pVendor ); - if ( pItem != nullptr ) - fItemAttrib = true; continue; + + case ITC_FUNC: + if (!pItem) + continue; + { + lptstr ptcFunctionName = s.GetArgRaw(); + std::unique_ptr pScriptArgs; + // Locate arguments for the called function + tchar* ptcArgs = strchr(ptcFunctionName, ' '); + if (ptcArgs) + { + *ptcArgs = 0; + ++ptcArgs; + GETNONWHITESPACE(ptcArgs); + pScriptArgs = std::make_unique(ptcArgs); + } + + CObjBaseTemplate* pContObjBaseT = pCont->GetTopLevelObj(); + ASSERT(pContObjBaseT); + pItem->r_Call(ptcFunctionName, dynamic_cast(pContObjBaseT), pScriptArgs.get()); + if (pItem->IsDeleted()) + { + pItem = nullptr; + //g_Log.EventDebug("FUNC deleted the template item.\n"); + } + continue; + } } - if ( pItem != nullptr && fItemAttrib ) + if ( pItem != nullptr ) pItem->r_LoadVal( s ); } diff --git a/src/game/items/CItem.h b/src/game/items/CItem.h index 9b5dd78d7..5842713d4 100644 --- a/src/game/items/CItem.h +++ b/src/game/items/CItem.h @@ -30,6 +30,7 @@ enum ITC_TYPE // Item Template commands ITC_BUY, ITC_CONTAINER, ITC_FULLINTERP, + ITC_FUNC, ITC_ITEM, ITC_ITEMNEWBIE, ITC_NEWBIESWAP,