• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] GetItemOwner

Level 31
Joined
Jul 10, 2007
Messages
6,306
JASS:
library GetItemOwner /* v1.1.0.1
*************************************************************************************
*
*   Retrieves the unit carrying an item.
*
*************************************************************************************
*
*   */uses/*
*   
*       */ UnitIndexer /*               hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*       */ Table /*                     hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*       */ RegisterPlayerUnitEvent /*   hiveworkshop.com/forums/jass-functions-413/snippet-registerplayerunitevent-203338/
*
************************************************************************************
*
*   Functions
*
*       function GetItemOwnerId takes item i returns integer
*           -   returns indexed id of owning unit
*       function GetItemOwner takes item i returns unit
*           -   returns owning unit of item
*
************************************************************************************/
    globals
        private Table ot                    //owner table
    endglobals
    private module init
        //when a unit picks up an item, update the owner
        private static method op takes nothing returns boolean
            set ot[GetHandleId(GetManipulatedItem())]=GetUnitUserData(GetTriggerUnit())
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            set ot=Table.create()
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.op)
        endmethod
    endmodule
    function GetItemOwnerId takes item i returns integer
        //if the item is owned, the owner is in the hashtable
        if (IsItemOwned(i)) then
            return ot[GetHandleId(i)]
        endif
        return 0
    endfunction
    function GetItemOwner takes item i returns unit
        return GetUnitById(GetItemOwnerId(i))
    endfunction
    private struct ItemLoc extends array
        implement init
    endstruct
endlibrary
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This would merit a nice update:

JASS:
library GetItemOwner uses /*
    */PlayerManager, /*[url]http://www.hiveworkshop.com/forums/showthread.php?t=142554[/url]
    */UnitIndexer /*[url]http://www.hiveworkshop.com/forums/showthread.php?t=172090[/url]
    */Table //http://www.hiveworkshop.com/forums/showthread.php?t=188084
    
    //Version 1.0.0.0
    
    //function GetItemOwnerId takes item i returns integer
    //  returns indexed id of owning unit
    
    //function GetItemOwner takes item i returns unit
    //  returns owning unit of item
    
    
    /*Notes
        This uses UnitIndexer so as not to have to clean up handle ids (thus allowing them to be recycled).
    */
        
    globals
        private Table table
        
        private integer foundOwner
        private item itemsearc
        private boolexpr efil
    endglobals
    
    private function EnumUnits takes nothing returns boolean
        if (UnitHasItem(GetFilterUnit(), itemsearc)) then
            set foundOwner = GetUnitUserData(GetFilterUnit())
        endif
        return false
    endfunction
    
    private module init
        private static method onPick takes nothing returns boolean
            set table[GetHandleId(GetManipulatedItem())] = GetUnitUserData(GetTriggerUnit())
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            local Players p = 0
            local trigger t = CreateTrigger()
            loop
                call TriggerRegisterPlayerUnitEvent(t, p.get, EVENT_PLAYER_UNIT_PICKUP_ITEM, null)
                set p = p.next
                exitwhen p.end
            endloop
            call TriggerAddCondition(t, Condition(function thistype.onPick))
            set efil = Condition(function EnumUnits)
            set table = Table.create()
            set t = null
        endmethod
    endmodule
    
    function GetItemOwnerId takes item i returns integer
        local Players p = 0
        if (IsItemOwned(i)) then
            set foundOwner = table[GetHandleId(i)]
            if (foundOwner == 0) then
                set itemsearc = i
                loop
                    call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p.get, efil)
                    if (foundOwner != 0) then
                        set table[GetHandleId(i)] = foundOwner
                        return foundOwner
                    endif
                    set p = p.next
                    exitwhen p.end
                endloop
            endif
        endif
        return 0
    endfunction
    
    function GetItemOwner takes item i returns unit
        return GetUnitById(GetItemOwnerId(i))
    endfunction
    
    private struct ItemLoc extends array
        implement init
    endstruct
endlibrary
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I did it before, Table is not needed, i simply used SetItemPlayer and GetItemPlayer.

EDIT :

Bribe, you were too fast, you posted just before i deleted my message :p

No you don't get it, these functions can be used instead of an (hash)table.
 
AutoIndex is a lot slower and generates a lot of shit-code and Trigger Evaluations everywhere.
It uses function interfaces and hooks.

UnitIndexer is faster and much easier to use.

Table by Vexorian is anything but decent.
It generates an extremely large amount of code.
Table by Bribe generates exactly 103 lines of code including all the constant integers from the structs and the constant boolean LIBRARY_Table.
After the optimizer, it would have only generated 58 lines of code.

This is pretty useful actually. It's better to depend on systems to do tiny tasks than to depend on one system to do a plentiful of unrelated tasks.
 
Level 6
Joined
Jun 20, 2011
Messages
249
It uses function interfaces and hooks.
35icdae.jpg
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
This is pretty useful actually. It's better to depend on systems to do tiny tasks than to depend on one system to do a plentiful of unrelated tasks.

Amen to that. I just rewrote my map to be entirely modular and it is so much easier to manage now. Every single script in it is very, very tiny and performs one very small task : P. Well... every script in it except for the core scripts like UnitIndexer and the Gameboard script, hahaha ;D.
 
Level 11
Joined
Nov 4, 2007
Messages
337
AutoIndex's advatage over Table is its speed. The dynamic solution of hooking RemoveUnit is a very elegant solution and the additional function call assigns a new value to a boolean and uses GetUnitUserData. If they were any circumstances possible at all that RemoveUnit could be called like 10000 times per second, I could understand seeing the hook of RemoveUnit as flaw. But in fact you can only remove a unit that you have created before. And creating so many units that this tiny speed disadvantage of a hooked RemoveUnit function would slow things down notably would certainly cause the map and coding to have far bigger problems than that ;)

AutoIndex does nothing more than requested by a system like that, it just makes certain that units you want to get the index of are really removed from the game and stuff like that. In what manner is it difficult to use GetUnitId(unit)?
Oh, furthermore, Nestharus:
You should learn to give your variables meaningful names. It's horrible to read your code.

Why don't you use Table? How is it importand at all how much code table generates?
Table functions are all inlined and faster than the Bribe system.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Table functions will inline in BribeTable just the same in VexorianTable. The speed is the same. Actually the speed of allocate/deallocating Tables is going to be better in BribeTable, and considerably better when you set the instance limit to something more reasonable, like 60000+ (the lines of text this would generate...). No, BribeTable is not slower.

The only overhead it adds is the extra functions for the different types, but they all inline, and the optimizer is supposed to remove inlined functions. The total generated code of BribeTable should work out to a bit less than VexorianTable, while adding some really good features like TableArray, no conceivable instance limit and of course multiple-type storage.

Instance limits are also horrible to work with because you never know how many you'll need of something like Table. You could hit the limit and not even know it.

The 2-D string array syntax offered by VexorianTable is also bad because StringHash will overflow in many cases, presenting a dangerous opportunity to clash.

The only problem BribeTable suffers is backwards compatibility, because I was too naive to keep the same function names or have a different library name. Well, actually I did have it called "NewTable" but I regrettably listened to Nestharus' advice to change the name to just say "Table".
 
Table by Bribe has beautiful syntax. Vexorian's is ugly. (StringTable? HandleTable? pfffff)

GetUnitId is also used in UnitIndexer.
Hooking RemoveUnit is neither more nor less effective than an undefend ability that automatically casts when a unit is removed, but my point is that using the undefend ability would be much cleaner. Trigger evaluations are pretty slow and using a pre-processor to spam them all over your code is a really bad idea :/
 
The odds of that happening are close to nil. I've played thousands upon thousands of maps and I only recall using a Transport ability maybe once or twice while playing Battleship AoS's.
Plus, the solution is to add a warning like this:
- If you are using a transport ability in your map, it is recommended that you set HOOK_REMOVE to true in the configuration.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Interesting, i wasn't aware about this bug.

@Mag : This is also true for UnitIndexer (i'm assuming that Nestharus is aware about this bug).
I mean even if he decide to don't care about it, he should be at very least mention this bug in the documentation, more, a constant boolean false by default wouldn't hurt.
 
Level 6
Joined
Jun 20, 2011
Messages
249
I think it's insane to change the system because of the most absurd or rare situations where it fails.
If a user is having issues because of this he might as well open up a thread in the help forum. And these kind of bugs should also be mentioned in the documentation.
But AIDS walks away shameless, how is that an indexing system wont fire up the structs it's implemented to for pre-placed units? That is a very important bug, very hard to fix, and Nes did it
 
Please don't get me wrong, but isn't this somehow useless?
You can always do something like this:
JASS:
function test takes nothing returns nothing
    call BJDebugMsg(GetPlayerName(GetItemPlayer(GetManipulatedItem())))
endfunction

function InitTrig_test takes nothing returns nothing
    set gg_trg_test = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_test, EVENT_PLAYER_UNIT_PICKUP_ITEM )
    call TriggerAddAction( gg_trg_test, function test )
    
    call SetItemPlayer(CreateItem('afac', 0,0), Player(0), false)
    call SetItemPlayer(CreateItem('afac', 0,0), Player(1), false)
    call SetItemPlayer(CreateItem('afac', 0,0), Player(2), false)
    call SetItemPlayer(CreateItem('afac', 0,0), Player(3), false)
    call SetItemPlayer(CreateItem('afac', 0,0), Player(4), false)
endfunction
To manipulate items...

For example, when hero buy item you set item owner to player owning unit.
Then if dropped or whatever and later picked by another unit you just
compare item and unit owner...
 

Attachments

  • x.w3m
    16 KB · Views: 59
That once again gets the player, not the item

I don't see how you can use that to get the unit owning that item without a group enum. That's what I asked you to elaborate on.
Yes it gets the player. My point here was to split items for each player not each unit.
Because after all GetOwner usually refers to player not unit itself.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
But this script does it for the unit, not the player.

You are saying to change the nature of the script because you say that getting the unit carrying a given item is useless. All I have to do is prove that there is one case where it is useful.


Consider an item that spawns a footman every 10 seconds at the position of the unit carrying it up to 6 footmen.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Can't you just make one that gets the owning player and another getting the unit; GetUnitItemOwner and GetPlayerItemOwner, something like that.
 
Top