mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fixed sprite AO, overhauled entity spawning, better enemy spawning in dungeons, made agents more skilled at combat
This commit is contained in:
parent
d0641ecbe3
commit
6448c17110
@ -5,6 +5,7 @@
|
||||
in vec3 f_pos;
|
||||
flat in vec3 f_norm;
|
||||
in vec3 f_col;
|
||||
in float f_ao;
|
||||
in float f_light;
|
||||
|
||||
out vec4 tgt_color;
|
||||
@ -24,6 +25,9 @@ void main() {
|
||||
vec3 point_light = light_at(f_pos, f_norm);
|
||||
light += point_light;
|
||||
diffuse_light += point_light;
|
||||
float ao = pow(f_ao, 0.5) * 0.85 + 0.15;
|
||||
ambient_light *= ao;
|
||||
diffuse_light *= ao;
|
||||
vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
|
@ -6,6 +6,7 @@
|
||||
in vec3 v_pos;
|
||||
in vec3 v_norm;
|
||||
in vec3 v_col;
|
||||
in float v_ao;
|
||||
in vec4 inst_mat0;
|
||||
in vec4 inst_mat1;
|
||||
in vec4 inst_mat2;
|
||||
@ -16,6 +17,7 @@ in float inst_wind_sway;
|
||||
out vec3 f_pos;
|
||||
flat out vec3 f_norm;
|
||||
out vec3 f_col;
|
||||
out float f_ao;
|
||||
out float f_light;
|
||||
|
||||
const float SCALE = 1.0 / 11.0;
|
||||
@ -41,6 +43,7 @@ void main() {
|
||||
f_norm = (inst_mat * vec4(v_norm, 0)).xyz;
|
||||
|
||||
f_col = srgb_to_linear(v_col) * srgb_to_linear(inst_col);
|
||||
f_ao = v_ao;
|
||||
|
||||
// Select glowing
|
||||
if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {
|
||||
|
@ -61,6 +61,7 @@ pub enum Activity {
|
||||
chaser: Chaser,
|
||||
time: f64,
|
||||
been_close: bool,
|
||||
powerup: f32,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,21 @@
|
||||
use vek::*;
|
||||
use crate::comp::{Alignment};
|
||||
use crate::{
|
||||
comp::{self, Alignment, Body, Item, humanoid},
|
||||
npc::{self, NPC_NAMES},
|
||||
};
|
||||
|
||||
pub enum EntityTemplate {
|
||||
Traveller,
|
||||
}
|
||||
|
||||
pub struct EntityInfo {
|
||||
pub pos: Vec3<f32>,
|
||||
pub is_waypoint: bool, // Edge case, overrides everything else
|
||||
pub is_giant: bool,
|
||||
pub alignment: Alignment,
|
||||
pub body: Body,
|
||||
pub name: Option<String>,
|
||||
pub main_tool: Option<Item>,
|
||||
}
|
||||
|
||||
impl EntityInfo {
|
||||
@ -15,6 +25,9 @@ impl EntityInfo {
|
||||
is_waypoint: false,
|
||||
is_giant: false,
|
||||
alignment: Alignment::Wild,
|
||||
body: Body::Humanoid(humanoid::Body::random()),
|
||||
name: None,
|
||||
main_tool: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +52,37 @@ impl EntityInfo {
|
||||
self.alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_body(mut self, body: Body) -> Self {
|
||||
self.body = body;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_name(mut self, name: String) -> Self {
|
||||
self.name = Some(name);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_main_tool(mut self, main_tool: Item) -> Self {
|
||||
self.main_tool = Some(main_tool);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_automatic_name(mut self) -> Self {
|
||||
self.name = match &self.body {
|
||||
Body::Humanoid(body) => Some(get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
Body::QuadrupedMedium(body) => Some(get_npc_name(&NPC_NAMES.quadruped_medium, body.species)),
|
||||
Body::BirdMedium(body) => Some(get_npc_name(&NPC_NAMES.bird_medium, body.species)),
|
||||
Body::Critter(body) => Some(get_npc_name(&NPC_NAMES.critter, body.species)),
|
||||
Body::QuadrupedSmall(body) => Some(get_npc_name(&NPC_NAMES.quadruped_small, body.species)),
|
||||
_ => None,
|
||||
}.map(|s| if self.is_giant {
|
||||
format!("Giant {}", s)
|
||||
} else {
|
||||
s.to_string()
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -49,3 +93,14 @@ pub struct ChunkSupplement {
|
||||
impl ChunkSupplement {
|
||||
pub fn add_entity(&mut self, entity: EntityInfo) { self.entities.push(entity); }
|
||||
}
|
||||
|
||||
fn get_npc_name<
|
||||
'a,
|
||||
Species,
|
||||
SpeciesData: for<'b> core::ops::Index<&'b Species, Output = npc::SpeciesNames>,
|
||||
>(
|
||||
body_data: &'a comp::BodyData<npc::BodyNames, SpeciesData>,
|
||||
species: Species,
|
||||
) -> &'a str {
|
||||
&body_data.species[&species].generic
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
comp::{self, agent::Activity, Agent, Alignment, Controller, MountState, Ori, Scale, Pos, Stats},
|
||||
comp::{self, agent::Activity, Agent, Alignment, Controller, MountState, Ori, Scale, Pos, Stats, Loadout, item::{ItemKind, tool::ToolKind}, CharacterState},
|
||||
path::Chaser,
|
||||
state::Time,
|
||||
state::{Time, DeltaTime},
|
||||
sync::UidAllocator,
|
||||
terrain::TerrainGrid,
|
||||
util::Dir,
|
||||
@ -20,11 +20,14 @@ impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, DeltaTime>,
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Loadout>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
WriteStorage<'a, Agent>,
|
||||
@ -37,11 +40,14 @@ impl<'a> System<'a> for Sys {
|
||||
(
|
||||
uid_allocator,
|
||||
time,
|
||||
dt,
|
||||
entities,
|
||||
positions,
|
||||
orientations,
|
||||
scales,
|
||||
stats,
|
||||
loadouts,
|
||||
character_states,
|
||||
terrain,
|
||||
alignments,
|
||||
mut agents,
|
||||
@ -49,11 +55,13 @@ impl<'a> System<'a> for Sys {
|
||||
mount_states,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
for (entity, pos, ori, alignment, agent, controller, mount_state) in (
|
||||
for (entity, pos, ori, alignment, loadout, character_state, agent, controller, mount_state) in (
|
||||
&entities,
|
||||
&positions,
|
||||
&orientations,
|
||||
alignments.maybe(),
|
||||
&loadouts,
|
||||
&character_states,
|
||||
&mut agents,
|
||||
&mut controllers,
|
||||
mount_states.maybe(),
|
||||
@ -100,7 +108,7 @@ impl<'a> System<'a> for Sys {
|
||||
thread_rng().gen::<f32>() - 0.5,
|
||||
thread_rng().gen::<f32>() - 0.5,
|
||||
) * 0.1
|
||||
- *bearing * 0.01
|
||||
- *bearing * 0.003
|
||||
- if let Some(patrol_origin) = agent.patrol_origin {
|
||||
Vec2::<f32>::from(pos.0 - patrol_origin) * 0.0002
|
||||
} else {
|
||||
@ -165,8 +173,24 @@ impl<'a> System<'a> for Sys {
|
||||
target,
|
||||
chaser,
|
||||
been_close,
|
||||
powerup,
|
||||
..
|
||||
} => {
|
||||
enum Tactic {
|
||||
Melee, // Attack: primary/secondary
|
||||
RangedPowerup,
|
||||
RangedConstant,
|
||||
}
|
||||
|
||||
let tactic = match loadout.active_item
|
||||
.as_ref()
|
||||
.and_then(|ic| if let ItemKind::Tool(tool) = &ic.item.kind { Some(&tool.kind) } else { None})
|
||||
{
|
||||
Some(ToolKind::Bow(_)) => Tactic::RangedPowerup,
|
||||
Some(ToolKind::Staff(_)) => Tactic::RangedConstant,
|
||||
_ => Tactic::Melee,
|
||||
};
|
||||
|
||||
if let (Some(tgt_pos), Some(tgt_stats), tgt_alignment) = (
|
||||
positions.get(*target),
|
||||
stats.get(*target),
|
||||
@ -196,10 +220,35 @@ impl<'a> System<'a> for Sys {
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::unit_y())
|
||||
* 0.7;
|
||||
inputs.primary.set_state(true);
|
||||
|
||||
if let Tactic::Melee = tactic {
|
||||
inputs.primary.set_state(true);
|
||||
} else if let Tactic::RangedPowerup = tactic {
|
||||
inputs.primary.set_state(true);
|
||||
} else {
|
||||
inputs.move_dir = -Vec2::from(tgt_pos.0 - pos.0)
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::unit_y());
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powf(2.0)
|
||||
|| (dist_sqrd < SIGHT_DIST.powf(2.0) && !*been_close)
|
||||
{
|
||||
if let Tactic::RangedPowerup = tactic {
|
||||
if *powerup > 2.0 {
|
||||
inputs.primary.set_state(false);
|
||||
*powerup = 0.0;
|
||||
} else {
|
||||
inputs.primary.set_state(true);
|
||||
*powerup += dt.0;
|
||||
}
|
||||
} else if let Tactic::RangedConstant = tactic {
|
||||
if !character_state.is_wield() {
|
||||
inputs.primary.set_state(true);
|
||||
}
|
||||
|
||||
inputs.secondary.set_state(true);
|
||||
}
|
||||
|
||||
if dist_sqrd < MAX_CHASE_DIST.powf(2.0) {
|
||||
*been_close = true;
|
||||
}
|
||||
@ -257,6 +306,7 @@ impl<'a> System<'a> for Sys {
|
||||
chaser: Chaser::default(),
|
||||
time: time.0,
|
||||
been_close: false,
|
||||
powerup: 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -280,6 +330,7 @@ impl<'a> System<'a> for Sys {
|
||||
chaser: Chaser::default(),
|
||||
time: time.0,
|
||||
been_close: false,
|
||||
powerup: 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -310,6 +361,7 @@ impl<'a> System<'a> for Sys {
|
||||
chaser: Chaser::default(),
|
||||
time: time.0,
|
||||
been_close: false,
|
||||
powerup: 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
event::{EventBus, ServerEvent},
|
||||
state::DeltaTime,
|
||||
sync::Uid,
|
||||
terrain::{Block, TerrainGrid},
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
@ -110,7 +110,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Neighbouring blocks iterator
|
||||
let near_iter = (-hdist..hdist + 1)
|
||||
.map(move |i| {
|
||||
(-hdist..hdist + 1).map(move |j| (0..vdist + 1).map(move |k| (i, j, k)))
|
||||
(-hdist..hdist + 1).map(move |j| (1 - BlockKind::MAX_HEIGHT.ceil() as i32..vdist + 1).map(move |k| (i, j, k)))
|
||||
})
|
||||
.flatten()
|
||||
.flatten();
|
||||
@ -154,14 +154,14 @@ impl<'a> System<'a> for Sys {
|
||||
for (i, j, k) in near_iter {
|
||||
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k);
|
||||
|
||||
if terrain.get(block_pos).map(hit).unwrap_or(false) {
|
||||
if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) {
|
||||
let player_aabb = Aabb {
|
||||
min: pos + Vec3::new(-player_rad, -player_rad, 0.0),
|
||||
max: pos + Vec3::new(player_rad, player_rad, player_height),
|
||||
};
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + 1.0,
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.get_height()),
|
||||
};
|
||||
|
||||
if player_aabb.collides_with_aabb(block_aabb) {
|
||||
@ -204,22 +204,24 @@ impl<'a> System<'a> for Sys {
|
||||
.clone()
|
||||
// Calculate the block's position in world space
|
||||
.map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k))
|
||||
// Calculate the AABB of the block
|
||||
.map(|block_pos| {
|
||||
(
|
||||
block_pos,
|
||||
Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + 1.0,
|
||||
},
|
||||
)
|
||||
})
|
||||
// Make sure the block is actually solid
|
||||
.filter(|(block_pos, _)| {
|
||||
terrain
|
||||
.get(*block_pos)
|
||||
.map(|vox| vox.is_solid())
|
||||
.unwrap_or(false)
|
||||
.filter_map(|block_pos| {
|
||||
if let Some(block) = terrain
|
||||
.get(block_pos)
|
||||
.ok()
|
||||
.filter(|block| block.is_solid())
|
||||
{
|
||||
// Calculate block AABB
|
||||
Some((
|
||||
block_pos,
|
||||
Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.get_height()),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// Determine whether the block's AABB collides with the player's AABB
|
||||
.filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb))
|
||||
|
@ -205,6 +205,18 @@ impl BlockKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_HEIGHT: f32 = 3.0;
|
||||
|
||||
// TODO: Integrate this into `is_solid` by returning an `Option<f32>`
|
||||
pub fn get_height(&self) -> f32 {
|
||||
// Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not properly
|
||||
// detect it!
|
||||
match self {
|
||||
BlockKind::LargeCactus => 2.5,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::BlueFlower => false,
|
||||
|
@ -116,88 +116,92 @@ impl<'a> System<'a> for Sys {
|
||||
&body_data.species[&species].generic
|
||||
}
|
||||
|
||||
const SPAWN_NPCS: &'static [fn() -> (
|
||||
String,
|
||||
comp::Body,
|
||||
Option<comp::Item>,
|
||||
comp::Alignment,
|
||||
)] = &[
|
||||
(|| {
|
||||
let body = comp::humanoid::Body::random();
|
||||
(
|
||||
format!(
|
||||
"{} Traveler",
|
||||
get_npc_name(&NPC_NAMES.humanoid, body.race)
|
||||
),
|
||||
comp::Body::Humanoid(body),
|
||||
Some(assets::load_expect_cloned(
|
||||
"common.items.weapons.starter_axe",
|
||||
)),
|
||||
comp::Alignment::Npc,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::humanoid::Body::random();
|
||||
(
|
||||
format!("{} Bandit", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
comp::Body::Humanoid(body),
|
||||
Some(assets::load_expect_cloned(
|
||||
"common.items.weapons.short_sword_0",
|
||||
)),
|
||||
comp::Alignment::Enemy,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::quadruped_medium::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.quadruped_medium, body.species).into(),
|
||||
comp::Body::QuadrupedMedium(body),
|
||||
None,
|
||||
comp::Alignment::Enemy,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::bird_medium::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.bird_medium, body.species).into(),
|
||||
comp::Body::BirdMedium(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::critter::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.critter, body.species).into(),
|
||||
comp::Body::Critter(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}) as _,
|
||||
(|| {
|
||||
let body = comp::quadruped_small::Body::random();
|
||||
(
|
||||
get_npc_name(&NPC_NAMES.quadruped_small, body.species).into(),
|
||||
comp::Body::QuadrupedSmall(body),
|
||||
None,
|
||||
comp::Alignment::Wild,
|
||||
)
|
||||
}),
|
||||
];
|
||||
let (name, mut body, main, mut alignment) = SPAWN_NPCS
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("SPAWN_NPCS is nonempty")(
|
||||
);
|
||||
// const SPAWN_NPCS: &'static [fn() -> (
|
||||
// String,
|
||||
// comp::Body,
|
||||
// Option<comp::Item>,
|
||||
// comp::Alignment,
|
||||
// )] = &[
|
||||
// (|| {
|
||||
// let body = comp::humanoid::Body::random();
|
||||
// (
|
||||
// format!(
|
||||
// "{} Traveler",
|
||||
// get_npc_name(&NPC_NAMES.humanoid, body.race)
|
||||
// ),
|
||||
// comp::Body::Humanoid(body),
|
||||
// Some(assets::load_expect_cloned(
|
||||
// "common.items.weapons.starter_axe",
|
||||
// )),
|
||||
// comp::Alignment::Npc,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::humanoid::Body::random();
|
||||
// (
|
||||
// format!("{} Bandit", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
// comp::Body::Humanoid(body),
|
||||
// Some(assets::load_expect_cloned(
|
||||
// "common.items.weapons.short_sword_0",
|
||||
// )),
|
||||
// comp::Alignment::Enemy,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::quadruped_medium::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.quadruped_medium, body.species).into(),
|
||||
// comp::Body::QuadrupedMedium(body),
|
||||
// None,
|
||||
// comp::Alignment::Enemy,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::bird_medium::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.bird_medium, body.species).into(),
|
||||
// comp::Body::BirdMedium(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::critter::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.critter, body.species).into(),
|
||||
// comp::Body::Critter(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }) as _,
|
||||
// (|| {
|
||||
// let body = comp::quadruped_small::Body::random();
|
||||
// (
|
||||
// get_npc_name(&NPC_NAMES.quadruped_small, body.species).into(),
|
||||
// comp::Body::QuadrupedSmall(body),
|
||||
// None,
|
||||
// comp::Alignment::Wild,
|
||||
// )
|
||||
// }),
|
||||
// ];
|
||||
// let (name, mut body, main, mut alignment) = SPAWN_NPCS
|
||||
// .choose(&mut rand::thread_rng())
|
||||
// .expect("SPAWN_NPCS is nonempty")(
|
||||
// );
|
||||
|
||||
let mut body = entity.body;
|
||||
let mut name = entity.name.unwrap_or("Unnamed".to_string());
|
||||
let alignment = entity.alignment;
|
||||
let main_tool = entity.main_tool;
|
||||
|
||||
let mut stats = comp::Stats::new(name, body);
|
||||
|
||||
let alignment = entity.alignment;
|
||||
|
||||
let active_item =
|
||||
if let Some(item::ItemKind::Tool(tool)) = main.as_ref().map(|i| &i.kind) {
|
||||
if let Some(item::ItemKind::Tool(tool)) = main_tool.as_ref().map(|i| &i.kind) {
|
||||
let mut abilities = tool.get_abilities();
|
||||
let mut ability_drain = abilities.drain(..);
|
||||
|
||||
main.map(|item| comp::ItemConfig {
|
||||
main_tool.map(|item| comp::ItemConfig {
|
||||
item,
|
||||
ability1: ability_drain.next(),
|
||||
ability2: ability_drain.next(),
|
||||
|
@ -88,9 +88,8 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
|
||||
SpriteVertex::new(
|
||||
origin,
|
||||
norm,
|
||||
linear_to_srgb(
|
||||
srgb_to_linear(col) * light.min(ao.powf(0.5) * 0.75 + 0.25),
|
||||
),
|
||||
linear_to_srgb(srgb_to_linear(col) * light),
|
||||
ao,
|
||||
)
|
||||
},
|
||||
&{
|
||||
|
@ -14,6 +14,7 @@ gfx_defines! {
|
||||
pos: [f32; 3] = "v_pos",
|
||||
norm: [f32; 3] = "v_norm",
|
||||
col: [f32; 3] = "v_col",
|
||||
ao: f32 = "v_ao",
|
||||
}
|
||||
|
||||
vertex Instance {
|
||||
@ -41,11 +42,12 @@ gfx_defines! {
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>) -> Self {
|
||||
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>, ao: f32) -> Self {
|
||||
Self {
|
||||
pos: pos.into_array(),
|
||||
col: col.into_array(),
|
||||
norm: norm.into_array(),
|
||||
ao,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
comp::{Alignment},
|
||||
comp::{self, humanoid, quadruped_medium, bird_medium, critter, quadruped_small},
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
@ -150,25 +150,29 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
let sample_get = |offs| {
|
||||
zcache_grid
|
||||
.get(grid_border + offs)
|
||||
.map(Option::as_ref)
|
||||
.flatten()
|
||||
.map(|zc| &zc.sample)
|
||||
};
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// Apply site generation
|
||||
sim_chunk.sites.iter().for_each(|site| {
|
||||
site.apply_to(
|
||||
chunk_wpos2d,
|
||||
|offs| {
|
||||
zcache_grid
|
||||
.get(grid_border + offs)
|
||||
.map(Option::as_ref)
|
||||
.flatten()
|
||||
.map(|zc| &zc.sample)
|
||||
},
|
||||
sample_get,
|
||||
&mut chunk,
|
||||
)
|
||||
});
|
||||
|
||||
let gen_entity_pos = || {
|
||||
let lpos2d = TerrainChunkSize::RECT_SIZE
|
||||
.map(|sz| rand::thread_rng().gen::<u32>().rem_euclid(sz));
|
||||
let mut lpos = Vec3::new(lpos2d.x as i32, lpos2d.y as i32, 0);
|
||||
.map(|sz| rand::thread_rng().gen::<u32>().rem_euclid(sz) as i32);
|
||||
let mut lpos = Vec3::new(lpos2d.x, lpos2d.y, sample_get(lpos2d).map(|s| s.alt as i32 - 32).unwrap_or(0));
|
||||
|
||||
while chunk.get(lpos).map(|vox| !vox.is_empty()).unwrap_or(false) {
|
||||
lpos.z += 1;
|
||||
@ -177,8 +181,6 @@ impl World {
|
||||
(Vec3::from(chunk_wpos2d) + lpos).map(|e: i32| e as f32) + 0.5
|
||||
};
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
const SPAWN_RATE: f32 = 0.1;
|
||||
const BOSS_RATE: f32 = 0.03;
|
||||
let mut supplement = ChunkSupplement {
|
||||
@ -187,8 +189,15 @@ impl World {
|
||||
&& !sim_chunk.is_underwater()
|
||||
{
|
||||
let entity = EntityInfo::at(gen_entity_pos())
|
||||
.with_alignment(Alignment::Wild)
|
||||
.do_if(rng.gen(), |e| e.into_giant());
|
||||
.with_alignment(comp::Alignment::Wild)
|
||||
.do_if(rng.gen_range(0, 8) == 0, |e| e.into_giant())
|
||||
.with_body(match rng.gen_range(0, 4) {
|
||||
0 => comp::Body::QuadrupedMedium(quadruped_medium::Body::random()),
|
||||
1 => comp::Body::BirdMedium(bird_medium::Body::random()),
|
||||
2 => comp::Body::Critter(critter::Body::random()),
|
||||
_ => comp::Body::QuadrupedSmall(quadruped_small::Body::random()),
|
||||
})
|
||||
.with_automatic_name();
|
||||
|
||||
vec![entity]
|
||||
} else {
|
||||
@ -204,14 +213,9 @@ impl World {
|
||||
// Apply site supplementary information
|
||||
sim_chunk.sites.iter().for_each(|site| {
|
||||
site.apply_supplement(
|
||||
&mut rng,
|
||||
chunk_wpos2d,
|
||||
|offs| {
|
||||
zcache_grid
|
||||
.get(grid_border + offs)
|
||||
.map(Option::as_ref)
|
||||
.flatten()
|
||||
.map(|zc| &zc.sample)
|
||||
},
|
||||
sample_get,
|
||||
&mut supplement,
|
||||
)
|
||||
});
|
||||
|
@ -6,9 +6,10 @@ use crate::{
|
||||
util::{attempt, Grid, RandomField, Sampler, StructureGen2d},
|
||||
};
|
||||
use common::{
|
||||
astar::Astar,
|
||||
comp::Alignment,
|
||||
assets,
|
||||
comp,
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
astar::Astar,
|
||||
path::Path,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
@ -118,6 +119,7 @@ impl Dungeon {
|
||||
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
@ -132,7 +134,7 @@ impl Dungeon {
|
||||
for floor in &self.floors {
|
||||
z -= floor.total_depth();
|
||||
let origin = Vec3::new(self.origin.x, self.origin.y, z);
|
||||
floor.apply_supplement(area, origin, supplement);
|
||||
floor.apply_supplement(rng, area, origin, supplement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,6 +184,7 @@ pub struct Room {
|
||||
seed: u32,
|
||||
enemies: bool,
|
||||
loot_density: f32,
|
||||
enemy_density: f32,
|
||||
area: Rect<i32, i32>,
|
||||
}
|
||||
|
||||
@ -242,10 +245,10 @@ impl Floor {
|
||||
}
|
||||
|
||||
fn create_rooms(&mut self, ctx: &mut GenCtx<impl Rng>, level: i32, n: usize) {
|
||||
let dim_limits = (3, 8);
|
||||
let dim_limits = (3, 6);
|
||||
|
||||
for _ in 0..n {
|
||||
let area = match attempt(30, || {
|
||||
let area = match attempt(64, || {
|
||||
let sz = Vec2::<i32>::zero().map(|_| ctx.rng.gen_range(dim_limits.0, dim_limits.1));
|
||||
let pos = FLOOR_SIZE.map2(sz, |floor_sz, room_sz| {
|
||||
ctx.rng.gen_range(0, floor_sz + 1 - room_sz)
|
||||
@ -269,7 +272,8 @@ impl Floor {
|
||||
let room = self.rooms.insert(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
enemies: ctx.rng.gen(),
|
||||
loot_density: level as f32 * 0.00025,
|
||||
loot_density: 0.00005 + level as f32 * 0.00015,
|
||||
enemy_density: 0.0005 + level as f32 * 0.00015,
|
||||
area,
|
||||
});
|
||||
|
||||
@ -332,6 +336,7 @@ impl Floor {
|
||||
|
||||
pub fn apply_supplement(
|
||||
&self,
|
||||
rng: &mut impl Rng,
|
||||
area: Aabr<i32>,
|
||||
origin: Vec3<i32>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
@ -354,17 +359,39 @@ impl Floor {
|
||||
let tile_pos = Vec2::new(x, y);
|
||||
if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) {
|
||||
let room = &self.rooms[*room];
|
||||
if room.enemies && tile_pos.x % 4 == 0 && tile_pos.y % 4 == 0 {
|
||||
// Bad
|
||||
let entity = EntityInfo::at(
|
||||
(origin
|
||||
+ Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE
|
||||
+ TILE_SIZE / 2)
|
||||
.map(|e| e as f32),
|
||||
)
|
||||
.into_giant()
|
||||
.with_alignment(Alignment::Enemy);
|
||||
supplement.add_entity(entity);
|
||||
|
||||
for x in 0..TILE_SIZE {
|
||||
for y in 0..TILE_SIZE {
|
||||
let pos = tile_pos * TILE_SIZE + Vec2::new(x, y);
|
||||
|
||||
if room.enemies && (pos.x + pos.y * TILE_SIZE * FLOOR_SIZE.x).rem_euclid(room.enemy_density.recip() as i32) == 0 {
|
||||
// Bad
|
||||
let entity = EntityInfo::at(
|
||||
(origin
|
||||
+ Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE
|
||||
+ TILE_SIZE / 2)
|
||||
.map(|e| e as f32)
|
||||
// Randomly displace them a little
|
||||
+ Vec3::<u32>::iota()
|
||||
.map(|e| (RandomField::new(room.seed.wrapping_add(10 + e)).get(Vec3::from(tile_pos)) % 32) as i32 - 16)
|
||||
.map(|e| e as f32 / 16.0),
|
||||
)
|
||||
.do_if(RandomField::new(room.seed.wrapping_add(1)).chance(Vec3::from(tile_pos), 0.2), |e| e.into_giant())
|
||||
.with_alignment(comp::Alignment::Enemy)
|
||||
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
|
||||
.with_automatic_name()
|
||||
.with_main_tool(assets::load_expect_cloned(match rng.gen_range(0, 6) {
|
||||
0 => "common.items.weapons.starter_axe",
|
||||
1 => "common.items.weapons.starter_sword",
|
||||
2 => "common.items.weapons.short_sword_0",
|
||||
3 => "common.items.weapons.hammer_1",
|
||||
4 => "common.items.weapons.starter_staff",
|
||||
_ => "common.items.weapons.starter_bow",
|
||||
}));
|
||||
|
||||
supplement.add_entity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use common::{
|
||||
terrain::Block,
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||
};
|
||||
use rand::Rng;
|
||||
use std::{fmt, sync::Arc};
|
||||
use vek::*;
|
||||
|
||||
@ -97,15 +98,16 @@ impl Site {
|
||||
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
) {
|
||||
match self {
|
||||
Site::Settlement(settlement) => {
|
||||
settlement.apply_supplement(wpos2d, get_column, supplement)
|
||||
settlement.apply_supplement(rng, wpos2d, get_column, supplement)
|
||||
},
|
||||
Site::Dungeon(dungeon) => dungeon.apply_supplement(wpos2d, get_column, supplement),
|
||||
Site::Dungeon(dungeon) => dungeon.apply_supplement(rng, wpos2d, get_column, supplement),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ use crate::{
|
||||
util::{Grid, RandomField, Sampler, StructureGen2d},
|
||||
};
|
||||
use common::{
|
||||
comp::{self, humanoid, quadruped_medium, bird_medium, critter, quadruped_small},
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
astar::Astar,
|
||||
generation::ChunkSupplement,
|
||||
path::Path,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
@ -105,6 +106,7 @@ impl Structure {
|
||||
}
|
||||
|
||||
pub struct Settlement {
|
||||
seed: u32,
|
||||
origin: Vec2<i32>,
|
||||
land: Land,
|
||||
farms: Store<Farm>,
|
||||
@ -130,6 +132,7 @@ impl Settlement {
|
||||
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
|
||||
let mut ctx = GenCtx { sim, rng };
|
||||
let mut this = Self {
|
||||
seed: ctx.rng.gen(),
|
||||
origin: wpos,
|
||||
land: Land::new(ctx.rng),
|
||||
farms: Store::default(),
|
||||
@ -480,8 +483,6 @@ impl Settlement {
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||
) {
|
||||
let rand_field = RandomField::new(0);
|
||||
|
||||
for y in 0..vol.size_xy().y as i32 {
|
||||
for x in 0..vol.size_xy().x as i32 {
|
||||
let offs = Vec2::new(x, y);
|
||||
@ -659,7 +660,7 @@ impl Settlement {
|
||||
let color = Lerp::lerp(
|
||||
Rgb::new(130i32, 100, 0),
|
||||
Rgb::new(90, 70, 50),
|
||||
(rand_field.get(wpos2d.into()) % 256) as f32 / 256.0,
|
||||
(RandomField::new(0).get(wpos2d.into()) % 256) as f32 / 256.0,
|
||||
)
|
||||
.map(|e| (e % 256) as u8);
|
||||
|
||||
@ -738,11 +739,69 @@ impl Settlement {
|
||||
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
) {
|
||||
// TODO
|
||||
for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 {
|
||||
for x in 0..TerrainChunkSize::RECT_SIZE.x as i32 {
|
||||
let offs = Vec2::new(x, y);
|
||||
|
||||
let wpos2d = wpos2d + offs;
|
||||
let rpos = wpos2d - self.origin;
|
||||
|
||||
// Sample terrain
|
||||
let col_sample = if let Some(col_sample) = get_column(offs) {
|
||||
col_sample
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let surface_z = col_sample.riverless_alt.floor() as i32;
|
||||
|
||||
let sample = self.land.get_at_block(rpos);
|
||||
|
||||
let entity_wpos = Vec3::new(wpos2d.x as f32, wpos2d.y as f32, col_sample.alt + 3.0);
|
||||
|
||||
if matches!(sample.plot, Some(Plot::Town)) &&
|
||||
RandomField::new(self.seed).chance(Vec3::from(wpos2d), 1.0 / (32.0 * 32.0))
|
||||
{
|
||||
let entity = EntityInfo::at(entity_wpos)
|
||||
.with_alignment(comp::Alignment::Npc)
|
||||
.with_body(match rng.gen_range(0, 4) {
|
||||
0 => {
|
||||
let species = match rng.gen_range(0, 3) {
|
||||
0 => quadruped_small::Species::Pig,
|
||||
1 => quadruped_small::Species::Sheep,
|
||||
_ => quadruped_small::Species::Cat,
|
||||
};
|
||||
|
||||
comp::Body::QuadrupedSmall(quadruped_small::Body::random_with(
|
||||
rng,
|
||||
&species,
|
||||
))
|
||||
},
|
||||
1 => {
|
||||
let species = match rng.gen_range(0, 4) {
|
||||
0 => bird_medium::Species::Duck,
|
||||
1 => bird_medium::Species::Chicken,
|
||||
2 => bird_medium::Species::Goose,
|
||||
_ => bird_medium::Species::Peacock,
|
||||
};
|
||||
|
||||
comp::Body::BirdMedium(bird_medium::Body::random_with(
|
||||
rng,
|
||||
&species,
|
||||
))
|
||||
},
|
||||
_ => comp::Body::Humanoid(humanoid::Body::random()),
|
||||
})
|
||||
.with_automatic_name();
|
||||
|
||||
supplement.add_entity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_color(&self, pos: Vec2<i32>) -> Option<Rgb<u8>> {
|
||||
|
Loading…
Reference in New Issue
Block a user