Dominion/static/game.html

1215 lines
36 KiB
HTML
Raw Permalink Normal View History

2020-12-28 23:30:20 +01:00
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>DnD</title>
<link rel="stylesheet" href="/static/main.css">
<style type="text/css">
2021-01-03 16:11:58 +01:00
#game {
display: grid;
grid-template-columns: auto 300px;
2021-01-03 16:11:58 +01:00
user-select: none;
position: relative;
2021-01-03 16:11:58 +01:00
}
2020-12-28 23:30:20 +01:00
.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;
2020-12-31 16:54:38 +01:00
grid-template-columns: 100px auto 90px;
grid-template-rows: auto auto;
2020-12-28 23:30:20 +01:00
width: 100%;
height: 145px;
position: relative;
box-sizing: border-box;
2020-12-31 16:54:38 +01:00
grid-gap: 10px;
}
.player-area .buttons {
grid-column-start: 3;
text-align: center;
2020-12-28 23:30:20 +01:00
}
.card {
position: relative;
width: 90px;
margin: 1px;
2020-12-31 16:54:38 +01:00
height: 144px;
2021-01-03 16:11:58 +01:00
border-radius: 6px;
2020-12-28 23:30:20 +01:00
}
2021-01-06 19:00:32 +01:00
img.card:hover {
box-shadow: 0 0 5px white;
}
2020-12-28 23:30:20 +01:00
.player-hand {
2021-01-06 19:00:32 +01:00
height: 145px;
2020-12-28 23:30:20 +01:00
margin: 0;
2021-01-06 19:00:32 +01:00
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;
2020-12-28 23:30:20 +01:00
}
2020-12-31 16:54:38 +01:00
.player-hand img.card:hover {
box-shadow: 0 0 5px blue;
2021-01-06 19:00:32 +01:00
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;
2020-12-31 16:54:38 +01:00
}
.player-hand img.card.selected {
transform: translate(0px, -40px) scale(1.1) !important;
box-shadow: 0 0 10px lime;
}
2021-01-20 20:11:11 +01:00
.supply-pile.selected {
box-shadow: 0 0 10px lime;
}
2020-12-28 23:30:20 +01:00
.chat-window {
border: 1px solid black;
width: 300px;
2021-01-03 16:11:58 +01:00
grid-column-start: 2;
grid-row-start: 1;
box-sizing: border-box;
2021-01-03 16:11:58 +01:00
}
2021-01-06 19:00:32 +01:00
.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 {
}
2021-01-03 16:11:58 +01:00
#chat li[data-repeat]::after {
content: "x" attr(data-repeat);
margin-left: 10px;
background-color: deepskyblue;
color: white;
border-radius: 4px;
2020-12-28 23:30:20 +01:00
}
.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;
}
2021-01-03 16:11:58 +01:00
.supply-pile {
width: 90px;
position: relative;
height: 145px;
border-radius: 6px;
margin: 1px;
}
.supply-pile::after {
position: absolute;
bottom: 20px;
2021-01-03 16:11:58 +01:00
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);
}
2020-12-28 23:30:20 +01:00
.draw-pile {
width: 90px;
position: relative;
height: 145px;
2021-01-03 16:11:58 +01:00
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);
2020-12-28 23:30:20 +01:00
}
.opponent-hand {
width: 90px;
position: relative;
height: 145px;
}
2021-01-03 16:11:58 +01:00
.opponent-hand img.card {
margin-top: 0;
margin-left: 0;
}
2020-12-28 23:30:20 +01:00
.setup-screen {
2020-12-31 16:54:38 +01:00
}
.hidden {
2020-12-28 23:30:20 +01:00
display: none;
}
.discard-pile {
width: 90px;
border: 1px dotted green;
2020-12-31 16:54:38 +01:00
height: 144px;
2020-12-28 23:30:20 +01:00
box-sizing: border-box;
2020-12-31 16:54:38 +01:00
position: relative;
2021-01-03 16:11:58 +01:00
border-radius: 5px;
2020-12-28 23:30:20 +01:00
}
2020-12-31 16:54:38 +01:00
.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;
}
2020-12-28 23:30:20 +01:00
.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;
}
2021-01-06 19:00:32 +01:00
.inplay-area h3 {
position: relative;
top: 50px;
left: 30px;
transform-origin: 0 0;
transform: rotate(90deg);
padding: 0;
margin: 0;
width: 100px;
}
2020-12-28 23:30:20 +01:00
.inplay-area.inactive {
height: 0;
transition: height 0.4s ease;
overflow: hidden;
2021-01-03 16:11:58 +01:00
padding-top: 0;
2020-12-28 23:30:20 +01:00
}
.inplay-area img.card {
margin-left: -55px;
2021-01-06 19:00:32 +01:00
transform: translate(85px, 10px);
}
2020-12-28 23:30:20 +01:00
.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;
2021-01-03 16:11:58 +01:00
position: relative;
2021-01-06 19:00:32 +01:00
min-height: 145px;
2020-12-28 23:30:20 +01:00
}
.opponent-status {
grid-template-rows: 30px auto;
grid-template-columns: auto auto auto;
box-sizing: border-box;
margin: 0 5px;
2021-01-03 16:11:58 +01:00
border: 1px solid dimgray;
2020-12-28 23:30:20 +01:00
display: grid;
grid-gap: 2px;
2020-12-31 16:54:38 +01:00
padding: 2px;
2020-12-28 23:30:20 +01:00
}
.opponent-status.active {
box-shadow: 0 0 10px red;
}
2021-01-03 16:11:58 +01:00
.opponent-status:first-child {
margin-left: 0;
}
2020-12-28 23:30:20 +01:00
.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;
}
2021-01-03 16:11:58 +01:00
.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;
}
.dialog {
position: absolute;
width: 500px;
border: 1px solid dimgray;
top: 50%;
z-index: 200;
background-color: rgba(1.0, 1.0, 1.0, 0.9);
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 20px black;
display: grid;
grid-template-columns: auto auto;
color: white;
visibility: hidden;
grid-template-rows: 5fr auto;
2021-01-20 20:11:11 +01:00
transition: top 0.1s ease-in;
}
.dialog.supply {
top: 80%;
}
.dialog.hand {
2021-01-22 19:21:51 +01:00
top: 20%;
}
.dialog img {
grid-row-start: 1;
grid-row-end: 3;
margin: 5px;
width: 180px;
height: 288px;
}
.dialog button {
grid-row-start: 2;
grid-column-start: 2;
margin: 10px auto;
}
2020-12-28 23:30:20 +01:00
</style>
<script src="/static/mithril.js"></script>
</head>
<body>
<div id="dialog" class="dialog">
<img class="card" style="margin: 5px; width:180px; height:288px;" src="/static/images/cards/cellar.jpg">
<p style="margin: 20px;">Choose any number of cards to discard.</p>
<button onclick="dialog_confirm()">Confirm</button>
</div>
2020-12-28 23:30:20 +01:00
<div id="game"></div>
<script>
var set_data = [
["Cellar", "Market", "Merchant", "Militia", "Mine", "Moat", "Remodel", "Smithy", "Village", "Workshop"],
["Artisan", "Bandit", "Bureaucrat", "Chapel", "Festival", "Gardens", "Sentry", "Throne Room", "Witch", "Workshop"],
];
var turnStartSound = new Audio("/static/audio/chipsStack1.ogg");
var resolve_request;
var enable_hand_selection = false;
2021-01-20 20:11:11 +01:00
var enable_supply_selection = false;
var toggle_hand_selection = function(index) {
document.querySelectorAll(".player-hand img.card")[index].classList.toggle("selected");
}
2021-01-20 20:11:11 +01:00
var toggle_supply_selection = function(index) {
2021-01-22 19:21:51 +01:00
document.querySelectorAll(".supply-area .supply-pile").forEach((c) => {
c.classList.remove("selected");
});
2021-01-20 20:11:11 +01:00
document.querySelectorAll(".supply-area .supply-pile")[index].classList.toggle("selected");
}
2021-01-27 20:02:39 +01:00
function send_command(command, data) {
var payload = data || {};
payload.type = command;
var msg = { type: "Command", "command": payload };
webSocket.send(JSON.stringify(msg));
}
function dialog_confirm(ev) {
2021-01-20 20:11:11 +01:00
if (resolve_request.request.type == "GainCard") {
var selected = document.querySelector(".supply-pile.selected");
if (selected) {
selected = parseInt(selected.dataset.index);
}
document.querySelectorAll(".supply-pile.selected").forEach((c) => {
c.classList.remove("selected");
});
var reply = { type: "SupplyCardChosen", choice: selected };
var msg = { type: "ResolveReply", reply: reply };
2021-01-27 20:02:39 +01:00
send_command("ResolveReply", msg);
2021-01-20 20:11:11 +01:00
enable_supply_selection = false;
} else {
var selected = [];
document.querySelectorAll(".player-hand img.card.selected").forEach((c) => {
selected.push(parseInt(c.dataset.index));
c.classList.remove("selected");
});
var reply = { type: "HandCardsChosen", choice: selected };
var msg = { type: "ResolveReply", reply: reply };
2021-01-27 20:02:39 +01:00
send_command("ResolveReply", msg);
2021-01-20 20:11:11 +01:00
enable_hand_selection = false;
}
document.querySelector("#dialog").style.visibility = "hidden";
2021-01-20 20:11:11 +01:00
dialog.classList.remove("hand");
dialog.classList.remove("supply");
}
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) {
2021-01-03 16:11:58 +01:00
if (data.source == "Hand" && data.dest == "InPlay") {
2021-01-27 20:02:39 +01:00
send_command("PlayCard", { index: data.index });
2021-01-03 16:11:58 +01:00
} else if (data.source == "Hand" && data.dest == "Discard") {
2021-01-27 20:02:39 +01:00
send_command("Discard", { index: data.index });
2021-01-03 16:11:58 +01:00
} else if (data.source == "Supply" && data.dest == "Discard") {
2021-01-27 20:02:39 +01:00
send_command("GainCard", { index: parseInt(data.index)});
2021-01-03 16:11:58 +01:00
} else if (data.source == "DrawPile" && data.dest == "Hand") {
2021-01-27 20:02:39 +01:00
send_command("DrawCard", null);
2021-01-03 16:11:58 +01:00
} else if (data.source == "Hand" && data.dest == "Trash") {
2021-01-27 20:02:39 +01:00
send_command("TrashHand", { index: data.index });
2021-01-03 16:11:58 +01:00
} else {
console.log("handle_dnd: unhandled data", data);
}
}
2020-12-28 23:30:20 +01:00
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",
2020-12-28 23:30:20 +01:00
m("h4", "Players"),
2021-01-06 19:00:32 +01:00
m("ul", {class: "players" }, vnode.attrs.players.map(function(p) {
2020-12-31 16:54:38 +01:00
return m("li", p.name);
})),
2020-12-28 23:30:20 +01:00
m("h4", "Messages"),
m("ul", {id: "chat"}),
m("input", {
id: "chat_input",
placeholder: "Type to chat...",
onkeyup: keyup
}),
),
m("#preview", m("img")),
]
2020-12-28 23:30:20 +01:00
}
}
}
function SupplyPile(initialVnode) {
2021-01-03 16:11:58 +01:00
var dragStart = function(ev) {
let data = {
source: "Supply",
name: ev.target.dataset.name,
index: ev.target.dataset.index,
};
ev.dataTransfer.setData("text", JSON.stringify(data));
}
2021-01-20 20:11:11 +01:00
var click = function(e) {
e.preventDefault();
if (enable_supply_selection) {
toggle_supply_selection(parseInt(e.srcElement.parentElement.dataset.index));
}
}
2021-01-09 21:23:52 +01:00
var doubleclick = function(ev) {
2021-01-27 20:02:39 +01:00
send_command("BuyCard", { index: parseInt(ev.srcElement.parentElement.dataset.index) });
2021-01-09 21:23:52 +01:00
}
return {
2020-12-28 23:30:20 +01:00
view: function(vnode) {
2021-01-03 16:11:58 +01:00
return m(".supply-pile",
{
"data-count": vnode.attrs.count,
"data-name": vnode.attrs.name,
"data-index": vnode.attrs.index,
ondragstart: dragStart,
draggable: true,
2021-01-20 20:11:11 +01:00
onclick: click,
2021-01-09 21:23:52 +01:00
ondblclick: doubleclick,
2021-01-03 16:11:58 +01:00
},
m("img", {
class: "card",
src: card_url(vnode.attrs.name),
2021-01-03 16:11:58 +01:00
draggable: false,
onmouseenter: mouseenter,
onmouseleave: mouseleave,
2021-01-03 16:11:58 +01:00
})
2020-12-28 23:30:20 +01:00
)
}
}
}
function SupplyArea(initialVnode) {
return {
view: function(vnode) {
return m(".supply-area",
m("h3", "Supply"),
2021-01-03 16:11:58 +01:00
vnode.attrs.supply.map(function(pile, i) {
return m(SupplyPile, {...pile, index: i})
2020-12-28 23:30:20 +01:00
})
)
}
}
}
2021-01-03 16:11:58 +01:00
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,
});
}
}
}
2020-12-28 23:30:20 +01:00
function DiscardPile(initialVnode) {
2021-01-03 16:11:58 +01:00
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);
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
2020-12-31 16:54:38 +01:00
var c;
if (vnode.attrs.card) {
c = m("img", {class: "card", src: card_url(vnode.attrs.card)});
2020-12-31 16:54:38 +01:00
}
2021-01-03 16:11:58 +01:00
return m(".discard-pile",
{
ondragover: dragover,
ondrop: drop,
},
c)
2020-12-28 23:30:20 +01:00
}
}
}
function DrawPile(initialVnode) {
2021-01-03 16:11:58 +01:00
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);
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
return m(".draw-pile",
2021-01-03 16:11:58 +01:00
{
draggable: vnode.attrs.draggable ? true : false,
ondragstart: dragStart,
ondragover: dragover,
ondrop: drop,
"data-count": vnode.attrs.count,
}
2020-12-28 23:30:20 +01:00
)
}
}
}
function PlayerHand(initialVnode) {
2021-01-03 16:11:58 +01:00
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);
}
var click = function(e) {
e.preventDefault();
console.log("hand card click", e.target.dataset.index);
if (enable_hand_selection) {
toggle_hand_selection(parseInt(e.target.dataset.index));
}
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
2021-01-03 16:11:58 +01:00
return m(".player-hand",
{
ondragover: dragover,
ondrop: drop,
},
vnode.attrs.hand.map(function(card, i) {
return m("img", {
class: "card",
src: card_url(card),
2021-01-03 16:11:58 +01:00
draggable: true,
ondragstart: dragStart,
"data-index": i,
2021-01-06 19:00:32 +01:00
onmouseenter: mouseenter,
onmouseleave: mouseleave,
onclick: click,
2021-01-03 16:11:58 +01:00
})
2020-12-28 23:30:20 +01:00
})
)
}
}
}
function OpponentHand(initialVnode) {
return {
view: function(vnode) {
return m(".opponent-hand",
m("img", {class: "card", src: "/static/images/cards/Card_back.jpg" }),
2020-12-28 23:30:20 +01:00
m("span", {class: "pile-counter" }, vnode.attrs.count)
)
}
}
}
function InPlayArea(initialVnode) {
2021-01-03 16:11:58 +01:00
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);
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
var active = vnode.attrs.active ? "" : "inactive";
2021-01-03 16:11:58 +01:00
var cards = game_state.players[game_state.active_player].played_cards || [];
return m(".inplay-area",
{
class: active,
ondragover: dragover,
ondrop: drop,
},
2021-01-06 19:00:32 +01:00
m("h3", "In-Play"),
2020-12-28 23:30:20 +01:00
m(".turn-status",
"Actions: " + vnode.attrs.actions + " | Buys: " + vnode.attrs.buys + " | ",
2021-01-06 19:00:32 +01:00
m("img", {
src: "/static/images/64px-Coin.png",
style: "height: 16px; position: relative; top: 2px;",
}),
2020-12-28 23:30:20 +01:00
": " + vnode.attrs.coin
2021-01-03 16:11:58 +01:00
),
cards.map(function(card) {
return m("img", {
class: "card",
src: card_url(card),
2021-01-03 16:11:58 +01:00
draggable: true,
//ondragstart: dragStart,
2021-01-06 19:00:32 +01:00
onmouseenter: mouseenter,
onmouseleave: mouseleave,
2021-01-03 16:11:58 +01:00
})
})
2020-12-28 23:30:20 +01:00
)
}
}
}
function PlayerArea(initialVnode) {
2020-12-31 16:54:38 +01:00
var end_turn_click = function(e) {
2021-01-27 20:02:39 +01:00
send_command("EndTurn", null);
2020-12-31 16:54:38 +01:00
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
2020-12-31 16:54:38 +01:00
var local_player = vnode.attrs.players[my_player_id];
2020-12-28 23:30:20 +01:00
return m(".player-area",
2021-01-03 16:11:58 +01:00
m(DrawPile, {count: local_player.draw_pile_count, draggable: true}),
2020-12-28 23:30:20 +01:00
m(PlayerHand, vnode.attrs.player),
2020-12-31 16:54:38 +01:00
m(DiscardPile, {card: local_player.discard_pile}),
m(".buttons",
m("button", {onclick: end_turn_click}, "Pass turn")
)
2020-12-28 23:30:20 +01:00
)
}
}
}
function OpponentStatus(initialVnode) {
return {
view: function(vnode) {
2020-12-31 16:54:38 +01:00
var active = vnode.attrs.id == game_state.active_player ? "active" : "";
2020-12-28 23:30:20 +01:00
return m(".opponent-status", {class: active },
m(".name", vnode.attrs.name),
2020-12-31 16:54:38 +01:00
m(DiscardPile, {card: vnode.attrs.discard_pile}),
2020-12-28 23:30:20 +01:00
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",
2020-12-31 16:54:38 +01:00
vnode.attrs.players
.filter((p, i) => i != my_player_id)
.map(function(o) {
return m(OpponentStatus, o)
2021-01-03 16:11:58 +01:00
}),
m(TrashPile),
2020-12-28 23:30:20 +01:00
)
}
}
}
function GameScreen(initialVnode) {
return {
view: function(vnode) {
2021-01-03 16:11:58 +01:00
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,
}
2020-12-31 16:54:38 +01:00
var cls = vnode.attrs.active ? "" : "hidden";
return m(".game-screen", {class: cls},
2020-12-28 23:30:20 +01:00
m(OpponentArea, vnode.attrs),
2021-01-03 16:11:58 +01:00
m(InPlayArea, opponent_state),
2020-12-28 23:30:20 +01:00
m(SupplyArea, vnode.attrs),
2021-01-03 16:11:58 +01:00
m(InPlayArea, player_state),
2020-12-28 23:30:20 +01:00
m(PlayerArea, vnode.attrs)
)
}
}
}
function card_url(name) {
return "/static/images/cards/" + name.toLowerCase().replace(" ", "-") + ".jpg";
}
2020-12-28 23:30:20 +01:00
2020-12-28 23:30:20 +01:00
function SetupScreen(initialVnode) {
var start_click = function(e) {
2021-01-27 20:02:39 +01:00
let msg = { type: "Command", command: { type: "StartGame" }};
2020-12-28 23:30:20 +01:00
initialVnode.attrs.socket.send(JSON.stringify(msg));
}
var set_changed = function(e) {
let choice = parseInt(e.srcElement.value);
for (card in set_data[choice]) {
let name = set_data[choice][card];
send_command("ChangeSupply", {index: parseInt(card), name: name });
}
}
2020-12-28 23:30:20 +01:00
return {
view: function(vnode) {
2020-12-31 16:54:38 +01:00
var cls = vnode.attrs.active ? "" : "hidden";
return m(".setup-screen", {class: cls},
2020-12-28 23:30:20 +01:00
m("h3", "Setup"),
m("h4", "Basic cards"),
m(".basic-cards",
vnode.attrs.basic_cards.map(function(card) {
return m("img", {
class: "card",
src: card_url(card),
onmouseenter: mouseenter,
onmouseleave: mouseleave,
})
2020-12-28 23:30:20 +01:00
})
),
m("h4", "Kingdom Cards"),
m("select",
{
name: "set",
onchange: set_changed,
},[
m("option", { value: 0, style: "background-image: url(/static/images/sets/Dominion.png);"}, "First Game"),
m("option", { value: 1}, "Size Distortion"),
]
),
2020-12-28 23:30:20 +01:00
m(".kingdom-cards",
vnode.attrs.kingdom_cards.map(function(card) {
return m("img", {
class: "card",
src: card_url(card),
onmouseenter: mouseenter,
onmouseleave: mouseleave,
})
2020-12-28 23:30:20 +01:00
})
),
m("h4", "Starting Deck"),
m(".start-deck",
vnode.attrs.starting_deck.map(function(card) {
return m("img", {
class: "card",
src: card_url(card),
onmouseenter: mouseenter,
onmouseleave: mouseleave,
})
2020-12-28 23:30:20 +01:00
})
),
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])
)
})
)
}
}
}
2020-12-28 23:30:20 +01:00
var game_state;
2020-12-31 16:54:38 +01:00
var setup_state;
var my_player_id = 0;
var webSocket;
var gameover_state;
2020-12-28 23:30:20 +01:00
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";
}
2020-12-31 16:54:38 +01:00
webSocket = new WebSocket(url);
2020-12-28 23:30:20 +01:00
2020-12-31 16:54:38 +01:00
setup_state = {
2020-12-28 23:30:20 +01:00
starting_deck: [],
2021-01-08 18:45:11 +01:00
basic_cards: ["Copper", "Silver", "Gold", "Estate", "Duchy", "Province", "Curse"],
kingdom_cards: [],
2020-12-28 23:30:20 +01:00
socket: webSocket
}
var handle_setup = function(data) {
setup_state.kingdom_cards = data.supply;
2020-12-28 23:30:20 +01:00
setup_state.starting_deck = data.deck;
}
2021-01-03 16:11:58 +01:00
game_state = {
supply: [],
turn_state: {
2020-12-28 23:30:20 +01:00
actions: 1,
buys: 1,
coin: 0,
},
player: {
hand: []
},
2020-12-31 16:54:38 +01:00
players: [
{
2021-01-03 16:11:58 +01:00
name: "You",
2020-12-28 23:30:20 +01:00
draw_pile_count: 10,
hand_count: 3
}
2020-12-31 16:54:38 +01:00
],
active_player: 0
2020-12-28 23:30:20 +01:00
}
var chat_state = {
2020-12-31 16:54:38 +01:00
players: [],
2020-12-28 23:30:20 +01:00
socket: webSocket
}
2020-12-31 16:54:38 +01:00
var handle_game_state = function(state) {
var last_player = game_state.active_player;
2020-12-31 16:54:38 +01:00
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_resolve_request = function(request) {
var dlg = document.querySelector("#dialog");
2021-01-20 20:11:11 +01:00
resolve_request = request;
document.querySelector("#dialog img").src = card_url(request.card);
2021-01-20 20:11:11 +01:00
if (request.request.type == "GainCard") {
let cost = request.request?.filter?.MaxCost;
if (cost) {
document.querySelector("#dialog p").innerHTML = "Choose a card to gain with max cost of " + cost + ".";
} else {
document.querySelector("#dialog p").innerHTML = "Choose a card to gain.";
}
enable_supply_selection = true;
dialog.classList.add("supply");
} else if (request.request.type == "ChooseHandCardsToDiscard") {
document.querySelector("#dialog p").innerHTML = "Choose any number of cards to discard.";
enable_hand_selection = true;
dialog.classList.add("hand");
}
dlg.style.visibility = "visible";
2020-12-31 16:54:38 +01:00
}
var handle_gameover = function(msg) {
gameover_state = msg.score;
var modal = document.querySelector("#modal");
modal.style.display = "block";
m.mount(modal, EndScreen);
}
2021-01-27 20:02:39 +01:00
var handle_notification = function(msg) {
if (msg.event.type == "CardPlayed") {
append_chat(player_name(msg.event.player) + " plays " + msg.event.name);
} else if (msg.event.type == "CardBought") {
append_chat(player_name(msg.event.player) + " buys " + msg.event.name);
} else if (msg.event.type == "CardGained") {
let card_name = game_state.supply[msg.event.index].name;
append_chat(player_name(msg.event.player) + " gains " + card_name);
} else if (msg.event.type == "CardDiscarded") {
append_chat(player_name(msg.event.player) + " discards a card.");
} else if (msg.event.type == "CardTrashed") {
append_chat(player_name(msg.event.player) + " trashes " + msg.event.name);
} else if (msg.event.type == "CardRevealed") {
append_chat(player_name(msg.event.player) + " reveals " + msg.event.name);
2021-01-27 20:02:39 +01:00
} else if (msg.event.type == "TurnStarted") {
if (msg.event.player == my_player_id) {
turnStartSound.play();
}
} else {
console.log(msg);
}
}
var player_name = function(index) {
return game_state.players[index].name;
}
var append_chat = function(text) {
let chatDiv = document.getElementById("chat");
let last_element = document.querySelector("#chat li:last-child");
if (last_element.innerText == text) {
last_element.dataset.repeat = (parseInt(last_element.dataset.repeat || 1)) + 1;
} else {
let newmsg = document.createElement("li");
newmsg.innerHTML = text;
chatDiv.append(newmsg);
newmsg.scrollIntoView();
}
}
2020-12-31 16:54:38 +01:00
2020-12-28 23:30:20 +01:00
webSocket.onopen = function(event) {
console.log("ws open");
};
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);
2021-01-03 16:11:58 +01:00
newmsg.scrollIntoView();
} else if (msg.type == "Notification") {
2021-01-27 20:02:39 +01:00
handle_notification(msg);
} else if (msg.type == "PlayerJoined") {
2020-12-28 23:30:20 +01:00
let chatDiv = document.getElementById("chat");
let newmsg = document.createElement("li");
newmsg.innerHTML = msg.player + " joined the game.";
chatDiv.append(newmsg);
2021-01-03 16:11:58 +01:00
newmsg.scrollIntoView();
2020-12-31 16:54:38 +01:00
} else if (msg.type == "GameState") {
handle_game_state(msg);
} else if (msg.type == "GameSetup") {
2020-12-28 23:30:20 +01:00
handle_setup(msg.setup);
} else if (msg.type == "GameOver") {
handle_gameover(msg);
2020-12-31 16:54:38 +01:00
} else if (msg.type == "PlayerHand") {
game_state.player.hand = msg.hand;
2021-01-03 16:11:58 +01:00
} else if (msg.type == "PlayerId") {
my_player_id = msg.id;
} else if (msg.type == "ResolveRequest") {
2021-01-20 20:11:11 +01:00
handle_resolve_request(msg);
2020-12-28 23:30:20 +01:00
} 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"),
2020-12-28 23:30:20 +01:00
m(Chat, chat_state)
]
}
}
}
m.mount(document.getElementById("game"), App)
</script>
</body>
</html>