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

[Solved] Expiration timer removes food cost?

Level 5
Joined
Nov 3, 2018
Messages
80
Hey everyone! i'm having an issue with expiration timer
which currently removes food cost from a unit for example
Peasant upgrades to footman (10 seconds) , (trigger makes him to only last 120 seconds)
footman its supossed to cost 10 food.
while upgrading to footman the food cost is 10 AFTER...it has been upgraded and the expiration timer kicks in , for some reason the food cost dissapears from the footman , is there a way to prevent expiration timer from taking away food costs?
 
Level 39
Joined
Feb 27, 2007
Messages
5,019
Perhaps the other timed life buffs don't have this issue. Here are the full specifics:
Code:
Adds a specific timed-life buff to a unit and kills the unit after a time. The unit will show a decreasing progress bar in its UI indicating the remaining time and what buff was used.

@param whichUnit Target unit.
@param buffId id of the buff to use.
The only allowed values are: `BUan` (Animate Dead), `Bapl` (Plague Ward), `BEfn` (Force of Nature), `BTLF` (Generic), `Bhwd` (Healing Ward), `Brai` (Raise Dead) and `BHwe` (Water Elemental).
@param duration game time from now until the unit is killed.

@note When another buff id than the allowed ones is given, it will fall back to use `BTLF` (Generic), even if the buff is derived from one of the allowed ones.

@note The text that appears on the progress bar is the tooltip (`Bufftip`/`ftip`) of the buff.

@note The buff won't show up in the unit's status UI.

@note The buff can be queried with `BlzGetUnitAbilityByIndex`.

@note Timed life can be stacked (even the same buff id), the timers will be running down in parallel, so the first expired timer will kill the unit but only the timed life applied first
will show up in the unit's UI. Timed life can also stem from summoning abilities as the allowed buff id values suggest.

@note Removing any timed-life buff from the unit will kill the unit.
 
Level 5
Joined
Nov 3, 2018
Messages
80
Perhaps the other timed life buffs don't have this issue. Here are the full specifics:
Code:
Adds a specific timed-life buff to a unit and kills the unit after a time. The unit will show a decreasing progress bar in its UI indicating the remaining time and what buff was used.

@param whichUnit Target unit.
@param buffId id of the buff to use.
The only allowed values are: `BUan` (Animate Dead), `Bapl` (Plague Ward), `BEfn` (Force of Nature), `BTLF` (Generic), `Bhwd` (Healing Ward), `Brai` (Raise Dead) and `BHwe` (Water Elemental).
@param duration game time from now until the unit is killed.

@note When another buff id than the allowed ones is given, it will fall back to use `BTLF` (Generic), even if the buff is derived from one of the allowed ones.

@note The text that appears on the progress bar is the tooltip (`Bufftip`/`ftip`) of the buff.

@note The buff won't show up in the unit's status UI.

@note The buff can be queried with `BlzGetUnitAbilityByIndex`.

@note Timed life can be stacked (even the same buff id), the timers will be running down in parallel, so the first expired timer will kill the unit but only the timed life applied first
will show up in the unit's UI. Timed life can also stem from summoning abilities as the allowed buff id values suggest.

@note Removing any timed-life buff from the unit will kill the unit.
sadly i couldnt find one that worked i think it may be an issue with the timer being trigger added instead of added by an ability
 
Level 5
Joined
Nov 3, 2018
Messages
80
Does the expiration timer really remove the food cost? That doesn't sound normal. Are you sure you aren't making a simple mistake like forgetting to set a food value?
to be totally sure i did these methods

1
place the upgraded unit via editor (food is consumed)
2
remove the timer from my trigger (food stays)

then as soon i added the "add expiration timer to triggering unit" food dissaparence came back again(nichilus apparently is right summoned units dont take food)
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,555
That's interesting, you'd think that they'd simply set Summoned units Food Costs to 0 in the Object Editor rather than hardcoding it so "Summoned units never cost food". I guess it had something to do with making Illusions use timed life as well.

Edit: See bottom post.
Anyway, here's a system I made for fixing the problem. Simply set some system specific variables and run the EFF Add Timed Life trigger:
  • DEMO 1
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • -------- The standard way to add timed life using this system: --------
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Set VariableSet EFF_Unit = (Last created unit)
      • Set VariableSet EFF_Timed_Life_Type = 4
      • Set VariableSet EFF_Duration = 5.00
      • Set VariableSet EFF_Food_Cost = 2
      • Trigger - Run EFF Add Timed Life <gen> (ignoring conditions)
The Timed_Life_Type and Food_Cost variables are optional, if you leave them unset then the system will assume you want to use a Generic timed life buff and get the Food Cost automatically by using the EFF_Unit's food cost:
  • DEMO 2
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • Set VariableSet EFF_Unit = (Last created unit)
      • Set VariableSet EFF_Duration = 5.00
      • Trigger - Run EFF Add Timed Life <gen> (ignoring conditions)
The system works by creating a Dummy unit which costs an amount of Food between 1 and 7 depending on what you set the EFF_Food_Cost to. This Dummy is linked to the EFF_Unit so that when the EFF_Unit dies, so does the Dummy.

Perhaps a better solution exists but I couldn't think of any other alternative.
vJASS:
library ExpirationFoodFix initializer Init

    globals
        private hashtable Dummy_Hash = InitHashtable()
        private trigger Death_Trigger = CreateTrigger()
        private integer array Dummy_Food_Array
        private integer array Timed_Buff_Array
        private integer Dummy_Count = 0
    endglobals

    private function KillDummyUnit takes nothing returns nothing
        // I think this happens so rarely that it's better to
        // avoid garbage collection and not use local variables.
        if LoadUnitHandle(Dummy_Hash, GetHandleId(GetTriggerUnit()), 0) != null then
            call RemoveUnit( LoadUnitHandle(Dummy_Hash, GetHandleId(GetTriggerUnit()), 0) )
            call FlushChildHashtable( Dummy_Hash, GetHandleId(GetTriggerUnit()) )
            set Dummy_Count = Dummy_Count - 1
            if Dummy_Count == 0 then
                call DisableTrigger(Death_Trigger)
            endif
        endif
    endfunction

    private function GetFoodCost takes integer foodCost returns integer
        if foodCost > 0 and foodCost < 8 then
            return Dummy_Food_Array[foodCost]
        endif
        return Dummy_Food_Array[1] // DEFAULT TO 1 FOOD COST
    endfunction

    private function GetBuffType takes integer buffIndex returns integer
        if buffIndex > 0 and buffIndex < 8 then
            return Timed_Buff_Array[buffIndex]
        endif
        return Timed_Buff_Array[4] // DEFAULT TO GENERIC
    endfunction

    function EFF_Add_Timed_Life takes unit u, integer buffIndex, real duration, integer foodCost returns nothing
        local unit dummy

        // If you leave food set to 0 it will assume
        // that you want to use the provided unit's food cost
        if foodCost == 0 then
            set foodCost = GetUnitFoodUsed(u)
        endif

        call UnitApplyTimedLife(u, GetBuffType(buffIndex), duration)
        set dummy = CreateUnit(GetOwningPlayer(u), GetFoodCost(foodCost), 0, 0, 0)

        // Link the dummy unit to the timed life unit
        call SaveUnitHandle(Dummy_Hash, GetHandleId(u), 0, dummy)

        set Dummy_Count = Dummy_Count + 1
        if Dummy_Count == 1 then
            call EnableTrigger(Death_Trigger)
        endif

        set dummy = null
    endfunction

    private function Init takes nothing returns nothing
        set Dummy_Food_Array[1] = 'ufd1'
        set Dummy_Food_Array[2] = 'ufd2'
        set Dummy_Food_Array[3] = 'ufd3'
        set Dummy_Food_Array[4] = 'ufd4'
        set Dummy_Food_Array[5] = 'ufd5'
        set Dummy_Food_Array[6] = 'ufd6'
        set Dummy_Food_Array[7] = 'ufd7'
        set Timed_Buff_Array[1] = 'BUan'
        set Timed_Buff_Array[2] = 'Bapl'
        set Timed_Buff_Array[3] = 'BEfn'
        set Timed_Buff_Array[4] = 'BTLF'
        set Timed_Buff_Array[5] = 'Bhwd'
        set Timed_Buff_Array[6] = 'Brai'
        set Timed_Buff_Array[7] = 'BHwe'
        call TriggerRegisterAnyUnitEventBJ( Death_Trigger, EVENT_PLAYER_UNIT_DEATH )
        call TriggerAddAction( Death_Trigger, function KillDummyUnit )
        call DisableTrigger( Death_Trigger )
    endfunction

endlibrary
 

Attachments

  • Expiration Food Fix 1.w3m
    20.5 KB · Views: 2
Last edited:
Level 25
Joined
Sep 26, 2009
Messages
2,378
Wouldn't you have easier time just modifying food used by the given player?
Something like this:
  • Actions
    • Set VariableSet foodCost = (Supply used by YourUnit)
    • Unit - Add a 60.00 second Generic expiration timer to YourUnit
    • Player - Add foodCost to (Owner of YourUnit).Food used
    • Unit Group - Add YourUnit to foodCostUnits
and just resetting the food used upon unit's death
  • Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • ((Triggering unit) is in foodCostUnits.) Equal to True
    • Actions
      • Set VariableSet foodCost = (Supply used by (Triggering unit))
      • Player - Add (-1 x foodCost) to (Owner of (Triggering unit)).Food used
      • Unit Group - Remove (Triggering unit) from foodCostUnits.
As far as I know, you cannot undo timed life without the unit dying, anyway.
The only edge case here is in case the unit is charmed and belongs to another player, but that can be detected as well.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,555
Wouldn't you have easier time just modifying food used by the given player?
Forgot you could do that to be honest.

But wouldn't this return 0 when it dies?
  • Set VariableSet foodCost = (Supply used by (Triggering unit))
I think you'd need to track that value prior to adding the Timed Life since it's food cost will be set to 0. Unit Indexing or a Hashtable would be simple enough.

Edit: Alright, this makes a lot more sense, thanks to Nichilus:
  • Food Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • -------- --------
      • Set VariableSet Food_Unit = (Last created unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • Set VariableSet Food_Cost[Food_CV] = (Supply used by Food_Unit)
      • Set VariableSet Food_Player[Food_CV] = (Owner of Food_Unit)
      • -------- --------
      • -------- Add the expiration timer prior to adjusting the food property: --------
      • Unit - Add a 3.00 second Generic expiration timer to Food_Unit
      • -------- --------
      • -------- Add the food cost now: --------
      • Player - Add Food_Cost[Food_CV] to Food_Player[Food_CV].Food used
      • -------- --------
      • -------- Turn on necessary triggers: --------
      • Set VariableSet Food_Unit_Count = (Food_Unit_Count + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Food_Unit_Count Equal to 1
        • Then - Actions
          • Trigger - Turn on Food Unit Dies <gen>
          • Trigger - Turn on Food Ownership Change <gen>
        • Else - Actions
  • Food Unit Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • Food_Cost[(Custom value of (Triggering unit))] Greater than 0
    • Actions
      • Set VariableSet Food_Unit = (Triggering unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • -------- --------
      • -------- Subtract food cost: --------
      • Player - Add (Food_Cost[Food_CV] x -1) to (Owner of Food_Unit).Food used
      • -------- --------
      • -------- Reset food cost variable: --------
      • Set VariableSet Food_Cost[Food_CV] = 0
      • -------- --------
      • -------- Turn off unnecessary triggers: --------
      • Set VariableSet Food_Unit_Count = (Food_Unit_Count - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Food_Unit_Count Equal to 0
        • Then - Actions
          • Trigger - Turn off Food Unit Dies <gen>
          • Trigger - Turn off Food Ownership Change <gen>
        • Else - Actions
  • Food Ownership Change
    • Events
      • Unit - A unit Changes owner
    • Conditions
      • Food_Cost[(Custom value of (Triggering unit))] Greater than 0
    • Actions
      • Set VariableSet Food_Unit = (Triggering unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • -------- --------
      • -------- Subtract food cost: --------
      • Player - Add (Food_Cost[Food_CV] x -1) to Food_Player[Food_CV].Food used
      • -------- --------
      • -------- Change owner: --------
      • Set VariableSet Food_Player[Food_CV] = (Owner of Food_Unit)
      • -------- --------
      • -------- Add food cost: --------
      • Player - Add Food_Cost[Food_CV] to Food_Player[Food_CV].Food used
It'd be nice to automate the first trigger so you could thin it down to just ~3 Actions. Maybe I'll do that once I get over my mild depression from wasting an hour creating my previous dumb solution.
 

Attachments

  • Food Fix 1.w3m
    22.8 KB · Views: 2
Last edited:
Level 5
Joined
Nov 3, 2018
Messages
80
Thank you uncle as always and
Thank you everyone for your replys! for now uncle's work around is useful for me this has been solved
Forgot you could do that to be honest.

But wouldn't this return 0 when it dies?
  • Set VariableSet foodCost = (Supply used by (Triggering unit))
I think you'd need to track that value prior to adding the Timed Life since it's food cost will be set to 0. Unit Indexing or a Hashtable would be simple enough.

Edit: Alright, this makes a lot more sense, thanks to Nichilus:
  • Food Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Unit - Create 1 Footman for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees
      • -------- --------
      • Set VariableSet Food_Unit = (Last created unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • Set VariableSet Food_Cost[Food_CV] = (Supply used by Food_Unit)
      • Set VariableSet Food_Player[Food_CV] = (Owner of Food_Unit)
      • -------- --------
      • -------- Add the expiration timer prior to adjusting the food property: --------
      • Unit - Add a 3.00 second Generic expiration timer to Food_Unit
      • -------- --------
      • -------- Add the food cost now: --------
      • Player - Add Food_Cost[Food_CV] to Food_Player[Food_CV].Food used
      • -------- --------
      • -------- Turn on necessary triggers: --------
      • Set VariableSet Food_Unit_Count = (Food_Unit_Count + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Food_Unit_Count Equal to 1
        • Then - Actions
          • Trigger - Turn on Food Unit Dies <gen>
          • Trigger - Turn on Food Ownership Change <gen>
        • Else - Actions
  • Food Unit Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • Food_Cost[(Custom value of (Triggering unit))] Greater than 0
    • Actions
      • Set VariableSet Food_Unit = (Triggering unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • -------- --------
      • -------- Subtract food cost: --------
      • Player - Add (Food_Cost[Food_CV] x -1) to (Owner of Food_Unit).Food used
      • -------- --------
      • -------- Reset food cost variable: --------
      • Set VariableSet Food_Cost[Food_CV] = 0
      • -------- --------
      • -------- Turn off unnecessary triggers: --------
      • Set VariableSet Food_Unit_Count = (Food_Unit_Count - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Food_Unit_Count Equal to 0
        • Then - Actions
          • Trigger - Turn off Food Unit Dies <gen>
          • Trigger - Turn off Food Ownership Change <gen>
        • Else - Actions
  • Food Ownership Change
    • Events
      • Unit - A unit Changes owner
    • Conditions
      • Food_Cost[(Custom value of (Triggering unit))] Greater than 0
    • Actions
      • Set VariableSet Food_Unit = (Triggering unit)
      • Set VariableSet Food_CV = (Custom value of Food_Unit)
      • -------- --------
      • -------- Subtract food cost: --------
      • Player - Add (Food_Cost[Food_CV] x -1) to Food_Player[Food_CV].Food used
      • -------- --------
      • -------- Change owner: --------
      • Set VariableSet Food_Player[Food_CV] = (Owner of Food_Unit)
      • -------- --------
      • -------- Add food cost: --------
      • Player - Add Food_Cost[Food_CV] to Food_Player[Food_CV].Food used
It'd be nice to automate the first trigger so you could thin it down to just ~3 Actions. Maybe I'll do that once I get over my mild depression from wasting an hour creating my previous dumb solution.
 
Top