summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrans Bongers <fransbongers@franss-mbp.home>2024-12-30 12:30:26 +0100
committerFrans Bongers <fransbongers@franss-mbp.home>2024-12-30 12:30:26 +0100
commitb952d532d4b84c136e3c3a6893a63b0ed5b5ece3 (patch)
treef7667fdb7f8e771c7b63fccc6760b5cb37a95376
parent23beb82e0344d895106351fe6d98f40c8ee50dc5 (diff)
downloadland-and-freedom-b952d532d4b84c136e3c3a6893a63b0ed5b5ece3.tar.gz
peek at fascist card event effect
-rw-r--r--data.js5
-rw-r--r--data.ts3
-rw-r--r--land-and-freedom.css17
-rw-r--r--land-and-freedom.scss17
-rw-r--r--play.html11
-rw-r--r--play.js29
-rw-r--r--play.ts34
-rw-r--r--rules.js45
-rw-r--r--rules.ts63
-rw-r--r--types.d.ts17
10 files changed, 186 insertions, 55 deletions
diff --git a/data.js b/data.js
index dde7a1e..5967124 100644
--- a/data.js
+++ b/data.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
-exports.LIBERTY_OR_COLLECTIVIZATION = exports.COMMUNIST_EXTRA_HERO_POINT = exports.ANARCHIST_EXTRA_HERO_POINT = exports.NORTHERN = exports.SOUTHERN = exports.ARAGON = exports.MADRID = exports.ORGANIZATION_MEDALLION_ID = exports.ARCHIVES_MEDALLION_ID = exports.VOLUNTEERS_MEDALLION_ID = exports.INTELLIGENCE_MEDALLION_ID = exports.PROPAGANDA_MEDALLION_ID = exports.STRATEGY_MEDALLION_ID = exports.FRONTS = exports.DEFEAT = exports.VICTORY = exports.TRASH = exports.TOWARDS_CENTER = exports.AWAY_FROM_CENTER = exports.SELF = exports.PLAYER_WITH_MOST_HERO_POINTS = exports.OTHER_PLAYERS = exports.ON = exports.OFF = exports.TEAMWORK_BONUS = exports.MORALE_BONUS = exports.FOREIGN_AID = exports.SOVIET_SUPPORT = exports.INITIATIVE_PLAYER = exports.GOVERNMENT = exports.COLLECTIVIZATION = exports.CLOSEST_TO_VICTORY = exports.CLOSEST_TO_DEFEAT = exports.LIBERTY = exports.ANY = exports.MODERATES_ID = exports.COMMUNISTS_ID = exports.ANARCHISTS_ID = exports.MODERATE = exports.COMMUNIST = exports.ANARCHIST = exports.ALL_PLAYERS = void 0;
+exports.LIBERTY_OR_COLLECTIVIZATION = exports.COMMUNIST_EXTRA_HERO_POINT = exports.ANARCHIST_EXTRA_HERO_POINT = exports.NORTHERN = exports.SOUTHERN = exports.ARAGON = exports.MADRID = exports.ORGANIZATION_MEDALLION_ID = exports.ARCHIVES_MEDALLION_ID = exports.VOLUNTEERS_MEDALLION_ID = exports.INTELLIGENCE_MEDALLION_ID = exports.PROPAGANDA_MEDALLION_ID = exports.STRATEGY_MEDALLION_ID = exports.FRONTS = exports.DEFEAT = exports.VICTORY = exports.TRASH = exports.TOWARDS_CENTER = exports.AWAY_FROM_CENTER = exports.SELF = exports.PLAYER_WITH_MOST_HERO_POINTS = exports.OTHER_PLAYERS = exports.ON = exports.OFF = exports.TEAMWORK_BONUS = exports.MORALE_BONUS = exports.FOREIGN_AID = exports.SOVIET_SUPPORT = exports.INITIATIVE_PLAYER = exports.GOVERNMENT = exports.COLLECTIVIZATION = exports.CLOSEST_TO_VICTORY = exports.CLOSEST_TO_DEFEAT = exports.LIBERTY = exports.ANY = exports.MODERATES_ID = exports.FASCIST_ID = exports.COMMUNISTS_ID = exports.ANARCHISTS_ID = exports.MODERATE = exports.COMMUNIST = exports.ANARCHIST = exports.ALL_PLAYERS = void 0;
exports.create_effect = create_effect;
const LIBERTY = 0;
exports.LIBERTY = LIBERTY;
@@ -58,6 +58,8 @@ const ANARCHISTS_ID = 'a';
exports.ANARCHISTS_ID = ANARCHISTS_ID;
const COMMUNISTS_ID = 'c';
exports.COMMUNISTS_ID = COMMUNISTS_ID;
+const FASCIST_ID = 'f';
+exports.FASCIST_ID = FASCIST_ID;
const MODERATES_ID = 'm';
exports.MODERATES_ID = MODERATES_ID;
const ANARCHIST = 'Anarchist';
@@ -682,6 +684,7 @@ const data = {
effects: [
create_effect('bonus', ANY, ON),
create_effect('track', LIBERTY, 2),
+ create_effect('function', 'card46_event3', 0),
create_effect('state', 'peek_fascist_cards', 0),
],
icons: ['teamwork_on', 'liberty', 'add_to_front'],
diff --git a/data.ts b/data.ts
index d5992a5..49bb25f 100644
--- a/data.ts
+++ b/data.ts
@@ -35,6 +35,7 @@ const TRASH = 'trash';
const ANARCHISTS_ID: FactionId = 'a';
const COMMUNISTS_ID: FactionId = 'c';
+const FASCIST_ID = 'f';
const MODERATES_ID: FactionId = 'm';
const ANARCHIST = 'Anarchist' as Player;
@@ -78,6 +79,7 @@ export {
MODERATE,
ANARCHISTS_ID,
COMMUNISTS_ID,
+ FASCIST_ID,
MODERATES_ID,
ANY,
LIBERTY,
@@ -704,6 +706,7 @@ const data: StaticData = {
effects: [
create_effect('bonus', ANY, ON),
create_effect('track', LIBERTY, 2),
+ create_effect('function', 'card46_event3', 0),
create_effect('state', 'peek_fascist_cards', 0),
],
icons: ['teamwork_on', 'liberty', 'add_to_front'],
diff --git a/land-and-freedom.css b/land-and-freedom.css
index 4ef7c15..be49582 100644
--- a/land-and-freedom.css
+++ b/land-and-freedom.css
@@ -102,6 +102,19 @@ main {
color: floralwhite;
}
+#selectable_cards {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ padding: 12px;
+ gap: 12px;
+ min-height: 260px;
+}
+
+#selectable_cards:empty {
+ display: none;
+}
+
.panel {
min-width: 1271px;
max-width: 1271px;
@@ -709,6 +722,10 @@ main {
box-shadow: 0 0 0 3px yellow;
}
+.blank_marker.action:hover {
+ opacity: 0.5;
+}
+
.standee[data-standee-id="0"] {
background-image: url("images/standees/standee_0.png");
}
diff --git a/land-and-freedom.scss b/land-and-freedom.scss
index cae059a..5c0c760 100644
--- a/land-and-freedom.scss
+++ b/land-and-freedom.scss
@@ -140,6 +140,19 @@ main {
}
}
+#selectable_cards {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ padding: 12px;
+ gap: 12px;
+ min-height: 260px;
+}
+
+#selectable_cards:empty {
+ display: none;
+}
+
.panel {
min-width: 1271px;
max-width: 1271px;
@@ -351,6 +364,10 @@ main {
box-shadow: 0 0 0 3px $selected-color;
}
+.blank_marker.action:hover {
+ opacity: 0.5;
+}
+
// .standee.action:hover {
// box-shadow: 0 0 0 2px blue;
// }
diff --git a/play.html b/play.html
index d15c6dd..1ae119c 100644
--- a/play.html
+++ b/play.html
@@ -87,8 +87,7 @@
</div>
</div>
<div id="glory"></div>
- <div id="spaces">
- </div>
+ <div id="spaces"></div>
<div id="pieces"></div>
<div id="markers"></div>
<div id="medallions"></div>
@@ -96,20 +95,20 @@
<div id="current_events"></div>
</div>
</div>
-
+ <div id="selectable_cards"></div>
<div class="panel">
<div id="hand_header" class="panel_header">Hand</div>
<div id="hand" class="panel_body"></div>
</div>
- <div class="panel">
+ <div class="panel">
<div class="panel_header" data-faction-id="a">Anarchist</div>
<div id="tableau_a" class="panel_body"></div>
</div>
- <div class="panel">
+ <div class="panel">
<div class="panel_header" data-faction-id="c">Communist</div>
<div id="tableau_c" class="panel_body"></div>
</div>
- <div class="panel">
+ <div class="panel">
<div class="panel_header" data-faction-id="m">Moderate</div>
<div id="tableau_m" class="panel_body"></div>
</div>
diff --git a/play.js b/play.js
index 00a436a..afb6b2a 100644
--- a/play.js
+++ b/play.js
@@ -55,6 +55,7 @@ const ui = {
hero_points: document.getElementById('pool_hero_points'),
},
},
+ selectable_cards: document.getElementById('selectable_cards'),
tableaus: {
a: document.getElementById('tableau_a'),
c: document.getElementById('tableau_c'),
@@ -257,6 +258,16 @@ function on_init() {
register_action(ui.fronts[front_id].front, 'front', front_id);
});
}
+function place_cards(e, cards) {
+ e.replaceChildren();
+ for (let c of cards) {
+ ui.cards[c].classList.remove('selected');
+ e.appendChild(ui.cards[c]);
+ if (view.selected_cards.includes(c)) {
+ ui.cards[c].classList.add('selected');
+ }
+ }
+}
function on_update() {
console.log('on_update', view);
on_init();
@@ -282,13 +293,10 @@ function on_update() {
for (let bonus_id of Object.keys(view.bonuses)) {
ui.bonuses[bonus_id].setAttribute('data-bonus-on', view.bonuses[bonus_id] + 0);
}
- ui.hand.replaceChildren();
- for (let c of view.hand) {
- ui.cards[c].classList.remove('selected');
- ui.hand.appendChild(ui.cards[c]);
- if (view.selected_cards.includes(c)) {
- ui.cards[c].classList.add('selected');
- }
+ place_cards(ui.hand, view.hand);
+ place_cards(ui.selectable_cards, view.selectable_cards);
+ for (let faction_id of FACTIONS) {
+ place_cards(ui.tableaus[faction_id], view.tableaus[faction_id]);
}
for (let i = 0; i < view.tracks.length; ++i) {
ui.standees[i].style.left = LAYOUT_TRACKS[i][view.tracks[i]][0] + 'px';
@@ -319,13 +327,6 @@ function on_update() {
}
ui.roles[view.initiative].medallions.appendChild(ui.initiative_token);
ui.initiative_token.setAttribute('data-year', view.year);
- for (let faction_id of FACTIONS) {
- ui.tableaus[faction_id].replaceChildren();
- for (let c of view.tableaus[faction_id]) {
- ui.cards[c].classList.remove('selected');
- ui.tableaus[faction_id].appendChild(ui.cards[c]);
- }
- }
if (view.played_card === null) {
ui.turn_info.style.display = 'none';
}
diff --git a/play.ts b/play.ts
index 61e6206..c040745 100644
--- a/play.ts
+++ b/play.ts
@@ -1,6 +1,6 @@
'use strict';
-import { StaticData, View } from './types';
+import { CardId, StaticData, View } from './types';
declare function action_button(action: string, text: string): void;
// declare function register_action(element: HTMLElement, type: string, s: number): void;
@@ -69,6 +69,7 @@ const ui = {
hero_points: document.getElementById('pool_hero_points'),
},
},
+ selectable_cards: document.getElementById('selectable_cards'),
tableaus: {
a: document.getElementById('tableau_a'),
c: document.getElementById('tableau_c'),
@@ -329,6 +330,17 @@ function on_init() {
});
}
+function place_cards(e: HTMLElement, cards: CardId[]) {
+ e.replaceChildren();
+ for (let c of cards) {
+ ui.cards[c].classList.remove('selected');
+ e.appendChild(ui.cards[c]);
+ if (view.selected_cards.includes(c)) {
+ ui.cards[c].classList.add('selected');
+ }
+ }
+}
+
// @ts-ignore
function on_update() {
console.log('on_update', view);
@@ -371,13 +383,11 @@ function on_update() {
);
}
- ui.hand.replaceChildren();
- for (let c of view.hand) {
- ui.cards[c].classList.remove('selected');
- ui.hand.appendChild(ui.cards[c]);
- if (view.selected_cards.includes(c)) {
- ui.cards[c].classList.add('selected');
- }
+ place_cards(ui.hand, view.hand);
+ place_cards(ui.selectable_cards, view.selectable_cards);
+
+ for (let faction_id of FACTIONS) {
+ place_cards(ui.tableaus[faction_id], view.tableaus[faction_id]);
}
for (let i = 0; i < view.tracks.length; ++i) {
@@ -418,13 +428,7 @@ function on_update() {
ui.roles[view.initiative].medallions.appendChild(ui.initiative_token);
ui.initiative_token.setAttribute('data-year', view.year);
- for (let faction_id of FACTIONS) {
- ui.tableaus[faction_id].replaceChildren();
- for (let c of view.tableaus[faction_id]) {
- ui.cards[c].classList.remove('selected');
- ui.tableaus[faction_id].appendChild(ui.cards[c]);
- }
- }
+
// ui.turn_info.replaceChildren();
// console.log('played_card', view.played_card);
diff --git a/rules.js b/rules.js
index c95b1c4..5974267 100644
--- a/rules.js
+++ b/rules.js
@@ -174,6 +174,7 @@ const engine_functions = {
card35_event2,
card42_event3,
card45_event2,
+ card46_event3,
card50_event2,
card53_event2,
card54_event1,
@@ -272,6 +273,7 @@ function game_view(state, player) {
initiative: game.initiative,
medallions: game.medallions,
played_card: game.played_card,
+ selectable_cards: game.selectable_cards,
selected_cards: game.selected_cards[faction_id],
tableaus: game.tableaus,
tracks: game.tracks,
@@ -357,6 +359,7 @@ function setup(seed, _scenario, _options) {
pool: [],
},
played_card: null,
+ selectable_cards: [],
selected_cards: {
a: [],
c: [],
@@ -377,6 +380,7 @@ function setup(seed, _scenario, _options) {
log: [],
undo: [],
used_medallions: [],
+ top_of_events_deck: null,
turn: 0,
year: 0,
};
@@ -415,7 +419,7 @@ function start_year() {
}
function start_turn() {
log_h1(`Year ${game.year} - Turn ${game.turn}`);
- const cardId = draw_card(list_deck('fascist'));
+ const cardId = draw_fascist_card();
game.current_events.push(cardId);
const card = cards[cardId];
log_h2('Fascist Event', 'fascist');
@@ -1032,6 +1036,19 @@ states.peek_fascist_cards = {
inactive: 'peek at Fascist cards',
prompt() {
view.prompt = 'Choose one card to return to the top of the deck';
+ for (const c of game.selectable_cards) {
+ gen_action_card(c);
+ }
+ },
+ card(c) {
+ game.top_of_events_deck = c;
+ for (const ec of game.selectable_cards) {
+ if (ec !== c) {
+ game.discard.f.push(ec);
+ }
+ }
+ game.selectable_cards = [];
+ resolve_active_and_proceed();
},
};
states.player_turn = {
@@ -1368,6 +1385,12 @@ function card45_event2() {
}
resolve_active_and_proceed();
}
+function card46_event3() {
+ for (let i = 0; i < 3; ++i) {
+ game.selectable_cards.push(draw_fascist_card());
+ }
+ resolve_active_and_proceed();
+}
function card50_event2() {
const value = game.tracks[data_1.COLLECTIVIZATION] >= 8 ? 3 : 2;
insert_after_active_node(resolve_effect((0, data_1.create_effect)('front', data_1.ARAGON, value)));
@@ -1507,6 +1530,7 @@ function end_of_year() {
gain_hero_points_in_player_order(players_to_gain_hero_points, game.year);
game.engine = get_player_order().map((f) => create_leaf_node('end_of_year_discard', f));
game.engine.push(create_function_node('start_year'));
+ game.top_of_events_deck = null;
next();
}
function gain_hero_points_in_player_order(factions, value) {
@@ -1934,6 +1958,14 @@ function draw_card(deck) {
set_delete(deck, c);
return c;
}
+function draw_fascist_card() {
+ if (game.top_of_events_deck !== null) {
+ const card_id = game.top_of_events_deck;
+ game.top_of_events_deck = null;
+ return card_id;
+ }
+ return draw_card(list_deck(data_1.FASCIST_ID));
+}
function lose_hero_point(faction, value) {
const points_lost = Math.min(game.hero_points[faction], Math.abs(value));
game.hero_points.pool += points_lost;
@@ -2078,13 +2110,16 @@ function make_list(first, last) {
}
function list_deck(id) {
const deck = [];
- const card_list = id === 'fascist' ? fascist_decks[game.year] : faction_cards[id];
+ const card_list = id === data_1.FASCIST_ID ? fascist_decks[game.year] : faction_cards[id];
card_list.forEach((card) => {
- if (id === 'fascist' &&
- (game.discard.f.includes(card) || game.current_events.includes(card))) {
+ if (game.discard[id].includes(card) ||
+ game.selectable_cards.includes(card)) {
+ return;
+ }
+ if (id === data_1.FASCIST_ID && game.current_events.includes(card)) {
return;
}
- else if (id !== 'fascist' &&
+ else if (id !== data_1.FASCIST_ID &&
(game.hands[id].includes(card) ||
game.discard[id].includes(card) ||
game.tableaus[id].includes(card) ||
diff --git a/rules.ts b/rules.ts
index a706f34..0b63c87 100644
--- a/rules.ts
+++ b/rules.ts
@@ -70,6 +70,7 @@ import data, {
LIBERTY_OR_COLLECTIVIZATION,
ANARCHIST_EXTRA_HERO_POINT,
ARAGON,
+ FASCIST_ID,
// StaticData,
// PLAYER_WITH_MOST_HERO_POINTS,
} from './data';
@@ -128,9 +129,9 @@ const faction_cards = {
};
const fascist_decks = {
- 1: make_list(55, 72),
- 2: make_list(73, 90),
- 3: make_list(91, 108),
+ 1: make_list(55, 72) as CardId[],
+ 2: make_list(73, 90) as CardId[],
+ 3: make_list(91, 108) as CardId[],
};
export const scenarios = ['Standard'];
@@ -313,6 +314,7 @@ const engine_functions: Record<string, Function> = {
card35_event2,
card42_event3,
card45_event2,
+ card46_event3,
card50_event2,
card53_event2,
card54_event1,
@@ -446,6 +448,7 @@ function game_view(state: Game, player: Player) {
initiative: game.initiative,
medallions: game.medallions,
played_card: game.played_card,
+ selectable_cards: game.selectable_cards,
selected_cards: game.selected_cards[faction_id],
tableaus: game.tableaus,
tracks: game.tracks,
@@ -535,6 +538,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
pool: [],
},
played_card: null,
+ selectable_cards: [],
selected_cards: {
a: [],
c: [],
@@ -555,6 +559,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
log: [],
undo: [],
used_medallions: [],
+ top_of_events_deck: null,
turn: 0,
year: 0,
};
@@ -607,7 +612,7 @@ function start_year() {
function start_turn() {
log_h1(`Year ${game.year} - Turn ${game.turn}`);
- const cardId = draw_card(list_deck('fascist'));
+ const cardId = draw_fascist_card();
game.current_events.push(cardId);
const card = cards[cardId] as EventCard;
@@ -1271,6 +1276,19 @@ states.peek_fascist_cards = {
inactive: 'peek at Fascist cards',
prompt() {
view.prompt = 'Choose one card to return to the top of the deck';
+ for (const c of game.selectable_cards) {
+ gen_action_card(c);
+ }
+ },
+ card(c: CardId) {
+ game.top_of_events_deck = c;
+ for (const ec of game.selectable_cards) {
+ if (ec !== c) {
+ game.discard.f.push(ec);
+ }
+ }
+ game.selectable_cards = [];
+ resolve_active_and_proceed();
},
};
@@ -1698,6 +1716,13 @@ function card45_event2() {
resolve_active_and_proceed();
}
+function card46_event3() {
+ for (let i = 0; i < 3; ++i) {
+ game.selectable_cards.push(draw_fascist_card());
+ }
+ resolve_active_and_proceed();
+}
+
function card50_event2() {
const value = game.tracks[COLLECTIVIZATION] >= 8 ? 3 : 2;
insert_after_active_node(
@@ -1869,6 +1894,11 @@ function end_of_year() {
create_leaf_node('end_of_year_discard', f)
);
game.engine.push(create_function_node('start_year'));
+
+ // New deck is used for next year so clear top card
+ // if it was set
+ game.top_of_events_deck = null;
+
next();
}
@@ -2441,6 +2471,15 @@ function draw_card(deck: CardId[]): CardId {
return c;
}
+function draw_fascist_card(): CardId {
+ if (game.top_of_events_deck !== null) {
+ const card_id = game.top_of_events_deck;
+ game.top_of_events_deck = null;
+ return card_id;
+ }
+ return draw_card(list_deck(FASCIST_ID));
+}
+
function lose_hero_point(faction: FactionId, value: number) {
const points_lost = Math.min(game.hero_points[faction], Math.abs(value));
game.hero_points.pool += points_lost;
@@ -2656,18 +2695,22 @@ function make_list(first: number, last: number): number[] {
return list;
}
-function list_deck(id: FactionId | 'fascist') {
+function list_deck(id: FactionId | 'f') {
const deck = [];
- const card_list =
- id === 'fascist' ? fascist_decks[game.year] : faction_cards[id];
+ const card_list: CardId[] =
+ id === FASCIST_ID ? fascist_decks[game.year] : faction_cards[id];
card_list.forEach((card) => {
if (
- id === 'fascist' &&
- (game.discard.f.includes(card) || game.current_events.includes(card))
+ game.discard[id].includes(card) ||
+ game.selectable_cards.includes(card)
) {
return;
+ }
+
+ if (id === FASCIST_ID && game.current_events.includes(card)) {
+ return;
} else if (
- id !== 'fascist' &&
+ id !== FASCIST_ID &&
(game.hands[id].includes(card) ||
game.discard[id].includes(card) ||
game.tableaus[id].includes(card) ||
diff --git a/types.d.ts b/types.d.ts
index b2b7aae..50d6819 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -28,7 +28,7 @@ export interface Game {
result?: string;
victory?: string;
// Game specific
- active_abilities: number[];
+ active_abilities: number[];
turn: number;
year: number;
bag_of_glory: FactionId[];
@@ -37,9 +37,11 @@ export interface Game {
current_events: CardId[];
discard: Record<FactionId | 'f', number[]>;
engine: EngineNode[];
- // Set to faction whos turn it is or null if not player turn
- // Used to determine who can spend Hero Points. Could be the case
- // a faction is active in another factions turn.
+ /**
+ * Set to faction whos turn it is or null if not player turn
+ * Used to determine who can spend Hero Points. Could be the case
+ * a player becomes active in another players turn.
+ */
faction_turn: FactionId | null;
fronts: {
a: Front;
@@ -54,7 +56,13 @@ export interface Game {
medallions: Record<FactionId, number[]> & { pool: Array<number | null> };
played_card: CardId | null;
selected_cards: Record<FactionId, CardId[]>;
+ selectable_cards: CardId[]; // used for specific events
tableaus: Record<FactionId, CardId[]>;
+ /**
+ * Used for event effect that allows Anarchist to put an event
+ * card on top of the deck
+ */
+ top_of_events_deck: CardId | null;
tracks: number[];
trash: Record<FactionId, CardId[]>;
triggered_track_effects: number[];
@@ -80,6 +88,7 @@ export interface View {
initiative: Game['initiative'];
medallions: Game['medallions'];
played_card: Game['played_card'];
+ selectable_cards: Game['selectable_cards'];
tableaus: Game['tableaus'];
tracks: number[];
triggered_track_effects: Game['triggered_track_effects'];