Impl main menu

Only "LocalMultiplayer" can be chosen for the game-mode since neither
AI nor Networked-Multiplayer has been implemented yet.
This commit is contained in:
Stephen Seo 2022-03-04 16:22:30 +09:00
parent 20d9562aa8
commit 130cc42d86
3 changed files with 184 additions and 9 deletions

View file

@ -4,6 +4,44 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Four-Line Dropper</title> <title>Four-Line Dropper</title>
<style> <style>
b {
color: #FFF;
}
div.menu {
background-color: #555;
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
grid-auto-rows: 33%;
}
button.menuSinglePlayer {
grid-row: 2;
grid-column: 2;
}
button.menuLocalMultiplayer {
grid-row: 2;
grid-column: 3;
}
button.menuMultiplayer {
grid-row: 2;
grid-column: 4;
}
b.menuText {
color: #FFF;
grid-row: 1;
grid-column: 2 / 5;
line-height: 64px;
text-align: center;
}
div.hidden_menu {
display: none;
}
div.wrapper { div.wrapper {
display: grid; display: grid;
grid-template-columns: repeat(7, 50px) 400px; grid-template-columns: repeat(7, 50px) 400px;

View file

@ -2,6 +2,21 @@ use std::cell::Cell;
use std::fmt::Display; use std::fmt::Display;
use std::rc::Rc; use std::rc::Rc;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum GameState {
MainMenu,
SinglePlayer,
LocalMultiplayer,
NetworkedMultiplayer,
PostGameResults(Turn),
}
impl Default for GameState {
fn default() -> Self {
GameState::MainMenu
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BoardState { pub enum BoardState {
Empty, Empty,
@ -83,6 +98,7 @@ impl Turn {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct SharedState { pub struct SharedState {
pub board: [Rc<Cell<BoardState>>; 56], pub board: [Rc<Cell<BoardState>>; 56],
pub game_state: Rc<Cell<GameState>>,
pub turn: Rc<Cell<Turn>>, pub turn: Rc<Cell<Turn>>,
} }
@ -148,6 +164,7 @@ impl Default for SharedState {
Rc::new(Cell::new(BoardState::default())), Rc::new(Cell::new(BoardState::default())),
Rc::new(Cell::new(BoardState::default())), Rc::new(Cell::new(BoardState::default())),
], ],
game_state: Rc::new(Cell::new(GameState::default())),
turn: Rc::new(Cell::new(Turn::CyanPlayer)), turn: Rc::new(Cell::new(Turn::CyanPlayer)),
} }
} }

View file

@ -1,9 +1,93 @@
use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS}; use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS};
use crate::state::{BoardState, SharedState}; use crate::state::{BoardState, GameState, SharedState, Turn};
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use yew::prelude::*; use yew::prelude::*;
pub struct MainMenu {}
pub enum MainMenuMessage {
SinglePlayer,
LocalMultiplayer,
NetworkedMultiplayer,
}
#[allow(clippy::from_over_into)]
impl Into<GameState> for MainMenuMessage {
fn into(self) -> GameState {
match self {
MainMenuMessage::SinglePlayer => GameState::SinglePlayer,
MainMenuMessage::LocalMultiplayer => GameState::LocalMultiplayer,
MainMenuMessage::NetworkedMultiplayer => GameState::NetworkedMultiplayer,
}
}
}
impl Component for MainMenu {
type Message = MainMenuMessage;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let (shared, _) = ctx
.link()
.context::<SharedState>(Callback::noop())
.expect("state to be set");
match shared.game_state.get() {
GameState::MainMenu => {
let onclick_local_multiplayer =
ctx.link().callback(|_| MainMenuMessage::LocalMultiplayer);
html! {
<div class={"menu"} id={"mainmenu"}>
<b class={"menuText"}>{"Please pick a game mode."}</b>
<button class={"menuSinglePlayer"}>
{"Singleplayer"}
</button>
<button class={"menuLocalMultiplayer"} onclick={onclick_local_multiplayer}>
{"Local Multiplayer"}
</button>
<button class={"menuMultiplayer"}>
{"Networked Multiplayer"}
</button>
</div>
}
}
_ => html! {
<div class={"hidden_menu"} id={"mainmenu"}>
</div>
},
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
let (shared, _) = ctx
.link()
.context::<SharedState>(Callback::noop())
.expect("state to be set");
let window = web_sys::window().expect("no window exists");
let document = window.document().expect("window should have a document");
shared.game_state.replace(msg.into());
if shared.game_state.get() != GameState::MainMenu {
let mainmenu = document
.get_element_by_id("mainmenu")
.expect("mainmenu should exist");
mainmenu.set_class_name("hidden_menu");
mainmenu.set_inner_html("");
let info_text_turn = document
.get_element_by_id("info_text1")
.expect("info_text1 should exist");
info_text_turn.set_inner_html("<p><b class=\"cyan\">It is CyanPlayer's Turn</b></p>");
}
true
}
}
pub struct Slot {} pub struct Slot {}
pub enum SlotMessage { pub enum SlotMessage {
@ -39,6 +123,15 @@ impl Component for Slot {
} }
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool { fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
let (shared, _) = ctx
.link()
.context::<SharedState>(Callback::noop())
.expect("state to be set");
if shared.game_state.get() == GameState::MainMenu {
return false;
}
match msg { match msg {
SlotMessage::Press(idx) => { SlotMessage::Press(idx) => {
// notify Wrapper with message // notify Wrapper with message
@ -74,6 +167,7 @@ impl Component for Wrapper {
.expect("state to be set"); .expect("state to be set");
html! { html! {
<div class="wrapper"> <div class="wrapper">
<MainMenu />
<Slot idx=0 state={shared.board[0].clone()} /> <Slot idx=0 state={shared.board[0].clone()} />
<Slot idx=1 state={shared.board[1].clone()} /> <Slot idx=1 state={shared.board[1].clone()} />
<Slot idx=2 state={shared.board[2].clone()} /> <Slot idx=2 state={shared.board[2].clone()} />
@ -302,6 +396,10 @@ impl Component for InfoText {
} }
fn view(&self, ctx: &Context<Self>) -> Html { fn view(&self, ctx: &Context<Self>) -> Html {
let (shared, _) = ctx
.link()
.context::<SharedState>(Callback::noop())
.expect("state to be set");
match ctx.props().id { match ctx.props().id {
0 => { 0 => {
html! { html! {
@ -311,6 +409,17 @@ impl Component for InfoText {
} }
} }
1 => { 1 => {
if shared.game_state.get() == GameState::MainMenu {
html! {
<div id={format!("info_text{}", ctx.props().id)} class={format!("info_text{}", ctx.props().id)}>
<p>
<b>
{"Waiting to choose game-mode..."}
</b>
</p>
</div>
}
} else if shared.turn.get() == Turn::CyanPlayer {
html! { html! {
<div id={format!("info_text{}", ctx.props().id)} class={format!("info_text{}", ctx.props().id)}> <div id={format!("info_text{}", ctx.props().id)} class={format!("info_text{}", ctx.props().id)}>
<p> <p>
@ -320,6 +429,17 @@ impl Component for InfoText {
</p> </p>
</div> </div>
} }
} else {
html! {
<div id={format!("info_text{}", ctx.props().id)} class={format!("info_text{}", ctx.props().id)}>
<p>
<b class={"magenta"}>
{"It is MagentaPlayer's turn"}
</b>
</p>
</div>
}
}
} }
_ => { _ => {
unreachable!(); unreachable!();