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
use js_sys::JsString;
use wasm_bindgen::{prelude::*, JsCast};

use crate::{
    local::{Position, RoomName},
    objects::{RoomObject, RoomPosition, Structure},
    prelude::*,
};

#[wasm_bindgen]
extern "C" {
    /// An object representing a [`StructurePortal`], which allows movement
    /// between remote locations or other shards.
    ///
    /// [Screeps documentation](https://docs.screeps.com/api/#StructurePortal)
    #[wasm_bindgen(extends = RoomObject, extends = Structure)]
    #[derive(Clone, Debug)]
    pub type StructurePortal;

    #[wasm_bindgen(method, getter)]
    fn destination_internal(this: &StructurePortal) -> JsValue;

    /// The number of ticks until the portal will decay, if it's unstable, or 0
    /// if it's stable.
    ///
    /// [Screeps documentation](https://docs.screeps.com/api/#StructurePortal.ticksToDecay)
    #[wasm_bindgen(method, getter = ticksToDecay)]
    pub fn ticks_to_decay(this: &StructurePortal) -> u32;
}

impl StructurePortal {
    pub fn destination(&self) -> PortalDestination {
        let dest = Self::destination_internal(self);
        match dest.dyn_ref::<RoomPosition>() {
            Some(room_pos) => PortalDestination::InterRoom(room_pos.into()),
            None => PortalDestination::InterShard(dest.unchecked_into()),
        }
    }
}

impl CanDecay for StructurePortal {
    fn ticks_to_decay(&self) -> u32 {
        Self::ticks_to_decay(self)
    }
}

pub enum PortalDestination {
    InterRoom(Position),
    InterShard(InterShardPortalDestination),
}

#[wasm_bindgen]
extern "C" {
    /// An object which contains the destination shard and room of an
    /// inter-shard portal.
    ///
    /// [Screeps documentation](https://docs.screeps.com/api/#StructurePortal.destination)
    #[wasm_bindgen]
    pub type InterShardPortalDestination;

    #[wasm_bindgen(method, getter = room)]
    fn room_internal(this: &InterShardPortalDestination) -> JsString;

    #[wasm_bindgen(method, getter)]
    pub fn shard(this: &InterShardPortalDestination) -> String;
}

impl InterShardPortalDestination {
    pub fn room(&self) -> RoomName {
        Self::room_internal(self)
            .try_into()
            .expect("expected parseable room name")
    }
}