use super::*;
use crate::ValueExt;
pub(crate) fn plugin(app: &mut App) {
#[cfg(all(feature = "scripting", feature = "level"))]
lua::plugin(app);
}
#[derive(Debug, Clone, Reflect)]
pub enum PropBy {
Pos(Vec2),
Rect(Rect),
Name(Cow<'static, str>),
}
impl From<Vec2> for PropBy {
fn from(v: Vec2) -> Self {
PropBy::Pos(v)
}
}
impl From<String> for PropBy {
fn from(v: String) -> Self {
PropBy::Name(v.into())
}
}
impl super::Pico8<'_, '_> {
#[cfg(feature = "level")]
pub fn props(&self, id: Entity) -> Result<tiled::Properties, Error> {
self.tiled.props(id)
}
#[cfg(feature = "level")]
pub fn mgetp(
&self,
prop_by: PropBy,
map_index: Option<usize>,
layer_index: Option<usize>,
) -> Option<tiled::Properties> {
let asset = self.pico8_asset().ok()?;
let index = map_index.unwrap_or(0);
let map = asset.maps.get(index)?;
match map {
SpriteMap::P8(_) => None,
#[cfg(feature = "level")]
SpriteMap::Level(map) => self.tiled.mgetp(map, prop_by, map_index, layer_index),
#[cfg(not(feature = "level"))]
_ => None,
}
}
}
#[cfg(all(feature = "scripting", feature = "level"))]
mod lua {
use super::*;
use crate::pico8::lua::with_pico8;
use bevy_mod_scripting::bindings::{
InteropError,
function::{
from::FromScript,
namespace::{GlobalNamespace, NamespaceBuilder},
script_function::FunctionCallContext,
},
script_value::ScriptValue,
};
impl FromScript for PropBy {
type This<'w> = Self;
fn from_script(
value: ScriptValue,
_world: bevy_mod_scripting::bindings::WorldAccessGuard<'_>,
) -> Result<Self::This<'_>, InteropError> {
match value {
ScriptValue::String(n) => Ok(PropBy::Name(n)),
ScriptValue::List(l) => {
let x = l.first().and_then(ValueExt::to_f32).unwrap_or(0.0);
let y = l.get(1).and_then(ValueExt::to_f32).unwrap_or(0.0);
Ok(PropBy::Pos(Vec2::new(x, y)))
}
ScriptValue::Map(v) => {
let x = v.get("x").and_then(ValueExt::to_f32).unwrap_or(0.0);
let y = v.get("y").and_then(ValueExt::to_f32).unwrap_or(0.0);
let w = v.get("width").and_then(ValueExt::to_f32);
let h = v.get("height").and_then(ValueExt::to_f32);
match (w, h) {
(Some(w), Some(h)) => Ok(PropBy::Rect(Rect::from_corners(
Vec2::new(x, y),
Vec2::new(x + w, y + h),
))),
_ => Ok(PropBy::Pos(Vec2::new(x, y))),
}
}
_ => Err(InteropError::value_mismatch(
std::any::TypeId::of::<PropBy>(),
value,
)),
}
}
}
pub(crate) fn plugin(app: &mut App) {
let world = app.world_mut();
NamespaceBuilder::<GlobalNamespace>::new_unregistered(world)
.register(
"mgetp",
|ctx: FunctionCallContext,
prop_by: ScriptValue,
map_index: Option<usize>,
layer_index: Option<usize>| {
let prop_by = PropBy::from_script(prop_by, ctx.world()?)?;
with_pico8(&ctx, move |pico8| {
Ok(pico8
.mgetp(prop_by, map_index, layer_index)
.map(|p| from_properties(&p)))
})
},
)
.register("props", |ctx: FunctionCallContext, id: i64| {
let id = Entity::from_bits(id as u64);
with_pico8(&ctx, move |pico8| {
pico8.props(id).map(|p| from_properties(&p))
})
});
}
fn from_properties(properties: &tiled::Properties) -> ScriptValue {
use bevy::platform::collections::HashMap as BevyHashMap;
let map: BevyHashMap<String, ScriptValue> = properties
.iter()
.flat_map(|(name, value)| from_property(value).map(|v| (name.to_owned(), v)))
.collect();
ScriptValue::Map(map)
}
fn from_property(v: &tiled::PropertyValue) -> Option<ScriptValue> {
use tiled::PropertyValue;
match v {
PropertyValue::BoolValue(v) => Some(ScriptValue::Bool(*v)),
PropertyValue::FloatValue(f) => Some(ScriptValue::Float(*f as f64)),
PropertyValue::IntValue(i) => Some(ScriptValue::Integer(*i as i64)),
PropertyValue::ColorValue(_color) => None,
PropertyValue::StringValue(s) => Some(ScriptValue::String(s.to_owned().into())),
PropertyValue::FileValue(f) => Some(ScriptValue::String(f.to_owned().into())),
PropertyValue::ObjectValue(_number) => None,
PropertyValue::ClassValue {
property_type,
properties,
} => Some(ScriptValue::Map(
[(property_type.to_owned(), from_properties(properties))]
.into_iter()
.collect(),
)),
}
}
}