// Before we start trading, we clean up what needs to be cleaned up before the trade window opens.
// We need an event for this.
// We can use the DialogStarted() event, or the setting of DB_DialogNPCs database, which might already
// contain an involved blackhole trader.
// But that would add more rule calls than necessary, for all non-blackhole NPCs when we are only interested in those.
//
// What always happens in PROC StartTrade() before opening the trade window is the setup of the database
// TradeRunning( _Player, _Trader, 1 ), and it is only ever set and reset in _Trade.txt, perfect for our
// purpose, since it already contains the trader and the party member involved in the trade.
IF
TradeRunning( _Player, _Trader, 1 )
AND
XCBlackholeDB_Traders( _Trader )
AND
_Player.DB_IsPlayer()
AND
CharacterGetLevel( _Player, _CharLevel )
AND
Time( _, _, _InGameHour )
THEN
XCBlackholeProc__RemoveSoldItems( _Trader, _CharLevel, _InGameHour );
XCBlackholeDB__ItemRegisterEnabled( _Trader, _CharLevel, _InGameHour );
// stop registration after trade ends, subsequent trades should start registration again
// because they run through the process where TradeRunning() is set.
// And hopefully this event is only ever thrown after completely closing the trade window, because
// otherwise subsequent trades of the same trade session would no longer be registered.
// (Since there is an explicit HappyWithDeal() event, I suppose that this is the case.)
IF
TradeEnds( _, _Trader )
AND
XCBlackholeDB__ItemRegisterEnabled( _Trader, _CharLevel, _InGameHour )
THEN
NOT XCBlackholeDB__ItemRegisterEnabled( _Trader, _CharLevel, _InGameHour );
// Remove sold items after the configured number of ingame hours
PROC
XCBlackholeProc__RemoveSoldItems( (CHARACTER)_Trader, (INTEGER)_CharLevel, (INTEGER)_InGameHour )
AND
XCBlackholeDB_TimeDifference( _TimeDiff )
AND
_TimeDiff > 0
AND
IntegerSubtract( _IngameHour, _TimeDiff, _MaxInGameHour )
AND
XCBlackholeDB__SoldItemHandles( _Trader, _RegisteredCharLevel, _RegisteredInGameHour, _ItemHandle, _Amount )
AND
_RegisteredInGameHour <= _MaxInGameHour
THEN
NOT XCBlackholeDB__SoldItemHandles( _Trader, _RegisteredCharLevel, _RegisteredInGameHour, _ItemHandle, _Amount );
// Move the stored amount to the black hole.
// Using ItemHandleDelete() here would never work here, because the _ItemHandle we registered is not the handle
// of a real item but the handle of the entity occupying one inventory slot.
// We also cannot move the handle to a container and then delete it, because it would still be the handle of
// a possibly remaining stack in the trader's inventory.
// So what we need is to move the registered amount of items away somewhere else and remove it there.
// The 'black hole' does exactly that, delete whatever is added to it.
// Basically, ItemHandleToContainer() takes a specified amount of a stack behind an 'item handle' and move it to
// wherever specified. This is the reason, why the call has an _Amount parameter in the first place !
// The move target again adds the moved target to an existing stack or creates a new stack and assigns it a new
// 'item handle'. (Well, not a stack, but an 'entitiy-that-occupies-one-inventory-slot, as said above.)
ItemHandleToContainer( _ItemHandle, ITEM_XC_STORAGE_BLACK_HOLE_SUN, _Amount );
// Remove sold items after the configured number of player level-ups
PROC
XCBlackholeProc__RemoveSoldItems( (CHARACTER)_Trader, (INTEGER)_CharLevel, (INTEGER)_InGameHour )
AND
XCBlackholeDB_LevelDifference( _ConfiguredDiff ) // get the configured 'age difference'
AND
IntegerMax( _ConfiguredDiff, 1, _MinDiff ) // must be at least 1
AND
IntegerMin( _MinDiff, 3, _Diff ) // may not be more than 3
AND
IntegerSubtract( _CharLevel, _Diff, _MaxCharLevelToRemove ) // subtract it from passed char level
AND
XCBlackholeDB__SoldItemHandles( _Trader, _RegisteredCharLevel, _RegisteredInGameHour, _ItemHandle, _Amount )
AND
_RegisteredCharLevel <= _MaxCharLevelToRemove // selects all items of this or lower level
THEN
NOT XCBlackholeDB__SoldItemHandles( _Trader, _RegisteredCharLevel, _RegisteredInGameHour, _ItemHandle, _Amount );
// same as above ;-)
ItemHandleToContainer( _ItemHandle, ITEM_XC_STORAGE_BLACK_HOLE_SUN, _Amount );
// After trade treasure has been generated, we add a signal token to the trader as a signal for the ...AddedTo...
// handler indicating that all items added to the trader so far were generated by treasure generation and all
// the following items added to the trader will be ones from the actual trade with the player.
//
// We need to find an event that safely happens AFTER treasure generation and before the trade window opens.
// (the latter condition is actually not necessary, because no items are yet transferred on ActivateTrade())
// ProcGenerateTradeTreasure( (CHARACTER)_Player, (CHARACTER)_Trader ) cannot be safely be used for that because
// that would make us dependent of the order or PROC executions and thus dependent on file names.
//
// The LastTradeItemGeneration() DB is only set in 3 places in _Trade.txt, twice as a reset with _TH = 0
// and once with _TH != 0 directly at the end of the ProcGenerateTradeTreasure()
// So we use that one.
//
// Since we also need to set up the disable registration fact and this must only be set somewhere within the whole
// trade generation process and before any Added() event handler can execute, which is the case for the
// ProcGenerateTradeTreasure() procedure and since the setting of the LastTradeItemGeneration() fact happens
// within the procedure context, we can use the setting of this fact to set the temporary disabled fact here as well
// and do not need to create another call to the procedure itself.
IF
LastTradeItemGeneration( _Trader, _TH )
AND
_TH != 0
AND
XCBlackholeDB_Traders( _Trader )
THEN
XCBlackholeDB__ItemRegisterTempDisabled( _Trader );
ItemTemplateAddToCharacter( "XC_SIGNALTOKEN_Internal_A_09bb7492-ed2b-4ccb-b610-f9835f20b649", _Trader, 1 );
// Handle registration must be done by a procedure. We need to registrate every single occurence of a template
// added to a character and this requires keeping an _Amount.
// So we need to look if we already have the handle registered and in that case, increase the amount, otherwise
// we add a new entry.
//
// The reason for this is, that the _ItemHandle is NOT the handle of an item but the handle of one
// 'object-that-occupies-one-inventory-slot', which can be a whole stack.
// That is the reason why we need the 'black hole' in the first place: we need to use a call that actually lets us
// specify an amount of items that should be removed from the trader.
// ItemHandleDelete() does not have any further parameters and would remove a complete stack, no matter if it was
// treasure-generation-added or trade-added.
// Which is no problem for item types the trader never sells but a problem for stuff the trader sells.
// (Skillbooks, scrolls and lockpicks are examples for items traders sell and players might sell to them.)
// But we want the traders 'original' inventory to stay intact.
PROC
XCBlackholeProcHelper__RegisterHandleForTrader( (CHARACTER)_Trader, (INTEGER)_CharLevel, (INTEGER)_ItemHandle, (INTEGER)_InGameHour )
AND
XCBlackholeDB__SoldItemHandles( _Trader, _CharLevel, _InGameHour, _ItemHandle, _Amount )
AND
IntegerSum( _Amount, 1, _NewAmount )
THEN
NOT XCBlackholeDB__SoldItemHandles( _Trader, _CharLevel, _InGameHour, _ItemHandle, _Amount );
XCBlackholeDB__SoldItemHandles( _Trader, _CharLevel, _InGameHour, _ItemHandle, _NewAmount );
PROC
XCBlackholeProcHelper__RegisterHandleForTrader( (CHARACTER)_Trader, (INTEGER)_CharLevel, (INTEGER)_ItemHandle, (INTEGER)_InGameHour )
AND
NOT XCBlackholeDB__SoldItemHandles( _Trader, _CharLevel, _InGameHour, _ItemHandle, _ )
THEN
XCBlackholeDB__SoldItemHandles( _Trader, _CharLevel, _InGameHour, _ItemHandle, 1 );
// Register an added item (or 'slot-entity' as mentioned above several times ;-)
// If registering is enabled and not temporarily disabled, it must be a sold item
IF
ItemTemplateAddedToCharacter( _ItemTemplate, _ItemHandle, _Trader )
AND
XCBlackholeDB_Traders( _Trader )
AND
NOT XCBlackholeDB__Ignore__Templates( _ItemTemplate )
AND
XCBlackholeDB__ItemRegisterEnabled( _Trader, _CharLevel, _InGameHour )
AND
NOT XCBlackholeDB__ItemRegisterTempDisabled( _Trader )
THEN
XCBlackholeProcHelper__RegisterHandleForTrader( _Trader, _CharLevel, _ItemHandle, _InGameHour );
// If new treasure was generated, the signal token was added afterwards, so the event it created was after trade
// treasure generation. We can remove the signal token and remove the disable flag.
// It seems safe here to use ItemHandleDelete() because the tokens are not stackable and we don't care about anything else.
// We only need to be very careful if we ever added another token of the same template to this character.
// But probably the delete() call would just silently fail but the logic would still kick in and that is all
// that is interesting.
// (The logic might be executed at the wrong time though, so it's still not a good idea to ever only use one
// type of token for a character.)
IF
ItemTemplateAddedToCharacter( "XC_SIGNALTOKEN_Internal_A_09bb7492-ed2b-4ccb-b610-f9835f20b649", _ItemHandle, _Trader )
AND
XCBlackholeDB_Traders( _Trader )
THEN
ItemHandleDelete( _ItemHandle );
NOT XCBlackholeDB__ItemRegisterTempDisabled( _Trader );
// This is the 'Black Hole', a container which destroys everything it receives immediately.
// Can be used to remove items. Simply put them in here.
// There is no hook to intervene and stop the destruction, so be careful.
IF
ItemTemplateAddedToContainer( _, _ItemHandle, ITEM_XC_STORAGE_BLACK_HOLE_SUN )
THEN
ItemHandleDelete( _ItemHandle );