M client/src/game_status/mod.rs => client/src/game_status/mod.rs +3 -0
@@ 35,6 35,7 @@ pub struct Card {
}
/// a supply pile holds an amount of some card
+#[derive(Clone)]
pub struct SupplyPile {
/// the card type that the supply pile holds
pub card: Card,
@@ 52,6 53,7 @@ pub enum PlayerState {
BuyPhase,
}
+#[derive(Clone)]
pub struct PlayerStatus {
pub display_name: String,
pub state: PlayerState,
@@ 65,6 67,7 @@ pub struct PlayerStatus {
}
/// constructed from a vector of [`Action`]s
+#[derive(Clone)]
pub struct GameStatus {
/// a modifiable Actions Vector, will be modified when parsing actions,
/// used for showing the log
M client/src/macros/async_task.rs => client/src/macros/async_task.rs +29 -0
@@ 147,3 147,32 @@ macro_rules! async_task_handle_call {
}
};
}
+
+#[macro_export]
+macro_rules! async_task_handle_call_refresh {
+ ($call_type:ty, |$response:ident, $game_data:ident, $parse_ev_w:ident| $handler_func:expr) => {
+ pub fn handle_call(
+ mut commands: Commands,
+ mut tasks: Query<(Entity, &mut $call_type)>,
+ mut $game_data: ResMut<GameData>,
+ mut $parse_ev_w: EventWriter<ParseGameStatusEvent>,
+ mut rg_ev_w: EventWriter<RefreshGameEvent>,
+ ) {
+ match tasks.get_single_mut() {
+ Ok((entity, mut task)) => {
+ if let Some($response) = future::block_on(future::poll_once(&mut task.0)) {
+ $handler_func;
+
+ rg_ev_w.send(RefreshGameEvent);
+
+ // remove the task
+ commands.entity(entity).remove::<$call_type>();
+ commands.entity(entity).despawn_recursive();
+ }
+ }
+ // NOTE: don't do anything if the wanted thingy doesn't exist
+ _ => {}
+ }
+ }
+ };
+}
M client/src/plugins/async_tasks/parse_game_status.rs => client/src/plugins/async_tasks/parse_game_status.rs +5 -2
@@ 6,7 6,7 @@
* See LICENSE for licensing information.
*/
-use crate::{api::game::Game, game_status::GameStatus, plugins::game::GameData};
+use crate::{api::game::Game, game_status::GameStatus, plugins::{game::GameData, FinishedRefreshGameEvent}};
use bevy::{
prelude::*,
@@ 35,11 35,14 @@ pub fn handle_call(
mut commands: Commands,
mut tasks: Query<(Entity, &mut ParseGameStatus)>,
mut game_data: ResMut<GameData>,
+ mut frg_ev_w: EventWriter<FinishedRefreshGameEvent>,
) {
if let Ok((entity, mut task)) = tasks.get_single_mut() {
if let Some(response) = future::block_on(future::poll_once(&mut task.0)) {
game_data.game_status = Some(response);
- game_data.parsing_data = false;
+ game_data.locked = false;
+
+ frg_ev_w.send(FinishedRefreshGameEvent);
// remove the task
commands.entity(entity).remove::<ParseGameStatus>();
M client/src/plugins/async_tasks/req_game_action_create.rs => client/src/plugins/async_tasks/req_game_action_create.rs +4 -5
@@ 11,8 11,8 @@ use crate::{
self,
game::{Action, CreateActionResponse},
},
- async_task_handle_call, async_task_start_call,
- plugins::GameData,
+ async_task_handle_call_refresh, async_task_start_call,
+ plugins::{GameData, RefreshGameEvent},
NetworkingOptions,
};
@@ 36,9 36,8 @@ async_task_start_call!(GameActionCreateCallEvent, GameActionCreateCall, |ev, no|
api::game::create_action(&no, &ev.action)
});
-async_task_handle_call!(
- GameActionCreateCall,
- |_u0, _u1, response, _game_data, _u2| {
+async_task_handle_call_refresh!(
+ GameActionCreateCall, |response, _game_data, _u2| {
match response {
Err(_err) => panic!("login failed, handle me"),
Ok(resp) => {
M client/src/plugins/async_tasks/req_game_details.rs => client/src/plugins/async_tasks/req_game_details.rs +0 -1
@@ 41,7 41,6 @@ async_task_handle_call!(
game_data.game = Some(resp);
// start data parsing
- game_data.parsing_data = true;
parse_ev_w.send(ParseGameStatusEvent {
game: game_data.game.as_ref().unwrap().clone(),
});
M client/src/plugins/game/mod.rs => client/src/plugins/game/mod.rs +21 -11
@@ 11,7 11,7 @@ use bevy_rapier3d::prelude::*;
use crate::{api::game::Game, game_status::GameStatus, AppState, Global};
-use self::card::VisualCardBundle;
+use self::supply::SpawnSupplyPilesEvent;
use super::GameDetailsCallEvent;
@@ 29,17 29,20 @@ impl Plugin for GamePlugin {
.add_plugin(card::CardPlugin)
.add_plugin(supply::SupplyPlugin)
.add_plugin(hand::HandPlugin)
+ .add_event::<RefreshGameEvent>()
+ .add_event::<FinishedRefreshGameEvent>()
.add_system(game_setup.in_schedule(OnEnter(AppState::InGame)))
- .add_system(handle_refresh_game_event.run_if(on_event::<RefreshGameEvent>()));
+ .add_system(handle_refresh_game_event.run_if(on_event::<RefreshGameEvent>()))
+ .add_system(handle_finished_refresh_game_event.run_if(on_event::<FinishedRefreshGameEvent>()));
}
}
-#[derive(Resource)]
+#[derive(Resource, Clone)]
pub struct GameData {
pub game: Option<Game>,
pub game_status: Option<GameStatus>,
- pub parsing_data: bool,
+ pub locked: bool,
}
impl Default for GameData {
@@ 48,19 51,16 @@ impl Default for GameData {
game: None,
game_status: None,
- parsing_data: false,
+ locked: true,
}
}
}
fn game_setup(
mut commands: Commands,
- mut details_ev_w: EventWriter<GameDetailsCallEvent>,
- global: Res<Global>,
+ mut rg_ev_w: EventWriter<RefreshGameEvent>,
) {
- details_ev_w.send(GameDetailsCallEvent {
- game_id: global.cur_game_id.clone(),
- });
+ rg_ev_w.send(RefreshGameEvent);
// create the playing surface
commands
@@ 73,11 73,21 @@ pub struct RefreshGameEvent;
fn handle_refresh_game_event(
mut details_ev_w: EventWriter<GameDetailsCallEvent>,
mut game_data: ResMut<GameData>,
+ global: Res<Global>,
) {
+ game_data.locked = true;
game_data.game = None;
game_data.game_status = None;
details_ev_w.send(GameDetailsCallEvent {
- game_id: game_data.game.as_ref().unwrap().id.clone(),
+ game_id: global.cur_game_id.clone(),
});
}
+
+pub struct FinishedRefreshGameEvent;
+
+fn handle_finished_refresh_game_event(
+ mut ssp_ev_w: EventWriter<SpawnSupplyPilesEvent>,
+) {
+ ssp_ev_w.send(SpawnSupplyPilesEvent);
+}
M client/src/plugins/game/supply/mod.rs => client/src/plugins/game/supply/mod.rs +13 -3
@@ 23,8 23,13 @@ impl Plugin for SupplyPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SpawnSupplyPilesEvent>()
.add_event::<PositionSupplyPilesEvent>()
- .add_system(spawn_supply_piles.run_if(on_event::<SpawnSupplyPilesEvent>()))
- .add_system(position_supply_piles.run_if(on_event::<PositionSupplyPilesEvent>()))
+ .add_systems(
+ (
+ spawn_supply_piles.run_if(on_event::<SpawnSupplyPilesEvent>()),
+ apply_system_buffers,
+ position_supply_piles.run_if(on_event::<PositionSupplyPilesEvent>())
+ ).chain()
+ )
.add_system(handle_clicked_supply_pile);
}
}
@@ 106,9 111,13 @@ fn handle_clicked_supply_pile(
mut commands: Commands,
mut card_query: Query<(Entity, &VisualCard, &visual_card_kind::Supply), (With<visual_card_kind::Supply>, With<ClickedCard>)>,
mut gac_ev_w: EventWriter<GameActionCreateCallEvent>,
- game_data: Res<GameData>,
+ mut game_data: ResMut<GameData>,
global: Res<Global>,
) {
+ if game_data.locked {
+ return;
+ }
+
let Ok((entity, card, card_kind)) = card_query.get_single() else {
return;
};
@@ 130,6 139,7 @@ fn handle_clicked_supply_pile(
return;
}
+ game_data.locked = true;
gac_ev_w.send(GameActionCreateCallEvent {
action: Action::new(
&game_data.game.as_ref().unwrap().id,
M client/src/plugins/game/ui/mod.rs => client/src/plugins/game/ui/mod.rs +14 -16
@@ 16,7 16,7 @@ use crate::{
AppState, Global, seed_gen,
};
-use super::{supply::SpawnSupplyPilesEvent, GameData};
+use super::{supply::{SpawnSupplyPilesEvent, PositionSupplyPilesEvent}, GameData, RefreshGameEvent};
mod state_button;
@@ 362,7 362,7 @@ pub fn dev_details_ui(
global: Res<Global>,
game_data: Res<GameData>,
mut create_action_ev_w: EventWriter<GameActionCreateCallEvent>,
- mut ssp_ev_w: EventWriter<SpawnSupplyPilesEvent>,
+ mut rg_ev_w: EventWriter<RefreshGameEvent>,
) {
egui::Window::new("Game Details").show(contexts.ctx_mut(), |ui| {
let Some(game) = &game_data.game else {
@@ 370,25 370,23 @@ pub fn dev_details_ui(
return;
};
let Some(status) = &game_data.game_status else {
- if game_data.parsing_data {
- // early return if game_status is None, and we're parsing it
- return;
- }
- // game_status is None, but we're not parsing it...
- // should be unreachable, I think?
+ // early return if game_status is None
return;
};
- if status.actions.is_empty() && game.host_id == global.user.as_ref().unwrap().id {
- if ui.button("Init Game").clicked() {
- // NOTE/FIXME: hardcoded game init
- hardcoded_init(game, &mut create_action_ev_w);
+ ui.add_enabled_ui(!game_data.locked, |ui| {
+ if status.actions.is_empty() && game.host_id == global.user.as_ref().unwrap().id {
+ if ui.button("Init Game").clicked() {
+ // NOTE/FIXME: hardcoded game init
+ hardcoded_init(game, &mut create_action_ev_w);
+ rg_ev_w.send(RefreshGameEvent);
+ }
}
- }
- if ui.button("spawn things").clicked() {
- ssp_ev_w.send(SpawnSupplyPilesEvent);
- }
+ if ui.button("Force Refresh").clicked() {
+ rg_ev_w.send(RefreshGameEvent);
+ }
+ });
ui.separator();
M client/src/plugins/game/ui/state_button.rs => client/src/plugins/game/ui/state_button.rs +10 -3
@@ 84,15 84,16 @@ fn update_state_button(
(Changed<Interaction>, With<StateButton>)
>,
mut text_query: Query<&mut Text>,
- game_data: Res<GameData>,
+ mut game_data: ResMut<GameData>,
global: Res<Global>,
mut gac_ev_w: EventWriter<GameActionCreateCallEvent>,
) {
for (interaction, mut color, mut visibility, children) in &mut interaction_query {
- let Some(game) = &game_data.game else {
+ // NOTE: horrible clones because of borrow funnies
+ let Some(game) = game_data.clone().game else {
return;
};
- let Some(status) = &game_data.game_status else {
+ let Some(status) = game_data.clone().game_status else {
return;
};
let Some(user) = &global.user else {
@@ 116,10 117,16 @@ fn update_state_button(
}
*visibility = Visibility::Inherited;
+ if game_data.locked {
+ // input locked rn, so don't show any signs of interactivity
+ return;
+ }
+
match interaction {
Interaction::Clicked => {
*color = PRESSED_BUTTON.into();
+ game_data.locked = true;
match player.state {
PlayerState::PlayPhase => {
gac_ev_w.send(GameActionCreateCallEvent {