GYVESXMMN2WA6ZE4NHRFIBVD5E62CWC4BGQHP24GYJQ6WJB2G2YAC fn main() {mount_to_body(move || view! { <App/> })
fn main() -> anyhow::Result<()> {std::panic::set_hook(Box::new(console_error_panic_hook::hook));let fmt_layer = tracing_subscriber::fmt::layer().with_ansi(false).with_timer(OffsetTime::local_rfc_3339()?).with_writer(MakeWebConsoleWriter::new());let perf_layer = performance_layer().with_details_from_fields(Pretty::default());tracing_subscriber::registry().with(fmt_layer).with(perf_layer).init();mount_to_body(move || view! { <App/> });Ok(())
let player_1_balance = ColorBalance::default();let player_1_deck = vec![];let player_1_guardians = vec![];let player_2_balance = ColorBalance {suffering: 3,divine: 3,..Default::default()};let player_2_deck = vec![];let player_2_guardians = vec![];let player_decks: Vec<PlayerInitDescriptor> = vec![(player_1_balance, player_1_deck, player_1_guardians).into(),(player_2_balance, player_2_deck, player_2_guardians).into(),];let player_decks_len = player_decks.len();
pub mana_pool: HashMap<color::Color, usize>,
pub mana_pool: HashMap<Color, usize>,pub balance: ColorBalance,pub field: Field,}/// Wrapper that definitely refers to a hecs entity that *is* a card instance#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub struct CardInstanced(hecs::Entity);impl CardInstanced {}/// Represents all the zones and places belonging to a player#[derive(Debug, Default, Clone)]pub struct Field {magister_place: Option<CardInstanced>,aide_places: [Option<CardInstanced>; 2],fragment_places: [Option<CardInstanced>; 5],// zones have no suffixdeck: VecDeque<CardInstanced>,guardian_deck: VecDeque<CardInstanced>,hand: VecDeque<CardInstanced>,graveyard: VecDeque<CardInstanced>,exile: VecDeque<CardInstanced>,
players: Vec<Player>,stack: Stack,turn: TurnState,}#[derive(thiserror::Error, Debug)]pub enum GameCreationError {}impl Game {fn new<I>(players: I) -> Result<Self, GameCreationError>whereI: IntoIterator<Item = PlayerInitDescriptor>,{let mut game = Self::default();for (idx, player_desc) in players.into_iter().enumerate() {_ = idx;_ = player_desc;// Validate that player_dec is validgame.players.push(Player {id: Ulid::new(),mana_pool: HashMap::new(),balance: player_desc.balance,field: Field {deck: vec![].into(),guardian_deck: vec![].into(),..Default::default()},})}Ok(game)}}#[derive(Debug, Clone)]struct PlayerInitDescriptor {balance: ColorBalance,deck: Vec<CardInstance>,guardians: Vec<CardInstance>,}impl From<(ColorBalance, Vec<CardInstance>, Vec<CardInstance>)> for PlayerInitDescriptor {fn from((balance, deck, guardians): (ColorBalance, Vec<CardInstance>, Vec<CardInstance>),) -> Self {Self {deck,guardians,balance,}}
fn GameProvider(children: Children) -> impl IntoView {let (game, set_game) = create_signal(Game::default());
fn GameProvider(#[prop(into)] player_decks: MaybeSignal<Vec<PlayerInitDescriptor>>,children: Children,) -> impl IntoView {let (game, set_game) = create_signal(Game::new(player_decks.get()));
//! Manages turns and phases#[derive(Debug, Default)]pub struct TurnState {current_phase: Phase,/// The game starts when this is 1.current_turn: usize,}impl TurnState {pub fn phase(&self) -> Phase {self.current_phase}}#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]pub enum Phase {#[default]Ready,Standby,Draw,PrecombatMain,Battle(BattleStep),PostcombatMain,End,}impl Phase {/// Does this phase default to a closed GES?pub fn is_closed(self) -> bool {matches!(self,Phase::Ready | Phase::Battle(BattleStep::ResolveDamage))}}#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum BattleStep {BeginBattle,DeclareAttackers,MakeAttacks,ResolveDamage,EndBattle,}
//! module for displaying current game stateuse std::collections::HashMap;use heck::ToTitleCase;use leptos::{html::Canvas, *};use plotters::{chart::{ChartBuilder, LabelAreaPosition},coord::ranged1d::{IntoSegmentedCoord, SegmentValue},drawing::IntoDrawingArea,series::Histogram,style::{Color as _, RED, WHITE},};use plotters_canvas::CanvasBackend;use web_sys::HtmlCanvasElement;use crate::app::{color::Color, GameReader};#[component]pub fn Player(#[prop(into)] index: MaybeSignal<usize>) -> impl IntoView {// Display the manapool and balanceconst COLORS: &[Color] = &Color::all();let game = expect_context::<GameReader>();let player_data = move || {game.0.with(|gs| {let gs = gs.as_ref().ok()?;Some(gs.players[index.get()].clone())})};view! {{move || {player_data().map(|pd| {let id = pd.id;let balance = pd.balance;let pool = pd.mana_pool;let (gen_count, set_gen_count) = create_signal(1usize);let (has_error, set_has_error) = create_signal(false);let (gen_mana, set_gen_mana) = create_signal(None);let (all_gen_mana, set_all_gen_mana) = create_signal(HashMap::new());let plot_ref = create_node_ref::<Canvas>();let plotted = move |plot_ref: HtmlElement<Canvas>, data: &[(Color, usize)]| {let backend = CanvasBackend::with_canvas_object(Clone::clone(HtmlCanvasElement::as_ref(&plot_ref)),).expect("plotters canvas failed to initialize").into_drawing_area();backend.fill(&WHITE.mix(1.0)).expect("failed to clear canvas");let mut chart = ChartBuilder::on(&backend).set_label_area_size(LabelAreaPosition::Left, 40).set_label_area_size(LabelAreaPosition::Bottom, 40).build_cartesian_2d((0usize..6).into_segmented(),0..all_gen_mana.get().values().copied().max().unwrap_or(1),).expect("Failed to create chart");const COLORS: &[Color] = &Color::all();chart.configure_mesh().disable_x_mesh().x_desc("Color").y_desc("Count").y_labels(5).x_labels(7).x_label_formatter(&|idx| {match idx {SegmentValue::Exact(idx) => format!("{}?", COLORS[*idx]),SegmentValue::CenterOf(idx) => {COLORS.get(*idx).map(ToString::to_string).unwrap_or(String::new())}SegmentValue::Last => String::new(),}},).draw().expect("Failed to draw axises");chart.draw_series(Histogram::vertical(&chart).style(RED.mix(0.5).filled()).data(data.into_iter().map(|(c, i)| (COLORS.iter().position(|oc| oc == c).unwrap(),*i,)),),).expect("Failed to draw data");backend.present().expect("failed to present chart");};create_effect(move |_| {let data = all_gen_mana.get();if let Some(plot_ref) = plot_ref.get() {plotted(plot_ref, &data.into_iter().collect::<Vec<_>>())}});view! {<div><h2 class="italic text-xl">"Player " {move || index.get() + 1} " : "<span class="font-mono not-italic bg-base-200 rounded p-1">{move || format!("{id}")}</span></h2><ul>{COLORS.iter().map(|c| {view! {<li>{c.color().to_title_case()} " Mana: "{pool.get(c).copied().unwrap_or(0)} " / (" {balance.get(*c)}")"</li>}}).collect_view()}</ul><div class="join"><inputclass="input input-bordered join-item"type="number"prop:value=move || gen_count.get()class:has-error=has_erroron:input=move |ev| {set_gen_count.set(match event_target_value(&ev).parse() {Ok(val) => {set_has_error.set(false);val}Err(_e) => {set_has_error.set(true);return;}},);}/><buttonclass="btn join-item"on:click=move |_| {let generated_mana: Vec<_> = balance.gen_mana(&mut rand::thread_rng()).take(gen_count.get()).collect();let counts = Color::all().into_iter().map(|c| (c,generated_mana.iter().filter(|col| col == &&c).count(),)).collect::<HashMap<_, _>>();set_all_gen_mana.update(|all_gen_mana| {all_gen_mana.extend(counts);});set_gen_mana.set(Some(generated_mana));}>"Generate Mana"</button></div><div class="font-mono">{move || {if let Some(gen_mana) = gen_mana.get() {let mapped = Color::all().into_iter().map(|c| (c,gen_mana.iter().filter(|col| col == &&c).count(),)).collect::<HashMap<_, _>>();format!("{mapped:?}")} else {"".to_string()}}}</div><Show when=move || {all_gen_mana.with(|agm| agm.values().copied().sum::<usize>() > 0)}><canvas width="600" height="200" _ref=plot_ref></canvas></Show></div>}})}}}}
pub fn get(&self, color: Color) -> usize {match color {Color::Divine => self.divine,Color::Revelation => self.revelation,Color::Grace => self.grace,Color::Growth => self.growth,Color::Crusade => self.crusade,Color::Suffering => self.suffering,Color::Mundane => self.mundane,}}
self.mundane.max(1),self.divine.max(1),self.revelation.max(1),self.grace.max(1),self.growth.max(1),self.crusade.max(1),self.suffering.max(1),
self.mundane.saturating_add(1),self.divine.saturating_add(1),self.revelation.saturating_add(1),self.grace.saturating_add(1),self.growth.saturating_add(1),self.crusade.saturating_add(1),self.suffering.saturating_add(1),
#[derive(Debug, Clone)]pub enum Card {}#[derive(Debug, Clone, Copy)]pub struct CardInstance {}
//! Handling of abilities and effectsuse std::collections::VecDeque;/// Contains stack items#[derive(Debug, Default)]pub struct Stack {/// Are we in the middle of resolving an effect?is_resolving: bool,stack: VecDeque<StackItem>,}#[derive(Debug, Clone)]pub enum StackItem {}#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub enum GameEffectState {Closed,HalfOpen,Open,}impl Stack {// The only way to override this is to look are the Phase::is_closed result// If that is true, GES is closed no matter whatpub fn game_effect_state(&self) -> GameEffectState {if self.stack.is_empty() {GameEffectState::Open} else if self.is_resolving {GameEffectState::Closed} else {// The stack is *not* empty, but we are *not* in the middle of effect resolutionGameEffectState::HalfOpen}}}