1+ use std:: collections:: HashMap ;
2+
13use bevy:: { asset:: LoadedFolder , prelude:: * , sprite:: Anchor } ;
2- use rand :: Rng ;
4+ use noise :: { Fbm , MultiFractal , NoiseFn , Perlin } ;
35use serde:: Deserialize ;
46
57use crate :: {
68 assets:: { loaders:: toml:: TomlAssetPlugin , tracking:: LoadResource } ,
79 gameplay:: {
810 sprite_sort:: { YSortSprite , ZIndexSprite } ,
911 world:: {
10- WorldSpawnSystems ,
1112 construction:: Constructions ,
12- tilemap:: { CHUNK_SIZE , TILE_SIZE , coord:: Coord } ,
13+ tilemap:: {
14+ CHUNK_SIZE , TILE_SIZE ,
15+ chunk:: { Chunk , ChunkLoaded , ChunkUnloaded } ,
16+ coord:: Coord ,
17+ } ,
1318 } ,
1419 } ,
1520 screens:: Screen ,
1621} ;
1722
1823pub fn plugin ( app : & mut App ) {
1924 app. add_plugins ( TomlAssetPlugin :: < DepositDef > :: extensions ( & [ "deposit.toml" ] ) ) ;
20-
2125 app. load_resource :: < DepositAssets > ( ) ;
2226
23- app. add_systems (
24- OnEnter ( Screen :: Gameplay ) ,
25- spawn_deposits. in_set ( WorldSpawnSystems :: SpawnDeposits ) ,
26- ) ;
27+ app. init_resource :: < DepositNoise > ( ) ;
28+ app. add_systems ( OnEnter ( Screen :: Gameplay ) , create_noise) ;
29+
30+ app. add_observer ( spawn_deposits) ;
31+ app. add_observer ( unload_deposits) ;
2732}
2833
2934#[ derive( Asset , Deserialize , Reflect ) ]
3035pub struct DepositDef {
3136 pub id : String ,
3237 pub name : String ,
3338 pub recipe_id : String ,
34- pub quantity : u32 ,
39+ pub seed : u32 ,
3540}
3641
3742#[ derive( Asset , Resource , Reflect , Clone ) ]
@@ -56,38 +61,100 @@ impl FromWorld for DepositAssets {
5661#[ reflect( Component ) ]
5762pub struct DepositRecipe ( pub String ) ;
5863
64+ #[ derive( Resource , Debug , Default ) ]
65+ pub struct DepositNoise {
66+ pub noises : HashMap < AssetId < DepositDef > , Fbm < Perlin > > ,
67+ }
68+
69+ fn create_noise (
70+ mut deposit_noise : ResMut < DepositNoise > ,
71+ deposit_definitions : Res < Assets < DepositDef > > ,
72+ ) {
73+ for ( deposit_id, deposit_def) in deposit_definitions. iter ( ) {
74+ let fbm = Fbm :: < Perlin > :: new ( deposit_def. seed )
75+ . set_octaves ( 5 )
76+ . set_frequency ( 1.0 / 70.0 )
77+ . set_lacunarity ( 2.5 )
78+ . set_persistence ( 0.45 ) ;
79+
80+ deposit_noise. noises . insert ( deposit_id, fbm) ;
81+ }
82+ }
83+
5984fn spawn_deposits (
85+ chunk_loaded : On < ChunkLoaded > ,
86+ chunk_query : Query < & Chunk > ,
6087 mut commands : Commands ,
6188 deposit_definitions : Res < Assets < DepositDef > > ,
6289 asset_server : Res < AssetServer > ,
90+ deposit_noise : Res < DepositNoise > ,
91+ mut constructions : ResMut < Constructions > ,
92+ ) {
93+ let chunk = chunk_query. get ( chunk_loaded. chunk ) . unwrap ( ) ;
94+
95+ let absolute_chunk_position = chunk. 0 * CHUNK_SIZE . as_ivec2 ( ) ;
96+
97+ for ( deposit_id, deposit_def) in deposit_definitions. iter ( ) {
98+ let Some ( fbm) = deposit_noise. noises . get ( & deposit_id) else {
99+ return ;
100+ } ;
101+
102+ for x in 0 ..CHUNK_SIZE . x {
103+ for y in 0 ..CHUNK_SIZE . y {
104+ let absolute_tile_pos = absolute_chunk_position + ivec2 ( x as i32 , y as i32 ) ;
105+ if constructions. contains_key ( & absolute_tile_pos) {
106+ continue ;
107+ }
108+
109+ let value = fbm. get ( absolute_tile_pos. as_dvec2 ( ) . into ( ) ) ;
110+ let normalized_value = ( value + 1.0 ) / 2.0 ;
111+ if normalized_value < 0.75 {
112+ continue ;
113+ }
114+
115+ let entity = commands
116+ . spawn ( (
117+ Name :: new ( deposit_def. name . clone ( ) ) ,
118+ Coord ( absolute_tile_pos) ,
119+ Anchor ( Vec2 :: new ( 0.0 , -0.25 ) ) ,
120+ YSortSprite ,
121+ ZIndexSprite ( 10 ) ,
122+ Sprite {
123+ image : asset_server
124+ . load ( format ! ( "sprites/deposits/{}.png" , deposit_def. id) ) ,
125+ custom_size : Vec2 :: new ( TILE_SIZE . x , TILE_SIZE . y ) . into ( ) ,
126+ ..default ( )
127+ } ,
128+ DepositRecipe ( deposit_def. recipe_id . clone ( ) ) ,
129+ ) )
130+ . id ( ) ;
131+
132+ constructions. insert ( absolute_tile_pos, entity) ;
133+ }
134+ }
135+ }
136+ }
137+
138+ fn unload_deposits (
139+ chunk_unloaded : On < ChunkUnloaded > ,
140+ chunk_query : Query < & Chunk > ,
63141 mut constructions : ResMut < Constructions > ,
142+ mut commands : Commands ,
64143) {
65- let mut rng = rand:: rng ( ) ;
66-
67- for ( _, deposit) in deposit_definitions. iter ( ) {
68- for _ in 0 ..deposit. quantity {
69- let pos = IVec2 :: new (
70- rng. random_range ( -( CHUNK_SIZE . x as i32 ) ..CHUNK_SIZE . x as i32 ) ,
71- rng. random_range ( -( CHUNK_SIZE . y as i32 ) ..CHUNK_SIZE . y as i32 ) ,
72- ) ;
73-
74- let entity = commands
75- . spawn ( (
76- Name :: new ( deposit. name . clone ( ) ) ,
77- Coord ( pos) ,
78- Anchor ( Vec2 :: new ( 0.0 , -0.25 ) ) ,
79- YSortSprite ,
80- ZIndexSprite ( 10 ) ,
81- Sprite {
82- image : asset_server. load ( format ! ( "sprites/deposits/{}.png" , deposit. id) ) ,
83- custom_size : Vec2 :: new ( TILE_SIZE . x , TILE_SIZE . y ) . into ( ) ,
84- ..default ( )
85- } ,
86- DepositRecipe ( deposit. recipe_id . clone ( ) ) ,
87- ) )
88- . id ( ) ;
89-
90- constructions. insert ( pos, entity) ;
144+ let chunk = chunk_query. get ( chunk_unloaded. chunk ) . unwrap ( ) ;
145+
146+ let absolute_chunk_position = chunk. 0 * CHUNK_SIZE . as_ivec2 ( ) ;
147+
148+ for x in 0 ..CHUNK_SIZE . x {
149+ for y in 0 ..CHUNK_SIZE . y {
150+ let absolute_tile_pos = absolute_chunk_position + ivec2 ( x as i32 , y as i32 ) ;
151+
152+ let Some ( construction) = constructions. get ( & absolute_tile_pos) else {
153+ continue ;
154+ } ;
155+
156+ commands. entity ( * construction) . despawn ( ) ;
157+ constructions. remove ( & absolute_tile_pos) ;
91158 }
92159 }
93160}
0 commit comments