1008 lines
28 KiB
HTML
1008 lines
28 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>DnD</title>
|
|
<link rel="stylesheet" href="/static/main.css">
|
|
<style type="text/css">
|
|
#game {
|
|
display: grid;
|
|
grid-template-columns: auto 300px;
|
|
user-select: none;
|
|
position: relative;
|
|
}
|
|
|
|
.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 90px;
|
|
grid-template-rows: auto auto;
|
|
width: 100%;
|
|
height: 145px;
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
grid-gap: 10px;
|
|
}
|
|
|
|
.player-area .buttons {
|
|
grid-column-start: 3;
|
|
text-align: center;
|
|
}
|
|
|
|
.card {
|
|
position: relative;
|
|
width: 90px;
|
|
margin: 1px;
|
|
height: 144px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
img.card:hover {
|
|
box-shadow: 0 0 5px white;
|
|
}
|
|
|
|
.player-hand {
|
|
height: 145px;
|
|
margin: 0;
|
|
display: grid;
|
|
justify-content: center;
|
|
grid-template-columns: repeat(auto-fit, minmax(10px, max-content));
|
|
padding-right: 115px;
|
|
}
|
|
|
|
.player-hand img.card {
|
|
transform: rotate(3deg);
|
|
transition: transform 0.1s ease-in;
|
|
}
|
|
|
|
.player-hand img.card:hover {
|
|
box-shadow: 0 0 5px blue;
|
|
transform: scale(1.2);
|
|
transition: transform 0.1s ease-in;
|
|
}
|
|
|
|
.player-hand img.card:hover ~ img.card {
|
|
transform: translate(60px, 0) rotate(3deg);
|
|
transition: transform 0.1s ease-in;
|
|
}
|
|
|
|
.chat-window {
|
|
border: 1px solid black;
|
|
width: 300px;
|
|
grid-column-start: 2;
|
|
grid-row-start: 1;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.chat-window h4 {
|
|
text-align: center;
|
|
margin-top: 0;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.chat-window .players {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0 5px 5px;
|
|
}
|
|
|
|
.chat-window .players li {
|
|
}
|
|
|
|
#chat li[data-repeat]::after {
|
|
content: "x" attr(data-repeat);
|
|
margin-left: 10px;
|
|
background-color: deepskyblue;
|
|
color: white;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.supply-area {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
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;
|
|
}
|
|
|
|
.supply-pile {
|
|
width: 90px;
|
|
position: relative;
|
|
height: 145px;
|
|
border-radius: 6px;
|
|
margin: 1px;
|
|
}
|
|
|
|
.supply-pile::after {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
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 {
|
|
width: 90px;
|
|
position: relative;
|
|
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 {
|
|
width: 90px;
|
|
position: relative;
|
|
height: 145px;
|
|
}
|
|
|
|
.opponent-hand img.card {
|
|
margin-top: 0;
|
|
margin-left: 0;
|
|
}
|
|
|
|
.setup-screen {
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.discard-pile {
|
|
width: 90px;
|
|
border: 1px dotted green;
|
|
height: 144px;
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.discard-pile img.card {
|
|
margin-top: -1px;
|
|
margin-left: -1px;
|
|
}
|
|
|
|
.discard-pile::after {
|
|
color: dimgray;
|
|
content: "Discard Pile";
|
|
font-size: 14px;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
white-space: nowrap;
|
|
z-index: -1;
|
|
}
|
|
|
|
.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 h3 {
|
|
position: relative;
|
|
top: 50px;
|
|
left: 30px;
|
|
transform-origin: 0 0;
|
|
transform: rotate(90deg);
|
|
padding: 0;
|
|
margin: 0;
|
|
width: 100px;
|
|
}
|
|
|
|
.inplay-area.inactive {
|
|
height: 0;
|
|
transition: height 0.4s ease;
|
|
overflow: hidden;
|
|
padding-top: 0;
|
|
}
|
|
|
|
.inplay-area img.card {
|
|
margin-left: -55px;
|
|
transform: translate(85px, 10px);
|
|
}
|
|
|
|
.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 {
|
|
display: flex;
|
|
position: relative;
|
|
min-height: 145px;
|
|
}
|
|
|
|
.opponent-status {
|
|
grid-template-rows: 30px auto;
|
|
grid-template-columns: auto auto auto;
|
|
box-sizing: border-box;
|
|
margin: 0 5px;
|
|
border: 1px solid dimgray;
|
|
display: grid;
|
|
grid-gap: 2px;
|
|
padding: 2px;
|
|
}
|
|
|
|
.opponent-status.active {
|
|
box-shadow: 0 0 10px red;
|
|
}
|
|
|
|
.opponent-status:first-child {
|
|
margin-left: 0;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
#preview {
|
|
grid-column-start: 2;
|
|
grid-row-start: 1;
|
|
height: 290px;
|
|
margin-top: 500px;
|
|
}
|
|
|
|
#preview img {
|
|
height: 290px;
|
|
width: 180px;
|
|
}
|
|
|
|
#modal {
|
|
display: none;
|
|
grid-row-start: 1;
|
|
grid-column-end: 2;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(1.0, 1.0, 1.0, 0.6);
|
|
}
|
|
|
|
.modal-content {
|
|
width: 600px;
|
|
height: 300px;
|
|
position: relative;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background-color: lightgray;
|
|
box-shadow: 0 0 10px;
|
|
border: 1px solid black;
|
|
}
|
|
|
|
.modal-content h1 {
|
|
text-align: center;
|
|
margin-top: 1px;
|
|
}
|
|
|
|
.rank {
|
|
display: flex;
|
|
border-bottom: 1px solid dimgray;
|
|
padding: 2px;
|
|
}
|
|
|
|
.rank span:first-child {
|
|
width: 120px;
|
|
}
|
|
|
|
</style>
|
|
<script src="/static/mithril.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="game"></div>
|
|
<script>
|
|
var mouseenter = function(ev) {
|
|
var img = ev.target.src;
|
|
document.querySelector("#preview img").src = img;
|
|
document.querySelector("#preview img").style.visibility = "visible";
|
|
}
|
|
|
|
var mouseleave = function(ev) {
|
|
document.querySelector("#preview img").style.visibility = "hidden";
|
|
}
|
|
|
|
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) {
|
|
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", {class: "players" }, vnode.attrs.players.map(function(p) {
|
|
return m("li", p.name);
|
|
})),
|
|
m("h4", "Messages"),
|
|
m("ul", {id: "chat"}),
|
|
m("input", {
|
|
id: "chat_input",
|
|
placeholder: "Type to chat...",
|
|
onkeyup: keyup
|
|
}),
|
|
),
|
|
m("#preview", m("img")),
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
view: function(vnode) {
|
|
return m(".supply-pile",
|
|
{
|
|
"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,
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function SupplyArea(initialVnode) {
|
|
return {
|
|
view: function(vnode) {
|
|
return m(".supply-area",
|
|
m("h3", "Supply"),
|
|
vnode.attrs.supply.map(function(pile, i) {
|
|
return m(SupplyPile, {...pile, index: i})
|
|
})
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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) {
|
|
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 {
|
|
view: function(vnode) {
|
|
var c;
|
|
if (vnode.attrs.card) {
|
|
c = m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.card.toLowerCase() + ".jpg"});
|
|
}
|
|
return m(".discard-pile",
|
|
{
|
|
ondragover: dragover,
|
|
ondrop: drop,
|
|
},
|
|
c)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
view: function(vnode) {
|
|
return m(".draw-pile",
|
|
{
|
|
draggable: vnode.attrs.draggable ? true : false,
|
|
ondragstart: dragStart,
|
|
ondragover: dragover,
|
|
ondrop: drop,
|
|
"data-count": vnode.attrs.count,
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
view: function(vnode) {
|
|
return m(".player-hand",
|
|
{
|
|
ondragover: dragover,
|
|
ondrop: drop,
|
|
},
|
|
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,
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
})
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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 {
|
|
view: function(vnode) {
|
|
var active = vnode.attrs.active ? "" : "inactive";
|
|
var cards = game_state.players[game_state.active_player].played_cards || [];
|
|
return m(".inplay-area",
|
|
{
|
|
class: active,
|
|
ondragover: dragover,
|
|
ondrop: drop,
|
|
},
|
|
m("h3", "In-Play"),
|
|
m(".turn-status",
|
|
"Actions: " + vnode.attrs.actions + " | Buys: " + vnode.attrs.buys + " | ",
|
|
m("img", {
|
|
src: "/static/images/64px-Coin.png",
|
|
style: "height: 16px; position: relative; top: 2px;",
|
|
}),
|
|
": " + vnode.attrs.coin
|
|
),
|
|
cards.map(function(card) {
|
|
return m("img", {
|
|
class: "card",
|
|
src: "/static/images/cards/" + card.toLowerCase() + ".jpg",
|
|
draggable: true,
|
|
//ondragstart: dragStart,
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
})
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function PlayerArea(initialVnode) {
|
|
var end_turn_click = function(e) {
|
|
var msg = { type: "EndTurn" };
|
|
webSocket.send(JSON.stringify(msg));
|
|
}
|
|
|
|
return {
|
|
view: function(vnode) {
|
|
var local_player = vnode.attrs.players[my_player_id];
|
|
return m(".player-area",
|
|
m(DrawPile, {count: local_player.draw_pile_count, draggable: true}),
|
|
m(PlayerHand, vnode.attrs.player),
|
|
m(DiscardPile, {card: local_player.discard_pile}),
|
|
m(".buttons",
|
|
m("button", {onclick: end_turn_click}, "Pass turn")
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function OpponentStatus(initialVnode) {
|
|
return {
|
|
view: function(vnode) {
|
|
var active = vnode.attrs.id == game_state.active_player ? "active" : "";
|
|
return m(".opponent-status", {class: active },
|
|
m(".name", vnode.attrs.name),
|
|
m(DiscardPile, {card: vnode.attrs.discard_pile}),
|
|
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.players
|
|
.filter((p, i) => i != my_player_id)
|
|
.map(function(o) {
|
|
return m(OpponentStatus, o)
|
|
}),
|
|
m(TrashPile),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function GameScreen(initialVnode) {
|
|
return {
|
|
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";
|
|
return m(".game-screen", {class: cls},
|
|
m(OpponentArea, vnode.attrs),
|
|
m(InPlayArea, opponent_state),
|
|
m(SupplyArea, vnode.attrs),
|
|
m(InPlayArea, player_state),
|
|
m(PlayerArea, vnode.attrs)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function SetupScreen(initialVnode) {
|
|
var start_click = function(e) {
|
|
let msg = { type: "StartGame" };
|
|
initialVnode.attrs.socket.send(JSON.stringify(msg));
|
|
}
|
|
|
|
|
|
return {
|
|
view: function(vnode) {
|
|
var cls = vnode.attrs.active ? "" : "hidden";
|
|
return m(".setup-screen", {class: cls},
|
|
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",
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
})
|
|
),
|
|
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",
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
})
|
|
),
|
|
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",
|
|
onmouseenter: mouseenter,
|
|
onmouseleave: mouseleave,
|
|
})
|
|
})
|
|
),
|
|
m("button", {onclick: start_click}, "Start Game")
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function EndScreen(initialVnode) {
|
|
return {
|
|
view: function(vnode) {
|
|
return m(".modal-content",
|
|
m("h1", "Score"),
|
|
gameover_state.map(function(rank) {
|
|
return m(".rank",
|
|
m("span", game_state.players[rank[0]].name),
|
|
m("span", rank[1])
|
|
)
|
|
})
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
var game_state;
|
|
var setup_state;
|
|
var my_player_id = 0;
|
|
var webSocket;
|
|
var gameover_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";
|
|
}
|
|
webSocket = new WebSocket(url);
|
|
|
|
setup_state = {
|
|
starting_deck: [],
|
|
basic_cards: ["Copper", "Silver", "Gold", "Estate", "Duchy", "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: [],
|
|
turn_state: {
|
|
actions: 1,
|
|
buys: 1,
|
|
coin: 0,
|
|
},
|
|
player: {
|
|
hand: []
|
|
},
|
|
players: [
|
|
{
|
|
name: "You",
|
|
draw_pile_count: 10,
|
|
hand_count: 3
|
|
}
|
|
],
|
|
active_player: 0
|
|
}
|
|
|
|
var chat_state = {
|
|
players: [],
|
|
socket: webSocket
|
|
}
|
|
|
|
var handle_game_state = function(state) {
|
|
game_state = {
|
|
...game_state,
|
|
...state,
|
|
};
|
|
chat_state.players = state.players;
|
|
|
|
if (state.state == "Setup") {
|
|
setup_state.active = true;
|
|
game_state.active = false;
|
|
} else {
|
|
setup_state.active = false;
|
|
game_state.active = true;
|
|
}
|
|
}
|
|
|
|
var handle_gameover = function(msg) {
|
|
gameover_state = msg.score;
|
|
var modal = document.querySelector("#modal");
|
|
modal.style.display = "block";
|
|
m.mount(modal, EndScreen);
|
|
}
|
|
|
|
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 == "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") {
|
|
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.type == "GameState") {
|
|
handle_game_state(msg);
|
|
} else if (msg.type == "GameSetup") {
|
|
handle_setup(msg.setup);
|
|
} else if (msg.type == "GameOver") {
|
|
handle_gameover(msg);
|
|
} else if (msg.type == "PlayerHand") {
|
|
game_state.player.hand = msg.hand;
|
|
} else if (msg.type == "PlayerId") {
|
|
my_player_id = msg.id;
|
|
} else {
|
|
console.log("event?");
|
|
console.log(event.data);
|
|
}
|
|
|
|
m.redraw();
|
|
}
|
|
|
|
return {
|
|
view: function(vnode) {
|
|
return [
|
|
m(SetupScreen, setup_state),
|
|
m(GameScreen, game_state),
|
|
m("#modal"),
|
|
m(Chat, chat_state)
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
m.mount(document.getElementById("game"), App)
|
|
</script>
|
|
</body>
|
|
</html>
|