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..4f947ba 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::{ @@ -119,7 +120,7 @@ fn subscriptions(stdb: SpacetimeDB) { } #[derive(Component)] -struct TileId(u32); +pub struct TileId(pub u32); fn on_player_insert_update( stdb: SpacetimeDB, @@ -134,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"); @@ -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/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/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..61b46d3 100644 --- a/jong/src/stdb/mod.rs +++ b/jong/src/stdb/mod.rs @@ -11,7 +11,9 @@ 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; pub mod join_or_create_lobby_reducer; pub mod lobby_table; @@ -36,7 +38,9 @@ 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; pub use join_or_create_lobby_reducer::{ join_or_create_lobby, set_flags_for_join_or_create_lobby, JoinOrCreateLobbyCallbackId, @@ -69,6 +73,8 @@ pub use wind_type::Wind; pub enum Reducer { AddBot { lobby_id: u32 }, + DiscardTile { tile_id: u32 }, + DrawTile, JoinOrCreateLobby { lobby_id: u32 }, LoginOrAddPlayer, SetReady { ready: bool }, @@ -84,6 +90,8 @@ 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", Reducer::SetReady { .. } => "set_ready", @@ -102,6 +110,20 @@ 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", + &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..e1852f4 100644 --- a/jong/src/tui.rs +++ b/jong/src/tui.rs @@ -1,16 +1,24 @@ +#![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; +use jong_types::{GameState, TurnState}; mod input; mod layout; @@ -82,7 +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))) + .add_systems(Update, discard_tile.run_if(in_state(TurnState::Tsumo))) .add_systems( Update, ( @@ -96,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/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/game/wall.rs b/spacetimedb/src/game/wall.rs deleted file mode 100644 index 99069f6..0000000 --- a/spacetimedb/src/game/wall.rs +++ /dev/null @@ -1,42 +0,0 @@ -use log::debug; -use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; - -use super::hand::deal_hands; -use crate::tables::*; -use jong_types::*; - -#[reducer] -pub fn shuffle_deal(ctx: &ReducerContext, lobby_id: u32) { - debug!("lobby_id: {lobby_id}"); - let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap(); - - if lobby.game_state == GameState::Setup { - lobby.game_state = GameState::Deal; - lobby = ctx.db.lobby().id().update(lobby); - - let tiles = new_shuffled_wall(ctx); - - ctx.db.wall().insert(DbWall { - // id: 0, - lobby_id, - tiles, - }); - - deal_hands(ctx, lobby_id); - - lobby.game_state = GameState::Play; - lobby.turn_state = TurnState::Tsumo; - ctx.db.lobby().id().update(lobby); - } -} - -pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec { - let mut rng = ctx.rng(); - let mut wall: Vec<_> = tiles() - .into_iter() - .map(|tile| ctx.db.tile().insert(DbTile { id: 0, tile })) - .collect(); - wall.shuffle(&mut rng); - - wall -} 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 90% rename from spacetimedb/src/game.rs rename to spacetimedb/src/reducers/game.rs index 8777e2d..4747375 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> { @@ -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); @@ -67,7 +68,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 +82,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/reducers/game/deal.rs b/spacetimedb/src/reducers/game/deal.rs new file mode 100644 index 0000000..bd871f7 --- /dev/null +++ b/spacetimedb/src/reducers/game/deal.rs @@ -0,0 +1,83 @@ +use log::{debug, trace}; +use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer}; + +use super::hand::deal_hands; +use crate::tables::{player::player, *}; +use jong_types::*; + +#[reducer] +pub fn shuffle_deal(ctx: &ReducerContext, lobby_id: u32) { + debug!("lobby_id: {lobby_id}"); + let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap(); + + if lobby.game_state == GameState::Setup { + lobby.game_state = GameState::Deal; + lobby = ctx.db.lobby().id().update(lobby); + + let tiles = new_shuffled_wall(ctx); + + ctx.db.wall().insert(DbWall { + // id: 0, + lobby_id, + tiles, + }); + + deal_hands(ctx, lobby_id); + + lobby.game_state = GameState::Play; + lobby.turn_state = TurnState::Tsumo; + ctx.db.lobby().id().update(lobby); + } +} + +pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec { + let mut rng = ctx.rng(); + let mut wall: Vec<_> = tiles() + .into_iter() + .map(|tile| ctx.db.tile().insert(DbTile { id: 0, tile })) + .collect(); + wall.shuffle(&mut rng); + + 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/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.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] 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)]