From 0b1a820762c10d03fd778c0be2bb7ff50760cacf Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 30 Aug 2021 16:02:13 +0100 Subject: [PATCH 1/2] Make arrows 'bonk' hanging sprites --- common/src/comp/projectile.rs | 3 ++- common/src/event.rs | 5 ++++ common/src/terrain/block.rs | 8 ++++++ common/src/terrain/sprite.rs | 1 + common/systems/src/phys.rs | 7 +++-- common/systems/src/projectile.rs | 16 +++++++++++ server/src/events/entity_manipulation.rs | 34 ++++++++++++++++++++++-- server/src/events/mod.rs | 7 ++--- 8 files changed, 71 insertions(+), 10 deletions(-) diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 27b5ce719b..742fd7283a 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -19,6 +19,7 @@ pub enum Effect { Vanish, Stick, Possess, + Bonk, // Knock/dislodge/change objects on hit } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -127,7 +128,7 @@ impl ProjectileConstructor { .with_combo_increment(); Projectile { - hit_solid: vec![Effect::Stick], + hit_solid: vec![Effect::Stick, Effect::Bonk], hit_entity: vec![Effect::Attack(attack), Effect::Vanish], time_left: Duration::from_secs(15), owner, diff --git a/common/src/event.rs b/common/src/event.rs index 1d48bac86b..79ae53fbdd 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -42,6 +42,11 @@ pub enum ServerEvent { explosion: Explosion, owner: Option, }, + Bonk { + pos: Vec3, + owner: Option, + target: Option, + }, Damage { entity: EcsEntity, change: comp::HealthChange, diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 5a99767d0b..5f653b2ce3 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -259,6 +259,14 @@ impl Block { .unwrap_or(false) } + #[inline] + pub fn is_bonkable(&self) -> bool { + match self.get_sprite() { + Some(SpriteKind::Apple | SpriteKind::Beehive | SpriteKind::Coconut) => self.is_solid(), + _ => false, + } + } + /// The tool required to mine this block. For blocks that cannot be mined, /// `None` is returned. #[inline] diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 9c48b4282b..d7e918dd9d 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -238,6 +238,7 @@ impl SpriteKind { | SpriteKind::MedFlatCactus | SpriteKind::ShortFlatCactus | SpriteKind::Apple + | SpriteKind::Beehive | SpriteKind::Velorite | SpriteKind::VeloriteFrag | SpriteKind::Coconut diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index b75c7e9315..a83ba2ddce 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -872,15 +872,14 @@ impl<'a> PhysicsData<'a> { .terrain .get(pos.0.map(|e| e.floor() as i32)) .ok() - .filter(|b| b.is_filled()) - // TODO: `is_solid`, when arrows are special-cased + .filter(|b| b.is_solid()) { (0.0, Some(block)) } else { let (dist, block) = read .terrain .ray(pos.0, pos.0 + pos_delta) - .until(|block: &Block| block.is_filled()) + .until(|block: &Block| block.is_solid()) .ignore_error() .cast(); (dist, block.unwrap()) // Can't fail since we do ignore_error above @@ -897,7 +896,7 @@ impl<'a> PhysicsData<'a> { .zip(read.bodies.get(entity).copied()) { outcomes.push(Outcome::ProjectileHit { - pos: pos.0, + pos: pos.0 + pos_delta * dist, body, vel: vel.0, source: projectile.owner, diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index f5c4b3d4c8..44f7fe66cd 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -181,6 +181,13 @@ impl<'a> System<'a> for Sys { }); projectile_vanished = true; }, + projectile::Effect::Bonk => { + server_emitter.emit(ServerEvent::Bonk { + pos: pos.0, + owner: projectile.owner, + target: None, + }); + }, _ => {}, } } @@ -325,6 +332,15 @@ fn dispatch_hit( owner: owner_uid, }); }, + projectile::Effect::Bonk => { + let Pos(pos) = *projectile_info.pos; + let owner_uid = projectile_info.owner_uid; + server_emitter.emit(ServerEvent::Bonk { + pos, + owner: owner_uid, + target: Some(projectile_target_info.uid), + }); + }, projectile::Effect::Vanish => { let entity = projectile_info.entity; server_emitter.emit(ServerEvent::Destroy { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 3adbcdbe9a..20ab8f2788 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -37,7 +37,7 @@ use common_state::BlockChange; use comp::chat::GenericChatMsg; use hashbrown::HashSet; use rand::Rng; -use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; +use specs::{join::Join, saveload::MarkerAllocator, Builder, Entity as EcsEntity, WorldExt}; use tracing::error; use vek::{Vec2, Vec3}; @@ -378,7 +378,6 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc { // Only drop loot if entity has agency (not a player), and if it is not owned by // another entity (not a pet) - use specs::Builder; // Decide for a loot drop before turning into a lootbag let old_body = state.ecs().write_storage::().remove(entity); @@ -1021,6 +1020,37 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o } } +pub fn handle_bonk(server: &mut Server, pos: Vec3, owner: Option, target: Option) { + let ecs = &server.state.ecs(); + let terrain = ecs.read_resource::(); + let mut block_change = ecs.write_resource::(); + + if let Some(_target) = target { + // TODO: bonk entities but do no damage? + drop(owner); + } else { + use common::terrain::SpriteKind; + let pos = pos.map(|e| e.floor() as i32); + if let Some(block) = terrain.get(pos).ok().copied().filter(|b| b.is_bonkable()) { + if let Some(item) = comp::Item::try_reclaim_from_block(block) { + if block_change + .try_set(pos, block.with_sprite(SpriteKind::Empty)) + .is_some() + { + drop(terrain); + drop(block_change); + server + .state + .create_object(Default::default(), comp::object::Body::Pouch) + .with(comp::Pos(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0))) + .with(item) + .build(); + } + } + } + } +} + pub fn handle_aura(server: &mut Server, entity: EcsEntity, aura_change: aura::AuraChange) { let ecs = &server.state.ecs(); let mut auras_all = ecs.write_storage::(); diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index af3d2baeed..ee78736acc 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -6,9 +6,9 @@ use entity_creation::{ handle_initialize_character, handle_loaded_character_data, handle_shockwave, handle_shoot, }; use entity_manipulation::{ - handle_aura, handle_buff, handle_combo_change, handle_damage, handle_delete, handle_destroy, - handle_energy_change, handle_explosion, handle_knockback, handle_land_on_ground, handle_poise, - handle_respawn, handle_teleport_to, + handle_aura, handle_bonk, handle_buff, handle_combo_change, handle_damage, handle_delete, + handle_destroy, handle_energy_change, handle_explosion, handle_knockback, + handle_land_on_ground, handle_poise, handle_respawn, handle_teleport_to, }; use group_manip::handle_group; use information::handle_site_info; @@ -67,6 +67,7 @@ impl Server { explosion, owner, } => handle_explosion(self, pos, explosion, owner), + ServerEvent::Bonk { pos, owner, target } => handle_bonk(self, pos, owner, target), ServerEvent::Shoot { entity, dir, From 6f15233448b7a8e8e49399b62ff617dc75d33981 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 31 Aug 2021 12:50:07 +0100 Subject: [PATCH 2/2] Fixed first-person zoom, added fixation to allow more precise mouse movement when zoomed --- CHANGELOG.md | 1 + server/src/events/entity_manipulation.rs | 3 +-- voxygen/src/scene/camera.rs | 30 ++++++++++++++++++++---- voxygen/src/scene/mod.rs | 2 +- voxygen/src/session/mod.rs | 14 +---------- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bc52937dc..ff9424baef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Explosions no longer change block colours within safe zones - The 'spot' system, which generates smaller site-like structures and scenarios - Chestnut and cedar tree varieties +- Shooting sprites, such as apples and hives, can knock them out of trees ### Changed diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 20ab8f2788..972fdf9a6c 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1020,14 +1020,13 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o } } -pub fn handle_bonk(server: &mut Server, pos: Vec3, owner: Option, target: Option) { +pub fn handle_bonk(server: &mut Server, pos: Vec3, _owner: Option, target: Option) { let ecs = &server.state.ecs(); let terrain = ecs.read_resource::(); let mut block_change = ecs.write_resource::(); if let Some(_target) = target { // TODO: bonk entities but do no damage? - drop(owner); } else { use common::terrain::SpriteKind; let pos = pos.map(|e| e.floor() as i32); diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index b5db7df96d..01dda44cb8 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -49,6 +49,8 @@ pub struct Camera { dist: f32, tgt_fov: f32, fov: f32, + tgt_fixate: f32, + fixate: f32, aspect: f32, mode: CameraMode, @@ -327,6 +329,8 @@ impl Camera { dist: 10.0, tgt_fov: 1.1, fov: 1.1, + tgt_fixate: 1.0, + fixate: 1.0, aspect, mode, @@ -466,13 +470,14 @@ impl Camera { * Mat4::translation_3d(-self.focus.map(|e| e.fract())); let view_mat_inv: Mat4 = view_mat.inverted(); + let fov = self.get_effective_fov(); // NOTE: We reverse the far and near planes to produce an inverted depth // buffer (1 to 0 z planes). let proj_mat = - perspective_rh_zo_general(self.fov, self.aspect, 1.0 / FAR_PLANE, 1.0 / NEAR_PLANE); + perspective_rh_zo_general(fov, self.aspect, 1.0 / FAR_PLANE, 1.0 / NEAR_PLANE); // For treeculler, we also produce a version without inverted depth. let proj_mat_treeculler = - perspective_rh_zo_general(self.fov, self.aspect, 1.0 / NEAR_PLANE, 1.0 / FAR_PLANE); + perspective_rh_zo_general(fov, self.aspect, 1.0 / NEAR_PLANE, 1.0 / FAR_PLANE); Dependents { view_mat, @@ -501,6 +506,7 @@ impl Camera { /// Rotate the camera about its focus by the given delta, limiting the input /// accordingly. pub fn rotate_by(&mut self, delta: Vec3) { + let delta = delta * self.fixate; // Wrap camera yaw self.tgt_ori.x = (self.tgt_ori.x + delta.x).rem_euclid(2.0 * PI); // Clamp camera pitch to the vertical limits @@ -575,6 +581,14 @@ impl Camera { ); } + if (self.fixate - self.tgt_fixate).abs() > 0.01 { + self.fixate = vek::Lerp::lerp( + self.fixate, + self.tgt_fixate, + 0.65 * (delta as f32) / self.interp_time(), + ); + } + if (self.focus - self.tgt_focus).magnitude_squared() > 0.001 { let lerped_focus = Lerp::lerp( self.focus, @@ -639,12 +653,20 @@ impl Camera { /// Get the orientation of the camera. pub fn get_orientation(&self) -> Vec3 { self.ori } - /// Get the field of view of the camera in radians. - pub fn get_fov(&self) -> f32 { self.fov } + /// Get the field of view of the camera in radians, taking into account + /// fixation. + pub fn get_effective_fov(&self) -> f32 { self.fov * self.fixate } + + // /// Get the field of view of the camera in radians. + // pub fn get_fov(&self) -> f32 { self.fov } /// Set the field of view of the camera in radians. pub fn set_fov(&mut self, fov: f32) { self.tgt_fov = fov; } + /// Set the 'fixation' proportion, allowing the camera to focus in with + /// precise aiming. Fixation is applied on top of the regular FoV. + pub fn set_fixate(&mut self, fixate: f32) { self.tgt_fixate = fixate; } + /// Set the FOV in degrees pub fn set_fov_deg(&mut self, fov: u16) { //Magic value comes from pi/180; no use recalculating. diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 4f6d0af4d5..fe7aa247c5 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -682,7 +682,7 @@ impl Scene { let sun_dir = scene_data.get_sun_dir(); let is_daylight = sun_dir.z < 0.0; if renderer.pipeline_modes().shadow.is_map() && (is_daylight || !lights.is_empty()) { - let fov = self.camera.get_fov(); + let fov = self.camera.get_effective_fov(); let aspect_ratio = self.camera.get_aspect_ratio(); let view_dir = ((focus_pos.map(f32::fract)) - cam_pos).normalized(); diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 07114633fb..fbfb92277b 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -77,7 +77,6 @@ pub struct SessionState { target_entity: Option, selected_entity: Option<(specs::Entity, std::time::Instant)>, interactable: Option, - saved_zoom_dist: Option, hitboxes: HashMap, } @@ -125,7 +124,6 @@ impl SessionState { target_entity: None, selected_entity: None, interactable: None, - saved_zoom_dist: None, hitboxes: HashMap::new(), } } @@ -328,18 +326,8 @@ impl PlayState for SessionState { if cr.charge_frac() > 0.5 { fov_scaling -= 3.0 * cr.charge_frac() / 5.0; } - let max_dist = if let Some(dist) = self.saved_zoom_dist { - dist - } else { - let dist = camera.get_distance(); - self.saved_zoom_dist = Some(dist); - dist - }; - camera.set_distance(Lerp::lerp(max_dist, 2.0, 1.0 - fov_scaling)); - } else if let Some(dist) = self.saved_zoom_dist.take() { - camera.set_distance(dist); } - camera.set_fov((global_state.settings.graphics.fov as f32 * fov_scaling).to_radians()); + camera.set_fixate(fov_scaling); // Compute camera data camera.compute_dependents(&*self.client.borrow().state().terrain());