summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-07-02 02:38:03 +0200
committerTor Andersson <tor@ccxvii.net>2023-07-07 19:05:52 +0200
commited0f37f1142f468199ca9420552ff0979bdfe43d (patch)
treee2283ef1cbc56d0d2382a80706e2f83013cdc72e
parentbd0b0887e57945ead36b7c395c4a6855342398e0 (diff)
downloadtime-of-crisis-ed0f37f1142f468199ca9420552ff0979bdfe43d.tar.gz
Emperor rules.
-rw-r--r--create.html4
-rw-r--r--play.css10
-rw-r--r--play.js2
-rw-r--r--rules.js281
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:
<option value="2">2 Player</option>
</select>
+<p>
+<label>
+<input type="checkbox" value="true" name="emperor">Optional Emperor Rules
+</label>
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,
}