WOWCube Docs logo
WOWCube Docs
Mission Control
Section Shortcuts
APIExamplesSourceWOWConnectChangelog
Filters
SDK and language defaults persist in cookies.
SDK version
Navigation Tree
Collapsed by default, focused on the active path.
Made byMcKay Seamons
GitHub
  1. Home
  2. Docs
  3. Source
  4. game.rs
Mission NodeSDK 6.1Rustgame.rs

game.rs

SDK Source File: game.rs

Source / SDK 6.1 / Rust / Core

game.rs

game.rs
RUST
1use std::fmt::Debug;
2
3use super::cubios;
4use super::cubios::{topology, comm, gfx};
5
6
7#[allow(dead_code)]
8#[derive(Debug)]
9pub enum GameErr {
10 ValueIsNone,
11 SceneNotFound(u8),
12 AssetNotFound(&'static str),
13 ImgNotFound(u8),
14 InvalidModule(u8, u8),
15 InvalidPlace(u8, u8),
16 InvalidTwist(u8, u8),
17 InvalidDirection(u8),
18 InvalidOrientation(u8)
19}
20
21#[allow(dead_code)]
22#[derive(Debug)]
23pub enum GamePkt {
24 SetNone = 0,
25 SetMap,
26 SetScene,
27 Custom
28}
29
30#[allow(dead_code)]
31#[derive(Debug)]
32pub enum RotationDir {
33 Forward,
34 Backward,
35 Right,
36 Left
37}
38
39#[allow(dead_code)]
40#[derive(Debug)]
41pub struct Anim<T> {
42 pub val: T,
43 pub anim: u8,
44 pub step: u8,
45 pub cycle: bool,
46 pub enable: bool
47}
48
49#[allow(dead_code)]
50pub trait Game {
51 fn on_render(&self) -> Result<(), GameErr> {
52 Ok(())
53 }
54
55 fn on_init(&mut self) -> Result<(), GameErr> {
56 Ok(())
57 }
58
59 fn on_tick(&mut self) -> Result<(), GameErr> {
60 Ok(())
61 }
62
63 fn on_phys_tick(&mut self) -> Result<(), GameErr> {
64 Ok(())
65 }
66
67 fn on_twist(&mut self) -> Result<(), GameErr> {
68 Ok(())
69 }
70
71 fn on_rx(&mut self, _line: usize) -> Result<(), GameErr> {
72 Ok(())
73 }
74 fn on_tap(&mut self) -> Result<(), GameErr> {
75 Ok(())
76 }
77}
78
79#[allow(dead_code)]
80pub trait Assetable {
81 fn get_asset_id(asset: &'static str) -> Result<u32, GameErr> {
82 let id = gfx::get_asset_id(asset);
83
84 if id.is_negative() {
85 return Err(GameErr::AssetNotFound(asset));
86 }
87 Ok(id as u32)
88 }
89}
90
91#[allow(dead_code)]
92pub trait Mappable {
93 fn get_place(&self, module: u32, screen: u32, orientation: cubios::TopologyOrientation) -> Result<(u32, u32), GameErr> {
94 let mut place = cubios::TopologyPlace {face:0xff, position: 0xff};
95 topology::get_place(module, screen, orientation, &mut place);
96
97 if place.position == 0xff || place.position == 4 || place.face == 0xff || place.face == 6 {
98 return Err(GameErr::InvalidPlace(place.face as u8, place.position as u8));
99 }
100 Ok((place.face as u32, place.position as u32))
101 }
102
103 fn get_place_orientation(&self, face: u32, pos: u32) -> Result<cubios::TopologyLocation, GameErr> {
104 let orient = topology::get_place_orientation(face, pos);
105
106 if orient == cubios::TopologyLocation::Max as i32 || orient == 0xff {
107 return Err(GameErr::InvalidOrientation(orient as u8));
108 }
109
110 let res = match orient {
111 0 => cubios::TopologyLocation::Up,
112 1 => cubios::TopologyLocation::Down,
113 2 => cubios::TopologyLocation::Front,
114 3 => cubios::TopologyLocation::Back,
115 4 => cubios::TopologyLocation::Left,
116 5 => cubios::TopologyLocation::Right,
117 _ => return Err(GameErr::InvalidOrientation(orient as u8))
118 };
119 Ok(res)
120 }
121
122 fn get_info(&self, face: u8, pos: u8, orientation: cubios::TopologyOrientation) -> Result<(u32, u32), GameErr> {
123 let mut info = cubios::TopologyFaceletInfo {
124 module: 0xff,
125 screen: 0xff,
126 connected: 0
127 };
128 topology::get_facelet(face as u32, pos as u32, orientation, &mut info);
129
130 if info.module == 0xff || info.screen == 0xff {
131 return Err(GameErr::InvalidModule(info.module as u8, info.screen as u8));
132 }
133 Ok((info.module as u32, info.screen as u32))
134 }
135
136 fn get_info_dir(&self, module: u32, screen: u32, dir: cubios::TopologyNeighbour) -> Result<(u32, u32), GameErr> {
137 let mut info = cubios::TopologyFaceletInfo {
138 module: 0xff,
139 screen: 0xff,
140 connected: 0
141 };
142 topology::get_adjacent_facelet(module, screen, dir, &mut info);
143
144 if info.module == 0xff || info.screen == 0xff {
145 return Err(GameErr::InvalidModule(info.module as u8, info.screen as u8));
146 }
147 Ok((info.module as u32, info.screen as u32))
148 }
149
150 fn get_twist(&self) -> Result<(u8, u8), GameErr> {
151 let mut twist_info = cubios::TopologyTwistInfo {
152 screen: 0xff,
153 direction: 0xff,
154 count: 0xff,
155 };
156
157 topology::get_twist(&mut twist_info);
158
159 if twist_info.screen == 0xff || twist_info.direction == 0xff {
160 return Err(GameErr::InvalidTwist(twist_info.screen as u8, twist_info.direction as u8))
161 }
162
163 let remap = match twist_info.screen {
164 0 => 1,
165 1 => 2,
166 2 => 0,
167 _ => return Err(GameErr::InvalidTwist(twist_info.screen as u8, twist_info.direction as u8))
168 };
169
170 Ok((remap, twist_info.direction as u8))
171 }
172}
173
174#[allow(dead_code)]
175pub trait Packable: Sized {
176 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4];
177 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8);
178}
179
180pub struct MapBase<T: Packable> {
181 pub inited: bool,
182 last_update_time: usize,
183 pub screen_map: [Option<T>; 3],
184 pub master_map: [[Option<T>; 3]; 8]
185}
186
187pub struct SceneSwitch {
188 last_update_time: usize,
189
190 pub scene_id: u8,
191 pub scenes: &'static [usize],
192 pub last_switch_time: usize,
193}
194
195#[allow(dead_code)]
196impl<T: Packable + Copy> MapBase<T> {
197 pub fn new(val: T) -> Self {
198 Self {
199 inited: false,
200 last_update_time: 0,
201 screen_map: [Some(val); 3],
202 master_map: [[Some(val); 3]; 8]
203 }
204 }
205}
206
207#[allow(dead_code)]
208pub trait Broadcastable {
209 fn broadcast_part(&mut self, _module: u8, _screen: u8, _delay: usize) -> Result<(), GameErr> {
210 Ok(())
211 }
212 fn broadcast(&mut self, delay: usize) -> Result<(), GameErr>;
213
214 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), GameErr>;
215}
216
217#[allow(dead_code)]
218pub trait Shuffable {
219 fn rotate(&mut self, dir: &RotationDir) -> Result<(), GameErr>;
220 fn shuffle(&mut self, algo: &[RotationDir]) -> Result<(), GameErr>;
221}
222
223impl<T: Packable + PartialEq + Debug> Broadcastable for MapBase<T> {
224 fn broadcast_part(&mut self, module: u8, screen: u8, delay: usize) -> Result<(), GameErr> {
225 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay) {
226 return Ok(());
227 }
228
229 let pkt = self.master_map[module as usize][screen as usize].to_pkt(module, screen);
230
231 for i in 0..3 {
232 comm::send_message(i, pkt.as_ptr(), 4);
233 }
234
235 Ok(())
236 }
237
238 fn broadcast(&mut self, delay: usize) -> Result<(), GameErr> {
239 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay) {
240 return Ok(());
241 }
242
243 for module in 1..8 {
244 for screen in 0..3 {
245 self.broadcast_part(module, screen, delay)?;
246 }
247 }
248 self.last_update_time = comm::get_time() as usize;
249
250 Ok(())
251 }
252
253 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), GameErr> {
254 match pkt[0] {
255 v if [GamePkt::SetMap as u8, GamePkt::SetNone as u8].contains(&v) => {
256 let (val, module, screen) = Option::<T>::from_pkt(pkt);
257
258 if module != topology::get_cuben() as u8 || screen >= 3 {
259 return Ok(());
260 }
261
262 self.screen_map[screen as usize] = val;
263
264 if !self.screen_map.iter().any(|e| e.is_none()) {
265 self.inited = true;
266 }
267 }
268 _ => ()
269 }
270 Ok(())
271 }
272}
273
274#[allow(dead_code)]
275impl<T: Packable> Mappable for MapBase<T> {}
276
277#[allow(dead_code)]
278impl<T: Packable + PartialEq + Copy> Shuffable for MapBase<T> {
279 fn rotate(&mut self, dir: &RotationDir) -> Result<(), GameErr> {
280 if topology::get_cuben() != 0 {
281 return Ok(());
282 }
283
284 let (rot_face, rot_offs) = match dir {
285 RotationDir::Forward => (2, 1),
286 RotationDir::Backward => (2, 3),
287 RotationDir::Right => (1, 1),
288 RotationDir::Left => (1, 3)
289 };
290
291 // get rotation face
292 let (face, pos) = self.get_place(0, rot_face, cubios::TopologyOrientation::Menu)?;
293 let left_face = topology::get_reversed_face(face as u32).ok_or(GameErr::InvalidPlace(face as u8, pos as u8))? as u8;
294
295 // save map
296 let mut tmp = Box::new([([None; 3], (0, 0)); 4]);
297
298 for i in 0..4 {
299 let (module, screen) = self.get_info(left_face, i, cubios::TopologyOrientation::Menu)?;
300 tmp[i as usize] = (self.master_map[module as usize], (module, screen));
301 }
302
303 // rotate
304 for i in 0..4 {
305 let offs = (i as usize + rot_offs) % 4;
306
307 let (module, screen) = self.get_info(left_face, i, cubios::TopologyOrientation::Menu)?;
308 let (_, right_screen) = self.get_info_dir(module, screen, cubios::TopologyNeighbour::Right)?;
309 let (_, bottom_screen) = self.get_info_dir(module, screen, cubios::TopologyNeighbour::Bottom)?;
310
311 let (next_module, next_screen) = tmp[offs].1;
312 let (_, next_right_screen) = self.get_info_dir(next_module, next_screen, cubios::TopologyNeighbour::Right)?;
313 let (_, next_bottom_screen) = self.get_info_dir(next_module, next_screen, cubios::TopologyNeighbour::Bottom)?;
314
315 self.master_map[module as usize][screen as usize] = tmp[offs].0[next_screen as usize];
316 self.master_map[module as usize][right_screen as usize] = tmp[offs].0[next_right_screen as usize];
317 self.master_map[module as usize][bottom_screen as usize] = tmp[offs].0[next_bottom_screen as usize];
318 }
319
320 Ok(())
321 }
322
323 fn shuffle(&mut self, algo: &[RotationDir]) -> Result<(), GameErr> {
324 if topology::get_cuben() != 0 {
325 return Ok(());
326 }
327
328 for mv in algo {
329 self.rotate(mv)?;
330 }
331 Ok(())
332 }
333}
334
335#[allow(dead_code)]
336impl<T: Packable + Copy> Default for MapBase<T> {
337 fn default() -> Self {
338 MapBase {
339 inited: false,
340 last_update_time: 0,
341 screen_map: [None; 3],
342 master_map: [[None; 3]; 8]
343 }
344 }
345}
346
347#[allow(dead_code)]
348impl<T: Packable> Packable for Option<T> {
349 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
350 if pkt[0] == GamePkt::SetNone as u8 {
351 return (None, pkt[1], pkt[2])
352 }
353
354 let (obj, module, screen) = T::from_pkt(pkt);
355 (Some(obj), module, screen)
356 }
357
358 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
359 match self {
360 Some(v) => v.to_pkt(module, screen),
361 None => [
362 GamePkt::SetNone as u8,
363 module,
364 screen,
365 0
366 ]
367 }
368 }
369}
370
371#[allow(dead_code)]
372impl<T: Default> Default for Anim<T> {
373 fn default() -> Self {
374 Anim {
375 val: Default::default(),
376 anim: 100,
377 step: 1,
378 cycle: false,
379 enable: true,
380 }
381 }
382}
383
384#[allow(dead_code)]
385impl Packable for u8 {
386 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
387 [
388 GamePkt::SetMap as u8,
389 module,
390 screen,
391 self.clone()
392 ]
393 }
394
395 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
396 (pkt[3], pkt[1], pkt[2])
397 }
398}
399
400#[allow(dead_code)]
401impl Packable for (u8, u8) {
402 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
403 [
404 GamePkt::SetMap as u8,
405 (module & 0xf) | (screen << 4),
406 self.0,
407 self.1
408 ]
409 }
410
411 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
412 let module = pkt[1] & 0xf;
413 let screen = (pkt[1] >> 4) & 0xf;
414
415 ((pkt[2], pkt[3]), module, screen)
416 }
417}
418
419#[allow(dead_code)]
420impl Broadcastable for SceneSwitch {
421 fn broadcast(&mut self, delay: usize) -> Result<(), GameErr> {
422 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay) {
423 return Ok(());
424 }
425
426 self.last_update_time = comm::get_time() as usize;
427
428 let pkt = [
429 GamePkt::SetScene as u8,
430 self.scene_id,
431 0,
432 0
433 ];
434
435 for i in 0..3 {
436 comm::send_message(i, pkt.as_ptr(), 4);
437 }
438 Ok(())
439 }
440
441 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), GameErr> {
442 match pkt[0] {
443 v if v == GamePkt::SetScene as u8 => self.set_scene(pkt[1]),
444 _ => Ok(())
445 }
446 }
447}
448
449#[allow(dead_code)]
450impl SceneSwitch {
451 pub fn new(scenes: &'static [usize], start_time: usize) -> Self {
452 Self {
453 last_update_time: 0,
454
455 scene_id: 0,
456 scenes,
457 last_switch_time: start_time,
458 }
459 }
460
461 pub fn next_scene(&mut self) {
462 if topology::get_cuben() != 0 {
463 return;
464 }
465
466 self.last_switch_time = comm::get_time() as usize;
467 self.scene_id = (self.scene_id + 1) % self.scenes.len() as u8;
468 }
469
470 pub fn set_scene(&mut self, id: u8) -> Result<(), GameErr> {
471 if id >= self.scenes.len() as u8 {
472 return Err(GameErr::SceneNotFound(id))
473 }
474
475 if id == self.scene_id {
476 return Ok(())
477 }
478
479 self.last_switch_time = comm::get_time() as usize;
480 self.scene_id = id;
481
482 Ok(())
483 }
484}
485
486#[allow(dead_code)]
487impl<T> Anim<T> {
488 pub fn animate(&mut self) {
489 if !self.enable {
490 return;
491 }
492
493 if self.anim <= 100 {
494 self.anim += self.step;
495 }
496
497 self.anim = self.anim.min(100);
498
499 if self.cycle {
500 self.anim %= 100;
501 }
502 }
503}
504
505
506// entry
507pub fn game_init<T: Game>(game: &mut T, cid: u32) {
508 cubios::on_init(cid);
509
510 while let Err(_e) = game.on_init() {
511 comm::log_e("on_init: failed to initialize game!");
512 }
513}
514
515pub fn game_run<T: Game>(game: &mut T) {
516 let event_list = cubios::event::get_list();
517
518 // events
519 if (cubios::event::Event::ON_CLOSE as i32 & event_list) != 0 {
520 comm::log_e("exit\n");
521 return;
522 }
523
524 if (cubios::event::Event::ON_NET_UART0_RX as i32 & event_list) != 0 {
525 if let Err(_e) = game.on_rx(0) {
526 // log_e(format!("on_rx:1: {:?}", e).as_str()); // issue with WASM compilation
527 comm::log_e("on_rx:0: failed to handle!");
528 }
529 }
530
531 if (cubios::event::Event::ON_NET_UART1_RX as i32 & event_list) != 0 {
532 if let Err(_e) = game.on_rx(1) {
533 // comm::log_e(format!("on_rx:1: {:?}", e).as_str()); // issue with WASM compilation
534 comm::log_e("on_rx:1: failed to handle!");
535 }
536 }
537
538 if (cubios::event::Event::ON_NET_UART2_RX as i32 & event_list) != 0 {
539 if let Err(_e) = game.on_rx(2) {
540 // comm::log_e(format!("on_rx:2: {:?}", e).as_str()); // issue with WASM compilation
541 comm::log_e("on_rx:2: failed to handle!");
542 }
543 }
544
545 if (cubios::event::Event::ON_PHYSICAL_TICK as i32 & event_list) != 0 {
546 if let Err(_e) = game.on_phys_tick() {
547 // comm::log_e(format!("on_phys_tick: {:?}", e).as_str()); // issue with WASM compilation
548 comm::log_e("on_phys_tick: failed to handle!");
549 }
550 }
551
552 if (cubios::event::Event::ON_TWIST as i32 & event_list) != 0 {
553 if let Err(_e) = game.on_twist() {
554 // comm::log_e(format!("on_twist: {:?}", e).as_str()); // issue with WASM compilation
555 comm::log_e("on_twist: failed to handle!");
556 }
557 }
558
559 if (cubios::event::Event::ON_TAP as i32 & event_list) != 0 {
560 if let Err(_e) = game.on_tap() {
561 // comm::log_e(format!("on_tap: {:?}", e).as_str()); // issue with WASM compilation
562 comm::log_e("on_tap: failed to handle!");
563 }
564 }
565
566 // mandatory events
567 if let Err(_e) = game.on_tick() {
568 // comm::log_e(format!("on_tick: {:?}", e).as_str()); // issue with WASM compilation
569 comm::log_e("on_tick: failed to handle!");
570 }
571
572 if let Err(_e) = game.on_render() {
573 // comm::log_e(format!("on_render: {:?}", e).as_str()); // issue with WASM compilation
574 comm::log_e("on_render: failed to handle!");
575 }
576}
577
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.
Context Rail

Related nodes

cubios.rs
Source / SDK 6.1 / Rust / Core
lib.rs
Source / SDK 6.1 / Rust / Core
game.rs
Source / SDK 6.2 / Rust / Core
AppManager.cpp
Source / SDK 6.1 / C++ / Core
Previous Node
cubios.rs
Source / SDK 6.1 / Rust / Core
Next Node
lib.rs
Source / SDK 6.1 / Rust / Core