/airship angle command and RtSim airships.

This commit is contained in:
Avi Weinstock 2021-03-12 13:53:06 -05:00
parent 64cc97ad59
commit 0b954f8db8
9 changed files with 100 additions and 57 deletions

View File

@ -224,7 +224,7 @@ impl ChatCommand {
"Temporarily gives a player admin permissions or removes them", "Temporarily gives a player admin permissions or removes them",
Admin, 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::Alias => cmd(vec![Any("name", Required)], "Change your alias", NoAdmin),
ChatCommand::Ban => cmd( ChatCommand::Ban => cmd(
vec![Any("username", Required), Message(Optional)], vec![Any("username", Required), Message(Optional)],

View File

@ -205,11 +205,11 @@ impl Agent {
self self
} }
pub fn with_destination() -> Self { pub fn with_destination(pos: Vec3<f32>) -> Self {
Self { Self {
can_speak: false, can_speak: true,
psyche: Psyche { aggro: 1.0 }, psyche: Psyche { aggro: 1.0 },
rtsim_controller: RtSimController::zero(), rtsim_controller: RtSimController::with_destination(pos),
..Default::default() ..Default::default()
} }
} }

View File

@ -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 { pub fn immune_to(&self, buff: BuffKind) -> bool {
match buff { match buff {
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)), BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),

View File

@ -46,10 +46,10 @@ impl Default for RtSimController {
impl RtSimController { impl RtSimController {
pub fn reset(&mut self) { *self = Self::default(); } pub fn reset(&mut self) { *self = Self::default(); }
pub fn zero() -> Self { pub fn with_destination(pos: Vec3<f32>) -> Self {
Self { Self {
travel_to: Some((Vec3::new(0.0, 0.0, 500.0), "".to_string())), travel_to: Some((pos, format!("{:0.1?}", pos))),
speed_factor: 0.05, speed_factor: 0.25,
} }
} }
} }

View File

@ -965,13 +965,16 @@ fn handle_spawn_airship(
args: String, args: String,
action: &ChatCommand, 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::<comp::Pos>(target) { match server.state.read_component_copied::<comp::Pos>(target) {
Some(mut pos) => { Some(mut pos) => {
pos.0.z += 50.0; 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 server
.state .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(comp::Scale(11.0 / 0.8))
.with(LightEmitter { .with(LightEmitter {
col: Rgb::new(1.0, 0.65, 0.2), col: Rgb::new(1.0, 0.65, 0.2),

View File

@ -27,7 +27,7 @@ impl Entity {
pub fn get_body(&self) -> comp::Body { pub fn get_body(&self) -> comp::Body {
match self.rng(PERM_GENUS).gen::<f32>() { match self.rng(PERM_GENUS).gen::<f32>() {
//we want 50% birds, 50% humans for now //we want 5% airships, 45% birds, 50% humans
x if x < 0.05 => { x if x < 0.05 => {
comp::Body::Ship(comp::ship::Body::DefaultAirship) comp::Body::Ship(comp::ship::Body::DefaultAirship)
}, },
@ -135,7 +135,7 @@ impl Entity {
.iter() .iter()
.filter(|s| match self.get_body() { .filter(|s| match self.get_body() {
comp::Body::Humanoid(_) => s.1.is_settlement() | s.1.is_castle(), 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(), _ => s.1.is_dungeon(),
}) })
.filter(|_| thread_rng().gen_range(0i32..4) == 0) .filter(|_| thread_rng().gen_range(0i32..4) == 0)

View File

@ -121,7 +121,10 @@ impl<'a> System<'a> for Sys {
comp::Body::Humanoid(_) => comp::Alignment::Npc, comp::Body::Humanoid(_) => comp::Alignment::Npc,
_ => comp::Alignment::Wild, _ => 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, drop_item: None,
home_chunk: None, home_chunk: None,
rtsim_entity: Some(RtSimEntity(id)), rtsim_entity: Some(RtSimEntity(id)),

View File

@ -41,7 +41,13 @@ pub trait StateExt {
) -> EcsEntityBuilder; ) -> EcsEntityBuilder;
/// Build a static object entity /// Build a static object entity
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder; 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<Vec3<f32>>,
) -> EcsEntityBuilder;
/// Build a projectile /// Build a projectile
fn create_projectile( fn create_projectile(
&mut self, &mut self,
@ -161,10 +167,15 @@ impl StateExt for State {
)) ))
.unwrap_or_default(), .unwrap_or_default(),
) )
.with(comp::Collider::Box { .with(match body {
radius: body.radius(), comp::Body::Ship(ship) => comp::Collider::Voxel {
z_min: 0.0, id: ship.manifest_entry().to_string(),
z_max: body.height(), },
_ => comp::Collider::Box {
radius: body.radius(),
z_min: 0.0,
z_max: body.height(),
},
}) })
.with(comp::Controller::default()) .with(comp::Controller::default())
.with(body) .with(body)
@ -203,46 +214,38 @@ impl StateExt for State {
.with(comp::Gravity(1.0)) .with(comp::Gravity(1.0))
} }
fn create_ship(&mut self, pos: comp::Pos, ship: comp::ship::Body, level: u16, moving: bool) -> EcsEntityBuilder { fn create_ship(
if moving { &mut self,
self.ecs_mut() pos: comp::Pos,
.create_entity_synced() ship: comp::ship::Body,
.with(pos) level: u16,
.with(comp::Vel(Vec3::zero())) destination: Option<Vec3<f32>>,
.with(comp::Ori::default()) ) -> EcsEntityBuilder {
.with(comp::Mass(50.0)) let mut builder = self
.with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() }) .ecs_mut()
.with(comp::Body::Ship(ship)) .create_entity_synced()
.with(comp::Gravity(1.0)) .with(pos)
.with(comp::Controller::default()) .with(comp::Vel(Vec3::zero()))
.with(comp::inventory::Inventory::new_empty()) .with(comp::Ori::default())
.with(comp::CharacterState::default()) .with(comp::Mass(50.0))
.with(comp::Energy::new(ship.into(), level)) .with(comp::Collider::Voxel {
.with(comp::Health::new(ship.into(), level)) id: ship.manifest_entry().to_string(),
.with(comp::Stats::new("Airship".to_string())) })
.with(comp::MountState::Unmounted) .with(comp::Body::Ship(ship))
.with(comp::Buffs::default()) .with(comp::Gravity(1.0))
.with(comp::Combo::default()) .with(comp::Controller::default())
.with(comp::Agent::with_destination()) .with(comp::inventory::Inventory::new_empty())
} else { .with(comp::CharacterState::default())
self.ecs_mut() .with(comp::Energy::new(ship.into(), level))
.create_entity_synced() .with(comp::Health::new(ship.into(), level))
.with(pos) .with(comp::Stats::new("Airship".to_string()))
.with(comp::Vel(Vec3::zero())) .with(comp::Buffs::default())
.with(comp::Ori::default()) .with(comp::MountState::Unmounted)
.with(comp::Mass(50.0)) .with(comp::Combo::default());
.with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() }) if let Some(pos) = destination {
.with(comp::Body::Ship(ship)) builder = builder.with(comp::Agent::with_destination(pos))
.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())
} }
builder
} }
fn create_projectile( fn create_projectile(

View File

@ -32,7 +32,7 @@ use specs::{
Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World, Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World,
Write, WriteStorage, Write, WriteStorage,
}; };
use std::f32::consts::PI; use std::{f32::consts::PI, sync::Arc};
use vek::*; use vek::*;
struct AgentData<'a> { struct AgentData<'a> {
@ -81,6 +81,7 @@ pub struct ReadData<'a> {
//ReadStorage<'a, Invite>, //ReadStorage<'a, Invite>,
time_of_day: Read<'a, TimeOfDay>, time_of_day: Read<'a, TimeOfDay>,
light_emitter: ReadStorage<'a, LightEmitter>, light_emitter: ReadStorage<'a, LightEmitter>,
world: ReadExpect<'a, Arc<world::World>>,
} }
// This is 3.1 to last longer than the last damage timer (3.0 seconds) // This is 3.1 to last longer than the last damage timer (3.0 seconds)
@ -603,6 +604,29 @@ impl<'a> AgentData<'a> {
.cast() .cast()
.1 .1
.map_or(true, |b| b.is_some()) .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 1.0 //fly up when approaching obstacles
} else { } else {