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

Tree Revival 1.2.3.2

Tree Revival System

Version: 1.2.3.2

Requirements:
- standard World Editor

Note: that this system is GUI-friendly.


Created due to limitation (max 64 destuctibles registered) brought by function TriggerRegisterDestDeathInRegionEvent(trig, r). Take a look:
JASS:
function RegisterDestDeathInRegionEnum takes nothing returns nothing
    set bj_destInRegionDiesCount = bj_destInRegionDiesCount + 1
    if (bj_destInRegionDiesCount <= bj_MAX_DEST_IN_REGION_EVENTS) then
        call TriggerRegisterDeathEvent(bj_destInRegionDiesTrig, GetEnumDestructable())
    endif
endfunction

function TriggerRegisterDestDeathInRegionEvent takes trigger trig, rect r returns event
    set bj_destInRegionDiesTrig = trig
    set bj_destInRegionDiesCount = 0
    call EnumDestructablesInRect(r, null, function RegisterDestDeathInRegionEnum)
    return trig
endfunction
When map initialization starts, bj_MAX_DEST_IN_REGION_EVENTS is automaticaly set to 64:
JASS:
constant integer   bj_MAX_DEST_IN_REGION_EVENTS     = 64
And thats the true reason why such issue occurs.
Idea is to enumerate through all destructibles on map and register death event of picked one (ofcourse at first we check if it's actually a tree).


Configurables:
- Recurrection time
- Show/hide birth animation

How to import:
- Copy and paste 'Tree Revival' category into your map
- 'TR Variable Creator' can be deleted as soon as you import system - it's here only for automatic variable creation

Additionaly:
- Features vanilla Jass IsDestructableTree version by PitzerMike.
So, whenever you want to check if given desctructible is actually a tree write down:

call TR_IsDestructableTree(somedest)or for GUI-users:
  • Custom script: call TR_IsDestructibleTree(<destructible here>)
Make sure to add 'udg_' prefix if you are using GUI globals.

- You can disable/enable tree reviving via global TRisEnabled.
Anytime you want tree not to be resurrected just use:

  • Set TRisEnabled = False
  • Destructable - Kill <tree here>
  • Set TRisEnabled = True
By defaul system has 'TRisEnabled' set to 'True', so trees are being revived anytime they die.


Credits:
- PitzerMike for IsDestructableTree.

Special thanks to:
- Bribe for 'uloc' dummy idea
- baassee for timers recycling idea

- Version 1.2.0.0 - system released
- Version 1.2.1.0 - added constant function for dummy, harvest ability and harvest order rawdatas
- Version 1.2.2.0 - replaced order of 'if' parameters in TRgetTimer function to improve script's speed
- Version 1.2.3.0 - added option TRisEnabled, you can now shut down the system anytime you want to
- Version 1.2.3.1 - function names has been changes, as Bribe suggested. Variable Creator added.
- Version 1.2.3.2 - Fixed debug tag usage for TR_recycleTimer so code compiles with new WE versions when debug mode is turned off.
Note: Units which step on position of tree which is currently being resurrected may get stuck between trees in that area.

System Code:
JASS:
//************************************************************************************************
//*     ____ ___  ___                                                                            *
//*    /_  _| _ \/ __/                     Tree Revival System                                   *
//*      | ||   /\__ \                         by Bannar                                         *
//*      |_||_|\_\___/ v1.2.3.2                                                                  *
//*                                                                                              *
//************************************************************************************************

//*********************************************************
//* Globals required
//*********************************************************

    //* udg_TRtrig                            Trigger for handling the revive actions
    //* udg_TRhash                            Hashtable for timer issues
    //* udg_TRindexD                          Stores revival instances
    //* udg_TRindexN                          Parameter for timers manipulation
    //* udg_TRtimers                          Timer array variable for recycle issues
    //* udg_TRdummyh                          Dummy harvester for IsDestructibleTree function
    //* udg_TRisEnabled                       Boolean parameter for enabling/disabling the system

//*********************************************************
//* Important
//*********************************************************

    // Note: Units which step on position of tree which is currently
    // being resurrected may get stuck between trees in that area

//*********************************************************
//* Constant configurable functions
//*********************************************************

    //* Rawdata of undead locust unit type
    constant function TR_dummyhId takes nothing returns integer
        return 'uloc'
    endfunction
   
    //* Rawdata of dummy ghoul harvest ability
    constant function TR_harvestId takes nothing returns integer
        return 'Ahrl'
    endfunction

    //* Harvest order ID
    constant function TR_orderId takes nothing returns integer
        return 852018
    endfunction
   
    //* Delay before tree gets resurrected     
    constant function TR_ReviveDelay takes nothing returns real
        return 5.
    endfunction

    //* Tells if birth animation should be shown while resurrecting a tree
    constant function TR_ShowAnimation takes nothing returns boolean
        return true
    endfunction

//*********************************************************   
//* System itself
//*********************************************************

function TR_recycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    debug else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    debug endif
endfunction

function TR_getTimer takes nothing returns timer
    if 0 == udg_TRindexN then
        return CreateTimer()
    endif
    set udg_TRindexN = udg_TRindexN - 1
    return udg_TRtimers[udg_TRindexN]
endfunction

function TR_Callback takes nothing returns nothing
    local destructable d = LoadDestructableHandle(udg_TRhash, 0, GetHandleId(GetExpiredTimer()))
    call DestructableRestoreLife(d, GetDestructableMaxLife(d), TR_ShowAnimation())
    call TR_recycleTimer(GetExpiredTimer())
    set udg_TRindexD = udg_TRindexD - 1
    if udg_TRindexD == 0 then
        call FlushChildHashtable(udg_TRhash, 0)
    endif
    set d = null
endfunction
   
function TR_CallRevive takes nothing returns boolean
    local timer t
    if udg_TRisEnabled then
        set t = TR_getTimer()
        set udg_TRindexD = udg_TRindexD + 1
        call SaveDestructableHandle(udg_TRhash, 0, GetHandleId(t), GetTriggerDestructable())
        call TimerStart(t, TR_ReviveDelay(), false, function TR_Callback)
        set t = null
    endif
    return false
endfunction

function TR_IsDestructableTree takes destructable dest returns boolean
    return IssueTargetOrderById(udg_TRdummyh, TR_orderId(), dest)
endfunction

function TR_AddTree takes nothing returns nothing
    if TR_IsDestructableTree(GetEnumDestructable()) then
        call TriggerRegisterDeathEvent(udg_TRtrig, GetEnumDestructable())
    endif
endfunction

//***************************************************************************
function InitTrig_TreeRevival takes nothing returns nothing
    set udg_TRtrig = CreateTrigger()
    set udg_TRhash = InitHashtable()
   
  //* By default enabled  
    set udg_TRisEnabled = true

  //* Actions required for IsDestructibleTree function
    set udg_TRdummyh = CreateUnit(Player(15), TR_dummyhId(), 0., 0., 0.)
    call ShowUnit(udg_TRdummyh, false)
    call UnitAddAbility(udg_TRdummyh, TR_harvestId())
    call UnitRemoveAbility(udg_TRdummyh, 'Amov')

  //* Revival setup
    call EnumDestructablesInRect(bj_mapInitialPlayableArea, null, function TR_AddTree)
    call TriggerAddCondition(udg_TRtrig, Condition(function TR_CallRevive))
endfunction

Keywords:
tree, revive, revival, system, Spinnaker, destructible
Contents

Tree Revival System. (Map)

Reviews
19th Oct 2011 Bribe: Thanks for making the changes - good system.
Dr Super Good
Confirmed to save without modification using 1.30.4. Sending back to approved.

Moderator

M

Moderator

19th Oct 2011
Bribe: Thanks for making the changes - good system.
 
if udg_TRindexD == 0 then
->
if 0 == udg_TRindexD then

Also, in this function:

JASS:
function InitTrig_TreeRevive takes nothing returns nothing
    set udg_TreeReviveTrig = CreateTrigger()
    set udg_TRhash = InitHashtable() 

  //* Actions required for IsDestructibleTree function
    set udg_dummyh = CreateUnit(Player(15), 'uloc', 0., 0., 0.)
    call ShowUnit(udg_dummyh, false)
    call UnitAddAbility(udg_dummyh, 'Ahrl')
    call UnitRemoveAbility(udg_dummyh, 'Amov')

  //* Revival setup
    call EnumDestructablesInRect(bj_mapInitialPlayableArea, null, function CallAddTree)
    call TriggerAddCondition(udg_TreeReviveTrig, Condition(function CallTreeRevive))
endfunction

When you enumerate the destructables, why don't you pass a the function CallAddTree as a boolexpr and just return false at the end.
You can pass null to the code parameter.

One thing:

JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    endif
endfunction


>_>
debug keyword?
This is Jass right? :p

Plus, even if it did exist in Jass, this wouldnt compile.
It's supposed to look like this:

JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    debug else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    debug endif
endfunction
 
I'm not sure about that but.. whatever you say :p

They still have to be changed though:

JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    endif
endfunction

->

JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    debug else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    debug endif
endfunction
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Bribe, I stayed with my enumerate function as it was, in case boolexpr in EnumDestructablesInRect() doesn't seem to work (or meaby it just can't revieve null code). You can test it though, since I was doing that long time ago.

EDIT: I just tried and my system registers totaly of 0 trees if I use boolexpr trick.
 
Last edited:
Level 19
Joined
Jul 12, 2010
Messages
1,713
i don't get it...is your system better than this?
  • Tree Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • Trigger - Add to TreeRevive Setup <gen> the event (Destructible - (Picked destructible) dies)
  • Tree Resurrection
    • Events
    • Conditions
    • Actions
      • Wait 6.00 seconds
      • Destructible - Resurrect (Dying destructible) with (Max life of (Dying destructible)) life and Show birth animation
it does pretty much the same thing...
 
@ xorkatoss: that would revive every destructable not just trees... and also buggy...

btw, nice trick to use harvest to check if its a tree... ^_^

just a question, can't we just change this:
JASS:
function CallAddTree takes nothing returns nothing
    if IsDestructableTree(GetEnumDestructable()) then
        call TriggerRegisterDeathEvent(udg_TreeReviveTrig, GetEnumDestructable())
    endif
endfunction

into

JASS:
function CallAddTree takes nothing returns nothing
    if IssueTargetOrderById(udg_dummyh, TRorderId(), GetEnumDestructable()) then
        call TriggerRegisterDeathEvent(udg_TreeReviveTrig, GetEnumDestructable())
    endif
endfunction
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
@xorkatoss Have you read explanation in first post? No.
Waits are inaccurate (if someone wants for example 60 sec delay before actually resurrecting a tree waits can take values ~55-72 if I'm not mistaken)

Your script doesn't contain option to check if destructable is tree and isn't user-friednly (doesn't have any options to choose from, user has to enter the script to change anything).

Plus it's much slower and much less efficient; since I've created GUI-friednly version there shouldn't be a problem for them (GUI-community) to implement the system. If they want to configure anything, there are multiple constant functions for that at the top (so user can omit the core).


@Adiktuz: The only reason why I haven't done such thing is because I want user be free to check if given destructible is a tree with just calling function (mentioned in first post) - I didn't want to force anyone (GUI-users especialy) to write:
  • Custom script: if IssueTargetOrderById(udg_dummyh, TRorderId(), <destructible here>) then
  • // stuff
  • Custom script: endif
I'm pretty sure there is nothing wrong with leaving that function alone, enabling anyone to just write:
  • Custom script: if IsDestructableTree(<destuctable here>) then
  • // stuff
  • Custom script: endif
I think it's much more handy and helps for further purposes, egzample: knockback which destroys only trees (not walls etc.).

~Version 1.2.3.0 released.
- You can now disable/enable tree reviving via global TRisEnabled.
Anytime you want tree not to be resurrected just use:

  • Set TRisEnabled = False
  • Destructable - Kill <tree here>
  • Set TRisEnabled = True
By defaul system has 'TRisEnabled' set to 'True', so trees are being revived anytime they die.
 
Last edited:
Level 19
Joined
Jul 12, 2010
Messages
1,713
@ xorkatoss: that would revive every destructable not just trees... and also buggy...
how about this one?
  • Regrow Trees Setup
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Destructible - Pick every destructible in (Entire map) and do (Actions)
        • Loop - Actions
          • Trigger - Add to RegrowTrees <gen> the event (Destructible - (Picked destructible) dies)
  • RegrowTrees
    • Events
    • Conditions
    • Actions
      • Custom script: local destructable BADTREE = GetDyingDestructable()
      • Wait 7.00 seconds
      • Custom script: call DestructableRestoreLife( BADTREE, GetDestructableMaxLife(BADTREE), true )
is his system better than this?
pls explain me what's the difference because i don't really understand it *_*
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Read this before actually posting nothing, yes nothing since you have just repeated your previous post.

After reading both (mine and Adiktuz) posts and accumulating that knowledge come back here with constructive criticism. It's simple system just to omit the limitation brought by TriggerRegisterDestDeathInRegionEvent(trig, r), and thats why systems are usually made for - to make life easier.

Just additional issue about your script (to those mentioned already above): GetDyingDestructible() is slower than GetTriggerDestructible().
Don't fail so much next tim dude. Good luck.
 
Why dont just make it a simple system of GUI periodic event each 5 mins or 8 mins or wharever.

Event:
each 10 mins of the game
Conditions:
Null
Action
IfThenElse:
Pick every destructible in entire map
-Or Multiple conditions
-If Picked destructible (all types of tree)
-If life of picked desructible is equal to 0
--Action:
Resurrect picked destructible and show birth anims
Else: Remove not matching destructibles.. etc

Else you can do a pick destructible group and try saving it on a hashtable by the use of another trigger (with custom script). Anyways its a good system.

Else you should remember that if there is a unit near a dead tree (100 units near) i recommend you to remove the tree or to avoid the resurrection of it.
 
Level 19
Joined
Jul 12, 2010
Messages
1,713
Just additional issue about your script (to those mentioned already above): GetDyingDestructible() is slower than GetTriggerDestructible().

ive already readed the one above, i just didn't understand anything xD
i really couldn't care less about the GetTriggerDestructible() thingy...
only useful scripts to me are "RemoveLocation" and "call want_DestroyGroup = true" since im a GUIer...

EDIT:
i forgot to tell you, that tree revival system is not mine, it was in this spell...so it wasn't me who actually failed lol
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
@Adiktuz
It gets inlined anyways :p

With Vex's Optmizer yes? Else no.

Why dont just make it a simple system of GUI periodic event each 5 mins or 8 mins or wharever.

Event:
each 10 mins of the game
Conditions:
Null
Action
IfThenElse:
Pick every destructible in entire map
-Or Multiple conditions
-If Picked destructible (all types of tree)
-If life of picked desructible is equal to 0
--Action:
Resurrect picked destructible and show birth anims
Else: Remove not matching destructibles.. etc

Else you can do a pick destructible group and try saving it on a hashtable by the use of another trigger (with custom script). Anyways its a good system.

Else you should remember that if there is a unit near a dead tree (100 units near) i recommend you to remove the tree or to avoid the resurrection of it.

1. If a tree dies 1 second before the 8 min mark, it will be revived.

2. He is already saving into a hashtable?

3. I don't see the need for this. It does what it's supposed to do. Reviving trees.

ive already readed the one above, i just didn't understand anything xD
i really couldn't care less about the GetTriggerDestructible() thingy...
only useful scripts to me are "RemoveLocation" and "call want_DestroyGroup = true" since im a GUIer...

EDIT:
i forgot to tell you, that tree revival system is not mine, it was in this spell...so it wasn't me who actually failed lol

Knowing a bit JASS could be good for a GUIer, especially when it comes to mapmaking (when you learn vJASS like me you'll find out how shit the map was before you fixed it).

That local tree system is a basic system made by someone at TH I think (maybe even Ryoko) or made by Niddhog-kunn back in the old days of THW (I don't remember which one who was the one who made it).

Waits depends on the latency of the player. And lower periods.
 
JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    endif
endfunction

Spinnaker, this wouldn't even compile outside debug mode :p

It would compile to this:

JASS:
function TRrecycleTimer takes timer t returns nothing
    else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    endif
endfunction

This would be a compile error.
It causes the map to be unplayable.

That's why you should do this:

JASS:
function TRrecycleTimer takes timer t returns nothing
    debug if t == null then
        debug call BJDebugMsg("Attempt to release a null timer")
    debug else
        call PauseTimer(t)
        set udg_TRtimers[udg_TRindexN] = t
        set udg_TRindexN = udg_TRindexN + 1
    debug endif
endfunction
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
What'd happen if tree's coords are 0,0? It will be attacked - tested and even if it's rare - that sucks. Will soon check if this occurs while using this snippet and correct if needed.

Edit: Adding IssueImmediateOrderById(udg_dummyh, 851972) will fix the issue.
 
Last edited:
Level 6
Joined
Mar 11, 2018
Messages
135
Well I did what you said but it does not work when i try to copy past the tree revival into my map.
 
Level 6
Joined
Mar 11, 2018
Messages
135
i don't get it...is your system better than this?
  • Tree Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Destructible - Pick every destructible in (Playable map area) and do (Actions)
        • Loop - Actions
          • Trigger - Add to TreeRevive Setup <gen> the event (Destructible - (Picked destructible) dies)
  • Tree Resurrection
    • Events
    • Conditions
    • Actions
      • Wait 6.00 seconds
      • Destructible - Resurrect (Dying destructible) with (Max life of (Dying destructible)) life and Show birth animation
it does pretty much the same thing...


ok do any of these even work any more? Half of those things are just not possible to do because the command does not exist. And any time I ask someone to prove it by giving me a map download i eather get "I don't have to prove it it is easy to do." or I put it in move it to another map and it does not work. So what can I possable be doing wrong that this stuff just can't happen?
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,180
07/Mar/2019
Moved to awaiting updated: The map fails to save on 1.30.4 editor due to bad debug tag usage. It can only be saved with JASSHelper set to debug build mode.

The error is in the TR_recycleTimer function. One has to debug tag the else and endif blocks so they are ignored when not built in debug mode.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
07/Mar/2019
Moved to awaiting updated: The map fails to save on 1.30.4 editor due to bad debug tag usage. It can only be saved with JASSHelper set to debug build mode.

The error is in the TR_recycleTimer function. One has to debug tag the else and endif blocks so they are ignored when not built in debug mode.
Thank you for reporting this.

Updated the resource as per moderator request.
 
Level 6
Joined
Oct 25, 2010
Messages
203
Amazing! I was thinking about adding this into a map I am working on but, I have some questions:

1- what happens if a player constructs buildings where the trees were cutdown? will trees regrow all around the building and block/trap units?

2- say if a player uses siege/aoe to cut a path through the trees, will the whole area regrow all at nearly the same time?

3- is it possible to set it so only 20% of trees will regrow? or maybe have some trees will regrow faster than others, and some will take a very long time (randomly)? that way I dont have to worry about buildings getting blocked because the entire area filled back in all at once

Maybe you could set it up like this:

10% of trees regrow in 10-20mins
15% of trees regrow in 25-35mins
20% of trees regrow in 40-50mins
25% of trees regrow in 60+ mins
30% of trees never regrow
and trees shouldnt regrow directly around buildings

or something like that, I think it would be nicer than having them all regrow at the same time, if you could update your triggers for me I would really appreciate this so then I can use it in my map without worrying that a players base might get blocked :) thanks!


Off-topic:
Another idea that could be included with this is: maybe possibly creating some kind of spell/item that allows players to raise/grow trees? maybe allowing players to grow trees in new places and create new walls to possibly trap enemies with? lol thats just a silly idea, its not necessary though :p actually... that might already exist so nvm
 
Last edited:
Top