Merge branch 'crabman/map_tweaks' into 'master'

Map tweaks & new default map

See merge request veloren/veloren!4383
This commit is contained in:
Marcel 2024-03-28 10:14:59 +00:00
commit 3ce82874a3
22 changed files with 178 additions and 91 deletions

View File

@ -105,6 +105,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dropped items now merge dynamically (including non-stackables). - Dropped items now merge dynamically (including non-stackables).
- You no longer need to unlock health, energy and roll skills to get to max. - You no longer need to unlock health, energy and roll skills to get to max.
- Rolls now don't skip recovery, and instead have increased buildup during ability interrupts. - Rolls now don't skip recovery, and instead have increased buildup during ability interrupts.
- Changed default world map
### Removed ### Removed

BIN
assets/world/map/veloren_0_16_0_0.bin (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -17,3 +17,37 @@ pub enum BiomeKind {
Savannah, Savannah,
Taiga, Taiga,
} }
impl BiomeKind {
/// Roughly represents the difficulty of a biome (value between 1 and 5)
pub fn difficulty(&self) -> i32 {
match self {
BiomeKind::Void => 1,
BiomeKind::Lake => 1,
BiomeKind::Grassland => 2,
BiomeKind::Ocean => 1,
BiomeKind::Mountain => 1,
BiomeKind::Snowland => 2,
BiomeKind::Desert => 5,
BiomeKind::Swamp => 2,
BiomeKind::Jungle => 3,
BiomeKind::Forest => 1,
BiomeKind::Savannah => 2,
BiomeKind::Taiga => 2,
}
}
}
#[cfg(test)]
#[test]
fn test_biome_difficulty() {
use strum::IntoEnumIterator;
for biome_kind in BiomeKind::iter() {
assert!(
(1..=5).contains(&biome_kind.difficulty()),
"Biome {biome_kind:?} has invalid difficulty {}",
biome_kind.difficulty()
);
}
}

View File

@ -316,17 +316,7 @@ impl Data {
// Try a few times to find a location that's not underwater // Try a few times to find a location that's not underwater
if let Some((wpos, chunk)) = (0..100) if let Some((wpos, chunk)) = (0..100)
.map(|_| world.sim().get_size().map(|sz| rng.gen_range(0..sz as i32))) .map(|_| world.sim().get_size().map(|sz| rng.gen_range(0..sz as i32)))
.find_map(|pos| { .find_map(|pos| Some((pos, world.sim().get(pos).filter(|c| !c.is_underwater())?)))
Some((
pos,
world
.sim()
.get(pos)
// This is currently a workaround to force Frost Gigas spawning in cold areas
// TODO: Once more Gigas are implemented remove this
.filter(|c| !c.is_underwater() && c.temp < CONFIG.snow_temp)?,
))
})
.map(|(pos, chunk)| { .map(|(pos, chunk)| {
let wpos2d = pos.cpos_to_wpos_center(); let wpos2d = pos.cpos_to_wpos_center();
( (

View File

@ -146,7 +146,7 @@ use crate::{chat::ChatCache, persistence::character_loader::CharacterScreenRespo
use common::comp::Anchor; use common::comp::Anchor;
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
pub use world::{ pub use world::{
sim::{FileOpts, GenOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, GenOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
IndexOwned, World, IndexOwned, World,
}; };

View File

@ -30,11 +30,10 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use tracing::{error, warn}; use tracing::{error, warn};
use world::sim::FileOpts; use world::sim::{FileOpts, DEFAULT_WORLD_SEED};
use self::server_description::ServerDescription; use self::server_description::ServerDescription;
const DEFAULT_WORLD_SEED: u32 = 230;
const CONFIG_DIR: &str = "server_config"; const CONFIG_DIR: &str = "server_config";
const SETTINGS_FILENAME: &str = "settings.ron"; const SETTINGS_FILENAME: &str = "settings.ron";
const WHITELIST_FILENAME: &str = "whitelist.ron"; const WHITELIST_FILENAME: &str = "whitelist.ron";

View File

@ -15,7 +15,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let pool = rayon::ThreadPoolBuilder::new().build().unwrap(); let pool = rayon::ThreadPoolBuilder::new().build().unwrap();
// Generate chunks here to test // Generate chunks here to test
let (world, index) = World::generate( let (world, index) = World::generate(
42, sim::DEFAULT_WORLD_SEED,
sim::WorldOpts { sim::WorldOpts {
// NOTE: If this gets too expensive, we can turn it off. // NOTE: If this gets too expensive, we can turn it off.
// TODO: Consider an option to turn off all erosion as well, or even provide altitude // TODO: Consider an option to turn off all erosion as well, or even provide altitude

View File

@ -32,6 +32,7 @@ use common::{
}; };
use common_net::msg::world_msg::{SiteId, SiteInfo}; use common_net::msg::world_msg::{SiteId, SiteInfo};
use i18n::{Localization, LocalizationHandle}; use i18n::{Localization, LocalizationHandle};
use rand::{thread_rng, Rng};
//ImageFrame, Tooltip, //ImageFrame, Tooltip,
use crate::settings::Settings; use crate::settings::Settings;
//use std::time::Duration; //use std::time::Duration;
@ -194,7 +195,7 @@ enum Mode {
/// mode as opposed to create mode. /// mode as opposed to create mode.
// TODO: Something less janky? Express the problem domain better! // TODO: Something less janky? Express the problem domain better!
character_id: Option<CharacterId>, character_id: Option<CharacterId>,
start_site_idx: usize, start_site_idx: Option<usize>,
}, },
} }
@ -248,7 +249,7 @@ impl Mode {
prev_starting_site_button: Default::default(), prev_starting_site_button: Default::default(),
next_starting_site_button: Default::default(), next_starting_site_button: Default::default(),
character_id: None, character_id: None,
start_site_idx: 0, start_site_idx: None,
} }
} }
@ -278,7 +279,7 @@ impl Mode {
prev_starting_site_button: Default::default(), prev_starting_site_button: Default::default(),
next_starting_site_button: Default::default(), next_starting_site_button: Default::default(),
character_id: Some(character_id), character_id: Some(character_id),
start_site_idx: 0, start_site_idx: None,
} }
} }
} }
@ -1362,10 +1363,12 @@ impl Controls {
//TODO: Add text-outline here whenever we updated iced to a version supporting //TODO: Add text-outline here whenever we updated iced to a version supporting
// this // this
let map = if let Some(info) = self.possible_starting_sites.get(*start_site_idx) let map = if let Some(info) = self
.possible_starting_sites
.get(start_site_idx.unwrap_or_default())
{ {
let site_name = Text::new( let site_name = Text::new(
self.possible_starting_sites[*start_site_idx] self.possible_starting_sites[start_site_idx.unwrap_or_default()]
.name .name
.as_deref() .as_deref()
.unwrap_or("Unknown"), .unwrap_or("Unknown"),
@ -1405,12 +1408,16 @@ impl Controls {
if self.possible_starting_sites.is_empty() { if self.possible_starting_sites.is_empty() {
vec![map] vec![map]
} else { } else {
let selected = start_site_idx.get_or_insert_with(|| {
thread_rng().gen_range(0..self.possible_starting_sites.len())
});
let site_slider = starter_slider( let site_slider = starter_slider(
i18n.get_msg("char_selection-starting_site").into_owned(), i18n.get_msg("char_selection-starting_site").into_owned(),
30, 30,
&mut sliders.starting_site, &mut sliders.starting_site,
self.possible_starting_sites.len() as u32 - 1, self.possible_starting_sites.len() as u32 - 1,
*start_site_idx as u32, *selected as u32,
|x| Message::StartingSite(x as usize), |x| Message::StartingSite(x as usize),
imgs, imgs,
); );
@ -1801,7 +1808,7 @@ impl Controls {
body: comp::Body::Humanoid(*body), body: comp::Body::Humanoid(*body),
start_site: self start_site: self
.possible_starting_sites .possible_starting_sites
.get(*start_site_idx) .get(start_site_idx.unwrap_or_default())
.map(|info| info.id), .map(|info| info.id),
}); });
self.mode = Mode::select(Some(InfoContent::CreatingCharacter)); self.mode = Mode::select(Some(InfoContent::CreatingCharacter));
@ -1863,7 +1870,6 @@ impl Controls {
//Todo: Add species and body type to randomization. //Todo: Add species and body type to randomization.
Message::RandomizeCharacter => { Message::RandomizeCharacter => {
if let Mode::CreateOrEdit { body, .. } = &mut self.mode { if let Mode::CreateOrEdit { body, .. } = &mut self.mode {
use rand::Rng;
let body_type = body.body_type; let body_type = body.body_type;
let species = body.species; let species = body.species;
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -1930,24 +1936,30 @@ impl Controls {
}, },
Message::StartingSite(idx) => { Message::StartingSite(idx) => {
if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode { if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode {
*start_site_idx = idx; *start_site_idx = Some(idx);
} }
}, },
Message::PrevStartingSite => { Message::PrevStartingSite => {
if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode { if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode {
if !self.possible_starting_sites.is_empty() { if !self.possible_starting_sites.is_empty() {
*start_site_idx = (*start_site_idx + self.possible_starting_sites.len() *start_site_idx = Some(
- 1) (start_site_idx.unwrap_or_default()
% self.possible_starting_sites.len(); + self.possible_starting_sites.len()
- 1)
% self.possible_starting_sites.len(),
);
} }
} }
}, },
Message::NextStartingSite => { Message::NextStartingSite => {
if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode { if let Mode::CreateOrEdit { start_site_idx, .. } = &mut self.mode {
if !self.possible_starting_sites.is_empty() { if !self.possible_starting_sites.is_empty() {
*start_site_idx = *start_site_idx = Some(
(*start_site_idx + self.possible_starting_sites.len() + 1) (start_site_idx.unwrap_or_default()
% self.possible_starting_sites.len(); + self.possible_starting_sites.len()
+ 1)
% self.possible_starting_sites.len(),
);
} }
} }
}, },

View File

@ -6,7 +6,7 @@ use std::{
use common::{assets::ASSETS_PATH, consts::DAY_LENGTH_DEFAULT}; use common::{assets::ASSETS_PATH, consts::DAY_LENGTH_DEFAULT};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use server::{FileOpts, GenOpts, DEFAULT_WORLD_MAP}; use server::{FileOpts, GenOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED};
use tracing::error; use tracing::error;
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
@ -84,7 +84,7 @@ fn migrate_old_singleplayer(from: &Path, to: &Path) {
return; return;
} }
let mut seed = 0; let mut seed = DEFAULT_WORLD_SEED;
let mut day_length = DAY_LENGTH_DEFAULT; let mut day_length = DAY_LENGTH_DEFAULT;
let (map_file, gen_opts) = fs::read_to_string(to.join("server_config/settings.ron")) let (map_file, gen_opts) = fs::read_to_string(to.join("server_config/settings.ron"))
.ok() .ok()
@ -238,7 +238,7 @@ impl SingleplayerWorlds {
name: "New World".to_string(), name: "New World".to_string(),
gen_opts: None, gen_opts: None,
day_length: DAY_LENGTH_DEFAULT, day_length: DAY_LENGTH_DEFAULT,
seed: 0, seed: DEFAULT_WORLD_SEED,
is_generated: false, is_generated: false,
map_path: path.join("map.bin"), map_path: path.join("map.bin"),
path, path,

View File

@ -3,14 +3,14 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
use veloren_world::{ use veloren_world::{
layer, layer,
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
CanvasInfo, Land, World, CanvasInfo, Land, World,
}; };
fn cave(c: &mut Criterion) { fn cave(c: &mut Criterion) {
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate( let (world, index) = World::generate(
230, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -6,14 +6,14 @@ use veloren_world::{
self, self,
cave::{Biome, LAYERS}, cave::{Biome, LAYERS},
}, },
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
CanvasInfo, Land, World, CanvasInfo, Land, World,
}; };
fn main() { fn main() {
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate( let (world, index) = World::generate(
230, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -29,7 +29,7 @@ use tracing::{debug, trace};
use vek::*; use vek::*;
use veloren_world::{ use veloren_world::{
civ::SiteKind, civ::SiteKind,
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
World, World,
}; };
@ -682,7 +682,7 @@ fn main() {
common_frontend::init_stdout(None); common_frontend::init_stdout(None);
println!("Loading world"); println!("Loading world");
let (world, index) = World::generate( let (world, index) = World::generate(
59686, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -12,7 +12,7 @@ use common::{
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
use vek::{Vec2, Vec3}; use vek::{Vec2, Vec3};
use veloren_world::{ use veloren_world::{
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
site2::{plot::PlotKind, Fill, Structure}, site2::{plot::PlotKind, Fill, Structure},
CanvasInfo, Land, World, CanvasInfo, Land, World,
}; };
@ -25,7 +25,7 @@ fn main() -> Result {
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
println!("Loading world"); println!("Loading world");
let (world, index) = World::generate( let (world, index) = World::generate(
59686, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -6,7 +6,7 @@ use rayon::ThreadPoolBuilder;
use std::{fs::File, io::Write}; use std::{fs::File, io::Write};
use vek::*; use vek::*;
use veloren_world::{ use veloren_world::{
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
Land, World, Land, World,
}; };
@ -120,7 +120,7 @@ fn main() {
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
println!("Loading world"); println!("Loading world");
let (world, _index) = World::generate( let (world, _index) = World::generate(
59686, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -9,7 +9,7 @@ use strum::IntoEnumIterator;
use vek::Vec2; use vek::Vec2;
use veloren_world::{ use veloren_world::{
index::Index, index::Index,
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
World, World,
}; };
@ -162,7 +162,7 @@ fn main() {
println!("Loading world"); println!("Loading world");
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate( let (world, index) = World::generate(
59686, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -15,7 +15,7 @@ use tracing_subscriber::{
}; };
use vek::*; use vek::*;
use veloren_world::{ use veloren_world::{
sim::{self, get_horizon_map, sample_pos, sample_wpos, WorldOpts}, sim::{self, get_horizon_map, sample_pos, sample_wpos, WorldOpts, DEFAULT_WORLD_SEED},
util::Sampler, util::Sampler,
ColumnSample, World, CONFIG, ColumnSample, World, CONFIG,
}; };
@ -43,7 +43,7 @@ fn main() {
_map_file.push(map_file); _map_file.push(map_file);
let (world, index) = World::generate( let (world, index) = World::generate(
5284, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: false, seed_elements: false,
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),

View File

@ -21,7 +21,7 @@ use std::{
}; };
use vek::*; use vek::*;
use veloren_world::{ use veloren_world::{
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
World, World,
}; };
@ -63,7 +63,7 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
println!("Loading world"); println!("Loading world");
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate( let (world, index) = World::generate(
59686, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),

View File

@ -1,6 +1,6 @@
use std::time::Instant; use std::time::Instant;
use veloren_world::{ use veloren_world::{
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, DEFAULT_WORLD_SEED},
World, World,
}; };
@ -9,7 +9,7 @@ fn main() {
let start = Instant::now(); let start = Instant::now();
let (world, index) = World::generate( let (world, index) = World::generate(
0, DEFAULT_WORLD_SEED,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
// Load default map from assets. // Load default map from assets.

View File

@ -278,7 +278,7 @@ impl Civs {
let world_dims = ctx.sim.get_aabr(); let world_dims = ctx.sim.get_aabr();
for _ in 0..initial_civ_count * 3 { for _ in 0..initial_civ_count * 3 {
attempt(5, || { attempt(5, || {
let (loc, kind) = match ctx.rng.gen_range(0..115) { let (loc, kind) = match ctx.rng.gen_range(0..90) {
0..=4 => { 0..=4 => {
if index.features().site2_giant_trees { if index.features().site2_giant_trees {
( (
@ -304,7 +304,7 @@ impl Civs {
) )
} }
}, },
5..=10 => ( 5..=15 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -314,7 +314,7 @@ impl Civs {
)?, )?,
SiteKind::Gnarling, SiteKind::Gnarling,
), ),
11..=16 => ( 16..=20 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -324,7 +324,7 @@ impl Civs {
)?, )?,
SiteKind::ChapelSite, SiteKind::ChapelSite,
), ),
17..=22 => ( 21..=27 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -334,7 +334,7 @@ impl Civs {
)?, )?,
SiteKind::Adlet, SiteKind::Adlet,
), ),
23..=35 => ( 28..=38 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -344,7 +344,7 @@ impl Civs {
)?, )?,
SiteKind::PirateHideout, SiteKind::PirateHideout,
), ),
36..=42 => ( 39..=45 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -354,7 +354,7 @@ impl Civs {
)?, )?,
SiteKind::JungleRuin, SiteKind::JungleRuin,
), ),
43..=49 => ( 46..=55 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -364,7 +364,7 @@ impl Civs {
)?, )?,
SiteKind::RockCircle, SiteKind::RockCircle,
), ),
50..=59 => ( 56..=66 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -374,7 +374,7 @@ impl Civs {
)?, )?,
SiteKind::TrollCave, SiteKind::TrollCave,
), ),
60..=69 => ( 67..=72 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -384,7 +384,7 @@ impl Civs {
)?, )?,
SiteKind::Camp, SiteKind::Camp,
), ),
70..=74 => ( 73..=76 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -394,7 +394,7 @@ impl Civs {
)?, )?,
SiteKind::Haniwa, SiteKind::Haniwa,
), ),
75..=85 => ( 77..=79 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -404,7 +404,7 @@ impl Civs {
)?, )?,
SiteKind::Terracotta, SiteKind::Terracotta,
), ),
/*86..=91 => ( /*80..=86 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -414,7 +414,7 @@ impl Civs {
)?, )?,
SiteKind::DwarvenMine, SiteKind::DwarvenMine,
), ),
92..=97 => ( 86..=97 => (
find_site_loc( find_site_loc(
&mut ctx, &mut ctx,
&ProximityRequirementsBuilder::new() &ProximityRequirementsBuilder::new()
@ -945,10 +945,10 @@ impl Civs {
fn birth_civ(&mut self, ctx: &mut GenCtx<impl Rng>) -> Option<Id<Civ>> { fn birth_civ(&mut self, ctx: &mut GenCtx<impl Rng>) -> Option<Id<Civ>> {
// TODO: specify SiteKind based on where a suitable location is found // TODO: specify SiteKind based on where a suitable location is found
let kind = match ctx.rng.gen_range(0..64) { let kind = match ctx.rng.gen_range(0..64) {
0..=10 => SiteKind::CliffTown, 0..=8 => SiteKind::CliffTown,
11..=12 => SiteKind::DesertCity, 9..=17 => SiteKind::DesertCity,
13..=18 => SiteKind::SavannahPit, 18..=23 => SiteKind::SavannahPit,
19..=36 => SiteKind::CoastalTown, 24..=33 => SiteKind::CoastalTown,
_ => SiteKind::Refactor, _ => SiteKind::Refactor,
}; };
let world_dims = ctx.sim.get_aabr(); let world_dims = ctx.sim.get_aabr();
@ -1558,11 +1558,7 @@ impl Civs {
} }
fn tree_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ { fn tree_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {
self.sites().filter_map(|s| match s.kind { self.sites().map(|s| s.center)
SiteKind::Castle => Some(s.center),
_ if s.is_settlement() => Some(s.center),
_ => None,
})
} }
fn castle_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ { fn castle_enemies(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {

View File

@ -36,7 +36,7 @@ pub use block::BlockGen;
use civ::WorldCivStage; use civ::WorldCivStage;
pub use column::ColumnSample; pub use column::ColumnSample;
pub use common::terrain::site::{DungeonKindMeta, SettlementKindMeta}; pub use common::terrain::site::{DungeonKindMeta, SettlementKindMeta};
use common::terrain::CoordinateConversions; use common::{spiral::Spiral2d, terrain::CoordinateConversions};
pub use index::{IndexOwned, IndexRef}; pub use index::{IndexOwned, IndexRef};
use sim::WorldSimStage; use sim::WorldSimStage;
@ -249,7 +249,7 @@ impl World {
})) }))
.collect(), .collect(),
possible_starting_sites: { possible_starting_sites: {
const STARTING_SITE_COUNT: usize = 4; const STARTING_SITE_COUNT: usize = 5;
let mut candidates = self let mut candidates = self
.civs() .civs()
@ -258,21 +258,55 @@ impl World {
.filter_map(|(_, civ_site)| Some((civ_site, civ_site.site_tmp?))) .filter_map(|(_, civ_site)| Some((civ_site, civ_site.site_tmp?)))
.map(|(civ_site, site_id)| { .map(|(civ_site, site_id)| {
// Score the site according to how suitable it is to be a starting site // Score the site according to how suitable it is to be a starting site
let mut score = 0.0;
if let SiteKind::Refactor(site2) = &index.sites[site_id].kind { let (site2, mut score) = match &index.sites[site_id].kind {
// Strongly prefer towns SiteKind::Refactor(site2) => (site2, 2.0),
score += 1000.0; // Non-town sites should not be chosen as starting sites and get a score of 0
// Prefer sites of a medium size _ => return (site_id.id(), 0.0)
score += 2.0 / (1.0 + (site2.plots().len() as f32 - 20.0).abs() / 10.0);
}; };
// Prefer sites in hospitable climates
if let Some(chunk) = self.sim().get(civ_site.center) { /// Optimal number of plots in a starter town
score += 1.0 / (1.0 + chunk.temp.abs()); const OPTIMAL_STARTER_TOWN_SIZE: f32 = 30.0;
score += 1.0 / (1.0 + (chunk.humidity - CONFIG.forest_hum).abs() * 2.0);
} // Prefer sites of a medium size
let plots = site2.plots().len() as f32;
let size_score = if plots > OPTIMAL_STARTER_TOWN_SIZE {
1.0 + (1.0 / (1.0 + ((plots - OPTIMAL_STARTER_TOWN_SIZE) / 15.0).powi(3)))
} else {
(2.05 / (1.0 + ((OPTIMAL_STARTER_TOWN_SIZE - plots) / 15.0).powi(5))) - 0.05
}.max(0.01);
score *= size_score;
// Prefer sites that are close to the centre of the world // Prefer sites that are close to the centre of the world
score += 4.0 / (1.0 + civ_site.center.map2(self.sim().get_size(), |e, sz| (e as f32 / sz as f32 - 0.5).abs() * 2.0).reduce_partial_max()); let pos_score = (
10.0 / (
1.0 + (
civ_site.center.map2(self.sim().get_size(),
|e, sz|
(e as f32 / sz as f32 - 0.5).abs() * 2.0).reduce_partial_max()
).powi(6) * 25.0
)
).max(0.02);
score *= pos_score;
// Check if neighboring biomes are beginner friendly
let mut chunk_scores = 2.0;
for (chunk, distance) in Spiral2d::with_radius(10)
.filter_map(|rel_pos| {
let chunk_pos = civ_site.center + rel_pos * 2;
self.sim().get(chunk_pos).zip(Some(rel_pos.as_::<f32>().magnitude()))
})
{
let weight = 1.0 / (distance * std::f32::consts::TAU + 1.0);
let chunk_difficulty = 20.0 / (20.0 + chunk.get_biome().difficulty().pow(4) as f32 / 5.0);
// let chunk_difficulty = 1.0 / chunk.get_biome().difficulty() as f32;
chunk_scores *= 1.0 - weight + chunk_difficulty * weight;
}
score *= chunk_scores;
(site_id.id(), score) (site_id.id(), score)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -560,7 +560,25 @@ pub type ModernMap = WorldMap_0_7_0;
/// TODO: Consider using some naming convention to automatically change this /// TODO: Consider using some naming convention to automatically change this
/// with changing versions, or at least keep it in a constant somewhere that's /// with changing versions, or at least keep it in a constant somewhere that's
/// easy to change. /// easy to change.
pub const DEFAULT_WORLD_MAP: &str = "world.map.veloren_0_9_0_0"; // Generation parameters:
//
// gen_opts: (
// erosion_quality: 1.0,
// map_kind: Circle,
// scale: 2.098048498703866,
// x_lg: 10,
// y_lg: 10,
// )
// seed: 469876673
//
// The biome seed can found below
pub const DEFAULT_WORLD_MAP: &str = "world.map.veloren_0_16_0_0";
/// This is *not* the seed used to generate the default map, this seed was used
/// to generate a better set of biomes on it as the original ones were
/// unsuitable.
///
/// See DEFAULT_WORLD_MAP to get the original worldgen parameters.
pub const DEFAULT_WORLD_SEED: u32 = 1948292704;
impl WorldFileLegacy { impl WorldFileLegacy {
#[inline] #[inline]

View File

@ -336,7 +336,7 @@ mod tests {
execute_with_tracing(Level::INFO, || { execute_with_tracing(Level::INFO, || {
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap(); let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
info!("init"); info!("init");
let seed = 59686; let seed = sim::DEFAULT_WORLD_SEED;
let opts = sim::WorldOpts { let opts = sim::WorldOpts {
seed_elements: true, seed_elements: true,
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
@ -362,7 +362,7 @@ mod tests {
execute_with_tracing(Level::INFO, || { execute_with_tracing(Level::INFO, || {
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap(); let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
info!("init"); info!("init");
let seed = 59686; let seed = sim::DEFAULT_WORLD_SEED;
let opts = sim::WorldOpts { let opts = sim::WorldOpts {
seed_elements: true, seed_elements: true,
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
@ -502,7 +502,7 @@ mod tests {
execute_with_tracing(Level::ERROR, || { execute_with_tracing(Level::ERROR, || {
let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap(); let threadpool = rayon::ThreadPoolBuilder::new().build().unwrap();
info!("init"); info!("init");
let seed = 59686; let seed = sim::DEFAULT_WORLD_SEED;
let opts = sim::WorldOpts { let opts = sim::WorldOpts {
seed_elements: true, seed_elements: true,
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()), world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),