1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  3. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  4. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Trigger] Drop gold when damaged

Discussion in 'Triggers & Scripts' started by SnowYell, May 8, 2019.

  1. SnowYell

    SnowYell

    Joined:
    Jun 13, 2010
    Messages:
    308
    Resources:
    0
    Resources:
    0
    I want to make an event where a Gold Chest drops a Gold Coin (item) on the floor for every 5 % it loses of its maximum hp.

    I do however want to avoid using the DDS if possible.

    I was thinking about using an array string with up to 20. For it I could make a periodic trigger checking what hp it is at, if it is less than value set string[integer] = done.

    I do need some help to arrange this though, if I am to make it work properly.

    If you do have other suggestions however, which seem to work better and are more easy I would gladly like to hear them.

    Thank you for reading.
     
    Last edited: May 8, 2019
  2. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,537
    Resources:
    0
    Resources:
    0
    Do you mean if a single attack does 5% or more of its max health, or every time it loses 5% total something occurs? Can the unit heal? Healing makes this more complicated.
    Why? You'll probably need a DDS for something else in your map at some point, and this desired effect is much simpler to do with a DDS than without it.
     
  3. SnowYell

    SnowYell

    Joined:
    Jun 13, 2010
    Messages:
    308
    Resources:
    0
    Resources:
    0
    No. Whenever the target unit loses 5 % hp, it drops a gold coin on the floor. That's just a matter to control how many gold coins it may drop in total.

    I want the map to be as transferable to the Reforged as it can be. I have never used DDS or anything else outside of WE3 except custom models and would for this matter want to keep everything out.

    I'll try rewrite the main thread.
     
  4. Sabe

    Sabe

    Joined:
    Jul 30, 2018
    Messages:
    244
    Resources:
    1
    Spells:
    1
    Resources:
    1
    DDS won't be a problem in Reforged. DDS's are done in JASS code and they aren't "outside of WE". They are basically just already-made triggers that you copy to your map.
     
  5. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,537
    Resources:
    0
    Resources:
    0
    Are you also determined not to use a unit indexer? That also makes a periodic timer based solution more difficult
     
  6. Veldris

    Veldris

    Joined:
    Jul 10, 2018
    Messages:
    262
    Resources:
    0
    Resources:
    0
    you can make a difficult trigger

    When a unit health becomes Equal to 300
    it drops a coin
    when a unit health becomes Equal to 250
    it drops a coin
    when a unit health becomes Equal to 200
    it drops a coin
    and so on
     
  7. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,537
    Resources:
    0
    Resources:
    0
    This is not a solution at all. If a unit at 305 health takes 10 damage it will end up with 295 health and never trigger an "equal to 300" event. If you instead use "less than or equal to" events then the event for <= 300 life will trigger when the unit hits 250 life, 200 life, 150 life, etc. In fact it will also trigger when the unit hits 299 life and 275 life. See my point? With 5 different triggers that you disable appropriately you could accomplish this but it's really not viable to make 5 triggers per unit you want this to work for.
     
  8. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    I made "life event" for unit's life changed (by value and percent) some time ago for my personal use. I'll show it when I come back from work. Basically it recreates trigger for each unit, each time its life changed by 1 (cause this is minimum displayed on UI)
     
  9. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,109
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    You can loop over a list of tracked units ...
    ... then get current/max HP of each unit and attach them to the unit. In the next iteration you already will have new current/max HP data which the unit currently has and you also can access the "old" current/max HP the ones that we attached to the unit before. So you now easily can calculate the difference and then just overwrite again the attached current/max HP with the new ones, for being correct for the next iteration.
     
  10. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    Here's my solution, (it requires UnitEvent by Bribe)
    Code (vJASS):


    //Life Event by ZibiTheWand3r3r
    //requires Unit Event by Bribe
    globals
        constant integer        HP_CHANGE_STEP=50 // at least 50hp change will trigger an event
        constant integer        HP_PERCENT_CHANGE_STEP=2 // at least 2% change will trigger an event

        trigger array           g_trgLifeTrack
       
        integer array               g_hpForUp
        integer array               g_hpForDown  
        integer array               g_prevHP
        integer array               g_prevEventHP
       
        integer array               g_hpPercentForUp
        integer array               g_hpPercentForDown  
        integer array               g_prevHPPercent
        integer array               g_prevEventHPPercent

    endglobals
    //------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------
    function R2IEx takes real r returns integer //rounded
        return R2I(r+0.5)
    endfunction

    function GetDisplayedHP takes real life returns integer// displayed on UI
        local integer hp=R2I(life)//cuts decimals  
        if life - (I2R(hp)) > 0.00 then
            return (hp+1)
        endif
        return hp
    endfunction
    /*
    function GetUnitSpeedChange takes integer percentLife returns real//1..100
        if percentLife>90 then
            return 0.00      
        endif
        return I2R(percentLife-90)
         //local real oldMove=GetUnitSpeedChange(g_prevLifePercent[id])
        //local real newMove=GetUnitSpeedChange(lifePercent)
    endfunction
    */

    function RunUnitLifeEvent takes unit u, integer currentLife, integer prevLife returns nothing
        set udg_UnitLife_Unit=u
        set udg_UnitLife_Current=currentLife
        set udg_UnitLife_Prev=prevLife
        set udg_UnitLife_Event=0.00
        set udg_UnitLife_Event=1.00
        set udg_UnitLife_Event=0.00
    endfunction

    function RunUnitLifePercentEvent takes unit u, integer currentLife, integer prevLife returns nothing
        set udg_UnitLifePercent_Unit=u
        set udg_UnitLifePercent_Current=currentLife
        set udg_UnitLifePercent_Prev=prevLife
        set udg_UnitLifePercent_Event=0.00
        set udg_UnitLifePercent_Event=1.00
        set udg_UnitLifePercent_Event=0.00
    endfunction
    //------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------
    function Trig_HP_Changed takes nothing returns boolean
        local unit u=GetTriggerUnit()
        local integer id=GetUnitUserData(u)
        local real life=GetWidgetLife(u)
        local integer ui_life = GetDisplayedHP(life)
        local integer lifePercent=R2IEx((ui_life/GetUnitState(u, UNIT_STATE_MAX_LIFE))*100)// 1..100
        local boolean currentChangeUp = (ui_life>g_prevHP[id])
       
        //by value
        if ui_life <= g_hpForDown[id] then
            call RunUnitLifeEvent(u, ui_life, g_prevEventHP[id])
            set g_hpForUp[id]=ui_life+HP_CHANGE_STEP
            set g_hpForDown[id]=ui_life-HP_CHANGE_STEP
            set g_prevEventHP[id]=ui_life
        elseif ui_life >= g_hpForUp[id] then
            call RunUnitLifeEvent(u, ui_life, g_prevEventHP[id])
            set g_hpForUp[id]=ui_life+HP_CHANGE_STEP
            set g_hpForDown[id]=ui_life-HP_CHANGE_STEP
            set g_prevEventHP[id]=ui_life
        endif
         
        if currentChangeUp then  
            if ui_life-HP_CHANGE_STEP > g_hpForDown[id] then//if new down-level is greater then old one
                set g_hpForDown[id]=ui_life-HP_CHANGE_STEP //change down-level
                //call Msg("Down level: "+I2S(g_hpForDown[id]))
            endif
        else
            if ui_life+HP_CHANGE_STEP < g_hpForUp[id] then
                set g_hpForUp[id]=ui_life+HP_CHANGE_STEP
                //call Msg("UP level: "+I2S(g_hpForUp[id]))
            endif
        endif
       
        //percent:---------------------------------------------------------------------------------
        //call Msg(I2S(lifePercent)+" / "+I2S(g_prevEventHPPercent[id]))
        if lifePercent <= g_hpPercentForDown[id] then
            //call BJDebugMsg("maxhp:" +I2S(BlzGetUnitMaxHP(u))+", life:"+I2S(ui_life))
            call RunUnitLifePercentEvent(u, lifePercent, g_prevEventHPPercent[id])
            set g_hpPercentForUp[id]=lifePercent+HP_PERCENT_CHANGE_STEP
            set g_hpPercentForDown[id]=lifePercent-HP_PERCENT_CHANGE_STEP
            set g_prevEventHPPercent[id]=lifePercent
        elseif lifePercent >= g_hpPercentForUp[id] then
            //call BJDebugMsg("maxhp:" +I2S(BlzGetUnitMaxHP(u))+", life:"+I2S(ui_life))
            call RunUnitLifePercentEvent(u, lifePercent, g_prevEventHPPercent[id])
            set g_hpPercentForUp[id]=lifePercent+HP_PERCENT_CHANGE_STEP
            set g_hpPercentForDown[id]=lifePercent-HP_PERCENT_CHANGE_STEP
            set g_prevEventHPPercent[id]=lifePercent
        endif
       
        if currentChangeUp then  
            if lifePercent-HP_PERCENT_CHANGE_STEP > g_hpPercentForDown[id] then//if new down-level is greater then old one
                set g_hpPercentForDown[id]=lifePercent-HP_PERCENT_CHANGE_STEP //change down-level
                //call Msg("Down level: "+I2S(g_hpPercentForDown[id])+"%")
            endif
        else
            if lifePercent+HP_PERCENT_CHANGE_STEP < g_hpPercentForUp[id] then
                set g_hpPercentForUp[id]=lifePercent+HP_PERCENT_CHANGE_STEP
                //call Msg("UP level: "+I2S(g_hpPercentForUp[id])+"%")
            endif
        endif
       
        //recreate trigger each time unit's life changed
        set g_prevHPPercent[id]=lifePercent
        set g_prevHP[id]=ui_life
        call DestroyTrigger(g_trgLifeTrack[id])
        set g_trgLifeTrack[id]=CreateTrigger()
        call TriggerRegisterUnitStateEvent(g_trgLifeTrack[id], u, UNIT_STATE_LIFE, LESS_THAN, life-1)
        call TriggerRegisterUnitStateEvent(g_trgLifeTrack[id], u, UNIT_STATE_LIFE, GREATER_THAN, life+1)
        call TriggerAddCondition(g_trgLifeTrack[id], Condition(function Trig_HP_Changed))
        //---
        set u=null
        return false
    endfunction
    //------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------
    function Trig_LifeEventEnters takes nothing returns nothing
        local integer id=udg_UDex
        local unit u=udg_UDexUnits[udg_UDex]
        local real life=GetWidgetLife(u)
        local integer ui_life = GetDisplayedHP(GetWidgetLife(u))
       
        set g_prevHP[id]=ui_life
        set g_hpForUp[id]=ui_life+HP_CHANGE_STEP
        set g_hpForDown[id]=ui_life-HP_CHANGE_STEP
        set g_prevEventHP[id]=ui_life
       
        set g_prevHPPercent[id]=R2IEx((ui_life/GetUnitState(u, UNIT_STATE_MAX_LIFE))*100)
        set g_hpPercentForUp[id]=g_prevHPPercent[id] + HP_PERCENT_CHANGE_STEP
        set g_hpPercentForDown[id]=g_prevHPPercent[id] - HP_PERCENT_CHANGE_STEP  
        set g_prevEventHPPercent[id]=g_prevHPPercent[id]
       
        set g_trgLifeTrack[id]=CreateTrigger()
        call TriggerRegisterUnitStateEvent(g_trgLifeTrack[id], u, UNIT_STATE_LIFE, LESS_THAN, life-1)
        call TriggerRegisterUnitStateEvent(g_trgLifeTrack[id], u, UNIT_STATE_LIFE, GREATER_THAN, life+1)

        call TriggerAddCondition(g_trgLifeTrack[id], Condition(function Trig_HP_Changed))
       
        set u=null
    endfunction

    function Trig_LifeEventRemoved takes nothing returns nothing
        local integer id=udg_UDex
        call DestroyTrigger(g_trgLifeTrack[id])
        set g_trgLifeTrack[id]=null
    endfunction

    //==============================================================
    function InitTrig_LifeEvent takes nothing returns nothing
        local trigger t=CreateTrigger()
       
        call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
        call TriggerAddAction(t, function Trig_LifeEventEnters)
        set t=CreateTrigger()
        call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
        call TriggerAddAction(t, function Trig_LifeEventRemoved)
       
    endfunction
     
     

    Attached Files:

  11. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,186
    Resources:
    16
    Tools:
    2
    Maps:
    2
    Spells:
    7
    Tutorials:
    4
    JASS:
    1
    Resources:
    16
    A solution I came up with for this goldChest behaviour. It generates UnitState Life below events for an wanted unit after all goldcoins droped or the unit is dead the trigger and the events will selfdestruct. ZiBitheWand3r3r's solution is less specific making it more useful in more situations but also much bigger.
    Mine is quite inflexible in the amount of gold droped.
    Code (vJASS):

    constant function GoldCoinAmount takes unit u returns integer
        return 20 //if one wants goldChests with different gold count one could use ifs here
    endfunction
    function DropGold takes nothing returns boolean
        call CreateItem('gold', GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))
        if GetTriggerEvalCount(GetTriggeringTrigger()) >= GoldCoinAmount(GetTriggerUnit()) or GetWidgetLife(GetTriggerUnit()) <= 0.45 then //all goldcoins droped or dead/removed
            call DestroyTrigger(GetTriggeringTrigger())
        endif
        return false
    endfunction

    function RegisterGoldChest takes unit u returns nothing
        local integer goldCoins = GoldCoinAmount(u)
        local real maxLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
        local real lifeStep = maxLife / goldCoins
        local real life = maxLife
        local trigger trig = CreateTrigger()
        call TriggerAddCondition(trig, Condition(function DropGold))
        loop
            set life = life - lifeStep
            call TriggerRegisterUnitStateEvent(trig, u, UNIT_STATE_LIFE, LESS_THAN_OR_EQUAL, life)
            exitwhen life < 0
        endloop
        set trig = null
    endfunction
     
     
    Last edited: May 12, 2019
  12. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,109
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    @ZiBitheWand3r3r , it looks like byValue and byPercent can run at same time, so one damage step could fire 2 gold events? For units with a bit higher hp, in fights, they keep getting damaged, and they perma keep HP regen... which might lead to high amounts of trigger re-creates.

    @Tasyen , HP regen, max HP change isn't considered. And it looks like further HP- steps may fire the trigger multiple times for one step, because multiple events are registered for lower HPs. (LESS_THAN_OR_EQUAL 100 will fire for 90, for example, and the same event will also run for 80, and for 70, ..., but which again use their own events in addition)
     
  13. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    yes there are 2 separate events (by value and by percent)
    • event1
      • Events
        • Game - UnitLifePercent_Event becomes Equal to 1.00
      • Conditions
      • Actions
        • Game - Display to (All players) the text: ((EVENT - + (Name of UnitLifePercent_Unit)) + (, + ((String(UnitLifePercent_Prev)) + (% --> + ((String(UnitLifePercent_Current)) + (% + <Empty String>))))))


    • event2
      • Events
        • Game - UnitLife_Event becomes Equal to 1.00
      • Conditions
      • Actions
        • Game - Display to (All players) the text: ((EVENT -- + (Name of UnitLife_Unit)) + (, + ((String(UnitLife_Prev)) + ( --> + ((String(UnitLife_Current)) + ( + <Empty String>))))))
        • -------- UnitLife_Prev is a value a last event ran with --------

    yes, you're right, but when I wrote it I had no other idea how to execute life event.
     
  14. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,186
    Resources:
    16
    Tools:
    2
    Maps:
    2
    Spells:
    7
    Tutorials:
    4
    JASS:
    1
    Resources:
    16
    Life state events do not retrigger, except if the unit is healed over the limit up again, but it wouldn't have an impact on the total gold droped in my code. But yes depending on usecase and wanted behaviour that might be a problem.
     
  15. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    227
    Resources:
    0
    Resources:
    0
    I would use a DDS and Unit Indexer. Although, that may be outdated with this latest patch. Didn't they just add Damage Detection events as natives to the World Editor in the new PTR patch?

    Anyway, using what we have now, here's a working example I created. I imagine you guys can come up with something a lot more efficient than looping 20 times.

    • Setup
      • Events
        • Time - Elapsed game time is 0.00 seconds
      • Conditions
      • Actions
        • Hashtable - Create a hashtable
        • Set TreasureHash = (Last created hashtable)


    • Damage Event
      • Events
        • Game - DamageEvent becomes Equal to 1.00
      • Conditions
      • Actions
        • -------- We store 20 Booleans as false inside the Hashtable, one for each 5% of the Treasure Chest's maximum life. --------
        • -------- Whenever we break past a life threshold (95%, 90%, 85%) we set the respective Boolean for that threshold to True. --------
        • -------- That way we don't spawn Gold Coins for life thresholds that have already been broken (set to True). --------
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DamagedUnit) Equal to Treasure Chest
          • Then - Actions
            • Set UnitIndex = (Custom value of DamagedUnit)
            • For each (Integer A) from 1 to 20, do (Actions)
              • Loop - Actions
                • Set LifeThresholds = (100.00 - (5.00 x (Real((Integer A)))))
                • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                  • If - Conditions
                    • (Life of TreasureChest) Less than or equal to LifeThresholds
                    • (Load (Integer A) of UnitIndex from TreasureHash) Equal to False
                  • Then - Actions
                    • Hashtable - Save True as (Integer A) of UnitIndex in TreasureHash
                    • Item - Create Gold Coins at (Center of (Playable map area))
                  • Else - Actions
          • Else - Actions


    Just make sure to clean up the Child of the Hashtable when the Treasure Chest is destroyed.
     
    Last edited: May 23, 2019
  16. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    241
    Resources:
    0
    Resources:
    0
    Have you considered a simpler approach? How about having it drop 1 coin every time that it takes damage, until 20 coins is dropped, at which point the chest dies?

    All you need to do is use the chests Custom Value to track the amount of times that it has dropped a coin.

    The trigger would look something like this: (sorry for the pseudocode, I don't have access to my editor right now)
    Code (Text):
    PSEUDOCODE!

    (a unit takes damage)

    (Unit type equal to Chest)

    (If custom value of chest > 20)
    {drop coin;
    custom value + 1}
     else
    {kill chest}
    Then you just have to set the custom value to 0 when the chest is spawned and you're good to go. This trigger can be made in like 1 minute and will work with any level of damage from the attacking units. If you want the HP bar to indicate how much gold is left, simply set the HP of the chest to a percentage based on 100 - (custom value*5). This may even kill the chest when it gets set to 0%, but I am not certain, as I have never done that.

    You can exclude spell damage by making it immune to spells. The way this works with just attacks feels somewhat similar to the gold chests in the Blackwater Bay map of HOTS.

    Oh, and obviously, you'd need to make the chest really really tough to avoid it taking actual damage, of course.
     
    Last edited: May 23, 2019
  17. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    227
    Resources:
    0
    Resources:
    0
    A unit is attacked will fire even if the attack doesn't finish. Meaning that you could spam Attack and Stop and create 20 coins without ever landing a single attack.
     
  18. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    241
    Resources:
    0
    Resources:
    0
    Yeah, the thought did occur to me after I wrote it... so...
    A unit takes damage.
    There, is there a problem with that event?
    You can use conditions to exclude spells and such, or just make it immune to spells.
     
    Last edited: May 23, 2019
  19. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    227
    Resources:
    0
    Resources:
    0
    A unit takes damage doesn't exist for Any unit as far as I'm aware, you can do it for specific units but that means the chest would have to be pre-placed in the map. Unless you're referring to the new natives in the PTR patch. The whole point of using a damage detection system is because we didn't have the natives (until this latest patch, I believe) to properly detect when damage is taken.

    Also, your code doesn't do what the guy is asking for. He wants it so it drops a coin for every 5% of maximum hp lost. Your code drops a coin for every 1 attack, regardless of damage dealt.

    If the chest has 100 hp, and I hit it for 10 damage with my footman it will drop 2 coins.
    Then if I hit the chest for 20 damage with my knight it will drops 4 coins.
    Every 5 damage = 1 coin dropped in this case.
     
    Last edited: May 23, 2019
  20. Cespie

    Cespie

    Joined:
    May 21, 2019
    Messages:
    241
    Resources:
    0
    Resources:
    0
    I have done DPS meter systems in maps using unit groups. Quite simply, you just add the unit to the group as it is created, then track the event based on a unit taking damage in that group. This is an entirely "vanilla" approach to doing damage detection systems. No idea why it isn't more common.

    Either way, my code doesn't do explicitly what he asked for, but it does do what I think he is trying to accomplish anyway, based on a portion of what he wrote in a message, namely, controlling the gold output of the chest. I think it's dangerous design to base it on hp% as that would make the chest slow in the early game, and fast in the later game, assuming that the map has some form of power accumulation.