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,6 +1,6 @@
|
||||||
# [language-server.lspmux]
|
[language-server.lspmux]
|
||||||
# command = "lspmux"
|
command = "lspmux"
|
||||||
|
|
||||||
# [[language]]
|
[[language]]
|
||||||
# name = "rust"
|
name = "rust"
|
||||||
# language-servers = ["lspmux"]
|
language-servers = ["lspmux"]
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
...
|
...
|
||||||
}: rec {
|
}: rec {
|
||||||
# https://devenv.sh/processes/
|
# https://devenv.sh/processes/
|
||||||
# processes.lspmux.exec = "lspmux server";
|
processes.lspmux.exec = "lspmux server";
|
||||||
processes.spacetimedb_start.exec = "spacetime start";
|
processes.spacetimedb_start.exec = "spacetime start";
|
||||||
processes.spacetimedb_dev = {
|
processes.spacetimedb_dev = {
|
||||||
exec = "spacetime dev --module-bindings-path jong-db/src/db jong-line --delete-data=always";
|
exec = "just spacetime_dev";
|
||||||
# notify.enable = true;
|
# notify.enable = true;
|
||||||
# TODO features not yet supp???
|
# TODO features not yet supp???
|
||||||
# restart = "always";
|
# restart = "always";
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,56 @@
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub use db::*;
|
pub use db::*;
|
||||||
|
|
||||||
impl From<GameState> for jong_types::GameState {
|
mod conversions {
|
||||||
fn from(value: GameState) -> Self {
|
impl From<crate::db::GameState> for jong_types::states::GameState {
|
||||||
Self::from_repr(value as usize).unwrap()
|
fn from(value: crate::db::GameState) -> Self {
|
||||||
|
Self::from_repr(value as usize).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TurnState> for jong_types::TurnState {
|
impl From<crate::db::TurnState> for jong_types::states::TurnState {
|
||||||
fn from(value: TurnState) -> Self {
|
fn from(value: crate::db::TurnState) -> Self {
|
||||||
Self::from_repr(value as usize).unwrap()
|
Self::from_repr(value as usize).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Tile> for jong_types::Tile {
|
impl From<&crate::db::Tile> for jong_types::tiles::Tile {
|
||||||
fn from(value: &tile_type::Tile) -> Self {
|
fn from(value: &crate::db::Tile) -> Self {
|
||||||
Self {
|
Self {
|
||||||
suit: value.suit.clone().into(),
|
suit: value.suit.clone().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::Suit> for jong_types::tiles::Suit {
|
||||||
|
fn from(value: crate::db::Suit) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::db::Suit::Man(rank) => Self::Man(rank.into()),
|
||||||
|
crate::db::Suit::Pin(rank) => Self::Pin(rank.into()),
|
||||||
|
crate::db::Suit::Sou(rank) => Self::Sou(rank.into()),
|
||||||
|
crate::db::Suit::Wind(wind) => Self::Wind(wind.into()),
|
||||||
|
crate::db::Suit::Dragon(dragon) => Self::Dragon(dragon.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::Rank> for jong_types::tiles::Rank {
|
||||||
|
fn from(value: crate::db::Rank) -> Self {
|
||||||
|
Self {
|
||||||
|
number: value.number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::Wind> for jong_types::tiles::Wind {
|
||||||
|
fn from(value: crate::db::Wind) -> Self {
|
||||||
|
Self::from_repr(value as usize).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::db::Dragon> for jong_types::tiles::Dragon {
|
||||||
|
fn from(value: crate::db::Dragon) -> Self {
|
||||||
|
Self::from_repr(value as usize).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Suit> for jong_types::Suit {
|
|
||||||
fn from(value: Suit) -> Self {
|
|
||||||
match value {
|
|
||||||
Suit::Man(rank) => Self::Man(rank.into()),
|
|
||||||
Suit::Pin(rank) => Self::Pin(rank.into()),
|
|
||||||
Suit::Sou(rank) => Self::Sou(rank.into()),
|
|
||||||
Suit::Wind(wind) => Self::Wind(wind.into()),
|
|
||||||
Suit::Dragon(dragon) => Self::Dragon(dragon.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rank> for jong_types::Rank {
|
|
||||||
fn from(value: Rank) -> Self {
|
|
||||||
Self {
|
|
||||||
number: value.number,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Wind> for jong_types::Wind {
|
|
||||||
fn from(value: Wind) -> Self {
|
|
||||||
Self::from_repr(value as usize).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Dragon> for jong_types::Dragon {
|
|
||||||
fn from(value: Dragon) -> Self {
|
|
||||||
Self::from_repr(value as usize).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use spacetimedb::{ReducerContext, Table, reducer};
|
||||||
use crate::tables::{player::player, *};
|
use crate::tables::{player::player, *};
|
||||||
|
|
||||||
mod reducers {
|
mod reducers {
|
||||||
mod game;
|
mod deal;
|
||||||
|
mod hand;
|
||||||
|
mod lobby;
|
||||||
}
|
}
|
||||||
mod tables;
|
mod tables;
|
||||||
|
|
||||||
|
|
@ -13,6 +15,18 @@ pub fn clear_all(ctx: &ReducerContext) {
|
||||||
for row in ctx.db.player().iter() {
|
for row in ctx.db.player().iter() {
|
||||||
ctx.db.player().delete(row);
|
ctx.db.player().delete(row);
|
||||||
}
|
}
|
||||||
|
for row in ctx.db.lobby().iter() {
|
||||||
|
ctx.db.lobby().delete(row);
|
||||||
|
}
|
||||||
|
for row in ctx.db.bot().iter() {
|
||||||
|
ctx.db.bot().delete(row);
|
||||||
|
}
|
||||||
|
for row in ctx.db.wall().iter() {
|
||||||
|
ctx.db.wall().delete(row);
|
||||||
|
}
|
||||||
|
for row in ctx.db.tile().iter() {
|
||||||
|
ctx.db.tile().delete(row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[reducer(client_connected)]
|
#[reducer(client_connected)]
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,14 @@ use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer};
|
||||||
|
|
||||||
use super::hand::deal_hands;
|
use super::hand::deal_hands;
|
||||||
use crate::tables::*;
|
use crate::tables::*;
|
||||||
use jong_types::*;
|
|
||||||
|
|
||||||
#[reducer]
|
#[reducer]
|
||||||
pub fn shuffle_deal(ctx: &ReducerContext, lobby_id: u32) {
|
pub fn shuffle_deal(ctx: &ReducerContext, lobby_id: u32) {
|
||||||
debug!("lobby_id: {lobby_id}");
|
debug!("lobby_id: {lobby_id}");
|
||||||
let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap();
|
let mut lobby = ctx.db.lobby().id().find(lobby_id).unwrap();
|
||||||
|
|
||||||
if lobby.game_state == GameState::Setup {
|
if lobby.game_state == jong_types::states::GameState::Setup {
|
||||||
lobby.game_state = GameState::Deal;
|
lobby.game_state = jong_types::states::GameState::Deal;
|
||||||
lobby = ctx.db.lobby().id().update(lobby);
|
lobby = ctx.db.lobby().id().update(lobby);
|
||||||
|
|
||||||
let tiles = new_shuffled_wall(ctx);
|
let tiles = new_shuffled_wall(ctx);
|
||||||
|
|
@ -24,15 +23,15 @@ pub fn shuffle_deal(ctx: &ReducerContext, lobby_id: u32) {
|
||||||
|
|
||||||
deal_hands(ctx, lobby_id);
|
deal_hands(ctx, lobby_id);
|
||||||
|
|
||||||
lobby.game_state = GameState::Play;
|
lobby.game_state = jong_types::states::GameState::Play;
|
||||||
lobby.turn_state = TurnState::Tsumo;
|
lobby.turn_state = jong_types::states::TurnState::Tsumo;
|
||||||
ctx.db.lobby().id().update(lobby);
|
ctx.db.lobby().id().update(lobby);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec<DbTile> {
|
pub fn new_shuffled_wall(ctx: &ReducerContext) -> Vec<DbTile> {
|
||||||
let mut rng = ctx.rng();
|
let mut rng = ctx.rng();
|
||||||
let mut wall: Vec<_> = tiles()
|
let mut wall: Vec<_> = jong_types::tiles::tiles()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|tile| ctx.db.tile().insert(DbTile { id: 0, tile }))
|
.map(|tile| ctx.db.tile().insert(DbTile { id: 0, tile }))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use spacetimedb::{ReducerContext, Table, reducer};
|
use spacetimedb::{ReducerContext, reducer};
|
||||||
|
|
||||||
use crate::tables::{player::player, *};
|
use crate::tables::{player::player, *};
|
||||||
use jong_types::*;
|
use jong_types::states::TurnState;
|
||||||
|
|
||||||
pub fn deal_hands(ctx: &ReducerContext, lobby_id: u32) {
|
pub fn deal_hands(ctx: &ReducerContext, lobby_id: u32) {
|
||||||
let players = ctx.db.player().lobby_id().filter(lobby_id);
|
let players = ctx.db.player().lobby_id().filter(lobby_id);
|
||||||
|
|
@ -2,10 +2,6 @@ use log::info;
|
||||||
use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer};
|
use spacetimedb::{ReducerContext, Table, rand::seq::SliceRandom, reducer};
|
||||||
|
|
||||||
use crate::tables::{player::player, *};
|
use crate::tables::{player::player, *};
|
||||||
use jong_types::*;
|
|
||||||
|
|
||||||
mod deal;
|
|
||||||
mod hand;
|
|
||||||
|
|
||||||
#[reducer]
|
#[reducer]
|
||||||
pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> {
|
pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(), String> {
|
||||||
|
|
@ -22,12 +18,12 @@ pub fn join_or_create_lobby(ctx: &ReducerContext, mut lobby_id: u32) -> Result<(
|
||||||
id: 0,
|
id: 0,
|
||||||
host_player_id: player.id,
|
host_player_id: player.id,
|
||||||
players: vec![PlayerOrBot::Player { id: player.id }],
|
players: vec![PlayerOrBot::Player { id: player.id }],
|
||||||
game_state: GameState::Lobby,
|
game_state: jong_types::states::GameState::Lobby,
|
||||||
turn_state: TurnState::None,
|
turn_state: jong_types::states::TurnState::None,
|
||||||
dealer_idx: 0,
|
dealer_idx: 0,
|
||||||
current_idx: 0,
|
current_idx: 0,
|
||||||
});
|
});
|
||||||
info!("created lobby: {:?}", lobby);
|
info!("created lobby: {}", lobby.id);
|
||||||
|
|
||||||
lobby_id = lobby.id;
|
lobby_id = lobby.id;
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +79,7 @@ pub fn start_game(ctx: &ReducerContext) {
|
||||||
PlayerOrBot::Bot { id } => ctx.db.bot().id().find(id).is_some(),
|
PlayerOrBot::Bot { id } => ctx.db.bot().id().find(id).is_some(),
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
lobby.game_state = GameState::Setup;
|
lobby.game_state = jong_types::states::GameState::Setup;
|
||||||
lobby.players.shuffle(&mut ctx.rng());
|
lobby.players.shuffle(&mut ctx.rng());
|
||||||
lobby.dealer_idx += 1;
|
lobby.dealer_idx += 1;
|
||||||
if lobby.dealer_idx > 3 {
|
if lobby.dealer_idx > 3 {
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use spacetimedb::table;
|
use spacetimedb::{SpacetimeType, table};
|
||||||
|
|
||||||
use jong_types::*;
|
use jong_types::{
|
||||||
|
tiles::Tile,
|
||||||
|
states::{GameState, TurnState},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub use player::*;
|
pub use player::*;
|
||||||
|
|
@ -14,7 +17,7 @@ pub struct Lobby {
|
||||||
|
|
||||||
#[unique]
|
#[unique]
|
||||||
pub host_player_id: u32,
|
pub host_player_id: u32,
|
||||||
pub players: Vec<player::PlayerOrBot>,
|
pub players: Vec<PlayerOrBot>,
|
||||||
pub dealer_idx: u8,
|
pub dealer_idx: u8,
|
||||||
pub current_idx: u8,
|
pub current_idx: u8,
|
||||||
|
|
||||||
|
|
@ -37,5 +40,11 @@ pub struct DbTile {
|
||||||
#[auto_inc]
|
#[auto_inc]
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
|
|
||||||
pub tile: jong_types::Tile,
|
pub tile: Tile,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, SpacetimeType)]
|
||||||
|
pub enum PlayerOrBot {
|
||||||
|
Player { id: u32 },
|
||||||
|
Bot { id: u32 },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,3 @@ pub struct Bot {
|
||||||
|
|
||||||
pub drawn_tile: Option<DbTile>,
|
pub drawn_tile: Option<DbTile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SpacetimeType)]
|
|
||||||
pub enum PlayerOrBot {
|
|
||||||
Player { id: u32 },
|
|
||||||
Bot { id: u32 },
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,127 +9,8 @@ mod derive_alias {
|
||||||
}
|
}
|
||||||
use derive_aliases::derive;
|
use derive_aliases::derive;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
pub mod states;
|
||||||
use spacetimedb::SpacetimeType;
|
pub mod tiles;
|
||||||
use strum::{EnumCount, FromRepr};
|
|
||||||
|
|
||||||
#[derive(..Base, Hash, Default, FromRepr)]
|
pub use states::*;
|
||||||
#[derive(States, SpacetimeType)]
|
pub use tiles::*;
|
||||||
pub enum GameState {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Lobby,
|
|
||||||
Setup,
|
|
||||||
Deal,
|
|
||||||
Play,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(..Base)]
|
|
||||||
#[derive(Component, SpacetimeType)]
|
|
||||||
pub struct Tile {
|
|
||||||
pub suit: Suit,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(..Base)]
|
|
||||||
#[derive(SpacetimeType)]
|
|
||||||
pub enum Suit {
|
|
||||||
Man(Rank),
|
|
||||||
Pin(Rank),
|
|
||||||
Sou(Rank),
|
|
||||||
Wind(Wind),
|
|
||||||
Dragon(Dragon),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Suit {
|
|
||||||
pub fn rank(&self) -> Option<Rank> {
|
|
||||||
match self {
|
|
||||||
Suit::Man(rank) => Some(*rank),
|
|
||||||
Suit::Pin(rank) => Some(*rank),
|
|
||||||
Suit::Sou(rank) => Some(*rank),
|
|
||||||
// Suit::Wind(wind) | Suit::Dragon(dragon) => None,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
..Base,
|
|
||||||
Deref,
|
|
||||||
DerefMut,
|
|
||||||
)]
|
|
||||||
#[derive(SpacetimeType)]
|
|
||||||
pub struct Rank {
|
|
||||||
pub number: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
..Base,
|
|
||||||
FromRepr,
|
|
||||||
)]
|
|
||||||
#[derive(SpacetimeType)]
|
|
||||||
pub enum Wind {
|
|
||||||
Ton,
|
|
||||||
Nan,
|
|
||||||
Shaa,
|
|
||||||
Pei,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
..Base,
|
|
||||||
FromRepr,
|
|
||||||
)]
|
|
||||||
#[derive(SpacetimeType)]
|
|
||||||
pub enum Dragon {
|
|
||||||
Haku,
|
|
||||||
Hatsu,
|
|
||||||
Chun,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tiles() -> Vec<Tile> {
|
|
||||||
let mut tiles = vec![];
|
|
||||||
for _ in 0..4 {
|
|
||||||
for i in 1..=9 {
|
|
||||||
tiles.push(Tile {
|
|
||||||
suit: Suit::Pin(Rank { number: i }),
|
|
||||||
});
|
|
||||||
tiles.push(Tile {
|
|
||||||
suit: Suit::Sou(Rank { number: i }),
|
|
||||||
});
|
|
||||||
tiles.push(Tile {
|
|
||||||
suit: Suit::Man(Rank { number: i }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for i in 0..4 {
|
|
||||||
tiles.push(Tile {
|
|
||||||
suit: Suit::Wind(Wind::from_repr(i).unwrap()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for i in 0..3 {
|
|
||||||
tiles.push(Tile {
|
|
||||||
suit: Suit::Dragon(Dragon::from_repr(i).unwrap()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tiles
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Default,
|
|
||||||
..Copy,
|
|
||||||
PartialEq,
|
|
||||||
Eq,
|
|
||||||
Hash,
|
|
||||||
Debug,
|
|
||||||
)]
|
|
||||||
#[derive(SubStates, FromRepr, EnumCount, SpacetimeType)]
|
|
||||||
#[source(GameState = GameState::Play)]
|
|
||||||
pub enum TurnState {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Tsumo,
|
|
||||||
Menzen,
|
|
||||||
RiichiKan,
|
|
||||||
RonChiiPonKan,
|
|
||||||
End,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
37
jong-types/src/states.rs
Normal file
37
jong-types/src/states.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use spacetimedb::SpacetimeType;
|
||||||
|
use strum::{EnumCount, FromRepr};
|
||||||
|
|
||||||
|
use super::derive;
|
||||||
|
|
||||||
|
#[derive(..Base, Hash, Default, FromRepr)]
|
||||||
|
#[derive(States, SpacetimeType)]
|
||||||
|
pub enum GameState {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Lobby,
|
||||||
|
Setup,
|
||||||
|
Deal,
|
||||||
|
Play,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Default,
|
||||||
|
..Copy,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Debug,
|
||||||
|
)]
|
||||||
|
#[derive(SubStates, FromRepr, EnumCount, SpacetimeType)]
|
||||||
|
#[source(GameState = GameState::Play)]
|
||||||
|
pub enum TurnState {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Tsumo,
|
||||||
|
Menzen,
|
||||||
|
RiichiKan,
|
||||||
|
RonChiiPonKan,
|
||||||
|
End,
|
||||||
|
}
|
||||||
94
jong-types/src/tiles.rs
Normal file
94
jong-types/src/tiles.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use spacetimedb::SpacetimeType;
|
||||||
|
use strum::{EnumCount, FromRepr};
|
||||||
|
|
||||||
|
use super::derive;
|
||||||
|
|
||||||
|
#[derive(..Base)]
|
||||||
|
#[derive(Component, SpacetimeType)]
|
||||||
|
pub struct Tile {
|
||||||
|
pub suit: Suit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(..Base)]
|
||||||
|
#[derive(SpacetimeType)]
|
||||||
|
pub enum Suit {
|
||||||
|
Man(Rank),
|
||||||
|
Pin(Rank),
|
||||||
|
Sou(Rank),
|
||||||
|
Wind(Wind),
|
||||||
|
Dragon(Dragon),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Suit {
|
||||||
|
pub fn rank(&self) -> Option<Rank> {
|
||||||
|
match self {
|
||||||
|
Suit::Man(rank) => Some(*rank),
|
||||||
|
Suit::Pin(rank) => Some(*rank),
|
||||||
|
Suit::Sou(rank) => Some(*rank),
|
||||||
|
// Suit::Wind(wind) | Suit::Dragon(dragon) => None,
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
..Base,
|
||||||
|
Deref,
|
||||||
|
DerefMut,
|
||||||
|
)]
|
||||||
|
#[derive(SpacetimeType)]
|
||||||
|
pub struct Rank {
|
||||||
|
pub number: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
..Base,
|
||||||
|
FromRepr,
|
||||||
|
)]
|
||||||
|
#[derive(SpacetimeType)]
|
||||||
|
pub enum Wind {
|
||||||
|
Ton,
|
||||||
|
Nan,
|
||||||
|
Shaa,
|
||||||
|
Pei,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
..Base,
|
||||||
|
FromRepr,
|
||||||
|
)]
|
||||||
|
#[derive(SpacetimeType)]
|
||||||
|
pub enum Dragon {
|
||||||
|
Haku,
|
||||||
|
Hatsu,
|
||||||
|
Chun,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles() -> Vec<Tile> {
|
||||||
|
let mut tiles = vec![];
|
||||||
|
for _ in 0..4 {
|
||||||
|
for i in 1..=9 {
|
||||||
|
tiles.push(Tile {
|
||||||
|
suit: Suit::Pin(Rank { number: i }),
|
||||||
|
});
|
||||||
|
tiles.push(Tile {
|
||||||
|
suit: Suit::Sou(Rank { number: i }),
|
||||||
|
});
|
||||||
|
tiles.push(Tile {
|
||||||
|
suit: Suit::Man(Rank { number: i }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for i in 0..4 {
|
||||||
|
tiles.push(Tile {
|
||||||
|
suit: Suit::Wind(Wind::from_repr(i).unwrap()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for i in 0..3 {
|
||||||
|
tiles.push(Tile {
|
||||||
|
suit: Suit::Dragon(Dragon::from_repr(i).unwrap()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tiles
|
||||||
|
}
|
||||||
|
|
@ -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 bevy_spacetimedb::StdbConnection;
|
||||||
use spacetimedb_sdk::credentials;
|
use spacetimedb_sdk::credentials;
|
||||||
|
|
||||||
pub mod game;
|
pub mod riichi;
|
||||||
pub mod tile;
|
|
||||||
pub mod yakus;
|
|
||||||
|
|
||||||
trait EnumNextCycle {
|
trait EnumNextCycle {
|
||||||
fn next(&self) -> Self;
|
fn next(&self) -> Self;
|
||||||
|
|
@ -15,6 +13,5 @@ trait EnumNextCycle {
|
||||||
pub type SpacetimeDB<'a> = Res<'a, StdbConnection<jong_db::DbConnection>>;
|
pub type SpacetimeDB<'a> = Res<'a, StdbConnection<jong_db::DbConnection>>;
|
||||||
|
|
||||||
fn creds_store() -> credentials::File {
|
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::Level;
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
mod gui;
|
// mod gui;
|
||||||
mod tui;
|
mod tui;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|
@ -25,15 +25,11 @@ fn main() {
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
let app = match args.mode {
|
let app = match args.mode {
|
||||||
Mode::RunGui => {
|
Mode::RunGui => app.add_plugins(DefaultPlugins.set(LogPlugin {
|
||||||
app.add_plugins(DefaultPlugins.set(LogPlugin {
|
filter: FILTERSTRING.into(),
|
||||||
filter: FILTERSTRING.into(),
|
level: Level::TRACE,
|
||||||
level: Level::TRACE,
|
..Default::default()
|
||||||
// custom_layer: todo!(),
|
})),
|
||||||
// fmt_layer: todo!(),
|
|
||||||
..Default::default()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
Mode::RunTui => {
|
Mode::RunTui => {
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(tui_logger::TuiTracingSubscriberLayer)
|
.with(tui_logger::TuiTracingSubscriberLayer)
|
||||||
|
|
@ -45,7 +41,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.add_plugins(jong::game::Riichi);
|
app.add_plugins(jong::riichi::Riichi);
|
||||||
|
|
||||||
app.run();
|
app.run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,22 @@ use bevy::prelude::*;
|
||||||
use bevy_spacetimedb::{
|
use bevy_spacetimedb::{
|
||||||
ReadInsertUpdateMessage, ReadStdbConnectedMessage, ReadStdbDisconnectedMessage, StdbPlugin,
|
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::{self, DbConnection, LobbyTableAccess, PlayerTableAccess, RemoteTables};
|
||||||
use jong_db::{add_bot, draw_tile, set_ready, shuffle_deal, skip_call, start_game};
|
use jong_db::{add_bot, draw_tile, set_ready, shuffle_deal, skip_call, start_game};
|
||||||
use jong_types::*;
|
use jong_types::*;
|
||||||
|
|
||||||
pub mod hand;
|
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod round;
|
use crate::riichi::player::*;
|
||||||
pub mod wall;
|
use crate::{SpacetimeDB, creds_store};
|
||||||
|
// pub mod round;
|
||||||
|
|
||||||
pub struct Riichi;
|
pub struct Riichi;
|
||||||
impl Plugin for Riichi {
|
impl Plugin for Riichi {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let plugins = StdbPlugin::default()
|
let plugins = StdbPlugin::default()
|
||||||
.with_uri("http://localhost:3000")
|
.with_uri("http://localhost:3000")
|
||||||
.with_module_name("jongline")
|
.with_module_name("jong-line")
|
||||||
.with_run_fn(DbConnection::run_threaded)
|
.with_run_fn(DbConnection::run_threaded)
|
||||||
// TODO why don't I need to call add_reducer?
|
// TODO why don't I need to call add_reducer?
|
||||||
|
|
||||||
|
|
@ -41,8 +36,8 @@ impl Plugin for Riichi {
|
||||||
};
|
};
|
||||||
|
|
||||||
app.add_plugins(plugins)
|
app.add_plugins(plugins)
|
||||||
.init_state::<GameState>()
|
.init_state::<jong_types::states::GameState>()
|
||||||
.add_sub_state::<TurnState>()
|
.add_sub_state::<jong_types::states::TurnState>()
|
||||||
// .init_resource::<round::MatchSettings>()
|
// .init_resource::<round::MatchSettings>()
|
||||||
// .init_resource::<round::Compass>()
|
// .init_resource::<round::Compass>()
|
||||||
// .add_systems(Startup, tile::init_tiles)
|
// .add_systems(Startup, tile::init_tiles)
|
||||||
|
|
@ -70,6 +65,7 @@ fn on_connect(stdb: SpacetimeDB, mut messages: ReadStdbConnectedMessage, _comman
|
||||||
for msg in messages.read() {
|
for msg in messages.read() {
|
||||||
info!("you're now jongline");
|
info!("you're now jongline");
|
||||||
|
|
||||||
|
// FIXME hack that doesn't work for startup crash?
|
||||||
while stdb.try_identity().is_none() {}
|
while stdb.try_identity().is_none() {}
|
||||||
|
|
||||||
debug!("with identity: {}", stdb.identity());
|
debug!("with identity: {}", stdb.identity());
|
||||||
|
|
@ -91,6 +87,7 @@ fn subscriptions(stdb: SpacetimeDB) {
|
||||||
.on_applied(|_| trace!("made all subs!"))
|
.on_applied(|_| trace!("made all subs!"))
|
||||||
.on_error(|_, err| error!("sub failed: {err}"))
|
.on_error(|_, err| error!("sub failed: {err}"))
|
||||||
.subscribe([
|
.subscribe([
|
||||||
|
// TODO until views work
|
||||||
format!(
|
format!(
|
||||||
"SELECT * FROM player p WHERE p.identity = '{}'",
|
"SELECT * FROM player p WHERE p.identity = '{}'",
|
||||||
stdb.identity()
|
stdb.identity()
|
||||||
|
|
@ -100,61 +97,36 @@ fn subscriptions(stdb: SpacetimeDB) {
|
||||||
// .subscribe_to_all_tables();
|
// .subscribe_to_all_tables();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct TileId(pub u32);
|
|
||||||
|
|
||||||
fn on_player_insert_update(
|
fn on_player_insert_update(
|
||||||
_stdb: SpacetimeDB,
|
|
||||||
mut messages: ReadInsertUpdateMessage<jong_db::Player>,
|
mut messages: ReadInsertUpdateMessage<jong_db::Player>,
|
||||||
|
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
|
||||||
tiles: Query<(&Tile, &TileId, Entity)>,
|
player: Option<Single<Entity, With<MainPlayer>>>,
|
||||||
player: Option<Single<&mut player::Player>>,
|
hand: Option<Single<Entity, With<Hand>>>,
|
||||||
hand_ent: 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() {
|
for msg in messages.read() {
|
||||||
// debug!("player_insert_update msg:\n{:#?}", msg.new);
|
let tiles: Vec<_> = msg
|
||||||
if let (Some(_player), Some(hand_ent)) = (player.as_ref(), hand_ent.as_ref()) {
|
.new
|
||||||
// if msg.old.as_ref().is_some_and(|m| !m.ready) && msg.new.ready {
|
.hand
|
||||||
// trace!("entered ready");
|
.iter()
|
||||||
// // TODO add a start game button in the future
|
.map(|dbt| {
|
||||||
// stdb.reducers().start_game().unwrap();
|
// drawn tiles will always be new entities to us, until wall isn't fake
|
||||||
// }
|
commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id))).id()
|
||||||
let tiles: Vec<_> = msg
|
})
|
||||||
.new
|
.collect();
|
||||||
.hand
|
|
||||||
.iter()
|
commands.entity(hand).replace_children(&tiles);
|
||||||
.map(|dbt| {
|
|
||||||
// TODO this seems a lil expensive
|
if let Some(dbt) = &msg.new.drawn_tile {
|
||||||
if let Some(ent) = tiles
|
commands.spawn((Tile::from(&dbt.tile), TileId(dbt.id), Drawn));
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -163,9 +135,9 @@ fn on_lobby_insert_update(
|
||||||
stdb: SpacetimeDB,
|
stdb: SpacetimeDB,
|
||||||
mut messages: ReadInsertUpdateMessage<jong_db::Lobby>,
|
mut messages: ReadInsertUpdateMessage<jong_db::Lobby>,
|
||||||
|
|
||||||
_commands: Commands,
|
commands: Commands,
|
||||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
mut next_gamestate: ResMut<NextState<jong_types::states::GameState>>,
|
||||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
mut next_turnstate: ResMut<NextState<jong_types::states::TurnState>>,
|
||||||
) {
|
) {
|
||||||
for msg in messages.read() {
|
for msg in messages.read() {
|
||||||
// trace!("on_lobby_insert_update msg:\n{:#?}", msg.new);
|
// trace!("on_lobby_insert_update msg:\n{:#?}", msg.new);
|
||||||
|
|
@ -212,7 +184,6 @@ fn on_lobby_insert_update(
|
||||||
stdb.reducers().skip_call().unwrap();
|
stdb.reducers().skip_call().unwrap();
|
||||||
}
|
}
|
||||||
jong_db::TurnState::End => todo!(),
|
jong_db::TurnState::End => todo!(),
|
||||||
// _ => todo!(),
|
|
||||||
}
|
}
|
||||||
next_turnstate.set(msg.new.turn_state.into());
|
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::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin};
|
||||||
use bevy_ratatui::RatatuiPlugins;
|
use bevy_ratatui::RatatuiPlugins;
|
||||||
use jong::game::TileId;
|
|
||||||
use jong::game::player::MainPlayer;
|
|
||||||
use tui_logger::TuiWidgetState;
|
use tui_logger::TuiWidgetState;
|
||||||
|
|
||||||
use crate::tui::{input::ConfirmSelect, states::ConsoleWidget};
|
use jong::{SpacetimeDB, riichi::player::*};
|
||||||
use jong::{
|
|
||||||
SpacetimeDB,
|
|
||||||
game::{
|
|
||||||
hand::{Drawn, Hand},
|
|
||||||
player::{CurrentPlayer, Player},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use jong_db::{self, discard_tile as _};
|
use jong_db::{self, discard_tile as _};
|
||||||
use jong_types::{GameState, TurnState};
|
use jong_types::states::{GameState, TurnState};
|
||||||
|
|
||||||
mod input;
|
mod input;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod render;
|
mod render;
|
||||||
|
use input::ConfirmSelect;
|
||||||
#[derive(Default)]
|
use states::ConsoleWidget;
|
||||||
pub struct TuiPlugin;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, SystemSet)]
|
|
||||||
pub enum TuiSet {
|
|
||||||
Input,
|
|
||||||
Layout,
|
|
||||||
Render,
|
|
||||||
}
|
|
||||||
|
|
||||||
mod states {
|
mod states {
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use tui_logger::TuiWidgetState;
|
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 {
|
impl Plugin for TuiPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
|
|
@ -93,7 +85,7 @@ impl Plugin for TuiPlugin {
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
render::render_hands.run_if(in_state(GameState::Play)),
|
render::render_hand.run_if(in_state(GameState::Play)),
|
||||||
render::render,
|
render::render,
|
||||||
)
|
)
|
||||||
.chain()
|
.chain()
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use jong_db::start_game;
|
||||||
use tui_logger::TuiWidgetEvent;
|
use tui_logger::TuiWidgetEvent;
|
||||||
|
|
||||||
use jong::SpacetimeDB;
|
use jong::SpacetimeDB;
|
||||||
use jong_types::GameState;
|
|
||||||
|
|
||||||
use crate::tui::layout::Overlays;
|
use crate::tui::layout::Overlays;
|
||||||
use crate::tui::states::ConsoleWidget;
|
use crate::tui::states::ConsoleWidget;
|
||||||
|
|
@ -21,9 +20,9 @@ pub(crate) fn keyboard(
|
||||||
mut consolewidget: ResMut<ConsoleWidget>,
|
mut consolewidget: ResMut<ConsoleWidget>,
|
||||||
mut exit: MessageWriter<AppExit>,
|
mut exit: MessageWriter<AppExit>,
|
||||||
|
|
||||||
curr_gamestate: Res<State<GameState>>,
|
curr_gamestate: Res<State<jong_types::states::GameState>>,
|
||||||
curr_tuistate: Res<State<TuiState>>,
|
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>>,
|
mut next_tuistate: ResMut<NextState<TuiState>>,
|
||||||
) {
|
) {
|
||||||
'message: for message in messages.read() {
|
'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::style::{Modifier, Stylize};
|
||||||
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
||||||
|
|
||||||
use jong::game::hand::{Drawn, Hand};
|
use jong::riichi::player::{CurrentPlayer, MainPlayer, Player};
|
||||||
use jong::game::player::{CurrentPlayer, MainPlayer, Player};
|
use jong::riichi::player::{Drawn, Hand};
|
||||||
use jong::game::round::Wind;
|
// use jong::riichi::round::Wind;
|
||||||
// use jong_types::*;
|
// use jong_types::*;
|
||||||
|
|
||||||
use crate::tui::input::Hovered;
|
use crate::tui::input::Hovered;
|
||||||
|
|
@ -95,26 +95,9 @@ pub(crate) fn render(
|
||||||
Ok(())
|
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
|
// FIXME we don't care about other players atm
|
||||||
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
||||||
pub(crate) fn render_hands(
|
pub(crate) fn render_hand(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut tui: ResMut<RatatuiContext>,
|
mut tui: ResMut<RatatuiContext>,
|
||||||
|
|
||||||
|
|
@ -123,79 +106,66 @@ pub(crate) fn render_hands(
|
||||||
|
|
||||||
tiles: Query<&jong_types::Tile>,
|
tiles: Query<&jong_types::Tile>,
|
||||||
main_player: Single<(&Player, Entity /* , &Wind */), With<MainPlayer>>,
|
main_player: Single<(&Player, Entity /* , &Wind */), With<MainPlayer>>,
|
||||||
curr_player: Single<Entity, With<CurrentPlayer>>,
|
hand: Single<(&Children, Entity), With<Hand>>,
|
||||||
players: Query<(&Player, Entity, &Children)>,
|
drawn_tile: Option<Single<Entity, With<Drawn>>>,
|
||||||
hands: Query<(&Children, Entity), With<Hand>>,
|
|
||||||
drawn_tile: Single<Entity, With<Drawn>>,
|
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let mut frame = tui.get_frame();
|
let mut frame = tui.get_frame();
|
||||||
debug_blocks(*layouts, &mut frame);
|
debug_blocks(*layouts, &mut frame);
|
||||||
|
|
||||||
for (hand, hand_ent) in hands {
|
let hand: Vec<_> = hand
|
||||||
// debug!("{hand:?}");
|
.0
|
||||||
// let (player, player_ent, _) = players
|
.iter()
|
||||||
// .iter()
|
.map(|entity| -> Result<_> {
|
||||||
// .find(|(_, e, c)| c.contains(&hand_ent))
|
let tile = tiles.get(entity).unwrap_or_else(|_| panic!("{entity:?}"));
|
||||||
// .unwrap();
|
let hovered = hovered.contains(entity);
|
||||||
let hand: Vec<_> = hand
|
let widget = render_tile(tile, hovered);
|
||||||
.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))
|
Ok((entity, widget, hovered))
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let (player, player_ent) = *main_player;
|
let (player, player_ent) = *main_player;
|
||||||
// if player == main_player.0 {
|
// split main box into thirds
|
||||||
// split main box into thirds
|
let mut this_hand = layouts.this_hand;
|
||||||
let mut this_hand = layouts.this_hand;
|
let tile_drawn = if drawn_tile.is_some() { 7 } else { 0 };
|
||||||
// let this_drawer = drawn_tile..is_some_and(|dt| dt.0 == player);
|
let hand_draw_meld = Layout::horizontal([
|
||||||
let this_drawer = player_ent == *curr_player;
|
Constraint::Max(hand.len() as u16 * 5),
|
||||||
let tile_drawn = if this_drawer { 7 } else { 0 };
|
Constraint::Max(tile_drawn),
|
||||||
let hand_draw_meld = Layout::horizontal([
|
Constraint::Fill(1),
|
||||||
Constraint::Max(hand.len() as u16 * 5),
|
])
|
||||||
Constraint::Max(tile_drawn),
|
.flex(Flex::SpaceBetween);
|
||||||
Constraint::Fill(1),
|
this_hand = this_hand.offset(Offset {
|
||||||
])
|
x: 0,
|
||||||
.flex(Flex::SpaceBetween);
|
y: this_hand.height.abs_diff(5) as i32 + 1,
|
||||||
this_hand = this_hand.offset(Offset {
|
});
|
||||||
x: 0,
|
this_hand = this_hand.resize(Size {
|
||||||
y: this_hand.height.abs_diff(5) as i32 + 1,
|
width: this_hand.width,
|
||||||
});
|
height: 4,
|
||||||
this_hand = this_hand.resize(Size {
|
});
|
||||||
width: this_hand.width,
|
let [hand_area, drawn_area, meld_area] = hand_draw_meld.areas::<3>(this_hand);
|
||||||
height: 4,
|
|
||||||
});
|
|
||||||
let [hand_area, drawn_area, meld_area] = hand_draw_meld.areas::<3>(this_hand);
|
|
||||||
|
|
||||||
// split hand area into tile areas
|
// split hand area into tile areas
|
||||||
let mut constraints = vec![Constraint::Max(5); hand.len()];
|
let mut constraints = vec![Constraint::Max(5); hand.len()];
|
||||||
constraints.push(Constraint::Fill(1));
|
constraints.push(Constraint::Fill(1));
|
||||||
let layout = Layout::horizontal(constraints).flex(Flex::Start);
|
let layout = Layout::horizontal(constraints).flex(Flex::Start);
|
||||||
let tile_areas = layout.split(hand_area);
|
let tile_areas = layout.split(hand_area);
|
||||||
|
|
||||||
for ((entity, widget, hovered), mut area) in
|
for ((entity, widget, hovered), mut area) in hand.into_iter().zip(tile_areas.iter().copied()) {
|
||||||
hand.into_iter().zip(tile_areas.iter().copied())
|
if hovered {
|
||||||
{
|
area = area.offset(Offset { x: 0, y: -1 });
|
||||||
if hovered {
|
let mut hitbox = area.as_size();
|
||||||
area = area.offset(Offset { x: 0, y: -1 });
|
hitbox.height += 1;
|
||||||
let mut hitbox = area.as_size();
|
commands.entity(entity).insert(PickRegion {
|
||||||
hitbox.height += 1;
|
area: area.resize(hitbox),
|
||||||
commands.entity(entity).insert(PickRegion {
|
});
|
||||||
area: area.resize(hitbox),
|
} else {
|
||||||
});
|
commands.entity(entity).insert(PickRegion { area });
|
||||||
} else {
|
|
||||||
commands.entity(entity).insert(PickRegion { area });
|
|
||||||
}
|
|
||||||
frame.render_widget(widget, area);
|
|
||||||
}
|
}
|
||||||
|
frame.render_widget(widget, area);
|
||||||
|
}
|
||||||
|
|
||||||
// tsumo tile
|
// tsumo tile
|
||||||
// if this_drawer {
|
if let Some(drawn_tile) = drawn_tile {
|
||||||
// // trace!("this_drawer");
|
|
||||||
let mut area = drawn_area.resize(Size {
|
let mut area = drawn_area.resize(Size {
|
||||||
width: 5,
|
width: 5,
|
||||||
height: 4,
|
height: 4,
|
||||||
|
|
@ -214,16 +184,16 @@ pub(crate) fn render_hands(
|
||||||
commands.entity(*drawn_tile).insert(PickRegion { area });
|
commands.entity(*drawn_tile).insert(PickRegion { area });
|
||||||
}
|
}
|
||||||
frame.render_widget(widget, 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
// const TSUMO;
|
|
||||||
4
justfile
4
justfile
|
|
@ -8,7 +8,7 @@ default:
|
||||||
just --list
|
just --list
|
||||||
|
|
||||||
run-tui:
|
run-tui:
|
||||||
spacetime call jong-line "clear_all"
|
spacetime call jong-line clear_all
|
||||||
cargo run -- run-tui
|
cargo run -- run-tui
|
||||||
|
|
||||||
update:
|
update:
|
||||||
|
|
@ -19,7 +19,7 @@ spacetime:
|
||||||
devenv up
|
devenv up
|
||||||
|
|
||||||
spacetime_dev:
|
spacetime_dev:
|
||||||
spacetime dev --module-bindings-path jong-db/src/db jong-line --delete-data=always
|
spacetime dev --module-project-path jong-line --module-bindings-path jong-db/src/db jong-line --delete-data=always
|
||||||
|
|
||||||
spacetime_generate-bindings:
|
spacetime_generate-bindings:
|
||||||
spacetime generate --lang rust --out-dir jong-db/src/db --project-path jong-line
|
spacetime generate --lang rust --out-dir jong-db/src/db --project-path jong-line
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue