Skip to main content

rpgx_dioxus/components/
engine.rs

1use std::any::Any;
2
3use dioxus::prelude::*;
4use rpgx::{
5    library::Library,
6    prelude::{Direction, RPGXError, Rect},
7};
8
9use crate::{
10    components::{grid::Grid, pawn::Pawn},
11    controller::{use_controller, Command},
12};
13
14#[derive(PartialEq, Props, Clone)]
15pub struct EngineProps {
16    pub engine: Signal<rpgx::prelude::Engine>,
17    pub library: Signal<Library<Box<dyn Any>>>,
18    pub square_size: u32,
19}
20
21#[allow(non_snake_case)]
22pub fn Engine(props: EngineProps) -> Element {
23    let engine = props.engine.clone();
24    let controller = use_controller(engine.clone(), props.library.clone());
25
26    let onclick = move |tile: Rect| -> Result<(), RPGXError> {
27        controller.send(Command::WalkTo(tile.origin));
28        Ok(())
29    };
30
31    let onkeydown = {
32        move |evt: KeyboardEvent| {
33            let direction = match evt.key() {
34                Key::ArrowUp => Some(Direction::Up),
35                Key::ArrowDown => Some(Direction::Down),
36                Key::ArrowLeft => Some(Direction::Left),
37                Key::ArrowRight => Some(Direction::Right),
38                Key::Character(k) => match k.as_str() {
39                    "w" | "W" => Some(Direction::Up),
40                    "s" | "S" => Some(Direction::Down),
41                    "a" | "A" => Some(Direction::Left),
42                    "d" | "D" => Some(Direction::Right),
43                    _ => None,
44                },
45                _ => None,
46            };
47
48            if let Some(d) = direction {
49                controller.send(Command::Step(d));
50            }
51        }
52    };
53
54    use_effect(move || {
55        let _ = engine(); // cause the effect to re-run when engine changes
56
57        let _js_code = r#"
58            (() => {
59                console.log('trigger update');
60                const container = document.querySelector('#scroll-container');
61                const pawn = document.querySelector('#pawn');
62                if (!container || !pawn) return;
63                const scrollX = pawn.offsetLeft + pawn.offsetWidth / 2 - container.clientWidth / 2;
64                const scrollY = pawn.offsetTop + pawn.offsetHeight / 2 - container.clientHeight / 2;
65                container.scrollTo({
66                    left: scrollX,
67                    top: scrollY,
68                    behavior: 'smooth'
69                });
70            })();
71        "#;
72
73        #[cfg(feature = "web")]
74        {
75            document::eval(_js_code); // desktop & web
76        }
77
78        #[cfg(feature = "desktop")]
79        {
80            spawn(async move {
81                let eval = document::eval(_js_code);
82                let _ = eval.await; // wait for execution
83            });
84        }
85    });
86
87    rsx! {
88        div {
89            id: "scroll-container",
90            class: "container",
91            tabindex: "0",
92            onkeydown,
93            style: "position: relative; overflow: auto; width: 100vw; height: 100vh;",
94
95            Grid {
96                engine: engine.clone(),
97                library: props.library.clone(),
98                square_size: props.square_size,
99                onclick: EventHandler::new(move |tile: Result<Rect, RPGXError>| {
100                    if let Ok(tile) = tile {
101                        let _ = onclick(tile);
102                    }
103                }),
104            }
105
106            Pawn {
107                engine: engine.clone(),
108                library: props.library.clone(),
109                square_size: props.square_size,
110            }
111        }
112    }
113}