kingkingyyk3
Visitor (Welcome to the Jungle, Baby!)
- Reaction score
- 216
Advanced Item Indexing v1.0.6
JASS:
library AII
/*
Advanced Item Indexing ~v1.0.6~
by kingking
Advanced Item Indexing is a library that indexes your items and provide
a clean interface for you to manage items.
======
APIs :
======
GetItemId(item) -> integer
- Returns item's id.
GetItemById(integer) -> item
- Returns item with assigned index
===============
AItem Struct :
===============
private struct Data extends array
private method AII_onCreate takes nothing returns nothing
endmethod
implement AII
endstruct
Members :
this.item -> item
Static methods :
AII_filter(item) -> boolean
- Serves as filter for struct creation.
Static method operators :
<Struct>[item] -> struct
- Return assigned struct.
Methods :
AII_onCreate()
- Will be fired when item is indexed.
AII_onDestroy()
- Will be fired when item is removed.
AII_addLock()
- Add lock to struct.
AII_removeLock()
- Remove lock from struct
***The index will only be removed when all locks are released.
===============
Limitation :
===============
There is no actual O(1) operation for detecting newly created items.
================
Implementation :
================
1) Copy this trigger into your map.
2) Replace all CreateItem with CreateItemEx in your map.
3) Adjust the setting if you want.
4) Enjoy!
================
Settings :
================ */
globals
private constant integer MAX_THRASH=100
//Max threshold for leaked event for death detection trigger.
endglobals
/*
================
Requirement :
================
Jasshelper v0.A.2.B or later.
*/
globals
private trigger alloItem=CreateTrigger()
private trigger dealloItem=CreateTrigger() //Callback triggers
private trigger pickUpItem=CreateTrigger()
private trigger sellItem=CreateTrigger()
private trigger pawnItem=CreateTrigger() //Event triggers
private integer EventStruct=0 //Current callback data
private item array HashedItem
private boolean IsInited=false
endglobals
static if LIBRARY_UID then
globals
private trigger dropItem=CreateTrigger()
endglobals
endif
private function FireEvent takes trigger trig, integer dat returns nothing
local integer old=EventStruct
set EventStruct=dat
call TriggerEvaluate(trig)
set EventStruct=old
//Bye bye to stack. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile :)" loading="lazy" data-shortname=":)" />
endfunction
globals
private constant integer HASH_NEXT=53
private constant integer MAX_HASH_VALUE=8191
private integer array HashedInt
endglobals
private function Hash takes widget w returns integer
local integer int=GetHandleId(w)
local integer hash=int-(int/MAX_HASH_VALUE)*MAX_HASH_VALUE
loop
exitwhen HashedInt[hash]==int
if HashedInt[hash]==0 then
set HashedInt[hash]=int
return hash
endif
set hash=hash+HASH_NEXT
if hash>=MAX_HASH_VALUE then
set hash=hash-MAX_HASH_VALUE
endif
endloop
return hash
endfunction
//Handy function from Jesus4Lyf
private function RemoveHash takes integer int returns nothing
local integer hash=int-(int/MAX_HASH_VALUE)*MAX_HASH_VALUE
loop
if HashedInt[hash]==int then
set HashedInt[hash]=0
return
endif
set hash=hash+HASH_NEXT
if hash>=MAX_HASH_VALUE then
set hash=hash-MAX_HASH_VALUE
endif
endloop
//To cleaned up unused values.
endfunction
private struct ItemStruct
private static trigger trig=CreateTrigger()
private static integer thrashCount=0
item it
private integer hid
private integer lockLevel
private thistype prevA //A=Active, B=Thrash
private thistype nextA
private static method refreshEventTrigger takes nothing returns nothing
//See if the thrash is trigger is necessary to clean or not.
local thistype this
set thistype.thrashCount=thistype.thrashCount+1
if thistype.thrashCount>=MAX_THRASH then
//If the thrash is over the default number, refresh the trigger.
call DisableTrigger(this.trig)
call TriggerClearConditions(this.trig)
call DestroyTrigger(this.trig)
set thistype.trig=CreateTrigger()
call TriggerAddCondition(thistype.trig,Condition(function thistype.onDeath))
//Reset the trigger.
set this=thistype(0).nextA
loop
exitwhen this==0
call TriggerRegisterDeathEvent(this.trig,this.it)
//Reregister back the event.
set this=this.nextA
endloop
set thistype.thrashCount=0
endif
endmethod
private static method onDeath takes nothing returns boolean
call thistype(GetItemUserData(HashedItem[Hash(GetTriggerWidget())])).manageLock(false) //Get the attached item.
//Item is removed.
return false
endmethod
private static thistype array toFireDeallocate
private static integer tFDCount=0
private method addToActive takes nothing returns nothing
set thistype(0).nextA.prevA=this
set this.nextA=thistype(0).nextA
set thistype(0).nextA=this
set this.prevA=thistype(0)
endmethod
private method removeFromActive takes nothing returns nothing
set this.prevA.nextA=this.nextA
set this.nextA.prevA=this.prevA
endmethod
method remove takes nothing returns nothing
call FireEvent(dealloItem,this)
call this.removeFromActive()
call RemoveHash(this.hid)
call thistype.refreshEventTrigger()
set this.it=null
if IsInited then
call this.deallocate()
else
set tFDCount=tFDCount+1
set toFireDeallocate[tFDCount]=this
endif
endmethod
method manageLock takes boolean b returns nothing
if b then
set this.lockLevel=this.lockLevel+1
else
set this.lockLevel=this.lockLevel-1
if this.lockLevel==0 then
call this.remove()
endif
endif
endmethod
static method load takes item i returns thistype
local thistype this
set this=GetItemUserData(i)
if i==null then //If item is null, then no point to index it..
return 0
endif
if this==0 then //Okay, the item is not indexed.
set this=thistype.allocate()
set this.it=i
set this.hid=GetHandleId(this.it)
call TriggerRegisterDeathEvent(thistype.trig,this.it) //Register item death event.
set HashedItem[Hash(this.it)]=this.it//Attach the item to itself.
set this.lockLevel=1
call SetItemUserData(i,this)
call FireEvent(alloItem,this) //Fire event!
call this.addToActive()
endif
return this
endmethod
static method refireAllocateCallback takes conditionfunc c returns nothing
local trigger trig=CreateTrigger()
local thistype this=thistype(0).nextA
call TriggerAddCondition(trig,c)
loop
exitwhen this==0
call FireEvent(trig,this)
set this=this.nextA
endloop
call DestroyTrigger(trig)
set trig=null
endmethod
static method refireDeallocateCallback takes conditionfunc c returns nothing
local trigger trig=CreateTrigger()
local integer count=tFDCount
call TriggerAddCondition(trig,c)
loop
exitwhen count==0
call FireEvent(trig,toFireDeallocate[count])
set count=count-1
endloop
call DestroyTrigger(trig)
set trig=null
endmethod
private static method flag takes nothing returns nothing
//Indicates the game is initialized.
local integer count=tFDCount
loop
exitwhen count==0
call toFireDeallocate[count].deallocate()
set count=count-1
endloop
set IsInited=true
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(thistype.trig,Condition(function thistype.onDeath))
call TimerStart(CreateTimer(),0.0,false,function thistype.flag)
endmethod
endstruct
function GetItemId takes item i returns integer
return GetItemUserData(i)
endfunction
function GetItemById takes integer i returns item
return ItemStruct(i).it
endfunction
function CreateItemEx takes integer itemid, real x, real y returns item
return ItemStruct.load(CreateItem(itemid,x,y)).it
endfunction
private module AIIInit
private static method onInit takes nothing returns nothing
local group g=CreateGroup()
local rect world=GetWorldBounds()
call EnumItemsInRect(world,Condition(function thistype.indexPreplaced),null)
call GroupEnumUnitsInRect(g,world,Condition(function thistype.indexItemOnUnit))
call RemoveRect(world)
call DestroyGroup(g)
set g=null
set world=null
call TriggerRegisterAnyUnitEventBJ(pickUpItem,EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerRegisterAnyUnitEventBJ(sellItem,EVENT_PLAYER_UNIT_SELL_ITEM)
call TriggerRegisterAnyUnitEventBJ(pawnItem,EVENT_PLAYER_UNIT_PAWN_ITEM)
call TriggerAddCondition(pickUpItem,Condition(function thistype.onPickUp))
call TriggerAddCondition(sellItem,Condition(function thistype.onSell))
call TriggerAddCondition(pawnItem,Condition(function thistype.onPawn))
endmethod
endmodule
private struct Initializer extends array
private static method onPickUp takes nothing returns boolean
call ItemStruct.load(GetManipulatedItem())
return false
endmethod
private static method onSell takes nothing returns boolean
call ItemStruct.load(GetSoldItem())
return false
endmethod
private static method onPawn takes nothing returns boolean
static if LIBRARY_UID then
call FireEvent(dropItem,ItemStruct.load(GetSoldItem()))
endif
call ItemStruct.load(GetSoldItem()).manageLock(false)
return false
endmethod
private static method indexPreplaced takes nothing returns boolean
call ItemStruct.load(GetFilterItem())
return false
endmethod
private static method indexItemOnUnit takes nothing returns boolean
local unit u=GetFilterUnit()
local integer i = 0
local integer maxSize = UnitInventorySize(u)
local ItemStruct it
loop
exitwhen i == maxSize
call ItemStruct.load(UnitItemInSlot(u,i))
set i = i + 1
endloop
set u=null
return false
endmethod
implement AIIInit
endstruct
module AII
readonly boolean flag
method operator item takes nothing returns item
return ItemStruct(this).it
endmethod
method AII_addLock takes nothing returns nothing
call ItemStruct(this).manageLock(true)
endmethod
method AII_removeLock takes nothing returns nothing
call ItemStruct(this).manageLock(false)
endmethod
static method operator [] takes item i returns thistype
return GetItemId(i)
endmethod
private static method aiicreate takes nothing returns boolean
local thistype this=EventStruct
static if thistype.AII_filter.exists then
if thistype.AII_filter(this.item) then
static if thistype.AII_onCreate.exists then
call this.AII_onCreate()
endif
set this.flag=true
endif
else
static if thistype.AII_onCreate.exists then
call this.AII_onCreate()
endif
set this.flag=true
endif
return false
endmethod
private static method aiidestroy takes nothing returns boolean
local thistype this=EventStruct
if this.flag then
static if thistype.AII_onDestroy.exists then
call this.AII_onDestroy()
endif
set this.flag=false
endif
return false
endmethod
static if thistype.AII_onDrop.exists then
private static method aiidrophook takes nothing returns boolean
if thistype(EventStruct).flag then
call thistype(EventStruct).AII_onDrop()
endif
return false
endmethod
endif
private static method onInit takes nothing returns nothing
call TriggerAddCondition(alloItem,Condition(function thistype.aiicreate))
call TriggerAddCondition(dealloItem,Condition(function thistype.aiidestroy))
static if thistype.AII_onDrop.exists then
call TriggerAddCondition(dropItem,Condition(function thistype.aiidrophook))
endif
if IsInited==false then
call ItemStruct.refireAllocateCallback(Condition(function thistype.aiicreate))
call ItemStruct.refireDeallocateCallback(Condition(function thistype.aiidestroy))
endif
endmethod
endmodule
endlibrary
Test :
JASS:
scope test
private struct tester
private static item it=null
private method AII_onCreate takes nothing returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,GetItemName(this.item) + " is allocated.")
endmethod
private method AII_onDestroy takes nothing returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0.0,0.0,GetItemName(this.item) + " is deallocated.")
endmethod
private static method createItem takes nothing returns nothing
call RemoveItem(it)
set it=CreateItemEx(ChooseRandomItem(-1),0.0,0.0)
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(),0.01,true,function thistype.createItem)
endmethod
implement AII
endstruct
endscope
It is very efficient, the test script ran flawlessly on my computer without FPS dropping.
The item removal detection is O(1) too. :thup:
The drawback is, you have to replace all CreateItem with CreateItemEx.
AII-based UID :
JASS:
/////////////////////////////////////////////////////////
// Units' Inventory Data (UID) v3.0.2
// by kingking
//
//=================
// What is this?
//=================
// ~ UID is meant to be replacing missing native, like
// GetItemOwner, GetItemInventoryPosition.
// ~ UID is able to detect an item carrier changes position
// of item(s) in it's inventory.
//
//=========================================
// Enough propagation? Let's try it out!
//=========================================
// Functions :
// ~ UID_RegisterOnMoveItem(trigger) -> EventReg
// ~ UID_UnregisterOnMoveItem(trigger)
//
// ~ GetItemOwner(item) -> unit
// ~ GetItemInventoryPosition(item) -> integer
// (returns -1 if it is not carried by a unit.)
// ~ GetMovedItem() -> item
// ~ GetLastInventoryPosition() -> integer
// (Position of item in inventory before changing.)
// ~ GetUnitItemInSlot(unit,slot) -> item
//
// Constants :
// ~ UID_FIRST_INVENTORY_ID
// ~ UID_LAST_INVENTORY_ID
//
// Modules (Requires implementation of AII module) :
// ~ implement ItemEvents
//
// ~ Methods :-
// AII_onPickUp() -> nothing
// - Will be fired when item is picked up.
// AII_onDrop() -> nothing
// - Will be fired when item is dropped.
// AII_onMove(integer lastPosition) -> nothing
// - Will be fired when item is moved in inventory.
//
// ~ Constants :-
// this.owner -> unit
// - Return the item's owner.
//
// Sample module implementation :
// private struct Data
// private method AII_onCreate takes nothing returns nothing
// endmethod
// private method AII_onDestroy takes nothing returns nothing
// endmethod
// private method AII_onPickUp takes nothing returns nothing
// endmethod
// private method AII_onDrop takes nothing returns nothing
// endmethod
// private method AII_onMove takes integer lastPosition returns nothing
// endmethod
// implement AII
// implement ItemEvents
// endstruct
//
//================
// Details :
//================
// Slot Position's Mapping :
// -------------
// | 0 | 1 |
// -------------
// | 2 | 3 |
// ------------
// | 4 | 5 |
// -------------
//
//==============
// Requires :
//==============
// Jasshelper 0.A.2.B
// AII
// AIDS
// Event
//
//==============
// Credits :
//==============
// Executor - Finding out the slot swapping event.
// Jesus4Lyf - Making AIDS.
//
//////////////////////////////////////////////////////////
library UID requires AIDS, Event, AII
globals
public constant integer FIRST_INVENTORY_ID = 852002
public constant integer LAST_INVENTORY_ID = 852007
endglobals
globals
private trigger ModulePickUpTrig=CreateTrigger()
private trigger ModuleDropTrig=CreateTrigger()
private trigger ModuleMoveTrig=CreateTrigger()
private Event OnMoveItem
private item MovedItem=null
private integer LastPosition=-1
endglobals
public function RegisterOnMoveItem takes trigger trig returns EventReg
return OnMoveItem.register(trig)
endfunction
public function UnregisterOnMoveItem takes trigger trig returns nothing
call OnMoveItem.unregister(trig)
endfunction
private function FireEvent takes trigger trig, item it, integer pos returns nothing
local item it2=MovedItem
local integer pos2=LastPosition
set MovedItem=it
set LastPosition=pos
call TriggerEvaluate(trig)
set MovedItem=it2
set LastPosition=pos2
set it2=null
endfunction
private keyword items
private keyword UnitStruct
private struct ItemStruct extends array
integer position
unit owner
private method AII_onCreate takes nothing returns nothing
set this.position = -1
set this.owner = null
endmethod
implement AII
private static method onInit takes nothing returns nothing
set thistype(0).position = -1
endmethod
endstruct
private type items extends ItemStruct array [6]
private struct UnitStruct extends array
items items
private method AIDS_onCreate takes nothing returns nothing
set this.items = items.create()
endmethod
private method AIDS_onDestroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i == 6
set this.items<i>.owner = null
set this.items<i>.position = -1
set i = i + 1
endloop
call this.items.destroy()
endmethod
//! runtextmacro AIDS()
endstruct
function GetItemOwner takes item i returns unit
return ItemStruct<i>.owner
endfunction
function GetItemInventoryPosition takes item i returns integer
return ItemStruct<i>.position
endfunction
function GetMovedItem takes nothing returns item
return MovedItem
endfunction
function GetLastInventoryPosition takes nothing returns integer
return LastPosition
endfunction
function GetUnitItemInSlot takes unit u, integer slot returns item
return UnitStruct<u>.items[slot].item
endfunction
private struct Initializer extends array
private static method onPickUp takes nothing returns boolean
local ItemStruct i = ItemStruct[GetManipulatedItem()]
local integer pos = 0
set i.owner = GetTriggerUnit()
loop
exitwhen pos==6
if UnitItemInSlot(i.owner,pos)==GetManipulatedItem() then
set i.position = pos
set UnitStruct[i.owner].items[pos]=i
call FireEvent(ModulePickUpTrig,null,i)
return false
endif
set pos = pos + 1
endloop
return false
endmethod
private static method onDrop takes nothing returns boolean
local ItemStruct i = ItemStruct[GetManipulatedItem()]
call FireEvent(ModuleDropTrig,null,i)
set UnitStruct[i.owner].items[i.position] = 0
set i.owner = null
set i.position = -1
return false
endmethod
private static method onMove takes nothing returns boolean
local integer orderId = GetIssuedOrderId()
local unit u = GetTriggerUnit()
local integer originalPos
local ItemStruct movedItem
local ItemStruct originalItem
set orderId = orderId - FIRST_INVENTORY_ID
set movedItem = ItemStruct[GetOrderTargetItem()]
set originalItem = UnitStruct<u>.items[orderId]
if movedItem != originalItem then
if originalItem != 0 then
set originalItem.position = movedItem.position
endif
set originalPos = movedItem.position
set movedItem.position = orderId
set UnitStruct<u>.items[orderId] = movedItem
set UnitStruct<u>.items[originalPos] = originalItem
call FireEvent(ModuleMoveTrig,movedItem.item,originalPos)
endif
set u = null
return false
endmethod
private static method hookedMoveEvent takes nothing returns boolean
call OnMoveItem.fire()
return false
endmethod
private static method checkOrder takes nothing returns boolean
return GetIssuedOrderId() >= FIRST_INVENTORY_ID and GetIssuedOrderId() <= LAST_INVENTORY_ID
endmethod
private static method onInit takes nothing returns nothing
local trigger pickUp = CreateTrigger()
local trigger drop = CreateTrigger()
local trigger move = CreateTrigger()
set OnMoveItem = Event.create()
call TriggerRegisterAnyUnitEventBJ(pickUp,EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerRegisterAnyUnitEventBJ(drop,EVENT_PLAYER_UNIT_DROP_ITEM)
call TriggerRegisterAnyUnitEventBJ(move,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(pickUp,Condition(function thistype.onPickUp))
call TriggerAddCondition(drop,Condition(function thistype.onDrop))
call TriggerAddCondition(move,And(Condition(function thistype.checkOrder),Condition(function thistype.onMove)))
call TriggerAddCondition(ModuleMoveTrig,Condition(function thistype.hookedMoveEvent))
endmethod
endstruct
module ItemEvents
method operator owner takes nothing returns unit
return ItemStruct(this).owner
endmethod
static if thistype.AII_onPickUp.exists then
private static method aiipickup takes nothing returns boolean
if thistype(LastPosition).flag then
call thistype(LastPosition).AII_onPickUp()
endif
return false
endmethod
endif
static if thistype.AII_onDrop.exists then
private static method aiidrop takes nothing returns boolean
if thistype(LastPosition).flag then
call thistype(LastPosition).AII_onDrop()
endif
return false
endmethod
endif
static if thistype.AII_onMove.exists then
private static method aiimove takes nothing returns boolean
if thistype[MovedItem].flag then
call thistype[MovedItem].AII_onMove(LastPosition)
endif
return false
endmethod
endif
private static method onInit takes nothing returns nothing
static if thistype.AII_onPickUp.exists then
call TriggerAddCondition(ModulePickUpTrig,Condition(function thistype.aiipickup))
endif
static if thistype.AII_onDrop.exists then
call TriggerAddCondition(ModuleDropTrig,Condition(function thistype.aiidrop))
endif
static if thistype.AII_onMove.exists then
call TriggerAddCondition(ModuleMoveTrig,Condition(function thistype.aiimove))
endif
endmethod
endmodule
endlibrary</u></u></u></u></i></i></i></i>
History :
- UID - v3.0.2
- Fixed bugs.
- Add this.owner for module
- v1.0.6 -
- Fires AII_onDrop when unit sells item, if ItemEvents is implemented in the struct.
- Deallocation requires exactly 0 for lock level.
- Cleaned up deallocate function firer.
- A little efficiency gain.
- v1.0.5 -
- Uses hash now.
- Cleaned up/Merged some functions.
- v1.0.4 -
- Use single trigger for death event.
- Fixed some initializations problem.
- Unhooked RemoveItem.
- v1.0.3 - First release.