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

[General] Reverse stuff

Status
Not open for further replies.
Level 25
Joined
Feb 2, 2006
Messages
1,686
Hey,
I would like to reverse:
  • animations
  • sounds
  • everything else in the game
using natives if possible.
I guess one can only play sounds forward and not reverse animations using any native? So my only option is to reverse sounds and animations using a program and adding a second file to the map but then I still cannot play the exact position of an animation or sound (sound might be possible?).

Is there any better way or does anyone know a program to reverse wc3 animations and add them as second version to the model?

For the rest I guess I can just write triggers which resurrect units drop picked up items etc.

Is there any way to reverse missiles and special effects?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,516
You can reverse Special Effects using this:
  • Special Effect - Set Time of (Last created special effect) to 0.00
So you would set the initial Time to be equal to the total Duration of the Special Effect's animation.

Then use a Timer that runs every 0.01 second which reduces the Special Effect Time by 0.01.
  • Sfx Setup
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Special Effect - Create a special effect at (Center of (Playable map area)) using units\human\Footman\Footman.mdl
      • Set VariableSet Sfx = (Last created special effect)
      • Set VariableSet SfxTime = 3.03
      • Special Effect - Play Special Effect: (Last created special effect), Animation: Death
      • Countdown Timer - Start SfxTimer as a Repeating timer that will expire in 0.01 seconds
  • Sfx Timer
    • Events
      • Time - SfxTimer expires
    • Conditions
    • Actions
      • Set VariableSet SfxTime = (SfxTime - 0.01)
      • Special Effect - Set Time of Sfx to SfxTime
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SfxTime Less than or equal to 0.00
        • Then - Actions
          • Countdown Timer - Pause SfxTimer
        • Else - Actions
This would be a lot easier to do in JASS/Lua.

Sounds will probably need to be edited by a program and imported into the map.

We have no control of Missiles besides their Art/Speed and once they're launched we lose all control of them, so you would have to create your own Missile system that enables this functionality.
 
Level 1
Joined
Apr 8, 2020
Messages
110
Try out this library.

JASS:
library ReverseAnimation requires TimerUtils

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   //  ReverseAnimation
   //====================================================================================================================
   //  Firstly, this script requires TimerUtils (by Vexorian @ [url]www.wc3campaigns.net):[/url]
   //          [url]http://wc3campaigns.net/showthread.php?t=101322[/url]
   //
   //  Background Info:
   //   [url]http://www.wc3campaigns.net/showpost.php?p=1017121&postcount=88[/url]
   //
   //  function SetUnitAnimationReverse takes:
   //
   //      unit u - animation of which reverse animation is played;
   //      integer index - animation index of the animation to be played;
   //      real animTime - the natural duration of the animation to be played. This can be referred to in the preview
   //                      window in the bottom-left part of the World Editor interface (it is the real value enclosed
   //                      in parenthesis);
   //      real runSpeed - the speed at which the animation to be played in reverse will be ran.
   //      boolean resetAnim - indicates to the system if it should set the unit's animation to "stand" after playing the
   //                          reverse animation.
   //
   //  function SetUnitAnimationReverseFollowed takes all of the above and:
   //      FollowUpFunc func - function to be ran after the animation is played. Expressed as a function interface
   //                          (see below). Takes an data object arguement pertaining to relevant stuff that must be
   //                          passed.
   //      integer data - a struct that is the above data object.
   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- Configuration --

   globals
       private constant real PREP_INTERVAL_DURATION = 0.03
   endglobals

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- User Functions --

   private keyword ReverseAnimation

   function SetUnitAnimationReverse takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim returns boolean
       return ReverseAnimation.Prepare(u, index, animTime, runSpeed, resetAnim, 0, 0)
   endfunction

   function SetUnitAnimationReverseFollowed takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim, FollowUpFunc func, integer data returns boolean
       return ReverseAnimation.Prepare(u, index, animTime, runSpeed, resetAnim, func, data)
   endfunction

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- Script Operation Functions --

   function interface FollowUpFunc takes integer data returns nothing

   private struct ReverseAnimation

       private unit u
       private integer intAnimIndex
       private real rAnimTime
       private real rRunSpeed
       private boolean boolResetAnim
       private FollowUpFunc func
       private integer data

       public static method Prepare takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim, FollowUpFunc func, integer data returns boolean
           local ReverseAnimation new = 0
           local timer TIM = null

           if u != null and index > 0 and runSpeed > 0.00 then
               set new = .allocate()
               set new.u = u
               set new.intAnimIndex = index
               set new.rAnimTime = animTime
               set new.rRunSpeed = -runSpeed
               set new.boolResetAnim = resetAnim
               set new.func = func
               set new.data = data

               call SetUnitTimeScale(u, animTime/PREP_INTERVAL_DURATION)
               call SetUnitAnimationByIndex(u, index)
               set TIM = NewTimer()
               call SetTimerData(TIM, integer(new))
               call TimerStart(TIM, PREP_INTERVAL_DURATION, false, function ReverseAnimation.Play)

               set TIM = null
               return true
           endif

           return false
       endmethod

       public static method Play takes nothing returns nothing
           local timer TIM = GetExpiredTimer()
           local ReverseAnimation INST = GetTimerData(TIM)

           call SetUnitTimeScale(INST.u, INST.rRunSpeed)
           call TimerStart(TIM, INST.rAnimTime/-INST.rRunSpeed, false, function ReverseAnimation.End)

           set TIM = null
       endmethod

       public static method End takes nothing returns nothing
           local timer TIM = GetExpiredTimer()
           local ReverseAnimation INST = GetTimerData(TIM)

           call SetUnitTimeScale(INST.u, 1.00)
           if INST.boolResetAnim then
               call SetUnitAnimation(INST.u, "stand")
           endif
           if INST.func != 0 then
               call INST.func.execute(INST.data)
           endif

           set INST.u = null
           call INST.destroy()
           call ReleaseTimer(TIM)
           set TIM = null
       endmethod

   endstruct

endlibrary
 
Level 18
Joined
Jan 1, 2018
Messages
728
I tried the -1.00 and it does not seem to be inverted. I have tried it with "Units\Demon\Infernal\InfernalBirth.mdl" it simply shows the final explosion. I think it simply cuts some part of the animation/effect.

What about unit animations? Is there at least any program which can invert animations?
If the animation does not loop, you need to set its time as well:
  • Actions
    • Special Effect - Create a special effect at (Center of (Playable map area)) using Abilities\Spells\NightElf\FanOfKnives\FanOfKnivesCaster.mdl
    • Special Effect - Set Time of (Last created special effect) to 5000.00
    • Special Effect - Set Time Scale of (Last created special effect) to -1.00
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
If the animation does not loop, you need to set its time as well:
  • Actions
    • Special Effect - Create a special effect at (Center of (Playable map area)) using Abilities\Spells\NightElf\FanOfKnives\FanOfKnivesCaster.mdl
    • Special Effect - Set Time of (Last created special effect) to 5000.00
    • Special Effect - Set Time Scale of (Last created special effect) to -1.00
thx it works now!
Try out this library.

JASS:
library ReverseAnimation requires TimerUtils

   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   //  ReverseAnimation
   //====================================================================================================================
   //  Firstly, this script requires TimerUtils (by Vexorian @ [url]www.wc3campaigns.net):[/url]
   //          [url]http://wc3campaigns.net/showthread.php?t=101322[/url]
   //
   //  Background Info:
   //   [url]http://www.wc3campaigns.net/showpost.php?p=1017121&postcount=88[/url]
   //
   //  function SetUnitAnimationReverse takes:
   //
   //      unit u - animation of which reverse animation is played;
   //      integer index - animation index of the animation to be played;
   //      real animTime - the natural duration of the animation to be played. This can be referred to in the preview
   //                      window in the bottom-left part of the World Editor interface (it is the real value enclosed
   //                      in parenthesis);
   //      real runSpeed - the speed at which the animation to be played in reverse will be ran.
   //      boolean resetAnim - indicates to the system if it should set the unit's animation to "stand" after playing the
   //                          reverse animation.
   //
   //  function SetUnitAnimationReverseFollowed takes all of the above and:
   //      FollowUpFunc func - function to be ran after the animation is played. Expressed as a function interface
   //                          (see below). Takes an data object arguement pertaining to relevant stuff that must be
   //                          passed.
   //      integer data - a struct that is the above data object.
   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- Configuration --

   globals
       private constant real PREP_INTERVAL_DURATION = 0.03
   endglobals

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- User Functions --

   private keyword ReverseAnimation

   function SetUnitAnimationReverse takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim returns boolean
       return ReverseAnimation.Prepare(u, index, animTime, runSpeed, resetAnim, 0, 0)
   endfunction

   function SetUnitAnimationReverseFollowed takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim, FollowUpFunc func, integer data returns boolean
       return ReverseAnimation.Prepare(u, index, animTime, runSpeed, resetAnim, func, data)
   endfunction

//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII//
//  -- Script Operation Functions --

   function interface FollowUpFunc takes integer data returns nothing

   private struct ReverseAnimation

       private unit u
       private integer intAnimIndex
       private real rAnimTime
       private real rRunSpeed
       private boolean boolResetAnim
       private FollowUpFunc func
       private integer data

       public static method Prepare takes unit u, integer index, real animTime, real runSpeed, boolean resetAnim, FollowUpFunc func, integer data returns boolean
           local ReverseAnimation new = 0
           local timer TIM = null

           if u != null and index > 0 and runSpeed > 0.00 then
               set new = .allocate()
               set new.u = u
               set new.intAnimIndex = index
               set new.rAnimTime = animTime
               set new.rRunSpeed = -runSpeed
               set new.boolResetAnim = resetAnim
               set new.func = func
               set new.data = data

               call SetUnitTimeScale(u, animTime/PREP_INTERVAL_DURATION)
               call SetUnitAnimationByIndex(u, index)
               set TIM = NewTimer()
               call SetTimerData(TIM, integer(new))
               call TimerStart(TIM, PREP_INTERVAL_DURATION, false, function ReverseAnimation.Play)

               set TIM = null
               return true
           endif

           return false
       endmethod

       public static method Play takes nothing returns nothing
           local timer TIM = GetExpiredTimer()
           local ReverseAnimation INST = GetTimerData(TIM)

           call SetUnitTimeScale(INST.u, INST.rRunSpeed)
           call TimerStart(TIM, INST.rAnimTime/-INST.rRunSpeed, false, function ReverseAnimation.End)

           set TIM = null
       endmethod

       public static method End takes nothing returns nothing
           local timer TIM = GetExpiredTimer()
           local ReverseAnimation INST = GetTimerData(TIM)

           call SetUnitTimeScale(INST.u, 1.00)
           if INST.boolResetAnim then
               call SetUnitAnimation(INST.u, "stand")
           endif
           if INST.func != 0 then
               call INST.func.execute(INST.data)
           endif

           set INST.u = null
           call INST.destroy()
           call ReleaseTimer(TIM)
           set TIM = null
       endmethod

   endstruct

endlibrary
I will try this library thx. I guess I have to record the animations by recording the orders? I don't know the exact time of the current animation. I could detect the orders and play a specific animation and record its current exact time. Otherwise, I have no chance of getting the current non-reversed animation right?
 
Level 25
Joined
Feb 2, 2006
Messages
1,686
Destructibles:
The ReverseAnimation library works but can it also be implemented for destructibles? There is a native
SetDestructableAnimationSpeed but it might not be the same for destructibles as SetUnitTimeScale is for units? I am wondering if I can reverse the destructibles by resurrecting them without needing to play any animation. If a box is destroyed I am not sure if I can properly resurrect it. Besides, I guess I can't detect destructible damage events? I could only try to detect orders from units on destructibles or just check their life periodically.

More things I can think of:

Interactive stuff:
  • Buying/selling items: How can I get the gold cost of an item type? There is no 'igol' entry in the trigger editor for item fields. The unit events are enough to detect the events but I need the gold cost. There is also a trigger action which allows adding item types to shops, so the charges could be restored. However, there is no function which returns the initial charges or cooldown/delay for the item to be available. There is no item integer field for 'isto' or something similar.
  • Moving items in the inventory: Order events by the unit for each inventory slot.
  • Player properties: There are only events for player resources matching certain conditions but not simply changing. Again periodic checks could work. Actually, this would only be required for gold and lumber (for example when players send each other gold/lumber).
  • Abilities: Only custom spells would work or certain spells with known effects? For example, there is no general event for units when they are healed etc. At least the orders could be detected.
  • Unit HP and mana: Damage events can be detected but no heal events. Spell cast events can be detected as well but again no mana potion events in general? Only periodic checks?
Visual stuff:
  • Weather effects: Probably needs a custom Weather.slk file and the weather effect has to be replaced by an inverted version (I can store the time from the beginning of the map but I cannot reverse it at the exact same time).
  • Water: Not possible? Only the color can be changed.
  • Floating text tags: Catch all calls on them (suspend/resume, permanent/expires, velocity etc.) and reverse them. It would only be useful for custom systems/spells.
  • Camera pans/objects/rotations/shakes: The target and source camera objects/positions need to be stored and the time. Not sure if one can reproduce the exact pans of Warcraft. It would be fun when watching a cinematic backwards.
It would be nice to avoid as many periodic checks as possible to improve the performance. Everything in a map a player can interact with should be reversed in some way, otherwise I can't add it to the map. The visual stuff is just for fun to look nice.
Since some stuff is too hard to reverse maybe the interactive stuff should be limited to custom items/spells etc. for which I know the exact effect. It might be easier than thinking about a generic way to reverse stuff.
 
Level 1
Joined
Apr 8, 2020
Messages
110
Interactive Stuff:
JASS:
globals
    trigger array manaDetection_trigger
    real array manaDetection_lastMana
    hashtable manaDetection_hashtable = InitHashtable()
endglobals

library ManaDetection
    function ManaDetection_Callback takes nothing returns boolean
        local integer id = LoadInteger(manaDetection_hashtable, GetHandleId(GetTriggeringTrigger()), 0)
        local real mana
  
        set mana = GetUnitState(udg_UDexUnits[id], UNIT_STATE_MANA)
        set udg_ManaDetection_Unit = udg_UDexUnits[id]
        if mana > manaDetection_lastMana[id] then
            set udg_ManaDetection_Change = R2I(mana - manaDetection_lastMana[id] + 0.5)
            if udg_ManaDetection_Change > 2 then
                set udg_ManaDetection_Event = 0
                set udg_ManaDetection_Event = 1
            endif
        else
            set udg_ManaDetection_Change = R2I(manaDetection_lastMana[id] - mana + 0.5)
            if udg_ManaDetection_Change > 2 then
                set udg_ManaDetection_Event = 0
                set udg_ManaDetection_Event = -1
            endif
        endif
  
        set manaDetection_lastMana[id] = GetUnitState(udg_UDexUnits[id], UNIT_STATE_MANA)
        call DestroyTrigger(manaDetection_trigger[id])
        set manaDetection_trigger[id] = CreateTrigger()
        call TriggerRegisterUnitStateEvent(manaDetection_trigger[id], udg_UDexUnits[id], UNIT_STATE_MANA, LESS_THAN, manaDetection_lastMana[id] - 0.1)
        call TriggerRegisterUnitStateEvent(manaDetection_trigger[id], udg_UDexUnits[id], UNIT_STATE_MANA, GREATER_THAN, manaDetection_lastMana[id] + 0.1)
        call TriggerAddCondition(manaDetection_trigger[id], Filter(function ManaDetection_Callback))
        call SaveInteger(manaDetection_hashtable, GetHandleId(manaDetection_trigger[id]), 0, id)
  
        return false
    endfunction

    function AddManaDetectionEvent takes unit whichUnit returns boolean
        local integer id = GetUnitUserData(whichUnit)
        if id == 0 or manaDetection_trigger[id] != null then
            return false
        endif
  
        set manaDetection_lastMana[id] = GetUnitState(whichUnit, UNIT_STATE_MANA)
        set manaDetection_trigger[id] = CreateTrigger()
        call TriggerRegisterUnitStateEvent(manaDetection_trigger[id], whichUnit, UNIT_STATE_MANA, LESS_THAN, manaDetection_lastMana[id] - 0.1)
        call TriggerRegisterUnitStateEvent(manaDetection_trigger[id], whichUnit, UNIT_STATE_MANA, GREATER_THAN, manaDetection_lastMana[id] + 0.1)
        call TriggerAddCondition(manaDetection_trigger[id], Filter(function ManaDetection_Callback))
        call SaveInteger(manaDetection_hashtable, GetHandleId(manaDetection_trigger[id]), 0, id)
  
        return true
    endfunction
    function RemoveManaDetectionEvent takes unit whichUnit returns boolean
        local integer id = GetUnitUserData(whichUnit)
        local real mana
        if id == 0 or manaDetection_trigger[id] == null then
            return false
        endif
  
        call RemoveSavedInteger(manaDetection_hashtable, GetHandleId(manaDetection_trigger[id]), 0)
        call DestroyTrigger(manaDetection_trigger[id])
        set manaDetection_trigger[id] = null
  
        return true
    endfunction
endlibrary
JASS:
/*****************************************************************************
*
*    InventoryEvent v1.0.1.8
*       by Bannar
*
*    For intuitive inventory event handling.
*
******************************************************************************
*
*    Requirements:
*
*       RegisterPlayerUnitEvent by Bannar
*          hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
*    Event API:
*
*       integer EVENT_ITEM_INVENTORY_MOVE
*       integer EVENT_ITEM_INVENTORY_USE
*
*       Use RegisterNativeEvent or RegisterIndexNativeEvent for event registration.
*       GetNativeEventTrigger and GetIndexNativeEventTrigger provide access to trigger handles.
*
*
*       function GetInventoryManipulatingUnit takes nothing returns unit
*          Returns unit which manipulated event item.
*
*       function GetInventoryManipulatedItem takes nothing returns item
*          Returns manupilated event item.
*
*       function GetInventorySlotFrom takes nothing returns integer
*          Returns slot index of manipulated item from which it was moved or used.
*
*       function GetInventorySlotTo takes nothing returns integer
*          Returns slot index of manipulated item to which it was moved.
*
*       function GetInventorySwappedItem takes nothing returns item
*          Returns item which manipulated item switched position with if any.
*
*****************************************************************************/
library InventoryEvent requires RegisterPlayerUnitEvent, ExtensionMethods

globals
    integer EVENT_ITEM_INVENTORY_MOVE
    integer EVENT_ITEM_INVENTORY_USE
endglobals

globals
    private unit eventUnit = null
    private item eventItem = null
    private integer eventSlotFrom = -1
    private integer eventSlotTo = -1
endglobals

function GetInventoryManipulatingUnit takes nothing returns unit
    return eventUnit
endfunction

function GetInventoryManipulatedItem takes nothing returns item
    return eventItem
endfunction

function GetInventorySlotFrom takes nothing returns integer
    return eventSlotFrom
endfunction

function GetInventorySlotTo takes nothing returns integer
    return eventSlotTo
endfunction

function GetInventorySwappedItem takes nothing returns item
    return UnitItemInSlot(eventUnit, eventSlotTo)
endfunction

function GetEventInventoryUnit takes nothing returns unit
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventoryUnit is obsolete, use GetInventoryManipulatingUnit instead.")
    return GetInventoryManipulatingUnit()
endfunction

function GetEventInventoryItem takes nothing returns item
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventoryItem is obsolete, use GetInventoryManipulatedItem instead.")
    return GetInventoryManipulatedItem()
endfunction

function GetEventInventorySlotFrom takes nothing returns integer
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySlotFrom is obsolete, use GetInventorySlotFrom instead.")
    return GetInventorySlotFrom()
endfunction

function GetEventInventorySlotTo takes nothing returns integer
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySlotTo is obsolete, use GetInventorySlotTo instead.")
    return GetInventorySlotTo()
endfunction

function GetEventInventorySwapped takes nothing returns item
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetEventInventorySwapped is obsolete, use GetInventorySwappedItem instead.")
    return GetInventorySwappedItem()
endfunction

function GetInventoryEventTrigger takes integer whichEvent returns trigger
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function GetInventoryEventTrigger is obsolete, use GetNativeEventTrigger instead.")
    return GetNativeEventTrigger(whichEvent)
endfunction

function RegisterInventoryEvent takes code func, integer whichEvent returns nothing
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Function RegisterInventoryEvent is obsolete, use RegisterNativeEvent instead.")
    call RegisterNativeEvent(whichEvent, func)
endfunction

private function FireEvent takes integer evt, unit u, item itm, integer slotFrom, integer slotTo returns nothing
    local unit prevUnit = eventUnit
    local item prevItem = eventItem
    local integer prevSlotFrom = eventSlotFrom
    local integer prevSlotTo = eventSlotTo
    local integer playerId = GetPlayerId(GetOwningPlayer(u))

    set eventUnit = u
    set eventItem = itm
    set eventSlotFrom = slotFrom
    set eventSlotTo = slotTo

    call TriggerEvaluate(GetNativeEventTrigger(evt))
    if IsNativeEventRegistered(playerId, evt) then
        call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, evt))
    endif

    set eventUnit = prevUnit
    set eventItem = prevItem
    set eventSlotFrom = prevSlotFrom
    set eventSlotTo = prevSlotTo

    set prevUnit = null
    set prevItem = null
endfunction

private function OnItemOrder takes nothing returns nothing
    local integer order = GetIssuedOrderId()
    local unit u = GetTriggerUnit()
    local item itm
    local integer slotFrom
    local integer slotTo

    if order >= 852002 and order <= 852007 then // between moveslot1 and moveslot6
        set itm = GetOrderTargetItem()
        set slotFrom = GetUnitItemSlot(u, itm)
        set slotTo = order - 852002 // moveslot1
        call FireEvent(EVENT_ITEM_INVENTORY_MOVE, u, itm, slotFrom, slotTo)
    else
        set slotFrom = order - 852008 //  useslot1
        set itm = UnitItemInSlot(u, slotFrom)
        call FireEvent(EVENT_ITEM_INVENTORY_USE, u, itm, slotFrom, -1)
    endif

    set u = null
    set itm = null
endfunction

private function OnAnyOrder takes nothing returns nothing
    local integer order = GetIssuedOrderId()
    if order >= 852002 and order <= 852013 then // between moveslot1 and useslot6
        call OnItemOrder()
    endif
endfunction

private module InventoryEventInit
    private static method onInit takes nothing returns nothing
        set EVENT_ITEM_INVENTORY_MOVE = CreateNativeEvent()
        set EVENT_ITEM_INVENTORY_USE = CreateNativeEvent()
        set MOVED = EVENT_ITEM_INVENTORY_MOVE
        set USED = EVENT_ITEM_INVENTORY_USE

        // MOVED is order of type TARGET_ORDER yet USED can be anyone of them
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function OnAnyOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function OnAnyOrder)
        call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function OnAnyOrder)
    endmethod
endmodule

struct InventoryEvent extends array
    // Events below are depreated in favor of EVENT_ alike globals
    readonly static integer MOVED
    readonly static integer USED

    implement InventoryEventInit
endstruct

endlibrary
Requirements:
ExtensionMethods
RegisterEvents
JASS:
library GetItemCost /* v1.0.0.1
************************************************************************************
*
*    Functions
*
*       function GetItemTypeIdGoldCost takes integer itemTypeId returns integer
*       function GetItemTypeIdWoodCost takes integer itemTypeId returns integer
*       function GetItemTypeIdCharges takes integer itemTypeId returns integer
*
*       function GetItemGoldCost takes item whichItem returns integer
*           -   Gets total item gold cost including item charges
*       function GetItemWoodCost takes item whichItem returns integer
*           -   Gets total item wood cost including item charges
*
************************************************************************************/
//! textmacro ITEM_COST
   globals
       private Table gi=0                  //item type id gold cost table
       private Table li=0                  //item type id lumber cost table
       private Table ci=0                  //item type id charge table
       private boolexpr b=null             //enum item function
       private real d=0                    //distance
       private item fi=null                //found item
       private integer it=0                //item type filter
       private rect r                      //world rect
       private real pe                     //percent divider
   endglobals
   //item cost (enum)
   //Select Unit method is not used in this case because it only seems to work
   //with players 0-11. Even with the use of 0 through 11, it can be buggy.
   private function E takes nothing returns boolean
       local real x                        //x coord
       local real y                        //y coord
       local item s=GetFilterItem()        //filter item
       //find the closest item of the matching type
       if (it==GetItemTypeId(s)) then
           //retrieve x,y as percents so that the values are smaller
           //magnitudes of coordinates can easily flow well above 2^31-1 limit
           set x=(GetWidgetX(s)-WorldBounds.minX)/pe        //percent
           set y=(GetWidgetY(s)-WorldBounds.minY)/pe        //percent
           set x=SquareRoot(x*x+y*y)         //magnitude
           //The item was sold at max coordinates, meaning 141.421356% (distance from bot left to top right)
           //the entire world must be enumerated over due to pathing (item might not be placed at unit)
           //when the pathing reaches a certain point, the item is placed at the unit
           //this will find the item with the greatest magnitude, thus approaching 141.421356%
           if (x>d) then
               set d=x
               set fi=s
           endif
       endif
       set s=null
       return false
   endfunction
   private function LogItem takes integer id returns nothing
       //get previous gold/lumber
       local integer k = GetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD)
       local integer w = GetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER)
       local item ts
       call SetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD,1000000)
       call SetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER,1000000)
       //build item
       call AddItemToStock(u,id,1,1)
       call IssueNeutralImmediateOrderById(p,u,id)
       call RemoveItemFromStock(u,id)
       //find and remove item
       set it=id         //item type id to find
       set d=-1          //closest range
       call EnumItemsInRect(r,b,null)
       set ci[id]=GetItemCharges(fi)
       call RemoveItem(fi)
       set fi=null
       //retrieve gold and lumber cost
       set gi[id]=1000000-GetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD)
       set li[id]=1000000-GetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER)
       //set player gold back to what it was
       call SetPlayerState(p,PLAYER_STATE_RESOURCE_GOLD,k)
       call SetPlayerState(p,PLAYER_STATE_RESOURCE_LUMBER,w)
   endfunction
   function GetItemTypeIdGoldCost takes integer id returns integer
       if (not gi.has(id)) then
           call LogItem(id)
       endif
       return gi[id]
   endfunction
   function GetItemTypeIdWoodCost takes integer id returns integer
       if (not gi.has(id)) then
           call LogItem(id)
       endif
       return li[id]
   endfunction
   function GetItemTypeIdCharges takes integer id returns integer
       if (not gi.has(id)) then
           call LogItem(id)
       endif
       return ci[id]
   endfunction
   function GetItemGoldCost takes item i returns integer
       local integer s = GetItemTypeId(i)
       local real s2 = GetItemTypeIdCharges(s)
       if (s2 > 0) then
           return R2I(gi[s]*(GetItemCharges(i)/s2))
       endif
       return gi[s]
   endfunction
   function GetItemWoodCost takes item i returns integer
       local integer s = GetItemTypeId(i)
       local real s2 = GetItemTypeIdCharges(s)
       if (s2 > 0) then
           return R2I(li[s]*(GetItemCharges(i)/s2))
       endif
       return li[s]
   endfunction
//! endtextmacro
//! textmacro ITEM_COST_2
   //item pathing can only go out 1024
   //after 1024, it is just placed at the unit it was sold at
   set r=Rect(WorldBounds.maxX-1088,WorldBounds.maxY-1088,WorldBounds.maxX,WorldBounds.maxY)
   set b=Condition(function E)           //item enum function
   set gi=Table.create()                 //gold item table
   set li=Table.create()                 //lumber item table
   set ci=Table.create()                 //charge item table
   set pe=(WorldBounds.maxX-WorldBounds.minX)*100        //percent divider
//! endtextmacro
endlibrary
Optional Requirements:
WorldBounds

Heal Event

Visual Stuff:
Water - You could use models instead though working with them can be tricky.
 
Last edited:
Level 25
Joined
Feb 2, 2006
Messages
1,686
Wow so many libraries. Does the Heal Event work similar to the ManaDetection library with the unit state events? It seems much more complicated. I already register damage events but using your unit state event approach for life might also work. My problem with the libraries is that in my map the events are NOT ALWAYS reversed. The past can be changed. For example, you can kill a reversed unit which caused the damage hence the damage event will not be reversed anymore, so the unit won't get its life back or if a unit uses a mana potion and drops it, in reverse it has to pick it up and get the mana removed. However, if you pick the item up before, the event wont be reversed.

Therefore, I must be able to detect the source of life/mana as well (item, spell, ability etc. etc.) which probably makes the systems useless for my map and might restrict it to custom spells/items only.

The InventoryEvent and GetItemGoldCost libraries are pretty useful. I was just wondering why you cannot get the gold cost with the new natives which get integer fields from items.

For the player resource events it will be the same as above. I always need to detect the source of gold and lumber to be able to decide whether to reverse the effect or not.

edit:

My map requires a system to keep track of loaded/unloaded units as well for copying them. Besides, I need a CopyUnit function which includes transported units. Is there any such system yet?
It would be really useful. Otherwise, I have to code it myself.
 
Last edited:
Level 25
Joined
Feb 2, 2006
Messages
1,686
Is there any library to get the current construction/upgrade/research/training/revival progress for one single building?


I only found:
JASS:
native UnitSetConstructionProgress  takes unit whichUnit, integer constructionPercentage returns nothing
but no get version. I can detect when the building construction is started/finished/canceled but not how fair the construction actually is. For non-stopping constructions I can achieve this by knowing the total construction time but for humans the workers can stop and then continue or you can use multiple workers. Hence, I need a system to detect it.

It's the same for upgrades although I could achieve this with the total time only:
JASS:
native UnitSetUpgradeProgress       takes unit whichUnit, integer upgradePercentage returns nothing

There are no natives for research/training/revival progresses?!

Is there no way to change the progress for these types?

If could write a library which detects the current progress by starting a timer but it seems that I cannot get the total fields like 'ubld' or 'gtib' and 'gtim'.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,516
On patch 1.35, and possibly some before patches before it, the order of Actions becomes important for this to work:
  • Untitled Trigger 001
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Special Effect - Create a special effect at (Center of (Playable map area)) using units\human\Footman\Footman.mdl
      • Special Effect - Play Special Effect: (Last created special effect), Animation: Death
      • Special Effect - Set Time of (Last created special effect) to 5000.00
      • Special Effect - Set Time Scale of (Last created special effect) to -1.00
The animation must be playing before you adjust the time scale.

Edit: At least that's the only way I could get the above trigger to work.
 
Last edited:
Level 25
Joined
Feb 2, 2006
Messages
1,686
Very interesting, so I have to switch the code in ReverseAnimation?

JASS:
call SetUnitTimeScale(u, animTime/PREP_INTERVAL_DURATION)
call SetUnitAnimationByIndex(u, index)

I am working on Rewind Time spells for my map World of Warcraft Reforged and record unit movements, stats etc. Maybe I can release the spell as separate system at some point. You choose a group of unit targets and the spell starts recording their actions. Then you can rewind the time for them and the actions are reversed. This can help to resurrect dead units or to heal damaged units or to move them out of a trap.
 
Status
Not open for further replies.
Top