summaryrefslogtreecommitdiff
path: root/rules.ts
diff options
context:
space:
mode:
authorFrans Bongers <fransbongers@franss-mbp.home>2024-12-15 21:38:52 +0100
committerFrans Bongers <fransbongers@franss-mbp.home>2024-12-15 21:38:52 +0100
commit72afc8e31c9dcef58b1308eddd4d1dde489e4670 (patch)
tree1e5ea6603d3144f9b45214c3d1466c4cd693dea9 /rules.ts
parent47b19b5e83850b5fac6e508468b227476d486025 (diff)
downloadland-and-freedom-72afc8e31c9dcef58b1308eddd4d1dde489e4670.tar.gz
victory and defeat on front and end game on defeat
Diffstat (limited to 'rules.ts')
-rw-r--r--rules.ts217
1 files changed, 152 insertions, 65 deletions
diff --git a/rules.ts b/rules.ts
index c6f01ab..2fea19e 100644
--- a/rules.ts
+++ b/rules.ts
@@ -6,6 +6,8 @@ import {
EngineNode,
EventCard,
FactionId,
+ Front,
+ FrontId,
FunctionNode,
Game,
Icon,
@@ -46,6 +48,10 @@ import data, {
TEAMWORK_BONUS,
MORALE_BONUS,
OFF,
+ VICTORY,
+ DEFEAT,
+ FRONTS,
+ create_effect,
// StaticData,
// PLAYER_WITH_MOST_HERO_POINTS,
} from './data';
@@ -177,7 +183,7 @@ const resolved = 1;
function create_leaf_node(
state: string,
- faction: FactionId,
+ faction: FactionId | 'None',
args?: any
): LeafNode {
return {
@@ -272,41 +278,11 @@ function get_active(
return null;
}
-// function get_active_node_parent(engine: EngineNode[]): EngineNode[] | null {
-// for (let i of engine) {
-// if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) {
-// return engine;
-// }
-// if (i.t === seq_node) {
-// const next_child = get_active_node_parent(i.c);
-// if (next_child !== null) {
-// return next_child;
-// }
-// }
-// }
-// return null;
-// }
-
function get_active_node(
engine: EngineNode[] = game.engine
): FunctionNode | LeafNode | null {
const a = get_active(engine);
return a === null ? null : a.node;
- // for (let i of engine) {
- // if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) {
- // return i;
- // }
- // // if (i.t === function_node && i.r !== resolved) {
- // // return i;
- // // }
- // if (i.t === seq_node) {
- // const next_child = get_active_node(i.c);
- // if (next_child !== null) {
- // return next_child;
- // }
- // }
- // }
- // return null;
}
function get_active_node_args(): any {
@@ -348,9 +324,7 @@ function insert_before_active_node(
}
function next() {
- console.log('next');
const node = get_active_node(game.engine);
- console.log('node', node);
if (node.t === function_node && engine_functions[node.f]) {
const args = node.a;
if (args) {
@@ -410,7 +384,9 @@ function game_view(state: Game, player: Player) {
year: game.year,
};
- if (player !== game.active) {
+ if (game.state === 'game_over') {
+ view.prompt = game.victory;
+ } else if (player !== game.active) {
let inactive = states[game.state].inactive || game.state;
view.prompt = `Waiting for ${game.active} to ${inactive}.`;
} else {
@@ -448,18 +424,22 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
a: {
value: -2,
contributions: [],
+ status: null,
},
m: {
value: -2,
contributions: [],
+ status: null,
},
n: {
value: -2,
contributions: [],
+ status: null,
},
s: {
value: -2,
contributions: [],
+ status: null,
},
},
glory: [],
@@ -660,7 +640,7 @@ states.add_to_front = {
gen_action_front(f);
}
},
- front(f: string) {
+ front(f: FrontId) {
const value = get_active_node_args().v;
update_front(f, value);
resolve_active_and_proceed();
@@ -673,14 +653,20 @@ states.attack_front = {
const node = get_active_node();
const front = node.a.t;
view.prompt = 'Attack ' + front_names[front];
+ let targets = [];
+
if (front === 'd' || front === 'v') {
- const fronts = get_fronts_closest_to(front);
- fronts.forEach((id) => gen_action('front', id));
+ targets = get_fronts_closest_to(front);
+ } else if (game.fronts[front].status === DEFEAT) {
+ targets = get_fronts_closest_to('d')
+ } else if (game.fronts[front].status === VICTORY) {
+ targets = get_fronts_to_add_to(ANY);
} else {
- gen_action_front(front);
+ targets.push(front);
}
+ targets.forEach((id) => gen_action('front', id));
},
- front(f: string) {
+ front(f: FrontId) {
const node = get_active_node();
const value = node.a.v;
update_front(f, value);
@@ -710,7 +696,7 @@ states.choose_area_ap = {
// TODO: insert action in case other bonus is OFF and AP left
resolve_active_and_proceed();
},
- front(f: string) {
+ front(f: FrontId) {
const s: number = get_active_node_args().strength;
update_front(f, s, get_active_faction_id());
resolve_active_and_proceed();
@@ -786,14 +772,20 @@ states.gain_hero_points = {
},
gain_hp() {
const value = get_active_node_args().v;
- if (game.hero_points.pool > value) {
- game.hero_points.pool -= value;
- game.hero_points[get_active_faction()] += value;
- }
+ gain_hero_points(get_active_faction(), value);
resolve_active_and_proceed();
},
};
+states.game_over = {
+ get inactive() {
+ return game.victory;
+ },
+ prompt() {
+ view.prompt = game.victory;
+ },
+};
+
states.lose_hero_points = {
inactive: 'choose a Player',
prompt() {
@@ -865,7 +857,7 @@ states.move_track_up_or_down = {
states.player_turn = {
inactive: 'play their turn',
prompt() {
- const faction_id = get_faction_id(game.active);
+ const faction_id = get_faction_id(game.active as Player);
const can_spend_hp = game.hero_points[faction_id] > 0;
const can_play_card = game.hands[faction_id].includes(
game.chosen_cards[faction_id]
@@ -893,7 +885,7 @@ states.player_turn = {
resolve_active_and_proceed();
},
play_for_ap() {
- const faction_id = get_faction_id(game.active);
+ const faction_id = get_faction_id(game.active as Player);
const card = game.chosen_cards[faction_id];
log_h3(`${game.active} plays ${cards[card].title} for the Action Points`);
array_remove(game.hands[faction_id], game.hands[faction_id].indexOf(card));
@@ -909,7 +901,7 @@ states.player_turn = {
next();
},
play_for_event() {
- const faction_id = get_faction_id(game.active);
+ const faction_id = get_faction_id(game.active as Player);
const card = game.chosen_cards[faction_id];
array_remove(game.hands[faction_id], game.hands[faction_id].indexOf(card));
game.trash[faction_id].push(card);
@@ -922,7 +914,7 @@ states.player_turn = {
spend_hp() {
insert_before_active_node({
t: leaf_node,
- p: get_faction_id(game.active),
+ p: get_faction_id(game.active as Player),
s: 'spend_hero_points',
});
// insert spend hero points node before current node
@@ -1083,12 +1075,46 @@ function end_of_year() {
start_year();
}
+function gain_hero_points_in_player_order(factions: FactionId[], value) {
+ for (const f of get_player_order()) {
+ if (factions.includes(f)) {
+ gain_hero_points(f, value);
+ }
+ }
+}
+
+function gain_hero_points(faction_id: FactionId, value: number) {
+ if (game.hero_points.pool === 0) {
+ return;
+ }
+ const gain = Math.min(game.hero_points.pool, value);
+ game.hero_points.pool -= gain;
+ game.hero_points[faction_id] += gain;
+ logi(
+ `${get_player(faction_id)} +${gain} ${
+ gain === 1 ? 'Hero Point' : 'Hero Points'
+ }`
+ );
+}
+
+function game_over(result: Player | 'None', victory: string) {
+ insert_after_active_node(create_leaf_node('game_over', 'None'));
+ // game.state = 'game_over';
+ // game.active = 'None';
+ game.result = result;
+ game.victory = victory;
+ log_br();
+ log(game.victory);
+}
+
function resolve_fascist_test() {
- console.log('resolve fascist test');
log_h2('Fascist Test', 'fascist');
const test = get_current_event().test;
- const test_passed = game.fronts[test.front].value >= test.value;
+ const status = game.fronts[test.front].status;
+ const test_passed =
+ status === VICTORY ||
+ (status !== DEFEAT && game.fronts[test.front].value >= test.value);
if (test_passed) {
log('The Test is passed');
} else {
@@ -1110,7 +1136,7 @@ function get_fronts_to_add_to(target: string): string[] {
if (target === CLOSEST_TO_DEFEAT || target === CLOSEST_TO_VICTORY) {
return get_fronts_closest_to(target);
} else if (target === ANY) {
- return ['a', 'm', 'n', 's'];
+ return FRONTS.filter((id) => game.fronts[id].status === null);
} else {
return [target];
}
@@ -1162,31 +1188,66 @@ function update_bonus(bonus_id: number, status: number) {
// TODO: acccount for victory / defeat of front
function update_front(
- f: string,
+ // f: string,
+ front_id: FrontId,
change: number,
faction_id: FactionId | null = null
) {
+ // Check teamwork bonus
const player_token_on_front =
- faction_id !== null && game.fronts[f].contributions.includes(faction_id);
+ faction_id !== null &&
+ game.fronts[front_id].contributions.includes(faction_id);
if (
game.bonuses[TEAMWORK_BONUS] === ON &&
change > 0 &&
faction_id !== null &&
!player_token_on_front &&
- game.fronts[f].contributions.length > 0
+ game.fronts[front_id].contributions.length > 0
) {
change += 1;
}
- game.fronts[f].value += change;
- logi(`${front_names[f]}: ${change > 0 ? '+' : ''}${change}`);
+
+ game.fronts[front_id].value += change;
+ logi(`${front_names[front_id]}: ${change > 0 ? '+' : ''}${change}`);
+
+ // Add token to front if player contributed
if (
faction_id !== null &&
- !game.fronts[f].contributions.includes(faction_id)
+ !game.fronts[front_id].contributions.includes(faction_id)
) {
- game.fronts[f].contributions.push(faction_id);
+ game.fronts[front_id].contributions.push(faction_id);
+ }
+
+ // Check victory / defeat on a front
+ if (game.fronts[front_id].value >= 10) {
+ victory_on_a_front(front_id);
+ } else if (game.fronts[front_id].value <= -10) {
+ defeat_on_a_front(front_id);
}
}
+function defeat_on_a_front(front_id: FrontId) {
+ game.fronts[front_id].status = DEFEAT;
+ log('Defeat on ' + get_front_name(front_id));
+ // Check game end
+ if (front_id === 'm' || get_defeated_front_count() == 2) {
+ game_over('None', 'All players lose the game!');
+ return;
+ }
+ insert_after_active_node(create_effects_node([
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('track', COLLECTIVIZATION, -1),
+ create_effect('track', SOVIET_SUPPORT, -1),
+ create_effect('track', FOREIGN_AID, -1),
+ ]));
+}
+
+function victory_on_a_front(front_id: FrontId) {
+ game.fronts[front_id].status = VICTORY;
+ log('Victory on ' + get_front_name(front_id));
+ gain_hero_points_in_player_order(game.fronts[front_id].contributions, 3);
+}
+
function create_effects_node(effects: Effect[]): EngineNode {
const nodes = effects.reduce((accrued: EngineNode[], current: Effect) => {
const node = resolve_effect(current);
@@ -1359,7 +1420,15 @@ function lose_hero_point(faction: FactionId, value: number) {
// #region FRONTS
function get_fronts_closest_to(target: 'd' | 'v') {
- const values = Object.values(game.fronts).map((front) => front.value);
+ const values = Object.values(game.fronts).reduce(
+ (accrued: number[], current: Front) => {
+ if (current.status === null) {
+ accrued.push(current.value);
+ }
+ return accrued;
+ },
+ []
+ );
const targetValue =
target === 'd' ? Math.min(...values) : Math.max(...values);
return Object.keys(game.fronts).filter(
@@ -1453,12 +1522,26 @@ function get_faction_id(player: Player): FactionId {
return player_faction_map[player];
}
+function get_front_name(id: FrontId | 'd' | 'v') {
+ return front_names[id];
+}
+
function get_current_event(): EventCard {
return cards[
game.current_events[game.current_events.length - 1]
] as EventCard;
}
+function get_defeated_front_count() {
+ let count = 0;
+ for (const front_id of FRONTS) {
+ if (game.fronts[front_id].status === DEFEAT) {
+ count++;
+ }
+ }
+ return count;
+}
+
function get_icon_count_in_tableau(
icon: Icon,
faction: FactionId = get_active_faction_id()
@@ -1560,15 +1643,19 @@ function set_delete<T>(set: T[], item: T) {
function list_deck(id: FactionId | 'fascist') {
const deck = [];
- const card_list = id === 'fascist' ? fascist_decks[game.year] : faction_cards[id];
+ const card_list =
+ id === 'fascist' ? fascist_decks[game.year] : faction_cards[id];
card_list.forEach((card) => {
- if (id === 'fascist' && (game.discard.f.includes(card))) {
- return
- } else if (id !== 'fascist' && (game.hands[id].includes(card) || game.discard[id].includes(card))) {
+ if (id === 'fascist' && game.discard.f.includes(card)) {
+ return;
+ } else if (
+ id !== 'fascist' &&
+ (game.hands[id].includes(card) || game.discard[id].includes(card))
+ ) {
return;
}
- deck.push(card)
- })
+ deck.push(card);
+ });
return deck;
}