Lots of fixes. Road mapping, longest army, monopoly, year of plenty (sort of), road building (sort of), etc.
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
433eff8473
commit
2152f6b93a
@ -2,9 +2,9 @@ import React from "react";
|
|||||||
import "./Resource.css";
|
import "./Resource.css";
|
||||||
import { assetsPath } from './Common.js';
|
import { assetsPath } from './Common.js';
|
||||||
|
|
||||||
const Resource = ({ type, disabled, count }) => {
|
const Resource = ({ type, select, disabled, count }) => {
|
||||||
const array = new Array(Number(count ? count : 0));
|
const array = new Array(Number(count ? count : 0));
|
||||||
const select = (event) => {
|
const click = select ? select : (event) => {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
event.target.classList.toggle('Selected');
|
event.target.classList.toggle('Selected');
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ const Resource = ({ type, disabled, count }) => {
|
|||||||
{ React.Children.map(array, i => (
|
{ React.Children.map(array, i => (
|
||||||
<div className="Resource"
|
<div className="Resource"
|
||||||
data-type={type}
|
data-type={type}
|
||||||
onClick={select}
|
onClick={click}
|
||||||
style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}>
|
style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}>
|
||||||
</div>
|
</div>
|
||||||
)) }
|
)) }
|
||||||
|
@ -16,6 +16,7 @@ import Dice from './Dice.js';
|
|||||||
import Resource from './Resource.js';
|
import Resource from './Resource.js';
|
||||||
import ViewCard from './ViewCard.js';
|
import ViewCard from './ViewCard.js';
|
||||||
import Winner from './Winner.js';
|
import Winner from './Winner.js';
|
||||||
|
import ChooseCard from './ChooseCard.js';
|
||||||
|
|
||||||
/* Start of withRouter polyfill */
|
/* Start of withRouter polyfill */
|
||||||
// https://reactrouter.com/docs/en/v6/faq#what-happened-to-withrouter-i-need-it
|
// https://reactrouter.com/docs/en/v6/faq#what-happened-to-withrouter-i-need-it
|
||||||
@ -549,6 +550,7 @@ class Table extends React.Component {
|
|||||||
this.buildClicked = this.buildClicked.bind(this);
|
this.buildClicked = this.buildClicked.bind(this);
|
||||||
this.closeCard = this.closeCard.bind(this);
|
this.closeCard = this.closeCard.bind(this);
|
||||||
this.playCard = this.playCard.bind(this);
|
this.playCard = this.playCard.bind(this);
|
||||||
|
this.selectResource = this.selectResource.bind(this);
|
||||||
|
|
||||||
this.mouse = { x: 0, y: 0 };
|
this.mouse = { x: 0, y: 0 };
|
||||||
this.radius = 0.317;
|
this.radius = 0.317;
|
||||||
@ -615,6 +617,10 @@ class Table extends React.Component {
|
|||||||
return this.sendAction('chat', undefined, {message: message});
|
return this.sendAction('chat', undefined, {message: message});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectResource(card) {
|
||||||
|
return this.sendAction('select-resource', card);
|
||||||
|
}
|
||||||
|
|
||||||
playCard(card) {
|
playCard(card) {
|
||||||
this.setState({ cardActive: undefined });
|
this.setState({ cardActive: undefined });
|
||||||
return this.sendAction('play-card', undefined, card);
|
return this.sendAction('play-card', undefined, card);
|
||||||
@ -1005,7 +1011,9 @@ class Table extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const game = this.state.game,
|
const game = this.state.game,
|
||||||
player = game ? game.player : undefined
|
player = game ? game.player : undefined,
|
||||||
|
isTurn = (game && game.turn && game.turn.color === game.color) ? true : false;
|
||||||
|
|
||||||
let color;
|
let color;
|
||||||
switch (game ? game.color : undefined) {
|
switch (game ? game.color : undefined) {
|
||||||
case "O": color = "orange"; break;
|
case "O": color = "orange"; break;
|
||||||
@ -1100,14 +1108,21 @@ class Table extends React.Component {
|
|||||||
<Trade table={this}/>
|
<Trade table={this}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ game
|
||||||
|
&& isTurn
|
||||||
|
&& game.turn.actions
|
||||||
|
&& game.turn.actions.indexOf('select-resource') !== -1 &&
|
||||||
|
<ChooseCard table={this} type={game.turn.active}/>
|
||||||
|
}
|
||||||
|
|
||||||
{ game && game.state === 'normal' &&
|
{ game && game.state === 'normal' &&
|
||||||
game.turn &&
|
game.turn &&
|
||||||
game.turn.color === game.color &&
|
isTurn &&
|
||||||
game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1 &&
|
game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1 &&
|
||||||
<SelectPlayer table={this} players={game.turn.limits.players}/>
|
<SelectPlayer table={this} players={game.turn.limits.players}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{ game && game.turn && game.turn.color !== game.color &&
|
{ game && game.turn && !isTurn &&
|
||||||
(game.state === 'initial-placement' || game.state === 'normal') &&
|
(game.state === 'initial-placement' || game.state === 'normal') &&
|
||||||
(!game.player || !game.player.mustDiscard) && <WaitingForPlayer table={this}/>
|
(!game.player || !game.player.mustDiscard) && <WaitingForPlayer table={this}/>
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,20 @@ const ViewCard = ({table, card}) => {
|
|||||||
knight or one progress card.</p></>,
|
knight or one progress card.</p></>,
|
||||||
vp: <><b>1</b> victory point.
|
vp: <><b>1</b> victory point.
|
||||||
<p>You only reveal your victory point cards when the game is over, either
|
<p>You only reveal your victory point cards when the game is over, either
|
||||||
when you or an opponent reaches <b>10+</b> victory points on their turn and declares
|
when you or an opponent
|
||||||
|
reaches <b>10+</b> victory points on their turn and declares
|
||||||
victory!</p></>,
|
victory!</p></>,
|
||||||
|
'progress-road-1': <>
|
||||||
|
<p>Play <b>2</b> new roads as if you had just built them.</p>
|
||||||
|
<p>This is still limited by the number of roads you have (a maximum of 15.)</p>
|
||||||
|
<p><b>NOTE:</b> This card is not yet implemented. The server will give you <b>2</b> wood
|
||||||
|
and <b>2</b> brick and we trust you will use them to build <b>2</b> roads.</p>
|
||||||
|
<p>If
|
||||||
|
you do not have enough roads remaining, you may end up with extra resources...
|
||||||
|
but the game is in beta, so... be happy :)
|
||||||
|
</p>
|
||||||
|
<p>As an FYI, you currently have {15 - table.game.player.roads} roads remaining.</p>
|
||||||
|
</>,
|
||||||
'progress-road-2': <>
|
'progress-road-2': <>
|
||||||
<p>Play <b>2</b> new roads as if you had just built them.</p>
|
<p>Play <b>2</b> new roads as if you had just built them.</p>
|
||||||
<p>This is still limited by the number of roads you have (a maximum of 15.)</p>
|
<p>This is still limited by the number of roads you have (a maximum of 15.)</p>
|
||||||
@ -46,7 +58,17 @@ const ViewCard = ({table, card}) => {
|
|||||||
but the game is in beta, so... be happy :)
|
but the game is in beta, so... be happy :)
|
||||||
</p>
|
</p>
|
||||||
<p>As an FYI, you currently have {15 - table.game.player.roads} roads remaining.</p>
|
<p>As an FYI, you currently have {15 - table.game.player.roads} roads remaining.</p>
|
||||||
</>
|
</>,
|
||||||
|
'progress-monopoly': <>
|
||||||
|
When you play this card, you will select <b>1</b> type of resource.
|
||||||
|
All other players must give you all their resource cards of that type.
|
||||||
|
</>,
|
||||||
|
'progress-year-of-plenty': <>
|
||||||
|
Take any <b>2</b> resources from the bank. Add them to your hand. They can be
|
||||||
|
<b>2</b> of the same resource or <b>2</b> different resources.
|
||||||
|
<p><b>Unfortunately</b> the current implementation only lets you pick a single
|
||||||
|
resource and you will then get <b>2</b> of those.</p>
|
||||||
|
</>
|
||||||
};
|
};
|
||||||
|
|
||||||
let description;
|
let description;
|
||||||
@ -57,7 +79,7 @@ const ViewCard = ({table, card}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (description === undefined) {
|
if (description === undefined) {
|
||||||
console.log('No description for ', card);
|
console.log(`No description for ${card.type}-${card.card}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let canPlay = false;
|
let canPlay = false;
|
||||||
@ -79,11 +101,15 @@ const ViewCard = ({table, card}) => {
|
|||||||
}
|
}
|
||||||
if (canPlay) {
|
if (canPlay) {
|
||||||
canPlay = table.game.player.playedCard !== table.game.turns;
|
canPlay = table.game.player.playedCard !== table.game.turns;
|
||||||
|
if (!canPlay) {
|
||||||
|
description = <>{description}<p>You have already played a development card this turn.</p></>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.played) {
|
if (card.played) {
|
||||||
description = <>{description}<p>You have already played this card.</p></>;
|
description = <>{description}<p>You have already played this card.</p></>;
|
||||||
|
canPlay = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -562,10 +562,12 @@ const adminActions = (game, action, value) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "give":
|
case "give":
|
||||||
parts = value.match(/^([^-]+)-([0-9]+)$/);
|
parts = value.match(/^([^-]+)-(.*)$/);
|
||||||
if (!parts) {
|
if (!parts) {
|
||||||
return `Unable to parse give request.`;
|
return `Unable to parse give request.`;
|
||||||
}
|
}
|
||||||
|
const type = parts[1], card = parts[2];
|
||||||
|
|
||||||
for (let id in game.sessions) {
|
for (let id in game.sessions) {
|
||||||
if (game.sessions[id].name === game.turn.name) {
|
if (game.sessions[id].name === game.turn.name) {
|
||||||
session = game.sessions[id];
|
session = game.sessions[id];
|
||||||
@ -574,13 +576,31 @@ const adminActions = (game, action, value) => {
|
|||||||
if (!session) {
|
if (!session) {
|
||||||
return `Unable to determine current player turn to give resources.`;
|
return `Unable to determine current player turn to give resources.`;
|
||||||
}
|
}
|
||||||
if (!(parts[1] in session.player)) {
|
if (type in session.player) {
|
||||||
return `Invalid resource request.`;
|
const count = parseInt(card);
|
||||||
|
session.player[card] += count;
|
||||||
|
addChatMessage(game, null, `Admin gave ${count} ${type} to ${game.turn.name}.`);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
session.player[parts[1]] += parseInt(parts[2]);
|
|
||||||
addChatMessage(game, null, `Admin gave ${parseInt(parts[2])} ${parts[1]} to ${game.turn.name}.`);
|
const index = game.developmentCards.findIndex(item =>
|
||||||
|
item.card === card && item.type === type);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
console.log({ card, type}, game.developmentCards);
|
||||||
|
return `Unable to find ${type}-${card} in the current deck of development cards.`;
|
||||||
|
}
|
||||||
|
let tmp = game.developmentCards.splice(index, 1)[0];
|
||||||
|
tmp.turn = game.turns ? game.turns - 1 : 0;
|
||||||
|
session.player.development.push(tmp);
|
||||||
|
addChatMessage(game, null, `Admin gave a ${card}-${type} to ${game.turn.name}.`);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "cards":
|
||||||
|
let results = game.developmentCards.map(card => `${card.type}-${card.card}`)
|
||||||
|
.join(', ');
|
||||||
|
return results;
|
||||||
|
|
||||||
case "roll":
|
case "roll":
|
||||||
parts = value.match(/^([1-6])(-([1-6]))?$/);
|
parts = value.match(/^([1-6])(-([1-6]))?$/);
|
||||||
if (!parts) {
|
if (!parts) {
|
||||||
@ -1036,7 +1056,7 @@ const calculateRoadLengths = (game, session) => {
|
|||||||
let checkForTies = false;
|
let checkForTies = false;
|
||||||
|
|
||||||
console.log(currentLongest, currentLength);
|
console.log(currentLongest, currentLength);
|
||||||
|
|
||||||
if (currentLongest && game.players[currentLongest].longestRoad < currentLength) {
|
if (currentLongest && game.players[currentLongest].longestRoad < currentLength) {
|
||||||
addChatMessage(game, session, `${playerNameFromColor(game, currentLongest)} had their longest road split!`);
|
addChatMessage(game, session, `${playerNameFromColor(game, currentLongest)} had their longest road split!`);
|
||||||
checkForTies = true;
|
checkForTies = true;
|
||||||
@ -1187,7 +1207,15 @@ const isCompatibleOffer = (player, offer) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(player.gets, player.gives, offer);
|
console.log({
|
||||||
|
player: getPlayerName(player),
|
||||||
|
gets: player.gets,
|
||||||
|
gives: player.gives
|
||||||
|
}, {
|
||||||
|
name: offer.name,
|
||||||
|
gets: offer.gets,
|
||||||
|
gives: offer.gives
|
||||||
|
});
|
||||||
|
|
||||||
player.gets.forEach(get => {
|
player.gets.forEach(get => {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
@ -1242,6 +1270,17 @@ const isSameOffer = (player, offer) => {
|
|||||||
|
|
||||||
const checkOffer = (player, offer) => {
|
const checkOffer = (player, offer) => {
|
||||||
let error = undefined;
|
let error = undefined;
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
player: getPlayerName(player),
|
||||||
|
gets: player.gets,
|
||||||
|
gives: player.gives
|
||||||
|
}, {
|
||||||
|
name: offer.name,
|
||||||
|
gets: offer.gets,
|
||||||
|
gives: offer.gives
|
||||||
|
});
|
||||||
|
|
||||||
offer.gives.forEach(give => {
|
offer.gives.forEach(give => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return;
|
return;
|
||||||
@ -1397,7 +1436,6 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
|
|
||||||
/* Any player can reject an offer */
|
/* Any player can reject an offer */
|
||||||
if (value === 'reject') {
|
if (value === 'reject') {
|
||||||
const offer = req.body;
|
|
||||||
session.player.offerRejected = true;
|
session.player.offerRejected = true;
|
||||||
addChatMessage(game, session, `${session.name} rejected ${game.turn.name}'s offer.`);
|
addChatMessage(game, session, `${session.name} rejected ${game.turn.name}'s offer.`);
|
||||||
break;
|
break;
|
||||||
@ -1469,7 +1507,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addChatMessage(game, session, `${session.name} has accepted a trade ` +
|
addChatMessage(game, session, `${session.name} has accepted a trade ` +
|
||||||
`offer for ${offerToString(session.player)} ` +
|
`offer to give ${offerToString(session.player)} ` +
|
||||||
`from ${(offer.name === 'The bank') ? 'the bank' : offer.name}.`);
|
`from ${(offer.name === 'The bank') ? 'the bank' : offer.name}.`);
|
||||||
|
|
||||||
delete game.turn.offer;
|
delete game.turn.offer;
|
||||||
@ -1709,17 +1747,35 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.type === 'progress' && card.card === 'road-2') {
|
if (card.type === 'progress') {
|
||||||
addChatMessage(game, session, `${session.name} played a Road Building card. The server is giving them 2 brick and 2 wood to build those roads!`);
|
switch (card.card) {
|
||||||
player.brick += 2;
|
case 'road-1':
|
||||||
player.wood += 2;
|
case 'road-2':
|
||||||
|
addChatMessage(game, session, `${session.name} played a Road Building card. The server is giving them 2 brick and 2 wood to build those roads!`);
|
||||||
|
player.brick += 2;
|
||||||
|
player.wood += 2;
|
||||||
|
break;
|
||||||
|
case 'monopoly':
|
||||||
|
game.turn.actions = [ 'select-resource' ];
|
||||||
|
game.turn.active = 'monopoly';
|
||||||
|
addChatMessage(game, session, `${session.name} played the Monopoly card.`);
|
||||||
|
break;
|
||||||
|
case 'year-of-plenty':
|
||||||
|
game.turn.actions = [ 'select-resource' ];
|
||||||
|
game.turn.active = 'year-of-plenty';
|
||||||
|
addChatMessage(game, session, `${session.name} played the Year of Plenty card.`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
addChatMessage(game, session, `Oh no! ${card.card} isn't impmented yet!`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
card.played = true;
|
card.played = true;
|
||||||
player.playedCard = game.turns;
|
player.playedCard = game.turns;
|
||||||
addChatMessage(game, session, `${session.name} played a ${card.type}-${card.card} development card.`);
|
|
||||||
|
|
||||||
if (card.type === 'army') {
|
if (card.type === 'army') {
|
||||||
player.army++;
|
player.army++;
|
||||||
|
addChatMessage(game, session, `${session.name} played a Kaniget!`);
|
||||||
|
|
||||||
if (player.army > 2 &&
|
if (player.army > 2 &&
|
||||||
(!game.largestArmy || game.players[game.largestArmy].army < player.army)) {
|
(!game.largestArmy || game.players[game.largestArmy].army < player.army)) {
|
||||||
@ -1736,6 +1792,72 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'select-resource':
|
||||||
|
if (!game || !game.turn || !game.turn.actions ||
|
||||||
|
game.turn.actions.indexOf('select-resource') === -1) {
|
||||||
|
error = `Please, let's not cheat. Ok?`;
|
||||||
|
console.log(game);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session.color !== game.turn.color) {
|
||||||
|
error = `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = value.trim();
|
||||||
|
switch (type) {
|
||||||
|
case 'wheat':
|
||||||
|
case 'brick':
|
||||||
|
case 'sheep':
|
||||||
|
case 'stone':
|
||||||
|
case 'wood':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = `That is not a valid resource type!`;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
if (error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
addChatMessage(game, session, `${session.name} has chosen ${type}!`);
|
||||||
|
|
||||||
|
switch (game.turn.active) {
|
||||||
|
case 'monopoly':
|
||||||
|
const gave = [];
|
||||||
|
let total = 0;
|
||||||
|
for (let color in game.players) {
|
||||||
|
const player = game.players[color];
|
||||||
|
if (player.status === 'Not active') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (color === session.color) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (player[type]) {
|
||||||
|
gave.push(`${playerNameFromColor(game, color)} gave ${player[type]} ${type}`);
|
||||||
|
session.player[type] += player[type];
|
||||||
|
total += player[type];
|
||||||
|
player[type] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gave.length) {
|
||||||
|
addChatMessage(game, session, `Players ${gave.join(', ')}. In total, ${session.name} received ${total} ${type}.`);
|
||||||
|
} else {
|
||||||
|
addChatMessage(game, session, 'No players had that resource. Wa-waaaa.');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'year-of-plenty':
|
||||||
|
session.player[type] += 2;
|
||||||
|
addChatMessage(game, session, `${session.name} received 2 ${type} from the bank.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete game.turn.active;
|
||||||
|
game.turn.actions = [];
|
||||||
|
break;
|
||||||
|
|
||||||
case 'buy-settlement':
|
case 'buy-settlement':
|
||||||
if (game.state !== 'normal') {
|
if (game.state !== 'normal') {
|
||||||
error = `You cannot purchase a settlement unless the game is active.`;
|
error = `You cannot purchase a settlement unless the game is active.`;
|
||||||
@ -2197,10 +2319,8 @@ const debugChat = (game, preamble) => {
|
|||||||
}
|
}
|
||||||
playerInventory += ` ${playerNameFromColor(game, key)} has `;
|
playerInventory += ` ${playerNameFromColor(game, key)} has `;
|
||||||
const has = [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].map(resource => {
|
const has = [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].map(resource => {
|
||||||
if (game.players[key][resource] > 0) {
|
const count = game.players[key][resource] ? game.players[key][resource] : 0;
|
||||||
return `${game.players[key][resource]} ${resource}`;
|
return `${count} ${resource}`;
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}).filter(item => item !== '').join(', ');
|
}).filter(item => item !== '').join(', ');
|
||||||
if (has) {
|
if (has) {
|
||||||
playerInventory += `${has}, `;
|
playerInventory += `${has}, `;
|
||||||
@ -2276,7 +2396,6 @@ const sendGame = async (req, res, game, error) => {
|
|||||||
lastTime = message.date;
|
lastTime = message.date;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* Calculate points and determine if there is a winner */
|
/* Calculate points and determine if there is a winner */
|
||||||
for (let key in game.players) {
|
for (let key in game.players) {
|
||||||
const player = game.players[key];
|
const player = game.players[key];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user