summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrans Bongers <fransbongers@franss-mbp.home>2024-12-30 22:21:32 +0100
committerFrans Bongers <fransbongers@franss-mbp.home>2024-12-30 22:21:32 +0100
commit4148cddd0370fa1ea9b9580a79ac1c73109c88a1 (patch)
tree6f94af6ca4c45bed4c9a8da9cf4adb2e24700270
parent2122adacc6c569e78bc71b049ea5cdffe13208a5 (diff)
downloadland-and-freedom-4148cddd0370fa1ea9b9580a79ac1c73109c88a1.tar.gz
spend hero points at any point during a turn
-rw-r--r--data.js2
-rw-r--r--data.ts2
-rw-r--r--play.js2
-rw-r--r--play.ts3
-rw-r--r--rules.js185
-rw-r--r--rules.ts219
-rw-r--r--types.d.ts4
7 files changed, 318 insertions, 99 deletions
diff --git a/data.js b/data.js
index 7202657..9ba1493 100644
--- a/data.js
+++ b/data.js
@@ -326,7 +326,7 @@ const data = {
create_effect('draw_card', SELF, 2),
],
icons: ['d_collectivization', 'government', 'draw_card'],
- strength: 1,
+ strength: 2,
title: 'IMPOSE FACTORY MANAGERS',
type: 'pc',
},
diff --git a/data.ts b/data.ts
index 35b9d75..f3da685 100644
--- a/data.ts
+++ b/data.ts
@@ -348,7 +348,7 @@ const data: StaticData = {
create_effect('draw_card', SELF, 2),
],
icons: ['d_collectivization', 'government', 'draw_card'],
- strength: 1,
+ strength: 2,
title: 'IMPOSE FACTORY MANAGERS',
type: 'pc',
},
diff --git a/play.js b/play.js
index 52240c0..a18aea4 100644
--- a/play.js
+++ b/play.js
@@ -362,7 +362,6 @@ function on_update() {
action_button('draw_cards', 'Draw cards');
action_button('play_for_ap', 'Play card for Action Points');
action_button('play_for_event', 'Play card for Event');
- action_button('spend_hp', 'Spend Hero Points');
action_button('add_glory', 'Add Glory');
action_button('up', 'Up');
action_button('down', 'Down');
@@ -373,6 +372,7 @@ function on_update() {
action_button('no', 'No');
action_button('skip', 'Skip');
action_button('done', 'Done');
+ action_button('spend_hp', 'Spend Hero Points');
action_button('undo', 'Undo');
}
function on_log(text) {
diff --git a/play.ts b/play.ts
index c6307fe..87ba7dc 100644
--- a/play.ts
+++ b/play.ts
@@ -476,7 +476,7 @@ function on_update() {
// action_button('draw_card', 'Draw card');
action_button('play_for_ap', 'Play card for Action Points');
action_button('play_for_event', 'Play card for Event');
- action_button('spend_hp', 'Spend Hero Points');
+
action_button('add_glory', 'Add Glory');
action_button('up', 'Up');
action_button('down', 'Down');
@@ -487,6 +487,7 @@ function on_update() {
action_button('no', 'No');
action_button('skip', 'Skip');
action_button('done', 'Done');
+ action_button('spend_hp', 'Spend Hero Points');
action_button('undo', 'Undo');
}
diff --git a/rules.js b/rules.js
index b7f9196..308d34e 100644
--- a/rules.js
+++ b/rules.js
@@ -70,6 +70,13 @@ function gen_action_medallion(medallion_id) {
function gen_action_standee(track_id) {
gen_action('standee', track_id);
}
+function gen_spend_hero_points() {
+ const faction = get_active_faction();
+ const can_spend_hp = game.faction_turn === faction && game.hero_points[faction] > 0;
+ if (can_spend_hp) {
+ gen_action('spend_hp');
+ }
+}
function action(state, player, action, arg) {
console.log('action', player, action, arg);
if (action !== 'undo') {
@@ -141,13 +148,29 @@ function setup_final_bid() {
next();
}
function setup_player_turn() {
- const player_order = get_player_order();
- game.engine = player_order.map((faction_id) => create_seq_node([
- create_function_node('start_of_player_turn', { f: faction_id }),
- create_leaf_node('player_turn', faction_id),
- ]));
- game.engine.push(create_function_node('resolve_fascist_test'));
- game.engine.push(create_function_node('setup_bag_of_glory'));
+ const next_faction = game.first_player === null
+ ? get_player_order()[0]
+ : get_next_faction(get_active_faction());
+ if (game.first_player === null) {
+ game.first_player = next_faction;
+ }
+ game.engine = [
+ create_function_node('start_of_player_turn', { f: next_faction }),
+ create_leaf_node('player_turn', next_faction),
+ create_function_node('end_of_player_turn', { f: next_faction }),
+ ];
+ next();
+}
+function end_of_player_turn() {
+ if (get_next_faction(get_active_faction()) === game.first_player) {
+ game.engine = [
+ create_function_node('resolve_fascist_test'),
+ create_function_node('setup_bag_of_glory'),
+ ];
+ }
+ else {
+ game.engine = [create_function_node('setup_player_turn')];
+ }
next();
}
function start_of_player_turn() {
@@ -160,6 +183,7 @@ function start_of_player_turn() {
const engine_functions = {
check_activate_icon,
checkpoint,
+ end_of_player_turn,
end_of_turn,
setup_bag_of_glory,
setup_choose_card,
@@ -357,6 +381,7 @@ function setup(seed, _scenario, _options) {
},
},
glory: [],
+ first_player: null,
hands: {
a: [],
c: [],
@@ -453,12 +478,16 @@ function start_turn() {
states.activate_icon = {
inactive: 'activate an icon',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose an icon to activate';
const c = cards[game.played_card];
for (const i of c.icons) {
gen_action(i);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
add_to_front() {
insert_after_active_node(create_leaf_node('add_to_front', get_active_faction(), {
t: data_1.ANY,
@@ -584,6 +613,7 @@ states.activate_icon = {
states.add_card_to_tableau = {
inactive: 'add a card to their tableau',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to add to your tableau';
const faction = get_active_faction();
for (const c of game.hands[faction]) {
@@ -593,6 +623,9 @@ states.add_card_to_tableau = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c) {
const faction_id = get_active_faction();
const card = cards[c];
@@ -608,9 +641,13 @@ states.add_card_to_tableau = {
states.add_glory = {
inactive: 'add tokens to the Bag of Glory',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Add tokens to the Bag of Glory';
gen_action('add_glory');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
add_glory() {
let number = 1;
if (game.turn === 4) {
@@ -623,6 +660,7 @@ states.add_glory = {
states.add_to_front = {
inactive: 'add strength to a Front',
prompt() {
+ gen_spend_hero_points();
const args = get_active_node_args();
const possible_fronts = get_fronts_to_add_to(args.t);
view.prompt =
@@ -633,6 +671,9 @@ states.add_to_front = {
gen_action_front(f);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(f) {
const value = get_active_node_args().v;
update_front(f, value, get_active_faction());
@@ -642,6 +683,7 @@ states.add_to_front = {
states.attack_front = {
inactive: 'attack a Front',
prompt() {
+ gen_spend_hero_points();
const { t: target, n } = get_active_node_args();
let fronts = [];
if (target === data_1.ANY) {
@@ -665,6 +707,9 @@ states.attack_front = {
: 'Attack a front';
fronts.forEach((id) => gen_action('front', id));
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(f) {
const node = get_active_node();
const value = node.a.v;
@@ -722,6 +767,7 @@ states.break_tie_winner = {
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose area of the board to affect';
for (const track of tracks) {
gen_action_standee(track.id);
@@ -736,6 +782,9 @@ states.choose_area_ap = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
bonus(b) {
update_bonus(b, data_1.ON);
const s = get_active_node_args().strength;
@@ -747,13 +796,13 @@ states.choose_area_ap = {
},
front(f) {
const s = get_active_node_args().strength;
- update_front(f, s, get_active_faction_id());
+ update_front(f, s, get_active_faction());
resolve_active_and_proceed();
},
standee(track_id) {
insert_after_active_node({
t: leaf_node,
- p: get_active_faction_id(),
+ p: get_active_faction(),
s: 'move_track_up_or_down',
a: {
track_id,
@@ -766,6 +815,7 @@ states.choose_area_ap = {
states.change_bonus = {
inactive: 'select Bonus',
prompt() {
+ gen_spend_hero_points();
const args = get_active_node_args();
if ((args.v === data_1.ON &&
game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON &&
@@ -786,6 +836,9 @@ states.change_bonus = {
gen_action_bonus(args.t);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
bonus(b) {
const value = get_active_node_args().v;
update_bonus(b, value);
@@ -798,6 +851,7 @@ states.change_bonus = {
states.choose_card = {
inactive: 'choose a card',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to play this turn';
const faction = get_active_faction();
const hand = game.hands[faction];
@@ -807,6 +861,9 @@ states.choose_card = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c) {
const faction = get_active_faction();
game.selected_cards[faction].push(c);
@@ -847,6 +904,7 @@ states.choose_final_bid = {
states.choose_medallion = {
inactive: 'choose a medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a medallion';
for (let m of game.medallions.pool) {
gen_action_medallion(m);
@@ -855,6 +913,9 @@ states.choose_medallion = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
medallion(m) {
const faction = get_active_faction();
const medallion = medallions[m];
@@ -893,10 +954,14 @@ states.confirm_turn = {
states.draw_card = {
inactive: 'draw a card',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt = v === 1 ? 'Draw a card' : `Draw ${v} cards`;
gen_action(v === 1 ? 'draw_card' : 'draw_cards');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
draw_card() {
const { v } = get_active_node_args();
draw_hand_cards(get_active_faction(), v);
@@ -943,9 +1008,10 @@ states.end_of_year_discard = {
}
},
};
-states.gain_hero_points = {
+states.hero_points = {
inactive: 'gain Hero Points',
prompt() {
+ gen_spend_hero_points();
const value = get_active_node_args().v;
if (value < 0) {
view.prompt =
@@ -965,6 +1031,9 @@ states.gain_hero_points = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
gain_hp() {
const value = get_active_node_args().v;
gain_hero_points(get_active_faction(), value);
@@ -972,7 +1041,7 @@ states.gain_hero_points = {
},
lose_hp() {
const value = get_active_node_args().v;
- lose_hero_point(get_active_faction(), value);
+ lose_hero_points(get_active_faction(), value);
resolve_active_and_proceed();
},
skip() {
@@ -990,7 +1059,7 @@ states.game_over = {
function resolve_player_with_most_hero_points(faction) {
const value = get_active_node_args().v;
if (value < 0) {
- lose_hero_point(faction, value);
+ lose_hero_points(faction, value);
}
else {
gain_hero_points(faction, value);
@@ -1000,6 +1069,7 @@ function resolve_player_with_most_hero_points(faction) {
states.select_player_with_most_hero_points = {
inactive: 'choose a Player',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt =
v < 0
@@ -1010,6 +1080,9 @@ states.select_player_with_most_hero_points = {
gen_action(faction_player_map[faction_id]);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
Anarchist() {
resolve_player_with_most_hero_points(data_1.ANARCHISTS_ID);
},
@@ -1020,15 +1093,10 @@ states.select_player_with_most_hero_points = {
resolve_player_with_most_hero_points(data_1.MODERATES_ID);
},
};
-states.move_attacks = {
- inactive: 'move attacks',
- prompt() {
- view.prompt = 'Choose a Front';
- },
-};
states.move_track = {
inactive: 'move a Track',
prompt() {
+ gen_spend_hero_points();
const node = get_active_node();
const track = node.a.t;
const value = node.a.v;
@@ -1050,6 +1118,9 @@ states.move_track = {
gen_action_standee(track);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
standee(s) {
const node = get_active_node();
let value = node.a.v;
@@ -1065,11 +1136,15 @@ states.move_track = {
states.move_track_up_or_down = {
inactive: 'move a track',
prompt() {
+ gen_spend_hero_points();
const node = get_active_node();
view.prompt = `Move ${get_track_name(node.a.track_id)} up or down`;
gen_action('up');
gen_action('down');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
down() {
const node = get_active_node();
move_track(node.a.track_id, -1 * node.a.strength);
@@ -1084,11 +1159,15 @@ states.move_track_up_or_down = {
states.peek_fascist_cards = {
inactive: 'peek at Fascist cards',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose one card to return to the top of the deck';
for (const c of game.selectable_cards) {
gen_action_card(c);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c) {
game.top_of_events_deck = c;
for (const ec of game.selectable_cards) {
@@ -1100,10 +1179,16 @@ states.peek_fascist_cards = {
resolve_active_and_proceed();
},
};
+function resolve_spend_hp() {
+ insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction()));
+ log('Spends Hero Points');
+ next();
+}
states.player_turn = {
inactive: 'play their turn',
prompt() {
- const faction_id = get_faction_id(game.active);
+ gen_spend_hero_points();
+ const faction_id = get_active_faction();
const can_spend_hp = game.faction_turn === faction_id && game.hero_points[faction_id] > 0;
const can_play_card = game.selected_cards[faction_id].length > 0;
view.prompt = 'Play a card or spend Hero points';
@@ -1123,9 +1208,9 @@ states.player_turn = {
else {
gen_action('done');
}
- if (can_spend_hp) {
- gen_action('spend_hp');
- }
+ },
+ spend_hp() {
+ resolve_spend_hp();
},
done() {
game.faction_turn = null;
@@ -1149,20 +1234,19 @@ states.player_turn = {
insert_before_active_node(create_effects_node(effects));
next();
},
- spend_hp() {
- insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction()));
- log('Spends Hero Points');
- next();
- },
};
states.remove_blank_marker = {
inactive: 'remove a Blank marker',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Remove a Blank marker';
for (const b of game.triggered_track_effects) {
gen_action_blank_marker(b);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
blank_marker(b) {
const faction = get_active_faction();
pay_hero_points(faction, 1);
@@ -1177,6 +1261,7 @@ states.remove_blank_marker = {
states.remove_attack_from_fronts = {
inactive: 'remove attacks',
prompt() {
+ gen_spend_hero_points();
const { f, v: card_id } = get_active_node_args();
view.prompt =
card_id === 6
@@ -1199,6 +1284,9 @@ states.remove_attack_from_fronts = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(id) {
const { f, v: card_id } = get_active_node_args();
const removed_value = card_id === 6 ? 1 : Math.min(3, Math.abs(game.fronts[id].value));
@@ -1233,6 +1321,7 @@ states.remove_attack_from_fronts = {
states.return_card = {
inactive: 'return a card to their hand',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to return to your hand';
if (game.selectable_cards.length === 0) {
view.prompt = 'No card in trash to return. You must skip';
@@ -1242,6 +1331,9 @@ states.return_card = {
gen_action_card(c);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c) {
const faction = get_active_faction();
array_remove(game.trash[faction], game.trash[faction].indexOf(c));
@@ -1353,6 +1445,7 @@ states.spend_hero_points = {
states.swap_card_tableau_hand = {
inactive: 'swap cards',
prompt() {
+ gen_spend_hero_points();
view.prompt =
'Choose a card in your tableau and a card in your hand to swap';
const faction = get_active_faction();
@@ -1376,6 +1469,9 @@ states.swap_card_tableau_hand = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c) {
console.log('card', c);
const faction = get_active_faction();
@@ -1403,13 +1499,14 @@ states.swap_card_tableau_hand = {
function resolve_take_hero_points(faction) {
const { v } = get_active_node_args();
const amount = Math.min(v, game.hero_points[faction]);
- lose_hero_point(faction, amount);
+ lose_hero_points(faction, amount);
gain_hero_points(get_active_faction(), amount);
resolve_active_and_proceed();
}
states.take_hero_points = {
inactive: 'take Hero Points',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt =
v === 1
@@ -1422,6 +1519,9 @@ states.take_hero_points = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
Anarchist() {
resolve_take_hero_points(data_1.ANARCHISTS_ID);
},
@@ -1435,10 +1535,14 @@ states.take_hero_points = {
states.use_organization_medallion = {
inactive: 'use Organization Medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Use Organization Medallion?';
gen_action('yes');
gen_action('no');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
yes() {
const faction = get_active_faction();
pay_hero_points(faction, 1);
@@ -1462,10 +1566,14 @@ states.use_organization_medallion = {
states.use_strategy_medallion = {
inactive: 'use Strategy Medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Use Strategy Medallion?';
gen_action('yes');
gen_action('no');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
yes() {
game.used_medallions.push(data_1.STRATEGY_MEDALLION_ID);
const { f } = get_active_node_args();
@@ -1587,7 +1695,7 @@ function add_glory(faction, amount, indent = false) {
}
function check_activate_icon() {
if (game.bonuses[data_1.MORALE_BONUS] === data_1.ON) {
- insert_after_active_node(create_leaf_node('activate_icon', get_active_faction_id()));
+ insert_after_active_node(create_leaf_node('activate_icon', get_active_faction()));
}
resolve_active_and_proceed();
}
@@ -1696,6 +1804,7 @@ function end_of_year() {
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('checkpoint'));
game.engine.push(create_function_node('start_year'));
game.top_of_events_deck = null;
next();
@@ -2031,7 +2140,7 @@ function resolve_effect(effect) {
{
condition: effect.type === 'hero_points' && effect.target === data_1.ALL_PLAYERS,
resolve: () => {
- return create_seq_node(get_player_order().map((faction) => create_leaf_node('gain_hero_points', faction, {
+ return create_seq_node(get_player_order().map((faction) => create_leaf_node('hero_points', faction, {
v: effect.value,
})));
},
@@ -2039,20 +2148,20 @@ function resolve_effect(effect) {
{
condition: effect.type === 'hero_points' && effect.target === data_1.SELF,
resolve: () => {
- return create_leaf_node('gain_hero_points', faction, args);
+ return create_leaf_node('hero_points', faction, args);
},
},
{
condition: effect.type === 'hero_points' &&
role_ids.includes(effect.target),
resolve: () => {
- return create_leaf_node('gain_hero_points', effect.target, args);
+ return create_leaf_node('hero_points', effect.target, args);
},
},
{
condition: effect.type === 'hero_points' && effect.target === data_1.INITIATIVE_PLAYER,
resolve: () => {
- return create_leaf_node('gain_hero_points', game.initiative, args);
+ return create_leaf_node('hero_points', game.initiative, args);
},
},
{
@@ -2134,7 +2243,7 @@ function draw_fascist_card() {
}
return draw_card(list_deck(data_1.FASCIST_ID));
}
-function lose_hero_point(faction, value) {
+function lose_hero_points(faction, value) {
const points_lost = Math.min(game.hero_points[faction], Math.abs(value));
game.hero_points.pool += points_lost;
game.hero_points[faction] -= points_lost;
@@ -2185,15 +2294,9 @@ function log_h3(msg) {
function get_active_faction() {
return player_faction_map[game.active];
}
-function get_active_faction_id() {
- return player_faction_map[game.active];
-}
function get_blank_marker_id(track_id, space_id) {
return track_id * 11 + space_id;
}
-function get_faction_id(player) {
- return player_faction_map[player];
-}
function get_front_name(id) {
return front_names[id];
}
@@ -2209,7 +2312,7 @@ function get_defeated_front_count() {
}
return count;
}
-function get_icon_count_in_tableau(icon, faction = get_active_faction_id()) {
+function get_icon_count_in_tableau(icon, faction = get_active_faction()) {
let count = 0;
for (const c of game.tableaus[faction]) {
const card = cards[c];
diff --git a/rules.ts b/rules.ts
index 2f80d49..ff2541d 100644
--- a/rules.ts
+++ b/rules.ts
@@ -171,6 +171,15 @@ function gen_action_standee(track_id: number) {
gen_action('standee', track_id);
}
+function gen_spend_hero_points() {
+ const faction = get_active_faction();
+ const can_spend_hp =
+ game.faction_turn === faction && game.hero_points[faction] > 0;
+ if (can_spend_hp) {
+ gen_action('spend_hp');
+ }
+}
+
// function gen_action_space(space) {
// gen_action('space', space);
// }
@@ -279,15 +288,42 @@ function setup_final_bid() {
}
function setup_player_turn() {
- const player_order = get_player_order();
- game.engine = player_order.map((faction_id) =>
- create_seq_node([
- create_function_node('start_of_player_turn', { f: faction_id }),
- create_leaf_node('player_turn', faction_id),
- ])
- );
- game.engine.push(create_function_node('resolve_fascist_test'));
- game.engine.push(create_function_node('setup_bag_of_glory'));
+ const next_faction =
+ game.first_player === null
+ ? get_player_order()[0]
+ : get_next_faction(get_active_faction());
+
+ if (game.first_player === null) {
+ game.first_player = next_faction;
+ }
+
+ // const player_order = get_player_order();
+ // game.engine = player_order.map((faction_id) =>
+ // create_seq_node([
+ // create_function_node('start_of_player_turn', { f: faction_id }),
+ // create_leaf_node('player_turn', faction_id),
+ // ])
+ // );
+ game.engine = [
+ create_function_node('start_of_player_turn', { f: next_faction }),
+ create_leaf_node('player_turn', next_faction),
+ create_function_node('end_of_player_turn', { f: next_faction }),
+ ];
+
+ // game.engine.push(create_function_node('resolve_fascist_test'));
+ // game.engine.push(create_function_node('setup_bag_of_glory'));
+ next();
+}
+
+function end_of_player_turn() {
+ if (get_next_faction(get_active_faction()) === game.first_player) {
+ game.engine = [
+ create_function_node('resolve_fascist_test'),
+ create_function_node('setup_bag_of_glory'),
+ ];
+ } else {
+ game.engine = [create_function_node('setup_player_turn')];
+ }
next();
}
@@ -302,8 +338,8 @@ function start_of_player_turn() {
const engine_functions: Record<string, Function> = {
check_activate_icon,
checkpoint,
+ end_of_player_turn,
end_of_turn,
- // end_of_year,
setup_bag_of_glory,
setup_choose_card,
setup_final_bid,
@@ -541,6 +577,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
},
},
glory: [],
+ first_player: null,
hands: {
a: [],
c: [],
@@ -660,12 +697,16 @@ function start_turn() {
states.activate_icon = {
inactive: 'activate an icon',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose an icon to activate';
const c = cards[game.played_card] as PlayerCard;
for (const i of c.icons) {
gen_action(i);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
add_to_front() {
insert_after_active_node(
create_leaf_node('add_to_front', get_active_faction(), {
@@ -790,6 +831,7 @@ states.activate_icon = {
states.add_card_to_tableau = {
inactive: 'add a card to their tableau',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to add to your tableau';
const faction = get_active_faction();
for (const c of game.hands[faction]) {
@@ -799,6 +841,9 @@ states.add_card_to_tableau = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c: CardId) {
const faction_id = get_active_faction();
const card = cards[c];
@@ -817,9 +862,13 @@ states.add_card_to_tableau = {
states.add_glory = {
inactive: 'add tokens to the Bag of Glory',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Add tokens to the Bag of Glory';
gen_action('add_glory');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
add_glory() {
let number = 1;
if (game.turn === 4) {
@@ -833,6 +882,7 @@ states.add_glory = {
states.add_to_front = {
inactive: 'add strength to a Front',
prompt() {
+ gen_spend_hero_points();
const args = get_active_node_args();
const possible_fronts = get_fronts_to_add_to(args.t);
view.prompt =
@@ -843,6 +893,9 @@ states.add_to_front = {
gen_action_front(f);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(f: FrontId) {
const value = get_active_node_args().v;
update_front(f, value, get_active_faction());
@@ -853,6 +906,7 @@ states.add_to_front = {
states.attack_front = {
inactive: 'attack a Front',
prompt() {
+ gen_spend_hero_points();
const { t: target, n } = get_active_node_args();
let fronts: Array<FrontId> = [];
@@ -875,6 +929,9 @@ states.attack_front = {
fronts.forEach((id) => gen_action('front', id));
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(f: FrontId) {
const node = get_active_node();
const value = node.a.v;
@@ -935,6 +992,7 @@ states.break_tie_winner = {
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose area of the board to affect';
for (const track of tracks) {
gen_action_standee(track.id);
@@ -949,6 +1007,9 @@ states.choose_area_ap = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
bonus(b: number) {
// Turn on bonus
update_bonus(b, ON);
@@ -965,13 +1026,13 @@ states.choose_area_ap = {
},
front(f: FrontId) {
const s: number = get_active_node_args().strength;
- update_front(f, s, get_active_faction_id());
+ update_front(f, s, get_active_faction());
resolve_active_and_proceed();
},
standee(track_id: number) {
insert_after_active_node({
t: leaf_node,
- p: get_active_faction_id(),
+ p: get_active_faction(),
s: 'move_track_up_or_down',
a: {
track_id,
@@ -985,6 +1046,7 @@ states.choose_area_ap = {
states.change_bonus = {
inactive: 'select Bonus',
prompt() {
+ gen_spend_hero_points();
const args = get_active_node_args();
if (
(args.v === ON &&
@@ -1008,6 +1070,9 @@ states.change_bonus = {
gen_action_bonus(args.t);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
bonus(b: number) {
const value = get_active_node_args().v;
update_bonus(b, value);
@@ -1021,6 +1086,7 @@ states.change_bonus = {
states.choose_card = {
inactive: 'choose a card',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to play this turn';
const faction = get_active_faction();
const hand = game.hands[faction];
@@ -1030,6 +1096,9 @@ states.choose_card = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c: CardId) {
const faction = get_active_faction();
game.selected_cards[faction].push(c);
@@ -1073,6 +1142,7 @@ states.choose_final_bid = {
states.choose_medallion = {
inactive: 'choose a medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a medallion';
for (let m of game.medallions.pool) {
gen_action_medallion(m);
@@ -1081,6 +1151,9 @@ states.choose_medallion = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
medallion(m: number) {
const faction = get_active_faction();
const medallion = medallions[m];
@@ -1125,10 +1198,14 @@ states.confirm_turn = {
states.draw_card = {
inactive: 'draw a card',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt = v === 1 ? 'Draw a card' : `Draw ${v} cards`;
gen_action(v === 1 ? 'draw_card' : 'draw_cards');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
draw_card() {
const { v } = get_active_node_args();
draw_hand_cards(get_active_faction(), v);
@@ -1178,10 +1255,10 @@ states.end_of_year_discard = {
},
};
-// TODO: rename to change_hero_points
-states.gain_hero_points = {
+states.hero_points = {
inactive: 'gain Hero Points',
prompt() {
+ gen_spend_hero_points();
const value = get_active_node_args().v;
if (value < 0) {
view.prompt =
@@ -1200,6 +1277,9 @@ states.gain_hero_points = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
gain_hp() {
const value = get_active_node_args().v;
gain_hero_points(get_active_faction(), value);
@@ -1207,7 +1287,7 @@ states.gain_hero_points = {
},
lose_hp() {
const value = get_active_node_args().v;
- lose_hero_point(get_active_faction(), value)
+ lose_hero_points(get_active_faction(), value);
resolve_active_and_proceed();
},
skip() {
@@ -1227,7 +1307,7 @@ states.game_over = {
function resolve_player_with_most_hero_points(faction: FactionId) {
const value = get_active_node_args().v;
if (value < 0) {
- lose_hero_point(faction, value);
+ lose_hero_points(faction, value);
} else {
gain_hero_points(faction, value);
}
@@ -1237,6 +1317,7 @@ function resolve_player_with_most_hero_points(faction: FactionId) {
states.select_player_with_most_hero_points = {
inactive: 'choose a Player',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt =
v < 0
@@ -1248,6 +1329,9 @@ states.select_player_with_most_hero_points = {
gen_action(faction_player_map[faction_id]);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
Anarchist() {
resolve_player_with_most_hero_points(ANARCHISTS_ID);
},
@@ -1259,17 +1343,10 @@ states.select_player_with_most_hero_points = {
},
};
-// TODO: implement
-states.move_attacks = {
- inactive: 'move attacks',
- prompt() {
- view.prompt = 'Choose a Front';
- },
-};
-
states.move_track = {
inactive: 'move a Track',
prompt() {
+ gen_spend_hero_points();
const node = get_active_node();
const track = node.a.t;
const value = node.a.v;
@@ -1293,6 +1370,9 @@ states.move_track = {
gen_action_standee(track);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
standee(s: number) {
const node = get_active_node();
let value = node.a.v;
@@ -1314,11 +1394,15 @@ states.move_track = {
states.move_track_up_or_down = {
inactive: 'move a track',
prompt() {
+ gen_spend_hero_points();
const node = get_active_node();
view.prompt = `Move ${get_track_name(node.a.track_id)} up or down`;
gen_action('up');
gen_action('down');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
down() {
const node = get_active_node();
move_track(node.a.track_id, -1 * node.a.strength);
@@ -1334,11 +1418,15 @@ states.move_track_up_or_down = {
states.peek_fascist_cards = {
inactive: 'peek at Fascist cards',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose one card to return to the top of the deck';
for (const c of game.selectable_cards) {
gen_action_card(c);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c: CardId) {
game.top_of_events_deck = c;
for (const ec of game.selectable_cards) {
@@ -1351,10 +1439,22 @@ states.peek_fascist_cards = {
},
};
+function resolve_spend_hp() {
+ // insert spend hero points node before current node
+ // so it will return to current node after resolving
+ insert_before_active_node(
+ create_leaf_node('spend_hero_points', get_active_faction())
+ );
+ log('Spends Hero Points');
+
+ next();
+}
+
states.player_turn = {
inactive: 'play their turn',
prompt() {
- const faction_id = get_faction_id(game.active as Player);
+ gen_spend_hero_points();
+ const faction_id = get_active_faction();
const can_spend_hp =
game.faction_turn === faction_id && game.hero_points[faction_id] > 0;
@@ -1378,9 +1478,9 @@ states.player_turn = {
} else {
gen_action('done');
}
- if (can_spend_hp) {
- gen_action('spend_hp');
- }
+ },
+ spend_hp() {
+ resolve_spend_hp();
},
done() {
game.faction_turn = null;
@@ -1409,27 +1509,21 @@ states.player_turn = {
next();
},
- spend_hp() {
- // insert spend hero points node before current node
- // so it will return to current node after resolving
- insert_before_active_node(
- create_leaf_node('spend_hero_points', get_active_faction())
- );
- log('Spends Hero Points');
-
- next();
- },
};
states.remove_blank_marker = {
inactive: 'remove a Blank marker',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Remove a Blank marker';
for (const b of game.triggered_track_effects) {
gen_action_blank_marker(b);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
blank_marker(b: number) {
const faction = get_active_faction();
pay_hero_points(faction, 1);
@@ -1461,6 +1555,7 @@ states.remove_blank_marker = {
states.remove_attack_from_fronts = {
inactive: 'remove attacks',
prompt() {
+ gen_spend_hero_points();
const { f, v: card_id } = get_active_node_args();
view.prompt =
card_id === 6
@@ -1484,6 +1579,9 @@ states.remove_attack_from_fronts = {
gen_action('skip');
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
front(id: FrontId) {
const { f, v: card_id } = get_active_node_args();
@@ -1528,6 +1626,7 @@ states.remove_attack_from_fronts = {
states.return_card = {
inactive: 'return a card to their hand',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Choose a card to return to your hand';
if (game.selectable_cards.length === 0) {
view.prompt = 'No card in trash to return. You must skip';
@@ -1537,6 +1636,9 @@ states.return_card = {
gen_action_card(c);
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c: CardId) {
const faction = get_active_faction();
array_remove(game.trash[faction], game.trash[faction].indexOf(c));
@@ -1658,6 +1760,7 @@ states.spend_hero_points = {
states.swap_card_tableau_hand = {
inactive: 'swap cards',
prompt() {
+ gen_spend_hero_points();
view.prompt =
'Choose a card in your tableau and a card in your hand to swap';
const faction = get_active_faction();
@@ -1689,6 +1792,9 @@ states.swap_card_tableau_hand = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c: CardId) {
console.log('card', c);
const faction = get_active_faction();
@@ -1727,7 +1833,7 @@ states.swap_card_tableau_hand = {
function resolve_take_hero_points(faction: FactionId) {
const { v } = get_active_node_args();
const amount = Math.min(v, game.hero_points[faction]);
- lose_hero_point(faction, amount);
+ lose_hero_points(faction, amount);
gain_hero_points(get_active_faction(), amount);
resolve_active_and_proceed();
}
@@ -1735,6 +1841,7 @@ function resolve_take_hero_points(faction: FactionId) {
states.take_hero_points = {
inactive: 'take Hero Points',
prompt() {
+ gen_spend_hero_points();
const { v } = get_active_node_args();
view.prompt =
v === 1
@@ -1747,6 +1854,9 @@ states.take_hero_points = {
}
}
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
Anarchist() {
resolve_take_hero_points(ANARCHISTS_ID);
},
@@ -1761,11 +1871,15 @@ states.take_hero_points = {
states.use_organization_medallion = {
inactive: 'use Organization Medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Use Organization Medallion?';
gen_action('yes');
gen_action('no');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
yes() {
const faction = get_active_faction();
pay_hero_points(faction, 1);
@@ -1793,11 +1907,15 @@ states.use_organization_medallion = {
states.use_strategy_medallion = {
inactive: 'use Strategy Medallion',
prompt() {
+ gen_spend_hero_points();
view.prompt = 'Use Strategy Medallion?';
gen_action('yes');
gen_action('no');
},
+ spend_hp() {
+ resolve_spend_hp();
+ },
yes() {
game.used_medallions.push(STRATEGY_MEDALLION_ID);
const { f } = get_active_node_args();
@@ -1982,7 +2100,7 @@ function add_glory(
function check_activate_icon() {
if (game.bonuses[MORALE_BONUS] === ON) {
insert_after_active_node(
- create_leaf_node('activate_icon', get_active_faction_id())
+ create_leaf_node('activate_icon', get_active_faction())
);
}
resolve_active_and_proceed();
@@ -2103,6 +2221,7 @@ function end_of_year() {
game.engine = get_player_order().map((f) =>
create_leaf_node('end_of_year_discard', f)
);
+ game.engine.push(create_function_node('checkpoint'));
game.engine.push(create_function_node('start_year'));
// New deck is used for next year so clear top card
@@ -2553,7 +2672,7 @@ function resolve_effect(
resolve: () => {
return create_seq_node(
get_player_order().map((faction) =>
- create_leaf_node('gain_hero_points', faction, {
+ create_leaf_node('hero_points', faction, {
v: effect.value,
})
)
@@ -2563,7 +2682,7 @@ function resolve_effect(
{
condition: effect.type === 'hero_points' && effect.target === SELF,
resolve: () => {
- return create_leaf_node('gain_hero_points', faction, args);
+ return create_leaf_node('hero_points', faction, args);
},
},
{
@@ -2572,7 +2691,7 @@ function resolve_effect(
role_ids.includes(effect.target as FactionId),
resolve: () => {
return create_leaf_node(
- 'gain_hero_points',
+ 'hero_points',
effect.target as FactionId,
args
);
@@ -2582,7 +2701,7 @@ function resolve_effect(
condition:
effect.type === 'hero_points' && effect.target === INITIATIVE_PLAYER,
resolve: () => {
- return create_leaf_node('gain_hero_points', game.initiative, args);
+ return create_leaf_node('hero_points', game.initiative, args);
},
},
{
@@ -2691,7 +2810,7 @@ function draw_fascist_card(): CardId {
return draw_card(list_deck(FASCIST_ID));
}
-function lose_hero_point(faction: FactionId, value: number) {
+function lose_hero_points(faction: FactionId, value: number) {
const points_lost = Math.min(game.hero_points[faction], Math.abs(value));
game.hero_points.pool += points_lost;
game.hero_points[faction] -= points_lost;
@@ -2797,18 +2916,10 @@ function get_active_faction(): FactionId {
return player_faction_map[game.active];
}
-function get_active_faction_id(): FactionId {
- return player_faction_map[game.active];
-}
-
function get_blank_marker_id(track_id: number, space_id: number) {
return track_id * 11 + space_id;
}
-function get_faction_id(player: Player): FactionId {
- return player_faction_map[player];
-}
-
function get_front_name(id: FrontId | 'd' | 'v') {
return front_names[id];
}
@@ -2831,7 +2942,7 @@ function get_defeated_front_count() {
function get_icon_count_in_tableau(
icon: Icon,
- faction: FactionId = get_active_faction_id()
+ faction: FactionId = get_active_faction()
) {
let count = 0;
for (const c of game.tableaus[faction]) {
diff --git a/types.d.ts b/types.d.ts
index eda1c59..7c2e648 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -43,6 +43,10 @@ export interface Game {
* a player becomes active in another players turn.
*/
faction_turn: FactionId | null;
+ /**
+ * First player of current game turn
+ */
+ first_player: FactionId | null;
fronts: {
a: Front;
m: Front;