diff --git a/crates/skate-game/src/graph_host/action_conditions.rs b/crates/skate-game/src/graph_host/action_conditions.rs index 8e2be6e5..106ccec2 100644 --- a/crates/skate-game/src/graph_host/action_conditions.rs +++ b/crates/skate-game/src/graph_host/action_conditions.rs @@ -22,6 +22,7 @@ pub enum ActionCondition { Tricking, InLocomotion, DisableDismount, + TimeToLand(NumericCondition), } impl ActionCondition { pub fn parse(a: &Attributes<'_>) -> Result, String> { @@ -42,11 +43,19 @@ impl ActionCondition { "IsTricking" => Self::Tricking, "IsInLocomotion" => Self::InLocomotion, "DisableDismount" => Self::DisableDismount, + "TimeToLand" => Self::TimeToLand(super::condition_nodes::numeric(a)), _ => return Ok(None), })) } pub fn evaluate(&self, host: &ActionHost, frame: &Frame) -> Result { Ok(match self { + // Same native physical leaf used by MotionGraph::TimeToLand. + Self::TimeToLand(numeric) => numeric.matches( + host.gameplay_conditions + .as_ref() + .ok_or("TimeToLand requires physical condition publication")? + .time_to_land, + ), Self::Gesture(group) => group.has_intent(&host.action_intents), Self::AnimationAttribute { name, diff --git a/crates/skate-game/src/physics/frame.rs b/crates/skate-game/src/physics/frame.rs index deb27f81..3f96c9ef 100644 --- a/crates/skate-game/src/physics/frame.rs +++ b/crates/skate-game/src/physics/frame.rs @@ -148,6 +148,7 @@ pub(super) fn advance( //All consumers of the preceding output have completed this frame's input. super::player_input::reset_outputs(&mut skater.player_input.physical); physics.finish_skater(skater)?; + super::offboard::trace::record(physics, skater); let simulation = physics.settings.step.simulation; skater.player_input.update_dynamic_normal( &physics.board, diff --git a/crates/skate-game/src/physics/offboard/mod.rs b/crates/skate-game/src/physics/offboard/mod.rs index d63f5aff..47eaf259 100644 --- a/crates/skate-game/src/physics/offboard/mod.rs +++ b/crates/skate-game/src/physics/offboard/mod.rs @@ -14,3 +14,4 @@ pub(crate) mod runtime; pub(crate) mod settings; pub(crate) mod skeleton_air; pub(crate) mod skeleton_ground; +pub(crate) mod trace; diff --git a/crates/skate-game/src/physics/offboard/runtime.rs b/crates/skate-game/src/physics/offboard/runtime.rs index 643c6924..3785060f 100644 --- a/crates/skate-game/src/physics/offboard/runtime.rs +++ b/crates/skate-game/src/physics/offboard/runtime.rs @@ -10,6 +10,7 @@ use skate_core::point_graph::PointGraph; use skate_data::{animation_metadata::AnimationMetadata, collections::Collections}; pub(crate) struct Runtime { + pub trace: super::trace::Trace, pub air_state: skate_core::player::offboard::air_state::State, pub air_prediction: Option, pub air_blend_curve: PointGraph<8>, @@ -148,6 +149,7 @@ impl Runtime { pub(crate) fn load(data: &Collections, metadata: &AnimationMetadata) -> Result { let settings = super::settings::Settings::load(data, metadata)?; Ok(Self { + trace: Default::default(), air_state: Default::default(), air_prediction: None, air_blend_curve: settings.air_blend_curve, diff --git a/crates/skate-game/src/physics/offboard/trace.rs b/crates/skate-game/src/physics/offboard/trace.rs new file mode 100644 index 00000000..26a32036 --- /dev/null +++ b/crates/skate-game/src/physics/offboard/trace.rs @@ -0,0 +1,50 @@ +//! Opt-in observations of user-controlled dismounts; never modifies simulation. +use crate::physics::{GamePhysics, SkaterRuntime}; + +pub(crate) struct Trace { + enabled: bool, + was_offboard: bool, + remaining: u32, + captures: u32, +} +impl Default for Trace { + fn default() -> Self { + Self { + enabled: std::env::var_os("SKATE_OFFBOARD_TRACE").is_some(), + was_offboard: false, + remaining: 0, + captures: 0, + } + } +} +pub(crate) fn record(physics: &GamePhysics, skater: &mut SkaterRuntime) { + let p = &skater.player_input.processed; + let trace = &mut skater.offboard.trace; + if !trace.enabled { return; } + let offboard = p.category_2512 == 500; + if offboard && !trace.was_offboard && trace.remaining == 0 && trace.captures < 3 { + trace.remaining = 600; + trace.captures += 1; + } + trace.was_offboard = offboard; + if trace.remaining == 0 { return; } + trace.remaining -= 1; + let motion = skater.offboard.controller.output(); + let s = &skater.animated_skeleton; + let a = &skater.animation_input.fields; + eprintln!( + "OFFBOARD_TRACE tick={} state={} clip={:?} flags={:08x}/{:08x}/{:08x}/{:08x}/{:08x} root={:?} root_velocity={:?} controller={:?} velocity={:?} up={:?} com_target={:?} com={:?} pose_error={:?} extra_error={:?} contact={:x} contact_pos={:?} contact_normal={:?} cadence={} anim_velocity={:?} anim_motion={:?} anim_time={} override_time={} hand={} board_target={:?} board={:?} bail={:?}", + physics.ticks, p.state_2508, skater.animation.motion.animation.current_name, + p.flags_2468, p.flags_2472, p.flags_2476, p.flags_2480, p.flags_2484, + s.roots.animation_to_world[3], skater.skeleton_input.root_velocity, + motion.physical_frame[3], motion.velocity, motion.animation_frame[1], + s.board_frames.com_frame[3], skater.skeleton.record.centre_of_mass, + skater.collision_maximum_error, skater.collision_extra_displacements, + skater.offboard.retained_contact.flags, skater.offboard.retained_contact.position, + skater.offboard.retained_contact.normal, a.cadence_end_percent, + s.motion.velocity_world, a.animation_translation, a.animation_time, + a.animation_physics_blend_seconds, skater.offboard.possession.selected_hand_424, + s.board_frames.animation_target[3], crate::physics::solve::deck_frame(&physics.board)[3], + skater.wipeout.state.reasons, + ); +}