From 66b0fee3c7f2575821818e49adc18c316dcde554 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 5 Aug 2020 17:49:04 -0500 Subject: [PATCH 01/17] Separated knockback out from basicmelee. Added weapon for cyclops boss to use, and added 1 ability to it. --- .../npc_weapons/npcweapon/cyclops_hammer.ron | 13 +++++++ assets/voxygen/item_image_manifest.ron | 5 +++ .../voxel/humanoid_main_weapon_manifest.ron | 7 +++- .../voxel/weapon/npcweapon/cyclops_hammer.vox | 3 ++ common/src/comp/ability.rs | 3 ++ common/src/comp/inventory/item/tool.rs | 37 +++++++++++++++++++ common/src/loadout_builder.rs | 1 + common/src/states/basic_melee.rs | 7 +++- server/src/sys/terrain.rs | 2 + tools/src/main.rs | 2 + voxygen/src/hud/util.rs | 1 + 11 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron create mode 100644 assets/voxygen/voxel/weapon/npcweapon/cyclops_hammer.vox diff --git a/assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron b/assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron new file mode 100644 index 0000000000..904f65a5e0 --- /dev/null +++ b/assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron @@ -0,0 +1,13 @@ +Item( + name: "Cyclops Hammer", + description: "Wielded by a mighty cyclops.", + kind: Tool( + ( + kind: NpcWeapon("CyclopsHammer"), + stats: ( + equip_time_millis: 500, + power: 1.00, + ), + ) + ), +) diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 7e2ebda6f1..6b98dedcf4 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -519,6 +519,11 @@ "voxel.weapon.shield.wood-0", (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 2.4, ), + // Npc weapons (for test purposes, remove when done) + Tool(NpcWeapon("CyclopsHammer")): VoxTrans( + "voxel.weapon.npcweapon.cyclops_hammer", + (2.0, -1.0, 0.0), (-135.0, 90.0, 0.0), 1.1, + ), // Lanterns Lantern("Black0"): Png( "element.icons.lantern_black-0", diff --git a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron index 79969d1c5c..d967085b4a 100644 --- a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron +++ b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron @@ -1,4 +1,9 @@ -({ //Swords +({ // Npc weapons (for test purposes, remove when done) + NpcWeapon("CyclopsHammer"): ( + vox_spec: ("weapon.npcweapon.cyclops_hammer", (-2.5, -7.5, -5.0)), + color: None + ), + //Swords Sword("BasicSword"): ( vox_spec: ("weapon.sword.rusty_2h", (-1.5, -6.5, -4.0)), color: None diff --git a/assets/voxygen/voxel/weapon/npcweapon/cyclops_hammer.vox b/assets/voxygen/voxel/weapon/npcweapon/cyclops_hammer.vox new file mode 100644 index 0000000000..93bf3d1752 --- /dev/null +++ b/assets/voxygen/voxel/weapon/npcweapon/cyclops_hammer.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef11b80feaf3144e07c4779baa8eaf248e4311e3c927e050193f254ff7136111 +size 29267 diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 3f4931de89..e921cc1767 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -50,6 +50,7 @@ pub enum CharacterAbility { buildup_duration: Duration, recover_duration: Duration, base_healthchange: i32, + knockback: f32, range: f32, max_angle: f32, }, @@ -250,6 +251,7 @@ impl From<&CharacterAbility> for CharacterState { buildup_duration, recover_duration, base_healthchange, + knockback, range, max_angle, energy_cost: _, @@ -258,6 +260,7 @@ impl From<&CharacterAbility> for CharacterState { buildup_duration: *buildup_duration, recover_duration: *recover_duration, base_healthchange: *base_healthchange, + knockback: *knockback, range: *range, max_angle: *max_angle, }), diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 8b36fa8756..2b6338a2f2 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -16,6 +16,7 @@ pub enum ToolKind { Dagger(String), Staff(String), Shield(String), + NpcWeapon(String), Debug(String), Farming(String), /// This is an placeholder item, it is used by non-humanoid npcs to attack @@ -32,6 +33,7 @@ impl ToolKind { ToolKind::Dagger(_) => Hands::OneHand, ToolKind::Staff(_) => Hands::TwoHand, ToolKind::Shield(_) => Hands::OneHand, + ToolKind::NpcWeapon(_) => Hands::TwoHand, ToolKind::Debug(_) => Hands::TwoHand, ToolKind::Farming(_) => Hands::TwoHand, ToolKind::Empty => Hands::OneHand, @@ -53,6 +55,7 @@ pub enum ToolCategory { Dagger, Staff, Shield, + NpcWeapon, Debug, Farming, Empty, @@ -68,6 +71,7 @@ impl From<&ToolKind> for ToolCategory { ToolKind::Dagger(_) => ToolCategory::Dagger, ToolKind::Staff(_) => ToolCategory::Staff, ToolKind::Shield(_) => ToolCategory::Shield, + ToolKind::NpcWeapon(_) => ToolCategory::NpcWeapon, ToolKind::Debug(_) => ToolCategory::Debug, ToolKind::Farming(_) => ToolCategory::Farming, ToolKind::Empty => ToolCategory::Empty, @@ -141,6 +145,7 @@ impl Tool { buildup_duration: Duration::from_millis(700), recover_duration: Duration::from_millis(300), base_healthchange: (-120.0 * self.base_power()) as i32, + knockback: 0.0, range: 3.5, max_angle: 20.0, }, @@ -157,6 +162,7 @@ impl Tool { buildup_duration: Duration::from_millis(700), recover_duration: Duration::from_millis(150), base_healthchange: (-50.0 * self.base_power()) as i32, + knockback: 0.0, range: 3.5, max_angle: 20.0, }], @@ -202,6 +208,7 @@ impl Tool { buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(400), base_healthchange: (-50.0 * self.base_power()) as i32, + knockback: 0.0, range: 3.5, max_angle: 20.0, }, @@ -220,6 +227,7 @@ impl Tool { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(300), base_healthchange: (-10.0 * self.base_power()) as i32, + knockback: 0.0, range: 5.0, max_angle: 20.0, }, @@ -228,6 +236,7 @@ impl Tool { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(1000), base_healthchange: (150.0 * self.base_power()) as i32, + knockback: 0.0, range: 100.0, max_angle: 90.0, }, @@ -239,6 +248,7 @@ impl Tool { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(300), base_healthchange: (-10.0 * self.base_power()) as i32, + knockback: 0.0, range: 5.0, max_angle: 20.0, }, @@ -247,6 +257,7 @@ impl Tool { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(1000), base_healthchange: (350.0 * self.base_power()) as i32, + knockback: 0.0, range: 100.0, max_angle: 90.0, }, @@ -258,6 +269,7 @@ impl Tool { buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(300), base_healthchange: (-40.0 * self.base_power()) as i32, + knockback: 0.0, range: 3.5, max_angle: 20.0, }, @@ -324,11 +336,35 @@ impl Tool { buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(400), base_healthchange: (-40.0 * self.base_power()) as i32, + knockback: 0.0, range: 3.0, max_angle: 120.0, }, BasicBlock, ], + NpcWeapon(kind) => { + if kind == "CyclopsHammer" { + vec![BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(300), + knockback: 20.0, + base_healthchange: -200, + range: 10.0, + max_angle: 120.0, + }] + } else { + vec![BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(100), + recover_duration: Duration::from_millis(300), + base_healthchange: -10, + knockback: 0.0, + range: 1.0, + max_angle: 30.0, + }] + } + }, Debug(kind) => { if kind == "Boost" { vec![ @@ -372,6 +408,7 @@ impl Tool { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(1000), base_healthchange: -20, + knockback: 0.0, range: 3.5, max_angle: 15.0, }], diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 24d41c39ab..3f40faccb9 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -70,6 +70,7 @@ impl LoadoutBuilder { buildup_duration: Duration::from_millis(600), recover_duration: Duration::from_millis(100), base_healthchange: -(body.base_dmg() as i32), + knockback: 0.0, range: body.base_range(), max_angle: 20.0, }), diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index caead1a54c..e234668661 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -14,6 +14,8 @@ pub struct Data { pub recover_duration: Duration, /// Base damage (negative) or healing (positive) pub base_healthchange: i32, + /// Knockback + pub knockback: f32, /// Max range pub range: f32, /// Max angle (45.0 will give you a 90.0 angle window) @@ -38,6 +40,7 @@ impl CharacterBehavior for Data { .unwrap_or_default(), recover_duration: self.recover_duration, base_healthchange: self.base_healthchange, + knockback: self.knockback, range: self.range, max_angle: self.max_angle, exhausted: false, @@ -50,13 +53,14 @@ impl CharacterBehavior for Data { max_angle: self.max_angle.to_radians(), applied: false, hit_count: 0, - knockback: 0.0, + knockback: self.knockback, }); update.character = CharacterState::BasicMelee(Data { buildup_duration: self.buildup_duration, recover_duration: self.recover_duration, base_healthchange: self.base_healthchange, + knockback: self.knockback, range: self.range, max_angle: self.max_angle, exhausted: true, @@ -70,6 +74,7 @@ impl CharacterBehavior for Data { .checked_sub(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), base_healthchange: self.base_healthchange, + knockback: self.knockback, range: self.range, max_angle: self.max_angle, exhausted: true, diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 6ba5401ac7..a48c16356f 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -147,6 +147,7 @@ impl<'a> System<'a> for Sys { buildup_duration: Duration::from_millis(0), recover_duration: Duration::from_millis(400), base_healthchange: -40, + knockback: 0.0, range: 3.5, max_angle: 15.0, }), @@ -277,6 +278,7 @@ impl<'a> System<'a> for Sys { buildup_duration: Duration::from_millis(800), recover_duration: Duration::from_millis(200), base_healthchange: -100, + knockback: 0.0, range: 3.5, max_angle: 60.0, }), diff --git a/tools/src/main.rs b/tools/src/main.rs index 37ee04f4c0..c5b2455f97 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -79,6 +79,7 @@ fn get_tool_kind(kind: &ToolKind) -> String { ToolKind::Shield(_) => "Shield".to_string(), ToolKind::Debug(_) => "Debug".to_string(), ToolKind::Farming(_) => "Farming".to_string(), + ToolKind::NpcWeapon(_) => "NpcWeapon".to_string(), ToolKind::Empty => "Empty".to_string(), } } @@ -94,6 +95,7 @@ fn get_tool_kind_kind(kind: &ToolKind) -> String { ToolKind::Shield(x) => x.clone(), ToolKind::Debug(x) => x.clone(), ToolKind::Farming(x) => x.clone(), + ToolKind::NpcWeapon(x) => x.clone(), ToolKind::Empty => "".to_string(), } } diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 2847cb221d..8b23ca3d6c 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -75,6 +75,7 @@ fn tool_desc(tool: &Tool, desc: &str) -> String { ToolKind::Dagger(_) => "Dagger", ToolKind::Staff(_) => "Staff", ToolKind::Shield(_) => "Shield", + ToolKind::NpcWeapon(_) => "Npc Weapon", ToolKind::Debug(_) => "Debug", ToolKind::Farming(_) => "Farming Tool", ToolKind::Empty => "Empty", From 68ecfba291be2e95b4ed90d8f8c118d15989f0a7 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 6 Aug 2020 13:17:38 -0500 Subject: [PATCH 02/17] Projectile speed is no longer hard-coded. --- common/src/comp/ability.rs | 12 ++++++++++++ common/src/comp/inventory/item/tool.rs | 7 +++++++ common/src/event.rs | 1 + common/src/states/basic_ranged.rs | 5 +++++ common/src/states/charged_ranged.rs | 26 ++++++++++++++++++++------ server/src/events/entity_creation.rs | 3 ++- server/src/events/mod.rs | 3 ++- 7 files changed, 49 insertions(+), 8 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index e921cc1767..2477760ea7 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -63,6 +63,7 @@ pub enum CharacterAbility { projectile_body: Body, projectile_light: Option, projectile_gravity: Option, + projectile_speed: f32, }, Boost { duration: Duration, @@ -105,6 +106,9 @@ pub enum CharacterAbility { recover_duration: Duration, projectile_body: Body, projectile_light: Option, + projectile_gravity: Option, + initial_projectile_speed: f32, + max_projectile_speed: f32, }, } @@ -272,6 +276,7 @@ impl From<&CharacterAbility> for CharacterState { projectile_body, projectile_light, projectile_gravity, + projectile_speed, energy_cost: _, } => CharacterState::BasicRanged(basic_ranged::Data { exhausted: false, @@ -283,6 +288,7 @@ impl From<&CharacterAbility> for CharacterState { projectile_body: *projectile_body, projectile_light: *projectile_light, projectile_gravity: *projectile_gravity, + projectile_speed: *projectile_speed, }), CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data { duration: *duration, @@ -366,6 +372,9 @@ impl From<&CharacterAbility> for CharacterState { recover_duration, projectile_body, projectile_light, + projectile_gravity, + initial_projectile_speed, + max_projectile_speed, } => CharacterState::ChargedRanged(charged_ranged::Data { exhausted: false, energy_drain: *energy_drain, @@ -379,6 +388,9 @@ impl From<&CharacterAbility> for CharacterState { recover_duration: *recover_duration, projectile_body: *projectile_body, projectile_light: *projectile_light, + projectile_gravity: *projectile_gravity, + initial_projectile_speed: *initial_projectile_speed, + max_projectile_speed: *max_projectile_speed, }), } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 2b6338a2f2..667571bda2 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -187,6 +187,7 @@ impl Tool { projectile_body: Body::Object(object::Body::Arrow), projectile_light: None, projectile_gravity: Some(Gravity(0.2)), + projectile_speed: 100.0, }, ChargedRanged { energy_cost: 0, @@ -200,6 +201,9 @@ impl Tool { recover_duration: Duration::from_millis(500), projectile_body: Body::Object(object::Body::MultiArrow), projectile_light: None, + projectile_gravity: Some(Gravity(0.2)), + initial_projectile_speed: 100.0, + max_projectile_speed: 500.0, }, ], Dagger(_) => vec![ @@ -296,6 +300,7 @@ impl Tool { }), projectile_gravity: None, + projectile_speed: 100.0, }, BasicRanged { energy_cost: 400, @@ -326,6 +331,7 @@ impl Tool { }), projectile_gravity: None, + projectile_speed: 100.0, }, ] } @@ -397,6 +403,7 @@ impl Tool { ..Default::default() }), projectile_gravity: None, + projectile_speed: 100.0, }, ] } else { diff --git a/common/src/event.rs b/common/src/event.rs index 7098522e93..bea3abf131 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -46,6 +46,7 @@ pub enum ServerEvent { light: Option, projectile: comp::Projectile, gravity: Option, + speed: f32, }, LandOnGround { entity: EcsEntity, diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index dd7f0ff820..4b0c928c64 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -21,6 +21,7 @@ pub struct Data { pub projectile_body: Body, pub projectile_light: Option, pub projectile_gravity: Option, + pub projectile_speed: f32, /// Whether the attack fired already pub exhausted: bool, } @@ -49,6 +50,7 @@ impl CharacterBehavior for Data { projectile_body: self.projectile_body, projectile_light: self.projectile_light, projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, exhausted: false, }); } else if !self.exhausted { @@ -62,6 +64,7 @@ impl CharacterBehavior for Data { projectile, light: self.projectile_light, gravity: self.projectile_gravity, + speed: self.projectile_speed, }); update.character = CharacterState::BasicRanged(Data { @@ -73,6 +76,7 @@ impl CharacterBehavior for Data { projectile_body: self.projectile_body, projectile_light: self.projectile_light, projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, exhausted: true, }); } else if self.recover_duration != Duration::default() { @@ -89,6 +93,7 @@ impl CharacterBehavior for Data { projectile_body: self.projectile_body, projectile_light: self.projectile_light, projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, exhausted: true, }); return update; diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 1d9d1e8f72..c16f594711 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -10,9 +10,6 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::time::Duration; -const MAX_GRAVITY: f32 = 0.2; -const MIN_GRAVITY: f32 = 0.05; - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { /// Whether the attack fired already @@ -38,6 +35,9 @@ pub struct Data { /// Projectile information pub projectile_body: Body, pub projectile_light: Option, + pub projectile_gravity: Option, + pub initial_projectile_speed: f32, + pub max_projectile_speed: f32, } impl CharacterBehavior for Data { @@ -65,6 +65,9 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, projectile_body: self.projectile_body, projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + initial_projectile_speed: self.initial_projectile_speed, + max_projectile_speed: self.max_projectile_speed, }); } else if data.inputs.secondary.is_pressed() && self.charge_timer < self.charge_duration @@ -87,6 +90,9 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, projectile_body: self.projectile_body, projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + initial_projectile_speed: self.initial_projectile_speed, + max_projectile_speed: self.max_projectile_speed, }); // Consumes energy if there's enough left and RMB is held down @@ -109,6 +115,9 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, projectile_body: self.projectile_body, projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + initial_projectile_speed: self.initial_projectile_speed, + max_projectile_speed: self.max_projectile_speed, }); // Consumes energy if there's enough left and RMB is held down @@ -145,9 +154,8 @@ impl CharacterBehavior for Data { body: self.projectile_body, projectile, light: self.projectile_light, - gravity: Some(Gravity( - MAX_GRAVITY - charge_amount * (MAX_GRAVITY - MIN_GRAVITY), - )), + gravity: self.projectile_gravity, + speed: self.initial_projectile_speed + charge_amount * (self.max_projectile_speed - self.initial_projectile_speed), }); update.character = CharacterState::ChargedRanged(Data { @@ -163,6 +171,9 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, projectile_body: self.projectile_body, projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + initial_projectile_speed: self.initial_projectile_speed, + max_projectile_speed: self.max_projectile_speed, }); } else if self.recover_duration != Duration::default() { // Recovery @@ -182,6 +193,9 @@ impl CharacterBehavior for Data { .unwrap_or_default(), projectile_body: self.projectile_body, projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + initial_projectile_speed: self.initial_projectile_speed, + max_projectile_speed: self.max_projectile_speed, }); } else { // Done diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 28034143a8..d9a2263557 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -87,6 +87,7 @@ pub fn handle_shoot( light: Option, projectile: Projectile, gravity: Option, + speed: f32, ) { let state = server.state_mut(); @@ -97,7 +98,7 @@ pub fn handle_shoot( .expect("Failed to fetch entity") .0; - let vel = *dir * 100.0; + let vel = *dir * speed; // Add an outcome state diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index d56c4fa8c4..31b3dc631a 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -68,7 +68,8 @@ impl Server { light, projectile, gravity, - } => handle_shoot(self, entity, dir, body, light, projectile, gravity), + speed, + } => handle_shoot(self, entity, dir, body, light, projectile, gravity, speed), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), From 717142d5ea616845be43b5bbcf27644155d183ea Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 8 Aug 2020 15:53:55 -0500 Subject: [PATCH 03/17] Started to implement shockwave system. --- common/src/comp/ability.rs | 35 ++++++ common/src/comp/character_state.rs | 5 + common/src/comp/inventory/item/tool.rs | 30 +++-- common/src/comp/mod.rs | 2 + common/src/comp/shockwave.rs | 25 +++++ common/src/event.rs | 1 + common/src/states/ground_shockwave.rs | 103 ++++++++++++++++++ common/src/states/mod.rs | 1 + common/src/sys/character_behavior.rs | 2 + common/src/sys/stats.rs | 3 +- server/src/events/entity_creation.rs | 8 +- server/src/events/mod.rs | 1 + .../audio/sfx/event_mapper/combat/tests.rs | 1 + 13 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 common/src/comp/shockwave.rs create mode 100644 common/src/states/ground_shockwave.rs diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 2477760ea7..843bea5933 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -24,6 +24,7 @@ pub enum CharacterAbilityType { TripleStrike(Stage), LeapMelee, SpinMelee, + GroundShockwave, } impl From<&CharacterState> for CharacterAbilityType { @@ -38,6 +39,7 @@ impl From<&CharacterState> for CharacterAbilityType { CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage), CharacterState::SpinMelee(_) => Self::SpinMelee, CharacterState::ChargedRanged(_) => Self::ChargedRanged, + CharacterState::GroundShockwave(_) => Self::ChargedRanged, _ => Self::BasicMelee, } } @@ -110,6 +112,16 @@ pub enum CharacterAbility { initial_projectile_speed: f32, max_projectile_speed: f32, }, + GroundShockwave { + energy_cost: u32, + buildup_duration: Duration, + recover_duration: Duration, + damage: u32, + knockback: f32, + shockwave_angle: f32, + shockwave_speed: f32, + shockwave_duration: Duration, + }, } impl CharacterAbility { @@ -155,6 +167,10 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), + CharacterAbility::GroundShockwave { energy_cost, .. } => update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok(), _ => true, } } @@ -392,6 +408,25 @@ impl From<&CharacterAbility> for CharacterState { initial_projectile_speed: *initial_projectile_speed, max_projectile_speed: *max_projectile_speed, }), + CharacterAbility::GroundShockwave { + energy_cost: _, + buildup_duration, + recover_duration, + damage, + knockback, + shockwave_angle, + shockwave_speed, + shockwave_duration, + } => CharacterState::GroundShockwave(ground_shockwave::Data { + exhausted: false, + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, + damage: *damage, + knockback: *knockback, + shockwave_angle: *shockwave_angle, + shockwave_speed: *shockwave_speed, + shockwave_duration: *shockwave_duration, + }), } } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 26b490a7f4..e262c86905 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -69,6 +69,8 @@ pub enum CharacterState { SpinMelee(spin_melee::Data), /// A charged ranged attack (e.g. bow) ChargedRanged(charged_ranged::Data), + /// A ground shockwave attack + GroundShockwave(ground_shockwave::Data), } impl CharacterState { @@ -83,6 +85,7 @@ impl CharacterState { | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } @@ -95,6 +98,7 @@ impl CharacterState { | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } @@ -107,6 +111,7 @@ impl CharacterState { | CharacterState::BasicBlock | CharacterState::LeapMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 667571bda2..0d2530b7ea 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -350,15 +350,27 @@ impl Tool { ], NpcWeapon(kind) => { if kind == "CyclopsHammer" { - vec![BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(300), - knockback: 20.0, - base_healthchange: -200, - range: 10.0, - max_angle: 120.0, - }] + vec![ + BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(300), + knockback: 20.0, + base_healthchange: -200, + range: 10.0, + max_angle: 120.0, + }, + GroundShockwave { + energy_cost: 0, + buildup_duration: Duration::from_millis(1000), + recover_duration: Duration::from_millis(2000), + damage: 300, + knockback: -30.0, + shockwave_angle: 15.0, + shockwave_speed: 10.0, + shockwave_duration: Duration::from_millis(3000), + }, + ] } else { vec![BasicMelee { energy_cost: 0, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 322d5025e7..bf73199a18 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -16,6 +16,7 @@ mod misc; mod phys; mod player; pub mod projectile; +pub mod shockwave; pub mod skills; mod stats; pub mod visual; @@ -51,6 +52,7 @@ pub use misc::Object; pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel}; pub use player::{Player, MAX_MOUNT_RANGE_SQR}; pub use projectile::Projectile; +pub use shockwave::Shockwave; pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet}; pub use stats::{Exp, HealthChange, HealthSource, Level, Stats}; pub use visual::{LightAnimation, LightEmitter}; diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs new file mode 100644 index 0000000000..49d5a28251 --- /dev/null +++ b/common/src/comp/shockwave.rs @@ -0,0 +1,25 @@ +use crate::{ + comp::phys::{Ori, Pos}, + sync::Uid, +}; +use serde::{Deserialize, Serialize}; +use specs::{Component, FlaggedStorage}; +use specs_idvs::IdvStorage; +use std::time::Duration; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Shockwave { + pub shockwave_origin: Pos, + pub shockwave_direction: Ori, + pub shockwave_angle: f32, + pub shockwave_speed: f32, + pub shockwave_duration: Duration, + pub damage: u32, + pub knockback: f32, + pub requires_ground: bool, + pub owner: Option, +} + +impl Component for Shockwave { + type Storage = FlaggedStorage>; +} diff --git a/common/src/event.rs b/common/src/event.rs index bea3abf131..e2bd4d1ea4 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -48,6 +48,7 @@ pub enum ServerEvent { gravity: Option, speed: f32, }, + Shockwave {shockwave: comp::Shockwave}, LandOnGround { entity: EcsEntity, vel: Vec3, diff --git a/common/src/states/ground_shockwave.rs b/common/src/states/ground_shockwave.rs new file mode 100644 index 0000000000..c9fc147dce --- /dev/null +++ b/common/src/states/ground_shockwave.rs @@ -0,0 +1,103 @@ +use crate::{ + comp::{Attacking, CharacterState, Shockwave, StateUpdate}, + event::ServerEvent, + states::utils::*, + sys::character_behavior::*, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Whether the attack can deal more damage + pub exhausted: bool, + /// How long until state should deal damage + pub buildup_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Base damage + pub damage: u32, + /// Knockback + pub knockback: f32, + /// Angle of the shockwave + pub shockwave_angle: f32, + /// Speed of the shockwave + pub shockwave_speed: f32, + /// How long the shockwave travels for + pub shockwave_duration: Duration, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 0.7); + handle_jump(data, &mut update); + + if self.buildup_duration != Duration::default() { + // Build up + update.character = CharacterState::GroundShockwave(Data { + exhausted: false, + buildup_duration: self + .buildup_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + recover_duration: self.recover_duration, + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else if !self.exhausted { + // Attack + let shockwave = Shockwave { + shockwave_origin: *data.pos, + shockwave_direction: *data.ori, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + damage: self.damage, + knockback: self.knockback, + requires_ground: true, + owner: Some(*data.uid), + }; + update.server_events.push_front(ServerEvent::Shockwave { + shockwave, + }); + + update.character = CharacterState::GroundShockwave(Data { + exhausted: true, + buildup_duration: self.buildup_duration, + recover_duration: self.recover_duration, + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::GroundShockwave(Data { + exhausted: false, + buildup_duration: self.buildup_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + } + + update + } +} diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 5c620b8c6b..435d35683c 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -9,6 +9,7 @@ pub mod dash_melee; pub mod equipping; pub mod glide; pub mod glide_wield; +pub mod ground_shockwave; pub mod idle; pub mod leap_melee; pub mod roll; diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 95feb503be..2c4b38186d 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -258,6 +258,7 @@ impl<'a> System<'a> for Sys { CharacterState::LeapMelee(data) => data.handle_event(&j, action), CharacterState::SpinMelee(data) => data.handle_event(&j, action), CharacterState::ChargedRanged(data) => data.handle_event(&j, action), + CharacterState::GroundShockwave(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -286,6 +287,7 @@ impl<'a> System<'a> for Sys { CharacterState::LeapMelee(data) => data.behavior(&j), CharacterState::SpinMelee(data) => data.behavior(&j), CharacterState::ChargedRanged(data) => data.behavior(&j), + CharacterState::GroundShockwave(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index eafa9c8e3d..e7b26d9436 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -112,7 +112,8 @@ impl<'a> System<'a> for Sys { | CharacterState::SpinMelee { .. } | CharacterState::TripleStrike { .. } | CharacterState::BasicRanged { .. } - | CharacterState::ChargedRanged { .. } => { + | CharacterState::ChargedRanged { .. } + | CharacterState::GroundShockwave { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index d9a2263557..f120bd7e34 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -3,7 +3,7 @@ use common::{ character::CharacterId, comp::{ self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, Agent, Alignment, Body, Gravity, Item, - ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Shockwave, Stats, Vel, WaypointArea, }, outcome::Outcome, util::Dir, @@ -124,6 +124,12 @@ pub fn handle_shoot( builder.build(); } +pub fn handle_shockwave(server: &mut Server, shockwave: Shockwave) { + let state = server.state_mut(); + let builder = state.create_shockwave(shockwave); + builder.build(); +} + pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { server .state diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 31b3dc631a..6cdd018c0b 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -70,6 +70,7 @@ impl Server { gravity, speed, } => handle_shoot(self, entity, dir, body, light, projectile, gravity, speed), + ServerEvent::Shockwave {shockwave} => handle_shockwave(self, shockwave), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 8dbb73ce5c..457908ef38 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -79,6 +79,7 @@ fn maps_basic_melee() { &CharacterState::BasicMelee(states::basic_melee::Data { buildup_duration: Duration::default(), recover_duration: Duration::default(), + knockback: 0.0, base_healthchange: 10, range: 1.0, max_angle: 1.0, From 829d8a20d1e60ac883c428180c71e69b19369fb1 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 8 Aug 2020 18:22:21 -0400 Subject: [PATCH 04/17] Add shockwave system to handle shockwaves colliding with other entities --- common/src/comp/inventory/item/tool.rs | 10 + common/src/comp/shockwave.rs | 31 ++- common/src/event.rs | 11 +- common/src/msg/ecs_packet.rs | 5 + common/src/state.rs | 1 + common/src/states/charged_ranged.rs | 3 +- common/src/states/ground_shockwave.rs | 16 +- common/src/sys/combat.rs | 4 +- common/src/sys/mod.rs | 3 + common/src/sys/phys.rs | 4 +- common/src/sys/shockwave.rs | 357 +++++++++++++++++++++++++ server/src/events/entity_creation.rs | 14 +- server/src/events/mod.rs | 8 +- server/src/state_ext.rs | 23 ++ server/src/sys/sentinel.rs | 17 +- 15 files changed, 473 insertions(+), 34 deletions(-) create mode 100644 common/src/sys/shockwave.rs diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 0d2530b7ea..f7c4489465 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -116,6 +116,16 @@ impl Tool { match &self.kind { Sword(_) => vec![ + GroundShockwave { + energy_cost: 0, + buildup_duration: Duration::from_millis(1000), + recover_duration: Duration::from_millis(2000), + damage: 300, + knockback: -30.0, + shockwave_angle: 15.0, + shockwave_speed: 10.0, + shockwave_duration: Duration::from_millis(3000), + }, TripleStrike { base_damage: (60.0 * self.base_power()) as u32, needs_timing: false, diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index 49d5a28251..e53d611716 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -1,25 +1,36 @@ -use crate::{ - comp::phys::{Ori, Pos}, - sync::Uid, -}; +use crate::sync::Uid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; use std::time::Duration; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Shockwave { - pub shockwave_origin: Pos, - pub shockwave_direction: Ori, - pub shockwave_angle: f32, - pub shockwave_speed: f32, - pub shockwave_duration: Duration, +pub struct Properties { + pub angle: f32, + pub speed: f32, pub damage: u32, pub knockback: f32, pub requires_ground: bool, + pub duration: Duration, pub owner: Option, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Shockwave { + pub properties: Properties, + #[serde(skip)] + /// Time that the shockwave was created at + /// Used to calculate shockwave propagation + /// Deserialized from the network as `None` + pub creation: Option, +} + impl Component for Shockwave { type Storage = FlaggedStorage>; } + +impl std::ops::Deref for Shockwave { + type Target = Properties; + + fn deref(&self) -> &Properties { &self.properties } +} diff --git a/common/src/event.rs b/common/src/event.rs index e2bd4d1ea4..616fb5736a 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,5 +1,8 @@ use crate::{character::CharacterId, comp, sync::Uid, util::Dir}; -use comp::item::{Item, Reagent}; +use comp::{ + item::{Item, Reagent}, + Ori, Pos, +}; use parking_lot::Mutex; use specs::Entity as EcsEntity; use std::{collections::VecDeque, ops::DerefMut}; @@ -48,7 +51,11 @@ pub enum ServerEvent { gravity: Option, speed: f32, }, - Shockwave {shockwave: comp::Shockwave}, + Shockwave { + properties: comp::shockwave::Properties, + pos: Pos, + ori: Ori, + }, LandOnGround { entity: EcsEntity, vel: Vec3, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 726d67321a..244733612b 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -29,6 +29,7 @@ sum_type! { Pos(comp::Pos), Vel(comp::Vel), Ori(comp::Ori), + Shockwave(comp::Shockwave), } } // Automatically derive From for EcsCompPhantom @@ -56,6 +57,7 @@ sum_type! { Pos(PhantomData), Vel(PhantomData), Ori(PhantomData), + Shockwave(PhantomData), } } impl sync::CompPacket for EcsCompPacket { @@ -83,6 +85,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Shockwave(comp) => sync::handle_insert(comp, entity, world), } } @@ -108,6 +111,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Shockwave(comp) => sync::handle_modify(comp, entity, world), } } @@ -137,6 +141,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::Pos(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Vel(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Ori(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::Shockwave(_) => sync::handle_remove::(entity, world), } } } diff --git a/common/src/state.rs b/common/src/state.rs index 78f34068fb..bc203820cb 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -126,6 +126,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index c16f594711..8f501785d3 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -155,7 +155,8 @@ impl CharacterBehavior for Data { projectile, light: self.projectile_light, gravity: self.projectile_gravity, - speed: self.initial_projectile_speed + charge_amount * (self.max_projectile_speed - self.initial_projectile_speed), + speed: self.initial_projectile_speed + + charge_amount * (self.max_projectile_speed - self.initial_projectile_speed), }); update.character = CharacterState::ChargedRanged(Data { diff --git a/common/src/states/ground_shockwave.rs b/common/src/states/ground_shockwave.rs index c9fc147dce..124684a45a 100644 --- a/common/src/states/ground_shockwave.rs +++ b/common/src/states/ground_shockwave.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{Attacking, CharacterState, Shockwave, StateUpdate}, + comp::{shockwave, Attacking, CharacterState, StateUpdate}, event::ServerEvent, states::utils::*, sys::character_behavior::*, @@ -51,19 +51,19 @@ impl CharacterBehavior for Data { }); } else if !self.exhausted { // Attack - let shockwave = Shockwave { - shockwave_origin: *data.pos, - shockwave_direction: *data.ori, - shockwave_angle: self.shockwave_angle, - shockwave_speed: self.shockwave_speed, - shockwave_duration: self.shockwave_duration, + let properties = shockwave::Properties { + angle: self.shockwave_angle, + speed: self.shockwave_speed, + duration: self.shockwave_duration, damage: self.damage, knockback: self.knockback, requires_ground: true, owner: Some(*data.uid), }; update.server_events.push_front(ServerEvent::Shockwave { - shockwave, + properties, + pos: *data.pos, + ori: *data.ori, }); update.character = CharacterState::GroundShockwave(Data { diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index 2de2ea4058..f4f4352ecc 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -33,8 +33,8 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Stats>, ReadStorage<'a, Loadout>, ReadStorage<'a, group::Group>, + ReadStorage<'a, CharacterState>, WriteStorage<'a, Attacking>, - WriteStorage<'a, CharacterState>, ); fn run( @@ -52,8 +52,8 @@ impl<'a> System<'a> for Sys { stats, loadouts, groups, - mut attacking_storage, character_states, + mut attacking_storage, ): Self::SystemData, ) { let start_time = std::time::Instant::now(); diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index 832dc37d63..c5716b6211 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -5,6 +5,7 @@ pub mod controller; mod mount; pub mod phys; mod projectile; +mod shockwave; mod stats; // External @@ -18,6 +19,7 @@ pub const CONTROLLER_SYS: &str = "controller_sys"; pub const MOUNT_SYS: &str = "mount_sys"; pub const PHYS_SYS: &str = "phys_sys"; pub const PROJECTILE_SYS: &str = "projectile_sys"; +pub const SHOCKWAVE_SYS: &str = "shockwave_sys"; pub const STATS_SYS: &str = "stats_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { @@ -30,5 +32,6 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(stats::Sys, STATS_SYS, &[]); dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS, MOUNT_SYS, STATS_SYS]); dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]); + dispatch_builder.add(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]); dispatch_builder.add(combat::Sys, COMBAT_SYS, &[PROJECTILE_SYS]); } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 36a5ed75f6..15b3bb0c54 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -173,7 +173,7 @@ impl<'a> System<'a> for Sys { mass_other, collider_other, _, - group, + group_b, ) in ( &entities, &uids, @@ -186,7 +186,7 @@ impl<'a> System<'a> for Sys { ) .join() { - if entity == entity_other || (ignore_group.is_some() && ignore_group == group) { + if entity == entity_other || (ignore_group.is_some() && ignore_group == group_b) { continue; } diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs new file mode 100644 index 0000000000..ee78384701 --- /dev/null +++ b/common/src/sys/shockwave.rs @@ -0,0 +1,357 @@ +use crate::{ + comp::{ + group, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource, Last, + Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, Stats, + }, + event::{EventBus, LocalEvent, ServerEvent}, + state::{DeltaTime, Time}, + sync::{Uid, UidAllocator}, + util::Dir, +}; +use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage}; +use vek::*; + +pub const BLOCK_ANGLE: f32 = 180.0; + +/// This system is responsible for handling accepted inputs like moving or +/// attacking +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + Read<'a, EventBus>, + Read<'a, EventBus>, + Read<'a, Time>, + Read<'a, DeltaTime>, + Read<'a, UidAllocator>, + ReadStorage<'a, Uid>, + ReadStorage<'a, Pos>, + ReadStorage<'a, Last>, + ReadStorage<'a, Ori>, + ReadStorage<'a, Scale>, + ReadStorage<'a, Body>, + ReadStorage<'a, Stats>, + ReadStorage<'a, Loadout>, + ReadStorage<'a, group::Group>, + ReadStorage<'a, CharacterState>, + ReadStorage<'a, PhysicsState>, + WriteStorage<'a, Shockwave>, + ); + + fn run( + &mut self, + ( + entities, + server_bus, + local_bus, + time, + dt, + uid_allocator, + uids, + positions, + last_positions, + orientations, + scales, + bodies, + stats, + loadouts, + groups, + character_states, + physics_states, + mut shockwaves, + ): Self::SystemData, + ) { + let mut server_emitter = server_bus.emitter(); + let mut local_emitter = local_bus.emitter(); + + let time = time.0; + let dt = dt.0; + + // Shockwaves + for (entity, uid, pos, ori, shockwave) in + (&entities, &uids, &positions, &orientations, &shockwaves).join() + { + let creation_time = match shockwave.creation { + Some(time) => time, + // Skip newly created shockwaves + None => continue, + }; + + let end_time = creation_time + shockwave.duration.as_secs_f64(); + + // If shockwave is out of time emit destroy event but still continue since it + // may have traveled and produced effects a bit before reaching it's + // end point + if end_time < time { + server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }); + } + + // Determine area that was covered by the shockwave in the last tick + let frame_time = dt.min((end_time - time) as f32); + if frame_time <= 0.0 { + continue; + } + + // Note: min() probably uneeded + let time_since_creation = (time - creation_time) as f32; + let frame_start_dist = (shockwave.speed * (time_since_creation - frame_time)).max(0.0); + let frame_end_dist = (shockwave.speed * time_since_creation).max(frame_start_dist); + let pos2 = Vec2::from(pos.0); + + // From one frame to the next a shockwave travels over a strip of an arc + // This is used for collision detection + let arc_strip = ArcStrip { + origin: pos2, + // TODO: make sure this is not Vec2::new(0.0, 0.0) + dir: ori.0.xy(), + angle: shockwave.angle, + start: frame_start_dist, + end: frame_end_dist, + }; + + // Group to ignore collisions with + // Might make this more nuanced if shockwaves are used for non damage effects + let group = shockwave + .owner + .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) + .and_then(|e| groups.get(e)); + + // Go through all other effectable entities + for ( + b, + uid_b, + pos_b, + last_pos_b_maybe, + ori_b, + scale_b_maybe, + character_b, + stats_b, + body_b, + physics_state_b, + ) in ( + &entities, + &uids, + &positions, + // TODO: make sure that these are maintained on the client and remove `.maybe()` + last_positions.maybe(), + &orientations, + scales.maybe(), + character_states.maybe(), + &stats, + &bodies, + &physics_states, + ) + .join() + { + // 2D versions + let pos_b2 = pos_b.0.xy(); + let last_pos_b2_maybe = last_pos_b_maybe.map(|p| (p.0).0.xy()); + + // Scales + let scale_b = scale_b_maybe.map_or(1.0, |s| s.0); + let rad_b = body_b.radius() * scale_b; + + // Check if it is a hit + let hit = entity != b + && !stats_b.is_dead + // Collision shapes + && { + // TODO: write code to collide rect with the arc strip so that we can do + // more complete collision detection for rapidly moving entities + arc_strip.collides_with_circle(Circle { + pos: pos_b2, + radius: rad_b, + }) || last_pos_b2_maybe.map_or(false, |pos| { + arc_strip.collides_with_circle(Circle { pos, radius: rad_b }) + }) + } + && (!shockwave.requires_ground || physics_state_b.on_ground); + + if hit { + // See if entities are in the same group + let same_group = group + .map(|group_a| Some(group_a) == groups.get(b)) + .unwrap_or(Some(*uid_b) == shockwave.owner); + + // Don't damage in the same group + if same_group { + continue; + } + + // Weapon gives base damage + let source = DamageSource::Melee; + + let mut damage = Damage { + healthchange: -(shockwave.damage as f32), + source, + }; + + let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) + // TODO: investigate whether this calculation is proper for shockwaves + && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; + + if let Some(loadout) = loadouts.get(b) { + damage.modify_damage(block, loadout); + } + + if damage.healthchange != 0.0 { + server_emitter.emit(ServerEvent::Damage { + uid: *uid_b, + change: HealthChange { + amount: damage.healthchange as i32, + cause: HealthSource::Attack { + by: shockwave.owner.unwrap_or(*uid), + }, + }, + }); + } + if shockwave.knockback != 0.0 { + local_emitter.emit(LocalEvent::ApplyForce { + entity: b, + force: shockwave.knockback + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), + }); + } + } + } + } + + // Set start time on new shockwaves + // This change doesn't need to be recorded as it is not sent to the client + shockwaves.set_event_emission(false); + (&mut shockwaves).join().for_each(|shockwave| { + if shockwave.creation.is_none() { + shockwave.creation = Some(time); + } + }); + shockwaves.set_event_emission(true); + } +} + +#[derive(Clone, Copy)] +struct ArcStrip { + origin: Vec2, + /// Normalizable direction + dir: Vec2, + /// Angle in degrees + angle: f32, + /// Start radius + start: f32, + /// End radius + end: f32, +} + +impl ArcStrip { + fn collides_with_circle(self, c: Circle) -> bool { + // Quit if aabb's don't collide + if (self.origin.x - c.pos.x).abs() > self.end + c.radius + || (self.origin.y - c.pos.y).abs() > self.end + c.radius + { + return false; + } + + let dist = self.origin.distance(c.pos); + let half_angle = self.angle.to_radians() / 2.0; + + if dist > self.end + c.radius || dist + c.radius < self.start { + // Completely inside or outside full ring + return false; + } + + let inside_edge = Circle { + pos: self.origin, + radius: self.start, + }; + let outside_edge = Circle { + pos: self.origin, + radius: self.end, + }; + let inner_corner_in_circle = || { + let midpoint = self.dir.normalized() * self.start; + c.contains_point(midpoint.rotated_z(half_angle) + self.origin) + || c.contains_point(midpoint.rotated_z(-half_angle) + self.origin) + }; + let arc_segment_in_circle = || { + let midpoint = self.dir.normalized(); + let segment_in_circle = |angle| { + let dir = midpoint.rotated_z(angle); + let side = LineSegment2 { + start: dir * self.start + self.origin, + end: dir * self.end + self.origin, + }; + c.contains_point(side.projected_point(c.pos)) + }; + segment_in_circle(half_angle) || segment_in_circle(-half_angle) + }; + + if dist > self.end { + // Circle center is outside ring + // Check intersection with line segments + arc_segment_in_circle() || { + // Check angle of intersection points on outside edge of ring + let (p1, p2) = outside_edge.intersection_points(c, dist); + self.dir.angle_between(p1 - self.origin) < half_angle + || self.dir.angle_between(p2 - self.origin) < half_angle + } + } else if dist < self.start { + // Circle center is inside ring + // Check angle of intersection points on inside edge of ring + // Check if circle contains one of the inner points of the arc + inner_corner_in_circle() + || ( + // Check that the circles aren't identical + !inside_edge.is_approx_eq(c) && { + let (p1, p2) = inside_edge.intersection_points(c, dist); + self.dir.angle_between(p1 - self.origin) < half_angle + || self.dir.angle_between(p2 - self.origin) < half_angle + } + ) + } else if c.radius > dist { + // Circle center inside ring + // but center of ring is inside the circle so we can't calculate the angle + inner_corner_in_circle() + } else { + // Circle center inside ring + // Calculate extra angle to account for circle radius + let extra_angle = (c.radius / dist).asin(); + self.dir.angle_between(c.pos - self.origin) < half_angle + extra_angle + } + } +} + +#[derive(Clone, Copy)] +struct Circle { + pos: Vec2, + radius: f32, +} +impl Circle { + // Assumes an intersection is occuring at 2 points + // Uses precalculated distance + // https://www.xarg.org/2016/07/calculate-the-intersection-points-of-two-circles/ + fn intersection_points(self, other: Self, dist: f32) -> (Vec2, Vec2) { + let e = (other.pos - self.pos) / dist; + + let x = (self.radius.powi(2) - other.radius.powi(2) + dist.powi(2)) / (2.0 * dist); + let y = (self.radius.powi(2) - x.powi(2)).sqrt(); + + let pxe = self.pos + x * e; + let eyx = e.yx(); + + let p1 = pxe + Vec2::new(-y, y) * eyx; + let p2 = pxe + Vec2::new(y, -y) * eyx; + + (p1, p2) + } + + fn contains_point(self, point: Vec2) -> bool { + point.distance_squared(self.pos) < self.radius.powi(2) + } + + fn is_approx_eq(self, other: Self) -> bool { + (self.pos - other.pos).is_approx_zero() && self.radius - other.radius < 0.001 + } +} diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index f120bd7e34..586d8400e7 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -2,8 +2,8 @@ use crate::{sys, Server, StateExt}; use common::{ character::CharacterId, comp::{ - self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, Agent, Alignment, Body, Gravity, Item, - ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Shockwave, Stats, Vel, WaypointArea, + self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, shockwave, Agent, Alignment, Body, Gravity, Item, + ItemDrop, LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, }, outcome::Outcome, util::Dir, @@ -124,10 +124,14 @@ pub fn handle_shoot( builder.build(); } -pub fn handle_shockwave(server: &mut Server, shockwave: Shockwave) { +pub fn handle_shockwave( + server: &mut Server, + properties: shockwave::Properties, + pos: Pos, + ori: Ori, +) { let state = server.state_mut(); - let builder = state.create_shockwave(shockwave); - builder.build(); + state.create_shockwave(properties, pos, ori).build(); } pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 6cdd018c0b..63ded0012f 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -5,7 +5,7 @@ use common::{ }; use entity_creation::{ handle_create_npc, handle_create_waypoint, handle_initialize_character, - handle_loaded_character_data, handle_shoot, + handle_loaded_character_data, handle_shockwave, handle_shoot, }; use entity_manipulation::{ handle_damage, handle_destroy, handle_explosion, handle_land_on_ground, handle_level_up, @@ -70,7 +70,11 @@ impl Server { gravity, speed, } => handle_shoot(self, entity, dir, body, light, projectile, gravity, speed), - ServerEvent::Shockwave {shockwave} => handle_shockwave(self, shockwave), + ServerEvent::Shockwave { + properties, + pos, + ori, + } => handle_shockwave(self, properties, pos, ori), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 97c3be95ec..bb577f2f1b 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -39,6 +39,13 @@ pub trait StateExt { body: comp::Body, projectile: comp::Projectile, ) -> EcsEntityBuilder; + /// Build a shockwave entity + fn create_shockwave( + &mut self, + properties: comp::shockwave::Properties, + pos: comp::Pos, + ori: comp::Ori, + ) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId); /// Update the components associated with the entity's current character. @@ -134,6 +141,22 @@ impl StateExt for State { .with(comp::Sticky) } + fn create_shockwave( + &mut self, + properties: comp::shockwave::Properties, + pos: comp::Pos, + ori: comp::Ori, + ) -> EcsEntityBuilder { + self.ecs_mut() + .create_entity_synced() + .with(pos) + .with(ori) + .with(comp::Shockwave { + properties, + creation: None, + }) + } + fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { let spawn_point = self.ecs().read_resource::().0; diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 5fb64ddd5f..ea208acd1b 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -2,7 +2,8 @@ use super::SysTimer; use common::{ comp::{ Body, CanBuild, CharacterState, Collider, Energy, Gravity, Group, Item, LightEmitter, - Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel, + Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Shockwave, Stats, Sticky, + Vel, }, msg::EcsCompPacket, span, @@ -57,6 +58,7 @@ pub struct TrackedComps<'a> { pub gravity: ReadStorage<'a, Gravity>, pub loadout: ReadStorage<'a, Loadout>, pub character_state: ReadStorage<'a, CharacterState>, + pub shockwave: ReadStorage<'a, Shockwave>, } impl<'a> TrackedComps<'a> { pub fn create_entity_package( @@ -132,6 +134,11 @@ impl<'a> TrackedComps<'a> { .get(entity) .cloned() .map(|c| comps.push(c.into())); + self.shockwave + .get(entity) + .cloned() + .map(|c| comps.push(c.into())); + // Add untracked comps // Add untracked comps pos.map(|c| comps.push(c.into())); vel.map(|c| comps.push(c.into())); @@ -160,6 +167,7 @@ pub struct ReadTrackers<'a> { pub gravity: ReadExpect<'a, UpdateTracker>, pub loadout: ReadExpect<'a, UpdateTracker>, pub character_state: ReadExpect<'a, UpdateTracker>, + pub shockwave: ReadExpect<'a, UpdateTracker>, } impl<'a> ReadTrackers<'a> { pub fn create_sync_packages( @@ -197,7 +205,8 @@ impl<'a> ReadTrackers<'a> { &*self.character_state, &comps.character_state, filter, - ); + ) + .with_component(&comps.uid, &*self.shockwave, &comps.shockwave, filter); (entity_sync_package, comp_sync_package) } @@ -223,6 +232,7 @@ pub struct WriteTrackers<'a> { gravity: WriteExpect<'a, UpdateTracker>, loadout: WriteExpect<'a, UpdateTracker>, character_state: WriteExpect<'a, UpdateTracker>, + shockwave: WriteExpect<'a, UpdateTracker>, } fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { @@ -247,6 +257,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers .character_state .record_changes(&comps.character_state); + trackers.shockwave.record_changes(&comps.shockwave); // Debug how many updates are being sent /* macro_rules! log_counts { @@ -278,6 +289,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { log_counts!(gravity, "Gravitys"); log_counts!(loadout, "Loadouts"); log_counts!(character_state, "Character States"); + log_counts!(shockwave, "Shockwaves"); */ } @@ -300,6 +312,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); + world.register_tracker::(); } /// Deleted entities grouped by region From f5dad20899f4516925a84712df66dd0dc7312957 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 9 Aug 2020 18:11:58 -0500 Subject: [PATCH 05/17] Tweaked shockwave values. Added shockwave damage type. Changed how knockback was handled in shockwaves to make negative knockback work better. --- common/src/comp/damage.rs | 18 ++++++++++++++++++ common/src/comp/inventory/item/tool.rs | 22 +++++++++++----------- common/src/states/ground_shockwave.rs | 7 +++---- common/src/sys/shockwave.rs | 20 ++++++++++++++------ 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs index 05cd6919a4..3d2f7b4c0b 100644 --- a/common/src/comp/damage.rs +++ b/common/src/comp/damage.rs @@ -15,6 +15,7 @@ pub enum DamageSource { Projectile, Explosion, Falling, + Shockwave, } impl Damage { @@ -74,6 +75,23 @@ impl Damage { self.healthchange = -10.0; } }, + DamageSource::Shockwave => { + // Critical hit + if rand::random() { + self.healthchange *= 1.2; + } + // Block + if block { + self.healthchange *= 1.0 - BLOCK_EFFICIENCY + } + // Armor + self.healthchange *= 1.0 - loadout.get_damage_reduction(); + + // Min damage + if self.healthchange > -10.0 { + self.healthchange = -10.0; + } + }, _ => {}, } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index f7c4489465..aa939ef37e 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -118,11 +118,11 @@ impl Tool { Sword(_) => vec![ GroundShockwave { energy_cost: 0, - buildup_duration: Duration::from_millis(1000), - recover_duration: Duration::from_millis(2000), - damage: 300, + buildup_duration: Duration::from_millis(500), + recover_duration: Duration::from_millis(1000), + damage: 500, knockback: -30.0, - shockwave_angle: 15.0, + shockwave_angle: 90.0, shockwave_speed: 10.0, shockwave_duration: Duration::from_millis(3000), }, @@ -363,20 +363,20 @@ impl Tool { vec![ BasicMelee { energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(300), - knockback: 20.0, + buildup_duration: Duration::from_millis(500), + recover_duration: Duration::from_millis(250), + knockback: 25.0, base_healthchange: -200, range: 10.0, max_angle: 120.0, }, GroundShockwave { energy_cost: 0, - buildup_duration: Duration::from_millis(1000), - recover_duration: Duration::from_millis(2000), - damage: 300, + buildup_duration: Duration::from_millis(500), + recover_duration: Duration::from_millis(1000), + damage: 500, knockback: -30.0, - shockwave_angle: 15.0, + shockwave_angle: 90.0, shockwave_speed: 10.0, shockwave_duration: Duration::from_millis(3000), }, diff --git a/common/src/states/ground_shockwave.rs b/common/src/states/ground_shockwave.rs index 124684a45a..10041aec84 100644 --- a/common/src/states/ground_shockwave.rs +++ b/common/src/states/ground_shockwave.rs @@ -31,13 +31,12 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_move(data, &mut update, 0.7); - handle_jump(data, &mut update); + handle_move(data, &mut update, 0.05); if self.buildup_duration != Duration::default() { // Build up update.character = CharacterState::GroundShockwave(Data { - exhausted: false, + exhausted: self.exhausted, buildup_duration: self .buildup_duration .checked_sub(Duration::from_secs_f32(data.dt.0)) @@ -79,7 +78,7 @@ impl CharacterBehavior for Data { } else if self.recover_duration != Duration::default() { // Recovery update.character = CharacterState::GroundShockwave(Data { - exhausted: false, + exhausted: self.exhausted, buildup_duration: self.buildup_duration, recover_duration: self .recover_duration diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index ee78384701..1e19f95735 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -183,7 +183,7 @@ impl<'a> System<'a> for Sys { } // Weapon gives base damage - let source = DamageSource::Melee; + let source = DamageSource::Shockwave; let mut damage = Damage { healthchange: -(shockwave.damage as f32), @@ -210,11 +210,19 @@ impl<'a> System<'a> for Sys { }); } if shockwave.knockback != 0.0 { - local_emitter.emit(LocalEvent::ApplyForce { - entity: b, - force: shockwave.knockback - * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), - }); + if shockwave.knockback < 0.0 { + local_emitter.emit(LocalEvent::ApplyForce { + entity: b, + force: shockwave.knockback + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.5), + }); + } else { + local_emitter.emit(LocalEvent::ApplyForce { + entity: b, + force: shockwave.knockback + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), + }); + } } } } From 23fa3c2f8d8c576e946260c4315f948f6f437562 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 15 Aug 2020 18:52:48 -0500 Subject: [PATCH 06/17] Added particles to shockwave attack. --- voxygen/src/scene/particle.rs | 45 ++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 127530d541..db00f3d6b1 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -8,7 +8,7 @@ use crate::{ }; use common::{ assets::Asset, - comp::{item::Reagent, object, Body, CharacterState, Pos}, + comp::{item::Reagent, object, Body, CharacterState, Ori, Pos, Shockwave}, figure::Segment, outcome::Outcome, span, @@ -113,6 +113,7 @@ impl ParticleMgr { self.maintain_body_particles(scene_data); self.maintain_boost_particles(scene_data); self.maintain_block_particles(scene_data, terrain); + self.maintain_shockwave_particles(scene_data); } else { // remove all particle lifespans self.particles.clear(); @@ -419,6 +420,48 @@ impl ParticleMgr { } } } + fn maintain_shockwave_particles(&mut self, scene_data: &SceneData) { + let state = scene_data.state; + let ecs = state.ecs(); + let time = state.get_time(); + + for (_i, (_entity, pos, ori, shockwave)) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ) + .join() + .enumerate() + { + let elapsed = time - shockwave.creation.unwrap_or_default(); + + let p = shockwave.properties.speed * elapsed as f32; + + let theta = ori.0.y.atan2(ori.0.x); + let dtheta = shockwave.properties.angle.to_radians() / p; + + for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) { + for d in 0..(p as i32) { + self.particles.push(Particle::new( + Duration::from_millis(250), + time, + ParticleMode::CampfireFire, // TODO: TerrainShockwave + pos.0 + + Vec3::new( + p * ((theta - (shockwave.properties.angle.to_radians() / 2.0)) + + (dtheta * d as f32)) + .cos(), + p * ((theta - (shockwave.properties.angle.to_radians() / 2.0)) + + (dtheta * d as f32)) + .sin(), + 0.0, + ), + )); + } + } + } + } fn upload_particles(&mut self, renderer: &mut Renderer) { span!(_guard, "upload_particles", "ParticleMgr::upload_particles"); From 3e4615daea7db1feb5ae277d1c4b36e7ef14f97d Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 15 Aug 2020 22:03:08 -0500 Subject: [PATCH 07/17] Loadouts are now generated with loadout_builder.rs. Creatures spawned naturally and via command are now spawned with the same mechanics. --- common/src/loadout_builder.rs | 154 +++++++++++++++++++++++++++++++++- server/src/cmd.rs | 2 +- server/src/sys/terrain.rs | 118 +------------------------- 3 files changed, 156 insertions(+), 118 deletions(-) diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 3f40faccb9..e890778ef2 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -1,4 +1,10 @@ -use crate::comp::{item::Item, Body, CharacterAbility, ItemConfig, Loadout}; +use crate::{ + comp::{ + item::{Item, ItemKind}, + Alignment, Body, biped_large, CharacterAbility, ItemConfig, Loadout, + }, +}; +use rand::Rng; use std::time::Duration; /// Builder for character Loadouts, containing weapon and armour items belonging @@ -60,6 +66,151 @@ impl LoadoutBuilder { ))) } + /// Builds loadout of creature when spawned + pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option) -> Self { + match body { + Body::BipedLarge(biped_large) => match biped_large.species { + biped_large::Species::Cyclops => { + main_tool = Some(Item::new_from_asset_expect("common.items.weapons.bossweapon.cyclops_hammer")); + }, + _ => {}, + } + _ => {}, + }; + + let active_item = + if let Some(ItemKind::Tool(tool)) = main_tool.as_ref().map(|i| i.kind()) { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); + + main_tool.map(|item| ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: None, + dodge_ability: Some(CharacterAbility::Roll), + }) + } else { + Some(ItemConfig { + // We need the empty item so npcs can attack + item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), + ability1: Some(CharacterAbility::BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(400), + base_healthchange: -40, + knockback: 0.0, + range: 3.5, + max_angle: 15.0, + }), + ability2: None, + ability3: None, + block_ability: None, + dodge_ability: None, + }) + }; + + let loadout = match body { + Body::Humanoid(_) => match alignment { + Alignment::Npc => Loadout { + active_item, + second_item: None, + shoulder: None, + chest: Some(Item::new_from_asset_expect( + match rand::thread_rng().gen_range(0, 10) { + 0 => "common.items.armor.chest.worker_green_0", + 1 => "common.items.armor.chest.worker_green_1", + 2 => "common.items.armor.chest.worker_red_0", + 3 => "common.items.armor.chest.worker_red_1", + 4 => "common.items.armor.chest.worker_purple_0", + 5 => "common.items.armor.chest.worker_purple_1", + 6 => "common.items.armor.chest.worker_yellow_0", + 7 => "common.items.armor.chest.worker_yellow_1", + 8 => "common.items.armor.chest.worker_orange_0", + _ => "common.items.armor.chest.worker_orange_1", + }, + )), + belt: Some(Item::new_from_asset_expect( + "common.items.armor.belt.leather_0", + )), + hand: None, + pants: Some(Item::new_from_asset_expect( + "common.items.armor.pants.worker_blue_0", + )), + foot: Some(Item::new_from_asset_expect( + match rand::thread_rng().gen_range(0, 2) { + 0 => "common.items.armor.foot.leather_0", + _ => "common.items.armor.starter.sandals_0", + }, + )), + back: None, + ring: None, + neck: None, + lantern: None, + glider: None, + head: None, + tabard: None, + }, + Alignment::Enemy => Loadout { + active_item, + second_item: None, + shoulder: Some(Item::new_from_asset_expect( + "common.items.armor.shoulder.cultist_shoulder_purple", + )), + chest: Some(Item::new_from_asset_expect( + "common.items.armor.chest.cultist_chest_purple", + )), + belt: Some(Item::new_from_asset_expect( + "common.items.armor.belt.cultist_belt", + )), + hand: Some(Item::new_from_asset_expect( + "common.items.armor.hand.cultist_hands_purple", + )), + pants: Some(Item::new_from_asset_expect( + "common.items.armor.pants.cultist_legs_purple", + )), + foot: Some(Item::new_from_asset_expect( + "common.items.armor.foot.cultist_boots", + )), + back: Some(Item::new_from_asset_expect( + "common.items.armor.back.dungeon_purple-0", + )), + ring: None, + neck: None, + lantern: Some(Item::new_from_asset_expect("common.items.lantern.black_0")), + glider: None, + head: None, + tabard: None, + }, + _ => LoadoutBuilder::animal(body).build(), + }, + Body::BipedLarge(biped_large) => match biped_large.species { + biped_large::Species::Cyclops => Loadout { + active_item, + second_item: None, + shoulder: None, + chest: None, + belt: None, + hand: None, + pants: None, + foot: None, + back: None, + ring: None, + neck: None, + lantern: None, + glider: None, + head: None, + tabard: None, + }, + _ => LoadoutBuilder::animal(body).build(), + } + _ => LoadoutBuilder::animal(body).build(), + }; + + Self(loadout) + } + /// Default animal configuration pub fn animal(body: Body) -> Self { Self(Loadout { @@ -114,6 +265,7 @@ impl LoadoutBuilder { Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref)) } + pub fn active_item(mut self, item: Option) -> Self { self.0.active_item = item; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7a8057a639..e1799417c6 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -582,7 +582,7 @@ fn handle_spawn( .create_npc( pos, comp::Stats::new(get_npc_name(id).into(), body), - LoadoutBuilder::animal(body).build(), + LoadoutBuilder::build_loadout(body, alignment, None).build(), body, ) .with(comp::Vel(vel)) diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index a48c16356f..46b7cdd1ac 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -3,8 +3,7 @@ use crate::{chunk_generator::ChunkGenerator, client::Client, Tick}; use common::{ comp::{ self, bird_medium, - item::{self}, - Alignment, CharacterAbility, ItemConfig, Player, Pos, + Alignment, CharacterAbility, Player, Pos, }, event::{EventBus, ServerEvent}, generation::get_npc_name, @@ -124,120 +123,7 @@ impl<'a> System<'a> for Sys { // let damage = stats.level.level() as i32; TODO: Make NPC base damage // non-linearly depend on their level - let active_item = if let Some(item::ItemKind::Tool(tool)) = - main_tool.as_ref().map(|i| i.kind()) - { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); - - main_tool.map(|item| comp::ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: None, - dodge_ability: Some(comp::CharacterAbility::Roll), - }) - } else { - Some(ItemConfig { - // We need the empty item so npcs can attack - item: comp::Item::new_from_asset_expect("common.items.weapons.empty.empty"), - ability1: Some(CharacterAbility::BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(400), - base_healthchange: -40, - knockback: 0.0, - range: 3.5, - max_angle: 15.0, - }), - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }) - }; - - let mut loadout = match alignment { - comp::Alignment::Npc => comp::Loadout { - active_item, - second_item: None, - shoulder: None, - chest: Some(comp::Item::new_from_asset_expect( - match rand::thread_rng().gen_range(0, 10) { - 0 => "common.items.npc_armor.chest.worker_green_0", - 1 => "common.items.npc_armor.chest.worker_green_1", - 2 => "common.items.npc_armor.chest.worker_red_0", - 3 => "common.items.npc_armor.chest.worker_red_1", - 4 => "common.items.npc_armor.chest.worker_purple_0", - 5 => "common.items.npc_armor.chest.worker_purple_1", - 6 => "common.items.npc_armor.chest.worker_yellow_0", - 7 => "common.items.npc_armor.chest.worker_yellow_1", - 8 => "common.items.npc_armor.chest.worker_orange_0", - _ => "common.items.npc_armor.chest.worker_orange_1", - }, - )), - belt: Some(comp::Item::new_from_asset_expect( - "common.items.armor.belt.leather_0", - )), - hand: None, - pants: Some(comp::Item::new_from_asset_expect( - "common.items.armor.pants.worker_blue_0", - )), - foot: Some(comp::Item::new_from_asset_expect( - match rand::thread_rng().gen_range(0, 2) { - 0 => "common.items.armor.foot.leather_0", - _ => "common.items.armor.starter.sandals_0", - }, - )), - back: None, - ring: None, - neck: None, - lantern: None, - glider: None, - head: None, - tabard: None, - }, - comp::Alignment::Enemy => comp::Loadout { - active_item, - second_item: None, - shoulder: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.shoulder.cultist_shoulder_purple", - )), - chest: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.chest.cultist_chest_purple", - )), - belt: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.belt.cultist_belt", - )), - hand: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.hand.cultist_hands_purple", - )), - pants: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.pants.cultist_legs_purple", - )), - foot: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.foot.cultist_boots", - )), - back: Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.back.dungeon_purple-0", - )), - ring: None, - neck: None, - lantern: Some(comp::Item::new_from_asset_expect( - "common.items.lantern.black_0", - )), - glider: None, - head: None, - tabard: None, - }, - _ => LoadoutBuilder::animal(entity.body).build(), - }; - - loadout = match body { - comp::Body::Humanoid(_) => loadout, - _ => LoadoutBuilder::animal(entity.body).build(), - }; + let mut loadout = LoadoutBuilder::build_loadout(body, alignment, main_tool).build(); let mut scale = entity.scale; From 0c48efd44534add4f145c90fa1b29a1189ea7d92 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 15 Aug 2020 23:47:51 -0500 Subject: [PATCH 08/17] Added particles dedicated for shockwave. --- assets/voxygen/shaders/particle-vert.glsl | 11 +++++++++++ voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 32de189e4c..1843774022 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -47,6 +47,7 @@ const int FIREWORK_YELLOW = 8; const int LEAF = 9; const int FIREFLY = 10; const int BEE = 11; +const int GROUND_SHOCKWAVE = 12; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -240,6 +241,16 @@ void main() { vec4(vec3(1, 0.7, 0), 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); + } else if (inst_mode == GROUND_SHOCKWAVE) { + attr = Attr( + linear_motion( + vec3(rand0 * 0.25, rand1 * 0.25, 0.0), + vec3(rand2 * 0.1, rand3 * 0.1, 0.0) + ), + 3.0 + 5.0 * rand5, + vec4(vec3(0.42, 0.32, 0.1), 1), + spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) + ); } else { attr = Attr( linear_motion( diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 5854009517..ba55782e50 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -105,6 +105,7 @@ pub enum ParticleMode { Leaf = 9, Firefly = 10, Bee = 11, + GroundShockwave = 12, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index db00f3d6b1..48878578df 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -446,7 +446,7 @@ impl ParticleMgr { self.particles.push(Particle::new( Duration::from_millis(250), time, - ParticleMode::CampfireFire, // TODO: TerrainShockwave + ParticleMode::GroundShockwave, pos.0 + Vec3::new( p * ((theta - (shockwave.properties.angle.to_radians() / 2.0)) From 1f2ed53c4223ce62b2a4bae6e7fc175d5cfe4c07 Mon Sep 17 00:00:00 2001 From: scott-c Date: Mon, 17 Aug 2020 21:10:05 +0800 Subject: [PATCH 09/17] Tweak particle effects --- assets/voxygen/shaders/particle-frag.glsl | 4 ++ assets/voxygen/shaders/particle-vert.glsl | 13 +++---- voxygen/src/scene/particle.rs | 45 ++++++++++++++++------- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index ad4152bce3..3667c2468a 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -86,5 +86,9 @@ void main() { vec3 color = surf_color; #endif +<<<<<<< HEAD tgt_color = vec4(color, f_col.a); +======= + tgt_color = vec4(color, 1.0); +>>>>>>> 62119c37a... Tweak particle effects } diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 1843774022..0c3c40e45e 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -243,13 +243,12 @@ void main() { ); } else if (inst_mode == GROUND_SHOCKWAVE) { attr = Attr( - linear_motion( - vec3(rand0 * 0.25, rand1 * 0.25, 0.0), - vec3(rand2 * 0.1, rand3 * 0.1, 0.0) - ), - 3.0 + 5.0 * rand5, - vec4(vec3(0.42, 0.32, 0.1), 1), - spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) + vec3(0.0), + vec3(1.0, 1.0, (3.0 * rand0 * sin(2.0 * lifetime * 3.14 * 2.0))) / 3, + //3.0 + 5.0 * rand5, + vec4(vec3(0.32 + (rand0 * 0.04), 0.22 + (rand1 * 0.03), 0.05 + (rand2 * 0.01)), 1), + //rotationMatrix(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) + spin_in_axis(vec3(1,0,0),0) ); } else { attr = Attr( diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 48878578df..2c523d992a 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -436,27 +436,44 @@ impl ParticleMgr { { let elapsed = time - shockwave.creation.unwrap_or_default(); - let p = shockwave.properties.speed * elapsed as f32; + let distance = shockwave.properties.speed * elapsed as f32; + + let radians = shockwave.properties.angle.to_radians(); let theta = ori.0.y.atan2(ori.0.x); - let dtheta = shockwave.properties.angle.to_radians() / p; + let dtheta = radians / distance; + + // 1 / 3 the size of terrain voxel + let scale = 1.0 / 3.0; + + let freqency_millis = shockwave.properties.speed * scale; + + for heartbeat in 0..self + .scheduler + .heartbeats(Duration::from_millis(freqency_millis as u64)) + { + let sub_tick_interpolation = freqency_millis * 1000.0 * heartbeat as f32; + + let distance = + shockwave.properties.speed * (elapsed as f32 - sub_tick_interpolation); + + for d in 0..((distance / scale) as i32) { + let arc_position = (theta - (radians / 2.0)) + (dtheta * d as f32 * scale); + + let position = pos.0 + + Vec3::new( + distance * arc_position.cos(), + distance * arc_position.sin(), + 0.0, + ); + + let position_snapped = (position / scale).round() * scale; - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) { - for d in 0..(p as i32) { self.particles.push(Particle::new( Duration::from_millis(250), time, ParticleMode::GroundShockwave, - pos.0 - + Vec3::new( - p * ((theta - (shockwave.properties.angle.to_radians() / 2.0)) - + (dtheta * d as f32)) - .cos(), - p * ((theta - (shockwave.properties.angle.to_radians() / 2.0)) - + (dtheta * d as f32)) - .sin(), - 0.0, - ), + position_snapped, )); } } From 819fb8ee77dfe336a8b67c3b0793fd5a2b0efde3 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 22 Aug 2020 18:35:58 -0500 Subject: [PATCH 10/17] Changed boss from cyclops to stone golem. Added ai specific for boss. Tweaked shockwave. Fix rebasing errors. --- ...clops_hammer.ron => stone_golems_fist.ron} | 6 ++-- assets/voxygen/item_image_manifest.ron | 5 ---- assets/voxygen/shaders/particle-frag.glsl | 4 --- assets/voxygen/shaders/particle-vert.glsl | 28 +++++++++---------- .../voxel/humanoid_main_weapon_manifest.ron | 7 +---- common/src/comp/body.rs | 8 +++--- common/src/comp/damage.rs | 8 ------ common/src/comp/inventory/item/tool.rs | 18 +++--------- common/src/loadout_builder.rs | 23 +++++++-------- common/src/sys/agent.rs | 19 ++++++++++++- common/src/sys/shockwave.rs | 2 +- 11 files changed, 56 insertions(+), 72 deletions(-) rename assets/common/items/npc_weapons/npcweapon/{cyclops_hammer.ron => stone_golems_fist.ron} (52%) diff --git a/assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron b/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron similarity index 52% rename from assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron rename to assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron index 904f65a5e0..e5dc1c1817 100644 --- a/assets/common/items/npc_weapons/npcweapon/cyclops_hammer.ron +++ b/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron @@ -1,9 +1,9 @@ Item( - name: "Cyclops Hammer", - description: "Wielded by a mighty cyclops.", + name: "Stone Golem's Fist", + description: "Was attached to a mighty stone golem.", kind: Tool( ( - kind: NpcWeapon("CyclopsHammer"), + kind: NpcWeapon("StoneGolemsFist"), stats: ( equip_time_millis: 500, power: 1.00, diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 6b98dedcf4..7e2ebda6f1 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -519,11 +519,6 @@ "voxel.weapon.shield.wood-0", (0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 2.4, ), - // Npc weapons (for test purposes, remove when done) - Tool(NpcWeapon("CyclopsHammer")): VoxTrans( - "voxel.weapon.npcweapon.cyclops_hammer", - (2.0, -1.0, 0.0), (-135.0, 90.0, 0.0), 1.1, - ), // Lanterns Lantern("Black0"): Png( "element.icons.lantern_black-0", diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index 3667c2468a..ad4152bce3 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -86,9 +86,5 @@ void main() { vec3 color = surf_color; #endif -<<<<<<< HEAD tgt_color = vec4(color, f_col.a); -======= - tgt_color = vec4(color, 1.0); ->>>>>>> 62119c37a... Tweak particle effects } diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 0c3c40e45e..10069112c1 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -54,7 +54,7 @@ const float earth_gravity = 9.807; struct Attr { vec3 offs; - float scale; + vec3 scale; vec4 col; mat4 rot; }; @@ -123,7 +123,7 @@ void main() { vec3(0), vec3(rand2 * 0.02, rand3 * 0.02, 1.0 + rand4 * 0.1) ), - linear_scale(0.5), + vec3(linear_scale(0.5)), vec4(1, 1, 1, start_end(1.0, 0.0)), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 0.5) ); @@ -133,7 +133,7 @@ void main() { vec3(rand0 * 0.25, rand1 * 0.25, 0.3), vec3(rand2 * 0.1, rand3 * 0.1, 2.0 + rand4 * 1.0) ), - 1.0, + vec3(1.0), vec4(2, 0.8 + rand5 * 0.3, 0, 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3) ); @@ -143,7 +143,7 @@ void main() { vec3(rand0, rand1, rand3) * 0.3, vec3(rand4, rand5, rand6) * 2.0 + grav_vel(earth_gravity) ), - 1.0, + vec3(1.0), vec4(3.5, 3 + rand7, 0, 1), spin_in_axis(vec3(1,0,0),0) ); @@ -153,7 +153,7 @@ void main() { vec3(0), vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(vec3(0.6 + rand7 * 0.4), 1), spin_in_axis(vec3(1,0,0),0) ); @@ -163,7 +163,7 @@ void main() { vec3(0), vec3(rand1, rand2, rand3) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(0.15, 0.4, 1, 1), identity() ); @@ -173,7 +173,7 @@ void main() { vec3(0), vec3(rand1, rand2, rand3) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(0, 1, 0, 1), identity() ); @@ -183,7 +183,7 @@ void main() { vec3(0), vec3(rand1, rand2, rand3) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(0.7, 0.0, 1.0, 1.0), identity() ); @@ -193,7 +193,7 @@ void main() { vec3(0), vec3(rand1, rand2, rand3) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(1, 0, 0, 1), identity() ); @@ -203,7 +203,7 @@ void main() { vec3(0), vec3(rand1, rand2, rand3) * 40.0 + grav_vel(earth_gravity) ), - 3.0 + rand0, + vec3(3.0 + rand0), vec4(1, 1, 0, 1), identity() ); @@ -213,7 +213,7 @@ void main() { vec3(0), vec3(0, 0, -2) ) + vec3(sin(lifetime), sin(lifetime + 0.7), sin(lifetime * 0.5)) * 2.0, - 4, + vec3(4), vec4(vec3(0.2 + rand7 * 0.2, 0.2 + (0.5 + rand6 * 0.5) * 0.6, 0), 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); @@ -244,10 +244,8 @@ void main() { } else if (inst_mode == GROUND_SHOCKWAVE) { attr = Attr( vec3(0.0), - vec3(1.0, 1.0, (3.0 * rand0 * sin(2.0 * lifetime * 3.14 * 2.0))) / 3, - //3.0 + 5.0 * rand5, + vec3(11.0, 11.0, (33.0 * rand0 * sin(2.0 * lifetime * 3.14 * 2.0))) / 3, vec4(vec3(0.32 + (rand0 * 0.04), 0.22 + (rand1 * 0.03), 0.05 + (rand2 * 0.01)), 1), - //rotationMatrix(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) spin_in_axis(vec3(1,0,0),0) ); } else { @@ -256,7 +254,7 @@ void main() { vec3(rand0 * 0.25, rand1 * 0.25, 1.7 + rand5), vec3(rand2 * 0.1, rand3 * 0.1, 1.0 + rand4 * 0.5) ), - exp_scale(-0.2), + vec3(exp_scale(-0.2)), vec4(1), spin_in_axis(vec3(1,0,0),0) ); diff --git a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron index d967085b4a..79969d1c5c 100644 --- a/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron +++ b/assets/voxygen/voxel/humanoid_main_weapon_manifest.ron @@ -1,9 +1,4 @@ -({ // Npc weapons (for test purposes, remove when done) - NpcWeapon("CyclopsHammer"): ( - vox_spec: ("weapon.npcweapon.cyclops_hammer", (-2.5, -7.5, -5.0)), - color: None - ), - //Swords +({ //Swords Sword("BasicSword"): ( vox_spec: ("weapon.sword.rusty_2h", (-1.5, -6.5, -4.0)), color: None diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index ec9ba06157..16435dc608 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -263,7 +263,7 @@ impl Body { _ => 1000, }, Body::Object(_) => 10000, - Body::Golem(_) => 1500, + Body::Golem(_) => 2560, Body::Theropod(_) => 50, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { quadruped_low::Species::Crocodile => 600, @@ -330,7 +330,7 @@ impl Body { _ => 100, }, Body::Object(_) => 10, - Body::Golem(_) => 150, + Body::Golem(_) => 260, Body::Theropod(_) => 20, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { quadruped_low::Species::Crocodile => 20, @@ -395,7 +395,7 @@ impl Body { _ => 100, }, Body::Object(_) => 1, - Body::Golem(_) => 75, + Body::Golem(_) => 256, Body::Theropod(_) => 2, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { quadruped_low::Species::Crocodile => 10, @@ -425,7 +425,7 @@ impl Body { Body::FishSmall(_) => 1, Body::BipedLarge(_) => 2, Body::Object(_) => 0, - Body::Golem(_) => 5, + Body::Golem(_) => 12, Body::Theropod(_) => 1, Body::QuadrupedLow(_) => 1, } diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs index 3d2f7b4c0b..ef2fab150a 100644 --- a/common/src/comp/damage.rs +++ b/common/src/comp/damage.rs @@ -76,14 +76,6 @@ impl Damage { } }, DamageSource::Shockwave => { - // Critical hit - if rand::random() { - self.healthchange *= 1.2; - } - // Block - if block { - self.healthchange *= 1.0 - BLOCK_EFFICIENCY - } // Armor self.healthchange *= 1.0 - loadout.get_damage_reduction(); diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index aa939ef37e..cb73b19547 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -116,16 +116,6 @@ impl Tool { match &self.kind { Sword(_) => vec![ - GroundShockwave { - energy_cost: 0, - buildup_duration: Duration::from_millis(500), - recover_duration: Duration::from_millis(1000), - damage: 500, - knockback: -30.0, - shockwave_angle: 90.0, - shockwave_speed: 10.0, - shockwave_duration: Duration::from_millis(3000), - }, TripleStrike { base_damage: (60.0 * self.base_power()) as u32, needs_timing: false, @@ -359,7 +349,7 @@ impl Tool { BasicBlock, ], NpcWeapon(kind) => { - if kind == "CyclopsHammer" { + if kind == "StoneGolemsFist" { vec![ BasicMelee { energy_cost: 0, @@ -375,10 +365,10 @@ impl Tool { buildup_duration: Duration::from_millis(500), recover_duration: Duration::from_millis(1000), damage: 500, - knockback: -30.0, + knockback: -40.0, shockwave_angle: 90.0, - shockwave_speed: 10.0, - shockwave_duration: Duration::from_millis(3000), + shockwave_speed: 20.0, + shockwave_duration: Duration::from_millis(2000), }, ] } else { diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index e890778ef2..0fee2d99a8 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -1,7 +1,8 @@ use crate::{ comp::{ + golem, item::{Item, ItemKind}, - Alignment, Body, biped_large, CharacterAbility, ItemConfig, Loadout, + Alignment, Body, CharacterAbility, ItemConfig, Loadout, }, }; use rand::Rng; @@ -66,15 +67,16 @@ impl LoadoutBuilder { ))) } - /// Builds loadout of creature when spawned + /// Builds loadout of creature when spawned pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option) -> Self { match body { - Body::BipedLarge(biped_large) => match biped_large.species { - biped_large::Species::Cyclops => { - main_tool = Some(Item::new_from_asset_expect("common.items.weapons.bossweapon.cyclops_hammer")); + Body::Golem(golem) => match golem.species { + golem::Species::StoneGolem => { + main_tool = Some(Item::new_from_asset_expect( + "common.items.weapons.bossweapon.stone_golems_fist", + )); }, - _ => {}, - } + }, _ => {}, }; @@ -185,8 +187,8 @@ impl LoadoutBuilder { }, _ => LoadoutBuilder::animal(body).build(), }, - Body::BipedLarge(biped_large) => match biped_large.species { - biped_large::Species::Cyclops => Loadout { + Body::Golem(golem) => match golem.species { + golem::Species::StoneGolem => Loadout { active_item, second_item: None, shoulder: None, @@ -203,8 +205,7 @@ impl LoadoutBuilder { head: None, tabard: None, }, - _ => LoadoutBuilder::animal(body).build(), - } + }, _ => LoadoutBuilder::animal(body).build(), }; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 239905cad0..aae09363b9 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -268,6 +268,7 @@ impl<'a> System<'a> for Sys { Melee, RangedPowerup, Staff, + StoneGolemBoss, } let tactic = match loadout.active_item.as_ref().and_then(|ic| { @@ -279,6 +280,13 @@ impl<'a> System<'a> for Sys { }) { Some(ToolKind::Bow(_)) => Tactic::RangedPowerup, Some(ToolKind::Staff(_)) => Tactic::Staff, + Some(ToolKind::NpcWeapon(kind)) => { + if kind == "StoneGolemsFist" { + Tactic::StoneGolemBoss + } else { + Tactic::Melee + } + }, _ => Tactic::Melee, }; @@ -355,7 +363,9 @@ impl<'a> System<'a> for Sys { * 0.1; match tactic { - Tactic::Melee | Tactic::Staff => inputs.primary.set_state(true), + Tactic::Melee | Tactic::Staff | Tactic::StoneGolemBoss => { + inputs.primary.set_state(true) + }, Tactic::RangedPowerup => inputs.roll.set_state(true), } } else if dist_sqrd < MAX_CHASE_DIST.powf(2.0) @@ -385,6 +395,13 @@ impl<'a> System<'a> for Sys { } inputs.secondary.set_state(true); + } else if let Tactic::StoneGolemBoss = tactic { + if *powerup > 5.0 { + inputs.secondary.set_state(true); + *powerup = 0.0; + } else { + *powerup += dt.0; + } } } diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index 1e19f95735..f5f9244df1 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -214,7 +214,7 @@ impl<'a> System<'a> for Sys { local_emitter.emit(LocalEvent::ApplyForce { entity: b, force: shockwave.knockback - * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.5), + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85), }); } else { local_emitter.emit(LocalEvent::ApplyForce { From 6327dd18b67cbad6d23f871c3fb06c8669ccce19 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 23 Aug 2020 16:10:58 -0400 Subject: [PATCH 11/17] Moved knockback to a server event so that it would actually be applied to the player. --- client/src/lib.rs | 10 ++++++++++ common/src/event.rs | 4 ++++ common/src/msg/server.rs | 1 + common/src/sys/combat.rs | 2 +- common/src/sys/shockwave.rs | 6 +++--- server/src/events/entity_manipulation.rs | 13 +++++++++++++ server/src/events/mod.rs | 5 +++-- voxygen/src/scene/particle.rs | 1 + 8 files changed, 36 insertions(+), 6 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 5750dd5f0b..f7c748ea6a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -22,6 +22,7 @@ use common::{ group, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InventoryManip, InventoryUpdateEvent, }, + event::{EventBus, LocalEvent}, msg::{ validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate, RegisterError, RequestStateError, @@ -1426,6 +1427,15 @@ impl Client { ServerMsg::Outcomes(outcomes) => { frontend_events.extend(outcomes.into_iter().map(Event::Outcome)) }, + ServerMsg::Knockback(force) => { + self.state + .ecs() + .read_resource::>() + .emit_now(LocalEvent::ApplyForce { + entity: self.entity, + force, + }); + }, } } } diff --git a/common/src/event.rs b/common/src/event.rs index 616fb5736a..c7568bdce7 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -56,6 +56,10 @@ pub enum ServerEvent { pos: Pos, ori: Ori, }, + Knockback { + entity: EcsEntity, + force: Vec3, + }, LandOnGround { entity: EcsEntity, vel: Vec3, diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 18d4ee6f27..ce86e46c73 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -254,6 +254,7 @@ pub enum ServerMsg { Notification(Notification), SetViewDistance(u32), Outcomes(Vec), + Knockback(Vec3), } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index f4f4352ecc..3909408419 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -154,7 +154,7 @@ impl<'a> System<'a> for Sys { } if attack.knockback != 0.0 { - local_emitter.emit(LocalEvent::ApplyForce { + server_emitter.emit(ServerEvent::Knockback { entity: b, force: attack.knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index f5f9244df1..81cef7662f 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -63,7 +63,7 @@ impl<'a> System<'a> for Sys { ): Self::SystemData, ) { let mut server_emitter = server_bus.emitter(); - let mut local_emitter = local_bus.emitter(); + let _local_emitter = local_bus.emitter(); let time = time.0; let dt = dt.0; @@ -211,13 +211,13 @@ impl<'a> System<'a> for Sys { } if shockwave.knockback != 0.0 { if shockwave.knockback < 0.0 { - local_emitter.emit(LocalEvent::ApplyForce { + server_emitter.emit(ServerEvent::Knockback { entity: b, force: shockwave.knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85), }); } else { - local_emitter.emit(LocalEvent::ApplyForce { + server_emitter.emit(ServerEvent::Knockback { entity: b, force: shockwave.knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 88428fc3d7..37331df6b0 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -32,6 +32,19 @@ pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) { } } +pub fn handle_knockback(server: &Server, entity: EcsEntity, force: Vec3) { + let state = &server.state; + let ecs = state.ecs(); + let mut velocities = ecs.write_storage::(); + if let Some(vel) = velocities.get_mut(entity) { + vel.0 = force; + } + let mut clients = state.ecs().write_storage::(); + if let Some(client) = clients.get_mut(entity) { + client.notify(ServerMsg::Knockback(force)); + } +} + /// Handle an entity dying. If it is a player, it will send a message to all /// other players. If the entity that killed it had stats, then give it exp for /// the kill. Experience given is equal to the level of the entity that was diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 63ded0012f..224abd1b4b 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -8,8 +8,8 @@ use entity_creation::{ handle_loaded_character_data, handle_shockwave, handle_shoot, }; use entity_manipulation::{ - handle_damage, handle_destroy, handle_explosion, handle_land_on_ground, handle_level_up, - handle_respawn, + handle_damage, handle_destroy, handle_explosion, handle_knockback, handle_land_on_ground, + handle_level_up, handle_respawn, }; use group_manip::handle_group; use interaction::{handle_lantern, handle_mount, handle_possess, handle_unmount}; @@ -75,6 +75,7 @@ impl Server { pos, ori, } => handle_shockwave(self, properties, pos, ori), + ServerEvent::Knockback { entity, force } => handle_knockback(&self, entity, force), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 2c523d992a..426274ef79 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -420,6 +420,7 @@ impl ParticleMgr { } } } + fn maintain_shockwave_particles(&mut self, scene_data: &SceneData) { let state = scene_data.state; let ecs = state.ecs(); From d0f068ba63ea3e4582c4f7b6268583ea7da34287 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 23 Aug 2020 16:53:43 -0400 Subject: [PATCH 12/17] Fixed shockwave and melee crit damage bypassing infinite armor. Made knockback not be applied if infinite armor. --- common/src/comp/damage.rs | 9 ++++++--- common/src/loadout_builder.rs | 1 + common/src/sys/combat.rs | 5 ++--- common/src/sys/shockwave.rs | 2 +- server/src/events/entity_creation.rs | 1 + 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs index ef2fab150a..07ffebf291 100644 --- a/common/src/comp/damage.rs +++ b/common/src/comp/damage.rs @@ -36,7 +36,9 @@ impl Damage { self.healthchange *= 1.0 - damage_reduction; // Critical damage applies after armor for melee - self.healthchange += critdamage; + if (damage_reduction - 1.0).abs() > f32::EPSILON { + self.healthchange += critdamage; + } // Min damage if (damage_reduction - 1.0).abs() > f32::EPSILON && self.healthchange > -10.0 { @@ -77,10 +79,11 @@ impl Damage { }, DamageSource::Shockwave => { // Armor - self.healthchange *= 1.0 - loadout.get_damage_reduction(); + let damage_reduction = loadout.get_damage_reduction(); + self.healthchange *= 1.0 - damage_reduction; // Min damage - if self.healthchange > -10.0 { + if (damage_reduction - 1.0).abs() > f32::EPSILON && self.healthchange > -10.0 { self.healthchange = -10.0; } }, diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 0fee2d99a8..b68a11d0ce 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -69,6 +69,7 @@ impl LoadoutBuilder { /// Builds loadout of creature when spawned pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option) -> Self { + #![allow(clippy::single_match)] // For when this is done to more than just golems. match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => { diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index 3909408419..54a414f002 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -59,7 +59,7 @@ impl<'a> System<'a> for Sys { let start_time = std::time::Instant::now(); span!(_guard, "run", "combat::Sys::run"); let mut server_emitter = server_bus.emitter(); - let mut local_emitter = local_bus.emitter(); + let mut _local_emitter = local_bus.emitter(); // Attacks for (entity, uid, pos, ori, scale_maybe, attack) in ( &entities, @@ -152,8 +152,7 @@ impl<'a> System<'a> for Sys { }, }); } - - if attack.knockback != 0.0 { + if attack.knockback != 0.0 && damage.healthchange != 0.0 { server_emitter.emit(ServerEvent::Knockback { entity: b, force: attack.knockback diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index 81cef7662f..d6513d51c3 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -209,7 +209,7 @@ impl<'a> System<'a> for Sys { }, }); } - if shockwave.knockback != 0.0 { + if shockwave.knockback != 0.0 && damage.healthchange != 0.0 { if shockwave.knockback < 0.0 { server_emitter.emit(ServerEvent::Knockback { entity: b, diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 586d8400e7..022f007b3c 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -79,6 +79,7 @@ pub fn handle_create_npc( entity.build(); } +#[allow(clippy::too_many_arguments)] pub fn handle_shoot( server: &mut Server, entity: EcsEntity, From 6a1acd47a1061dd8ed37e7c5692d641d4e99d83f Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 5 Sep 2020 22:06:28 -0500 Subject: [PATCH 13/17] Added maximum vertical angle for shockwave. --- assets/voxygen/shaders/particle-vert.glsl | 4 ++-- common/src/loadout_builder.rs | 1 - common/src/sys/shockwave.rs | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 10069112c1..c22cf9a264 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -225,7 +225,7 @@ void main() { sin(lifetime * 3.0 + rand1) + sin(lifetime * 8.0 + rand4) * 0.3, sin(lifetime * 2.0 + rand2) + sin(lifetime * 9.0 + rand5) * 0.3 ), - raise, + vec3(raise), vec4(vec3(5, 5, 1.1), 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); @@ -237,7 +237,7 @@ void main() { sin(lifetime * 3.0 + rand1) + sin(lifetime * 10.0 + rand4) * 0.3, sin(lifetime * 4.0 + rand2) + sin(lifetime * 11.0 + rand5) * 0.3 ) * 0.5, - lower, + vec3(lower), vec4(vec3(1, 0.7, 0), 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index b68a11d0ce..b92eb60337 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -267,7 +267,6 @@ impl LoadoutBuilder { Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref)) } - pub fn active_item(mut self, item: Option) -> Self { self.0.active_item = item; diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index d6513d51c3..312ee2effc 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -155,6 +155,10 @@ impl<'a> System<'a> for Sys { let scale_b = scale_b_maybe.map_or(1.0, |s| s.0); let rad_b = body_b.radius() * scale_b; + // Angle checks + let pos_b_ground = Vec3::new(pos_b.0.x, pos_b.0.y, pos.0.z); + let max_angle = 15.0_f32.to_radians(); + // Check if it is a hit let hit = entity != b && !stats_b.is_dead @@ -169,6 +173,7 @@ impl<'a> System<'a> for Sys { arc_strip.collides_with_circle(Circle { pos, radius: rad_b }) }) } + && (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle && (!shockwave.requires_ground || physics_state_b.on_ground); if hit { From 99aba7842213d2d43639ab216605f2b03222132b Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 11 Sep 2020 13:07:09 -0500 Subject: [PATCH 14/17] Added healing healthsource logic to shockwave system. --- .../npc_weapons/npcweapon/stone_golems_fist.ron | 2 +- common/src/loadout_builder.rs | 2 +- common/src/sys/shockwave.rs | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron b/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron index e5dc1c1817..ad00cb189c 100644 --- a/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron +++ b/assets/common/items/npc_weapons/npcweapon/stone_golems_fist.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Stone Golem's Fist", description: "Was attached to a mighty stone golem.", kind: Tool( diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index b92eb60337..1030729764 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -74,7 +74,7 @@ impl LoadoutBuilder { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => { main_tool = Some(Item::new_from_asset_expect( - "common.items.weapons.bossweapon.stone_golems_fist", + "common.items.npc_weapons.npcweapon.stone_golems_fist", )); }, }, diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index 312ee2effc..dd27e4d496 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -203,7 +203,7 @@ impl<'a> System<'a> for Sys { damage.modify_damage(block, loadout); } - if damage.healthchange != 0.0 { + if damage.healthchange < 0.0 { server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { @@ -213,6 +213,16 @@ impl<'a> System<'a> for Sys { }, }, }); + } else if damage.healthchange > 0.0 { + server_emitter.emit(ServerEvent::Damage { + uid: *uid_b, + change: HealthChange { + amount: damage.healthchange as i32, + cause: HealthSource::Healing { + by: Some(shockwave.owner.unwrap_or(*uid)), + }, + }, + }); } if shockwave.knockback != 0.0 && damage.healthchange != 0.0 { if shockwave.knockback < 0.0 { From e39770d1d9bca17d09d1a31ab2362683489771a3 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 18 Sep 2020 16:28:21 -0500 Subject: [PATCH 15/17] Golem now spawns in dungeon. --- CHANGELOG.md | 1 + common/src/comp/inventory/item/tool.rs | 2 +- common/src/loadout_builder.rs | 71 ++++++++++++-------------- server/src/sys/terrain.rs | 5 +- voxygen/src/scene/figure/mod.rs | 9 ++++ world/src/site/dungeon/mod.rs | 22 +++----- 6 files changed, 52 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d21aeda126..d890aa03d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fireflies - Fullscreen modes now show two options (exclusive and borderless) - Added banlist and `/ban`, `/unban`, and `/kick` commands for admins +- A new dungeon boss (venture there and discover it yourself) ### Changed diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index cb73b19547..58e8489058 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -357,7 +357,7 @@ impl Tool { recover_duration: Duration::from_millis(250), knockback: 25.0, base_healthchange: -200, - range: 10.0, + range: 5.0, max_angle: 120.0, }, GroundShockwave { diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 1030729764..7120b48f8a 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -1,9 +1,7 @@ -use crate::{ - comp::{ - golem, - item::{Item, ItemKind}, - Alignment, Body, CharacterAbility, ItemConfig, Loadout, - }, +use crate::comp::{ + golem, + item::{Item, ItemKind}, + Alignment, Body, CharacterAbility, ItemConfig, Loadout, }; use rand::Rng; use std::time::Duration; @@ -81,38 +79,37 @@ impl LoadoutBuilder { _ => {}, }; - let active_item = - if let Some(ItemKind::Tool(tool)) = main_tool.as_ref().map(|i| i.kind()) { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); + let active_item = if let Some(ItemKind::Tool(tool)) = main_tool.as_ref().map(|i| i.kind()) { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); - main_tool.map(|item| ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: None, - dodge_ability: Some(CharacterAbility::Roll), - }) - } else { - Some(ItemConfig { - // We need the empty item so npcs can attack - item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), - ability1: Some(CharacterAbility::BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(400), - base_healthchange: -40, - knockback: 0.0, - range: 3.5, - max_angle: 15.0, - }), - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }) - }; + main_tool.map(|item| ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: None, + dodge_ability: Some(CharacterAbility::Roll), + }) + } else { + Some(ItemConfig { + // We need the empty item so npcs can attack + item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), + ability1: Some(CharacterAbility::BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(400), + base_healthchange: -40, + knockback: 0.0, + range: 3.5, + max_angle: 15.0, + }), + ability2: None, + ability3: None, + block_ability: None, + dodge_ability: None, + }) + }; let loadout = match body { Body::Humanoid(_) => match alignment { diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 46b7cdd1ac..4329b48550 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,10 +1,7 @@ use super::SysTimer; use crate::{chunk_generator::ChunkGenerator, client::Client, Tick}; use common::{ - comp::{ - self, bird_medium, - Alignment, CharacterAbility, Player, Pos, - }, + comp::{self, bird_medium, Alignment, CharacterAbility, Player, Pos}, event::{EventBus, ServerEvent}, generation::get_npc_name, msg::ServerMsg, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 7a6c93c8be..93793d9819 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -2115,6 +2115,15 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::GroundShockwave(_) => { + anim::golem::ShockwaveAnimation::update_skeleton( + &target_base, + (vel.0.magnitude(), time), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + }, // TODO! _ => target_base, }; diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index db9817404b..a3a2890166 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -13,7 +13,6 @@ use common::{ comp::{self}, generation::{ChunkSupplement, EntityInfo}, lottery::Lottery, - npc, store::{Id, Store}, terrain::{Block, BlockKind, Structure, TerrainChunkSize}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, @@ -509,22 +508,13 @@ impl Floor { ); let chosen = chosen.choose(); let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_scale(4.0) - .with_level(rng.gen_range(75, 100)) + .with_level(rng.gen_range(1, 5)) .with_alignment(comp::Alignment::Enemy) - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_name(format!( - "Cult Leader {}", - npc::get_npc_name(npc::NpcKind::Humanoid) - )) - .with_main_tool(comp::Item::new_from_asset_expect( - match rng.gen_range(0, 1) { - //Add more possible cult leader npc_weapons here - _ => { - "common.items.npc_weapons.sword.cultist_purp_2h_boss-0" - }, - }, - )) + .with_body(comp::Body::Golem(comp::golem::Body::random_with( + rng, + &comp::golem::Species::StoneGolem, + ))) + .with_name("Stonework Defender".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)); supplement.add_entity(entity); From 45fef87f324cbd79fa5313d2f5c7b5203115a1be Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 19 Sep 2020 11:55:31 -0500 Subject: [PATCH 16/17] Addressed comments --- client/src/lib.rs | 6 +- common/src/comp/ability.rs | 3 + common/src/comp/inventory/item/tool.rs | 1 + common/src/event.rs | 9 +- common/src/state.rs | 6 +- common/src/states/ground_shockwave.rs | 7 +- common/src/sys/combat.rs | 2 +- common/src/sys/projectile.rs | 4 +- common/src/sys/shockwave.rs | 175 +++++++++-------------- server/src/events/entity_creation.rs | 5 +- server/src/events/entity_manipulation.rs | 9 +- server/src/events/mod.rs | 4 +- voxygen/src/scene/particle.rs | 18 +-- 13 files changed, 108 insertions(+), 141 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index f7c748ea6a..e74ed9bd9d 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1427,13 +1427,13 @@ impl Client { ServerMsg::Outcomes(outcomes) => { frontend_events.extend(outcomes.into_iter().map(Event::Outcome)) }, - ServerMsg::Knockback(force) => { + ServerMsg::Knockback(impulse) => { self.state .ecs() .read_resource::>() - .emit_now(LocalEvent::ApplyForce { + .emit_now(LocalEvent::ApplyImpulse { entity: self.entity, - force, + impulse, }); }, } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 843bea5933..4c328be14d 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -121,6 +121,7 @@ pub enum CharacterAbility { shockwave_angle: f32, shockwave_speed: f32, shockwave_duration: Duration, + requires_ground: bool, }, } @@ -417,6 +418,7 @@ impl From<&CharacterAbility> for CharacterState { shockwave_angle, shockwave_speed, shockwave_duration, + requires_ground, } => CharacterState::GroundShockwave(ground_shockwave::Data { exhausted: false, buildup_duration: *buildup_duration, @@ -426,6 +428,7 @@ impl From<&CharacterAbility> for CharacterState { shockwave_angle: *shockwave_angle, shockwave_speed: *shockwave_speed, shockwave_duration: *shockwave_duration, + requires_ground: *requires_ground, }), } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 58e8489058..58cb8aae6a 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -369,6 +369,7 @@ impl Tool { shockwave_angle: 90.0, shockwave_speed: 20.0, shockwave_duration: Duration::from_millis(2000), + requires_ground: true, }, ] } else { diff --git a/common/src/event.rs b/common/src/event.rs index c7568bdce7..f5bd89c79d 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -11,8 +11,11 @@ use vek::*; pub enum LocalEvent { /// Applies upward force to entity's `Vel` Jump(EcsEntity), - /// Applies the `force` to `entity`'s `Vel` - ApplyForce { entity: EcsEntity, force: Vec3 }, + /// Applies the `impulse` to `entity`'s `Vel` + ApplyImpulse { + entity: EcsEntity, + impulse: Vec3, + }, /// Applies leaping force to `entity`'s `Vel` away from `wall_dir` direction WallLeap { entity: EcsEntity, @@ -58,7 +61,7 @@ pub enum ServerEvent { }, Knockback { entity: EcsEntity, - force: Vec3, + impulse: Vec3, }, LandOnGround { entity: EcsEntity, diff --git a/common/src/state.rs b/common/src/state.rs index bc203820cb..3e8e1e62a2 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -390,11 +390,9 @@ impl State { vel.0.z = HUMANOID_JUMP_ACCEL; } }, - LocalEvent::ApplyForce { entity, force } => { - // TODO: this sets the velocity directly to the value of `force`, consider - // renaming the event or changing the behavior + LocalEvent::ApplyImpulse { entity, impulse } => { if let Some(vel) = velocities.get_mut(entity) { - vel.0 = force; + vel.0 = impulse; } }, LocalEvent::WallLeap { entity, wall_dir } => { diff --git a/common/src/states/ground_shockwave.rs b/common/src/states/ground_shockwave.rs index 10041aec84..81038fc58b 100644 --- a/common/src/states/ground_shockwave.rs +++ b/common/src/states/ground_shockwave.rs @@ -25,6 +25,8 @@ pub struct Data { pub shockwave_speed: f32, /// How long the shockwave travels for pub shockwave_duration: Duration, + /// Whether the shockwave requires the target to be on the ground + pub requires_ground: bool, } impl CharacterBehavior for Data { @@ -47,6 +49,7 @@ impl CharacterBehavior for Data { shockwave_angle: self.shockwave_angle, shockwave_speed: self.shockwave_speed, shockwave_duration: self.shockwave_duration, + requires_ground: self.requires_ground, }); } else if !self.exhausted { // Attack @@ -56,7 +59,7 @@ impl CharacterBehavior for Data { duration: self.shockwave_duration, damage: self.damage, knockback: self.knockback, - requires_ground: true, + requires_ground: self.requires_ground, owner: Some(*data.uid), }; update.server_events.push_front(ServerEvent::Shockwave { @@ -74,6 +77,7 @@ impl CharacterBehavior for Data { shockwave_angle: self.shockwave_angle, shockwave_speed: self.shockwave_speed, shockwave_duration: self.shockwave_duration, + requires_ground: self.requires_ground, }); } else if self.recover_duration != Duration::default() { // Recovery @@ -89,6 +93,7 @@ impl CharacterBehavior for Data { shockwave_angle: self.shockwave_angle, shockwave_speed: self.shockwave_speed, shockwave_duration: self.shockwave_duration, + requires_ground: self.requires_ground, }); } else { // Done diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index 54a414f002..7e022f1463 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -155,7 +155,7 @@ impl<'a> System<'a> for Sys { if attack.knockback != 0.0 && damage.healthchange != 0.0 { server_emitter.emit(ServerEvent::Knockback { entity: b, - force: attack.knockback + impulse: attack.knockback * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), }); } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index a662eeb0f4..c44822c9a3 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -117,9 +117,9 @@ impl<'a> System<'a> for Sys { if let Some(entity) = uid_allocator.retrieve_entity_internal(other.into()) { - local_emitter.emit(LocalEvent::ApplyForce { + local_emitter.emit(LocalEvent::ApplyImpulse { entity, - force: knockback + impulse: knockback * *Dir::slerp(ori.0, Dir::new(Vec3::unit_z()), 0.5), }); } diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index dd27e4d496..ced2d336e1 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -83,22 +83,17 @@ impl<'a> System<'a> for Sys { // If shockwave is out of time emit destroy event but still continue since it // may have traveled and produced effects a bit before reaching it's // end point - if end_time < time { + if time > end_time { server_emitter.emit(ServerEvent::Destroy { entity, cause: HealthSource::World, }); - } - - // Determine area that was covered by the shockwave in the last tick - let frame_time = dt.min((end_time - time) as f32); - if frame_time <= 0.0 { continue; } - // Note: min() probably uneeded + // Determine area that was covered by the shockwave in the last tick let time_since_creation = (time - creation_time) as f32; - let frame_start_dist = (shockwave.speed * (time_since_creation - frame_time)).max(0.0); + let frame_start_dist = (shockwave.speed * (time_since_creation - dt)).max(0.0); let frame_end_dist = (shockwave.speed * time_since_creation).max(frame_start_dist); let pos2 = Vec2::from(pos.0); @@ -159,6 +154,11 @@ impl<'a> System<'a> for Sys { let pos_b_ground = Vec3::new(pos_b.0.x, pos_b.0.y, pos.0.z); let max_angle = 15.0_f32.to_radians(); + // See if entities are in the same group + let same_group = group + .map(|group_a| Some(group_a) == groups.get(b)) + .unwrap_or(Some(*uid_b) == shockwave.owner); + // Check if it is a hit let hit = entity != b && !stats_b.is_dead @@ -166,78 +166,54 @@ impl<'a> System<'a> for Sys { && { // TODO: write code to collide rect with the arc strip so that we can do // more complete collision detection for rapidly moving entities - arc_strip.collides_with_circle(Circle { - pos: pos_b2, - radius: rad_b, - }) || last_pos_b2_maybe.map_or(false, |pos| { - arc_strip.collides_with_circle(Circle { pos, radius: rad_b }) + arc_strip.collides_with_circle(Disk::new(pos_b2, rad_b)) || last_pos_b2_maybe.map_or(false, |pos| { + arc_strip.collides_with_circle(Disk::new(pos, rad_b)) }) } && (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle - && (!shockwave.requires_ground || physics_state_b.on_ground); + && (!shockwave.requires_ground || physics_state_b.on_ground) + && !same_group; if hit { - // See if entities are in the same group - let same_group = group - .map(|group_a| Some(group_a) == groups.get(b)) - .unwrap_or(Some(*uid_b) == shockwave.owner); - - // Don't damage in the same group - if same_group { - continue; - } - - // Weapon gives base damage - let source = DamageSource::Shockwave; - let mut damage = Damage { healthchange: -(shockwave.damage as f32), - source, + source: DamageSource::Shockwave, }; let block = character_b.map(|c_b| c_b.is_block()).unwrap_or(false) - // TODO: investigate whether this calculation is proper for shockwaves && ori_b.0.angle_between(pos.0 - pos_b.0) < BLOCK_ANGLE.to_radians() / 2.0; if let Some(loadout) = loadouts.get(b) { damage.modify_damage(block, loadout); } - if damage.healthchange < 0.0 { + if damage.healthchange != 0.0 { + let cause = if damage.healthchange < 0.0 { + HealthSource::Attack { + by: shockwave.owner.unwrap_or(*uid), + } + } else { + HealthSource::Healing { + by: Some(shockwave.owner.unwrap_or(*uid)), + } + }; server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { amount: damage.healthchange as i32, - cause: HealthSource::Attack { - by: shockwave.owner.unwrap_or(*uid), - }, - }, - }); - } else if damage.healthchange > 0.0 { - server_emitter.emit(ServerEvent::Damage { - uid: *uid_b, - change: HealthChange { - amount: damage.healthchange as i32, - cause: HealthSource::Healing { - by: Some(shockwave.owner.unwrap_or(*uid)), - }, + cause, }, }); } if shockwave.knockback != 0.0 && damage.healthchange != 0.0 { - if shockwave.knockback < 0.0 { - server_emitter.emit(ServerEvent::Knockback { - entity: b, - force: shockwave.knockback - * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85), - }); + let impulse = if shockwave.knockback < 0.0 { + shockwave.knockback + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85) } else { - server_emitter.emit(ServerEvent::Knockback { - entity: b, - force: shockwave.knockback - * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), - }); - } + shockwave.knockback + * *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5) + }; + server_emitter.emit(ServerEvent::Knockback { entity: b, impulse }); } } } @@ -269,34 +245,28 @@ struct ArcStrip { } impl ArcStrip { - fn collides_with_circle(self, c: Circle) -> bool { + fn collides_with_circle(self, d: Disk) -> bool { // Quit if aabb's don't collide - if (self.origin.x - c.pos.x).abs() > self.end + c.radius - || (self.origin.y - c.pos.y).abs() > self.end + c.radius + if (self.origin.x - d.center.x).abs() > self.end + d.radius + || (self.origin.y - d.center.y).abs() > self.end + d.radius { return false; } - let dist = self.origin.distance(c.pos); + let dist = self.origin.distance(d.center); let half_angle = self.angle.to_radians() / 2.0; - if dist > self.end + c.radius || dist + c.radius < self.start { + if dist > self.end + d.radius || dist + d.radius < self.start { // Completely inside or outside full ring return false; } - let inside_edge = Circle { - pos: self.origin, - radius: self.start, - }; - let outside_edge = Circle { - pos: self.origin, - radius: self.end, - }; + let inside_edge = Disk::new(self.origin, self.start); + let outside_edge = Disk::new(self.origin, self.end); let inner_corner_in_circle = || { let midpoint = self.dir.normalized() * self.start; - c.contains_point(midpoint.rotated_z(half_angle) + self.origin) - || c.contains_point(midpoint.rotated_z(-half_angle) + self.origin) + d.contains_point(midpoint.rotated_z(half_angle) + self.origin) + || d.contains_point(midpoint.rotated_z(-half_angle) + self.origin) }; let arc_segment_in_circle = || { let midpoint = self.dir.normalized(); @@ -306,7 +276,7 @@ impl ArcStrip { start: dir * self.start + self.origin, end: dir * self.end + self.origin, }; - c.contains_point(side.projected_point(c.pos)) + d.contains_point(side.projected_point(d.center)) }; segment_in_circle(half_angle) || segment_in_circle(-half_angle) }; @@ -316,7 +286,7 @@ impl ArcStrip { // Check intersection with line segments arc_segment_in_circle() || { // Check angle of intersection points on outside edge of ring - let (p1, p2) = outside_edge.intersection_points(c, dist); + let (p1, p2) = intersection_points(outside_edge, d, dist); self.dir.angle_between(p1 - self.origin) < half_angle || self.dir.angle_between(p2 - self.origin) < half_angle } @@ -327,54 +297,43 @@ impl ArcStrip { inner_corner_in_circle() || ( // Check that the circles aren't identical - !inside_edge.is_approx_eq(c) && { - let (p1, p2) = inside_edge.intersection_points(c, dist); + inside_edge != d && { + let (p1, p2) = intersection_points(inside_edge, d, dist); self.dir.angle_between(p1 - self.origin) < half_angle || self.dir.angle_between(p2 - self.origin) < half_angle } ) - } else if c.radius > dist { + } else if d.radius > dist { // Circle center inside ring // but center of ring is inside the circle so we can't calculate the angle inner_corner_in_circle() } else { // Circle center inside ring // Calculate extra angle to account for circle radius - let extra_angle = (c.radius / dist).asin(); - self.dir.angle_between(c.pos - self.origin) < half_angle + extra_angle + let extra_angle = (d.radius / dist).asin(); + self.dir.angle_between(d.center - self.origin) < half_angle + extra_angle } } } -#[derive(Clone, Copy)] -struct Circle { - pos: Vec2, - radius: f32, -} -impl Circle { - // Assumes an intersection is occuring at 2 points - // Uses precalculated distance - // https://www.xarg.org/2016/07/calculate-the-intersection-points-of-two-circles/ - fn intersection_points(self, other: Self, dist: f32) -> (Vec2, Vec2) { - let e = (other.pos - self.pos) / dist; - - let x = (self.radius.powi(2) - other.radius.powi(2) + dist.powi(2)) / (2.0 * dist); - let y = (self.radius.powi(2) - x.powi(2)).sqrt(); - - let pxe = self.pos + x * e; - let eyx = e.yx(); - - let p1 = pxe + Vec2::new(-y, y) * eyx; - let p2 = pxe + Vec2::new(y, -y) * eyx; - - (p1, p2) - } - - fn contains_point(self, point: Vec2) -> bool { - point.distance_squared(self.pos) < self.radius.powi(2) - } - - fn is_approx_eq(self, other: Self) -> bool { - (self.pos - other.pos).is_approx_zero() && self.radius - other.radius < 0.001 - } +// Assumes an intersection is occuring at 2 points +// Uses precalculated distance +// https://www.xarg.org/2016/07/calculate-the-intersection-points-of-two-circles/ +fn intersection_points( + disk1: Disk, + disk2: Disk, + dist: f32, +) -> (Vec2, Vec2) { + let e = (disk2.center - disk1.center) / dist; + + let x = (disk1.radius.powi(2) - disk2.radius.powi(2) + dist.powi(2)) / (2.0 * dist); + let y = (disk1.radius.powi(2) - x.powi(2)).sqrt(); + + let pxe = disk1.center + x * e; + let eyx = e.yx(); + + let p1 = pxe + Vec2::new(-y, y) * eyx; + let p2 = pxe + Vec2::new(y, -y) * eyx; + + (p1, p2) } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 022f007b3c..4c9e3d048a 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -2,8 +2,9 @@ use crate::{sys, Server, StateExt}; use common::{ character::CharacterId, comp::{ - self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, shockwave, Agent, Alignment, Body, Gravity, Item, - ItemDrop, LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, shockwave, Agent, Alignment, Body, Gravity, + Item, ItemDrop, LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, + WaypointArea, }, outcome::Outcome, util::Dir, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 37331df6b0..ea1cdabb6b 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -32,16 +32,15 @@ pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) { } } -pub fn handle_knockback(server: &Server, entity: EcsEntity, force: Vec3) { +pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) { let state = &server.state; - let ecs = state.ecs(); - let mut velocities = ecs.write_storage::(); + let mut velocities = state.ecs().write_storage::(); if let Some(vel) = velocities.get_mut(entity) { - vel.0 = force; + vel.0 = impulse; } let mut clients = state.ecs().write_storage::(); if let Some(client) = clients.get_mut(entity) { - client.notify(ServerMsg::Knockback(force)); + client.notify(ServerMsg::Knockback(impulse)); } } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 224abd1b4b..8bbcf9926e 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -75,7 +75,9 @@ impl Server { pos, ori, } => handle_shockwave(self, properties, pos, ori), - ServerEvent::Knockback { entity, force } => handle_knockback(&self, entity, force), + ServerEvent::Knockback { entity, impulse } => { + handle_knockback(&self, entity, impulse) + }, ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 426274ef79..3424783e01 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -447,28 +447,24 @@ impl ParticleMgr { // 1 / 3 the size of terrain voxel let scale = 1.0 / 3.0; - let freqency_millis = shockwave.properties.speed * scale; + let scaled_speed = shockwave.properties.speed * scale; for heartbeat in 0..self .scheduler - .heartbeats(Duration::from_millis(freqency_millis as u64)) + .heartbeats(Duration::from_millis(scaled_speed as u64)) { - let sub_tick_interpolation = freqency_millis * 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); for d in 0..((distance / scale) as i32) { - let arc_position = (theta - (radians / 2.0)) + (dtheta * d as f32 * scale); + let arc_position = theta - radians / 2.0 + dtheta * d as f32 * scale; - let position = pos.0 - + Vec3::new( - distance * arc_position.cos(), - distance * 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).round() * scale; + let position_snapped = ((position / scale).floor() + 0.5) * scale; self.particles.push(Particle::new( Duration::from_millis(250), From 6deeb7fcfe0226d059d3c1ea12c2b5750ef3bf2c Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 19 Sep 2020 13:00:31 -0500 Subject: [PATCH 17/17] Addressed another comment. --- voxygen/src/scene/particle.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 3424783e01..a5c45fb843 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -449,10 +449,13 @@ impl ParticleMgr { let scaled_speed = shockwave.properties.speed * scale; - for heartbeat in 0..self + let heartbeats = self .scheduler - .heartbeats(Duration::from_millis(scaled_speed as u64)) - { + .heartbeats(Duration::from_millis(scaled_speed as u64)); + let new_particle_count = distance / scale * heartbeats as f32; + self.particles.reserve(new_particle_count as usize); + + for heartbeat in 0..heartbeats { let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32; let distance =