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:
parent
20d9562aa8
commit
130cc42d86
3 changed files with 184 additions and 9 deletions
|
@ -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;
|
||||||
|
|
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!();
|
||||||
|
|
Loading…
Reference in a new issue