2024-08-03 00:03:44 -05:00
use crate ::View ;
2024-08-02 16:32:28 -05:00
use crate ::{ inspector ::HideFromInspector , prelude ::* } ;
2024-08-03 00:52:19 -05:00
use bevy ::ecs ::system ::SystemState ;
2024-08-04 20:26:46 -05:00
use bevy ::ecs ::world ::CommandQueue ;
2024-08-03 00:03:44 -05:00
use bevy ::tasks ::{ block_on , futures_lite ::future , AsyncComputeTaskPool , Task } ;
2024-08-02 16:43:30 -05:00
use bevy_ecs_tilemap ::prelude ::* ;
2024-08-02 13:40:43 -05:00
use rand ::prelude ::* ;
use rand_pcg ::Pcg64 ;
use rand_seeder ::Seeder ;
2024-08-03 00:03:44 -05:00
use std ::sync ::mpsc ::{ channel , Receiver } ;
use std ::sync ::Mutex ;
2024-08-02 13:40:43 -05:00
#[ derive(Component, Debug) ]
pub struct Tilemap ;
2024-08-03 00:03:44 -05:00
#[ derive(Component, Debug) ]
pub struct TileChan ( Mutex < Receiver < TileBundle > > ) ;
2024-08-02 23:31:32 -05:00
2024-08-02 23:32:41 -05:00
#[ derive(Component, Debug) ]
2024-08-04 20:26:46 -05:00
pub struct TileLoaderTask ( Task < ( ) > ) ;
2024-08-02 16:32:28 -05:00
2024-08-02 23:32:41 -05:00
pub fn init ( mut commands : Commands , assets : Res < AssetServer > ) {
2024-08-02 17:04:16 -05:00
// TODO: I'm pretty determined to not have this sieze up the game despite the large number of entities being added. Should work with a "loading" screen and doing this in the background?
2024-08-02 13:40:43 -05:00
2024-08-02 16:32:28 -05:00
let mut rng : Pcg64 = Seeder ::from ( " default_seed " ) . make_rng ( ) ;
2024-08-02 16:43:30 -05:00
let size = TilemapSize ::new ( 1024 , 1024 ) ;
2024-08-02 23:16:54 -05:00
let tile_size = TilemapTileSize ::new ( 16. , 16. ) ;
let grid_size = TilemapGridSize ::new ( 16. , 16. ) ;
let map_type = TilemapType ::Square ;
let texture = TilemapTexture ::Single ( assets . load ( " img/Tileset Grass.png " ) ) ;
let storage = TileStorage ::empty ( size ) ;
let tilemap = commands
. spawn ( (
Tilemap ,
Name ::new ( " Tilemap " ) ,
TilemapBundle {
grid_size ,
map_type ,
size ,
storage ,
texture ,
tile_size ,
transform : get_tilemap_center_transform ( & size , & grid_size , & map_type , f32 ::MIN ) ,
.. Default ::default ( )
} ,
) )
. id ( ) ;
2024-08-02 22:53:52 -05:00
2024-08-02 23:16:54 -05:00
let pool = AsyncComputeTaskPool ::get ( ) ;
2024-08-03 00:03:44 -05:00
let ( tx , rx ) = channel ::< TileBundle > ( ) ;
2024-08-02 23:16:54 -05:00
let task = pool . spawn ( async move {
for x in 0 .. size . x {
for y in 0 .. size . y {
let position = TilePos ::new ( x , y ) ;
let texture_index = TileTextureIndex ( if rng . gen_range ( 0 .. 1000 ) > 925 {
rng . gen_range ( 0 .. ( 16 * 8 ) )
} else {
0
2024-08-02 17:04:16 -05:00
} ) ;
2024-08-02 23:32:41 -05:00
tx . send ( TileBundle {
2024-08-02 23:16:54 -05:00
position ,
tilemap_id : TilemapId ( tilemap ) ,
texture_index ,
2024-08-02 23:32:41 -05:00
.. Default ::default ( )
} )
. unwrap ( ) ;
2024-08-02 23:16:54 -05:00
}
2024-08-02 17:04:16 -05:00
}
2024-08-03 00:03:44 -05:00
return ( ) ;
2024-08-02 23:16:54 -05:00
} ) ;
2024-08-03 00:03:44 -05:00
commands
. entity ( tilemap )
2024-08-04 20:26:46 -05:00
. insert ( ( TileLoaderTask ( task ) , TileChan ( Mutex ::new ( rx ) ) ) ) ;
2024-08-02 21:43:30 -05:00
}
2024-08-02 17:04:16 -05:00
2024-08-03 00:19:54 -05:00
const MAX_TILES_TO_LOAD_IN_ONE_UPDATE : usize = 5_000 ;
2024-08-03 00:03:44 -05:00
pub fn tile_loaders (
2024-08-03 00:52:19 -05:00
world : & mut World ,
2024-08-04 20:26:46 -05:00
tilemap : QueryState < Entity , With < Tilemap > > ,
storage : & mut QueryState < & mut TileStorage , With < Tilemap > > ,
chan : QueryState < & TileChan , With < Tilemap > > ,
task : QueryState < & mut TileLoaderTask , With < Tilemap > > ,
params : & mut SystemState < ResMut < NextState < View > > > ,
2024-08-03 00:03:44 -05:00
) {
2024-08-04 20:26:46 -05:00
// TODO: this state should maybe be converted to a resource or something?
let mut tiles_done = false ;
let mut task_done = false ;
type AddingTile = ( HideFromInspector , TileBundle ) ;
type AddedTile < ' a > = ( Entity , & ' a TilePos ) ;
let mut tiles_to_add : Vec < AddingTile > = Vec ::with_capacity ( MAX_TILES_TO_LOAD_IN_ONE_UPDATE ) ;
let mut added_tiles : Vec < AddedTile > = Vec ::with_capacity ( MAX_TILES_TO_LOAD_IN_ONE_UPDATE ) ;
2024-08-03 00:52:19 -05:00
2024-08-04 20:26:46 -05:00
// let mut task = task;
// {
// task_done = true;
// let mut task = task_query.get_single();
// if let Ok(task) = task {
// let poll = future::poll_once(task.0);
// if block_on(poll).is_some() {
// task_done = true;
// // task is done, remove it so we don't poll it again
// world.entity_mut(tilemap).remove::<TileLoaderTask>();
// }
// } else {
// next_view.set(View::InGame)
// }
// let (tilemap, mut storage) = entity_query.single_mut();
// let tiles: Vec<TileBundle> = vec![];
// if let Ok(channel) = channel_query.get_single_mut() {
// let channel = channel.0.lock().unwrap();
// tiles_done = false;
// let mut counter = 0;
// loop {
// match channel.recv() {
// Ok(tile) => {
// let position = &tile.position;
// let tile = world.spawn((HideFromInspector, tile)).id();
// storage.set(position, tile);
// counter += 1;
// if counter >= MAX_TILES_TO_LOAD_IN_ONE_UPDATE {
// info!("Finished {}!", counter);
// break;
// }
// }
// Err(_) => {
// world.entity_mut(tilemap).remove::<TileChan>();
// if counter > 0 {
// info!("Finished {counter}! Probably finishing mapgen soon...");
// }
// // the channel is likely closed, so let's note that and be done
// tiles_done = true;
// break;
// }
// }
// }
// }
for tile in tiles_to_add {
added_tiles . push ( ( id , & tile . 1. position ) ) ;
let id = world . spawn ( tile ) . id ( ) ;
2024-08-03 00:03:44 -05:00
}
2024-08-04 20:26:46 -05:00
// world.spawn_batch(
// tiles
// .into_iter()
// .map(|t| (HideFromInspector, t))
// .into_iter(),
// );
{
let mut storage = storage . single_mut ( world ) ;
// storage.set(&
let mut next_view = params . get_mut ( world ) ;
if task_done & & tiles_done {
next_view . set ( View ::InGame )
}
2024-08-03 00:03:44 -05:00
}
2024-08-02 21:43:30 -05:00
}
2024-08-02 16:43:30 -05:00
2024-08-02 13:40:43 -05:00
pub fn exit ( mut commands : Commands , q : Query < Entity , With < Tilemap > > ) {
for id in q . iter ( ) {
commands . entity ( id ) . despawn_recursive ( ) ;
}
}