use std::sync::Arc;
use super::sprite as s;
use crate::anim::{Clip, Cond, Graph, GraphSpec, Name, Playback, StateSpec, TransitionSpec};
const STILL: f32 = 10.0;
const SPRINT: f32 = 110.0;
const APEX: f32 = 40.0;
const LOOK_DELAY: f32 = 0.15;
const FIDGET_AFTER: f32 = 5.0;
fn is(p: &'static str) -> Cond<Name> {
Cond::Is(p.into())
}
fn not(p: &'static str) -> Cond<Name> {
Cond::not(is(p))
}
fn trig(p: &'static str) -> Cond<Name> {
Cond::Trigger(p.into())
}
fn above(p: &'static str, v: f32) -> Cond<Name> {
Cond::Above(p.into(), v)
}
fn below(p: &'static str, v: f32) -> Cond<Name> {
Cond::Below(p.into(), v)
}
fn looping(sprites: &[u16], secs: f32) -> Clip {
Clip::uniform(Playback::Loop, sprites, secs)
}
fn once(frames: &[(u16, f32)]) -> Clip {
Clip::new(Playback::Once, frames)
}
fn once_each(sprites: &[u16], secs: f32) -> Clip {
Clip::uniform(Playback::Once, sprites, secs)
}
const AIR: [&str; 9] = [
"takeoff",
"rise",
"apex",
"fall",
"double",
"wings",
"wall_jump",
"hop",
"drop",
];
const STANDING: [&str; 4] = ["idle", "idle_fidget", "look_up", "look_down"];
const GROUND: [&str; 11] = [
"idle",
"sprint",
"idle_fidget",
"look_up",
"look_down",
"run",
"skid",
"land",
"push",
"pull",
"box_hold",
];
const BLOCK: [&str; 3] = ["push", "pull", "box_hold"];
const DASHES: [&str; 3] = ["dash_side", "dash_up", "dash_down"];
pub fn spec() -> GraphSpec {
GraphSpec::new()
.bool("grounded")
.bool("skid")
.bool("rolling")
.bool("dashing")
.bool("climbing")
.bool("slipping")
.bool("wall_slide")
.bool("hard_landing")
.bool("wings")
.bool("wall_push")
.bool("pushing")
.bool("pulling")
.bool("holding")
.bool("gliding")
.bool("charging")
.bool("charged")
.bool("super_flying")
.float("speed")
.float("vy")
.float("dash_y")
.float("look")
.trigger("jump", 0.05)
.trigger("double", 0.05)
.trigger("wall_jump", 0.05)
.trigger("hop", 0.05)
.trigger("drop", 0.05)
.trigger("land", 0.0)
.trigger("roll", 0.0)
.trigger("hard", 0.0)
.trigger("dash", 0.05)
.trigger("grab", 0.05)
.trigger("dive", 0.05)
.trigger("impact", 0.0)
.trigger("super_launch", 0.0)
.trigger("super_bonk", 0.0)
.state(StateSpec::new(
"idle",
Clip::new(Playback::PingPong, &[(s::IDLE_A, 0.6), (s::IDLE_B, 0.4)]),
))
.state(
StateSpec::new(
"idle_fidget",
once(&[
(s::FIDGET[0], 0.2),
(s::FIDGET[1], 0.6),
(s::FIDGET[2], 0.7),
(s::FIDGET[3], 0.25),
]),
)
.then("idle"),
)
.state(StateSpec::new(
"look_up",
once(&[(s::LOOK_UP_A, 0.08), (s::LOOK_UP_B, 0.1)]),
))
.state(StateSpec::new(
"look_down",
once(&[(s::LOOK_DOWN_A, 0.08), (s::LOOK_DOWN_B, 0.1)]),
))
.state(
StateSpec::new(
"run",
looping(&[s::RUN_A, s::RUN_B, s::RUN_C, s::RUN_D], 0.08)
.marker(0, "step")
.marker(2, "step"),
)
.rate_by("speed", 90.0, 0.4, 1.6),
)
.state(
StateSpec::new(
"sprint",
looping(&s::SPRINT, 0.07)
.marker(0, "step")
.marker(2, "step"),
)
.rate_by("speed", 140.0, 0.6, 1.4),
)
.state(StateSpec::new("skid", looping(&[s::SKID], 0.1)))
.state(StateSpec::new("land", once(&[(s::LAND_A, 0.05), (s::LAND_B, 0.05)])).then("idle"))
.state(
StateSpec::new("hard_land", once(&[(s::HARD_A, 0.1), (s::HARD_B, 0.2)]))
.lock(0.2)
.then("idle"),
)
.state(StateSpec::new("roll", once_each(&s::ROLL, 0.08)))
.state(
StateSpec::new(
"push",
looping(&s::PUSH, 0.14).marker(0, "step").marker(2, "step"),
)
.rate_by("speed", 40.0, 0.35, 1.5),
)
.state(
StateSpec::new(
"pull",
looping(&s::PULL, 0.16).marker(0, "step").marker(2, "step"),
)
.rate_by("speed", 30.0, 0.35, 1.5),
)
.state(StateSpec::new("box_hold", looping(&[s::BOX_HOLD], 0.1)))
.state(StateSpec::new("takeoff", once(&[(s::TAKEOFF, 0.06)])).then("rise"))
.state(StateSpec::new("rise", looping(&[s::RISE], 0.1)))
.state(StateSpec::new("apex", looping(&[s::APEX], 0.1)))
.state(StateSpec::new(
"fall",
looping(&[s::FALL_A, s::FALL_B], 0.1),
))
.state(
StateSpec::new("double", once_each(&s::FLIP, 0.05))
.lock(0.1)
.then("rise"),
)
.state(
StateSpec::new(
"wings",
once(&[
(s::WINGS[0], 0.05),
(s::WINGS[1], 0.08),
(s::WINGS[2], 0.12),
(s::WINGS[3], 0.06),
]),
)
.lock(0.13)
.then("rise"),
)
.state(StateSpec::new("wall_jump", once(&[(s::WALL_JUMP, 0.1)])).then("rise"))
.state(StateSpec::new("hop", once(&[(s::HOP, 0.12)])).then("rise"))
.state(StateSpec::new("drop", once(&[(s::DROP, 0.1)])).then("fall"))
.state(StateSpec::new("glide", looping(&s::GLIDE, 0.25)))
.state(StateSpec::new("dash_side", looping(&s::DASH_SIDE, 0.04)))
.state(StateSpec::new("dash_up", looping(&s::DASH_UP, 0.04)))
.state(StateSpec::new("dash_down", looping(&s::DASH_DOWN, 0.04)))
.state(StateSpec::new("climb", looping(&s::CLIMB, 0.12)).rate_by("vy", 45.0, 0.0, 2.0))
.state(StateSpec::new("grip_slip", looping(&s::WALL_SLIDE, 0.12)))
.state(StateSpec::new("wall_slide", looping(&s::WALL_SLIDE, 0.08)))
.state(StateSpec::new("super_charge", looping(&s::CHARGE, 0.1)))
.state(StateSpec::new("super_ready", looping(&s::CHARGED, 0.05)))
.state(StateSpec::new("super_dash", looping(&s::SUPER, 0.03)))
.state(StateSpec::new("super_bonk", once(&[(s::SUPER_BONK, 0.25)])).then("fall"))
.state(StateSpec::new("dive_windup", once(&[(s::DIVE_WINDUP, 0.08)])).then("dive"))
.state(StateSpec::new("dive", looping(&s::DIVE, 0.04)))
.state(
StateSpec::new(
"dive_land",
once(&[(s::DIVE_LAND_A, 0.06), (s::DIVE_LAND_B, 0.14)]),
)
.lock(0.2)
.then("idle"),
)
.transition(
TransitionSpec::any("super_bonk", trig("super_bonk"))
.priority(99)
.breaks_lock(),
)
.transition(
TransitionSpec::any("super_dash", trig("super_launch"))
.priority(98)
.breaks_lock(),
)
.transition(
TransitionSpec::any("super_charge", is("charging").and(not("charged")))
.except(["super_charge", "super_ready"])
.priority(92)
.breaks_lock(),
)
.transition(
TransitionSpec::new(["super_charge"], "super_ready", is("charged")).priority(93),
)
.transition(
TransitionSpec::any("dive_land", trig("impact"))
.priority(100)
.breaks_lock(),
)
.transition(
TransitionSpec::any("dive_windup", trig("dive"))
.priority(95)
.breaks_lock(),
)
.transition(
TransitionSpec::any("roll", trig("roll"))
.priority(90)
.breaks_lock(),
)
.transition(
TransitionSpec::any("hard_land", trig("hard"))
.priority(90)
.breaks_lock(),
)
.transition(
TransitionSpec::any("dash_up", trig("dash").and(below("dash_y", -0.3)))
.priority(85)
.breaks_lock()
.restart(),
)
.transition(
TransitionSpec::any("dash_down", trig("dash").and(above("dash_y", 0.3)))
.priority(85)
.breaks_lock()
.restart(),
)
.transition(
TransitionSpec::any("dash_side", trig("dash"))
.priority(84)
.breaks_lock()
.restart(),
)
.transition(
TransitionSpec::any("wings", trig("double").and(is("wings")))
.priority(81)
.breaks_lock()
.restart(),
)
.transition(
TransitionSpec::any("double", trig("double"))
.priority(80)
.breaks_lock()
.restart(),
)
.transition(
TransitionSpec::any("wall_jump", trig("wall_jump"))
.priority(80)
.breaks_lock(),
)
.transition(
TransitionSpec::any("climb", trig("grab"))
.priority(75)
.breaks_lock(),
)
.transition(
TransitionSpec::any("takeoff", trig("jump"))
.priority(70)
.breaks_lock(),
)
.transition(
TransitionSpec::any("hop", trig("hop"))
.priority(70)
.breaks_lock(),
)
.transition(
TransitionSpec::any("drop", trig("drop"))
.priority(70)
.breaks_lock(),
)
.transition(
TransitionSpec::any("land", trig("land"))
.except(["climb", "grip_slip", "roll", "hard_land", "dive_land"])
.priority(60)
.breaks_lock(),
)
.transition(
TransitionSpec::new(DASHES, "idle", not("dashing").and(is("grounded"))).priority(50),
)
.transition(
TransitionSpec::new(DASHES, "rise", not("dashing").and(below("vy", -APEX)))
.priority(49),
)
.transition(TransitionSpec::new(DASHES, "fall", not("dashing")).priority(48))
.transition(TransitionSpec::new(["climb"], "grip_slip", is("slipping")).priority(51))
.transition(
TransitionSpec::new(["grip_slip"], "climb", not("slipping").and(is("climbing")))
.priority(51),
)
.transition(
TransitionSpec::new(["climb", "grip_slip"], "fall", not("climbing")).priority(50),
)
.transition(
TransitionSpec::new(
["roll"],
"run",
not("rolling")
.and(is("grounded"))
.and(above("speed", STILL)),
)
.priority(50),
)
.transition(
TransitionSpec::new(["roll"], "idle", not("rolling").and(is("grounded"))).priority(49),
)
.transition(TransitionSpec::new(["roll"], "fall", not("rolling")).priority(48))
.transition(
TransitionSpec::new(
["hard_land"],
"run",
not("hard_landing").and(above("speed", STILL)),
)
.priority(50),
)
.transition(
TransitionSpec::new(AIR, "glide", is("gliding"))
.priority(46)
.breaks_lock(),
)
.transition(TransitionSpec::new(["glide"], "fall", not("gliding")).priority(45))
.transition(
TransitionSpec::new(
["super_charge", "super_ready", "super_dash"],
"idle",
not("charging").and(not("super_flying")).and(is("grounded")),
)
.priority(51),
)
.transition(
TransitionSpec::new(
["super_charge", "super_ready", "super_dash"],
"fall",
not("charging").and(not("super_flying")),
)
.priority(50),
)
.transition(
TransitionSpec::new(["rise", "apex", "fall"], "wall_slide", is("wall_slide"))
.priority(40),
)
.transition(TransitionSpec::new(["wall_slide"], "idle", is("grounded")).priority(41))
.transition(TransitionSpec::new(["wall_slide"], "fall", not("wall_slide")).priority(40))
.transition(TransitionSpec::new(GROUND, "fall", not("grounded")).priority(30))
.transition(TransitionSpec::new(GROUND, "push", is("pushing")).priority(25))
.transition(TransitionSpec::new(GROUND, "push", is("wall_push")).priority(25))
.transition(TransitionSpec::new(GROUND, "pull", is("pulling")).priority(25))
.transition(TransitionSpec::new(GROUND, "box_hold", is("holding")).priority(25))
.transition(
TransitionSpec::new(
BLOCK,
"idle",
not("pushing")
.and(not("pulling"))
.and(not("holding"))
.and(not("wall_push")),
)
.priority(24),
)
.transition(TransitionSpec::new(STANDING, "skid", is("skid")).priority(21))
.transition(TransitionSpec::new(["run", "sprint"], "skid", is("skid")).priority(21))
.transition(
TransitionSpec::new(["skid"], "run", not("skid").and(above("speed", STILL)))
.priority(20),
)
.transition(TransitionSpec::new(["skid"], "idle", not("skid")).priority(19))
.transition(TransitionSpec::new(STANDING, "run", above("speed", STILL)).priority(10))
.transition(TransitionSpec::new(["run"], "idle", below("speed", STILL)).priority(10))
.transition(TransitionSpec::new(["run"], "sprint", above("speed", SPRINT)).priority(11))
.transition(
TransitionSpec::new(["sprint"], "run", below("speed", SPRINT - 10.0)).priority(11),
)
.transition(
TransitionSpec::new(
["land"],
"run",
above("speed", STILL).and(Cond::After(0.05)),
)
.priority(10),
)
.transition(
TransitionSpec::new(
["idle", "idle_fidget", "look_down"],
"look_up",
below("look", -LOOK_DELAY),
)
.priority(9),
)
.transition(
TransitionSpec::new(
["idle", "idle_fidget", "look_up"],
"look_down",
above("look", LOOK_DELAY),
)
.priority(9),
)
.transition(TransitionSpec::new(["look_up"], "idle", above("look", -0.01)).priority(8))
.transition(TransitionSpec::new(["look_down"], "idle", below("look", 0.01)).priority(8))
.transition(
TransitionSpec::new(["idle"], "idle_fidget", Cond::After(FIDGET_AFTER)).priority(1),
)
.transition(TransitionSpec::new(["rise"], "fall", above("vy", APEX)).priority(11))
.transition(TransitionSpec::new(["rise"], "apex", above("vy", -APEX)).priority(10))
.transition(TransitionSpec::new(["apex"], "fall", above("vy", APEX)).priority(10))
.transition(TransitionSpec::new(
AIR,
"idle",
is("grounded").and(Cond::After(0.05)),
))
}
pub fn graph() -> Arc<Graph> {
Arc::new(
spec()
.compile()
.expect("the hero's animation graph compiles"),
)
}