- Joined
- Sep 6, 2013
- Messages
- 6,743
Post the demo you made up, showing the issue.
library Inventory /* v2.1a -- hiveworkshop.com/threads/bag-v1-6-1.252002/
*/ requires /* Credits
*/ VectorT /* Bannar -- hiveworkshop.com/threads/containers-vector-t.248942/
*/ RegisterPlayerUnitEvent /* Bannar (new version) -- http://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
or
Magtherigon96 (old version) -- hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
*/ Table /* Bribe -- hiveworkshop.com/threads/snippet-new-table.188084/
*/ UnitDex /* TriggerHappy -- hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
VectorT requires Alloc module.
Example: http://www.hiveworkshop.com/threads/unique-id-allocation.260897/
Information
¯¯¯¯¯¯¯¯¯¯¯
Inventory is an improvement of the normal hero inventory.
It extends the treatment with items in inventory, (stack/split)
and also adds the possibilty to add abstract inventories.
Mechanics
¯¯¯¯¯¯¯¯¯¯¯
Each unit can have infinite inventories.
Each inventory can have infinte items.
So each unit has a vector of inventories, and
each inventory has a vector of items in itself.
With both vectors you can use the VecotorT's API.
When adding a new item to an inventory,
the item will be hided automaticaly, and when removed from the inventory,
it will be unhided again, and moved to unit's position.
When an item is added there is a default procedure.
It goes from step 1 to 4 and in case one step succeed, it will stop.
1) Try to stack item with current inventory.
2) Try to stack item with all inventories of the unit.
3) Try to find a free slot in current inventory.
4) Try to find a free slot in any inventory of the unit.
If none of the steps was succesfull it means that the item was not added.
*/
// --- API ---
//! novjass
// read StackConfig trigger for stack API
// read SplitConfig trigger for stack API
// read Fully Inventory Order trigger for FullInventory API
struct InventoryData
readonly ItemVector item
// vector of items
// to read, VectorAPI can be used, item[0] -> returns first element
readonly Inventory inventory // represents the unit's entire inventory
// this is actually the UnitId of the unit where the inventory is applied.
// so when using GetUnitById() of UnitIndexer function we can retrive the respective unit.
// all the getSlot functions return "-1" if wanted slot doesnt exist
method init takes integer size returns nothing
// inits the vector with empty slots
method getFreeSlot takes nothing returns integer
// returns an empty slot
method getItemSlot takes item it returns integer
// which slot item has
method addItem takes item it returns boolean
// returns if item was succesfully added
method addItemAtSlot takes item it, integer slot returns boolean
// returns if item was succesfully added
method stackItem takes item it returns boolean
// tries to stack item with any items inside the vector
method getItemStackableSlot takes item it returns integer
// returns an slot with which item could stack with
method switchItems takes integer slot1, integer slot2 returns boolean
// you can switch slots of two items
method hasItem takes item it returns boolean
// does this vector contain the item
method removeItem takes item it returns boolean
// returns if item was succesfully removed
method flush takes nothing returns nothing
// removed all items from the vector
static method isItemOwned takes item it returns boolean
// returns if this item is part of ANY inventory already
static method getItemVector takes item it returns thistype
// returns the vector the item belongs to
struct Inventory
static constant group GROUP
// defines all units which have a custom inventory applied
readonly InventoryVector inventory
// vector of "InventoryData" , see struct above
// you may read for example, with the vector api inventory[0] -> returns first element
public integer vectorPos
// current position in vector
// this can be used to keep track of current inventory
// it always starts with "0"
static method operator[] takes unit u returns thistype
// Functions to handle inventories.
method setInventoryAmount takes integer amount returns boolean
// set amount of inventories
method getInventoryAmount takes nothing returns integer
// how many custom inventories the unit has
method addInventory takes integer amount returns boolean
// to add/remove custom inventories
method removeInventory takes integer whichInventory returns boolean
// remove a certain inventory from vector
method flush takes nothing returns boolean
// removes all inventories from the unit
// Functions to handle inventory content.
// all the getSlot functions return "-1" if wanted slot doesnt exist
method hasFreeSlot takes nothing returns boolean
// returns if there is any inventory left with a free slot
method hasItem takes item it returns boolean
// returns if the item is part of any of the unit's inventory
method hasStackableItem takes item it returns boolean
// returns if there is any item with which "it" could stack
method stackItem takes item it returns boolean
// stack item "it" with any items inside any inventory until it's used up
// returns if item could be completly stacked
method addItem takes item it returns boolean
// 1. tries to stack the item with whole inventory
// 2. if item still exists, it will get a free slot anywhere
// 3. if item was not stacked and could not be added, return false
// You can registder code that runs when a item is added to an inventory.
static method register takes boolexpr bx returns triggercondition
static method unregister takes triggercondition tc returns nothing
//inside the code refer to:
static unit Unit
// inventory unit
static integer InitialCharges
// charges the item had originaly, before it tried to apply
static item InitialItem
// item itself (can be removed, too, in case it was fully stacked)
static integer InitialItemId
// itemId of item that was added
struct UnitInventory
// UnitInventory works only with the current/actual inventory of the unit,
// instead of "Inventory" struct, which works with all inventories.
// all the getSlot functions return "-1" if wanted slot doesnt exist
static method getFreeSlot takes unit u returns integer
// get free slot
static method getItemSlot takes unit u, item it returns integer
// which slot the item has
static method stackItem takes unit u, item it returns boolean
// stack item "it" with any items inside any inventory until it's used up
// returns if item could be completly stacked
static method getItemStackableSlot takes unit u, item it returns integer
// returns an slot with which "it" could stack with
// method operator to enable/disable triggers:
// The system works with some events, such as drop, pickup, and inventory orders
// to properly stack/split, and add/remove items automaticaly from/in the inventory.
// if you manipulate some data on your own, such as "Bag" library does,
// you may require some functions to toggle some system triggers.
static method operator order_enabled= takes boolean flag returns nothing
// toggle the Inventory_Order trigger
static method operator pickUp_enabled= takes boolean flag returns nothing
// toogle the Item_Pick_Up trigger
static method operator drop_enabled= takes boolean flag returns nothing
// toggle the Item_Drop trigger
//! endnovjass
// ==== End API ====
// _g suffix means global, not struct specific
// _p suffix means private, to avoid name collision in case there is something (same) public
globals
private boolean pickup_enabled_p = true
private boolean drop_enabled_p = true
private boolean order_enabled_p = true
endglobals
native UnitAlive takes unit id returns boolean
// fast init, so stack/split modules work
private module Init_T
private static method onInit takes nothing returns nothing
set table = Table.create()
endmethod
endmodule
struct ItemSplit extends array
private static Table table
private static method SetSplitAmount takes integer itemType, integer amount returns nothing
set table.integer[itemType] = amount
endmethod
public static method operator [] takes integer itemType returns integer
if table.integer.has(itemType) then
return table.integer[itemType]
else
return 1
endif
endmethod
public static method exists takes integer itemType returns boolean
return table.integer.has(itemType)
endmethod
implement Init_T
implement ItemSplitConfig
endstruct
struct ItemStack extends array
private static Table table
private static method SetStackLimit takes integer itemType, integer limit returns nothing
set table.integer[itemType] = limit
endmethod
public static method operator [] takes integer itemType returns integer
if table.integer.has(itemType) then
return table.integer[itemType]
else
return DEFAULT_STACK_LIMIT
endif
endmethod
public static method exists takes integer itemType returns boolean
return table.integer.has(itemType)
endmethod
implement Init_T
implement ItemStackConfig
public static method isStackable takes item it returns boolean
return GetItemCharges(it) > 0 and GetItemCharges(it) < thistype[GetItemTypeId(it)]
endmethod
// return if item was fully stacked.
public static method stackItems takes item oldItem, item newItem returns boolean
local integer max
local integer charges_old
local integer charges_new = GetItemCharges(newItem)
if (not isStackable(oldItem) or charges_new < 1 or GetItemTypeId(oldItem) != GetItemTypeId(newItem) or oldItem == null) then
return false
endif
set max = ItemStack[GetItemTypeId(oldItem)]
set charges_old = GetItemCharges(oldItem)
if (charges_old + charges_new) > max then
// charges is too big, stack only partialy
call SetItemCharges(newItem, max - charges_old)
call SetItemCharges(oldItem, max)
return false
else
// items can completly stack, we can remove one
call SetItemCharges(oldItem, charges_old + charges_new)
// clean from inventory
if InventoryData.isItemOwned(newItem) then
call InventoryData.getItemVector(newItem).removeItem(newItem)
endif
set UnitInventory.drop_enabled = false
call RemoveItem(newItem)
set UnitInventory.drop_enabled = true
return true
endif
endmethod
endstruct
//! runtextmacro DEFINE_VECTOR("", "ItemVector", "item")
struct InventoryData
readonly ItemVector item
readonly Inventory inventory
private static Table table
implement Init_T
// just the read the unit later, by it's id
method operator Inventory= takes Inventory i returns nothing
set inventory = i
if .item == 0 then
set .item = ItemVector.create()
endif
endmethod
// we create empty slots
public method init takes integer size returns nothing
local integer max = .item.size()
loop
exitwhen (max > size)
call .item.push(null)
set max = max + 1
endloop
endmethod
public static method isItemOwned takes item it returns boolean
return table.has(GetHandleId(it))
endmethod
public method hasItem takes item it returns boolean
return table[GetHandleId(it)] == this
endmethod
public static method getItemVector takes item it returns thistype
return table[GetHandleId(it)]
endmethod
// find item slot
public method getItemSlot takes item it returns integer
if .hasItem(it) then
return table[-GetHandleId(it)]
endif
return -1
endmethod
// find any free slot
public method getFreeSlot takes nothing returns integer
local integer max = .item.size() - 1
local integer i = 0
loop
exitwhen (i > max)
if (.item[i] == null) then
return i
endif
set i = i + 1
endloop
return -1
endmethod
// find slot with same item type that is stackable
public method getItemStackableSlot takes item it returns integer
local integer max
local integer i
local integer iType
if (it == null or GetItemTypeId(it) == 0 or GetItemCharges(it) < 1) then
return -1
endif
set max = .item.size() - 1
set i = 0
set iType = GetItemTypeId(it)
loop
exitwhen (i > max)
if iType == GetItemTypeId(.item[i]) and ItemStack.isStackable(.item[i]) and it != .item[i] then
return i
endif
set i = i + 1
endloop
return -1
endmethod
// stack item until it's used up
public method stackItem takes item it returns boolean
local integer slot
if (GetItemTypeId(it) == 0 or GetItemCharges(it) < 1) then
return false
endif
loop
set slot = .getItemStackableSlot(it)
exitwhen (slot ==-1)
call ItemStack.stackItems(.item[slot], it)
if (GetItemTypeId(it) == 0) then
return true
endif
endloop
return false
endmethod
// adds item at specific slot
public method addItemAtSlot takes item it, integer slot returns boolean
if (slot >= item.size() or slot < 0) then
return false
endif
if (.item[slot] != null) then
call .removeItem(.item[slot])
endif
set .item[slot] = it
// link data to item
if it != null then
set table.integer[GetHandleId(it)] = this
set table.integer[-GetHandleId(it)] = slot
// always hide added items
call SetItemVisible(it, false)
endif
return true
endmethod
// adds item at empty slot
public method addItem takes item it returns boolean
local integer slot = .getFreeSlot()
if (slot != -1) then
return .addItemAtSlot(it, slot)
endif
return false
endmethod
public method removeItem takes item it returns boolean
local integer handleId
local unit u
if (GetItemTypeId(it) == 0 or it == null) then
return false
endif
if .hasItem(it) then
set handleId = GetHandleId(it)
if not IsItemOwned(it) then
// only move items that are somewhere on map,
// else they will be dropped
set u = GetUnitById(this.inventory)
call SetItemVisible(it, true)
call SetItemPosition(it, GetUnitX(u), GetUnitY(u))
set u = null
endif
set .item[table[-handleId]] = null
// unlink data from item
call table.integer.remove(handleId)
call table.integer.remove(-handleId)
return true
endif
return false
endmethod
public method switchItems takes integer slot1, integer slot2 returns boolean
local item it1
local item it2
if slot1 >= item.size() or slot1 < 0 or slot2 >= item.size() or slot2 < 0 then
return false
endif
set it1 = .item[slot1]
set it2 = .item[slot2]
call .removeItem(.item[slot1])
call .removeItem(.item[slot2])
call .addItemAtSlot(it2, slot1)
call .addItemAtSlot(it1, slot2)
set it1 = null
set it2 = null
return true
endmethod
// removes all items from an inventory
public method flush takes nothing returns nothing
local integer i = .item.size() - 1
loop
exitwhen (i < 0)
call .removeItem(.item[i])
set i = i - 1
endloop
endmethod
endstruct
//! runtextmacro DEFINE_STRUCT_VECTOR("", "InventoryVector", "InventoryData")
struct Inventory extends array
readonly InventoryVector inventory
public integer vectorPos // current popsition in vector
public static constant group GROUP = CreateGroup() // all units that use custom inventories
private static constant trigger Handler = CreateTrigger()
public static unit Unit
public static integer InitialCharges
public static item InitialItem
public static integer InitialItemId
public static method register takes boolexpr bx returns triggercondition
return TriggerAddCondition(Handler, bx)
endmethod
public static method unregister takes triggercondition tc returns nothing
call TriggerRemoveCondition(Handler, tc)
endmethod
public method getInventoryAmount takes nothing returns integer
if IsUnitInGroup(GetUnitById(this), GROUP) then
return .inventory.size() - 1
else
return 0
endif
endmethod
public method hasFreeSlot takes nothing returns boolean
local integer i
if IsUnitInGroup(GetUnitById(this), GROUP) then
set i = .getInventoryAmount()
loop
exitwhen (i < 0)
if (.inventory[i].getFreeSlot()!= -1) then
return true
endif
set i = i - 1
endloop
endif
return false
endmethod
// if item is in any inventory
public method hasItem takes item it returns boolean
local integer i
if IsUnitInGroup(GetUnitById(this), GROUP) then
set i = .getInventoryAmount()
loop
exitwhen (i < 0)
if (.inventory[i].hasItem(it)) then
return true
endif
set i = i - 1
endloop
endif
return false
endmethod
// has any inventory a stackable item
public method hasStackableItem takes item it returns boolean
local integer i
if not IsUnitInGroup(GetUnitById(this), GROUP) or GetItemCharges(it) < 1 then
return false
endif
set i = .getInventoryAmount()
loop
exitwhen (i < 0)
if (.inventory[i].getItemStackableSlot(it) != -1) then
return true
endif
set i = i - 1
endloop
return false
endmethod
// stack item with any other items in any inventory
public method stackItem takes item it returns boolean
local integer i
local integer max
if IsUnitInGroup(GetUnitById(this), GROUP) then
set i = 0
set max = .getInventoryAmount()
loop
exitwhen (i > max)
if .inventory[i].stackItem(it) then
return true
else
set i = i + 1
endif
endloop
endif
return false
endmethod
public method addItem takes item it returns boolean
local integer max
local integer i
local integer slot
local unit u = GetUnitById(this)
set Unit = u
set InitialItem = it
set InitialItemId = GetItemTypeId(it)
set InitialCharges = GetItemCharges(it)
if InitialCharges > 0 then
// try to stack with items in current inventory
if UnitInventory.stackItem(u, it) then
call TriggerEvaluate(Handler)
set u = null
return true
endif
// try to stack with items in any inventory
if .stackItem(it) then
call TriggerEvaluate(Handler)
set u = null
return true
endif
endif
// item didn't stack -- try to find a free slot
if UnitHasItem(u, it) then
set slot = UnitInventory.getItemSlot(u, it)
call .inventory[.vectorPos].addItemAtSlot(it, slot)
call TriggerEvaluate(Handler)
set u = null
return true
else
set slot = UnitInventory.getFreeSlot(u)
if(slot != -1) then
if IsUnitInGroup(u, GROUP) then
call .inventory[.vectorPos].addItemAtSlot(it, slot)
endif
call TriggerEvaluate(Handler)
set u = null
return true
endif
endif
// find free slot in any inventory
set i = 0
set max = .getInventoryAmount()
loop
exitwhen i > max
if .inventory[i].addItem(it) then
call TriggerEvaluate(Handler)
set u = null
return true
endif
set i = i + 1
endloop
if (GetItemTypeId(it) != 0) and InitialCharges != GetItemCharges(it) then
// if this runs item was partialy added
call TriggerEvaluate(Handler)
else
// item was not added at all
endif
set u = null
return false
endmethod
// just adds a new inventory to the vector
// "_p" because there is also such a public method to add x inventories
private method addInventory_p takes nothing returns nothing
local InventoryData ID = InventoryData.create()
set ID.Inventory = this
call .inventory.push(ID)
endmethod
public method removeInventory takes integer whichInventory returns boolean
if (whichInventory < 1) or (whichInventory > .getInventoryAmount()) then
return false
endif
call .inventory[whichInventory].flush()
call .inventory[whichInventory].item.destroy()
call .inventory.erase(whichInventory, 1)
// fix current inventory position
if whichInventory == .vectorPos then
set .vectorPos = .vectorPos - 1
endif
return true
endmethod
// clears unit from all inventories
public method flush takes nothing returns boolean
local integer i
if IsUnitInGroup(GetUnitById(this), GROUP) then
set i = getInventoryAmount()
loop
exitwhen (i < 0)
call .inventory[i].flush()
call .inventory[i].item.destroy()
set i = i - 1
endloop
call .inventory.destroy()
call GroupRemoveUnit(GROUP, GetUnitById(this))
set .vectorPos = 0
return true
else
return false
endif
endmethod
private method apply takes nothing returns nothing
set .inventory = InventoryVector.create()
// immediatly create an inventory, so the
// current inventory can be applied
call .addInventory_p()
set .vectorPos = 0
call GroupAddUnit(GROUP, GetUnitById(this))
endmethod
public method setInventoryAmount takes integer amount returns boolean
local integer size
if (GetUnitTypeId(GetUnitById(this)) == 0) then
return false
endif
if (amount < 1) then
return .flush()
endif
if not IsUnitInGroup(GetUnitById(this), GROUP) then
call .apply()
endif
set size = .getInventoryAmount()
if (size == amount) then
return true
endif
if (amount > size) then
// add inventories
loop
exitwhen (size >= amount)
call .addInventory_p()
set size = size + 1
endloop
else
// amount < size
// remove inventories
loop
exitwhen (size <= amount)
call .removeInventory(size)
set size = size - 1
endloop
endif
return true
endmethod
public method addInventory takes integer amount returns boolean
return .setInventoryAmount(.getInventoryAmount() + amount)
endmethod
public static method operator[] takes unit u returns thistype
return GetUnitId(u)
endmethod
private static method onDeindex takes nothing returns boolean
return thistype(GetIndexedUnitId()).flush()
endmethod
private static method onInit takes nothing returns nothing
call OnUnitDeindex(function thistype.onDeindex)
endmethod
endstruct
struct UnitInventory extends array
// finds an other item than "it" in current inventory to stack with
public static method getItemStackableSlot takes unit u, item it returns integer
local integer i
local integer max
local integer iType
local item temp
if (GetItemTypeId(it) == 0 or GetItemCharges(it) < 1) then
return -1
endif
set max = UnitInventorySize(u)
set i = 0
set iType = GetItemTypeId(it)
loop
exitwhen (i >= max)
set temp = UnitItemInSlot(u, i)
if GetItemTypeId(temp) == iType and it != temp and ItemStack.isStackable(temp) then
set temp = null
return i
endif
set i = i + 1
endloop
set temp = null
return -1
endmethod
public static method getFreeSlot takes unit u returns integer
local integer i = 0
local integer max = UnitInventorySize(u)
loop
exitwhen i >= max
if UnitItemInSlot(u, i) == null then
return i
endif
set i = i + 1
endloop
return -1
endmethod
public static method getItemSlot takes unit u, item it returns integer
local integer i = 0
local integer max = UnitInventorySize(u)
loop
exitwhen i >= max
if UnitItemInSlot(u, i) == it then
return i
endif
set i = i + 1
endloop
return -1
endmethod
// tries to stack the item with current inventory until it's used up
public static method stackItem takes unit u, item it returns boolean
local integer slot
if (GetItemTypeId(it) == 0 or GetItemCharges(it) < 1) then
return false
endif
loop
set slot = getItemStackableSlot(u, it)
exitwhen (slot ==-1)
call ItemStack.stackItems(UnitItemInSlot(u, slot), it)
if (GetItemTypeId(it) == 0) then
return true
endif
endloop
return false
endmethod
private static method convertoToInventoryOrderId takes integer orderId returns integer
return (orderId - 852002)
endmethod
public static method operator order_enabled= takes boolean flag returns nothing
set order_enabled_p = flag
endmethod
// just to save some redundancy -- used to clean next function
//! textmacro UNIT_INTERFACE_ON_ORDER_CLEAN
set u = null
set itemTarget = null
set itemOrigin = null
return
//! endtextmacro
private static method onOrder takes nothing returns nothing
local item itemOrigin
local item itemTarget
local integer slot
local integer charges
local integer stackCharges
local integer itemType
local integer slotTarget
local integer slotOrigin
local unit u
local Inventory this
local integer max
local integer i
if order_enabled_p then
set itemOrigin = GetOrderTargetItem()
set itemType = GetItemTypeId(itemOrigin)
set slotTarget = convertoToInventoryOrderId(GetIssuedOrderId())
set u = GetTriggerUnit()
set this = GetUnitId(u)
if (slotTarget > -1 and slotTarget < 6) then
// get slot of item that is moved.
set slotOrigin = 0
loop
exitwhen UnitItemInSlot(u, slotOrigin) == itemOrigin
set slotOrigin = slotOrigin + 1
endloop
set charges = GetItemCharges(itemOrigin)
// potential split
if (slotOrigin == slotTarget) then
if charges > 1 then
// split
// we first abstractly try to split the item,
// so we can stack the split amount with other items
// without even creating a new item
set stackCharges = ItemSplit[itemType]
if (GetItemCharges(itemOrigin) <= stackCharges) then
set stackCharges = 1
endif
call SetItemCharges(itemOrigin, charges - stackCharges)
// try stack with current inventory
loop
set slot = UnitInventory.getItemStackableSlot(u, itemOrigin)
exitwhen (slot == -1)
set itemTarget = UnitItemInSlot(u, slot)
set max = ItemStack[GetItemTypeId(itemTarget)]
if max >= (GetItemCharges(itemTarget) + stackCharges) then
call SetItemCharges(itemTarget, GetItemCharges(itemTarget) + stackCharges)
//! runtextmacro UNIT_INTERFACE_ON_ORDER_CLEAN()
else
set stackCharges = stackCharges - (max - GetItemCharges(itemTarget))
call SetItemCharges(itemTarget, max)
endif
endloop
// try stack with any inventory
if IsUnitInGroup(u, Inventory.GROUP) then
set i = 0
set max = this.getInventoryAmount()
loop
exitwhen i > max
loop
set slot = this.inventory[i].getItemStackableSlot(itemOrigin)
exitwhen (slot == -1)
set itemTarget = this.inventory[i].item[slot]
set max = ItemStack[GetItemTypeId(itemTarget)]
if max >= (GetItemCharges(itemTarget) + stackCharges) then
call SetItemCharges(itemTarget, GetItemCharges(itemTarget) + stackCharges)
//! runtextmacro UNIT_INTERFACE_ON_ORDER_CLEAN()
else
set stackCharges = stackCharges - (max - GetItemCharges(itemTarget))
call SetItemCharges(itemTarget, max)
endif
endloop
set i = i + 1
endloop
endif
// Item didn't stack, or did stack only partialy.
// We create the rest of the split and try to find a free slot
set itemTarget = CreateItem(itemType, GetUnitX(u), GetUnitY(u))
call SetItemCharges(itemTarget, stackCharges)
// in current incentory
set slot = UnitInventory.getFreeSlot(u)
if(slot != -1) then
set pickup_enabled_p = false
call UnitAddItem(u, itemTarget)
set pickup_enabled_p = true
if IsUnitInGroup(u, Inventory.GROUP) then
call this.inventory[this.vectorPos].addItemAtSlot(itemTarget, slot)
endif
//! runtextmacro UNIT_INTERFACE_ON_ORDER_CLEAN()
endif
// find free slot in any inventory
if IsUnitInGroup(u, Inventory.GROUP) then
set i = 0
set max = this.getInventoryAmount()
loop
exitwhen i > max
if this.inventory[i].addItem(itemTarget) then
//! runtextmacro UNIT_INTERFACE_ON_ORDER_CLEAN()
endif
set i = i + 1
endloop
endif
// if this runs, the split was dropped at unit's position
else
// not splitable, do nothing
endif
else
// order was on other slot, try to strack
if not ItemStack.stackItems(itemOrigin, UnitItemInSlot(u, slotTarget)) then
if IsUnitInGroup(u, Inventory.GROUP) then
set drop_enabled = false
call this.inventory[this.vectorPos].switchItems(slotOrigin, slotTarget)
set drop_enabled = true
endif
endif
endif
endif
//! runtextmacro UNIT_INTERFACE_ON_ORDER_CLEAN()
endif
endmethod
public static method operator pickUp_enabled= takes boolean flag returns nothing
set pickup_enabled_p = flag
endmethod
private static method onPickup takes nothing returns nothing
if pickup_enabled_p then
call Inventory[GetTriggerUnit()].addItem(GetManipulatedItem())
endif
endmethod
public static method operator drop_enabled= takes boolean flag returns nothing
set drop_enabled_p = flag
endmethod
// clean from inventory
private static method onDrop takes nothing returns nothing
local item it
if drop_enabled_p then
if IsUnitInGroup(GetTriggerUnit(), Inventory.GROUP) then
set it = GetManipulatedItem()
if InventoryData.isItemOwned(it) then
call InventoryData.getItemVector(it).removeItem(it)
endif
set it = null
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.onPickup)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
endmethod
endstruct
endlibrary
library Bag /* v2.1a -- related: hiveworkshop.com/threads/bag-v1-6-1.252002/
*/ requires /*
*/ Inventory /* hiveworkshop.com/threads/bag-v1-6-1.252002/
Information
¯¯¯¯¯¯¯¯¯¯¯
Extenstion library to "Inventory".
It allows units the usage of the Bag Abilty,
and handles all kind of item drop and pickup internaly with using the Inventor's API.
Mechanics
¯¯¯¯¯¯¯¯¯¯
Items registered in Inventory are hidden by default.
The Bag library switches between the current items inside the unit inventory,
and the hidden items from Inventory, depending on the usage of the Bag ability.
Importing
¯¯¯¯¯¯¯¯¯¯
Copy the bag ability to your map.
Ensure that ABILITY_ID (directly after API docu) hold the bag's rawcode.
*/
// --- API ---
//! novjass
struct Bag
// Functions to handle bags. (The bag ability will be added/removed automaticaly)
static method getBagAmount takes unit u returns integer
static method setBagAmount takes unit u, integer amount returns boolean
static method addBag takes unit u, integer amount returns nothing
static method getCurrentBag takes unit u returns integer
static method setCurrentBag takes unit u, integer whichBag returns boolean
static method removeBag takes unit u, integer whichBag returns boolean
// Functions to handle inventory.
static method getItem takes unit u, integer whichBag, integer whichSlot returns item
static method setItem takes unit u, item it, integer whichBag, integer whichSlot returns boolean
static method removeItem takes unit u, integer whichBag, item it returns boolean
// The size of a bag equals the "UnitInventorySize()" of the unit when registered.
// You can registder code that runs when a unit uses bag ability.
static method register takes boolexpr bx returns triggercondition
static method unregister takes triggercondition tc returns nothing
//inside the code refer to:
static unit Unit
//! endnovjass
// ==== End API ====
struct Bag extends array
private static constant integer ABILITY_ID = 'A000'
private static constant trigger Handler = CreateTrigger()
public static unit Unit
public static method register takes boolexpr bx returns triggercondition
return TriggerAddCondition(Handler, bx)
endmethod
public static method unregister takes triggercondition tc returns nothing
call TriggerRemoveCondition(Handler, tc)
endmethod
public static method getBagAmount takes unit u returns integer
return Inventory[u].getInventoryAmount()
endmethod
public static method setBagAmount takes unit u, integer amount returns boolean
local Inventory this
local integer size = getBagAmount(u)
local integer i
local boolean initCurrentBag
if amount == size then
return true
endif
set this = GetUnitId(u)
set initCurrentBag = false
if amount < 1 then
if size < 1 then
return true
endif
// Remove all bags from unit
if getCurrentBag(u) != 0 then
call setCurrentBag(u, 0)
set Unit = u
call TriggerEvaluate(Handler)
endif
call UnitRemoveAbility(u, ABILITY_ID)
return Inventory[u].flush()
endif
if size > 0 then
// Remove some bags from unit
if amount < size then
if amount < getCurrentBag(u) then
call setCurrentBag(u, amount)
set Unit = u
call TriggerEvaluate(Handler)
endif
loop
exitwhen amount >= size
call Inventory[u].removeInventory(size)
set size = size - 1
endloop
endif
else
set initCurrentBag = UnitAddAbility(u, ABILITY_ID)
endif
call Inventory[u].setInventoryAmount(amount)
loop
exitwhen size > amount
call this.inventory[size].init(5)
set size = size + 1
endloop
if initCurrentBag then
// apply current inventory to first bag
set size = UnitInventorySize(u)
loop
exitwhen size < 0
call this.inventory[0].addItemAtSlot(UnitItemInSlot(u, size), size)
set size = size - 1
endloop
endif
return true
endmethod
public static method addBag takes unit u, integer amount returns nothing
call setBagAmount(u, getBagAmount(u) + amount)
endmethod
private static constant integer SLOT_OFFSET = 852002
private static method openInventory takes unit u, integer vectorPos returns nothing
local Inventory this = GetUnitId(u)
local integer i = this.inventory[vectorPos].item.size() - 1
local item it
set UnitInventory.pickUp_enabled = false
set UnitInventory.order_enabled = false
loop
exitwhen i < 0
set it = this.inventory[vectorPos].item[i]
if it != null then
call SetItemVisible(it, true)
call UnitAddItem(u, it)
// For safety, move item to wanted slot
call IssueTargetOrderById(u, SLOT_OFFSET + i, it)
endif
set it = null
set i = i - 1
endloop
set UnitInventory.pickUp_enabled = true
set UnitInventory.order_enabled = true
set it = null
endmethod
private static method dropCurrentInventory takes unit u returns nothing
local integer i = UnitInventorySize(u)
local item it
set UnitInventory.drop_enabled = false
loop
exitwhen i < 0
set it = UnitItemInSlot(u, i)
if (it != null) then
call UnitRemoveItem(u, it)
call SetItemVisible(it, false)
endif
set i = i - 1
endloop
set UnitInventory.drop_enabled = true
endmethod
public static method getCurrentBag takes unit u returns integer
if getBagAmount(u) == 0 then
return -1
else
return Inventory[u].vectorPos
endif
endmethod
public static method setCurrentBag takes unit u, integer whichBag returns boolean
if (getBagAmount(u) == 0 or (not UnitAlive(u)) or (whichBag > getBagAmount(u)) )then
return false
endif
if (whichBag == getCurrentBag(u) ) then
return true
endif
call dropCurrentInventory(u)
set Inventory[u].vectorPos = whichBag
call openInventory(u, whichBag)
return true
endmethod
private static method onCast takes nothing returns nothing
local unit u
if (GetSpellAbilityId() == ABILITY_ID) then
set u = GetTriggerUnit()
if getCurrentBag(u) == getBagAmount(u) then
// open 1st bag
call setCurrentBag(u, 0)
else
// open next bag
call setCurrentBag(u, getCurrentBag(u) + 1)
endif
set Unit = u
call TriggerEvaluate(Handler)
endif
set u = null
endmethod
public static method removeBag takes unit u, integer whichBag returns boolean
return Inventory[u].removeInventory(whichBag)
endmethod
public static method getItem takes unit u, integer whichBag, integer whichSlot returns item
return Inventory[u].inventory[whichBag].item[whichSlot]
endmethod
public static method removeItem takes unit u, integer whichBag, item it returns boolean
return Inventory[u].inventory[whichBag].removeItem(it)
endmethod
public static method setItem takes unit u, item it, integer whichBag, integer whichSlot returns boolean
return Inventory[u].inventory[whichBag].addItemAtSlot(it, whichSlot)
endmethod
private static method onInit takes nothing returns nothing
//call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onCast)
endmethod
endstruct
endlibrary
library FullInventory /* v1.1a -- related: hiveworkshop.com/threads/bag-v1-6-1.252002/
*/ requires /*
*/ Inventory /* hiveworkshop.com/threads/bag-v1-6-1.252002/
*/ OrderType /* hiveworkshop.com/threads/ordertype.291223/
*/ TimerUtils /* Vexorian -- wc3c.net/showthread.php?t=101322
Information
¯¯¯¯¯¯¯¯¯¯¯
Extenstion library to "Inventory".
It allows to pickup/stack items with having full inventory.
The default wc3 mechnaics don't allow picking up items with full inventory.
Mechanics
¯¯¯¯¯¯¯¯¯¯
When a unit gets a move/smart order towards an item, and at same time
it has full inventory, the unit would stop with error "Full Inventory".
The system ignores this error, and orders the unit to move to x/y of wanted item,
so it looks like the unit accpeted the first order. When the unit is close enough
of the target item, the system will emulate a possible pickup/stacking with the item.
ATTENTION.
The default wc3 mechanis will play an eror sound and display a error message,
when a unit tries to pickup an item with full inventory.
You easily can disable both.
GameInterface -> Text-Message-FullInventory -> Shift-double click it, and write in a space character.
GameInterface -> Sound-FullInventory -> Shift-double click it, and write in a space character.
*/
// API -- You can registder code that runs when a unit tries to add an item to a full inventory.
//! novjass
struct FullInventory
static method register takes boolexpr bx returns triggercondition
static method unregister takes triggercondition tc returns nothing
//inside the code refer to:
static unit Unit
//! endnovjass
// ==== End API ====
struct FullInventory extends array
// set this to something close to the value inside your gameplay constants
private static constant real PICKUP_RANGE = 120.
// ============================================================== //
private unit whichUnit
private item whichItem
private real x
private real y
private timer clock
private static constant trigger Handler = CreateTrigger()
public static unit Unit
public static method register takes boolexpr bx returns triggercondition
return TriggerAddCondition(Handler, bx)
endmethod
public static method unregister takes triggercondition tc returns nothing
call TriggerRemoveCondition(Handler, tc)
endmethod
private method destroy takes nothing returns nothing
if IsUnitInGroup(.whichUnit, GROUP) then
call GroupRemoveUnit(GROUP, .whichUnit)
call ReleaseTimer(.clock)
set .whichItem = null
set .whichUnit = null
endif
endmethod
// check if item is still available to be picked up
private static method itemValidate takes item it returns boolean
return GetItemTypeId(it) != 0 and IsItemVisible(it) and not IsItemOwned(it)
endmethod
// callback perodicly checks distance from unit and item, and then adds to inventory
private static constant real INTERVAL = 0.1
private static method callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if (itemValidate(.whichItem)) then
if (IsUnitInRangeXY(.whichUnit, .x, .y, PICKUP_RANGE)) then
// double check with the current x/y of the item, to ensure the item wasn't moved somewhere else, however
if IsUnitInRangeXY(.whichUnit, GetItemX(.whichItem), GetItemY(.whichItem), PICKUP_RANGE + 1) then
call Inventory[.whichUnit].addItem(.whichItem)
endif
set cancel_enabled_p = false
call IssueImmediateOrderById(.whichUnit, ORDER_stop)
set cancel_enabled_p = true
call .destroy()
endif
else
set cancel_enabled_p = false
call IssueImmediateOrderById(.whichUnit, ORDER_stop)
set cancel_enabled_p = true
call .destroy()
endif
endmethod
// start periodic callback
private static constant group GROUP = CreateGroup()
private static method create takes unit u, item it returns thistype
local thistype this = GetUnitId(u)
set .whichUnit = u
set .whichItem = it
set .x = GetItemX(it)
set .y = GetItemY(it)
set cancel_enabled_p = false
call IssuePointOrderById(u, ORDER_move, .x, .y)
set cancel_enabled_p = true
if not IsUnitInGroup(u, GROUP) then
set .clock = NewTimerEx(this)
call TimerStart(.clock, INTERVAL, true, function thistype.callback)
call GroupAddUnit(GROUP, u)
endif
return this
endmethod
// kein patrol, no
private static method isMoveOrder takes integer orderId returns boolean
return orderId == ORDER_smart or orderId == ORDER_move
endmethod
// onTargetOrder actually
private static method onOrder takes nothing returns nothing
local unit u = GetTriggerUnit()
local item it = GetOrderTargetItem()
if it != null and isMoveOrder(GetIssuedOrderId()) and (UnitInventory.getFreeSlot(u) == -1) then
// order was on item, and inventory is full
if (it != thistype(GetUnitId(u)).whichItem) then
if IsUnitInGroup(u, Inventory.GROUP) then
if (Inventory[u].hasFreeSlot() or Inventory[u].hasStackableItem(it)) then
// start callback
call create(u, it)
else
set Unit = u
call TriggerEvaluate(Handler)
endif
elseif (UnitInventory.getItemStackableSlot(u, it) != -1) then
// start callback
call create(u, it)
else
set Unit = u
call TriggerEvaluate(Handler)
endif
else
// targeted same item, do nothing
// actually we must order unit to move again,
// because by default it would stop, as it received a move order on an item with having ful inventory
set cancel_enabled_p = false
call IssuePointOrderById(u, ORDER_move, GetItemX(it), GetItemY(it))
set cancel_enabled_p = true
endif
elseif IsUnitInGroup(u, GROUP) then
// targeted something different, stop callback
call thistype(GetUnitId(u)).destroy()
endif
set u = null
set it = null
endmethod
/*
onCancel will cancel the periodic check for any new no-target, or point-orders the unit gets
because then, the unit obviously doesn't wanna pick up the item anymore.
problem:
The problem is that the unit has currently a "move" order onto a point,
instead of a default itemTarget-order.
So, when a Queue-Order like Avatar finished, then it will automaticaly order this normal move-point order again.
The problem is, by nature we don't trust move orders, and usually cancel the periodic check.
So the trick is, after we ordered a Queue-Order, we allow the same unit to make ONE move order,
and if the move order is exactly the same as the old, means same x/y and onto a point, then we allow it.
We also make a item validation check, to check if item visible at that very position, and pickable.
These together hopefully ensure not to fail.
*/
private static boolean cancel_enabled_p = true
private boolean dirtyTrick
private static method onCancel takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer orderId = GetIssuedOrderId()
local thistype this
if IsUnitInGroup(u, GROUP) and cancel_enabled_p and OrderType[orderId] != OrderType.INSTANT then
set this = thistype(GetUnitId(u))
if OrderType[orderId] == OrderType.QUEUE then
set .dirtyTrick = true
elseif not .dirtyTrick or not (orderId == ORDER_move) or not (GetOrderPointX() == .x) or not (GetOrderPointY() == .y) and itemValidate(.whichItem) then
set .dirtyTrick = false
call .destroy()
endif
endif
set u = null
endmethod
private static method onDeindex takes nothing returns boolean
if IsUnitInGroup(GetIndexedUnit(), GROUP) then
call thistype(GetUnitId(GetIndexedUnit())).destroy()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
//call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onCancel)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onCancel)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onCancel)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onCancel)
call OnUnitDeindex(function thistype.onDeindex)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
endmethod
endstruct
endlibrary
module ItemStackConfig /* v1.0a -- related: hiveworkshop.com/threads/bag-v1-6-1.252002/
If no specific limit is defined the DEFAULT_STACK_LIMIT will be applied.
*/
public static constant integer DEFAULT_STACK_LIMIT = 3
//! novjass
struct ItemStack
static method operator [] takes integer itemType returns integer
// returns stack limit for this item type
static method exists takes integer itemType returns boolean
// returns if a stack limit was defined
static method SetStackLimit takes integer itemType, integer limit returns nothing
// only here in this onInit allowed to be used
//! endnovjass
private static method onInit takes nothing returns nothing
// Example for big healing potion:
// call SetStackLimit('pghe', 4)
call SetStackLimit('pghe', 4)
endmethod
endmodule
module ItemSplitConfig /* v1.0a -- related: hiveworkshop.com/threads/bag-v1-6-1.252002/
If no specific limit is defined the default split amount will be "1".
Also, if the item does have less stacks than the defined amount it will also result in an "1" amount split.
*/
//! novjass
struct ItemSplit
static method operator [] takes integer itemType returns integer
// returns split amount for this item type
static method exists takes integer itemType returns boolean
// returns if a split amount was defined
static method SetSplitAmount takes integer itemType, integer amount returns nothing
// only here in this onInit allowed to be used
//! endnovjass
private static method onInit takes nothing returns nothing
// Example for big healing potion:
//call SetSplitAmount('pghe', 2)
call SetSplitAmount('pghe', 2)
endmethod
endmodule
v2.1 - fixes - some new functions v2.0a tiny change v2.0 Inventory -- a completly new system v1.0 Bag - until v1.6 |