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

Terrain with a negative health regeneration rate a-ka 'reverse blight'

Status
Not open for further replies.
Level 2
Joined
Nov 15, 2010
Messages
16
Hi,

is there a way to associate a certain terrain type with a negative health regeneration rate when a unit steps on the terrain? The same way undead units health regeneration rate is increased when they step into blight terrain, but to make that a negative health regeneration rate for a specific terrain type?

In this map, I want my unit to start losing health per second when it steps on that lava-looking terrain that I don't use anywhere else on the map, e.g. I want unit to lose -1 health per second on that terrain type, and when the unit stands on the other terrain types, that would have no effect on unit health regeneration rate.

Bonus if this can be done without a use of dummies or time periodic events (I assume blight health regeneration mechanic is not using either of those?)
 

Attachments

  • TerrainType - Copy.w3x
    17.4 KB · Views: 18
  • Loop through all units you want to check, doing so each second. (periodic timer or trigger is required)
  • Perform a check for terrain under each unit. ( terrain comparison at position of unit )
  • Lastly put an action in the check, for setting the unit HP - 1
There's no way without something periodic, as per per definition it's requested to do something periodically, something which can be done only with triggers. (checking terrain type at location)
 
Level 12
Joined
Jan 30, 2020
Messages
875
Using periodic timers to check units situations is ugly scripting in an event-driven scripting language.

Why not simply settings regions to cover these lava zones ?

Then you could use the event a unit enters region ?

And then you can start your repeating timer that remove 1HP every second

And stop that timer when the unit leaves the region ?

EDIT :
Looking at your terrain, and considering regions in GUI are just rects and not real regions, I understand it won't be as easy to make in GUI, but doable and cleaner.

In Jass or Lua, regions are containers that can have many rects added to them, and thus if you used Jass or Lua you could add all the rects covering your lava terrain to a single region for the event.

In GUI unfortunately, you might need 1 event for every rect...
 
I forgot about regions approach. Good point.

But it's maybe only more easy in very specific cases, though, I don't think it's really superior. And I honestly also fail to see that it's cleaner to use rects.
  • may need big amount of rects for defining lava places
  • needs to be maintained after terrain changed in editor
  • works only statically - in case terrain type is changed via trigger, it may lead to issues
One could compute whole map at map start, fill a table, which tile is lava, and work with regions, and maintaining it in case of terrain changes, but it seems to be more work, too.

Checking the terrain type with a timer seems pure and straight forward, without relying on other things. At least my opinion.^^
 
Level 12
Joined
Jan 30, 2020
Messages
875
The rects would still be more efficient performance wise.
Unless your map has very few units, and/or you use a slower timer.

Fact is if you pick up all units on the map with a filter on a high frequency (to make sure they're of the specified type, and above a lava tile) is simply a huge amount of useless strain on the game engine. I mean if that kind of event seems to offer easy solutions, most of the time the trade off is not worth it, unless there is no alternative.

Maybe use a bit of Lua / Jass (I would advise Lua, because it's so much better than Jass, and it's virtual machine much more efficient), then create a region, add all your rects containing Lava (you can store these rects in an array at map init yes, and then it's easy to add or remove them with code if you change the terrain dynamically).

Now you don't have to trust me, you can go the "apparently" easy way, but thats definitely not how things should get done with scripting and with efficiency.
This said, if you do not plan to polish the map out in the long run, or have no intent to make it too complex or heavy on the engine (number of units, sfx, and other agents), then yes it wouldn't matter too much.

EDIT : oops I nearly confused you with OP lol
 
Looping over unit[array], instead of PickingAllUnits on map. All units that need to be checked, can be put into the array.

I believe it's actually also better performance-wise over enter/leave event, as there's no region overhead.
The game still needs to periodically check, if unit comes-into/leaves region cells. Furthermore, maps that have too many region cells in usage, might suffer from unit-pathing issues. ( case scenario , more talk )
Now, that might be a bit over the top, but might still be interesting for enter/leave events, that the internal batch which fires the enter/leave events might not be in sync anymore with current state, if unit is still inside a region, or not. Strange location issues
 
Level 12
Joined
Jan 30, 2020
Messages
875
Well honestly I do not regret having started this discussion, as It is indeed very instructive.

Indeed @Dr Super Good gave clear explanations about the number of cells issue, so yes in the end I was wrong, adding all these rects in a center region would be less viable than actually having an array of triggers, one for each rect containing lava terrain. This also means that ingame created lava rects will need to have their triggers added dynamically too.
So far my map, since I switched to Lua, behaves with really high performance with each rect having its own trigger.

As for the issues with enter/leave region, I didn't get any problem once I coded things properly : in my map I use waypoints, and they're actually points on the map where I create a rect around with a half side of 64. Just for the record, here is my entire trigger that create all waypoints note that as I said earlier, I register a trigger for each region(containing each a single rect)) :
Lua:
function CreateWPs()
    -- WayPoints : IDs and coordinates note that WP[*0] are the spawn coordinates for each player.
    -- The coordinate adjustments are only made for the WayPoints Numbers to display properly
    -- Citadel will be considered as Destination number 50 when using the global array Destination
    Destination=__jarray(0)
    NextWayPoint,WP,WPr={},{},64.00
    WayPointID={FourCC("B011"),
                FourCC("B012"),
                FourCC("B013"),
                FourCC("B014")}
    WPx,WPy=__jarray(0.0),__jarray(0.0)
    WPx[10],WPy[10]=-7360.0,7808.0
    WPx[11],WPy[11]=-5750.0,7805.0
    WPx[12],WPy[12]=-1535.0,7885.0
    WPx[13],WPy[13]=-1560.0,6505.0
    WPx[14],WPy[14]=-3980.0,6480
    WPx[15],WPy[15]=-2933.0,4075.0
    WPx[16],WPy[16]=-1535.0,4040.0
    WPx[17],WPy[17]=-896.0,2496.0
    WPx[18],WPy[18]=-512.0,1472.0
    WPx[20],WPy[20]=WPy[10],-WPx[10]
    WPx[21],WPy[21]=WPy[11],-WPx[11]-35.0
    WPx[22],WPy[22]=WPy[12]+20.0,-WPx[12]-5.0
    WPx[23],WPy[23]=WPy[13]+15.0,-WPx[13]
    WPx[24],WPy[24]=WPy[14]+10.0,-WPx[14]-20.0
    WPx[25],WPy[25]=WPy[15]+10.0,-WPx[15]-35.0
    WPx[26],WPy[26]=WPy[16]+15.0,-WPx[16]-20.0
    WPx[27],WPy[27]=WPy[17]+40.0,-WPx[17]-20.0
    WPx[28],WPy[28]=WPy[18]+20.0,-WPx[18]-15.0
    WPx[30],WPy[30]=-WPx[10],-WPy[10]
    WPx[31],WPy[31]=-WPx[11],-WPy[11]-10.0
    WPx[32],WPy[32]=-WPx[12],-WPy[12]-25.0
    WPx[33],WPy[33]=-WPx[13]-5.0,-WPy[13]-25.0
    WPx[34],WPy[34]=-WPx[14]-20.0,-WPy[14]-15.0
    WPx[35],WPy[35]=-WPx[15]-10.0,-WPy[15]-30.0
    WPx[36],WPy[36]=-WPx[16],-WPy[16]-25.0
    WPx[37],WPy[37]=-WPx[17]-5.0,-WPy[17]-40.0
    WPx[38],WPy[38]=-WPx[18]-5.0,-WPy[18]-30.0
    WPx[40],WPy[40]=-WPy[10],WPx[10]
    WPx[41],WPy[41]=-WPy[11],WPx[11]+10.0
    WPx[42],WPy[42]=-WPy[12]-20.0,WPx[12]-10.0
    WPx[43],WPy[43]=-WPy[13]-15.0,WPx[13]-15.0
    WPx[44],WPy[44]=-WPy[14]-20.0,WPx[14]
    WPx[45],WPy[45]=-WPy[15]-15.0,WPx[15]
    WPx[46],WPy[46]=-WPy[16]-20.0,WPx[16]-10.0
    WPx[47],WPy[47]=-WPy[17]-35.0,WPx[17]-15.0
    WPx[48],WPy[48]=-WPy[18]-25.0,WPx[18]-15.0
    WPx[50],WPy[50]=0.0,0.0
    WPRegion={}
    for i=1,4 do
        for j=0,8 do
            local k=i*10+j
            wpRect=Rect(WPx[k]-WPr, WPy[k]-WPr, WPx[k]+WPr, WPy[k]+WPr)
            WPRegion[k]=CreateRegion()
            RegionAddRect(WPRegion[k], wpRect)
            RemoveRect(wpRect)
        end
    end
    wpRect=Rect(- 1024.0,-1024.0, 1024.0, 1024.0)
    WPRegion[50]=CreateRegion()
    RegionAddRect(WPRegion[50], wpRect)
    RemoveRect(wpRect)
    -- Creating the WayPoints and their triggers
    for i=1, 4 do
        if Playing[i] then
            for j=1,8 do
                local wn=i*10+j
                WP[wn]=CreateDestructable(WayPointID[i], WPx[wn], WPy[wn], 270.0, 2.0, 0)
                SetDestructableAnimation(WP[wn], Anim[53+j])
                NextWayPoint[wn]=CreateTrigger()
                TriggerRegisterEnterRegion(NextWayPoint[wn], WPRegion[wn], nil)
                TriggerAddCondition(NextWayPoint[wn], Condition(NextWP))
            end
        end
    end
    NextWayPoint[50]=CreateTrigger()
    TriggerRegisterEnterRegion(NextWayPoint[50], WPRegion[50], nil)
    TriggerAddCondition(NextWayPoint[50], Condition(NextWP))
end
Now, here is the action (run in condition for performance) :
Lua:
function NextWP()
    local b=GetEnteringUnit()
    if (GetOwningPlayer(b)==BallsMaster) then
        local bn=GetUnitUserData(b)
        local d=Destination[bn]
        local r=GetTriggeringRegion()
        if (r==WPRegion[d]) then
            if (GetUnitAbilityLevel(b, BuffID[9])>0) then -- Removing Going Back Home buff and text
               UnitRemoveAbility(b, BuffID[9])
               BlzUnitDisableAbility (b, GBHTextID, true, false)
               UnitRemoveAbility(b, GBHTextID)
            end
            if (d<50) then
                d=d+1
                if (math.fmod(d, 10)>8) then
                    d=50
                end
                Destination[bn]=d
                IssuePointOrder(b, "move", WPx[d], WPy[d])
            else
                local pn=math.floor((bn-1)/BallsPerLevel)+1
                PlayCleanSFX(SFXCitadel , GetUnitX(b), GetUnitY(b), 2.0, 0.0, 0, 1.0)
                PlayGlobalSound(SNDLeak,SNDLeakDur)
                local lost=1
                if IsGiant then
                    lost=4
                end
                if IsFlyingGiant then
                    lost=5
                elseif (Level>(MaxLevel-2)) then
                    lost=3
                end
                PlayerLives[pn]=PlayerLives[pn]-lost
                GameboardInit()
                if (PlayerLives[pn]<=0) then
                    PlayerLives[pn]=0
                    DefeatPlayer(pn)
                end
                StoreBall(b, bn, pn)
            end
        end
    end
    return false
end

As you can see, I just check that the entered region is the destination of the creep (because it can potentially enter or leave other regions, and that won't be considered as the desired target). Now these triggers work rock solid, without any hiccup ever.
Note that in the past, I used similar triggers in Jass with 8 players instead of 4, so twice as many regions.

I am not sure TriggerRegisterLeaveRegion is any less functional.

As for OP's problem, I do believe he wants to add more and more features to his map, like changing terrain dynamically.
So of course managing all these regions would not be as easy as a simple periodic timer to check if the array of units has the proper tile around its position, but I still believe using them with the dedicated triggers would be beneficial, because the game engine won't be asked to do constant extra checks outside those triggered by the right context.

I do believe the game engine is also event driven, I really doubt constant numerous checks on the entire set of agents would allow the game to run that fast. If I am wrong, this entire grief I have against continuous fast repeating timers with checks is a mistake.
 
There are still internal checks, if to fire enter/leave events. But yes, it's most likely faster than checks from user script.
One other point would be the rects exist globally, as just perma objects. They influence potentially all map/units. You make a filter then here, of course, if triggered region is the wanted destination, to ensure correct unit or region.
It just gives me the feel of overhead, having to deal with rects and regions, if I actually only want to check the terrain under the unit. Or in your case probably distance to point x/y. (though for waypoints, it might make more sense already to use regions, as you may really want to cover some rect-shape, instead of working only with one x/y)

Well honestly I do not regret having started this discussion, as It is indeed very instructive.
Absolutely! Different opinions, but I see nothing bad with it. :peasant-ok-hand:
 
Level 12
Joined
Jan 30, 2020
Messages
875
Thats a pretty clever and efficient alternative !


It just gives me the feel of overhead, having to deal with rects and regions, if I actually only want to check the terrain under the unit. Or in your case probably distance to point x/y. (though for waypoints, it might make more sense already to use regions, as you may really want to cover some rect-shape, instead of working only with one x/y)

In fact I used to just check distance when I used dummy units as waypoints, but as since Reforged we can not hide the units from the minimap, I had to switch to destructables.

Fun thing is that I initially had some issues with strange behaviors that forced me to check the distance (actually the square of it to avoid slow Jass square root calculations by then) when the event was firing. In the end, for some reason the switch with rects and their region ended up just as efficient.

One must admit that we never know for sure how the game engine handles all this exactly, thats also a reason why I generally try to keep some good practices when scripting, even if nothing guarantees they are always the most efficient way.
 
Last edited:

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,870
Regions are doable, even if you had 5000 regions on the map doesn't mean the pathing will break, unless each of those regions cover most of the map for no apparent reason.
You could check when a unit enters/leaves region and then check the terrain type at that point to see if it's lava or not and then add/remove ability (item life regeneration with negative regen) from the unit.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Yes that was roughly the idea with the regions, although the check might be counter productive (if the border of the rect is not precisely inside a lava tile, it might not work as the trigger won't fire again until the unit enters the region again). We would have really needed an event like "unit moves in region", it would have been better overall ^^

This said, I now believe @FeelsGoodMan 's method to be the most elegant if the range of the dummy unit's immolation matches the tiles size.
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,870
It will never match, since the range is circumference, a tile is rectangular. The dummy might not be placed at the exact center of the tile, thus why regions are better.
Yes my terrain check won't work if the regions are not put properly, but it is needed to know when the unit leaves the region and check if the tile is still lava or not because one might leave a region but in the meantime still in lava tile, in the process of entering another region.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Yes you have a good point indeed, if the immolation route was chosen, all corners of the tiles would fail to function properly.
@spellwalker : if you chose the regions route and need some help, please let us know !
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
Indeed @Dr Super Good gave clear explanations about the number of cells issue, so yes in the end I was wrong, adding all these rects in a center region would be less viable than actually having an array of triggers, one for each rect containing lava terrain. This also means that ingame created lava rects will need to have their triggers added dynamically too.
Rects and regions are different. There is no native "unit enters rect" event. The event implicitly converts the rect into a region and uses unit enters region event. It is usually better to use a single region with all rects added to it since it avoids intersecting cells. Issues occur if too many cells are added to regions.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Oh, thanks, I hadn't thought of the intersections.

I suppose it also means that the concern with a single big region containing the cells only becomes relevant with very large amount of cells.

In this particular case here, it means a single region would be better then, unless the majority of the map has lava tiles.
I guess if the map has more lava tiles than other tiles, doing the opposite than what was suggested would be beneficial.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
I suppose it also means that the concern with a single big region containing the cells only becomes relevant with very large amount of cells.
It only becomes an issue if multiple such regions exist. Having 1 region holding every cell being used for both unit enters and exits region events is perfectly fine.

A single region for all lava tiles is in theory fine, even on top of a generic one covering the entire map. One just has to avoid a lot of regions covering the entire map.
 
Status
Not open for further replies.
Top