diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b5d68eb9..29e66c3154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optimized rendering of quads (most of the graphics in the game) using an index buffer, decreasing the number of vertices that need to be processed by 33%. - Moved the rest of screenshot work into the background. Screenshoting no longer induces large pauses. - Reworked tidal warrior to have unique attacks +- Reworked yeti to have unique attacks ### Removed diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 9f3b3f8b26..d9e401f986 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -237,6 +237,14 @@ (None, "common.abilities.custom.claygolem.rocket"), ], ), + Custom("Yeti"): ( + primary: "common.abilities.custom.yeti.strike", + secondary: "common.abilities.custom.yeti.icespikes", + abilities: [ + (None, "common.abilities.custom.yeti.frostbreath"), + (None, "common.abilities.custom.yeti.snowball"), + ], + ), Custom("Bird Large Breathe"): ( primary: "common.abilities.custom.birdlargebreathe.firebomb", secondary: "common.abilities.custom.birdlargebreathe.triplestrike", diff --git a/assets/common/abilities/custom/yeti/frostbreath.ron b/assets/common/abilities/custom/yeti/frostbreath.ron new file mode 100644 index 0000000000..826f59bbcb --- /dev/null +++ b/assets/common/abilities/custom/yeti/frostbreath.ron @@ -0,0 +1,19 @@ +BasicBeam( + buildup_duration: 0.4, + recover_duration: 0.25, + beam_duration: 0.25, + damage: 50, + tick_rate: 1.0, + range: 15.0, + max_angle: 30.0, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 3.0, + strength: Value(1.5), + chance: 1.0, + ))), + energy_regen: 0, + energy_drain: 0, + orientation_behavior: Normal, + specifier: Frost, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/yeti/icespikes.ron b/assets/common/abilities/custom/yeti/icespikes.ron new file mode 100644 index 0000000000..3b99b3af93 --- /dev/null +++ b/assets/common/abilities/custom/yeti/icespikes.ron @@ -0,0 +1,17 @@ +Shockwave( + energy_cost: 0, + buildup_duration: 0.6, + swing_duration: 0.12, + recover_duration: 0.3, + damage: 100, + poise_damage: 10, + knockback: (strength: 25.0, direction: Up), + shockwave_angle: 90.0, + shockwave_vertical_angle: 15.0, + shockwave_speed: 50.0, + shockwave_duration: 0.5, + requires_ground: true, + move_efficiency: 0.5, + damage_kind: Piercing, + specifier: IceSpikes, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/yeti/snowball.ron b/assets/common/abilities/custom/yeti/snowball.ron new file mode 100644 index 0000000000..7cd3348c8e --- /dev/null +++ b/assets/common/abilities/custom/yeti/snowball.ron @@ -0,0 +1,13 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.5, + recover_duration: 0.4, + projectile: Snowball( + damage: 200.0, + radius: 5.0, + ), + projectile_body: Object(Snowball), + projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, +) diff --git a/assets/common/abilities/custom/yeti/strike.ron b/assets/common/abilities/custom/yeti/strike.ron new file mode 100644 index 0000000000..1624a3c6e2 --- /dev/null +++ b/assets/common/abilities/custom/yeti/strike.ron @@ -0,0 +1,13 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.6, + swing_duration: 0.1, + recover_duration: 0.5, + base_damage: 100, + base_poise_damage: 50, + knockback: ( strength: 40.0, direction: Away), + range: 4.0, + max_angle: 20.0, + damage_effect: None, + damage_kind: Crushing, +) diff --git a/assets/common/items/npc_weapons/hammer/yeti_hammer.ron b/assets/common/items/npc_weapons/hammer/yeti_hammer.ron index d906f684c9..602926451b 100644 --- a/assets/common/items/npc_weapons/hammer/yeti_hammer.ron +++ b/assets/common/items/npc_weapons/hammer/yeti_hammer.ron @@ -2,11 +2,11 @@ ItemDef( name: "Yeti Hammer", description: "Placeholder", kind: Tool(( - kind: Hammer, + kind: Natural, hands: Two, stats: Direct(( - equip_time_secs: 0.0, - power: 2.0, + equip_time_secs: 0.5, + power: 1.0, poise_strength: 1.0, speed: 1.0, crit_chance: 0.046875, @@ -15,5 +15,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Hammer Simple")), + ability_spec: Some(Custom("Yeti")), ) \ No newline at end of file diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index a74f714892..4bad57d31e 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -69,6 +69,7 @@ const int BIG_SHRAPNEL = 27; const int LASER = 28; const int BUBBLES = 29; const int WATER = 30; +const int ICE_SPIKES = 31; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -144,6 +145,12 @@ vec3 perp_axis2(vec3 axis1, vec3 axis2) { return normalize(vec3(axis1.y * axis2.z - axis1.z * axis2.y, axis1.z * axis2.x - axis1.x * axis2.z, axis1.x * axis2.y - axis1.y * axis2.x)); } +// Line is the axis of the spiral, it goes from the start position to the end position +// Radius is the distance from the axis the particle is +// Time function is some value that ideally goes from 0 to 1. When it is 0, it is as +// the point (0, 0, 0), when it is 1, it is at the point provided by the coordinates of line +// Frequency increases the frequency of rotation +// Offset is an offset to the angle of the rotation vec3 spiral_motion(vec3 line, float radius, float time_function, float frequency, float offset) { vec3 axis2 = perp_axis1(line); vec3 axis3 = perp_axis2(line, axis2); @@ -420,10 +427,11 @@ void main() { break; case ICE: f_reflect = 0.0; // Ice doesn't reflect to look like magic + float ice_color = 1.9 + rand5 * 0.3; attr = Attr( inst_dir * ((rand0+1.0)/2 + 0.4) * slow_end(2.0) + 0.3 * grav_vel(earth_gravity), - vec3((3 * (1 - slow_start(0.1)))), - vec4(0.2, 1.6 + rand5 * 0.3 - 0.4 * percent(), 3, 1), + vec3((5 * (1 - slow_start(.1)))), + vec4(0.8 * ice_color, 0.9 * ice_color, ice_color, 1), spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) ); break; @@ -513,6 +521,16 @@ void main() { spin_in_axis(vec3(rand6, rand7, rand8), percent() * 5 + 3 * rand9) ); break; + case ICE_SPIKES: + f_reflect = 0.0; // Ice doesn't reflect to look like magic + ice_color = 1.7 + rand5 * 0.2; + attr = Attr( + vec3(0.0), + vec3(11.0, 11.0, 11.0 * length(inst_dir) * 2.0 * (0.5 - abs(0.5 - slow_end(0.5)))) / 3, + vec4(0.8 * ice_color, 0.9 * ice_color, ice_color, 1), + spin_in_axis(vec3(1,0,0),0) + ); + break; default: attr = Attr( linear_motion( diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 00f99e525c..cb43158072 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -719,4 +719,14 @@ central: ("armor.empty"), ) ), + Snowball: ( + bone0: ( + offset: (-12.5, -12.5, 0.0), + central: ("weapon.projectile.snowball"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/assets/voxygen/voxel/weapon/projectile/snowball.vox b/assets/voxygen/voxel/weapon/projectile/snowball.vox new file mode 100644 index 0000000000..a1f2a3b875 --- /dev/null +++ b/assets/voxygen/voxel/weapon/projectile/snowball.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6de3e25adcf704d7024d358642ce8e37ef01fe88577ff06c886cf8836067300 +size 33964 diff --git a/common/src/comp/beam.rs b/common/src/comp/beam.rs index 50e056ad1b..9111c35453 100644 --- a/common/src/comp/beam.rs +++ b/common/src/comp/beam.rs @@ -54,4 +54,5 @@ pub enum FrontendSpecifier { Cultist, ClayGolem, Bubbles, + Frost, } diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 1a95d6c6d6..73724f5f7d 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -471,7 +471,7 @@ impl Body { biped_large::Species::Dullahan => 3000, biped_large::Species::Mindflayer => 12500, biped_large::Species::Tidalwarrior => 16000, - biped_large::Species::Yeti => 4000, + biped_large::Species::Yeti => 12000, biped_large::Species::Minotaur => 30000, biped_large::Species::Harvester => 3000, biped_large::Species::Blueoni => 2400, @@ -586,7 +586,7 @@ impl Body { biped_large::Species::Wendigo => 80, biped_large::Species::Troll => 60, biped_large::Species::Dullahan => 120, - biped_large::Species::Yeti => 80, + biped_large::Species::Yeti => 0, biped_large::Species::Harvester => 80, // Boss enemies have their health set, not adjusted by level. biped_large::Species::Mindflayer => 0, @@ -650,6 +650,7 @@ impl Body { biped_large::Species::Mindflayer => 4.8, biped_large::Species::Minotaur => 3.2, biped_large::Species::Tidalwarrior => 2.25, + biped_large::Species::Yeti => 2.0, _ => 1.0, }, Body::Golem(g) => match g.species { diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index cf509fdc1c..97f2d203a5 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -84,6 +84,7 @@ make_case_elim!( ClayRocket = 69, HaniwaSentry = 70, SeaLantern = 71, + Snowball = 72, } ); @@ -94,7 +95,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 72] = [ +pub const ALL_OBJECTS: [Body; 73] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -167,6 +168,7 @@ pub const ALL_OBJECTS: [Body; 72] = [ Body::ClayRocket, Body::HaniwaSentry, Body::SeaLantern, + Body::Snowball, ]; impl From
for super::Body { @@ -248,6 +250,7 @@ impl Body { Body::ClayRocket => "clay_rocket", Body::HaniwaSentry => "haniwa_sentry", Body::SeaLantern => "sea_lantern", + Body::Snowball => "snowball", } } @@ -270,6 +273,7 @@ impl Body { Body::Crate => 300.0, // let's say it's a lot of wood and maybe some contents Body::Scarecrow => 900.0, Body::TrainingDummy => 2000.0, + Body::Snowball => 0.9 * WATER_DENSITY, // let them sink _ => 1.1 * WATER_DENSITY, }; @@ -340,6 +344,7 @@ impl Body { Body::ClayRocket => 50.0, Body::HaniwaSentry => 300.0, Body::SeaLantern => 1000.0, + Body::Snowball => 7360.0, // 2.5 m diamter }; Mass(m) @@ -354,6 +359,7 @@ impl Body { Body::Crossbow => Vec3::new(3.0, 3.0, 1.5), 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), _ => Vec3::broadcast(0.5), } } diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index a50bbc3788..a3e716e1be 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -32,6 +32,10 @@ pub struct Projectile { /// Whether projectile collides with entities in the same group as its /// owner pub ignore_group: bool, + /// Whether the projectile is sticky + pub is_sticky: bool, + /// Whether the projectile should use a point collider + pub is_point: bool, } impl Component for Projectile { @@ -64,6 +68,10 @@ pub enum ProjectileConstructor { radius: f32, knockback: f32, }, + Snowball { + damage: f32, + radius: f32, + }, } impl ProjectileConstructor { @@ -113,6 +121,8 @@ impl ProjectileConstructor { time_left: Duration::from_secs(15), owner, ignore_group: true, + is_sticky: true, + is_point: true, } }, Fireball { @@ -149,6 +159,8 @@ impl ProjectileConstructor { time_left: Duration::from_secs(10), owner, ignore_group: true, + is_sticky: true, + is_point: true, } }, Frostball { damage, radius } => { @@ -167,7 +179,7 @@ impl ProjectileConstructor { let explosion = Explosion { effects: vec![RadiusEffect::Attack(attack)], radius, - reagent: Some(Reagent::Blue), + reagent: Some(Reagent::White), }; Projectile { hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish], @@ -175,6 +187,8 @@ impl ProjectileConstructor { time_left: Duration::from_secs(10), owner, ignore_group: true, + is_sticky: true, + is_point: true, } }, NecroticSphere { damage, radius } => { @@ -201,6 +215,8 @@ impl ProjectileConstructor { time_left: Duration::from_secs(10), owner, ignore_group: true, + is_sticky: true, + is_point: true, } }, Possess => Projectile { @@ -209,6 +225,8 @@ impl ProjectileConstructor { time_left: Duration::from_secs(10), owner, ignore_group: false, + is_sticky: true, + is_point: true, }, ClayRocket { damage, @@ -249,6 +267,35 @@ impl ProjectileConstructor { time_left: Duration::from_secs(10), owner, ignore_group: true, + is_sticky: true, + is_point: true, + } + }, + Snowball { damage, radius } => { + let damage = AttackDamage::new( + Damage { + source: DamageSource::Explosion, + kind: DamageKind::Energy, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + ); + let attack = Attack::default() + .with_damage(damage) + .with_crit(crit_chance, crit_mult); + let explosion = Explosion { + effects: vec![RadiusEffect::Attack(attack)], + radius, + reagent: Some(Reagent::White), + }; + Projectile { + hit_solid: vec![], + hit_entity: vec![Effect::Explode(explosion), Effect::Vanish], + time_left: Duration::from_secs(120), + owner, + ignore_group: true, + is_sticky: false, + is_point: false, } }, } @@ -300,6 +347,13 @@ impl ProjectileConstructor { *damage *= power; *radius *= range; }, + Snowball { + ref mut damage, + ref mut radius, + } => { + *damage *= power; + *radius *= range; + }, } self } diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index 588960de72..df6b666c6a 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -50,4 +50,5 @@ pub enum FrontendSpecifier { Ground, Fire, Water, + IceSpikes, } diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index f6d83ab4f0..8acf61ef7f 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -84,7 +84,7 @@ impl CharacterBehavior for Data { - self.timer.as_secs_f32() / self.static_data.movement_duration.as_secs_f32()) / 2.0 - + 0.5), + + 0.25), }); if self.timer < self.static_data.movement_duration { diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index c1a57cd7f9..a929d8224d 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -285,17 +285,29 @@ impl StateExt for State { body: comp::Body, projectile: comp::Projectile, ) -> EcsEntityBuilder { - self.ecs_mut() + let mut projectile_base = self + .ecs_mut() .create_entity_synced() .with(pos) .with(vel) .with(comp::Ori::from_unnormalized_vec(vel.0).unwrap_or_default()) .with(body.mass()) - .with(body.density()) - .with(comp::Collider::Point) - .with(body) - .with(projectile) - .with(comp::Sticky) + .with(body.density()); + + if projectile.is_sticky { + projectile_base = projectile_base.with(comp::Sticky) + } + if projectile.is_point { + projectile_base = projectile_base.with(comp::Collider::Point) + } else { + projectile_base = projectile_base.with(comp::Collider::Box { + radius: body.radius(), + z_min: 0.0, + z_max: body.height(), + }) + } + + projectile_base.with(projectile).with(body) } fn create_shockwave( diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 55e27466d6..152e49030b 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -119,6 +119,7 @@ pub enum Tactic { Minotaur, ClayGolem, TidalWarrior, + Yeti, } #[derive(SystemData)] @@ -1609,6 +1610,7 @@ impl<'a> AgentData<'a> { "Clay Golem" => Tactic::ClayGolem, "Tidal Warrior" => Tactic::TidalWarrior, "Tidal Totem" => Tactic::RadialTurret, + "Yeti" => Tactic::Yeti, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1697,6 +1699,18 @@ impl<'a> AgentData<'a> { ), ) }, + Tactic::Yeti if matches!(self.char_state, CharacterState::BasicRanged(_)) => { + const SNOWBALL_SPEED: f32 = 60.0; + aim_projectile( + SNOWBALL_SPEED, + Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), + Vec3::new( + tgt_data.pos.0.x, + tgt_data.pos.0.y, + tgt_data.pos.0.z + tgt_eye_offset, + ), + ) + }, _ => Dir::from_unnormalized( Vec3::new( tgt_data.pos.0.x, @@ -1865,6 +1879,9 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::Yeti => { + self.handle_yeti_attack(agent, controller, &attack_data, &tgt_data, &read_data) + }, } } @@ -3476,6 +3493,65 @@ impl<'a> AgentData<'a> { self.path_toward_target(agent, controller, tgt_data, read_data, false, None); } + fn handle_yeti_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const ICE_SPIKES_RANGE: f32 = 20.0; + const ICE_BREATH_RANGE: f32 = 15.0; + const ICE_BREATH_TIMER: f32 = 10.0; + const SNOWBALL_MAX_RANGE: f32 = 50.0; + + agent.action_state.counter += read_data.dt.0; + + if attack_data.dist_sqrd < ICE_BREATH_RANGE.powi(2) && attack_data.angle < 60.0 { + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(1)) + { + // Keep using ice breath until a second has passed + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else if agent.action_state.counter > ICE_BREATH_TIMER { + // Use ice breath if timer has gone for long enough + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + + if matches!(self.char_state, CharacterState::BasicBeam(_)) { + // Resets action counter when using beam + agent.action_state.counter = 0.0; + } + } else if attack_data.in_min_range() { + // Basic attack if on top of them + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else { + // Use ice spikes if too far for other abilities + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } + } else if attack_data.dist_sqrd < ICE_SPIKES_RANGE.powi(2) && attack_data.angle < 60.0 { + // Use ice spikes if in range + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } else if attack_data.dist_sqrd < SNOWBALL_MAX_RANGE.powi(2) && attack_data.angle < 60.0 { + // Otherwise, chuck all the snowballs + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(1))); + } + + // Always attempt to path towards target + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + } + fn follow( &self, agent: &mut Agent, diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index b7ca5ce30c..f5255db813 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -132,6 +132,8 @@ impl<'a> System<'a> for Sys { time_left: Duration::from_secs(60), owner: *owner, ignore_group: true, + is_sticky: true, + is_point: true, }, speed, object: Some(Object::Firework { diff --git a/voxygen/anim/src/biped_large/alpha.rs b/voxygen/anim/src/biped_large/alpha.rs index b752566a0c..1e52049cc7 100644 --- a/voxygen/anim/src/biped_large/alpha.rs +++ b/voxygen/anim/src/biped_large/alpha.rs @@ -297,6 +297,35 @@ impl Animation for AlphaAnimation { next.head.orientation = Quaternion::rotation_x(move1 * -0.6 + move2 * 0.4) }, + "Yeti" => { + next.control_l.position = Vec3::new(-1.0, 2.0, 12.0 + move2 * -10.0); + next.control_r.position = Vec3::new(1.0, 2.0, -2.0); + + next.control.position = Vec3::new( + 4.0 + move1 * -12.0 + move2 * 20.0, + (s_a.grip.0 / 1.0) + move1 * -3.0 + move2 * 5.0, + (-s_a.grip.0 / 0.8) + move1 * -2.0 + move2 * 8.0, + ); + next.head.orientation = Quaternion::rotation_x(move1 * -0.25) + * Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6); + next.upper_torso.orientation = + Quaternion::rotation_z(move1 * 0.2 + move2 * -0.4); + next.lower_torso.orientation = + Quaternion::rotation_z(move1 * -0.2 + move2 * 0.2); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0 + move2 * 0.8) + * Quaternion::rotation_y(-0.0); + next.control_r.orientation = + Quaternion::rotation_x(PI / 2.0 + 0.2 + move2 * 0.8) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); + + next.control.orientation = + Quaternion::rotation_x(-1.0 + move1 * -0.5 + move2 * -0.3) + * Quaternion::rotation_y(-1.8 + move1 * -0.8 + move2 * 3.0) + * Quaternion::rotation_z(move1 * -0.8 + move2 * -0.8); + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/beam.rs b/voxygen/anim/src/biped_large/beam.rs index 9bf1828295..62f8e01481 100644 --- a/voxygen/anim/src/biped_large/beam.rs +++ b/voxygen/anim/src/biped_large/beam.rs @@ -73,17 +73,12 @@ impl Animation for BeamAnimation { next.hand_l.orientation = Quaternion::rotation_x(0.0); next.hand_r.orientation = Quaternion::rotation_x(0.0); - let (move1base, move2shake, _move2base, move3) = match stage_section { - Some(StageSection::Buildup) => ( - (anim_time.powf(0.25)).min(1.0), - (anim_time * 15.0 + PI).sin(), - (anim_time * 10.0 + PI).sin(), - 0.0, - ), + let (move1base, move2shake, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => ((anim_time.powf(0.25)).min(1.0), 0.0, 0.0, 0.0), Some(StageSection::Cast) => ( 1.0, (anim_time * 15.0 + PI).sin(), - anim_time.powf(0.25), + (anim_time.powf(0.1)).min(1.0), 0.0, ), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), @@ -91,6 +86,7 @@ impl Animation for BeamAnimation { }; let pullback = 1.0 - move3; let move1 = move1base * pullback; + let move2 = move2base * pullback; match active_tool_kind { Some(ToolKind::Sceptre) | Some(ToolKind::Staff) => { next.control_l.position = Vec3::new(-1.0, 3.0, 12.0); @@ -128,8 +124,6 @@ impl Animation for BeamAnimation { ); next.shoulder_r.orientation = Quaternion::rotation_x(move1 * 0.2 + 0.3 + 0.6 * speednorm + (footrotl * -0.2)); - next.torso.orientation = Quaternion::rotation_x(move1 * -0.1); - next.torso.position = Vec3::new(0.0, 0.0, move1 * 1.0); }, Some(ToolKind::Natural) => { if let Some(AbilitySpec::Custom(spec)) = active_tool_spec { @@ -177,6 +171,50 @@ impl Animation for BeamAnimation { next.shoulder_r.orientation = Quaternion::rotation_z(move1 * 0.3); }; }, + "Yeti" => { + next.second.scale = Vec3::one() * 0.0; + + next.head.orientation = Quaternion::rotation_x( + move1 * 0.5 + move2 * -0.5 + move2shake * -0.02, + ); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = + Quaternion::rotation_x(move2 * -0.5 + move2shake * -0.1); + next.control_l.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_r.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_l.orientation = Quaternion::rotation_x(1.57); + next.control_r.orientation = Quaternion::rotation_x(1.57); + + next.weapon_l.position = Vec3::new(-12.0, -1.0, -15.0); + next.weapon_r.position = Vec3::new(12.0, -1.0, -15.0); + + next.weapon_l.orientation = Quaternion::rotation_x(-1.57 - 0.1); + next.weapon_r.orientation = Quaternion::rotation_x(-1.57 - 0.1); + + next.arm_control_r.orientation = + Quaternion::rotation_x(move1 * 1.1 + move2 * -1.6) + * Quaternion::rotation_y(move1 * 1.4 + move2 * -1.8); + + next.shoulder_l.orientation = + Quaternion::rotation_x(move1 * 1.4 + move2 * -1.8); + + next.shoulder_r.orientation = + Quaternion::rotation_x(move1 * 1.4 + move2 * -1.8); + + next.upper_torso.position = Vec3::new( + 0.0, + s_a.upper_torso.0, + s_a.upper_torso.1 + move1 * -1.9 + move2 * 1.2, + ); + next.upper_torso.orientation = Quaternion::rotation_x( + move1 * 0.8 + move2 * -1.1 + move2shake * -0.02, + ); + next.lower_torso.position = + Vec3::new(0.0, s_a.lower_torso.0, s_a.lower_torso.1); + next.lower_torso.orientation = Quaternion::rotation_x( + move1 * -0.8 + move2 * 1.1 + move2shake * 0.02, + ); + }, _ => {}, } } diff --git a/voxygen/anim/src/biped_large/shockwave.rs b/voxygen/anim/src/biped_large/shockwave.rs index e2ba47be04..81d373014a 100644 --- a/voxygen/anim/src/biped_large/shockwave.rs +++ b/voxygen/anim/src/biped_large/shockwave.rs @@ -2,18 +2,22 @@ use super::{ super::{vek::*, Animation}, BipedLargeSkeleton, SkeletonAttr, }; -use common::{comp::item::ToolKind, states::utils::StageSection}; +use common::{ + comp::item::{AbilitySpec, ToolKind}, + states::utils::StageSection, +}; pub struct ShockwaveAnimation; +type ShockwaveAnimationDependency<'a> = ( + (Option