/* * This file is part of laurelin_client * Copyright (C) 2023 Jonni Liljamo * * Licensed under GPL-3.0-only. * See LICENSE for licensing information. */ use bevy::prelude::*; use bevy_mod_picking::prelude::*; use bevy_rapier3d::prelude::*; use bevy_text_mesh::prelude::*; use crate::{ api::game::{Action, Command}, game_status::PlayerState, plugins::GameActionCreateCallEvent, Global, }; use super::{ card::{visual_card_kind, ClickedCard, VisualCard, VisualCardBundle, VisualCardData}, GameData, }; pub struct SupplyPlugin; impl Plugin for SupplyPlugin { fn build(&self, app: &mut App) { app.add_event::() .add_event::() .add_systems( ( spawn_supply_piles.run_if(on_event::()), apply_system_buffers, position_supply_piles.run_if(on_event::()), ) .chain(), ) .add_system(handle_clicked_supply_pile); } } pub struct SpawnSupplyPilesEvent; fn spawn_supply_piles( mut commands: Commands, game_data: Res, mut psp_ev_w: EventWriter, pile_query: Query>, card_data: Res, ) { let Some(status) = &game_data.game_status else { warn!("game_status was none"); return; }; // despawn possible existing supply piles for entity in pile_query.iter() { commands.entity(entity).despawn_recursive(); } for (index, pile) in status.supply_piles.iter().enumerate() { commands .spawn(VisualCardBundle { visual_card: VisualCard { card: pile.card.clone(), }, rigid_body: RigidBody::Fixed, ..Default::default() }) .insert(visual_card_kind::Supply(index)) .insert(OnPointer::::target_component_mut::( |_over, transform| { transform.translation.y += 0.1; }, )) .insert(OnPointer::::target_component_mut::( |_over, transform| { transform.translation.y -= 0.1; }, )) .with_children(|parent| { parent.spawn(TextMeshBundle { text_mesh: TextMesh { text: format!("left: {}", pile.amount), style: TextMeshStyle { font: card_data.font_bold.clone(), font_size: SizeUnit::NonStandard(7.), color: Color::rgb(1., 1., 1.), ..Default::default() }, ..Default::default() }, transform: Transform::from_xyz(-0.25, -0.725, 0.), ..Default::default() }); }); } psp_ev_w.send(PositionSupplyPilesEvent); } pub struct PositionSupplyPilesEvent; fn position_supply_piles( mut pile_query: Query<(&VisualCard, &mut Transform), With>, ) { let mut piles: Vec<(&VisualCard, Mut)> = pile_query.iter_mut().collect::>(); piles.sort_by_key(|(vc, _t)| vc.card.name.clone()); piles.sort_by_key(|(vc, _t)| vc.card.cost); // split piles into top and bottom row let mid = piles.len() / 2; let (p1, p2) = piles.split_at_mut(mid); // keep track of offset between cards let mut offset = 0.; // top row for (_, t) in p1 { t.translation.x += offset; offset += 1.0; } // reset offset when changing rows offset = 0.; // bottom row for (_, t) in p2 { t.translation.z += 1.5; t.translation.x += offset; offset += 1.0; } } fn handle_clicked_supply_pile( mut commands: Commands, card_query: Query< (Entity, &VisualCard, &visual_card_kind::Supply), (With, With), >, mut gac_ev_w: EventWriter, mut game_data: ResMut, global: Res, ) { if game_data.locked { return; } let Ok((entity, card, card_kind)) = card_query.get_single() else { return; }; commands.entity(entity).remove::(); let player = game_data .game_status .as_ref() .unwrap() .players .get(&global.user.as_ref().unwrap().id) .unwrap(); #[allow(clippy::if_same_then_else)] if player.state != PlayerState::BuyPhase { // we ain't buying rn return; } else if player.buys == 0 { // not enough buys return; } else if player.currency < card.card.cost { // not enough currency return; } else if game_data .game_status .as_ref() .unwrap() .supply_piles .get(card_kind.0) .unwrap() .amount == 0 { // no cards in supply return; } game_data.locked = true; gac_ev_w.send(GameActionCreateCallEvent { action: Action::new( &game_data.game.as_ref().unwrap().id, &global.user.as_ref().unwrap().id, &global.user.as_ref().unwrap().id, &Command::TakeFromPile { index: card_kind.0, for_cost: card.card.cost, }, None, ), }); }