mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
big birds!
This commit is contained in:
parent
259bb6fce4
commit
dda1be58d4
@ -446,6 +446,12 @@ impl EntityInfo {
|
|||||||
self.no_flee = true;
|
self.no_flee = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_loadout(mut self, loadout: LoadoutBuilder) -> Self {
|
||||||
|
self.loadout = loadout;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Body, Controller, InputKind, Ori, Pos, Scale, Vel, ControlAction},
|
comp::{Body, ControlAction, Controller, InputKind, Ori, Pos, Scale, Vel},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::Mount,
|
||||||
uid::UidAllocator,
|
uid::UidAllocator,
|
||||||
|
@ -5,7 +5,8 @@ use common::{
|
|||||||
grid::Grid,
|
grid::Grid,
|
||||||
rtsim::{FactionId, RtSimController, SiteId, VehicleId},
|
rtsim::{FactionId, RtSimController, SiteId, VehicleId},
|
||||||
store::Id,
|
store::Id,
|
||||||
uid::Uid, vol::RectVolSize,
|
uid::Uid,
|
||||||
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -21,7 +22,11 @@ use std::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use world::{civ::Track, site::Site as WorldSite, util::{RandomPerm, LOCALITY}};
|
use world::{
|
||||||
|
civ::Track,
|
||||||
|
site::Site as WorldSite,
|
||||||
|
util::{RandomPerm, LOCALITY},
|
||||||
|
};
|
||||||
|
|
||||||
use super::Actor;
|
use super::Actor;
|
||||||
|
|
||||||
@ -78,6 +83,7 @@ pub struct Npc {
|
|||||||
pub seed: u32,
|
pub seed: u32,
|
||||||
pub wpos: Vec3<f32>,
|
pub wpos: Vec3<f32>,
|
||||||
|
|
||||||
|
pub body: comp::Body,
|
||||||
pub profession: Option<Profession>,
|
pub profession: Option<Profession>,
|
||||||
pub home: Option<SiteId>,
|
pub home: Option<SiteId>,
|
||||||
pub faction: Option<FactionId>,
|
pub faction: Option<FactionId>,
|
||||||
@ -113,6 +119,7 @@ impl Clone for Npc {
|
|||||||
home: self.home,
|
home: self.home,
|
||||||
faction: self.faction,
|
faction: self.faction,
|
||||||
riding: self.riding.clone(),
|
riding: self.riding.clone(),
|
||||||
|
body: self.body,
|
||||||
// Not persisted
|
// Not persisted
|
||||||
chunk_pos: None,
|
chunk_pos: None,
|
||||||
current_site: Default::default(),
|
current_site: Default::default(),
|
||||||
@ -124,13 +131,11 @@ impl Clone for Npc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Npc {
|
impl Npc {
|
||||||
const PERM_BODY: u32 = 1;
|
pub fn new(seed: u32, wpos: Vec3<f32>, body: comp::Body) -> Self {
|
||||||
const PERM_SPECIES: u32 = 0;
|
|
||||||
|
|
||||||
pub fn new(seed: u32, wpos: Vec3<f32>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
seed,
|
seed,
|
||||||
wpos,
|
wpos,
|
||||||
|
body,
|
||||||
profession: None,
|
profession: None,
|
||||||
home: None,
|
home: None,
|
||||||
faction: None,
|
faction: None,
|
||||||
@ -154,21 +159,17 @@ impl Npc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn steering(mut self, vehicle: impl Into<Option<VehicleId>>) -> Self {
|
pub fn steering(mut self, vehicle: impl Into<Option<VehicleId>>) -> Self {
|
||||||
self.riding = vehicle.into().map(|vehicle| {
|
self.riding = vehicle.into().map(|vehicle| Riding {
|
||||||
Riding {
|
|
||||||
vehicle,
|
vehicle,
|
||||||
steering: true,
|
steering: true,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn riding(mut self, vehicle: impl Into<Option<VehicleId>>) -> Self {
|
pub fn riding(mut self, vehicle: impl Into<Option<VehicleId>>) -> Self {
|
||||||
self.riding = vehicle.into().map(|vehicle| {
|
self.riding = vehicle.into().map(|vehicle| Riding {
|
||||||
Riding {
|
|
||||||
vehicle,
|
vehicle,
|
||||||
steering: false,
|
steering: false,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -179,13 +180,6 @@ impl Npc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed.wrapping_add(perm)) }
|
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed.wrapping_add(perm)) }
|
||||||
|
|
||||||
pub fn get_body(&self) -> comp::Body {
|
|
||||||
let species = *(&comp::humanoid::ALL_SPECIES)
|
|
||||||
.choose(&mut self.rng(Self::PERM_SPECIES))
|
|
||||||
.unwrap();
|
|
||||||
comp::humanoid::Body::random_with(&mut self.rng(Self::PERM_BODY), &species).into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
@ -204,7 +198,7 @@ pub enum VehicleKind {
|
|||||||
pub struct Vehicle {
|
pub struct Vehicle {
|
||||||
pub wpos: Vec3<f32>,
|
pub wpos: Vec3<f32>,
|
||||||
|
|
||||||
pub kind: VehicleKind,
|
pub body: comp::ship::Body,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub chunk_pos: Option<Vec2<i32>>,
|
pub chunk_pos: Option<Vec2<i32>>,
|
||||||
@ -216,41 +210,36 @@ pub struct Vehicle {
|
|||||||
// TODO: Find a way to detect riders when the vehicle is loaded
|
// TODO: Find a way to detect riders when the vehicle is loaded
|
||||||
pub riders: Vec<Actor>,
|
pub riders: Vec<Actor>,
|
||||||
|
|
||||||
/// Whether the Vehicle is in simulated or loaded mode (when rtsim is run on the
|
/// Whether the Vehicle is in simulated or loaded mode (when rtsim is run on
|
||||||
/// server, loaded corresponds to being within a loaded chunk). When in
|
/// the server, loaded corresponds to being within a loaded chunk). When
|
||||||
/// loaded mode, the interactions of the Vehicle should not be simulated but
|
/// in loaded mode, the interactions of the Vehicle should not be
|
||||||
/// should instead be derived from the game.
|
/// simulated but should instead be derived from the game.
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub mode: SimulationMode,
|
pub mode: SimulationMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vehicle {
|
impl Vehicle {
|
||||||
pub fn new(wpos: Vec3<f32>, kind: VehicleKind) -> Self {
|
pub fn new(wpos: Vec3<f32>, body: comp::ship::Body) -> Self {
|
||||||
Self {
|
Self {
|
||||||
wpos,
|
wpos,
|
||||||
kind,
|
body,
|
||||||
chunk_pos: None,
|
chunk_pos: None,
|
||||||
driver: None,
|
driver: None,
|
||||||
riders: Vec::new(),
|
riders: Vec::new(),
|
||||||
mode: SimulationMode::Simulated,
|
mode: SimulationMode::Simulated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_ship(&self) -> comp::ship::Body {
|
|
||||||
match self.kind {
|
|
||||||
VehicleKind::Airship => comp::ship::Body::DefaultAirship,
|
|
||||||
VehicleKind::Boat => comp::ship::Body::Galleon,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_body(&self) -> comp::Body {
|
pub fn get_body(&self) -> comp::Body { comp::Body::Ship(self.body) }
|
||||||
comp::Body::Ship(self.get_ship())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Max speed in block/s
|
/// Max speed in block/s
|
||||||
pub fn get_speed(&self) -> f32 {
|
pub fn get_speed(&self) -> f32 {
|
||||||
match self.kind {
|
match self.body {
|
||||||
VehicleKind::Airship => 15.0,
|
comp::ship::Body::DefaultAirship => 15.0,
|
||||||
VehicleKind::Boat => 13.0,
|
comp::ship::Body::AirBalloon => 16.0,
|
||||||
|
comp::ship::Body::SailBoat => 12.0,
|
||||||
|
comp::ship::Body::Galleon => 13.0,
|
||||||
|
_ => 10.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -274,23 +263,25 @@ fn construct_npc_grid() -> Grid<GridCell> { Grid::new(Vec2::zero(), Default::def
|
|||||||
impl Npcs {
|
impl Npcs {
|
||||||
pub fn create_npc(&mut self, npc: Npc) -> NpcId { self.npcs.insert(npc) }
|
pub fn create_npc(&mut self, npc: Npc) -> NpcId { self.npcs.insert(npc) }
|
||||||
|
|
||||||
pub fn create_vehicle(&mut self, vehicle: Vehicle) -> VehicleId { self.vehicles.insert(vehicle) }
|
pub fn create_vehicle(&mut self, vehicle: Vehicle) -> VehicleId {
|
||||||
|
self.vehicles.insert(vehicle)
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries nearby npcs, not garantueed to work if radius > 32.0
|
/// Queries nearby npcs, not garantueed to work if radius > 32.0
|
||||||
pub fn nearby(&self, wpos: Vec2<f32>, radius: f32) -> impl Iterator<Item = NpcId> + '_ {
|
pub fn nearby(&self, wpos: Vec2<f32>, radius: f32) -> impl Iterator<Item = NpcId> + '_ {
|
||||||
let chunk_pos = wpos.as_::<i32>() / common::terrain::TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
let chunk_pos =
|
||||||
|
wpos.as_::<i32>() / common::terrain::TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||||
let r_sqr = radius * radius;
|
let r_sqr = radius * radius;
|
||||||
LOCALITY
|
LOCALITY
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(move |neighbor| {
|
.filter_map(move |neighbor| {
|
||||||
self
|
self.npc_grid.get(chunk_pos + neighbor).map(|cell| {
|
||||||
.npc_grid
|
cell.npcs
|
||||||
.get(chunk_pos + neighbor)
|
.iter()
|
||||||
.map(|cell| {
|
|
||||||
cell.npcs.iter()
|
|
||||||
.copied()
|
.copied()
|
||||||
.filter(|npc| {
|
.filter(|npc| {
|
||||||
self.npcs.get(*npc)
|
self.npcs
|
||||||
|
.get(*npc)
|
||||||
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -8,7 +8,11 @@ use crate::data::{
|
|||||||
Data, Nature,
|
Data, Nature,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
grid::Grid, resources::TimeOfDay, rtsim::WorldSettings, terrain::TerrainChunkSize,
|
comp::{self, Body},
|
||||||
|
grid::Grid,
|
||||||
|
resources::TimeOfDay,
|
||||||
|
rtsim::WorldSettings,
|
||||||
|
terrain::TerrainChunkSize,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@ -72,12 +76,12 @@ impl Data {
|
|||||||
"Registering {} rtsim sites from world sites.",
|
"Registering {} rtsim sites from world sites.",
|
||||||
this.sites.len()
|
this.sites.len()
|
||||||
);
|
);
|
||||||
|
/*
|
||||||
// Spawn some test entities at the sites
|
// Spawn some test entities at the sites
|
||||||
for (site_id, site) in this.sites.iter()
|
for (site_id, site) in this.sites.iter()
|
||||||
// TODO: Stupid
|
// TODO: Stupid
|
||||||
.filter(|(_, site)| site.world_site.map_or(false, |ws|
|
.filter(|(_, site)| site.world_site.map_or(false, |ws|
|
||||||
matches!(&index.sites.get(ws).kind, SiteKind::Refactor(_)))) .skip(1)
|
matches!(&index.sites.get(ws).kind, SiteKind::Refactor(_))))
|
||||||
{
|
{
|
||||||
let Some(good_or_evil) = site
|
let Some(good_or_evil) = site
|
||||||
.faction
|
.faction
|
||||||
@ -91,10 +95,17 @@ impl Data {
|
|||||||
.map(|e| e as f32 + 0.5)
|
.map(|e| e as f32 + 0.5)
|
||||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||||
};
|
};
|
||||||
|
let random_humanoid = |rng: &mut SmallRng| {
|
||||||
|
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
|
||||||
|
Body::Humanoid(comp::humanoid::Body::random_with(
|
||||||
|
rng,
|
||||||
|
species,
|
||||||
|
))
|
||||||
|
};
|
||||||
if good_or_evil {
|
if good_or_evil {
|
||||||
for _ in 0..32 {
|
for _ in 0..32 {
|
||||||
this.npcs.create_npc(
|
this.npcs.create_npc(
|
||||||
Npc::new(rng.gen(), rand_wpos(&mut rng))
|
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
|
||||||
.with_faction(site.faction)
|
.with_faction(site.faction)
|
||||||
.with_home(site_id)
|
.with_home(site_id)
|
||||||
.with_profession(match rng.gen_range(0..20) {
|
.with_profession(match rng.gen_range(0..20) {
|
||||||
@ -112,7 +123,7 @@ impl Data {
|
|||||||
} else {
|
} else {
|
||||||
for _ in 0..15 {
|
for _ in 0..15 {
|
||||||
this.npcs.create_npc(
|
this.npcs.create_npc(
|
||||||
Npc::new(rng.gen(), rand_wpos(&mut rng))
|
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
|
||||||
.with_faction(site.faction)
|
.with_faction(site.faction)
|
||||||
.with_home(site_id)
|
.with_home(site_id)
|
||||||
.with_profession(match rng.gen_range(0..20) {
|
.with_profession(match rng.gen_range(0..20) {
|
||||||
@ -122,17 +133,54 @@ impl Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.npcs.create_npc(
|
this.npcs.create_npc(
|
||||||
Npc::new(rng.gen(), rand_wpos(&mut rng))
|
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
|
||||||
.with_home(site_id)
|
.with_home(site_id)
|
||||||
.with_profession(Profession::Merchant),
|
.with_profession(Profession::Merchant),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if rng.gen_bool(0.4) {
|
||||||
let wpos = rand_wpos(&mut rng) + Vec3::unit_z() * 50.0;
|
let wpos = rand_wpos(&mut rng) + Vec3::unit_z() * 50.0;
|
||||||
let vehicle_id = this.npcs.create_vehicle(Vehicle::new(wpos, VehicleKind::Airship));
|
let vehicle_id = this
|
||||||
|
.npcs
|
||||||
|
.create_vehicle(Vehicle::new(wpos, comp::body::ship::Body::DefaultAirship));
|
||||||
|
|
||||||
this.npcs.create_npc(Npc::new(rng.gen(), wpos).with_home(site_id).with_profession(Profession::Captain).steering(vehicle_id));
|
this.npcs.create_npc(
|
||||||
|
Npc::new(rng.gen(), wpos, random_humanoid(&mut rng))
|
||||||
|
.with_home(site_id)
|
||||||
|
.with_profession(Profession::Captain)
|
||||||
|
.steering(vehicle_id),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
for (site_id, site) in this.sites.iter()
|
||||||
|
// TODO: Stupid
|
||||||
|
.filter(|(_, site)| site.world_site.map_or(false, |ws|
|
||||||
|
matches!(&index.sites.get(ws).kind, SiteKind::Dungeon(_))))
|
||||||
|
{
|
||||||
|
let rand_wpos = |rng: &mut SmallRng| {
|
||||||
|
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||||
|
wpos2d
|
||||||
|
.map(|e| e as f32 + 0.5)
|
||||||
|
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||||
|
};
|
||||||
|
|
||||||
|
let species = [
|
||||||
|
comp::body::bird_large::Species::Phoenix,
|
||||||
|
comp::body::bird_large::Species::Cockatrice,
|
||||||
|
comp::body::bird_large::Species::Roc,
|
||||||
|
]
|
||||||
|
.choose(&mut rng)
|
||||||
|
.unwrap();
|
||||||
|
this.npcs.create_npc(
|
||||||
|
Npc::new(
|
||||||
|
rng.gen(),
|
||||||
|
rand_wpos(&mut rng),
|
||||||
|
Body::BirdLarge(comp::body::bird_large::Body::random_with(&mut rng, species)),
|
||||||
|
)
|
||||||
|
.with_home(site_id),
|
||||||
|
);
|
||||||
|
}
|
||||||
info!("Generated {} rtsim NPCs.", this.npcs.len());
|
info!("Generated {} rtsim NPCs.", this.npcs.len());
|
||||||
|
|
||||||
this
|
this
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
rtsim::{Profession, SiteId},
|
rtsim::{Profession, SiteId},
|
||||||
store::Id,
|
store::Id,
|
||||||
terrain::{TerrainChunkSize, SiteKindMeta},
|
terrain::{SiteKindMeta, TerrainChunkSize},
|
||||||
time::DayPeriod,
|
time::DayPeriod,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
@ -32,7 +32,8 @@ use world::{
|
|||||||
civ::{self, Track},
|
civ::{self, Track},
|
||||||
site::{Site as WorldSite, SiteKind},
|
site::{Site as WorldSite, SiteKind},
|
||||||
site2::{self, PlotKind, TileKind},
|
site2::{self, PlotKind, TileKind},
|
||||||
IndexRef, World, util::NEIGHBORS,
|
util::NEIGHBORS,
|
||||||
|
IndexRef, World,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct NpcAi;
|
pub struct NpcAi;
|
||||||
@ -329,7 +330,11 @@ fn goto(wpos: Vec3<f32>, speed_factor: f32, goal_dist: f32) -> impl Action {
|
|||||||
|
|
||||||
// Get the next waypoint on the route toward the goal
|
// Get the next waypoint on the route toward the goal
|
||||||
let waypoint =
|
let waypoint =
|
||||||
waypoint.get_or_insert_with(|| ctx.npc.wpos + (rpos / len) * len.min(STEP_DIST));
|
waypoint.get_or_insert_with(|| {
|
||||||
|
let wpos = ctx.npc.wpos + (rpos / len) * len.min(STEP_DIST);
|
||||||
|
|
||||||
|
wpos.with_z(ctx.world.sim().get_surface_alt_approx(wpos.xy().as_()).unwrap_or(wpos.z))
|
||||||
|
});
|
||||||
|
|
||||||
*ctx.controller = Controller::goto(*waypoint, speed_factor);
|
*ctx.controller = Controller::goto(*waypoint, speed_factor);
|
||||||
})
|
})
|
||||||
@ -635,7 +640,11 @@ fn follow(npc: NpcId, distance: f32) -> impl Action {
|
|||||||
.map(|_| {})
|
.map(|_| {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_path(from: Vec2<i32>, to: Vec2<i32>, chunk_height: impl Fn(Vec2<i32>) -> Option<i32>) -> Box<dyn Action> {
|
fn chunk_path(
|
||||||
|
from: Vec2<i32>,
|
||||||
|
to: Vec2<i32>,
|
||||||
|
chunk_height: impl Fn(Vec2<i32>) -> Option<i32>,
|
||||||
|
) -> Box<dyn Action> {
|
||||||
let heuristics = |(p, _): &(Vec2<i32>, i32)| p.distance_squared(to) as f32;
|
let heuristics = |(p, _): &(Vec2<i32>, i32)| p.distance_squared(to) as f32;
|
||||||
let start = (from, chunk_height(from).unwrap());
|
let start = (from, chunk_height(from).unwrap());
|
||||||
let mut astar = Astar::new(
|
let mut astar = Astar::new(
|
||||||
@ -648,35 +657,45 @@ fn chunk_path(from: Vec2<i32>, to: Vec2<i32>, chunk_height: impl Fn(Vec2<i32>) -
|
|||||||
let path = astar.poll(
|
let path = astar.poll(
|
||||||
1000,
|
1000,
|
||||||
heuristics,
|
heuristics,
|
||||||
|&(p, _)| NEIGHBORS.into_iter().map(move |n| p + n).filter_map(|p| Some((p, chunk_height(p)?))),
|
|&(p, _)| {
|
||||||
|
NEIGHBORS
|
||||||
|
.into_iter()
|
||||||
|
.map(move |n| p + n)
|
||||||
|
.filter_map(|p| Some((p, chunk_height(p)?)))
|
||||||
|
},
|
||||||
|(p0, h0), (p1, h1)| {
|
|(p0, h0), (p1, h1)| {
|
||||||
let diff = ((p0 - p1).as_() * TerrainChunkSize::RECT_SIZE.as_()).with_z((h0 - h1) as f32);
|
let diff =
|
||||||
|
((p0 - p1).as_() * TerrainChunkSize::RECT_SIZE.as_()).with_z((h0 - h1) as f32);
|
||||||
|
|
||||||
diff.magnitude_squared()
|
diff.magnitude_squared()
|
||||||
},
|
},
|
||||||
|(e, _)| *e == to
|
|(e, _)| *e == to,
|
||||||
);
|
);
|
||||||
let path = match path {
|
let path = match path {
|
||||||
PathResult::Exhausted(p) | PathResult::Path(p) => p,
|
PathResult::Exhausted(p) | PathResult::Path(p) => p,
|
||||||
_ => return finish().boxed(),
|
_ => return finish().boxed(),
|
||||||
};
|
};
|
||||||
let len = path.len();
|
let len = path.len();
|
||||||
seq(
|
seq(path
|
||||||
path
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(i, (chunk_pos, height))| {
|
.map(move |(i, (chunk_pos, height))| {
|
||||||
let wpos = TerrainChunkSize::center_wpos(chunk_pos).with_z(height).as_();
|
let wpos = TerrainChunkSize::center_wpos(chunk_pos)
|
||||||
goto(wpos, 1.0, 5.0).debug(move || format!("chunk path {i}/{len} chunk: {chunk_pos}, height: {height}"))
|
.with_z(height)
|
||||||
})
|
.as_();
|
||||||
).boxed()
|
goto(wpos, 1.0, 5.0)
|
||||||
|
.debug(move || format!("chunk path {i}/{len} chunk: {chunk_pos}, height: {height}"))
|
||||||
|
}))
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pilot() -> impl Action {
|
fn pilot() -> impl Action {
|
||||||
// Travel between different towns in a straight line
|
// Travel between different towns in a straight line
|
||||||
now(|ctx| {
|
now(|ctx| {
|
||||||
let data = &*ctx.state.data();
|
let data = &*ctx.state.data();
|
||||||
let site = data.sites.iter()
|
let site = data
|
||||||
|
.sites
|
||||||
|
.iter()
|
||||||
.filter(|(id, _)| Some(*id) != ctx.npc.current_site)
|
.filter(|(id, _)| Some(*id) != ctx.npc.current_site)
|
||||||
.filter(|(_, site)| {
|
.filter(|(_, site)| {
|
||||||
site.world_site
|
site.world_site
|
||||||
@ -685,12 +704,14 @@ fn pilot() -> impl Action {
|
|||||||
})
|
})
|
||||||
.choose(&mut thread_rng());
|
.choose(&mut thread_rng());
|
||||||
if let Some((_id, site)) = site {
|
if let Some((_id, site)) = site {
|
||||||
let start_chunk = ctx.npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
let start_chunk =
|
||||||
|
ctx.npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||||
let end_chunk = site.wpos / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
let end_chunk = site.wpos / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||||
chunk_path(start_chunk, end_chunk, |chunk| {
|
chunk_path(start_chunk, end_chunk, |chunk| {
|
||||||
ctx.world.sim().get_alt_approx(TerrainChunkSize::center_wpos(chunk)).map(|f| {
|
ctx.world
|
||||||
(f + 150.0) as i32
|
.sim()
|
||||||
})
|
.get_alt_approx(TerrainChunkSize::center_wpos(chunk))
|
||||||
|
.map(|f| (f + 150.0) as i32)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
finish().boxed()
|
finish().boxed()
|
||||||
@ -707,27 +728,42 @@ fn captain() -> impl Action {
|
|||||||
if let Some(chunk) = NEIGHBORS
|
if let Some(chunk) = NEIGHBORS
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|neighbor| chunk + neighbor)
|
.map(|neighbor| chunk + neighbor)
|
||||||
.filter(|neighbor| ctx.world.sim().get(*neighbor).map_or(false, |c| c.river.river_kind.is_some()))
|
.filter(|neighbor| {
|
||||||
|
ctx.world
|
||||||
|
.sim()
|
||||||
|
.get(*neighbor)
|
||||||
|
.map_or(false, |c| c.river.river_kind.is_some())
|
||||||
|
})
|
||||||
.choose(&mut thread_rng())
|
.choose(&mut thread_rng())
|
||||||
{
|
{
|
||||||
let wpos = TerrainChunkSize::center_wpos(chunk);
|
let wpos = TerrainChunkSize::center_wpos(chunk);
|
||||||
let wpos = wpos.as_().with_z(ctx.world.sim().get_interpolated(wpos, |chunk| chunk.water_alt).unwrap_or(0.0));
|
let wpos = wpos.as_().with_z(
|
||||||
|
ctx.world
|
||||||
|
.sim()
|
||||||
|
.get_interpolated(wpos, |chunk| chunk.water_alt)
|
||||||
|
.unwrap_or(0.0),
|
||||||
|
);
|
||||||
goto(wpos, 0.7, 5.0).boxed()
|
goto(wpos, 0.7, 5.0).boxed()
|
||||||
} else {
|
} else {
|
||||||
idle().boxed()
|
idle().boxed()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.repeat().map(|_| ())
|
.repeat()
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn think() -> impl Action {
|
fn humanoid() -> impl Action {
|
||||||
choose(|ctx| {
|
choose(|ctx| {
|
||||||
if let Some(riding) = &ctx.npc.riding {
|
if let Some(riding) = &ctx.npc.riding {
|
||||||
if riding.steering {
|
if riding.steering {
|
||||||
if let Some(vehicle) = ctx.state.data().npcs.vehicles.get(riding.vehicle) {
|
if let Some(vehicle) = ctx.state.data().npcs.vehicles.get(riding.vehicle) {
|
||||||
match vehicle.kind {
|
match vehicle.body {
|
||||||
VehicleKind::Airship => important(pilot()),
|
common::comp::ship::Body::DefaultAirship
|
||||||
VehicleKind::Boat => important(captain()),
|
| common::comp::ship::Body::AirBalloon => important(pilot()),
|
||||||
|
common::comp::ship::Body::SailBoat | common::comp::ship::Body::Galleon => {
|
||||||
|
important(captain())
|
||||||
|
},
|
||||||
|
_ => casual(idle()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
casual(finish())
|
casual(finish())
|
||||||
@ -748,6 +784,66 @@ fn think() -> impl Action {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bird_large() -> impl Action {
|
||||||
|
choose(|ctx| {
|
||||||
|
let data = ctx.state.data();
|
||||||
|
if let Some(home) = ctx.npc.home {
|
||||||
|
let is_home = ctx.npc.current_site.map_or(false, |site| home == site);
|
||||||
|
if is_home {
|
||||||
|
if let Some((id, site)) = data
|
||||||
|
.sites
|
||||||
|
.iter()
|
||||||
|
.filter(|(id, site)| {
|
||||||
|
*id != home
|
||||||
|
&& site.world_site.map_or(false, |site| {
|
||||||
|
matches!(ctx.index.sites.get(site).kind, SiteKind::Dungeon(_))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
{
|
||||||
|
casual(goto(
|
||||||
|
site.wpos.as_::<f32>().with_z(
|
||||||
|
ctx.world
|
||||||
|
.sim()
|
||||||
|
.get_surface_alt_approx(site.wpos)
|
||||||
|
.unwrap_or(0.0)
|
||||||
|
+ ctx.npc.body.flying_height(),
|
||||||
|
),
|
||||||
|
1.0,
|
||||||
|
20.0,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
casual(idle())
|
||||||
|
}
|
||||||
|
} else if let Some(site) = data.sites.get(home) {
|
||||||
|
casual(goto(
|
||||||
|
site.wpos.as_::<f32>().with_z(
|
||||||
|
ctx.world
|
||||||
|
.sim()
|
||||||
|
.get_surface_alt_approx(site.wpos)
|
||||||
|
.unwrap_or(0.0)
|
||||||
|
+ ctx.npc.body.flying_height(),
|
||||||
|
),
|
||||||
|
1.0,
|
||||||
|
20.0,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
casual(idle())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
casual(idle())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn think() -> impl Action {
|
||||||
|
choose(|ctx| match ctx.npc.body {
|
||||||
|
common::comp::Body::Humanoid(_) => casual(humanoid()),
|
||||||
|
common::comp::Body::BirdLarge(_) => casual(bird_large()),
|
||||||
|
_ => casual(idle()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// if !matches!(stages.front(), Some(TravelStage::IntraSite { .. })) {
|
// if !matches!(stages.front(), Some(TravelStage::IntraSite { .. })) {
|
||||||
// let data = ctx.state.data();
|
// let data = ctx.state.data();
|
||||||
// if let Some((site2, site)) = npc
|
// if let Some((site2, site)) = npc
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
event::{OnSetup, OnTick},
|
event::{OnSetup, OnTick},
|
||||||
RtState, Rule, RuleError,
|
RtState, Rule, RuleError,
|
||||||
};
|
};
|
||||||
use common::{terrain::TerrainChunkSize, vol::RectVolSize, grid::Grid};
|
use common::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -77,8 +77,6 @@ impl Rule for SimulateNpcs {
|
|||||||
|
|
||||||
// Simulate the NPC's movement and interactions
|
// Simulate the NPC's movement and interactions
|
||||||
if matches!(npc.mode, SimulationMode::Simulated) {
|
if matches!(npc.mode, SimulationMode::Simulated) {
|
||||||
let body = npc.get_body();
|
|
||||||
|
|
||||||
if let Some(riding) = &npc.riding {
|
if let Some(riding) = &npc.riding {
|
||||||
if let Some(vehicle) = data.npcs.vehicles.get_mut(riding.vehicle) {
|
if let Some(vehicle) = data.npcs.vehicles.get_mut(riding.vehicle) {
|
||||||
if let Some(action) = npc.action && riding.steering {
|
if let Some(action) = npc.action && riding.steering {
|
||||||
@ -94,28 +92,30 @@ impl Rule for SimulateNpcs {
|
|||||||
.min(1.0))
|
.min(1.0))
|
||||||
.with_z(0.0);
|
.with_z(0.0);
|
||||||
|
|
||||||
let is_valid = match vehicle.kind {
|
let is_valid = match vehicle.body {
|
||||||
crate::data::npc::VehicleKind::Airship => true,
|
common::comp::ship::Body::DefaultAirship | common::comp::ship::Body::AirBalloon => true,
|
||||||
crate::data::npc::VehicleKind::Boat => {
|
common::comp::ship::Body::SailBoat | common::comp::ship::Body::Galleon => {
|
||||||
let chunk_pos = wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
let chunk_pos = wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||||
ctx.world.sim().get(chunk_pos).map_or(true, |f| f.river.river_kind.is_some())
|
ctx.world.sim().get(chunk_pos).map_or(true, |f| f.river.river_kind.is_some())
|
||||||
},
|
},
|
||||||
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_valid {
|
if is_valid {
|
||||||
match vehicle.kind {
|
match vehicle.body {
|
||||||
crate::data::npc::VehicleKind::Airship => {
|
common::comp::ship::Body::DefaultAirship | common::comp::ship::Body::AirBalloon => {
|
||||||
if let Some(alt) = ctx.world.sim().get_alt_approx(wpos.xy().as_()).filter(|alt| wpos.z < *alt) {
|
if let Some(alt) = ctx.world.sim().get_alt_approx(wpos.xy().as_()).filter(|alt| wpos.z < *alt) {
|
||||||
wpos.z = alt;
|
wpos.z = alt;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
crate::data::npc::VehicleKind::Boat => {
|
common::comp::ship::Body::SailBoat | common::comp::ship::Body::Galleon => {
|
||||||
wpos.z = ctx
|
wpos.z = ctx
|
||||||
.world
|
.world
|
||||||
.sim()
|
.sim()
|
||||||
.get_interpolated(wpos.xy().map(|e| e as i32), |chunk| chunk.water_alt)
|
.get_interpolated(wpos.xy().map(|e| e as i32), |chunk| chunk.water_alt)
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
vehicle.wpos = wpos;
|
vehicle.wpos = wpos;
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ impl Rule for SimulateNpcs {
|
|||||||
|
|
||||||
if dist2 > 0.5f32.powi(2) {
|
if dist2 > 0.5f32.powi(2) {
|
||||||
npc.wpos += (diff
|
npc.wpos += (diff
|
||||||
* (body.max_speed_approx() * speed_factor * ctx.event.dt
|
* (npc.body.max_speed_approx() * speed_factor * ctx.event.dt
|
||||||
/ dist2.sqrt())
|
/ dist2.sqrt())
|
||||||
.min(1.0))
|
.min(1.0))
|
||||||
.with_z(0.0);
|
.with_z(0.0);
|
||||||
@ -150,8 +150,8 @@ impl Rule for SimulateNpcs {
|
|||||||
npc.wpos.z = ctx
|
npc.wpos.z = ctx
|
||||||
.world
|
.world
|
||||||
.sim()
|
.sim()
|
||||||
.get_alt_approx(npc.wpos.xy().map(|e| e as i32))
|
.get_surface_alt_approx(npc.wpos.xy().map(|e| e as i32))
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0) + npc.body.flying_height();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1486,7 +1486,8 @@ fn handle_spawn_airship(
|
|||||||
animated: true,
|
animated: true,
|
||||||
});
|
});
|
||||||
if let Some(pos) = destination {
|
if let Some(pos) = destination {
|
||||||
let (kp, ki, kd) = comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
let (kp, ki, kd) =
|
||||||
|
comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
||||||
fn pure_z(sp: Vec3<f32>, pv: Vec3<f32>) -> f32 { (sp - pv).z }
|
fn pure_z(sp: Vec3<f32>, pv: Vec3<f32>) -> f32 { (sp - pv).z }
|
||||||
let agent = comp::Agent::from_body(&comp::Body::Ship(ship))
|
let agent = comp::Agent::from_body(&comp::Body::Ship(ship))
|
||||||
.with_destination(pos)
|
.with_destination(pos)
|
||||||
@ -1534,7 +1535,8 @@ fn handle_spawn_ship(
|
|||||||
animated: true,
|
animated: true,
|
||||||
});
|
});
|
||||||
if let Some(pos) = destination {
|
if let Some(pos) = destination {
|
||||||
let (kp, ki, kd) = comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
let (kp, ki, kd) =
|
||||||
|
comp::agent::pid_coefficients(&comp::Body::Ship(ship)).unwrap_or((1.0, 0.0, 0.0));
|
||||||
fn pure_z(sp: Vec3<f32>, pv: Vec3<f32>) -> f32 { (sp - pv).z }
|
fn pure_z(sp: Vec3<f32>, pv: Vec3<f32>) -> f32 { (sp - pv).z }
|
||||||
let agent = comp::Agent::from_body(&comp::Body::Ship(ship))
|
let agent = comp::Agent::from_body(&comp::Body::Ship(ship))
|
||||||
.with_destination(pos)
|
.with_destination(pos)
|
||||||
@ -1575,11 +1577,9 @@ fn handle_make_volume(
|
|||||||
};
|
};
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_ship(
|
.create_ship(comp::Pos(pos.0 + Vec3::unit_z() * 50.0), ship, move |_| {
|
||||||
comp::Pos(pos.0 + Vec3::unit_z() * 50.0),
|
collider
|
||||||
ship,
|
})
|
||||||
move |_| collider,
|
|
||||||
)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
server.notify_client(
|
server.notify_client(
|
||||||
|
@ -14,14 +14,15 @@ use common::{
|
|||||||
LightEmitter, Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats,
|
LightEmitter, Object, Ori, PidController, Poise, Pos, Projectile, Scale, SkillSet, Stats,
|
||||||
TradingBehavior, Vel, WaypointArea,
|
TradingBehavior, Vel, WaypointArea,
|
||||||
},
|
},
|
||||||
event::{EventBus, UpdateCharacterMetadata, NpcBuilder},
|
event::{EventBus, NpcBuilder, UpdateCharacterMetadata},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
|
mounting::Mounting,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
rtsim::{RtSimEntity, RtSimVehicle},
|
rtsim::{RtSimEntity, RtSimVehicle},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
ViewDistances, mounting::Mounting,
|
ViewDistances,
|
||||||
};
|
};
|
||||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||||
@ -91,14 +92,18 @@ pub fn handle_loaded_character_data(
|
|||||||
server.notify_client(entity, ServerGeneral::CharacterDataLoadResult(Ok(metadata)));
|
server.notify_client(entity, ServerGeneral::CharacterDataLoadResult(Ok(metadata)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_create_npc(
|
pub fn handle_create_npc(server: &mut Server, pos: Pos, mut npc: NpcBuilder) -> EcsEntity {
|
||||||
server: &mut Server,
|
|
||||||
pos: Pos,
|
|
||||||
mut npc: NpcBuilder,
|
|
||||||
) -> EcsEntity {
|
|
||||||
let entity = server
|
let entity = server
|
||||||
.state
|
.state
|
||||||
.create_npc(pos, npc.stats, npc.skill_set, npc.health, npc.poise, npc.inventory, npc.body)
|
.create_npc(
|
||||||
|
pos,
|
||||||
|
npc.stats,
|
||||||
|
npc.skill_set,
|
||||||
|
npc.health,
|
||||||
|
npc.poise,
|
||||||
|
npc.inventory,
|
||||||
|
npc.body,
|
||||||
|
)
|
||||||
.with(npc.scale);
|
.with(npc.scale);
|
||||||
|
|
||||||
if let Some(agent) = &mut npc.agent {
|
if let Some(agent) = &mut npc.agent {
|
||||||
@ -194,7 +199,6 @@ pub fn handle_create_ship(
|
|||||||
rtsim_vehicle: Option<RtSimVehicle>,
|
rtsim_vehicle: Option<RtSimVehicle>,
|
||||||
driver: Option<NpcBuilder>,
|
driver: Option<NpcBuilder>,
|
||||||
passangers: Vec<NpcBuilder>,
|
passangers: Vec<NpcBuilder>,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
let mut entity = server
|
let mut entity = server
|
||||||
.state
|
.state
|
||||||
@ -213,7 +217,6 @@ pub fn handle_create_ship(
|
|||||||
}
|
}
|
||||||
let entity = entity.build();
|
let entity = entity.build();
|
||||||
|
|
||||||
|
|
||||||
if let Some(driver) = driver {
|
if let Some(driver) = driver {
|
||||||
let npc_entity = handle_create_npc(server, pos, driver);
|
let npc_entity = handle_create_npc(server, pos, driver);
|
||||||
|
|
||||||
@ -222,10 +225,13 @@ pub fn handle_create_ship(
|
|||||||
(uids.get(npc_entity).copied(), uids.get(entity).copied())
|
(uids.get(npc_entity).copied(), uids.get(entity).copied())
|
||||||
{
|
{
|
||||||
drop(uids);
|
drop(uids);
|
||||||
server.state.link(Mounting {
|
server
|
||||||
|
.state
|
||||||
|
.link(Mounting {
|
||||||
mount: mount_uid,
|
mount: mount_uid,
|
||||||
rider: rider_uid,
|
rider: rider_uid,
|
||||||
}).expect("Failed to link driver to ship");
|
})
|
||||||
|
.expect("Failed to link driver to ship");
|
||||||
} else {
|
} else {
|
||||||
panic!("Couldn't get Uid from newly created ship and npc");
|
panic!("Couldn't get Uid from newly created ship and npc");
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,9 @@ use common::{
|
|||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
loot_owner::LootOwnerKind,
|
loot_owner::LootOwnerKind,
|
||||||
|
pet::is_mountable,
|
||||||
tool::ToolKind,
|
tool::ToolKind,
|
||||||
Inventory, LootOwner, Pos, SkillGroupKind, pet::is_mountable,
|
Inventory, LootOwner, Pos, SkillGroupKind,
|
||||||
},
|
},
|
||||||
consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
@ -119,10 +120,13 @@ pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
|||||||
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
||||||
);
|
);
|
||||||
|
|
||||||
let can_ride = state.ecs()
|
let can_ride = state
|
||||||
|
.ecs()
|
||||||
.read_storage()
|
.read_storage()
|
||||||
.get(mount)
|
.get(mount)
|
||||||
.map_or(false, |mount_body| is_mountable(mount_body, state.ecs().read_storage().get(rider)));
|
.map_or(false, |mount_body| {
|
||||||
|
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
||||||
|
});
|
||||||
|
|
||||||
if is_pet && can_ride {
|
if is_pet && can_ride {
|
||||||
drop(uids);
|
drop(uids);
|
||||||
|
@ -188,15 +188,8 @@ impl Server {
|
|||||||
ServerEvent::ExitIngame { entity } => {
|
ServerEvent::ExitIngame { entity } => {
|
||||||
handle_exit_ingame(self, entity, false);
|
handle_exit_ingame(self, entity, false);
|
||||||
},
|
},
|
||||||
ServerEvent::CreateNpc {
|
ServerEvent::CreateNpc { pos, npc } => {
|
||||||
pos,
|
handle_create_npc(self, pos, npc);
|
||||||
npc,
|
|
||||||
} => {
|
|
||||||
handle_create_npc(
|
|
||||||
self,
|
|
||||||
pos,
|
|
||||||
npc,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
ServerEvent::CreateShip {
|
ServerEvent::CreateShip {
|
||||||
pos,
|
pos,
|
||||||
|
@ -4,7 +4,7 @@ pub mod tick;
|
|||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
rtsim::{ChunkResource, RtSimEntity, WorldSettings, RtSimVehicle},
|
rtsim::{ChunkResource, RtSimEntity, RtSimVehicle, WorldSettings},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::{Block, TerrainChunk},
|
terrain::{Block, TerrainChunk},
|
||||||
vol::RectRasterableVol,
|
vol::RectRasterableVol,
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::rtsim2::{event::OnBlockChange, ChunkStates};
|
use crate::rtsim2::{event::OnBlockChange, ChunkStates};
|
||||||
use common::{terrain::{TerrainChunk, CoordinateConversions}, vol::RectRasterableVol};
|
use common::{
|
||||||
|
terrain::{CoordinateConversions, TerrainChunk},
|
||||||
|
vol::RectRasterableVol,
|
||||||
|
};
|
||||||
use rtsim2::{RtState, Rule, RuleError};
|
use rtsim2::{RtState, Rule, RuleError};
|
||||||
|
|
||||||
pub struct DepleteResources;
|
pub struct DepleteResources;
|
||||||
@ -7,10 +10,7 @@ pub struct DepleteResources;
|
|||||||
impl Rule for DepleteResources {
|
impl Rule for DepleteResources {
|
||||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||||
rtstate.bind::<Self, OnBlockChange>(|ctx| {
|
rtstate.bind::<Self, OnBlockChange>(|ctx| {
|
||||||
let key = ctx
|
let key = ctx.event.wpos.xy().wpos_to_cpos();
|
||||||
.event
|
|
||||||
.wpos
|
|
||||||
.xy().wpos_to_cpos();
|
|
||||||
if let Some(Some(chunk_state)) = ctx.state.resource_mut::<ChunkStates>().0.get(key) {
|
if let Some(Some(chunk_state)) = ctx.state.resource_mut::<ChunkStates>().0.get(key) {
|
||||||
let mut chunk_res = ctx.state.data().nature.get_chunk_resources(key);
|
let mut chunk_res = ctx.state.data().nature.get_chunk_resources(key);
|
||||||
// Remove resources
|
// Remove resources
|
||||||
|
@ -3,19 +3,20 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::sys::terrain::NpcData;
|
use crate::sys::terrain::NpcData;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, inventory::loadout::Loadout, skillset::skills, Body, Agent},
|
comp::{self, inventory::loadout::Loadout, skillset::skills, Agent, Body},
|
||||||
event::{EventBus, ServerEvent, NpcBuilder},
|
event::{EventBus, NpcBuilder, ServerEvent},
|
||||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||||
resources::{DeltaTime, Time, TimeOfDay},
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
rtsim::{RtSimController, RtSimEntity, RtSimVehicle},
|
rtsim::{RtSimController, RtSimEntity, RtSimVehicle},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
|
terrain::CoordinateConversions,
|
||||||
trade::{Good, SiteInformation},
|
trade::{Good, SiteInformation},
|
||||||
LoadoutBuilder, SkillSetBuilder, terrain::CoordinateConversions,
|
LoadoutBuilder, SkillSetBuilder, lottery::LootSpec,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use rtsim2::data::{
|
use rtsim2::data::{
|
||||||
npc::{SimulationMode, Profession},
|
npc::{Profession, SimulationMode},
|
||||||
Npc, Sites, Actor,
|
Actor, Npc, Sites,
|
||||||
};
|
};
|
||||||
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
use specs::{Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
@ -126,9 +127,9 @@ fn profession_agent_mark(profession: Option<&Profession>) -> Option<comp::agent:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo {
|
fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo {
|
||||||
let body = npc.get_body();
|
|
||||||
let pos = comp::Pos(npc.wpos);
|
let pos = comp::Pos(npc.wpos);
|
||||||
|
|
||||||
|
let mut rng = npc.rng(3);
|
||||||
if let Some(ref profession) = npc.profession {
|
if let Some(ref profession) = npc.profession {
|
||||||
let economy = npc.home.and_then(|home| {
|
let economy = npc.home.and_then(|home| {
|
||||||
let site = sites.get(home)?.world_site?;
|
let site = sites.get(home)?.world_site?;
|
||||||
@ -137,9 +138,8 @@ fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo
|
|||||||
|
|
||||||
let config_asset = humanoid_config(profession);
|
let config_asset = humanoid_config(profession);
|
||||||
|
|
||||||
let entity_config =
|
let entity_config = EntityConfig::from_asset_expect_owned(config_asset)
|
||||||
EntityConfig::from_asset_expect_owned(config_asset).with_body(BodyBuilder::Exact(body));
|
.with_body(BodyBuilder::Exact(npc.body));
|
||||||
let mut rng = npc.rng(3);
|
|
||||||
EntityInfo::at(pos.0)
|
EntityInfo::at(pos.0)
|
||||||
.with_entity_config(entity_config, Some(config_asset), &mut rng)
|
.with_entity_config(entity_config, Some(config_asset), &mut rng)
|
||||||
.with_alignment(if matches!(profession, Profession::Cultist) {
|
.with_alignment(if matches!(profession, Profession::Cultist) {
|
||||||
@ -151,10 +151,23 @@ fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo
|
|||||||
.with_lazy_loadout(profession_extra_loadout(npc.profession.as_ref()))
|
.with_lazy_loadout(profession_extra_loadout(npc.profession.as_ref()))
|
||||||
.with_agent_mark(profession_agent_mark(npc.profession.as_ref()))
|
.with_agent_mark(profession_agent_mark(npc.profession.as_ref()))
|
||||||
} else {
|
} else {
|
||||||
|
let config_asset = match npc.body {
|
||||||
|
Body::BirdLarge(body) => match body.species {
|
||||||
|
comp::bird_large::Species::Phoenix => "common.entity.wild.peaceful.phoenix",
|
||||||
|
comp::bird_large::Species::Cockatrice => "common.entity.wild.aggressive.cockatrice",
|
||||||
|
comp::bird_large::Species::Roc => "common.entity.wild.aggressive.roc",
|
||||||
|
// Wildcard match used here as there is an array above
|
||||||
|
// which limits what species are used
|
||||||
|
_ => unimplemented!(),
|
||||||
|
},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
let entity_config = EntityConfig::from_asset_expect_owned(config_asset)
|
||||||
|
.with_body(BodyBuilder::Exact(npc.body));
|
||||||
|
|
||||||
EntityInfo::at(pos.0)
|
EntityInfo::at(pos.0)
|
||||||
.with_body(body)
|
.with_entity_config(entity_config, Some(config_asset), &mut rng)
|
||||||
.with_alignment(comp::Alignment::Wild)
|
.with_alignment(comp::Alignment::Wild)
|
||||||
.with_name("Rtsim NPC")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +241,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
let npc = data.npcs.npcs.get_mut(npc_id)?;
|
let npc = data.npcs.npcs.get_mut(npc_id)?;
|
||||||
if matches!(npc.mode, SimulationMode::Simulated) {
|
if matches!(npc.mode, SimulationMode::Simulated) {
|
||||||
npc.mode = SimulationMode::Loaded;
|
npc.mode = SimulationMode::Loaded;
|
||||||
let entity_info = get_npc_entity_info(npc, &data.sites, index.as_index_ref());
|
let entity_info =
|
||||||
|
get_npc_entity_info(npc, &data.sites, index.as_index_ref());
|
||||||
|
|
||||||
Some(match NpcData::from_entity_info(entity_info) {
|
Some(match NpcData::from_entity_info(entity_info) {
|
||||||
NpcData::Data {
|
NpcData::Data {
|
||||||
@ -265,11 +279,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
emitter.emit(ServerEvent::CreateShip {
|
emitter.emit(ServerEvent::CreateShip {
|
||||||
pos: comp::Pos(vehicle.wpos),
|
pos: comp::Pos(vehicle.wpos),
|
||||||
ship: vehicle.get_ship(),
|
ship: vehicle.body,
|
||||||
// agent: None,//Some(Agent::from_body(&Body::Ship(ship))),
|
// agent: None,//Some(Agent::from_body(&Body::Ship(ship))),
|
||||||
rtsim_entity: Some(RtSimVehicle(vehicle_id)),
|
rtsim_entity: Some(RtSimVehicle(vehicle_id)),
|
||||||
driver: vehicle.driver.and_then(&mut actor_info),
|
driver: vehicle.driver.and_then(&mut actor_info),
|
||||||
passangers: vehicle.riders.iter().copied().filter(|actor| vehicle.driver != Some(*actor)).filter_map(actor_info).collect(),
|
passangers: vehicle
|
||||||
|
.riders
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|actor| vehicle.driver != Some(*actor))
|
||||||
|
.filter_map(actor_info)
|
||||||
|
.collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,10 +339,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Synchronise rtsim NPC with entity data
|
// Synchronise rtsim NPC with entity data
|
||||||
for (pos, rtsim_vehicle) in
|
for (pos, rtsim_vehicle) in (&positions, &rtsim_vehicles).join() {
|
||||||
(&positions, &rtsim_vehicles).join()
|
data.npcs
|
||||||
{
|
.vehicles
|
||||||
data.npcs.vehicles
|
|
||||||
.get_mut(rtsim_vehicle.0)
|
.get_mut(rtsim_vehicle.0)
|
||||||
.filter(|npc| matches!(npc.mode, SimulationMode::Loaded))
|
.filter(|npc| matches!(npc.mode, SimulationMode::Loaded))
|
||||||
.map(|vehicle| {
|
.map(|vehicle| {
|
||||||
|
@ -19,7 +19,7 @@ use common_base::prof_span;
|
|||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{Join, ParJoin, Read, WriteExpect, WriteStorage, saveload::MarkerAllocator};
|
use specs::{saveload::MarkerAllocator, Join, ParJoin, Read, WriteExpect, WriteStorage};
|
||||||
|
|
||||||
/// This system will allow NPCs to modify their controller
|
/// This system will allow NPCs to modify their controller
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -101,7 +101,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
// The entity that is moving, if riding it's the mount, otherwise it's itself
|
// The entity that is moving, if riding it's the mount, otherwise it's itself
|
||||||
let moving_entity = is_rider.and_then(|is_rider| read_data.uid_allocator.retrieve_entity_internal(is_rider.mount.into())).unwrap_or(entity);
|
let moving_entity = is_rider
|
||||||
|
.and_then(|is_rider| {
|
||||||
|
read_data
|
||||||
|
.uid_allocator
|
||||||
|
.retrieve_entity_internal(is_rider.mount.into())
|
||||||
|
})
|
||||||
|
.unwrap_or(entity);
|
||||||
|
|
||||||
let moving_body = read_data.bodies.get(moving_entity);
|
let moving_body = read_data.bodies.get(moving_entity);
|
||||||
|
|
||||||
@ -147,8 +153,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(CharacterState::GlideWield(_) | CharacterState::Glide(_))
|
Some(CharacterState::GlideWield(_) | CharacterState::Glide(_))
|
||||||
) && physics_state.on_ground.is_none();
|
) && physics_state.on_ground.is_none();
|
||||||
|
|
||||||
if let Some((kp, ki, kd)) = moving_body.and_then(comp::agent::pid_coefficients) {
|
if let Some((kp, ki, kd)) = moving_body.and_then(comp::agent::pid_coefficients)
|
||||||
if agent.position_pid_controller.as_ref().map_or(false, |pid| (pid.kp, pid.ki, pid.kd) != (kp, ki, kd)) {
|
{
|
||||||
|
if agent
|
||||||
|
.position_pid_controller
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |pid| (pid.kp, pid.ki, pid.kd) != (kp, ki, kd))
|
||||||
|
{
|
||||||
agent.position_pid_controller = None;
|
agent.position_pid_controller = None;
|
||||||
}
|
}
|
||||||
let pid = agent.position_pid_controller.get_or_insert_with(|| {
|
let pid = agent.position_pid_controller.get_or_insert_with(|| {
|
||||||
|
@ -19,7 +19,7 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self, agent, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate, Pos, Waypoint,
|
self, agent, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate, Pos, Waypoint,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent, NpcBuilder},
|
event::{EventBus, NpcBuilder, ServerEvent},
|
||||||
generation::EntityInfo,
|
generation::EntityInfo,
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
resources::{Time, TimeOfDay},
|
resources::{Time, TimeOfDay},
|
||||||
@ -225,7 +225,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.with_agent(agent)
|
.with_agent(agent)
|
||||||
.with_scale(scale)
|
.with_scale(scale)
|
||||||
.with_anchor(comp::Anchor::Chunk(key))
|
.with_anchor(comp::Anchor::Chunk(key))
|
||||||
.with_loot(loot)
|
.with_loot(loot),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1914,6 +1914,13 @@ impl WorldSim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the altitude of the surface, could be water or ground.
|
||||||
|
pub fn get_surface_alt_approx(&self, wpos: Vec2<i32>) -> Option<f32> {
|
||||||
|
self.get_interpolated(wpos, |chunk| chunk.alt)
|
||||||
|
.zip(self.get_interpolated(wpos, |chunk| chunk.water_alt))
|
||||||
|
.map(|(alt, water_alt)| alt.max(water_alt))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_alt_approx(&self, wpos: Vec2<i32>) -> Option<f32> {
|
pub fn get_alt_approx(&self, wpos: Vec2<i32>) -> Option<f32> {
|
||||||
self.get_interpolated(wpos, |chunk| chunk.alt)
|
self.get_interpolated(wpos, |chunk| chunk.alt)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user