summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-06-26 11:24:34 +0200
committerTor Andersson <tor@ccxvii.net>2023-07-07 19:05:52 +0200
commit09bec88e3ad8894217f4f426372e9d94b2ad1ba9 (patch)
tree7043574f679b3057bcf18eaff94ade0f94ca6c43
parent7fdc9800cef0b86c6dc77ed799a7d35904593b1e (diff)
downloadtime-of-crisis-09bec88e3ad8894217f4f426372e9d94b2ad1ba9.tar.gz
Highlight current crisis and player on the crisis table.
-rw-r--r--play.css53
-rw-r--r--play.html2
-rw-r--r--play.js24
-rw-r--r--rules.js153
4 files changed, 161 insertions, 71 deletions
diff --git a/play.css b/play.css
index 9350ba0..5e02eea 100644
--- a/play.css
+++ b/play.css
@@ -28,10 +28,16 @@ body.Green .your_turn { background-color: hsl(99,36%,65%) }
#log .h1.p_yellow { background-color: hsl(47,100%,78%) }
#log .h1.p_green { background-color: hsl(99,36%,70%) }
+#log img { height: 14px; vertical-align: -6px; }
+
+#log span.M { color: #e31f26 }
+#log span.S { color: #0066b3 }
+#log span.P { color: hsl(44, 100%, 35%) }
+
#log { background-color: whitesmoke; }
#log .h1 { background-color: silver; font-weight: bold; padding-top:2px; padding-bottom:2px; margin: 8px 0; text-align: center; }
#log .h1 { border-bottom: 1px solid #444; border-top: 1px solid #444; }
-#log .h2 { background-color: gainsboro; padding-top:2px; padding-bottom:2px; text-align: center; }
+#log .h2 { background-color: gainsboro; text-align: center; }
#log .h3 { text-decoration: underline; }
#log div { padding-left: 20px; text-indent: -12px; }
#log div.i { padding-left: 32px; text-indent: -12px; }
@@ -48,6 +54,11 @@ body.Green .your_turn { background-color: hsl(99,36%,65%) }
background-repeat: no-repeat;
}
+#log img {
+ pointer-events: none;
+ vertical-align: -3px;
+}
+
#log .white {
background-image: url(images/die_black_pips.svg);
background-color: #fff;
@@ -642,6 +653,46 @@ body.shift #zenobia { background-image: url(images/rival_back.png) }
background-image: repeating-linear-gradient(135deg, #555555, #555555 60px, #505050 60px, #505050 120px);
}
+/* CRISIS TABLE HIGHLIGHT */
+
+#crisis_highlight {
+ transition-property: inset, background-color;
+ transition-duration: 500ms;
+ transition-timing-function: ease;
+ box-sizing: border-box;
+ position: absolute;
+ background-color: transparent;
+ border: 4px solid transparent;
+ top: 189px;
+ left: 2194px;
+ width: 263px;
+ height: 30px;
+ box-shadow: 0 0 0 1px #444;
+}
+
+#crisis_highlight.p_red { background-color: hsla(354,85%,67%,20%) }
+#crisis_highlight.p_blue { background-color: hsla(207,67%,78%,20%) }
+#crisis_highlight.p_yellow { background-color: hsla(47,100%,53%,20%) }
+#crisis_highlight.p_green { background-color: hsla(99,36%,60%,20%) }
+
+#crisis_highlight.p_red { border-color: hsl(354,85%,67%) }
+#crisis_highlight.p_blue { border-color: hsl(207,67%,78%) }
+#crisis_highlight.p_yellow { border-color: hsl(47,100%,53%) }
+#crisis_highlight.p_green { border-color: hsl(99,36%,60%) }
+
+#crisis_highlight.c0 { display: none }
+#crisis_highlight.c2 { top: 216px; }
+#crisis_highlight.c3 { top: 243px; }
+#crisis_highlight.c4 { top: 270px; }
+#crisis_highlight.c5 { top: 297px; }
+#crisis_highlight.c6 { top: 324px; }
+#crisis_highlight.c7 { top: 351px; }
+#crisis_highlight.c8 { top: 378px; }
+#crisis_highlight.c9 { top: 405px; }
+#crisis_highlight.c10 { top: 432px; }
+#crisis_highlight.c11 { top: 459px; }
+#crisis_highlight.c12 { top: 486px; }
+
/* COMBAT MASK */
#combat_mask {
diff --git a/play.html b/play.html
index 18f4068..28172fd 100644
--- a/play.html
+++ b/play.html
@@ -25,7 +25,6 @@
<a class="menu_item" target="_blanK" href="/time-of-crisis/info/cards.html">Card Gallery</a>
</div>
</div>
- <div class="icon_button" onclick="toggle_pieces()"><img src="/images/earth-africa-europe.svg"></div>
<div class="icon_button" onclick="toggle_zoom()"><img src="/images/magnifying-glass.svg"></div>
<div class="icon_button" onclick="toggle_log()"><img src="/images/scroll-quill.svg"></div>
</div>
@@ -48,6 +47,7 @@
<div id="mapwrap">
<div id="map">
<div id="crisis_table"></div>
+<div id="crisis_highlight" class="c0"></div>
<svg id="mapsvg" width="2550" height="1650" viewBox="0 0 2550 1650">
diff --git a/play.js b/play.js
index 90056c6..710610a 100644
--- a/play.js
+++ b/play.js
@@ -3,7 +3,7 @@
// TODO: battle dialog popup for rolling and assigning hits!
// TODO: show killed leaders taken for bonus purchase
-const DICE = {
+const ICONS = {
B0: '<span class="black d0"></span>',
B1: '<span class="black d1"></span>',
B2: '<span class="black d2"></span>',
@@ -691,6 +691,7 @@ let ui = {
amphitheater: [],
basilica: [],
limes: [],
+ crisis: document.getElementById("crisis_highlight"),
dice: [
document.getElementById("crisis_die_1"),
document.getElementById("crisis_die_2"),
@@ -734,10 +735,6 @@ function hide(elt) {
elt.classList.add("hide")
}
-function toggle_pieces() {
- ui.pieces.classList.toggle("hide")
-}
-
function create(t, p, ...c) {
let e = document.createElement(t)
Object.assign(e, p)
@@ -1377,6 +1374,7 @@ function on_update() {
ui.body.classList.toggle("populace", view.color === 2)
layout_barbarian_dice(ui.dice[2], ui.dice[3], view.crisis[0])
+ ui.crisis.className = "p_" + PLAYER_CLASS[view.current] + " c" + (view.crisis[1] + view.crisis[2])
ui.dice[0].className = "dice black d" + view.crisis[1]
ui.dice[1].className = "dice white d" + view.crisis[2]
ui.dice[2].className = "dice black d" + view.crisis[3]
@@ -1477,8 +1475,16 @@ function sub_event_name(match, p1) {
return EVENT_NAME[x]
}
-function sub_dice_image(match) {
- return DICE[match]
+function sub_icon(match) {
+ return ICONS[match]
+}
+
+function sub_card_1(match) {
+ return match
+}
+
+function sub_card_x(match, p1, p2) {
+ return p1 + "\xa0" + p2
}
function on_log(text) {
@@ -1495,7 +1501,9 @@ function on_log(text) {
text = text.replace(/%(\d+)/g, sub_region_name)
text = text.replace(/\bE(\d+)/g, sub_event_name)
- text = text.replace(/\b[BW]\d\b/g, sub_dice_image)
+ text = text.replace(/\b[BW]\d\b/g, sub_icon)
+ text = text.replace(/\b[MSP][1]\b/g, sub_card_1)
+ text = text.replace(/\b([MSP][234])\(([^)]*)\)/g, sub_card_x)
if (text.match(/^.turn/)) {
text = text.substring(6)
diff --git a/rules.js b/rules.js
index 2c06cc5..756374b 100644
--- a/rules.js
+++ b/rules.js
@@ -39,7 +39,7 @@ exports.roles = function (scenario, options) {
// === CONSTANTS ===
-const PLAYER_NAMES = [ P1, P2, P3, P4 ]
+const PLAYER_NAME = [ P1, P2, P3, P4 ]
const PLAYER_INDEX = {
[P1]: 0,
@@ -350,25 +350,25 @@ const CARD_INFO = [
{ 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 Castra", type: 0, value: 2, event: "Castra" },
- { name: "S2 Tribute", type: 1, value: 2, event: "Tribute" },
- { name: "P2 Quaestor", type: 2, value: 2, event: "Quaestor" },
- { name: "M2 Cavalry", type: 0, value: 2, event: "Cavalry" },
- { name: "S2 Princeps Senatus", type: 1, value: 2, event: "Princeps Senatus" },
- { name: "P2 Ambitus", type: 2, value: 2, event: "Ambitus" },
- { name: "M3 Flanking Maneuver", type: 0, value: 3, event: "Flanking Maneuver" },
- { name: "S3 Foederati", type: 1, value: 3, event: "Foederati" },
- { name: "P3 Mob", type: 2, value: 3, event: "Mob" },
- { name: "M3 Force March", type: 0, value: 3, event: "Force March" },
- { name: "S3 Frumentarii", type: 1, value: 3, event: "Frumentarii" },
- { name: "P3 Mobile Vulgus", type: 2, value: 3, event: "Mobile Vulgus" },
- { name: "M4 Praetorian Guard", type: 0, value: 4, event: "Praetorian Guard" },
- { name: "S4 Damnatio Memoriae", type: 1, value: 4, event: "Damnatio Memoriae" },
- { name: "S4 Damnatio Memoriae", type: 1, value: 4, event: "Damnatio Memoriae (exp)" },
- { name: "P4 Pretender", type: 2, value: 4, event: "Pretender" },
- { name: "M4 Spiculum", type: 0, value: 4, event: "Spiculum" },
- { name: "S4 Triumph", type: 1, value: 4, event: "Triumph" },
- { name: "P4 Demagogue", type: 2, value: 4, event: "Demagogue" },
+ { name: "M2(Castra)", type: 0, value: 2, event: "Castra" },
+ { name: "S2(Tribute)", type: 1, value: 2, event: "Tribute" },
+ { name: "P2(Quaestor)", type: 2, value: 2, event: "Quaestor" },
+ { name: "M2(Cavalry)", type: 0, value: 2, event: "Cavalry" },
+ { name: "S2(Princeps Senatus)", type: 1, value: 2, event: "Princeps Senatus" },
+ { name: "P2(Ambitus)", type: 2, value: 2, event: "Ambitus" },
+ { name: "M3(Flanking Maneuver)", type: 0, value: 3, event: "Flanking Maneuver" },
+ { name: "S3(Foederati)", type: 1, value: 3, event: "Foederati" },
+ { name: "P3(Mob)", type: 2, value: 3, event: "Mob" },
+ { name: "M3(Force March)", type: 0, value: 3, event: "Force March" },
+ { name: "S3(Frumentarii)", type: 1, value: 3, event: "Frumentarii" },
+ { name: "P3(Mobile Vulgus)", type: 2, value: 3, event: "Mobile Vulgus" },
+ { name: "M4(Praetorian Guard)", type: 0, value: 4, event: "Praetorian Guard" },
+ { name: "S4(Damnatio Memoriae)", type: 1, value: 4, event: "Damnatio Memoriae" },
+ { name: "S4(Damnatio Memoriae)", type: 1, value: 4, event: "Damnatio Memoriae (exp)" },
+ { name: "P4(Pretender)", type: 2, value: 4, event: "Pretender" },
+ { name: "M4(Spiculum)", type: 0, value: 4, event: "Spiculum" },
+ { name: "S4(Triumph)", type: 1, value: 4, event: "Triumph" },
+ { name: "P4(Demagogue)", type: 2, value: 4, event: "Demagogue" },
]
function card_name(c) {
@@ -1129,7 +1129,7 @@ states.setup_province = {
capital(where) {
push_undo()
- log(PLAYER_NAMES[game.current] + " started in %" + where + ".")
+ log(PLAYER_NAME[game.current] + " in %" + where + ".")
set_governor_location(game.current * 6 + 0, where)
@@ -1172,7 +1172,7 @@ states.setup_hand = {
// === UPKEEP ===
function goto_start_turn() {
- log_h1(PLAYER_NAMES[game.current])
+ log_h1(PLAYER_NAME[game.current])
game.killed = 0
game.battled = 0
@@ -1849,7 +1849,6 @@ states.take_actions = {
let hand = current_hand()
while (hand.length > 0) {
let c = hand[0]
- log("Played " + card_name(c) + ".")
set_delete(hand, c)
set_add(game.played, c)
add_card_ip(c)
@@ -1860,7 +1859,6 @@ states.take_actions = {
push_undo()
let hand = current_hand()
if (set_has(hand, c)) {
- log("Played " + card_name(c) + ".")
set_delete(hand, c)
set_add(game.played, c)
add_card_ip(c)
@@ -2096,7 +2094,7 @@ function increase_support(where) {
}
function remove_governor(where) {
- log("Removed Governor from %" + where + ".")
+ log("Removed governor from %" + where + ".")
eliminate_militia(where)
set_mobs(where, 0)
@@ -2918,27 +2916,24 @@ function play_flanking_maneuver() {
}
function goto_battle_vs_general(where, attacker, target) {
- log_h3("Battle " + PLAYER_NAMES[target/6|0] + " in %" + where)
goto_battle("general", where, attacker, target)
}
function goto_battle_vs_barbarian(where, attacker, target) {
let tribe = get_barbarian_tribe(target)
- log_h3("Battle " + BARBARIAN_NAME[tribe] + " in %" + where)
goto_battle("barbarians", where, attacker, tribe)
}
function goto_battle_vs_rival_emperor(where, attacker, target) {
- log_h3("Battle " + RIVAL_EMPEROR_NAME[target] + " in %" + where)
goto_battle("rival_emperor", where, attacker, target)
}
function goto_battle_vs_militia(where, attacker) {
- log_h3("Battle militia in %" + where)
goto_battle("militia", where, attacker, -1)
}
function goto_battle(type, where, attacker, target) {
+ log_h2("Battle in %" + where)
spend_military(1)
game.where = where
game.battle = { type, attacker, target, flanking: 0, killed: 0 }
@@ -2983,9 +2978,9 @@ function gen_initiate_battle(where) {
function format_battle_target() {
switch (game.battle.type) {
- case "militia": return PLAYER_NAMES[get_province_player(game.battle.target)] + " militia"
+ case "militia": return PLAYER_NAME[get_province_player(game.battle.target)] + " militia"
case "barbarians": return BARBARIAN_NAME[game.battle.target]
- case "general": return PLAYER_NAMES[game.battle.target / 6 | 0] + " army"
+ case "general": return PLAYER_NAME[game.battle.target / 6 | 0] + " army"
case "rival_emperor": return RIVAL_EMPEROR_NAME[game.battle.target]
}
}
@@ -3074,7 +3069,7 @@ function roll_general_dice(general) {
let drm = get_roman_drm()
- log(GENERAL_NAME[general])
+ log(PLAYER_NAME[general/6|0])
if (is_general_inside_capital(general) && has_militia(game.where)) {
log("Militia")
@@ -3425,6 +3420,8 @@ states.combat_victory = {
}
function goto_combat_victory() {
+ log_br()
+ log("VICTORY")
let de = is_defender_eliminated()
let ae = is_attacker_eliminated()
if (de && ae)
@@ -3437,9 +3434,9 @@ function goto_combat_victory() {
function award_legacy(p, reason, n) {
if (n > 0)
- log(PLAYER_NAMES[p] + " +" + n + " Legacy for " + reason + ".")
+ log(PLAYER_NAME[p] + " +" + n + " Legacy for " + reason + ".")
if (n < 0)
- log(PLAYER_NAMES[p] + " " + n + " Legacy for " + reason + ".")
+ log(PLAYER_NAME[p] + " " + n + " Legacy for " + reason + ".")
game.legacy[p] += n
}
@@ -3452,11 +3449,13 @@ function award_legacy_summary(p, reason, n) {
}
function goto_combat_no_victory() {
+ log("Nobody")
game.battle.killed = 0
end_battle()
}
function goto_combat_victory_defender() {
+ log("Defender")
game.battle.killed = 0
if (game.battle.type === "general")
award_legacy(game.battle.target / 6 | 0, "Victory", 2)
@@ -3466,6 +3465,8 @@ function goto_combat_victory_defender() {
}
function goto_combat_victory_attacker() {
+ log("Attacker")
+
if (game.battle.type === "barbarians") {
award_legacy(game.current, "Victory", 2 + game.battle.dtaken)
@@ -3570,6 +3571,8 @@ function goto_support_check() {
}
game.count = 0
+ if (needs_any_support_check())
+ log_h2("Support Check")
resume_support_check()
}
@@ -3583,18 +3586,24 @@ function is_any_rival_emperor_or_pretender() {
return false
}
+function needs_any_support_check() {
+ return needs_support_check() || needs_support_check_emperor() || needs_support_check_mobs()
+}
+
+function needs_support_check() {
+ for (let where = 0; where < 12; ++where)
+ if ((game.count & (1 << where)) === 0)
+ if (is_own_province(where))
+ if (has_active_barbarians(where) || has_rival_emperor(where) || has_enemy_general_in_capital(where))
+ return true
+ return false
+}
+
function resume_support_check() {
- for (let where = 0; where < 12; ++where) {
- if ((game.count & (1 << where)) === 0) {
- if (is_own_province(where)) {
- if (has_active_barbarians(where) || has_rival_emperor(where) || has_enemy_general_in_capital(where)) {
- game.state = "support_check"
- return
- }
- }
- }
- }
- goto_support_check_emperor()
+ if (needs_support_check())
+ game.state = "support_check"
+ else
+ goto_support_check_emperor()
}
states.support_check = {
@@ -3611,17 +3620,21 @@ states.support_check = {
region(where) {
push_undo()
game.count |= (1 << where)
+ log("Reduced support level in %" + where + ".")
reduce_support(where)
resume_support_check()
},
}
+function needs_support_check_emperor() {
+ return is_emperor_player() && is_any_rival_emperor_or_pretender()
+}
+
function goto_support_check_emperor() {
- if (is_emperor_player() && is_any_rival_emperor_or_pretender()) {
+ if (needs_support_check_emperor())
game.state = "support_check_emperor"
- return
- }
- goto_support_check_mobs()
+ else
+ goto_support_check_mobs()
}
states.support_check_emperor = {
@@ -3634,19 +3647,23 @@ states.support_check_emperor = {
region(where) {
push_undo()
game.count |= (1 << where)
+ log("Reduced support level in %" + where + ".")
reduce_support(where)
goto_support_check_mobs()
},
}
+function needs_support_check_mobs() {
+ for (let where = 0; where < 12; ++where)
+ if (is_own_province(where) && get_mobs(where) >= get_support(where))
+ return true
+}
+
function goto_support_check_mobs() {
- for (let where = 0; where < 12; ++where) {
- if (is_own_province(where) && get_mobs(where) >= get_support(where)) {
- game.state = "support_check_mobs"
- return
- }
- }
- goto_expand_pretender_empire()
+ if (needs_support_check_mobs())
+ game.state = "support_check_mobs"
+ else
+ goto_expand_pretender_empire()
}
states.support_check_mobs = {
@@ -3660,6 +3677,7 @@ states.support_check_mobs = {
},
region(where) {
push_undo()
+ log("More mobs than support in %" + where + ".")
remove_governor(where)
goto_support_check_mobs()
},
@@ -3670,6 +3688,7 @@ states.support_check_mobs = {
function goto_expand_pretender_empire() {
for (let where = 1; where < 12; ++where) {
if (is_expand_pretender_province(where)) {
+ log_h4("Expand Pretender Empire")
game.state = "expand_pretender_empire"
return
}
@@ -3688,6 +3707,7 @@ states.expand_pretender_empire = {
},
region(where) {
push_undo()
+ logi("Breakaway %" + where)
add_breakaway(where)
remove_quaestor(where) // no effect anymore
goto_expand_pretender_empire()
@@ -3697,8 +3717,7 @@ states.expand_pretender_empire = {
// === GAIN LEGACY ===
function goto_gain_legacy() {
- log_br()
- log("Gain Legacy")
+ log_h4("Gain Legacy")
if (is_only_pretender_player())
award_legacy_summary(game.current, "Pretender", count_own_breakaway_provinces())
@@ -3768,11 +3787,16 @@ function count_political_points() {
}
function goto_buy_trash_cards() {
+ //log_h4("Played")
+ //logi(game.played.map(c=>card_name(c)).join(", "))
+ log_br()
+ log("Played " + game.played.map(c=>card_name(c)).join(", ") + ".")
log_br()
let discard = current_discard()
for (let c of game.played)
set_add(discard, c)
+
game.played.length = 0
game.count = 0
game.pp = count_political_points()
@@ -3795,6 +3819,7 @@ states.buy_trash_discard = {
},
card(c) {
push_undo()
+ log("Discarded " + card_name(c) + ".")
set_delete(current_hand(), c)
set_add(current_discard(), c)
},
@@ -4070,6 +4095,12 @@ function vp_tie(p) {
function goto_game_end() {
log_h2("Game End")
+ game.crisis[0] = -1
+ game.crisis[1] = 0
+ game.crisis[2] = 0
+ game.crisis[3] = 0
+ game.crisis[4] = 0
+
let cutoff = award_emperor_turns(10, 1000)
cutoff = award_emperor_turns(6, cutoff)
cutoff = award_emperor_turns(3, cutoff)
@@ -4077,7 +4108,7 @@ function goto_game_end() {
let victor = game.legacy.map((legacy,p) => [vp_tie(p),p]).sort((a,b) => b[0] - a[0])[0][1]
- goto_game_over(PLAYER_NAMES[victor], PLAYER_NAMES[victor] + " won!")
+ goto_game_over(PLAYER_NAME[victor], PLAYER_NAME[victor] + " won!")
}
function goto_game_over(result, victory) {
@@ -4236,7 +4267,7 @@ function load_game(state) {
}
function save_game() {
- game.active = PLAYER_NAMES[game.current]
+ game.active = PLAYER_NAME[game.current]
return game
}
@@ -4292,7 +4323,7 @@ exports.view = function (state, player_name) {
view.prompt = game.victory
} else if (game.current !== player) {
let inactive = states[game.state].inactive || game.state
- view.prompt = `Waiting for ${PLAYER_NAMES[game.current]}: ${inactive}.`
+ view.prompt = `Waiting for ${PLAYER_NAME[game.current]}: ${inactive}.`
} else {
view.actions = {}
states[game.state].prompt()