summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrans Bongers <fransbongers@franss-mbp.home>2024-12-22 14:45:51 +0100
committerFrans Bongers <fransbongers@franss-mbp.home>2024-12-22 14:45:51 +0100
commitfa18a313b5a57b106a8c41a542f08ba4697435ca (patch)
tree2146925d47aaf3107c8007bd513f4f99c4682c41
parentb80ba3e739ed5dd1fa2796e3683bbe98659d865a (diff)
downloadland-and-freedom-fa18a313b5a57b106a8c41a542f08ba4697435ca.tar.gz
Add final bid and determine winner
-rw-r--r--data.js2
-rw-r--r--data.ts2
-rw-r--r--play.js2
-rw-r--r--play.ts2
-rw-r--r--rules.js270
-rw-r--r--rules.ts296
-rw-r--r--types.d.ts2
7 files changed, 540 insertions, 36 deletions
diff --git a/data.js b/data.js
index 8ce7712..33f2374 100644
--- a/data.js
+++ b/data.js
@@ -920,7 +920,7 @@ const data = {
pass: create_effect('draw_card', MODERATES_ID, 1),
fail: create_effect('track', GOVERNMENT, TOWARDS_CENTER),
},
- title: 'MASSACRE OF BADAJOZ',
+ title: 'THE BOMBING OF MADRID',
type: 'ec',
year: 1,
},
diff --git a/data.ts b/data.ts
index cb52484..7c61705 100644
--- a/data.ts
+++ b/data.ts
@@ -966,7 +966,7 @@ const data: StaticData = {
pass: create_effect('draw_card', MODERATES_ID, 1),
fail: create_effect('track', GOVERNMENT, TOWARDS_CENTER),
},
- title: 'MASSACRE OF BADAJOZ',
+ title: 'THE BOMBING OF MADRID',
type: 'ec',
year: 1,
},
diff --git a/play.js b/play.js
index ffa817a..1ff8d8d 100644
--- a/play.js
+++ b/play.js
@@ -279,7 +279,7 @@ function on_update() {
for (let c of view.hand) {
ui.cards[c].classList.remove('selected');
ui.hand.appendChild(ui.cards[c]);
- if (c === view.selected_card) {
+ if (c === view.selected_card || view.final_bid.includes(c)) {
ui.cards[c].classList.add('selected');
}
}
diff --git a/play.ts b/play.ts
index b97839b..feb7201 100644
--- a/play.ts
+++ b/play.ts
@@ -368,7 +368,7 @@ function on_update() {
for (let c of view.hand) {
ui.cards[c].classList.remove('selected');
ui.hand.appendChild(ui.cards[c]);
- if (c === view.selected_card) {
+ if (c === view.selected_card || view.final_bid.includes(c)) {
ui.cards[c].classList.add('selected');
}
}
diff --git a/rules.js b/rules.js
index cc93228..bd1f473 100644
--- a/rules.js
+++ b/rules.js
@@ -118,6 +118,14 @@ function setup_choose_card() {
game.engine.push(create_function_node('setup_player_turn'));
next();
}
+function setup_final_bid() {
+ log_h1('Final Bid');
+ const player_order = get_player_order();
+ game.engine = player_order.map((faction_id) => create_leaf_node('choose_final_bid', faction_id));
+ game.engine.push(create_function_node('resolve_final_bid'));
+ game.engine.push(create_function_node('setup_choose_card'));
+ next();
+}
function setup_player_turn() {
const player_order = get_player_order();
game.engine = player_order.map((faction_id) => create_seq_node([
@@ -139,12 +147,14 @@ function start_of_player_turn() {
const engine_functions = {
check_activate_icon,
end_of_turn,
- end_of_year,
setup_bag_of_glory,
setup_choose_card,
+ setup_final_bid,
setup_player_turn,
start_of_player_turn,
+ start_year,
resolve_fascist_test,
+ resolve_final_bid,
};
function get_active(engine) {
for (let i of engine) {
@@ -227,6 +237,7 @@ function game_view(state, player) {
bag_of_glory: game.bag_of_glory,
bonuses: game.bonuses,
current_events: game.current_events,
+ final_bid: game.final_bid[faction_id],
fronts: game.fronts,
glory: game.glory,
hand: game.hands[faction_id],
@@ -272,6 +283,11 @@ function setup(seed, _scenario, _options) {
f: [],
},
engine: [],
+ final_bid: {
+ [data_1.ANARCHISTS_ID]: [],
+ [data_1.COMMUNISTS_ID]: [],
+ [data_1.MODERATES_ID]: [],
+ },
fronts: {
a: {
value: -2,
@@ -333,24 +349,34 @@ function setup(seed, _scenario, _options) {
triggered_track_effects: [[], [], [], [], []],
log: [],
undo: [],
- turn: 1,
- year: 1,
+ turn: 0,
+ year: 0,
state_data: null,
};
start_year();
return game;
}
function draw_hand_cards(faction_id, count) {
+ const deck = list_deck(faction_id);
+ if (deck.length < count) {
+ count = count - deck.length;
+ game.hands[faction_id] = game.hands[faction_id].concat(deck);
+ game.discard[faction_id] = [];
+ }
+ console.log('draw_hand_cards', count);
const log = count === 1
? `${get_player(faction_id)} draws 1 card`
: `${get_player(faction_id)} draws ${count} cards`;
logi(log);
+ console.log('deck', list_deck(faction_id));
for (let i = 0; i < count; i++) {
const deck = list_deck(faction_id);
game.hands[faction_id].push(draw_card(deck));
}
}
function start_year() {
+ game.year++;
+ game.turn = 1;
game.current_events = [];
role_ids.forEach((role) => {
draw_hand_cards(role, 5);
@@ -365,10 +391,12 @@ function start_turn() {
log_h2('Fascist Event', 'fascist');
log(card.title);
game.engine = card.effects.map((effect) => resolve_effect(effect, game.initiative));
- game.engine.push({
- t: 'f',
- f: 'setup_choose_card',
- });
+ if (game.year === 3 && game.turn === 1) {
+ game.engine.push(create_function_node('setup_final_bid'));
+ }
+ else {
+ game.engine.push(create_function_node('setup_choose_card'));
+ }
next();
}
states.activate_icon = {
@@ -514,6 +542,53 @@ states.attack_front = {
resolve_active_and_proceed();
},
};
+states.break_tie_final_bid = {
+ inactive: 'break tie for Final Bid',
+ prompt() {
+ view.prompt = 'Choose the winner of the Final Bid';
+ const { winners } = get_active_node_args();
+ for (const f of winners) {
+ gen_action(faction_player_map[f]);
+ }
+ },
+ Anarchist() {
+ win_final_bid(data_1.ANARCHISTS_ID);
+ resolve_active_and_proceed();
+ },
+ Communist() {
+ win_final_bid(data_1.COMMUNISTS_ID);
+ resolve_active_and_proceed();
+ },
+ Moderate() {
+ win_final_bid(data_1.MODERATES_ID);
+ resolve_active_and_proceed();
+ },
+};
+states.break_tie_winner = {
+ inactive: 'break tie for winner of the game',
+ prompt() {
+ view.prompt = 'Choose the winner of the game';
+ const { winners } = get_active_node_args();
+ for (const f of winners) {
+ gen_action(faction_player_map[f]);
+ }
+ },
+ Anarchist() {
+ const { glory } = get_active_node_args();
+ win_game(data_1.ANARCHIST, glory);
+ resolve_active_and_proceed();
+ },
+ Communist() {
+ const { glory } = get_active_node_args();
+ win_game(data_1.COMMUNIST, glory);
+ resolve_active_and_proceed();
+ },
+ Moderate() {
+ const { glory } = get_active_node_args();
+ win_game(data_1.MODERATE, glory);
+ resolve_active_and_proceed();
+ },
+};
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
@@ -598,6 +673,68 @@ states.choose_card = {
resolve_active_and_proceed();
},
};
+states.choose_final_bid = {
+ inactive: 'choose Final Bid',
+ prompt() {
+ view.prompt = 'Choose a card to add to the Final Bid';
+ const faction = get_active_faction();
+ for (let c of game.hands[faction]) {
+ if (!game.final_bid[faction].includes(c)) {
+ gen_action_card(c);
+ }
+ }
+ gen_action('done');
+ },
+ card(c) {
+ const faction = get_active_faction();
+ game.final_bid[faction].push(c);
+ if (game.final_bid[faction].length < 3) {
+ next();
+ }
+ else {
+ resolve_active_and_proceed();
+ }
+ },
+ done() {
+ resolve_active_and_proceed();
+ },
+};
+states.end_of_year_discard = {
+ inactive: 'discard cards from hand and tableau',
+ prompt() {
+ view.prompt = 'Discard a card';
+ const faction_id = get_active_faction();
+ const hand = game.hands[faction_id];
+ if (hand.length > game.year) {
+ for (let c of hand)
+ gen_action_card(c);
+ }
+ const tableau = game.tableaus[faction_id];
+ if (tableau.length > game.year) {
+ for (let c of tableau)
+ gen_action_card(c);
+ }
+ },
+ card(c) {
+ const faction_id = get_active_faction();
+ console.log('list deck', list_deck(faction_id));
+ if (game.hands[faction_id].includes(c)) {
+ game.hands[faction_id] = game.hands[faction_id].filter((id) => id !== c);
+ }
+ else if (game.tableaus[faction_id].includes(c)) {
+ game.tableaus[faction_id] = game.tableaus[faction_id].filter((id) => id !== c);
+ }
+ game.discard[faction_id].push(c);
+ if (game.hands[faction_id].length > game.year ||
+ game.tableaus[faction_id].length > game.year) {
+ next();
+ }
+ else {
+ log(`${faction_player_map[faction_id]} discards cards`);
+ resolve_active_and_proceed();
+ }
+ },
+};
states.gain_hero_points = {
inactive: 'gain Hero Points',
prompt() {
@@ -639,12 +776,12 @@ states.lose_hero_points = {
},
Communist() {
const value = get_active_node_args().v;
- lose_hero_point(data_1.ANARCHISTS_ID, value);
+ lose_hero_point(data_1.COMMUNISTS_ID, value);
resolve_active_and_proceed();
},
Moderate() {
const value = get_active_node_args().v;
- lose_hero_point(data_1.ANARCHISTS_ID, value);
+ lose_hero_point(data_1.MODERATES_ID, value);
resolve_active_and_proceed();
},
};
@@ -843,6 +980,46 @@ function check_initiative() {
game.initiative = initiative;
logi(`${faction_player_map[initiative]} claims the Initiative`);
}
+function war_is_won() {
+ let won_fronts = 0;
+ for (const f of data_1.FRONTS) {
+ if (game.fronts[f].value >= 1) {
+ won_fronts++;
+ }
+ }
+ return won_fronts >= 3;
+}
+function determine_winner() {
+ const glory = {
+ [data_1.ANARCHISTS_ID]: 0,
+ [data_1.COMMUNISTS_ID]: 0,
+ [data_1.MODERATES_ID]: 0,
+ };
+ for (const g of game.glory) {
+ glory[g]++;
+ }
+ let highest_glory = 0;
+ let winners = [];
+ for (let f of role_ids) {
+ if (glory[f] === highest_glory) {
+ winners.push(f);
+ }
+ else if (glory[f] > highest_glory) {
+ highest_glory = glory[f];
+ winners = [f];
+ }
+ }
+ if (winners.length === 1) {
+ win_game(faction_player_map[winners[0]], highest_glory);
+ }
+ else {
+ insert_after_active_node(create_leaf_node('break_tie_winner', game.initiative, {
+ winners,
+ glory: highest_glory,
+ }));
+ }
+ resolve_active_and_proceed();
+}
function end_of_turn() {
Object.keys(game.fronts).forEach((front_id) => {
game.fronts[front_id].contributions = [];
@@ -856,14 +1033,39 @@ function end_of_turn() {
}
}
function end_of_year() {
- const gloryToDraw = [0, 1, 2, 5];
- for (let i = 0; i < gloryToDraw[game.year]; ++i) {
+ if (game.year === 3) {
+ log_h1('End of the game');
+ const is_won = war_is_won();
+ if (is_won) {
+ log('The war is won!');
+ }
+ else {
+ game_over('None', 'The war is lost. All Players lose the game!');
+ return;
+ }
+ }
+ const glory_to_draw = [0, 1, 2, 5];
+ const glory_this_year = {
+ [data_1.ANARCHISTS_ID]: false,
+ [data_1.COMMUNISTS_ID]: false,
+ [data_1.MODERATES_ID]: false,
+ };
+ for (let i = 0; i < glory_to_draw[game.year]; ++i) {
const index = random(game.bag_of_glory.length);
- game.glory.push(game.bag_of_glory[index]);
+ const faction = game.bag_of_glory[index];
+ game.glory.push(faction);
+ glory_this_year[faction] = true;
array_remove(game.bag_of_glory, index);
}
- game.year++;
- start_year();
+ if (game.year === 3) {
+ determine_winner();
+ return;
+ }
+ const players_to_gain_hero_points = role_ids.filter((f) => !glory_this_year[f]);
+ 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'));
+ next();
}
function gain_hero_points_in_player_order(factions, value) {
for (const f of get_player_order()) {
@@ -907,6 +1109,34 @@ function resolve_fascist_test() {
}
resolve_active_and_proceed();
}
+function resolve_final_bid() {
+ let highest_bid = 0;
+ let winners = [];
+ for (const f of get_player_order()) {
+ let player_bid = 0;
+ for (const c of game.final_bid[f]) {
+ player_bid += cards[c].strength;
+ }
+ log(`${faction_player_map[f]} bids ${player_bid}`);
+ if (player_bid === highest_bid) {
+ winners.push(f);
+ }
+ else if (player_bid > highest_bid) {
+ highest_bid = player_bid;
+ winners = [f];
+ }
+ game.hands[f] = game.hands[f].filter((c) => !game.final_bid[f].includes(c));
+ game.discard[f].concat(game.final_bid[f]);
+ game.final_bid[f] = [];
+ }
+ if (winners.length === 1) {
+ win_final_bid(winners[0]);
+ }
+ else {
+ insert_after_active_node(create_leaf_node('break_tie_final_bid', game.initiative, { winners }));
+ }
+ resolve_active_and_proceed();
+}
function get_fronts_to_add_to(target) {
console.log('get_fronts_to_add_to', target);
if (target === data_1.CLOSEST_TO_DEFEAT || target === data_1.CLOSEST_TO_VICTORY) {
@@ -1075,6 +1305,14 @@ function resolve_effect(effect, faction = get_active_faction()) {
}
return state === undefined ? null : create_leaf_node(state, faction, args);
}
+function win_final_bid(faction_id) {
+ log_br();
+ log(`${faction_player_map[faction_id]} wins the Final Bid`);
+ game.glory.push(faction_id);
+}
+function win_game(player, glory) {
+ game_over(player, `${player} wins the game with a total of ${glory} Glory!`);
+}
function draw_card(deck) {
clear_undo();
let i = random(deck.length);
@@ -1260,7 +1498,9 @@ function list_deck(id) {
return;
}
else if (id !== 'fascist' &&
- (game.hands[id].includes(card) || game.discard[id].includes(card))) {
+ (game.hands[id].includes(card) ||
+ game.discard[id].includes(card) ||
+ game.tableaus[id].includes(card))) {
return;
}
deck.push(card);
diff --git a/rules.ts b/rules.ts
index 2a94a8d..dc3700a 100644
--- a/rules.ts
+++ b/rules.ts
@@ -230,6 +230,17 @@ function setup_choose_card() {
next();
}
+function setup_final_bid() {
+ log_h1('Final Bid');
+ const player_order = get_player_order();
+ game.engine = player_order.map((faction_id) =>
+ create_leaf_node('choose_final_bid', faction_id)
+ );
+ game.engine.push(create_function_node('resolve_final_bid'));
+ game.engine.push(create_function_node('setup_choose_card'));
+ next();
+}
+
function setup_player_turn() {
const player_order = get_player_order();
game.engine = player_order.map((faction_id) =>
@@ -255,12 +266,15 @@ function start_of_player_turn() {
const engine_functions: Record<string, Function> = {
check_activate_icon,
end_of_turn,
- end_of_year,
+ // end_of_year,
setup_bag_of_glory,
setup_choose_card,
+ setup_final_bid,
setup_player_turn,
start_of_player_turn,
+ start_year,
resolve_fascist_test,
+ resolve_final_bid,
};
function get_active(
@@ -373,6 +387,7 @@ function game_view(state: Game, player: Player) {
bag_of_glory: game.bag_of_glory,
bonuses: game.bonuses,
current_events: game.current_events,
+ final_bid: game.final_bid[faction_id],
fronts: game.fronts,
glory: game.glory,
hand: game.hands[faction_id],
@@ -422,6 +437,11 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
f: [],
},
engine: [],
+ final_bid: {
+ [ANARCHISTS_ID]: [],
+ [COMMUNISTS_ID]: [],
+ [MODERATES_ID]: [],
+ },
fronts: {
a: {
value: -2,
@@ -483,8 +503,8 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
triggered_track_effects: [[], [], [], [], []],
log: [],
undo: [],
- turn: 1,
- year: 1,
+ turn: 0,
+ year: 0,
state_data: null,
};
@@ -493,11 +513,22 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
}
function draw_hand_cards(faction_id: FactionId, count: number) {
+ const deck = list_deck(faction_id);
+
+ // Draw all remaining cards
+ if (deck.length < count) {
+ count = count - deck.length;
+ game.hands[faction_id] = game.hands[faction_id].concat(deck);
+ game.discard[faction_id] = [];
+ }
+
+ console.log('draw_hand_cards', count);
const log =
count === 1
? `${get_player(faction_id)} draws 1 card`
: `${get_player(faction_id)} draws ${count} cards`;
logi(log);
+ console.log('deck', list_deck(faction_id));
for (let i = 0; i < count; i++) {
const deck = list_deck(faction_id);
game.hands[faction_id].push(draw_card(deck));
@@ -507,7 +538,8 @@ function draw_hand_cards(faction_id: FactionId, count: number) {
// #endregion
function start_year() {
- // log_h1('Year ' + game.year);
+ game.year++;
+ game.turn = 1;
game.current_events = [];
role_ids.forEach((role) => {
draw_hand_cards(role, 5);
@@ -528,10 +560,11 @@ function start_turn() {
game.engine = card.effects.map((effect) =>
resolve_effect(effect, game.initiative)
);
- game.engine.push({
- t: 'f',
- f: 'setup_choose_card',
- });
+ if (game.year === 3 && game.turn === 1) {
+ game.engine.push(create_function_node('setup_final_bid'));
+ } else {
+ game.engine.push(create_function_node('setup_choose_card'));
+ }
next();
// game.state = 'resolve_event';
// game.active = faction_player_map[game.initiative];
@@ -702,6 +735,55 @@ states.attack_front = {
},
};
+states.break_tie_final_bid = {
+ inactive: 'break tie for Final Bid',
+ prompt() {
+ view.prompt = 'Choose the winner of the Final Bid';
+ const { winners } = get_active_node_args();
+ for (const f of winners) {
+ gen_action(faction_player_map[f]);
+ }
+ },
+ Anarchist() {
+ win_final_bid(ANARCHISTS_ID);
+ resolve_active_and_proceed();
+ },
+ Communist() {
+ win_final_bid(COMMUNISTS_ID);
+ resolve_active_and_proceed();
+ },
+ Moderate() {
+ win_final_bid(MODERATES_ID);
+ resolve_active_and_proceed();
+ },
+};
+
+states.break_tie_winner = {
+ inactive: 'break tie for winner of the game',
+ prompt() {
+ view.prompt = 'Choose the winner of the game';
+ const { winners } = get_active_node_args();
+ for (const f of winners) {
+ gen_action(faction_player_map[f]);
+ }
+ },
+ Anarchist() {
+ const { glory } = get_active_node_args();
+ win_game(ANARCHIST, glory);
+ resolve_active_and_proceed();
+ },
+ Communist() {
+ const { glory } = get_active_node_args();
+ win_game(COMMUNIST, glory);
+ resolve_active_and_proceed();
+ },
+ Moderate() {
+ const { glory } = get_active_node_args();
+ win_game(MODERATE, glory);
+ resolve_active_and_proceed();
+ },
+};
+
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
@@ -790,6 +872,70 @@ states.choose_card = {
},
};
+states.choose_final_bid = {
+ inactive: 'choose Final Bid',
+ prompt() {
+ view.prompt = 'Choose a card to add to the Final Bid';
+ const faction = get_active_faction();
+ for (let c of game.hands[faction]) {
+ if (!game.final_bid[faction].includes(c)) {
+ gen_action_card(c);
+ }
+ }
+ gen_action('done');
+ },
+ card(c: CardId) {
+ const faction = get_active_faction();
+ game.final_bid[faction].push(c);
+ if (game.final_bid[faction].length < 3) {
+ next();
+ } else {
+ resolve_active_and_proceed();
+ }
+ },
+ done() {
+ resolve_active_and_proceed();
+ },
+};
+
+states.end_of_year_discard = {
+ inactive: 'discard cards from hand and tableau',
+ prompt() {
+ view.prompt = 'Discard a card';
+ const faction_id = get_active_faction();
+ const hand = game.hands[faction_id];
+ if (hand.length > game.year) {
+ for (let c of hand) gen_action_card(c);
+ }
+ const tableau = game.tableaus[faction_id];
+ if (tableau.length > game.year) {
+ for (let c of tableau) gen_action_card(c);
+ }
+ },
+ card(c: CardId) {
+ const faction_id = get_active_faction();
+ console.log('list deck', list_deck(faction_id));
+ if (game.hands[faction_id].includes(c)) {
+ game.hands[faction_id] = game.hands[faction_id].filter((id) => id !== c);
+ } else if (game.tableaus[faction_id].includes(c)) {
+ game.tableaus[faction_id] = game.tableaus[faction_id].filter(
+ (id) => id !== c
+ );
+ }
+ game.discard[faction_id].push(c);
+ if (
+ game.hands[faction_id].length > game.year ||
+ game.tableaus[faction_id].length > game.year
+ ) {
+ // More cards to discard so resolve same state again
+ next();
+ } else {
+ log(`${faction_player_map[faction_id]} discards cards`);
+ resolve_active_and_proceed();
+ }
+ },
+};
+
states.gain_hero_points = {
inactive: 'gain Hero Points',
prompt() {
@@ -833,12 +979,12 @@ states.lose_hero_points = {
},
Communist() {
const value = get_active_node_args().v;
- lose_hero_point(ANARCHISTS_ID, value);
+ lose_hero_point(COMMUNISTS_ID, value);
resolve_active_and_proceed();
},
Moderate() {
const value = get_active_node_args().v;
- lose_hero_point(ANARCHISTS_ID, value);
+ lose_hero_point(MODERATES_ID, value);
resolve_active_and_proceed();
},
};
@@ -1089,11 +1235,52 @@ function check_initiative() {
logi(`${faction_player_map[initiative]} claims the Initiative`);
}
+function war_is_won() {
+ let won_fronts = 0;
+ for (const f of FRONTS) {
+ if (game.fronts[f].value >= 1) {
+ won_fronts++;
+ }
+ }
+ return won_fronts >= 3;
+}
+
+function determine_winner() {
+ const glory = {
+ [ANARCHISTS_ID]: 0,
+ [COMMUNISTS_ID]: 0,
+ [MODERATES_ID]: 0,
+ };
+ for (const g of game.glory) {
+ glory[g]++;
+ }
+ let highest_glory = 0;
+ let winners = [];
+ for (let f of role_ids) {
+ if (glory[f] === highest_glory) {
+ winners.push(f);
+ } else if (glory[f] > highest_glory) {
+ highest_glory = glory[f];
+ winners = [f];
+ }
+ }
+ if (winners.length === 1) {
+ win_game(faction_player_map[winners[0]], highest_glory);
+ } else {
+ insert_after_active_node(
+ create_leaf_node('break_tie_winner', game.initiative, {
+ winners,
+ glory: highest_glory,
+ })
+ );
+ }
+ resolve_active_and_proceed();
+}
+
function end_of_turn() {
Object.keys(game.fronts).forEach((front_id) => {
game.fronts[front_id].contributions = [];
});
- // log_h2('End of turn');
if (game.turn === 4) {
end_of_year();
} else {
@@ -1103,15 +1290,47 @@ function end_of_turn() {
}
function end_of_year() {
- const gloryToDraw = [0, 1, 2, 5];
- for (let i = 0; i < gloryToDraw[game.year]; ++i) {
+ if (game.year === 3) {
+ log_h1('End of the game');
+ const is_won = war_is_won();
+ if (is_won) {
+ log('The war is won!');
+ } else {
+ game_over('None', 'The war is lost. All Players lose the game!');
+ return;
+ }
+ }
+ const glory_to_draw = [0, 1, 2, 5];
+ const glory_this_year: Record<FactionId, boolean> = {
+ [ANARCHISTS_ID]: false,
+ [COMMUNISTS_ID]: false,
+ [MODERATES_ID]: false,
+ };
+ for (let i = 0; i < glory_to_draw[game.year]; ++i) {
const index = random(game.bag_of_glory.length);
- game.glory.push(game.bag_of_glory[index]);
+ const faction = game.bag_of_glory[index];
+ game.glory.push(faction);
+ glory_this_year[faction] = true;
array_remove(game.bag_of_glory, index);
}
- game.year++;
- start_year();
+ if (game.year === 3) {
+ // end of game
+ determine_winner();
+ return;
+ }
+
+ const players_to_gain_hero_points = role_ids.filter(
+ (f) => !glory_this_year[f]
+ );
+ gain_hero_points_in_player_order(players_to_gain_hero_points, game.year);
+
+ // Setup card discarding
+ game.engine = get_player_order().map((f) =>
+ create_leaf_node('end_of_year_discard', f)
+ );
+ game.engine.push(create_function_node('start_year'));
+ next();
}
function gain_hero_points_in_player_order(factions: FactionId[], value) {
@@ -1169,6 +1388,37 @@ function resolve_fascist_test() {
resolve_active_and_proceed();
}
+function resolve_final_bid() {
+ let highest_bid = 0;
+ let winners: FactionId[] = [];
+ for (const f of get_player_order()) {
+ let player_bid = 0;
+ for (const c of game.final_bid[f]) {
+ player_bid += (cards[c] as PlayerCard).strength;
+ }
+ log(`${faction_player_map[f]} bids ${player_bid}`);
+ if (player_bid === highest_bid) {
+ winners.push(f);
+ } else if (player_bid > highest_bid) {
+ highest_bid = player_bid;
+ winners = [f];
+ }
+ game.hands[f] = game.hands[f].filter((c) => !game.final_bid[f].includes(c));
+ game.discard[f].concat(game.final_bid[f]);
+ game.final_bid[f] = [];
+ }
+
+ if (winners.length === 1) {
+ win_final_bid(winners[0]);
+ } else {
+ insert_after_active_node(
+ create_leaf_node('break_tie_final_bid', game.initiative, { winners })
+ );
+ }
+
+ resolve_active_and_proceed();
+}
+
// TODO: check for defeated / won fronts
function get_fronts_to_add_to(target: string): FrontId[] {
console.log('get_fronts_to_add_to', target);
@@ -1386,6 +1636,16 @@ function resolve_effect(
return state === undefined ? null : create_leaf_node(state, faction, args);
}
+function win_final_bid(faction_id: FactionId) {
+ log_br();
+ log(`${faction_player_map[faction_id]} wins the Final Bid`);
+ game.glory.push(faction_id);
+}
+
+function win_game(player: Player, glory: number) {
+ game_over(player, `${player} wins the game with a total of ${glory} Glory!`);
+}
+
// #endregion
// #region CARDS
@@ -1662,7 +1922,9 @@ function list_deck(id: FactionId | 'fascist') {
return;
} else if (
id !== 'fascist' &&
- (game.hands[id].includes(card) || game.discard[id].includes(card))
+ (game.hands[id].includes(card) ||
+ game.discard[id].includes(card) ||
+ game.tableaus[id].includes(card))
) {
return;
}
diff --git a/types.d.ts b/types.d.ts
index f0c4772..2b44d65 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -32,6 +32,7 @@ export interface Game {
current_events: CardId[];
discard: Record<FactionId | 'f', number[]>;
engine: EngineNode[];
+ final_bid: Record<FactionId, CardId[]>;
fronts: {
a: Front;
m: Front;
@@ -73,6 +74,7 @@ export interface View {
bag_of_glory: Game['bag_of_glory'];
bonuses: Game['bonuses'];
current_events: CardId[];
+ final_bid: CardId[];
fronts: Game['fronts'];
glory: Game['glory'];
hand: CardId[];