Fmt, make clippy happy

This commit is contained in:
Joshua Barretto 2020-11-23 15:39:03 +00:00
parent 6215682619
commit 26fd40c0e3
43 changed files with 660 additions and 504 deletions

View File

@ -1,8 +1,8 @@
use crate::{
comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, Body},
path::Chaser,
sync::Uid,
rtsim::RtSimController,
sync::Uid,
};
use specs::{Component, Entity as EcsEntity};
use specs_idvs::IdvStorage;

View File

@ -1,11 +1,4 @@
use crate::{
character::CharacterId,
sync::Uid,
util::Dir,
comp,
Explosion,
rtsim::RtSimEntity,
};
use crate::{character::CharacterId, comp, rtsim::RtSimEntity, sync::Uid, util::Dir, Explosion};
use comp::{
item::{Item, Reagent},
Ori, Pos,

View File

@ -1,7 +1,7 @@
use crate::{
comp::{self, humanoid, Alignment, Body, Item},
npc::{self, NPC_NAMES},
loadout_builder::LoadoutConfig,
npc::{self, NPC_NAMES},
};
use vek::*;

View File

@ -88,7 +88,7 @@ impl LoadoutBuilder {
#[allow(clippy::single_match)]
pub fn build_loadout(
body: Body,
alignment: Alignment,
_alignment: Alignment,
mut main_tool: Option<Item>,
is_giant: bool,
map: &AbilityMap,
@ -410,13 +410,11 @@ impl LoadoutBuilder {
back: None,
ring: None,
neck: None,
lantern: Some(Item::new_from_asset_expect(
"common.items.lantern.black_0",
)),
lantern: Some(Item::new_from_asset_expect("common.items.lantern.black_0")),
glider: None,
head: None,
tabard: None,
}
},
}
} else {
match body {

View File

@ -130,7 +130,8 @@ impl Route {
// Determine whether we're close enough to the next to to consider it completed
let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy());
if dist_sqrd < traversal_cfg.node_tolerance.powf(2.0) * if be_precise { 0.25 } else { 1.0 }
if dist_sqrd
< traversal_cfg.node_tolerance.powf(2.0) * if be_precise { 0.25 } else { 1.0 }
&& (((pos.z - closest_tgt.z > 1.2 || (pos.z - closest_tgt.z > -0.2 && traversal_cfg.on_ground))
&& (pos.z - closest_tgt.z < 1.2 || (pos.z - closest_tgt.z < 2.9 && vel.z < -0.05))
&& vel.z <= 0.0
@ -142,7 +143,9 @@ impl Route {
.0
> pos.distance(closest_tgt) * 0.9 || dist_sqrd < 0.5)
&& self.next_idx < self.path.len())
|| (traversal_cfg.in_liquid && pos.z < closest_tgt.z + 0.8 && pos.z > closest_tgt.z))
|| (traversal_cfg.in_liquid
&& pos.z < closest_tgt.z + 0.8
&& pos.z > closest_tgt.z))
{
// Node completed, move on to the next one
self.next_idx += 1;
@ -438,15 +441,14 @@ fn walkable<V>(vol: &V, pos: Vec3<i32>) -> bool
where
V: BaseVol<Vox = Block> + ReadVol,
{
let below = vol.get(pos - Vec3::unit_z())
let below = vol
.get(pos - Vec3::unit_z())
.ok()
.copied()
.unwrap_or_else(Block::empty);
let a = vol.get(pos)
.ok()
.copied()
.unwrap_or_else(Block::empty);
let b = vol.get(pos + Vec3::unit_z())
let a = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
let b = vol
.get(pos + Vec3::unit_z())
.ok()
.copied()
.unwrap_or_else(Block::empty);
@ -513,10 +515,10 @@ where
];
const JUMPS: [Vec3<i32>; 4] = [
Vec3::new(0, 1, 2), // Forward Upwardx2
Vec3::new(1, 0, 2), // Right Upwardx2
Vec3::new(0, -1, 2), // Backward Upwardx2
Vec3::new(-1, 0, 2), // Left Upwardx2
Vec3::new(0, 1, 2), // Forward Upwardx2
Vec3::new(1, 0, 2), // Right Upwardx2
Vec3::new(0, -1, 2), // Backward Upwardx2
Vec3::new(-1, 0, 2), // Left Upwardx2
];
// let walkable = [
@ -538,13 +540,15 @@ where
// ];
DIRS.iter()
.chain(Some(JUMPS.iter())
.filter(|_| vol
.get(pos)
.map(|b| !b.is_liquid())
.unwrap_or(true) || traversal_cfg.can_climb)
.into_iter()
.flatten())
.chain(
Some(JUMPS.iter())
.filter(|_| {
vol.get(pos).map(|b| !b.is_liquid()).unwrap_or(true)
|| traversal_cfg.can_climb
})
.into_iter()
.flatten(),
)
.map(move |dir| (pos, dir))
.filter(move |(pos, dir)| {
is_walkable(pos)

View File

@ -3,8 +3,8 @@
// `Agent`). When possible, this should be moved to the `rtsim`
// module in `server`.
use specs_idvs::IdvStorage;
use specs::Component;
use specs_idvs::IdvStorage;
use vek::*;
pub type RtSimId = usize;
@ -16,20 +16,20 @@ impl Component for RtSimEntity {
type Storage = IdvStorage<Self>;
}
/// This type is the map route through which the rtsim (real-time simulation) aspect
/// of the game communicates with the rest of the game. It is analagous to
/// `comp::Controller` in that it provides a consistent interface for simulation NPCs
/// to control their actions. Unlike `comp::Controller`, it is very abstract and is
/// intended for consumption by both the agent code and the internal rtsim simulation
/// code (depending on whether the entity is loaded into the game as a physical entity
/// or not). Agent code should attempt to act upon its instructions where reasonable
/// although deviations for various reasons (obstacle avoidance, counter-attacking,
/// etc.) are expected.
/// This type is the map route through which the rtsim (real-time simulation)
/// aspect of the game communicates with the rest of the game. It is analagous
/// to `comp::Controller` in that it provides a consistent interface for
/// simulation NPCs to control their actions. Unlike `comp::Controller`, it is
/// very abstract and is intended for consumption by both the agent code and the
/// internal rtsim simulation code (depending on whether the entity is loaded
/// into the game as a physical entity or not). Agent code should attempt to act
/// upon its instructions where reasonable although deviations for various
/// reasons (obstacle avoidance, counter-attacking, etc.) are expected.
#[derive(Clone, Debug)]
pub struct RtSimController {
/// When this field is `Some(..)`, the agent should attempt to make progress
/// toward the given location, accounting for obstacles and other high-priority
/// situations like being attacked.
/// toward the given location, accounting for obstacles and other
/// high-priority situations like being attacked.
pub travel_to: Option<Vec3<f32>>,
/// Proportion of full speed to move
pub speed_factor: f32,
@ -45,7 +45,5 @@ impl Default for RtSimController {
}
impl RtSimController {
pub fn reset(&mut self) {
*self = Self::default();
}
pub fn reset(&mut self) { *self = Self::default(); }
}

View File

@ -5,7 +5,10 @@ use crate::{
},
event::LocalEvent,
states::*,
sys::{character_behavior::JoinData, phys::{GRAVITY, FRIC_GROUND}},
sys::{
character_behavior::JoinData,
phys::{FRIC_GROUND, GRAVITY},
},
util::Dir,
};
use serde::{Deserialize, Serialize};
@ -95,6 +98,7 @@ impl Body {
}
}
#[allow(clippy::match_like_matches_macro)]
pub fn can_climb(&self) -> bool {
match self {
Body::Humanoid(_) => true,

View File

@ -223,24 +223,18 @@ impl<'a> System<'a> for Sys {
match &mut agent.activity {
Activity::Idle { bearing, chaser } => {
if let Some(travel_to) = agent.rtsim_controller.travel_to {
if let Some((bearing, speed)) = chaser.chase(
&*terrain,
pos.0,
vel.0,
travel_to,
TraversalConfig {
if let Some((bearing, speed)) =
chaser.chase(&*terrain, pos.0, vel.0, travel_to, TraversalConfig {
min_tgt_dist: 1.25,
..traversal_config
},
) {
inputs.move_dir = bearing
.xy()
.try_normalized()
.unwrap_or(Vec2::zero())
* speed.min(agent.rtsim_controller.speed_factor);
})
{
inputs.move_dir =
bearing.xy().try_normalized().unwrap_or(Vec2::zero())
* speed.min(agent.rtsim_controller.speed_factor);
inputs.jump.set_state(bearing.z > 1.5);
inputs.climb = Some(comp::Climb::Up);
//.filter(|_| bearing.z > 0.1 || physics_state.in_liquid.is_some());
//.filter(|_| bearing.z > 0.1 || physics_state.in_liquid.is_some());
inputs.move_z = bearing.z + 0.05;
}
} else {

View File

@ -19,7 +19,7 @@ use roots::find_roots_cubic;
use serde::{Deserialize, Serialize};
use crate::{
vol::{RectVolSize, ReadVol},
vol::{ReadVol, RectVolSize},
volumes::vol_grid_2d::VolGrid2d,
};
use vek::*;

View File

@ -437,14 +437,15 @@ fn handle_time(
// Accept `u12345`, seconds since midnight day 0
Err(_) => match n
.get(1..)
.filter(|_| n.chars().next() == Some('u'))
.and_then(|n| n.trim_left_matches('u').parse::<u64>().ok())
.filter(|_| n.starts_with('u'))
.and_then(|n| n.trim_start_matches('u').parse::<u64>().ok())
{
Some(n) => n as f64,
None => {
server.notify_client(
client,
ChatType::CommandError.server_msg(format!("'{}' is not a valid time.", n)),
ChatType::CommandError
.server_msg(format!("'{}' is not a valid time.", n)),
);
return;
},
@ -454,9 +455,49 @@ fn handle_time(
None => {
let time_in_seconds = server.state.ecs_mut().read_resource::<TimeOfDay>().0;
// Would this ever change? Perhaps in a few hundred thousand years some
// game archeologists of the future will resurrect the best game of all
// time which, obviously, would be Veloren. By that time, the inescapable
// laws of thermodynamics will mean that the earth's rotation period
// would be slower. Of course, a few hundred thousand years is enough
// for the circadian rhythm of human biology to have shifted to account
// accordingly. When booting up Veloren for the first time in 337,241
// years, they might feel a touch of anguish at the fact that their
// earth days and the days within the game do not neatly divide into
// one-another. Understandably, they'll want to change this. Who
// wouldn't? It would be like turning the TV volume up to an odd number
// or having a slightly untuned radio (assuming they haven't begun
// broadcasting information directly into their brains). Totally
// unacceptable. No, the correct and proper thing to do would be to
// release a retroactive definitive edition DLC for $99 with the very
// welcome addition of shorter day periods and a complementary
// 'developer commentary' mode created by digging up the long-decayed
// skeletons of the Veloren team, measuring various attributes of their
// jawlines, and using them to recreate their voices. But how to go about
// this Herculean task? This code is jibberish! The last of the core Rust
// dev team died exactly 337,194 years ago! Rust is now a long-forgotten
// dialect of the ancient ones, lost to the sands of time. Ashes to ashes,
// dust to dust. When all hope is lost, one particularly intrepid
// post-human hominid exployed by the 'Veloren Revival Corp' (no doubt we
// still won't have gotted rid of this blasted 'capitalism' thing by then)
// might notice, after years of searching, a particularly curious
// inscription within the code. The letters `D`, `A`, `Y`. Curious! She
// consults the post-human hominid scholars of the old. Care to empathise
// with her shock when she discovers that these symbols, as alien as they
// may seem, correspond exactly to the word `ⓕя𝐢ᵇᵇ𝔩E`, the word for
// 'day' in the post-human hominid language, which is of course universal.
// Imagine also her surprise when, after much further translating, she
// finds a comment predicting her very existence and her struggle to
// decode this great mystery. Rejoyce! The Veloren Revival Corp. may now
// persist with their great Ultimate Edition DLC because the day period
// might now be changed because they have found the constant that controls
// it! Everybody was henceforth happy until the end of time.
//
// This one's for you, xMac ;)
const DAY: u64 = 86400;
let current_time = NaiveTime::from_num_seconds_from_midnight_opt(
// Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s)
(time_in_seconds as u64 % 86400) as u32,
(time_in_seconds as u64 % DAY) as u32,
0,
);
let msg = match current_time {
@ -470,14 +511,14 @@ fn handle_time(
server.state.ecs_mut().write_resource::<TimeOfDay>().0 = new_time;
if let Some(new_time) = NaiveTime::from_num_seconds_from_midnight_opt(((new_time as u64) % 86400) as u32, 0) {
if let Some(new_time) =
NaiveTime::from_num_seconds_from_midnight_opt(((new_time as u64) % 86400) as u32, 0)
{
server.notify_client(
client,
ChatType::CommandInfo.server_msg(format!(
"Time changed to: {}",
new_time
.format("%H:%M")
.to_string(),
new_time.format("%H:%M").to_string(),
)),
);
}

View File

@ -6,8 +6,8 @@ use common::{
LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
},
outcome::Outcome,
util::Dir,
rtsim::RtSimEntity,
util::Dir,
};
use comp::group;
use specs::{Builder, Entity as EcsEntity, WorldExt};

View File

@ -1,8 +1,8 @@
use crate::{
client::Client,
comp::{biped_large, quadruped_medium, quadruped_small, PhysicsState},
Server, SpawnPoint, StateExt,
rtsim::RtSim,
Server, SpawnPoint, StateExt,
};
use common::{
assets::Asset,
@ -16,12 +16,12 @@ use common::{
lottery::Lottery,
msg::{PlayerListUpdate, ServerGeneral},
outcome::Outcome,
rtsim::RtSimEntity,
state::BlockChange,
sync::{Uid, UidAllocator, WorldSyncExt},
terrain::{Block, TerrainGrid},
vol::ReadVol,
Damage, DamageSource, Explosion, GroupTarget, RadiusEffect,
rtsim::RtSimEntity,
};
use comp::item::Reagent;
use rand::prelude::*;
@ -462,8 +462,16 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
};
if should_delete {
if let Some(rtsim_entity) = state.ecs().read_storage::<RtSimEntity>().get(entity).copied() {
state.ecs().write_resource::<RtSim>().destroy_entity(rtsim_entity.0);
if let Some(rtsim_entity) = state
.ecs()
.read_storage::<RtSimEntity>()
.get(entity)
.copied()
{
state
.ecs()
.write_resource::<RtSim>()
.destroy_entity(rtsim_entity.0);
}
let _ = state

View File

@ -8,8 +8,8 @@ use entity_creation::{
handle_loaded_character_data, handle_shockwave, handle_shoot,
};
use entity_manipulation::{
handle_buff, handle_damage, handle_destroy, handle_delete, handle_energy_change, handle_explosion,
handle_knockback, handle_land_on_ground, handle_level_up, handle_respawn,
handle_buff, handle_damage, handle_delete, handle_destroy, handle_energy_change,
handle_explosion, handle_knockback, handle_land_on_ground, handle_level_up, handle_respawn,
};
use group_manip::handle_group;
use interaction::{handle_lantern, handle_mount, handle_possess, handle_unmount};
@ -120,8 +120,18 @@ impl Server {
drop_item,
rtsim_entity,
} => handle_create_npc(
self, pos, stats, health, loadout, body, agent, alignment, scale, drop_item,
home_chunk, rtsim_entity,
self,
pos,
stats,
health,
loadout,
body,
agent,
alignment,
scale,
drop_item,
home_chunk,
rtsim_entity,
),
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
ServerEvent::ClientDisconnect(entity) => {

View File

@ -52,9 +52,7 @@ use common::{
comp::{self, ChatType},
event::{EventBus, ServerEvent},
msg::{
world_msg::{SiteInfo, SiteKind},
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg,
WorldMapMsg,
ClientType, DisconnectReason, ServerGeneral, ServerInfo, ServerInit, ServerMsg, WorldMapMsg,
},
outcome::Outcome,
recipe::default_recipe_book,
@ -62,7 +60,7 @@ use common::{
state::{State, TimeOfDay},
sync::WorldSyncExt,
terrain::TerrainChunkSize,
vol::{ReadVol, RectVolSize},
vol::RectVolSize,
};
use futures_executor::block_on;
use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics};
@ -327,7 +325,7 @@ impl Server {
// Insert the world into the ECS (todo: Maybe not an Arc?)
let world = Arc::new(world);
state.ecs_mut().insert(world.clone());
state.ecs_mut().insert(Arc::clone(&world));
state.ecs_mut().insert(index.clone());
// Set starting time for the server.

View File

@ -10,9 +10,7 @@ pub struct Chunks {
impl Chunks {
pub fn new(size: Vec2<u32>) -> Self {
Chunks {
chunks: Grid::populate_from(size.map(|e| e as i32), |_| Chunk {
is_loaded: false,
}),
chunks: Grid::populate_from(size.map(|e| e as i32), |_| Chunk { is_loaded: false }),
chunks_to_load: Vec::new(),
chunks_to_unload: Vec::new(),
}
@ -25,7 +23,9 @@ impl Chunks {
pub fn chunk_mut(&mut self, key: Vec2<i32>) -> Option<&mut Chunk> { self.chunks.get_mut(key) }
pub fn chunk_at(&self, pos: Vec2<f32>) -> Option<&Chunk> {
self.chunks.get(pos.map2(TerrainChunk::RECT_SIZE, |e, sz| (e.floor() as i32).div_euclid(sz as i32)))
self.chunks.get(pos.map2(TerrainChunk::RECT_SIZE, |e, sz| {
(e.floor() as i32).div_euclid(sz as i32)
}))
}
}

View File

@ -1,16 +1,14 @@
use super::*;
use common::event::{EventBus, ServerEvent};
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect};
use specs::{Read, System, WriteExpect};
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Read<'a, EventBus<ServerEvent>>,
WriteExpect<'a, RtSim>,
);
#[allow(clippy::type_complexity)]
type SystemData = (Read<'a, EventBus<ServerEvent>>, WriteExpect<'a, RtSim>);
fn run(&mut self, (server_event_bus, mut rtsim): Self::SystemData) {
for chunk in std::mem::take(&mut rtsim.chunks.chunks_to_load) {
fn run(&mut self, (_server_event_bus, mut rtsim): Self::SystemData) {
for _chunk in std::mem::take(&mut rtsim.chunks.chunks_to_load) {
// TODO
}
}

View File

@ -1,25 +1,23 @@
mod load_chunks;
mod unload_chunks;
mod tick;
mod entity;
mod chunks;
#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out
use vek::*;
mod chunks;
mod entity;
mod load_chunks;
mod tick;
mod unload_chunks;
use self::{chunks::Chunks, entity::Entity};
use common::{
comp,
rtsim::{RtSimController, RtSimEntity, RtSimId},
state::State,
terrain::TerrainChunk,
rtsim::{RtSimEntity, RtSimId, RtSimController},
vol::RectRasterableVol,
comp,
};
use specs::{DispatcherBuilder, WorldExt};
use specs_idvs::IdvStorage;
use slab::Slab;
use rand::prelude::*;
use self::{
entity::Entity,
chunks::Chunks,
};
use slab::Slab;
use specs::{DispatcherBuilder, WorldExt};
use vek::*;
pub struct RtSim {
tick: u64,
@ -88,12 +86,14 @@ pub fn init(state: &mut State, world: &world::World) {
let mut rtsim = RtSim::new(world.sim().get_size());
for _ in 0..2500 {
let pos = rtsim.chunks.size().map2(
TerrainChunk::RECT_SIZE,
|sz, chunk_sz| thread_rng().gen_range(0, sz * chunk_sz) as i32,
);
let pos = rtsim
.chunks
.size()
.map2(TerrainChunk::RECT_SIZE, |sz, chunk_sz| {
thread_rng().gen_range(0, sz * chunk_sz) as i32
});
let id = rtsim.entities.insert(Entity {
rtsim.entities.insert(Entity {
is_loaded: false,
pos: Vec3::from(pos.map(|e| e as f32)),
seed: thread_rng().gen(),
@ -101,8 +101,6 @@ pub fn init(state: &mut State, world: &world::World) {
last_tick: 0,
brain: Default::default(),
});
// tracing::info!("Spawned rtsim NPC {} at {:?}", id, pos);
}
state.ecs_mut().insert(rtsim);

View File

@ -1,3 +1,5 @@
#![allow(dead_code)] // TODO: Remove this when rtsim is fleshed out
use super::*;
use common::{
comp,
@ -12,6 +14,7 @@ const ENTITY_TICK_PERIOD: u64 = 30;
pub struct Sys;
impl<'a> System<'a> for Sys {
#[allow(clippy::type_complexity)]
type SystemData = (
Read<'a, DeltaTime>,
Read<'a, EventBus<ServerEvent>>,
@ -33,7 +36,7 @@ impl<'a> System<'a> for Sys {
mut rtsim,
terrain,
world,
index,
_index,
positions,
rtsim_entities,
mut agents,

View File

@ -4,10 +4,11 @@ use common::{
event::{EventBus, ServerEvent},
terrain::TerrainGrid,
};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect};
use specs::{Entities, Read, ReadExpect, ReadStorage, System, WriteExpect};
pub struct Sys;
impl<'a> System<'a> for Sys {
#[allow(clippy::type_complexity)]
type SystemData = (
Read<'a, EventBus<ServerEvent>>,
WriteExpect<'a, RtSim>,
@ -20,17 +21,17 @@ impl<'a> System<'a> for Sys {
fn run(
&mut self,
(
server_event_bus,
_server_event_bus,
mut rtsim,
terrain_grid,
entities,
rtsim_entities,
positions,
_terrain_grid,
_entities,
_rtsim_entities,
_positions,
): Self::SystemData,
) {
let chunks = std::mem::take(&mut rtsim.chunks.chunks_to_unload);
for chunk in chunks {
for _chunk in chunks {
// TODO
}
}

View File

@ -5,7 +5,7 @@ use common::{
use criterion::{black_box, criterion_group, criterion_main, Benchmark, Criterion};
use std::sync::Arc;
use vek::*;
use veloren_voxygen::mesh::Meshable;
use veloren_voxygen::{mesh::Meshable, scene::terrain::BlocksOfInterest};
use world::{sim, World};
const CENTER: Vec2<i32> = Vec2 { x: 512, y: 512 };
@ -133,7 +133,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
c.bench(
"meshing",
Benchmark::new(&format!("Terrain mesh {}, {}", x, y), move |b| {
b.iter(|| volume.generate_mesh(black_box((range, Vec2::new(8192, 8192)))))
b.iter(|| volume.generate_mesh(black_box((range, Vec2::new(8192, 8192), &BlocksOfInterest::default()))))
})
// Lower sample size to save time
.sample_size(15),

View File

@ -489,15 +489,11 @@ impl<'a> Widget for Map<'a> {
{
continue;
}
let title =
site.name
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| match &site.kind {
SiteKind::Town => i18n.get("hud.map.town"),
SiteKind::Dungeon { .. } => i18n.get("hud.map.dungeon"),
SiteKind::Castle => i18n.get("hud.map.castle"),
});
let title = site.name.as_deref().unwrap_or_else(|| match &site.kind {
SiteKind::Town => i18n.get("hud.map.town"),
SiteKind::Dungeon { .. } => i18n.get("hud.map.dungeon"),
SiteKind::Castle => i18n.get("hud.map.castle"),
});
let (difficulty, desc) = match &site.kind {
SiteKind::Town => (0, i18n.get("hud.map.town").to_string()),
SiteKind::Dungeon { difficulty } => (
@ -656,10 +652,9 @@ impl<'a> Widget for Map<'a> {
Vec2::new(32.0, 37.0) * scale
};
// Hide if icon could go off of the edge of the map
let arrow_mag = arrow_sz.map(|e| e as f32 / 2.0).magnitude();
if !rpos
.map2(map_size, |e, sz| {
e.abs() + arrow_sz.map(|e| e as f32 / 2.0).magnitude() > sz as f32 / 2.0
})
.map2(map_size, |e, sz| e.abs() + arrow_mag > sz as f32 / 2.0)
.reduce_or()
{
Image::new(self.rot_imgs.indicator_mmap_small.target_north)

View File

@ -2291,7 +2291,7 @@ impl Hud {
// Reset the map position when it's not showing
let drag = &global_state.settings.gameplay.map_drag;
if drag.x != 0.0 || drag.y != 0.0 {
events.push(Event::MapDrag(drag - drag))
events.push(Event::MapDrag(Vec2::zero()))
}
}

View File

@ -586,16 +586,15 @@ fn draw_col_lights<D>(
0.0
}
) / 4.0;
let glowiness = (
get_glow(data, light_pos)
+ get_glow(data, light_pos - uv.x)
+ get_glow(data, light_pos - uv.y)
+ if direct_u_opacity || direct_v_opacity {
get_glow(data, light_pos - uv.x - uv.y)
} else {
0.0
}
) / 4.0;
let glowiness = (get_glow(data, light_pos)
+ get_glow(data, light_pos - uv.x)
+ get_glow(data, light_pos - uv.y)
+ if direct_u_opacity || direct_v_opacity {
get_glow(data, light_pos - uv.x - uv.y)
} else {
0.0
})
/ 4.0;
let col = get_color(data, pos);
let light = (darkness * 31.5) as u8;
let glow = (glowiness * 31.5) as u8;

View File

@ -77,7 +77,7 @@ where
0.0
}
};
let get_glow = |vol: &mut V, pos: Vec3<i32>| 0.0;
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos)
.ok()

View File

@ -250,6 +250,7 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
Meshable<TerrainPipeline, FluidPipeline> for &'a VolGrid2d<V>
{
type Pipeline = TerrainPipeline;
#[allow(clippy::type_complexity)]
type Result = (
Aabb<f32>,
ColLightInfo,
@ -468,7 +469,12 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
opaque_mesh,
fluid_mesh,
Mesh::new(),
(bounds, (col_lights, col_lights_size), Box::new(light), Box::new(glow)),
(
bounds,
(col_lights, col_lights_size),
Box::new(light),
Box::new(glow),
),
)
}
}

View File

@ -115,7 +115,14 @@ impl Vertex {
}
impl Instance {
pub fn new(mat: Mat4<f32>, wind_sway: f32, pos: Vec3<i32>, ori_bits: u8, light: f32, glow: f32) -> Self {
pub fn new(
mat: Mat4<f32>,
wind_sway: f32,
pos: Vec3<i32>,
ori_bits: u8,
light: f32,
glow: f32,
) -> Self {
const EXTRA_NEG_Z: i32 = 32768;
let mat_arr = mat.into_col_arrays();

View File

@ -107,23 +107,25 @@ impl Vertex {
) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType
{
//[col.r, col.g, col.b, light]
// It would be nice for this to be cleaner, but we want to squeeze 5 fields into 4.
// We can do this because both `light` and `glow` go from 0 to 31, meaning that they
// can both fit into 5 bits. If we steal a bit from red and blue each (not green,
// human eyes are more sensitive to changes in green) then we get just enough to
// expand the nibbles of the alpha field enough to fit both `light` and `glow`.
// It would be nice for this to be cleaner, but we want to squeeze 5 fields into
// 4. We can do this because both `light` and `glow` go from 0 to 31,
// meaning that they can both fit into 5 bits. If we steal a bit from
// red and blue each (not green, human eyes are more sensitive to
// changes in green) then we get just enough to expand the nibbles of
// the alpha field enough to fit both `light` and `glow`.
//
// However, we now have a problem. In the shader code with use hardware filtering to
// get at the `light` and `glow` attributes (but not colour, that remains constant
// across a block). How do we resolve this if we're twiddling bits? The answer is to
// very carefully manipulate the bit pattern such that the fields we want to filter
// (`light` and `glow`) always sit as the higher bits of the fields. Then, we can do
// However, we now have a problem. In the shader code with use hardware
// filtering to get at the `light` and `glow` attributes (but not
// colour, that remains constant across a block). How do we resolve this
// if we're twiddling bits? The answer is to very carefully manipulate
// the bit pattern such that the fields we want to filter (`light` and
// `glow`) always sit as the higher bits of the fields. Then, we can do
// some modulation magic to extract them from the filtering unharmed and use
// unfiltered texture access (i.e: `texelFetch`) to access the colours, plus a little
// bit-fiddling.
// unfiltered texture access (i.e: `texelFetch`) to access the colours, plus a
// little bit-fiddling.
//
// TODO: This isn't currently working (no idea why). See `srgb.glsl` for current impl
// that intead does manual bit-twiddling and filtering.
// TODO: This isn't currently working (no idea why). See `srgb.glsl` for current
// impl that intead does manual bit-twiddling and filtering.
[
(light.min(31) << 3) | ((col.r & 0b1110) >> 1),
(glow.min(31) << 3) | ((col.r & 0b1110) >> 1),

View File

@ -33,7 +33,8 @@ use tracing::{error, warn};
use vek::*;
/// Represents the format of the pre-processed color target.
// TODO: `(gfx::format::R11_G11_B10, gfx::format::Float)` would be better in theory, but it doesn't seem to work
// TODO: `(gfx::format::R11_G11_B10, gfx::format::Float)` would be better in
// theory, but it doesn't seem to work
pub type TgtColorFmt = gfx::format::Rgba16F;
/// Represents the format of the pre-processed depth and stencil target.
pub type TgtDepthStencilFmt = gfx::format::Depth;
@ -1001,10 +1002,10 @@ impl Renderer {
size: [u16; 2],
data: &[<<T as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType],
) -> Result<(), RenderError>
where
<T as gfx::format::Formatted>::Surface: gfx::format::TextureSurface,
<T as gfx::format::Formatted>::Channel: gfx::format::TextureChannel,
<<T as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType: Copy,
where
<T as gfx::format::Formatted>::Surface: gfx::format::TextureSurface,
<T as gfx::format::Formatted>::Channel: gfx::format::TextureChannel,
<<T as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType: Copy,
{
texture.update(&mut self.encoder, offset, size, data)
}

View File

@ -12,8 +12,9 @@ use crate::{
},
scene::{
camera::{Camera, CameraMode, Dependents},
math, LodData, SceneData,
math,
terrain::Terrain,
LodData, SceneData,
},
};
use anim::{
@ -3486,7 +3487,8 @@ impl<S: Skeleton> FigureState<S> {
})
.unwrap_or((1.0, 0.0));
// Fade between light and glow levels
// TODO: Making this temporal rather than spatial is a bit dumb but it's a very subtle difference
// TODO: Making this temporal rather than spatial is a bit dumb but it's a very
// subtle difference
self.last_light = vek::Lerp::lerp(self.last_light, light, 16.0 * dt);
self.last_glow = vek::Lerp::lerp(self.last_glow, glow, 16.0 * dt);

View File

@ -540,7 +540,8 @@ impl Scene {
lights.clear();
// Maintain the particles.
self.particle_mgr.maintain(renderer, &scene_data, &self.terrain, lights);
self.particle_mgr
.maintain(renderer, &scene_data, &self.terrain, lights);
// Update light constants
lights.extend(

View File

@ -2,8 +2,8 @@ use super::{terrain::BlocksOfInterest, SceneData, Terrain};
use crate::{
mesh::{greedy::GreedyMesh, Meshable},
render::{
pipelines::particle::ParticleMode, GlobalModel, Instances, LodData, Model,
ParticleInstance, ParticlePipeline, Renderer, Light,
pipelines::particle::ParticleMode, GlobalModel, Instances, Light, LodData, Model,
ParticleInstance, ParticlePipeline, Renderer,
},
};
use common::{
@ -73,7 +73,8 @@ impl ParticleMgr {
ParticleMode::EnergyNature,
*pos + Vec3::<f32>::zero()
.map(|_| rng.gen_range(-1.0, 1.0))
.normalized() * *radius,
.normalized()
* *radius,
)
},
);
@ -87,7 +88,8 @@ impl ParticleMgr {
ParticleMode::CampfireFire,
*pos + Vec3::<f32>::zero()
.map(|_| rng.gen_range(-1.0, 1.0))
.normalized() * *radius,
.normalized()
* *radius,
)
},
);
@ -121,7 +123,8 @@ impl ParticleMgr {
ParticleMode::CampfireSmoke,
*pos + Vec3::<f32>::zero()
.map(|_| rng.gen_range(-1.0, 1.0))
.normalized() * *radius,
.normalized()
* *radius,
)
},
);
@ -377,11 +380,7 @@ impl ParticleMgr {
if b.stage_section == StageSection::Cast {
if b.static_data.base_hps > 0 {
// Emit a light when using healing
lights.push(Light::new(
pos.0 + b.offset,
Rgb::new(0.1, 1.0, 0.15),
1.0,
));
lights.push(Light::new(pos.0 + b.offset, Rgb::new(0.1, 1.0, 0.15), 1.0));
for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) {
self.particles.push(Particle::new_beam(
b.static_data.beam_duration,

View File

@ -144,7 +144,11 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug + '
span!(_guard, "mesh_worker");
let blocks_of_interest = BlocksOfInterest::from_chunk(&chunk);
let (opaque_mesh, fluid_mesh, _shadow_mesh, (bounds, col_lights_info, light_map, glow_map)) =
volume.generate_mesh((range, Vec2::new(max_texture_size, max_texture_size), &blocks_of_interest));
volume.generate_mesh((
range,
Vec2::new(max_texture_size, max_texture_size),
&blocks_of_interest,
));
MeshWorkerResponse {
pos,
z_bounds: (bounds.min.z, bounds.max.z),
@ -472,14 +476,24 @@ impl<V: RectRasterableVol> Terrain<V> {
/// Find the light level (sunlight) at the given world position.
pub fn light_at_wpos(&self, wpos: Vec3<i32>) -> f32 {
let chunk_pos = Vec2::from(wpos).map2(TerrainChunk::RECT_SIZE, |e: i32, sz| e.div_euclid(sz as i32));
self.chunks.get(&chunk_pos).map(|c| (c.light_map)(wpos)).unwrap_or(1.0)
let chunk_pos = Vec2::from(wpos).map2(TerrainChunk::RECT_SIZE, |e: i32, sz| {
e.div_euclid(sz as i32)
});
self.chunks
.get(&chunk_pos)
.map(|c| (c.light_map)(wpos))
.unwrap_or(1.0)
}
/// Find the glow level (light from lamps) at the given world position.
pub fn glow_at_wpos(&self, wpos: Vec3<i32>) -> f32 {
let chunk_pos = Vec2::from(wpos).map2(TerrainChunk::RECT_SIZE, |e: i32, sz| e.div_euclid(sz as i32));
self.chunks.get(&chunk_pos).map(|c| (c.glow_map)(wpos)).unwrap_or(0.0)
let chunk_pos = Vec2::from(wpos).map2(TerrainChunk::RECT_SIZE, |e: i32, sz| {
e.div_euclid(sz as i32)
});
self.chunks
.get(&chunk_pos)
.map(|c| (c.glow_map)(wpos))
.unwrap_or(0.0)
}
/// Maintain terrain data. To be called once per tick.

View File

@ -6,6 +6,7 @@ use common::{
use rand::prelude::*;
use vek::*;
#[derive(Default)]
pub struct BlocksOfInterest {
pub leaves: Vec<Vec3<i32>>,
pub grass: Vec<Vec3<i32>>,
@ -22,8 +23,6 @@ pub struct BlocksOfInterest {
pub lights: Vec<(Vec3<i32>, u8)>,
}
use inline_tweak::*;
impl BlocksOfInterest {
pub fn from_chunk(chunk: &TerrainChunk) -> Self {
span!(_guard, "from_chunk", "BlocksOfInterest::from_chunk");
@ -73,8 +72,10 @@ impl BlocksOfInterest {
// Offset positions to account for block height.
// TODO: Is this a good idea?
Some(SpriteKind::StreetLamp) => fire_bowls.push(pos + Vec3::unit_z() * 2),
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z() * 1),
Some(SpriteKind::StreetLampTall) => fire_bowls.push(pos + Vec3::unit_z() * 4),
Some(SpriteKind::FireBowlGround) => fire_bowls.push(pos + Vec3::unit_z()),
Some(SpriteKind::StreetLampTall) => {
fire_bowls.push(pos + Vec3::unit_z() * 4)
},
Some(SpriteKind::Beehive) => beehives.push(pos),
Some(SpriteKind::Reed) => reeds.push(pos),
Some(SpriteKind::PinkFlower) => flowers.push(pos),

View File

@ -1,4 +1,4 @@
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, Canvas, CONFIG};
use crate::{column::ColumnSample, sim::SimChunk, Canvas, CONFIG};
use common::terrain::SpriteKind;
use noise::NoiseFn;
use rand::prelude::*;

View File

@ -31,6 +31,7 @@ static MODEL_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
#[allow(clippy::if_same_then_else)]
pub fn apply_trees_to(canvas: &mut Canvas) {
struct Tree {
pos: Vec3<i32>,
@ -51,15 +52,17 @@ pub fn apply_trees_to(canvas: &mut Canvas) {
let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0);
// Ensure that it's valid to place a tree here
// Ensure that it's valid to place a *thing* here
if col.alt < col.water_level
|| col.spawn_rate < 0.9
|| col.water_dist.map(|d| d < 8.0).unwrap_or(false)
|| col.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false)
{
return None;
} else if !is_quirky
&& ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density
}
// Ensure that it's valid to place a tree here
if !is_quirky && ((seed.wrapping_mul(13)) & 0xFF) as f32 / 256.0 > col.tree_density
{
return None;
}

View File

@ -182,10 +182,20 @@ impl World {
};
let meta = TerrainChunkMeta::new(
sim_chunk.sites
sim_chunk
.sites
.iter()
.filter(|id| index.sites[**id].get_origin().distance_squared(chunk_center_wpos2d) as f32 <= index.sites[**id].radius().powf(2.0))
.min_by_key(|id| index.sites[**id].get_origin().distance_squared(chunk_center_wpos2d))
.filter(|id| {
index.sites[**id]
.get_origin()
.distance_squared(chunk_center_wpos2d) as f32
<= index.sites[**id].radius().powf(2.0)
})
.min_by_key(|id| {
index.sites[**id]
.get_origin()
.distance_squared(chunk_center_wpos2d)
})
.map(|id| index.sites[*id].name().to_string()),
sim_chunk.get_biome(),
sim_chunk.alt,

View File

@ -1,11 +1,9 @@
use crate::sim::WorldSim;
use common::{
astar::Astar,
path::Path,
};
use hashbrown::hash_map::DefaultHashBuilder;
use common::path::Path;
//use hashbrown::hash_map::DefaultHashBuilder;
use vek::*;
#[allow(dead_code)]
pub struct SearchCfg {
// 0.0 = no discount, 1.0 = free travel
path_discount: f32,
@ -14,15 +12,19 @@ pub struct SearchCfg {
gradient_aversion: f32,
}
#[allow(dead_code)]
pub struct Searcher<'a> {
land: &'a WorldSim,
pub cfg: SearchCfg,
}
#[allow(dead_code)]
impl<'a> Searcher<'a> {
/// Attempt to find a path between two chunks on the map.
pub fn search(self, a: Vec2<i32>, b: Vec2<i32>) -> Option<Path<i32>> {
let heuristic = |pos: &Vec2<i32>| (pos - b).map(|e| e as f32).magnitude();
pub fn search(self, _a: Vec2<i32>, _b: Vec2<i32>) -> Option<Path<i32>> {
// TODO: implement this function
//let heuristic = |pos: &Vec2<i32>| (pos - b).map(|e| e as f32).magnitude();
// Astar::new(
// 100_000,
// a,

View File

@ -103,7 +103,7 @@ pub fn sample_pos(
spline_derivative,
is_path,
is_cave,
near_site,
_near_site,
) = sampler
.get(pos)
.map(|sample| {

View File

@ -3,11 +3,11 @@ use crate::{
column::ColumnSample,
sim::WorldSim,
site::{
namegen::NameGen,
settlement::building::{
archetype::keep::{Attr, FlagColor, Keep as KeepArchetype, StoneColor},
Archetype, Ori,
},
namegen::NameGen,
},
IndexRef,
};
@ -156,9 +156,7 @@ impl Castle {
this
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name(&self) -> &str { &self.name }
pub fn contains_point(&self, wpos: Vec2<i32>) -> bool {
let lpos = wpos - self.origin;

View File

@ -48,8 +48,6 @@ pub struct Colors {
const ALT_OFFSET: i32 = -2;
const LEVELS: usize = 5;
impl Dungeon {
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
@ -321,7 +319,10 @@ impl Floor {
enemy_density: Some((0.0002 * difficulty as f32).min(0.001)), // Minions!
miniboss: false,
boss: true,
area: Rect::from((new_stair_tile - tile_offset - 4, Extent2::broadcast(width as i32 * 2 + 1))),
area: Rect::from((
new_stair_tile - tile_offset - 4,
Extent2::broadcast(width as i32 * 2 + 1),
)),
height: height as i32,
pillars: Some(2),
difficulty,
@ -482,6 +483,8 @@ impl Floor {
origin: Vec3<i32>,
supplement: &mut ChunkSupplement,
) {
/*
// Add stair waypoint
let stair_rcenter =
Vec3::from((self.stair_tile + self.tile_offset).map(|e| e * TILE_SIZE + TILE_SIZE / 2));
@ -493,13 +496,14 @@ impl Floor {
.try_normalized()
.unwrap_or_else(Vec2::unit_y)
* (TILE_SIZE as f32 / 2.0 - 4.0);
// if !self.final_level {
// supplement.add_entity(
// EntityInfo::at((origin + stair_rcenter).map(|e| e as f32)
// + Vec3::from(offs)) .into_waypoint(),
// );
// }
if !self.final_level {
supplement.add_entity(
EntityInfo::at((origin + stair_rcenter).map(|e| e as f32)
+ Vec3::from(offs)) .into_waypoint(),
);
}
}
*/
for x in area.min.x..area.max.x {
for y in area.min.y..area.max.y {
@ -601,11 +605,11 @@ impl Floor {
(room.difficulty as f32).powf(1.5) + 4.0,
).round() as u32);
let entity = match room.difficulty {
0 => entity.with_name("Outcast")
.with_config(common::loadout_builder::LoadoutConfig::Outcast)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
0 => entity
.with_name("Outcast")
.with_config(common::loadout_builder::LoadoutConfig::Outcast)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.starter_axe",
1 => "common.items.weapons.sword.starter_sword",
@ -614,13 +618,12 @@ impl Floor {
4 => "common.items.weapons.staff.starter_staff",
_ => "common.items.weapons.bow.starter_bow",
},
),
),
1 => entity.with_name("Highwayman")
.with_config(common::loadout_builder::LoadoutConfig::Highwayman)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
)),
1 => entity
.with_name("Highwayman")
.with_config(common::loadout_builder::LoadoutConfig::Highwayman)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.worn_iron_axe-0",
1 => "common.items.weapons.sword.zweihander_sword_0",
@ -629,13 +632,12 @@ impl Floor {
4 => "common.items.weapons.staff.bone_staff",
_ => "common.items.weapons.bow.wood_shortbow-1",
},
),
),
2 => entity.with_name("Bandit")
.with_config(common::loadout_builder::LoadoutConfig::Bandit)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
)),
2 => entity
.with_name("Bandit")
.with_config(common::loadout_builder::LoadoutConfig::Bandit)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.bronze_axe-0",
1 => "common.items.weapons.sword.greatsword_2h_simple-0",
@ -644,13 +646,12 @@ impl Floor {
4 => "common.items.weapons.staff.bone_staff",
_ => "common.items.weapons.bow.wood_longbow-0",
},
),
),
3 => entity.with_name("Cultist Novice")
.with_config(common::loadout_builder::LoadoutConfig::CultistNovice)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
)),
3 => entity
.with_name("Cultist Novice")
.with_config(common::loadout_builder::LoadoutConfig::CultistNovice)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.steel_axe-0",
1 => "common.items.weapons.sword.long_2h_orn-0",
@ -659,13 +660,12 @@ impl Floor {
4 => "common.items.weapons.staff.amethyst_staff",
_ => "common.items.weapons.bow.horn_longbow-0",
},
),
),
4 => entity.with_name("Cultist Acolyte")
.with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
)),
4 => entity
.with_name("Cultist Acolyte")
.with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.malachite_axe-0",
1 => "common.items.weapons.sword.cultist_purp_2h-0",
@ -674,20 +674,20 @@ impl Floor {
4 => "common.items.weapons.staff.cultist_staff",
_ => "common.items.weapons.bow.horn_longbow-0",
},
),
),
)),
5 => match dynamic_rng.gen_range(0, 6) {
0 => entity.with_name("Cultist Warlock")
.with_config(common::loadout_builder::LoadoutConfig::Warlock)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect("common.items.npc_weapons.staff.cultist_staff"),
),
_ => entity.with_name("Cultist Warlord")
.with_config(common::loadout_builder::LoadoutConfig::Warlord)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(
comp::Item::new_from_asset_expect(
0 => entity
.with_name("Cultist Warlock")
.with_config(common::loadout_builder::LoadoutConfig::Warlock)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
"common.items.npc_weapons.staff.cultist_staff",
)),
_ => entity
.with_name("Cultist Warlord")
.with_config(common::loadout_builder::LoadoutConfig::Warlord)
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 5) {
0 => "common.items.weapons.axe.malachite_axe-0",
1 => "common.items.weapons.sword.cultist_purp_2h-0",
@ -695,8 +695,7 @@ impl Floor {
3 => "common.items.weapons.hammer.cultist_purp_2h-0",
_ => "common.items.weapons.bow.horn_longbow-0",
},
),
),
)),
},
_ => entity.with_name("Humanoid").with_main_tool(
comp::Item::new_from_asset_expect(
@ -723,77 +722,101 @@ impl Floor {
if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d {
let chosen = match room.difficulty {
0 => Lottery::<String>::load_expect("common.loot_tables.loot_table_weapon_uncommon"),
1 => Lottery::<String>::load_expect("common.loot_tables.loot_table_weapon_uncommon"),
2 => Lottery::<String>::load_expect("common.loot_tables.loot_table_armor_heavy"),
3 => Lottery::<String>::load_expect("common.loot_tables.loot_table_weapon_rare"),
4 => Lottery::<String>::load_expect("common.loot_tables.loot_table_boss_cultist-leader"),
5 => Lottery::<String>::load_expect("common.loot_tables.loot_table_boss_cultist-leader"),
0 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_weapon_uncommon",
),
1 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_weapon_uncommon",
),
2 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_armor_heavy",
),
3 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_weapon_rare",
),
4 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_boss_cultist-leader",
),
5 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_boss_cultist-leader",
),
_ => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_armor_misc",
),
};
let chosen = chosen.choose();
let entity =
match room.difficulty {
0 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_name("Outcast Leader".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(common::loadout_builder::LoadoutConfig::Outcast)
.with_scale(2.0)
.with_main_tool(
comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.worn_iron_axe-0",
1 => "common.items.weapons.sword.zweihander_sword_0",
2 => "common.items.weapons.sword.zweihander_sword_0",
3 => "common.items.weapons.hammer.worn_iron_hammer-0",
4 => "common.items.weapons.staff.bone_staff",
_ => "common.items.weapons.bow.wood_shortbow-1",
},
),
),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Tarasque,
)))
.with_name("Tarasque".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
let entity = match room.difficulty {
0 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(
comp::humanoid::Body::random(),
))
.with_name("Outcast Leader".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(
common::loadout_builder::LoadoutConfig::Outcast,
)
.with_scale(2.0)
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.worn_iron_axe-0",
1 => {
"common.items.weapons.sword.zweihander_sword_0"
},
2 => {
"common.items.weapons.sword.zweihander_sword_0"
},
3 => {
"common.items.weapons.hammer.worn_iron_hammer-0"
},
4 => "common.items.weapons.staff.bone_staff",
_ => "common.items.weapons.bow.wood_shortbow-1",
},
)),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Tarasque,
),
))
.with_name("Tarasque".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
1 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Theropod(comp::theropod::Body::random_with(
dynamic_rng,
&comp::theropod::Species::Odonto,
)))
.with_name("Odonto".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
1 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Theropod(
comp::theropod::Body::random_with(
dynamic_rng,
&comp::theropod::Species::Odonto,
),
))
.with_name("Odonto".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
2 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_name("Bandit Captain".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(common::loadout_builder::LoadoutConfig::Bandit)
.with_scale(2.0)
.with_main_tool(
comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.steel_axe-0",
1 => "common.items.weapons.sword.long_2h_orn-0",
2 => "common.items.weapons.sword.long_2h_orn-0",
3 => "common.items.weapons.hammer.cobalt_hammer-0",
4 => "common.items.weapons.staff.amethyst_staff",
_ => "common.items.weapons.bow.horn_longbow-0",
},
),
)
; 2],
3 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
2 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(
comp::humanoid::Body::random()
))
.with_name("Bandit Captain".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(common::loadout_builder::LoadoutConfig::Bandit)
.with_scale(2.0)
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.steel_axe-0",
1 => "common.items.weapons.sword.long_2h_orn-0",
2 => "common.items.weapons.sword.long_2h_orn-0",
3 => "common.items.weapons.hammer.cobalt_hammer-0",
4 => "common.items.weapons.staff.amethyst_staff",
_ => "common.items.weapons.bow.horn_longbow-0",
},
),);
2
],
3 => {
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_name("Cultist Acolyte".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
@ -811,46 +834,57 @@ impl Floor {
},
),
)
; 2],
4 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Golem(comp::golem::Body::random_with(
dynamic_rng,
&comp::golem::Species::StoneGolem,
)))
.with_name("Stonework Defender".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
; 2]
},
4 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Golem(
comp::golem::Body::random_with(
dynamic_rng,
&comp::golem::Species::StoneGolem,
),
))
.with_name("Stonework Defender".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
5 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::BipedLarge(comp::biped_large::Body::random_with(
dynamic_rng,
&comp::biped_large::Species::Mindflayer,
)))
.with_name("Mindflayer".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
_ =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedSmall(comp::quadruped_small::Body::random_with(
dynamic_rng,
&comp::quadruped_small::Species::Sheep,
))),
5 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::BipedLarge(
comp::biped_large::Body::random_with(
dynamic_rng,
&comp::biped_large::Species::Mindflayer,
),
))
.with_name("Mindflayer".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
_ => {
vec![EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(
comp::Body::QuadrupedSmall(
comp::quadruped_small::Body::random_with(
dynamic_rng,
&comp::quadruped_small::Species::Sheep,
),
),
)]
},
};
for entity in entity {
supplement.add_entity(entity.with_level(
dynamic_rng
.gen_range(
(room.difficulty as f32).powf(1.25) + 3.0,
(room.difficulty as f32).powf(1.5) + 4.0,
)
.round() as u32
* 5,
)
.with_alignment(comp::Alignment::Enemy),
);
supplement.add_entity(
entity
.with_level(
dynamic_rng
.gen_range(
(room.difficulty as f32).powf(1.25) + 3.0,
(room.difficulty as f32).powf(1.5) + 4.0,
)
.round()
as u32
* 5,
)
.with_alignment(comp::Alignment::Enemy),
);
}
}
}
@ -868,123 +902,161 @@ impl Floor {
let miniboss_spawn_tile =
miniboss_spawn_tile + if miniboss_tile_is_pillar { 1 } else { 0 };
if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d {
let chosen = match room.difficulty {
0 => Lottery::<String>::load_expect("common.loot_tables.loot_table_animal_parts"),
1 => Lottery::<String>::load_expect("common.loot_tables.loot_table_animal_parts"),
2 => Lottery::<String>::load_expect("common.loot_tables.loot_table_animal_parts"),
3 => Lottery::<String>::load_expect("common.loot_tables.loot_table_weapon_rare"),
4 => Lottery::<String>::load_expect("common.loot_tables.loot_table_weapon_rare"),
5 => Lottery::<String>::load_expect("common.loot_tables.loot_table_boss_cultist-leader"),
_ => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_armor_misc",
),
};
let chosen = chosen.choose();
let entity =
match room.difficulty {
0 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Bonerattler,
)))
.with_name("Bonerattler".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
1 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Bonerattler,
)))
.with_name("Bonerattler".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
; 3],
2 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Tarasque,
)))
.with_name("Tarasque".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
3 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_name("Animal Trainer".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte)
.with_scale(2.0)
.with_main_tool(
comp::Item::new_from_asset_expect(
if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d {
let chosen = match room.difficulty {
0 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_animal_parts",
),
1 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_animal_parts",
),
2 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_animal_parts",
),
3 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_weapon_rare",
),
4 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_weapon_rare",
),
5 => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_boss_cultist-leader",
),
_ => Lottery::<String>::load_expect(
"common.loot_tables.loot_table_armor_misc",
),
};
let chosen = chosen.choose();
let entity = match room.difficulty {
0 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Bonerattler,
),
))
.with_name("Bonerattler".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
1 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Bonerattler,
)
))
.with_name("Bonerattler".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(
chosen
));
3
],
2 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Tarasque,
),
))
.with_name("Tarasque".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
3 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(
comp::humanoid::Body::random(),
))
.with_name("Animal Trainer".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
.with_config(
common::loadout_builder::LoadoutConfig::CultistAcolyte,
)
.with_scale(2.0)
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 6) {
0 => "common.items.weapons.axe.malachite_axe-0",
1 => "common.items.weapons.sword.cultist_purp_2h-0",
2 => "common.items.weapons.sword.cultist_purp_2h-0",
3 => "common.items.weapons.hammer.cultist_purp_2h-0",
3 => {
"common.items.weapons.hammer.cultist_purp_2h-0"
},
4 => "common.items.weapons.staff.cultist_staff",
_ => "common.items.weapons.bow.horn_longbow-0",
},
)),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Wolf,
),
))
.with_name("Tamed Wolf".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(
comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Wolf,
),
))
.with_name("Tamed Wolf".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
4 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::BipedLarge(
comp::biped_large::Body::random_with(
dynamic_rng,
&comp::biped_large::Species::Dullahan,
),
))
.with_name("Dullahan Guard".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
5 => vec![
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Golem(
comp::golem::Body::random_with(
dynamic_rng,
&comp::golem::Species::StoneGolem,
),
))
.with_name("Stonework Defender".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
_ => {
vec![EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(
comp::Body::QuadrupedSmall(
comp::quadruped_small::Body::random_with(
dynamic_rng,
&comp::quadruped_small::Species::Sheep,
),
),
),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Wolf,
)))
.with_name("Tamed Wolf".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random_with(
dynamic_rng,
&comp::quadruped_medium::Species::Wolf,
)))
.with_name("Tamed Wolf".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
4 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::BipedLarge(comp::biped_large::Body::random_with(
dynamic_rng,
&comp::biped_large::Species::Dullahan,
)))
.with_name("Dullahan Guard".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
5 =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Golem(comp::golem::Body::random_with(
dynamic_rng,
&comp::golem::Species::StoneGolem,
)))
.with_name("Stonework Defender".to_string())
.with_loot_drop(comp::Item::new_from_asset_expect(chosen)),
],
_ =>
vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::QuadrupedSmall(comp::quadruped_small::Body::random_with(
dynamic_rng,
&comp::quadruped_small::Species::Sheep,
))),
],
};
)]
},
};
for entity in entity {
supplement.add_entity(entity.with_level(
dynamic_rng
.gen_range(
(room.difficulty as f32).powf(1.25) + 3.0,
(room.difficulty as f32).powf(1.5) + 4.0,
for entity in entity {
supplement.add_entity(
entity
.with_level(
dynamic_rng
.gen_range(
(room.difficulty as f32).powf(1.25) + 3.0,
(room.difficulty as f32).powf(1.5) + 4.0,
)
.round()
as u32
* 5,
)
.round() as u32
* 5,
)
.with_alignment(comp::Alignment::Enemy),
.with_alignment(comp::Alignment::Enemy),
);
}
}
}
}
}
}

View File

@ -1,8 +1,8 @@
mod block_mask;
mod castle;
mod dungeon;
mod namegen;
pub mod economy;
mod namegen;
mod settlement;
// Reexports

View File

@ -17,13 +17,14 @@ impl<'a, R: Rng> NameGen<'a, R> {
pub fn generate(self) -> String {
let cons = vec![
"d", "f", "ph", "r", "st", "t", "s", "p", "sh", "th", "br", "tr", "m", "k", "st", "w", "y",
"cr", "fr", "dr", "pl", "wr", "sn", "g", "qu", "l",
"d", "f", "ph", "r", "st", "t", "s", "p", "sh", "th", "br", "tr", "m", "k", "st", "w",
"y", "cr", "fr", "dr", "pl", "wr", "sn", "g", "qu", "l",
];
let mut start = cons.clone();
start.extend(vec![
"cr", "thr", "str", "br", "iv", "est", "ost", "ing", "kr", "in", "on", "tr", "tw", "wh",
"eld", "ar", "or", "ear", "irr", "mi", "en", "ed", "et", "ow", "fr", "shr", "wr", "gr", "pr",
"cr", "thr", "str", "br", "iv", "est", "ost", "ing", "kr", "in", "on", "tr", "tw",
"wh", "eld", "ar", "or", "ear", "irr", "mi", "en", "ed", "et", "ow", "fr", "shr", "wr",
"gr", "pr",
]);
let mut middle = cons.clone();
middle.extend(vec!["tt"]);
@ -43,14 +44,9 @@ impl<'a, R: Rng> NameGen<'a, R> {
}
name += end.choose(self.rng).unwrap();
name
.chars()
name.chars()
.enumerate()
.map(|(i, c)| if i == 0 {
c.to_ascii_uppercase()
} else {
c
})
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
.collect()
}
}

View File

@ -9,8 +9,8 @@ use super::SpawnRules;
use crate::{
column::ColumnSample,
sim::WorldSim,
util::{RandomField, Sampler, StructureGen2d},
site::namegen::NameGen,
util::{RandomField, Sampler, StructureGen2d},
IndexRef,
};
use common::{
@ -188,9 +188,7 @@ impl Settlement {
this
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name(&self) -> &str { &self.name }
pub fn get_origin(&self) -> Vec2<i32> { self.origin }
@ -921,14 +919,16 @@ impl Settlement {
.do_if(is_dummy, |e| e.with_name("Training Dummy"))
.do_if(is_human && dynamic_rng.gen(), |entity| {
match dynamic_rng.gen_range(0, 5) {
0 => entity.with_main_tool(Item::new_from_asset_expect(
"common.items.weapons.sword.greatsword_2h_simple-0",
))
.with_name("Guard")
.with_level(dynamic_rng.gen_range(10, 15))
.with_config(common::loadout_builder::LoadoutConfig::Guard),
_ => entity.with_main_tool(Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 7) {
0 => entity
.with_main_tool(Item::new_from_asset_expect(
"common.items.weapons.sword.greatsword_2h_simple-0",
))
.with_name("Guard")
.with_level(dynamic_rng.gen_range(10, 15))
.with_config(common::loadout_builder::LoadoutConfig::Guard),
_ => entity
.with_main_tool(Item::new_from_asset_expect(
match dynamic_rng.gen_range(0, 7) {
0 => "common.items.npc_weapons.tool.broom",
1 => "common.items.npc_weapons.tool.hoe",
2 => "common.items.npc_weapons.tool.pickaxe",
@ -938,9 +938,9 @@ impl Settlement {
_ => "common.items.npc_weapons.tool.shovel-1",
//_ => "common.items.npc_weapons.bow.starter_bow", TODO: Re-Add this when we have a better way of distributing npc_weapons here
},
))
.with_config(common::loadout_builder::LoadoutConfig::Villager)
}
))
.with_config(common::loadout_builder::LoadoutConfig::Villager),
}
});
supplement.add_entity(entity);