1use std::fmt;
2
3use js_sys::{Array, JsString, Object};
4use num_traits::*;
5use serde::{
6 de::{self, MapAccess, Visitor},
7 Deserialize, Deserializer, Serialize,
8};
9use serde_repr::{Deserialize_repr, Serialize_repr};
10use wasm_bindgen::prelude::*;
11
12use crate::{
13 constants::{
14 find::*, look::*, Color, Direction, ExitDirection, PowerType, ResourceType, StructureType,
15 },
16 enums::action_error_codes::room::*,
17 local::{LodashFilter, RoomName},
18 objects::*,
19 pathfinder::RoomCostResult,
20 prelude::*,
21};
22
23#[wasm_bindgen]
24extern "C" {
25 #[derive(Clone, Debug)]
30 pub type Room;
31
32 #[wasm_bindgen(method, getter)]
37 pub fn controller(this: &Room) -> Option<StructureController>;
38
39 #[wasm_bindgen(method, getter = energyAvailable)]
43 pub fn energy_available(this: &Room) -> u32;
44
45 #[wasm_bindgen(method, getter = energyCapacityAvailable)]
49 pub fn energy_capacity_available(this: &Room) -> u32;
50
51 #[wasm_bindgen(method, getter)]
55 pub fn memory(this: &Room) -> JsValue;
56
57 #[wasm_bindgen(method, setter)]
61 pub fn set_memory(this: &Room, val: &JsValue);
62
63 #[wasm_bindgen(method, getter = name)]
64 fn name_internal(this: &Room) -> JsString;
65
66 #[wasm_bindgen(method, getter)]
71 pub fn storage(this: &Room) -> Option<StructureStorage>;
72
73 #[wasm_bindgen(method, getter)]
78 pub fn terminal(this: &Room) -> Option<StructureTerminal>;
79
80 #[wasm_bindgen(static_method_of = Room, js_name = serializePath)]
81 fn serialize_path_internal(path: &Array) -> JsString;
82
83 #[wasm_bindgen(static_method_of = Room, js_name = deserializePath)]
84 fn deserialize_path_internal(path: &JsString) -> Array;
85
86 #[wasm_bindgen(final, method, js_name = createConstructionSite)]
87 fn create_construction_site_internal(
88 this: &Room,
89 x: u8,
90 y: u8,
91 ty: StructureType,
92 name: Option<&JsString>,
93 ) -> i8;
94
95 #[wasm_bindgen(final, method, js_name = createFlag)]
96 fn create_flag_internal(
97 this: &Room,
98 x: u8,
99 y: u8,
100 name: Option<&JsString>,
101 color: Option<Color>,
102 secondary_color: Option<Color>,
103 ) -> JsValue;
104
105 #[wasm_bindgen(method, js_name = find)]
106 fn find_internal(this: &Room, ty: Find, options: Option<&FindOptions>) -> Array;
107
108 #[wasm_bindgen(final, method, js_name = findExitTo)]
109 fn find_exit_to_internal(this: &Room, room: &JsString) -> i8;
110
111 #[wasm_bindgen(final, method, js_name = findPath)]
112 fn find_path_internal(
113 this: &Room,
114 origin: &RoomPosition,
115 goal: &RoomPosition,
116 options: Option<&Object>,
117 ) -> JsValue;
118
119 #[wasm_bindgen(final, method, js_name = getEventLog)]
120 fn get_event_log_internal(this: &Room, raw: bool) -> JsValue;
121
122 #[wasm_bindgen(final, method, js_name = getPositionAt)]
126 pub fn get_position_at(this: &Room, x: u8, y: u8) -> RoomPosition;
127
128 #[wasm_bindgen(final, method, js_name = getTerrain)]
132 pub fn get_terrain(this: &Room) -> RoomTerrain;
133
134 #[wasm_bindgen(final, method, js_name = lookAt)]
135 fn look_at_internal(this: &Room, target: &RoomPosition) -> Array;
136
137 #[wasm_bindgen(final, method, js_name = lookAt)]
138 fn look_at_xy_internal(this: &Room, x: u8, y: u8) -> Array;
139
140 #[wasm_bindgen(final, method, js_name = lookAtArea)]
143 fn look_at_area_internal(
144 this: &Room,
145 top_y: u8,
146 left_x: u8,
147 bottom_y: u8,
148 right_x: u8,
149 as_array: bool,
150 ) -> Option<Array>;
151
152 #[wasm_bindgen(final, method, js_name = lookForAt)]
153 fn look_for_at_internal(this: &Room, ty: Look, target: &RoomPosition) -> Option<Array>;
154
155 #[wasm_bindgen(final, method, js_name = lookForAt)]
156 fn look_for_at_xy_internal(this: &Room, ty: Look, x: u8, y: u8) -> Option<Array>;
157
158 #[wasm_bindgen(final, method, js_name = lookForAtArea)]
161 fn look_for_at_area_internal(
162 this: &Room,
163 ty: Look,
164 top_y: u8,
165 left_x: u8,
166 bottom_y: u8,
167 right_x: u8,
168 as_array: bool,
169 ) -> Option<Array>;
170}
171
172impl Room {
173 pub fn name(&self) -> RoomName {
177 self.name_internal()
178 .try_into()
179 .expect("expected parseable room name")
180 }
181
182 pub fn serialize_path(path: &[Step]) -> String {
187 Self::serialize_path_internal(
188 serde_wasm_bindgen::to_value(path)
189 .expect("invalid path passed to serialize")
190 .unchecked_ref(),
191 )
192 .into()
193 }
194
195 pub fn deserialize_path(path: &str) -> Vec<Step> {
200 serde_wasm_bindgen::from_value(
201 Self::deserialize_path_internal(&JsString::from(path)).unchecked_into(),
202 )
203 .expect("invalid deserialized path")
204 }
205
206 pub fn visual(&self) -> RoomVisual {
207 RoomVisual::new(Some(self.name()))
208 }
209
210 pub fn create_construction_site(
223 &self,
224 x: u8,
225 y: u8,
226 ty: StructureType,
227 name: Option<&JsString>,
228 ) -> Result<(), RoomCreateConstructionSiteErrorCode> {
229 RoomCreateConstructionSiteErrorCode::result_from_i8(
230 self.create_construction_site_internal(x, y, ty, name),
231 )
232 }
233
234 pub fn create_flag(
239 &self,
240 x: u8,
241 y: u8,
242 name: Option<&JsString>,
243 color: Option<Color>,
244 secondary_color: Option<Color>,
245 ) -> Result<JsString, RoomCreateFlagErrorCode> {
246 let result = self.create_flag_internal(x, y, name, color, secondary_color);
247 if result.is_string() {
248 Ok(result.unchecked_into())
249 } else {
250 Err(RoomCreateFlagErrorCode::from_f64(
251 result
252 .as_f64()
253 .expect("expected non-string flag return to be a number"),
254 )
255 .expect("expected valid error code"))
256 }
257 }
258
259 pub fn find<T>(&self, ty: T, options: Option<&FindOptions>) -> Vec<T::Item>
267 where
268 T: FindConstant,
269 {
270 self.find_internal(ty.find_code(), options)
271 .iter()
272 .map(T::convert_and_check_item)
273 .collect()
274 }
275
276 pub fn find_exit_to(&self, room: RoomName) -> Result<ExitDirection, FindExitToErrorCode> {
280 let result = self.find_exit_to_internal(&room.into());
281 if result >= 0 {
282 Ok(ExitDirection::from_i8(result)
283 .expect("expect find exit return to be a valid exit constant"))
284 } else {
285 Err(FindExitToErrorCode::result_from_i8(result).unwrap_err())
288 }
289 }
290
291 pub fn find_path<F, R>(
295 &self,
296 origin: &RoomPosition,
297 goal: &RoomPosition,
298 options: Option<FindPathOptions<F, R>>,
299 ) -> Path
300 where
301 F: FnMut(RoomName, CostMatrix) -> R,
302 R: RoomCostResult,
303 {
304 if let Some(options) = options {
305 options.into_js_options(|js_options| {
306 serde_wasm_bindgen::from_value(self.find_path_internal(
307 origin,
308 goal,
309 Some(js_options.unchecked_ref()),
310 ))
311 .expect("invalid path from Room.findPath")
312 })
313 } else {
314 serde_wasm_bindgen::from_value(self.find_path_internal(origin, goal, None))
315 .expect("invalid path from Room.findPath")
316 }
317 }
318
319 pub fn get_event_log(&self) -> Vec<Event> {
320 serde_json::from_str(&self.get_event_log_raw()).expect("Malformed Event Log")
321 }
322
323 pub fn get_event_log_raw(&self) -> String {
324 let js_log: JsString = Room::get_event_log_internal(self, true).into();
325 js_log.into()
326 }
327
328 pub fn look_at(&self, target: &RoomPosition) -> Vec<LookResult> {
332 self.look_at_internal(target)
333 .iter()
334 .map(LookResult::from_jsvalue_unknown_type)
335 .collect()
336 }
337
338 pub fn look_at_xy(&self, x: u8, y: u8) -> Vec<LookResult> {
342 self.look_at_xy_internal(x, y)
343 .iter()
344 .map(LookResult::from_jsvalue_unknown_type)
345 .collect()
346 }
347
348 pub fn look_at_area(
352 &self,
353 top_y: u8,
354 left_x: u8,
355 bottom_y: u8,
356 right_x: u8,
357 ) -> Vec<PositionedLookResult> {
358 self.look_at_area_internal(top_y, left_x, bottom_y, right_x, true)
359 .map(|arr| {
360 arr.iter()
361 .map(PositionedLookResult::from_jsvalue_unknown_type)
362 .collect()
363 })
364 .unwrap_or_default()
365 }
366
367 pub fn look_for_at<T, U>(&self, _ty: T, target: &U) -> Vec<T::Item>
371 where
372 T: LookConstant,
373 U: HasPosition,
374 {
375 let pos = target.pos().into();
376
377 self.look_for_at_internal(T::look_code(), &pos)
378 .map(|arr| arr.iter().map(T::convert_and_check_item).collect())
379 .unwrap_or_default()
380 }
381
382 pub fn look_for_at_xy<T>(&self, _ty: T, x: u8, y: u8) -> Vec<T::Item>
387 where
388 T: LookConstant,
389 {
390 self.look_for_at_xy_internal(T::look_code(), x, y)
391 .map(|arr| arr.iter().map(T::convert_and_check_item).collect())
392 .unwrap_or_default()
393 }
394
395 pub fn look_for_at_area<T>(
399 &self,
400 _ty: T,
401 top_y: u8,
402 left_x: u8,
403 bottom_y: u8,
404 right_x: u8,
405 ) -> Vec<PositionedLookResult>
406 where
407 T: LookConstant,
408 {
409 self.look_for_at_area_internal(T::look_code(), top_y, left_x, bottom_y, right_x, true)
410 .map(|arr| {
411 arr.iter()
412 .map(|lr| PositionedLookResult::from_jsvalue_with_type(lr, T::look_code()))
413 .collect()
414 })
415 .unwrap_or_default()
416 }
417}
418
419impl PartialEq for Room {
420 fn eq(&self, other: &Room) -> bool {
421 self.name() == other.name()
422 }
423}
424
425impl Eq for Room {}
426
427impl JsCollectionFromValue for Room {
428 fn from_value(val: JsValue) -> Self {
429 val.unchecked_into()
430 }
431}
432
433#[wasm_bindgen]
434extern "C" {
435 #[wasm_bindgen]
436 pub type FindOptions;
437
438 #[wasm_bindgen(method, setter = filter)]
439 pub fn filter(this: &FindOptions, filter: LodashFilter);
440}
441
442#[wasm_bindgen]
443extern "C" {
444 #[wasm_bindgen]
445 pub type JsFindPathOptions;
446
447 #[wasm_bindgen(method, setter = ignoreCreeps)]
448 pub fn ignore_creeps(this: &JsFindPathOptions, ignore: bool);
449
450 #[wasm_bindgen(method, setter = ignoreDestructibleStructures)]
451 pub fn ignore_destructible_structures(this: &JsFindPathOptions, ignore: bool);
452
453 #[wasm_bindgen(method, setter = costCallback)]
454 pub fn cost_callback(
455 this: &JsFindPathOptions,
456 callback: &Closure<dyn FnMut(JsString, CostMatrix) -> JsValue>,
457 );
458
459 #[wasm_bindgen(method, setter = maxOps)]
460 pub fn max_ops(this: &JsFindPathOptions, ops: u32);
461
462 #[wasm_bindgen(method, setter = heuristicWeight)]
463 pub fn heuristic_weight(this: &JsFindPathOptions, weight: f64);
464
465 #[wasm_bindgen(method, setter = serialize)]
466 pub fn serialize(this: &JsFindPathOptions, serialize: bool);
467
468 #[wasm_bindgen(method, setter = maxRooms)]
469 pub fn max_rooms(this: &JsFindPathOptions, max: u8);
470
471 #[wasm_bindgen(method, setter = range)]
472 pub fn range(this: &JsFindPathOptions, range: u32);
473
474 #[wasm_bindgen(method, setter = plainCost)]
475 pub fn plain_cost(this: &JsFindPathOptions, cost: u8);
476
477 #[wasm_bindgen(method, setter = swampCost)]
478 pub fn swamp_cost(this: &JsFindPathOptions, cost: u8);
479}
480
481impl JsFindPathOptions {
482 pub fn new() -> JsFindPathOptions {
483 Object::new().unchecked_into()
484 }
485}
486
487impl Default for JsFindPathOptions {
488 fn default() -> Self {
489 Self::new()
490 }
491}
492
493pub struct FindPathOptions<F, R>
494where
495 F: FnMut(RoomName, CostMatrix) -> R,
496 R: RoomCostResult,
497{
498 pub(crate) ignore_creeps: Option<bool>,
499 pub(crate) ignore_destructible_structures: Option<bool>,
500 pub(crate) cost_callback: F,
501 pub(crate) max_ops: Option<u32>,
502 pub(crate) heuristic_weight: Option<f64>,
503 pub(crate) serialize: Option<bool>,
504 pub(crate) max_rooms: Option<u8>,
505 pub(crate) range: Option<u32>,
506 pub(crate) plain_cost: Option<u8>,
507 pub(crate) swamp_cost: Option<u8>,
508}
509
510impl<R> Default for FindPathOptions<fn(RoomName, CostMatrix) -> R, R>
511where
512 R: RoomCostResult + Default,
513{
514 fn default() -> Self {
515 FindPathOptions {
516 ignore_creeps: None,
517 ignore_destructible_structures: None,
518 cost_callback: |_, _| R::default(),
519 max_ops: None,
520 heuristic_weight: None,
521 serialize: None,
522 max_rooms: None,
523 range: None,
524 plain_cost: None,
525 swamp_cost: None,
526 }
527 }
528}
529
530impl<R> FindPathOptions<fn(RoomName, CostMatrix) -> R, R>
531where
532 R: RoomCostResult + Default,
533{
534 pub fn new() -> Self {
535 Self::default()
536 }
537}
538
539impl<F, R> FindPathOptions<F, R>
540where
541 F: FnMut(RoomName, CostMatrix) -> R,
542 R: RoomCostResult,
543{
544 pub fn ignore_creeps(mut self, ignore: bool) -> Self {
546 self.ignore_creeps = Some(ignore);
547 self
548 }
549
550 pub fn ignore_destructible_structures(mut self, ignore: bool) -> Self {
553 self.ignore_destructible_structures = Some(ignore);
554 self
555 }
556
557 pub fn cost_callback<F2, R2>(self, cost_callback: F2) -> FindPathOptions<F2, R2>
559 where
560 F2: FnMut(RoomName, CostMatrix) -> R2,
561 R2: RoomCostResult,
562 {
563 let FindPathOptions {
564 ignore_creeps,
565 ignore_destructible_structures,
566 max_ops,
567 heuristic_weight,
568 serialize,
569 max_rooms,
570 range,
571 plain_cost,
572 swamp_cost,
573 ..
574 } = self;
575
576 FindPathOptions {
577 ignore_creeps,
578 ignore_destructible_structures,
579 cost_callback,
580 max_ops,
581 heuristic_weight,
582 serialize,
583 max_rooms,
584 range,
585 plain_cost,
586 swamp_cost,
587 }
588 }
589
590 pub fn max_ops(mut self, ops: u32) -> Self {
592 self.max_ops = Some(ops);
593 self
594 }
595
596 pub fn heuristic_weight(mut self, weight: f64) -> Self {
598 self.heuristic_weight = Some(weight);
599 self
600 }
601
602 pub fn serialize(mut self, s: bool) -> Self {
604 self.serialize = Some(s);
605 self
606 }
607
608 pub fn max_rooms(mut self, rooms: u8) -> Self {
610 self.max_rooms = Some(rooms);
611 self
612 }
613
614 pub fn range(mut self, k: u32) -> Self {
615 self.range = Some(k);
616 self
617 }
618
619 pub fn plain_cost(mut self, cost: u8) -> Self {
621 self.plain_cost = Some(cost);
622 self
623 }
624
625 pub fn swamp_cost(mut self, cost: u8) -> Self {
627 self.swamp_cost = Some(cost);
628 self
629 }
630
631 pub(crate) fn into_js_options<CR>(self, callback: impl Fn(&JsFindPathOptions) -> CR) -> CR {
632 let mut raw_callback = self.cost_callback;
633
634 let mut owned_callback = move |room: RoomName, cost_matrix: CostMatrix| -> JsValue {
635 raw_callback(room, cost_matrix).into()
636 };
637
638 let callback_type_erased: &mut dyn FnMut(RoomName, CostMatrix) -> JsValue =
644 &mut owned_callback;
645
646 let callback_lifetime_erased: &'static mut dyn FnMut(RoomName, CostMatrix) -> JsValue =
654 unsafe { std::mem::transmute(callback_type_erased) };
655
656 let boxed_callback = Box::new(move |room: JsString, cost_matrix: CostMatrix| -> JsValue {
657 let room = room
658 .try_into()
659 .expect("expected room name in cost callback");
660
661 callback_lifetime_erased(room, cost_matrix)
662 }) as Box<dyn FnMut(JsString, CostMatrix) -> JsValue>;
663
664 let closure = Closure::wrap(boxed_callback);
665
666 let js_options = JsFindPathOptions::new();
671
672 js_options.cost_callback(&closure);
673
674 if let Some(ignore_creeps) = self.ignore_creeps {
675 js_options.ignore_creeps(ignore_creeps);
676 }
677
678 if let Some(ignore_destructible_structures) = self.ignore_destructible_structures {
679 js_options.ignore_destructible_structures(ignore_destructible_structures);
680 }
681
682 if let Some(max_ops) = self.max_ops {
683 js_options.max_ops(max_ops);
684 }
685
686 if let Some(heuristic_weight) = self.heuristic_weight {
687 js_options.heuristic_weight(heuristic_weight);
688 }
689
690 if let Some(serialize) = self.serialize {
691 js_options.serialize(serialize);
692 }
693
694 if let Some(max_rooms) = self.max_rooms {
695 js_options.max_rooms(max_rooms);
696 }
697
698 if let Some(range) = self.range {
699 js_options.range(range);
700 }
701
702 if let Some(plain_cost) = self.plain_cost {
703 js_options.plain_cost(plain_cost);
704 }
705
706 if let Some(swamp_cost) = self.swamp_cost {
707 js_options.swamp_cost(swamp_cost);
708 }
709
710 callback(&js_options)
711 }
712}
713
714#[derive(Clone, Debug, Deserialize, Serialize)]
715pub struct Step {
716 pub x: u32,
717 pub y: u32,
718 pub dx: i32,
719 pub dy: i32,
720 pub direction: Direction,
721}
722
723#[derive(Debug, Deserialize)]
724#[serde(untagged)]
725pub enum Path {
726 Vectorized(Vec<Step>),
727 Serialized(String),
728}
729
730#[derive(Clone, Debug, PartialEq, Eq)]
731pub struct Event {
732 pub event: EventType,
733 pub object_id: String,
734}
735
736impl<'de> Deserialize<'de> for Event {
737 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
738 where
739 D: Deserializer<'de>,
740 {
741 #[derive(Deserialize)]
742 #[serde(field_identifier, rename_all = "camelCase")]
743 enum Field {
744 Event,
745 ObjectId,
746 Data,
747 }
748
749 struct EventVisitor;
750
751 impl<'de> Visitor<'de> for EventVisitor {
752 type Value = Event;
753
754 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
755 formatter.write_str("struct Event")
756 }
757
758 fn visit_map<V>(self, mut map: V) -> Result<Event, V::Error>
759 where
760 V: MapAccess<'de>,
761 {
762 let mut event_type = None;
763 let mut obj_id = None;
764 let mut data_buffer: Option<serde_json::Value> = None;
765
766 while let Some(key) = map.next_key()? {
767 match key {
768 Field::Event => {
769 if event_type.is_some() {
770 return Err(de::Error::duplicate_field("event"));
771 }
772 event_type = Some(map.next_value()?);
773 }
774 Field::ObjectId => {
775 if obj_id.is_some() {
776 return Err(de::Error::duplicate_field("objectId"));
777 }
778 obj_id = Some(map.next_value()?);
779 }
780 Field::Data => {
781 if data_buffer.is_some() {
782 return Err(de::Error::duplicate_field("data"));
783 }
784
785 data_buffer = map.next_value()?;
786 }
787 }
788 }
789
790 let event_id = event_type.ok_or_else(|| de::Error::missing_field("event"))?;
791
792 let err = |e| {
793 de::Error::custom(format_args!(
794 "can't parse event data due to inner error {e}"
795 ))
796 };
797
798 let data = if let Some(val) = data_buffer {
799 match event_id {
800 1 => Some(EventType::Attack(serde_json::from_value(val).map_err(err)?)),
801 2 => Some(EventType::ObjectDestroyed(
802 serde_json::from_value(val).map_err(err)?,
803 )),
804 3 => Some(EventType::AttackController),
805 4 => Some(EventType::Build(serde_json::from_value(val).map_err(err)?)),
806 5 => Some(EventType::Harvest(
807 serde_json::from_value(val).map_err(err)?,
808 )),
809 6 => Some(EventType::Heal(serde_json::from_value(val).map_err(err)?)),
810 7 => Some(EventType::Repair(serde_json::from_value(val).map_err(err)?)),
811 8 => Some(EventType::ReserveController(
812 serde_json::from_value(val).map_err(err)?,
813 )),
814 9 => Some(EventType::UpgradeController(
815 serde_json::from_value(val).map_err(err)?,
816 )),
817 10 => Some(EventType::Exit(serde_json::from_value(val).map_err(err)?)),
818 11 => Some(EventType::Power(serde_json::from_value(val).map_err(err)?)),
819 12 => Some(EventType::Transfer(
820 serde_json::from_value(val).map_err(err)?,
821 )),
822 _ => {
823 return Err(de::Error::custom(format!(
824 "Event Type Unrecognized: {event_id}"
825 )));
826 }
827 }
828 } else {
829 match event_id {
831 3 => Some(EventType::AttackController),
832 _ => None,
833 }
834 };
835
836 let data = data.ok_or_else(|| de::Error::missing_field("data"))?;
837 let obj_id = obj_id.ok_or_else(|| de::Error::missing_field("objectId"))?;
838
839 Ok(Event {
840 event: data,
841 object_id: obj_id,
842 })
843 }
844 }
845
846 const FIELDS: &[&str] = &["event", "objectId", "data"];
847 deserializer.deserialize_struct("Event", FIELDS, EventVisitor)
848 }
849}
850
851#[derive(Clone, Debug, PartialEq, Eq)]
852pub enum EventType {
853 Attack(AttackEvent),
854 ObjectDestroyed(ObjectDestroyedEvent),
855 AttackController,
856 Build(BuildEvent),
857 Harvest(HarvestEvent),
858 Heal(HealEvent),
859 Repair(RepairEvent),
860 ReserveController(ReserveControllerEvent),
861 UpgradeController(UpgradeControllerEvent),
862 Exit(ExitEvent),
863 Power(PowerEvent),
864 Transfer(TransferEvent),
865}
866
867#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
868#[serde(rename_all = "camelCase")]
869pub struct AttackEvent {
870 pub target_id: String,
871 pub damage: u32,
872 pub attack_type: AttackType,
873}
874
875#[derive(Clone, Debug, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
876#[repr(u8)]
877pub enum AttackType {
878 Melee = 1,
879 Ranged = 2,
880 RangedMass = 3,
881 Dismantle = 4,
882 HitBack = 5,
883 Nuke = 6,
884}
885
886#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
887pub struct ObjectDestroyedEvent {
888 #[serde(rename = "type")]
889 pub object_type: String,
890}
891
892#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
893#[serde(rename_all = "camelCase")]
894pub struct BuildEvent {
895 pub target_id: String,
896 pub amount: u32,
897 pub structure_type: StructureType,
902 pub x: u8,
903 pub y: u8,
904 pub incomplete: bool,
905}
906
907#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
908#[serde(rename_all = "camelCase")]
909pub struct HarvestEvent {
910 pub target_id: String,
911 pub amount: u32,
912}
913
914#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
915#[serde(rename_all = "camelCase")]
916pub struct HealEvent {
917 pub target_id: String,
918 pub amount: u32,
919 pub heal_type: HealType,
920}
921
922#[derive(Clone, Debug, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
923#[repr(u8)]
924pub enum HealType {
925 Melee = 1,
926 Ranged = 2,
927}
928
929#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
930#[serde(rename_all = "camelCase")]
931pub struct RepairEvent {
932 pub target_id: String,
933 pub amount: u32,
934 pub energy_spent: u32,
935}
936
937#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
938#[serde(rename_all = "camelCase")]
939pub struct ReserveControllerEvent {
940 pub amount: u32,
941}
942
943#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
944#[serde(rename_all = "camelCase")]
945pub struct UpgradeControllerEvent {
946 pub amount: u32,
947 pub energy_spent: u32,
948}
949
950#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
951#[serde(rename_all = "camelCase")]
952pub struct ExitEvent {
953 pub room: String,
954 pub x: u32,
955 pub y: u32,
956}
957
958#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
959#[serde(rename_all = "camelCase")]
960pub struct TransferEvent {
961 pub target_id: String,
962 pub resource_type: ResourceType,
963 pub amount: u32,
964}
965
966#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
967#[serde(rename_all = "camelCase")]
968pub struct PowerEvent {
969 pub target_id: String,
970 pub power: PowerType,
971}