mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Plant growth simulation, not yet attached to anything.
This commit is contained in:
parent
8667db45bc
commit
9998e53f66
@ -116,4 +116,21 @@ SpriteBehaviorManifest(
|
||||
Mud: LootTable("common.loot_tables.sprite.mud"),
|
||||
Crate: LootTable("common.loot_tables.sprite.crate"),
|
||||
},
|
||||
growth_specs: {
|
||||
RedFlower: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Sunflower: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Tomato: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Cabbage: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Carrot: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Corn: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Garlic: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Onion: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Turnip: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Radish: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Blueberry: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Pumpkin: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
WheatYellow: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
WheatGreen: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
Flax: GrowthSpec(start_growth: 0, max_growth: 16, days_per_growth: 0.0625),
|
||||
},
|
||||
)
|
||||
|
@ -166,6 +166,15 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_growth(&self) -> Option<u8> {
|
||||
if self.get_sprite()?.get_growth_spec().is_some() {
|
||||
Some((self.attr[1] >> 3) & ((1 << 5) - 1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
match self.get_sprite()? {
|
||||
@ -270,6 +279,18 @@ impl Block {
|
||||
self
|
||||
}
|
||||
|
||||
/// If this block is a growable sprite, set its growth
|
||||
#[inline]
|
||||
pub fn with_sprite_growth(mut self, growth: u8) -> Self {
|
||||
if self
|
||||
.get_sprite()
|
||||
.map_or(false, |s| s.get_growth_spec().is_some())
|
||||
{
|
||||
self.attr[1] |= (growth & ((1 << 5) - 1)) << 3;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// If this block can have orientation, give it a new orientation.
|
||||
#[inline]
|
||||
pub fn with_ori(mut self, ori: u8) -> Option<Self> {
|
||||
|
@ -3,6 +3,8 @@ use crate::{
|
||||
comp::tool::ToolKind,
|
||||
lottery::LootSpec,
|
||||
make_case_elim,
|
||||
terrain::TerrainChunk,
|
||||
vol::{IntoVolIterator, ReadVol, RectRasterableVol, WriteVol},
|
||||
};
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use hashbrown::HashMap;
|
||||
@ -10,6 +12,7 @@ use lazy_static::lazy_static;
|
||||
use num_derive::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
use vek::Vec3;
|
||||
|
||||
make_case_elim!(
|
||||
sprite_kind,
|
||||
@ -162,6 +165,7 @@ make_case_elim!(
|
||||
pub struct SpriteBehaviorManifest {
|
||||
pub solid_height: HashMap<SpriteKind, f32>,
|
||||
pub collectible_id: HashMap<SpriteKind, LootSpec>,
|
||||
pub growth_specs: HashMap<SpriteKind, GrowthSpec>,
|
||||
}
|
||||
|
||||
impl Asset for SpriteBehaviorManifest {
|
||||
@ -175,6 +179,13 @@ lazy_static! {
|
||||
AssetExt::load_expect("common.sprite_behavior_manifest");
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize)]
|
||||
pub struct GrowthSpec {
|
||||
pub start_growth: u8,
|
||||
pub max_growth: u8,
|
||||
pub days_per_growth: f32,
|
||||
}
|
||||
|
||||
impl SpriteKind {
|
||||
pub fn solid_height(&self) -> Option<f32> {
|
||||
SPRITE_BEHAVIOR_MANIFEST
|
||||
@ -190,7 +201,15 @@ impl SpriteKind {
|
||||
.collectible_id
|
||||
.get(self)
|
||||
.is_some()
|
||||
&& !self.mine_tool().is_some()
|
||||
&& self.mine_tool().is_none()
|
||||
}
|
||||
|
||||
pub fn get_growth_spec(&self) -> Option<GrowthSpec> {
|
||||
SPRITE_BEHAVIOR_MANIFEST
|
||||
.read()
|
||||
.growth_specs
|
||||
.get(self)
|
||||
.copied()
|
||||
}
|
||||
|
||||
/// Is the sprite a container that will emit a mystery item?
|
||||
@ -286,3 +305,88 @@ impl<'a> TryFrom<&'a str> for SpriteKind {
|
||||
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
|
||||
}
|
||||
|
||||
/// Per-chunk index of plant data, for efficiently growing plants, and
|
||||
/// persisting plant growth when the chunk is unloaded with low memory footprint
|
||||
pub struct PlantGrowthData {
|
||||
data: HashMap<SpriteKind, PlantGrowthPerKind>,
|
||||
}
|
||||
|
||||
struct PlantGrowthPerKind {
|
||||
// TODO: if we made use of the assumption that chunks are 32x32xk voxels, we could pack
|
||||
// positions into 10+log_2(k) bits instead of using the whole 12 bytes that a Vec3<i32> uses
|
||||
positions: Vec<Vec3<i32>>,
|
||||
growth_amounts: Vec<u8>,
|
||||
last_growth_tick: f32,
|
||||
}
|
||||
|
||||
impl PlantGrowthPerKind {
|
||||
fn new(time: f32) -> Self {
|
||||
Self {
|
||||
positions: Vec::new(),
|
||||
growth_amounts: Vec::new(),
|
||||
last_growth_tick: time,
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_growth(&mut self, sprite: SpriteKind, time: f32) {
|
||||
if let Some(growth_spec) = sprite.get_growth_spec() {
|
||||
let dt = self.last_growth_tick - time;
|
||||
let dt_days = dt / (60.0 * 60.0 * 24.0);
|
||||
if dt_days > growth_spec.days_per_growth {
|
||||
self.last_growth_tick += dt;
|
||||
for growth in self.growth_amounts.iter_mut() {
|
||||
*growth += (dt_days / growth_spec.days_per_growth) as u8;
|
||||
*growth = (*growth).min(growth_spec.max_growth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlantGrowthData {
|
||||
pub fn from_fresh_chunk(chunk: &TerrainChunk, time: f32) -> Self {
|
||||
let mut ret = Self {
|
||||
data: HashMap::new(),
|
||||
};
|
||||
for (pos, block) in chunk.vol_iter(
|
||||
Vec3::new(0, 0, chunk.get_min_z()),
|
||||
<TerrainChunk as RectRasterableVol>::RECT_SIZE
|
||||
.as_()
|
||||
.with_z(chunk.get_max_z()),
|
||||
) {
|
||||
if let Some(sprite) = block.get_sprite() {
|
||||
if let Some(growth_spec) = sprite.get_growth_spec() {
|
||||
let entry = ret
|
||||
.data
|
||||
.entry(sprite)
|
||||
.or_insert_with(|| PlantGrowthPerKind::new(time));
|
||||
entry.positions.push(pos);
|
||||
entry.growth_amounts.push(growth_spec.start_growth);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn calculate_growth(&mut self, time: f32) {
|
||||
for (sprite, per_kind_growth) in self.data.iter_mut() {
|
||||
per_kind_growth.calculate_growth(*sprite, time);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn overwrite_plants(&self, chunk: &mut TerrainChunk) {
|
||||
for (sprite, per_kind_growth) in self.data.iter() {
|
||||
for (pos, growth) in per_kind_growth
|
||||
.positions
|
||||
.iter()
|
||||
.zip(per_kind_growth.growth_amounts.iter())
|
||||
{
|
||||
if let Ok(block) = chunk.get(*pos) {
|
||||
let block = *block;
|
||||
let _ = chunk.set(*pos, block.with_sprite(*sprite).with_sprite_growth(*growth));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ mod build_areas;
|
||||
mod state;
|
||||
// TODO: breakup state module and remove glob
|
||||
pub use build_areas::{BuildAreaError, BuildAreas};
|
||||
pub use state::{BlockChange, State, TerrainChanges};
|
||||
pub use state::{insert_terrain_chunk, BlockChange, State, TerrainChanges};
|
||||
|
@ -78,6 +78,21 @@ impl TerrainChanges {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_terrain_chunk<'a>(
|
||||
terrain_grid: &mut specs::WriteExpect<'a, TerrainGrid>,
|
||||
terrain_changes: &mut specs::Write<'a, TerrainChanges>,
|
||||
key: Vec2<i32>,
|
||||
chunk: Arc<TerrainChunk>,
|
||||
new_hook: impl FnOnce(),
|
||||
) {
|
||||
if terrain_grid.insert(key, chunk).is_some() {
|
||||
terrain_changes.modified_chunks.insert(key);
|
||||
} else {
|
||||
terrain_changes.new_chunks.insert(key);
|
||||
new_hook();
|
||||
}
|
||||
}
|
||||
|
||||
/// A type used to represent game state stored on both the client and the
|
||||
/// server. This includes things like entity components, terrain data, and
|
||||
/// global states like weather, time of day, etc.
|
||||
@ -384,22 +399,13 @@ impl State {
|
||||
|
||||
/// Insert the provided chunk into this state's terrain.
|
||||
pub fn insert_chunk(&mut self, key: Vec2<i32>, chunk: Arc<TerrainChunk>) {
|
||||
if self
|
||||
.ecs
|
||||
.write_resource::<TerrainGrid>()
|
||||
.insert(key, chunk)
|
||||
.is_some()
|
||||
{
|
||||
self.ecs
|
||||
.write_resource::<TerrainChanges>()
|
||||
.modified_chunks
|
||||
.insert(key);
|
||||
} else {
|
||||
self.ecs
|
||||
.write_resource::<TerrainChanges>()
|
||||
.new_chunks
|
||||
.insert(key);
|
||||
}
|
||||
insert_terrain_chunk(
|
||||
&mut self.ecs.write_resource::<TerrainGrid>().into(),
|
||||
&mut self.ecs.write_resource::<TerrainChanges>().into(),
|
||||
key,
|
||||
chunk,
|
||||
|| (),
|
||||
);
|
||||
}
|
||||
|
||||
/// Remove the chunk with the given key from this state's terrain, if it
|
||||
|
@ -15,7 +15,7 @@ use common::{
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
||||
use common_state::TerrainChanges;
|
||||
use common_state::{insert_terrain_chunk, TerrainChanges};
|
||||
use comp::Behavior;
|
||||
use specs::{Join, Read, ReadExpect, ReadStorage, Write, WriteExpect};
|
||||
use std::sync::Arc;
|
||||
@ -150,14 +150,10 @@ impl<'a> System<'a> for Sys {
|
||||
// Add to list of chunks to send to nearby players.
|
||||
new_chunks.push((key, Arc::clone(&chunk)));
|
||||
|
||||
// TODO: code duplication for chunk insertion between here and state.rs
|
||||
// Insert the chunk into terrain changes
|
||||
if terrain.insert(key, chunk).is_some() {
|
||||
terrain_changes.modified_chunks.insert(key);
|
||||
} else {
|
||||
terrain_changes.new_chunks.insert(key);
|
||||
insert_terrain_chunk(&mut terrain, &mut terrain_changes, key, chunk, || {
|
||||
rtsim.hook_load_chunk(key);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle chunk supplement
|
||||
for entity in supplement.entities {
|
||||
|
Loading…
Reference in New Issue
Block a user