diff options
-rw-r--r-- | rules.js | 563 |
1 files changed, 305 insertions, 258 deletions
@@ -6,12 +6,9 @@ exports.scenarios = [ // TODO: Avalon Digital scenarios? ] -exports.roles = [ - "Caesar", - "Pompeius", -] +exports.roles = [ "Caesar", "Pompeius" ] -const { CARDS, SPACES, EDGES, BLOCKS } = require('./data') +const { CARDS, SPACES, EDGES, BLOCKS } = require("./data") function find_block(name) { return BLOCKS.findIndex(b => b.name === name) @@ -52,8 +49,8 @@ const DEAD = find_space("Dead") const LEVY = find_space("Levy") // serif cirled numbers -const DIE_HIT = [ 0, '\u2776', '\u2777', '\u2778', '\u2779', '\u277A', '\u277B' ] -const DIE_MISS = [ 0, '\u2460', '\u2461', '\u2462', '\u2463', '\u2464', '\u2465' ] +const DIE_HIT = [ 0, "\u2776", "\u2777", "\u2778", "\u2779", "\u277A", "\u277B" ] +const DIE_MISS = [ 0, "\u2460", "\u2461", "\u2462", "\u2463", "\u2464", "\u2465" ] const ATTACK_MARK = "*" const RESERVE_MARK = "" @@ -66,11 +63,11 @@ function random(n) { // Uses BigInt for arithmetic, so is an order of magnitude slower. // https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf // m = 2**53 - 111 - return (game.seed = Number(BigInt(game.seed) * 5667072534355537n % 9007199254740881n)) % n + return (game.seed = Number((BigInt(game.seed) * 5667072534355537n) % 9007199254740881n)) % n } function logbr() { - if (game.log.length > 0 && game.log[game.log.length-1] !== "") + if (game.log.length > 0 && game.log[game.log.length - 1] !== "") game.log.push("") } @@ -112,24 +109,26 @@ function log_move_end() { } function log_levy(where) { - game.turn_log.push([where]) + game.turn_log.push([ where ]) } function print_turn_log_no_active(text) { - let lines = game.turn_log.map(function (move) { - let s = "" - for (let i = 0; i < move.length; ++i) { - let x = move[i] - if (typeof x === 'string') { - s += x - } else { - if (i > 0) - s += " \u2192 " - s += "#" + x + let lines = game.turn_log + .map(function (move) { + let s = "" + for (let i = 0; i < move.length; ++i) { + let x = move[i] + if (typeof x === "string") { + s += x + } else { + if (i > 0) + s += " \u2192 " + s += "#" + x + } } - } - return s - }).sort() + return s + }) + .sort() delete game.turn_log log(text) @@ -176,7 +175,7 @@ function gen_action_undo(view) { function gen_action_pass(view, text) { if (!view.actions) view.actions = {} - view.actions['pass'] = text + view.actions["pass"] = text } function gen_action(view, action, argument) { @@ -196,11 +195,11 @@ function gen_action_battle(view, action, b) { } function gen_action_block(view, b) { - gen_action(view, 'block', b) + gen_action(view, "block", b) } function gen_action_space(view, s) { - gen_action(view, 'space', s) + gen_action(view, "space", s) } function edge_id(A, B) { @@ -224,7 +223,7 @@ function reset_deck() { function deal_cards(deck, n) { let hand = [] let events = 0 - let max_events = (game.max_2_events ? 2 : n) + let max_events = game.max_2_events ? 2 : n while (hand.length < n) { let c = random(deck.length) if (events < max_events || deck[c] > 7) { @@ -306,8 +305,8 @@ function block_type(who) { } function block_initiative(who) { - if (block_type(who) === 'ballista') - return is_defender(who) ? 'B' : 'D' + if (block_type(who) === "ballista") + return is_defender(who) ? "B" : "D" return BLOCKS[who].initiative } @@ -316,7 +315,7 @@ function block_fire_power(who) { } function block_strength(who) { - if (block_type(who) === 'elephant') + if (block_type(who) === "elephant") return game.steps[who] * 2 return game.steps[who] } @@ -433,16 +432,16 @@ function is_pinned(who) { function is_city(where) { let t = SPACES[where].type - return t === 'city' || t === 'major-port' || t === 'port' + return t === "city" || t === "major-port" || t === "port" } function is_port(where) { let t = SPACES[where].type - return t === 'major-port' || t === 'port' + return t === "major-port" || t === "port" } function is_sea(where) { - return SPACES[where].type === 'sea' + return SPACES[where].type === "sea" } function is_map_space(where) { @@ -450,21 +449,41 @@ function is_map_space(where) { } function is_navis(b) { - return BLOCKS[b].type === 'navis' + return BLOCKS[b].type === "navis" } -function is_friendly_space(where) { return has_friendly(where) && count_enemy(where) === 0 } -function is_enemy_space(where) { return count_friendly(where) === 0 && has_enemy(where) } -function is_vacant_space(where) { return count_friendly(where) === 0 && count_enemy(where) === 0 } -function is_contested_space(where) { return has_friendly(where) && has_enemy(where) } +function is_friendly_space(where) { + return has_friendly(where) && count_enemy(where) === 0 +} +function is_enemy_space(where) { + return count_friendly(where) === 0 && has_enemy(where) +} +function is_vacant_space(where) { + return count_friendly(where) === 0 && count_enemy(where) === 0 +} +function is_contested_space(where) { + return has_friendly(where) && has_enemy(where) +} -function is_friendly_city(where) { return is_city(where) && is_friendly_space(where) } -function is_enemy_city(where) { return is_city(where) && is_enemy_space(where) } -function is_contested_city(where) { return is_city(where) && is_contested_space(where) } +function is_friendly_city(where) { + return is_city(where) && is_friendly_space(where) +} +function is_enemy_city(where) { + return is_city(where) && is_enemy_space(where) +} +function is_contested_city(where) { + return is_city(where) && is_contested_space(where) +} -function is_friendly_sea(where) { return is_sea(where) && is_friendly_space(where) } -function is_vacant_sea(where) { return is_sea(where) && is_vacant_space(where) } -function is_contested_sea(where) { return is_sea(where) && is_contested_space(where) } +function is_friendly_sea(where) { + return is_sea(where) && is_friendly_space(where) +} +function is_vacant_sea(where) { + return is_sea(where) && is_vacant_space(where) +} +function is_contested_sea(where) { + return is_sea(where) && is_contested_space(where) +} function have_contested_spaces() { for (let s = first_map_space; s < space_count; ++s) @@ -474,7 +493,7 @@ function have_contested_spaces() { } function supply_limit(where) { - if (SPACES[where].type === 'sea') + if (SPACES[where].type === "sea") return 0 return 3 + SPACES[where].value } @@ -500,12 +519,18 @@ function count_vp() { if (is_enemy_city(s)) game.p_vp += SPACES[s].value } - if (is_dead(B_POMPEIUS)) game.c_vp += 1 - if (is_dead(B_SCIPIO)) game.c_vp += 1 - if (is_dead(B_BRUTUS)) game.c_vp += 1 - if (is_dead(B_CAESAR)) game.p_vp += 1 - if (is_dead(B_ANTONIUS)) game.p_vp += 1 - if (is_dead(B_OCTAVIAN)) game.p_vp += 1 + if (is_dead(B_POMPEIUS)) + game.c_vp += 1 + if (is_dead(B_SCIPIO)) + game.c_vp += 1 + if (is_dead(B_BRUTUS)) + game.c_vp += 1 + if (is_dead(B_CAESAR)) + game.p_vp += 1 + if (is_dead(B_ANTONIUS)) + game.p_vp += 1 + if (is_dead(B_OCTAVIAN)) + game.p_vp += 1 game.active = old_active } @@ -513,7 +538,7 @@ function count_vp() { function can_amphibious_move_to(_who, from, to) { let e = edge_id(from, to) - if (EDGES[e] === 'sea') { + if (EDGES[e] === "sea") { if (is_city(to)) { if (is_friendly_space(to) || is_vacant_space(to)) { return true @@ -528,7 +553,7 @@ function can_amphibious_move_to(_who, from, to) { function can_amphibious_move(b) { if (block_owner(b) === game.active && !set_has(game.moved, b)) { - if (BLOCKS[b].type === 'navis') + if (BLOCKS[b].type === "navis") return false if (is_pinned(b)) return false @@ -545,13 +570,13 @@ function can_regroup_to(b, from, to) { let e = edge_id(from, to) let b_type = BLOCKS[b].type let e_type = EDGES[e] - if (b_type === 'navis') - return e_type === 'sea' - if (e_type === 'major') + if (b_type === "navis") + return e_type === "sea" + if (e_type === "major") return road_limit(e) < 4 - if (e_type === 'minor') + if (e_type === "minor") return road_limit(e) < 2 - if (e_type === 'strait') + if (e_type === "strait") return road_limit(e) < 2 } return false @@ -562,14 +587,14 @@ function can_block_use_road(b, from, to) { let b_type = BLOCKS[b].type let e_type = EDGES[e] - if (b_type === 'navis') { + if (b_type === "navis") { if (game.mars === game.active) return false if (game.mercury === game.active) return false if (game.pluto === game.active) return false - return e_type === 'sea' + return e_type === "sea" } else { if (game.neptune === game.active) return false @@ -577,20 +602,20 @@ function can_block_use_road(b, from, to) { if (game.pluto === game.active) { if (is_enemy_space(to) || is_contested_space(to)) { - if (e_type === 'major') + if (e_type === "major") return road_limit(e) < 6 - if (e_type === 'minor') + if (e_type === "minor") return road_limit(e) < 3 - if (e_type === 'strait') + if (e_type === "strait") return road_limit(e) < 2 } } - if (e_type === 'major') + if (e_type === "major") return road_limit(e) < 4 - if (e_type === 'minor') + if (e_type === "minor") return road_limit(e) < 2 - if (e_type === 'strait') { + if (e_type === "strait") { if (is_enemy_space(to) || is_contested_space(to)) return road_limit(e) < 1 else @@ -603,13 +628,13 @@ function can_block_use_road_to_retreat(b, from, to) { let e = edge_id(from, to) let b_type = BLOCKS[b].type let e_type = EDGES[e] - if (b_type === 'navis') - return e_type === 'sea' - if (e_type === 'major') + if (b_type === "navis") + return e_type === "sea" + if (e_type === "major") return road_limit(e) < 4 - if (e_type === 'minor') + if (e_type === "minor") return road_limit(e) < 2 - if (e_type === 'strait') + if (e_type === "strait") return road_limit(e) < 1 return false } @@ -677,7 +702,7 @@ function can_block_continue(b, last_from) { function can_sea_retreat(who, _from, to) { if (game.sea_retreated) return false - if (BLOCKS[who].type === 'navis') + if (BLOCKS[who].type === "navis") return false if (is_friendly_sea(to)) { for (let next of SPACES[to].exits) @@ -703,7 +728,7 @@ function can_attacker_retreat_to(who, from, to) { function can_defender_retreat_to(who, from, to) { let e = edge_id(from, to) - if (BLOCKS[who].type === 'navis') { + if (BLOCKS[who].type === "navis") { // Navis can only retreat to vacant seas, not ports! if (is_vacant_sea(to)) { if (can_block_use_road_to_retreat(who, from, to)) @@ -715,7 +740,6 @@ function can_defender_retreat_to(who, from, to) { if (can_block_use_road_to_retreat(who, from, to)) return true } - } else { if (can_sea_retreat(who, from, to)) return true @@ -763,8 +787,8 @@ function can_levy_to(b, to) { if (is_friendly_city(to)) { if (BLOCKS[b].levy) return BLOCKS[b].levy === to - if (BLOCKS[b].type === 'navis') - return SPACES[to].type === 'major-port' + if (BLOCKS[b].type === "navis") + return SPACES[to].type === "major-port" if (b === B_OCTAVIAN) return is_dead(B_CAESAR) || is_dead(B_ANTONIUS) if (b === B_BRUTUS) @@ -800,7 +824,7 @@ function start_free_deployment() { game.setup_limit[space] = count_friendly(space) + count_enemy(space) } validate_free_deployment() - game.state = 'free_deployment' + game.state = "free_deployment" clear_undo() } @@ -825,7 +849,7 @@ function format_deployment_error(view) { states.free_deployment = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to redeploy blocks..." + return (view.prompt = "Waiting for " + game.active + " to redeploy blocks...") gen_action_undo(view) if (game.setup_error.length === 0) { view.prompt = "Free Deployment: You may rearrange blocks on the map." @@ -840,7 +864,7 @@ states.free_deployment = { block(who) { push_undo() game.who = who - game.state = 'free_deployment_to' + game.state = "free_deployment_to" }, pass() { if (game.active === CAESAR) { @@ -854,13 +878,13 @@ states.free_deployment = { start_year() } }, - undo: pop_undo + undo: pop_undo, } states.free_deployment_to = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to redeploy blocks..." + return (view.prompt = "Waiting for " + game.active + " to redeploy blocks...") if (game.setup_error.length === 0) { view.prompt = "Free Deployment: You may rearrange blocks on the map." } else { @@ -871,7 +895,7 @@ states.free_deployment_to = { for (let space = first_map_space; space < space_count; ++space) { if (game.setup_limit[space] > 0 && space !== game.location[game.who]) { if (!is_enemy_city(space)) { - if (block_type(game.who) === 'navis') { + if (block_type(game.who) === "navis") { if (is_port(space)) gen_action_space(view, space) } else { @@ -886,10 +910,10 @@ states.free_deployment_to = { set_add(game.moved, game.who) validate_free_deployment() game.who = -1 - game.state = 'free_deployment' + game.state = "free_deployment" }, block: pop_undo, - undo: pop_undo + undo: pop_undo, } function start_year() { @@ -906,7 +930,7 @@ function start_year() { game.c_discard = 0 game.p_discard = 0 game.active = BOTH - game.state = 'discard_and_play_card' + game.state = "discard_and_play_card" game.show_cards = false } @@ -924,33 +948,32 @@ function resume_discard_and_play_card() { states.discard_and_play_card = { prompt(current) { if (current === "Observer") - return view.prompt = "Waiting for players to discard one card and play one card." + return (view.prompt = "Waiting for players to discard one card and play one card.") if (current === CAESAR) { if (!game.c_discard) { view.prompt = "Discard a card." for (let c of game.c_hand) - gen_action(view, 'card', c) + gen_action(view, "card", c) } else if (!game.c_card) { view.prompt = "Play a card." for (let c of game.c_hand) if (c !== APOLLO) - gen_action(view, 'card', c) - gen_action(view, 'undo') + gen_action(view, "card", c) + gen_action(view, "undo") } else { view.prompt = "Waiting for Pompeius..." } - } - else if (current === POMPEIUS) { + } else if (current === POMPEIUS) { if (!game.p_discard) { view.prompt = "Discard a card." for (let c of game.p_hand) - gen_action(view, 'card', c) + gen_action(view, "card", c) } else if (!game.p_card) { view.prompt = "Play a card." for (let c of game.p_hand) if (c !== APOLLO) - gen_action(view, 'card', c) - gen_action(view, 'undo') + gen_action(view, "card", c) + gen_action(view, "undo") } else { view.prompt = "Waiting for Caesar..." } @@ -995,7 +1018,7 @@ states.discard_and_play_card = { } } resume_discard_and_play_card() - } + }, } function start_first_turn() { @@ -1022,7 +1045,7 @@ function start_turn() { game.c_card = 0 game.p_card = 0 game.active = BOTH - game.state = 'play_card' + game.state = "play_card" game.show_cards = false game.surprise = 0 logbr() @@ -1045,7 +1068,7 @@ states.play_card = { if (current === "Observer") { view.prior_c_card = game.prior_c_card view.prior_p_card = game.prior_p_card - return view.prompt = "Waiting for players to play a card." + return (view.prompt = "Waiting for players to play a card.") } if (current === CAESAR) { view.prior_p_card = game.prior_p_card @@ -1055,7 +1078,7 @@ states.play_card = { view.prior_c_card = game.prior_c_card view.prompt = "Play a card." for (let c of game.c_hand) - gen_action(view, 'card', c) + gen_action(view, "card", c) } } if (current === POMPEIUS) { @@ -1066,7 +1089,7 @@ states.play_card = { view.prior_p_card = game.prior_p_card view.prompt = "Play a card." for (let c of game.p_hand) - gen_action(view, 'card', c) + gen_action(view, "card", c) } } }, @@ -1095,7 +1118,7 @@ states.play_card = { } } resume_play_card() - } + }, } function reveal_cards() { @@ -1161,46 +1184,46 @@ function start_player_turn() { reset_road_limits() game.activated = [] - let card = (game.active === CAESAR ? CARDS[game.c_card] : CARDS[game.p_card]) + let card = game.active === CAESAR ? CARDS[game.c_card] : CARDS[game.p_card] if (card.event) { switch (card.event) { // Apollo has already been handled in reveal_cards! - case 'Jupiter': - game.state = 'jupiter' + case "Jupiter": + game.state = "jupiter" break - case 'Vulcan': - game.state = 'vulcan' + case "Vulcan": + game.state = "vulcan" break - case 'Mercury': + case "Mercury": game.mercury = game.active game.moves = 1 game.levies = 0 game.amphibious_available = false - game.state = 'move_who' + game.state = "move_who" game.turn_log = [] break - case 'Pluto': + case "Pluto": game.pluto = game.active game.moves = 1 game.levies = 0 game.amphibious_available = false - game.state = 'move_who' + game.state = "move_who" game.turn_log = [] break - case 'Mars': + case "Mars": game.mars = game.active game.moves = 1 game.levies = 0 game.amphibious_available = false - game.state = 'move_who' + game.state = "move_who" game.turn_log = [] break - case 'Neptune': + case "Neptune": game.neptune = game.active game.moves = 1 game.levies = 0 game.amphibious_available = false - game.state = 'move_who' + game.state = "move_who" game.turn_log = [] break } @@ -1208,7 +1231,7 @@ function start_player_turn() { game.moves = card.move game.levies = card.levy game.amphibious_available = true - game.state = 'move_who' + game.state = "move_who" game.turn_log = [] } @@ -1217,21 +1240,21 @@ function start_player_turn() { function jupiter_block(b) { let type = BLOCKS[b].type - if (type === 'navis' || type === 'leader') { + if (type === "navis" || type === "leader") { log("Jupiter reduced " + block_name(b) + ".") reduce_block(b) end_player_turn() } else { set_toggle(game.traitor, b) game.who = b - game.state = 'jupiter_to' + game.state = "jupiter_to" } } states.jupiter = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + "..." + return (view.prompt = "Waiting for " + game.active + "...") view.prompt = "Jupiter: Choose an enemy city adjacent to a friendly city." for (let s = first_map_space; s < space_count; ++s) { if (is_friendly_city(s)) { @@ -1259,7 +1282,7 @@ states.jupiter = { states.jupiter_to = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + "..." + return (view.prompt = "Waiting for " + game.active + "...") view.prompt = "Jupiter: Move " + block_name(game.who) + " to your city." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1268,7 +1291,7 @@ states.jupiter_to = { }, space(to) { log(block_name(game.who) + " joined " + game.active + ":") - logi("#"+game.location[game.who] + " \u2192 #" + to + ".") + logi("#" + game.location[game.who] + " \u2192 #" + to + ".") game.location[game.who] = to game.who = -1 end_player_turn() @@ -1278,7 +1301,7 @@ states.jupiter_to = { states.vulcan = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + "..." + return (view.prompt = "Waiting for " + game.active + "...") view.prompt = "Vulcan: Choose an enemy city to suffer a volcanic eruption." for (let s = first_map_space; s < space_count; ++s) if (is_enemy_city(s)) @@ -1297,14 +1320,14 @@ states.vulcan = { } } game.active = enemy(game.active) - game.state = 'apply_vulcan' + game.state = "apply_vulcan" }, } states.apply_vulcan = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + "..." + return (view.prompt = "Waiting for " + game.active + "...") view.prompt = "Apply Vulcan hits in " + space_name(game.where) + "." for (let i = 0; i < game.vulcan.length; ++i) gen_action_block(view, game.vulcan[i]) @@ -1337,14 +1360,14 @@ function goto_mars_and_neptune() { delete game.surprise_list return end_player_turn() } - game.state = 'mars_and_neptune' + game.state = "mars_and_neptune" } states.mars_and_neptune = { prompt(current) { let god = game.mars === game.active ? "Mars: " : "Neptune: " if (is_inactive_player(current)) - return view.prompt = god + ": Waiting for " + game.active + "." + return (view.prompt = god + ": Waiting for " + game.active + ".") view.prompt = god + "Select battle for surprise attack." for (let space of game.surprise_list) gen_action_space(view, space) @@ -1358,10 +1381,10 @@ states.mars_and_neptune = { } function is_amphibious_move(who, from, to) { - if (BLOCKS[who].type === 'navis') + if (BLOCKS[who].type === "navis") return false let e = edge_id(from, to) - if (EDGES[e] === 'sea') + if (EDGES[e] === "sea") return true return false } @@ -1390,7 +1413,7 @@ function move_or_attack(to) { states.move_who = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") if (game.pluto === game.active) view.prompt = "Pluto: Move one group. Road limits increase for attacks. No Navis movement." else if (game.mars === game.active) @@ -1400,9 +1423,9 @@ states.move_who = { else if (game.mercury === game.active) view.prompt = "Mercury: Move one group three cities, or two cities and attack. No Navis movement." else if (game.amphibious_available) - view.prompt = "Choose an army to group or amphibious move. "+game.moves+"MP left." + view.prompt = "Choose an army to group or amphibious move. " + game.moves + "MP left." else - view.prompt = "Choose an army to group move. "+game.moves+"MP left." + view.prompt = "Choose an army to group move. " + game.moves + "MP left." if (game.moves === 0) { for (let b = 0; b < block_count; ++b) { let from = game.location[b] @@ -1433,9 +1456,9 @@ states.move_who = { push_undo() game.who = who if (game.mercury === game.active) - game.state = 'mercury_move_1' + game.state = "mercury_move_1" else - game.state = 'move_where' + game.state = "move_where" }, pass() { push_undo() @@ -1447,7 +1470,7 @@ states.move_who = { states.move_where = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Move " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) { @@ -1465,35 +1488,35 @@ states.move_where = { space(to) { let from = game.location[game.who] if (is_amphibious_move(game.who, from, to)) { - game.moves -- + game.moves-- game.location[game.who] = to game.last_from = from log_move_start(from, to) logp("amphibious moved.") if (is_sea(to)) { set_add(game.sea_moved, to) - game.state = 'amphibious_move_to' + game.state = "amphibious_move_to" } else { set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } } else { if (!game.activated.includes(from)) { logp("activated #" + from + ".") - game.moves -- + game.moves-- game.activated.push(from) } game.amphibious_available = false let mark = move_or_attack(to) log_move_start(from, to, mark) if (can_block_continue(game.who, game.last_from)) { - game.state = 'move_where_2' + game.state = "move_where_2" } else { set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } } @@ -1505,7 +1528,7 @@ states.move_where = { states.move_where_2 = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Move " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1522,7 +1545,7 @@ states.move_where_2 = { } set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() }, undo: pop_undo, @@ -1531,7 +1554,7 @@ states.move_where_2 = { states.amphibious_move_to = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Move " + block_name(game.who) + " to a friendly sea or port." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1546,11 +1569,11 @@ states.amphibious_move_to = { log_move_continue(to) if (is_sea(to)) { set_add(game.sea_moved, to) - game.state = 'amphibious_move_to' + game.state = "amphibious_move_to" } else { set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } }, @@ -1560,7 +1583,7 @@ states.amphibious_move_to = { states.mercury_move_1 = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Mercury: Move " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1572,16 +1595,16 @@ states.mercury_move_1 = { let from = game.location[game.who] if (!game.activated.includes(from)) { logp("activated #" + from + ".") - game.moves -- + game.moves-- game.activated.push(from) } let mark = move_or_attack(to) log_move_start(from, to, mark) if (!is_contested_space(to) && can_block_move(game.who)) { - game.state = 'mercury_move_2' + game.state = "mercury_move_2" } else { game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } }, @@ -1591,7 +1614,7 @@ states.mercury_move_1 = { states.mercury_move_2 = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Mercury: Move " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1606,17 +1629,17 @@ states.mercury_move_2 = { let mark = move_or_attack(to) log_move_continue(to, mark) if (can_block_continue(game.who, game.last_from)) { - game.state = 'mercury_move_3' + game.state = "mercury_move_3" } else { set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } } else { set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() } }, @@ -1626,7 +1649,7 @@ states.mercury_move_2 = { states.mercury_move_3 = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move..." + return (view.prompt = "Waiting for " + game.active + " to move...") view.prompt = "Mercury: Move " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) @@ -1643,7 +1666,7 @@ states.mercury_move_3 = { } set_add(game.moved, game.who) game.who = -1 - game.state = 'move_who' + game.state = "move_who" log_move_end() }, undo: pop_undo, @@ -1660,15 +1683,15 @@ function end_movement() { game.who = -1 game.moves = 0 - game.state = 'levy' + game.state = "levy" game.turn_log = [] } states.levy = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to levy..." - view.prompt = "Choose an army to levy. "+game.levies+"LP left." + return (view.prompt = "Waiting for " + game.active + " to levy...") + view.prompt = "Choose an army to levy. " + game.levies + "LP left." let is_levy_possible = false if (game.levies > 0) { for (let b = 0; b < block_count; ++b) { @@ -1688,18 +1711,18 @@ states.levy = { if (BLOCKS[who].levy) { let to = BLOCKS[who].levy log_levy(to) - game.levies -- + game.levies-- game.steps[who] = 1 game.location[who] = to } else { game.who = who - game.state = 'levy_where' + game.state = "levy_where" } } else { log_levy(game.location[who]) - game.levies -- + game.levies-- game.steps[who]++ - game.state = 'levy' + game.state = "levy" } }, pass() { @@ -1711,7 +1734,7 @@ states.levy = { states.levy_where = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to levy..." + return (view.prompt = "Waiting for " + game.active + " to levy...") view.prompt = "Choose a friendly city to levy " + block_name(game.who) + " in." for (let s = first_map_space; s < space_count; ++s) if (can_levy_to(game.who, s)) @@ -1721,11 +1744,11 @@ states.levy_where = { }, space(to) { log_levy(to) - game.levies -- + game.levies-- game.steps[game.who] = 1 game.location[game.who] = to game.who = -1 - game.state = 'levy' + game.state = "levy" }, block: pop_undo, undo: pop_undo, @@ -1749,7 +1772,7 @@ function goto_pick_battle() { reset_road_limits() game.active = game.p1 if (have_contested_spaces()) - game.state = 'pick_battle' + game.state = "pick_battle" else end_turn() } @@ -1757,7 +1780,7 @@ function goto_pick_battle() { states.pick_battle = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to pick a battle..." + return (view.prompt = "Waiting for " + game.active + " to pick a battle...") view.prompt = "Choose the next battle to fight!" for (let s = first_map_space; s < space_count; ++s) if (is_contested_city(s) || is_contested_sea(s)) @@ -1807,13 +1830,13 @@ function start_battle() { log(".h4 Battle in #" + game.where) if (game.surprise === game.where) log("Surprise attack.") - game.state = 'battle_round' + game.state = "battle_round" start_battle_round() } function resume_battle() { game.who = -1 - game.state = 'battle_round' + game.state = "battle_round" pump_battle_round() } @@ -1833,7 +1856,7 @@ function goto_disrupt_reserves() { if (game.reserves.includes(b)) game.disrupted.push(b) game.active = get_attacker(game.where) - game.state = 'disrupt_reserves' + game.state = "disrupt_reserves" } function disrupt_block(who) { @@ -1845,7 +1868,7 @@ function disrupt_block(who) { } function end_disrupt_reserves() { - game.state = 'battle_round' + game.state = "battle_round" game.flash = "" delete game.disrupted bring_on_reserves() @@ -1857,10 +1880,10 @@ states.disrupt_reserves = { show_battle: true, prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to apply disruption hits..." + return (view.prompt = "Waiting for " + game.active + " to apply disruption hits...") view.prompt = "Apply disruption hits to reserves." for (let b of game.disrupted) { - gen_action_battle(view, 'battle_hit', b) + gen_action_battle(view, "battle_hit", b) gen_action_block(view, b) } }, @@ -1941,23 +1964,39 @@ function pump_battle_round() { let defender = enemy(attacker) if (game.surprise === game.where) { - if (battle_step(attacker, 'A', is_attacker)) return - if (battle_step(attacker, 'B', is_attacker)) return - if (battle_step(attacker, 'C', is_attacker)) return - if (battle_step(attacker, 'D', is_attacker)) return - if (battle_step(defender, 'A', is_defender)) return - if (battle_step(defender, 'B', is_defender)) return - if (battle_step(defender, 'C', is_defender)) return - if (battle_step(defender, 'D', is_defender)) return + if (battle_step(attacker, "A", is_attacker)) + return + if (battle_step(attacker, "B", is_attacker)) + return + if (battle_step(attacker, "C", is_attacker)) + return + if (battle_step(attacker, "D", is_attacker)) + return + if (battle_step(defender, "A", is_defender)) + return + if (battle_step(defender, "B", is_defender)) + return + if (battle_step(defender, "C", is_defender)) + return + if (battle_step(defender, "D", is_defender)) + return } else { - if (battle_step(defender, 'A', is_defender)) return - if (battle_step(attacker, 'A', is_attacker)) return - if (battle_step(defender, 'B', is_defender)) return - if (battle_step(attacker, 'B', is_attacker)) return - if (battle_step(defender, 'C', is_defender)) return - if (battle_step(attacker, 'C', is_attacker)) return - if (battle_step(defender, 'D', is_defender)) return - if (battle_step(attacker, 'D', is_attacker)) return + if (battle_step(defender, "A", is_defender)) + return + if (battle_step(attacker, "A", is_attacker)) + return + if (battle_step(defender, "B", is_defender)) + return + if (battle_step(attacker, "B", is_attacker)) + return + if (battle_step(defender, "C", is_defender)) + return + if (battle_step(attacker, "C", is_attacker)) + return + if (battle_step(defender, "D", is_defender)) + return + if (battle_step(attacker, "D", is_attacker)) + return } if (game.hits > 0) { @@ -1990,7 +2029,7 @@ function can_fire_with_block(b) { } function count_enemy_hp_in_battle() { - let is_candidate = (game.active === get_attacker(game.where)) ? is_defender : is_attacker + let is_candidate = game.active === get_attacker(game.where) ? is_defender : is_attacker let n = 0 for (let b = 0; b < block_count; ++b) if (is_candidate(b)) @@ -2066,14 +2105,14 @@ function can_retreat_with_block(who) { function must_retreat_with_block(who) { if (game.location[who] === game.where) if (game.battle_round === 4) - return (block_owner(who) === get_attacker(game.where)) + return block_owner(who) === get_attacker(game.where) return false } function retreat_with_block(who) { if (can_retreat_with_block(who)) { game.who = who - game.state = 'retreat' + game.state = "retreat" } else { eliminate_block(who) resume_battle() @@ -2091,19 +2130,24 @@ states.battle_round = { show_battle: true, prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to choose a combat action..." + return (view.prompt = "Waiting for " + game.active + " to choose a combat action...") let can_fire = false let can_retreat = false let must_retreat = false let can_pass = false if (game.active === get_attacker(game.where)) { - if (game.battle_round < 4) can_fire = true - if (game.battle_round > 1) can_retreat = true - if (game.battle_round < 4) can_pass = true - if (game.battle_round === 4) must_retreat = true + if (game.battle_round < 4) + can_fire = true + if (game.battle_round > 1) + can_retreat = true + if (game.battle_round < 4) + can_pass = true + if (game.battle_round === 4) + must_retreat = true } else { can_fire = true - if (game.battle_round > 1) can_retreat = true + if (game.battle_round > 1) + can_retreat = true can_pass = true } if (can_fire && can_retreat) @@ -2113,14 +2157,16 @@ states.battle_round = { else view.prompt = "Retreat with an army." for (let b of game.battle_list) { - if (can_fire) gen_action_battle(view, 'battle_fire', b) + if (can_fire) + gen_action_battle(view, "battle_fire", b) if (must_retreat || (can_retreat && can_retreat_with_block(b))) - gen_action_battle(view, 'battle_retreat', b) - if (can_pass) gen_action_battle(view, 'battle_pass', b) + gen_action_battle(view, "battle_retreat", b) + if (can_pass) + gen_action_battle(view, "battle_pass", b) gen_action_block(view, b) } if (game.hits > 0) - gen_action(view, 'assign') + gen_action(view, "assign") }, assign() { game.active = enemy(game.active) @@ -2160,7 +2206,7 @@ function goto_battle_hits() { while (game.battle_list.length > 0) { let who = game.battle_list.pop() log_battle(block_name(who) + " took a hit.") - reduce_block(who, 'combat') + reduce_block(who, "combat") game.hits-- ++n } @@ -2175,12 +2221,12 @@ function goto_battle_hits() { game.hits = 0 resume_battle() } else { - game.state = 'battle_hits' + game.state = "battle_hits" } } function list_victims(p) { - let is_candidate = (p === get_attacker(game.where)) ? is_attacker : is_defender + let is_candidate = p === get_attacker(game.where) ? is_attacker : is_defender let max = 0 for (let b = 0; b < block_count; ++b) if (is_candidate(b) && block_strength(b) > max) @@ -2214,10 +2260,10 @@ states.battle_hits = { show_battle: true, prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to apply hits..." + return (view.prompt = "Waiting for " + game.active + " to apply hits...") view.prompt = "Assign " + game.hits + (game.hits !== 1 ? " hits" : " hit") + " to your armies." for (let b of game.battle_list) { - gen_action_battle(view, 'battle_hit', b) + gen_action_battle(view, "battle_hit", b) gen_action_block(view, b) } }, @@ -2233,7 +2279,7 @@ states.retreat = { show_battle: false, prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to retreat..." + return (view.prompt = "Waiting for " + game.active + " to retreat...") view.prompt = "Retreat " + block_name(game.who) + "." let from = game.location[game.who] for (let to of SPACES[from].exits) { @@ -2245,7 +2291,7 @@ states.retreat = { gen_action_space(view, to) } } - gen_action(view, 'undo') + gen_action(view, "undo") gen_action_block(view, game.who) }, space(to) { @@ -2254,12 +2300,12 @@ states.retreat = { push_undo() move_to(game.who, from, to) game.sea_retreated = true - game.state = 'sea_retreat' + game.state = "sea_retreat" } else { move_to(game.who, from, to) game.flash = block_name(game.who) + " retreated." log_battle(game.flash) - game.turn_log.push([game.active, to]) + game.turn_log.push([ game.active, to ]) set_add(game.moved, game.who) resume_battle() } @@ -2276,21 +2322,21 @@ states.sea_retreat = { show_battle: false, prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to retreat..." + return (view.prompt = "Waiting for " + game.active + " to retreat...") view.prompt = "Retreat " + block_name(game.who) + " to a friendly port." let from = game.location[game.who] for (let to of SPACES[from].exits) { if (is_friendly_city(to)) gen_action_space(view, to) } - gen_action(view, 'undo') + gen_action(view, "undo") }, space(to) { clear_undo() let from = game.location[game.who] game.flash = block_name(game.who) + " retreated." log_battle(game.flash) - game.turn_log.push([game.active, from, to]) + game.turn_log.push([ game.active, from, to ]) move_to(game.who, from, to) set_add(game.moved, game.who) resume_battle() @@ -2303,7 +2349,7 @@ function goto_regroup() { if (is_enemy_space(game.where)) game.active = enemy(game.active) log(game.active + " won the battle in #" + game.where + "!") - game.state = 'regroup' + game.state = "regroup" game.turn_log = [] clear_undo() } @@ -2311,7 +2357,7 @@ function goto_regroup() { states.regroup = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to regroup..." + return (view.prompt = "Waiting for " + game.active + " to regroup...") view.prompt = "Regroup: Choose an army to move." for (let b = 0; b < block_count; ++b) { if (game.location[b] === game.where) { @@ -2325,7 +2371,7 @@ states.regroup = { block(who) { push_undo() game.who = who - game.state = 'regroup_to' + game.state = "regroup_to" }, pass() { print_turn_log("regrouped") @@ -2339,7 +2385,7 @@ states.regroup = { states.regroup_to = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to regroup..." + return (view.prompt = "Waiting for " + game.active + " to regroup...") view.prompt = "Regroup: Move " + block_name(game.who) + " to a friendly or vacant location." let from = game.location[game.who] for (let to of SPACES[from].exits) { @@ -2351,10 +2397,10 @@ states.regroup_to = { }, space(to) { let from = game.location[game.who] - game.turn_log.push([from, to]) + game.turn_log.push([ from, to ]) move_to(game.who, from, to) game.who = -1 - game.state = 'regroup' + game.state = "regroup" }, block: pop_undo, undo: pop_undo, @@ -2366,7 +2412,7 @@ function end_turn() { cleopatra_goes_home() check_victory() } else { - game.turn ++ + game.turn++ start_turn() } } @@ -2387,7 +2433,7 @@ function check_victory() { if (game.c_vp >= 10) { game.result = CAESAR game.active = null - game.state = 'game_over' + game.state = "game_over" game.victory = "Caesar won an early victory." logbr() log(game.victory) @@ -2395,7 +2441,7 @@ function check_victory() { game.victory = "Pompeius won an early victory." game.result = POMPEIUS game.active = null - game.state = 'game_over' + game.state = "game_over" logbr() log(game.victory) } else { @@ -2413,8 +2459,8 @@ function check_victory() { function count_navis_to_port() { let count = 0 for (let b = 0; b < block_count; ++b) { - if (block_owner(b) === game.active && BLOCKS[b].type === 'navis') - if (SPACES[game.location[b]].type === 'sea') + if (block_owner(b) === game.active && BLOCKS[b].type === "navis") + if (SPACES[game.location[b]].type === "sea") if (can_navis_move_to_port(b)) ++count } @@ -2425,7 +2471,7 @@ function start_navis_to_port() { game.active = CAESAR let count = count_navis_to_port() if (count > 0) { - game.state = 'navis_to_port' + game.state = "navis_to_port" game.turn_log = [] clear_undo() } else { @@ -2438,7 +2484,7 @@ function next_navis_to_port() { game.active = POMPEIUS let count = count_navis_to_port() if (count > 0) { - game.state = 'navis_to_port' + game.state = "navis_to_port" game.turn_log = [] clear_undo() return @@ -2451,12 +2497,12 @@ function next_navis_to_port() { states.navis_to_port = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move navis to port..." + return (view.prompt = "Waiting for " + game.active + " to move navis to port...") view.prompt = "Move all Navis to a friendly port." let count = 0 for (let b = 0; b < block_count; ++b) { - if (block_owner(b) === game.active && BLOCKS[b].type === 'navis') { - if (SPACES[game.location[b]].type === 'sea') { + if (block_owner(b) === game.active && BLOCKS[b].type === "navis") { + if (SPACES[game.location[b]].type === "sea") { if (can_navis_move_to_port(b)) { gen_action_block(view, b) ++count @@ -2473,7 +2519,7 @@ states.navis_to_port = { block(who) { push_undo() game.who = who - game.state = 'navis_to_port_where' + game.state = "navis_to_port_where" }, pass() { print_turn_log("moved to port") @@ -2485,7 +2531,7 @@ states.navis_to_port = { states.navis_to_port_where = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to move navis to port..." + return (view.prompt = "Waiting for " + game.active + " to move navis to port...") view.prompt = "Move " + block_name(game.who) + " to a friendly port." let from = game.location[game.who] for (let to of SPACES[from].exits) { @@ -2497,10 +2543,10 @@ states.navis_to_port_where = { }, space(to) { let from = game.location[game.who] - game.turn_log.push([from, to]) + game.turn_log.push([ from, to ]) game.location[game.who] = to game.who = -1 - game.state = 'navis_to_port' + game.state = "navis_to_port" }, block: pop_undo, undo: pop_undo, @@ -2508,7 +2554,7 @@ states.navis_to_port_where = { function winter_supply() { game.active = CAESAR - game.state = 'disband' + game.state = "disband" game.turn_log = [] clear_undo() } @@ -2516,7 +2562,7 @@ function winter_supply() { states.disband = { prompt(current) { if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to disband..." + return (view.prompt = "Waiting for " + game.active + " to disband...") let okay_to_end = true for (let b = 0; b < block_count; ++b) { if (block_owner(b) === game.active && is_map_space(game.location[b]) && b !== B_CLEOPATRA) { @@ -2537,13 +2583,12 @@ states.disband = { gen_action_block(view, b) } gen_action_pass(view, "End disbanding") - } gen_action_undo(view) }, block(who) { push_undo() - game.turn_log.push([game.location[who]]) + game.turn_log.push([ game.location[who] ]) disband_block(who) }, pass() { @@ -2561,9 +2606,9 @@ states.disband = { } function end_year() { - game.year ++ + game.year++ for (let b = 0; b < block_count; ++b) { - if (game.location[b] === DEAD && BLOCKS[b].type !== 'leader') { + if (game.location[b] === DEAD && BLOCKS[b].type !== "leader") { disband_block(b) } } @@ -2592,14 +2637,14 @@ function end_game() { else game.victory = "The game ended in a draw." game.active = null - game.state = 'game_over' + game.state = "game_over" logbr() log(game.victory) } states.game_over = { prompt() { - return view.prompt = game.victory + return (view.prompt = game.victory) }, } @@ -2762,10 +2807,12 @@ function make_battle_view() { let attacker = get_attacker(game.where) let bv = { A: attacker, - CF: [], CR: [], - PF: [], PR: [], + CF: [], + CR: [], + PF: [], + PR: [], flash: game.flash, - where: game.where + where: game.where, } bv.title = attacker @@ -2782,7 +2829,7 @@ function make_battle_view() { function fill_cell(name, p, fn) { for (let b = 0; b < block_count; ++b) { - if (game.location[b] === game.where & block_owner(b) === p && fn(b)) { + if ((game.location[b] === game.where) & (block_owner(b) === p) && fn(b)) { bv[name].push(b) } } @@ -2803,7 +2850,7 @@ function observer_hand() { return hand } -exports.dont_snap = function(state) { +exports.dont_snap = function (state) { if (state.state === "play_card" && state.active !== BOTH) return true if (state.state === "discard_and_play_card" && state.active !== BOTH) @@ -2811,7 +2858,7 @@ exports.dont_snap = function(state) { return false } -exports.view = function(state, current) { +exports.view = function (state, current) { game = state count_vp() @@ -2823,10 +2870,10 @@ exports.view = function(state, current) { active: game.active, c_vp: game.c_vp, p_vp: game.p_vp, - c_card: (game.show_cards || current === CAESAR) ? game.c_card : 0, - p_card: (game.show_cards || current === POMPEIUS) ? game.p_card : 0, - hand: (current === CAESAR) ? game.c_hand : (current === POMPEIUS) ? game.p_hand : observer_hand(), - who: (game.active === current) ? game.who : -1, + c_card: game.show_cards || current === CAESAR ? game.c_card : 0, + p_card: game.show_cards || current === POMPEIUS ? game.p_card : 0, + hand: current === CAESAR ? game.c_hand : current === POMPEIUS ? game.p_hand : observer_hand(), + who: game.active === current ? game.who : -1, location: game.location, traitor: game.traitor, steps: game.steps, @@ -2879,11 +2926,11 @@ function array_insert(array, index, item) { function array_insert_pair(array, index, key, value) { for (let i = array.length; i > index; i -= 2) { - array[i] = array[i-2] - array[i+1] = array[i-1] + array[i] = array[i - 2] + array[i + 1] = array[i - 1] } array[index] = key - array[index+1] = value + array[index + 1] = value } function set_clear(set) { @@ -2959,13 +3006,13 @@ function map_get(map, key, missing) { let b = (map.length >> 1) - 1 while (a <= b) { let m = (a + b) >> 1 - let x = map[m<<1] + let x = map[m << 1] if (key < x) b = m - 1 else if (key > x) a = m + 1 else - return map[(m<<1)+1] + return map[(m << 1) + 1] } return missing } @@ -2975,17 +3022,17 @@ function map_set(map, key, value) { let b = (map.length >> 1) - 1 while (a <= b) { let m = (a + b) >> 1 - let x = map[m<<1] + let x = map[m << 1] if (key < x) b = m - 1 else if (key > x) a = m + 1 else { - map[(m<<1)+1] = value + map[(m << 1) + 1] = value return } } - array_insert_pair(map, a<<1, key, value) + array_insert_pair(map, a << 1, key, value) } // Fast deep copy for objects without cycles |