From 0b954f8db825bf1e49c671f133f324bf37332d35 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Fri, 12 Mar 2021 13:53:06 -0500 Subject: [PATCH] `/airship angle` command and RtSim airships. --- common/src/cmd.rs | 2 +- common/src/comp/agent.rs | 6 +-- common/src/comp/body.rs | 10 +++++ common/src/rtsim.rs | 6 +-- server/src/cmd.rs | 7 ++- server/src/rtsim/entity.rs | 4 +- server/src/rtsim/tick.rs | 5 ++- server/src/state_ext.rs | 91 ++++++++++++++++++++------------------ server/src/sys/agent.rs | 26 ++++++++++- 9 files changed, 100 insertions(+), 57 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 9aee158547..3da0e9b52d 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -224,7 +224,7 @@ impl ChatCommand { "Temporarily gives a player admin permissions or removes them", Admin, ), - ChatCommand::Airship => cmd(vec![Boolean("moving", "true".to_string(), Optional)], "Spawns an airship", Admin), + ChatCommand::Airship => cmd(vec![Float("destination_degrees_ccw_of_east", 90.0, Optional)], "Spawns an airship", Admin), ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", NoAdmin), ChatCommand::Ban => cmd( vec![Any("username", Required), Message(Optional)], diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 39af9ab5fb..d70221833b 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -205,11 +205,11 @@ impl Agent { self } - pub fn with_destination() -> Self { + pub fn with_destination(pos: Vec3) -> Self { Self { - can_speak: false, + can_speak: true, psyche: Psyche { aggro: 1.0 }, - rtsim_controller: RtSimController::zero(), + rtsim_controller: RtSimController::with_destination(pos), ..Default::default() } } diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 711399a8d1..929b7dbfd3 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -509,6 +509,16 @@ impl Body { } } + pub fn flying_height(&self) -> f32 { + match self { + Body::BirdSmall(_) => 30.0, + Body::BirdMedium(_) => 40.0, + Body::Dragon(_) => 60.0, + Body::Ship(ship::Body::DefaultAirship) => 60.0, + _ => 0.0, + } + } + pub fn immune_to(&self, buff: BuffKind) -> bool { match buff { BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)), diff --git a/common/src/rtsim.rs b/common/src/rtsim.rs index 809a505b1f..716bacf382 100644 --- a/common/src/rtsim.rs +++ b/common/src/rtsim.rs @@ -46,10 +46,10 @@ impl Default for RtSimController { impl RtSimController { pub fn reset(&mut self) { *self = Self::default(); } - pub fn zero() -> Self { + pub fn with_destination(pos: Vec3) -> Self { Self { - travel_to: Some((Vec3::new(0.0, 0.0, 500.0), "".to_string())), - speed_factor: 0.05, + travel_to: Some((pos, format!("{:0.1?}", pos))), + speed_factor: 0.25, } } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 2991fbdbd4..1fc70d95c2 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -965,13 +965,16 @@ fn handle_spawn_airship( args: String, action: &ChatCommand, ) { - let moving = scan_fmt_some!(&args, &action.arg_fmt(), String).unwrap_or_else(|| "false".to_string()) == "true"; + let angle = scan_fmt!(&args, &action.arg_fmt(), f32).ok(); match server.state.read_component_copied::(target) { Some(mut pos) => { pos.0.z += 50.0; + const DESTINATION_RADIUS: f32 = 2000.0; + let angle = angle.map(|a| a * std::f32::consts::PI / 180.0); + let destination = angle.map(|a| pos.0 + Vec3::new(DESTINATION_RADIUS*a.cos(), DESTINATION_RADIUS*a.sin(), 200.0)); server .state - .create_ship(pos, comp::ship::Body::DefaultAirship, 1, moving) + .create_ship(pos, comp::ship::Body::DefaultAirship, 1, destination) .with(comp::Scale(11.0 / 0.8)) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 60ac8fd832..7a42334261 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -27,7 +27,7 @@ impl Entity { pub fn get_body(&self) -> comp::Body { match self.rng(PERM_GENUS).gen::() { - //we want 50% birds, 50% humans for now + //we want 5% airships, 45% birds, 50% humans x if x < 0.05 => { comp::Body::Ship(comp::ship::Body::DefaultAirship) }, @@ -135,7 +135,7 @@ impl Entity { .iter() .filter(|s| match self.get_body() { comp::Body::Humanoid(_) => s.1.is_settlement() | s.1.is_castle(), - comp::Body::Ship(_) => s.1.is_castle(), + comp::Body::Ship(_) => s.1.is_settlement(), _ => s.1.is_dungeon(), }) .filter(|_| thread_rng().gen_range(0i32..4) == 0) diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 0ae3503f11..34ec9386f7 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -121,7 +121,10 @@ impl<'a> System<'a> for Sys { comp::Body::Humanoid(_) => comp::Alignment::Npc, _ => comp::Alignment::Wild, }, - scale: comp::Scale(1.0), + scale: match body { + comp::Body::Ship(_) => comp::Scale(11.0 / 0.8), + _ => comp::Scale(1.0), + }, drop_item: None, home_chunk: None, rtsim_entity: Some(RtSimEntity(id)), diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 8883d11408..e8fa9b6131 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -41,7 +41,13 @@ pub trait StateExt { ) -> EcsEntityBuilder; /// Build a static object entity fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder; - fn create_ship(&mut self, pos: comp::Pos, ship: comp::ship::Body, level: u16, moving: bool) -> EcsEntityBuilder; + fn create_ship( + &mut self, + pos: comp::Pos, + ship: comp::ship::Body, + level: u16, + destination: Option>, + ) -> EcsEntityBuilder; /// Build a projectile fn create_projectile( &mut self, @@ -161,10 +167,15 @@ impl StateExt for State { )) .unwrap_or_default(), ) - .with(comp::Collider::Box { - radius: body.radius(), - z_min: 0.0, - z_max: body.height(), + .with(match body { + comp::Body::Ship(ship) => comp::Collider::Voxel { + id: ship.manifest_entry().to_string(), + }, + _ => comp::Collider::Box { + radius: body.radius(), + z_min: 0.0, + z_max: body.height(), + }, }) .with(comp::Controller::default()) .with(body) @@ -203,46 +214,38 @@ impl StateExt for State { .with(comp::Gravity(1.0)) } - fn create_ship(&mut self, pos: comp::Pos, ship: comp::ship::Body, level: u16, moving: bool) -> EcsEntityBuilder { - if moving { - self.ecs_mut() - .create_entity_synced() - .with(pos) - .with(comp::Vel(Vec3::zero())) - .with(comp::Ori::default()) - .with(comp::Mass(50.0)) - .with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() }) - .with(comp::Body::Ship(ship)) - .with(comp::Gravity(1.0)) - .with(comp::Controller::default()) - .with(comp::inventory::Inventory::new_empty()) - .with(comp::CharacterState::default()) - .with(comp::Energy::new(ship.into(), level)) - .with(comp::Health::new(ship.into(), level)) - .with(comp::Stats::new("Airship".to_string())) - .with(comp::MountState::Unmounted) - .with(comp::Buffs::default()) - .with(comp::Combo::default()) - .with(comp::Agent::with_destination()) - } else { - self.ecs_mut() - .create_entity_synced() - .with(pos) - .with(comp::Vel(Vec3::zero())) - .with(comp::Ori::default()) - .with(comp::Mass(50.0)) - .with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() }) - .with(comp::Body::Ship(ship)) - .with(comp::Gravity(1.0)) - .with(comp::Controller::default()) - .with(comp::inventory::Inventory::new_empty()) - .with(comp::CharacterState::default()) - .with(comp::Energy::new(ship.into(), level)) - .with(comp::Health::new(ship.into(), level)) - .with(comp::Stats::new("Airship".to_string())) - .with(comp::Buffs::default()) - .with(comp::Combo::default()) + fn create_ship( + &mut self, + pos: comp::Pos, + ship: comp::ship::Body, + level: u16, + destination: Option>, + ) -> EcsEntityBuilder { + let mut builder = self + .ecs_mut() + .create_entity_synced() + .with(pos) + .with(comp::Vel(Vec3::zero())) + .with(comp::Ori::default()) + .with(comp::Mass(50.0)) + .with(comp::Collider::Voxel { + id: ship.manifest_entry().to_string(), + }) + .with(comp::Body::Ship(ship)) + .with(comp::Gravity(1.0)) + .with(comp::Controller::default()) + .with(comp::inventory::Inventory::new_empty()) + .with(comp::CharacterState::default()) + .with(comp::Energy::new(ship.into(), level)) + .with(comp::Health::new(ship.into(), level)) + .with(comp::Stats::new("Airship".to_string())) + .with(comp::Buffs::default()) + .with(comp::MountState::Unmounted) + .with(comp::Combo::default()); + if let Some(pos) = destination { + builder = builder.with(comp::Agent::with_destination(pos)) } + builder } fn create_projectile( diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index e2d3db0aeb..d4d6c8a98c 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -32,7 +32,7 @@ use specs::{ Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World, Write, WriteStorage, }; -use std::f32::consts::PI; +use std::{f32::consts::PI, sync::Arc}; use vek::*; struct AgentData<'a> { @@ -81,6 +81,7 @@ pub struct ReadData<'a> { //ReadStorage<'a, Invite>, time_of_day: Read<'a, TimeOfDay>, light_emitter: ReadStorage<'a, LightEmitter>, + world: ReadExpect<'a, Arc>, } // This is 3.1 to last longer than the last damage timer (3.0 seconds) @@ -603,6 +604,29 @@ impl<'a> AgentData<'a> { .cast() .1 .map_or(true, |b| b.is_some()) + || self + .body + .map(|body| { + let height_approx = self.pos.0.y + - read_data + .world + .sim() + .get_alt_approx(self.pos.0.xy().map(|x: f32| x as i32)) + .unwrap_or(0.0); + + height_approx < body.flying_height() + || read_data + .terrain + .ray( + self.pos.0, + self.pos.0 - body.flying_height() * Vec3::unit_z(), + ) + .until(|b: &Block| b.is_solid() || b.is_liquid()) + .cast() + .1 + .map_or(false, |b| b.is_some()) + }) + .unwrap_or(false) { 1.0 //fly up when approaching obstacles } else {