• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Solved] Get Terrain at Point?

Status
Not open for further replies.
Level 3
Joined
Mar 1, 2009
Messages
14
Hey, so I'm making a map and it involves items spawning on the ground, but I want it to spawn somewhere pathable.

In short: How do I use JASS to check for terrain at a given point?

I want a given item to only spawn on the ground if the terrain is, say, Dalaran - Grass. (I'm using this terrain because I've set... say, Dalaran - Trim Grass, under unpathable things like trees and doodads.) I have a trigger for the rest of the item spawning that's all fine and well, but this is the part of the trigger I want to troubleshoot:

  • Custom script: loop
  • Set L = (Random point in safe zone west <gen>)
  • Custom script: exitwhen //need to find terrain type here to check against preset variable 'dalaran_grass'
  • Custom script: endloop
I know you can do a Terrain Type Comparison in the GUI editor which is the exact effect I'm looking for. I guess what I'm asking after is how to translate that into JASS, since as far as I know I have to keep "exitwhen" and the rest of the statement on the same line.

EDIT:

I used this guide by deathismyfriend to convert a trigger with only "If (Terrain type at (L)) equal to Dalaran - Grass" to JASS. I pulled what I think is what I need from that, so now my trigger looks like this:

  • Custom script: loop
  • Set L = (Random point in safe zone west <gen>)
  • Custom script: exitwhen exitwhen (GetTerrainTypeBJ(udg_L) == udg_dalaran_grass)
  • Custom script: endloop
Note that (udg_dalaran_grass) is a global variable that's just Dalaran - Grass where it's easy to call upon. (This isn't the only trigger that's gonna need to look for Dalaran - Grass, so I figure I'm not legally culpable for this.)

Am I right to use this, or is there a better/less stupid way?

EDIT 2: Looks like it works. Items are generating and I noticed none of them generating on tiles that weren't Dalaran - Grass. I'll strike this edit out and bump the thread if I later on notice that they're generating on other terrain types. Until then, leaving this up for posterity because Google didn't help me how I wanted when I was originally trying to figure this out.
 
Last edited:
The gras might be under a bulding, at cliffs, under unreachable doodads, etc. It might be checking for gras alone does not suffice.
But maybe there's no extra check needed, if it's specifically for spawning items. Actually, moving an item is already used as method to check if a point is walkable, or not. Check Walkability
 
Code:
function TerrainChangeInRect takes rect r returns nothing
    local real minx = GetRectMinX(r)
    local real miny = GetRectMinY(r)
    local real maxx = GetRectMaxX(r)
    local real maxy = GetRectMaxY(r)
    local real x=minx
    local real y=maxy
    local integer TType
    loop
        exitwhen y<miny
        loop
            exitwhen x>maxx
            set TType = GetTerrainType(x, y)
            if TType == 'Ldrt' then // 'Ldrt' is the TerrainType-Id that should be found
                call SetTerrainType(x, y, 'Jsqd', -1, 1, 0)
                // 'Jsqd' ist the TerrainType-Id that will replace 'Ldrt
            elseif TType == 'Lgrd' then
                // use more elseif's to add more terrain types that can be found in the rect and replace them with other ones
                call SetTerrainType(x, y, 'Jsqd', -1, 1, 0)
            endif
            set x=x+128
        endloop
        set x=minx
        set y=y-128.0
        call TriggerSleepAction(0.01) // The wait could be needed to prevent a small lag and to make the terrain completely change if the rect is too big
    endloop
endfunction

  • Events
    • Player - Player 1 (Red) types a chat message containing 2 as An exact match
  • Conditions
  • Actions
    • Custom script: call TerrainChangeInRect(gg_rct_Region_000)

Instead of calling SetTerrainType you just create the appropriate item.

I'm not sure if it will actually work as I don't know JASS but I know this effectively tracks terrain type and changes it out. If my theory is correct you should be able to just remove the functions that replaces the terrain and insert functions to create items instead.
 
Level 3
Joined
Mar 1, 2009
Messages
14
You could use it without the BJ wrapper but otherwise that’s a fine way to do it.

So... in other words, you're saying that this would be a valid way of doing it? Or am I mistaking what you're saying somehow?
  • Custom script: exitwhen (GetTerrainType == udg_dalaran_grass)
The gras might be under a bulding, at cliffs, under unreachable doodads, etc. It might be checking for gras alone does not suffice.
But maybe there's no extra check needed, if it's specifically for spawning items. Actually, moving an item is already used as method to check if a point is walkable, or not. Check Walkability

The way I have it set is that I put Ashenvale - Leaves underneath all the trees, and Dalaran - Grass where it's pathable (in the area of the map where items should spawn). I basically don't want it to be on Dalaran - Dirt Cliff (where it could be halfway off the cliff and be hard-to-reach or unreachable) or on Ashenvale - Leaves (trees, exclusively). That said, that link is very fascinating, so thank you very much for linking it.

Code:
function TerrainChangeInRect takes rect r returns nothing
    local real minx = GetRectMinX(r)
    local real miny = GetRectMinY(r)
    local real maxx = GetRectMaxX(r)
    local real maxy = GetRectMaxY(r)
    local real x=minx
    local real y=maxy
    local integer TType
    loop
        exitwhen y<miny
        loop
            exitwhen x>maxx
            set TType = GetTerrainType(x, y)
            if TType == 'Ldrt' then // 'Ldrt' is the TerrainType-Id that should be found
                call SetTerrainType(x, y, 'Jsqd', -1, 1, 0)
                // 'Jsqd' ist the TerrainType-Id that will replace 'Ldrt
            elseif TType == 'Lgrd' then
                // use more elseif's to add more terrain types that can be found in the rect and replace them with other ones
                call SetTerrainType(x, y, 'Jsqd', -1, 1, 0)
            endif
            set x=x+128
        endloop
        set x=minx
        set y=y-128.0
        call TriggerSleepAction(0.01) // The wait could be needed to prevent a small lag and to make the terrain completely change if the rect is too big
    endloop
endfunction

  • Events
    • Player - Player 1 (Red) types a chat message containing 2 as An exact match
  • Conditions
  • Actions
    • Custom script: call TerrainChangeInRect(gg_rct_Region_000)

Instead of calling SetTerrainType you just create the appropriate item.

I'm not sure if it will actually work as I don't know JASS but I know this effectively tracks terrain type and changes it out. If my theory is correct you should be able to just remove the functions that replaces the terrain and insert functions to create items instead.

This is a very interesting system as well. Honestly, all this in code makes it easy for me to just stick it in a New Custom Script instead of having to pick at individual Custom Script lines in an otherwise-purely-GUI trigger. I'll try this one out too and see which one I find good.

Thank you, everyone who's responded so far!
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
So... in other words, you're saying that this would be a valid way of doing it? Or am I mistaking what you're saying somehow?
No. Usually functions that have BJ (Blizzard Jass) at the end are just unnecessary wrappers for non-bj functions. It is recommended not to call these functions as they are by definition less efficient than calling the function they wrap directly. In most instances this would never make a noticeable difference, and GUI uses all these BJs anyway, but it's best practice not to use them. An example of one of these is below, followed by the function you're using.
JASS:
//useless BJ
function GetIssuedOrderIdBJ takes nothing returns integer
    return GetIssuedOrderId()
endfunction

//the function you used
function GetTerrainTypeBJ takes location where returns integer
    return GetTerrainType(GetLocationX(where), GetLocationY(where))
endfunction
You can see that the second isn't a simple wrapper since it takes a location argument and the native takes coordinates. This means I was wrong that you can't simply replace it unless you instead randomize coordinates, but honestly that wouldn't be any noticeable performance difference for you and locations are easier to work with in GUI. Leave it as it is.
 
Status
Not open for further replies.
Top