diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 553ff212db..b9d8bb19fc 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -182,6 +182,7 @@ https://account.veloren.net."#, "hud.chat.pvp_melee_kill_msg": "[{attacker}] defeated [{victim}]", "hud.chat.pvp_ranged_kill_msg": "[{attacker}] shot [{victim}]", "hud.chat.pvp_explosion_kill_msg": "[{attacker}] blew up [{victim}]", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] used magic to kill [{victim}]", "hud.chat.npc_melee_kill_msg": "{attacker} killed [{victim}]", "hud.chat.npc_ranged_kill_msg": "{attacker} shot [{victim}]", diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 66046d86cd..83120dfcab 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -123,9 +123,9 @@ vec3 spiral_motion(vec3 line, float radius, float time_function) { vec3 axis3 = perp_axis2(line, axis2); return line * time_function + vec3( - radius * cos(10 * time_function) * axis2.x + radius * sin(10 * time_function) * axis3.x, - radius * cos(10 * time_function) * axis2.y + radius * sin(10 * time_function) * axis3.y, - radius * cos(10 * time_function) * axis2.z + radius * sin(10 * time_function) * axis3.z + 1.0); + radius * cos(10 * time_function - inst_time) * axis2.x + radius * sin(10 * time_function - inst_time) * axis3.x, + radius * cos(10 * time_function - inst_time) * axis2.y + radius * sin(10 * time_function - inst_time) * axis3.y, + radius * cos(10 * time_function - inst_time) * axis2.z + radius * sin(10 * time_function - inst_time) * axis3.z + 1.0); } void main() { @@ -275,7 +275,7 @@ void main() { ); } else if (inst_mode == HEALING_BEAM) { attr = Attr( - spiral_motion(beam_pos2() - inst_pos, 0.3 * (floor(2 * hash(vec4(inst_time)) + 0.5) - 0.5), lifetime / 2), + spiral_motion(beam_pos2() - inst_pos, 0.3 * (floor(2 * hash(vec4(inst_time)) + 0.5) - 0.5), lifetime / 1), // The 1 that lifetime is divided by is the duration of the beam. It is currently hardcoded here due to limitations in passing in variables. vec3((1.7 - 0.7 * abs(floor(2 * hash(vec4(inst_time)) - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))), vec4(vec3(0.3, 0.7 + 0.4 * sin(tick.x * 8 - lifetime * 3), 0.3 + 0.1 * sin (tick.x * 2)), 0.3), spin_in_axis(vec3(inst_entropy, inst_misc, inst_lifespan), tick.z) diff --git a/client/src/lib.rs b/client/src/lib.rs index 4103f5507b..00543fdd7b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1673,6 +1673,11 @@ impl Client { alias_of_uid(attacker_uid), alias_of_uid(victim) ), + KillSource::Player(attacker_uid, KillType::Energy) => format!( + "[{}] used magic to kill [{}]", + alias_of_uid(attacker_uid), + alias_of_uid(victim) + ), KillSource::NonPlayer(attacker_name, KillType::Melee) => { format!("{} killed [{}]", attacker_name, alias_of_uid(victim)) }, @@ -1682,6 +1687,11 @@ impl Client { KillSource::NonPlayer(attacker_name, KillType::Explosion) => { format!("{} blew up [{}]", attacker_name, alias_of_uid(victim)) }, + KillSource::NonPlayer(attacker_name, KillType::Energy) => format!( + "{} used magic to kill [{}]", + attacker_name, + alias_of_uid(victim) + ), KillSource::Environment(environment) => { format!("[{}] died in {}", alias_of_uid(victim), environment) }, @@ -1704,6 +1714,9 @@ impl Client { KillSource::Player(attacker_uid, KillType::Explosion) => message .replace("{attacker}", &alias_of_uid(attacker_uid)) .replace("{victim}", &alias_of_uid(victim)), + KillSource::Player(attacker_uid, KillType::Energy) => message + .replace("{attacker}", &alias_of_uid(attacker_uid)) + .replace("{victim}", &alias_of_uid(victim)), KillSource::NonPlayer(attacker_name, KillType::Melee) => message .replace("{attacker}", attacker_name) .replace("{victim}", &alias_of_uid(victim)), @@ -1713,6 +1726,9 @@ impl Client { KillSource::NonPlayer(attacker_name, KillType::Explosion) => message .replace("{attacker}", attacker_name) .replace("{victim}", &alias_of_uid(victim)), + KillSource::NonPlayer(attacker_name, KillType::Energy) => message + .replace("{attacker}", attacker_name) + .replace("{victim}", &alias_of_uid(victim)), KillSource::Environment(environment) => message .replace("{name}", &alias_of_uid(victim)) .replace("{environment}", environment), diff --git a/common/src/comp/chat.rs b/common/src/comp/chat.rs index 613f9f38e2..71af8a6960 100644 --- a/common/src/comp/chat.rs +++ b/common/src/comp/chat.rs @@ -50,6 +50,7 @@ pub enum KillType { Melee, Projectile, Explosion, + Energy, // Projectile(String), TODO: add projectile name when available } diff --git a/common/src/comp/damage.rs b/common/src/comp/damage.rs index a125045eb8..6fae9d3e71 100644 --- a/common/src/comp/damage.rs +++ b/common/src/comp/damage.rs @@ -40,11 +40,6 @@ impl Damage { 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 { - self.healthchange = -10.0; - } }, DamageSource::Projectile => { // Critical hit @@ -58,11 +53,6 @@ impl Damage { // Armor let damage_reduction = loadout.get_damage_reduction(); self.healthchange *= 1.0 - damage_reduction; - - // Min damage - if (damage_reduction - 1.0).abs() > f32::EPSILON && self.healthchange > -10.0 { - self.healthchange = -10.0; - } }, DamageSource::Explosion => { // Block @@ -72,31 +62,16 @@ impl Damage { // Armor let damage_reduction = loadout.get_damage_reduction(); self.healthchange *= 1.0 - damage_reduction; - - // Min damage - if (damage_reduction - 1.0).abs() > f32::EPSILON && self.healthchange > -10.0 { - self.healthchange = -10.0; - } }, DamageSource::Shockwave => { // Armor let damage_reduction = loadout.get_damage_reduction(); self.healthchange *= 1.0 - damage_reduction; - - // Min damage - if (damage_reduction - 1.0).abs() > f32::EPSILON && self.healthchange > -10.0 { - self.healthchange = -10.0; - } }, DamageSource::Energy => { // Armor let damage_reduction = loadout.get_damage_reduction(); self.healthchange *= 1.0 - damage_reduction; - - // Min damage - if (damage_reduction - 1.0).abs() > f32::EPSILON && 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 58eec4a82c..19405e5432 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -306,7 +306,7 @@ impl Tool { energy_cost: 0, buildup_duration: Duration::from_millis(250), recover_duration: Duration::from_millis(250), - beam_duration: Duration::from_secs(2), + beam_duration: Duration::from_secs(1), base_hps: (60.0 * self.base_power()) as u32, base_dps: (40.0 * self.base_power()) as u32, tick_rate: 2.0, diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 4cb6bfd6b3..e1e129b6b3 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -19,6 +19,7 @@ pub enum HealthSource { Attack { by: Uid }, // TODO: Implement weapon Projectile { owner: Option }, Explosion { owner: Option }, + Energy { owner: Option }, Suicide, World, Revive, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 1ca7bed62f..27ea8a810a 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -498,7 +498,9 @@ impl<'a> System<'a> for Sys { // Only if the attack was recent if my_stats.health.last_change.0 < 3.0 { if let comp::HealthSource::Attack { by } - | comp::HealthSource::Projectile { owner: Some(by) } = + | comp::HealthSource::Projectile { owner: Some(by) } + | comp::HealthSource::Energy { owner: Some(by) } + | comp::HealthSource::Explosion { owner: Some(by) } = my_stats.health.last_change.1.cause { if !agent.activity.is_attack() { diff --git a/common/src/sys/beam.rs b/common/src/sys/beam.rs index 4d8a5e44a4..7b8f62fc88 100644 --- a/common/src/sys/beam.rs +++ b/common/src/sys/beam.rs @@ -190,7 +190,11 @@ impl<'a> System<'a> for Sys { } if damage.healthchange != 0.0 { - let cause = if is_heal { HealthSource::Healing { by: Some(*uid) } } else { HealthSource::Attack { by: *uid } }; + let cause = if is_heal { + HealthSource::Healing { by: beam.owner } + } else { + HealthSource::Energy { owner: beam.owner } + }; server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { @@ -203,9 +207,7 @@ impl<'a> System<'a> for Sys { uid: beam.owner.unwrap_or(*uid), change: HealthChange { amount: (-damage.healthchange * beam.lifesteal_eff) as i32, - cause: HealthSource::Attack { - by: beam.owner.unwrap_or(*uid), - }, + cause: HealthSource::Healing { by: beam.owner }, }, }); } diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index ce0cc8cdee..d190083b34 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -148,7 +148,11 @@ impl<'a> System<'a> for Sys { } if damage.healthchange != 0.0 { - let cause = if is_heal { HealthSource::Healing { by: Some(*uid) } } else { HealthSource::Attack { by: *uid } }; + let cause = if is_heal { + HealthSource::Healing { by: Some(*uid) } + } else { + HealthSource::Attack { by: *uid } + }; server_emitter.emit(ServerEvent::Damage { uid: *uid_b, change: HealthChange { diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index bf754691fd..461d17197a 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,6 +1,6 @@ use crate::{ comp::{ - Collider, Gravity, Group, Mass, Mounting, Ori, PhysicsState, Pos, Projectile, Scale, + Beam, Collider, Gravity, Group, Mass, Mounting, Ori, PhysicsState, Pos, Projectile, Scale, Sticky, Vel, }, event::{EventBus, ServerEvent}, @@ -70,6 +70,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Mounting>, ReadStorage<'a, Group>, ReadStorage<'a, Projectile>, + ReadStorage<'a, Beam>, ); #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 @@ -96,6 +97,7 @@ impl<'a> System<'a> for Sys { mountings, groups, projectiles, + beams, ): Self::SystemData, ) { let start_time = std::time::Instant::now(); @@ -133,7 +135,7 @@ impl<'a> System<'a> for Sys { // it means the step needs to take into account the speeds of both // entities. span!(guard, "Apply pushback"); - for (entity, pos, scale, mass, collider, _, _, physics, projectile) in ( + for (entity, pos, scale, mass, collider, _, _, physics, projectile, _) in ( &entities, &positions, scales.maybe(), @@ -145,10 +147,13 @@ impl<'a> System<'a> for Sys { // TODO: if we need to avoid collisions for other things consider moving whether it // should interact into the collider component or into a separate component projectiles.maybe(), + beams.maybe(), ) .join() - .filter(|(_, _, _, _, _, _, sticky, physics, _)| { - sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground) + .filter(|(_, _, _, _, _, _, sticky, physics, _, beam)| { + sticky.is_none() + || (physics.on_wall.is_none() && !physics.on_ground) + || beam.is_none() }) { let scale = scale.map(|s| s.0).unwrap_or(1.0); @@ -177,6 +182,7 @@ impl<'a> System<'a> for Sys { collider_other, _, group_b, + _, ) in ( &entities, &uids, @@ -186,8 +192,10 @@ impl<'a> System<'a> for Sys { colliders.maybe(), !&mountings, groups.maybe(), + beams.maybe(), ) .join() + .filter(|(_, _, _, _, _, _, _, _, beam)| beam.is_none()) { if entity == entity_other || (ignore_group.is_some() && ignore_group == group_b) { continue; diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 44eb6f0804..d41dab793c 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -138,10 +138,33 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc KillSource::NonPlayer("".to_string(), KillType::Explosion) } }, + HealthSource::Energy { owner: Some(by) } => { + // Get energy owner entity + if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) { + // Check if attacker is another player or entity with stats (npc) + if state + .ecs() + .read_storage::() + .get(char_entity) + .is_some() + { + KillSource::Player(by, KillType::Energy) + } else if let Some(stats) = + state.ecs().read_storage::().get(char_entity) + { + KillSource::NonPlayer(stats.name.clone(), KillType::Energy) + } else { + KillSource::NonPlayer("".to_string(), KillType::Energy) + } + } else { + KillSource::NonPlayer("".to_string(), KillType::Energy) + } + }, HealthSource::World => KillSource::FallDamage, HealthSource::Suicide => KillSource::Suicide, HealthSource::Projectile { owner: None } | HealthSource::Explosion { owner: None } + | HealthSource::Energy { owner: None } | HealthSource::Revive | HealthSource::Command | HealthSource::LevelUp @@ -158,8 +181,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc // Give EXP to the killer if entity had stats (|| { let mut stats = state.ecs().write_storage::(); - let by = if let HealthSource::Attack { by } | HealthSource::Projectile { owner: Some(by) } = - cause + let by = if let HealthSource::Attack { by } + | HealthSource::Projectile { owner: Some(by) } + | HealthSource::Energy { owner: Some(by) } + | HealthSource::Explosion { owner: Some(by) } = cause { by } else { @@ -534,7 +559,11 @@ pub fn handle_explosion( } if damage.healthchange != 0.0 { - let cause = if is_heal { HealthSource::Healing { by: owner } } else { HealthSource::Explosion { owner } }; + let cause = if is_heal { + HealthSource::Healing { by: owner } + } else { + HealthSource::Explosion { owner } + }; stats_b.health.change_by(HealthChange { amount: damage.healthchange as i32, cause, diff --git a/voxygen/src/ecs/sys/floater.rs b/voxygen/src/ecs/sys/floater.rs index fcf5cd7eb6..6964010d9f 100644 --- a/voxygen/src/ecs/sys/floater.rs +++ b/voxygen/src/ecs/sys/floater.rs @@ -72,7 +72,11 @@ impl<'a> System<'a> for Sys { // (maybe health changes could be sent to the client as a list // of events) if match health.last_change.1.cause { - HealthSource::Attack { by } | HealthSource::Projectile { owner: Some(by) } => { + HealthSource::Attack { by } + | HealthSource::Projectile { owner: Some(by) } + | HealthSource::Energy { owner: Some(by) } + | HealthSource::Explosion { owner: Some(by) } + | HealthSource::Healing { by: Some(by) } => { let by_me = my_uid.map_or(false, |&uid| by == uid); // If the attack was by me also reset this timer if by_me { diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 20baed4b9f..8194ddf445 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -369,6 +369,10 @@ impl<'a> Widget for Chat<'a> { .localized_strings .get("hud.chat.pvp_explosion_kill_msg") .to_string(), + KillSource::Player(_, KillType::Energy) => self + .localized_strings + .get("hud.chat.pvp_energy_kill_msg") + .to_string(), KillSource::NonPlayer(_, KillType::Melee) => self .localized_strings .get("hud.chat.npc_melee_kill_msg") @@ -381,6 +385,10 @@ impl<'a> Widget for Chat<'a> { .localized_strings .get("hud.chat.npc_explosion_kill_msg") .to_string(), + KillSource::NonPlayer(_, KillType::Energy) => self + .localized_strings + .get("hud.chat.npc_energy_kill_msg") + .to_string(), KillSource::Environment(_) => self .localized_strings .get("hud.chat.environmental_kill_msg") diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 0551c263c4..7014eef8fe 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1257,14 +1257,14 @@ impl Hud { let fade = ((crate::ecs::sys::floater::HP_SHOWTIME - timer) * 0.25) + 0.2; if hp_damage < 10 { // Damage and heal below 10/10 are shown as decimals - Text::new(&format!("{}", hp_damage as f32 / 10.0)) + Text::new(&format!("{}", hp_damage.abs() as f32 / 10.0)) .font_size(font_size) .font_id(self.fonts.cyri.conrod_id) .color(Color::Rgba(0.0, 0.0, 0.0, fade)) .x_y(0.0, y - 3.0) .position_ingame(ingame_pos) .set(sct_bg_id, ui_widgets); - Text::new(&format!("{}", hp_damage as f32 / 10.0)) + Text::new(&format!("{}", hp_damage.abs() as f32 / 10.0)) .font_size(font_size) .font_id(self.fonts.cyri.conrod_id) .x_y(0.0, y) diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index d9f28875d2..f30027b0e2 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -352,13 +352,16 @@ impl ParticleMgr { { if let CharacterState::BasicBeam(b) = character_state { let particle_ori = b.particle_ori.unwrap_or(*ori.vec()); - for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) { + for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) { if b.buildup_duration == Duration::default() { self.particles.push(Particle::new_beam( b.beam_duration, - time, + time + (i as f64) / 1000.0, ParticleMode::HealingBeam, - pos.0, + pos.0 + + (i as f32 / b.beam_duration.as_millis() as f32) + * particle_ori + * b.range, pos.0 + particle_ori * b.range, )); }