Add basic player actions
This commit is contained in:
393
static/game.html
393
static/game.html
@@ -6,6 +6,12 @@
|
||||
<title>DnD</title>
|
||||
<link rel="stylesheet" href="/static/main.css">
|
||||
<style type="text/css">
|
||||
#game {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pile-counter {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
@@ -42,14 +48,16 @@
|
||||
width: 90px;
|
||||
margin: 1px;
|
||||
height: 144px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.player-hand {
|
||||
border: 1px solid red;
|
||||
height: 100%;
|
||||
height: 160px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
top: 0;
|
||||
overflow-x: scroll;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.player-hand img.card:hover {
|
||||
@@ -59,16 +67,20 @@
|
||||
.chat-window {
|
||||
border: 1px solid black;
|
||||
width: 300px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
grid-column-start: 2;
|
||||
}
|
||||
|
||||
#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;
|
||||
padding-right: 300px;
|
||||
background-color: dimgray;
|
||||
box-sizing: border-box;
|
||||
margin: 5px 0;
|
||||
@@ -87,10 +99,54 @@
|
||||
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 {
|
||||
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 {
|
||||
@@ -99,6 +155,11 @@
|
||||
height: 145px;
|
||||
}
|
||||
|
||||
.opponent-hand img.card {
|
||||
margin-top: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.setup-screen {
|
||||
}
|
||||
|
||||
@@ -112,6 +173,7 @@
|
||||
height: 144px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.discard-pile img.card {
|
||||
@@ -146,6 +208,7 @@
|
||||
height: 0;
|
||||
transition: height 0.4s ease;
|
||||
overflow: hidden;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.turn-status {
|
||||
@@ -169,6 +232,7 @@
|
||||
|
||||
.opponent-area {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.opponent-status {
|
||||
@@ -176,7 +240,7 @@
|
||||
grid-template-columns: auto auto auto;
|
||||
box-sizing: border-box;
|
||||
margin: 0 5px;
|
||||
border: 1px solid saddlebrown;
|
||||
border: 1px solid dimgray;
|
||||
display: grid;
|
||||
grid-gap: 2px;
|
||||
padding: 2px;
|
||||
@@ -186,6 +250,10 @@
|
||||
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;
|
||||
@@ -218,6 +286,29 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="/static/mithril.js"></script>
|
||||
</head>
|
||||
@@ -225,6 +316,27 @@
|
||||
<body>
|
||||
<div id="game"></div>
|
||||
<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) {
|
||||
var keyup = function(e) {
|
||||
if (e.key == "Enter" && e.srcElement.value.length > 0) {
|
||||
@@ -254,11 +366,31 @@
|
||||
}
|
||||
|
||||
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(".card",
|
||||
m("img", {class: "card", src: "/static/images/cards/" + vnode.attrs.name.toLowerCase() + ".jpg"}),
|
||||
m("span", {class: "pile-counter" }, vnode.attrs.count)
|
||||
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,
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -270,44 +402,146 @@
|
||||
view: function(vnode) {
|
||||
return m(".supply-area",
|
||||
m("h3", "Supply"),
|
||||
vnode.attrs.supply.map(function(pile) {
|
||||
return m(SupplyPile, pile)
|
||||
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", c)
|
||||
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",
|
||||
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) {
|
||||
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", "Player Hand",
|
||||
vnode.attrs.hand.map(function(card) {
|
||||
return m("img", {class: "card", src: "/static/images/cards/" + card.toLowerCase() + ".jpg"})
|
||||
return m(".player-hand",
|
||||
{
|
||||
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) {
|
||||
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";
|
||||
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",
|
||||
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
|
||||
)
|
||||
),
|
||||
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) {
|
||||
var local_player = vnode.attrs.players[my_player_id];
|
||||
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(DiscardPile, {card: local_player.discard_pile}),
|
||||
m(".buttons",
|
||||
@@ -384,7 +646,8 @@
|
||||
.filter((p, i) => i != my_player_id)
|
||||
.map(function(o) {
|
||||
return m(OpponentStatus, o)
|
||||
})
|
||||
}),
|
||||
m(TrashPile),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -393,12 +656,21 @@
|
||||
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, vnode.attrs.opponent_turn_state),
|
||||
m(InPlayArea, opponent_state),
|
||||
m(SupplyArea, vnode.attrs),
|
||||
m(InPlayArea, vnode.attrs.player_turn_state),
|
||||
m(InPlayArea, player_state),
|
||||
m(PlayerArea, vnode.attrs)
|
||||
)
|
||||
}
|
||||
@@ -466,61 +738,19 @@
|
||||
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: {
|
||||
game_state = {
|
||||
supply: [],
|
||||
turn_state: {
|
||||
actions: 1,
|
||||
buys: 1,
|
||||
coin: 0,
|
||||
active: false
|
||||
},
|
||||
player_turn_state: {
|
||||
actions: 1,
|
||||
buys: 1,
|
||||
coin: 0,
|
||||
active: true
|
||||
},
|
||||
player: {
|
||||
hand: []
|
||||
},
|
||||
players: [
|
||||
{
|
||||
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",
|
||||
name: "You",
|
||||
draw_pile_count: 10,
|
||||
hand_count: 3
|
||||
}
|
||||
@@ -538,8 +768,6 @@
|
||||
...game_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;
|
||||
|
||||
if (state.state == "Setup") {
|
||||
@@ -565,19 +793,32 @@
|
||||
let newmsg = document.createElement("li");
|
||||
newmsg.innerHTML = msg.sender + ": " + msg.message;
|
||||
chatDiv.append(newmsg);
|
||||
//newmsg.scrollIntoView();
|
||||
} else if (msg.type == "PlayerJoined") {
|
||||
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();
|
||||
newmsg.scrollIntoView();
|
||||
} else if (msg.type == "GameState") {
|
||||
handle_game_state(msg);
|
||||
} else if (msg.type == "GameSetup") {
|
||||
handle_setup(msg.setup);
|
||||
} 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);
|
||||
|
Reference in New Issue
Block a user