feat: Add notification feature and display notification for berry reward
(#63)
This commit is contained in:
@@ -5,6 +5,7 @@ pub mod grid;
|
||||
pub mod hud;
|
||||
pub mod input;
|
||||
pub mod inventory;
|
||||
pub mod notification;
|
||||
pub mod phase;
|
||||
pub mod pom;
|
||||
pub mod savegame;
|
||||
@@ -19,6 +20,7 @@ pub use grid::GridPlugin;
|
||||
pub use hud::HudPlugin;
|
||||
pub use input::InputPlugin;
|
||||
pub use inventory::InventoryPlugin;
|
||||
pub use notification::NotificationPlugin;
|
||||
pub use phase::PhasePlugin;
|
||||
pub use pom::PomPlugin;
|
||||
pub use savegame::SavegamePlugin;
|
||||
|
||||
74
src/features/notification/components.rs
Normal file
74
src/features/notification/components.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct Notifications {
|
||||
items: Vec<(Notification, NotificationLevel)>,
|
||||
}
|
||||
|
||||
impl Default for Notifications {
|
||||
fn default() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Notifications {
|
||||
fn new(
|
||||
&mut self,
|
||||
level: NotificationLevel,
|
||||
title: Option<impl Into<String>>,
|
||||
message: impl Into<String>,
|
||||
) {
|
||||
self.items.push((
|
||||
Notification {
|
||||
title: title.map(|s| s.into()),
|
||||
message: message.into(),
|
||||
},
|
||||
level,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn drain(&mut self) -> std::vec::Drain<'_, (Notification, NotificationLevel)> {
|
||||
self.items.drain(..)
|
||||
}
|
||||
|
||||
pub fn info(&mut self, title: Option<impl Into<String>>, message: impl Into<String>) {
|
||||
self.new(NotificationLevel::Info, title, message);
|
||||
}
|
||||
|
||||
pub fn warn(&mut self, title: Option<impl Into<String>>, message: impl Into<String>) {
|
||||
self.new(NotificationLevel::Warning, title, message);
|
||||
}
|
||||
|
||||
pub fn error(&mut self, title: Option<impl Into<String>>, message: impl Into<String>) {
|
||||
self.new(NotificationLevel::Error, title, message);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct NotificationContainer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct NotificationLifetime(pub Timer);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Notification {
|
||||
pub title: Option<String>,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum NotificationLevel {
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl NotificationLevel {
|
||||
pub fn bg_color(&self) -> Color {
|
||||
match self {
|
||||
NotificationLevel::Info => Color::srgba(0.0, 0.0, 0.0, 0.7),
|
||||
NotificationLevel::Warning => Color::srgba(1.0, 1.0, 0.0, 0.7),
|
||||
NotificationLevel::Error => Color::srgba(1.0, 0.0, 0.0, 0.7),
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/features/notification/mod.rs
Normal file
58
src/features/notification/mod.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use crate::{
|
||||
features::notification::ui::{notification_container, spawn_notification},
|
||||
prelude::*,
|
||||
};
|
||||
use components::{NotificationContainer, NotificationLifetime, Notifications};
|
||||
|
||||
pub mod components;
|
||||
pub mod ui;
|
||||
|
||||
pub struct NotificationPlugin;
|
||||
|
||||
impl Plugin for NotificationPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<Notifications>()
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(Update, handle_notification);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawns the notification container up
|
||||
fn setup(mut commands: Commands) {
|
||||
commands.spawn(notification_container());
|
||||
}
|
||||
|
||||
// Spawns/Despawns UI elements for each item in the `Notifications` Resource
|
||||
fn handle_notification(
|
||||
mut commands: Commands,
|
||||
mut notifications: ResMut<Notifications>,
|
||||
container_q: Query<Entity, With<NotificationContainer>>,
|
||||
mut notification_entities: Query<(Entity, &mut NotificationLifetime)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if let Some(container) = container_q.iter().next() {
|
||||
let mut spawned_entities = Vec::new();
|
||||
|
||||
for (content, level) in notifications.drain() {
|
||||
let entity = spawn_notification(&mut commands, content, level);
|
||||
commands.entity(container).add_child(entity);
|
||||
spawned_entities.push(entity);
|
||||
}
|
||||
|
||||
for entity in spawned_entities {
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(NotificationLifetime(Timer::from_seconds(
|
||||
5.0,
|
||||
TimerMode::Once,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, mut timer) in notification_entities.iter_mut() {
|
||||
timer.0.tick(time.delta());
|
||||
if timer.0.is_finished() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/features/notification/ui.rs
Normal file
60
src/features/notification/ui.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use super::components::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn notification_container() -> impl Bundle {
|
||||
(
|
||||
NotificationContainer,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: px(0),
|
||||
left: px(0),
|
||||
padding: UiRect::all(px(5)),
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: px(5),
|
||||
..default()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spawn_notification(
|
||||
commands: &mut Commands,
|
||||
content: Notification,
|
||||
level: NotificationLevel,
|
||||
) -> Entity {
|
||||
let entity = commands
|
||||
.spawn((
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: px(5),
|
||||
padding: UiRect::all(px(10)),
|
||||
margin: UiRect::all(px(2)),
|
||||
width: px(400),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(level.bg_color()),
|
||||
BorderRadius::all(px(4)),
|
||||
))
|
||||
.with_children(|p| {
|
||||
if let Some(title) = content.title {
|
||||
p.spawn((
|
||||
Text::new(format!("{}\n", title)),
|
||||
TextFont {
|
||||
font_size: 20.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::WHITE),
|
||||
));
|
||||
}
|
||||
p.spawn((
|
||||
Text::new(content.message),
|
||||
TextFont {
|
||||
font_size: 16.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::WHITE),
|
||||
));
|
||||
})
|
||||
.id();
|
||||
|
||||
entity
|
||||
}
|
||||
@@ -75,6 +75,8 @@ fn tick_timer(
|
||||
mut commands: Commands,
|
||||
mut items_query: Query<&mut ItemStack>,
|
||||
mut session_tracker: ResMut<SessionTracker>,
|
||||
game_config: Res<GameConfig>,
|
||||
mut notifications: ResMut<Notifications>,
|
||||
) {
|
||||
let delta = time.delta_secs();
|
||||
let phase = &mut phase_res.0;
|
||||
@@ -98,6 +100,18 @@ fn tick_timer(
|
||||
config.berries_per_focus_minute as i32,
|
||||
);
|
||||
session_tracker.total_berries_earned += config.berries_per_focus_minute;
|
||||
|
||||
let berries_name = match config.berries_per_focus_minute {
|
||||
1 => ItemType::Berry.singular(&game_config),
|
||||
_ => ItemType::Berry.plural(&game_config),
|
||||
};
|
||||
notifications.info(
|
||||
Some("Fokus Belohnung"),
|
||||
format!(
|
||||
"Du hast {} {} als Belohnung für das Abschließen einer Minute der Fokus-Phase erhalten!",
|
||||
config.berries_per_focus_minute, berries_name
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if *duration <= 0.0 {
|
||||
|
||||
@@ -35,6 +35,7 @@ fn main() {
|
||||
features::InventoryPlugin,
|
||||
features::ShopPlugin,
|
||||
features::WonderEventPlugin,
|
||||
features::NotificationPlugin,
|
||||
))
|
||||
.insert_resource(config)
|
||||
.add_systems(Startup, overwrite_default_font)
|
||||
|
||||
@@ -8,6 +8,7 @@ pub use crate::features::{
|
||||
utils::{grid_to_world_coords, world_to_grid_coords},
|
||||
},
|
||||
inventory::components::{Inventory, ItemStack, ItemType},
|
||||
notification::components::Notifications,
|
||||
phase::components::{CurrentPhase, Phase},
|
||||
pom::{
|
||||
components::{GridPosition, MovingState, Pom},
|
||||
|
||||
Reference in New Issue
Block a user