summaryrefslogtreecommitdiff
path: root/rules.ts
diff options
context:
space:
mode:
Diffstat (limited to 'rules.ts')
-rw-r--r--rules.ts219
1 files changed, 165 insertions, 54 deletions
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]) {