Terrain-Aligned Special Effect

This simple function creates a special effect and orients it such that it is perpendicular to the local terrain orientation. The yaw (the rotation around the z-axis) is a free parameter in the orientation. Use 0 for default.

Lua:
    ---@param effectPath string
    ---@param x number
    ---@param y number
    ---@param yaw? number
    ---@return effect
    function AddSpecialEffectTerrainAligned(effectPath, x, y, yaw)
        yaw = yaw or 0
        local effect = AddSpecialEffect(effectPath, x, y)
        local dzdx = (GetLocZ(x + 1, y) - GetLocZ(x - 1, y))/2
        local dzdy = (GetLocZ(x, y + 1) - GetLocZ(x, y - 1))/2
        local nx, ny = -dzdx, -dzdy
        local totalAngle = math.acos(1/math.sqrt(nx*nx + ny*ny + 1))
        local phiNormal = math.atan(ny, nx)
        local pitch = totalAngle*math.cos(yaw - phiNormal)
        local roll = totalAngle*math.sin(yaw - phiNormal)

        BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)

        return effect
    end

    ---@param whichEffect effect
    function TerrainAlignEffect(whichEffect)
        local x, y = BlzGetLocalSpecialEffectX(whichEffect), BlzGetLocalSpecialEffectY(whichEffect)
        local dzdx = (GetLocZ(x + 1, y) - GetLocZ(x - 1, y))/2
        local dzdy = (GetLocZ(x, y + 1) - GetLocZ(x, y - 1))/2
        local nx, ny = -dzdx, -dzdy
        local totalAngle = math.acos(1/math.sqrt(nx*nx + ny*ny + 1))
        local phiNormal = math.atan(ny, nx)
        local pitch = totalAngle*math.cos(-phiNormal)
        local roll = totalAngle*math.sin(-phiNormal)

        BlzSetSpecialEffectOrientation(whichEffect, 0, pitch, roll)
    end

JASS:
    function AddSpecialEffectTerrainAligned takes string effectPath, real x, real y, real yaw returns effect
        local effect newEffect = AddSpecialEffect(effectPath, x, y)
        local real dzdx = (GetLocZ(x + 1, y) - GetLocZ(x - 1, y))/2
        local real dzdy = (GetLocZ(x, y + 1) - GetLocZ(x, y - 1))/2
        local real nx = -dzdx
        local real ny = -dzdy
        local real totalAngle = Acos(1/SquareRoot(nx*nx + ny*ny + 1))
        local real phiNormal = Atan2(ny, nx)
        local real pitch = totalAngle*Cos(yaw - phiNormal)
        local real roll = totalAngle*Sin(yaw - phiNormal)

        call BlzSetSpecialEffectOrientation(newEffect, yaw, pitch, roll)

        return newEffect
    endfunction

    function TerrainAlignEffect takes effect whichEffect returns nothing
        local real x = BlzGetLocalSpecialEffectX(whichEffect)
        local real y = BlzGetLocalSpecialEffectY(whichEffect)
        local real dzdx = (GetLocZ(x + 1, y) - GetLocZ(x - 1, y))/2
        local real dzdy = (GetLocZ(x, y + 1) - GetLocZ(x, y - 1))/2
        local real nx = -dzdx
        local real ny = -dzdy
        local real totalAngle = Acos(1/SquareRoot(nx*nx + ny*ny + 1))
        local real phiNormal = Atan2(ny, nx)
        local real pitch = totalAngle*Cos(-phiNormal)
        local real roll = totalAngle*Sin(-phiNormal)

        call BlzSetSpecialEffectOrientation(whichEffect, 0, pitch, roll)
    endfunction

You will need the GetLocZ function, if you don't have one already:


Lua:
do
    local moveableLoc
    local function GetLocZPostInit(x,y)
        MoveLocation(moveableLoc, x, y)
        return GetLocationZ(moveableLoc)
    end
    function GetLocZ(x, y)
        moveableLoc = Location(0,0)
        GetLocZ = GetLocZPostInit
        return GetLocZPostInit(x,y)
    end
end

vJASS:
scope GetLocZ
    globals
        private location moveableLoc = Location(0, 0)
    endglobals

    function GetLocZ takes real x, real y returns real
        call MoveLocation(moveableLoc, x, y)
        return GetLocationZ(moveableLoc)
    endfunction
endscope
Contents

TerrainAlignedSpecialEffect (Map)

Reviews
Wrda
Approved for a nice addition for applying additional 3D functionality. It's always a pain in the ass to decide whether something is a snippet or something else. To me, this kind of "resource" serves more in a template where it is devoid of any...
Level 45
Joined
Feb 27, 2007
Messages
5,578
I want to say this should go in the code snippets thread but it would get no visibility there. While not specific/hardcoded like the lumber resource you just rejected, this is literally just a single generalized function; is there even precedent for where this should end up? Speaks to Hive needing a better way to organize resources of this caliber/type, IMO.
 
We combined the Code forum and the Spell section I think because the traffic to these sections is very low already, so resources get almost no visibility, and often you'll end up searching both sections anyway if you're looking for a piece of code. Having everything in one place is preferable in my opinion, as long as you have a good search function.

Regarding the resource I rejected: It's not because that resource was simple/small (for example we have @Tasyen 's GetDisabledIcon and GetPlayerColorTexture), but because what it did was very basic and not really anything that warrants a resource, on top of that also not being executed particularly well.

While this is just a few lines of code, the math behind it is probably beyond the ability of many mapmakers. But I agree that having it as a single resource is not ideal; ideally it would be part of some kind of special effect orientation pack. I still thought it might be useful to someone, I didn't find anything similar, so I decided to upload it.
 
Last edited:
Level 9
Joined
Oct 20, 2010
Messages
228
I see a great hole in the Hive’s current support of simple content. Simple content has value - to me - in that they can showcase ideas I would have never had myself. It sucks that, for example, the way the model section is established does a poor job at showcasing the actual volume of models and edits available to map makers as they are often rejected or forced into a simple edit forum. I agree I wouldn’t want the model section flooded with awful content, but surely there’s a better way. The community of Warcraft is too small to not support simple resources that are without a doubt still useful. A lot of what I’ve learned as a map maker and a coder has come from simple resources show casing useful ideas.

To share my presumptions as a user: when I see a rejected rating I am led to believe that the resource is flawed/broken. I would much rather see simple resources accepted to inform us as users they’re working as intended, as opposed to be rejected because they are not arbitrarily complicated enough. These simple resources should still be categorized to separate them from remarkable systems like ALICE.

Surely theres a better way of managing submissions that would encourage people to submit more works, but that’s just my four cents as someone who’s never been compelled to upload my own works.

On topic, though, I wouldn’t even consider this a simple resources. The math is something else and what it accomplishes is fundamentally useful. Cheers!
 
Last edited:

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
Approved for a nice addition for applying additional 3D functionality.

It's always a pain in the ass to decide whether something is a snippet or something else. To me, this kind of "resource" serves more in a template where it is devoid of any processes of initialization, and perhaps requirements.
Imagine a resource which is a template called "Advanced Math Functions" where there's packs of functions/scripts one could just get easily without hassle.
 
This might look like a double post but this one is something to highlight for this resources. I found that auras (ones from my test are Brilliance and Devotion, but I think it applies to other default auras) does not adjust properly with the alignment from this resources. However, others models I used during testing functions properly (Magic Sentry, Cloud <this one is hard to notice for me>, and Starfall).

I think there's something tied with the models, or maybe I am doing something wrong.

Regardless, I am implementing this as part of v2.2 Timed Special Effect update. It will take a while since I have a ton of things to iron out.
 
Oh shoot, it looks like some of these aura models use this "always face the camera" option. Devotion Aura doesn't work, Trueshot Aura does.

The only fix would be to reimport the model with that flag disabled and overwrite the original.

This might look like a double post but this one is something to highlight for this resources.
If you had only edited your first post, I wouldn't have seen it.
 
Oh shoot, it looks like some of these aura models use this "always face the camera" option. Devotion Aura doesn't work, Trueshot Aura does.

The only fix would be to reimport the model with that flag disabled and overwrite the original.


If you had only edited your first post, I wouldn't have seen it.
I will highlight this limitation on TSELL, thank you for the insight. Is it possible to request a script that will adjust the position of an existing effect without creating new ones?
 
Testing the new script and it works wonderful!

I'll drop this information from my newest tests so for people utilizing this resources can be aware of something critical when trying to make a 'follower'/'missile' effect:
When working with effects that plays with Z orientation, remember to update the Z value to the current position Z otherwise the effect might 'sink' when started at lower terrain or 'fly' when started at high terrain.
 
Top