From 480df7113c50d45f748b4af8ce59172d58568376 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 23 Jun 2023 12:51:59 +0200 Subject: Events. --- rules.js | 289 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 244 insertions(+), 45 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 9a3d6c8..9b51295 100644 --- a/rules.js +++ b/rules.js @@ -202,6 +202,14 @@ const BARBARIAN_HOMELAND = [ SASSANIDS_HOMELAND, ] +const CNIVA = 0 +const ARDASHIR = 1 +const SHAPUR = 2 + +const POSTUMUS = 0 +const PRIEST_KING = 1 +const ZENOBIA = 2 + const BARBARIAN_INVASION = [ // Alamanni [ @@ -292,6 +300,25 @@ const EVENT_INFLATION = 13 const EVENT_GOOD_AUGURIES = 14 const EVENT_DIOCLETIAN = 15 +const EVENT_NAME = [ + "None", + "Plague of Cyprian", + "Ardashir", + "Priest King", + "Palmyra Allies", + "Shapur I", + "Postumus", + "Ludi Saeculares", + "Cniva", + "Zenobia", + "Bad Auguries", + "Raiding Parties", + "Preparing for War", + "Inflation", + "Good_auguries", + "Diocletian", +] + const CARD_M1 = [ 0, 11 ] const CARD_S1 = [ 12, 23 ] const CARD_P1 = [ 24, 35 ] @@ -325,36 +352,36 @@ const CARD_INDEX = [ ] const CARD_INFO = [ - { name: "M1", type: 0, cost: 1, event: "None" }, - { name: "S1", type: 1, cost: 1, event: "None" }, - { name: "P1", type: 2, cost: 1, event: "None" }, - { name: "M2", type: 0, cost: 2, event: "Castra" }, - { name: "S2", type: 1, cost: 2, event: "Tribute" }, - { name: "P2", type: 2, cost: 2, event: "Quaestor" }, - { name: "M2X", type: 0, cost: 2, event: "Cavalry" }, - { name: "S2X", type: 1, cost: 2, event: "Princeps Senatus" }, - { name: "P2X", type: 2, cost: 2, event: "Ambitus" }, - { name: "M3", type: 0, cost: 3, event: "Flanking Maneuver" }, - { name: "S3", type: 1, cost: 3, event: "Foederati" }, - { name: "P3", type: 2, cost: 3, event: "Mob" }, - { name: "M3X", type: 0, cost: 3, event: "Force March" }, - { name: "S3X", type: 1, cost: 3, event: "Frumentarii" }, - { name: "P3X", type: 2, cost: 3, event: "Mobile Vulgus" }, - { name: "M4", type: 0, cost: 4, event: "Praetorian Guard" }, - { name: "S4", type: 1, cost: 4, event: "Damnatio Memoriae" }, - { name: "S4B", type: 1, cost: 4, event: "Damnatio Memoriae (exp)" }, - { name: "P4", type: 2, cost: 4, event: "Pretender" }, - { name: "M4X", type: 0, cost: 4, event: "Spiculum" }, - { name: "S4X", type: 1, cost: 4, event: "Triumph" }, - { name: "P4X", type: 2, cost: 4, event: "Demagogue" }, + { name: "M1", type: 0, value: 1, event: "None" }, + { name: "S1", type: 1, value: 1, event: "None" }, + { name: "P1", type: 2, value: 1, event: "None" }, + { name: "M2", type: 0, value: 2, event: "Castra" }, + { name: "S2", type: 1, value: 2, event: "Tribute" }, + { name: "P2", type: 2, value: 2, event: "Quaestor" }, + { name: "M2X", type: 0, value: 2, event: "Cavalry" }, + { name: "S2X", type: 1, value: 2, event: "Princeps Senatus" }, + { name: "P2X", type: 2, value: 2, event: "Ambitus" }, + { name: "M3", type: 0, value: 3, event: "Flanking Maneuver" }, + { name: "S3", type: 1, value: 3, event: "Foederati" }, + { name: "P3", type: 2, value: 3, event: "Mob" }, + { name: "M3X", type: 0, value: 3, event: "Force March" }, + { name: "S3X", type: 1, value: 3, event: "Frumentarii" }, + { name: "P3X", type: 2, value: 3, event: "Mobile Vulgus" }, + { name: "M4", type: 0, value: 4, event: "Praetorian Guard" }, + { name: "S4", type: 1, value: 4, event: "Damnatio Memoriae" }, + { name: "S4B", type: 1, value: 4, event: "Damnatio Memoriae (exp)" }, + { name: "P4", type: 2, value: 4, event: "Pretender" }, + { name: "M4X", type: 0, value: 4, event: "Spiculum" }, + { name: "S4X", type: 1, value: 4, event: "Triumph" }, + { name: "P4X", type: 2, value: 4, event: "Demagogue" }, ] function card_name(c) { return CARD_INFO[CARD_INDEX[c]].name } -function card_cost(c) { - return CARD_INFO[CARD_INDEX[c]].cost +function card_value(c) { + return CARD_INFO[CARD_INDEX[c]].value } function card_influence(c) { @@ -401,9 +428,9 @@ function play_card_event(c) { } function add_card_ip(c) { - let cost = CARD_INFO[CARD_INDEX[c]].cost + let value = CARD_INFO[CARD_INDEX[c]].value let type = CARD_INFO[CARD_INDEX[c]].type - game.ip[type] += cost + game.ip[type] += value } function is_region(where) { @@ -445,6 +472,9 @@ function is_barbarian_active(id) { return !is_barbarian_inactive(id) } function set_barbarian_inactive(id) { game.barbarians[id] |= 64 } function set_barbarian_active(id) { game.barbarians[id] &= 63 } +function get_rival_emperor_location(id) { return game.rival_emperors[id] } +function set_rival_emperor_location(id, loc) { game.rival_emperors[id] = loc } + function get_legion_location(ix) { return game.legions[ix] & 63 } function set_legion_location(ix, loc) { game.legions[ix] = loc } function is_legion_reduced(ix) { return game.legions[ix] & 64 } @@ -734,7 +764,7 @@ function has_barbarian_leader(where) { function has_rival_emperor(where) { for (let i = 0; i < 3; ++i) - if (game.rival_emperors[i] === where) + if (get_rival_emperor_location(i) === where) return true return false } @@ -834,6 +864,13 @@ function count_legions_in_army(army_id) { return n } +function count_own_legions() { + let n = 0 + for (let i = 0; i < 6; ++i) + n += count_legions_in_army(game.current * 6 + i) + return n +} + function count_barbarians_in_army(army_id) { let n = 0 for (let id = 0; id < game.barbarians.length; ++id) @@ -960,6 +997,22 @@ function roll_dice(count, target) { return hits } +function roll_dice_no_reroll(count, target) { + let hits = 0 + let summary = [] + for (let i = 0; i < count; ++i) { + let die = roll_die() + if (die >= target) { + summary.push("B" + die) + hits += 1 + } else { + summary.push("W" + die) + } + } + log("Rolled " + summary.join(" ")) + return hits +} + function eliminate_barbarian(id) { let tribe = get_barbarian_tribe(id) set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) @@ -1134,7 +1187,7 @@ function goto_crisis() { if (sum === 12) return goto_pax_deorum() if (sum === 7) - return goto_event() + return goto_crisis_event() if (game.legacy.length === 2) return goto_barbarian_crisis(CRISIS_TABLE_2P[sum - 2]) @@ -1143,6 +1196,8 @@ function goto_crisis() { return goto_barbarian_crisis(CRISIS_TABLE_4P[sum - 2]) } +// CRISIS: IRA DEORUM + function goto_ira_deorum() { logi("Ira Deorum") @@ -1177,17 +1232,15 @@ states.ira_deorum = { }, } +// CRISIS: PAX DEORUM + function goto_pax_deorum() { logi("Pax Deorum") logi("TODO") goto_take_actions() } -function goto_event() { - logi("Event") - logi("TODO") - goto_take_actions() -} +// CRISIS: BARBARIAN INVASION function goto_barbarian_crisis(tribe) { logi(BARBARIAN_NAME[tribe]) @@ -1276,6 +1329,154 @@ function invade_with_barbarian_counter(id, path, where) { } } +// CRISIS: EVENT + +function goto_crisis_event() { + game.active_event = game.events.pop() + + log_h3(EVENT_NAME[game.active_event]) + + switch (game.active_event) { + case EVENT_ARDASHIR: return goto_crisis_barbarian_leader(ARDASHIR, SASSANIDS_HOMELAND) + case EVENT_SHAPUR_I: return goto_crisis_barbarian_leader(SHAPUR, SASSANIDS_HOMELAND) + case EVENT_CNIVA: return goto_crisis_barbarian_leader(CNIVA, GOTHS_HOMELAND) + case EVENT_PRIEST_KING: return goto_crisis_rival_emperor(PRIEST_KING, SYRIA) + case EVENT_POSTUMUS: return goto_crisis_rival_emperor(POSTUMUS, GALLIA) + case EVENT_ZENOBIA: return goto_crisis_rival_emperor(ZENOBIA, AEGYPTUS) + case EVENT_PALMYRA_ALLIES: return goto_palmyra_allies() + case EVENT_LUDI_SAECULARES: return goto_ludi_saeculares() + case EVENT_DIOCLETIAN: return goto_game_end() + } + + goto_take_actions() +} + +// CRISIS: BARBARIAN LEADER + +function goto_crisis_barbarian_leader(id, where) { + game.count = id + game.where = where + game.state = "crisis_barbarian_leader" +} + +states.crisis_barbarian_leader = { + prompt() { + prompt("Crisis: " + EVENT_NAME[game.active_event] + ".") + gen_action_region(game.where) + }, + region(where) { + set_barbarian_leader_location(game.count, game.where) + goto_take_actions() + }, +} + +function set_barbarian_leader_location(id, where) { +} + +// CRISIS: RIVAL EMPEROR + +function goto_crisis_rival_emperor(id, where) { + game.count = id + game.where = where + game.state = "crisis_rival_emperor" +} + +states.crisis_rival_emperor = { + prompt() { + prompt("Crisis: " + EVENT_NAME[game.active_event] + ".") + gen_action_region(game.where) + }, + region(where) { + console.log("RE", game.count, game.where) + set_rival_emperor_location(game.count, game.where) + goto_take_actions() + }, +} + +// CRISIS: PALMYRA ALLIES + +function goto_palmyra_allies() { + game.count = roll_dice_no_reroll(4, 4) + resume_palmyra_allies() +} + +function resume_palmyra_allies() { + // TODO: barbarian leaders + if ( + (find_active_barbarian_of_tribe(SASSANIDS, GALATIA) >= 0) || + (find_active_barbarian_of_tribe(SASSANIDS, SYRIA) >= 0) || + (find_active_barbarian_of_tribe(SASSANIDS, SASSANIDS_HOMELAND) >= 0) + ) + game.state = "palmyra_allies" + else + goto_take_actions() +} + +states.palmyra_allies = { + prompt() { + prompt("Palmyra Allies: Remove " + game.count + " active Sassanids.") + let id + + // TODO: barbarian leaders + id = find_active_barbarian_of_tribe(SASSANIDS, GALATIA) + if (id >= 0) + gen_action_barbarian(id) + id = find_active_barbarian_of_tribe(SASSANIDS, SYRIA) + if (id >= 0) + gen_action_barbarian(id) + id = find_active_barbarian_of_tribe(SASSANIDS, SASSANIDS_HOMELAND) + if (id >= 0) + gen_action_barbarian(id) + }, + barbarian(id) { + push_undo() + eliminate_barbarian(id) + resume_palmyra_allies() + }, +} + +// CRISIS: LUDI SAECULARES + +function goto_ludi_saeculares() { + game.count = game.current // remember current player + let emperor = get_province_player(ITALIA) + if (emperor >= 0) { + game.current = emperor + game.state = "ludi_saeculares" + } else { + log("There is no Emperor.") + goto_take_actions() + } +} + +states.ludi_saeculares = { + prompt() { + prompt("Ludi Saeculares: Discard one card from your hand.") + for (let c of current_hand()) + gen_action_card(c) + }, + card(c) { + push_undo() + set_remove(current_hand(), c) + set_add(current_discard(), c) + award_legacy(game.current, "Ludi Saeculares", 2 * card_value(c)) + game.state = "ludi_saeculares_done" + }, +} + +states.ludi_saeculares_done = { + prompt() { + prompt("Ludi Saeculares: Discard one card from your hand.") + view.actions.done = 1 + }, + done() { + if (game.current !== game.count) + clear_undo() + game.current = game.count + goto_take_actions() + }, +} + // === TAKE ACTIONS === function goto_take_actions() { @@ -2213,13 +2414,13 @@ function can_foederati_from_region(where) { return false } -function gen_foederati(where, recruit) { +function gen_foederati(where) { let tribe_count = get_tribe_count() for (let tribe = 0; tribe < tribe_count; ++tribe) { let id = find_active_barbarian_of_tribe(where, tribe) if (id >= 0) gen_action_barbarian(id) - if (recruit || is_province(where)) { + if (is_province(where)) { id = find_inactive_barbarian_of_tribe(where, tribe) if (id >= 0) gen_action_barbarian(id) @@ -2258,10 +2459,9 @@ states.foederati = { prompt("Foederati: Remove or recruit a Barbarian.") view.selected_general = game.selected_general let from = get_general_location(game.selected_general) - let recruit = (game.selected_general >= 0 && count_legions_in_army(game.selected_general) > count_barbarians_in_army(game.selected_general)) - gen_foederati(from, recruit) + gen_foederati(from) for (let to of ADJACENT[from]) - gen_foederati(to, recruit) + gen_foederati(to) }, barbarian(id) { let tribe = get_barbarian_tribe(id) @@ -2709,7 +2909,6 @@ function has_hits_on_defender() { } function goto_assign_hits() { - // TODO: flanking maneuver goto_assign_hits_on_attacker() } @@ -2898,7 +3097,7 @@ function goto_support_check() { function is_any_rival_emperor_or_pretender() { for (let i = 0; i < 3; ++i) - if (game.rival_emperors[i] !== UNAVAILABLE) + if (get_rival_emperor_location(i) === UNAVAILABLE) return true for (let where = 0; where < 12; ++where) if (is_seat_of_power(where) && is_enemy_province(where)) @@ -3141,7 +3340,7 @@ states.buy_trash = { for (let m of game.market) { if (m.length > 0) { let c = m[0] - let cost = card_cost(c) + let cost = card_value(c) if (cost > nprov) cost *= 2 cost += game.count @@ -3161,7 +3360,7 @@ states.buy_trash = { log("Bought " + card_name(c)) set_add(current_discard(), c) set_delete(find_market_with_card(c), c) - let cost = card_cost(c) + let cost = card_value(c) if (cost > count_own_provinces()) cost *= 2 cost += game.count @@ -3376,7 +3575,7 @@ function setup_events() { let deck = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ] shuffle(deck) // Shuffle Diocletian with last 3 cards - array_insert(deck, 11 + random(4), 15) + array_insert(deck, random(4), 15) return deck } @@ -3409,7 +3608,7 @@ exports.setup = function (seed, scenario, options) { state: "setup_province", first: 0, events: null, - active_events: [], + active_event: 0, ip: [], pp: 0, @@ -3572,7 +3771,7 @@ exports.view = function (state, player_name) { barbarian_leaders: game.barbarian_leaders, dice: game.dice, - events: game.active_events, + event: game.active_event, played: game.played, used: game.used, market: game.market.map(m => m[0] | 0), -- cgit v1.2.3