2017-10-13 22:23:07 +02:00
|
|
|
/*****************************************************************************
|
2020-07-21 15:04:34 +02:00
|
|
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
2017-10-13 22:23:07 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
2017-10-13 22:23:07 +02:00
|
|
|
*
|
2018-06-15 14:07:34 +02:00
|
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
2017-10-13 22:23:07 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "../common.h"
|
|
|
|
|
2019-01-02 12:59:37 +01:00
|
|
|
#include <algorithm>
|
|
|
|
|
2020-05-11 14:32:56 +02:00
|
|
|
constexpr const int16_t LOCATION_NULL = -32768;
|
2017-10-13 22:23:07 +02:00
|
|
|
|
2019-12-30 16:03:51 +01:00
|
|
|
constexpr const int32_t COORDS_XY_STEP = 32;
|
2020-05-13 11:51:38 +02:00
|
|
|
constexpr const int32_t COORDS_XY_HALF_TILE = (COORDS_XY_STEP / 2);
|
2019-12-30 16:03:51 +01:00
|
|
|
constexpr const int32_t COORDS_Z_STEP = 8;
|
2020-03-07 21:07:18 +01:00
|
|
|
constexpr const int32_t COORDS_Z_PER_TINY_Z = 16;
|
2019-12-30 16:03:51 +01:00
|
|
|
|
2019-12-30 12:29:08 +01:00
|
|
|
constexpr const auto NumOrthogonalDirections = 4;
|
|
|
|
|
2019-12-22 14:03:32 +01:00
|
|
|
constexpr int32_t COORDS_NULL = 0xFFFF8000;
|
2018-03-07 19:10:50 +01:00
|
|
|
|
2019-08-18 09:02:46 +02:00
|
|
|
struct ScreenCoordsXY
|
|
|
|
{
|
|
|
|
int32_t x = 0;
|
|
|
|
int32_t y = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr ScreenCoordsXY() = default;
|
2019-08-18 09:02:46 +02:00
|
|
|
constexpr ScreenCoordsXY(int32_t _x, int32_t _y)
|
|
|
|
: x(_x)
|
|
|
|
, y(_y)
|
|
|
|
{
|
|
|
|
}
|
2019-10-20 19:10:30 +02:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const ScreenCoordsXY operator-(const ScreenCoordsXY& rhs) const
|
2019-10-20 19:10:30 +02:00
|
|
|
{
|
2019-10-21 04:44:26 +02:00
|
|
|
return { x - rhs.x, y - rhs.y };
|
2019-10-20 19:10:30 +02:00
|
|
|
}
|
2020-03-01 13:56:14 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr ScreenCoordsXY& operator+=(const ScreenCoordsXY& rhs)
|
2020-03-01 13:56:14 +01:00
|
|
|
{
|
|
|
|
x += rhs.x;
|
|
|
|
y += rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr ScreenCoordsXY& operator-=(const ScreenCoordsXY& rhs)
|
2020-03-01 13:56:14 +01:00
|
|
|
{
|
|
|
|
x -= rhs.x;
|
|
|
|
y -= rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const ScreenCoordsXY operator+(const ScreenCoordsXY& rhs) const
|
2020-03-01 13:56:14 +01:00
|
|
|
{
|
|
|
|
return { x + rhs.x, y + rhs.y };
|
|
|
|
}
|
2020-03-01 14:34:58 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const ScreenCoordsXY& other) const
|
2020-03-01 14:34:58 +01:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator!=(const ScreenCoordsXY& other) const
|
2020-03-01 14:34:58 +01:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
2019-08-18 09:02:46 +02:00
|
|
|
};
|
|
|
|
|
2020-05-02 21:11:07 +02:00
|
|
|
struct ScreenSize
|
|
|
|
{
|
|
|
|
int32_t width{};
|
|
|
|
int32_t height{};
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr ScreenSize() = default;
|
2020-05-02 21:11:07 +02:00
|
|
|
constexpr ScreenSize(int32_t _width, int32_t _height)
|
|
|
|
: width(_width)
|
|
|
|
, height(_height)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const ScreenSize& other) const
|
2020-05-02 21:11:07 +02:00
|
|
|
{
|
|
|
|
return width == other.width && height == other.height;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator!=(const ScreenSize& other) const
|
2020-05-02 21:11:07 +02:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-04-26 17:30:37 +02:00
|
|
|
/**
|
|
|
|
* Tile coordinates use 1 x/y increment per tile and 1 z increment per step.
|
|
|
|
* Regular ('big', 'sprite') coordinates use 32 x/y increments per tile and 8 z increments per step.
|
|
|
|
*/
|
2018-03-10 21:35:24 +01:00
|
|
|
struct CoordsXY
|
2018-02-14 11:31:49 +01:00
|
|
|
{
|
2019-01-02 12:59:37 +01:00
|
|
|
int32_t x = 0;
|
|
|
|
int32_t y = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXY() = default;
|
2019-01-02 12:59:37 +01:00
|
|
|
constexpr CoordsXY(int32_t _x, int32_t _y)
|
|
|
|
: x(_x)
|
|
|
|
, y(_y)
|
|
|
|
{
|
|
|
|
}
|
2019-05-25 15:53:11 +02:00
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr CoordsXY& operator+=(const CoordsXY& rhs)
|
2019-05-25 15:53:11 +02:00
|
|
|
{
|
|
|
|
x += rhs.x;
|
|
|
|
y += rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr CoordsXY& operator-=(const CoordsXY& rhs)
|
2019-05-25 15:53:11 +02:00
|
|
|
{
|
|
|
|
x -= rhs.x;
|
|
|
|
y -= rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
constexpr CoordsXY& operator*=(const int32_t rhs)
|
|
|
|
{
|
|
|
|
x *= rhs;
|
|
|
|
y *= rhs;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr CoordsXY& operator/=(const int32_t rhs)
|
|
|
|
{
|
|
|
|
x /= rhs;
|
|
|
|
y /= rhs;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr bool operator>=(const CoordsXY& rhs) const
|
2020-02-18 22:42:38 +01:00
|
|
|
{
|
|
|
|
return x >= rhs.x && y >= rhs.y;
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr bool operator<=(const CoordsXY& rhs) const
|
2020-02-18 22:42:38 +01:00
|
|
|
{
|
|
|
|
return x <= rhs.x && y <= rhs.y;
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr const CoordsXY operator+(const CoordsXY& rhs) const
|
2019-08-10 09:17:47 +02:00
|
|
|
{
|
2019-10-21 07:23:01 +02:00
|
|
|
return { x + rhs.x, y + rhs.y };
|
2019-08-10 09:17:47 +02:00
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr const CoordsXY operator-(const CoordsXY& rhs) const
|
2019-08-10 09:17:47 +02:00
|
|
|
{
|
2019-10-21 07:23:01 +02:00
|
|
|
return { x - rhs.x, y - rhs.y };
|
2019-08-10 09:17:47 +02:00
|
|
|
}
|
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
constexpr const CoordsXY operator*(const int32_t rhs) const
|
|
|
|
{
|
|
|
|
return { x * rhs, y * rhs };
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr const CoordsXY operator/(const int32_t rhs) const
|
|
|
|
{
|
|
|
|
return { x / rhs, y / rhs };
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr CoordsXY Rotate(int32_t direction) const
|
2019-05-25 15:53:11 +02:00
|
|
|
{
|
|
|
|
CoordsXY rotatedCoords;
|
|
|
|
switch (direction & 3)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case 0:
|
|
|
|
rotatedCoords.x = x;
|
|
|
|
rotatedCoords.y = y;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rotatedCoords.x = y;
|
|
|
|
rotatedCoords.y = -x;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rotatedCoords.x = -x;
|
|
|
|
rotatedCoords.y = -y;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rotatedCoords.x = -y;
|
|
|
|
rotatedCoords.y = x;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rotatedCoords;
|
|
|
|
}
|
2019-11-30 16:47:23 +01:00
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr bool operator==(const CoordsXY& other) const
|
2020-02-11 23:01:14 +01:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y;
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr bool operator!=(const CoordsXY& other) const
|
2020-02-11 23:01:14 +01:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr CoordsXY ToTileCentre() const
|
2019-11-30 16:47:23 +01:00
|
|
|
{
|
2020-05-13 11:51:38 +02:00
|
|
|
return ToTileStart() + CoordsXY{ COORDS_XY_HALF_TILE, COORDS_XY_HALF_TILE };
|
2019-11-30 16:47:23 +01:00
|
|
|
}
|
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr CoordsXY ToTileStart() const
|
2019-11-30 16:47:23 +01:00
|
|
|
{
|
2020-03-07 21:07:18 +01:00
|
|
|
return { floor2(x, COORDS_XY_STEP), floor2(y, COORDS_XY_STEP) };
|
2019-11-30 16:47:23 +01:00
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr bool isNull() const
|
2019-12-20 12:29:42 +01:00
|
|
|
{
|
|
|
|
return x == COORDS_NULL;
|
|
|
|
};
|
2019-12-27 13:33:08 +01:00
|
|
|
|
2020-10-23 08:54:32 +02:00
|
|
|
constexpr void setNull()
|
2019-12-27 13:33:08 +01:00
|
|
|
{
|
|
|
|
x = COORDS_NULL;
|
2020-04-07 20:43:20 +02:00
|
|
|
y = 0;
|
2019-12-27 13:33:08 +01:00
|
|
|
}
|
2018-02-14 11:31:49 +01:00
|
|
|
};
|
|
|
|
|
2020-07-10 18:40:23 +02:00
|
|
|
struct CoordsXYZ : public CoordsXY
|
|
|
|
{
|
|
|
|
int32_t z = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZ() = default;
|
2020-07-10 18:40:23 +02:00
|
|
|
constexpr CoordsXYZ(int32_t _x, int32_t _y, int32_t _z)
|
|
|
|
: CoordsXY(_x, _y)
|
|
|
|
, z(_z)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr CoordsXYZ(const CoordsXY& c, int32_t _z)
|
|
|
|
: CoordsXY(c)
|
|
|
|
, z(_z)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZ operator+(const CoordsXYZ& rhs) const
|
2020-07-10 18:40:23 +02:00
|
|
|
{
|
|
|
|
return { x + rhs.x, y + rhs.y, z + rhs.z };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZ operator-(const CoordsXYZ& rhs) const
|
2020-07-10 18:40:23 +02:00
|
|
|
{
|
|
|
|
return { x - rhs.x, y - rhs.y, z - rhs.z };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const CoordsXYZ& other) const
|
2020-07-10 18:40:23 +02:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y && z == other.z;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZ ToTileStart() const
|
2020-07-10 18:40:23 +02:00
|
|
|
{
|
|
|
|
return { floor2(x, COORDS_XY_STEP), floor2(y, COORDS_XY_STEP), z };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZ ToTileCentre() const
|
2020-07-10 18:40:23 +02:00
|
|
|
{
|
|
|
|
return ToTileStart() + CoordsXYZ{ COORDS_XY_HALF_TILE, COORDS_XY_HALF_TILE, 0 };
|
|
|
|
}
|
2021-07-27 20:34:53 +02:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr void setNull()
|
2021-07-27 20:34:53 +02:00
|
|
|
{
|
|
|
|
CoordsXY::setNull();
|
|
|
|
z = 0;
|
|
|
|
}
|
2020-07-10 18:40:23 +02:00
|
|
|
};
|
|
|
|
|
2019-12-28 09:44:33 +01:00
|
|
|
struct CoordsXYRangedZ : public CoordsXY
|
|
|
|
{
|
|
|
|
int32_t baseZ = 0;
|
|
|
|
int32_t clearanceZ = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYRangedZ() = default;
|
2019-12-28 09:44:33 +01:00
|
|
|
constexpr CoordsXYRangedZ(int32_t _x, int32_t _y, int32_t _baseZ, int32_t _clearanceZ)
|
|
|
|
: CoordsXY(_x, _y)
|
|
|
|
, baseZ(_baseZ)
|
|
|
|
, clearanceZ(_clearanceZ)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-13 10:25:42 +01:00
|
|
|
constexpr CoordsXYRangedZ(const CoordsXY& _c, int32_t _baseZ, int32_t _clearanceZ)
|
2019-12-28 09:44:33 +01:00
|
|
|
: CoordsXY(_c)
|
|
|
|
, baseZ(_baseZ)
|
|
|
|
, clearanceZ(_clearanceZ)
|
|
|
|
{
|
|
|
|
}
|
2020-07-10 18:40:23 +02:00
|
|
|
|
|
|
|
constexpr CoordsXYRangedZ(const CoordsXYZ& _c, int32_t _clearanceZ)
|
|
|
|
: CoordsXY(_c)
|
|
|
|
, baseZ(_c.z)
|
|
|
|
, clearanceZ(_clearanceZ)
|
|
|
|
{
|
|
|
|
}
|
2019-12-28 09:44:33 +01:00
|
|
|
};
|
|
|
|
|
2018-03-10 21:35:24 +01:00
|
|
|
struct TileCoordsXY
|
2018-02-14 11:31:49 +01:00
|
|
|
{
|
2019-12-20 12:29:42 +01:00
|
|
|
int32_t x = 0;
|
|
|
|
int32_t y = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXY() = default;
|
2019-11-01 02:07:14 +01:00
|
|
|
constexpr TileCoordsXY(int32_t x_, int32_t y_)
|
2018-06-22 23:17:03 +02:00
|
|
|
: x(x_)
|
|
|
|
, y(y_)
|
|
|
|
{
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr explicit TileCoordsXY(const CoordsXY& c)
|
2020-03-07 21:07:18 +01:00
|
|
|
: x(c.x / COORDS_XY_STEP)
|
|
|
|
, y(c.y / COORDS_XY_STEP)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const TileCoordsXY operator+(const TileCoordsXY& rhs) const
|
2019-12-23 08:35:41 +01:00
|
|
|
{
|
|
|
|
return { x + rhs.x, y + rhs.y };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXY& operator+=(const TileCoordsXY& rhs)
|
2018-04-18 20:34:34 +02:00
|
|
|
{
|
|
|
|
x += rhs.x;
|
|
|
|
y += rhs.y;
|
2018-04-18 21:10:15 +02:00
|
|
|
return *this;
|
2018-04-18 20:34:34 +02:00
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXY& operator-=(const TileCoordsXY& rhs)
|
2019-05-25 15:53:11 +02:00
|
|
|
{
|
|
|
|
x -= rhs.x;
|
|
|
|
y -= rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXY ToCoordsXY() const
|
2019-12-18 04:25:12 +01:00
|
|
|
{
|
2020-05-11 20:28:40 +02:00
|
|
|
if (isNull())
|
|
|
|
{
|
|
|
|
CoordsXY ret{};
|
|
|
|
ret.setNull();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-03-07 21:07:18 +01:00
|
|
|
return { x * COORDS_XY_STEP, y * COORDS_XY_STEP };
|
2019-12-18 04:25:12 +01:00
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXY Rotate(int32_t direction) const
|
2019-05-25 15:53:11 +02:00
|
|
|
{
|
|
|
|
TileCoordsXY rotatedCoords;
|
|
|
|
switch (direction & 3)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case 0:
|
|
|
|
rotatedCoords.x = x;
|
|
|
|
rotatedCoords.y = y;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rotatedCoords.x = y;
|
|
|
|
rotatedCoords.y = -x;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rotatedCoords.x = -x;
|
|
|
|
rotatedCoords.y = -y;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rotatedCoords.x = -y;
|
|
|
|
rotatedCoords.y = x;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rotatedCoords;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const TileCoordsXY& other) const
|
2019-12-21 13:12:10 +01:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator!=(const TileCoordsXY& other) const
|
2019-12-21 13:12:10 +01:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool isNull() const
|
2019-12-20 12:29:42 +01:00
|
|
|
{
|
|
|
|
return x == COORDS_NULL;
|
|
|
|
};
|
2019-12-21 12:48:13 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr void setNull()
|
2019-12-21 12:48:13 +01:00
|
|
|
{
|
|
|
|
x = COORDS_NULL;
|
2020-04-07 20:43:20 +02:00
|
|
|
y = 0;
|
2019-12-21 12:48:13 +01:00
|
|
|
}
|
2018-02-14 11:31:49 +01:00
|
|
|
};
|
|
|
|
|
2019-12-20 12:29:42 +01:00
|
|
|
struct TileCoordsXYZ : public TileCoordsXY
|
2018-02-14 11:31:49 +01:00
|
|
|
{
|
2019-12-20 12:29:42 +01:00
|
|
|
int32_t z = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZ() = default;
|
2019-12-20 12:29:42 +01:00
|
|
|
constexpr TileCoordsXYZ(int32_t x_, int32_t y_, int32_t z_)
|
|
|
|
: TileCoordsXY(x_, y_)
|
2018-06-22 23:17:03 +02:00
|
|
|
, z(z_)
|
|
|
|
{
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZ(const TileCoordsXY& c, int32_t z_)
|
2019-12-23 13:08:46 +01:00
|
|
|
: TileCoordsXY(c.x, c.y)
|
|
|
|
, z(z_)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZ(const CoordsXY& c, int32_t z_)
|
2019-12-20 12:29:42 +01:00
|
|
|
: TileCoordsXY(c)
|
2018-06-22 23:17:03 +02:00
|
|
|
, z(z_)
|
|
|
|
{
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr explicit TileCoordsXYZ(const CoordsXYZ& c)
|
2019-12-20 12:29:42 +01:00
|
|
|
: TileCoordsXY(c)
|
2020-03-13 12:03:43 +01:00
|
|
|
, z(c.z / COORDS_Z_STEP)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZ& operator+=(const TileCoordsXY& rhs)
|
2018-04-18 20:56:09 +02:00
|
|
|
{
|
|
|
|
x += rhs.x;
|
|
|
|
y += rhs.y;
|
2018-04-18 21:10:15 +02:00
|
|
|
return *this;
|
2018-04-18 20:56:09 +02:00
|
|
|
}
|
2018-12-30 00:47:21 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZ& operator-=(const TileCoordsXY& rhs)
|
2019-08-12 19:34:33 +02:00
|
|
|
{
|
|
|
|
x -= rhs.x;
|
|
|
|
y -= rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const TileCoordsXYZ& other) const
|
2018-12-30 00:47:21 +01:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y && z == other.z;
|
|
|
|
}
|
2019-12-20 12:29:42 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator!=(const TileCoordsXYZ& other) const
|
2018-12-30 00:47:21 +01:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZ ToCoordsXYZ() const
|
2019-12-06 01:43:20 +01:00
|
|
|
{
|
2020-05-11 20:28:40 +02:00
|
|
|
if (isNull())
|
|
|
|
{
|
|
|
|
CoordsXYZ ret{};
|
|
|
|
ret.setNull();
|
|
|
|
return ret;
|
|
|
|
}
|
2020-03-07 21:07:18 +01:00
|
|
|
return { x * COORDS_XY_STEP, y * COORDS_XY_STEP, z * COORDS_Z_STEP };
|
2019-12-06 01:43:20 +01:00
|
|
|
}
|
2021-07-27 20:34:53 +02:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr void setNull()
|
2021-07-27 20:34:53 +02:00
|
|
|
{
|
|
|
|
TileCoordsXY::setNull();
|
|
|
|
z = 0;
|
|
|
|
}
|
2018-02-14 11:31:49 +01:00
|
|
|
};
|
|
|
|
|
2019-01-05 15:28:24 +01:00
|
|
|
/**
|
|
|
|
* Cardinal directions are represented by the Direction type. It has four
|
|
|
|
* possible values:
|
|
|
|
* 0 is X-decreasing
|
|
|
|
* 1 is Y-increasing
|
|
|
|
* 2 is X-increasing
|
|
|
|
* 3 is Y-decreasing
|
|
|
|
* Direction is not used to model up/down, or diagonal directions.
|
|
|
|
*/
|
2020-02-17 11:43:47 +01:00
|
|
|
using Direction = uint8_t;
|
2019-01-05 14:58:32 +01:00
|
|
|
|
2019-08-26 11:57:44 +02:00
|
|
|
const Direction INVALID_DIRECTION = 0xFF;
|
|
|
|
|
2019-08-26 19:50:37 +02:00
|
|
|
/**
|
2019-09-01 20:10:27 +02:00
|
|
|
* Array of all valid cardinal directions, to make it easy to write range-based for loops like:
|
|
|
|
* for (Direction d : ALL_DIRECTIONS)
|
2019-08-26 19:50:37 +02:00
|
|
|
*/
|
|
|
|
constexpr Direction ALL_DIRECTIONS[] = { 0, 1, 2, 3 };
|
|
|
|
|
2019-01-05 15:28:24 +01:00
|
|
|
/**
|
|
|
|
* Given a direction, return the direction that points the other way,
|
|
|
|
* on the same axis.
|
|
|
|
*/
|
2019-02-09 22:56:50 +01:00
|
|
|
[[maybe_unused]] static constexpr Direction direction_reverse(Direction dir)
|
2019-01-05 15:28:24 +01:00
|
|
|
{
|
|
|
|
return dir ^ 2;
|
|
|
|
}
|
|
|
|
|
2019-03-02 21:23:42 +01:00
|
|
|
[[maybe_unused]] static constexpr bool direction_valid(Direction dir)
|
2019-02-08 11:06:26 +01:00
|
|
|
{
|
2019-12-30 12:29:08 +01:00
|
|
|
return dir < NumOrthogonalDirections;
|
2019-02-08 11:06:26 +01:00
|
|
|
}
|
|
|
|
|
2019-08-26 19:50:50 +02:00
|
|
|
/**
|
2019-09-01 20:10:27 +02:00
|
|
|
* Given a direction, return the next cardinal direction, wrapping around if necessary.
|
|
|
|
* (TODO: Figure out if this is CW or CCW)
|
2019-08-26 19:50:50 +02:00
|
|
|
*/
|
|
|
|
[[maybe_unused]] static constexpr Direction direction_next(Direction dir)
|
|
|
|
{
|
|
|
|
return (dir + 1) & 0x03;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-09-01 20:10:27 +02:00
|
|
|
* Given a direction, return the previous cardinal direction, wrapping around if necessary.
|
|
|
|
* (TODO: Figure out if this is CW or CCW)
|
2019-08-26 19:50:50 +02:00
|
|
|
*/
|
|
|
|
[[maybe_unused]] static constexpr Direction direction_prev(Direction dir)
|
|
|
|
{
|
|
|
|
return (dir - 1) & 0x03;
|
|
|
|
}
|
|
|
|
|
2020-09-28 02:07:09 +02:00
|
|
|
/**
|
|
|
|
* Given two positions, return the cardinal direction which is closest to the direction from 'from' to 'to'.
|
|
|
|
*/
|
|
|
|
[[maybe_unused]] static constexpr Direction DirectionFromTo(const CoordsXY& from, const CoordsXY& to)
|
|
|
|
{
|
|
|
|
int16_t x_diff = to.x - from.x;
|
|
|
|
int16_t y_diff = to.y - from.y;
|
|
|
|
|
|
|
|
int16_t abs_x = x_diff < 0 ? -x_diff : x_diff;
|
|
|
|
int16_t abs_y = y_diff < 0 ? -y_diff : y_diff;
|
|
|
|
|
|
|
|
if (abs_x <= abs_y)
|
|
|
|
{
|
|
|
|
return y_diff < 0 ? 3 : 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return x_diff < 0 ? 0 : 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-16 19:32:44 +02:00
|
|
|
struct CoordsXYZD : public CoordsXYZ
|
2018-02-14 11:31:49 +01:00
|
|
|
{
|
2019-08-16 19:32:44 +02:00
|
|
|
Direction direction = 0;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZD() = default;
|
2019-08-16 19:32:44 +02:00
|
|
|
constexpr CoordsXYZD(int32_t _x, int32_t _y, int32_t _z, Direction _d)
|
|
|
|
: CoordsXYZ(_x, _y, _z)
|
|
|
|
, direction(_d)
|
|
|
|
{
|
|
|
|
}
|
2018-03-07 19:10:50 +01:00
|
|
|
|
2020-02-13 10:25:42 +01:00
|
|
|
constexpr CoordsXYZD(const CoordsXY& _c, int32_t _z, Direction _d)
|
2019-11-30 16:47:23 +01:00
|
|
|
: CoordsXYZ(_c, _z)
|
|
|
|
, direction(_d)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-02-13 10:25:42 +01:00
|
|
|
constexpr CoordsXYZD(const CoordsXYZ& _c, Direction _d)
|
2019-12-11 21:33:58 +01:00
|
|
|
: CoordsXYZ(_c)
|
|
|
|
, direction(_d)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator==(const CoordsXYZD& other) const
|
2019-11-13 22:46:02 +01:00
|
|
|
{
|
|
|
|
return x == other.x && y == other.y && z == other.z && direction == other.direction;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool operator!=(const CoordsXYZD& other) const
|
2019-11-13 22:46:02 +01:00
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
2020-03-07 21:07:18 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZD& operator+=(const CoordsXY& rhs)
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
|
|
|
x += rhs.x;
|
|
|
|
y += rhs.y;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZD operator+(const CoordsXY& rhs) const
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
|
|
|
return { x + rhs.x, y + rhs.y, z, direction };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZD operator+(const CoordsXYZ& rhs) const
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
|
|
|
return { x + rhs.x, y + rhs.y, z + rhs.z, direction };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZD operator-(const CoordsXY& rhs) const
|
2020-08-09 08:25:44 +02:00
|
|
|
{
|
|
|
|
return { x - rhs.x, y - rhs.y, z, direction };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr const CoordsXYZD operator-(const CoordsXYZ& rhs) const
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
|
|
|
return { x - rhs.x, y - rhs.y, z - rhs.z, direction };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZD ToTileStart() const
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
|
|
|
return { floor2(x, COORDS_XY_STEP), floor2(y, COORDS_XY_STEP), z, direction };
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZD ToTileCentre() const
|
2020-03-07 21:07:18 +01:00
|
|
|
{
|
2020-05-13 11:51:38 +02:00
|
|
|
return ToTileStart() + CoordsXYZD{ COORDS_XY_HALF_TILE, COORDS_XY_HALF_TILE, 0, 0 };
|
2020-03-07 21:07:18 +01:00
|
|
|
}
|
2018-02-14 11:31:49 +01:00
|
|
|
};
|
|
|
|
|
2019-12-20 12:29:42 +01:00
|
|
|
struct TileCoordsXYZD : public TileCoordsXYZ
|
2018-02-14 11:31:49 +01:00
|
|
|
{
|
2019-01-05 14:58:32 +01:00
|
|
|
Direction direction;
|
2018-03-07 19:10:50 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD() = default;
|
2019-12-20 12:29:42 +01:00
|
|
|
constexpr TileCoordsXYZD(int32_t x_, int32_t y_, int32_t z_, Direction d_)
|
|
|
|
: TileCoordsXYZ(x_, y_, z_)
|
|
|
|
, direction(d_)
|
2018-06-22 23:17:03 +02:00
|
|
|
{
|
2019-12-20 12:29:42 +01:00
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD(const TileCoordsXYZ& t_, Direction d_)
|
2021-07-27 18:57:45 +02:00
|
|
|
: TileCoordsXYZ(t_)
|
|
|
|
, direction(d_)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD(const TileCoordsXY& t_, int32_t z_, Direction d_)
|
2020-01-04 12:33:19 +01:00
|
|
|
: TileCoordsXYZ(t_, z_)
|
|
|
|
, direction(d_)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD(const CoordsXY& c_, int32_t z_, Direction d_)
|
2019-12-20 12:29:42 +01:00
|
|
|
: TileCoordsXYZ(c_, z_)
|
|
|
|
, direction(d_)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD(const CoordsXYZ& c_, Direction d_)
|
2019-12-20 12:29:42 +01:00
|
|
|
: TileCoordsXYZ(c_)
|
|
|
|
, direction(d_)
|
|
|
|
{
|
|
|
|
}
|
2019-12-19 04:11:26 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr TileCoordsXYZD(const CoordsXYZD& c_)
|
2019-12-27 15:57:40 +01:00
|
|
|
: TileCoordsXYZ(c_)
|
|
|
|
, direction(c_.direction)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsXYZD ToCoordsXYZD() const
|
2019-12-19 04:11:26 +01:00
|
|
|
{
|
2020-05-11 20:28:40 +02:00
|
|
|
if (isNull())
|
|
|
|
{
|
|
|
|
CoordsXYZD ret{};
|
|
|
|
ret.setNull();
|
|
|
|
return ret;
|
|
|
|
}
|
2020-03-07 21:07:18 +01:00
|
|
|
return { x * COORDS_XY_STEP, y * COORDS_XY_STEP, z * COORDS_Z_STEP, direction };
|
2019-12-19 04:11:26 +01:00
|
|
|
}
|
2021-07-27 20:34:53 +02:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr void setNull()
|
2021-07-27 20:34:53 +02:00
|
|
|
{
|
|
|
|
TileCoordsXYZ::setNull();
|
|
|
|
direction = INVALID_DIRECTION;
|
|
|
|
}
|
2018-02-14 11:31:49 +01:00
|
|
|
};
|
2019-01-02 12:59:37 +01:00
|
|
|
|
|
|
|
/**
|
2020-06-30 14:00:20 +02:00
|
|
|
* Represents a range of the map using regular coordinates.
|
2019-01-02 12:59:37 +01:00
|
|
|
*/
|
2020-06-30 14:00:20 +02:00
|
|
|
template<class T> struct CoordsRange
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
T Point1{ 0, 0 };
|
|
|
|
T Point2{ 0, 0 };
|
2019-01-02 12:59:37 +01:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetX1() const
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return Point1.x;
|
2019-01-02 12:59:37 +01:00
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetY1() const
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return Point1.y;
|
2019-01-02 12:59:37 +01:00
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetX2() const
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return Point2.x;
|
2019-01-02 12:59:37 +01:00
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetY2() const
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return Point2.y;
|
2019-01-02 12:59:37 +01:00
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsRange() = default;
|
|
|
|
constexpr CoordsRange(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
|
2020-07-06 14:42:11 +02:00
|
|
|
: CoordsRange({ x1, y1 }, { x2, y2 })
|
2020-06-30 14:12:14 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr CoordsRange(const T& pointOne, const T& pointTwo)
|
2020-07-06 14:42:11 +02:00
|
|
|
: Point1(pointOne)
|
|
|
|
, Point2(pointTwo)
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
|
|
|
}
|
2020-06-30 14:00:20 +02:00
|
|
|
};
|
2019-01-02 12:59:37 +01:00
|
|
|
|
2020-06-30 14:44:28 +02:00
|
|
|
template<class T> struct RectRange : public CoordsRange<T>
|
2020-06-30 14:00:20 +02:00
|
|
|
{
|
2020-06-30 14:44:28 +02:00
|
|
|
using CoordsRange<T>::CoordsRange;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetLeft() const
|
2020-07-06 14:42:11 +02:00
|
|
|
{
|
|
|
|
return CoordsRange<T>::GetX1();
|
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetTop() const
|
2020-07-06 14:42:11 +02:00
|
|
|
{
|
|
|
|
return CoordsRange<T>::GetY1();
|
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetRight() const
|
2020-07-06 14:42:11 +02:00
|
|
|
{
|
|
|
|
return CoordsRange<T>::GetX2();
|
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetBottom() const
|
2020-07-06 14:42:11 +02:00
|
|
|
{
|
|
|
|
return CoordsRange<T>::GetY2();
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr RectRange(int32_t left, int32_t top, int32_t right, int32_t bottom)
|
2020-06-30 14:44:28 +02:00
|
|
|
: RectRange({ left, top }, { right, bottom })
|
2020-06-30 14:12:14 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr RectRange(const T& leftTop, const T& rightBottom)
|
2020-06-30 14:44:28 +02:00
|
|
|
: CoordsRange<T>(leftTop, rightBottom)
|
2020-06-30 14:00:20 +02:00
|
|
|
{
|
|
|
|
}
|
2020-06-30 14:44:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a rectangular range of the map using regular coordinates (32 per tile).
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct MapRange : public RectRange<CoordsXY>
|
|
|
|
{
|
|
|
|
using RectRange::RectRange;
|
2020-06-30 14:26:42 +02:00
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr MapRange Normalise() const
|
2019-01-02 12:59:37 +01:00
|
|
|
{
|
|
|
|
auto result = MapRange(
|
|
|
|
std::min(GetLeft(), GetRight()), std::min(GetTop(), GetBottom()), std::max(GetLeft(), GetRight()),
|
|
|
|
std::max(GetTop(), GetBottom()));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
2020-06-30 14:26:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a line on the screen
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct ScreenLine : public CoordsRange<ScreenCoordsXY>
|
|
|
|
{
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr ScreenLine(const ScreenCoordsXY& leftTop, const ScreenCoordsXY& rightBottom)
|
2020-06-30 14:26:42 +02:00
|
|
|
: CoordsRange<ScreenCoordsXY>(leftTop, rightBottom)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
2020-06-30 14:32:22 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a rectangular range on the screen
|
|
|
|
*/
|
|
|
|
|
2020-06-30 14:44:28 +02:00
|
|
|
struct ScreenRect : public RectRange<ScreenCoordsXY>
|
2020-06-30 14:32:22 +02:00
|
|
|
{
|
2020-06-30 14:44:28 +02:00
|
|
|
using RectRange::RectRange;
|
|
|
|
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetWidth() const
|
2020-06-30 14:32:22 +02:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return GetRight() - GetLeft();
|
2020-06-30 14:32:22 +02:00
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr int32_t GetHeight() const
|
2020-06-30 14:32:22 +02:00
|
|
|
{
|
2020-07-06 14:42:11 +02:00
|
|
|
return GetBottom() - GetTop();
|
2020-06-30 14:32:22 +02:00
|
|
|
}
|
2021-08-22 22:39:58 +02:00
|
|
|
constexpr bool Contains(const ScreenCoordsXY& coords) const
|
2020-06-30 14:32:22 +02:00
|
|
|
{
|
|
|
|
return coords.x >= GetLeft() && coords.x <= GetRight() && coords.y >= GetTop() && coords.y <= GetBottom();
|
|
|
|
}
|
|
|
|
};
|