use bevy::prelude::*;
use bevy::ui::{AlignItems, FlexDirection, JustifyContent, PositionType, Val};
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
pub enum UiFlexDirection {
#[default]
Row,
Column,
RowReverse,
ColumnReverse,
}
impl From<UiFlexDirection> for FlexDirection {
fn from(val: UiFlexDirection) -> Self {
match val {
UiFlexDirection::Row => FlexDirection::Row,
UiFlexDirection::Column => FlexDirection::Column,
UiFlexDirection::RowReverse => FlexDirection::RowReverse,
UiFlexDirection::ColumnReverse => FlexDirection::ColumnReverse,
}
}
}
#[derive(Asset, TypePath, Debug, Deserialize, Clone)]
pub struct UILayoutAsset {
#[allow(dead_code)]
pub version: u32,
pub roots: Vec<UINodeDef>,
#[serde(default)]
pub navigation: Option<HashMap<String, NavigationRuleDef>>,
#[serde(default)]
pub transitions: Option<HashMap<String, LayerTransitionsDef>>,
#[serde(default)]
pub global_triggers: Option<HashMap<String, Vec<GlobalTriggerRuleDef>>>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct GlobalTriggerRuleDef {
pub target_state: String,
#[serde(default)]
pub sound: Option<String>,
#[serde(default)]
pub allowed_states: Option<Vec<String>>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct UINodeDef {
pub name: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default)]
#[allow(dead_code)]
pub style: StyleDef,
#[serde(default)]
pub visibility_rule: Option<UIVisibilityRuleDef>,
#[serde(default)]
#[allow(dead_code)]
pub background_color: Option<SerializableColor>,
#[serde(default)]
#[allow(dead_code)]
pub border_color: Option<SerializableColor>,
#[serde(default)]
#[allow(dead_code)]
pub image: Option<ImageDef>,
#[serde(default)]
pub sprite: Option<SpriteDef>,
#[serde(default)]
pub texts: Vec<TextDef>,
#[serde(default)]
pub cursor: Option<CursorDef>,
#[serde(default)]
#[serde(alias = "ui_box_logic")]
pub ui_shape_logic: Option<UIBoxLogicDef>,
#[serde(default)]
#[allow(dead_code)]
pub children: Vec<UINodeDef>,
#[serde(default = "default_camera_anchored")]
pub camera_anchored: bool,
}
fn default_camera_anchored() -> bool {
true
}
#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct StyleDef {
#[serde(default)]
pub width: Option<SerializableVal>,
#[serde(default)]
pub height: Option<SerializableVal>,
#[serde(default)]
pub left: Option<SerializableVal>,
#[serde(default)]
pub right: Option<SerializableVal>,
#[serde(default)]
pub top: Option<SerializableVal>,
#[serde(default)]
pub bottom: Option<SerializableVal>,
#[serde(default)]
pub position_type: Option<SerializablePositionType>,
#[serde(default)]
pub flex_direction: Option<UiFlexDirection>,
#[serde(default)]
pub justify_content: Option<SerializableJustifyContent>,
#[serde(default)]
pub align_items: Option<SerializableAlignItems>,
}
#[derive(Debug, Deserialize, Clone)]
pub enum SerializableVal {
Auto,
Px(f32),
Percent(f32),
Vw(f32),
Vh(f32),
}
impl From<SerializableVal> for Val {
fn from(val: SerializableVal) -> Self {
match val {
SerializableVal::Auto => Val::Auto,
SerializableVal::Px(v) => Val::Px(v),
SerializableVal::Percent(v) => Val::Percent(v),
SerializableVal::Vw(v) => Val::Vw(v),
SerializableVal::Vh(v) => Val::Vh(v),
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub enum SerializablePositionType {
Relative,
Absolute,
}
impl From<SerializablePositionType> for PositionType {
fn from(val: SerializablePositionType) -> Self {
match val {
SerializablePositionType::Relative => PositionType::Relative,
SerializablePositionType::Absolute => PositionType::Absolute,
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub enum SerializableJustifyContent {
Start,
End,
Center,
SpaceBetween,
SpaceAround,
SpaceEvenly,
}
impl From<SerializableJustifyContent> for JustifyContent {
fn from(val: SerializableJustifyContent) -> Self {
match val {
SerializableJustifyContent::Start => JustifyContent::Start,
SerializableJustifyContent::End => JustifyContent::End,
SerializableJustifyContent::Center => JustifyContent::Center,
SerializableJustifyContent::SpaceBetween => JustifyContent::SpaceBetween,
SerializableJustifyContent::SpaceAround => JustifyContent::SpaceAround,
SerializableJustifyContent::SpaceEvenly => JustifyContent::SpaceEvenly,
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub enum SerializableAlignItems {
Start,
End,
Center,
Baseline,
Stretch,
}
impl From<SerializableAlignItems> for AlignItems {
fn from(val: SerializableAlignItems) -> Self {
match val {
SerializableAlignItems::Start => AlignItems::Start,
SerializableAlignItems::End => AlignItems::End,
SerializableAlignItems::Center => AlignItems::Center,
SerializableAlignItems::Baseline => AlignItems::Baseline,
SerializableAlignItems::Stretch => AlignItems::Stretch,
}
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct SerializableColor {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl From<SerializableColor> for Color {
fn from(val: SerializableColor) -> Self {
Color::srgba(val.r, val.g, val.b, val.a)
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct DynamicColor {
pub r: FloatOrExpr,
pub g: FloatOrExpr,
pub b: FloatOrExpr,
pub a: FloatOrExpr,
}
impl DynamicColor {
pub fn to_static_color(&self) -> Color {
Color::srgba(
self.r.as_float(),
self.g.as_float(),
self.b.as_float(),
self.a.as_float(),
)
}
pub fn is_dynamic(&self) -> bool {
self.r.is_dynamic() || self.g.is_dynamic() || self.b.is_dynamic() || self.a.is_dynamic()
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct UIVisibilityRuleDef {
pub rule_type: String,
#[serde(default)]
pub layers: Option<Vec<String>>,
}
#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
pub struct ImageDef {
pub path: String,
#[serde(default)]
pub color: Option<SerializableColor>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct SpriteDef {
pub path: String,
#[serde(default)]
pub is_animation: bool,
#[serde(default)]
pub initial_state: Option<String>,
#[serde(default)]
pub color: Option<SerializableColor>,
#[serde(default)]
pub flip_x: bool,
#[serde(default)]
pub flip_y: bool,
#[serde(default)]
pub transform: Option<SerializableTransform>,
#[serde(default)]
pub custom_shader: Option<String>,
#[serde(default)]
pub shader_params: Option<DynamicColor>,
#[serde(default)]
pub pivot: Option<SerializableVec2>,
}
#[derive(Debug, Deserialize, Clone)]
pub enum UIFontDef {
DeterminationMono,
DeterminationSans,
Hud,
BattleHud,
}
impl From<UIFontDef> for super::components::UIFont {
fn from(val: UIFontDef) -> Self {
match val {
UIFontDef::DeterminationMono => super::components::UIFont::DeterminationMono,
UIFontDef::DeterminationSans => super::components::UIFont::DeterminationSans,
UIFontDef::Hud => super::components::UIFont::Hud,
UIFontDef::BattleHud => super::components::UIFont::BattleHud,
}
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
pub enum FloatOrExpr {
Static(f32),
Dynamic(String),
}
impl FloatOrExpr {
pub fn as_float(&self) -> f32 {
match self {
FloatOrExpr::Static(v) => *v,
FloatOrExpr::Dynamic(_) => 0.0,
}
}
pub fn as_expr(&self) -> Option<&String> {
match self {
FloatOrExpr::Dynamic(s) => Some(s),
_ => None,
}
}
pub fn is_dynamic(&self) -> bool {
matches!(self, FloatOrExpr::Dynamic(_))
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct SerializableVec3 {
pub x: FloatOrExpr,
pub y: FloatOrExpr,
pub z: FloatOrExpr,
}
impl SerializableVec3 {
pub fn to_static_vec3(&self) -> Vec3 {
Vec3::new(self.x.as_float(), self.y.as_float(), self.z.as_float())
}
pub fn is_dynamic(&self) -> bool {
self.x.is_dynamic() || self.y.is_dynamic() || self.z.is_dynamic()
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct SerializableVec2 {
pub x: f32,
pub y: f32,
}
impl From<SerializableVec2> for Vec2 {
fn from(val: SerializableVec2) -> Self {
Vec2::new(val.x, val.y)
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct SerializableTransform {
pub translation: SerializableVec3,
#[serde(default)]
pub rotation: Option<f32>,
#[serde(default)]
pub scale: Option<SerializableVec3>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct TextDef {
pub id: String,
#[serde(default)]
pub content: Option<String>,
pub font: UIFontDef,
pub world_scale: SerializableVec2,
pub color: SerializableColor,
pub transform: SerializableTransform,
#[serde(default)]
pub line_height: Option<f32>,
#[serde(default)]
pub conditional_style: Option<ConditionalStyleDef>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct ConditionalStyleDef {
pub condition: String,
pub color: SerializableColor,
}
#[derive(Debug, Deserialize, Clone)]
pub struct CursorDef {
#[allow(dead_code)]
pub sprite_path: String,
#[serde(default)]
pub default_translation: Option<BoxCursorPositionDef>,
#[serde(default)]
pub overrides: HashMap<String, BoxCursorPositionDef>,
#[serde(default)]
pub visibility_rule: Option<UIVisibilityRuleDef>,
#[serde(default)]
pub transform: Option<CursorTransformDef>,
}
#[derive(Debug, Deserialize, Clone)]
pub enum BoxCursorPositionDef {
Static(SerializableVec3),
Linear {
origin: SerializableVec3,
step: SerializableVec3,
},
Custom {
positions: Vec<SerializableVec3>,
},
}
#[derive(Debug, Deserialize, Clone)]
pub struct CursorTransformDef {
#[serde(default)]
pub translation: Option<SerializableVec3>,
#[serde(default)]
pub scale: Option<SerializableVec3>,
#[serde(default)]
pub rotation: Option<f32>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct UIBoxLogicDef {
pub width: f32,
pub height: f32,
#[serde(default)]
pub border_width: f32,
pub offset: SerializableVec3,
#[serde(default)]
pub fill_shader: Option<String>,
#[serde(default)]
pub structure_file: Option<String>,
#[serde(default)]
pub fill_color: Option<SerializableColor>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct NavigationRuleDef {
#[serde(default)]
pub mappings: HashMap<String, isize>,
#[serde(default)]
pub looping: bool,
#[serde(default)]
pub min_index: Option<IndexBoundDef>,
#[serde(default)]
pub max_index: Option<IndexBoundDef>,
#[serde(default)]
pub sound_on_navigate: Option<String>,
}
#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
pub enum IndexBoundDef {
Static(usize),
Dynamic(String),
}
#[derive(Debug, Deserialize, Clone)]
pub struct LayerTransitionsDef {
#[serde(default)]
pub on_confirm: Option<Vec<TransitionRuleDef>>,
#[serde(default)]
pub on_cancel: Option<TransitionActionDef>,
#[serde(default)]
pub sound_on_confirm: Option<String>,
#[serde(default)]
pub sound_on_cancel: Option<String>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct TransitionRuleDef {
#[serde(default)]
pub condition: Option<String>,
pub action: TransitionActionDef,
}
#[derive(Debug, Deserialize, Clone)]
pub enum TransitionActionDef {
GotoLayer(String),
PopState,
PushState(String),
}
#[derive(Asset, TypePath, Debug, Deserialize, Clone)]
pub struct SmudStructureAsset {
pub layer_count: usize,
pub root: SmudLayerDef,
}
#[derive(Debug, Deserialize, Clone)]
pub struct SmudLayerDef {
pub name: String,
pub sdf_type: SmudSdfType,
#[serde(default)]
pub color_source: SmudColorSource,
#[serde(default = "default_z_offset")]
pub z_offset: f32,
#[serde(default)]
pub is_filler: bool,
#[serde(default)]
pub children: Vec<SmudLayerDef>,
}
fn default_z_offset() -> f32 {
0.1
}
#[derive(Debug, Deserialize, Clone)]
pub enum SmudSdfType {
Outer,
Inner,
}
#[derive(Debug, Deserialize, Clone, Default)]
pub enum SmudColorSource {
#[default]
FillColor,
White,
Custom(SerializableColor),
}