refactor entites to reduce complexity and crashes, switch to Querys
This commit is contained in:
parent
3182916832
commit
c7200b1fd3
8 changed files with 95 additions and 89 deletions
|
|
@ -25,9 +25,6 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
tui-logger = { version = "0.18.0", features = ["tracing-support", "crossterm"] }
|
tui-logger = { version = "0.18.0", features = ["tracing-support", "crossterm"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
bevy = { version = "0.17.3", features = ["track_location"] }
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|
||||||
|
|
|
||||||
11
src/game.rs
11
src/game.rs
|
|
@ -1,10 +1,10 @@
|
||||||
use bevy::{ecs::query::QueryData, prelude::*};
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game::{
|
game::{
|
||||||
hand::Hand,
|
hand::Hand,
|
||||||
player::MainPlayer,
|
player::{CurrentPlayer, MainPlayer},
|
||||||
round::{CurrentPlayer, TurnState, Wind},
|
round::{TurnState, Wind},
|
||||||
wall::Wall,
|
wall::Wall,
|
||||||
},
|
},
|
||||||
tile::{self},
|
tile::{self},
|
||||||
|
|
@ -60,7 +60,6 @@ pub(crate) fn setup(
|
||||||
matchsettings: Res<round::MatchSettings>,
|
matchsettings: Res<round::MatchSettings>,
|
||||||
// mut compass: ResMut<Compass>
|
// mut compass: ResMut<Compass>
|
||||||
// tiles: Query<Entity, With<Tile>>,
|
// tiles: Query<Entity, With<Tile>>,
|
||||||
curr_gamestate: Res<State<GameState>>,
|
|
||||||
mut next_gamestate: ResMut<NextState<GameState>>,
|
mut next_gamestate: ResMut<NextState<GameState>>,
|
||||||
) {
|
) {
|
||||||
for i in 1..=matchsettings.player_count {
|
for i in 1..=matchsettings.player_count {
|
||||||
|
|
@ -77,8 +76,8 @@ pub(crate) fn setup(
|
||||||
);
|
);
|
||||||
|
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
let player = commands.spawn((bundle, MainPlayer)).id();
|
let player = commands.spawn((bundle, MainPlayer, CurrentPlayer)).id();
|
||||||
commands.insert_resource(CurrentPlayer(player));
|
// commands.insert_resource(CurrentPlayer(player));
|
||||||
} else {
|
} else {
|
||||||
commands.spawn(bundle);
|
commands.spawn(bundle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ use crate::{
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Hand;
|
pub struct Hand;
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component)]
|
||||||
pub struct DrawnTile(pub Entity);
|
pub struct Drawn;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct DiscardedTile(pub Entity);
|
pub struct Discarded;
|
||||||
|
|
||||||
// #[derive(Component, Default)]
|
// #[derive(Component, Default)]
|
||||||
// enum SortHand {
|
// enum SortHand {
|
||||||
|
|
@ -48,19 +48,16 @@ pub(crate) fn shuffle_deal(
|
||||||
let mut walltiles: Vec<_> = tiles.iter().collect();
|
let mut walltiles: Vec<_> = tiles.iter().collect();
|
||||||
walltiles.shuffle(&mut rng);
|
walltiles.shuffle(&mut rng);
|
||||||
|
|
||||||
// commands.get_entity(*wall_ent)?.replace_children(&walltiles);
|
|
||||||
|
|
||||||
for player_ent in players {
|
for player_ent in players {
|
||||||
debug!("deal to player_ent {player_ent:?}");
|
|
||||||
let handtiles = walltiles.split_off(walltiles.len() - 13);
|
let handtiles = walltiles.split_off(walltiles.len() - 13);
|
||||||
// commands.get_entity(*wall_ent)?.remove_children(&handtiles);
|
|
||||||
|
|
||||||
let hand_ent = commands.spawn(Hand).add_children(&handtiles).id();
|
let hand_ent = commands.spawn(Hand).add_children(&handtiles).id();
|
||||||
debug!("hand_ent: {hand_ent:?}");
|
commands.entity(player_ent).add_child(hand_ent);
|
||||||
|
debug!("deal to player_ent {player_ent:?} {hand_ent:?}");
|
||||||
commands.entity(player_ent).replace_children(&[hand_ent]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't need to remove hands from wall if we don't insert to wall to begin with
|
||||||
|
// TODO probably do this later on when animating the draw
|
||||||
|
debug!("shuffled: {walltiles:?}");
|
||||||
commands.entity(*wall_ent).replace_children(&walltiles);
|
commands.entity(*wall_ent).replace_children(&walltiles);
|
||||||
|
|
||||||
next_gamestate.set(GameState::Play);
|
next_gamestate.set(GameState::Play);
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,11 @@ pub struct Points(pub isize);
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct MainPlayer;
|
pub struct MainPlayer;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct CurrentPlayer;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Dealer;
|
pub struct Dealer;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Tsumo(pub Entity);
|
pub struct Tsumo;
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ use crate::{
|
||||||
EnumNextCycle,
|
EnumNextCycle,
|
||||||
game::{
|
game::{
|
||||||
GameMessage, GameState,
|
GameMessage, GameState,
|
||||||
hand::{DiscardedTile, DrawnTile, Hand},
|
hand::{Discarded, Drawn, Hand},
|
||||||
player::Player,
|
player::{CurrentPlayer, Player},
|
||||||
wall::Wall,
|
wall::Wall,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Resource)]
|
// #[derive(Resource)]
|
||||||
pub struct CurrentPlayer(pub Entity);
|
// pub struct CurrentPlayer(pub Entity);
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub(crate) struct MatchSettings {
|
pub(crate) struct MatchSettings {
|
||||||
|
|
@ -58,9 +58,6 @@ pub(crate) enum TurnState {
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EntityEvent)]
|
|
||||||
pub struct Discard(pub Entity);
|
|
||||||
|
|
||||||
impl Default for MatchSettings {
|
impl Default for MatchSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -116,22 +113,22 @@ impl EnumNextCycle for TurnState {
|
||||||
|
|
||||||
pub(crate) fn tsumo(
|
pub(crate) fn tsumo(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
curr_player: Res<CurrentPlayer>,
|
|
||||||
// players: Populated<Entity, With<Player>>,
|
// curr_player: Res<CurrentPlayer>,
|
||||||
wall_ent: Single<Entity, With<Wall>>,
|
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||||
|
wall: Single<Entity, With<Wall>>,
|
||||||
walltiles: Single<&Children, With<Wall>>,
|
walltiles: Single<&Children, With<Wall>>,
|
||||||
|
|
||||||
curr_turnstate: Res<State<TurnState>>,
|
curr_turnstate: Res<State<TurnState>>,
|
||||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||||
) {
|
) {
|
||||||
debug!("tsumo for: {:?}", curr_player.0);
|
|
||||||
|
|
||||||
let drawn = walltiles.last().unwrap();
|
let drawn = walltiles.last().unwrap();
|
||||||
commands.entity(*wall_ent).remove_child(*drawn);
|
commands.entity(*wall).remove_child(*drawn);
|
||||||
let drawn_ent = commands.spawn(DrawnTile(*drawn)).id();
|
|
||||||
commands.entity(curr_player.0).add_child(drawn_ent);
|
|
||||||
|
|
||||||
debug!("drew: ent: {drawn_ent:?} tile_ent: {:?}", 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());
|
next_turnstate.set(curr_turnstate.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,39 +153,43 @@ pub(crate) fn discard(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut reader: MessageReader<GameMessage>,
|
mut reader: MessageReader<GameMessage>,
|
||||||
|
|
||||||
currplayer: Res<CurrentPlayer>,
|
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||||
drawntile: Single<(&DrawnTile, Entity), With<DrawnTile>>,
|
players: Query<&Children, With<Player>>,
|
||||||
player_hands: Populated<(&Player, &Children), With<Hand>>,
|
mut hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||||
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
|
drawn: Single<Entity, With<Drawn>>,
|
||||||
|
|
||||||
curr_turnstate: Res<State<TurnState>>,
|
curr_turnstate: Res<State<TurnState>>,
|
||||||
mut next_turnstate: ResMut<NextState<TurnState>>,
|
mut next_turnstate: ResMut<NextState<TurnState>>,
|
||||||
) {
|
) -> Result {
|
||||||
let curr = currplayer.0;
|
// trace!("discard");
|
||||||
let hand = player_hands.get(curr).unwrap().1.iter().next().unwrap();
|
let curr_hand = hands.get_mut(players.get(*curr_player)?.iter().next().unwrap())?;
|
||||||
let handtiles = hands.get(hand).unwrap();
|
|
||||||
let (drawntile, drawn_ent) = *drawntile;
|
|
||||||
|
|
||||||
// debug!("discard turn for: {curr:?}");
|
|
||||||
|
|
||||||
|
let mut done = false;
|
||||||
while let Some(message) = reader.read().next() {
|
while let Some(message) = reader.read().next() {
|
||||||
if let GameMessage::Discarded(entity) = message {
|
if let GameMessage::Discarded(discarded) = message {
|
||||||
debug!("{curr:?} discarded: {entity:?}");
|
if *discarded == *drawn {
|
||||||
// commands.entity(drawn_ent).despawn();
|
} else if curr_hand.0.contains(discarded) {
|
||||||
if *entity == drawntile.0 {
|
|
||||||
} else if handtiles.contains(entity) {
|
|
||||||
commands
|
commands
|
||||||
.entity(hand)
|
.entity(curr_hand.1)
|
||||||
.remove_child(*entity)
|
.remove_child(*discarded)
|
||||||
.add_child(drawntile.0);
|
.add_child(*drawn);
|
||||||
} else {
|
} else {
|
||||||
panic!("discarded illegal player tile?")
|
panic!("current hand nor drawn tile contains discarded tile")
|
||||||
}
|
}
|
||||||
commands.spawn(DiscardedTile(*entity));
|
commands
|
||||||
next_turnstate.set(curr_turnstate.next());
|
.entity(*discarded)
|
||||||
|
.remove::<Drawn>()
|
||||||
|
.insert(Discarded);
|
||||||
|
|
||||||
|
done = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if done {
|
||||||
|
next_turnstate.set(curr_turnstate.next());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ron_chi_pon_kan(
|
pub(crate) fn ron_chi_pon_kan(
|
||||||
|
|
|
||||||
26
src/tui.rs
26
src/tui.rs
|
|
@ -1,13 +1,13 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin};
|
use bevy::{app::ScheduleRunnerPlugin, prelude::*, state::app::StatesPlugin, time::TimePlugin};
|
||||||
use bevy_ratatui::RatatuiPlugins;
|
use bevy_ratatui::RatatuiPlugins;
|
||||||
use jong::game::{
|
use jong::game::{
|
||||||
GameMessage, GameState,
|
GameMessage, GameState,
|
||||||
hand::{DrawnTile, Hand},
|
hand::{Drawn, Hand},
|
||||||
player::{MainPlayer, Player},
|
player::{CurrentPlayer, Player},
|
||||||
round::{CurrentPlayer, Discard},
|
|
||||||
};
|
};
|
||||||
|
use tracing::instrument;
|
||||||
use tui_logger::TuiWidgetState;
|
use tui_logger::TuiWidgetState;
|
||||||
|
|
||||||
use crate::tui::{input::ConfirmSelect, states::ConsoleWidget};
|
use crate::tui::{input::ConfirmSelect, states::ConsoleWidget};
|
||||||
|
|
@ -72,17 +72,25 @@ impl Plugin for TuiPlugin {
|
||||||
fn discard_tile(
|
fn discard_tile(
|
||||||
mut writer: MessageWriter<GameMessage>,
|
mut writer: MessageWriter<GameMessage>,
|
||||||
mut selected: MessageReader<ConfirmSelect>,
|
mut selected: MessageReader<ConfirmSelect>,
|
||||||
drawntile: Single<Entity, With<DrawnTile>>,
|
|
||||||
currplayer: Res<CurrentPlayer>,
|
drawn: Single<Entity, With<Drawn>>,
|
||||||
|
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||||
player_hands: Populated<(&Player, &Children), With<Hand>>,
|
player_hands: Populated<(&Player, &Children), With<Hand>>,
|
||||||
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
|
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
|
||||||
) {
|
) {
|
||||||
let curr = currplayer.0;
|
// trace!("discard_tile");
|
||||||
let hand_ent = player_hands.get(curr).unwrap().1.iter().next().unwrap();
|
|
||||||
|
let hand_ent = player_hands
|
||||||
|
.get(*curr_player)
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
let hand = hands.get(hand_ent).unwrap();
|
let hand = hands.get(hand_ent).unwrap();
|
||||||
|
|
||||||
while let Some(message) = selected.read().next()
|
while let Some(message) = selected.read().next()
|
||||||
&& (message.0 == *drawntile || hand.contains(&message.0))
|
&& (message.0 == *drawn || hand.contains(&message.0))
|
||||||
{
|
{
|
||||||
writer.write(GameMessage::Discarded(message.0));
|
writer.write(GameMessage::Discarded(message.0));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ pub(crate) struct Hovered;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub(crate) struct StartSelect;
|
pub(crate) struct StartSelect;
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(Message, Debug)]
|
||||||
pub(crate) struct ConfirmSelect(pub(crate) Entity);
|
pub(crate) struct ConfirmSelect(pub(crate) Entity);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
use std::io::Write as _;
|
use std::io::Write as _;
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
use bevy::platform::collections::HashSet;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_ratatui::RatatuiContext;
|
use bevy_ratatui::RatatuiContext;
|
||||||
use ratatui::layout::{Constraint, Flex, Layout, Offset, Rect, Size};
|
use ratatui::layout::{Constraint, Flex, Layout, Offset, Rect, Size};
|
||||||
use ratatui::style::{Modifier, Style, Stylize};
|
use ratatui::style::{Modifier, Stylize};
|
||||||
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
|
||||||
|
|
||||||
use jong::game::hand::{DrawnTile, Hand};
|
use jong::game::hand::{Drawn, Hand};
|
||||||
use jong::game::player::{MainPlayer, Player};
|
use jong::game::player::{CurrentPlayer, MainPlayer, Player};
|
||||||
use jong::game::round::Wind;
|
use jong::game::round::Wind;
|
||||||
use jong::tile::Tile;
|
use jong::tile::Tile;
|
||||||
|
|
||||||
|
|
@ -22,7 +20,7 @@ pub(crate) struct PickRegion {
|
||||||
pub(crate) area: Rect,
|
pub(crate) area: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tile(tile: &Tile, hovered: bool) -> (Paragraph<'_>) {
|
fn render_tile(tile: &Tile, hovered: bool) -> Paragraph<'_> {
|
||||||
let block = ratatui::widgets::Block::bordered();
|
let block = ratatui::widgets::Block::bordered();
|
||||||
let mut widget = Paragraph::new(match &tile.suit {
|
let mut widget = Paragraph::new(match &tile.suit {
|
||||||
jong::tile::Suit::Pin(rank) => format!("{}\np", rank.0),
|
jong::tile::Suit::Pin(rank) => format!("{}\np", rank.0),
|
||||||
|
|
@ -99,32 +97,35 @@ pub(crate) fn render(
|
||||||
pub(crate) fn render_hands(
|
pub(crate) fn render_hands(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut tui: ResMut<RatatuiContext>,
|
mut tui: ResMut<RatatuiContext>,
|
||||||
|
|
||||||
hovered: Query<Entity, With<Hovered>>,
|
hovered: Query<Entity, With<Hovered>>,
|
||||||
layouts: Res<HandLayouts>,
|
layouts: Res<HandLayouts>,
|
||||||
mainplayer: Single<(&Player, &Wind), With<MainPlayer>>,
|
|
||||||
players: Populated<(&Player, /* &Wind, */ &Children)>,
|
tiles: Query<&Tile>,
|
||||||
hands: Populated<&Children, (With<Hand>, Without<Player>)>,
|
main_player: Single<(&Player, &Wind), With<MainPlayer>>,
|
||||||
tiles: Populated<&Tile>,
|
curr_player: Single<Entity, With<CurrentPlayer>>,
|
||||||
drawn_tile: Option<Single<(&Player, &DrawnTile, Entity)>>,
|
players: Query<(&Player, &Children)>,
|
||||||
|
hands: Query<(&Children, Entity), (With<Hand>, Without<Player>)>,
|
||||||
|
drawn_tile: Option<Single<(&Player, 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 (player, /* wind, */ hand_ent) in players {
|
for (hand, hand_ent) in hands {
|
||||||
let hand = hand_ent.iter().next().unwrap();
|
// debug!("{hand:?}");
|
||||||
let hand: Vec<_> = hands
|
let (player, _) = players.iter().find(|(_, c)| c.contains(&hand_ent)).unwrap();
|
||||||
.get(hand)?
|
let hand: Vec<_> = hand
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entity| -> Result<_> {
|
.map(|entity| -> Result<_> {
|
||||||
let tile = tiles.get(entity)?;
|
let tile = tiles.get(entity).unwrap_or_else(|_| panic!("{entity:?}"));
|
||||||
// let paragraph = render_tile(tile,);
|
|
||||||
let hovered = hovered.contains(entity);
|
let hovered = hovered.contains(entity);
|
||||||
let widget = render_tile(tile, hovered);
|
let widget = render_tile(tile, hovered);
|
||||||
|
|
||||||
Ok((entity, widget, hovered))
|
Ok((entity, widget, hovered))
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
if player == mainplayer.0 {
|
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 this_drawer = drawn_tile.as_ref().is_some_and(|dt| dt.0 == player);
|
let this_drawer = drawn_tile.as_ref().is_some_and(|dt| dt.0 == player);
|
||||||
|
|
@ -169,14 +170,14 @@ pub(crate) fn render_hands(
|
||||||
|
|
||||||
// tsumo tile
|
// tsumo tile
|
||||||
if this_drawer {
|
if this_drawer {
|
||||||
let (_, tile, entity) = **drawn_tile.as_ref().unwrap();
|
let (_, entity) = **drawn_tile.as_ref().unwrap();
|
||||||
let mut area = drawn_area.resize(Size {
|
let mut area = drawn_area.resize(Size {
|
||||||
width: 5,
|
width: 5,
|
||||||
height: 4,
|
height: 4,
|
||||||
});
|
});
|
||||||
area = area.offset(Offset { x: 2, y: 0 });
|
area = area.offset(Offset { x: 2, y: 0 });
|
||||||
let hovered = hovered.contains(entity);
|
let hovered = hovered.contains(entity);
|
||||||
let widget = render_tile(tiles.get(tile.0)?, hovered);
|
let widget = render_tile(tiles.get(entity)?, hovered);
|
||||||
if hovered {
|
if hovered {
|
||||||
area = area.offset(Offset { x: 0, y: -1 });
|
area = area.offset(Offset { x: 0, y: -1 });
|
||||||
let mut hitbox = area.as_size();
|
let mut hitbox = area.as_size();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue