Skip to content

Commit

Permalink
Added: FUNC keyword to item templates triggers with special parsing (#…
Browse files Browse the repository at this point in the history
…1110)

* Added: FUNC keyword to item templates triggers with special parsing (@CreateLoot, @NPCRestock). It allows to call a function with arguments on the last created ITEM.
Default object: the item. SRC: the character holding it.
Added function i16_narrow8.
  • Loading branch information
cbnolok authored Oct 7, 2023
1 parent 99abb67 commit 42a252d
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 62 deletions.
6 changes: 5 additions & 1 deletion Changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- 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.
17 changes: 17 additions & 0 deletions src/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,23 @@ auto i32_narrow16(const T a) noexcept
return static_cast<uint16>(a & umask);
}

template <typename T>
auto i16_narrow8(const T a) noexcept
{
static_assert(std::is_arithmetic_v<T>, "Input variable is not an arithmetic type.");
static_assert(std::is_integral_v<T>, "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<T>)
{
return static_cast<int8>(a & umask);
}
else
return static_cast<uint8>(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
Expand Down
121 changes: 74 additions & 47 deletions src/game/chars/CChar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CScriptTriggerArgs> pScriptArgs;
// Locate arguments for the called function
tchar* ptcArgs = strchr(ptcFunctionName, ' ');
if (ptcArgs)
{
*ptcArgs = 0;
++ptcArgs;
GETNONWHITESPACE(ptcArgs);
pScriptArgs = std::make_unique<CScriptTriggerArgs>(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)
{
Expand All @@ -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);
Expand All @@ -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:
Expand All @@ -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;
}
}

}

Expand All @@ -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 )
{
Expand Down
48 changes: 34 additions & 14 deletions src/game/items/CItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ lpctstr const CItem::sm_szTemplateTable[ITC_QTY+1] =
"BUY",
"CONTAINER",
"FULLINTERP",
"FUNC",
"ITEM",
"ITEMNEWBIE",
"NEWBIESWAP",
Expand Down Expand Up @@ -581,35 +582,31 @@ CItem * CItem::ReadTemplate( CResourceLock & s, CObjBase * pCont ) // static
}
}

bool fItemAttrib = false;
CItem * pNewTopCont = nullptr;
CItem * pItem = nullptr;
while ( s.ReadKeyParse())
{
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 )
Expand All @@ -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;
}
Expand All @@ -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<CScriptTriggerArgs> pScriptArgs;
// Locate arguments for the called function
tchar* ptcArgs = strchr(ptcFunctionName, ' ');
if (ptcArgs)
{
*ptcArgs = 0;
++ptcArgs;
GETNONWHITESPACE(ptcArgs);
pScriptArgs = std::make_unique<CScriptTriggerArgs>(ptcArgs);
}

CObjBaseTemplate* pContObjBaseT = pCont->GetTopLevelObj();
ASSERT(pContObjBaseT);
pItem->r_Call(ptcFunctionName, dynamic_cast<CTextConsole*>(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 );
}

Expand Down
1 change: 1 addition & 0 deletions src/game/items/CItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum ITC_TYPE // Item Template commands
ITC_BUY,
ITC_CONTAINER,
ITC_FULLINTERP,
ITC_FUNC,
ITC_ITEM,
ITC_ITEMNEWBIE,
ITC_NEWBIESWAP,
Expand Down

0 comments on commit 42a252d

Please sign in to comment.