use std::ops::Deref;
use bevy::prelude::*;
use bevy::asset::Handle;
use bevy_asset_loader::prelude::*;
use crate::scene::loading::AssetState;
#[derive(Debug, AssetCollection, Resource)]
pub struct SeedBankAssets {
#[asset(path = "seeds.png")]
pub seed_packet_background: Handle<TextureAtlasLayout>,
#[asset(path = "SeedPacket_Larger.png")]
pub seed_packet_large: Handle<Image>,
#[asset(path = "SeedPacketSilhouette.png")]
pub seed_packet_silhouette: Handle<Image>,
#[asset(path = "SeedBank.png")]
pub seed_bank_background: Handle<Image>,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct SeedBankPlugin;
impl Plugin for SeedBankPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(GridInfo::default())
.add_loading_state(LoadingState::new(AssetState::AssetLoading)
.continue_to_state(AssetState::AssetReady)
.on_failure_continue_to_state(AssetState::LoadFailure)
.load_collection::<SeedBankAssets>())
.add_systems(OnEnter(AssetState::AssetReady), spawn_seed_bank)
.add_systems(Update, update_seed_bank.run_if(in_state(AssetState::AssetReady)));
}
}
#[derive(Debug, Copy, Clone, Component)]
pub struct SeedBank {
packet_number: usize,
}
#[derive(Debug, Copy, Clone, Component)]
pub struct SeedPacketArea;
#[derive(Debug, Copy, Clone, Component)]
pub struct SeedPacketIndex(pub usize);
#[derive(Copy, Clone, PartialEq, Resource)]
struct GridInfo {
position: Vec2,
bank_size: Vec2,
packet_size: Vec2,
packet_separator: f32,
seed_area_top_left: Vec2,
natural_packet_count: usize,
extension_packet_count: usize,
extension_left_padding: f32,
}
impl Default for GridInfo {
fn default() -> Self {
GridInfo {
position: Vec2::new(230.0, 0.0),
bank_size: Vec2::new(446.0, 87.0),
packet_size: Vec2::new(50.0, 70.0),
packet_separator: 1.0,
seed_area_top_left: Vec2::new(79.0, 7.0),
natural_packet_count: 7,
extension_packet_count: 6,
extension_left_padding: 4.0,
}
}
}
impl GridInfo {
fn packet_area_width(&self, packet_count: usize) -> f32 {
packet_count as f32 * (self.packet_size.x + self.packet_separator)
}
fn extension_offset(&self, packet_count: usize) -> f32 {
assert!(packet_count <= self.extension_packet_count);
let left_cut = self.natural_packet_count - packet_count;
let left_cut_width = left_cut as f32 * (self.packet_size.x + self.packet_separator);
self.seed_area_top_left.x + left_cut_width - self.extension_left_padding
}
fn extension_width(&self, packet_count: usize) -> f32 {
self.bank_size.x - self.extension_offset(packet_count)
}
fn background_at(&self, index: usize) -> f32 {
assert_ne!(index, 0);
self.seed_area_top_left.x + self.packet_area_width(self.natural_packet_count)
+ (index - 1) as f32 * self.packet_area_width(self.extension_packet_count)
- self.extension_left_padding
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SeedPacket {
Gray,
Purple,
Green,
Crater,
Refresh,
Sun,
Diamond,
Snorkel,
Trophy,
}
fn spawn_seed_bank(
seed_bank_assets: Res<SeedBankAssets>,
grid_info: Res<GridInfo>,
mut commands: Commands,
) {
let bank = commands.spawn((
SeedBank { packet_number: 10 },
NodeBundle {
style: Style {
left: Val::Px(grid_info.position.x),
top: Val::Px(grid_info.position.y),
..default()
},
..default()
},
)).id();
let container = commands.spawn(NodeBundle {
background_color: Color::NONE.into(),
z_index: ZIndex::Local(129),
style: Style {
flex_direction: FlexDirection::Row,
position_type: PositionType::Absolute,
left: Val::Px(grid_info.seed_area_top_left.x),
top: Val::Px(grid_info.seed_area_top_left.y),
..default()
},
..default()
}).id();
commands.entity(bank).add_child(container);
commands.entity(container).with_children(|parent| for i in 0..10 {
parent.spawn((
ImageBundle {
image: UiImage::new(seed_bank_assets.seed_packet_large.clone()),
style: Style {
margin: UiRect {
right: Val::Px(grid_info.packet_separator),
..default()
},
width: Val::Px(grid_info.packet_size.x),
height: Val::Px(grid_info.packet_size.y),
..default()
},
..default()
},
SeedPacketIndex(i),
));
});
}
#[derive(Copy, Clone, Component)]
struct Clipped;
#[derive(Copy, Clone, Component)]
struct BackgroundIndex(u8);
fn update_seed_bank(
seed_bank_assets: Res<SeedBankAssets>,
grid_info: Res<GridInfo>,
seed_bank: Query<(Entity, &SeedBank, &Children), Changed<SeedBank>>,
mut background: Query<(Entity, &Children, &mut Style, &mut ZIndex, &BackgroundIndex), Without<Clipped>>,
mut extension: Query<&mut Style, With<Clipped>>,
mut commands: Commands,
) {
for (bank_entity, seed_bank, children) in &seed_bank {
let remaining = seed_bank.packet_number.saturating_sub(grid_info.natural_packet_count);
let complete_extensions = remaining / grid_info.extension_packet_count;
let remaining_packets = remaining % grid_info.extension_packet_count;
let background_expected = complete_extensions + 1 + if remaining_packets != 0 { 1 } else { 0 };
let mut background_count = 0;
for child in children {
let Ok(child) = background.get_mut(*child) else { continue; };
let (this, children, mut style, mut z_index, &BackgroundIndex(index)) = child;
if index as usize >= background_expected {
commands.entity(this).despawn_recursive();
continue;
}
background_count += 1;
*z_index = ZIndex::Local(index as i32);
if index == 0 { continue; }
style.left = Val::Px(grid_info.background_at(index as usize));
let complete = index as usize <= complete_extensions;
let packet_count = if complete {
grid_info.extension_packet_count
} else {
remaining_packets
};
style.width = Val::Px(grid_info.extension_width(packet_count));
let &[clipped] = children.deref() else { unreachable!() };
let mut clipped = extension.get_mut(clipped).unwrap();
clipped.left = Val::Px(-grid_info.extension_offset(packet_count));
}
let bank_width = Val::Px(grid_info.bank_size.x);
let bank_height = Val::Px(grid_info.bank_size.y);
if background_count == 0 {
let background = commands.spawn((
BackgroundIndex(0),
ImageBundle {
image: UiImage::new(seed_bank_assets.seed_bank_background.clone()),
z_index: ZIndex::Local(0),
style: Style {
position_type: PositionType::Absolute,
left: Val::Px(0.0),
top: Val::Px(0.0),
width: bank_width,
height: bank_height,
min_width: bank_width,
min_height: bank_height,
max_width: bank_width,
max_height: bank_height,
..default()
},
..default()
},
)).id();
commands.entity(bank_entity).add_child(background);
background_count += 1;
}
for index in background_count..background_expected {
let complete = index <= complete_extensions;
let packet_count = if complete {
grid_info.extension_packet_count
} else {
remaining_packets
};
let container = commands.spawn((
BackgroundIndex(index.try_into().unwrap()),
NodeBundle {
z_index: ZIndex::Local(index as i32),
style: Style {
overflow: Overflow::clip(),
position_type: PositionType::Absolute,
left: Val::Px(grid_info.background_at(index)),
width: Val::Px(grid_info.extension_width(packet_count)),
height: Val::Px(grid_info.bank_size.y),
..default()
},
..default()
},
)).id();
let image = commands.spawn((
Clipped,
ImageBundle {
image: UiImage::new(seed_bank_assets.seed_bank_background.clone()),
style: Style {
left: Val::Px(-grid_info.extension_offset(packet_count)),
width: bank_width,
height: bank_height,
min_width: bank_width,
min_height: bank_height,
max_width: bank_width,
max_height: bank_height,
..default()
},
..default()
},
)).id();
commands.entity(bank_entity).add_child(container);
commands.entity(container).add_child(image);
}
}
}