Initial commit
This commit is contained in:
commit
bb8162e4c1
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
*.dae filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.blend filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gltf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1751
Cargo.lock
generated
Normal file
1751
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "dominion"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Markus Wagner <murk@ferrum-et-magica.de>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-std = { version = "1.8.0", features = ["attributes"] }
|
||||||
|
html-escape = "0.2.6"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0.60"
|
||||||
|
tide = "0.15.0"
|
||||||
|
tide-websockets = "0.1.0"
|
||||||
|
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
238
src/main.rs
Normal file
238
src/main.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
extern crate html_escape;
|
||||||
|
|
||||||
|
use async_std::{
|
||||||
|
prelude::*,
|
||||||
|
sync::RwLock
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc}
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tide::{Body, Redirect, Request, Response};
|
||||||
|
use tide_websockets::{Message, WebSocket, WebSocketConnection};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
struct CreateGameMessage {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct GameStateMessage {
|
||||||
|
id: String,
|
||||||
|
state: GameState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct GameSetupMessage {
|
||||||
|
id: String,
|
||||||
|
setup: GameSetup,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum ClientMessage {
|
||||||
|
Chat {
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
StartGame,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
enum ServerMessage {
|
||||||
|
Chat {
|
||||||
|
sender: String,
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
PlayerJoined {
|
||||||
|
player: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Player {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Serialize)]
|
||||||
|
enum GameState {
|
||||||
|
Setup,
|
||||||
|
InProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize)]
|
||||||
|
struct GameSetup {
|
||||||
|
deck: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameSetup {
|
||||||
|
fn default() -> GameSetup {
|
||||||
|
GameSetup {
|
||||||
|
deck: vec!["Copper".into(), "Copper".into(), "Copper".into(), "Copper".into(), "Copper".into(),
|
||||||
|
"Copper".into(), "Copper".into(), "Estate".into(), "Estate".into(), "Estate".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
players: Vec<Player>,
|
||||||
|
state: GameState,
|
||||||
|
setup: GameSetup,
|
||||||
|
connections: HashMap<Uuid, WebSocketConnection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
fn new(player: Player) -> Self {
|
||||||
|
Self {
|
||||||
|
players: vec![player],
|
||||||
|
state: GameState::Setup,
|
||||||
|
setup: GameSetup::default(),
|
||||||
|
connections: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
struct State {
|
||||||
|
games: Arc<RwLock<HashMap<Uuid, Game>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
tide::log::with_level(tide::log::LevelFilter::Debug);
|
||||||
|
|
||||||
|
let mut app = tide::with_state(State::default());
|
||||||
|
|
||||||
|
app.with(tide::sessions::SessionMiddleware::new(
|
||||||
|
tide::sessions::MemoryStore::new(),
|
||||||
|
"1234567890abcdef1234567890abcdef".as_bytes(),
|
||||||
|
));
|
||||||
|
|
||||||
|
app.at("/").get(|_| async { Ok(Body::from_file("static/index.html").await?) });
|
||||||
|
app.at("/static").serve_dir("static/")?;
|
||||||
|
|
||||||
|
app.at("/game").post(|mut req: Request<State>| async move {
|
||||||
|
let msg: CreateGameMessage = req.body_form().await?;
|
||||||
|
let session = req.session_mut();
|
||||||
|
|
||||||
|
session.insert("player_name", msg.name.clone()).unwrap();
|
||||||
|
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let player = Player { name: msg.name };
|
||||||
|
|
||||||
|
games.insert(id, Game::new(player));
|
||||||
|
|
||||||
|
let url = format!("/game/{}", id.to_simple().to_string());
|
||||||
|
let res: tide::Result = Ok(Redirect::new(url).into());
|
||||||
|
res
|
||||||
|
});
|
||||||
|
|
||||||
|
app.at("/game/:id").get(|req: Request<State>| async move {
|
||||||
|
let games = req.state().games.read().await;
|
||||||
|
let id = req.param("id")?;
|
||||||
|
let game_id = Uuid::parse_str(&id).unwrap();
|
||||||
|
|
||||||
|
match games.get(&game_id) {
|
||||||
|
None => Ok(Response::new(404)),
|
||||||
|
Some(_) => {
|
||||||
|
let mut res = Response::new(200);
|
||||||
|
res.set_body(Body::from_file("static/game.html").await?);
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.at("/game/:id/ws")
|
||||||
|
.get(WebSocket::new(|req: Request<State>, mut stream| async move {
|
||||||
|
let id = req.param("id")?;
|
||||||
|
let game_id = Uuid::parse_str(&id).unwrap();
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
let session = req.session();
|
||||||
|
|
||||||
|
let client_id = Uuid::new_v4();
|
||||||
|
game.connections.insert(client_id, stream.clone());
|
||||||
|
|
||||||
|
//Ensure the write locks are freed, possibly better to move to a function
|
||||||
|
//with implicit drop?
|
||||||
|
drop(game);
|
||||||
|
drop(games);
|
||||||
|
|
||||||
|
let games = req.state().games.read().await;
|
||||||
|
let game = games.get(&game_id).unwrap();
|
||||||
|
|
||||||
|
let msg = GameStateMessage {
|
||||||
|
id: "state".into(),
|
||||||
|
state: game.state,
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.send_json(&msg).await?;
|
||||||
|
|
||||||
|
let msg = GameSetupMessage {
|
||||||
|
id: "setup".into(),
|
||||||
|
setup: game.setup.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.send_json(&msg).await?;
|
||||||
|
|
||||||
|
let player: String = session.get("player_name").unwrap_or("SOMEONE".into());
|
||||||
|
let sm = ServerMessage::PlayerJoined {
|
||||||
|
player: html_escape::encode_text(&player).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (_, con) in game.connections.iter() {
|
||||||
|
con.send_json(&sm).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(game); drop(games);
|
||||||
|
|
||||||
|
while let Some(Ok(Message::Text(input))) = stream.next().await {
|
||||||
|
let msg: ClientMessage = serde_json::from_str(&input)?;
|
||||||
|
|
||||||
|
match msg {
|
||||||
|
ClientMessage::Chat { message } => {
|
||||||
|
let sender: String = session.get("player_name").unwrap_or("SOMEONE".into());
|
||||||
|
let sm = ServerMessage::Chat {
|
||||||
|
sender: html_escape::encode_text(&sender).into(),
|
||||||
|
message: html_escape::encode_text(&message).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let games = req.state().games.read().await;
|
||||||
|
let game = games.get(&game_id).unwrap();
|
||||||
|
|
||||||
|
for (_, con) in game.connections.iter() {
|
||||||
|
con.send_json(&sm).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage::StartGame => {
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
match game.state {
|
||||||
|
GameState::Setup => {
|
||||||
|
game.state = GameState::InProgress;
|
||||||
|
}
|
||||||
|
_ => {} //Ignore, if game not in setup state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut games = req.state().games.write().await;
|
||||||
|
let game = games.get_mut(&game_id).unwrap();
|
||||||
|
|
||||||
|
game.connections.remove(&client_id);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
|
||||||
|
app.listen("0.0.0.0:5000").await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
523
static/game.html
Normal file
523
static/game.html
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>DnD</title>
|
||||||
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
|
<style type="text/css">
|
||||||
|
.pile-counter {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-area {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 100px auto 100px;
|
||||||
|
border: 1px solid black;
|
||||||
|
width: 100%;
|
||||||
|
height: 145px;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
width: 90px;
|
||||||
|
margin: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-hand {
|
||||||
|
border: 1px solid red;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-window {
|
||||||
|
border: 1px solid black;
|
||||||
|
width: 300px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supply-area {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 30px;
|
||||||
|
padding-right: 300px;
|
||||||
|
background-color: dimgray;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 5px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supply-area h3 {
|
||||||
|
position: relative;
|
||||||
|
top: 50px;
|
||||||
|
left: 0;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draw-pile {
|
||||||
|
width: 90px;
|
||||||
|
position: relative;
|
||||||
|
height: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-hand {
|
||||||
|
width: 90px;
|
||||||
|
position: relative;
|
||||||
|
height: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-screen {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discard-pile {
|
||||||
|
width: 90px;
|
||||||
|
border: 1px dotted green;
|
||||||
|
height: 145px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inplay-area {
|
||||||
|
width: 100%;
|
||||||
|
height: 180px;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: height 0.4s ease;
|
||||||
|
background-color: dimgray;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inplay-area.inactive {
|
||||||
|
height: 0;
|
||||||
|
transition: height 0.4s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.turn-status {
|
||||||
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
background-color: rgb(255 255 255 / 34%);
|
||||||
|
border-radius: 0 0 20px 20px;
|
||||||
|
padding: 0 20px 0;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inplay-area.inactive .turn-status {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-area {
|
||||||
|
height: 180px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status {
|
||||||
|
grid-template-rows: 30px auto;
|
||||||
|
grid-template-columns: auto auto auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0 5px;
|
||||||
|
border: 1px solid saddlebrown;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status.active {
|
||||||
|
box-shadow: 0 0 10px red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status .name {
|
||||||
|
grid-column-start: 1;
|
||||||
|
grid-column-end: span 3;
|
||||||
|
text-align: center;
|
||||||
|
grid-row-start: 1;
|
||||||
|
grid-row-end: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status .discard-pile {
|
||||||
|
grid-column-start: 1;
|
||||||
|
grid-column-end: 1;
|
||||||
|
grid-row-start: 2;
|
||||||
|
grid-row-end: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status .draw-pile {
|
||||||
|
grid-column-start: 2;
|
||||||
|
grid-column-end: 2;
|
||||||
|
grid-row-start: 2;
|
||||||
|
grid-row-end: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opponent-status .opponent-hand {
|
||||||
|
grid-column-start: 3;
|
||||||
|
grid-column-end: 3;
|
||||||
|
grid-row-start: 2;
|
||||||
|
grid-row-end: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-screen {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/static/mithril.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="game"></div>
|
||||||
|
<script>
|
||||||
|
function Chat(initialVnode) {
|
||||||
|
var keyup = function(e) {
|
||||||
|
if (e.key == "Enter" && e.srcElement.value.length > 0) {
|
||||||
|
let msg = { type: "Chat", message: e.srcElement.value };
|
||||||
|
initialVnode.attrs.socket.send(JSON.stringify(msg));
|
||||||
|
e.srcElement.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".chat-window",
|
||||||
|
m("h4", "Players"),
|
||||||
|
m("ul", m("li", "Markus (you)")),
|
||||||
|
m("h4", "Messages"),
|
||||||
|
m("ul", {id: "chat"}),
|
||||||
|
m("input", {
|
||||||
|
id: "chat_input",
|
||||||
|
placeholder: "Type to chat...",
|
||||||
|
onkeyup: keyup
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SupplyPile(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".card",
|
||||||
|
m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.name.toLowerCase() + ".jpg"}),
|
||||||
|
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SupplyArea(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".supply-area",
|
||||||
|
m("h3", "Supply"),
|
||||||
|
vnode.attrs.supply.map(function(pile) {
|
||||||
|
return m(SupplyPile, pile)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DiscardPile(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".discard-pile", "Discard Pile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DrawPile(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".draw-pile",
|
||||||
|
m("img", {class: "card", src: "/static/images/cards/Card_back.jpg"}),
|
||||||
|
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayerHand(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".player-hand", "Player Hand",
|
||||||
|
vnode.attrs.hand.map(function(card) {
|
||||||
|
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpponentHand(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".opponent-hand",
|
||||||
|
m("img", {class: "card", src: "/static/images/cards/Card_back.jpg"}),
|
||||||
|
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function InPlayArea(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
var active = vnode.attrs.active ? "" : "inactive";
|
||||||
|
return m(".inplay-area", {class: active},
|
||||||
|
"Player play area",
|
||||||
|
m(".turn-status",
|
||||||
|
"Actions: " + vnode.attrs.actions + " | Buys: " + vnode.attrs.buys + " | ",
|
||||||
|
m("img", {src: "/static/images/64px-Coin.png", style: "height: 16px;"}),
|
||||||
|
": " + vnode.attrs.coin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayerArea(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".player-area",
|
||||||
|
m(DrawPile, {count: vnode.attrs.player_drawpile_count}),
|
||||||
|
m(PlayerHand, vnode.attrs.player),
|
||||||
|
m(DiscardPile)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpponentStatus(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
var active = vnode.attrs.active ? "active" : "";
|
||||||
|
return m(".opponent-status", {class: active },
|
||||||
|
m(".name", vnode.attrs.name),
|
||||||
|
m(DiscardPile),
|
||||||
|
m(DrawPile, {count: vnode.attrs.draw_pile_count}),
|
||||||
|
m(OpponentHand, {count: vnode.attrs.hand_count })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function OpponentArea(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".opponent-area",
|
||||||
|
vnode.attrs.opponents.map(function(o) {
|
||||||
|
return m(OpponentStatus, o)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GameScreen(initialVnode) {
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".game-screen",
|
||||||
|
m(OpponentArea, vnode.attrs),
|
||||||
|
m(InPlayArea, vnode.attrs.opponent_turn_state),
|
||||||
|
m(SupplyArea, vnode.attrs),
|
||||||
|
m(InPlayArea, vnode.attrs.player_turn_state),
|
||||||
|
m(PlayerArea, vnode.attrs)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function SetupScreen(initialVnode) {
|
||||||
|
var start_click = function(e) {
|
||||||
|
console.log("start_click", e);
|
||||||
|
|
||||||
|
let msg = { id: "start_game" };
|
||||||
|
initialVnode.attrs.socket.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return m(".setup-screen",
|
||||||
|
m("h3", "Setup"),
|
||||||
|
m("h4", "Basic cards"),
|
||||||
|
m(".basic-cards",
|
||||||
|
vnode.attrs.basic_cards.map(function(card) {
|
||||||
|
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
m("h4", "Kingdom Cards"),
|
||||||
|
m(".kingdom-cards",
|
||||||
|
vnode.attrs.kingdom_cards.map(function(card) {
|
||||||
|
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
m("h4", "Starting Deck"),
|
||||||
|
m(".start-deck",
|
||||||
|
vnode.attrs.starting_deck.map(function(card) {
|
||||||
|
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
m("button", {onclick: start_click}, "Start Game")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var game_state;
|
||||||
|
|
||||||
|
function App(initialVnode) {
|
||||||
|
if (document.location.protocol == "https:") {
|
||||||
|
url = document.location.href.replace("https://", "wss://") + "/ws";
|
||||||
|
} else {
|
||||||
|
url = document.location.href.replace("http://", "ws://") + "/ws";
|
||||||
|
}
|
||||||
|
const webSocket = new WebSocket(url);
|
||||||
|
|
||||||
|
var setup_state = {
|
||||||
|
starting_deck: [],
|
||||||
|
basic_cards: ["Copper", "Silver", "Gold", "Estate", "Duchery", "Province", "Curse"],
|
||||||
|
kingdom_cards: ["Cellar", "Moat", "Village", "Merchant", "Workshop", "Smithy", "Remodel", "Militia", "Market", "Mine"],
|
||||||
|
socket: webSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle_setup = function(data) {
|
||||||
|
setup_state.starting_deck = data.deck;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_state = {
|
||||||
|
supply: [
|
||||||
|
{ name: "Copper", count: 46 },
|
||||||
|
{ 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,
|
||||||
|
buys: 1,
|
||||||
|
coin: 0,
|
||||||
|
active: false
|
||||||
|
},
|
||||||
|
player_turn_state: {
|
||||||
|
actions: 1,
|
||||||
|
buys: 1,
|
||||||
|
coin: 0,
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
player_drawpile_count: 10,
|
||||||
|
player: {
|
||||||
|
hand: []
|
||||||
|
},
|
||||||
|
opponents: [
|
||||||
|
{
|
||||||
|
name: "Alice",
|
||||||
|
draw_pile_count: 10,
|
||||||
|
hand_count: 5,
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bob",
|
||||||
|
draw_pile_count: 8,
|
||||||
|
hand_count: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mallory",
|
||||||
|
draw_pile_count: 10,
|
||||||
|
hand_count: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
var chat_state = {
|
||||||
|
socket: webSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
webSocket.onopen = function(event) {
|
||||||
|
console.log("ws open");
|
||||||
|
//webSocket.send("HALLO");
|
||||||
|
};
|
||||||
|
|
||||||
|
webSocket.onmessage = function(event) {
|
||||||
|
var msg = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (msg.type == "Chat") {
|
||||||
|
let chatDiv = document.getElementById("chat");
|
||||||
|
let newmsg = document.createElement("li");
|
||||||
|
newmsg.innerHTML = msg.sender + ": " + msg.message;
|
||||||
|
chatDiv.append(newmsg);
|
||||||
|
//newmsg.scrollIntoView();
|
||||||
|
} else if (msg.type == "PlayerJoined") {
|
||||||
|
let chatDiv = document.getElementById("chat");
|
||||||
|
let newmsg = document.createElement("li");
|
||||||
|
newmsg.innerHTML = msg.player + " joined the game.";
|
||||||
|
chatDiv.append(newmsg);
|
||||||
|
//newmsg.scrollIntoView();
|
||||||
|
} else if (msg.id == "setup") {
|
||||||
|
handle_setup(msg.setup);
|
||||||
|
} else {
|
||||||
|
console.log("event?");
|
||||||
|
console.log(event.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
m.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
view: function(vnode) {
|
||||||
|
return [
|
||||||
|
m(SetupScreen, setup_state),
|
||||||
|
m(GameScreen, game_state),
|
||||||
|
m(Chat, chat_state)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mount(document.getElementById("game"), App)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
static/images/64px-Coin.png
(Stored with Git LFS)
Normal file
BIN
static/images/64px-Coin.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/Card_back.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/Card_back.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/abandoned-mine.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/abandoned-mine.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/academy.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/academy.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/acting-troupe.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/acting-troupe.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/advance.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/advance.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/adventurer.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/adventurer.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/advisor.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/advisor.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/alchemist.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/alchemist.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/alms.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/alms.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/altar.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/altar.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/ambassador.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/ambassador.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/amulet.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/amulet.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/annex.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/annex.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/apothecary.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/apothecary.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/apprentice.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/apprentice.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/aqueduct.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/aqueduct.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/archive.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/archive.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/arena.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/arena.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/armory.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/armory.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/artificer.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/artificer.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/artisan.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/artisan.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/avanto.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/avanto.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bad-omens.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bad-omens.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bag-of-gold.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bag-of-gold.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/baker.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/baker.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/ball.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/ball.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/band-of-misfits.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/band-of-misfits.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bandit-camp.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bandit-camp.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bandit-fort.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bandit-fort.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bandit.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bandit.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bank.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bank.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/banquet.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/banquet.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bard.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bard.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/baron.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/baron.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/barracks.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/barracks.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/basilica.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/basilica.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bat.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bat.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/baths.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/baths.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/battlefield.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/battlefield.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bazaar.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bazaar.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/beggar.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/beggar.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bishop.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bishop.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/black-market.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/black-market.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/blessed-village.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/blessed-village.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bonfire.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bonfire.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/border-guard.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/border-guard.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/border-village.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/border-village.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/borrow.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/borrow.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bridge-troll.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bridge-troll.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bridge.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bridge.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bureaucrat.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bureaucrat.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/bustling-village.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/bustling-village.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/butcher.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/butcher.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cache.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cache.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/canal.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/canal.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/candlestick-maker.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/candlestick-maker.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/capital.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/capital.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/capitalism.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/capitalism.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/captain.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/captain.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/caravan-guard.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/caravan-guard.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/caravan.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/caravan.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cargo-ship.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cargo-ship.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cartographer.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cartographer.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/castles.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/castles.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/catacombs.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/catacombs.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/catapult.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/catapult.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cathedral.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cathedral.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cellar.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cellar.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cemetery.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cemetery.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/champion.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/champion.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/chancellor.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/chancellor.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/changeling.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/changeling.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/chapel.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/chapel.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/chariot-race.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/chariot-race.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/charm.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/charm.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/church.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/church.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/citadel.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/citadel.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/city-gate.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/city-gate.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/city-quarter.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/city-quarter.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/city.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/city.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/cobbler.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/cobbler.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/coin-of-the-realm.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/coin-of-the-realm.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/colonnade.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/colonnade.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/colony.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/colony.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/conclave.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/conclave.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/conquest.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/conquest.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/conspirator.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/conspirator.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/contraband.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/contraband.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/copper.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/copper.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/coppersmith.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/coppersmith.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/council-room.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/council-room.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/count.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/count.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
static/images/cards/counterfeit.jpg
(Stored with Git LFS)
Normal file
BIN
static/images/cards/counterfeit.jpg
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user