From 5bf99eac11b85759ad10f915eed99214fa702f19 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 29 May 2021 17:27:55 -0500 Subject: [PATCH] Added totem ability, totem ai, totem ability set, particles for totem abilities, and totem voxel model. --- .../common/abilities/ability_set_manifest.ron | 5 + .../custom/birdlargefire/fireshockwave.ron | 1 + .../abilities/custom/claygolem/shockwave.ron | 1 + .../custom/stonegolemfist/shockwave.ron | 1 + .../abilities/custom/tidalwarrior/totem.ron | 7 +- .../custom/tidalwarrior/totem_wave.ron | 17 +++ .../common/abilities/staff/fireshockwave.ron | 1 + .../items/npc_weapons/unique/tidal_totem.ron | 19 +++ assets/voxygen/shaders/particle-vert.glsl | 12 ++ assets/voxygen/voxel/object/sea_lantern.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 10 ++ common/src/comp/ability.rs | 5 +- common/src/comp/body.rs | 1 + common/src/comp/body/object.rs | 6 +- common/src/comp/inventory/loadout_builder.rs | 3 + common/src/comp/shockwave.rs | 8 ++ common/src/states/shockwave.rs | 3 + server/src/sys/agent.rs | 29 ++++ voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 126 ++++++++++++------ 20 files changed, 213 insertions(+), 46 deletions(-) create mode 100644 assets/common/abilities/custom/tidalwarrior/totem_wave.ron create mode 100644 assets/common/items/npc_weapons/unique/tidal_totem.ron create mode 100644 assets/voxygen/voxel/object/sea_lantern.vox diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 13491aa70c..9f3b3f8b26 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -116,6 +116,11 @@ (None, "common.abilities.custom.tidalwarrior.totem"), ], ), + Custom("Tidal Totem"): ( + primary: "common.abilities.custom.tidalwarrior.totem_wave", + secondary: "common.abilities.custom.tidalwarrior.totem_wave", + abilities: [], + ), Custom("Quad Med Quick"): ( primary: "common.abilities.custom.quadmedquick.triplestrike", secondary: "common.abilities.custom.quadmedquick.dash", diff --git a/assets/common/abilities/custom/birdlargefire/fireshockwave.ron b/assets/common/abilities/custom/birdlargefire/fireshockwave.ron index c951d349f6..07637b0099 100644 --- a/assets/common/abilities/custom/birdlargefire/fireshockwave.ron +++ b/assets/common/abilities/custom/birdlargefire/fireshockwave.ron @@ -13,4 +13,5 @@ Shockwave( requires_ground: false, move_efficiency: 0.1, damage_kind: Energy, + specifier: Fire, ) diff --git a/assets/common/abilities/custom/claygolem/shockwave.ron b/assets/common/abilities/custom/claygolem/shockwave.ron index 61c4a77289..5a88d9f395 100644 --- a/assets/common/abilities/custom/claygolem/shockwave.ron +++ b/assets/common/abilities/custom/claygolem/shockwave.ron @@ -13,4 +13,5 @@ Shockwave( requires_ground: true, move_efficiency: 0.0, damage_kind: Crushing, + specifier: Ground, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/stonegolemfist/shockwave.ron b/assets/common/abilities/custom/stonegolemfist/shockwave.ron index 5d6781e2b1..0d0ced9475 100644 --- a/assets/common/abilities/custom/stonegolemfist/shockwave.ron +++ b/assets/common/abilities/custom/stonegolemfist/shockwave.ron @@ -13,4 +13,5 @@ Shockwave( requires_ground: true, move_efficiency: 0.05, damage_kind: Crushing, + specifier: Ground, ) diff --git a/assets/common/abilities/custom/tidalwarrior/totem.ron b/assets/common/abilities/custom/tidalwarrior/totem.ron index 013a0aade2..343f0e4c2d 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem.ron @@ -2,12 +2,11 @@ BasicSummon( buildup_duration: 0.5, cast_duration: 1.0, recover_duration: 0.5, - summon_amount: 6, + summon_amount: 1, summon_info: ( - // If this is still HaniwaSentry, code reviewers open a comment. I'll know what to do. - body: Object(HaniwaSentry), + body: Object(SeaLantern), scale: None, - health_scaling: 20, + health_scaling: 0, loadout_config: None, skillset_config: None, ), diff --git a/assets/common/abilities/custom/tidalwarrior/totem_wave.ron b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron new file mode 100644 index 0000000000..93f076461b --- /dev/null +++ b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron @@ -0,0 +1,17 @@ +Shockwave( + energy_cost: 0, + buildup_duration: 1.4, + swing_duration: 0.1, + recover_duration: 0.5, + damage: 10, + poise_damage: 0, + knockback: ( strength: 100.0, direction: Up), + shockwave_angle: 360.0, + shockwave_vertical_angle: 30.0, + shockwave_speed: 10.0, + shockwave_duration: 5.0, + requires_ground: true, + move_efficiency: 0.0, + damage_kind: Crushing, + specifier: Water, +) diff --git a/assets/common/abilities/staff/fireshockwave.ron b/assets/common/abilities/staff/fireshockwave.ron index c951d349f6..07637b0099 100644 --- a/assets/common/abilities/staff/fireshockwave.ron +++ b/assets/common/abilities/staff/fireshockwave.ron @@ -13,4 +13,5 @@ Shockwave( requires_ground: false, move_efficiency: 0.1, damage_kind: Energy, + specifier: Fire, ) diff --git a/assets/common/items/npc_weapons/unique/tidal_totem.ron b/assets/common/items/npc_weapons/unique/tidal_totem.ron new file mode 100644 index 0000000000..06bd8017df --- /dev/null +++ b/assets/common/items/npc_weapons/unique/tidal_totem.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Tidal Totem", + description: "Yeet", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + crit_mult: 1.9142857, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Tidal Totem")), +) \ No newline at end of file diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 5cc4f45f27..a74f714892 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -68,6 +68,7 @@ const int ENRAGED = 26; const int BIG_SHRAPNEL = 27; const int LASER = 28; const int BUBBLES = 29; +const int WATER = 30; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -501,6 +502,17 @@ void main() { spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) ); break; + case WATER: + f_reflect = 0.0; // Magic water doesn't reflect light, it emits it + blue_color = 1.25 + 0.2 * rand3 + 1.75 * max(floor(rand4 + 0.15), 0.0); + size = 8.0 * (1 - slow_start(0.1)) * slow_end(0.15); + attr = Attr( + (inst_dir * slow_end(0.2)) + vec3(rand0, rand1, rand2) * 0.5, + vec3(size), + vec4(0.5 * blue_color, 0.9 * blue_color, blue_color, 1), + spin_in_axis(vec3(rand6, rand7, rand8), percent() * 5 + 3 * rand9) + ); + break; default: attr = Attr( linear_motion( diff --git a/assets/voxygen/voxel/object/sea_lantern.vox b/assets/voxygen/voxel/object/sea_lantern.vox new file mode 100644 index 0000000000..c063e37e7e --- /dev/null +++ b/assets/voxygen/voxel/object/sea_lantern.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:392b22a007200459c8578c651a9b3aa0237e2103652e69d5129abf1edce79ec5 +size 2556 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index fcea90705a..00f99e525c 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -709,4 +709,14 @@ central: ("object.haniwa_sentry.bone1"), ) ), + SeaLantern: ( + bone0: ( + offset: (-4.5, -4.5, 0.0), + central: ("object.sea_lantern"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index cf59f89ace..8ab75b8928 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2,7 +2,7 @@ use crate::{ assets::{self, Asset}, combat::{self, CombatEffect, DamageKind, Knockback}, comp::{ - aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, + self, aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, Body, CharacterState, EnergySource, LightEmitter, StateUpdate, }, states::{ @@ -233,6 +233,7 @@ pub enum CharacterAbility { requires_ground: bool, move_efficiency: f32, damage_kind: DamageKind, + specifier: comp::shockwave::FrontendSpecifier, }, BasicBeam { buildup_duration: f32, @@ -1596,6 +1597,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { requires_ground, move_efficiency, damage_kind, + specifier, } => CharacterState::Shockwave(shockwave::Data { static_data: shockwave::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), @@ -1612,6 +1614,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { move_efficiency: *move_efficiency, ability_info, damage_kind: *damage_kind, + specifier: *specifier, }, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 82b0d3d46f..6aaf2f7230 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -491,6 +491,7 @@ impl Body { object::Body::TrainingDummy => 10000, object::Body::Crossbow => 800, object::Body::HaniwaSentry => 600, + object::Body::SeaLantern => 1000, _ => 10000, }, Body::Golem(golem) => match golem.species { diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 6f8eb252e6..06c4e052e9 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -83,6 +83,7 @@ make_case_elim!( SilverOre = 68, ClayRocket = 69, HaniwaSentry = 70, + SeaLantern = 71, } ); @@ -93,7 +94,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 71] = [ +pub const ALL_OBJECTS: [Body; 72] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -165,6 +166,7 @@ pub const ALL_OBJECTS: [Body; 71] = [ Body::GoldOre, Body::ClayRocket, Body::HaniwaSentry, + Body::SeaLantern, ]; impl From for super::Body { @@ -245,6 +247,7 @@ impl Body { Body::GoldOre => "gold_ore", Body::ClayRocket => "clay_rocket", Body::HaniwaSentry => "haniwa_sentry", + Body::SeaLantern => "sea_lantern", } } @@ -336,6 +339,7 @@ impl Body { Body::GoldOre => 1000.0, Body::ClayRocket => 50.0, Body::HaniwaSentry => 300.0, + Body::SeaLantern => 1000.0, }; Mass(m) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index c74193f1fb..909963088a 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -308,6 +308,9 @@ pub fn default_main_tool(body: &Body) -> Option { object::Body::HaniwaSentry => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.haniwa_sentry", )), + object::Body::SeaLantern => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.tidal_totem", + )), _ => None, }, Body::BipedSmall(biped_small) => match (biped_small.species, biped_small.body_type) { diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index 50c1fd1412..588960de72 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -13,6 +13,7 @@ pub struct Properties { pub requires_ground: bool, pub duration: Duration, pub owner: Option, + pub specifier: FrontendSpecifier, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -43,3 +44,10 @@ pub struct ShockwaveHitEntities { impl Component for ShockwaveHitEntities { type Storage = IdvStorage; } + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum FrontendSpecifier { + Ground, + Fire, + Water, +} diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 0cdd474001..09278a3698 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -44,6 +44,8 @@ pub struct StaticData { pub ability_info: AbilityInfo, /// What kind of damage the attack does pub damage_kind: DamageKind, + /// Used to specify the shockwave to the frontend + pub specifier: shockwave::FrontendSpecifier, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -108,6 +110,7 @@ impl CharacterBehavior for Data { attack, requires_ground: self.static_data.requires_ground, owner: Some(*data.uid), + specifier: self.static_data.specifier, }; update.server_events.push_front(ServerEvent::Shockwave { properties, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 78ab37a999..55e27466d6 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -112,6 +112,7 @@ pub enum Tactic { Turret, FixedTurret, RotatingTurret, + RadialTurret, Mindflayer, BirdLargeBreathe, BirdLargeFire, @@ -1607,6 +1608,7 @@ impl<'a> AgentData<'a> { "Minotaur" => Tactic::Minotaur, "Clay Golem" => Tactic::ClayGolem, "Tidal Warrior" => Tactic::TidalWarrior, + "Tidal Totem" => Tactic::RadialTurret, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1856,6 +1858,13 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::RadialTurret => self.handle_radial_turret_attack( + agent, + controller, + &attack_data, + &tgt_data, + &read_data, + ), } } @@ -2860,6 +2869,26 @@ impl<'a> AgentData<'a> { } } + fn handle_radial_turret_attack( + &self, + _agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + if can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } + } + fn handle_mindflayer_attack( &self, agent: &mut Agent, diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 70b5fede76..5f3944d607 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -80,6 +80,7 @@ pub enum ParticleMode { BigShrapnel = 27, Laser = 28, Bubbles = 29, + Water = 30, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 78b3e2b3ac..5b6ae849bc 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -9,8 +9,8 @@ use crate::{ use common::{ assets::{AssetExt, DotVoxAsset}, comp::{ - self, aura, beam, body, buff, item::Reagent, object, BeamSegment, Body, CharacterState, - Ori, Pos, Shockwave, Vel, + self, aura, beam, body, buff, item::Reagent, object, shockwave, BeamSegment, Body, + CharacterState, Ori, Pos, Shockwave, Vel, }, figure::Segment, outcome::Outcome, @@ -1118,6 +1118,7 @@ impl ParticleMgr { let state = scene_data.state; let ecs = state.ecs(); let time = state.get_time(); + let dt = scene_data.state.ecs().fetch::().0; for (_entity, pos, ori, shockwave) in ( &ecs.entities(), @@ -1127,9 +1128,10 @@ impl ParticleMgr { ) .join() { - let elapsed = time - shockwave.creation.unwrap_or_default(); + let elapsed = time - shockwave.creation.unwrap_or(time); + let speed = shockwave.properties.speed; - let distance = shockwave.properties.speed * elapsed as f32; + let distance = speed * elapsed as f32; let radians = shockwave.properties.angle.to_radians(); @@ -1137,55 +1139,99 @@ impl ParticleMgr { let theta = ori_vec.y.atan2(ori_vec.x); let dtheta = radians / distance; - let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2)); + // Number of particles derived from arc length (for new particles at least, old + // can be converted later) + let arc_length = distance * radians; - for heartbeat in 0..heartbeats { - if shockwave.properties.requires_ground { - // 1 / 3 the size of terrain voxel - let scale = 1.0 / 3.0; + use shockwave::FrontendSpecifier; + match shockwave.properties.specifier { + FrontendSpecifier::Ground => { + let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2)); + for heartbeat in 0..heartbeats { + // 1 / 3 the size of terrain voxel + let scale = 1.0 / 3.0; - let scaled_speed = shockwave.properties.speed * scale; + let scaled_speed = speed * scale; - let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32; + let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32; - let distance = - shockwave.properties.speed * (elapsed as f32 - sub_tick_interpolation); + let distance = speed * (elapsed as f32 - sub_tick_interpolation); - let particle_count_factor = radians / (3.0 * scale); - let new_particle_count = distance * particle_count_factor; - self.particles.reserve(new_particle_count as usize); + let particle_count_factor = radians / (3.0 * scale); + let new_particle_count = distance * particle_count_factor; + self.particles.reserve(new_particle_count as usize); - for d in 0..(new_particle_count as i32) { - let arc_position = - theta - radians / 2.0 + dtheta * d as f32 / particle_count_factor; + for d in 0..(new_particle_count as i32) { + let arc_position = + theta - radians / 2.0 + dtheta * d as f32 / particle_count_factor; - let position = pos.0 - + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0); + let position = pos.0 + + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0); - let position_snapped = ((position / scale).floor() + 0.5) * scale; + let position_snapped = ((position / scale).floor() + 0.5) * scale; - self.particles.push(Particle::new( - Duration::from_millis(250), - time, - ParticleMode::GroundShockwave, - position_snapped, - )); + self.particles.push(Particle::new( + Duration::from_millis(250), + time, + ParticleMode::GroundShockwave, + position_snapped, + )); + } } - } else { - for d in 0..3 * distance as i32 { - let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 3.0; + }, + FrontendSpecifier::Fire => { + let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2)); + for _ in 0..heartbeats { + for d in 0..3 * distance as i32 { + let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 3.0; - let position = pos.0 - + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0); + let position = pos.0 + + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0); - self.particles.push(Particle::new( - Duration::from_secs_f32((distance + 10.0) / 50.0), - time, - ParticleMode::FireShockwave, - position, - )); + self.particles.push(Particle::new( + Duration::from_secs_f32((distance + 10.0) / 50.0), + time, + ParticleMode::FireShockwave, + position, + )); + } } - } + }, + FrontendSpecifier::Water => { + // 4 particles per unit length of arc + let particles_per_length = (arc_length) as usize; + let dtheta = radians / particles_per_length as f32; + // Scales number of desired heartbeats from speed - thicker arc = higher speed = + // lower duration = more particles + let heartbeats = self + .scheduler + .heartbeats(Duration::from_secs_f32(1.0 / speed)); + + // Reserves capacity for new particles + let new_particle_count = particles_per_length * heartbeats as usize; + self.particles.reserve(new_particle_count); + + for i in 0..particles_per_length { + let angle = dtheta * i as f32; + let direction = Vec3::new(angle.cos(), angle.sin(), 0.0); + for j in 0..heartbeats { + // Sub tick dt + let dt = (j as f32 / heartbeats as f32) * dt; + let distance = distance + speed * dt; + let pos1 = pos.0 + distance * direction - Vec3::unit_z(); + let pos2 = pos1 + (Vec3::unit_z() + direction) * 3.0; + let time = time + dt as f64; + + self.particles.push(Particle::new_directed( + Duration::from_secs_f32(0.5), + time, + ParticleMode::Water, + pos1, + pos2, + )); + } + } + }, } } }