use log::{debug, trace}; use spacetimedb::{ReducerContext, reducer}; use crate::tables::{player::player, *}; use jong_types::states::TurnState; pub fn deal_hands(ctx: &ReducerContext, lobby_id: u32) { let players = ctx.db.player().lobby_id().filter(lobby_id); let bots = ctx.db.bot().lobby_id().filter(lobby_id); let mut wall = ctx.db.wall().lobby_id().find(lobby_id).unwrap(); // FIXME rectify deal orders for mut player in players { let mut tiles = wall.tiles.split_off(wall.tiles.len() - 13); wall = ctx.db.wall().lobby_id().update(wall); tiles.sort_by_key(|t| t.tile); player.hand = tiles; ctx.db.player().id().update(player); } for mut bot in bots { let mut tiles = wall.tiles.split_off(wall.tiles.len() - 13); wall = ctx.db.wall().lobby_id().update(wall); tiles.sort_by_key(|t| t.tile); bot.hand = tiles; ctx.db.bot().id().update(bot); } } #[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); } // TODO make sure this can't be called or just error here? #[reducer] pub fn discard_tile(ctx: &ReducerContext, tile_id: u32) -> Result<(), String> { let mut player = ctx.db.player().identity().find(ctx.sender).unwrap(); let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); let dealt_tile = if let Some(dealt) = ctx.db.tile().id().find(tile_id) { if let Some(drawn) = player.drawn_tile { if drawn.id == dealt.id { // dealt from drawn tile dealt } else if let Some((i, _)) = player .hand .iter() .enumerate() .find(|(_, t)| t.id == tile_id) { // dealt from hand let dealt = player.hand.remove(i); player.hand.push(drawn); player.hand.sort_by_key(|t| t.tile); dealt } else { return Err(format!( "player {} attempted to deal tile {} not in hand or drawn", player.id, tile_id )); } } else { return Err(format!( "player {} attempted to deal tile {} without having drawn", player.id, tile_id )); } } else { return Err(format!( "player {} attempted to deal nonexistant tile {}", player.id, tile_id )); }; player.pond.push(dealt_tile); player.drawn_tile = None; lobby.turn_state = TurnState::RonChiiPonKan; let player = ctx.db.player().id().update(player); ctx.db.lobby().id().update(lobby); debug!("player {} discarded tile {:?}", player.id, dealt_tile.tile); Ok(()) } #[reducer] pub fn skip_call(ctx: &ReducerContext) { trace!("skip_call"); let player = ctx.db.player().identity().find(ctx.sender).unwrap(); let mut lobby = ctx.db.lobby().id().find(player.lobby_id).unwrap(); lobby.turn_state = TurnState::Tsumo; lobby.current_idx += 1; if lobby.current_idx >= 3 { lobby.current_idx = 0; } // FIXME where better can this go bot_moves(ctx, &mut lobby); ctx.db.player().id().update(player); ctx.db.lobby().id().update(lobby); } fn bot_moves(ctx: &ReducerContext, lobby: &mut Lobby) { let mut wall = ctx.db.wall().lobby_id().find(lobby.id).unwrap(); if let Some(PlayerOrBot::Bot { id }) = lobby.players.get(lobby.current_idx as usize + 1) { let mut bot = ctx.db.bot().id().find(id).unwrap(); bot.pond.push(wall.tiles.pop().unwrap()); ctx.db.bot().id().update(bot); lobby.turn_state = TurnState::RonChiiPonKan; } else { lobby.turn_state = TurnState::Tsumo; } lobby.current_idx += 1; if lobby.current_idx >= 3 { lobby.current_idx = 0; } } // #[view(name = view_player_hand, public)] // pub fn view_player_hand(ctx: &ViewContext) -> Option { // ctx.db // .player() // .identity() // .find(ctx.sender) // .map(|p| ctx.db.hand().id().find(p.hand_id))? // } // #[reducer] // pub fn sort_hand(ctx: &ReducerContext) { // todo!() // }