1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
use std::{
    collections::HashMap,
    fmt::{Display, Formatter},
};

use serde::{Deserialize, Serialize};
use serde_tuple::{Deserialize_tuple, Serialize_tuple};

use crate::{
    maps::{
        continents::{ContinentId, Dimensions},
        MapId,
    },
    Endpoint, EndpointWithId,
};

pub type FloorId = i16;
pub type RegionId = u8;
pub type MasteryPointId = u16;
pub type PointOfInterestId = u16;
pub type GodShrineId = u8;
pub type TaskId = u16;
pub type SectorId = u16;

#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ContinentFloorId {
    pub continent: ContinentId,
    pub floor: FloorId,
}

impl From<(ContinentId, FloorId)> for ContinentFloorId {
    fn from(value: (ContinentId, FloorId)) -> Self {
        Self {
            continent: value.0,
            floor: value.1,
        }
    }
}

impl Display for ContinentFloorId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}/floors/{}", self.continent, self.floor)
    }
}

#[derive(Clone, Debug, PartialOrd, PartialEq, Serialize_tuple, Deserialize_tuple)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Coordinates {
    pub x: f32,
    pub y: f32,
}

#[derive(Clone, Debug, PartialOrd, PartialEq, Serialize_tuple, Deserialize_tuple)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct ContinentRectangle {
    pub top_left: Coordinates,
    pub bottom_right: Coordinates,
}

#[derive(Clone, Debug, PartialOrd, PartialEq, Serialize_tuple, Deserialize_tuple)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct MapRectangle {
    pub bottom_left: Coordinates,
    pub top_right: Coordinates,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub enum PointOfInterestType {
    Landmark,
    Waypoint,
    Vista,
    Unlock,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct PointOfInterest {
    /// The POI id.
    pub id: PointOfInterestId,
    /// The POI name, if any.
    pub name: Option<String>,
    /// The POI type.
    #[serde(rename = "type")]
    pub _type: PointOfInterestType,
    /// The floor of this POI.
    pub floor: FloorId,
    /// The POI coordinates.
    pub coord: Coordinates,
    /// The POI chat link.
    pub chat_link: String,
    /// For [`Unlock`](PointOfInterestType::Unlock) type, provides the render
    /// service url for the POI's icon.
    pub icon: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct GodShrine {
    /// The shrine id.
    pub id: GodShrineId,
    /// The shrine name when not contested.
    pub name: String,
    /// The shrine name when contested.
    pub name_contested: String,
    /// The shrine coordinates.
    pub coord: Coordinates,
    /// The associated waypoint id.
    pub poi_id: PointOfInterestId,
    /// The render service url for the shrine's icon when not contested.
    pub icon: String,
    /// The render service url for the shrine's icon when contested.
    pub icon_contested: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Task {
    /// The task id.
    pub id: TaskId,
    /// The objective or name of the task.
    pub objective: String,
    /// The level of the task.
    pub level: u8,
    /// The coordinates of the task.
    pub coord: Coordinates,
    /// A list of coordinates marking the boundary of the task.
    pub bounds: Vec<Coordinates>,
    /// The task chat link (provides an invalid link if attempting to display
    /// in-game).
    pub chat_link: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct SkillChallenge {
    /// The hero challenge id, if any.
    ///
    /// It is formed of two numbers separated by a dash. The first number
    /// represents the expansion (0 for Core Tyria, 1 for Heart of Thorns
    /// and 2 for Path of Fire), and therefore could be used to change the
    /// hero challenge map marker icon. If the first number and dash prefix is
    /// removed from the string, the second number is no longer unique among
    /// other hero challenges.
    pub id: Option<String>,
    /// The coordinates of this hero challenge.
    pub coord: Coordinates,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Sector {
    /// The sector id.
    pub id: SectorId,
    /// The name of the sector, if any.
    pub name: Option<String>,
    /// The level of the sector.
    pub level: u8,
    /// The coordinates of the sector (this is usually the center position).
    pub coord: Coordinates,
    /// A list of coordinates marking the boundary of the sector.
    pub bounds: Vec<Coordinates>,
    /// The sector chat link (provides an invalid link if attempting to display
    /// in-game).
    pub chat_link: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Adventure {
    /// The adventure UUID.
    pub id: String,
    /// The name of the adventure.
    pub name: String,
    /// The description of the adventure.
    pub description: String,
    /// The coordinates of the start of the adventure.
    pub coord: Coordinates,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub enum MasteryPointRegion {
    /// Core Tyria, in red.
    Tyria,
    /// Heart of Thorns, in green.
    Maguuma,
    /// Path of Fire, in purple
    Desert,
    /// Living World Season 5, in blue.
    Tundra,
    /// Seems to be used for End of Dragons, in blue.
    Unknown,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct MasteryPoint {
    /// The mastery point id.
    pub id: MasteryPointId,
    /// The type of mastery.
    pub region: MasteryPointRegion,
    /// The coordinates of the start of the adventure.
    pub coord: Coordinates,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Map {
    /// The map id.
    pub id: MapId,
    /// The map name.
    pub name: String,
    /// The minimum level on this map.
    pub min_level: u8,
    /// The maximum level on this map.
    pub max_level: u8,
    /// The default floor of the map.
    pub default_floor: FloorId,
    /// The coordinates of the map label.
    pub label_coord: Option<Coordinates>,
    /// The dimensions of the map.
    pub map_rect: MapRectangle,
    /// The dimensions of the map within the continent coordinate system.
    pub continent_rect: ContinentRectangle,
    /// The list of points of interest (landmarks, waypoints, vistas, etc) of
    /// the map.
    pub points_of_interest: HashMap<PointOfInterestId, PointOfInterest>,
    pub god_shrines: Option<Vec<GodShrine>>,
    /// The list of renown hearts of the map.
    pub tasks: HashMap<TaskId, Task>,
    /// The list of hero challenges of the map.
    pub skill_challenges: Vec<SkillChallenge>,
    /// The list of areas of the map.
    pub sectors: HashMap<SectorId, Sector>,
    /// The list of adventures of the map.
    pub adventures: Vec<Adventure>,
    /// The list of mastery points of the map.
    pub mastery_points: Vec<MasteryPoint>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Region {
    /// The region id.
    pub id: RegionId,
    /// The region name.
    pub name: String,
    ///  The coordinates of the region label.
    pub label_coord: Coordinates,
    /// The dimensions of the region in the continent.
    pub continent_rect: ContinentRectangle,
    /// The list of maps in this region.
    pub maps: HashMap<MapId, Map>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Floor {
    pub id: FloorId,
    /// The dimensions of the texture.
    pub texture_dims: Dimensions,
    /// If present, it represents a rectangle of downloadable textures. Every
    /// tile coordinate outside this rectangle is not available on the tile
    /// server.
    pub clamped_view: Option<ContinentRectangle>,
    /// The list of regions in on this floor.
    pub regions: HashMap<RegionId, Region>,
}

impl EndpointWithId for Floor {
    type IdType = ContinentFloorId;

    fn format_id(id: &Self::IdType) -> String {
        id.to_string()
    }
}

impl Endpoint for Floor {
    const AUTHENTICATED: bool = false;
    const LOCALE: bool = true;
    const URL: &'static str = "v2/continents";
    const VERSION: &'static str = "2023-03-31T00:00:00.000Z";
}