|
|
|
@ -1,6 +1,16 @@
|
|
|
|
|
mod utils;
|
|
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
|
use js_sys::Function;
|
|
|
|
|
|
|
|
|
|
use stackline::pane::Pane as SLPane;
|
|
|
|
|
use stackline::signal::Signal as SLSignal;
|
|
|
|
|
use stackline::signal::Value;
|
|
|
|
|
use stackline::tile::AnyTile;
|
|
|
|
|
use stackline::tile::FullTile as SLFullTile;
|
|
|
|
|
use stackline::utils::Direction;
|
|
|
|
|
use stackline::world::World as SLWorld;
|
|
|
|
|
|
|
|
|
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
|
|
|
|
// allocator.
|
|
|
|
@ -14,6 +24,247 @@ extern "C" {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub fn greet() {
|
|
|
|
|
alert("Hello, stackline2-wasm!");
|
|
|
|
|
pub struct World(SLWorld);
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(start)]
|
|
|
|
|
pub fn set_panic() {
|
|
|
|
|
utils::set_panic_hook();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub fn available_tiles() -> Vec<JsValue> {
|
|
|
|
|
AnyTile::available().iter().map(|name| {
|
|
|
|
|
JsValue::from_str(name)
|
|
|
|
|
}).collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
impl World {
|
|
|
|
|
/// Creates a new World instance
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self(SLWorld::new())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Initializes the World, making it ready to run
|
|
|
|
|
pub fn init(&mut self) {
|
|
|
|
|
self.0.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_blink_duration(&mut self, blink_duration: f64) {
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
self.0
|
|
|
|
|
.set_blink_duration(Duration::from_secs_f64(blink_duration));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
pub fn toString(&self) -> String {
|
|
|
|
|
format!("{:#}", self.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// NOTE: We have to [`Clone`] the FullTile
|
|
|
|
|
pub fn get(&self, x: i32, y: i32) -> Option<FullTile> {
|
|
|
|
|
self.0.get((x, y)).map(|tile| FullTile((*tile).clone()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set(&mut self, x: i32, y: i32, tile: &FullTile) {
|
|
|
|
|
if let Some(tile_ref) = self.0.get_mut((x, y)) {
|
|
|
|
|
*tile_ref = tile.0.clone();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_pane(&self, name: String) -> Option<Pane> {
|
|
|
|
|
self.0.get_pane(&name).map(|pane| Pane(pane.clone()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_pane(&mut self, name: String, pane: Pane) {
|
|
|
|
|
self.0.set_pane(name, pane.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub struct Pane(SLPane);
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
impl Pane {
|
|
|
|
|
pub fn empty(width: usize, height: usize) -> Option<Pane> {
|
|
|
|
|
SLPane::empty(width, height).map(|pane| Self(pane))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
pub fn toString(&self) -> String {
|
|
|
|
|
format!("{:#?}", self.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn width(&self) -> usize {
|
|
|
|
|
self.0.width().get()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn height(&self) -> usize {
|
|
|
|
|
self.0.height().get()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn position(&self) -> Vec<i32> {
|
|
|
|
|
let (x, y) = self.0.position();
|
|
|
|
|
vec![x, y]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(setter)]
|
|
|
|
|
pub fn set_position(&mut self, position: &[i32]) {
|
|
|
|
|
if let [x, y] = position[..] {
|
|
|
|
|
self.0.set_position((x, y));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub struct FullTile(SLFullTile);
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
impl FullTile {
|
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
|
FullTile(SLFullTile::new(None))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(constructor)]
|
|
|
|
|
pub fn new(name: &str) -> Option<FullTile> {
|
|
|
|
|
AnyTile::new(name).map(|tile| FullTile(SLFullTile::new(Some(tile))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
pub fn toString(&self) -> String {
|
|
|
|
|
format!("{:#?}", self.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn signal(&self) -> Option<Signal> {
|
|
|
|
|
self.0.signal().map(|signal| Signal(signal.clone()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(setter)]
|
|
|
|
|
pub fn set_signal(&mut self, signal: Option<Signal>) {
|
|
|
|
|
self.0.set_signal(signal.map(|s| s.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn tile(&self) -> JsValue {
|
|
|
|
|
self.0.get().map(|tile| {
|
|
|
|
|
JsValue::from_serde(tile).map_err(|err| {
|
|
|
|
|
err!("Error while serializing AnyTile: {}", err);
|
|
|
|
|
}).ok()
|
|
|
|
|
}).flatten().unwrap_or(JsValue::UNDEFINED)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(setter)]
|
|
|
|
|
pub fn set_tile(&mut self, tile: JsValue) {
|
|
|
|
|
if tile.is_null() || tile.is_undefined() {
|
|
|
|
|
self.0.set(None);
|
|
|
|
|
} else {
|
|
|
|
|
match tile.into_serde::<AnyTile>() {
|
|
|
|
|
Ok(tile) => self.0.set(Some(tile)),
|
|
|
|
|
Err(err) => err!("Error while deserializing AnyTile: {}", err),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn map_tile(&mut self, callback: &Function) {
|
|
|
|
|
let res = callback.call1(&JsValue::NULL, &self.tile()).expect("Error while calling javascript callback");
|
|
|
|
|
self.set_tile(res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
|
|
|
#[serde(untagged)]
|
|
|
|
|
enum UntaggedValue {
|
|
|
|
|
Number(f64),
|
|
|
|
|
String(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<&'_ Value> for UntaggedValue {
|
|
|
|
|
fn from(value: &'_ Value) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
Value::Number(x) => Self::Number(*x),
|
|
|
|
|
Value::String(x) => Self::String(x.clone()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Value> for UntaggedValue {
|
|
|
|
|
fn from(value: Value) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
Value::Number(x) => Self::Number(x),
|
|
|
|
|
Value::String(x) => Self::String(x),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<UntaggedValue> for Value {
|
|
|
|
|
fn from(value: UntaggedValue) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
UntaggedValue::Number(x) => Self::Number(x),
|
|
|
|
|
UntaggedValue::String(x) => Self::String(x),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub struct Signal(SLSignal);
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
impl Signal {
|
|
|
|
|
#[wasm_bindgen(constructor)]
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self(SLSignal::empty(Direction::Up))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a read-only array
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn stack(&self) -> JsValue {
|
|
|
|
|
JsValue::from_serde(
|
|
|
|
|
&self
|
|
|
|
|
.0
|
|
|
|
|
.stack()
|
|
|
|
|
.iter()
|
|
|
|
|
.map(<UntaggedValue as From<&'_ Value>>::from)
|
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(getter)]
|
|
|
|
|
pub fn direction(&self) -> u8 {
|
|
|
|
|
match self.0.direction() {
|
|
|
|
|
Direction::Up => 0,
|
|
|
|
|
Direction::Right => 1,
|
|
|
|
|
Direction::Down => 2,
|
|
|
|
|
Direction::Left => 3,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[wasm_bindgen(setter)]
|
|
|
|
|
pub fn set_direction(&mut self, direction: u8) {
|
|
|
|
|
let direction = match direction {
|
|
|
|
|
0 => Direction::Up,
|
|
|
|
|
1 => Direction::Right,
|
|
|
|
|
2 => Direction::Down,
|
|
|
|
|
3 => Direction::Left,
|
|
|
|
|
_ => return,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.0.set_direction(direction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn push(&mut self, value: JsValue) {
|
|
|
|
|
if let Some(num) = value.as_f64() {
|
|
|
|
|
self.0.push(Value::Number(num));
|
|
|
|
|
} else if let Some(string) = value.as_string() {
|
|
|
|
|
self.0.push(Value::String(string));
|
|
|
|
|
} else {
|
|
|
|
|
panic!("Invalid value: expected number or string, got {:?}", value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|