use std::fmt::Debug;
use super::*;
use fxhash::FxHashMap;
use fyrox_autotile::{
AutoPatternConstraint, AutoPatternValueMap, AutoTerrainPatternMap, AutoTileContext, AutoTiler,
HashConstraintMap, HashWfcConstraint, OffsetPosition, PatternSource, TileConstraint,
Vector2Offset, WfcConstrain, WfcFailure, WfcPropagator,
};
use fyrox_core::log::Log;
use crate::core::rand::Rng;
pub use fyrox_autotile::PatternBits;
impl From<NineI8> for PatternBits {
fn from(value: NineI8) -> Self {
Self(value.0)
}
}
pub type TileTerrainId = i8;
#[derive(Debug, Default, Clone)]
pub struct TileSetAutoTiler(AutoTiler<Vector2<i32>, PatternBits>);
#[derive(Default, Clone)]
pub struct TileSetAutoTileContext(
AutoTileContext<TileTerrainId, PatternBits, TileDefinitionHandle>,
);
impl Debug for TileSetAutoTileContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
pub type TileSetConstraintMap = HashConstraintMap<Vector2<i32>, TileTerrainId, PatternBits>;
pub type TileSetTerrainPatternMap = AutoTerrainPatternMap<TileTerrainId, PatternBits>;
pub type TileSetAutoValueMap = AutoPatternValueMap<PatternBits, TileDefinitionHandle>;
pub type TileSetAutoTileConstraint<'a, 'b> =
AutoPatternConstraint<'a, 'b, Vector2<i32>, TileTerrainId, PatternBits>;
#[derive(Default)]
pub struct TileSetWfcConstraint(HashWfcConstraint<PatternBits, TileDefinitionHandle>);
impl Deref for TileSetWfcConstraint {
type Target = HashWfcConstraint<PatternBits, TileDefinitionHandle>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TileSetWfcConstraint {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl TileSetWfcConstraint {
pub fn fill_pattern_map(
&mut self,
tile_set: &TileSet,
pattern_property: TileSetPropertyNine,
frequency_property: Option<TileSetPropertyF32>,
terrain_freq: &FxHashMap<TileTerrainId, f32>,
) -> Result<(), FillPatternMapError> {
self.clear();
if let Some(&frequency) = terrain_freq.get(&0) {
self.add(
PatternBits::default(),
frequency,
TileDefinitionHandle::EMPTY,
);
}
if tile_set
.find_property(*pattern_property.property_uuid())
.is_none()
{
return Err(FillPatternMapError::PatternInvalidId);
}
if let Some(id) = frequency_property {
if tile_set.find_property(*id.property_uuid()).is_none() {
return Err(FillPatternMapError::FrequencyInvalidId);
}
}
for handle in tile_set.all_tiles() {
let frequency = if let Some(id) = frequency_property {
id.get_from_tile_set(tile_set, handle)
.map_err(|_| FillPatternMapError::FrequencyWrongType)?
} else {
1.0
};
if frequency <= 0.0 {
continue;
}
let pattern: NineI8 = pattern_property
.get_from_tile_set(tile_set, handle)
.map_err(|_| FillPatternMapError::PatternWrongType)?;
let pattern: PatternBits = pattern.into();
let center = pattern.center();
if center != 0 {
if let Some(terrain_frequency) = terrain_freq.get(¢er) {
self.add(pattern, frequency * terrain_frequency, handle);
}
}
}
self.finalize_with_terrain_normalization(PatternBits::center);
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum FillPatternMapError {
FrequencyInvalidId,
FrequencyWrongType,
PatternInvalidId,
PatternWrongType,
}
impl Error for FillPatternMapError {}
impl Display for FillPatternMapError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FillPatternMapError::FrequencyInvalidId => write!(
f,
"The property UUID for the frequency does not match any property in the tile set."
),
FillPatternMapError::FrequencyWrongType => {
write!(f, "The frequency property should be an f32.")
}
FillPatternMapError::PatternInvalidId => write!(
f,
"The property UUID for the pattern does not match any property in the tile set."
),
FillPatternMapError::PatternWrongType => {
write!(f, "The pattern property should be a nine-slice property.")
}
}
}
}
impl Deref for TileSetAutoTileContext {
type Target = AutoTileContext<TileTerrainId, PatternBits, TileDefinitionHandle>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TileSetAutoTileContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl TileSetAutoTileContext {
pub fn fill_pattern_map(
&mut self,
tile_set: &TileSet,
pattern_property: TileSetPropertyNine,
frequency_property: Option<TileSetPropertyF32>,
) -> Result<(), FillPatternMapError> {
self.clear();
self.add(0, PatternBits::default(), 1.0, TileDefinitionHandle::EMPTY);
if tile_set
.find_property(*pattern_property.property_uuid())
.is_none()
{
return Err(FillPatternMapError::PatternInvalidId);
}
if let Some(id) = frequency_property {
if tile_set.find_property(*id.property_uuid()).is_none() {
return Err(FillPatternMapError::FrequencyInvalidId);
}
}
for handle in tile_set.all_tiles() {
let frequency = if let Some(id) = frequency_property {
id.get_from_tile_set(tile_set, handle)
.map_err(|_| FillPatternMapError::FrequencyWrongType)?
} else {
1.0
};
if frequency <= 0.0 {
continue;
}
let pattern: NineI8 = pattern_property
.get_from_tile_set(tile_set, handle)
.map_err(|_| FillPatternMapError::PatternWrongType)?;
let pattern: PatternBits = pattern.into();
let center = pattern.center();
if center != 0 {
self.add(center, pattern, frequency, handle);
}
}
self.sort();
Ok(())
}
}
impl Deref for TileSetAutoTiler {
type Target = AutoTiler<Vector2<i32>, PatternBits>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TileSetAutoTiler {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl TileSetAutoTiler {
pub fn apply_autotile_to_update<R: Rng + ?Sized>(
&self,
rng: &mut R,
value_map: &TileSetAutoValueMap,
update: &mut MacroTilesUpdate,
) {
for (pos, pat) in self.iter() {
let Some(handle) = value_map
.get(pat)
.and_then(|ps| ps.get_random(rng))
.cloned()
else {
continue;
};
let source = update.get(pos).cloned().flatten().and_then(|el| el.source);
let handle = if handle.is_empty() {
None
} else {
Some(StampElement { handle, source })
};
_ = update.insert(*pos, handle);
}
}
}
pub struct TileSetPatternSource<'a, 'b, 'c> {
pub tile_map: &'a TileMap,
pub tile_set: &'b TileSet,
pub update: &'c MacroTilesUpdate,
pub property_id: TileSetPropertyNine,
}
impl TileSetPatternSource<'_, '_, '_> {
fn pattern_at(&self, position: &Vector2<i32>) -> Result<PatternBits, TilePropertyError> {
let Some(element) = self
.update
.get(position)
.cloned()
.unwrap_or_else(|| self.tile_map.tile_handle(*position).map(|h| h.into()))
else {
return Ok(PatternBits::default());
};
self.property_id
.get_from_tile_set(self.tile_set, element.handle)
.map(|v| v.into())
}
}
impl PatternSource for TileSetPatternSource<'_, '_, '_> {
type Position = Vector2<i32>;
type Terrain = TileTerrainId;
type Pattern = PatternBits;
fn get_terrain(&self, position: &Vector2<i32>) -> Option<TileTerrainId> {
match self.pattern_at(position) {
Ok(pattern) => Some(pattern.center()),
Err(err) => {
Log::err(err.to_string());
None
}
}
}
fn get(&self, position: &Vector2<i32>) -> TileConstraint<TileTerrainId, PatternBits> {
match self.pattern_at(position) {
Ok(pattern) => TileConstraint::Pattern(pattern),
Err(err) => {
Log::err(err.to_string());
TileConstraint::None
}
}
}
}
#[derive(Debug, Default, Clone)]
pub struct TileSetWfcPropagator {
propagator: WfcPropagator<Vector2<i32>, PatternBits>,
edge_restrictions: FxHashMap<Vector2<i32>, PatternBits>,
}
impl Deref for TileSetWfcPropagator {
type Target = WfcPropagator<Vector2<i32>, PatternBits>;
fn deref(&self) -> &Self::Target {
&self.propagator
}
}
impl DerefMut for TileSetWfcPropagator {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.propagator
}
}
impl TileSetWfcPropagator {
pub fn constrain_edges<Con>(
&mut self,
tile_set: &TileSet,
pattern_property: TileSetPropertyNine,
tile_map: &TileMap,
update: &MacroTilesUpdate,
constraint: &Con,
) -> Result<(), WfcFailure>
where
Con: WfcConstrain<Pattern = PatternBits, Offset = Vector2Offset>,
{
let tiles = tile_map.tiles();
let tiles = tiles.map(|r| r.data_ref());
let mut edge_restrictions = std::mem::take(&mut self.edge_restrictions);
edge_restrictions.clear();
for &p in self.positions() {
for offset in Vector2::all_offsets() {
let p = p + offset;
if self.edge_restrictions.contains_key(&p) {
continue;
}
if self.contains_cell(&p) {
continue;
}
let handle = update.get(&p).map(|el| {
el.as_ref()
.map(|el| el.handle)
.unwrap_or(TileDefinitionHandle::EMPTY)
});
let handle = if let Some(handle) = handle {
handle
} else if let Some(tiles) = tiles.as_ref() {
tiles.get(p).unwrap_or(TileDefinitionHandle::EMPTY)
} else {
TileDefinitionHandle::EMPTY
};
let pattern = pattern_property
.get_from_tile_set(tile_set, handle)
.unwrap_or_default();
edge_restrictions.insert(p, PatternBits(pattern.into()));
}
}
for (p, pattern) in edge_restrictions.drain() {
self.restrict_edge(&p, &pattern, constraint)?;
}
self.edge_restrictions = edge_restrictions;
Ok(())
}
pub fn apply_autotile_to_update<R: Rng + ?Sized>(
&self,
rng: &mut R,
value_map: &TileSetWfcConstraint,
update: &mut MacroTilesUpdate,
) {
for (pos, pat) in self.assigned_patterns() {
let Some(&handle) = value_map.get_random(rng, pat) else {
continue;
};
let source = update.get(pos).cloned().flatten().and_then(|el| el.source);
let handle = if handle.is_empty() {
None
} else {
Some(StampElement { handle, source })
};
_ = update.insert(*pos, handle);
}
}
pub fn apply_autotile_to_data<R: Rng + ?Sized>(
&self,
rng: &mut R,
value_map: &TileSetWfcConstraint,
data: &mut TileMapData,
) {
for (pos, pat) in self.assigned_patterns() {
let Some(&handle) = value_map.get_random(rng, pat) else {
continue;
};
data.set(*pos, handle);
}
}
}