• 🏆 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] Get Destructible Killer / Attacker

Status
Not open for further replies.
Is there a way to find out what unit hit or killed a destructible? I need this for a custom lumber-harvesting system that either attributes lumber upon striking a destructible, or killing it. The destructible cannot be replaced with a unit, since that would mean filling the map with units, so you can guess how well that would work :p

_________________________________________

UPDATE 16/August/2017: This is the latest version of the system:

Features:
  • Simulates attacking a tree to detect when the tree is 'struck'
  • Provides event for when a tree is struck, and another for when the cycle restarts.
  • Workers do not go idle in between strikes on a tree.
 

Attachments

  • CustomHarvestLumber.w3x
    43.7 KB · Views: 36
Last edited:
Level 15
Joined
Mar 25, 2016
Messages
1,327
This recent thread was also about destructible interaction: Attack animation
https://www.hiveworkshop.com/threads/attack-animation.296624/
I think there are no natives for that, so you will need a different solution. You could use abilities to detect it. You will need an ability that targets trees (I used eat tree).
When a unit is ordered to attack a destructible, you order it to use the ability instead. When the ability is used you do the same as if it would attack (dealing damage). You have to order stop to the unit, before eat tree kills the tree.

I made this for a custom lumber system and it detected interaction with the destructible very nicely. I can upload a map if you want to see how it works.
 
Level 15
Joined
Mar 25, 2016
Messages
1,327
Well the map I was talking about was a bit different, because only the first time you hit the tree needs to be detected.
Here is the map. The unit has eat tree and you detect when the ability is used. The problem is that you need to stop the order immediatly, so the tree does not get destroyed.
I used a timer and checked, if the unit was ordered a different order in between. It does not work correctly, if you order eat tree multiple times and you also have to keep in mind that stuns can interrupt harvesting.
I hope this map gives you an idea, but needs to be improved a lot.

edit: code added

Main Trigger
JASS:
function Trig_StartHarvestLumber_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction

function Trig_StartHarvestLumber_Timer takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer h = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_HashTable, h, 0)
    local destructable d = LoadDestructableHandle(udg_HashTable, h, 1)

    if(not LoadBoolean(udg_HashTable, GetHandleId(u), 0)) then
        call SetDestructableAnimation(d, "stand hit")
        call IssueTargetOrder( u, "eattree", d )
        call BJDebugMsg("harvest")
    endif
    call PauseTimer(t)
    call DestroyTimer(t)
    set t = null
    set u = null
    set d = null
endfunction

function Trig_StartHarvestLumber_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local destructable d = GetSpellTargetDestructable()
    local timer t = CreateTimer()
    local integer h = GetHandleId(t)
    call SaveUnitHandle(udg_HashTable, h, 0, u)
    call SaveDestructableHandle(udg_HashTable, h, 1, d)
    call DisableTrigger(gg_trg_OrderTracking)
    call IssueImmediateOrder( u, "stop" )
    call EnableTrigger(gg_trg_OrderTracking)
    call SetUnitAnimation( u, "attack" )
    call TimerStart(t, 1, false, function Trig_StartHarvestLumber_Timer)
    call SaveBoolean(udg_HashTable, GetHandleId(u), 0, false)
    call BJDebugMsg("start")
    set u = null
    set d = null
    set t = null
endfunction

//===========================================================================
function InitTrig_StartHarvestLumber takes nothing returns nothing
    set gg_trg_StartHarvestLumber = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_StartHarvestLumber, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_StartHarvestLumber, Condition( function Trig_StartHarvestLumber_Conditions ) )
    call TriggerAddAction( gg_trg_StartHarvestLumber, function Trig_StartHarvestLumber_Actions )
endfunction


Trigger to detect, if harvesting is canceled, but as I said this does not work completely.
JASS:
function Trig_OrderTracking_Actions takes nothing returns nothing
    call SaveBoolean(udg_HashTable, GetHandleId(GetTriggerUnit()), 0, true)
    call BJDebugMsg("canceled")
endfunction

//===========================================================================
function InitTrig_OrderTracking takes nothing returns nothing
    set gg_trg_OrderTracking = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OrderTracking, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OrderTracking, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_OrderTracking, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddAction( gg_trg_OrderTracking, function Trig_OrderTracking_Actions )
endfunction
 

Attachments

  • LumberHarvest.w3x
    19 KB · Views: 37
Last edited:
Level 15
Joined
Mar 25, 2016
Messages
1,327
It does not kill anything. The eat tree ability is interrupted before the tree gets destroyed.
The whole idea is, that you don't use attacks, but an ability, because for abilities you have target destructible and triggering unit.
The only idea that works nice is eat tree in my opinion, because other abilities like sentinel have side effects that cannot be avoided.
You have to stop the order immediatly though to not kill the tree.
Now you know when a unit interacts with a tree.
Then you have to create a custom attack, which means that you have to trigger attack animation, damage, hit animation and so on. You also have to keep in mind, that attacks can be canceled (if the player gives the unit a different order) or interrupted (death, stun, ensnare, teleport, ...). This is what my second trigger tries to do, but it does not work completely and if you really want to use this method, you will have to invest some work to make it better. You should probably use two timers to have something similar as damage point and backswing point for the animation.
This is obviously very complicated, but I have not found an easier way.
 
@Jampion , ah I understand your idea now to just completly replace the default harvesting behaviour. Yeh, it sounds like some decent work, but could be worth it.

--
It would be probably also neat to account for things like smart/move order on the tree.

The new order has not to cancel the process if the orderid is correct (<harvest> AND the targeted tree is the same as the previous one while being in process of an other harvesting attack)

Stun - maybe periodic order check could help, if it doesnt fire event.
 
Level 15
Joined
Mar 25, 2016
Messages
1,327
It would be probably also neat to account for things like smart/move order on the tree.
Right-click automatically uses eat tree.
One thing I don't know how to do is calculate backswing and damage point. Those fields are still a mystery to me...
1.Unit starts attacking (unit is attacked event)
Wait Damage Point duration
2.Actual attack happens (damage, etc.)
Wait Backswing Point duration
3.Now attack is finished

If you give a different order during Damage Point, the attack will be canceled, because 2. did not happen yet.
If you give a different order during Backswing Point you can just do this order earlier, because Backswing Point animation will be canceled, but the attack already happened.

In this case you have:
1.Starts the effect of eat tree
Wait Damage Point duration and check whether the order was canceled or interrupted
2.If not canceled or interrupted play "stand hit" animation for tree, deal damage, increase lumber carried, ...
Wait Backswing Point duration and check whether the order was canceled or interrupted
3.If not canceled or interrupted order the unit to use "eattree" on the same target (continue harvesting)

For canceling you have to track the orders given to the unit and its targets. If order is eating the same tree, it is not a cancel as IcemanBo said.
For interrupting I would do periodic buff checks and position checks and of course if the unit has died.
Check for all buffs that interrupt harvesting (stuns, ensnare, entangle, sleep, cyclone, ...)
If your map has town portals, staffs, mass teleport, knockback or something that changes a units position, you also need to check this.
Some other problems I can think of are:
Worker is loaded into zeppelin
Worker is target unit of mass teleport


Stun - maybe periodic order check could help, if it doesnt fire event.
How does stun affect orders? Can you use orders to check if a unit is stunned?
 
How does stun affect orders? Can you use orders to check if a unit is stunned?
Yes, stun is an order, units becoming stuned perform it and units beeing stuned still use it.
stunned=851973
OrderId list Order Ids
This will print out any unit becoming stuned (does not trigger on stun duration enlargment)
  • Stunned
    • Events
      • Unit - A unit Is issued an order targeting an object
    • Conditions
    • Actions
      • Custom script: set udg_Id = GetIssuedOrderId()
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Id Equal to 851973
        • Then - Actions
          • Game - Display to (All players) for 30.00 seconds the text: ((Name of (Triggering unit)) + Stunned)
        • Else - Actions
If one overwrites this StunOrder one can make an Unit ignore stuns.

This will print out on esc press who is stuned
  • Is Stunned
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Custom script: set bj_wantDestroyGroup =true
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • Custom script: set udg_Id = GetUnitCurrentOrder(GetEnumUnit())
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Id Equal to 851973
            • Then - Actions
              • Game - Display to (All players) for 30.00 seconds the text: ((Name of (Picked unit)) + is stunned)
            • Else - Actions
 
Level 15
Joined
Mar 25, 2016
Messages
1,327
Very interesting...
I was wondering why the order has a target object and it seems like the target is the stunning unit.
If one overwrites this StunOrder one can make an Unit ignore stuns.
How do you overwrite orders?
From my experience, if you give orders to stunned units they will perform the last given order once the stun expires.
 
Use the Stun-event and simply give a different order, then the unit will have the stun buff but can act freely. Channeled spells would still break.
Instant orders with no target seem not working (stop/hold ground).
That with the stunner i did not know cool, but makes sense.
  • Stunned
    • Events
      • Unit - A unit Is issued an order targeting an object
    • Conditions
    • Actions
      • Custom script: set udg_Id = GetIssuedOrderId()
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Id Equal to 851973
        • Then - Actions
          • Game - Display to (All players) for 30.00 seconds the text: ((Name of (Triggering unit)) + Stunned)
          • Game - Display to (All players) for 30.00 seconds the text: ((Name of (Target unit of issued order)) + Stunner)
          • -------- Anyone will charge onto stunner, instead of beeing stuned. --------
          • Unit - Order (Triggering unit) to Attack (Target unit of issued order)
        • Else - Actions
 
So I'm trying to make this thing work, and I'm at a point where I have to create triggers for every 'harvest instance', which is created per tree. I'm wondering how performance-inefficient it is to create/destroy a trigger with 4 events everytime the worker switches trees or cancels harvesting. Anyone have any insight on that?
 
Apologies for double-posting, but here's a WIP of what I've been working on. Currently, while trying to fix the behaviour of re-ordering the unit to harvest the same tree, I inadvertently broke the function to switch trees mid-harvest.

Not gonna lie, the code is a bit messy. If anyone has the patience to read through it, be my guest. I need to run to the movies now (Apes), so I'm a bit pressed for time.

[Check OP for download]
 
Last edited:
Not my code: From the LumberHarvest:
JASS:
function Trig_Melee_Initialization_Actions takes nothing returns nothing
    local integer id = GetUnitUserData(gg_unit_hpea_0000)
    call FogEnableOff()
    call FogMaskEnableOff()
    set CustomHarvestLumber_DamagePoint[id] = .433
    set CustomHarvestLumber_BackswingPoint[id] = 1.
    set CustomHarvestLumber_HarvestAnimation[id] = "attack"
endfunction

//===========================================================================
function InitTrig_Melee_Initialization takes nothing returns nothing
    set gg_trg_Melee_Initialization = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Melee_Initialization, function Trig_Melee_Initialization_Actions )
endfunction
JASS:
library CustomHarvestLumber initializer init requires TimerUtils, SpellEffectEvent

    /*
        This system also requires a Unit Indexer that uses custom values. I use Bribe's.
        I'm a bit pressed for time, so I'm unable to make detailed notes.
    */
 
    globals
        private constant integer CUSTOM_EAT_TREE = 'A003'
        private constant integer EATTREE = 852146
        private boolean array ignoreOrder //I use this boolean to ensure all instanced triggers know not to interrupt the worker
        private boolean array newCycle //This tells the system to animate the worker's attack after a cycle has ended
        private integer array instance //This stores the most recent instance of the harvest action. If there's a mismatch from the struct timer, the instance terminates.
        public real array BackswingPoint //Setup on indexing
        public real array DamagePoint //Setup on indexing
        public string array HarvestAnimation //The animation the unit will perform to 'hit' the tree. Setup on indexing.
    endglobals
     
     
    struct CustomHarvestLumber
     
     
        unit harvester
        destructable tree
        boolean terminate_instance
        trigger trg
        real x
        real y
     
     
        private static method PassCheck takes unit u, integer id, thistype this returns boolean
            if (not this.terminate_instance) and GetWidgetLife(this.tree) > .405 and (not IsUnitHidden(u)) and GetUnitX(u) == this.x and GetUnitY(u) == this.y and instance[id] == this then
                return true
            else
                return false
            endif
        endmethod
     
     
        private method destroy takes nothing returns nothing
            set this.harvester = null
            set this.tree = null
            set this.terminate_instance = false
            set this.trg = null
            call this.deallocate()
        endmethod
     
     
        private static method CooldownTimer takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            /*/*if the unit passes all the checks, order it to use eattree again.*/*/
            if PassCheck(this.harvester, id, this) then
                set ignoreOrder[id] = true
                set newCycle[id] = true
                call IssueTargetOrderById(this.harvester, EATTREE, this.tree)
            /*/*if the unit doesn't pass all the checks, end the instance.*/*/
            else
                call DestroyTrigger(this.trg)
                call this.destroy()
                call BJDebugMsg("cancelled on 'cooldown'")
            endif
            call ReleaseTimer(t)
            set t = null
        endmethod
     
     
        private static method AttackTimer takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            call ReleaseTimer(t)
            set t = null
            /*/*if the unit passes all the checks, 'hit' the tree once.*/*/
            if thistype.PassCheck(this.harvester, id, this) then
                call SetDestructableAnimation(this.tree, "stand hit")
                set t = NewTimerEx(this)
                call TimerStart(t, BackswingPoint[id], false, function thistype.CooldownTimer)
                set t = null
            /*/*if the unit doesn't pass all the checks, end the instance.*/*/
            else
                call DestroyTrigger(this.trg)
                call this.destroy()
                call BJDebugMsg("cancelled on 'attack'")
            endif
        endmethod
     
     
        /*private static method InterruptOrder takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            set ignoreOrder[id] = true
            call IssueImmediateOrderById(this.harvester, 851973)//order stunned
            call IssueImmediateOrderById(this.harvester, 851972) //order stop
            set ignoreOrder[id] = false
            call ReleaseTimer(t)
            set t = null
        endmethod*/
     
     
        private static method HarvesterEventTrack takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local integer id = GetUnitUserData(u)
            local integer issuedOrder = GetIssuedOrderId()
            local thistype this = instance[id]
            if not ignoreOrder[id] then
                if GetTriggerEventId() == EVENT_UNIT_ISSUED_TARGET_ORDER then
                    if issuedOrder == EATTREE or issuedOrder == 851971 then//order smart
                        if GetOrderTargetDestructable() == this.tree then
                            call BJDebugMsg("same tree")
                        else
                            set this.terminate_instance = true
                            call BJDebugMsg("different tree")
                        endif
                    endif
                else
                    call BJDebugMsg("other order")
                    set this.terminate_instance = true
                endif
            else
                set ignoreOrder[id] = false
            endif
            set u = null
        endmethod
     
     
        static method createInstance takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local integer id = GetUnitUserData(u)
            local timer t = null
            local destructable d = GetSpellTargetDestructable()
            local thistype this = instance[id]
            if this.tree == null or this.tree != d then
                set this = allocate()
                set instance[id] = this
                set this.harvester = u
                set this.tree = d
                set this.x = GetUnitX(u)
                set this.y = GetUnitY(u)
                if this.trg == null then
                    set this.trg = CreateTrigger()
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_DEATH)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_TARGET_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_POINT_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_ORDER)
                    call TriggerAddCondition(this.trg, function thistype.HarvesterEventTrack)
                endif
                set ignoreOrder[id] = true
                call IssueImmediateOrderById(u, 851973)//order stunned
                call SetUnitAnimation(u, HarvestAnimation[id])
                set t = NewTimerEx(this)
                call TimerStart(t, DamagePoint[id], false, function thistype.AttackTimer)
                set t = null
            elseif this.tree == d then
                set ignoreOrder[id] = true
                call IssueImmediateOrderById(u, 851973)//order stunned
                if newCycle[id] then//this exist to tell the system that a full cycle has completed and to animate the attack again
                    set newCycle[id] = false
                    call SetUnitAnimation(u, HarvestAnimation[id])
                    set t = NewTimerEx(this)
                    call TimerStart(t, DamagePoint[id], false, function thistype.AttackTimer)
                    set t = null
                endif
            endif
            set u = null
            set d = null
        endmethod
     
 
    endstruct
 
 
    //===========================================================================
    private function init takes nothing returns nothing
        call RegisterSpellEffectEvent(CUSTOM_EAT_TREE, function CustomHarvestLumber.createInstance)
    endfunction

endlibrary
 
UPDATE: Nevermind, I fixed it.

  • onIndex
    • Events
      • Game - UnitIndexEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Custom script: local unit u = udg_UDexUnits[udg_UDex]
      • Custom script: if GetUnitTypeId(u) == 'hpea' then
      • Custom script: set CustomHarvestLumber_DamagePoint[udg_UDex] = .433
      • Custom script: set CustomHarvestLumber_BackswingPoint[udg_UDex] = 1.
      • Custom script: set CustomHarvestLumber_HarvestAnimation[udg_UDex] = "attack"
      • Custom script: endif
      • Custom script: set u = null
JASS:
library CustomHarvestLumber initializer init requires TimerUtils, SpellEffectEvent

    /*
        This system also requires a Unit Indexer that uses custom values. I use Bribe's.
        I'm a bit pressed for time, so I'm unable to make detailed notes.
    */
 
    globals
        private constant integer CUSTOM_EAT_TREE = 'A000'
        private constant integer EATTREE = 852146
        private boolean array ignoreOrder //I use this boolean to ensure all instanced triggers know not to interrupt the worker
        private boolean array newCycle //This tells the system to animate the worker's attack after a cycle has ended
        private integer array instance //This stores the most recent instance of the harvest action. If there's a mismatch from the struct timer, the instance terminates.
        public real array BackswingPoint //Setup on indexing
        public real array DamagePoint //Setup on indexing
        public string array HarvestAnimation //The animation the unit will perform to 'hit' the tree. Setup on indexing.
    endglobals
    
    
    struct CustomHarvestLumber
    
    
        unit harvester
        destructable tree
        boolean terminate_instance
        trigger trg
        real x
        real y
    
    
        private static method PassCheck takes unit u, integer id, thistype this returns boolean
            if (not this.terminate_instance) and GetWidgetLife(this.tree) > .405 and (not IsUnitHidden(u)) and GetUnitX(u) == this.x and GetUnitY(u) == this.y and instance[id] == this then
                return true
            else
                return false
            endif
        endmethod
    
    
        private method destroy takes nothing returns nothing
            set this.harvester = null
            set this.tree = null
            set this.terminate_instance = false
            set this.trg = null
            call this.deallocate()
        endmethod
    
    
        private static method CooldownTimer takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            /*/*if the unit passes all the checks, order it to use eattree again.*/*/
            if PassCheck(this.harvester, id, this) then
                set ignoreOrder[id] = true
                set newCycle[id] = true
                call IssueTargetOrderById(this.harvester, EATTREE, this.tree)
                set ignoreOrder[id] = false
            /*/*if the unit doesn't pass all the checks, end the instance.*/*/
            else
                call DestroyTrigger(this.trg)
                call this.destroy()
                call BJDebugMsg("cancelled on 'cooldown'")
            endif
            call ReleaseTimer(t)
            set t = null
        endmethod
    
    
        private static method AttackTimer takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            call ReleaseTimer(t)
            set t = null
            /*/*if the unit passes all the checks, 'hit' the tree once.*/*/
            if thistype.PassCheck(this.harvester, id, this) then
                call SetDestructableAnimation(this.tree, "stand hit")
                set t = NewTimerEx(this)
                call TimerStart(t, BackswingPoint[id], false, function thistype.CooldownTimer)
                set t = null
            /*/*if the unit doesn't pass all the checks, end the instance.*/*/
            else
                call DestroyTrigger(this.trg)
                call this.destroy()
                call BJDebugMsg("cancelled on 'attack'")
            endif
        endmethod
    
    
        /*private static method InterruptOrder takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local integer id = GetUnitUserData(this.harvester)
            set ignoreOrder[id] = true
            call IssueImmediateOrderById(this.harvester, 851973)//order stunned
            call IssueImmediateOrderById(this.harvester, 851972) //order stop
            set ignoreOrder[id] = false
            call ReleaseTimer(t)
            set t = null
        endmethod*/
    
    
        private static method HarvesterEventTrack takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local integer id = GetUnitUserData(u)
            local integer issuedOrder = GetIssuedOrderId()
            local thistype this = instance[id]
            if not ignoreOrder[id] then
                if GetTriggerEventId() == EVENT_UNIT_ISSUED_TARGET_ORDER then
                    if issuedOrder == EATTREE or issuedOrder == 851971 then//order smart
                        if GetOrderTargetDestructable() == this.tree then
                            call BJDebugMsg("same tree")
                        else
                            set this.terminate_instance = true
                            call BJDebugMsg("different tree")
                        endif
                    endif
                else
                    call BJDebugMsg("other order")
                    set this.terminate_instance = true
                endif
            endif
            set u = null
        endmethod
    
    
        static method createInstance takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local integer id = GetUnitUserData(u)
            local timer t = null
            local destructable d = GetSpellTargetDestructable()
            local thistype this = instance[id]
            if this.tree == null or this.tree != d then
                set this = allocate()
                set instance[id] = this
                set this.harvester = u
                set this.tree = d
                set this.x = GetUnitX(u)
                set this.y = GetUnitY(u)
                if this.trg == null then
                    set this.trg = CreateTrigger()
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_DEATH)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_TARGET_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_POINT_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_ORDER)
                    call TriggerAddCondition(this.trg, function thistype.HarvesterEventTrack)
                endif
                set ignoreOrder[id] = true
                call IssueImmediateOrderById(u, 851973)//order stunned
                set ignoreOrder[id] = false
                call SetUnitAnimation(u, HarvestAnimation[id])
                set t = NewTimerEx(this)
                call TimerStart(t, DamagePoint[id], false, function thistype.AttackTimer)
                set t = null
            elseif this.tree == d then
                set ignoreOrder[id] = true
                call IssueImmediateOrderById(u, 851973)//order stunned
                set ignoreOrder[id] = false
                if newCycle[id] then//this exist to tell the system that a full cycle has completed and to animate the attack again
                    set newCycle[id] = false
                    call SetUnitAnimation(u, HarvestAnimation[id])
                    set t = NewTimerEx(this)
                    call TimerStart(t, DamagePoint[id], false, function thistype.AttackTimer)
                    set t = null
                endif
            endif
            set u = null
            set d = null
        endmethod
    
 
    endstruct
 
 
    //===========================================================================
    private function init takes nothing returns nothing
        call RegisterSpellEffectEvent(CUSTOM_EAT_TREE, function CustomHarvestLumber.createInstance)
    endfunction

endlibrary

The code is still kinda messy, but it works now. Yay \o/

[Check OP for download]


@IcemanBo Right, sorry, I should have posted the code itself. The events bit is:
JASS:
                if this.trg == null then
                    set this.trg = CreateTrigger()
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_DEATH)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_TARGET_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_POINT_ORDER)
                    call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_ISSUED_ORDER)
                    call TriggerAddCondition(this.trg, function thistype.HarvesterEventTrack)
                endif
Basically, when the worker begins harvest, that trigger is created. This trigger will persist for as long as he is hitting the same tree, but will be destroyed if he switches trees or is displaced/dies/tree dies/is ordered to stop/etc. Right now, I'm running into an issue where switching trees mid-harvest will cancel the new harvest. From what I gathered, it has something to do with the fact that the stun order to prevent eat tree from killing the tree is somehow being registed despite the boolean set ignoreOrder[id] = true being set to true to tell the events trigger created to NOT set this.terminate_instance to true.

@Tasyen thank you :)

EDIT: well, shit, something wrong with the test map. Will check and report back. *Fixed.
 
Last edited:
Huh. This does solve the issue of keeping the worker from going idle, though the way I do it is a bit different. My method also allow me to harvest per-hit and doesn't require a place to deposit the lumber at - you can if you want to, but for my purposes, it's not something I need. This makes me wonder whether there might be a way to use one of the harvesting abilities instead of Eat Tree to make the worker automatically seek out new trees...
 
Level 18
Joined
Nov 21, 2012
Messages
835
Can you specify what are your needs from harvest system you're building?
From 1st post I understood that you want to collect lumber from normal harvesting and from attack trees, right? And you do not want to have delivery structures?

I was wondering why the order has a target object and it seems like the target is the stunning unit.
for 851973 order event: GetOrderedUnit() is stunned unit, and GetOrderTargetUnit() is a stunner.
However there is small problem with stun event detection. Order 851973 will not be issued if stunner uses missle-ability (like storm bolt) and stunner dies after bolt is launched and before bolt lands on victim.
So this detection is not 100% save.
 
Last edited:
Well I've already accomplished what I wanted, it's only a matter of tweaking the spells being used, like perhaps to use a harvest ability like you have and set the duration to a high number to prevent then worker from ever hitting the tree, and to trigger everything to make it look as though it has. This way once the tree dies, it can automatically move on without the need for a GetClosestWidget call. It also prevents the worker for going idle.

What I'm curious about is whether your system allows you to detect when a tree was struck?
 
Level 18
Joined
Nov 21, 2012
Messages
835
What I'm curious about is whether your system allows you to detect when a tree was struck?
unfortunately not. Its offers event: "worker brings resource" but it uses dummy tree at every registered delivery structure. So if you do not want delivery building its a diffrent story.

I didnt mean to push new solution if you already have one.
But I'm still curious about list of abilities your system has(or should include)
I guess you know such a simple trick with giving worker 'Arlm' ability ;)
 
Oh, not to worry, it's still good to know alternative solutions exist.

But I'm still curious about list of abilities your system has(or should include)
Currently, only a custom Eat Tree so that the worker may target the tree itself. Once the game detects an attempt to eat a tree, it gives the worker the stun order and creates a 'harvest instance'. This instance is per-tree, so if the worker switches trees/is displaced/performs any action that isn't casting 'eattree' on the currently targetted tree, the instance is destroyed and deallocated and the trigger catching the orders is also destroyed. The instance will remain, however, for however many cycles the timer will repeat for.

The sequence is thus: Cast eat tree > order stun + animate and start timer > time expires, animate tree being hit (trigger real variable event) > start timer that will repeat the whole process.

I guess you know such a simple trick with giving worker 'Arlm' ability ;)
Not sure I understand what you mean. Does giving a worker Lumber Harvest do something specific to it?

Right now I'm pondering using a secondary ability based on channel that is ordered to be cast right after the stun order is issued. This will prevent the worker from idling and thus prevent its icon from showing up in the bottom - left corner.
 
Level 18
Joined
Nov 21, 2012
Messages
835
I still dont understand your goal. What you need that normal war3 lumber harvesting doesnt offer?
You can avoid delivery structures by giving harvester ability 'return lumber' 'Arlm', set parameters in ability harvest 'Ahrl' Damage to tree and Lumber Capacity.
Only if you want to attack order gives resource, then you can re-order "attack"-->"harvest"
 
Giving units return lumber makes them turn around before they deliver the lumber. The system I made is meant to simulate harvesting and provide events for when the destructible is stuck - what happens after is up to the modder. You could make a unit heal itself by 'hitting' a tree, for instance. It's really a 'a tree is stuck' event system.

UPDATE: now uses 2 abilities but now prevents idling.

[Check OP for download]
 
Last edited:
Status
Not open for further replies.
Top