/*
* This file is part of laurelin_client
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::prelude::*;
use bevy_mod_picking::prelude::*;
use bevy_rapier3d::prelude::*;
use crate::{
api::game::{Action, Command},
game_status::PlayerState,
plugins::GameActionCreateCallEvent,
Global,
};
use super::{
card::{visual_card_kind, ClickedCard, VisualCard, VisualCardBundle},
GameData,
};
pub struct HandPlugin;
impl Plugin for HandPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SpawnHandEvent>()
.add_event::<PositionHandEvent>()
.add_systems(
(
spawn_hand.run_if(on_event::<SpawnHandEvent>()),
apply_system_buffers,
position_hand.run_if(on_event::<PositionHandEvent>()),
)
.chain(),
)
.add_system(handle_clicked_hand_card);
}
}
pub struct SpawnHandEvent;
fn spawn_hand(
mut commands: Commands,
game_data: Res<GameData>,
global: Res<Global>,
mut ph_ev_w: EventWriter<PositionHandEvent>,
hand_query: Query<Entity, With<visual_card_kind::Hand>>,
) {
let Some(status) = &game_data.game_status else {
warn!("game_status was none");
return;
};
// despawn possible existing supply piles
for entity in hand_query.iter() {
commands.entity(entity).despawn_recursive();
}
for (index, card) in status
.players
.get(&global.user.as_ref().unwrap().id)
.unwrap()
.hand
.iter()
.enumerate()
{
commands
.spawn(VisualCardBundle {
visual_card: VisualCard { card: card.clone() },
rigid_body: RigidBody::Fixed,
..Default::default()
})
.insert(visual_card_kind::Hand(index))
.insert(OnPointer::<Over>::target_component_mut::<Transform>(
|_over, transform| {
transform.translation.y += 0.2;
},
))
.insert(OnPointer::<Out>::target_component_mut::<Transform>(
|_over, transform| {
transform.translation.y -= 0.2;
},
));
}
ph_ev_w.send(PositionHandEvent);
}
pub struct PositionHandEvent;
fn position_hand(
mut card_query: Query<(&VisualCard, &mut Transform), With<visual_card_kind::Hand>>,
) {
let mut cards: Vec<(&VisualCard, Mut<Transform>)> = card_query.iter_mut().collect::<Vec<_>>();
cards.sort_by_key(|(vc, _t)| vc.card.name.clone());
cards.sort_by_key(|(vc, _t)| vc.card.cost);
// keep track of offset between cards
let mut offset = 0.;
for (_, mut t) in cards {
t.translation.z += 1.5 * 3.; // leave room for 3 cards from z0.0
t.translation.x += offset;
offset += 1.0;
}
}
fn handle_clicked_hand_card(
mut commands: Commands,
card_query: Query<
(Entity, &visual_card_kind::Hand),
(With<visual_card_kind::Hand>, With<ClickedCard>),
>,
mut gac_ev_w: EventWriter<GameActionCreateCallEvent>,
mut game_data: ResMut<GameData>,
global: Res<Global>,
) {
if game_data.locked {
return;
}
let Ok((entity, card_kind)) = card_query.get_single() else {
return;
};
commands.entity(entity).remove::<ClickedCard>();
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::PlayPhase {
// we ain't playing rn
return;
} else if player.plays == 0 {
// not enough plays
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::PlayCard { index: card_kind.0 },
None,
),
});
}