use super::utils::*; use crate::{ comp::{ character_state::OutputEvents, slot::EquipSlot, CharacterState, InventoryAction, Ori, StateUpdate, }, states::{ behavior::{CharacterBehavior, JoinData}, glide, idle, }, }; use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { pub ori: Ori, span_length: f32, chord_length: f32, } impl From<&JoinData<'_>> for Data { fn from(data: &JoinData) -> Self { let scale = data.body.dimensions().z.sqrt(); Self { // Aspect ratio is what really matters for lift/drag ratio // and the aerodynamics model works for ARs up to 25. // The inflated dimensions are hopefully only a temporary // bandaid for the poor glide ratio experienced under 2.5G. // A span/chord ratio of 4.5 gives an AR of ~5.73. span_length: scale * 4.5, chord_length: scale, ori: *data.ori, } } } impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_jump(data, output_events, &mut update, 1.0); handle_dodge_input(data, &mut update); handle_wield(data, &mut update); // If still in this state, do the things if matches!(update.character, CharacterState::GlideWield(_)) { // If not on the ground while wielding glider enter gliding state update.character = if data.physics.on_ground.is_none() { CharacterState::Glide(glide::Data::new( self.span_length, self.chord_length, self.ori, )) // make sure we have a glider and we're not (too deep) in water } else if data .inventory .and_then(|inv| inv.equipped(EquipSlot::Glider)) .is_some() && data.physics.in_liquid().map_or(true, |depth| depth < 0.5) { CharacterState::GlideWield(Self { // Glider tilt follows look dir ori: self.ori.slerped_towards( data.ori.slerped_towards( Ori::from(data.inputs.look_dir).pitched_up(0.6), (1.0 + data.inputs.look_dir.dot(*data.ori.look_dir()).max(0.0)) / 3.0, ), 5.0 * data.dt.0, ), ..*self }) } else { CharacterState::Idle(idle::Data { is_sneaking: false }) }; } update } fn manipulate_loadout( &self, data: &JoinData, output_events: &mut OutputEvents, inv_action: InventoryAction, ) -> StateUpdate { let mut update = StateUpdate::from(data); handle_manipulate_loadout(data, output_events, &mut update, inv_action); update } fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sit(data, &mut update); update } fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_dance(data, &mut update); update } fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); attempt_sneak(data, &mut update); update } }