jong/src/game/round.rs

207 lines
5.1 KiB
Rust
Raw Normal View History

2026-01-13 00:01:38 -08:00
use bevy::prelude::*;
2026-01-13 12:38:41 -08:00
use strum::{EnumCount, FromRepr};
2026-01-13 00:01:38 -08:00
2026-01-13 13:07:21 -08:00
use crate::{
EnumNextCycle,
2026-01-16 22:37:05 -08:00
game::{
GameMessage, GameState,
2026-01-17 02:22:47 -08:00
hand::{DiscardedTile, DrawnTile, Hand},
2026-01-16 22:37:05 -08:00
player::Player,
wall::Wall,
},
2026-01-13 13:07:21 -08:00
};
2026-01-13 00:01:38 -08:00
2026-01-16 22:37:05 -08:00
#[derive(Resource)]
pub struct CurrentPlayer(pub Entity);
#[derive(Resource)]
pub(crate) struct MatchSettings {
pub(crate) starting_points: isize,
pub(crate) player_count: u8,
}
2026-01-13 00:01:38 -08:00
#[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,
}
2026-01-16 22:37:05 -08:00
#[derive(Component, Clone, Copy, FromRepr, EnumCount, PartialEq)]
pub enum Wind {
Ton,
Nan,
Shaa,
Pei,
2026-01-13 00:01:38 -08:00
}
2026-01-16 22:37:05 -08:00
pub enum WindRelation {
Shimocha,
Toimen,
Kamicha,
2026-01-13 00:01:38 -08:00
}
2026-01-16 22:37:05 -08:00
#[derive(SubStates, Default, Clone, Copy, PartialEq, Eq, Hash, Debug, FromRepr, EnumCount)]
#[source(GameState = GameState::Play)]
pub(crate) enum TurnState {
#[default]
Tsumo,
Menzen,
RiichiKan,
Discard,
RonChiiPonKan,
End,
}
#[derive(EntityEvent)]
pub struct Discard(pub Entity);
2026-01-13 00:01:38 -08:00
impl Default for MatchSettings {
fn default() -> Self {
Self {
starting_points: 25000,
player_count: 4,
}
}
}
2026-01-13 12:38:41 -08:00
2026-01-16 22:37:05 -08:00
impl Default for Compass {
fn default() -> Self {
Self {
prevalent_wind: Wind::Ton,
round: 1,
dealer_wind: Wind::Ton,
riichi: 0,
honba: 0,
}
}
2026-01-15 14:00:55 -08:00
}
2026-01-13 12:38:41 -08:00
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()
}
}
}
2026-01-15 14:00:55 -08:00
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
}
}
}
2026-01-13 12:38:41 -08:00
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()
}
}
}
2026-01-13 13:07:21 -08:00
pub(crate) fn tsumo(
mut commands: Commands,
curr_player: Res<CurrentPlayer>,
// players: Populated<Entity, With<Player>>,
wall_ent: Single<Entity, With<Wall>>,
walltiles: Single<&Children, With<Wall>>,
curr_turnstate: Res<State<TurnState>>,
mut next_turnstate: ResMut<NextState<TurnState>>,
) {
2026-01-15 14:00:55 -08:00
debug!("tsumo for: {:?}", curr_player.0);
2026-01-13 13:07:21 -08:00
let drawn = walltiles.last().unwrap();
commands.entity(*wall_ent).remove_child(*drawn);
2026-01-17 02:22:47 -08:00
let drawn_ent = commands.spawn(DrawnTile(*drawn)).id();
commands.entity(curr_player.0).add_child(drawn_ent);
2026-01-13 13:07:21 -08:00
2026-01-17 02:22:47 -08:00
debug!("drew: ent: {drawn_ent:?} tile_ent: {:?}", drawn);
2026-01-13 13:07:21 -08:00
next_turnstate.set(curr_turnstate.next());
}
2026-01-16 22:37:05 -08:00
pub(crate) fn menzen(
curr_turnstate: Res<State<TurnState>>,
mut next_turnstate: ResMut<NextState<TurnState>>,
) {
2026-01-17 02:22:47 -08:00
trace!("menzen check");
2026-01-16 22:37:05 -08:00
next_turnstate.set(curr_turnstate.next());
}
pub(crate) fn riichi_kan(
curr_turnstate: Res<State<TurnState>>,
mut next_turnstate: ResMut<NextState<TurnState>>,
) {
2026-01-17 02:22:47 -08:00
trace!("riichi_kan");
2026-01-16 22:37:05 -08:00
next_turnstate.set(curr_turnstate.next());
}
2026-01-17 02:22:47 -08:00
#[allow(clippy::too_many_arguments, irrefutable_let_patterns)]
2026-01-16 22:37:05 -08:00
pub(crate) fn discard(
2026-01-17 02:22:47 -08:00
mut commands: Commands,
2026-01-16 22:37:05 -08:00
mut reader: MessageReader<GameMessage>,
currplayer: Res<CurrentPlayer>,
2026-01-17 02:22:47 -08:00
drawntile: Single<(&DrawnTile, Entity), With<DrawnTile>>,
2026-01-16 22:37:05 -08:00
player_hands: Populated<(&Player, &Children), With<Hand>>,
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
curr_turnstate: Res<State<TurnState>>,
mut next_turnstate: ResMut<NextState<TurnState>>,
) {
let curr = currplayer.0;
2026-01-17 02:22:47 -08:00
let hand = player_hands.get(curr).unwrap().1.iter().next().unwrap();
let handtiles = hands.get(hand).unwrap();
let (drawntile, drawn_ent) = *drawntile;
// debug!("discard turn for: {curr:?}");
2026-01-16 22:37:05 -08:00
while let Some(message) = reader.read().next() {
if let GameMessage::Discarded(entity) = message {
2026-01-17 02:22:47 -08:00
debug!("{curr:?} discarded: {entity:?}");
// commands.entity(drawn_ent).despawn();
if *entity == drawntile.0 {
} else if handtiles.contains(entity) {
commands
.entity(hand)
.remove_child(*entity)
.add_child(drawntile.0);
2026-01-16 22:37:05 -08:00
} else {
panic!("discarded illegal player tile?")
}
2026-01-17 02:22:47 -08:00
commands.spawn(DiscardedTile(*entity));
next_turnstate.set(curr_turnstate.next());
2026-01-16 22:37:05 -08:00
break;
}
}
}
2026-01-17 02:22:47 -08:00
pub(crate) fn ron_chi_pon_kan(
curr_turnstate: Res<State<TurnState>>,
mut next_turnstate: ResMut<NextState<TurnState>>,
) {
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());
}