Terrain Check

Azylaminaz

Vox Populi
Reaction score
91
I think I've put too much brainpower into this already... I'm clearly missing something. :X

OK, so, basically I want to create a "smarter" terrain check. The classic means (simply detecting the terrain with GetTerrainType) gives funny results because "terrain" is seen as simple squares, ignoring how the art actually looks. What I want is a system that accurately reflects the art....

Terrain actually works in triangles (eight per a traditional square).

GWogS.jpg

Blue & light blue: correctly valid
Purple: incorrectly valid
Red: correctly invalid
Green: incorrectly invalid
*valid means marble, invalid means square tiles.

This is also just one of two ways terrain can act. A tile can "bridge" over another type or "break" at it (in terms of going diagonally). Mrable is an example of bridging. Further more, a tile that bridges can over-extend past itself. Marble is an example of one that doesn't; brick, round/square tiles do over-extend.

Preloading the validity of each triangle would be near impossible as even a tiny map (52x52 playable) has 173,056 triangles. The other way would be simply detect on command. IE, pass over a point to check if it is valid or not - this is the approach I've been trying and failing on for days.

I've attacked this problem from a number of sides. -.- The following is my latest attempt at doing this.
Pre-note: View the terrain as a diamond-checkerboard. The deep-blue diamonds are an example of these (as well as the diamonds diagonally between them).

Rules for simple tiles such as marble:
1) Any diamond that contains three or more valid classically checked triangles is 100% valid (contains obvious blue diamonds as well as "3 light blue 1 green" diamonds)
2) Any diamond that contains two opposite triangles that share obtuse sides with blue triangles is 100% valid (contains diamonds that are bridges, "2 light blue 2 green")
3) Any diamond that contains two adjacent triangles that share obtuse sides with blue triangles is 50% valid, those sharing triangles being the valid ones (contains "2 light blue 2 red")
4) Every other triangle is invalid.

Step 1: For any given point, get a rigid point at the center of the corresponding diamond. IE, a point in any triangle will fall to exactly where the 90 degree angle is. Get angles radiating from the center of the diamond to the midpoints of the obtuse feeding triangles (45/135/225/315).

Step 2: Use above rules to see if a the triangle is valid or not.
I can probably pretty easily do step 2, the problem is step 1 (finding the center of diamonds/triangles). I want to tell myself the solution is obvious and I'm just running into a mental block, but eh. :/

Edit: OH. And my attempts at trying to find the center of dimaonds were:
First: To find if the x or y of the diamonds were closest. The x of the diamond would be Int(x / 64 + .5) * 64, the y similar. Whichever was closer would get it. The other would be +32 or -32. This simply wasn't working and was getting ugly with so many if's.

Second: To work in a rotated Cartesian system. I tried this by converting x/y to polar coordinates, rotating it by 45 degrees, converting back into Cartesian coordinates (on a rotated Cartesian plane, now). Then I'd simply do "Int(x / 64 + .5) * 64" and "Int(y / 64 + .5) * 64". I'd then turn these points back to polar, rotate by -45, and convert back to traditional Cartesian.

I was failing at converting back and fourth (was testing with a simple x = 1, y = 2; x' and y' being the values on the rotated field):
x' = (x^2 + y^2)^.5 * Cos(Atan2(y/x) + Pi/4)
y' = (x^2 + y^2)^.5 * Sin(Atan2(y/x) + Pi/4)

x = (x'^2 + y'^2)^.5 * Cos(Atan2(y'/x') - Pi/4)
y = (x'^2 + y'^2)^.5 * Sin(Atan2(y'/x') - Pi/4)

I failed.

Somebody help before I have an accident resulting in my unfortunate death. :X
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Azylaminaz
1. Preloading the validity of each triangle would be near impossible as even a tiny map (52x52 playable) has 173,056 triangles.

1. Hm... shouldn't a hashtable do the job? Or maybe 10 hashtables, a 100? Your map can make use of the 256 limit for hashtables, and
256 limit for gamecaches, if you don't want to overload 1 single hashtable. I am not sure but maybe you can generate the need data
for the triangles with an external program and just copy it in to functions, and of course coupled with a dozen of ExecuteFunc calls, to
prevent hitting the oplimit. So I guess this could be a brute force solution.
 

inevit4ble

Well-Known Member
Reaction score
38
As I understand it, Position of Unit (GetUnitX/Y) gives the center point of the unit. As with regions, an event like UnitEntersRegion it will only trigger if the unit's center point enters the region.

Don't know if the will help but if the used the Position of Unit, you would have the center point of where the unit is standing and check terrain at Point?
 

Switch33

New Member
Reaction score
12
inevit4ble might have something there. You could place a unit per square then check a distance with the facing angles to get at least 4 of the triangles for the terrain.

Another maybe more realisitc approach for this might be to just export the terrain models from the mpq and re-create them as triangles somewhat?
 

Azylaminaz

Vox Populi
Reaction score
91
Have made some progress! Finally have hammered out my biggest problem: finding the reference point for any given point.

From here it should be free sailing, but considering this function may be called thousands of times a second, I'd love feedback on making it more efficient (ignoring the BJs, I mean another METHOD).

JASS:
function grabCenter takes real x, real y returns nothing
    local real x2
    local real y2
    if x < 0 then
        set x2 = R2I((x - 32)/64) * 64
    else
        set x2 = R2I((x + 32)/64) * 64
    endif
    if y < 0 then
        set y2 = R2I((y - 32)/64) * 64
    else
        set y2 = R2I((y + 32)/64) * 64
    endif
    if x2 + y2 != 0 and ModuloReal(x2 + y2, 128) != 0 then
        if ModuloReal(x, 32) < ModuloReal(y, 32) then
            call BJDebugMsg("a")
            if RAbsBJ(y2 + 64 - y) > RAbsBJ(y2 - 64 - y) then
                set y2 = y2 - 64
            else
                set y2 = y2 + 64
            endif
        else
            if RAbsBJ(x2 + 64 - x) > RAbsBJ(x2 - 64 - x) then
                set x2 = x2 - 64
            else
                set x2 = x2 + 64
            endif
        endif
    endif
    call BJDebugMsg("in: (" + R2S(x) + ", " + R2S(y) + "), out: (" + R2S(x2) + ", " + R2S(y2) + ")")
endfunction




Edit:
You guys aren't understanding the problem. The GetTerrainType() native returns false positives and false negatives, as depicted by the picture.
 

Azylaminaz

Vox Populi
Reaction score
91
Yee. :D I did it (well, sort of, it works for marble over... anything). :D

JASS:
globals
    integer array ttID  //NEEDS TO BE WRITTEN IN ORDER
    boolean array ttBridge
    string array ttName
endglobals

function getTerrainType takes real x, real y returns integer
    //variables for grabbing center of x/y (into x2/y2)
    local real x2
    local real y2
    local real modPrime
    local real modX
    local real modY
    local real upDif
    local real downDif
    //variables for finding terrain type
    local integer i
    local integer typeA = 0
    local integer typeB = 0
    if x < 0 then
        set x2 = R2I((x - 32)/64) * 64
    else
        set x2 = R2I((x + 32)/64) * 64
    endif
    if y < 0 then
        set y2 = R2I((y - 32)/64) * 64
    else
        set y2 = R2I((y + 32)/64) * 64
    endif
    if x2 + y2 != 0 then
        set modPrime = x2 + y2 - I2R(R2I((x2 + y2) / 128)) * 128
        if modPrime < 0 then
            set modPrime = modPrime + 128
        endif
        if modPrime != 0 then
            set modX = x - I2R(R2I(x / 32)) * 32
            if modX < 0 then
                set modX = modX + 32
            endif
            set modY = y - I2R(R2I(x / 32)) * 32
            if modY < 0 then
                set modY = modY + 32
            endif
            if modX < modY then
                set upDif = y2 + 64 - y
                if upDif < 0 then
                    set upDif = -upDif
                endif
                set downDif = y2 - 64 - y
                if downDif < 0 then
                    set downDif = -downDif
                endif
                if upDif > downDif then
                    set y2 = y2 - 64
                else
                    set y2 = y2 + 64
                endif
            else
                set upDif = x2 + 64 - x
                if upDif < 0 then
                    set upDif = -upDif
                endif
                set downDif = x2 - 64 - x
                if downDif < 0 then
                    set downDif = -downDif
                endif
                if upDif > downDif then
                    set x2 = x2 - 64
                else
                    set x2 = x2 + 64
                endif
            endif
        endif
    endif
    
    //simple diamond
    if GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 - 16, y2 + 16) and GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 - 16, y2 - 16) then
        return GetTerrainType(x2 + 16, y2 + 16)
    elseif GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 - 16, y2 + 16) and GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 + 16, y2 - 16) then
        return GetTerrainType(x2 + 16, y2 + 16)
    elseif GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 - 16, y2 - 16) and GetTerrainType(x2 + 16, y2 + 16) == GetTerrainType(x2 + 16, y2 - 16) then
        return GetTerrainType(x2 + 16, y2 + 16)
    elseif GetTerrainType(x2 - 16, y2 - 16) == GetTerrainType(x2 + 16, y2 - 16) and GetTerrainType(x2 - 16, y2 - 16) == GetTerrainType(x2 - 16, y2 + 16) then
        return GetTerrainType(x2 - 16, y2 - 16)
    endif
    
    //adjacent
    if x >= x2 and GetTerrainType(x2 + 48, y2 + 48) == GetTerrainType(x2 + 48, y2 - 48) then
        return GetTerrainType(x2 + 48, y2 + 48)
    elseif y >= y2 and GetTerrainType(x2 + 48, y2 + 48) == GetTerrainType(x2 - 48, y2 + 48) then
        return GetTerrainType(x2 + 48, y2 + 48)
    elseif x <= x2 and GetTerrainType(x2 - 48, y2 - 48) == GetTerrainType(x2 - 48, y2 + 48) then
        return GetTerrainType(x2 - 48, y2 + 48)
    elseif y <= y2 and GetTerrainType(x2 - 48, y2 - 48) == GetTerrainType(x2 + 48, y2 - 48) then
        return GetTerrainType(x2 - 48, y2 - 48)
    endif
    
    //diangol IN REALITY I NEED TO CHECK IF IT BRIDGES
    if GetTerrainType(x2 + 48, y2 + 48) == GetTerrainType(x2 - 48, y2 - 48) then
        set typeA = GetTerrainType(x2 + 48, y2 + 48)
    endif
    if GetTerrainType(x2 + 48, y2 - 48) == GetTerrainType(x2 - 48, y2 + 48) then
        set typeB = GetTerrainType(x2 + 48, y2 - 48)
    endif
    
    if typeA != 0 and typeB != 0 then
        set i = 0
        loop
            if ttID<i> == typeA then
                return typeA
            elseif ttID<i> == typeB then
                return typeB
            endif
            if ttID<i> == null then
                exitwhen true
            endif
            set i = i + 1
        endloop
    else
        if typeA != 0 then
            return typeA
        elseif typeB != 0 then
            return typeB
        endif
    endif
    
    return 0
endfunction

function Trig_Center_Actions takes nothing returns nothing
    local integer terrain
    call Split(GetEventPlayerChatString(), &quot; &quot;, false)
    set terrain = getTerrainType(S2I(myArg[0]), S2I(myArg[1]))
    if terrain == &#039;Xwmb&#039; then
        call BJDebugMsg(&quot;marble&quot;)
    elseif terrain == &#039;Xsqd&#039; then
        call BJDebugMsg(&quot;square tile&quot;)
    else
        call BJDebugMsg(&quot;IDK&quot;)
    endif
endfunction

//===========================================================================
function InitTrig_Center takes nothing returns nothing
    set gg_trg_Center = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Center, function Trig_Center_Actions )
    call TriggerRegisterPlayerChatEvent(gg_trg_Center, Player(0), &quot;&quot;, false)
    
    set ttID[0] = &#039;Xwmb&#039;
    set ttBridge[0] = true
    set ttName[0] = &quot;marble&quot;
    
    set ttID[1] = &#039;Xsqd&#039;
    set ttBridge[1] = false
endfunction</i></i></i>
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top