• 🏆 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!

[vJASS] Item Loot System

Status
Not open for further replies.
Level 5
Joined
Jul 14, 2008
Messages
121
Would that cause desync ?

JASS:
library ItemLoot
    
    private function HideItem takes item itemCreated, boolean show, force forWhom returns nothing
        if (IsPlayerInForce(GetLocalPlayer(), forWhom)) then
            call SetItemVisible(itemCreated, show)
        endif
    endfunction
    
    private function MakeItem takes integer itemID, unit dyingUnit, player ownerOfKillingUnit, integer chances returns nothing
        local integer randomNumber = GetRandomInt(1, 100)
        local real locX = GetUnitX(dyingUnit)
        local real locY = GetUnitY(dyingUnit)
        local force forWhom = CreateForce()
        local item itemCreated
        if randomNumber <= chances then
            set itemCreated = CreateItem(itemID, locX, locY)
            call SetItemInvulnerable(itemCreated, true)
            call ForceAddPlayer(forWhom, ownerOfKillingUnit)
            call HideItem(itemCreated, false, bj_FORCE_ALL_PLAYERS)
            call HideItem(itemCreated, true, forWhom)
            call TriggerSleepAction(5)
            call HideItem(itemCreated, true, bj_FORCE_ALL_PLAYERS)
        endif
        set itemCreated = null
        call DestroyForce(forWhom)
    endfunction
    
    function ItemLoot takes unit dyingUnit, player ownerOfKillingUnit returns nothing
        local integer itemID
        local integer chances
        if GetUnitTypeId(GetTriggerUnit()) == 'hfoo' then
            set chances = 100
            set itemID = 'modt'
            call MakeItem(itemID, dyingUnit, ownerOfKillingUnit, chances)
        endif
    endfunction
    
endlibrary

I'm making this for a larger system that I will make if this work correctly.

Edit: Btw I call this function from a Unit Respawn System that's why there's no Init in this trigger. In singleplayer it definitively works, but I got no idea if it will multiplayer. There's probably better ways to do a few things in there too.

Edit2: Yeah I know I got a "useless" condition in there, it's only set to be able to change drop rate, but I made it 100% for testing purposes.

Edit3: Updated my system to make it easier to add items drops.

Edit4: Updated to be easier the read, changed variable names. It was suggested by Teelo :D.

Edit5: Updated found a Force Leak.

Edit6: Added GoldLoot that was on 4th post to first post:

JASS:
library GoldLoot

    globals
        private integer goldMinX = 3
        private integer goldMinPlus = 4
        private integer goldMaxX = 6
        private integer goldMaxPlus = 8
    endglobals
    
    function GoldLoot takes unit dyingUnit, player ownerOfKillingUnit returns nothing
        local integer goldDrop = GetRandomInt((GetUnitLevel(dyingUnit) * goldMinX) + goldMinPlus, (GetUnitLevel(dyingUnit) * goldMaxX) + goldMaxPlus)
        local texttag goldNumber = CreateTextTag()
        local force forWhom = CreateForce()
        call SetPlayerState(ownerOfKillingUnit, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(ownerOfKillingUnit, PLAYER_STATE_RESOURCE_GOLD) + goldDrop)
        call SetTextTagText(goldNumber, I2S(goldDrop), 0.026)
        call SetTextTagPermanent(goldNumber, false)
        call ForceAddPlayer(forWhom, ownerOfKillingUnit)
        call ShowTextTagForceBJ(false, goldNumber, bj_FORCE_ALL_PLAYERS)
        call ShowTextTagForceBJ(true, goldNumber, forWhom)
        call SetTextTagColor(goldNumber, 255, 225, 0, 255)
        call SetTextTagFadepoint(goldNumber, 0.50)
        call SetTextTagLifespan(goldNumber, 1.50)
        call SetTextTagPos(goldNumber, GetUnitX(dyingUnit) - 30.00, GetUnitY(dyingUnit), 75.00)
        call SetTextTagVelocity(goldNumber, 0.00, 0.03)
        call DestroyForce(forWhom)
    endfunction
    
endlibrary

Edit7: Updated both Triggers as rulerofiron99 indicated, with the exception of nulling the item variable since I don't how :/...

Edit8: Updated ItemLoot Trigger, nulled itemCreated.

Edit9: It caused desync so I built another system that I;m having problems with it creates the item but then any other call seem to have no effect. Seems like the variable ain't registering the item correctly.

New trigger:

JASS:
library ItemLoot initializer ILInit requires PlayerColors

    globals
        private hashtable itemLootHashTable InitHashtable()
    endglobals
      
    private function MakeItem takes integer itemId, unit dyingUnit, unit killingUnit, integer itemIdWhite returns nothing
        local real locX = GetUnitX(dyingUnit)
        local real locY = GetUnitY(dyingUnit)
        local item itemCreated = CreateItem(itemId, locX, locY)
        local integer id = itemIdWhite
        call SaveInteger(itemLootHashTable, GetHandleId(itemCreated), StringHash("itemIdWhite"), itemIdWhite)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("killingUnit"), killingUnit)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("dyingUnit"), dyingUnit)
        call TriggerSleepAction(5)
        call RemoveItem(itemCreated)
        call CreateItem(id, locX, locY)
        call FlushChildHashtable(itemLootHashTable, GetHandleId(itemCreated))
        set itemCreated = null
    endfunction

    private function UnitFootman takes unit dyingUnit, unit killingUnit returns nothing
        local player ownerOfKillingUnit = GetOwningPlayer(killingUnit)
        local integer chances = 50
        local integer randomInteger = GetRandomInt(1, 100)
        local integer itemId
        local integer itemIdWhite // "white" meaning "neutral" item
        if randomInteger <= chances then
            if ownerOfKillingUnit == Player(0) then
                set itemId = 'I001'
                set itemIdWhite = 'I000'
            endif
            if ownerOfKillingUnit == Player(1) then
                set itemId = 'I002'
                set itemIdWhite = 'I000'
            endif
            call MakeItem(itemId, dyingUnit, killingUnit, itemIdWhite)
        endif
    endfunction

    function ItemLoot takes unit dyingUnit, unit killingUnit returns nothing
        if GetUnitTypeId(dyingUnit) == 'hfoo' then
            call UnitFootman(dyingUnit, killingUnit)
        endif
    endfunction

    private function ItemPickup takes nothing returns nothing
        local unit triggeringUnit = GetTriggerUnit()
        local item manipulatedItem = GetManipulatedItem()
        local integer itemIdWhite = LoadInteger(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("itemIdWhite"))
        local unit killingUnit = LoadUnitHandle(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("killingUnit"))
        local unit dyingUnit = LoadUnitHandle(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("dyingUnit"))
        local texttag textTag = CreateTextTag()
        local player ownerOfTriggeringUnit = GetOwningPlayer(triggeringUnit)
        local force f = CreateForce()
        if triggeringUnit == killingUnit then
            call RemoveItem(manipulatedItem)
            call UnitAddItemById(triggeringUnit, itemIdWhite)
        else
            call RemoveItem(manipulatedItem)
            call MakeItem(GetItemTypeId(manipulatedItem), dyingUnit, killingUnit, itemIdWhite)
            call SetTextTagText(textTag, "This item is owned by " + GetPlayerColorCode(ownerOfTriggeringUnit) + GetPlayerName(ownerOfTriggeringUnit) + "|r!", 0.026)
            call SetTextTagPermanent(textTag, false)
            call ForceAddPlayer(f, ownerOfTriggeringUnit)
            call ShowTextTagForceBJ(false, textTag, bj_FORCE_ALL_PLAYERS)
            call ShowTextTagForceBJ(true, textTag, f)
            call SetTextTagFadepoint(textTag, 2.00)
            call SetTextTagLifespan(textTag, 3.00)
            call SetTextTagPos(textTag, GetUnitX(dyingUnit), GetUnitY(dyingUnit), 100.00)
        endif
        call FlushChildHashtable(itemLootHashTable, GetHandleId(manipulatedItem))
        call DestroyForce(f)
        call DestroyTextTag(textTag)
        set manipulatedItem = null
    endfunction

    private function ILInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddAction(t, function ItemPickup)
    endfunction
    
endlibrary
 
Last edited:
Level 5
Joined
Jul 14, 2008
Messages
121
Well either this system caused the desync or:

(Code was here, now on first post)

Which is also called by my respawn function.

Edit: I know BJ aren't good but this one does the same as my hide function except it does it for texttag.

Edit2: Put GoldLoot code on first post with better variables name for easier read.
 
Last edited:
Level 5
Joined
Jul 14, 2008
Messages
121
Yea, I probably should, any suggestions ? (I'm new to this, and I got no clue how to name my variables to make it easy to read to anyone else then me)
 
Level 10
Joined
Mar 31, 2009
Messages
732
You name them by their purpose.

JASS:
library ItemLoot
   
    private function HideItem takes item itemToHide, boolean visibility, force forWhom returns nothing
        if (IsPlayerInForce(GetLocalPlayer(), forWhom)) then
            call SetItemVisible(itemToHide, visibility)
        endif
    endfunction
   
    private function MakeItem takes integer itemId, unit recipient, player owner, integer proc returns nothing
        local integer luck = GetRandomInt(1, 100)
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local force recipients = CreateForce()
        local item createdItem

And so on. Instead of naming your variable "u", give it something more meaningful. Describe what its purpose is. destroyingUnit. killingUnit. dyingUnit. birthedUnit. enrichingUnit.
 
Level 5
Joined
Jul 14, 2008
Messages
121
Updated to be easier the read, changed variable names. It was suggested by Teelo :D. (In first post)

Edit: Updated found a Force Leak (at least I think they leak).

Edit2: Update GoldLoot (that was on 4th post, now on first post), better variable names for easier read.
 
Level 5
Joined
Jul 14, 2008
Messages
121
Well I went all with it completely XD. But I actually kept it the same in my map since shorter variables just means less typing :p.
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
Looks good.

You could/should make the gold drop values configurable in a globals block.
JASS:
library GoldLoot
    
    function GoldLoot takes unit DyingUnit, player OwnerOfKillingUnit returns nothing
        local integer GoldDrop = GetRandomInt((GetUnitLevel(DyingUnit) * 3) + 4, (GetUnitLevel(DyingUnit) * 6) + 8)
    endfunction
->
JASS:
library GoldLoot
    globals
        private integer goldMinX = 3
        private integer goldMinPlus = 4
        private integer goldMaxX = 6 
        private integer goldMaxPlus = 8
    endglobals

    function GoldLoot takes unit DyingUnit, player OwnerOfKillingUnit returns nothing
        local integer GoldDrop = GetRandomInt((GetUnitLevel(DyingUnit) * goldMinX) + goldMinPlus, (GetUnitLevel(DyingUnit) * goldMaxX) + GoldMaxPlus)
    endfunction

By convention, variable names follow lowerCamelCase, so
DyingUnit -> dyingUnit
OwnerOfKillingUnit -> ownerOfKillingUnit
In fact since they're being passed to the function, you can make them much shorter:
DyingUnit -> u
OwnerOfKillingUnit -> p (for player)
(you can do this for your other functions as well)

You need to null ItemCreated at the end of the function. Anything that isn't a string, integer, real or boolean, creates a handle that must be nulled or it will leak.

IIRC, hidden items can cause desyncs. If you don't have anyone to test on battle.net with you, you can use this: http://www.hiveworkshop.com/forums/...multiple-warcraft-3-running-single-pc-226287/

PS: Nice work so far :)
 
Level 5
Joined
Jul 14, 2008
Messages
121
I updated both Triggers as you indicated with the exception of nulling the item variable since I'm not sure what function to use... I didn't change the variables name except the first letter as suggested. because the name are btw 1 and 3 letters in my map triggers, those ones are only to make it more readable for people on the forums :p.

PS: Thx, learned most of it from you and Maker XD.

Edit: Anything I could do to make this not desync ?
 
Level 5
Joined
Jul 14, 2008
Messages
121
Oh, so you can do per player models ?

Edit: Updated ItemLoot Trigger, nulled itemCreated.

Edit2: Per player TextTags desync?
 
Level 25
Joined
Jul 10, 2006
Messages
3,315
Oh, so you can do per player models ?

Yes, but I'm not sure how it works for items.

Edit2: Per player TextTags desync?

Nope.

In fact, local text tags is recommended, since it increases the text tag limit from 100 across all players, to 100 per player.

Handy:
http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/getlocalplayer-225738/
http://www.hiveworkshop.com/forums/world-editor-help-zone-98/getlocalplayer-faq-224876/
 
Level 5
Joined
Jul 14, 2008
Messages
121

Ah, exactly what I was looking for, well I'll read on that and try to find how to change item models. I will update this when I find out.

Edit: Would item size, selection size and tint per player desync ?

Edit2: How much time would it takes for the game to desync (having hide item per player) using your multi-run wc3 program?

Edit3: Nvm it does desync at random interval as far as I can tell.
 
Last edited:
Level 5
Joined
Jul 14, 2008
Messages
121
Yea well I tested it with him, and it did desync, then rulerofiron99 showed me kLoader, and I've been using it since. I learn that anything item per player desync...
 
Level 5
Joined
Jul 14, 2008
Messages
121
I made a new code for the same use just trying something else, right now it ain't working and I don't know why.

JASS:
library ItemLoot initializer ILInit requires PlayerColors
//PlayerColors only have one function that takes player and return string (to get player color string code)

    globals
        private hashtable itemLootHashTable InitHashtable()
    endglobals
    
//This simply create the item then store some value to be used later in ItemPickup, also wait to then change item to "neutral" version    
    private function MakeItem takes integer itemId, unit dyingUnit, unit killingUnit, integer itemIdWhite returns nothing
        local real locX = GetUnitX(dyingUnit)
        local real locY = GetUnitY(dyingUnit)
        local item itemCreated = CreateItem(itemId, locX, locY)
        local integer id = itemIdWhite
        call SaveInteger(itemLootHashTable, GetHandleId(itemCreated), StringHash("itemIdWhite"), itemIdWhite)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("killingUnit"), killingUnit)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("dyingUnit"), dyingUnit)
        call TriggerSleepAction(5)
        call RemoveItem(itemCreated)
        call CreateItem(id, locX, locY)
        call FlushChildHashtable(itemLootHashTable, GetHandleId(itemCreated))
        set itemCreated = null
    endfunction
//--------------------------------------------------------------------------
    
//This is called by function ItemLoot it for footman (meaning i would need to make one of these each dying unit that drops loots)
//This also gives the % chance to items to drop, after that it calls MakeItem to create the item.   
    private function UnitFootman takes unit dyingUnit, unit killingUnit returns nothing
        local player ownerOfKillingUnit = GetOwningPlayer(killingUnit)
        local integer chances = 50
        local integer randomInteger = GetRandomInt(1, 100)
        local integer itemId
        local integer itemIdWhite // "white" meaning "neutral" item
        if randomInteger <= chances then
            if ownerOfKillingUnit == Player(0) then
                set itemId = 'I001'
                set itemIdWhite = 'I000'
            endif
            if ownerOfKillingUnit == Player(1) then
                set itemId = 'I002'
                set itemIdWhite = 'I000'
            endif
            call MakeItem(itemId, dyingUnit, killingUnit, itemIdWhite)
        endif
    endfunction
//---------------------------------------------------------------
    
// This is called by a respawn trigger and it's checks dying unit type id ------ 
    function ItemLoot takes unit dyingUnit, unit killingUnit returns nothing
        if GetUnitTypeId(dyingUnit) == 'hfoo' then
            call UnitFootman(dyingUnit, killingUnit)
        endif
    endfunction
//-------------------------------------------------------------------------  
    
//This checks units picking up items, if it's the owner the item get converted into "neutral" item in his inventory
//else the item stays where it was at first and give a message to picking unit telling him who's the owner   
    private function ItemPickup takes nothing returns nothing
        local unit triggeringUnit = GetTriggerUnit()
        local item manipulatedItem = GetManipulatedItem()
        local integer itemIdWhite = LoadInteger(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("itemIdWhite"))
        local unit killingUnit = LoadUnitHandle(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("killingUnit"))
        local unit dyingUnit = LoadUnitHandle(itemLootHashTable, GetHandleId(manipulatedItem), StringHash("dyingUnit"))
        local texttag textTag = CreateTextTag()
        local player ownerOfTriggeringUnit = GetOwningPlayer(triggeringUnit)
        local force f = CreateForce()
        if triggeringUnit == killingUnit then
            call RemoveItem(manipulatedItem)
            call UnitAddItemById(triggeringUnit, itemIdWhite)
        else
            call RemoveItem(manipulatedItem)
            call MakeItem(GetItemTypeId(manipulatedItem), dyingUnit, killingUnit, itemIdWhite)
            call SetTextTagText(textTag, "This item is owned by " + GetPlayerColorCode(ownerOfTriggeringUnit) + GetPlayerName(ownerOfTriggeringUnit) + "|r!", 0.026)
            call SetTextTagPermanent(textTag, false)
            call ForceAddPlayer(f, ownerOfTriggeringUnit)
            call ShowTextTagForceBJ(false, textTag, bj_FORCE_ALL_PLAYERS)
            call ShowTextTagForceBJ(true, textTag, f)
            call SetTextTagFadepoint(textTag, 2.00)
            call SetTextTagLifespan(textTag, 3.00)
            call SetTextTagPos(textTag, GetUnitX(dyingUnit), GetUnitY(dyingUnit), 100.00)
        endif
        call FlushChildHashtable(itemLootHashTable, GetHandleId(manipulatedItem))
        call DestroyForce(f)
        call DestroyTextTag(textTag)
        set manipulatedItem = null
    endfunction
//-------------------------------------------------------------------------  
    
//This simply start ItemPickup------------------------------------ 
    private function ILInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddAction(t, function ItemPickup)
    endfunction
//----------------------------------------------------------------
    
endlibrary
 
Level 5
Joined
Jul 14, 2008
Messages
121
GetHandleId(itemCreated) doesn't seems to be give me any handle ? Does items have handle ?
 
Level 5
Joined
Jul 14, 2008
Messages
121
The item is created, but I try putting debug lines giving message of the values in the hashtables and it's always 0 ...

Edit: The function seems to stop running at some random point...

JASS:
private function MakeItem takes integer itemId, unit dyingUnit, unit killingUnit, integer itemIdWhite returns nothing
        local real locX = GetUnitX(dyingUnit)
        local real locY = GetUnitY(dyingUnit)
        local item itemCreated = CreateItem(itemId, locX, locY)
        local integer id = itemIdWhite
        //It seems to stop working right here
        call SaveInteger(itemLootHashTable, GetHandleId(itemCreated), StringHash("itemIdWhite"), itemIdWhite)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("killingUnit"), killingUnit)
        call SaveUnitHandle(itemLootHashTable, GetHandleId(itemCreated), StringHash("dyingUnit"), dyingUnit)
        call TriggerSleepAction(5)
        call RemoveItem(itemCreated)
        call CreateItem(id, locX, locY)
        call FlushChildHashtable(itemLootHashTable, GetHandleId(itemCreated))
        set itemCreated = null
    endfunction
 
Last edited:
Level 6
Joined
Nov 24, 2012
Messages
218
I can imagine a way of showing "items" for the killer only, though not very efficient and quite a roundabout way.
But I do intend to use this myself in the future (just the auto-loot portion).
You have a dummy model of item chest, with locust. Locally change transparency.
Now, create local texttag for the player on top of item telling item name, stats, etc.
Then, detect item pick up via either in-range or move/right click order (don't know what registers on right click empty ground) at the location of item.
 
Level 5
Joined
Jul 14, 2008
Messages
121
As you said it's quite a work arround, that why I changed my system, and the new one is giving me other problem at the moment.

Edit: You would have to register distance, I don't there's a way to to register right click on ground. Still blocked with my new trigger, which I added to first post so ppls can find it easier.
 
Last edited:
Status
Not open for further replies.
Top