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

How to get whether an item is moved inside the inventory?

Status
Not open for further replies.
Level 9
Joined
Jul 10, 2011
Messages
562
hey all...

simple question but i cant find a solution.

How can i make a trigger recognizing whether an item is moved into another slot or switched with another item in another slot? or better how can i prevent it?


thanks in advance

greetz clapto

p.s. i know i could make a periodic trigger and check whether the item still has the same slot but thats a bad solution...there has to be another....dont mind if in jass or GUI.


*EDIT:
another question regarding items....how to detect if an item is dropped?
 
Last edited:
Level 20
Joined
Jul 14, 2011
Messages
3,213
For an item is droped there's a event "A unit looses an item", which also triggers when it's sold or transfered.

I don't think there's a way to detect item movement inside inventory without a periodic trigger.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
The solution is simple: when a unit drops an item loop through their inventory and check in which slot it is. Then, start a one-shot timer with a 0.1 seconds timeout, and give the unit back the item, into the very same slot. It's that simple, I'm gonna be using this method in the upcoming patch of my map, and it works perfectly.
 
Level 9
Joined
Jul 10, 2011
Messages
562
Luorax but the problem isnt the dropping on the ground its the moving of one item into another slot or switching 2 items slots.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
I'm mentally exhausted (and retarded as well), must've misunderstood something (dunno how's that possible o.o).

Well, you can prevent it by making the item undroppable. There's a native, dunno if BZ made a BJ for this:

JASS:
call SetItemDroppable(item whichItem, boolean droppable)

As a side-effect, you can't drop the item either. If that's a problem, then the only remaining option is periodically monitoring the unit's inventory, which you've already mentioned in the main post.
 
Level 9
Joined
Jul 10, 2011
Messages
562
making them undroppable wont work....so i would say i have to periodically set the inventory and check whether the slots changed....-.-
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
If you want them to be droppable then yea, I don't know about another solution, unfortunately. (I clearly remember a system that utilized double right clicking the item, but couldn't find it; maybe no such thing exists after all)

I don't get it though why is it such a problem to trigger it, it takes like a minute or two to make such a trigger.
 
Level 9
Joined
Jul 10, 2011
Messages
562
its not the trigger itself ^^ its the thing that another periodic trigger has to run ^^ and afaik many periodic executions can cause laggs.
 
There is an event for that.

852002 to 852007 (moveslot): These are item targeted orders that move the target item to a certain inventory slot of the ordered hero. The id 852002 will move it to slot 1, the id 852003 will move it to slot 2 and so on.

You just have to use "EVENT_UNIT_ISSUED_TARGET_ORDER". The target is the item moved and the order id tells which slot it is moved on. There is no order string for those orders so be sure to get the order id directly without any conversion ("GetIssuedOrderId() == 852002", for instance).
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
To quote this tutorial:

852002 to 852007 (moveslot): These are item targeted orders that move the target item to a certain inventory slot of the ordered hero. The id 852002 will move it to slot 1, the id 852003 will move it to slot 2 and so on.

So just wait for issue target orders and pause the unit, stop it and unpause it.
This has some caveats, for example channeling spells will be aborted.

You could try to just re-move the item, but i think that'll still cancel channeling spells.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
To quote this tutorial:



So just wait for issue target orders and pause the unit, stop it and unpause it.
This has some caveats, for example channeling spells will be aborted.

You could try to just re-move the item, but i think that'll still cancel channeling spells.

Why would you need to pause the unit and do such things? Just start a new timer with a .01 seconds timeout and order the unit to do move the item back to its original slot.

And yea, it actually works, I've just tested it; I knew I've seen such thing before.
 
Level 9
Joined
Jul 10, 2011
Messages
562
Tirlititi or LeP....could one of you make a trigger stopping the order whenever a hero changes the slots of an item?

i dont really get what the trigger would have to look like. *shame on me*
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Why would you need to pause the unit and do such things? Just start a new timer with a .01 seconds timeout and order the unit to do move the item back to its original slot.

And yea, it actually works, I've just tested it; I knew I've seen such thing before.

Because it's way easier this way. No need for timers or function callbacks. And both abort channeling spells, so why bother. But if you find a way without aborting, that's ofc the better method. Also 0-second timer work too.

JASS:
library bla initializer init

private function cond takes nothing returns boolean
  return GetIssuedOrderId() <= 852007 and GetIssuedOrderId() >= 852002
endfunction

private function act takes nothing returns nothing
  local unit u = GetTriggerUnit()

  // if the unit was already paused this would unpause it, so watch out
  call PauseUnit(u, true)
  call IssueImmediateOrder(u, "stop")
  call PauseUnit(u, false)

  set u = null
endfunction

private function init takes nothing returns nothing
  local trigger t = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
  call TriggerAddCondition(t, Condition(function cond))
  call TriggerAddAction(t, function act)
endfunction

endlibrary
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
A ".01" second timer DOES work. And it doesn't stop channeled spells, which is quite important in my opinion. It also doesn't involve pausing units, which may cause further problems if you're not using stacks for such status effects.

I never said your method wouldn't work though, I just find mine better as it has less side-effect and the speed difference doesn't bother me at all.
 
Level 9
Joined
Jul 10, 2011
Messages
562
Because it's way easier this way. No need for timers or function callbacks. And both abort channeling spells, so why bother. But if you find a way without aborting, that's ofc the better method. Also 0-second timer work too.

JASS:
library bla initializer init

private function cond takes nothing returns boolean
  return GetIssuedOrderId() <= 852007 and GetIssuedOrderId() >= 852002
endfunction

private function act takes nothing returns nothing
  local unit u = GetTriggerUnit()

  // if the unit was already paused this would unpause it, so watch out
  call PauseUnit(u, true)
  call IssueImmediateOrder(u, "stop")
  call PauseUnit(u, false)

  set u = null
endfunction

private function init takes nothing returns nothing
  local trigger t = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
  call TriggerAddCondition(t, Condition(function cond))
  call TriggerAddAction(t, function act)
endfunction

endlibrary

ill test it ^-^but i think itll work xD

+rep anyway (for every helper ;D)

EDIT: works like a charm ^.^ thank you all for this great an fast help^^
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
A ".01" second timer DOES work. And it doesn't stop channeled spells, which is quite important in my opinion. It also doesn't involve pausing units, which may cause further problems if you're not using stacks for such status effects.

I never said your method wouldn't work though, I just find mine better as it has less side-effect and the speed difference doesn't bother me at all.

Do you use UnitDropItemSlot or IssueTargetOrderById?
Because with the first one channeling spells still get aborted for me. Post your coeds.
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
JASS:
scope ItemTest2

struct ItemTest2 extends array
    implement Alloc
    private integer tempIndex
    private unit tempUnit
    private item tempItem
    
    private static trigger moveTrigger
    
    private static method moveCallback takes nothing returns nothing
        local thistype this=ReleaseTimer(GetExpiredTimer())
        call DisableTrigger(thistype.moveTrigger)
        call IssueTargetOrderById(this.tempUnit,this.tempIndex,this.tempItem)
        call EnableTrigger(thistype.moveTrigger)
        call this.deallocate()
    endmethod
    static method create takes unit triggerUnit,item triggerItem returns thistype
        local thistype this=thistype.allocate()
        local integer i=0
        set this.tempUnit=triggerUnit
        set this.tempItem=triggerItem
        loop
            if UnitItemInSlot(this.tempUnit,i)==this.tempItem then
                set this.tempIndex=i+852002
            endif
            set i=i+1
            exitwhen i==6
        endloop
        call TimerStart(NewTimerEx(this),.01,false,function thistype.moveCallback)
        return this
    endmethod
    private static method onMove takes nothing returns boolean
        if GetIssuedOrderId()>852001 and GetIssuedOrderId()<852008  then
            call thistype.create(GetTriggerUnit(),GetOrderTargetItem())
        endif
        return false
    endmethod
    private static method onInit takes nothing returns nothing
        set thistype.moveTrigger=CreateTrigger()
        call TriggerRegisterPlayerUnitEvent(thistype.moveTrigger,Player(0),EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER,null)
        call TriggerAddCondition(thistype.moveTrigger,Condition(function thistype.onMove))
    endmethod

endstruct

endscope

Now that I think of it though, this one also orders the unit to do something else, so I think it also stops channeled spells :S Either way, I prefer this one as it doesn't involve pausing units.

PS: OP, this code works perfectly for preventing units from moving items in their inventory. You need TimerUtils for this though and Alloc, which you can Google (or PM me for them)
 
This one doesn't stop the unit or his channeling :
JASS:
scope ItemSlot initializer init

globals
    private constant integer NULL_ITEM_ID = 'crys'

    // For GUI users : comment the following line by writing "//" before it
    // and create a GUI boolean called "ItemSlotRunning".
    // You should add the condition "ItemSlotRunning == FALSE" in all your triggers
    // that use the events "A unit acquires/drops an item".
    boolean udg_ItemSlotRunning

    private unit TheUnit
    private item TheItem1
    private item TheItem2
    private integer TheSlot1
    private integer TheSlot2
    private timer TheTimer
endglobals

private function UnitAddItemToSlotSafe takes unit u,item it,integer slot returns nothing
    local integer i=0
    local item array tmp
    local item swap
    loop
        exitwhen i==slot
        if (UnitItemInSlot(u,i)==null) then
            set tmp[i]=UnitAddItemById(u,NULL_ITEM_ID)
        endif
        set i=i+1
    endloop
    set swap=UnitRemoveItemFromSlot(u,slot)
    call UnitAddItem(u,it)
    set i=0
    loop
        exitwhen i==slot
        call RemoveItem(tmp[i])
        set tmp[i]=null
        set i=i+1
    endloop
    call UnitAddItem(u,swap)
    set swap=null
endfunction

private function Callback takes nothing returns nothing
    set udg_ItemSlotRunning=true
    call UnitRemoveItemFromSlot(TheUnit,TheSlot1)
    call UnitRemoveItemFromSlot(TheUnit,TheSlot2)
    call UnitAddItemToSlotSafe(TheUnit,TheItem1,TheSlot1)
    call UnitAddItemToSlotSafe(TheUnit,TheItem2,TheSlot2)
    set udg_ItemSlotRunning=false
endfunction

private function cond takes nothing returns boolean
    local integer id=GetIssuedOrderId()
    local integer i
    if id<=852007 and id>=852002 then
        set TheUnit=GetTriggerUnit()
        set TheItem1=GetOrderTargetItem()
        set i=0
        loop
            if UnitItemInSlot(TheUnit,i)==TheItem1 then
                set TheSlot1=i
            endif
            set i=i+1
            exitwhen i==6
        endloop
        set TheSlot2=id-852002
        set TheItem2=UnitItemInSlot(TheUnit,TheSlot2)
        call TimerStart(TheTimer,0.00,false,function Callback)
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerAddCondition(t,Condition(function cond))
    set TheTimer=CreateTimer()
    set udg_ItemSlotRunning=false
endfunction

endscope

However, it fires the events "A unit acquires/drops an item", which may be worse in several situation. I added a boolean for that but one could prefer LeP's or Luorax's solution. I guess it's safe to use a single timer in the scope.

Luorax's code works with a 0.00 timer, btw.
 
Level 9
Joined
Jul 10, 2011
Messages
562
lol thank you guys^^ now i have 3 different solution for a question that seems to be easier than i thought it is ^^

you guys are great ^^
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
However, it fires the events "A unit acquires/drops an item", which may be worse in several situation. I added a boolean for that but one could prefer LeP's or Luorax's solution. I guess it's safe to use a single timer in the scope.

Luorax's code works with a 0.00 timer, btw.

It's not necessarily bad, if you know what you're doing then you can easily bypass this issue, thus making it the best solution. For GUI users though I wouldn't recommend it.

Mine uses TimerUtils so it's just recycled, but yea, using one timer is safe too. The difference between "0." and ".01" is also negligible IMO, I'm accustomed to using ".01", it's some sort of a bad habbit, or IDK - but true, "0." works as well.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
Be aware that a timer with 0 delay still has a delay although it is less than at 0.01. Through hacks such as packet injection it might be possible to issue more than 1 order during the period of a timer which may cause bugs.

This is a huge problem with damage absorption systems and why they are difficult to make.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
I've never heard of that "packet injection" thing, though.
It is how all the melee exploits were done. Although it is usually only developed for melee play the principles can be applied to multiplayer maps, taking advantage of poor code.

An example is it being possible to send dialog button click events even if the dialog in question is not physically capable of being clicked (hidden). You could also use it to cast dummy abilities that are inside a hidden spellbook (like how trigger script can). This is why on such events you check if it should be possible for the event to occur and always discard the event response if it is not.

If a dialog should be hidden for a player, if you recieve press events from that player they clearly should not happen.

Proof of this is when you use dialogs in a laggy session. The events might fire after the dialog is hidden.
 
Status
Not open for further replies.
Top