Add basic player actions
This commit is contained in:
parent
a6659fd538
commit
a67aa2c0d9
287
src/main.rs
287
src/main.rs
@ -1,13 +1,7 @@
|
|||||||
use async_std::{
|
use async_std::{prelude::*, sync::RwLock};
|
||||||
prelude::*,
|
|
||||||
sync::RwLock
|
|
||||||
};
|
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc}
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use tide::{Body, Redirect, Request, Response};
|
use tide::{Body, Redirect, Request, Response};
|
||||||
use tide_websockets::{Message, WebSocket, WebSocketConnection};
|
use tide_websockets::{Message, WebSocket, WebSocketConnection};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -17,14 +11,16 @@ type Card = String;
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
enum ClientMessage {
|
enum ClientMessage {
|
||||||
Chat {
|
Chat { message: String },
|
||||||
message: String,
|
CreateGame { name: String },
|
||||||
},
|
JoinGame { name: String },
|
||||||
CreateGame {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
StartGame,
|
StartGame,
|
||||||
EndTurn,
|
EndTurn,
|
||||||
|
PlayCard { name: String, index: usize },
|
||||||
|
GainCard { name: String, index: usize },
|
||||||
|
DrawCard,
|
||||||
|
Discard { index: usize },
|
||||||
|
TrashHand { index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@ -41,6 +37,9 @@ enum ServerMessage {
|
|||||||
state: GameState,
|
state: GameState,
|
||||||
players: Vec<PlayerState>,
|
players: Vec<PlayerState>,
|
||||||
active_player: usize,
|
active_player: usize,
|
||||||
|
supply: Vec<PileState>,
|
||||||
|
turn_state: TurnState,
|
||||||
|
trash: Vec<Card>,
|
||||||
},
|
},
|
||||||
GameSetup {
|
GameSetup {
|
||||||
setup: GameSetup,
|
setup: GameSetup,
|
||||||
@ -48,6 +47,12 @@ enum ServerMessage {
|
|||||||
PlayerHand {
|
PlayerHand {
|
||||||
hand: Vec<Card>,
|
hand: Vec<Card>,
|
||||||
},
|
},
|
||||||
|
PlayerId {
|
||||||
|
id: usize,
|
||||||
|
},
|
||||||
|
Notification {
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
#[derive(Clone, Serialize)]
|
||||||
@ -55,7 +60,6 @@ struct GameSetup {
|
|||||||
deck: Vec<Card>,
|
deck: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct PlayerState {
|
struct PlayerState {
|
||||||
id: usize,
|
id: usize,
|
||||||
@ -63,8 +67,21 @@ struct PlayerState {
|
|||||||
draw_pile_count: usize,
|
draw_pile_count: usize,
|
||||||
hand_count: usize,
|
hand_count: usize,
|
||||||
discard_pile: Option<Card>,
|
discard_pile: Option<Card>,
|
||||||
|
played_cards: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct PileState {
|
||||||
|
name: String,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct TurnState {
|
||||||
|
actions: u32,
|
||||||
|
buys: u32,
|
||||||
|
coin: u32,
|
||||||
|
}
|
||||||
|
|
||||||
struct Player {
|
struct Player {
|
||||||
id: String,
|
id: String,
|
||||||
@ -72,6 +89,7 @@ struct Player {
|
|||||||
draw_pile: Vec<Card>,
|
draw_pile: Vec<Card>,
|
||||||
hand: Vec<Card>,
|
hand: Vec<Card>,
|
||||||
discard_pile: Vec<Card>,
|
discard_pile: Vec<Card>,
|
||||||
|
played_cards: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
@ -82,6 +100,7 @@ impl Player {
|
|||||||
draw_pile: vec![],
|
draw_pile: vec![],
|
||||||
hand: vec![],
|
hand: vec![],
|
||||||
discard_pile: vec![],
|
discard_pile: vec![],
|
||||||
|
played_cards: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,9 +116,17 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn play_card(&mut self, index: usize) {
|
||||||
|
self.played_cards.push(self.hand.remove(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Serialize)]
|
pub fn discard(&mut self, index: usize) {
|
||||||
|
self.discard_pile.push(self.hand.remove(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Serialize)]
|
||||||
enum GameState {
|
enum GameState {
|
||||||
Setup,
|
Setup,
|
||||||
InProgress,
|
InProgress,
|
||||||
@ -108,8 +135,18 @@ enum GameState {
|
|||||||
impl Default for GameSetup {
|
impl Default for GameSetup {
|
||||||
fn default() -> GameSetup {
|
fn default() -> GameSetup {
|
||||||
GameSetup {
|
GameSetup {
|
||||||
deck: vec!["Copper".into(), "Copper".into(), "Copper".into(), "Copper".into(), "Copper".into(),
|
deck: vec![
|
||||||
"Copper".into(), "Copper".into(), "Estate".into(), "Estate".into(), "Estate".into()]
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Copper".into(),
|
||||||
|
"Estate".into(),
|
||||||
|
"Estate".into(),
|
||||||
|
"Estate".into(),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,9 +157,10 @@ struct Game {
|
|||||||
setup: GameSetup,
|
setup: GameSetup,
|
||||||
connections: HashMap<Uuid, (String, WebSocketConnection)>,
|
connections: HashMap<Uuid, (String, WebSocketConnection)>,
|
||||||
active_player: usize,
|
active_player: usize,
|
||||||
|
supply: Vec<(String, usize)>,
|
||||||
|
trash: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
fn new(player: Player) -> Self {
|
fn new(player: Player) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -131,6 +169,8 @@ impl Game {
|
|||||||
setup: GameSetup::default(),
|
setup: GameSetup::default(),
|
||||||
connections: HashMap::new(),
|
connections: HashMap::new(),
|
||||||
active_player: 0,
|
active_player: 0,
|
||||||
|
supply: vec![],
|
||||||
|
trash: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +183,28 @@ impl Game {
|
|||||||
(*p).discard_pile = self.setup.deck.clone();
|
(*p).discard_pile = self.setup.deck.clone();
|
||||||
p.draw(5);
|
p.draw(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.supply = vec![
|
||||||
|
("Copper".into(), 46),
|
||||||
|
("Silver".into(), 38),
|
||||||
|
("Gold".into(), 30),
|
||||||
|
("Estate".into(), 8),
|
||||||
|
("Duchery".into(), 8),
|
||||||
|
("Province".into(), 8),
|
||||||
|
("Curse".into(), 10),
|
||||||
|
("Cellar".into(), 10),
|
||||||
|
("Moat".into(), 10),
|
||||||
|
("Village".into(), 10),
|
||||||
|
("Merchant".into(), 10),
|
||||||
|
("Workshop".into(), 10),
|
||||||
|
("Smithy".into(), 10),
|
||||||
|
("Remodel".into(), 10),
|
||||||
|
("Militia".into(), 10),
|
||||||
|
("Market".into(), 10),
|
||||||
|
("Mine".into(), 10),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {} // Ignore if game is not in setup state
|
_ => {} // Ignore if game is not in setup state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +213,7 @@ impl Game {
|
|||||||
match self.players.get_mut(self.active_player) {
|
match self.players.get_mut(self.active_player) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(p) => {
|
Some(p) => {
|
||||||
|
p.discard_pile.append(&mut p.played_cards);
|
||||||
p.discard_pile.append(&mut p.hand);
|
p.discard_pile.append(&mut p.hand);
|
||||||
p.draw(5);
|
p.draw(5);
|
||||||
}
|
}
|
||||||
@ -164,10 +226,13 @@ impl Game {
|
|||||||
fn is_active_player(&self, player_id: &str) -> bool {
|
fn is_active_player(&self, player_id: &str) -> bool {
|
||||||
match self.players.get(self.active_player) {
|
match self.players.get(self.active_player) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(p) => {
|
Some(p) => p.id == *player_id,
|
||||||
p.id == *player_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trash_hand(&mut self, player_number: usize, index: usize) {
|
||||||
|
self.trash
|
||||||
|
.push(self.players[player_number].hand.remove(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +241,11 @@ struct State {
|
|||||||
games: Arc<RwLock<HashMap<Uuid, Game>>>,
|
games: Arc<RwLock<HashMap<Uuid, Game>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn notify_players(game: &Game, text: String) {
|
||||||
|
let sm = ServerMessage::Notification { text };
|
||||||
|
broadcast(game, &sm).await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn broadcast(game: &Game, sm: &ServerMessage) {
|
async fn broadcast(game: &Game, sm: &ServerMessage) {
|
||||||
for (_, (_, con)) in game.connections.iter() {
|
for (_, (_, con)) in game.connections.iter() {
|
||||||
con.send_json(&sm).await.unwrap();
|
con.send_json(&sm).await.unwrap();
|
||||||
@ -185,14 +255,34 @@ async fn broadcast(game: &Game, sm: &ServerMessage) {
|
|||||||
async fn broadcast_state(game: &Game) {
|
async fn broadcast_state(game: &Game) {
|
||||||
let sm = ServerMessage::GameState {
|
let sm = ServerMessage::GameState {
|
||||||
state: game.state,
|
state: game.state,
|
||||||
players: game.players.iter().enumerate().map(|(i, p)| PlayerState {
|
players: game
|
||||||
|
.players
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, p)| PlayerState {
|
||||||
id: i,
|
id: i,
|
||||||
name: html_escape::encode_text(&p.name).into(),
|
name: html_escape::encode_text(&p.name).into(),
|
||||||
draw_pile_count: p.draw_pile.len(),
|
draw_pile_count: p.draw_pile.len(),
|
||||||
hand_count: p.hand.len(),
|
hand_count: p.hand.len(),
|
||||||
discard_pile: p.discard_pile.last().map(|c| c.clone()),
|
discard_pile: p.discard_pile.last().map(|c| c.clone()),
|
||||||
}).collect(),
|
played_cards: p.played_cards.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
active_player: game.active_player,
|
active_player: game.active_player,
|
||||||
|
supply: game
|
||||||
|
.supply
|
||||||
|
.iter()
|
||||||
|
.map(|(name, count)| PileState {
|
||||||
|
name: name.into(),
|
||||||
|
count: count.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
turn_state: TurnState {
|
||||||
|
actions: 1,
|
||||||
|
buys: 1,
|
||||||
|
coin: 0,
|
||||||
|
},
|
||||||
|
trash: game.trash.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
broadcast(&game, &sm).await;
|
broadcast(&game, &sm).await;
|
||||||
@ -214,7 +304,6 @@ async fn send_msg(game: &Game, player: &Player, sm: &ServerMessage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> Result<(), std::io::Error> {
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
tide::log::with_level(tide::log::LevelFilter::Debug);
|
tide::log::with_level(tide::log::LevelFilter::Debug);
|
||||||
@ -226,7 +315,8 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
"1234567890abcdef1234567890abcdef".as_bytes(),
|
"1234567890abcdef1234567890abcdef".as_bytes(),
|
||||||
));
|
));
|
||||||
|
|
||||||
app.at("/").get(|_| async { Ok(Body::from_file("static/index.html").await?) });
|
app.at("/")
|
||||||
|
.get(|_| async { Ok(Body::from_file("static/index.html").await?) });
|
||||||
|
|
||||||
app.at("/static").serve_dir("static/")?;
|
app.at("/static").serve_dir("static/")?;
|
||||||
|
|
||||||
@ -249,9 +339,7 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
let res: tide::Result = Ok(Redirect::new(url).into());
|
let res: tide::Result = Ok(Redirect::new(url).into());
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Ok(Response::new(400)),
|
||||||
Ok(Response::new(400))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -259,20 +347,47 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
let games = req.state().games.read().await;
|
let games = req.state().games.read().await;
|
||||||
let id = req.param("id")?;
|
let id = req.param("id")?;
|
||||||
let game_id = Uuid::parse_str(&id).unwrap();
|
let game_id = Uuid::parse_str(&id).unwrap();
|
||||||
|
let session = req.session();
|
||||||
|
|
||||||
match games.get(&game_id) {
|
match games.get(&game_id) {
|
||||||
None => Ok(Response::new(404)),
|
None => return Ok(Response::new(404)),
|
||||||
Some(_) => {
|
Some(_) => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
match session.get::<String>("player_name") {
|
||||||
|
None => {
|
||||||
|
let url = format!("/game/{}/join", id);
|
||||||
|
return Ok(Redirect::new(url).into());
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
};
|
||||||
|
|
||||||
let mut res = Response::new(200);
|
let mut res = Response::new(200);
|
||||||
res.set_body(Body::from_file("static/game.html").await?);
|
res.set_body(Body::from_file("static/game.html").await?);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
});
|
||||||
|
|
||||||
|
app.at("/game/:id/join")
|
||||||
|
.get(|_| async { Ok(Body::from_file("static/join.html").await?) })
|
||||||
|
.post(|mut req: Request<State>| async move {
|
||||||
|
let msg: ClientMessage = req.body_form().await?;
|
||||||
|
let id: String = { req.param("id").unwrap().into() };
|
||||||
|
let session = req.session_mut();
|
||||||
|
|
||||||
|
match msg {
|
||||||
|
ClientMessage::JoinGame { name } => {
|
||||||
|
session.insert("player_name", name.clone())?;
|
||||||
|
|
||||||
|
let url = format!("/game/{}", id);
|
||||||
|
let res: tide::Result = Ok(Redirect::new(url).into());
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
_ => Ok(Response::new(500)),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.at("/game/:id/ws").get(WebSocket::new(
|
||||||
app.at("/game/:id/ws")
|
|req: Request<State>, mut stream| async move {
|
||||||
.get(WebSocket::new(|req: Request<State>, mut stream| async move {
|
|
||||||
let id = req.param("id")?;
|
let id = req.param("id")?;
|
||||||
let game_id = Uuid::parse_str(&id).unwrap();
|
let game_id = Uuid::parse_str(&id).unwrap();
|
||||||
let mut games = req.state().games.write().await;
|
let mut games = req.state().games.write().await;
|
||||||
@ -282,17 +397,27 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
let player_id: String = session.id().clone().into();
|
let player_id: String = session.id().clone().into();
|
||||||
|
|
||||||
let client_id = Uuid::new_v4();
|
let client_id = Uuid::new_v4();
|
||||||
game.connections.insert(client_id, (player_id.clone(), stream.clone()));
|
game.connections
|
||||||
|
.insert(client_id, (player_id.clone(), stream.clone()));
|
||||||
|
|
||||||
let player_name: String = match session.get("player_name") {
|
let player_name: String = match session.get("player_name") {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return Ok(())
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !game.players.iter().any(|p| p.id == player_id) {
|
if game.state == GameState::Setup && !game.players.iter().any(|p| p.id == player_id) {
|
||||||
game.players.push(Player::new(player_id.clone(), player_name.clone()));
|
game.players
|
||||||
|
.push(Player::new(player_id.clone(), player_name.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let player_number = game
|
||||||
|
.players
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, p)| p.id == player_id)
|
||||||
|
.fold(0, |_, (i, _)| i);
|
||||||
|
let msg = ServerMessage::PlayerId { id: player_number };
|
||||||
|
stream.send_json(&msg).await?;
|
||||||
|
|
||||||
//Ensure the write locks are freed, possibly better to move to a function
|
//Ensure the write locks are freed, possibly better to move to a function
|
||||||
//with implicit drop?
|
//with implicit drop?
|
||||||
@ -355,6 +480,90 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClientMessage::CreateGame { .. } => {}
|
ClientMessage::CreateGame { .. } => {}
|
||||||
|
|
||||||
|
ClientMessage::JoinGame { .. } => {}
|
||||||
|
|
||||||
|
ClientMessage::PlayCard { name: _, index } => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
let card_name = game.players[player_number].hand[index].clone();
|
||||||
|
game.players[player_number].play_card(index);
|
||||||
|
|
||||||
|
notify_players(
|
||||||
|
&game,
|
||||||
|
format!("{} spielt {}", game.players[player_number].name, card_name),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
broadcast_state(&game).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage::GainCard { name: _, index } => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
game.supply.get_mut(index).unwrap().1 =
|
||||||
|
game.supply.get(index).unwrap().1 - 1;
|
||||||
|
let card_name = game.supply[index].0.clone();
|
||||||
|
|
||||||
|
game.players[player_number]
|
||||||
|
.discard_pile
|
||||||
|
.push(card_name.clone());
|
||||||
|
|
||||||
|
notify_players(
|
||||||
|
&game,
|
||||||
|
format!("{} nimmt {}", game.players[player_number].name, card_name),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
broadcast_state(&game).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage::DrawCard => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
game.players[player_number].draw(1);
|
||||||
|
|
||||||
|
notify_players(
|
||||||
|
&game,
|
||||||
|
format!("{} zieht eine Karte", game.players[player_number].name),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
broadcast_state(&game).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage::Discard { index } => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
let card_name = game.players[player_number].hand[index].clone();
|
||||||
|
game.players[player_number].discard(index);
|
||||||
|
|
||||||
|
notify_players(
|
||||||
|
&game,
|
||||||
|
format!("{} legt {} ab", game.players[player_number].name, card_name),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
broadcast_state(&game).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage::TrashHand { index } => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
let card_name = game.players[player_number].hand[index].clone();
|
||||||
|
game.trash_hand(player_number, index);
|
||||||
|
|
||||||
|
notify_players(
|
||||||
|
&game,
|
||||||
|
format!(
|
||||||
|
"{} entsorgt {}",
|
||||||
|
game.players[player_number].name, card_name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
broadcast_state(&game).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,9 +573,9 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
game.connections.remove(&client_id);
|
game.connections.remove(&client_id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
|
|
||||||
app.listen("0.0.0.0:5000").await?;
|
app.listen("0.0.0.0:5000").await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
389
static/game.html
389
static/game.html
@ -6,6 +6,12 @@
|
|||||||
<title>DnD</title>
|
<title>DnD</title>
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
#game {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.pile-counter {
|
.pile-counter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
@ -42,14 +48,16 @@
|
|||||||
width: 90px;
|
width: 90px;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
height: 144px;
|
height: 144px;
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand {
|
.player-hand {
|
||||||
border: 1px solid red;
|
border: 1px solid red;
|
||||||
height: 100%;
|
height: 160px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
top: 0;
|
overflow-x: scroll;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand img.card:hover {
|
.player-hand img.card:hover {
|
||||||
@ -59,16 +67,20 @@
|
|||||||
.chat-window {
|
.chat-window {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
position: absolute;
|
grid-column-start: 2;
|
||||||
top: 0;
|
}
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
#chat li[data-repeat]::after {
|
||||||
|
content: "x" attr(data-repeat);
|
||||||
|
margin-left: 10px;
|
||||||
|
background-color: deepskyblue;
|
||||||
|
color: white;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.supply-area {
|
.supply-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
padding-right: 300px;
|
|
||||||
background-color: dimgray;
|
background-color: dimgray;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
@ -87,10 +99,54 @@
|
|||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.supply-pile {
|
||||||
|
width: 90px;
|
||||||
|
position: relative;
|
||||||
|
height: 145px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supply-pile::after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 5px;
|
||||||
|
color: wheat;
|
||||||
|
background-color: darkred;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 24px;
|
||||||
|
width: 35px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: 24px;
|
||||||
|
content: attr(data-count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.draw-pile {
|
.draw-pile {
|
||||||
width: 90px;
|
width: 90px;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 145px;
|
height: 145px;
|
||||||
|
background-image: url(/static/images/cards/Card_back.jpg);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draw-pile::after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 5px;
|
||||||
|
color: wheat;
|
||||||
|
background-color: darkred;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 24px;
|
||||||
|
width: 35px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
line-height: 24px;
|
||||||
|
content: attr(data-count);
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-hand {
|
.opponent-hand {
|
||||||
@ -99,6 +155,11 @@
|
|||||||
height: 145px;
|
height: 145px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opponent-hand img.card {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.setup-screen {
|
.setup-screen {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +173,7 @@
|
|||||||
height: 144px;
|
height: 144px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discard-pile img.card {
|
.discard-pile img.card {
|
||||||
@ -146,6 +208,7 @@
|
|||||||
height: 0;
|
height: 0;
|
||||||
transition: height 0.4s ease;
|
transition: height 0.4s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.turn-status {
|
.turn-status {
|
||||||
@ -169,6 +232,7 @@
|
|||||||
|
|
||||||
.opponent-area {
|
.opponent-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opponent-status {
|
.opponent-status {
|
||||||
@ -176,7 +240,7 @@
|
|||||||
grid-template-columns: auto auto auto;
|
grid-template-columns: auto auto auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
border: 1px solid saddlebrown;
|
border: 1px solid dimgray;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 2px;
|
grid-gap: 2px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
@ -186,6 +250,10 @@
|
|||||||
box-shadow: 0 0 10px red;
|
box-shadow: 0 0 10px red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opponent-status:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.opponent-status .name {
|
.opponent-status .name {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
grid-column-end: span 3;
|
grid-column-end: span 3;
|
||||||
@ -218,6 +286,29 @@
|
|||||||
.game-screen {
|
.game-screen {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.trash {
|
||||||
|
height: 145px;
|
||||||
|
border: 1px solid saddlebrown;
|
||||||
|
width: 90px;
|
||||||
|
position: absolute;
|
||||||
|
text-align: right;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trash::after {
|
||||||
|
content: "Trash";
|
||||||
|
color: saddlebrown;
|
||||||
|
font-size: 14px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<script src="/static/mithril.js"></script>
|
<script src="/static/mithril.js"></script>
|
||||||
</head>
|
</head>
|
||||||
@ -225,6 +316,27 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="game"></div>
|
<div id="game"></div>
|
||||||
<script>
|
<script>
|
||||||
|
function handle_dnd(data) {
|
||||||
|
if (data.source == "Hand" && data.dest == "InPlay") {
|
||||||
|
var msg = { type: "PlayCard", name: "", index: data.index};
|
||||||
|
webSocket.send(JSON.stringify(msg));
|
||||||
|
} else if (data.source == "Hand" && data.dest == "Discard") {
|
||||||
|
var msg = { type: "Discard", index: data.index};
|
||||||
|
webSocket.send(JSON.stringify(msg));
|
||||||
|
} else if (data.source == "Supply" && data.dest == "Discard") {
|
||||||
|
var msg = { type: "GainCard", name: data.name, index: parseInt(data.index) };
|
||||||
|
webSocket.send(JSON.stringify(msg));
|
||||||
|
} else if (data.source == "DrawPile" && data.dest == "Hand") {
|
||||||
|
var msg = { type: "DrawCard" };
|
||||||
|
webSocket.send(JSON.stringify(msg));
|
||||||
|
} else if (data.source == "Hand" && data.dest == "Trash") {
|
||||||
|
var msg = { type: "TrashHand", index: data.index };
|
||||||
|
webSocket.send(JSON.stringify(msg));
|
||||||
|
} else {
|
||||||
|
console.log("handle_dnd: unhandled data", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Chat(initialVnode) {
|
function Chat(initialVnode) {
|
||||||
var keyup = function(e) {
|
var keyup = function(e) {
|
||||||
if (e.key == "Enter" && e.srcElement.value.length > 0) {
|
if (e.key == "Enter" && e.srcElement.value.length > 0) {
|
||||||
@ -254,11 +366,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SupplyPile(initialVnode) {
|
function SupplyPile(initialVnode) {
|
||||||
|
var dragStart = function(ev) {
|
||||||
|
console.log(ev);
|
||||||
|
let data = {
|
||||||
|
source: "Supply",
|
||||||
|
name: ev.target.dataset.name,
|
||||||
|
index: ev.target.dataset.index,
|
||||||
|
};
|
||||||
|
ev.dataTransfer.setData("text", JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return m(".card",
|
return m(".supply-pile",
|
||||||
m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.name.toLowerCase() + ".jpg"}),
|
{
|
||||||
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
"data-count": vnode.attrs.count,
|
||||||
|
"data-name": vnode.attrs.name,
|
||||||
|
"data-index": vnode.attrs.index,
|
||||||
|
ondragstart: dragStart,
|
||||||
|
draggable: true,
|
||||||
|
},
|
||||||
|
m("img", {
|
||||||
|
class: "card",
|
||||||
|
src: "/static/images/cards/" + vnode.attrs.name.toLowerCase() + ".jpg",
|
||||||
|
draggable: false,
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,8 +402,8 @@
|
|||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return m(".supply-area",
|
return m(".supply-area",
|
||||||
m("h3", "Supply"),
|
m("h3", "Supply"),
|
||||||
vnode.attrs.supply.map(function(pile) {
|
vnode.attrs.supply.map(function(pile, i) {
|
||||||
return m(SupplyPile, pile)
|
return m(SupplyPile, {...pile, index: i})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -279,35 +411,137 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function TrashPile(initialVnode) {
|
||||||
|
var dragover = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var data = {
|
||||||
|
dest: "Trash",
|
||||||
|
...JSON.parse(e.dataTransfer.getData("text")),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_dnd(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".trash",
|
||||||
|
{
|
||||||
|
ondragover: dragover,
|
||||||
|
ondrop: drop,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function DiscardPile(initialVnode) {
|
function DiscardPile(initialVnode) {
|
||||||
|
var dragover = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var data = {
|
||||||
|
dest: "Discard",
|
||||||
|
...JSON.parse(e.dataTransfer.getData("text")),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_dnd(data);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
var c;
|
var c;
|
||||||
if (vnode.attrs.card) {
|
if (vnode.attrs.card) {
|
||||||
c = m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.card.toLowerCase() + ".jpg"});
|
c = m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.card.toLowerCase() + ".jpg"});
|
||||||
}
|
}
|
||||||
return m(".discard-pile", c)
|
return m(".discard-pile",
|
||||||
|
{
|
||||||
|
ondragover: dragover,
|
||||||
|
ondrop: drop,
|
||||||
|
},
|
||||||
|
c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function DrawPile(initialVnode) {
|
function DrawPile(initialVnode) {
|
||||||
|
var dragStart = function(ev) {
|
||||||
|
//console.log(ev);
|
||||||
|
let data = {
|
||||||
|
source: "DrawPile",
|
||||||
|
};
|
||||||
|
ev.dataTransfer.setData("text", JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dragover = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("drop", e);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return m(".draw-pile",
|
return m(".draw-pile",
|
||||||
m("img", {class: "card", src: "/static/images/cards/Card_back.jpg"}),
|
{
|
||||||
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
draggable: vnode.attrs.draggable ? true : false,
|
||||||
|
ondragstart: dragStart,
|
||||||
|
ondragover: dragover,
|
||||||
|
ondrop: drop,
|
||||||
|
"data-count": vnode.attrs.count,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function PlayerHand(initialVnode) {
|
function PlayerHand(initialVnode) {
|
||||||
|
var dragStart = function(ev) {
|
||||||
|
//console.log(ev);
|
||||||
|
let data = {
|
||||||
|
source: "Hand",
|
||||||
|
index: parseInt(ev.target.dataset.index),
|
||||||
|
};
|
||||||
|
ev.dataTransfer.setData("text", JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dragover = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var data = {
|
||||||
|
dest: "Hand",
|
||||||
|
...JSON.parse(e.dataTransfer.getData("text")),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_dnd(data);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return m(".player-hand", "Player Hand",
|
return m(".player-hand",
|
||||||
vnode.attrs.hand.map(function(card) {
|
{
|
||||||
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
ondragover: dragover,
|
||||||
|
ondrop: drop,
|
||||||
|
},
|
||||||
|
"Player Hand",
|
||||||
|
vnode.attrs.hand.map(function(card, i) {
|
||||||
|
return m("img", {
|
||||||
|
class: "card",
|
||||||
|
src: "/static/images/cards/" + card.toLowerCase() + ".jpg",
|
||||||
|
draggable: true,
|
||||||
|
ondragstart: dragStart,
|
||||||
|
"data-index": i,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -326,16 +560,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function InPlayArea(initialVnode) {
|
function InPlayArea(initialVnode) {
|
||||||
|
var dragover = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var data = {
|
||||||
|
dest: "InPlay",
|
||||||
|
...JSON.parse(e.dataTransfer.getData("text")),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_dnd(data);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
var active = vnode.attrs.active ? "" : "inactive";
|
var active = vnode.attrs.active ? "" : "inactive";
|
||||||
return m(".inplay-area", {class: active},
|
var cards = game_state.players[game_state.active_player].played_cards || [];
|
||||||
|
return m(".inplay-area",
|
||||||
|
{
|
||||||
|
class: active,
|
||||||
|
ondragover: dragover,
|
||||||
|
ondrop: drop,
|
||||||
|
},
|
||||||
"Player play area",
|
"Player play area",
|
||||||
m(".turn-status",
|
m(".turn-status",
|
||||||
"Actions: " + vnode.attrs.actions + " | Buys: " + vnode.attrs.buys + " | ",
|
"Actions: " + vnode.attrs.actions + " | Buys: " + vnode.attrs.buys + " | ",
|
||||||
m("img", {src: "/static/images/64px-Coin.png", style: "height: 16px;"}),
|
m("img", {src: "/static/images/64px-Coin.png", style: "height: 16px;"}),
|
||||||
": " + vnode.attrs.coin
|
": " + vnode.attrs.coin
|
||||||
)
|
),
|
||||||
|
cards.map(function(card) {
|
||||||
|
return m("img", {
|
||||||
|
class: "card",
|
||||||
|
src: "/static/images/cards/" + card.toLowerCase() + ".jpg",
|
||||||
|
draggable: true,
|
||||||
|
//ondragstart: dragStart,
|
||||||
|
})
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,7 +613,7 @@
|
|||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
var local_player = vnode.attrs.players[my_player_id];
|
var local_player = vnode.attrs.players[my_player_id];
|
||||||
return m(".player-area",
|
return m(".player-area",
|
||||||
m(DrawPile, {count: local_player.draw_pile_count}),
|
m(DrawPile, {count: local_player.draw_pile_count, draggable: true}),
|
||||||
m(PlayerHand, vnode.attrs.player),
|
m(PlayerHand, vnode.attrs.player),
|
||||||
m(DiscardPile, {card: local_player.discard_pile}),
|
m(DiscardPile, {card: local_player.discard_pile}),
|
||||||
m(".buttons",
|
m(".buttons",
|
||||||
@ -384,7 +646,8 @@
|
|||||||
.filter((p, i) => i != my_player_id)
|
.filter((p, i) => i != my_player_id)
|
||||||
.map(function(o) {
|
.map(function(o) {
|
||||||
return m(OpponentStatus, o)
|
return m(OpponentStatus, o)
|
||||||
})
|
}),
|
||||||
|
m(TrashPile),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,12 +656,21 @@
|
|||||||
function GameScreen(initialVnode) {
|
function GameScreen(initialVnode) {
|
||||||
return {
|
return {
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
|
var player_state = {
|
||||||
|
...vnode.attrs.turn_state,
|
||||||
|
active: vnode.attrs.active_player == my_player_id,
|
||||||
|
}
|
||||||
|
var opponent_state = {
|
||||||
|
...vnode.attrs.turn_state,
|
||||||
|
active: vnode.attrs.active_player != my_player_id,
|
||||||
|
}
|
||||||
|
|
||||||
var cls = vnode.attrs.active ? "" : "hidden";
|
var cls = vnode.attrs.active ? "" : "hidden";
|
||||||
return m(".game-screen", {class: cls},
|
return m(".game-screen", {class: cls},
|
||||||
m(OpponentArea, vnode.attrs),
|
m(OpponentArea, vnode.attrs),
|
||||||
m(InPlayArea, vnode.attrs.opponent_turn_state),
|
m(InPlayArea, opponent_state),
|
||||||
m(SupplyArea, vnode.attrs),
|
m(SupplyArea, vnode.attrs),
|
||||||
m(InPlayArea, vnode.attrs.player_turn_state),
|
m(InPlayArea, player_state),
|
||||||
m(PlayerArea, vnode.attrs)
|
m(PlayerArea, vnode.attrs)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -467,60 +739,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
game_state = {
|
game_state = {
|
||||||
supply: [
|
supply: [],
|
||||||
{ name: "Copper", count: 46 },
|
turn_state: {
|
||||||
{ name: "Silver", count: 38 },
|
|
||||||
{ name: "Gold", count: 30 },
|
|
||||||
{ name: "Estate", count: 8 },
|
|
||||||
{ name: "Duchery", count: 8 },
|
|
||||||
{ name: "Province", count: 8 },
|
|
||||||
{ name: "Curse", count: 10 },
|
|
||||||
|
|
||||||
{ name: "Cellar", count: 10},
|
|
||||||
{ name: "Moat", count: 10},
|
|
||||||
{ name: "Village", count: 10},
|
|
||||||
{ name: "Merchant", count: 10},
|
|
||||||
{ name: "Workshop", count: 10},
|
|
||||||
|
|
||||||
{ name: "Smithy", count: 10},
|
|
||||||
{ name: "Remodel", count: 10},
|
|
||||||
{ name: "Militia", count: 10},
|
|
||||||
{ name: "Market", count: 10},
|
|
||||||
{ name: "Mine", count: 10}
|
|
||||||
],
|
|
||||||
opponent_turn_state: {
|
|
||||||
actions: 1,
|
actions: 1,
|
||||||
buys: 1,
|
buys: 1,
|
||||||
coin: 0,
|
coin: 0,
|
||||||
active: false
|
|
||||||
},
|
|
||||||
player_turn_state: {
|
|
||||||
actions: 1,
|
|
||||||
buys: 1,
|
|
||||||
coin: 0,
|
|
||||||
active: true
|
|
||||||
},
|
},
|
||||||
player: {
|
player: {
|
||||||
hand: []
|
hand: []
|
||||||
},
|
},
|
||||||
players: [
|
players: [
|
||||||
{
|
{
|
||||||
name: "YOU",
|
name: "You",
|
||||||
draw_pile_count: 10,
|
|
||||||
hand_count: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Alice",
|
|
||||||
draw_pile_count: 10,
|
|
||||||
hand_count: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bob",
|
|
||||||
draw_pile_count: 8,
|
|
||||||
hand_count: 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Mallory",
|
|
||||||
draw_pile_count: 10,
|
draw_pile_count: 10,
|
||||||
hand_count: 3
|
hand_count: 3
|
||||||
}
|
}
|
||||||
@ -538,8 +768,6 @@
|
|||||||
...game_state,
|
...game_state,
|
||||||
...state,
|
...state,
|
||||||
};
|
};
|
||||||
game_state.opponent_turn_state.active = game_state.active_player != my_player_id;
|
|
||||||
game_state.player_turn_state.active = game_state.active_player == my_player_id;
|
|
||||||
chat_state.players = state.players;
|
chat_state.players = state.players;
|
||||||
|
|
||||||
if (state.state == "Setup") {
|
if (state.state == "Setup") {
|
||||||
@ -565,19 +793,32 @@
|
|||||||
let newmsg = document.createElement("li");
|
let newmsg = document.createElement("li");
|
||||||
newmsg.innerHTML = msg.sender + ": " + msg.message;
|
newmsg.innerHTML = msg.sender + ": " + msg.message;
|
||||||
chatDiv.append(newmsg);
|
chatDiv.append(newmsg);
|
||||||
//newmsg.scrollIntoView();
|
newmsg.scrollIntoView();
|
||||||
|
} else if (msg.type == "Notification") {
|
||||||
|
let chatDiv = document.getElementById("chat");
|
||||||
|
let last_element = document.querySelector("#chat li:last-child");
|
||||||
|
if (last_element.innerText == msg.text) {
|
||||||
|
last_element.dataset.repeat = (parseInt(last_element.dataset.repeat || 1)) + 1;
|
||||||
|
} else {
|
||||||
|
let newmsg = document.createElement("li");
|
||||||
|
newmsg.innerHTML = msg.text;
|
||||||
|
chatDiv.append(newmsg);
|
||||||
|
newmsg.scrollIntoView();
|
||||||
|
}
|
||||||
} else if (msg.type == "PlayerJoined") {
|
} else if (msg.type == "PlayerJoined") {
|
||||||
let chatDiv = document.getElementById("chat");
|
let chatDiv = document.getElementById("chat");
|
||||||
let newmsg = document.createElement("li");
|
let newmsg = document.createElement("li");
|
||||||
newmsg.innerHTML = msg.player + " joined the game.";
|
newmsg.innerHTML = msg.player + " joined the game.";
|
||||||
chatDiv.append(newmsg);
|
chatDiv.append(newmsg);
|
||||||
//newmsg.scrollIntoView();
|
newmsg.scrollIntoView();
|
||||||
} else if (msg.type == "GameState") {
|
} else if (msg.type == "GameState") {
|
||||||
handle_game_state(msg);
|
handle_game_state(msg);
|
||||||
} else if (msg.type == "GameSetup") {
|
} else if (msg.type == "GameSetup") {
|
||||||
handle_setup(msg.setup);
|
handle_setup(msg.setup);
|
||||||
} else if (msg.type == "PlayerHand") {
|
} else if (msg.type == "PlayerHand") {
|
||||||
game_state.player.hand = msg.hand;
|
game_state.player.hand = msg.hand;
|
||||||
|
} else if (msg.type == "PlayerId") {
|
||||||
|
my_player_id = msg.id;
|
||||||
} else {
|
} else {
|
||||||
console.log("event?");
|
console.log("event?");
|
||||||
console.log(event.data);
|
console.log(event.data);
|
||||||
|
Loading…
Reference in New Issue
Block a user