use std::time::Duration; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy_tweening::lens::{TextColorLens, UiPositionLens}; use bevy_tweening::{Animator, EaseFunction, Tween, TweeningType}; use crate::assets::AssetHandles; use crate::control::ai::{Automated, ShouldAct}; use crate::entities::animations::FrameAnimation; use crate::entities::curses::{CurseEvent, CurseText}; use crate::entities::lifecycle::{GameEntity, Player}; use crate::system::camera::ChaseCam; use crate::system::graphics::LAYER_CREATURE; use crate::world::level_map::GridPosition; const PLAYER_SPRITE: usize = 2; #[derive(SystemParam)] pub struct EntitySpawner<'w, 's> { pub commands: Commands<'w, 's>, pub handles: Res<'w, AssetHandles>, } impl<'w, 's> EntitySpawner<'w, 's> { pub fn spawn_player(&mut self, grid_position: UVec2) { let mut entity = self.commands.spawn(); entity .insert(ChaseCam) .insert(GameEntity) .insert(Player) .insert(ShouldAct) .insert(FrameAnimation::new( vec![PLAYER_SPRITE, PLAYER_SPRITE + 1], Duration::from_millis(250), )) .insert(GridPosition(grid_position)); entity.insert_bundle(SpriteSheetBundle { texture_atlas: self.handles.atlas("creatures"), transform: Transform::from_translation(grid_position.as_vec2().extend(LAYER_CREATURE)), sprite: TextureAtlasSprite { index: PLAYER_SPRITE, ..Default::default() }, ..Default::default() }); } pub fn spawn_creature(&mut self, sprite: usize, grid_position: UVec2) { let mut entity = self.commands.spawn(); entity .insert(GameEntity) .insert(Automated) .insert(FrameAnimation::new( vec![sprite, sprite + 1], Duration::from_millis(250), )) .insert(GridPosition(grid_position)); entity.insert_bundle(SpriteSheetBundle { texture_atlas: self.handles.atlas("creatures"), transform: Transform::from_translation(grid_position.as_vec2().extend(LAYER_CREATURE)), sprite: TextureAtlasSprite { index: sprite, ..Default::default() }, ..Default::default() }); } pub fn spawn_curse_text(&mut self, curse: CurseEvent) { let position_animation = Tween::new( EaseFunction::ExponentialIn, TweeningType::Once, Duration::from_millis(750), UiPositionLens { start: UiRect::new(Val::Auto, Val::Px(20.0), Val::Px(200.0), Val::Auto), end: UiRect::new(Val::Auto, Val::Px(20.0), Val::Px(20.0), Val::Auto), }, ); let colour_in_animation = Tween::new( EaseFunction::SineIn, TweeningType::Once, Duration::from_millis(500), TextColorLens { section: 0, start: (*Color::GOLD.set_a(0.0)).into(), end: (*Color::GOLD.set_a(1.0)).into(), }, ); self.commands .spawn_bundle(NodeBundle { color: Color::rgba(0.0, 0.0, 0.0, 0.0).into(), style: Style { position: UiRect::new(Val::Auto, Val::Px(20.0), Val::Px(100.0), Val::Auto), position_type: PositionType::Absolute, ..Default::default() }, ..Default::default() }) .insert(Animator::new(position_animation)) .insert(CurseText) .with_children(|builder| { builder .spawn_bundle(TextBundle { text: Text::from_section( curse.to_string(), TextStyle { color: (*Color::GOLD.set_a(0.0)), font: self.handles.font("main"), font_size: 32.0, }, ), ..Default::default() }) .insert(Animator::new(colour_in_animation)); }); } }