- Joined
- May 9, 2014
- Messages
- 1,820
Sorry for not adding a comment why your resource is set to Awaiting Update. I'm feeling too sleepy to add them right now.
/*****************************************************************************
*
* StackNSplit v1.1.2.2
* by Bannar
*
* Easy item charges stacking and splitting.
*
* Thanks to Dangerb0y for original system.
* Container idea provided by Spinnaker.
*
******************************************************************************
*
* Requirements:
*
* ListT by Bannar
* hiveworkshop.com/threads/containers-list-t.249011/
*
* InventoryEvent by Bannar
* hiveworkshop.com/threads/snippet-inventoryevent.287084/
*
* RegisterPlayerUnitEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
*
* Optional requirement:
*
* SmoothItemPickup by Bannar
* hiveworkshop.com/threads/smoothitempickup.306016/
*
******************************************************************************
*
* Event API:
*
* integer EVENT_ITEM_CHARGES_ADDED
* integer EVENT_ITEM_CHARGES_REMOVED
*
* Use RegisterNativeEvent or RegisterIndexNativeEvent for event registration.
* GetNativeEventTrigger and GetIndexNativeEventTrigger provide access to trigger handles.
*
*
* function GetItemStackingUnit takes nothing returns unit
* Returns unit which manupilated event item.
*
* function GetItemStackingItem takes nothing returns item
* Returns manipulated event item.
*
* function GetItemStackingCharges takes nothing returns integer
* Returns number of charges that has been added or removed.
*
******************************************************************************
*
* Containers - idea behind:
*
* Each item type is allowed to have another item type assigned as its container,
* and becoming an element at the same time.
*
* Example:
*
* | element - Gold Coin
* | container - Endless Sack of Gold
*
* Any item type can become container for another as long as it is not a stackable item type.
* Containers cannot have thier own containers assigned.
* Containers may declare different maximum stack and split count values.
* Container is always prioritized over element type when item charges are being redistributed.
* Each element can have multiple item types assigned as its containers.
*
******************************************************************************
*
* Functions:
*
* function IsUnitItemFullyStacked takes unit whichUnit, integer itemTypeId returns boolean
* Checks if specified unit has hold any additional charges of provided item.
*
* function UnitStackItem takes unit whichUnit, item whichItem returns boolean
* Attempts to stack provided item for specified unit.
*
* function UnitSplitItem takes unit whichUnit, item whichItem returns boolean
* Attempts to split provided item for specified unit.
*
*
* Functions (Container):
*
* function IsItemContainer takes integer containerType returns boolean
* Returns value indicating whether specifed item is stackable or not.
*
* function GetItemContainerMaxStacks takes integer containerType returns integer
* Returns maximum number of charges for specified container.
*
* function GetItemContainerSplitCount takes integer containerType returns integer
* Returns number of charges lost by specified container per split.
*
* function GetItemContainerItem takes integer containerType returns integer
* Returns item type assigned to specified container as its elements.
*
* function GetItemContainerMinCharges takes integer containerType returns integer
* Number of charges that container cannot go below during split operation.
*
* function UnsetItemContainer takes integer elementType returns nothing
* Unsets any container related data related to specified element item type.
*
* function SetItemContainer takes integer elementType, integer containerType, integer stacks, integer splits, integer minCharges returns boolean
* Sets specified containerType item type as container for item type elementType.
* Argument minCharges specifies number of charges that container item cannot
* go below during split operation.
*
*
* Functions (Element):
*
* function IsItemStackable takes integer elementType returns boolean
* Returns value indicating whether specifed item is stackable or not.
*
* function GetItemMaxStacks takes integer elementType returns integer
* Returns maximum number of charges for specified item.
*
* function GetItemSplitCount takes integer elementType returns integer
* Returns number of charges lost by specified item per split.
*
* function ItemHasContainer takes integer elementType returns boolean
* Indicates if specifed element type has container assigned to it.
*
* function GetItemContainers takes integer elementType returns IntegerList
* Returns list of item types assigned to specified element as its containers.
*
* function MakeItemUnstackable takes integer elementType returns nothing
* Unregisters specified item from being stackable.
*
* function MakeItemStackable takes integer elementType, integer stacks, integer splits returns boolean
* Registers specified item as stackable.
*
*****************************************************************************/
library StackNSplit requires /*
*/ ListT /*
*/ InventoryEvent /*
*/ RegisterPlayerUnitEvent /*
*/ optional SmoothItemPickup
globals
integer EVENT_ITEM_CHARGES_ADDED
integer EVENT_ITEM_CHARGES_REMOVED
endglobals
globals
private unit eventUnit = null
private item eventItem = null
private integer eventCharges = -1
private TableArray table = 0
endglobals
function GetItemStackingUnit takes nothing returns unit
return eventUnit
endfunction
function GetItemStackingItem takes nothing returns item
return eventItem
endfunction
function GetItemStackingCharges takes nothing returns integer
return eventCharges
endfunction
function IsItemContainer takes integer containerType returns boolean
return table[3].has(containerType)
endfunction
function GetItemContainerMaxStacks takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[0][containerType]
endif
return -1
endfunction
function GetItemContainerSplitCount takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[1][containerType]
endif
return -1
endfunction
function GetItemContainerItem takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[3][containerType]
endif
return 0
endfunction
function GetItemContainerMinCharges takes integer containerType returns integer
if IsItemContainer(containerType) then
return table[4][containerType]
endif
return -1
endfunction
function IsItemStackable takes integer elementType returns boolean
return not IsItemContainer(elementType) and table[0].has(elementType)
endfunction
function GetItemMaxStacks takes integer elementType returns integer
if IsItemStackable(elementType) then
return table[0][elementType]
endif
return -1
endfunction
function GetItemSplitCount takes integer elementType returns integer
if IsItemStackable(elementType) then
return table[1][elementType]
endif
return -1
endfunction
function ItemHasContainer takes integer elementType returns boolean
return table[2].has(elementType)
endfunction
function GetItemContainers takes integer elementType returns IntegerList
if ItemHasContainer(elementType) then
return table[2][elementType]
endif
return 0
endfunction
function MakeItemUnstackable takes integer elementType returns nothing
if IsItemStackable(elementType) then
call table[0].remove(elementType)
call table[1].remove(elementType)
endif
endfunction
function MakeItemStackable takes integer elementType, integer stacks, integer splits returns boolean
if not IsItemContainer(elementType) and stacks > 0 then
set table[0][elementType] = stacks
set table[1][elementType] = IMaxBJ(splits, 1)
return true
endif
return false
endfunction
function UnsetItemContainer takes integer containerType returns nothing
local integer elementType = GetItemContainerItem(containerType)
local IntegerList containers
if elementType != 0 then
call table[0].remove(containerType)
call table[1].remove(containerType)
call table[3].remove(containerType)
call table[4].remove(containerType)
// Remove containerType from containers list
set containers = GetItemContainers(elementType)
call containers.removeElem(containerType)
if containers.empty() then
call containers.destroy()
call table[2].remove(elementType)
endif
endif
endfunction
function SetItemContainer takes integer elementType, integer containerType, integer stacks, integer splits, integer minCharges returns boolean
local IntegerList containers
if elementType == 0 or containerType == 0 then
return false
elseif stacks <= 0 or elementType == containerType then
return false
elseif IsItemContainer(elementType) or IsItemContainer(containerType) then
return false
elseif IsItemStackable(containerType) then
return false
endif
set containers = GetItemContainers(elementType)
if containers == 0 then
set containers = IntegerList.create()
set table[2][elementType] = containers
endif
call containers.push(containerType)
set table[0][containerType] = stacks
set table[1][containerType] = IMaxBJ(splits, 1)
set table[3][containerType] = elementType
set table[4][containerType] = IMaxBJ(minCharges, 0)
return true
endfunction
function IsUnitItemFullyStacked takes unit whichUnit, integer itemTypeId returns boolean
local boolean result = true
local integer max
local item itm
local integer size
local integer slot = 0
local IntegerListItem iter
if not IsUnitInventoryFull(whichUnit) then
return false
elseif IsItemContainer(itemTypeId) then
return result
endif
set size = UnitInventorySize(whichUnit)
if ItemHasContainer(itemTypeId) then
set iter = GetItemContainers(itemTypeId).first
loop
exitwhen iter == 0
set max = GetItemContainerMaxStacks(iter.data)
if max > 0 then
loop
exitwhen slot >= size
set itm = UnitItemInSlot(whichUnit, slot)
if GetItemTypeId(itm) == iter.data and GetItemCharges(itm) < max then
set result = false
exitwhen true
endif
set slot = slot + 1
endloop
endif
set iter = iter.next
endloop
endif
if result and IsItemStackable(itemTypeId) then
set max = GetItemMaxStacks(itemTypeId)
if max > 0 then
set slot = 0
loop
exitwhen slot >= size
set itm = UnitItemInSlot(whichUnit, slot)
if GetItemTypeId(itm) == itemTypeId and GetItemCharges(itm) < max then
set result = false
exitwhen true
endif
set slot = slot + 1
endloop
endif
endif
set itm = null
return result
endfunction
private function FireEvent takes integer evt, unit u, item itm, integer charges returns nothing
local unit prevUnit = eventUnit
local item prevItem = eventItem
local integer prevCharges = eventCharges
local integer playerId = GetPlayerId(GetOwningPlayer(u))
set eventUnit = u
set eventItem = itm
set eventCharges = charges
call TriggerEvaluate(GetNativeEventTrigger(evt))
if IsNativeEventRegistered(playerId, evt) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, evt))
endif
set eventUnit = prevUnit
set eventItem = prevItem
set eventCharges = prevCharges
set prevUnit = null
set prevItem = null
endfunction
private function StackItem takes unit u, item itm, item ignored, integer withTypeId, integer max returns integer
local integer charges = GetItemCharges(itm)
local integer slot = 0
local integer size = UnitInventorySize(u)
local item with
local integer withCharges
local integer diff
loop
exitwhen slot >= size
set with = UnitItemInSlot(u, slot)
if with != ignored and GetItemTypeId(with) == withTypeId then
set withCharges = GetItemCharges(with)
if withCharges < max then
set diff = max - withCharges
if diff >= charges then
call SetItemCharges(with, withCharges + charges)
call RemoveItem(itm)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, with, charges)
set charges = 0
exitwhen true
else
set charges = charges - diff
call SetItemCharges(with, max)
call SetItemCharges(itm, charges)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, itm, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, with, diff)
endif
endif
endif
set slot = slot + 1
endloop
set with = null
return charges
endfunction
function UnitStackItem takes unit whichUnit, item whichItem returns boolean
local integer charges = GetItemCharges(whichItem)
local integer itemTypeId = GetItemTypeId(whichItem)
local integer containerType
local integer max
local boolean result = false
local IntegerListItem iter
if whichUnit == null or charges == 0 then
return result
endif
if not IsItemContainer(itemTypeId) then
if ItemHasContainer(itemTypeId) then
set iter = GetItemContainers(itemTypeId).first
loop
exitwhen iter == 0
set containerType = iter.data
set max = GetItemContainerMaxStacks(containerType)
set charges = StackItem(whichUnit, whichItem, whichItem, containerType, max)
exitwhen charges == 0
set iter = iter.next
endloop
set result = true
endif
if IsItemStackable(itemTypeId) and charges > 0 then
set max = GetItemMaxStacks(itemTypeId)
call StackItem(whichUnit, whichItem, whichItem, itemTypeId, max)
set result = true
endif
else
set max = GetItemContainerMaxStacks(itemTypeId)
call StackItem(whichUnit, whichItem, whichItem, GetItemContainerItem(itemTypeId), max)
set result = true
endif
return result
endfunction
function UnitSplitItem takes unit whichUnit, item whichItem returns boolean
local integer charges = GetItemCharges(whichItem)
local integer itemTypeId = GetItemTypeId(whichItem)
local integer toSplit
local integer elementType
local IntegerListItem iter
local integer containerType
local item with
local trigger t
local integer minCharges = 1
if IsItemContainer(itemTypeId) then
set minCharges = GetItemContainerMinCharges(itemTypeId)
if charges <= minCharges then
return false
endif
set elementType = GetItemContainerItem(itemTypeId)
set toSplit = GetItemContainerSplitCount(itemTypeId)
elseif IsItemStackable(itemTypeId) and charges > minCharges then
set elementType = itemTypeId
set toSplit = GetItemSplitCount(itemTypeId)
else
return false
endif
if toSplit >= charges then
set toSplit = charges - minCharges
endif
call SetItemCharges(whichItem, charges - toSplit)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, whichUnit, whichItem, toSplit)
set with = CreateItem(elementType, GetUnitX(whichUnit), GetUnitY(whichUnit))
call SetItemCharges(with, toSplit)
// Redistribute splitted stacks if possible
if ItemHasContainer(elementType) then
set iter = GetItemContainers(elementType).first
loop
exitwhen iter == 0
set containerType = iter.data
set toSplit = StackItem(whichUnit, with, whichItem, containerType, GetItemContainerMaxStacks(containerType))
exitwhen toSplit == 0
set iter = iter.next
endloop
endif
if IsItemStackable(elementType) and toSplit > 0 then
set toSplit = StackItem(whichUnit, with, whichItem, elementType, GetItemMaxStacks(elementType))
endif
if toSplit > 0 then // something is left
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
call DisableTrigger(t)
call UnitAddItem(whichUnit, with)
call EnableTrigger(t)
set t = null
endif
set with = null
return true
endfunction
private function PickupItem takes unit u, item itm returns nothing
local integer itemTypeId = GetItemTypeId(itm)
local integer charges
local integer elementType
local integer max
local item with
local integer withCharges
local integer diff
local integer slot = 0
local integer size
if IsItemContainer(itemTypeId) then
set max = GetItemContainerMaxStacks(itemTypeId)
set elementType = GetItemContainerItem(itemTypeId)
set charges = GetItemCharges(itm)
set size = UnitInventorySize(u)
loop
exitwhen charges >= max
exitwhen slot >= size
set with = UnitItemInSlot(u, slot)
set withCharges = GetItemCharges(with)
if with != itm and withCharges > 0 and GetItemTypeId(with) == elementType then
if charges + withCharges > max then
set diff = max - charges
call SetItemCharges(itm, max)
call SetItemCharges(with, withCharges - diff)
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, with, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, itm, diff)
exitwhen true
else
set charges = charges + withCharges
call SetItemCharges(itm, charges)
call RemoveItem(with)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, itm, withCharges)
endif
endif
set slot = slot + 1
endloop
else
call UnitStackItem(u, itm)
endif
endfunction
private function OnPickup takes nothing returns nothing
call PickupItem(GetTriggerUnit(), GetManipulatedItem())
endfunction
private function OnMoved takes nothing returns nothing
local unit u = GetInventoryManipulatingUnit()
local item itm = GetInventoryManipulatedItem()
local integer slotFrom = GetInventorySlotFrom()
local integer itemTypeId = GetItemTypeId(itm)
local integer charges
local item swapped
local integer swappedTypeId
local integer swappedCharges
local integer max = 0
local integer total
local integer diff
local trigger t
if slotFrom == GetInventorySlotTo() then // splitting
call UnitSplitItem(u, itm)
elseif not IsItemContainer(itemTypeId) then
set charges = GetItemCharges(itm)
set swapped = GetInventorySwappedItem()
set swappedTypeId = GetItemTypeId(swapped)
set swappedCharges = GetItemCharges(swapped)
if charges > 0 then
if swappedTypeId == itemTypeId and swappedCharges > 0 then
set max = GetItemMaxStacks(itemTypeId)
elseif GetItemContainerItem(swappedTypeId) == itemTypeId then
set max = GetItemContainerMaxStacks(swappedTypeId)
endif
endif
if max > 0 then
set total = charges + swappedCharges
if total > max then
if swappedCharges < max then // if not met, allow for standard replacement action
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM)
call DisableTrigger(t)
call RemoveItem(itm) // Remove the item to prevent item swap from occurring
call EnableTrigger(t)
set t = GetAnyPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
call DisableTrigger(t)
call UnitAddItemToSlotById(u, itemTypeId, slotFrom) // Create and add new item replacing removed one
call EnableTrigger(t)
set t = null
set itm = UnitItemInSlot(u, slotFrom)
call SetItemCharges(itm, total - max)
call SetItemCharges(swapped, max)
set diff = max - charges
call FireEvent(EVENT_ITEM_CHARGES_REMOVED, u, itm, diff)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, swapped, diff)
endif
else
call SetItemCharges(swapped, total)
call RemoveItem(itm)
call FireEvent(EVENT_ITEM_CHARGES_ADDED, u, swapped, charges)
endif
endif
set swapped = null
endif
set u = null
set itm = null
endfunction
static if LIBRARY_SmoothItemPickup then
private function OnSmoothPickup takes nothing returns nothing
call PickupItem(GetSmoothItemPickupUnit(), GetSmoothItemPickupItem())
endfunction
private struct SmoothChargesStack extends array
static method canPickup takes unit whichUnit, item whichItem returns boolean
local integer itemTypeId = GetItemTypeId(whichItem)
if IsItemContainer(itemTypeId) then
return not IsUnitItemFullyStacked(whichUnit, GetItemContainerItem(itemTypeId))
elseif IsItemStackable(itemTypeId) then
return not IsUnitItemFullyStacked(whichUnit, itemTypeId)
endif
return false
endmethod
implement optional SmoothPickupPredicateModule
endstruct
endif
private module StackNSplitInit
private static method onInit takes nothing returns nothing
set EVENT_ITEM_CHARGES_ADDED = CreateNativeEvent()
set EVENT_ITEM_CHARGES_REMOVED = CreateNativeEvent()
set table = TableArray[5]
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function OnPickup)
call RegisterNativeEvent(EVENT_ITEM_INVENTORY_MOVE, function OnMoved)
static if LIBRARY_SmoothItemPickup then
call RegisterNativeEvent(EVENT_ITEM_SMOOTH_PICKUP, function OnSmoothPickup)
call AddSmoothItemPickupCondition(SmoothChargesStack.create())
endif
endmethod
endmodule
private struct StackNSplit extends array
implement StackNSplitInit
endstruct
endlibrary
scope StackNSplitDemo initializer Init
globals
string description = "Potion of Healing stacks up to 10 times and splits for 2.\n" + /*
*/ "Healing Salve is a container for Potion of Healing. Holds up to 20 charges, splits for 5. Can be emptied.\n" + /*
*/ "Tiny Farm stacks up to 2 times and splits for 1.\n" + /*
*/ "Tine Castly is a container for Tiny Farm. Holds up to 7 charges, splits for 1. Has to have at least 1 charge to allow for splitting.\n" + /*
*/ "\nYou can order hero to stack up item charges despite their inventory being full."
endglobals
private function Init takes nothing returns nothing
call MakeItemStackable('phea', 10, 2)
call SetItemContainer('phea', 'hslv', 20, 5, 0)
call MakeItemStackable('tfar', 2, 1)
call SetItemContainer('tfar', 'tcas', 7, 1, 1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 45, description)
endfunction
endscope
/*
* StackNSplit v1.1.1.9
* by Bannar
*
* Easy item charges stacking and splitting.
*/
package StackNSplit
import RegisterEvents
import InventoryEvent
import SmoothItemPickup
import LinkedList
tuple eventInfo(unit u, item itm, int charges)
var eventState = eventInfo(null, null, -1)
constant eventAddedTrigger = CreateTrigger()
constant eventRemovedTrigger = CreateTrigger()
constant table = InitHashtable()
public enum EVENT_ITEM_CHARGES
ADDED
REMOVED
/** Returns unit which manupilated event item. */
public function getItemStackingUnit() returns unit
return eventState.u
/** Returns manipulated event item. */
public function getItemStackingItem() returns item
return eventState.itm
/** Returns number of charges that has been added or removed. */
public function getItemStackingCharges() returns int
return eventState.charges
/** Returns trigger handle associated with specified item stacking event. */
public function getItemStackingEventTrigger(EVENT_ITEM_CHARGES whichEvent) returns trigger
trigger result = null
switch whichEvent
case EVENT_ITEM_CHARGES.ADDED
result = eventAddedTrigger
case EVENT_ITEM_CHARGES.REMOVED
result = eventRemovedTrigger
return result
/** Registers new event handler for specified item stacking event. */
public function registerItemStackingEvent(EVENT_ITEM_CHARGES whichEvent, code func)
switch whichEvent
case EVENT_ITEM_CHARGES.ADDED
eventAddedTrigger.addCondition(Condition(func))
case EVENT_ITEM_CHARGES.REMOVED
eventRemovedTrigger.addCondition(Condition(func))
/** Returns value indicating whether specifed item is stackable or not. */
public function isItemContainer(int containerType) returns boolean
return table.hasInt(3, containerType)
/** Returns maximum number of charges for specified container. */
public function getItemContainerMaxStacks(int containerType) returns int
var result = -1
if isItemContainer(containerType)
result = table.loadInt(0, containerType)
return result
/** Returns item type assigned to specified container as its elements. */
public function getItemContainerSplitCount(int containerType) returns int
var result = -1
if isItemContainer(containerType)
result = table.loadInt(1, containerType)
return result
/** Returns item type assigned to specified container as its elements. */
public function getItemContainerItem(int containerType) returns int
var result = 0
if isItemContainer(containerType)
result = table.loadInt(3, containerType)
return result
/** Number of charges that container cannot go below during split operation. */
public function getItemContainerMinCharges(int containerType) returns int
var result = -1
if isItemContainer(containerType)
result = table.loadInt(4, containerType)
return result
/** Returns value indicating whether specifed item is stackable or not. */
public function isItemStackable(int elementType) returns boolean
return not isItemContainer(elementType) and table.hasInt(0, elementType)
/** Retrieves maximum amount of stacks for specified item. */
public function getItemMaxStacks(int elementType) returns int
var result = -1
if isItemStackable(elementType)
result = table.loadInt(0, elementType)
return result
/** Returns number of charges lost by specified item per split. */
public function getItemSplitCount(int elementType) returns int
var result = -1
if isItemStackable(elementType)
result = table.loadInt(1, elementType)
return result
/** Indicates if specifed element type has container assigned to it. */
public function itemHasContainer(int elementType) returns boolean
return table.hasInt(2, elementType)
/** Returns list of item types assigned to specified element as its containers. */
public function getItemContainers(int elementType) returns LinkedList<int>
LinkedList<int> result = null
if itemHasContainer(elementType)
result = table.loadInt(2, elementType) castTo LinkedList<int>
return result
/** Unregisters specified item from being stackable. */
public function makeItemUnstackable(int elementType)
if isItemStackable(elementType)
table.removeInt(0, elementType)
table.removeInt(1, elementType)
/** Registers specified item as stackable. */
public function makeItemStackable(int elementType, int stacks, int splits) returns boolean
var result = false
if not isItemContainer(elementType) and stacks > 0
table.saveInt(0, elementType, stacks)
table.saveInt(1, elementType, max(splits, 1))
result = true
return result
/** Registers specified item as stackable. */
public function makeItemStackable(int elementType, int stacks) returns boolean
return makeItemStackable(elementType, stacks, 1)
/** Unsets any container related data related to specified element item type. */
public function unsetItemContainer(int containerType)
var elementType = getItemContainerItem(containerType)
if elementType != 0
table.removeInt(0, containerType)
table.removeInt(1, containerType)
table.removeInt(3, containerType)
table.removeInt(4, containerType)
// Remove containerType from containers list
var containers = getItemContainers(elementType)
containers.remove(containerType)
if containers.isEmpty()
destroy containers
table.removeInt(2, elementType)
/** Sets specified containerType item type as container for item type elementType.
Argument minCharges specifies number of charges that container item cannot
go below during split operation. */
public function setItemContainer(int elementType, int containerType, int stacks, int splits, int minCharges) returns boolean
if elementType == 0 or containerType == 0
return false
else if stacks <= 0 or elementType == containerType
return false
else if isItemContainer(elementType) or isItemContainer(containerType)
return false
else if isItemStackable(containerType)
return false
var containers = getItemContainers(elementType)
if containers == null
containers = new LinkedList<int>()
table.saveInt(2, elementType, containers castTo int)
containers.push(containerType)
table.saveInt(0, containerType, stacks)
table.saveInt(1, containerType, max(splits, 1))
table.saveInt(3, containerType, elementType)
table.saveInt(4, containerType, max(minCharges, 0))
return true
/** Sets specified containerType item type as container for item type elementType. */
public function setItemContainer(int elementType, int containerType, int stacks) returns boolean
return setItemContainer(elementType, containerType, stacks, 1, 0)
/** Checks if unit inventory is fully stacked and no charges can be added. */
public function unit.isItemFullyStacked(int itemTypeId) returns boolean
if not this.isInventoryFull()
return false
else if isItemContainer(itemTypeId)
return true
var result = true
var last = this.inventorySize() - 1
if itemHasContainer(itemTypeId)
for containerType in getItemContainers(itemTypeId)
var max = getItemContainerMaxStacks(containerType)
if max > 0
for slot = 0 to last
var itm = this.itemInSlot(slot)
if itm.getTypeId() == containerType and itm.getCharges() < max
result = false
break
if result and isItemStackable(itemTypeId)
var max = getItemMaxStacks(itemTypeId)
if max > 0
for slot = 0 to last
var itm = this.itemInSlot(slot)
if itm.getTypeId() == itemTypeId and itm.getCharges() < max
result = false
break
return result
function fireEvent(trigger evt, eventInfo currState)
var prevState = eventState
eventState = currState
evt.evaluate()
eventState = prevState
function stackItem(unit u, item itm, item ignored, int withTypeId, int max) returns int
var charges = itm.getCharges()
var last = u.inventorySize() - 1
for slot = 0 to last
var with = u.itemInSlot(slot)
if with != ignored and with.getTypeId() == withTypeId
var withCharges = with.getCharges()
if withCharges < max
var diff = max - withCharges
if diff >= charges
with.setCharges(withCharges + charges)
itm.remove()
fireEvent(eventAddedTrigger, eventInfo(u, with, charges))
charges = 0
break
else
charges -= diff
with.setCharges(max)
itm.setCharges(charges)
fireEvent(eventRemovedTrigger, eventInfo(u, itm, diff))
fireEvent(eventAddedTrigger, eventInfo(u, with, diff))
return charges
/** Attempts to stack provided item for specified unit. */
public function unit.stackItem(item whichItem) returns boolean
var charges = whichItem.getCharges()
if charges == 0
return false
var result = false
var itemTypeId = whichItem.getTypeId()
if not isItemContainer(itemTypeId)
if itemHasContainer(itemTypeId)
for containerType in getItemContainers(itemTypeId)
charges = stackItem(this, whichItem, whichItem, containerType, getItemContainerMaxStacks(containerType))
if charges == 0
break
result = true
if isItemStackable(itemTypeId) and charges > 0
stackItem(this, whichItem, whichItem, itemTypeId, getItemMaxStacks(itemTypeId))
result = true
else
stackItem(this, whichItem, whichItem, getItemContainerItem(itemTypeId), getItemContainerMaxStacks(itemTypeId))
result = true
return result
/** Attempts to split provided item for specified unit. */
public function unit.splitItem(item whichItem) returns boolean
var charges = whichItem.getCharges()
var itemTypeId = whichItem.getTypeId()
int elementType
int toSplit
var minCharges = 1
if isItemContainer(itemTypeId)
minCharges = getItemContainerMinCharges(itemTypeId)
if charges <= minCharges
return false
elementType = getItemContainerItem(itemTypeId)
toSplit = getItemContainerSplitCount(itemTypeId)
else if isItemStackable(itemTypeId) and charges > minCharges
elementType = itemTypeId
toSplit = getItemSplitCount(itemTypeId)
else
return false
if toSplit >= charges
toSplit = charges - minCharges
whichItem.setCharges(charges - toSplit)
fireEvent(eventRemovedTrigger, eventInfo(this, whichItem, toSplit))
var with = createItem(elementType, this.getPos())
with.setCharges(toSplit)
// Redistribute splitted stacks if possible
if itemHasContainer(elementType)
for containerType in getItemContainers(elementType)
toSplit = stackItem(this, with, whichItem, containerType, getItemContainerMaxStacks(containerType))
if toSplit == 0
break
if isItemStackable(elementType) and toSplit > 0
toSplit = stackItem(this, with, whichItem, elementType, getItemMaxStacks(elementType))
if toSplit > 0 // something is left
var t = getPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
t.disable()
this.addItemHandle(with)
t.enable()
return true
function pickupItem(unit u, item itm)
var itemTypeId = itm.getTypeId()
if isItemContainer(itemTypeId)
var max = getItemContainerMaxStacks(itemTypeId)
var elementType = getItemContainerItem(itemTypeId)
var charges = itm.getCharges()
var last = u.inventorySize() - 1
for slot = 0 to last
if charges >= max
break
var with = u.itemInSlot(slot)
var withCharges = with.getCharges()
if with != itm and withCharges > 0 and with.getTypeId() == elementType
if charges + withCharges > max
int diff = max - charges
itm.setCharges(max)
with.setCharges(withCharges - diff)
fireEvent(eventRemovedTrigger, eventInfo(u, with, diff))
fireEvent(eventAddedTrigger, eventInfo(u, itm, diff))
break
else
charges += withCharges
itm.setCharges(charges)
with.remove()
fireEvent(eventAddedTrigger, eventInfo(u, itm, withCharges))
else
u.stackItem(itm)
function onPickup()
pickupItem(GetTriggerUnit(), GetManipulatedItem())
function onMoved()
var slotFrom = getInventorySlotFrom()
if slotFrom == getInventorySlotTo() // splitting
getInventoryManipulatingUnit().splitItem(getInventoryManipulatedItem())
return
var u = getInventoryManipulatingUnit()
var itm = getInventoryManipulatedItem()
var itemTypeId = itm.getTypeId()
if not isItemContainer(itemTypeId)
var charges = itm.getCharges()
var swapped = getInventorySwappedItem()
var swappedTypeId = swapped.getTypeId()
var swappedCharges = swapped.getCharges()
var max = 0
if charges > 0
if swappedTypeId == itemTypeId and swappedCharges > 0
max = getItemMaxStacks(itemTypeId)
else if getItemContainerItem(swappedTypeId) == itemTypeId
max = getItemContainerMaxStacks(swappedTypeId)
if max > 0
var total = charges + swappedCharges
if total > max
if swappedCharges < max // if not met, allow for standard replacement action
var t = getPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM)
t.disable()
itm.remove() // Remove the item to prevent item swap from occurring
t.enable()
t = getPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM)
t.disable()
u.addItemToSlot(itemTypeId, slotFrom) // Create and add new item replacing removed one
t.enable()
itm = u.itemInSlot(slotFrom)
itm.setCharges(total - max)
swapped.setCharges(max)
var diff = max - charges
fireEvent(eventRemovedTrigger, eventInfo(u, itm, diff))
fireEvent(eventAddedTrigger, eventInfo(u, swapped, diff))
else
swapped.setCharges(total)
itm.remove()
fireEvent(eventAddedTrigger, eventInfo(u, swapped, charges))
function onSmoothPickup()
pickupItem(getSmoothItemPickupUnit(), getSmoothItemPickupItem())
class SmoothChargesStack implements SmoothPickupPredicate
function canPickup(unit whichUnit, item whichItem) returns boolean
var itemTypeId = whichItem.getTypeId()
var result = false
if isItemContainer(itemTypeId)
result = not whichUnit.isItemFullyStacked(getItemContainerItem(itemTypeId))
else if isItemStackable(itemTypeId)
result = not whichUnit.isItemFullyStacked(itemTypeId)
return result
init
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, () -> onPickup())
registerInventoryEvent(EVENT_ITEM_INVENTORY.MOVE, () -> onMoved())
registerSmoothItemPickupEvent(() -> onSmoothPickup())
addSmoothItemPickupCondition(new SmoothChargesStack())
package StackNSplitDemo
import StackNSplit
string description = "Potion of Healing stacks up to 10 times and splits for 2.\n" +
"Healing Salve is a container for Potion of Healing. Holds up to 20 charges, splits for 5. Can be emptied.\n" +
"Tiny Farm stacks up to 2 times and splits for 1.\n" +
"Tine Castly is a container for Tiny Farm. Holds up to 7 charges, splits for 1. Has to have at least 1 charge to allow for splitting.\n" +
"\nYou can order hero to stack up item charges despite their inventory being full."
function createAllItems()
CreateItem('ckng', - 402.8, 548.5)
CreateItem('modt', - 521.4, 434.9)
CreateItem('ratf', - 524.1, 552.4)
CreateItem('rde4', - 404.8, 435.1)
CreateItem('hslv', - 150.9, 367.1)
CreateItem('hslv', - 207.9, 267.3)
CreateItem('hslv', - 262.7, 367.5)
CreateItem('phea', - 55.3, 108.0).setCharges(3)
CreateItem('phea', - 138.6, 107.1).setCharges(3)
CreateItem('phea', - 236.8, 96.9).setCharges(3)
CreateItem('phea', - 324.2, 93.8).setCharges(3)
CreateItem('phea', - 59.8, 23.2).setCharges(3)
CreateItem('phea', - 135.4, 27.6).setCharges(3)
CreateItem('phea', - 226.7, 17.6).setCharges(3)
CreateItem('phea', - 311.4, 10.7).setCharges(3)
CreateItem('tcas', - 938.9, - 54.9)
CreateItem('tcas', - 937.4, 63.4)
CreateItem('tfar', - 794.6, 157.8)
CreateItem('tfar', - 796.7, 57.9)
CreateItem('tfar', - 798.0, - 39.2)
CreateItem('tfar', - 799.0, - 140.1)
CreateItem('tfar', - 703.2, 173.7)
CreateItem('tfar', - 703.0, 67.6)
CreateItem('tfar', - 695.6, - 33.0)
CreateItem('tfar', - 694.6, - 140.7)
public function initStackNSplitDemo()
createAllItems()
makeItemStackable('phea', 10, 2)
setItemContainer('phea', 'hslv', 20, 5, 0)
makeItemStackable('tfar', 2, 1)
setItemContainer('tfar', 'tcas', 7, 1, 1)
printTimed(description, 45)