From 63e21713ab2fea3470bc06ce3327ef258ba8eaab Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Fri, 13 Feb 2026 06:10:09 -0800 Subject: [PATCH 1/2] partialy draw a tile wind assignment and turn ordering --- Cargo.lock | 1 - jong-types/src/lib.rs | 2 +- jong/src/game.rs | 20 +++- jong/src/lib.rs | 15 ++- jong/src/stdb/draw_tile_reducer.rs | 100 ++++++++++++++++++ jong/src/stdb/mod.rs | 11 ++ jong/src/stdb/player_type.rs | 1 + jong/src/tui.rs | 7 +- jong/src/tui/render.rs | 38 +++---- spacetimedb/src/lib.rs | 7 +- spacetimedb/src/{ => reducers}/game.rs | 9 +- .../{game/wall.rs => reducers/game/deal.rs} | 0 spacetimedb/src/{ => reducers}/game/hand.rs | 13 +++ spacetimedb/src/tables/player.rs | 3 + 14 files changed, 189 insertions(+), 38 deletions(-) create mode 100644 jong/src/stdb/draw_tile_reducer.rs rename spacetimedb/src/{ => reducers}/game.rs (91%) rename spacetimedb/src/{game/wall.rs => reducers/game/deal.rs} (100%) rename spacetimedb/src/{ => reducers}/game/hand.rs (77%) diff --git a/Cargo.lock b/Cargo.lock index 0b0ffed..fa24bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1229,7 +1229,6 @@ dependencies = [ "bitflags 2.10.0", "color-eyre", "ratatui", - "smol_str", "tracing", ] diff --git a/jong-types/src/lib.rs b/jong-types/src/lib.rs index 4d38b2d..535a9cd 100644 --- a/jong-types/src/lib.rs +++ b/jong-types/src/lib.rs @@ -13,7 +13,7 @@ use bevy::prelude::*; use spacetimedb::SpacetimeType; use strum::{EnumCount, FromRepr}; -#[derive(..Base, Hash, Default)] +#[derive(..Base, Hash, Default, FromRepr)] #[derive(States, SpacetimeType)] pub enum GameState { #[default] diff --git a/jong/src/game.rs b/jong/src/game.rs index 0c0d2b8..b132ec6 100644 --- a/jong/src/game.rs +++ b/jong/src/game.rs @@ -8,8 +8,9 @@ use bevy_spacetimedb::{ use spacetimedb::Identity; use spacetimedb_sdk::{DbContext, Table, credentials}; +use crate::game::hand::Drawn; use crate::stdb::{ - self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables, add_bot, + self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables, add_bot, draw_tile, join_or_create_lobby, login_or_add_player, set_ready, shuffle_deal, start_game, }; use crate::{ @@ -146,6 +147,7 @@ fn on_player_insert_update( .hand .iter() .map(|dbt| { + // TODO this seems a lil expensive if let Some(ent) = tiles .iter() .find(|(_, id, _)| id.0 == dbt.id) @@ -158,6 +160,9 @@ fn on_player_insert_update( }) .collect(); commands.entity(**hand_ent).replace_children(&tiles); + if let Some(dbt) = &msg.new.drawn_tile { + commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id), Drawn)); + } } else { let player = Player { name: msg @@ -179,6 +184,7 @@ fn on_lobby_insert_update( mut commands: Commands, mut next_gamestate: ResMut>, + mut next_turnstate: ResMut>, ) { for msg in messages.read() { // trace!("on_lobby_insert_update msg:\n{:#?}", msg.new); @@ -214,6 +220,18 @@ fn on_lobby_insert_update( } stdb::GameState::Play => { trace!("game entered play"); + match msg.new.turn_state { + stdb::TurnState::None => {} + stdb::TurnState::Tsumo => { + stdb.reducers().draw_tile().unwrap(); + } + stdb::TurnState::Menzen => todo!(), + stdb::TurnState::RiichiKan => todo!(), + stdb::TurnState::Discard => todo!(), + stdb::TurnState::RonChiiPonKan => todo!(), + stdb::TurnState::End => todo!(), + } + next_turnstate.set(msg.new.turn_state.into()); } stdb::GameState::Exit => { trace!("game enetered exit"); diff --git a/jong/src/lib.rs b/jong/src/lib.rs index dc56646..e6a3fe9 100644 --- a/jong/src/lib.rs +++ b/jong/src/lib.rs @@ -21,14 +21,13 @@ fn creds_store() -> credentials::File { impl From for jong_types::GameState { fn from(value: stdb::GameState) -> Self { - match value { - stdb::GameState::None => Self::None, - stdb::GameState::Lobby => Self::Lobby, - stdb::GameState::Setup => Self::Setup, - stdb::GameState::Deal => Self::Deal, - stdb::GameState::Play => Self::Play, - stdb::GameState::Exit => Self::Exit, - } + Self::from_repr(value as usize).unwrap() + } +} + +impl From for jong_types::TurnState { + fn from(value: stdb::TurnState) -> Self { + Self::from_repr(value as usize).unwrap() } } diff --git a/jong/src/stdb/draw_tile_reducer.rs b/jong/src/stdb/draw_tile_reducer.rs new file mode 100644 index 0000000..daed29a --- /dev/null +++ b/jong/src/stdb/draw_tile_reducer.rs @@ -0,0 +1,100 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct DrawTileArgs {} + +impl From for super::Reducer { + fn from(args: DrawTileArgs) -> Self { + Self::DrawTile + } +} + +impl __sdk::InModule for DrawTileArgs { + type Module = super::RemoteModule; +} + +pub struct DrawTileCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `draw_tile`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait draw_tile { + /// Request that the remote module invoke the reducer `draw_tile` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_draw_tile`] callbacks. + fn draw_tile(&self) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `draw_tile`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`DrawTileCallbackId`] can be passed to [`Self::remove_on_draw_tile`] + /// to cancel the callback. + fn on_draw_tile( + &self, + callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> DrawTileCallbackId; + /// Cancel a callback previously registered by [`Self::on_draw_tile`], + /// causing it not to run in the future. + fn remove_on_draw_tile(&self, callback: DrawTileCallbackId); +} + +impl draw_tile for super::RemoteReducers { + fn draw_tile(&self) -> __sdk::Result<()> { + self.imp.call_reducer("draw_tile", DrawTileArgs {}) + } + fn on_draw_tile( + &self, + mut callback: impl FnMut(&super::ReducerEventContext) + Send + 'static, + ) -> DrawTileCallbackId { + DrawTileCallbackId(self.imp.on_reducer( + "draw_tile", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::DrawTile {}, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx) + }), + )) + } + fn remove_on_draw_tile(&self, callback: DrawTileCallbackId) { + self.imp.remove_on_reducer("draw_tile", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `draw_tile`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_draw_tile { + /// Set the call-reducer flags for the reducer `draw_tile` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn draw_tile(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_draw_tile for super::SetReducerFlags { + fn draw_tile(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("draw_tile", flags); + } +} diff --git a/jong/src/stdb/mod.rs b/jong/src/stdb/mod.rs index 61267e0..eea234f 100644 --- a/jong/src/stdb/mod.rs +++ b/jong/src/stdb/mod.rs @@ -12,6 +12,7 @@ pub mod bot_type; pub mod db_tile_type; pub mod db_wall_type; pub mod dragon_type; +pub mod draw_tile_reducer; pub mod game_state_type; pub mod join_or_create_lobby_reducer; pub mod lobby_table; @@ -37,6 +38,7 @@ pub use bot_type::Bot; pub use db_tile_type::DbTile; pub use db_wall_type::DbWall; pub use dragon_type::Dragon; +pub use draw_tile_reducer::{draw_tile, set_flags_for_draw_tile, DrawTileCallbackId}; pub use game_state_type::GameState; pub use join_or_create_lobby_reducer::{ join_or_create_lobby, set_flags_for_join_or_create_lobby, JoinOrCreateLobbyCallbackId, @@ -69,6 +71,7 @@ pub use wind_type::Wind; pub enum Reducer { AddBot { lobby_id: u32 }, + DrawTile, JoinOrCreateLobby { lobby_id: u32 }, LoginOrAddPlayer, SetReady { ready: bool }, @@ -84,6 +87,7 @@ impl __sdk::Reducer for Reducer { fn reducer_name(&self) -> &'static str { match self { Reducer::AddBot { .. } => "add_bot", + Reducer::DrawTile => "draw_tile", Reducer::JoinOrCreateLobby { .. } => "join_or_create_lobby", Reducer::LoginOrAddPlayer => "login_or_add_player", Reducer::SetReady { .. } => "set_ready", @@ -102,6 +106,13 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { &value.args, )? .into()), + "draw_tile" => Ok( + __sdk::parse_reducer_args::( + "draw_tile", + &value.args, + )? + .into(), + ), "join_or_create_lobby" => Ok(__sdk::parse_reducer_args::< join_or_create_lobby_reducer::JoinOrCreateLobbyArgs, >("join_or_create_lobby", &value.args)? diff --git a/jong/src/stdb/player_type.rs b/jong/src/stdb/player_type.rs index d902625..582ca86 100644 --- a/jong/src/stdb/player_type.rs +++ b/jong/src/stdb/player_type.rs @@ -17,6 +17,7 @@ pub struct Player { pub sort: bool, pub hand: Vec, pub pond: Vec, + pub drawn_tile: Option, } impl __sdk::InModule for Player { diff --git a/jong/src/tui.rs b/jong/src/tui.rs index 821e3a1..4a6eebb 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -10,7 +10,7 @@ use jong::game::{ hand::{Drawn, Hand}, player::{CurrentPlayer, Player}, }; -use jong_types::GameState; +use jong_types::{GameState, TurnState}; mod input; mod layout; @@ -82,7 +82,10 @@ impl Plugin for TuiPlugin { (input::keyboard, input::mouse).in_set(TuiSet::Input), ) .add_systems(Update, layout::layout.in_set(TuiSet::Layout)) - .add_systems(Update, discard_tile.run_if(in_state(GameState::Play))) + .add_systems( + Update, + discard_tile.run_if(in_state(GameState::Play).and(in_state(TurnState::Tsumo))), + ) .add_systems( Update, ( diff --git a/jong/src/tui/render.rs b/jong/src/tui/render.rs index 2377516..5fe828b 100644 --- a/jong/src/tui/render.rs +++ b/jong/src/tui/render.rs @@ -126,7 +126,7 @@ pub(crate) fn render_hands( curr_player: Single>, players: Query<(&Player, Entity, &Children)>, hands: Query<(&Children, Entity), With>, - // drawn_tile: Single>, + drawn_tile: Single>, ) -> Result { let mut frame = tui.get_frame(); debug_blocks(*layouts, &mut frame); @@ -196,24 +196,24 @@ pub(crate) fn render_hands( // tsumo tile // if this_drawer { // // trace!("this_drawer"); - // let mut area = drawn_area.resize(Size { - // width: 5, - // height: 4, - // }); - // area = area.offset(Offset { x: 2, y: 0 }); - // let hovered = hovered.contains(*drawn_tile); - // let widget = render_tile(tiles.get(*drawn_tile)?, hovered); - // if hovered { - // area = area.offset(Offset { x: 0, y: -1 }); - // let mut hitbox = area.as_size(); - // hitbox.height += 1; - // commands.entity(*drawn_tile).insert(PickRegion { - // area: area.resize(hitbox), - // }); - // } else { - // commands.entity(*drawn_tile).insert(PickRegion { area }); - // } - // frame.render_widget(widget, area); + let mut area = drawn_area.resize(Size { + width: 5, + height: 4, + }); + area = area.offset(Offset { x: 2, y: 0 }); + let hovered = hovered.contains(*drawn_tile); + let widget = render_tile(tiles.get(*drawn_tile)?, hovered); + if hovered { + area = area.offset(Offset { x: 0, y: -1 }); + let mut hitbox = area.as_size(); + hitbox.height += 1; + commands.entity(*drawn_tile).insert(PickRegion { + area: area.resize(hitbox), + }); + } else { + commands.entity(*drawn_tile).insert(PickRegion { area }); + } + frame.render_widget(widget, area); // } // TODO draw melds // } else { diff --git a/spacetimedb/src/lib.rs b/spacetimedb/src/lib.rs index 2c6d8d1..9589173 100644 --- a/spacetimedb/src/lib.rs +++ b/spacetimedb/src/lib.rs @@ -1,9 +1,11 @@ use log::info; -use spacetimedb::{ReducerContext, Table, reducer}; +use spacetimedb::{ReducerContext, Table, ViewContext, reducer, view}; use crate::tables::{player::player, *}; -mod game; +mod reducers { + mod game; +} mod tables; #[reducer(client_connected)] @@ -20,6 +22,7 @@ pub fn login_or_add_player(ctx: &ReducerContext) { sort: true, hand: vec![], pond: vec![], + drawn_tile: None, }) { info!("added player: {:?}", player); } else { diff --git a/spacetimedb/src/game.rs b/spacetimedb/src/reducers/game.rs similarity index 91% rename from spacetimedb/src/game.rs rename to spacetimedb/src/reducers/game.rs index 8777e2d..9d01633 100644 --- a/spacetimedb/src/game.rs +++ b/spacetimedb/src/reducers/game.rs @@ -1,11 +1,11 @@ use log::{info, trace}; -use spacetimedb::{ReducerContext, Table, reducer}; +use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; -use crate::tables::{player::player, *}; +use crate::tables::{player::player, wall, *}; use jong_types::*; +mod deal; mod hand; -mod wall; #[reducer] pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> { @@ -67,7 +67,7 @@ pub fn set_ready(ctx: &ReducerContext, ready: bool) { let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); player.ready = ready; - let player = ctx.db.player().identity().update(player); + ctx.db.player().identity().update(player); } #[reducer] @@ -81,6 +81,7 @@ pub fn start_game(ctx: &ReducerContext) { }) { lobby.game_state = GameState::Setup; + lobby.players.shuffle(&mut ctx.rng()); ctx.db.lobby().id().update(lobby); } } diff --git a/spacetimedb/src/game/wall.rs b/spacetimedb/src/reducers/game/deal.rs similarity index 100% rename from spacetimedb/src/game/wall.rs rename to spacetimedb/src/reducers/game/deal.rs diff --git a/spacetimedb/src/game/hand.rs b/spacetimedb/src/reducers/game/hand.rs similarity index 77% rename from spacetimedb/src/game/hand.rs rename to spacetimedb/src/reducers/game/hand.rs index 6db0b6b..72d83e3 100644 --- a/spacetimedb/src/game/hand.rs +++ b/spacetimedb/src/reducers/game/hand.rs @@ -26,6 +26,19 @@ pub fn deal_hands(ctx: &ReducerContext, lobby_id: u32) { } } +#[reducer] +pub fn draw_tile(ctx: &ReducerContext) { + let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); + let mut wall = ctx.db.wall().lobby_id().find(player.lobby_id).unwrap(); + + // TODO if no more tiles, exhaust somehow + + player.drawn_tile = wall.tiles.pop(); + + ctx.db.wall().lobby_id().update(wall); + ctx.db.player().id().update(player); +} + // #[view(name = view_player_hand, public)] // pub fn view_player_hand(ctx: &ViewContext) -> Option { // ctx.db diff --git a/spacetimedb/src/tables/player.rs b/spacetimedb/src/tables/player.rs index 8c94365..f0b2f8a 100644 --- a/spacetimedb/src/tables/player.rs +++ b/spacetimedb/src/tables/player.rs @@ -3,6 +3,7 @@ use spacetimedb::{SpacetimeType, table}; use super::DbTile; +// FIXME this shant be public, use views #[table(name = player, public)] #[derive(Debug)] pub struct Player { @@ -23,6 +24,8 @@ pub struct Player { pub hand: Vec, pub pond: Vec, + + pub drawn_tile: Option, } #[table(name = bot)] From 8c4132e628f7402934ff95548c0812bc74619563 Mon Sep 17 00:00:00 2001 From: Tao Tien <29749622+taotien@users.noreply.github.com> Date: Fri, 13 Feb 2026 07:49:09 -0800 Subject: [PATCH 2/2] sorta discard a tile --- jong/src/game.rs | 4 +- jong/src/stdb/discard_tile_reducer.rs | 105 ++++++++++++++++++++++++++ jong/src/stdb/mod.rs | 11 +++ jong/src/tui.rs | 55 ++++++++------ spacetimedb/src/reducers/game.rs | 1 + spacetimedb/src/reducers/game/deal.rs | 45 ++++++++++- spacetimedb/src/tables.rs | 4 +- 7 files changed, 199 insertions(+), 26 deletions(-) create mode 100644 jong/src/stdb/discard_tile_reducer.rs diff --git a/jong/src/game.rs b/jong/src/game.rs index b132ec6..4f947ba 100644 --- a/jong/src/game.rs +++ b/jong/src/game.rs @@ -120,7 +120,7 @@ fn subscriptions(stdb: SpacetimeDB) { } #[derive(Component)] -struct TileId(u32); +pub struct TileId(pub u32); fn on_player_insert_update( stdb: SpacetimeDB, @@ -135,7 +135,7 @@ fn on_player_insert_update( use player::*; for msg in messages.read() { - debug!("player_insert_update msg:\n{:#?}", msg.new); + // debug!("player_insert_update msg:\n{:#?}", msg.new); if let (Some(player), Some(hand_ent)) = (player.as_ref(), hand_ent.as_ref()) { // if msg.old.as_ref().is_some_and(|m| !m.ready) && msg.new.ready { // trace!("entered ready"); diff --git a/jong/src/stdb/discard_tile_reducer.rs b/jong/src/stdb/discard_tile_reducer.rs new file mode 100644 index 0000000..c3bcb15 --- /dev/null +++ b/jong/src/stdb/discard_tile_reducer.rs @@ -0,0 +1,105 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct DiscardTileArgs { + pub tile_id: u32, +} + +impl From for super::Reducer { + fn from(args: DiscardTileArgs) -> Self { + Self::DiscardTile { + tile_id: args.tile_id, + } + } +} + +impl __sdk::InModule for DiscardTileArgs { + type Module = super::RemoteModule; +} + +pub struct DiscardTileCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `discard_tile`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait discard_tile { + /// Request that the remote module invoke the reducer `discard_tile` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_discard_tile`] callbacks. + fn discard_tile(&self, tile_id: u32) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `discard_tile`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`DiscardTileCallbackId`] can be passed to [`Self::remove_on_discard_tile`] + /// to cancel the callback. + fn on_discard_tile( + &self, + callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, + ) -> DiscardTileCallbackId; + /// Cancel a callback previously registered by [`Self::on_discard_tile`], + /// causing it not to run in the future. + fn remove_on_discard_tile(&self, callback: DiscardTileCallbackId); +} + +impl discard_tile for super::RemoteReducers { + fn discard_tile(&self, tile_id: u32) -> __sdk::Result<()> { + self.imp + .call_reducer("discard_tile", DiscardTileArgs { tile_id }) + } + fn on_discard_tile( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &u32) + Send + 'static, + ) -> DiscardTileCallbackId { + DiscardTileCallbackId(self.imp.on_reducer( + "discard_tile", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::DiscardTile { tile_id }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, tile_id) + }), + )) + } + fn remove_on_discard_tile(&self, callback: DiscardTileCallbackId) { + self.imp.remove_on_reducer("discard_tile", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `discard_tile`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_discard_tile { + /// Set the call-reducer flags for the reducer `discard_tile` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn discard_tile(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_discard_tile for super::SetReducerFlags { + fn discard_tile(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("discard_tile", flags); + } +} diff --git a/jong/src/stdb/mod.rs b/jong/src/stdb/mod.rs index eea234f..61b46d3 100644 --- a/jong/src/stdb/mod.rs +++ b/jong/src/stdb/mod.rs @@ -11,6 +11,7 @@ pub mod bot_table; pub mod bot_type; pub mod db_tile_type; pub mod db_wall_type; +pub mod discard_tile_reducer; pub mod dragon_type; pub mod draw_tile_reducer; pub mod game_state_type; @@ -37,6 +38,7 @@ pub use bot_table::*; pub use bot_type::Bot; pub use db_tile_type::DbTile; pub use db_wall_type::DbWall; +pub use discard_tile_reducer::{discard_tile, set_flags_for_discard_tile, DiscardTileCallbackId}; pub use dragon_type::Dragon; pub use draw_tile_reducer::{draw_tile, set_flags_for_draw_tile, DrawTileCallbackId}; pub use game_state_type::GameState; @@ -71,6 +73,7 @@ pub use wind_type::Wind; pub enum Reducer { AddBot { lobby_id: u32 }, + DiscardTile { tile_id: u32 }, DrawTile, JoinOrCreateLobby { lobby_id: u32 }, LoginOrAddPlayer, @@ -87,6 +90,7 @@ impl __sdk::Reducer for Reducer { fn reducer_name(&self) -> &'static str { match self { Reducer::AddBot { .. } => "add_bot", + Reducer::DiscardTile { .. } => "discard_tile", Reducer::DrawTile => "draw_tile", Reducer::JoinOrCreateLobby { .. } => "join_or_create_lobby", Reducer::LoginOrAddPlayer => "login_or_add_player", @@ -106,6 +110,13 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { &value.args, )? .into()), + "discard_tile" => Ok( + __sdk::parse_reducer_args::( + "discard_tile", + &value.args, + )? + .into(), + ), "draw_tile" => Ok( __sdk::parse_reducer_args::( "draw_tile", diff --git a/jong/src/tui.rs b/jong/src/tui.rs index 4a6eebb..e1852f4 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -1,14 +1,22 @@ +#![allow(unused)] + use std::time::Duration; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; use bevy_ratatui::RatatuiPlugins; +use jong::game::TileId; +use jong::game::player::MainPlayer; use tui_logger::TuiWidgetState; use crate::tui::{input::ConfirmSelect, states::ConsoleWidget}; -use jong::game::{ - GameMessage, - hand::{Drawn, Hand}, - player::{CurrentPlayer, Player}, +use jong::stdb::{self, discard_tile as _}; +use jong::{ + SpacetimeDB, + game::{ + GameMessage, + hand::{Drawn, Hand}, + player::{CurrentPlayer, Player}, + }, }; use jong_types::{GameState, TurnState}; @@ -82,10 +90,7 @@ impl Plugin for TuiPlugin { (input::keyboard, input::mouse).in_set(TuiSet::Input), ) .add_systems(Update, layout::layout.in_set(TuiSet::Layout)) - .add_systems( - Update, - discard_tile.run_if(in_state(GameState::Play).and(in_state(TurnState::Tsumo))), - ) + .add_systems(Update, discard_tile.run_if(in_state(TurnState::Tsumo))) .add_systems( Update, ( @@ -99,28 +104,36 @@ impl Plugin for TuiPlugin { } fn discard_tile( + stdb: SpacetimeDB, + mut writer: MessageWriter, mut selected: MessageReader, - drawn: Single>, - curr_player: Single>, - player_hands: Populated<(&Player, &Children), With>, - hands: Populated<&Children, (With, Without)>, + drawn: Single<(Entity, &TileId), With>, + // curr_player: Single>, + // player_hands: Populated<(&Player, &Children), With>, + // hands: Populated<&Children, (With, Without)>, + main_player: Single<(&Player, Entity), With>, + players: Query<(&Player, Entity, &Children)>, + hands: Query<(&Children, Entity), With>, + tiles: Populated<&TileId>, ) { // trace!("discard_tile"); - let hand_ent = player_hands - .get(*curr_player) - .unwrap() - .1 - .iter() - .next() - .unwrap(); - let hand = hands.get(hand_ent).unwrap(); + let (hand, hand_ent) = hands.iter().next().unwrap(); while let Some(message) = selected.read().next() - && (message.0 == *drawn || hand.contains(&message.0)) + // && (message.0 == drawn.0 || hand.contains(&message.0)) { + if message.0 == drawn.0 { + stdb.reducers().discard_tile(drawn.1.0).unwrap(); + } else if let Some(tile_ent) = hand.iter().find(|t| *t == message.0) { + stdb.reducers() + .discard_tile(tiles.get(tile_ent).unwrap().0) + .unwrap(); + } + + // FIXME check if discard actually in hand? writer.write(GameMessage::Discarded(message.0)); } } diff --git a/spacetimedb/src/reducers/game.rs b/spacetimedb/src/reducers/game.rs index 9d01633..4747375 100644 --- a/spacetimedb/src/reducers/game.rs +++ b/spacetimedb/src/reducers/game.rs @@ -24,6 +24,7 @@ pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<( players: vec![PlayerOrBot::Player { id: player.id }], game_state: GameState::Lobby, turn_state: TurnState::None, + // dealt_idx: -1, }); info!("created lobby: {:?}", lobby); diff --git a/spacetimedb/src/reducers/game/deal.rs b/spacetimedb/src/reducers/game/deal.rs index 99069f6..bd871f7 100644 --- a/spacetimedb/src/reducers/game/deal.rs +++ b/spacetimedb/src/reducers/game/deal.rs @@ -1,8 +1,8 @@ -use log::debug; +use log::{debug, trace}; use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; use super::hand::deal_hands; -use crate::tables::*; +use crate::tables::{player::player, *}; use jong_types::*; #[reducer] @@ -40,3 +40,44 @@ pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec { wall } + +// TODO make sure this can't be called or just error here? +#[reducer] +pub fn discard_tile(ctx: &ReducerContext, tile_id: u32) -> Result<(), String> { + trace!("discard_tile"); + + let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); + let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); + + let tile = if let Some(drawn) = player.drawn_tile + && drawn.id == tile_id + { + player.hand.push(drawn); + // lobby.dealt_idx = -1; + + drawn + } else if let Some((i, &hand)) = player + .hand + .iter() + .enumerate() + .find(|(_, t)| t.id == tile_id) + { + player.hand.remove(i); + // lobby.dealt_idx = 0; + + hand + } else { + return Err(format!( + "player {} attempted to deal nonexistant tile {}", + player.id, tile_id + )); + }; + + player.drawn_tile = None; + lobby.turn_state = TurnState::Discard; + + ctx.db.player().id().update(player); + ctx.db.lobby().id().update(lobby); + + Ok(()) +} diff --git a/spacetimedb/src/tables.rs b/spacetimedb/src/tables.rs index 7250513..890eb1e 100644 --- a/spacetimedb/src/tables.rs +++ b/spacetimedb/src/tables.rs @@ -18,6 +18,8 @@ pub struct Lobby { pub game_state: GameState, pub turn_state: TurnState, + + // pub dealt_idx: i8, } #[table(name = wall)] @@ -29,7 +31,7 @@ pub struct DbWall { } #[table(name = tile)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct DbTile { #[primary_key] #[auto_inc]