use super::*;
use crate::{ColumnSample, Land};
use common::terrain::{
sprite::{Owned, RelativeNeighborPosition},
Block, BlockKind, SpriteKind,
};
use rand::prelude::*;
use strum::{EnumIter, IntoEnumIterator};
use vek::*;
#[derive(EnumIter)]
enum Crop {
Wildflower,
Wheat,
Flax,
Corn,
Tomato,
Carrot,
Radish,
Turnip,
Cabbage,
Pumpkin,
Sunflower,
Cactus,
}
impl Crop {
fn row_spacing(&self) -> Option<(f32, f32)> {
match self {
Self::Wildflower => None,
Self::Wheat | Self::Flax | Self::Corn => Some((6.0, 0.8)),
Self::Tomato | Self::Cactus => Some((3.0, 1.0 / 3.0)),
Self::Carrot | Self::Radish | Self::Turnip | Self::Cabbage | Self::Pumpkin => {
Some((6.0, 0.75))
},
Self::Sunflower => Some((4.0, 0.5)),
}
}
fn sprites(&self) -> &[(f32, Option<SpriteKind>)] {
match self {
Self::Wheat => &[
(4.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::WheatGreen)),
(1.0, Some(SpriteKind::WheatYellow)),
],
Self::Flax => &[
(4.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Flax)),
],
Self::Corn => &[
(4.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Corn)),
],
Self::Wildflower => &[
(40.0, None),
(1.0, Some(SpriteKind::BlueFlower)),
(1.0, Some(SpriteKind::PinkFlower)),
(1.0, Some(SpriteKind::PurpleFlower)),
(0.1, Some(SpriteKind::RedFlower)),
(1.0, Some(SpriteKind::WhiteFlower)),
(1.0, Some(SpriteKind::YellowFlower)),
(1.0, Some(SpriteKind::Sunflower)),
(4.0, Some(SpriteKind::LongGrass)),
(4.0, Some(SpriteKind::MediumGrass)),
(4.0, Some(SpriteKind::ShortGrass)),
],
Self::Tomato => &[
(1.5, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Tomato)),
],
Self::Carrot => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Carrot)),
],
Self::Radish => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Radish)),
],
Self::Turnip => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Turnip)),
],
Self::Cabbage => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Cabbage)),
],
Self::Pumpkin => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Pumpkin)),
],
Self::Sunflower => &[
(5.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::Sunflower)),
],
Self::Cactus => &[
(10.0, Some(SpriteKind::Empty)),
(1.0, Some(SpriteKind::BarrelCactus)),
(1.0, Some(SpriteKind::RoundCactus)),
(1.0, Some(SpriteKind::ShortCactus)),
(1.0, Some(SpriteKind::MedFlatCactus)),
(1.0, Some(SpriteKind::ShortFlatCactus)),
(1.0, Some(SpriteKind::LargeCactus)),
(1.0, Some(SpriteKind::TallCactus)),
],
}
}
}
pub struct FarmField {
crop: Crop,
bounds: Aabr<i32>,
pub(crate) alt: i32,
ori: Vec2<f32>,
is_desert: bool,
}
impl FarmField {
pub fn generate(
land: &Land,
rng: &mut impl Rng,
site: &Site,
door_tile: Vec2<i32>,
door_dir: Vec2<i32>,
tile_aabr: Aabr<i32>,
is_desert: bool,
) -> Self {
let bounds = Aabr {
min: site.tile_wpos(tile_aabr.min),
max: site.tile_wpos(tile_aabr.max),
};
let ori = rng.gen_range(0.0..std::f32::consts::TAU);
let crop = if is_desert {
Crop::Cactus
} else {
Crop::iter()
.filter(|crop| !matches!(crop, Crop::Cactus))
.choose(rng)
.unwrap()
};
Self {
bounds,
alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32,
ori: Vec2::new(ori.sin(), ori.cos()),
crop,
is_desert,
}
}
}
impl Structure for FarmField {
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"render_farmfield\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "render_farmfield")]
fn terrain_surface_at<R: Rng>(
&self,
wpos: Vec2<i32>,
old: Block,
rng: &mut R,
col: &ColumnSample,
z_off: i32,
) -> Option<Block> {
let t = (self.ori * wpos.as_()).magnitude();
let is_trench = self
.crop
.row_spacing()
.map(|(w, p)| (t / w).fract() <= p)
.unwrap_or(false);
let hit_min_x_bounds = wpos.x == self.bounds.min.x;
let hit_min_y_bounds = wpos.y == self.bounds.min.y;
let hit_max_x_bounds = wpos.x == self.bounds.max.x - 1;
let hit_max_y_bounds = wpos.y == self.bounds.max.y - 1;
let is_bounds =
hit_min_x_bounds || hit_min_y_bounds || hit_max_x_bounds || hit_max_y_bounds;
let is_corner = (hit_max_y_bounds || hit_min_y_bounds)
&& (hit_max_x_bounds || hit_min_x_bounds)
&& is_bounds;
if z_off == 0 {
Some(Block::new(
if self.is_desert {
BlockKind::Sand
} else {
BlockKind::Grass
},
(Lerp::lerp(
col.surface_color,
col.sub_surface_color * 0.5,
is_trench as i32 as f32,
) * 255.0)
.as_(),
))
} else if z_off == 1 && is_bounds {
let adjacent_type = if is_corner {
RelativeNeighborPosition::L
} else {
RelativeNeighborPosition::I
};
let ori = if !is_corner {
if hit_min_x_bounds || hit_max_x_bounds {
2
} else {
0
}
} else {
if hit_min_x_bounds && hit_min_y_bounds {
4
} else if hit_max_x_bounds && hit_min_y_bounds {
6
} else if hit_min_x_bounds && hit_max_y_bounds {
2
} else {
0
}
};
Some(
old.into_vacant()
.with_sprite(SpriteKind::Fence)
.with_ori(ori)
.unwrap()
.with_adjacent_type(adjacent_type)
.unwrap(),
)
} else if z_off == 1 && (is_trench || self.crop.row_spacing().is_none()) {
self.crop
.sprites()
.choose_weighted(rng, |(w, _)| *w)
.ok()
.and_then(|&(_, s)| {
let new = old.into_vacant().with_sprite(s?);
let new = new.with_attr(Owned(true)).unwrap_or(new);
Some(new)
})
} else if z_off == 1 && rng.gen_bool(0.001) {
Some(old.into_vacant().with_sprite(SpriteKind::Scarecrow))
} else {
None
}
}
}