summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js188
1 files changed, 105 insertions, 83 deletions
diff --git a/rules.js b/rules.js
index 455475f..ced9de7 100644
--- a/rules.js
+++ b/rules.js
@@ -79,7 +79,7 @@ function gen_spend_hero_points() {
}
}
function action(state, player, action, arg) {
- if (action !== 'undo') {
+ if (action !== 'undo' && state.state !== 'choose_card') {
state.undo = push_undo();
}
game = state;
@@ -92,13 +92,13 @@ function action(state, player, action, arg) {
throw new Error('Invalid action: ' + action);
return game;
}
-const leaf_node = 'l';
+const state_node = 'l';
const seq_node = 's';
const function_node = 'f';
const resolved = 1;
-function create_leaf_node(state, faction, args) {
+function create_state_node(state, faction, args) {
return {
- t: leaf_node,
+ t: state_node,
s: state,
p: faction,
a: args,
@@ -121,27 +121,27 @@ function create_seq_node(children) {
}
function checkpoint() {
if (game.undo.length > 0) {
- insert_after_active_node(create_leaf_node('confirm_turn', get_active_faction()));
+ insert_after_active_node(create_state_node('confirm_turn', get_active_faction()));
}
resolve_active_and_proceed();
}
function setup_bag_of_glory() {
log_h1('Bag of Glory');
game.engine = [
- create_leaf_node('add_glory', game.initiative),
+ create_state_node('add_glory', game.initiative),
create_function_node('end_of_turn'),
];
next();
}
function setup_choose_card() {
- game.engine = [create_leaf_node('choose_card', 'all')];
+ game.engine = [create_state_node('choose_card', 'all')];
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 = player_order.map((faction_id) => create_state_node('choose_final_bid', faction_id));
game.engine.push(create_function_node('checkpoint'));
game.engine.push(create_function_node('resolve_final_bid'));
game.engine.push(create_function_node('setup_choose_card'));
@@ -156,7 +156,7 @@ function setup_player_turn() {
}
game.engine = [
create_function_node('start_of_player_turn', { f: next_faction }),
- create_leaf_node('player_turn', next_faction),
+ create_state_node('player_turn', next_faction),
create_function_node('end_of_player_turn', { f: next_faction }),
];
next();
@@ -165,7 +165,7 @@ function check_end_of_year_discard() {
const { f: faction } = get_active_node_args();
if (game.hands[faction].length > get_hand_limit(faction) ||
game.tableaus[faction].length > game.year) {
- insert_after_active_node(create_leaf_node('end_of_year_discard', faction));
+ insert_after_active_node(create_state_node('end_of_year_discard', faction));
}
resolve_active_and_proceed();
}
@@ -173,7 +173,7 @@ function end_of_player_turn() {
const { f: faction } = get_active_node_args();
if (get_next_faction_in_player_order(faction) === game.first_player) {
game.engine = [
- create_leaf_node('change_active_player', game.initiative),
+ create_state_node('change_active_player', game.initiative),
create_function_node('resolve_fascist_test'),
create_function_node('setup_bag_of_glory'),
];
@@ -227,7 +227,7 @@ const engine_functions = {
};
function get_active(engine) {
for (let i of engine) {
- if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) {
+ if ((i.t === state_node || i.t === function_node) && i.r !== resolved) {
return { parent: engine, node: i };
}
if (i.t === seq_node) {
@@ -246,7 +246,7 @@ function get_active_node(engine = game.engine) {
function get_nodes_for_state(state, engine = game.engine) {
let nodes = [];
for (let i of engine) {
- if (i.t === leaf_node && i.s === state) {
+ if (i.t === state_node && i.s === state) {
nodes.push(i);
}
if (i.t === seq_node) {
@@ -257,14 +257,14 @@ function get_nodes_for_state(state, engine = game.engine) {
}
function get_active_node_args() {
const node = get_active_node(game.engine);
- if (node.t === leaf_node || node.t === function_node) {
+ if (node.t === state_node || node.t === function_node) {
return node.a ?? {};
}
return null;
}
function update_active_node_args(args) {
const node = get_active_node(game.engine);
- if (node.t === leaf_node || node.t === function_node) {
+ if (node.t === state_node || node.t === function_node) {
node.a = {
...node.a,
...args,
@@ -320,7 +320,7 @@ function next(checkpoint = false) {
const current_active = game.active;
const next_active = get_next_active(node.p);
if (next_active !== current_active && game.undo.length > 0) {
- insert_before_active_node(create_leaf_node('confirm_turn', get_active_faction()));
+ insert_before_active_node(create_state_node('confirm_turn', get_active_faction()));
game.state = 'confirm_turn';
return;
}
@@ -380,17 +380,20 @@ function game_view(state, current) {
if (game.state === 'game_over') {
view.prompt = game.victory;
}
- else if (current !== game.active) {
+ else if (current !== game.active &&
+ !game.active.includes(current)) {
let inactive = states[game.state].inactive || game.state;
- view.prompt = `Waiting for ${game.active} to ${inactive}.`;
+ view.prompt = Array.isArray(game.active)
+ ? `Waiting for other players to ${inactive}.`
+ : `Waiting for ${game.active} to ${inactive}.`;
}
else {
view.actions = {};
- states[game.state].prompt();
if (game.undo && game.undo.length > 0)
view.actions.undo = 1;
else
view.actions.undo = 0;
+ states[game.state].prompt(current);
}
return view;
}
@@ -560,7 +563,7 @@ states.activate_icon = {
resolve_spend_hp();
},
add_to_front() {
- insert_after_active_node(create_leaf_node('add_to_front', get_active_faction(), {
+ insert_after_active_node(create_state_node('add_to_front', get_active_faction(), {
t: data_1.ANY,
v: get_icon_count_in_tableau('add_to_front'),
}));
@@ -890,15 +893,10 @@ states.choose_area_ap = {
resolve_active_and_proceed();
},
standee(track_id) {
- insert_after_active_node({
- t: leaf_node,
- p: get_active_faction(),
- s: 'move_track_up_or_down',
- a: {
- track_id,
- strength: get_active_node()?.a.strength,
- },
- });
+ insert_after_active_node(create_state_node('move_track_up_or_down', get_active_faction(), {
+ track_id,
+ strength: get_active_node()?.a.strength,
+ }));
resolve_active_and_proceed();
},
};
@@ -938,17 +936,12 @@ states.change_bonus = {
resolve_active_and_proceed();
},
};
-states.choose_card = {
+states.play_card = {
inactive: 'choose a card',
- prompt(player) {
- console.log('player', player);
+ prompt() {
gen_spend_hero_points();
- const { src } = get_active_node_args();
- view.prompt = 'Choose a card to play this turn';
- if (src === 'momentum') {
- view.prompt = 'Choose a card to play';
- }
- const faction = player_faction_map[player];
+ view.prompt = 'Choose a card to play';
+ const faction = get_active_faction();
const hand = game.hands[faction];
for (let c of hand) {
if (!game.selected_cards[faction].includes(c)) {
@@ -959,12 +952,49 @@ states.choose_card = {
spend_hp() {
resolve_spend_hp();
},
+ card(c) {
+ const faction = get_active_faction();
+ game.selected_cards[faction].push(c);
+ game.played_card = game.selected_cards[faction][0];
+ resolve_active_and_proceed();
+ },
+};
+states.choose_card = {
+ inactive: 'choose a card',
+ prompt(player) {
+ gen_spend_hero_points();
+ view.prompt = 'Choose a card to play this turn';
+ const faction = player_faction_map[player];
+ if (game.selected_cards[faction].length === 0) {
+ view.actions.undo = 0;
+ const hand = game.hands[faction];
+ for (let c of hand) {
+ if (!game.selected_cards[faction].includes(c)) {
+ gen_action_card(c);
+ }
+ }
+ }
+ else {
+ view.actions.undo = 1;
+ view.actions.confirm = 1;
+ view.prompt = 'Confirm your actions or undo';
+ }
+ },
+ spend_hp() {
+ resolve_spend_hp();
+ },
card(c, player) {
const faction = player_faction_map[player];
game.selected_cards[faction].push(c);
- const { src } = get_active_node_args();
- if (src === 'momentum') {
- game.played_card = game.selected_cards[faction][0];
+ },
+ undo(_, player) {
+ const faction = player_faction_map[player];
+ game.selected_cards[faction] = [];
+ },
+ confirm(_, player) {
+ set_delete(game.active, player);
+ if (game.active.length === 0) {
+ resolve_active_and_proceed();
}
},
};
@@ -1004,7 +1034,6 @@ function setup_momentum() {
return;
}
const node = get_nodes_for_state('player_turn')[0];
- console.log('node', node);
const player_needs_to_play_card = game.selected_cards[faction].length > 0;
const { use_ap, use_morale_bonus, resolving_event } = node.a ?? {};
if (player_needs_to_play_card ||
@@ -1017,7 +1046,7 @@ function setup_momentum() {
};
}
else {
- insert_before_active_node(create_leaf_node('choose_card', faction, {
+ insert_after_active_node(create_state_node('play_card', faction, {
src: 'momentum',
}));
}
@@ -1393,17 +1422,11 @@ states.peek_fascist_cards = {
},
};
function resolve_spend_hp() {
- insert_before_active_node(create_leaf_node('spend_hero_points', get_active_faction()));
+ insert_before_active_node(create_state_node('spend_hero_points', get_active_faction()));
log('Spends Hero Points');
next();
}
function set_player_turn_prompt({ can_play_card, can_spend_hp, use_ap, use_momentum, use_morale_bonus, }) {
- console.log('set_player_turn_prompt', {
- can_play_card,
- use_ap,
- use_morale_bonus,
- use_momentum,
- });
if (can_play_card && can_spend_hp) {
view.prompt = 'Play a card or spend Hero points';
}
@@ -1446,7 +1469,6 @@ states.player_turn = {
use_morale_bonus = use_morale_bonus && game.bonuses[data_1.MORALE_BONUS] === data_1.ON;
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;
- console.log('can_play_card', can_play_card);
if (use_momentum) {
gen_action('use_momentum');
if (use_ap || use_morale_bonus || can_play_card) {
@@ -1509,7 +1531,7 @@ states.player_turn = {
update_active_node_args({
use_ap: false,
});
- insert_before_active_node(create_leaf_node('choose_area_ap', faction, {
+ insert_before_active_node(create_state_node('choose_area_ap', faction, {
strength,
}));
next();
@@ -1526,7 +1548,7 @@ states.player_turn = {
update_active_node_args({
use_morale_bonus: false,
});
- insert_before_active_node(create_leaf_node('activate_icon', get_active_faction()));
+ insert_before_active_node(create_state_node('activate_icon', get_active_faction()));
next();
},
};
@@ -1593,7 +1615,7 @@ states.remove_attack_from_fronts = {
resolve_active_and_proceed();
}
else if (card_id === 39 || card_id === 16) {
- insert_after_active_node(create_leaf_node('attack_front', get_active_faction(), {
+ insert_after_active_node(create_state_node('attack_front', get_active_faction(), {
t: data_1.ANY,
v: card_id === 39 ? -2 : -1 * removed_value,
n: card_id === 16 ? id : undefined,
@@ -1605,7 +1627,7 @@ states.remove_attack_from_fronts = {
const { f, v: card_id } = get_active_node_args();
const values = Object.values(f ?? {});
if (card_id === 39 && values.length > 0) {
- insert_after_active_node(create_leaf_node('attack_front', get_active_faction(), {
+ insert_after_active_node(create_state_node('attack_front', get_active_faction(), {
t: data_1.ANY,
v: -2,
}));
@@ -1712,11 +1734,11 @@ states.spend_hero_points = {
const faction = get_active_faction();
pay_hero_points(faction, 1);
insert_after_active_node(create_seq_node([
- create_leaf_node('add_to_front', faction, {
+ create_state_node('add_to_front', faction, {
t: data_1.ANY,
v: 1,
}),
- create_leaf_node('spend_hero_points', faction),
+ create_state_node('spend_hero_points', faction),
]));
resolve_active_and_proceed();
},
@@ -1752,8 +1774,8 @@ states.spend_hero_points = {
game.used_medallions = [data_1.ARCHIVES_MEDALLION_ID];
}
insert_after_active_node(create_seq_node([
- create_leaf_node('remove_blank_marker', faction),
- create_leaf_node('spend_hero_points', faction),
+ create_state_node('remove_blank_marker', faction),
+ create_state_node('spend_hero_points', faction),
]));
resolve_active_and_proceed();
},
@@ -1771,11 +1793,11 @@ states.spend_hero_points = {
const faction = get_active_faction();
pay_hero_points(faction, amount);
insert_after_active_node(create_seq_node([
- create_leaf_node('move_track_up_or_down', faction, {
+ create_state_node('move_track_up_or_down', faction, {
track_id,
strength: 1,
}),
- create_leaf_node('spend_hero_points', faction),
+ create_state_node('spend_hero_points', faction),
]));
resolve_active_and_proceed();
},
@@ -2096,7 +2118,7 @@ function determine_winner() {
win_game(faction_player_map[winners[0]], highest_glory);
}
else {
- insert_after_active_node(create_leaf_node('break_tie_winner', game.initiative, {
+ insert_after_active_node(create_state_node('break_tie_winner', game.initiative, {
winners,
glory: highest_glory,
}));
@@ -2143,7 +2165,7 @@ function end_of_year() {
const player_order = get_player_order();
const engine = [];
for (let i = 0; i < glory_to_draw[game.year]; ++i) {
- engine.push(create_leaf_node('draw_glory', player_order[i % 3]));
+ engine.push(create_state_node('draw_glory', player_order[i % 3]));
}
engine.push(create_function_node('end_of_year_cleanup'));
game.engine = engine;
@@ -2200,7 +2222,7 @@ function gain_hero_points(faction_id, value, skip_abilities = false) {
logi(`${get_player(faction_id)} +${gain} ${gain === 1 ? 'Hero Point' : 'Hero Points'}`);
}
function game_over(result, victory) {
- insert_after_active_node(create_leaf_node('game_over', 'None'));
+ insert_after_active_node(create_state_node('game_over', 'None'));
game.result = result;
game.victory = victory;
game.undo = [];
@@ -2290,7 +2312,7 @@ function resolve_final_bid() {
win_final_bid(winners[0]);
}
else {
- insert_after_active_node(create_leaf_node('break_tie_final_bid', game.initiative, { winners }));
+ insert_after_active_node(create_state_node('break_tie_final_bid', game.initiative, { winners }));
}
resolve_active_and_proceed();
}
@@ -2391,7 +2413,7 @@ function can_use_medallion(medallion_id, faction) {
}
function insert_use_organization_medallion_node(track_id, value) {
const faction = get_active_faction();
- insert_after_active_node(create_leaf_node('use_organization_medallion', faction, {
+ insert_after_active_node(create_state_node('use_organization_medallion', faction, {
t: track_id,
v: value,
}));
@@ -2429,7 +2451,7 @@ function update_front(front_id, change, faction_id = null) {
faction_id !== undefined &&
game.fronts[front_id].value < 10 &&
can_use_medallion(data_1.STRATEGY_MEDALLION_ID)) {
- insert_after_active_node(create_leaf_node('use_strategy_medallion', get_active_faction(), {
+ insert_after_active_node(create_state_node('use_strategy_medallion', get_active_faction(), {
f: front_id,
}));
}
@@ -2501,87 +2523,87 @@ function resolve_effect(effect, source) {
return create_function_node(effect.target);
}
if (effect.type === 'state') {
- return create_leaf_node(effect.target, faction, {
+ return create_state_node(effect.target, faction, {
v: effect.value,
src: source,
});
}
let state = effect_type_state_map[effect.type];
if (state !== undefined) {
- return create_leaf_node(state, faction, args);
+ return create_state_node(state, faction, args);
}
const strategies = [
{
condition: effect.type === 'hero_points' &&
effect.target === data_1.PLAYER_WITH_MOST_HERO_POINTS,
resolve: () => {
- return create_leaf_node('select_player_with_most_hero_points', faction, args);
+ return create_state_node('select_player_with_most_hero_points', faction, args);
},
},
{
condition: effect.type === 'hero_points' && effect.target === data_1.ALL_PLAYERS,
resolve: () => {
- return create_seq_node(get_player_order().map((faction) => create_leaf_node('hero_points', faction, args)));
+ return create_seq_node(get_player_order().map((faction) => create_state_node('hero_points', faction, args)));
},
},
{
condition: effect.type === 'hero_points' && effect.target === data_1.SELF,
resolve: () => {
- return create_leaf_node('hero_points', faction, args);
+ return create_state_node('hero_points', faction, args);
},
},
{
condition: effect.type === 'hero_points' &&
role_ids.includes(effect.target),
resolve: () => {
- return create_leaf_node('hero_points', effect.target, args);
+ return create_state_node('hero_points', effect.target, args);
},
},
{
condition: effect.type === 'hero_points' && effect.target === data_1.INITIATIVE_PLAYER,
resolve: () => {
- return create_leaf_node('hero_points', game.initiative, args);
+ return create_state_node('hero_points', game.initiative, args);
},
},
{
condition: effect.type === 'draw_card' && effect.target === data_1.SELF,
resolve: () => {
- return create_leaf_node('draw_card', faction, args);
+ return create_state_node('draw_card', faction, args);
},
},
{
condition: effect.type === 'draw_card' && effect.target === data_1.INITIATIVE_PLAYER,
resolve: () => {
- return create_leaf_node('draw_card', game.initiative, args);
+ return create_state_node('draw_card', game.initiative, args);
},
},
{
condition: effect.type === 'draw_card' &&
role_ids.includes(effect.target),
resolve: () => {
- return create_leaf_node('draw_card', effect.target, args);
+ return create_state_node('draw_card', effect.target, args);
},
},
{
condition: effect.type === 'draw_card' && effect.target === data_1.ALL_PLAYERS,
resolve: () => {
- return create_seq_node(get_player_order(get_active_faction()).map((faction) => create_leaf_node('draw_card', faction, args)));
+ return create_seq_node(get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args)));
},
},
{
condition: effect.type === 'draw_card' && effect.target === data_1.OTHER_PLAYERS,
resolve: () => {
- const leaf_nodes = get_player_order(get_active_faction()).map((faction) => create_leaf_node('draw_card', faction, args));
- array_remove(leaf_nodes, 0);
- return create_seq_node(leaf_nodes);
+ const state_nodes = get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args));
+ array_remove(state_nodes, 0);
+ return create_seq_node(state_nodes);
},
},
{
condition: effect.type === 'play_card',
resolve: () => {
return create_seq_node([
- create_leaf_node('choose_card', faction, { src: source }),
- create_leaf_node('player_turn', faction, { src: source }),
+ create_state_node('play_card', faction, { src: source }),
+ create_state_node('player_turn', faction, { src: source }),
]);
},
},