1use js_sys::{Array, JsString, Object};
2use num_traits::*;
3use wasm_bindgen::prelude::*;
4
5use crate::{
6 constants::{find::*, look::*, Color, Direction, ErrorCode, StructureType},
7 enums::action_error_codes::room_position::*,
8 local::{Position, RoomCoordinate, RoomName},
9 objects::{CostMatrix, FindPathOptions, Path},
10 pathfinder::RoomCostResult,
11 prelude::*,
12 prototypes::ROOM_POSITION_PROTOTYPE,
13};
14
15#[wasm_bindgen]
16extern "C" {
17 pub type RoomPosition;
24
25 #[wasm_bindgen(constructor)]
26 fn new_internal(x: u8, y: u8, room_name: &JsString) -> RoomPosition;
27
28 #[wasm_bindgen(method, getter = roomName)]
29 fn room_name_internal(this: &RoomPosition) -> JsString;
30
31 #[wasm_bindgen(method, setter = roomName)]
35 pub fn set_room_name(this: &RoomPosition) -> JsString;
36
37 #[wasm_bindgen(method, getter)]
41 pub fn x(this: &RoomPosition) -> u8;
42
43 #[wasm_bindgen(method, setter)]
47 pub fn set_x(this: &RoomPosition) -> u8;
48
49 #[wasm_bindgen(method, getter)]
53 pub fn y(this: &RoomPosition) -> u8;
54
55 #[wasm_bindgen(method, setter)]
59 pub fn set_y(this: &RoomPosition) -> u8;
60
61 #[wasm_bindgen(method, getter = __packedPos)]
64 pub fn packed(this: &RoomPosition) -> u32;
65
66 #[wasm_bindgen(method, setter = __packedPos)]
70 pub fn set_packed(this: &RoomPosition, val: u32);
71
72 #[wasm_bindgen(method, catch, js_name = createConstructionSite)]
73 fn create_construction_site_internal(
74 this: &RoomPosition,
75 ty: StructureType,
76 name: Option<&JsString>,
77 ) -> Result<i8, JsValue>;
78
79 #[wasm_bindgen(method, catch, js_name = createFlag)]
80 fn create_flag_internal(
81 this: &RoomPosition,
82 name: Option<&JsString>,
83 color: Option<Color>,
84 secondary_color: Option<Color>,
85 ) -> Result<JsValue, JsValue>;
86
87 #[wasm_bindgen(method, js_name = findClosestByPath)]
89 fn find_closest_by_path_internal(
90 this: &RoomPosition,
91 goal: Find,
92 options: Option<&Object>,
93 ) -> Option<Object>;
94
95 #[wasm_bindgen(method, js_name = findClosestByRange)]
97 fn find_closest_by_range_internal(
98 this: &RoomPosition,
99 goal: Find,
100 options: Option<&Object>,
101 ) -> Option<Object>;
102
103 #[wasm_bindgen(method, js_name = findInRange)]
105 fn find_in_range_internal(
106 this: &RoomPosition,
107 goal: Find,
108 range: u8,
109 options: Option<&Object>,
110 ) -> Option<Array>;
111
112 #[wasm_bindgen(method, js_name = findPathTo)]
113 fn find_path_to_internal(
114 this: &RoomPosition,
115 target: &JsValue,
116 options: Option<&Object>,
117 ) -> JsValue;
118
119 #[wasm_bindgen(method, js_name = findPathTo)]
120 fn find_path_to_xy_internal(
121 this: &RoomPosition,
122 x: u8,
123 y: u8,
124 options: Option<&Object>,
125 ) -> JsValue;
126
127 #[wasm_bindgen(method, js_name = getDirectionTo)]
131 pub fn get_direction_to(this: &RoomPosition, goal: &JsValue) -> Direction;
132
133 #[wasm_bindgen(method, js_name = getDirectionTo)]
137 pub fn get_direction_to_xy(this: &RoomPosition, x: u8, y: u8) -> Direction;
138
139 #[wasm_bindgen(method, js_name = getRangeTo)]
143 pub fn get_range_to(this: &RoomPosition, goal: &JsValue) -> u32;
144
145 #[wasm_bindgen(method, js_name = getRangeTo)]
149 pub fn get_range_to_xy(this: &RoomPosition, x: u8, y: u8) -> u32;
150
151 #[wasm_bindgen(method, js_name = inRangeTo)]
155 pub fn in_range_to(this: &RoomPosition, goal: &JsValue, range: u8) -> bool;
156
157 #[wasm_bindgen(method, js_name = inRangeTo)]
161 pub fn in_range_to_xy(this: &RoomPosition, x: u8, y: u8, range: u8) -> bool;
162
163 #[wasm_bindgen(method, js_name = isEqualTo)]
168 pub fn is_equal_to(this: &RoomPosition, goal: &JsValue) -> bool;
169
170 #[wasm_bindgen(method, js_name = isEqualTo)]
174 pub fn is_equal_to_xy(this: &RoomPosition, x: u8, y: u8) -> bool;
175
176 #[wasm_bindgen(method, js_name = isNearTo)]
181 pub fn is_near_to(this: &RoomPosition, goal: &JsValue) -> bool;
182
183 #[wasm_bindgen(method, js_name = isNearTo)]
188 pub fn is_near_to_xy(this: &RoomPosition, x: u8, y: u8) -> bool;
189
190 #[wasm_bindgen(method, catch, js_name = look)]
191 fn look_internal(this: &RoomPosition) -> Result<Array, JsValue>;
192
193 #[wasm_bindgen(method, catch, js_name = lookFor)]
194 fn look_for_internal(this: &RoomPosition, ty: Look) -> Result<Option<Array>, JsValue>;
195}
196
197impl RoomPosition {
198 pub fn new(x: u8, y: u8, room_name: RoomName) -> RoomPosition {
203 let room_name = room_name.into();
204
205 Self::new_internal(x, y, &room_name)
206 }
207
208 pub fn room_name(&self) -> RoomName {
213 Self::room_name_internal(self)
214 .try_into()
215 .expect("expected parseable room name")
216 }
217
218 pub fn create_construction_site(
226 &self,
227 ty: StructureType,
228 name: Option<&JsString>,
229 ) -> Result<(), RoomPositionCreateConstructionSiteErrorCode> {
230 match Self::create_construction_site_internal(self, ty, name) {
231 Ok(result) => RoomPositionCreateConstructionSiteErrorCode::result_from_i8(result),
232 Err(_) => {
233 Err(RoomPositionCreateConstructionSiteErrorCode::NotInRange)
235 }
236 }
237 }
238
239 pub fn create_flag(
246 &self,
247 name: Option<&JsString>,
248 color: Option<Color>,
249 secondary_color: Option<Color>,
250 ) -> Result<JsString, RoomPositionCreateFlagErrorCode> {
251 match self.create_flag_internal(name, color, secondary_color) {
252 Ok(result) => {
253 if result.is_string() {
254 Ok(result.unchecked_into())
255 } else {
256 Err(RoomPositionCreateFlagErrorCode::from_f64(
257 result
258 .as_f64()
259 .expect("expected non-string flag return to be a number"),
260 )
261 .expect("expected valid error code"))
262 }
263 }
264 Err(_) => {
265 Err(RoomPositionCreateFlagErrorCode::NotInRange)
267 }
268 }
269 }
270
271 pub fn find_closest_by_path<T>(&self, find: T, options: Option<&Object>) -> Option<T::Item>
279 where
280 T: FindConstant,
281 {
282 self.find_closest_by_path_internal(find.find_code(), options)
283 .map(|reference| T::convert_and_check_item(reference.into()))
284 }
285
286 pub fn find_closest_by_range<T>(&self, find: T) -> Option<T::Item>
295 where
296 T: FindConstant,
297 {
298 self.find_closest_by_range_internal(find.find_code(), None)
299 .map(|reference| T::convert_and_check_item(reference.into()))
300 }
301
302 pub fn find_in_range<T>(&self, find: T, range: u8) -> Vec<T::Item>
311 where
312 T: FindConstant,
313 {
314 self.find_in_range_internal(find.find_code(), range, None)
315 .map(|arr| arr.iter().map(T::convert_and_check_item).collect())
316 .unwrap_or_default()
317 }
318
319 pub fn find_path_to<T, F, R>(&self, target: &T, options: Option<FindPathOptions<F, R>>) -> Path
324 where
325 T: HasPosition,
326 F: FnMut(RoomName, CostMatrix) -> R,
327 R: RoomCostResult,
328 {
329 let target: RoomPosition = target.pos().into();
330
331 if let Some(options) = options {
332 options.into_js_options(|js_options| {
333 serde_wasm_bindgen::from_value(
334 self.find_path_to_internal(&target, Some(js_options.unchecked_ref())),
335 )
336 .expect("invalid path from RoomPosition.findPathTo")
337 })
338 } else {
339 serde_wasm_bindgen::from_value(self.find_path_to_internal(&target, None))
340 .expect("invalid path from RoomPosition.findPathTo")
341 }
342 }
343
344 pub fn find_path_to_xy<F, R>(
349 &self,
350 x: RoomCoordinate,
351 y: RoomCoordinate,
352 options: Option<FindPathOptions<F, R>>,
353 ) -> Path
354 where
355 F: FnMut(RoomName, CostMatrix) -> R,
356 R: RoomCostResult,
357 {
358 if let Some(options) = options {
359 options.into_js_options(|js_options| {
360 serde_wasm_bindgen::from_value(self.find_path_to_xy_internal(
361 x.into(),
362 y.into(),
363 Some(js_options.unchecked_ref()),
364 ))
365 .expect("invalid path from RoomPosition.findPathTo")
366 })
367 } else {
368 serde_wasm_bindgen::from_value(self.find_path_to_xy_internal(x.into(), y.into(), None))
369 .expect("invalid path from RoomPosition.findPathTo")
370 }
371 }
372
373 pub fn look(&self) -> Result<Vec<LookResult>, ErrorCode> {
378 match self.look_internal() {
379 Ok(array) => Ok(array
380 .iter()
381 .map(LookResult::from_jsvalue_unknown_type)
382 .collect()),
383 Err(_) => Err(ErrorCode::NotInRange),
384 }
385 }
386
387 pub fn look_for<T>(&self, _ty: T) -> Result<Vec<T::Item>, ErrorCode>
392 where
393 T: LookConstant,
394 {
395 match self.look_for_internal(T::look_code()) {
396 Ok(array) => Ok(array
397 .map(|arr| arr.iter().map(T::convert_and_check_item).collect())
398 .unwrap_or_else(Vec::new)),
399 Err(_) => Err(ErrorCode::NotInRange),
400 }
401 }
402}
403
404impl Clone for RoomPosition {
405 fn clone(&self) -> Self {
406 let new_pos = RoomPosition::from(JsValue::from(Object::create(&ROOM_POSITION_PROTOTYPE)));
407 new_pos.set_packed(self.packed());
408 new_pos
409 }
410}
411
412impl HasPosition for RoomPosition {
413 fn pos(&self) -> Position {
414 self.into()
415 }
416}
417
418impl From<Position> for RoomPosition {
419 fn from(pos: Position) -> Self {
420 let js_pos = RoomPosition::from(JsValue::from(Object::create(&ROOM_POSITION_PROTOTYPE)));
421 js_pos.set_packed(pos.packed_repr());
422 js_pos
423 }
424}
425
426impl From<&Position> for RoomPosition {
427 fn from(pos: &Position) -> Self {
428 let js_pos = RoomPosition::from(JsValue::from(Object::create(&ROOM_POSITION_PROTOTYPE)));
429 js_pos.set_packed(pos.packed_repr());
430 js_pos
431 }
432}