very simple repopulation

This commit is contained in:
Isse 2023-03-09 16:07:06 +01:00 committed by Joshua Barretto
parent dda1be58d4
commit adb2e1ba85
9 changed files with 157 additions and 21 deletions

View File

@ -15,5 +15,7 @@
(10, "common.items.consumable.potion_med"), (10, "common.items.consumable.potion_med"),
], ],
), ),
meta: [], meta: [
SkillSetAsset("common.skillset.preset.rank5.fullskill"),
],
) )

View File

@ -52,6 +52,15 @@ pub type ReadError = rmp_serde::decode::Error;
pub type WriteError = rmp_serde::encode::Error; pub type WriteError = rmp_serde::encode::Error;
impl Data { impl Data {
pub fn spawn_npc(&mut self, npc: Npc) -> NpcId {
let home = npc.home;
let id = self.npcs.create_npc(npc);
if let Some(home) = home.and_then(|home| self.sites.get_mut(home)) {
home.population.insert(id);
}
id
}
pub fn from_reader<R: Read>(reader: R) -> Result<Self, ReadError> { pub fn from_reader<R: Read>(reader: R) -> Result<Self, ReadError> {
rmp_serde::decode::from_read(reader) rmp_serde::decode::from_read(reader)
} }

View File

@ -1,6 +1,10 @@
pub use common::rtsim::SiteId; pub use common::rtsim::SiteId;
use common::{rtsim::FactionId, store::Id, uid::Uid}; use common::{
use hashbrown::HashMap; rtsim::{FactionId, NpcId},
store::Id,
uid::Uid,
};
use hashbrown::{HashMap, HashSet};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slotmap::HopSlotMap; use slotmap::HopSlotMap;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -27,6 +31,9 @@ pub struct Site {
/// being too). /// being too).
#[serde(skip_serializing, skip_deserializing)] #[serde(skip_serializing, skip_deserializing)]
pub world_site: Option<Id<WorldSite>>, pub world_site: Option<Id<WorldSite>>,
#[serde(skip_serializing, skip_deserializing)]
pub population: HashSet<NpcId>,
} }
impl Site { impl Site {

View File

@ -76,7 +76,6 @@ 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
@ -97,10 +96,7 @@ impl Data {
}; };
let random_humanoid = |rng: &mut SmallRng| { let random_humanoid = |rng: &mut SmallRng| {
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap(); let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
Body::Humanoid(comp::humanoid::Body::random_with( Body::Humanoid(comp::humanoid::Body::random_with(rng, species))
rng,
species,
))
}; };
if good_or_evil { if good_or_evil {
for _ in 0..32 { for _ in 0..32 {
@ -152,7 +148,7 @@ impl Data {
); );
} }
} }
*/
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|

View File

@ -1,5 +1,6 @@
use crate::data::{FactionId, Factions, Site}; use crate::data::{FactionId, Factions, Site};
use common::store::Id; use common::store::Id;
use hashbrown::HashSet;
use vek::*; use vek::*;
use world::{ use world::{
site::{Site as WorldSite, SiteKind}, site::{Site as WorldSite, SiteKind},
@ -48,6 +49,7 @@ impl Site {
.min_by_key(|(faction_wpos, _)| faction_wpos.distance_squared(wpos)) .min_by_key(|(faction_wpos, _)| faction_wpos.distance_squared(wpos))
.map(|(_, faction)| *faction) .map(|(_, faction)| *faction)
}), }),
population: HashSet::new(),
} }
} }
} }

View File

@ -329,12 +329,16 @@ 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(|| {
waypoint.get_or_insert_with(|| { let wpos = ctx.npc.wpos + (rpos / len) * len.min(STEP_DIST);
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)) 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);
}) })

View File

@ -1,11 +1,17 @@
use crate::{ use crate::{
data::npc::SimulationMode, data::{npc::SimulationMode, Npc},
event::{OnSetup, OnTick}, event::{OnDeath, OnSetup, OnTick},
RtState, Rule, RuleError, RtState, Rule, RuleError,
}; };
use common::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize}; use common::{
use tracing::info; comp::{self, Body},
use vek::*; grid::Grid,
terrain::TerrainChunkSize,
vol::RectVolSize,
};
use rand::{rngs::ThreadRng, seq::SliceRandom, Rng};
use tracing::warn;
use world::site::SiteKind;
pub struct SimulateNpcs; pub struct SimulateNpcs;
@ -27,8 +33,98 @@ impl Rule for SimulateNpcs {
} }
} }
} }
if let Some(home) = npc.home.and_then(|home| data.sites.get_mut(home)) {
home.population.insert(npc_id);
}
} }
}); });
rtstate.bind::<Self, OnDeath>(|ctx| {
let data = &mut *ctx.state.data_mut();
let npc_id = ctx.event.npc_id;
let Some(npc) = data.npcs.get(npc_id) else {
return;
};
if let Some(home) = npc.home.and_then(|home| data.sites.get_mut(home)) {
home.population.remove(&npc_id);
}
let mut rng = rand::thread_rng();
match npc.body {
Body::Humanoid(_) => {
if let Some((site_id, site)) = data
.sites
.iter()
.filter(|(id, site)| {
Some(*id) != npc.home
&& site.faction == npc.faction
&& site.world_site.map_or(false, |s| {
matches!(ctx.index.sites.get(s).kind, SiteKind::Refactor(_))
})
})
.min_by_key(|(_, site)| site.population.len())
{
let rand_wpos = |rng: &mut ThreadRng| {
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
wpos2d
.map(|e| e as f32 + 0.5)
.with_z(ctx.world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
};
let random_humanoid = |rng: &mut ThreadRng| {
let species = comp::humanoid::ALL_SPECIES.choose(&mut *rng).unwrap();
Body::Humanoid(comp::humanoid::Body::random_with(rng, species))
};
data.spawn_npc(
Npc::new(rng.gen(), rand_wpos(&mut rng), random_humanoid(&mut rng))
.with_home(site_id)
.with_faction(npc.faction)
.with_profession(npc.profession.clone()),
);
} else {
warn!("No site found for respawning humaniod");
}
},
Body::BirdLarge(_) => {
if let Some((site_id, site)) = data
.sites
.iter()
.filter(|(id, site)| {
Some(*id) != npc.home
&& site.world_site.map_or(false, |s| {
matches!(ctx.index.sites.get(s).kind, SiteKind::Dungeon(_))
})
})
.min_by_key(|(_, site)| site.population.len())
{
let rand_wpos = |rng: &mut ThreadRng| {
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
wpos2d
.map(|e| e as f32 + 0.5)
.with_z(ctx.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();
data.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),
);
} else {
warn!("No site found for respawning bird");
}
},
_ => unimplemented!(),
}
});
rtstate.bind::<Self, OnTick>(|ctx| { rtstate.bind::<Self, OnTick>(|ctx| {
let data = &mut *ctx.state.data_mut(); let data = &mut *ctx.state.data_mut();
for (vehicle_id, vehicle) in data.npcs.vehicles.iter_mut() { for (vehicle_id, vehicle) in data.npcs.vehicles.iter_mut() {

View File

@ -1997,7 +1997,26 @@ fn handle_kill_npcs(
true true
}; };
should_kill.then_some(entity) if should_kill {
if let Some(rtsim_entity) = ecs
.read_storage::<common::rtsim::RtSimEntity>()
.get(entity)
.copied()
{
ecs
.write_resource::<crate::rtsim2::RtSim>()
.hook_rtsim_entity_delete(
&ecs.read_resource::<Arc<world::World>>(),
ecs
.read_resource::<world::IndexOwned>()
.as_index_ref(),
rtsim_entity,
);
}
Some(entity)
} else {
None
}
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
}; };

View File

@ -6,12 +6,13 @@ use common::{
comp::{self, inventory::loadout::Loadout, skillset::skills, Agent, Body}, comp::{self, inventory::loadout::Loadout, skillset::skills, Agent, Body},
event::{EventBus, NpcBuilder, ServerEvent}, event::{EventBus, NpcBuilder, ServerEvent},
generation::{BodyBuilder, EntityConfig, EntityInfo}, generation::{BodyBuilder, EntityConfig, EntityInfo},
lottery::LootSpec,
resources::{DeltaTime, Time, TimeOfDay}, resources::{DeltaTime, Time, TimeOfDay},
rtsim::{RtSimController, RtSimEntity, RtSimVehicle}, rtsim::{RtSimController, RtSimEntity, RtSimVehicle},
slowjob::SlowJobPool, slowjob::SlowJobPool,
terrain::CoordinateConversions, terrain::CoordinateConversions,
trade::{Good, SiteInformation}, trade::{Good, SiteInformation},
LoadoutBuilder, SkillSetBuilder, lottery::LootSpec, LoadoutBuilder, SkillSetBuilder,
}; };
use common_ecs::{Job, Origin, Phase, System}; use common_ecs::{Job, Origin, Phase, System};
use rtsim2::data::{ use rtsim2::data::{