diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 7c300eebeb..e84291d621 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -265,8 +265,8 @@ ], ), Custom("Bird Large Basic"): ( - primary: "common.abilities.custom.birdlargebreathe.firebomb", - secondary: "common.abilities.custom.birdlargebreathe.triplestrike", + primary: "common.abilities.custom.birdlargebasic.dash", + secondary: "common.abilities.custom.birdlargebasic.triplestrike", abilities: [ (None, "common.abilities.custom.birdlargebasic.summontornadoes"), ], diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron new file mode 100644 index 0000000000..2421a1dd02 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -0,0 +1,20 @@ +DashMelee( + energy_cost: 0, + base_damage: 70, + scaled_damage: 150, + base_poise_damage: 50, + scaled_poise_damage: 100, + base_knockback: 12.0, + scaled_knockback: 17.0, + range: 6.0, + angle: 20.0, + energy_drain: 0, + forward_speed: 1.5, + buildup_duration: 0.5, + charge_duration: 1.2, + swing_duration: 0.1, + recover_duration: 1.1, + charge_through: true, + is_interruptible: false, + damage_kind: Crushing, +) diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 21c90ae04a..9e487d8922 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -1,9 +1,9 @@ BasicSummon( buildup_duration: 0.5, - cast_duration: 1.0, - recover_duration: 0.5, + cast_duration: 0.2, + recover_duration: 0.2, summon_amount: 6, - summon_distance: (1, 5), + summon_distance: (1, 3), summon_info: ( body: Object(Tornado), scale: None, @@ -12,7 +12,7 @@ BasicSummon( skillset_config: None, ), duration: Some(( - secs: 5, + secs: 10, nanos: 0, )), ) \ No newline at end of file diff --git a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron new file mode 100644 index 0000000000..eb332435a4 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron @@ -0,0 +1,57 @@ +ComboMelee( + stage_data: [ + ( + stage: 1, + base_damage: 100, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 4.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 2.0, + damage_kind: Slashing, + ), + ( + stage: 2, + base_damage: 80, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ( + stage: 3, + base_damage: 130, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 10.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.65, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ], + initial_energy_gain: 0, + max_energy_gain: 0, + energy_increase: 0, + speed_increase: 0.0, + max_speed_increase: 0.0, + scales_from_combo: 0, + is_interruptible: false, + ori_modifier: 0.7, +) diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index de072c981e..2418c50b38 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -2,9 +2,9 @@ SpinMelee( buildup_duration: 0.0, swing_duration: 0.5, recover_duration: 0.0, - base_damage: 15000, - base_poise_damage: 200, - knockback: ( strength: 200.0, direction: Away), + base_damage: 50, + base_poise_damage: 100, + knockback: ( strength: 50.0, direction: Away), range: 3.5, damage_effect: None, energy_cost: 0, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 10854409bb..7b1765ebed 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -472,7 +472,10 @@ impl Body { }, Body::FishMedium(_) => 250, Body::Dragon(_) => 5000, - Body::BirdLarge(_) => 3000, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 2400, + _ => 3000, + }, Body::FishSmall(_) => 20, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 3200, @@ -591,7 +594,10 @@ impl Body { }, Body::FishMedium(_) => 10, Body::Dragon(_) => 500, - Body::BirdLarge(_) => 120, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 100, + _ => 120, + }, Body::FishSmall(_) => 10, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 70, diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 2cdb72aaac..b452715362 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -376,6 +376,7 @@ impl Body { Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4), Body::SeaLantern => Vec3::new(0.5, 0.5, 1.0), Body::Snowball => Vec3::broadcast(2.5), + Body::Tornado => Vec3::new(2.0, 2.0, 3.4), _ => Vec3::broadcast(0.5), } } diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 1978cf3fd9..c1ced2481c 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -253,13 +253,12 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Ogre, biped_large::BodyType::Female) => Some( Item::new_from_asset_expect("common.items.npc_weapons.staff.ogre_staff"), ), - (biped_large::Species::Cavetroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Mountaintroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Swamptroll, _) => Some(Item::new_from_asset_expect( + ( + biped_large::Species::Mountaintroll + | biped_large::Species::Swamptroll + | biped_large::Species::Cavetroll, + _, + ) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )), (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 70f0608f61..cb4457f37f 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -137,6 +137,11 @@ impl<'a> System<'a> for Sys { agent, alignment: match body { comp::Body::Humanoid(_) => comp::Alignment::Npc, + comp::Body::BirdLarge(bird_large) => match bird_large.species { + comp::bird_large::Species::Roc => comp::Alignment::Enemy, + comp::bird_large::Species::Cockatrice => comp::Alignment::Enemy, + _ => comp::Alignment::Wild, + }, _ => comp::Alignment::Wild, }, scale: match body { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index d22a68604d..33d181693c 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,18 +3286,57 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { - if can_see_tgt( - &*read_data.terrain, - self.pos, - tgt_data.pos, - attack_data.dist_sqrd, - ) && attack_data.angle < 15.0 + if !read_data + .terrain + .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0)) + .until(Block::is_solid) + .cast() + .1 + .map_or(true, |b| b.is_some()) + { + // Fly to target + controller + .actions + .push(ControlAction::basic_input(InputKind::Fly)); + let move_dir = tgt_data.pos.0 - self.pos.0; + controller.inputs.move_dir = + move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; + controller.inputs.move_z = move_dir.z - 0.5; + } else if agent.action_state.timer > 7.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + // Reset timer + agent.action_state.timer = 0.0; + } else if attack_data.angle < 90.0 + && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) + && agent.action_state.timer < 6.0 + { + controller.inputs.move_dir = Vec2::zero(); + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) + && attack_data.dist_sqrd > (2.0 * attack_data.min_attack_dist).powi(2) + && attack_data.angle < 90.0 + && agent.action_state.timer < 6.0 { controller .actions .push(ControlAction::basic_input(InputKind::Primary)); + controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0) + .xy() + .rotated_z(-0.47 * PI) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + self.path_toward_target(agent, controller, tgt_data, read_data, true, None); + agent.action_state.timer += read_data.dt.0; } else { - agent.target = None; + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + agent.action_state.timer += read_data.dt.0; } } diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index b16f0cb019..cee364ed3b 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -419,7 +419,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Wendigo, _) => (15.0, 0.0), (Cavetroll, _) => (13.0, 1.5), (Mountaintroll, _) => (13.0, 1.5), - (Swamptroll, _) => (13.0, 1.5), + (Swamptroll, _) => (15.0, 0.5), (Dullahan, _) => (15.0, 0.0), (Werewolf, _) => (13.0, 0.0), (Occultsaurok, _) => (10.0, 0.0), diff --git a/voxygen/anim/src/bird_large/dash.rs b/voxygen/anim/src/bird_large/dash.rs new file mode 100644 index 0000000000..65717f32fd --- /dev/null +++ b/voxygen/anim/src/bird_large/dash.rs @@ -0,0 +1,177 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::states::utils::StageSection; +use std::f32::consts::PI; + +pub struct DashAnimation; +type DashAnimationDependency<'a> = ( + Vec3, + Vec3, + Vec3, + f32, + Option, + f32, + f32, +); + +impl Animation for DashAnimation { + type Dependency<'a> = DashAnimationDependency<'a>; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_dash\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_dash")] + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (velocity, orientation, last_ori, acc_vel, stage_section, global_time, timer): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + let speed = (Vec2::::from(velocity).magnitude()).min(22.0); + *rate = 1.0; + + let (movement1base, chargemovementbase, movement2base, movement3, legtell) = + match stage_section { + Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0, anim_time), + Some(StageSection::Charge) => (1.0, 1.0, 0.0, 0.0, 0.0), + Some(StageSection::Swing) => (1.0, 0.0, anim_time.powi(4), 0.0, 1.0), + Some(StageSection::Recover) => (1.0, 0.0, 1.0, anim_time, 1.0), + _ => (0.0, 0.0, 0.0, 0.0, 0.0), + }; + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + let legtwitch = (legtell * 6.0).sin() * pullback; + let legswing = legtell * pullback; + let chargeanim = (chargemovementbase * anim_time * 15.0).sin(); + + //let speednorm = speed / 13.0; + let speednorm = (speed / 13.0).powf(0.25); + + let speedmult = 0.8; + let lab: f32 = 0.6; //6 + + // acc_vel and anim_time mix to make sure phase lenght isn't starting at + // +infinite + let mixed_vel = acc_vel + anim_time * 5.0; //sets run frequency using speed, with anim_time setting a floor + + let short = ((1.0 + / (0.72 + + 0.28 * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()).powi(2))) + .sqrt()) + * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()) + * speednorm; + + // + let shortalt = (mixed_vel * 1.0 * lab * speedmult + PI * 3.0 / 8.0 - 0.5).sin() * speednorm; + + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_x( + -0.1 * speednorm + short * -0.05 + movement1abs * -0.8 + movement2abs * 0.2, + ) * Quaternion::rotation_y(tilt * 0.2) + * Quaternion::rotation_z(shortalt * -0.05 - tilt * 1.5); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = + Quaternion::rotation_x(short * -0.02 - 0.02 + movement1abs * -0.4 + movement2abs * 0.4); + + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(-0.1 * speednorm + short * -0.04) + * Quaternion::rotation_y(tilt * 0.1) + * Quaternion::rotation_z(shortalt * -0.1 - tilt * 0.5); + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + short * 0.5 + 0.5 * speednorm, + ) * s_a.scaler + / 8.0; + next.chest.orientation = + Quaternion::rotation_x(short * 0.07 + movement1abs * 0.8 + movement2abs * -1.2) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_z(shortalt * 0.10); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(0.6 + short * -0.02 + movement1abs * -0.8 + movement2abs * 0.8); + + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = Quaternion::rotation_x(-0.2 + short * -0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_y( + -0.8 + movement1abs * 1.0 + chargeanim * 0.2 - movement2abs * 0.6, + ) * Quaternion::rotation_z(0.2 - movement1abs * 0.6 - movement2abs * 0.6); + next.wing_in_r.orientation = + Quaternion::rotation_y( + 0.8 - movement1abs * 1.0 - chargeanim * 0.2 + movement2abs * 0.6, + ) * Quaternion::rotation_z(-0.2 + movement1abs * 0.6 + movement2abs * 0.6); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 + short * 0.05) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + short * -0.05) * Quaternion::rotation_z(-0.2); + + if legtell > 0.0 { + if mirror.is_sign_positive() { + next.leg_l.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_l.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + + next.leg_r.orientation = Quaternion::rotation_x(0.0); + + next.foot_r.orientation = Quaternion::rotation_x(0.0); + } else { + next.leg_l.orientation = Quaternion::rotation_x(0.0); + + next.foot_l.orientation = Quaternion::rotation_x(0.0); + + next.leg_r.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_r.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + } + } + + next + } +} diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 67e7da5166..58a3ae27a1 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -1,5 +1,6 @@ pub mod alpha; pub mod breathe; +pub mod dash; pub mod feed; pub mod fly; pub mod idle; @@ -7,13 +8,14 @@ pub mod run; pub mod shockwave; pub mod shoot; pub mod stunned; +pub mod summon; pub mod swim; // Reexports pub use self::{ - alpha::AlphaAnimation, breathe::BreatheAnimation, feed::FeedAnimation, fly::FlyAnimation, - idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, - stunned::StunnedAnimation, swim::SwimAnimation, + alpha::AlphaAnimation, breathe::BreatheAnimation, dash::DashAnimation, feed::FeedAnimation, + fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, + shoot::ShootAnimation, stunned::StunnedAnimation, summon::SummonAnimation, swim::SwimAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; diff --git a/voxygen/anim/src/bird_large/summon.rs b/voxygen/anim/src/bird_large/summon.rs new file mode 100644 index 0000000000..5176540e77 --- /dev/null +++ b/voxygen/anim/src/bird_large/summon.rs @@ -0,0 +1,114 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::{states::utils::StageSection, util::Dir}; + +pub struct SummonAnimation; + +type SummonAnimationDependency = (f32, Option, f32, Dir, bool); + +impl Animation for SummonAnimation { + type Dependency<'a> = SummonAnimationDependency; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_summon\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_summon")] + #[allow(clippy::approx_constant)] // TODO: Pending review in #587 + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (global_time, stage_section, timer, look_dir, on_ground): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let (movement1base, movement2base, movement3, twitch) = match stage_section { + Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), + Some(StageSection::Cast) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), + Some(StageSection::Recover) => (1.0, 1.0, anim_time.min(1.0).powi(2), 1.0), + _ => (0.0, 0.0, 0.0, 0.0), + }; + + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let twitch2 = mirror * (twitch * 20.0).sin() * pullback; + + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + + let wave_slow_cos = (anim_time * 4.5).cos(); + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + wave_slow_cos * 0.06 + twitch2 * 0.1, + ) * s_a.scaler + / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = + Quaternion::rotation_x(movement1abs * -1.0 - movement2abs * 0.1 + look_dir.z * 0.4); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = Quaternion::rotation_x(movement1abs * -0.7 + twitch2 * 0.1); + + if on_ground { + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(movement1abs * 0.5 - movement2abs * 0.5); + + next.chest.orientation = + Quaternion::rotation_x(movement1abs * 1.1 - movement2abs * 0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(-1.0 + movement1abs * 1.6 - movement2abs * 1.8) + * Quaternion::rotation_z(0.2 - movement1abs * 1.8 + movement2abs * 0.4); + next.wing_in_r.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(1.0 - movement1abs * 1.6 + movement2abs * 1.8) + * Quaternion::rotation_z(-0.2 + movement1abs * 1.8 - movement2abs * 0.4); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = + Quaternion::rotation_y(-0.1 - movement2abs * 0.4) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = + Quaternion::rotation_y(0.1 + movement2abs * 0.4) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 - movement2abs * 0.4) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + movement2abs * 0.4) * Quaternion::rotation_z(-0.2); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + } else { + } + + next + } +} diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index 662f5db4c6..5502b4babd 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -80,7 +80,7 @@ impl Default for BoneData { } pub struct FigureModel { - pub opaque: Model, + pub opaque: Option>, /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different * LOD levels. */ } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 167bbf2571..c43d262185 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -88,11 +88,12 @@ pub struct FigureModelEntry { } impl FigureModelEntry { - pub fn lod_model(&self, lod: usize) -> SubModel { + pub fn lod_model(&self, lod: usize) -> Option> { // Note: Range doesn't impl Copy even for trivially Cloneable things self.model .opaque - .submodel(self.lod_vertex_ranges[lod].clone()) + .as_ref() + .map(|m| m.submodel(self.lod_vertex_ranges[lod].clone())) } } @@ -3480,6 +3481,70 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::BasicSummon(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + + StageSection::Cast => { + stage_time / s.static_data.cast_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::bird_large::SummonAnimation::update_skeleton( + &target_base, + ( + time, + Some(s.stage_section), + state.state_time, + look_dir, + physics.on_ground, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, + CharacterState::DashMelee(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Charge => { + stage_time / s.static_data.charge_duration.as_secs_f32() + }, + StageSection::Swing => { + stage_time / s.static_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::bird_large::DashAnimation::update_skeleton( + &target_base, + ( + rel_vel, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + state.acc_vel, + Some(s.stage_section), + time, + state.state_time, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::Stunned(s) => { let stage_time = s.timer.as_secs_f32(); let stage_progress = match s.stage_section { @@ -5163,7 +5228,7 @@ impl FigureMgr { model_entry.lod_model(0) }; - Some((bound, model, col_lights_.texture(model_entry))) + Some((bound, model?, col_lights_.texture(model_entry))) } else { // trace!("Body has no saved figure"); None @@ -5221,9 +5286,7 @@ impl FigureColLights { let col_lights = renderer.figure_bind_col_light(col_lights); let model_len = u32::try_from(opaque.vertices().len()) .expect("The model size for this figure does not fit in a u32!"); - let model = renderer - .create_model(&opaque) - .expect("The model contains no vertices!"); + let model = renderer.create_model(&opaque); vertex_ranges.iter().for_each(|range| { assert!( diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 54fd63bea4..19cec5b20d 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -366,20 +366,20 @@ impl Scene { ); if let Some(model) = model { - figure_drawer.draw( - model.lod_model(0), - self.figure_state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw( + lod, + self.figure_state.bound(), + &self.col_lights.texture(model), + ); + } } } if let Some((model, state)) = &self.backdrop { - figure_drawer.draw( - model.lod_model(0), - state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw(lod, state.bound(), &self.col_lights.texture(model)); + } } drop(figure_drawer); diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 5c240d1235..362fcf2fed 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -119,9 +119,15 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body( - biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), - ) + .with_body(match rng.gen_range(0..2) { + 0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo) + .into(), + _ => biped_large::Body::random_with( + rng, + &biped_large::Species::Mountaintroll, + ) + .into(), + }) .with_alignment(Alignment::Enemy) }, group_size: 1..2, @@ -623,7 +629,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..5) { 0 => theropod::Body::random_with(rng, &theropod::Species::Odonto).into(), 1 => { biped_large::Body::random_with(rng, &biped_large::Species::Mightysaurok) @@ -633,6 +639,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( biped_large::Body::random_with(rng, &biped_large::Species::Occultsaurok) .into() }, + 3 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) + .into(), _ => biped_large::Body::random_with(rng, &biped_large::Species::Slysaurok) .into(), }) @@ -652,12 +660,11 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..3) { 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) .into(), - 1 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) - .into(), - 2 => quadruped_small::Body::random_with( + + 1 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Quokka, )