From ed0f37f1142f468199ca9420552ff0979bdfe43d Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 2 Jul 2023 02:38:03 +0200 Subject: Emperor rules. --- create.html | 4 + play.css | 10 ++- play.js | 2 + rules.js | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 260 insertions(+), 37 deletions(-) diff --git a/create.html b/create.html index dffd67b..5eb0076 100644 --- a/create.html +++ b/create.html @@ -7,3 +7,7 @@ Player count: +

+ diff --git a/play.css b/play.css index f8d8536..0275752 100644 --- a/play.css +++ b/play.css @@ -473,10 +473,18 @@ body.shift .zenobia { background-image: url(images/rival_back.png) } .green.emperor_turns { background-image: url(images/green_emperor_turns.png) } .red.emperor_turns { background-image: url(images/red_emperor_turns.png) } .yellow.emperor_turns { background-image: url(images/yellow_emperor_turns.png) } - .general.unavailable { background-image: url(images/general_numbers.png) } .governor.unavailable { background-image: url(images/governor_numbers.png) } +.blue.governor.emperor { background-image: url(images/blue_governor_emperor.png) } +.green.governor.emperor { background-image: url(images/green_governor_emperor.png) } +.red.governor.emperor { background-image: url(images/red_governor_emperor.png) } +.yellow.governor.emperor { background-image: url(images/yellow_governor_emperor.png) } +.blue.general.emperor { background-image: url(images/blue_general_emperor.png) } +.green.general.emperor { background-image: url(images/green_general_emperor.png) } +.red.general.emperor { background-image: url(images/red_general_emperor.png) } +.yellow.general.emperor { background-image: url(images/yellow_general_emperor.png) } + .castra { background-image: url(images/castra.svg) } .quaestor { background-image: url(images/quaestor.svg) } .mob { background-image: url(images/mob.svg) } diff --git a/play.js b/play.js index 8a97fa6..3862f75 100644 --- a/play.js +++ b/play.js @@ -1355,6 +1355,7 @@ function on_update() { } e.classList.toggle("unavailable", region === UNAVAILABLE) e.classList.toggle("selected", view.selected_general === pi * 6 + ai) + e.classList.toggle("emperor", view.emperor === army) } if (avail_stack.length >= 6) layout_available(avail_stack, 48, pi * 625 + 0, 30) @@ -1380,6 +1381,7 @@ function on_update() { } e.classList.toggle("unavailable", region === UNAVAILABLE) e.classList.toggle("selected", view.selected_governor === pi * 6 + ai) + e.classList.toggle("emperor", view.emperor === pi * 6 + ai) } if (avail_stack.length >= 6) layout_available(avail_stack, 43, pi * 625 + 325, 27) diff --git a/rules.js b/rules.js index 19f7368..e60dde4 100644 --- a/rules.js +++ b/rules.js @@ -58,6 +58,8 @@ const POPULACE = 2 const LEGION_COUNT = 33 const BARBARIAN_COUNT = [ 36, 46, 56 ] +const NEUTRAL_EMPEROR = 64 + // REGIONS const ITALIA = 0 @@ -590,6 +592,7 @@ function clear_general_battled(id) { game.battled &= ~(1 << id) } function has_general_force_marched(id) { return game.force_march & (1 << id) } function set_general_force_marched(id) { game.force_march |= (1 << id) } +function clear_general_force_marched(id) { game.force_march &= ~(1 << id) } function has_militia_battled(province) { return game.mbattled & (1 << province) } function set_militia_battled(province) { game.mbattled |= (1 << province) } @@ -810,7 +813,7 @@ function is_own_province(where) { return is_own_governor(get_province_governor(where)) } -function is_emperor_governor(governor) { +function is_governor_of_emperor_player(governor) { let emperor = get_province_governor(ITALIA) if (emperor >= 0 && governor >= 0) return (emperor / 6 | 0) === (governor / 6 | 0) @@ -1092,14 +1095,25 @@ function flip_discard_to_available() { game.discard[game.current] = [] } +function eliminate_military_emperor(id) { + if (is_emperor_player()) + game.combat.own_military_emperor_died = 1 + log(PLAYER_NAME[id/6|0] + " Emperor died!") + remove_governor(ITALIA) +} + function assign_hit_to_legion(id) { - let army = get_legion_location(id) - ARMY + let general = get_legion_location(id) - ARMY if (is_legion_reduced(id)) { set_legion_location(id, AVAILABLE) - if (count_legions_in_army(army) === 0) { - log(GENERAL_NAME[army] + " killed!") - set_general_location(army, AVAILABLE) - clear_general_battled(army) + if (count_legions_in_army(general) === 0) { + if (game.emperor === ARMY + general) + eliminate_military_emperor(general) + else + log(PLAYER_NAME[general/6|0] + " General died!") + set_general_location(general, AVAILABLE) + clear_general_battled(general) + clear_general_force_marched(general) } } else { set_legion_reduced(id) @@ -1219,6 +1233,7 @@ function goto_start_turn() { game.force_march = 0 game.mbattled = 0 game.placed = 0 + game.combat_legacy = 0 goto_upkeep() } @@ -2144,12 +2159,22 @@ function increase_support(where) { set_support(where, get_support(where) + 1) } +function remove_emperor_token(where) { + if (game.emperor >= 0) { + if (where === ITALIA) + game.emperor = NEUTRAL_EMPEROR + else if (is_populace_emperor_seat(where)) + remove_governor(ITALIA) + } +} + function remove_governor(where) { log("Remove governor from %" + where + ".") eliminate_militia(where) set_mobs(where, 0) remove_quaestor(where) + remove_emperor_token(where) // TODO: manual? if (is_seat_of_power(where)) @@ -2159,7 +2184,7 @@ function remove_governor(where) { let old_governor = get_province_governor(where) if (old_governor >= 0) { set_governor_location(old_governor, AVAILABLE) - if (where !== ITALIA && is_emperor_governor(old_governor)) + if (where !== ITALIA && is_governor_of_emperor_player(old_governor)) reduce_support(ITALIA) } @@ -2173,11 +2198,12 @@ function place_governor(where, new_governor) { eliminate_militia(where) set_mobs(where, 0) remove_quaestor(where) + remove_emperor_token(where) let old_governor = get_province_governor(where) if (old_governor >= 0) { set_governor_location(old_governor, AVAILABLE) - if (where !== ITALIA && is_emperor_governor(old_governor)) + if (where !== ITALIA && is_governor_of_emperor_player(old_governor)) reduce_support(ITALIA) } @@ -2187,7 +2213,7 @@ function place_governor(where, new_governor) { else set_support(where, Math.max(1, get_support(where) - 1)) - if (where !== ITALIA && is_emperor_governor(new_governor)) + if (where !== ITALIA && is_governor_of_emperor_player(new_governor)) increase_support(ITALIA) update_neutral_italia() @@ -2221,11 +2247,22 @@ function calc_needed_votes(where, pg) { if (has_militia(where)) n += 1 } - // Ambitus adds guaranteed votes. - n -= game.ambitus return Math.max(1, n) } +function calc_extra_votes(where) { + let n = 0 + + // Ambitus adds guaranteed votes. + n += game.ambitus + + // Populace Emperor - Special Disadvantage + if (where === ITALIA && is_populace_emperor()) + n += 2 + + return n +} + states.place_governor = { inactive: "Place Governor", prompt() { @@ -2237,6 +2274,10 @@ states.place_governor = { view.selected_region = game.where view.selected_governor = game.selected_governor prompt(`Place Governor: ${game.sip} senate. Rolling ${dice} dice. ${need} votes needed.`) + if (game.where === ITALIA && is_populace_emperor()) + view.prompt += ` +2 vs Populace Emperor.` + if (game.ambitus > 0) + view.prompt += ` +${game.ambitus} from Ambitus.` if (game.ambitus < game.count && has_card_event(CARD_P2X)) { view.prompt += " Ambitus?" gen_card_event(CARD_P2X) @@ -2274,6 +2315,10 @@ states.praetorian_guard = { view.selected_region = game.where view.selected_governor = game.selected_governor prompt(`Praetorian Guard: ${game.mip} military. Rolling ${dice} dice. ${need} votes needed.`) + if (is_populace_emperor()) + view.prompt += ` +2 vs Populace Emperor.` + if (game.ambitus > 0) + view.prompt += ` +${game.ambitus} from Ambitus.` if (game.ambitus < game.count && has_card_event(CARD_P2X)) { view.prompt += " Ambitus?" gen_card_event(CARD_P2X) @@ -2322,22 +2367,34 @@ function roll_to_place_governor(pg) { else have = roll_dice(game.count, 2) + if (game.where === ITALIA && is_populace_emperor()) { + log("Populace Emperor B0 B0") + have += 2 + } + + if (game.ambitus > 0) { + for (let i = 0; i < game.ambitus; ++i) + log("Ambitus B0") + have += game.ambitus + } + if (have >= need) { log("Success!") log_br() if (game.where === ITALIA) { // Remember for Damnatio Memoriae + let was_senate_emperor = is_senate_emperor() let old_emperor = get_province_player(ITALIA) let old_support = get_support(ITALIA) place_governor(game.where, game.selected_governor) - if (old_emperor >= 0 && (has_card_event(CARD_S4) || has_card_event(CARD_S4B))) { + if (old_emperor >= 0 && !was_senate_emperor && (has_card_event(CARD_S4) || has_card_event(CARD_S4B))) { game.count = (old_emperor << 3) | old_support game.state = "damnatio_memoriae" } else { - game.state = "take_actions" + goto_becoming_emperor() } } else { place_governor(game.where, game.selected_governor) @@ -2353,6 +2410,8 @@ function roll_to_place_governor(pg) { // CARD: PRAETORIAN GUARD function can_play_praetorian_guard() { + if (is_military_emperor()) + return false for (let i = 0; i < 6; ++i) { let id = game.current * 6 + i if (get_governor_location(id) === AVAILABLE) @@ -2466,6 +2525,105 @@ states.damnatio_memoriae_mobs = { }, } +// BECOMING EMPEROR + +function is_default_emperor() { + return game.emperor < 0 +} + +function is_senate_emperor() { + return game.emperor === get_province_governor(ITALIA) +} + +function is_populace_emperor() { + return game.emperor >= 0 && game.emperor < 24 && get_governor_location(game.emperor) !== ITALIA +} + +function is_populace_emperor_seat(where) { + return game.emperor >= 0 && game.emperor < 24 && get_governor_location(game.emperor) === where +} + +function is_military_emperor() { + return game.emperor >= ARMY +} + +function is_military_emperor_general(id) { + return game.emperor === ARMY + id +} + +function is_province_of_populace_emperor(where) { + return is_populace_emperor() && (get_province_player(where) === get_province_player(ITALIA)) +} + +function goto_becoming_emperor() { + if (game.emperor === NEUTRAL_EMPEROR) + game.state = "becoming_emperor" + else + game.state = "take_actions" +} + +function has_mobs_in_own_provinces() { + for (let where = 0; where < 12; ++where) + if (is_own_province(where)) + if (get_mobs(where)) + return true + return false +} + +states.becoming_emperor = { + inactive: "Becoming Emperor", + prompt() { + prompt("Becoming Emperor: Place your Emperor token.") + for (let i = 0; i < 6; ++i) { + let id = game.current * 6 + i + if (is_region(get_governor_location(id))) + gen_action_governor(id) + if (is_region(get_general_location(id))) + gen_action_general(id) + } + }, + governor(id) { + push_undo() + game.emperor = id + let where = get_governor_location(id) + if (where === ITALIA) { + log("Senate Emperor in Italia.") + game.state = "take_actions" + } else { + log("Populace Emperor in %" + where + ".") + if (has_mobs_in_own_provinces()) + game.state = "becoming_populace_emperor" + else + game.state = "take_actions" + } + }, + general(id) { + push_undo() + game.emperor = ARMY + id + let where = get_general_location(id) + log("Military Emperor in %" + where + ".") + game.state = "take_actions" + }, +} + +states.becoming_populace_emperor = { + inactive: "Becoming Emperor", + prompt() { + prompt("Becoming Emperor: Remove all mobs from your provinces.") + for (let where = 0; where < 12; ++where) + if (is_own_province(where)) + if (get_mobs(where)) + gen_action_region(where) + }, + region(where) { + push_undo() + log("Remove mobs from %" + where + ".") + set_mobs(where, 0) + if (!has_mobs_in_own_provinces()) + game.state = "take_actions" + }, +} + // ACTION: CREATE ARMY function gen_create_army() { @@ -2778,9 +2936,8 @@ function can_foederati_from_region(where) { for (let tribe = 0; tribe < tribe_count; ++tribe) { if (find_active_non_leader_barbarian_of_tribe(where, tribe) >= 0) return true - if (is_province(where)) - if (find_inactive_non_leader_barbarian_of_tribe(where, tribe) >= 0) - return true + if (find_inactive_non_leader_barbarian_of_tribe(where, tribe) >= 0) + return true } return false } @@ -2791,11 +2948,9 @@ function gen_foederati(where) { let id = find_active_non_leader_barbarian_of_tribe(where, tribe) if (id >= 0) gen_action_barbarian(id) - if (is_province(where)) { - id = find_inactive_non_leader_barbarian_of_tribe(where, tribe) - if (id >= 0) - gen_action_barbarian(id) - } + id = find_inactive_non_leader_barbarian_of_tribe(where, tribe) + if (id >= 0) + gen_action_barbarian(id) } } @@ -2906,7 +3061,7 @@ states.mob = { prompt("Mob: Place a mob in a province with no mobs.") view.color = POPULACE for (let where = 0; where < 12; ++where) - if (!get_mobs(where) && is_enemy_province(where)) + if (!get_mobs(where) && is_enemy_province(where) && !is_province_of_populace_emperor(where)) gen_action_region(where) }, region(where) { @@ -3008,7 +3163,6 @@ function play_princeps_senatus() { // CARD: Ambitus function play_ambitus() { - log("Ambitus.") game.ambitus += 1 } @@ -3060,7 +3214,7 @@ states.force_march = { let where = UNAVAILABLE view.color = MILITARY - if (game.selected_general > 0) { + if (game.selected_general >= 0) { view.selected_general = game.selected_general where = get_general_location(game.selected_general) } else { @@ -3072,7 +3226,7 @@ states.force_march = { gen_initiate_battle(where) // Move Army - if (game.selected_general > 0) { + if (game.selected_general >= 0) { for (let to of ADJACENT[where]) { if (!is_sea(to)) { gen_action_region(to) @@ -4007,6 +4161,13 @@ states.combat_victory = { } function goto_combat_victory() { + + // Military Emperor check for death in battle + if (game.emperor === ARMY + game.combat.attacker) + roll_military_emperor_check(game.combat.ataken) + else if (game.combat.type === "general" && game.emperor === ARMY + game.combat.target) + roll_military_emperor_check(game.combat.dtaken) + log_br() log("RESULT") let de = is_defender_eliminated() @@ -4035,6 +4196,11 @@ function award_legacy_summary(p, reason, n) { game.legacy[p] += n } +function award_combat_legacy(p, reason, n) { + award_legacy(p, reason, n) + game.combat_legacy += n +} + function goto_combat_no_victory() { log("Both sides eliminated.") game.combat.killed = 0 @@ -4043,9 +4209,12 @@ function goto_combat_no_victory() { function goto_combat_victory_defender() { game.combat.killed = 0 - if (game.combat.type === "general") - award_legacy(game.combat.target / 6 | 0, "Victory", 2) - else if (game.combat.type === "militia") + if (game.combat.type === "general") { + if (game.emperor === ARMY + game.combat.target) + award_legacy(game.combat.target / 6 | 0, "Military Emperor Victory", 4) + else + award_legacy(game.combat.target / 6 | 0, "Victory", 2) + } else if (game.combat.type === "militia") award_legacy(get_province_player(game.where), "Victory", 2) else if (game.combat.type === "barbarians") log(BARBARIAN_NAME[game.combat.target] + " won.") @@ -4056,15 +4225,23 @@ function goto_combat_victory_defender() { function goto_combat_victory_attacker() { if (game.combat.type === "barbarians") { - award_legacy(game.current, "Victory", 2 + game.combat.dtaken) + let inflicted = game.combat.dtaken + game.combat.staken + if (game.emperor === ARMY + game.combat.attacker) + award_combat_legacy(game.current, "Military Emperor Victory", 4 + inflicted * 2) + else + award_combat_legacy(game.current, "Victory", 2 + inflicted) // Surviving Barbarians go home (to active) - let tribe = get_barbarian_tribe(game.combat.target) + let tribe = game.combat.target + console.log("BARB", tribe, game.combat) for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) - if (get_barbarian_location(id) === game.combat) + if (get_barbarian_location(id) === game.where) set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) } else { - award_legacy(game.current, "Victory", 2) + if (game.emperor === ARMY + game.combat.attacker) + award_combat_legacy(game.current, "Military Emperor Victory", 4) + else + award_combat_legacy(game.current, "Victory", 2) } // Defending Romans must retreat into province @@ -4104,6 +4281,14 @@ function goto_post_combat() { goto_free_increase_support_level() } +function roll_military_emperor_check(n) { + if (n > 0) { + log_h3("Military Emperor") + if (roll_dice_no_reroll(n, 5) > 0) + eliminate_military_emperor(game.emperor - ARMY) + } +} + function can_free_increase_support_level(where) { return where !== ITALIA && get_support(where) < 4 && is_own_province(where) } @@ -4131,11 +4316,10 @@ function goto_free_increase_support_level() { } } - if (game.combat.target === "barbarians" && has_card_event(CARD_S4X)) { + if (game.combat.target === "barbarians" && !game.combat.own_military_emperor_died && has_card_event(CARD_S4X)) game.state = "triumph" - } else { + else end_combat() - } } states.free_increase_support_level = { @@ -4393,7 +4577,23 @@ function goto_gain_legacy_emperor() { logi("+1 Emperor Turn") game.emperor_turns[game.current] += 1 } - award_legacy_summary(game.current, "Emperor", Math.max(0, get_support(ITALIA) - count_pretender_provinces())) + + if (is_default_emperor()) + award_legacy_summary(game.current, "Emperor", Math.max(0, get_support(ITALIA) - count_pretender_provinces())) + + if (is_senate_emperor()) + award_legacy_summary(game.current, "Senate Emperor", Math.max(0, get_support(ITALIA) - count_pretender_provinces())) + + if (is_populace_emperor()) { + let where = get_governor_location(game.emperor) + award_legacy_summary(game.current, "Populace Emperor", Math.max(0, get_support(where) * 2 - count_pretender_provinces())) + } + + if (is_military_emperor()) { + let lose = Math.min(count_pretender_provinces(), game.combat_legacy) + award_legacy_summary(game.current, "Military Emperor", -lose) + } + goto_gain_legacy_provinces() } @@ -4839,6 +5039,7 @@ exports.setup = function (seed, scenario, options) { current: 0, state: "setup_province", + emperor: -1, selected_governor: -1, selected_general: -1, selected_militia: -1, @@ -4856,6 +5057,7 @@ exports.setup = function (seed, scenario, options) { mbattled: 0, killed: 0, combat: null, + military: 0, ambitus: 0, frumentarii: 0, @@ -4884,6 +5086,12 @@ exports.setup = function (seed, scenario, options) { log_h1("Time of Crisis") + if (options.emperor) { + log("Emperor Rules.") + log_br() + game.emperor = NEUTRAL_EMPEROR + } + switch (scenario) { default: case "Standard": @@ -5010,6 +5218,7 @@ exports.view = function (state, player_name) { played: game.played, used: game.used, + emperor: game.emperor, legacy: game.legacy, emperor_turns: game.emperor_turns, } -- cgit v1.2.3