
#guard yln_unit_coordinates

library YLN_UnitCoordinates uses YLN_CoreMath initializer init {

//========================================
// Constants:
//========================================

    #define margin = 64.0;
    #define maxZ   = 4096.0;

//========================================
// Functions:
//========================================

    #define GetTerrainZ = YLN_GetTerrainZ

    #define AddUnitZ    = YLN_AddUnitZ
    #define GetUnitZ    = YLN_GetUnitZ

    #define SetUnitX    = YLN_SetUnitX
    #define SetUnitY    = YLN_SetUnitY
    #define SetUnitZ    = YLN_SetUnitZ

    #define SetUnitXY   = YLN_SetUnitXY
    #define SetUnitXZ   = YLN_SetUnitXZ
    #define SetUnitYZ   = YLN_SetUnitYZ

    #define SetUnitXYZ  = YLN_SetUnitXYZ

//========================================
// Misc:
//========================================

    private float    minX = 0.0;
    private float    maxX = 0.0;
    private float    minY = 0.0;
    private float    maxY = 0.0;
    private location tempLocation;

    private void init() {
        maxX = GetRectMaxX(bj_mapInitialPlayableArea) - margin;
        minX = GetRectMinX(bj_mapInitialPlayableArea) + margin;
        maxY = GetRectMaxY(bj_mapInitialPlayableArea) - margin;
        minY = GetRectMinY(bj_mapInitialPlayableArea) + margin;
        tempLocation = Location(0.0, 0.0);
    }

//========================================
// Implementations:
//========================================

    float YLN_GetTerrainZ(float x, float y) {
        MoveLocation(tempLocation, x, y);
        return GetLocationZ(tempLocation);
    }

    void YLN_AddUnitZ(unit u) {
        UnitAddAbility   (u, 0x41726176);
        UnitRemoveAbility(u, 0x41726176);
    }

    float YLN_GetUnitZ(unit u) {
        return GetTerrainZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u);
    }


    /*  Following methods will return true if can move unit to precise coordinates.
     *  Unit will be moved to given coordinates or will not be moved at all.
     */

    bool YLN_SetUnitX(unit u, float newX) {
        float setX = fclamp(minX, newX, maxX);

        bool canMove = (setX == newX);

        if (canMove) {
            SetUnit##X(u, setX);
        }
        return canMove;
    }

    bool YLN_SetUnitY(unit u, float newY) {
        float setY = fclamp(minY, newY, maxY);

        bool canMove = (setY == newY);

        if (canMove) {
            SetUnit##Y(u, setY);
        }
        return canMove;
    }

    bool YLN_SetUnitZ(unit u, float newZ) {
        float minZ = GetTerrainZ(GetUnitX(u), GetUnitY(u));
        float setZ = fclamp(minZ, newZ, maxZ);

        bool canMove = (setZ == newZ);

        if (canMove) {
            SetUnitFlyHeight(u, setZ - minZ);
        }
        return canMove;
    }

    bool YLN_SetUnitXY(unit u, float newX, float newY) {
        float setX = fclamp(minX, newX, maxX);
        float setY = fclamp(minY, newY, maxY);

        bool canMove = (setX == newX && setY == newY);

        if (canMove) {
            SetUnit##X(u, setX);
            SetUnit##Y(u, setY);
        }
        return canMove;
    }

    bool YLN_SetUnitXZ(unit u, float newX, float newZ) {
        float minZ = GetTerrainZ(GetUnitX(u), GetUnitY(u));
        float setX = fclamp(minX, newX, maxX);
        float setZ = fclamp(minZ, newZ, maxZ);

        bool canMove = (setX == newX && setZ == newZ);

        if (canMove) {
            SetUnit##X(u, setX);
            SetUnitFlyHeight(u, setZ - minZ);
        }
        return canMove;
    }

    bool YLN_SetUnitYZ(unit u, float newY, float newZ) {
        float minZ = GetTerrainZ(GetUnitX(u), GetUnitY(u));
        float setY = fclamp(minY, newY, maxY);
        float setZ = fclamp(minZ, newZ, maxZ);

        bool canMove = (setY == newY && setZ == newZ);

        if (canMove) {
            SetUnit##Y(u, setY);
            SetUnitFlyHeight(u, setZ - minZ);
        }
        return canMove;
    }

    bool YLN_SetUnitXYZ(unit u, float newX, float newY, float newZ) {
        float minZ = GetTerrainZ(GetUnitX(u), GetUnitY(u));
        float setX = fclamp(minX, newX, maxX);
        float setY = fclamp(minY, newY, maxY);
        float setZ = fclamp(minZ, newZ, maxZ);

        bool canMove = (setX == newX && setY == newY && setZ == newZ);

        if (canMove) {
            SetUnit##X(u, setX);
            SetUnit##Y(u, setY);
            SetUnitFlyHeight(u, setZ - minZ);
        }
        return canMove;
    }
}
