---@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