more refactor, start using stdb for everything???
4.5th rewrite for tui
This commit is contained in:
parent
034e543d40
commit
c3686221aa
29 changed files with 478 additions and 744 deletions
|
|
@ -1,41 +0,0 @@
|
|||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use jong_types::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Hand;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Pond;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Drawn;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Discarded;
|
||||
|
||||
// #[derive(Component, Default)]
|
||||
// enum SortHand {
|
||||
// #[default]
|
||||
// Unsorted,
|
||||
// Sort,
|
||||
// Manual,
|
||||
// }
|
||||
|
||||
/// assumes hand is sorted
|
||||
pub(crate) fn check_wincon(_hand: &[Tile; 14], _melds: &[&[Tile]]) -> bool {
|
||||
// 4x3 + pair
|
||||
// assume sorted
|
||||
//
|
||||
// let melds = hand.iter().array_chunks::<3>().all(|tiles| {
|
||||
// let suit = discriminant(&tiles[0].suit);
|
||||
// let starting_rank = tiles[0].suit
|
||||
// // tiles.iter().all(|t| discriminant(&t.suit) == suit) && tiles.iter().zip(tiles[0].suit.rank())
|
||||
// }) && melds.iter().all(|meld| todo!());
|
||||
// let eyeball = todo!();
|
||||
|
||||
todo!();
|
||||
|
||||
// melds && eyeball
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component, Debug, PartialEq)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Points(pub isize);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MainPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CurrentPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dealer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Tsumo;
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
|
||||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
use strum::{EnumCount, FromRepr};
|
||||
|
||||
use crate::EnumNextCycle;
|
||||
use jong_types::TurnState;
|
||||
|
||||
// #[derive(Resource)]
|
||||
// pub struct CurrentPlayer(pub Entity);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct MatchSettings {
|
||||
pub(crate) starting_points: isize,
|
||||
pub(crate) player_count: u8,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct Dice(u8, u8);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct Compass {
|
||||
pub(crate) prevalent_wind: Wind,
|
||||
pub(crate) round: u8,
|
||||
pub(crate) dealer_wind: Wind,
|
||||
pub(crate) riichi: usize,
|
||||
pub(crate) honba: usize,
|
||||
}
|
||||
|
||||
#[derive(Component, Clone, Copy, FromRepr, EnumCount, PartialEq)]
|
||||
pub enum Wind {
|
||||
Ton,
|
||||
Nan,
|
||||
Shaa,
|
||||
Pei,
|
||||
}
|
||||
|
||||
pub enum WindRelation {
|
||||
Shimocha,
|
||||
Toimen,
|
||||
Kamicha,
|
||||
}
|
||||
|
||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum CallType {
|
||||
Skip,
|
||||
Ron,
|
||||
Chii,
|
||||
Pon,
|
||||
Kan,
|
||||
}
|
||||
|
||||
impl Default for MatchSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
starting_points: 25000,
|
||||
player_count: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Compass {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
prevalent_wind: Wind::Ton,
|
||||
round: 1,
|
||||
dealer_wind: Wind::Ton,
|
||||
riichi: 0,
|
||||
honba: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for Wind {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wind {
|
||||
pub fn relate(&self, other: &Self) -> WindRelation {
|
||||
if self.next() == *other {
|
||||
WindRelation::Shimocha
|
||||
} else if other.next() == *self {
|
||||
WindRelation::Kamicha
|
||||
} else {
|
||||
WindRelation::Toimen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for TurnState {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub(crate) fn tsumo(
|
||||
// mut commands: Commands,
|
||||
|
||||
// // curr_player: Res<CurrentPlayer>,
|
||||
// curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
// wall: Single<Entity, With<Wall>>,
|
||||
// walltiles: Single<&Children, With<Wall>>,
|
||||
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) {
|
||||
// let drawn = walltiles.last().unwrap();
|
||||
// commands.entity(*wall).remove_child(*drawn);
|
||||
|
||||
// let drawn = commands.entity(*drawn).insert(Drawn).id();
|
||||
// commands.entity(*curr_player).add_child(drawn);
|
||||
|
||||
// debug!("tsumo for: {:?}, tile: {:?}", *curr_player, drawn);
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
|
||||
// pub(crate) fn menzen(
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) {
|
||||
// trace!("menzen check");
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
|
||||
// pub(crate) fn riichi_kan(
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) {
|
||||
// trace!("riichi_kan");
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
|
||||
// #[allow(clippy::too_many_arguments, irrefutable_let_patterns)]
|
||||
// pub(crate) fn discard(
|
||||
// mut commands: Commands,
|
||||
// mut reader: MessageReader<GameMessage>,
|
||||
|
||||
// curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
// players: Query<&Children, With<Player>>,
|
||||
// mut hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||
// drawn: Single<Entity, With<Drawn>>,
|
||||
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) -> Result {
|
||||
// // trace!("discard");
|
||||
// let (handtiles, hand) = hands.get_mut(players.get(*curr_player)?.iter().next().unwrap())?;
|
||||
|
||||
// let mut done = false;
|
||||
// while let Some(message) = reader.read().next() {
|
||||
// if let GameMessage::Discarded(discarded) = message {
|
||||
// debug!("discarded: {discarded:?}");
|
||||
// if *discarded == *drawn {
|
||||
// } else if handtiles.contains(discarded) {
|
||||
// commands
|
||||
// .entity(hand)
|
||||
// .remove_child(*discarded)
|
||||
// .add_child(*drawn);
|
||||
// } else {
|
||||
// panic!("current hand nor drawn tile contains discarded tile")
|
||||
// }
|
||||
// commands.entity(*drawn).remove::<Drawn>();
|
||||
// commands.entity(*discarded).insert(Discarded);
|
||||
|
||||
// done = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if done {
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct PendingCalls {
|
||||
eligible: Vec<Entity>,
|
||||
calls: HashMap<Entity, CallType>,
|
||||
}
|
||||
|
||||
// pub(crate) fn notify_callable() {}
|
||||
|
||||
// pub(crate) fn ron_chi_pon_kan(
|
||||
// mut commands: Commands,
|
||||
// mut reader: MessageReader<GameMessage>,
|
||||
|
||||
// discarded: Single<Entity, With<Discarded>>,
|
||||
// mut ponds: Query<(&Children, Entity), (With<Pond>, Without<Player>)>,
|
||||
// calls: Query<&CallType>,
|
||||
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) {
|
||||
// // check if can call?
|
||||
// // message players?
|
||||
// // collect then prioritize
|
||||
|
||||
// // let mut received = vec![];
|
||||
// let mut received: Vec<_> = reader
|
||||
// .read()
|
||||
// .filter_map(|m| {
|
||||
// if let GameMessage::Called { player, calltype } = m
|
||||
// && let Ok(calltype) = calls.get(*calltype)
|
||||
// {
|
||||
// Some((calltype, player))
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// })
|
||||
// .collect();
|
||||
// // received.sort_unstable_by_key(|(c, t)| c);
|
||||
// // received.sort_unstable_by_key(|m| m.);
|
||||
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
|
||||
// pub(crate) fn end(
|
||||
// curr_turnstate: Res<State<TurnState>>,
|
||||
// mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
// ) {
|
||||
// next_turnstate.set(curr_turnstate.next());
|
||||
// }
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Wall;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dead;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
use bevy::{color::palettes::css::GREEN, prelude::*};
|
||||
|
||||
pub(crate) fn init_environment(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
DirectionalLight {
|
||||
shadows_enabled: true,
|
||||
..default()
|
||||
},
|
||||
// Transform::from_xyz(),
|
||||
));
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
Transform::from_xyz(-200.5, 100., 0.).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn init_table(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let green: Color = GREEN.into();
|
||||
let table = Cuboid::new(1000., 5., 1000.);
|
||||
|
||||
commands.spawn((
|
||||
Mesh3d(meshes.add(table)),
|
||||
MeshMaterial3d(materials.add(green)),
|
||||
));
|
||||
}
|
||||
|
|
@ -4,9 +4,7 @@ use bevy::prelude::*;
|
|||
use bevy_spacetimedb::StdbConnection;
|
||||
use spacetimedb_sdk::credentials;
|
||||
|
||||
pub mod game;
|
||||
pub mod tile;
|
||||
pub mod yakus;
|
||||
pub mod riichi;
|
||||
|
||||
trait EnumNextCycle {
|
||||
fn next(&self) -> Self;
|
||||
|
|
@ -15,6 +13,5 @@ trait EnumNextCycle {
|
|||
pub type SpacetimeDB<'a> = Res<'a, StdbConnection<jong_db::DbConnection>>;
|
||||
|
||||
fn creds_store() -> credentials::File {
|
||||
credentials::File::new("jongline")
|
||||
credentials::File::new("jong-line")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clap::{Parser, Subcommand};
|
|||
use tracing::Level;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
mod gui;
|
||||
// mod gui;
|
||||
mod tui;
|
||||
|
||||
#[derive(Parser)]
|
||||
|
|
@ -25,15 +25,11 @@ fn main() {
|
|||
|
||||
let mut app = App::new();
|
||||
let app = match args.mode {
|
||||
Mode::RunGui => {
|
||||
app.add_plugins(DefaultPlugins.set(LogPlugin {
|
||||
filter: FILTERSTRING.into(),
|
||||
level: Level::TRACE,
|
||||
// custom_layer: todo!(),
|
||||
// fmt_layer: todo!(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
Mode::RunGui => app.add_plugins(DefaultPlugins.set(LogPlugin {
|
||||
filter: FILTERSTRING.into(),
|
||||
level: Level::TRACE,
|
||||
..Default::default()
|
||||
})),
|
||||
Mode::RunTui => {
|
||||
tracing_subscriber::registry()
|
||||
.with(tui_logger::TuiTracingSubscriberLayer)
|
||||
|
|
@ -45,7 +41,7 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
app.add_plugins(jong::game::Riichi);
|
||||
app.add_plugins(jong::riichi::Riichi);
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,27 +2,22 @@ use bevy::prelude::*;
|
|||
use bevy_spacetimedb::{
|
||||
ReadInsertUpdateMessage, ReadStdbConnectedMessage, ReadStdbDisconnectedMessage, StdbPlugin,
|
||||
};
|
||||
use spacetimedb_sdk::{DbContext, Table};
|
||||
|
||||
use crate::{
|
||||
SpacetimeDB, creds_store,
|
||||
game::hand::{Drawn, Hand, Pond},
|
||||
};
|
||||
use jong_db::{self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables};
|
||||
use jong_db::{add_bot, draw_tile, set_ready, shuffle_deal, skip_call, start_game};
|
||||
use jong_types::*;
|
||||
|
||||
pub mod hand;
|
||||
pub mod player;
|
||||
pub mod round;
|
||||
pub mod wall;
|
||||
use crate::riichi::player::*;
|
||||
use crate::{SpacetimeDB, creds_store};
|
||||
// pub mod round;
|
||||
|
||||
pub struct Riichi;
|
||||
impl Plugin for Riichi {
|
||||
fn build(&self, app: &mut App) {
|
||||
let plugins = StdbPlugin::default()
|
||||
.with_uri("http://localhost:3000")
|
||||
.with_module_name("jongline")
|
||||
.with_module_name("jong-line")
|
||||
.with_run_fn(DbConnection::run_threaded)
|
||||
// TODO why don't I need to call add_reducer?
|
||||
|
||||
|
|
@ -41,8 +36,8 @@ impl Plugin for Riichi {
|
|||
};
|
||||
|
||||
app.add_plugins(plugins)
|
||||
.init_state::<GameState>()
|
||||
.add_sub_state::<TurnState>()
|
||||
.init_state::<jong_types::states::GameState>()
|
||||
.add_sub_state::<jong_types::states::TurnState>()
|
||||
// .init_resource::<round::MatchSettings>()
|
||||
// .init_resource::<round::Compass>()
|
||||
// .add_systems(Startup, tile::init_tiles)
|
||||
|
|
@ -70,6 +65,7 @@ fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessage, _comman
|
|||
for msg in messages.read() {
|
||||
info!("you're now jongline");
|
||||
|
||||
// FIXME hack that doesn't work for startup crash?
|
||||
while stdb.try_identity().is_none() {}
|
||||
|
||||
debug!("with identity: {}", stdb.identity());
|
||||
|
|
@ -91,6 +87,7 @@ fn subscriptions(stdb: SpacetimeDB) {
|
|||
.on_applied(|_| trace!("made all subs!"))
|
||||
.on_error(|_, err| error!("sub failed: {err}"))
|
||||
.subscribe([
|
||||
// TODO until views work
|
||||
format!(
|
||||
"SELECT * FROM player p WHERE p.identity = '{}'",
|
||||
stdb.identity()
|
||||
|
|
@ -100,61 +97,36 @@ fn subscriptions(stdb: SpacetimeDB) {
|
|||
// .subscribe_to_all_tables();
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct TileId(pub u32);
|
||||
|
||||
fn on_player_insert_update(
|
||||
_stdb: SpacetimeDB,
|
||||
mut messages: ReadInsertUpdateMessage<jong_db::Player>,
|
||||
|
||||
mut commands: Commands,
|
||||
|
||||
tiles: Query<(&Tile, &TileId, Entity)>,
|
||||
player: Option<Single<&mut player::Player>>,
|
||||
hand_ent: Option<Single<Entity, With<Hand>>>,
|
||||
player: Option<Single<Entity, With<MainPlayer>>>,
|
||||
hand: Option<Single<Entity, With<Hand>>>,
|
||||
) {
|
||||
use player::*;
|
||||
let hand = if player.is_none() && hand.is_none() {
|
||||
let hand = commands.spawn(Hand).id();
|
||||
commands.spawn((Player, MainPlayer)).add_child(hand);
|
||||
hand
|
||||
} else {
|
||||
*hand.unwrap()
|
||||
};
|
||||
|
||||
for msg in messages.read() {
|
||||
// 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");
|
||||
// // TODO add a start game button in the future
|
||||
// stdb.reducers().start_game().unwrap();
|
||||
// }
|
||||
let tiles: Vec<_> = msg
|
||||
.new
|
||||
.hand
|
||||
.iter()
|
||||
.map(|dbt| {
|
||||
// TODO this seems a lil expensive
|
||||
if let Some(ent) = tiles
|
||||
.iter()
|
||||
.find(|(_, id, _)| id.0 == dbt.id)
|
||||
.map(|(_, _, e)| e)
|
||||
{
|
||||
ent
|
||||
} else {
|
||||
commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()
|
||||
}
|
||||
})
|
||||
.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
|
||||
.new
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&"nameless".to_string())
|
||||
.clone(),
|
||||
};
|
||||
let bundle = (player, Hand, Pond, MainPlayer, CurrentPlayer);
|
||||
commands.spawn(bundle);
|
||||
let tiles: Vec<_> = msg
|
||||
.new
|
||||
.hand
|
||||
.iter()
|
||||
.map(|dbt| {
|
||||
// drawn tiles will always be new entities to us, until wall isn't fake
|
||||
commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()
|
||||
})
|
||||
.collect();
|
||||
|
||||
commands.entity(hand).replace_children(&tiles);
|
||||
|
||||
if let Some(dbt) = &msg.new.drawn_tile {
|
||||
commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id), Drawn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,9 +135,9 @@ fn on_lobby_insert_update(
|
|||
stdb: SpacetimeDB,
|
||||
mut messages: ReadInsertUpdateMessage<jong_db::Lobby>,
|
||||
|
||||
_commands: Commands,
|
||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||
commands: Commands,
|
||||
mut next_gamestate: ResMut<NextState<jong_types::states::GameState>>,
|
||||
mut next_turnstate: ResMut<NextState<jong_types::states::TurnState>>,
|
||||
) {
|
||||
for msg in messages.read() {
|
||||
// trace!("on_lobby_insert_update msg:\n{:#?}", msg.new);
|
||||
|
|
@ -212,7 +184,6 @@ fn on_lobby_insert_update(
|
|||
stdb.reducers().skip_call().unwrap();
|
||||
}
|
||||
jong_db::TurnState::End => todo!(),
|
||||
// _ => todo!(),
|
||||
}
|
||||
next_turnstate.set(msg.new.turn_state.into());
|
||||
}
|
||||
25
jong/src/riichi/player.rs
Normal file
25
jong/src/riichi/player.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Player;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MainPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct CurrentPlayer;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct TileId(pub u32);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Hand;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Pond;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Drawn;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Discarded;
|
||||
100
jong/src/riichi/round.rs
Normal file
100
jong/src/riichi/round.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
use strum::{EnumCount, FromRepr};
|
||||
|
||||
use crate::EnumNextCycle;
|
||||
use jong_types::states::TurnState;
|
||||
|
||||
// #[derive(Resource)]
|
||||
// pub struct CurrentPlayer(pub Entity);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct MatchSettings {
|
||||
pub(crate) starting_points: isize,
|
||||
pub(crate) player_count: u8,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub(crate) struct Dice(u8, u8);
|
||||
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct Compass {
|
||||
pub(crate) prevalent_wind: Wind,
|
||||
pub(crate) round: u8,
|
||||
pub(crate) dealer_wind: Wind,
|
||||
pub(crate) riichi: usize,
|
||||
pub(crate) honba: usize,
|
||||
}
|
||||
|
||||
pub enum WindRelation {
|
||||
Shimocha,
|
||||
Toimen,
|
||||
Kamicha,
|
||||
}
|
||||
|
||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum CallType {
|
||||
Skip,
|
||||
Ron,
|
||||
Chii,
|
||||
Pon,
|
||||
Kan,
|
||||
}
|
||||
|
||||
impl Default for MatchSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
starting_points: 25000,
|
||||
player_count: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Compass {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
prevalent_wind: Wind::Ton,
|
||||
round: 1,
|
||||
dealer_wind: Wind::Ton,
|
||||
riichi: 0,
|
||||
honba: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for Wind {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wind {
|
||||
pub fn relate(&self, other: &Self) -> WindRelation {
|
||||
if self.next() == *other {
|
||||
WindRelation::Shimocha
|
||||
} else if other.next() == *self {
|
||||
WindRelation::Kamicha
|
||||
} else {
|
||||
WindRelation::Toimen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumNextCycle for TurnState {
|
||||
fn next(&self) -> Self {
|
||||
if (*self as usize + 1) >= Self::COUNT {
|
||||
Self::from_repr(0).unwrap()
|
||||
} else {
|
||||
Self::from_repr(*self as usize + 1).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct PendingCalls {
|
||||
eligible: Vec<Entity>,
|
||||
calls: HashMap<Entity, CallType>,
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use jong_types::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Dora;
|
||||
|
||||
pub fn init_tiles(mut commands: Commands) {
|
||||
let tiles = tiles();
|
||||
commands.spawn_batch(tiles);
|
||||
}
|
||||
|
|
@ -4,35 +4,17 @@ 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::{
|
||||
SpacetimeDB,
|
||||
game::{
|
||||
hand::{Drawn, Hand},
|
||||
player::{CurrentPlayer, Player},
|
||||
},
|
||||
};
|
||||
use jong::{SpacetimeDB, riichi::player::*};
|
||||
use jong_db::{self, discard_tile as _};
|
||||
use jong_types::{GameState, TurnState};
|
||||
use jong_types::states::{GameState, TurnState};
|
||||
|
||||
mod input;
|
||||
mod layout;
|
||||
mod render;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TuiPlugin;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, SystemSet)]
|
||||
pub enum TuiSet {
|
||||
Input,
|
||||
Layout,
|
||||
Render,
|
||||
}
|
||||
|
||||
use input::ConfirmSelect;
|
||||
use states::ConsoleWidget;
|
||||
mod states {
|
||||
use bevy::prelude::*;
|
||||
use tui_logger::TuiWidgetState;
|
||||
|
|
@ -59,6 +41,16 @@ mod states {
|
|||
// }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TuiPlugin;
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, SystemSet)]
|
||||
pub enum TuiSet {
|
||||
Input,
|
||||
Layout,
|
||||
Render,
|
||||
}
|
||||
|
||||
impl Plugin for TuiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins((
|
||||
|
|
@ -93,7 +85,7 @@ impl Plugin for TuiPlugin {
|
|||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
render::render_hands.run_if(in_state(GameState::Play)),
|
||||
render::render_hand.run_if(in_state(GameState::Play)),
|
||||
render::render,
|
||||
)
|
||||
.chain()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use jong_db::start_game;
|
|||
use tui_logger::TuiWidgetEvent;
|
||||
|
||||
use jong::SpacetimeDB;
|
||||
use jong_types::GameState;
|
||||
|
||||
use crate::tui::layout::Overlays;
|
||||
use crate::tui::states::ConsoleWidget;
|
||||
|
|
@ -21,9 +20,9 @@ pub(crate) fn keyboard(
|
|||
mut consolewidget: ResMut<ConsoleWidget>,
|
||||
mut exit: MessageWriter<AppExit>,
|
||||
|
||||
curr_gamestate: Res<State<GameState>>,
|
||||
curr_gamestate: Res<State<jong_types::states::GameState>>,
|
||||
curr_tuistate: Res<State<TuiState>>,
|
||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||
mut next_gamestate: ResMut<NextState<jong_types::states::GameState>>,
|
||||
mut next_tuistate: ResMut<NextState<TuiState>>,
|
||||
) {
|
||||
'message: for message in messages.read() {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ use ratatui::layout::{Constraint, Flex, Layout, Offset, Rect, Size};
|
|||
use ratatui::style::{Modifier, Stylize};
|
||||
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
||||
|
||||
use jong::game::hand::{Drawn, Hand};
|
||||
use jong::game::player::{CurrentPlayer, MainPlayer, Player};
|
||||
use jong::game::round::Wind;
|
||||
use jong::riichi::player::{CurrentPlayer, MainPlayer, Player};
|
||||
use jong::riichi::player::{Drawn, Hand};
|
||||
// use jong::riichi::round::Wind;
|
||||
// use jong_types::*;
|
||||
|
||||
use crate::tui::input::Hovered;
|
||||
|
|
@ -95,26 +95,9 @@ pub(crate) fn render(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// pub(crate) fn render_arg_check(
|
||||
// mut commands: Commands,
|
||||
// mut tui: ResMut<RatatuiContext>,
|
||||
|
||||
// hovered: Query<Entity, With<Hovered>>,
|
||||
// layouts: Res<HandLayouts>,
|
||||
|
||||
// tiles: Query<&jong_types::Tile>,
|
||||
// // main_player: Single<(&Player, Entity, &Wind), With<MainPlayer>>,
|
||||
// curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
// players: Query<(&Player, Entity, &Children)>,
|
||||
// hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||
// // drawn_tile: Single<Entity, With<Drawn>>,
|
||||
// ) {
|
||||
// // trace!("arg!");
|
||||
// }
|
||||
|
||||
// FIXME we don't care about other players atm
|
||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||
pub(crate) fn render_hands(
|
||||
pub(crate) fn render_hand(
|
||||
mut commands: Commands,
|
||||
mut tui: ResMut<RatatuiContext>,
|
||||
|
||||
|
|
@ -123,79 +106,66 @@ pub(crate) fn render_hands(
|
|||
|
||||
tiles: Query<&jong_types::Tile>,
|
||||
main_player: Single<(&Player, Entity /* , &Wind */), With<MainPlayer>>,
|
||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||
players: Query<(&Player, Entity, &Children)>,
|
||||
hands: Query<(&Children, Entity), With<Hand>>,
|
||||
drawn_tile: Single<Entity, With<Drawn>>,
|
||||
hand: Single<(&Children, Entity), With<Hand>>,
|
||||
drawn_tile: Option<Single<Entity, With<Drawn>>>,
|
||||
) -> Result {
|
||||
let mut frame = tui.get_frame();
|
||||
debug_blocks(*layouts, &mut frame);
|
||||
|
||||
for (hand, hand_ent) in hands {
|
||||
// debug!("{hand:?}");
|
||||
// let (player, player_ent, _) = players
|
||||
// .iter()
|
||||
// .find(|(_, e, c)| c.contains(&hand_ent))
|
||||
// .unwrap();
|
||||
let hand: Vec<_> = hand
|
||||
.iter()
|
||||
.map(|entity| -> Result<_> {
|
||||
let tile = tiles.get(entity).unwrap_or_else(|_| panic!("{entity:?}"));
|
||||
let hovered = hovered.contains(entity);
|
||||
let widget = render_tile(tile, hovered);
|
||||
let hand: Vec<_> = hand
|
||||
.0
|
||||
.iter()
|
||||
.map(|entity| -> Result<_> {
|
||||
let tile = tiles.get(entity).unwrap_or_else(|_| panic!("{entity:?}"));
|
||||
let hovered = hovered.contains(entity);
|
||||
let widget = render_tile(tile, hovered);
|
||||
|
||||
Ok((entity, widget, hovered))
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
Ok((entity, widget, hovered))
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
let (player, player_ent) = *main_player;
|
||||
// if player == main_player.0 {
|
||||
// split main box into thirds
|
||||
let mut this_hand = layouts.this_hand;
|
||||
// let this_drawer = drawn_tile..is_some_and(|dt| dt.0 == player);
|
||||
let this_drawer = player_ent == *curr_player;
|
||||
let tile_drawn = if this_drawer { 7 } else { 0 };
|
||||
let hand_draw_meld = Layout::horizontal([
|
||||
Constraint::Max(hand.len() as u16 * 5),
|
||||
Constraint::Max(tile_drawn),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.flex(Flex::SpaceBetween);
|
||||
this_hand = this_hand.offset(Offset {
|
||||
x: 0,
|
||||
y: this_hand.height.abs_diff(5) as i32 + 1,
|
||||
});
|
||||
this_hand = this_hand.resize(Size {
|
||||
width: this_hand.width,
|
||||
height: 4,
|
||||
});
|
||||
let [hand_area, drawn_area, meld_area] = hand_draw_meld.areas::<3>(this_hand);
|
||||
let (player, player_ent) = *main_player;
|
||||
// split main box into thirds
|
||||
let mut this_hand = layouts.this_hand;
|
||||
let tile_drawn = if drawn_tile.is_some() { 7 } else { 0 };
|
||||
let hand_draw_meld = Layout::horizontal([
|
||||
Constraint::Max(hand.len() as u16 * 5),
|
||||
Constraint::Max(tile_drawn),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.flex(Flex::SpaceBetween);
|
||||
this_hand = this_hand.offset(Offset {
|
||||
x: 0,
|
||||
y: this_hand.height.abs_diff(5) as i32 + 1,
|
||||
});
|
||||
this_hand = this_hand.resize(Size {
|
||||
width: this_hand.width,
|
||||
height: 4,
|
||||
});
|
||||
let [hand_area, drawn_area, meld_area] = hand_draw_meld.areas::<3>(this_hand);
|
||||
|
||||
// split hand area into tile areas
|
||||
let mut constraints = vec![Constraint::Max(5); hand.len()];
|
||||
constraints.push(Constraint::Fill(1));
|
||||
let layout = Layout::horizontal(constraints).flex(Flex::Start);
|
||||
let tile_areas = layout.split(hand_area);
|
||||
// split hand area into tile areas
|
||||
let mut constraints = vec![Constraint::Max(5); hand.len()];
|
||||
constraints.push(Constraint::Fill(1));
|
||||
let layout = Layout::horizontal(constraints).flex(Flex::Start);
|
||||
let tile_areas = layout.split(hand_area);
|
||||
|
||||
for ((entity, widget, hovered), mut area) in
|
||||
hand.into_iter().zip(tile_areas.iter().copied())
|
||||
{
|
||||
if hovered {
|
||||
area = area.offset(Offset { x: 0, y: -1 });
|
||||
let mut hitbox = area.as_size();
|
||||
hitbox.height += 1;
|
||||
commands.entity(entity).insert(PickRegion {
|
||||
area: area.resize(hitbox),
|
||||
});
|
||||
} else {
|
||||
commands.entity(entity).insert(PickRegion { area });
|
||||
}
|
||||
frame.render_widget(widget, area);
|
||||
for ((entity, widget, hovered), mut area) in hand.into_iter().zip(tile_areas.iter().copied()) {
|
||||
if hovered {
|
||||
area = area.offset(Offset { x: 0, y: -1 });
|
||||
let mut hitbox = area.as_size();
|
||||
hitbox.height += 1;
|
||||
commands.entity(entity).insert(PickRegion {
|
||||
area: area.resize(hitbox),
|
||||
});
|
||||
} else {
|
||||
commands.entity(entity).insert(PickRegion { area });
|
||||
}
|
||||
frame.render_widget(widget, area);
|
||||
}
|
||||
|
||||
// tsumo tile
|
||||
// if this_drawer {
|
||||
// // trace!("this_drawer");
|
||||
// tsumo tile
|
||||
if let Some(drawn_tile) = drawn_tile {
|
||||
let mut area = drawn_area.resize(Size {
|
||||
width: 5,
|
||||
height: 4,
|
||||
|
|
@ -214,16 +184,16 @@ pub(crate) fn render_hands(
|
|||
commands.entity(*drawn_tile).insert(PickRegion { area });
|
||||
}
|
||||
frame.render_widget(widget, area);
|
||||
// }
|
||||
// TODO draw melds
|
||||
// } else {
|
||||
// match mainplayer.1.relate(wind) {
|
||||
// jong::game::round::WindRelation::Shimocha => todo!(),
|
||||
// jong::game::round::WindRelation::Toimen => todo!(),
|
||||
// jong::game::round::WindRelation::Kamicha => todo!(),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO draw melds
|
||||
// } else {
|
||||
// match mainplayer.1.relate(wind) {
|
||||
// jong::game::round::WindRelation::Shimocha => todo!(),
|
||||
// jong::game::round::WindRelation::Toimen => todo!(),
|
||||
// jong::game::round::WindRelation::Kamicha => todo!(),
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
// const TSUMO;
|
||||
Loading…
Add table
Add a link
Reference in a new issue