Turn giant trees into proper sites

This commit is contained in:
Joshua Barretto 2021-03-06 14:55:42 +00:00
parent b6ac4e46fb
commit db573f6b2d
13 changed files with 170 additions and 25 deletions

View File

@ -9,9 +9,11 @@
"hud.map.difficulty": "Difficulty",
"hud.map.towns": "Towns",
"hud.map.castles": "Castles",
"hud.map.dungeons": "Dungeons",
"hud.map.dungeons": "Dungeons",
"hud.map.caves": "Caves",
"hud.map.cave": "Cave",
"hud.map.trees": "Trees",
"hud.map.tree": "Tree",
"hud.map.town": "Town",
"hud.map.castle": "Castle",
"hud.map.dungeon": "Dungeon",

View File

@ -138,4 +138,5 @@ pub enum SiteKind {
Dungeon { difficulty: u32 },
Castle,
Cave,
Tree,
}

View File

@ -49,6 +49,12 @@ widget_ids! {
show_dungeons_img,
show_dungeons_box,
show_dungeons_text,
show_caves_img,
show_caves_box,
show_caves_text,
show_trees_img,
show_trees_box,
show_trees_text,
show_difficulty_img,
show_difficulty_box,
show_difficulty_text,
@ -57,9 +63,6 @@ widget_ids! {
drag_ico,
zoom_txt,
zoom_ico,
show_caves_img,
show_caves_box,
show_caves_text,
}
}
@ -117,6 +120,7 @@ pub enum Event {
ShowCastles(bool),
ShowDungeons(bool),
ShowCaves(bool),
ShowTrees(bool),
Close,
}
@ -143,6 +147,7 @@ impl<'a> Widget for Map<'a> {
let show_dungeons = self.global_state.settings.gameplay.map_show_dungeons;
let show_castles = self.global_state.settings.gameplay.map_show_castles;
let show_caves = self.global_state.settings.gameplay.map_show_caves;
let show_trees = self.global_state.settings.gameplay.map_show_trees;
let mut events = Vec::new();
let i18n = &self.localized_strings;
// Tooltips
@ -471,6 +476,40 @@ impl<'a> Widget for Map<'a> {
.graphics_for(state.ids.show_caves_box)
.color(TEXT_COLOR)
.set(state.ids.show_caves_text, ui);
// Trees
Image::new(self.imgs.mmap_site_tree)
.down_from(state.ids.show_caves_img, 10.0)
.w_h(20.0, 20.0)
.set(state.ids.show_trees_img, ui);
if Button::image(if show_trees {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox
})
.w_h(18.0, 18.0)
.hover_image(if show_trees {
self.imgs.checkbox_checked_mo
} else {
self.imgs.checkbox_mo
})
.press_image(if show_trees {
self.imgs.checkbox_checked
} else {
self.imgs.checkbox_press
})
.right_from(state.ids.show_trees_img, 10.0)
.set(state.ids.show_trees_box, ui)
.was_clicked()
{
events.push(Event::ShowTrees(!show_trees));
}
Text::new(i18n.get("hud.map.trees"))
.right_from(state.ids.show_trees_box, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.show_trees_box)
.color(TEXT_COLOR)
.set(state.ids.show_trees_text, ui);
// Map icons
if state.ids.mmap_site_icons.len() < self.client.sites().len() {
state.update(|state| {
@ -512,6 +551,7 @@ impl<'a> Widget for Map<'a> {
SiteKind::Dungeon { .. } => i18n.get("hud.map.dungeon"),
SiteKind::Castle => i18n.get("hud.map.castle"),
SiteKind::Cave => i18n.get("hud.map.cave"),
SiteKind::Tree => i18n.get("hud.map.tree"),
});
let (difficulty, desc) = match &site.kind {
SiteKind::Town => (0, i18n.get("hud.map.town").to_string()),
@ -522,6 +562,7 @@ impl<'a> Widget for Map<'a> {
),
SiteKind::Castle => (0, i18n.get("hud.map.castle").to_string()),
SiteKind::Cave => (0, i18n.get("hud.map.cave").to_string()),
SiteKind::Tree => (0, i18n.get("hud.map.tree").to_string()),
};
let site_btn = Button::image(match &site.kind {
SiteKind::Town => self.imgs.mmap_site_town,
@ -648,6 +689,11 @@ impl<'a> Widget for Map<'a> {
dif_img.set(state.ids.site_difs[i], ui)
}
},
SiteKind::Tree => {
if show_trees {
dif_img.set(state.ids.site_difs[i], ui)
}
},
}
}
}

View File

@ -325,6 +325,7 @@ impl<'a> Widget for MiniMap<'a> {
_ => Color::Rgba(1.0, 1.0, 1.0, 0.0),
},
SiteKind::Cave => Color::Rgba(1.0, 1.0, 1.0, 0.0),
SiteKind::Tree => Color::Rgba(1.0, 1.0, 1.0, 0.0),
}))
.parent(state.ids.grid)
.set(state.ids.mmap_site_icons_bgs[i], ui);
@ -333,6 +334,7 @@ impl<'a> Widget for MiniMap<'a> {
SiteKind::Dungeon { .. } => self.imgs.mmap_site_dungeon,
SiteKind::Castle => self.imgs.mmap_site_castle,
SiteKind::Cave => self.imgs.mmap_site_cave,
SiteKind::Tree => self.imgs.mmap_site_tree,
_ => self.imgs.mmap_site_excl,
})
.middle_of(state.ids.mmap_site_icons_bgs[i])

View File

@ -362,6 +362,7 @@ pub enum Event {
MapShowDungeons(bool),
MapShowCastles(bool),
MapShowCaves(bool),
MapShowTrees(bool),
AdjustWindowSize([u16; 2]),
ChangeFullscreenMode(FullScreenSettings),
ToggleParticlesEnabled(bool),
@ -2642,6 +2643,9 @@ impl Hud {
map::Event::ShowCaves(map_show_caves) => {
events.push(Event::MapShowCaves(map_show_caves));
},
map::Event::ShowTrees(map_show_trees) => {
events.push(Event::MapShowTrees(map_show_trees));
},
}
}
} else {

View File

@ -1266,6 +1266,10 @@ impl PlayState for SessionState {
global_state.settings.gameplay.map_show_caves = map_show_caves;
global_state.settings.save_to_file_warn();
},
HudEvent::MapShowTrees(map_show_trees) => {
global_state.settings.gameplay.map_show_trees = map_show_trees;
global_state.settings.save_to_file_warn();
},
HudEvent::ChangeGamma(new_gamma) => {
global_state.settings.graphics.gamma = new_gamma;
global_state.settings.save_to_file_warn();

View File

@ -463,6 +463,7 @@ pub struct GameplaySettings {
pub map_show_castles: bool,
pub loading_tips: bool,
pub map_show_caves: bool,
pub map_show_trees: bool,
pub minimap_show: bool,
pub minimap_face_north: bool,
}
@ -502,6 +503,7 @@ impl Default for GameplaySettings {
map_show_castles: true,
loading_tips: true,
map_show_caves: true,
map_show_trees: true,
minimap_show: true,
minimap_face_north: false,
}

View File

@ -6,7 +6,7 @@ use self::{Occupation::*, Stock::*};
use crate::{
config::CONFIG,
sim::WorldSim,
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite},
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite, Tree},
site2,
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
Index, Land,
@ -106,10 +106,11 @@ impl Civs {
for _ in 0..initial_civ_count * 3 {
attempt(5, || {
let (kind, size) = match ctx.rng.gen_range(0..8) {
0 => (SiteKind::Castle, 3),
1 => (SiteKind::Dungeon, 0),
_ => (SiteKind::Refactor, 5),
let (kind, size) = match ctx.rng.gen_range(0..16) {
0..=1 => (SiteKind::Castle, 3),
2..=7 => (SiteKind::Refactor, 6),
8 => (SiteKind::Tree, 4),
_ => (SiteKind::Dungeon, 0),
};
let loc = find_site_loc(&mut ctx, None, size)?;
this.establish_site(&mut ctx.reseed(), loc, |place| Site {
@ -151,6 +152,7 @@ impl Civs {
SiteKind::Dungeon => (8i32, 2.0),
SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (0i32, 0.0),
SiteKind::Tree => (12i32, 8.0),
};
let (raise, raise_dist): (f32, i32) = match &site.kind {
@ -220,6 +222,9 @@ impl Civs {
&mut rng,
wpos,
)),
SiteKind::Tree => {
WorldSite::tree(Tree::generate(wpos, &Land::from_sim(&ctx.sim), &mut rng))
},
});
sim_site.site_tmp = Some(site);
let site_ref = &index.sites[site];
@ -899,6 +904,7 @@ pub enum SiteKind {
Dungeon,
Castle,
Refactor,
Tree,
}
impl Site {

View File

@ -571,6 +571,7 @@ struct Branch {
impl Branch {
/// Determine whether there are either branches or leaves at the given
/// position in the branch.
/// (branch, leaves, stairs, forced_air)
pub fn is_branch_or_leaves_at(
&self,
pos: Vec3<f32>,

View File

@ -122,6 +122,7 @@ impl World {
},
civ::SiteKind::Castle => world_msg::SiteKind::Castle,
civ::SiteKind::Refactor => world_msg::SiteKind::Town,
civ::SiteKind::Tree => world_msg::SiteKind::Tree,
},
wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
}

View File

@ -2109,18 +2109,20 @@ impl WorldSim {
})
});
let giant_trees = std::array::IntoIter::new(self.gen_ctx.big_structure_gen.get(wpos))
// Don't even consider trees if we aren't close
.filter(move |(pos, _)| pos.distance_squared(wpos) < 512i32.pow(2))
.map(move |(pos, seed)| TreeAttr {
pos,
seed,
scale: 5.0,
forest_kind: ForestKind::Giant,
inhabited: (seed / 13) % 2 == 0,
});
// // For testing
// let giant_trees =
// std::array::IntoIter::new(self.gen_ctx.big_structure_gen.get(wpos))
// // Don't even consider trees if we aren't close
// .filter(move |(pos, _)| pos.distance_squared(wpos) < 512i32.pow(2))
// .map(move |(pos, seed)| TreeAttr {
// pos,
// seed,
// scale: 5.0,
// forest_kind: ForestKind::Giant,
// inhabited: (seed / 13) % 2 == 0,
// });
normal_trees.chain(giant_trees)
normal_trees //.chain(giant_trees)
}
}

View File

@ -4,11 +4,12 @@ mod dungeon;
pub mod economy;
pub mod namegen;
mod settlement;
mod tree;
// Reexports
pub use self::{
block_mask::BlockMask, castle::Castle, dungeon::Dungeon, economy::Economy,
settlement::Settlement,
settlement::Settlement, tree::Tree,
};
use crate::{column::ColumnSample, site2, Canvas, IndexRef};
@ -46,6 +47,7 @@ pub enum SiteKind {
Dungeon(Dungeon),
Castle(Castle),
Refactor(site2::Site),
Tree(tree::Tree),
}
impl Site {
@ -77,12 +79,20 @@ impl Site {
}
}
pub fn tree(t: tree::Tree) -> Self {
Self {
kind: SiteKind::Tree(t),
economy: Economy::default(),
}
}
pub fn radius(&self) -> f32 {
match &self.kind {
SiteKind::Settlement(s) => s.radius(),
SiteKind::Dungeon(d) => d.radius(),
SiteKind::Castle(c) => c.radius(),
SiteKind::Refactor(s) => s.radius(),
SiteKind::Tree(t) => t.radius(),
}
}
@ -92,6 +102,7 @@ impl Site {
SiteKind::Dungeon(d) => d.get_origin(),
SiteKind::Castle(c) => c.get_origin(),
SiteKind::Refactor(s) => s.origin,
SiteKind::Tree(t) => t.origin,
}
}
@ -101,6 +112,7 @@ impl Site {
SiteKind::Dungeon(d) => d.spawn_rules(wpos),
SiteKind::Castle(c) => c.spawn_rules(wpos),
SiteKind::Refactor(s) => s.spawn_rules(wpos),
SiteKind::Tree(t) => t.spawn_rules(wpos),
}
}
@ -109,7 +121,8 @@ impl Site {
SiteKind::Settlement(s) => s.name(),
SiteKind::Dungeon(d) => d.name(),
SiteKind::Castle(c) => c.name(),
SiteKind::Refactor(s) => "Experimental",
SiteKind::Refactor(s) => "Town",
SiteKind::Tree(t) => "Tree",
}
}
@ -120,9 +133,8 @@ impl Site {
SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Dungeon(d) => d.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Castle(c) => c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Refactor(s) => {
s.render(canvas, dynamic_rng);
},
SiteKind::Refactor(s) => s.render(canvas, dynamic_rng),
SiteKind::Tree(t) => t.render(canvas, dynamic_rng),
}
}
@ -141,6 +153,7 @@ impl Site {
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
SiteKind::Refactor(_) => {},
SiteKind::Tree(_) => {},
}
}
}

61
world/src/site/tree.rs Normal file
View File

@ -0,0 +1,61 @@
use crate::{
layer::tree::{ProceduralTree, TreeConfig},
site::SpawnRules,
Canvas, Land,
};
use common::terrain::{Block, BlockKind};
use rand::prelude::*;
use vek::*;
// Temporary, do trees through the new site system later
pub struct Tree {
pub origin: Vec2<i32>,
alt: i32,
tree: ProceduralTree,
}
impl Tree {
pub fn generate(origin: Vec2<i32>, land: &Land, rng: &mut impl Rng) -> Self {
Self {
origin,
alt: land.get_alt_approx(origin) as i32,
tree: {
let config = TreeConfig::giant(rng, 4.0, false);
ProceduralTree::generate(config, rng)
},
}
}
pub fn radius(&self) -> f32 { 512.0 }
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
let trunk_radius = 48i32;
SpawnRules {
trees: wpos.distance_squared(self.origin) > trunk_radius.pow(2),
}
}
pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
canvas.foreach_col(|canvas, wpos2d, col| {
let rpos2d = wpos2d - self.origin;
let bounds = self.tree.get_bounds().map(|e| e as i32);
if !Aabr::from(bounds).contains_point(rpos2d) {
// Skip this column
return;
}
for z in (bounds.min.z..bounds.max.z + 1).rev() {
let wpos = wpos2d.with_z(self.alt + z);
let rposf = (wpos - self.origin.with_z(self.alt)).map(|e| e as f32 + 0.5);
let (branch, leaves, _, _) = self.tree.is_branch_or_leaves_at(rposf);
if leaves {
canvas.set(wpos, Block::new(BlockKind::Leaves, Rgb::new(30, 130, 40)));
} else if branch {
canvas.set(wpos, Block::new(BlockKind::Wood, Rgb::new(80, 32, 0)));
}
}
});
}
}