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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
//! Screeps object wrappers.
//!
//! # Unsafe traits
//!
//! This module contains a number of unsafe traits. Each is unsafe purely to
//! prevent accidental implementations on things which don't uphold the trait
//! contracts that have not been put into code. There is no unsafe code
//! in this crate which relies on these traits being implemented correctly,
//! only code which will panic if they are not.
//!
//! Even though this crate does not contain any, other crate unsafe could
//! rely on these contracts being upheld as long as JavaScript code does not
//! do anything mischievous, like removing properties from objects or sticking
//! unexpected things into dictionaries which we trust.

use stdweb::{Reference, ReferenceType, Value};
use stdweb_derive::ReferenceType;

use crate::{
    constants::{ResourceType, ReturnCode, StructureType},
    local::Position,
    macros::*,
    traits::{IntoExpectedType, TryFrom, TryInto},
    ConversionError,
};

mod impls;
mod structure;

pub use self::{
    impls::{
        AttackEvent, AttackType, Bodypart, BuildEvent, Event, EventType, ExitEvent, FindOptions,
        HarvestEvent, HealEvent, HealType, LookResult, MoveToOptions, ObjectDestroyedEvent, Path,
        PortalDestination, PositionedLookResult, RepairEvent, Reservation, ReserveControllerEvent,
        Sign, SpawnOptions, Step, UpgradeControllerEvent,
    },
    structure::Structure,
};

reference_wrappers!(
    #[reference(instance_of = "ConstructionSite")]
    ConstructionSite,
    #[reference(instance_of = "Creep")]
    Creep,
    #[reference(instance_of = "Flag")]
    Flag,
    #[reference(instance_of = "Mineral")]
    Mineral,
    #[reference(instance_of = "Nuke")]
    Nuke,
    #[reference(instance_of = "OwnedStructure")]
    OwnedStructure,
    #[reference(instance_of = "Resource")]
    Resource,
    #[reference(instance_of = "Room")]
    Room,
    #[reference(instance_of = "RoomObject")]
    RoomObject,
    #[reference(instance_of = "Room.Terrain")]
    RoomTerrain,
    #[reference(instance_of = "Source")]
    Source,
    #[reference(instance_of = "StructureContainer")]
    StructureContainer,
    #[reference(instance_of = "StructureController")]
    StructureController,
    #[reference(instance_of = "StructureExtension")]
    StructureExtension,
    #[reference(instance_of = "StructureExtractor")]
    StructureExtractor,
    #[reference(instance_of = "StructureKeeperLair")]
    StructureKeeperLair,
    #[reference(instance_of = "StructureLab")]
    StructureLab,
    #[reference(instance_of = "StructureLink")]
    StructureLink,
    #[reference(instance_of = "StructureNuker")]
    StructureNuker,
    #[reference(instance_of = "StructureObserver")]
    StructureObserver,
    #[reference(instance_of = "StructurePowerBank")]
    StructurePowerBank,
    #[reference(instance_of = "StructurePowerSpawn")]
    StructurePowerSpawn,
    #[reference(instance_of = "StructurePortal")]
    StructurePortal,
    #[reference(instance_of = "StructureRampart")]
    StructureRampart,
    #[reference(instance_of = "StructureRoad")]
    StructureRoad,
    #[reference(instance_of = "StructureSpawn")]
    StructureSpawn,
    #[reference(instance_of = "Spawning")]
    Spawning,
    #[reference(instance_of = "StructureStorage")]
    StructureStorage,
    #[reference(instance_of = "StructureTerminal")]
    StructureTerminal,
    #[reference(instance_of = "StructureTower")]
    StructureTower,
    #[reference(instance_of = "StructureWall")]
    StructureWall,
    // this is implemented later
    // #[reference(instance_of = "Structure")]
    // Structure,
    #[reference(instance_of = "Tombstone")]
    Tombstone,
    #[reference(instance_of = "PowerCreep")]
    PowerCreep,
);

/// Trait for things which have positions in the Screeps world.
///
/// This can be freely implemented for anything with a way to get a position.
pub trait HasPosition {
    fn pos(&self) -> Position;
}

impl HasPosition for Position {
    fn pos(&self) -> Position {
        self.clone()
    }
}

/// All `RoomObject`s have positions.
impl<T> HasPosition for T
where
    T: RoomObjectProperties,
{
    fn pos(&self) -> Position {
        Position::from_packed(js_unwrap!(@{self.as_ref()}.pos.__packedPos))
    }
}

/// Trait covering all objects with an id.
pub unsafe trait HasId: RoomObjectProperties {
    fn id(&self) -> String {
        js_unwrap!(@{self.as_ref()}.id)
    }
}

impl_has_id! {
    ConstructionSite;
    Creep;
    Mineral;
    Nuke;
    Resource;
    Source;
    OwnedStructure;
    Structure;
    StructureContainer;
    StructureController;
    StructureExtension;
    StructureExtractor;
    StructureKeeperLair;
    StructureLab;
    StructureLink;
    StructureNuker;
    StructureObserver;
    StructurePowerBank;
    StructurePowerSpawn;
    StructurePortal;
    StructureRampart;
    StructureRoad;
    StructureSpawn;
    StructureStorage;
    StructureTerminal;
    StructureTower;
    StructureWall;
    Tombstone;
    PowerCreep;
}

/// Trait for all wrappers over Screeps JavaScript objects extending
/// the `RoomObject` class.
///
/// # Contracts
///
/// The reference returned by `AsRef<Reference>::as_ref` must reference a
/// JavaScript object extending the `RoomObject` class.
pub unsafe trait RoomObjectProperties: AsRef<Reference> + HasPosition {
    fn room(&self) -> Room {
        js_unwrap_ref!(@{self.as_ref()}.room)
    }
}

/// Trait representing things that are both `RoomObjectProperties` and `Sized`.
///
/// These bounds would be on `RoomObjectProperties`, but for the fact that they
/// then require all `T: RoomObjectProperties` to be `T: Sized`, and thus
/// disallow creating trait objects like `&dyn RoomObjectProperties` (or more
/// usefully, `&dyn Attackable` or `&dyn HasStore`)
///
/// This trait is automatically implemented for all structures implementing the
/// traits it requires, and everything implement `RoomObjectProperties` and
/// being `Sized` should also implement this.
pub trait SizedRoomObject:
    Into<Reference>
    + ReferenceType
    + TryFrom<Value, Error = ConversionError>
    + TryFrom<Reference, Error = ConversionError>
{
}

impl<T> SizedRoomObject for T where
    T: RoomObjectProperties
        + Into<Reference>
        + ReferenceType
        + TryFrom<Value, Error = ConversionError>
        + TryFrom<Reference, Error = ConversionError>
{
}

/// Trait for all wrappers over Screeps JavaScript objects extending
/// the `Structure` class.
///
/// # Contracts
///
/// The reference returned by `AsRef<Reference>::as_ref` must reference a
/// JavaScript object extending the `Structure` class.
pub unsafe trait StructureProperties: RoomObjectProperties + HasId {
    fn structure_type(&self) -> StructureType {
        js_unwrap!(__structure_type_str_to_num(@{self.as_ref()}.structureType))
    }
    fn destroy(&self) -> ReturnCode {
        js_unwrap!(@{self.as_ref()}.destroy())
    }
    fn is_active(&self) -> bool {
        js_unwrap!(@{self.as_ref()}.isActive())
    }
    /// Usable on either owned structures or neutral structures in owned rooms,
    /// returns `ReturnCode::NotOwner` otherwise.
    fn notify_when_attacked(&self, notify_when_attacked: bool) -> ReturnCode {
        js_unwrap!(@{self.as_ref()}.notifyWhenAttacked(@{notify_when_attacked}))
    }
    fn as_structure(self) -> Structure
    where
        Self: SizedRoomObject,
    {
        Into::<Reference>::into(self)
            .into_expected_type()
            .expect("expected converting a StructureProperties to a Structure would suceed.")
    }
}

/// Trait for all wrappers over Screeps JavaScript objects extending
/// the `OwnedStructure` class.
///
/// # Contracts
///
/// The reference returned by `AsRef<Reference>::as_ref` must reference a
/// JavaScript object extending the `OwnedStructure` class.
pub unsafe trait OwnedStructureProperties: StructureProperties {
    /// Whether this structure is owned by you (in JS: `my || false`)
    fn my(&self) -> bool {
        js_unwrap!(@{self.as_ref()}.my || false)
    }
    /// Whether this structure is currently owned by someone (in JS: `my !==
    /// undefined`)
    fn has_owner(&self) -> bool {
        js_unwrap!(@{self.as_ref()}.my !== undefined)
    }
    /// The name of the owner of this structure, if any.
    fn owner_name(&self) -> Option<String> {
        (js! {
            var self = @{self.as_ref()};
            if (self.owner) {
                return self.owner.username;
            } else {
                return null;
            }
        })
        .try_into()
        .expect("expected OwnedStructure.owner.username to be a string")
    }
    /// Anonymize this as an owned structure.
    fn as_owned_structure(self) -> OwnedStructure
    where
        Self: SizedRoomObject,
    {
        OwnedStructure(self.into())
    }
}

/// Trait for all wrappers over Screeps JavaScript objects with a
/// `store` property.
///
/// # Contracts
///
/// The JavaScript object referenced by the return of `AsRef<Reference>::as_ref`
/// must have a `store` property. Additionally, if it does not have a
/// `storeCapacity` property, `HasStore::store_capacity` must be overridden.
///
/// The `store` property must be a dict from string resource types to integers.
///
/// If present, the `storeCapacity` property must be an integer.
pub unsafe trait HasStore: RoomObjectProperties {
    fn store_total(&self) -> u32 {
        js_unwrap!(_.sum(@{self.as_ref()}.store))
    }

    fn store_types(&self) -> Vec<ResourceType> {
        js_unwrap!(Object.keys(@{self.as_ref()}.store).map(__resource_type_str_to_num))
    }

    fn store_of(&self, ty: ResourceType) -> u32 {
        js_unwrap!(@{self.as_ref()}.store[__resource_type_num_to_str(@{ty as u32})] || 0)
    }

    fn energy(&self) -> u32 {
        js_unwrap!(@{self.as_ref()}.store[RESOURCE_ENERGY])
    }

    fn store_capacity(&self) -> u32 {
        js_unwrap!(@{self.as_ref()}.storeCapacity)
    }
}

/// Trait for objects which can only store energy.
///
/// # Contract
///
/// The reference returned from `AsRef<Reference>::as_ref` must be have an
/// `energy` and an `energyCapacity` properties.
pub unsafe trait CanStoreEnergy: StructureProperties {
    fn energy(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.energy }
    }

    fn energy_capacity(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.energyCapacity }
    }
}

/// Used to specify which structures can use their stored energy for spawning
/// creeps.
///
/// # Contract
///
/// The reference returned from `AsRef<Reference>::as_ref` must be able to be
/// used by a spawner to create a new creep.
pub unsafe trait HasEnergyForSpawn: CanStoreEnergy {}

/// Trait for objects which have to cooldown.
///
/// # Contract
///
/// The reference returned from `AsRef<Reference>::as_ref` must be have a
/// `cooldown` properties.
pub unsafe trait HasCooldown: StructureProperties {
    fn cooldown(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.cooldown }
    }
}

/// Trait for objects which can decay.
///
/// # Contract
///
/// The reference returned from `AsRef<Reference>::as_ref` must be have a
/// `ticksToDecay` properties.
pub unsafe trait CanDecay: RoomObjectProperties {
    fn ticks_to_decay(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.ticksToDecay }
    }
}

/// Trait for all wrappers over Screeps JavaScript objects which can be the
/// target of `Creep.transfer`.
///
/// # Contracts
///
/// The reference returned from `AsRef<Reference>::as_ref` must be a valid
/// target for `Creep.transfer`.
pub unsafe trait Transferable: RoomObjectProperties {}

/// Trait for all wrappers over Screeps JavaScript objects which can be the
/// target of `Creep.withdraw`.
///
/// # Contracts
///
/// The reference returned from `AsRef<Reference>::as_ref` must be a valid
/// target for `Creep.withdraw`.
pub unsafe trait Withdrawable: RoomObjectProperties {}

/// Trait for all wrappers over Screeps JavaScript objects which can be the
/// target of `Creep.attack`.
///
/// # Contracts
///
/// The reference returned from `AsRef<Reference>::as_ref` must be a valid
/// target for `Creep.attack`.
pub unsafe trait Attackable: RoomObjectProperties {
    /// Retrieve this hits of this structure, or `0` if this structure doesn't
    /// have a hit count.
    ///
    /// For instance, this retrieves the hitpoints of a `Creep`. Or for a
    /// `StructureWall` that's part of a novice area border, this will return
    /// `0`.
    fn hits(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.hits || 0 }
    }

    /// Retrieve the maximum hits of this structure, or `0` if this structure
    /// doesn't have a hit count.
    ///
    /// For instance, this retrieves the maximum full health of a `Creep`. Or
    /// for a `StructureWall` that's part of a novice area border, this will
    /// return `0`.
    fn hits_max(&self) -> u32 {
        js_unwrap! { @{self.as_ref()}.hitsMax || 0 }
    }
}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl Transferable for StructureExtension {}
unsafe impl Transferable for Creep {}
unsafe impl Transferable for StructureContainer {}
unsafe impl Transferable for StructureLab {}
unsafe impl Transferable for StructureLink {}
unsafe impl Transferable for StructureNuker {}
unsafe impl Transferable for StructureSpawn {}
unsafe impl Transferable for StructureStorage {}
unsafe impl Transferable for StructureTower {}
unsafe impl Transferable for StructurePowerSpawn {}
unsafe impl Transferable for StructureTerminal {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl Withdrawable for StructureExtension {}
unsafe impl Withdrawable for StructureContainer {}
unsafe impl Withdrawable for StructureLab {}
unsafe impl Withdrawable for StructureLink {}
unsafe impl Withdrawable for StructureSpawn {}
unsafe impl Withdrawable for StructureStorage {}
unsafe impl Withdrawable for StructureTower {}
unsafe impl Withdrawable for StructurePowerSpawn {}
unsafe impl Withdrawable for StructureTerminal {}
unsafe impl Withdrawable for Tombstone {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl Attackable for Creep {}
unsafe impl Attackable for OwnedStructure {}
unsafe impl Attackable for StructureContainer {}
unsafe impl Attackable for StructureExtension {}
unsafe impl Attackable for StructureExtractor {}
unsafe impl Attackable for StructureKeeperLair {}
unsafe impl Attackable for StructureLab {}
unsafe impl Attackable for StructureLink {}
unsafe impl Attackable for StructureNuker {}
unsafe impl Attackable for StructureObserver {}
unsafe impl Attackable for StructurePowerBank {}
unsafe impl Attackable for StructurePowerSpawn {}
unsafe impl Attackable for StructureRampart {}
unsafe impl Attackable for StructureRoad {}
unsafe impl Attackable for StructureSpawn {}
unsafe impl Attackable for StructureStorage {}
unsafe impl Attackable for StructureTerminal {}
unsafe impl Attackable for StructureTower {}
unsafe impl Attackable for StructureWall {}

unsafe impl RoomObjectProperties for ConstructionSite {}
unsafe impl RoomObjectProperties for Creep {}
unsafe impl RoomObjectProperties for Flag {}
unsafe impl RoomObjectProperties for Mineral {}
unsafe impl RoomObjectProperties for Nuke {}
unsafe impl RoomObjectProperties for OwnedStructure {}
unsafe impl RoomObjectProperties for Resource {}
unsafe impl RoomObjectProperties for RoomObject {}
unsafe impl RoomObjectProperties for Source {}
unsafe impl RoomObjectProperties for StructureContainer {}
unsafe impl RoomObjectProperties for StructureController {}
unsafe impl RoomObjectProperties for StructureExtension {}
unsafe impl RoomObjectProperties for StructureExtractor {}
unsafe impl RoomObjectProperties for StructureKeeperLair {}
unsafe impl RoomObjectProperties for StructureLab {}
unsafe impl RoomObjectProperties for StructureLink {}
unsafe impl RoomObjectProperties for StructureNuker {}
unsafe impl RoomObjectProperties for StructureObserver {}
unsafe impl RoomObjectProperties for StructurePowerBank {}
unsafe impl RoomObjectProperties for StructurePowerSpawn {}
unsafe impl RoomObjectProperties for StructurePortal {}
unsafe impl RoomObjectProperties for StructureRampart {}
unsafe impl RoomObjectProperties for StructureRoad {}
unsafe impl RoomObjectProperties for StructureSpawn {}
unsafe impl RoomObjectProperties for StructureStorage {}
unsafe impl RoomObjectProperties for StructureTerminal {}
unsafe impl RoomObjectProperties for StructureTower {}
unsafe impl RoomObjectProperties for StructureWall {}
unsafe impl RoomObjectProperties for Structure {}
unsafe impl RoomObjectProperties for Tombstone {}
unsafe impl RoomObjectProperties for PowerCreep {}

impl_structure_properties! {
    OwnedStructure,
    Structure,
    StructureContainer,
    StructureController,
    StructureExtension,
    StructureExtractor,
    StructureKeeperLair,
    StructureLab,
    StructureLink,
    StructureNuker,
    StructureObserver,
    StructurePowerBank,
    StructurePowerSpawn,
    StructurePortal,
    StructureRampart,
    StructureRoad,
    StructureSpawn,
    StructureStorage,
    StructureTerminal,
    StructureTower,
    StructureWall
}

unsafe impl OwnedStructureProperties for OwnedStructure {}
unsafe impl OwnedStructureProperties for StructureController {}
unsafe impl OwnedStructureProperties for StructureExtension {}
unsafe impl OwnedStructureProperties for StructureExtractor {}
unsafe impl OwnedStructureProperties for StructureKeeperLair {}
unsafe impl OwnedStructureProperties for StructureLab {}
unsafe impl OwnedStructureProperties for StructureLink {}
unsafe impl OwnedStructureProperties for StructureNuker {}
unsafe impl OwnedStructureProperties for StructureObserver {}
unsafe impl OwnedStructureProperties for StructurePowerBank {}
unsafe impl OwnedStructureProperties for StructurePowerSpawn {}
unsafe impl OwnedStructureProperties for StructureRampart {}
unsafe impl OwnedStructureProperties for StructureSpawn {}
unsafe impl OwnedStructureProperties for StructureStorage {}
unsafe impl OwnedStructureProperties for StructureTerminal {}
unsafe impl OwnedStructureProperties for StructureTower {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl HasStore for StructureContainer {}
unsafe impl HasStore for StructureStorage {}
unsafe impl HasStore for StructureTerminal {}
unsafe impl HasStore for Tombstone {
    fn store_capacity(&self) -> u32 {
        0 // no storeCapacity property
    }
}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl CanStoreEnergy for StructureExtension {}
unsafe impl CanStoreEnergy for StructureLab {}
unsafe impl CanStoreEnergy for StructureLink {}
unsafe impl CanStoreEnergy for StructureNuker {}
unsafe impl CanStoreEnergy for StructurePowerSpawn {}
unsafe impl CanStoreEnergy for StructureSpawn {}
unsafe impl CanStoreEnergy for StructureTower {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl HasEnergyForSpawn for StructureExtension {}
unsafe impl HasEnergyForSpawn for StructureSpawn {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl HasCooldown for StructureExtractor {}
unsafe impl HasCooldown for StructureLab {}
unsafe impl HasCooldown for StructureLink {}
unsafe impl HasCooldown for StructureNuker {}
unsafe impl HasCooldown for StructureTerminal {}

// NOTE: keep impls for Structure* in sync with accessor methods in
// src/objects/structure.rs

unsafe impl CanDecay for StructureContainer {}
unsafe impl CanDecay for StructurePowerBank {}
unsafe impl CanDecay for StructurePortal {}
unsafe impl CanDecay for StructureRampart {}
unsafe impl CanDecay for StructureRoad {}
unsafe impl CanDecay for Tombstone {}