detect hand change and render it

This commit is contained in:
Tao Tien 2026-01-11 20:10:30 -08:00
parent d506a25716
commit 3417384b86
9 changed files with 138 additions and 68 deletions

View file

@ -3,30 +3,32 @@ use tracing::instrument;
use crate::tiles::{self, *}; use crate::tiles::{self, *};
mod player; pub mod player;
pub mod wall; pub mod wall;
pub struct Riichi; #[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)]
pub enum GameState {
#[default]
None,
Setup,
// Deal,
Play,
Score,
}
pub struct Riichi;
impl Plugin for Riichi { impl Plugin for Riichi {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<Compass>() app.init_resource::<Compass>()
.add_systems(Startup, init_match) .add_systems(Startup, init_match)
.add_systems(Startup, (tiles::init_tiles, wall::build_wall).chain()) .add_systems(Startup, tiles::init_tiles)
.init_state::<GameState>()
.add_systems(OnEnter(GameState::Setup), (wall::build_wall, player::deal_hands).chain())
// semicolon stopper // semicolon stopper
; ;
} }
} }
#[derive(States, Default, Hash, Clone, Eq, Debug, PartialEq)]
enum GameState {
#[default]
Setup,
Deal,
Play,
Score,
}
#[derive(Component)] #[derive(Component)]
pub(crate) struct Dice(u8, u8); pub(crate) struct Dice(u8, u8);

View file

@ -1,9 +1,44 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{
game::wall::{Wall, WallTiles},
tiles::Tile,
};
#[derive(Component)] #[derive(Component)]
pub(crate) struct Player { pub(crate) struct Player {
pub(crate) name: String, pub(crate) name: String,
} }
fn spawn_players(mut commands: Commands) {}
#[derive(Component)] #[derive(Component)]
pub(crate) struct Points(pub isize); pub(crate) struct Points(pub isize);
#[derive(Component)]
pub struct Hand;
#[derive(Component)]
#[relationship_target(relationship = InHand, linked_spawn)]
pub struct HandTiles(Vec<Entity>);
#[derive(Component)]
#[relationship(relationship_target = HandTiles)]
pub struct InHand(pub Entity);
pub(crate) fn deal_hands(
mut commands: Commands,
wall: Single<Entity, With<Wall>>,
wall_tiles: Query<Entity, With<WallTiles>>,
tiles: Query<Entity, With<Tile>>,
) -> Result {
let hand = wall_tiles.iter().collect::<Vec<_>>();
commands
.get_entity(*wall)?
.remove_children(hand.last_chunk::<13>().unwrap());
commands.spawn((Hand, HandTiles(hand)));
Ok(())
}

View file

@ -2,17 +2,20 @@ use bevy::log::tracing::instrument;
use bevy::prelude::*; use bevy::prelude::*;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use crate::game::InWall;
use crate::tiles::Tile; use crate::tiles::Tile;
#[derive(Component)]
pub struct Wall;
#[derive(Component)] #[derive(Component)]
#[relationship_target(relationship = InWall, linked_spawn)] #[relationship_target(relationship = InWall, linked_spawn)]
pub struct WallTiles(Vec<Entity>); pub struct WallTiles(Vec<Entity>);
#[derive(Component)] #[derive(Component)]
pub struct Wall; #[relationship(relationship_target = WallTiles)]
pub struct InWall(pub Entity);
pub(crate) fn build_wall(mut commands: Commands, tiles: Query<Entity, With<Tile>>) -> Result { pub(crate) fn build_wall(mut commands: Commands, tiles: Query<Entity, With<Tile>>) {
let mut rng = rand::rng(); let mut rng = rand::rng();
let mut shuffled = tiles let mut shuffled = tiles
@ -21,7 +24,5 @@ pub(crate) fn build_wall(mut commands: Commands, tiles: Query<Entity, With<Tile>
.collect::<Vec<_>>(); .collect::<Vec<_>>();
shuffled.shuffle(&mut rng); shuffled.shuffle(&mut rng);
let mut wall = commands.spawn((Wall, WallTiles(shuffled))); commands.spawn((Wall, WallTiles(shuffled)));
Ok(())
} }

View file

@ -43,7 +43,7 @@ fn main() {
tui_logger::init_logger(tui_logger::LevelFilter::Trace).unwrap(); tui_logger::init_logger(tui_logger::LevelFilter::Trace).unwrap();
tui_logger::set_env_filter_from_string(FILTERSTRING); tui_logger::set_env_filter_from_string(FILTERSTRING);
app.add_plugins(tui::RiichiTui) app.add_plugins(tui::RiichiTui::default())
} }
}; };

View file

@ -2,15 +2,7 @@ use bevy::{ecs::entity::MapEntities, prelude::*};
use strum::FromRepr; use strum::FromRepr;
use tracing::instrument; use tracing::instrument;
use crate::game::wall::WallTiles; use crate::game::{player::HandTiles, wall::WallTiles};
#[derive(Component)]
#[relationship(relationship_target = WallTiles)]
pub struct InWall(pub Entity);
// #[derive(Component)]
// #[relationship(relationship_target = WallTiles)]
// pub(crate) struct InHand(pub Entity);
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct Tile { pub struct Tile {

View file

@ -1,6 +1,7 @@
use bevy::input::keyboard::Key; use bevy::input::keyboard::Key;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ratatui::RatatuiContext; use bevy_ratatui::RatatuiContext;
use ratatui::widgets::Block;
use tui_logger::TuiLoggerWidget; use tui_logger::TuiLoggerWidget;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
@ -33,7 +34,8 @@ pub(crate) fn toggle_console(
pub(crate) fn draw_console(mut tui_ctx: ResMut<RatatuiContext>) -> Result { pub(crate) fn draw_console(mut tui_ctx: ResMut<RatatuiContext>) -> Result {
tui_ctx.draw(|frame| { tui_ctx.draw(|frame| {
frame.render_widget(TuiLoggerWidget::default(), frame.area()); let block = Block::bordered().title("console");
frame.render_widget(TuiLoggerWidget::default().block(block), frame.area());
})?; })?;
Ok(()) Ok(())

View file

@ -2,14 +2,26 @@ use std::time::Duration;
use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin}; use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin};
use bevy_ratatui::RatatuiPlugins; use bevy_ratatui::RatatuiPlugins;
use jong::tiles::InWall;
use ratatui::{text::ToSpan, widgets::Paragraph}; use ratatui::{text::ToSpan, widgets::Paragraph};
use jong::game::GameState;
use jong::game::wall::InWall;
mod console; mod console;
mod input; mod input;
mod render; mod render;
pub struct RiichiTui; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)]
enum TuiState {
MainMenu,
#[default]
InGame,
}
#[derive(Default)]
pub struct RiichiTui {
// player_names: Vec<String>,
}
impl Plugin for RiichiTui { impl Plugin for RiichiTui {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@ -25,20 +37,19 @@ impl Plugin for RiichiTui {
}, },
)) ))
.add_plugins(StatesPlugin) .add_plugins(StatesPlugin)
// setup console
.init_state::<console::ConsoleState>() .init_state::<console::ConsoleState>()
.add_systems(Update, console::toggle_console) .add_systems(Update, console::toggle_console)
.add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open))) .add_systems(Update, console::draw_console.run_if(in_state(console::ConsoleState::Open)))
.init_state::<TuiState>() .init_state::<TuiState>()
.add_systems(Update, input::keyboard_input_system) // .add_systems(Update, input::keyboard_input_system)
.add_systems(Update, render::draw_ingame.run_if(in_state(TuiState::InGame))) .add_systems(Update, render::draw_ingame.run_if(in_state(TuiState::InGame)))
.add_systems(Update,render::render_changed_hand.run_if(in_state(GameState::Play)))
// semicolon stopper // semicolon stopper
; ;
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, States, Default)] // fn prompt_names() {}
enum TuiState {
MainMenu,
#[default]
InGame,
}

View file

@ -1,54 +1,81 @@
use std::marker::PhantomData;
use bevy::ecs::system::SystemParam; use bevy::ecs::system::SystemParam;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_ratatui::RatatuiContext; use bevy_ratatui::RatatuiContext;
use ratatui::widgets::Paragraph; use ratatui::widgets::Paragraph;
use jong::game::wall::Wall; use jong::{
use jong::game::wall::WallTiles; game::{
use jong::tiles::Tile; player::{Hand, HandTiles},
wall::{Wall, WallTiles},
},
tiles::Tile,
};
mod tiles; mod tiles;
pub(crate) fn draw_ingame( #[derive(Component)]
pub(crate) struct RenderedHand<'a>(Vec<Paragraph<'a>>);
pub(crate) fn render_changed_hand(
hand: Single<&HandTiles, Changed<HandTiles>>,
// hand_tiles: Query<&HandTiles, With<Hand>>,
tiles: Query<&Tile>, tiles: Query<&Tile>,
wall: Option<Single<&WallTiles, With<Wall>>>, mut target: Single<&'static mut RenderedHand>,
) -> Result {
let hand_tiles = hand
.iter()
.map(|inhand| -> Result<_> { Ok(tiles.get(inhand).map(tiles::draw_tile)?) })
.collect::<Result<Vec<_>>>()?;
target.0 = hand_tiles;
Ok(())
}
pub(crate) fn draw_ingame(
// tiles: Query<&Tile>,
// wall_tiles: Option<Single<&WallTiles, With<Wall>>>,
// hand_tiles: Query<&HandTiles, With<Hand>>,
rendered_hand: Single<&'static RenderedHand>,
mut tui_ctx: ResMut<RatatuiContext>, mut tui_ctx: ResMut<RatatuiContext>,
) -> Result { ) -> Result {
use ratatui::layout::Flex; use ratatui::layout::Flex;
use ratatui::prelude::*; use ratatui::prelude::*;
let title = ratatui::text::Text::raw("tiny riichi"); // let title = ratatui::text::Text::raw("tiny riichi");
let wall = if let Some(wall) = wall { // let wall = if let Some(wall_tiles) = wall_tiles {
let wall = wall // let wall_tiles = wall_tiles
.iter() // .iter()
.map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) }) // .map(|inwall| -> Result<_> { Ok(tiles.get(inwall).map(tiles::draw_tile)?) })
.collect::<Result<Vec<_>>>()?; // .collect::<Result<Vec<_>>>()?;
// let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true }); // // let paragraph = Paragraph::new(wall.join(", ")).wrap(ratatui::widgets::Wrap { trim: true });
Some(wall) // Some(wall_tiles)
} else { // } else {
None // None
}; // };
tui_ctx.draw(|frame| { tui_ctx.draw(|frame| {
// frame.render_widget(title, frame.area()); // frame.render_widget(title, frame.area());
debug!("{}", frame.area()); debug!("{}", frame.area());
if let Some(wall) = wall { // if let Some(wall) = wall {
// let tile_area = Rect::new(0, 0, 5, 4); // // let tile_area = Rect::new(0, 0, 5, 4);
let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start); let layout = Layout::horizontal(vec![Constraint::Max(5); 13]).flex(Flex::Start);
let mut area = frame.area(); let mut area = frame.area();
area.height = 4; area.height = 4;
let areas = layout.areas::<13>(area); let areas = layout.areas::<13>(area);
// debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len()); for (tile, area) in rendered_hand.0.iter().zip(areas.iter()) {
for (tile, rect) in wall.iter().zip(areas.iter()) { frame.render_widget(tile, *area);
// debug!("{rect:?}");
frame.render_widget(tile, *rect);
}
} }
// // debug!("wall.len(): {}, areas.len(): {}", wall.len(), areas.len());
// for (tile, rect) in wall.iter().zip(areas.iter()) {
// // debug!("{rect:?}");
// frame.render_widget(tile, *rect);
// }
// }
})?; })?;
Ok(()) Ok(())

View file

@ -2,7 +2,7 @@ use ratatui::widgets::Paragraph;
use jong::tiles::Tile; use jong::tiles::Tile;
pub(crate) fn draw_tile(tile: &Tile) -> Paragraph<'_> { pub(crate) fn draw_tile(tile: &Tile) -> Paragraph<'static> {
use ratatui::prelude::*; use ratatui::prelude::*;
let block = ratatui::widgets::Block::bordered(); let block = ratatui::widgets::Block::bordered();