"use strict"
// vim: set nowrap:
/* globals
* data, political_cards, view
* action_button, action_button_with_argument, confirm_action_button, send_action, scroll_into_view, send_message
* params, roles, game_cookie
*/
// TODO: animate_position on pieces instead of top/left transition
// TODO: animate_position on political markers instead of top/left transition
function toggle_pieces() {
// Cycle between showing everything, only pieces, and nothing.
let hidden_pieces = ui.pieces_element.classList.contains("hide")
let hidden_markers = ui.markers_element.classList.contains("hide")
if (hidden_pieces && hidden_markers) {
ui.pieces_element.classList.remove("hide")
ui.markers_element.classList.remove("hide")
} else if (hidden_pieces) {
ui.markers_element.classList.add("hide")
} else {
ui.pieces_element.classList.add("hide")
}
}
function toggle_shift() {
document.body.classList.toggle("shift")
}
function action_menu_item(action) {
let menu = document.getElementById(action + "_menu")
if (view.actions && action in view.actions) {
menu.classList.toggle("disabled", view.actions[action] === 0)
return 1
} else {
menu.classList.toggle("disabled", true)
return 0
}
}
const R_LOUIS_XV = "Louis XV"
const R_FREDERICK = "Frederick"
const R_MARIA_THERESA = "Maria Theresa"
const R_PLAYER_A = "Player A"
const R_PLAYER_B = "Player B"
/* DATA (SHARED) */
const deck_name = [ "red", "green", "blue", "yellow" ]
const suit_name = [ "\u2660", "\u2663", "\u2665", "\u2666", "R" ]
const suit_class = [ "spades", "clubs", "hearts", "diamonds", "reserve" ]
const suit_letter = [ "S", "C", "H", "D", "R" ]
const P_FRANCE = 0
const P_PRUSSIA = 1
const P_PRAGMATIC = 2
const P_AUSTRIA = 3
const P_BAVARIA = 4
const P_SAXONY = 5
const SET_ASIDE_FRANCE = 4
const SET_ASIDE_PRUSSIA = 5
const power_name = [ "France", "Prussia", "Pragmatic Army", "Austria", "Bavaria", "Saxony" ]
const power_class = [
"france", "prussia", "pragmatic", "austria", "bavaria", "saxony",
"france bavaria",
"prussia saxony",
"austria pragmatic",
"austria pragmatic saxony",
]
const turn_name = [
"Setup",
"Turn 1",
"Turn 2",
"Turn 3",
"Winter 1741",
"Turn 4",
"Turn 5",
"Turn 6",
"Winter 1742",
"Turn 7",
"Turn 8",
"Turn 9",
"Winter 1743",
"Turn 10",
"Turn 11",
"Turn 12",
"Winter 1744",
]
const DI_A_POWER = 0
const DI_B_POWER = 1
const DI_A_TURN = 2
const DI_B_TURN = 3
const DI_A_PHASE = 4
const DI_B_PHASE = 5
const DI_A_PROMISE = 6
const DI_B_PROMISE = 7
const F_MOVE_FLANDERS = 1
const F_MOVE_DISPUTE = 2
const F_SAXONY_TC_ONCE = 4
const F_FRANCE_REDUCED = 8
const F_EMPEROR_FRANCE = 16
const F_EMPEROR_AUSTRIA = 32
const F_ITALY_FRANCE = 64
const F_ITALY_AUSTRIA = 128
const F_SILESIA_ANNEXED = 256
const F_IMPERIAL_ELECTION = 512
const F_PRUSSIA_NEUTRAL = 1024
const F_PRUSSIA_NEUTRAL_2 = 2048
const F_WAR_OF_JENKINS_EAR = 4096
const F_INTRODUCTORY = 8192
const F_TWO_PLAYER = 16384
const F_PLAYER_A_PICK = 32768 // picked PC card
const F_PLAYER_B_PICK = 65536
// promise validation phases
const V_POLITICS = 1
const V_HUSSARS = 2
const V_MOVEMENT = 4
const V_RETREAT = 8
const SPADES = 0
const CLUBS = 1
const HEARTS = 2
const DIAMONDS = 3
const RESERVE = 4
const IMPERIAL_ELECTION = 24
const ELIMINATED = data.cities.name.length
const ARENBERG = 17
const SAXONY_GENERAL = 19
const PRUSSIAN_TRAIN_2 = 23
const all_powers = [ 0, 1, 2, 3, 4, 5 ]
const all_major_powers = [ 0, 1, 2, 3 ]
const all_minor_powers = [ 4, 5 ]
const all_power_generals = [
[ 0, 1, 2, 3, 4 ],
[ 5, 6, 7, 8 ],
[ 9, 10, 11 ],
[ 12, 13, 14, 15, 16, 17 ],
[ 18 ],
[ 19 ],
]
const all_power_trains = [
[ 20, 21 ],
[ 22, 23 ],
[ 24 ],
[ 25, 26, 27 ],
[ 28 ],
[ 29 ],
]
const all_power_pieces = [
[ ...all_power_generals[0], ...all_power_trains[0] ],
[ ...all_power_generals[1], ...all_power_trains[1] ],
[ ...all_power_generals[2], ...all_power_trains[2] ],
[ ...all_power_generals[3], ...all_power_trains[3] ],
[ ...all_power_generals[4], ...all_power_trains[4] ],
[ ...all_power_generals[5], ...all_power_trains[5] ],
]
const last_piece = 29
const all_hussars = [ 30, 31 ]
const piece_power = [
P_FRANCE, P_FRANCE, P_FRANCE, P_FRANCE, P_FRANCE,
P_PRUSSIA, P_PRUSSIA, P_PRUSSIA, P_PRUSSIA,
P_PRAGMATIC, P_PRAGMATIC, P_PRAGMATIC,
P_AUSTRIA, P_AUSTRIA, P_AUSTRIA, P_AUSTRIA, P_AUSTRIA, P_AUSTRIA,
P_BAVARIA,
P_SAXONY,
P_FRANCE, P_FRANCE,
P_PRUSSIA, P_PRUSSIA,
P_PRAGMATIC,
P_AUSTRIA, P_AUSTRIA, P_AUSTRIA,
P_BAVARIA,
P_SAXONY,
P_AUSTRIA, P_AUSTRIA
]
const piece_rank = [
1, 2, 3, 4, 5,
1, 2, 3, 4,
1, 2, 3,
1, 2, 3, 4, 5, 6,
1,
1,
]
const piece_abbr = [
"F1", "F2", "F3", "F4", "F5",
"P1", "P2", "P3", "P4",
"PA1", "PA2", "PA3",
"A1", "A2", "A3", "A4", "A5", "A6",
"B1",
"S1",
]
const piece_name = [
"Moritz",
"Belle-Isle",
"Broglie",
"Maillebois",
"Noailles",
"Friedrich",
"Schwerin",
"Leopold",
"Dessauer",
"George II",
"Cumberland",
"Earl of Stair",
"Karl",
"Traun",
"Khevenhüller",
"Batthyány",
"Neipperg",
"Arenberg",
"Törring",
"Rutowski",
"supply train", "supply train",
"supply train", "supply train",
"supply train",
"supply train", "supply train", "supply train",
"supply train",
"supply train",
"hussar", "hussar",
]
const piece_log_name = [
"Moritz",
"Belle-Isle",
"Broglie",
"Maillebois",
"Noailles",
"Friedrich",
"Schwerin",
"Leopold",
"Dessauer",
"George II",
"Cumberland",
"Earl of Stair",
"Karl",
"Traun",
"Khevenhüller",
"Batthyány",
"Neipperg",
"Arenberg",
"Törring",
"Rutowski",
"French ST", "French ST",
"Prussian ST", "Prussian ST",
"Pragmatic Army ST",
"Austrian ST", "Austrian ST", "Austrian ST",
"Bavarian ST",
"Saxon ST",
"Hussars", "Hussars",
]
function to_deck(c) {
return c >> 7
}
function to_suit(c) {
return (c >> 4) & 7
}
function to_value(c) {
if (to_suit(c) === RESERVE)
return 8
return c & 15
}
/* DATA */
const last_city = data.cities.name.length - 1
const ELIMINATED_TRAIN_X = 340
const ELIMINATED_TRAIN_Y = 160
const ELIMINATED_TRAIN_DX = 33
const ELIMINATED_GENERAL_X = 340
const ELIMINATED_GENERAL_Y = 110
const ELIMINATED_GENERAL_DX = 50
const ELECTORAL_COLLEGE_X = 526
const ELECTORAL_COLLEGE_Y = 1574
const ELECTORAL_COLLEGE_DX = 52
const HUSSAR_X = 2485 - 36
const HUSSAR_Y = 960
const HUSSAR_DX = 0
const HUSSAR_DY = -48
const VICTORY_BOX_X = [ 115, 170, 235 ]
const VICTORY_BOX_Y = [ 235, 290, 345, 401 ]
function find_city(city) {
return data.cities.name.indexOf(city)
}
function is_bohemia_space(s) {
return s >= 0 && s <= 401
}
const all_electoral_colleges = [
// find_city("England"),
find_city("Prag"),
find_city("Mainz"),
find_city("Trier"),
find_city("Köln"),
find_city("Mannheim"),
find_city("München"),
find_city("Dresden"),
find_city("Berlin"),
]
const all_elector_fortresses = []
set_add(all_elector_fortresses, find_city("Trier"))
set_add(all_elector_fortresses, find_city("Mainz"))
set_add(all_elector_fortresses, find_city("Köln"))
set_add(all_elector_fortresses, find_city("Mannheim"))
const all_fortresses = []
set_add_all(all_fortresses, data.type.major_fortress)
set_add_all(all_fortresses, data.type.minor_fortress)
const all_home_country_fortresses = []
all_home_country_fortresses[P_FRANCE] = set_intersect(all_fortresses, data.country.France)
all_home_country_fortresses[P_PRUSSIA] = set_intersect(all_fortresses, data.country.Prussia)
all_home_country_fortresses[P_PRAGMATIC] = set_intersect(all_fortresses, data.country.Netherlands)
all_home_country_fortresses[P_AUSTRIA] = set_intersect(all_fortresses, data.country.Austria)
all_home_country_fortresses[P_BAVARIA] = set_intersect(all_fortresses, data.country.Bavaria)
all_home_country_fortresses[P_SAXONY] = set_intersect(all_fortresses, data.country.Saxony)
const all_silesian_fortresses = set_intersect(all_fortresses, data.country.Silesia)
const all_core_austria_fortresses = all_home_country_fortresses[P_AUSTRIA].filter(is_bohemia_space)
const all_core_austria_and_silesia_fortresses = set_union(
all_core_austria_fortresses,
all_silesian_fortresses
)
function is_intro() {
return view.flags & F_INTRODUCTORY
}
function is_two_player() {
return view.flags & F_TWO_PLAYER
}
function is_saxony_neutral() {
return view.saxony > 2 && view.saxony < 5
}
function count_victory_markers_in_country(pow, country) {
let n = 0
map_for_each(view.victory, (vspace, vpow) => {
if (vpow === pow && set_has(country, vspace))
++n
})
return n
}
/* SHOW PATHS */
const svgNS = "http://www.w3.org/2000/svg"
var _show_path_pcs = []
var _show_path = []
function make_road(a, b, type) {
let e = document.createElementNS(svgNS, "line")
e.setAttribute("class", type)
let x1 = data.cities.x[a]
let y1 = data.cities.y[a]
let x2 = data.cities.x[b]
let y2 = data.cities.y[b]
/*
let v = Math.hypot(x2 - x1, y2 - y1)
let dx = (x2 - x1) / v
let dy = (y2 - y1) / v
let r = 18
x1 += r * dx
y1 += r * dy
x2 -= r * dx
y2 -= r * dy
*/
e.setAttribute("x1", x1)
e.setAttribute("y1", y1)
e.setAttribute("x2", x2)
e.setAttribute("y2", y2)
e.setAttribute("visibility", "hidden")
document.getElementById("roads").appendChild(e)
if (!ui.roads[a]) ui.roads[a] = {}
if (!ui.roads[b]) ui.roads[b] = {}
ui.roads[a][b] = e
ui.roads[b][a] = e
}
function on_focus_path_tip(ps, ss) {
_show_path_pcs = ps
for (let p of _show_path_pcs)
on_focus_piece_tip(p)
hide_move_path()
_show_path = ss
show_move_path(view.pos[ps[0]] !== ss[ss.length-1])
ui.status.textContent = ss.map(s => data.cities.name[s]).join(" - ")
}
function on_blur_path_tip() {
for (let p of _show_path_pcs)
on_blur_piece_tip(p)
hide_move_path()
ui.status.textContent = ""
}
function show_move_path(tip_end) {
if (_show_path && _show_path.length > 0) {
for (let i = 0; i < _show_path.length; ++i)
ui.cities[_show_path[i]].classList.add("move")
if (tip_end)
ui.cities[_show_path[_show_path.length-1]].classList.add("tip")
for (let i = 1; i < _show_path.length; ++i) {
let x = _show_path[i-1]
let y = _show_path[i]
ui.roads[x][y].setAttribute("visibility", "visible")
}
}
}
function hide_move_path() {
if (_show_path && _show_path.length > 0) {
for (let i = 0; i < _show_path.length; ++i)
ui.cities[_show_path[i]].classList.remove("move")
ui.cities[_show_path[_show_path.length-1]].classList.remove("tip")
for (let i = 1; i < _show_path.length; ++i) {
let x = _show_path[i-1]
let y = _show_path[i]
ui.roads[x][y].setAttribute("visibility", "hidden")
}
_show_path = null
}
}
/* PANEL ORDER */
const panel_order_p = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA, 6 ]
const panel_order_a = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_PRAGMATIC, P_AUSTRIA, P_SAXONY, 6 ]
const panel_start_p = {
"Observer": 0,
"Louis XV": 0,
"Frederick": 2,
"Maria Theresa": 5,
}
const panel_start_a = {
"Observer": 0,
"Louis XV": 0,
"Frederick": 2,
"Maria Theresa": 4,
}
function remember_position(e) {
if (e.parentElement) {
let rect = e.getBoundingClientRect()
e.my_parent = e.parentElement
e.my_x = rect.x
e.my_y = rect.y
} else {
e.my_parent = null
e.my_x = 0
e.my_y = 0
}
}
function animate_position(e) {
if (e.parentElement) {
if (e.my_parent) {
let rect = e.getBoundingClientRect()
let dx = e.my_x - rect.x
let dy = e.my_y - rect.y
if (dx !== 0 || dy !== 0) {
e.animate(
[
{ transform: `translate(${dx}px, ${dy}px)`, },
{ transform: "translate(0, 0)", },
],
{ duration: 333, easing: "ease" }
)
}
}
}
}
function sort_power_panel(animate) {
let panel_order = (!view || view.saxony < 3) ? panel_order_p : panel_order_a
let panel_start = (!view || view.saxony < 3) ? panel_start_p : panel_start_a
let start = panel_start[params.role] | 0
if (animate)
for (let i = 0; i < 7; ++i)
remember_position(ui.power_panel[i])
ui.power_panel_list.replaceChildren()
for (let i = 0; i < 7; ++i) {
let p = panel_order[(i + start) % 7]
ui.power_panel_list.appendChild(ui.power_panel[p])
}
if (view && view.actions)
ui.power_panel_list.prepend(ui.power_panel[view.power])
if (view && (view.political || (view.actions && view.actions.shift)))
ui.power_panel_list.prepend(ui.power_panel[6])
if (animate)
for (let i = 0; i < 7; ++i)
animate_position(ui.power_panel[i])
}
/* BUILD UI */
const ui = {
prompt: document.getElementById("prompt"),
status: document.getElementById("status"),
header: document.querySelector("header"),
tooltip: document.getElementById("tooltip"),
spaces_element: document.getElementById("spaces"),
pieces_element: document.getElementById("pieces"),
markers_element: document.getElementById("markers"),
pol_tracks: document.getElementById("pol_tracks"),
pc_deck: document.getElementById("pc_deck"),
pc_show_1: document.getElementById("pc_show_1"),
pc_show_2: document.getElementById("pc_show_2"),
pc_placed: [
document.getElementById("placed_france"),
document.getElementById("placed_prussia"),
document.getElementById("placed_pragmatic"),
document.getElementById("placed_austria"),
],
discard: [
document.getElementById("discard_1"),
document.getElementById("discard_2"),
document.getElementById("discard_3"),
document.getElementById("discard_4"),
],
power_panel_list: document.getElementById("power_panel_list"),
power_header: [
document.getElementById("hand_france_header"),
document.getElementById("hand_prussia_header"),
document.getElementById("hand_pragmatic_header"),
document.getElementById("hand_austria_header"),
document.getElementById("hand_bavaria_header"),
document.getElementById("hand_saxony_header"),
document.getElementById("political_header"),
],
power_panel: [
document.getElementById("hand_france_panel"),
document.getElementById("hand_prussia_panel"),
document.getElementById("hand_pragmatic_panel"),
document.getElementById("hand_austria_panel"),
document.getElementById("hand_bavaria_panel"),
document.getElementById("hand_saxony_panel"),
document.getElementById("political_panel"),
],
hand: [
document.getElementById("hand_france"),
document.getElementById("hand_prussia"),
document.getElementById("hand_pragmatic"),
document.getElementById("hand_austria"),
document.getElementById("hand_bavaria"),
document.getElementById("hand_saxony"),
],
missing: [
document.getElementById("missing_france"),
document.getElementById("missing_prussia"),
document.getElementById("missing_pragmatic"),
document.getElementById("missing_austria"),
],
cities: [],
roads: [],
action_register: [],
}
function register_action(target, action, id) {
target.my_id = id
target.my_action = action
target.onmousedown = (evt) => on_click_action(evt, target)
ui.action_register.push(target)
}
function on_click_action(evt, target) {
if (evt.button === 0)
if (send_action(target.my_action, target.my_id))
evt.stopPropagation()
}
function process_actions() {
for (let target of ui.action_register)
target.classList.toggle("action", is_action(target.my_action, target.my_id))
}
function is_action(action, arg) {
if (arg === undefined)
return !!(view.actions && view.actions[action] === 1)
return !!(view.actions && view.actions[action] && set_has(view.actions[action], arg))
}
function create_element(action, id, style) {
let e = document.createElement("div")
e.className = style
register_action(e, action, id)
return e
}
function create_piece(action, id, style) {
let e = document.createElement("div")
e.className = style
register_action(e, action, id)
e.onmouseenter = on_focus_piece
e.onmouseleave = on_blur_piece
return e
}
function create_marker(style) {
let e = document.createElement("div")
e.className = style
return e
}
function make_tc_deck(n) {
for (let suit = 0; suit <= 3; ++suit) {
for (let value = 2; value <= 10; ++value) {
let c = (n << 7) | (suit << 4) | value
ui.tc[c] = create_element("card", c, "card tc deck_" + (n+1) + " " + suit_class[suit] + " " + suit_letter[suit] + value)
}
}
for (let value = 2; value <= 3; ++value) {
let c = (n << 7) | (4 << 4) | value
ui.tc[c] = create_element("card", c, "card tc deck_" + (n+1) + " reserve R")
}
}
function make_tc_deck_back(n) {
let list = []
for (let i = 0; i < 38; ++i) {
let e = document.createElement("div")
e.className = "card tc reverse " + n
list.push(e)
}
return list
}
function make_tc_deck_pile(n) {
let list = []
for (let i = 0; i < 10; ++i) {
let e = document.createElement("div")
e.className = "card tc pile reverse " + n
list.push(e)
}
return list
}
function make_tc_deck_hand(n) {
let list = []
for (let i = 0; i < 100; ++i) {
let e = document.createElement("div")
e.className = "card tc hand reverse " + n
list.push(e)
}
return list
}
function make_political_card(id) {
let e = document.createElement("div")
e.className = "card polcard c" + id
register_action(e, "political", id)
return e
}
function make_political_back(deck) {
let e = document.createElement("div")
e.className = "pile card polcard reverse deck_" + deck
return e
}
function make_badge(power) {
let e = document.createElement("div")
e.className = "role_marker " + power
return e
}
const power_image = [
'',
'',
'',
'',
'',
'',
]
const RU_LEFT = ''
const IT_LEFT = ''
const SX_LEFT = ''
const RU_RIGHT = ''
const IT_RIGHT = ''
const SX_RIGHT = ''
const SX_BOX = ''
const IT_BOX = ''
const RU_BOX = ''
const power_badge = [
make_badge("france"),
make_badge("prussia"),
make_badge("pragmatic"),
make_badge("austria"),
make_badge("bavaria"),
make_badge("saxony"),
]
function on_init() {
ui.pieces = [
create_piece("piece", 0, "piece cylinder france france_1"),
create_piece("piece", 1, "piece cylinder france france_2"),
create_piece("piece", 2, "piece cylinder france france_3"),
create_piece("piece", 3, "piece cylinder france france_4"),
create_piece("piece", 4, "piece cylinder france france_5"),
create_piece("piece", 5, "piece cylinder prussia prussia_1"),
create_piece("piece", 6, "piece cylinder prussia prussia_2"),
create_piece("piece", 7, "piece cylinder prussia prussia_3"),
create_piece("piece", 8, "piece cylinder prussia prussia_4"),
create_piece("piece", 9, "piece cylinder pragmatic pragmatic_1"),
create_piece("piece", 10, "piece cylinder pragmatic pragmatic_2"),
create_piece("piece", 11, "piece cylinder pragmatic pragmatic_3"),
create_piece("piece", 12, "piece cylinder austria austria_1"),
create_piece("piece", 13, "piece cylinder austria austria_2"),
create_piece("piece", 14, "piece cylinder austria austria_3"),
create_piece("piece", 15, "piece cylinder austria austria_4"),
create_piece("piece", 16, "piece cylinder austria austria_5"),
create_piece("piece", 17, "piece cylinder austria austria_6"),
create_piece("piece", 18, "piece cylinder bavaria bavaria_1"),
create_piece("piece", 19, "piece cylinder saxony saxony_1"),
create_piece("piece", 20, "piece cube france"),
create_piece("piece", 21, "piece cube france"),
create_piece("piece", 22, "piece cube prussia"),
create_piece("piece", 23, "piece cube prussia"),
create_piece("piece", 24, "piece cube pragmatic"),
create_piece("piece", 25, "piece cube austria"),
create_piece("piece", 26, "piece cube austria"),
create_piece("piece", 27, "piece cube austria"),
create_piece("piece", 28, "piece cube bavaria"),
create_piece("piece", 29, "piece cube saxony"),
create_piece("piece", 30, "piece disc austria"),
create_piece("piece", 31, "piece disc austria"),
]
ui.troops = []
for (let i = 0; i < 20; ++i)
ui.troops[i] = create_marker("hide")
for (let e of ui.troops)
ui.pieces_element.appendChild(e)
ui.victory = [ [], [], [], [] ]
ui.retro = [ [], [], [], [] ]
for (let pow of [ P_FRANCE, P_PRUSSIA, P_PRAGMATIC, P_AUSTRIA ]) {
ui.victory[pow] = []
ui.retro[pow] = []
for (let i = 0; i < 100; ++i) {
ui.victory[pow].push(create_conquest("marker victory_marker_" + power_class[pow]))
ui.retro[pow].push(create_conquest("marker question_marker_" + power_class[pow]))
}
}
ui.elector = [ [], [], [], [], [], [] ]
for (let i = 0; i < 50; ++i) {
ui.elector[P_FRANCE].push(create_conquest("marker elector_marker_france"))
ui.elector[P_PRUSSIA].push(create_conquest("marker elector_marker_prussia"))
ui.elector[P_PRAGMATIC].push(create_conquest("marker elector_marker_austria_pragmatic"))
ui.elector[P_AUSTRIA].push(create_conquest("marker elector_marker_austria_pragmatic"))
ui.elector[P_BAVARIA].push(create_conquest("marker elector_marker_france"))
ui.elector[P_SAXONY].push(create_conquest("marker elector_marker_saxony"))
}
ui.tc = []
make_tc_deck(0)
make_tc_deck(1)
make_tc_deck(2)
make_tc_deck(3)
ui.tc_back = [
make_tc_deck_back("deck_1"),
make_tc_deck_back("deck_2"),
make_tc_deck_back("deck_3"),
make_tc_deck_back("deck_4"),
]
ui.tc_hand = [
// TODO: or make_tc_deck_pile ???
make_tc_deck_hand("deck_1"),
make_tc_deck_hand("deck_1"),
make_tc_deck_hand("deck_1"),
make_tc_deck_hand("deck_1"),
make_tc_deck_hand("deck_1"),
make_tc_deck_hand("deck_1"),
]
ui.tc_discard = [
make_tc_deck_pile("deck_1"),
make_tc_deck_pile("deck_2"),
make_tc_deck_pile("deck_3"),
make_tc_deck_pile("deck_4"),
]
ui.combat = document.createElement("div")
ui.combat.id = "combat"
ui.combat.style.zIndex = 2000
ui.tcbreak1 = document.createElement("div")
ui.tcbreak1.className = "draw-break"
ui.tcbreak2 = document.createElement("div")
ui.tcbreak2.className = "draw-break"
ui.tcbreak3 = document.createElement("div")
ui.tcbreak3.className = "draw-break"
ui.turn = document.createElement("div")
ui.turn.className = "marker turn_marker"
ui.saxony = document.createElement("div")
ui.saxony.className = "marker political_marker_saxony"
ui.russia = document.createElement("div")
ui.russia.className = "marker political_marker_russia"
ui.italy = document.createElement("div")
ui.italy.className = "marker political_marker_italy"
ui.political = []
for (let pc = 0; pc < 25; ++pc)
ui.political[pc] = make_political_card(pc)
ui.political_back = [
[],
[ make_political_back(4), make_political_back(3), make_political_back(2), make_political_back(1) ],
[ make_political_back(4), make_political_back(3), make_political_back(2) ],
[ make_political_back(4), make_political_back(3) ],
[ make_political_back(4) ],
]
for (let a = 0; a <= last_city; ++a) {
let e = (ui.cities[a] = document.createElement("div"))
let x = data.cities.x[a]
let y = data.cities.y[a]
if (set_has(data.type.major_fortress, a)) {
e.className = "space major_fortress"
x -= 16 + 6 + 4
y -= 16 + 6 + 4
} else if (set_has(data.type.minor_fortress, a)) {
e.className = "space minor_fortress"
x -= 14 + 5 + 4
y -= 14 + 5 + 4
} else if (set_has(data.type.box, a)) {
e.className = "space box"
if (data.cities.name[a] === "England") {
e.className = "space box england"
x -= 24 + 4
y -= 28 + 4
} else {
x -= 22 + 4
y -= 22 + 4
}
} else {
e.className = "space city"
x -= 9 + 5 + 4
y -= 9 + 5 + 4
}
if (set_has(data.country.Austria, a))
e.classList.add("country_austria")
if (set_has(data.country.France, a))
e.classList.add("country_france")
if (set_has(data.country.Prussia, a))
e.classList.add("country_prussia")
if (set_has(data.country.Netherlands, a))
e.classList.add("country_netherlands")
if (set_has(data.country.Silesia, a))
e.classList.add("country_silesia")
if (set_has(data.country.Saxony, a))
e.classList.add("country_saxony")
if (set_has(data.country.Bavaria, a))
e.classList.add("country_bavaria")
if (set_has(data.country.Poland, a))
e.classList.add("country_poland")
if (set_has(data.country.Empire, a))
e.classList.add("country_empire")
register_action(e, "space", a)
e.onmouseenter = on_focus_city
e.onmouseleave = on_blur_city
//e.classList.add("hide")
e.style.left = x + "px"
e.style.top = y + "px"
ui.spaces_element.appendChild(e)
}
for (let a = 0; a <= last_city; ++a) {
for (let b of data.cities.main_roads[a])
if (a < b)
make_road(a, b, "road")
for (let b of data.cities.roads[a])
if (a < b)
make_road(a, b, "road")
}
sort_power_panel(false)
update_favicon()
}
on_init()
/* TOOLTIPS */
function on_click_city_tip(loc) {
ui.cities[loc].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
}
function on_focus_city_tip(s) {
ui.cities[s].classList.add("tip")
}
function on_blur_city_tip(s) {
ui.cities[s].classList.remove("tip")
}
function on_click_piece_tip(loc) {
ui.pieces[loc].scrollIntoView({ block: "center", inline: "center", behavior: "smooth" })
}
function on_focus_piece_tip(s) {
ui.pieces[s].classList.add("tip")
}
function on_blur_piece_tip(s) {
ui.pieces[s].classList.remove("tip")
}
function on_focus_political_tip(c) {
ui.tooltip.className = "card polcard c" + c
}
function on_blur_political_tip() {
ui.tooltip.className = "hide"
}
function on_focus_city(evt) {
ui.status.textContent = data.cities.name[evt.target.my_id]
}
function on_blur_city() {
ui.status.textContent = ""
}
function on_focus_piece(evt) {
let p = evt.target.my_id
let text = piece_log_name[p]
if (p < 20 && view.troops[p] > 0) {
text += " (" + view.troops[p] + " troops"
if (view.oos & (1 << p))
text += " \u2013 flipped"
text += ")"
}
ui.status.textContent = text
}
function on_blur_piece() {
ui.status.textContent = ""
}
/* UPDATE UI */
function is_power_controlled_fortress(pow, s) {
let owner = map_get(view.elector, s, -1)
if (owner < 0)
owner = map_get(view.victory, s, -1)
if (owner < 0)
return set_has(all_home_country_fortresses[pow], s)
return owner === pow
}
function layout_general_offset(who, here) {
let other = -1
for (let p = 0; p < 20; ++p)
if (view.pos[p] === here && who !== p)
other = p
if (other < 0)
return 0
if ((view.supreme & (1 << who)) || view.selected === who)
return 1
if ((view.supreme & (1 << other)) || view.selected === other)
return 0
if (piece_rank[who] < piece_rank[other])
return 1
return 0
}
function is_other_general_on_top(who, here) {
let other = -1
for (let p = 0; p < 20; ++p)
if (view.pos[p] === here && who !== p)
other = p
// no other general!
if (other < 0)
return false // alone
if ((view.supreme & (1 << who)) || view.selected === who)
return false // on top
if ((view.supreme & (1 << other)) || view.selected === other)
return true // on bottom
if (piece_rank[who] < piece_rank[other])
return false // on top
return true // on bottom
}
function layout_general_offset_elim(g) {
let n = 0
let p = get_cylinder_power(g)
for (let i of all_power_generals[p])
if (i > g && view.pos[i] === ELIMINATED)
++n
return n
}
function layout_train_offset(g, s) {
let n = 0
for (let i = 20; i < g; ++i)
if (view.pos[i] === s)
++n
return n
}
function get_cylinder_power(id) {
for (let p of all_powers)
if (set_has(all_power_generals[p], id))
return p
return -1
}
function layout_hussar(id, s) {
let e = ui.pieces[id]
let n = 0
let x, y
if (e.parentElement !== ui.pieces_element)
ui.pieces_element.appendChild(e)
if (s === ELIMINATED) {
x = HUSSAR_X
y = HUSSAR_Y
n = id - 30
} else {
x = data.cities.x[s]
y = data.cities.y[s]
}
e.style.left = (x - 21 + n * HUSSAR_DX) + "px"
e.style.top = (y - 29 + 7 + n * HUSSAR_DY) + "px"
e.classList.toggle("selected", set_has(view.selected, id))
}
function layout_general(id, s) {
let x, y, n
if (s === ELIMINATED) {
n = layout_general_offset_elim(id)
x = ELIMINATED_GENERAL_X + ELIMINATED_GENERAL_DX * get_cylinder_power(id)
y = ELIMINATED_GENERAL_Y
} else {
n = layout_general_offset(id, s)
x = data.cities.x[s]
y = data.cities.y[s]
}
if (is_other_general_on_top(id, s)) {
if (view.oos & (1 << id))
n = -2/3
}
let selected = set_has(view.selected, id)
let e = ui.pieces[id]
if (e.parentElement !== ui.pieces_element)
ui.pieces_element.appendChild(e)
e.style.left = (x - 21) + "px"
e.style.top = (y - 29 - 15 * n) + "px"
e.style.zIndex = y + n
e.classList.toggle("selected", selected)
e.classList.toggle("oos", (view.oos & (1 << id)) !== 0)
e = ui.troops[id]
if (e.parentElement !== ui.pieces_element)
ui.pieces_element.appendChild(e)
e.style.left = (x - 7) + "px"
e.style.top = (y + 2 - 15 * n) + "px"
e.style.zIndex = y + n + 1
e.className = power_class[piece_power[id]] + " piece number n" + view.troops[id]
}
function layout_train(id, s) {
let e = ui.pieces[id]
let n = layout_train_offset(id, s)
let x, y
if (e.parentElement !== ui.pieces_element)
ui.pieces_element.appendChild(e)
if (s === ELIMINATED) {
x = ELIMINATED_TRAIN_X
y = ELIMINATED_TRAIN_Y
} else {
x = data.cities.x[s]
y = data.cities.y[s]
}
e.style.left = (x - 14 + n * ELIMINATED_TRAIN_DX) + "px"
e.style.top = (y - 21 - n * 0) + "px"
e.classList.toggle("selected", set_has(view.selected, id))
}
function layout_combat_marker() {
let x = (data.cities.x[view.attacker] + data.cities.x[view.defender]) >> 1
let y = (data.cities.y[view.attacker] + data.cities.y[view.defender]) >> 1
ui.combat.style.left = x - 20 + "px"
ui.combat.style.top = y - 20 + "px"
}
function layout_turn_marker() {
if (view.turn > 0) {
let t = view.turn - 1
let row = (t / 4) | 0
let col = (t % 4)
let x = 2267 + col * 45
let y = 79 + row * 52
ui.turn.style.left = x - 16 + "px"
ui.turn.style.top = y - 16 + "px"
ui.markers_element.appendChild(ui.turn)
}
}
function create_conquest(style) {
let e = document.createElement("div")
e.className = style
return e
}
const used_victory = [ 0, 0, 0, 0, 0, 0 ]
const used_elector = [ 0, 0, 0, 0, 0, 0 ]
const used_retro = [ 0, 0, 0, 0, 0, 0 ]
function layout_victory(s, pow) {
let e = ui.victory[pow][used_victory[pow]++]
let x = data.cities.x[s]
let y = data.cities.y[s]
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.markers_element.appendChild(e)
}
function layout_victory_box(pow, col, row) {
let e = ui.victory[pow][used_victory[pow]++]
let x = VICTORY_BOX_X[row]
let y = VICTORY_BOX_Y[col]
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.markers_element.appendChild(e)
}
function layout_political_marker(e, col, row) {
let x = 190 + (col-1) * 69
let y = 72 + row * 86
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.pol_tracks.appendChild(e)
}
function layout_retro(s, pow) {
let e = ui.retro[pow][used_retro[pow]++]
let x = data.cities.x[s]
let y = data.cities.y[s]
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.markers_element.appendChild(e)
}
function elector_majority() {
let elector_france = 0
let elector_pragmatic = 0
for (let i = 0; i < 8; i += 2) {
if (view.elector[i+1] === P_FRANCE)
elector_france ++
else
elector_pragmatic ++
}
if (elector_france >= 3)
return P_FRANCE
if (elector_pragmatic >= 3)
return P_PRAGMATIC
return -1
}
function count_victory_markers(pow) {
let n = view.vp[pow]
if (pow === P_PRUSSIA) {
n += view.vp[SET_ASIDE_PRUSSIA]
if (view.flags & F_SILESIA_ANNEXED) ++n
}
if (pow === P_FRANCE) {
n += view.vp[SET_ASIDE_FRANCE]
if (view.flags & F_ITALY_FRANCE) ++n
if (view.flags & F_EMPEROR_FRANCE) ++n
if (elector_majority() === P_FRANCE) ++n
}
if (pow === P_AUSTRIA) {
if (view.flags & F_ITALY_AUSTRIA) ++n
if (view.flags & F_EMPEROR_AUSTRIA) ++n
}
if (pow === P_PRAGMATIC) {
if (elector_majority() === P_PRAGMATIC) ++n
}
for (let i = 0; i < view.victory.length; i += 2)
if (view.victory[i+1] === pow)
++n
// count retroactive markers for display (except 4 special electoral colleges)
for (let i = 0; i < view.retro.length; i += 2)
if (!set_has(all_elector_fortresses, view.retro[i]))
if (actual_retro_power(view.retro[i], view.retro[i+1]) === pow)
++n
return n
}
function layout_victory_pool(pow, max, x, y) {
let n = count_victory_markers(pow)
let m = 0
if (pow === P_PRUSSIA)
m = view.vp[SET_ASIDE_PRUSSIA]
if (pow === P_FRANCE)
m = view.vp[SET_ASIDE_FRANCE]
for (let i = 0; i < max - n; ++i) {
let e = ui.victory[pow][used_victory[pow]++]
e.style.left = (x - 16 + (i%5) * 20) + "px"
e.style.top = (y - 16 + (i/5|0) * 20) + "px"
ui.markers_element.appendChild(e)
}
if (n > max) {
ui.missing[pow].textContent = "overfulfilled\n" + (n-max)
ui.missing[pow].className = "missing"
} else {
ui.missing[pow].textContent = ""
ui.missing[pow].className = "hide"
}
for (let i = 0; i < m; ++i) {
let e = ui.victory[pow][used_victory[pow]++]
if (pow === P_FRANCE) {
e.style.left = (x - 16 + i * 20) + "px"
e.style.top = (1635 - 16) + "px"
} else {
e.style.left = (x + 80 - 16 - i * 20) + "px"
e.style.top = (19 - 16) + "px"
}
ui.markers_element.appendChild(e)
}
}
function layout_elector(s, pow) {
let e = ui.elector[pow][used_elector[pow]++]
let x = data.cities.x[s]
let y = data.cities.y[s]
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.markers_element.appendChild(e)
}
function layout_electoral_college(ix, pow) {
let e = ui.elector[pow][used_elector[pow]++]
let x = ELECTORAL_COLLEGE_X + ELECTORAL_COLLEGE_DX * ix
let y = ELECTORAL_COLLEGE_Y
e.style.left = (x - 16) + "px"
e.style.top = (y - 16) + "px"
ui.markers_element.appendChild(e)
}
function update_favicon() {
let favicon = document.querySelector('link[rel="icon"]')
switch (params.role) {
case R_PLAYER_A:
case R_LOUIS_XV: favicon.href = "favicon/louis.png"; break
case R_FREDERICK: favicon.href = "favicon/fritz.png"; break
default:
case R_PLAYER_B:
case R_MARIA_THERESA: favicon.href = "favicon/maria.png"; break
}
}
function is_saxony_prussian() {
return view.saxony <= 2
}
function player_from_power(pow) {
if (is_two_player()) {
switch (pow) {
case P_FRANCE:
return R_PLAYER_A
case P_PRUSSIA:
return R_PLAYER_A
case P_PRAGMATIC:
if (is_intro())
return "NONE"
return R_PLAYER_B
case P_AUSTRIA:
return R_PLAYER_B
case P_SAXONY:
if (is_saxony_prussian())
return R_PLAYER_A
else
return R_PLAYER_B
case P_BAVARIA:
return R_PLAYER_A
}
}
switch (pow) {
case P_FRANCE:
return R_LOUIS_XV
case P_PRUSSIA:
return R_FREDERICK
case P_PRAGMATIC:
if (is_intro())
return "NONE"
return R_FREDERICK
case P_AUSTRIA:
return R_MARIA_THERESA
case P_SAXONY:
if (is_saxony_prussian())
return R_FREDERICK
else
return R_MARIA_THERESA
case P_BAVARIA:
return R_LOUIS_XV
}
return "NONE"
}
function update_player_power_list(role) {
if (roles[role]) {
roles[role].stat.replaceChildren()
for (let pow of all_powers)
if (player_from_power(pow) === role)
roles[role].stat.appendChild(power_badge[pow])
}
}
function update_player_active(name) {
if (roles[name])
roles[name].element.classList.toggle("active", player_from_power(view.power) === name)
}
function cmp_tc(a, b) {
let ax = (to_suit(a) << 7) + (to_value(a) << 3) + to_deck(a)
let bx = (to_suit(b) << 7) + (to_value(b) << 3) + to_deck(b)
return ax - bx
}
const colorize_S = '\u2660'
const colorize_C = '\u2663'
const colorize_H = '\u2665'
const colorize_D = '\u2666'
const colorize_R = '$1R'
const colorize_1 = '$1'
const colorize_2 = '$1'
const colorize_3 = '$1'
const colorize_4 = '$1'
function colorize(text) {
text = text.replace(/1\^(\d+[\u2660\u2663\u2665\u2666R])/g, colorize_1)
text = text.replace(/2\^(\d+[\u2660\u2663\u2665\u2666R])/g, colorize_2)
text = text.replace(/3\^(\d+[\u2660\u2663\u2665\u2666R])/g, colorize_3)
text = text.replace(/4\^(\d+[\u2660\u2663\u2665\u2666R])/g, colorize_4)
text = text.replace(/(\d+)R/g, colorize_R)
text = text.replaceAll("\u2660", colorize_S)
text = text.replaceAll("\u2663", colorize_C)
text = text.replaceAll("\u2665", colorize_H)
text = text.replaceAll("\u2666", colorize_D)
text = text.replace(/-( ?[\d(])/g, "\u2212$1")
return text
}
function on_prompt(text) {
return colorize(text)
}
let back = [ 0, 0, 0, 0 ]
function show_tc(c) {
if ((c & 15) === 0)
return ui.tc_back[c>>7][back[c>>7]++]
return ui.tc[c]
}
function update_political() {
ui.pol_tracks.replaceChildren()
layout_political_marker(ui.saxony, view.saxony, 0)
layout_political_marker(ui.russia, view.russia, 1)
layout_political_marker(ui.italy, view.italy, 2)
ui.pc_deck.replaceChildren()
for (let e of ui.political_back[view.pol_deck])
ui.pc_deck.appendChild(e)
if (view.flags & F_IMPERIAL_ELECTION)
ui.pc_deck.appendChild(ui.political[24])
for (let pc = 0; pc < 25; ++pc)
ui.political[pc].classList.toggle("selected", pc === view.pc)
for (let pow of all_major_powers) {
ui.pc_placed[pow].replaceChildren()
for (let tc of view.face_up[pow])
ui.pc_placed[pow].appendChild(show_tc(tc))
for (let tc of view.face_down[pow])
ui.pc_placed[pow].appendChild(show_tc(tc))
}
ui.pc_show_1.replaceChildren()
ui.pc_show_2.replaceChildren()
if (view.political) {
if (view.political.length > 0)
ui.pc_show_1.appendChild(ui.political[view.political[0]])
if (view.political.length > 1)
ui.pc_show_2.appendChild(ui.political[view.political[1]])
}
}
function actual_retro_power(s, attacker) {
// conquest retroactive markers have color of new conquering power
// re-conquest retroactive markers have color of old conquest marker
let previous = map_get(view.victory, s, -1)
// no previous marker
if (previous < 0)
return attacker
// attacker's marker in silesia which always has a marker
if (set_has(data.country.Silesia, s))
return attacker
// attacker's marker when reconquering minor power fortresses
if (set_has(data.country.Bavaria, s))
return attacker
if (set_has(data.country.Saxony, s))
return attacker
// previous marker to be removed
return previous
}
function on_update() {
ui.header.classList.toggle("france", view.power === P_FRANCE)
ui.header.classList.toggle("bavaria", view.power === P_BAVARIA)
ui.header.classList.toggle("prussia", view.power === P_PRUSSIA)
ui.header.classList.toggle("saxony", view.power === P_SAXONY)
ui.header.classList.toggle("pragmatic", view.power === P_PRAGMATIC)
ui.header.classList.toggle("austria", view.power === P_AUSTRIA)
if (is_two_player()) {
update_player_power_list(R_PLAYER_A)
update_player_power_list(R_PLAYER_B)
update_player_active(R_PLAYER_A)
update_player_active(R_PLAYER_B)
} else {
update_player_power_list(R_MARIA_THERESA)
update_player_power_list(R_FREDERICK)
update_player_power_list(R_LOUIS_XV)
update_player_active(R_MARIA_THERESA)
update_player_active(R_FREDERICK)
update_player_active(R_LOUIS_XV)
}
used_victory.fill(0)
used_elector.fill(0)
used_retro.fill(0)
back.fill(0)
sort_power_panel(true)
if (is_intro()) {
ui.power_panel[P_PRAGMATIC].style.display = "none"
ui.power_panel[6].style.display = "none"
}
for (let p = 0; p < 20; ++p)
layout_general(p, view.pos[p])
for (let p = 20; p < 30; ++p)
layout_train(p, view.pos[p])
for (let p = 30; p < 32; ++p)
layout_hussar(p, view.pos[p])
for (let pow of all_powers) {
let banner = power_name[pow]
if (view.contracts[pow] && view.contracts[pow].length > 0) {
map_for_each(view.contracts[pow], (other, n) => {
banner += " \u2014 "
for (let i = 0; i < n; ++i)
banner += `S`
})
}
if (pow === P_PRUSSIA && (view.flags & F_PRUSSIA_NEUTRAL))
banner += " \u2014 Neutral"
if (pow === P_SAXONY && is_saxony_neutral())
banner += " \u2014 Neutral"
if (pow === P_FRANCE && (view.flags & F_WAR_OF_JENKINS_EAR))
banner += " \u2014 receives 1 TC less"
if (is_intro()) {
if (pow === P_FRANCE) {
let n = count_victory_markers_in_country(P_FRANCE, all_core_austria_fortresses)
banner += ` \u2014 ${n} / 9 fortresses`
}
if (pow === P_PRUSSIA) {
let n = count_victory_markers_in_country(P_PRUSSIA, all_core_austria_and_silesia_fortresses)
banner += ` \u2014 ${n} / 12 fortresses`
}
}
banner += ` \u2014 ${view.pt[pow]} troops`
ui.power_header[pow].innerHTML = banner
ui.hand[pow].replaceChildren()
for (let c of view.hand1[pow].slice().sort(cmp_tc))
ui.hand[pow].appendChild(show_tc(c))
if (view.hand2[pow].length > 0) {
ui.hand[pow].appendChild(ui.tcbreak1)
for (let c of view.hand2[pow].slice().sort(cmp_tc))
ui.hand[pow].appendChild(show_tc(c))
}
if (view.hand3[pow].length > 0) {
ui.hand[pow].appendChild(ui.tcbreak2)
for (let c of view.hand3[pow].slice().sort(cmp_tc))
ui.hand[pow].appendChild(show_tc(c))
}
}
if (view.draw) {
if (view.hand1[view.power].length + view.hand2[view.power].length > 0)
ui.hand[view.power].appendChild(ui.tcbreak3)
for (let c of view.draw.slice().sort(cmp_tc))
ui.hand[view.power].appendChild(ui.tc[c])
}
for (let deck = 0; deck < 4; ++deck) {
ui.discard[deck].replaceChildren()
for (let i = 0; i < view.discard[deck]; ++i)
ui.discard[deck].appendChild(ui.tc_discard[deck][i])
}
ui.markers_element.replaceChildren()
for (let i = 0; i < view.retro.length; i += 2)
layout_retro(view.retro[i], actual_retro_power(view.retro[i], view.retro[i+1]))
for (let i = 0; i < view.victory.length; i += 2)
if (!map_has(view.retro, view.victory[i]))
layout_victory(view.victory[i], view.victory[i+1])
if (!is_intro()) {
update_political()
for (let i = 0; i < view.elector.length; i += 2)
if (!map_has(view.retro, view.elector[i]))
layout_elector(view.elector[i], view.elector[i+1])
layout_electoral_college(0, P_PRAGMATIC)
for (let i = 0; i < all_electoral_colleges.length; ++i) {
for (let pow of all_powers)
if (is_power_controlled_fortress(pow, all_electoral_colleges[i]))
layout_electoral_college(i+1, pow)
}
layout_victory_pool(P_FRANCE, 11, 83, 1560)
layout_victory_pool(P_AUSTRIA, 8, 2334, 1561)
layout_victory_pool(P_PRUSSIA, 13, 1927, 76)
layout_victory_pool(P_PRAGMATIC, 8, 797, 98)
// battle vp
if (view.vp[P_PRUSSIA] > 0) layout_victory_box(P_PRUSSIA, 0, 0)
if (view.vp[P_PRUSSIA] > 1) layout_victory_box(P_PRUSSIA, 0, 1)
if (view.vp[P_PRAGMATIC] > 0) layout_victory_box(P_PRAGMATIC, 1, 0)
if (view.vp[P_PRAGMATIC] > 1) layout_victory_box(P_PRAGMATIC, 1, 1)
if (view.vp[P_FRANCE] > 0) layout_victory_box(P_FRANCE, 2, 0)
if (view.vp[P_FRANCE] > 1) layout_victory_box(P_FRANCE, 2, 1)
if (view.vp[P_AUSTRIA] > 0) layout_victory_box(P_AUSTRIA, 3, 0)
if (view.vp[P_AUSTRIA] > 1) layout_victory_box(P_AUSTRIA, 3, 1)
if (view.flags & F_SILESIA_ANNEXED)
layout_victory_box(P_PRUSSIA, 0, 2)
let elector_france = 0
let elector_pragmatic = 0
for (let i = 0; i < 8; i += 2) {
if (view.elector[i+1] === P_FRANCE)
elector_france ++
else
elector_pragmatic ++
}
if (elector_france >= 3)
layout_victory_box(P_FRANCE, 1, 2)
if (elector_pragmatic >= 3)
layout_victory_box(P_PRAGMATIC, 1, 2)
if (view.flags & F_ITALY_FRANCE)
layout_victory_box(P_FRANCE, 2, 2)
if (view.flags & F_ITALY_AUSTRIA)
layout_victory_box(P_AUSTRIA, 2, 2)
if (view.flags & F_EMPEROR_FRANCE)
layout_victory_box(P_FRANCE, 3, 2)
if (view.flags & F_EMPEROR_AUSTRIA)
layout_victory_box(P_AUSTRIA, 3, 2)
}
layout_turn_marker()
if (view.attacker !== undefined && view.defender !== undefined) {
ui.markers_element.appendChild(ui.combat)
layout_combat_marker()
}
window.subsidy_menu.classList.toggle("hide", is_intro())
action_menu_item("propose_subsidy")
action_menu_item("cancel_subsidy")
action_menu_item("propose_deal")
action_menu_item("ping")
update_subsidy_list(view.contracts, window.subsidy_list, "Active Subsidies")
update_deal_list(view.deals, window.active_deal_list, "Active Deals")
if (view.proposed_deal) {
update_deal_list([ view.proposed_deal ], window.proposed_deal_list, "Proposed Deal")
setTimeout(() => scroll_into_view(window.proposed_deal_list), 333)
} else {
update_deal_list(null, window.proposed_deal_list, "Proposed Deal")
}
action_button_with_argument("suit", SPADES, colorize_S)
action_button_with_argument("suit", CLUBS, colorize_C)
action_button_with_argument("suit", HEARTS, colorize_H)
action_button_with_argument("suit", DIAMONDS, colorize_D)
action_button_with_argument("shift_russia", -2, RU_LEFT + RU_LEFT + RU_BOX)
action_button_with_argument("shift_russia", -1, RU_LEFT + RU_BOX)
action_button_with_argument("shift_russia", 1, RU_BOX + RU_RIGHT)
action_button_with_argument("shift_russia", 2, RU_BOX + RU_RIGHT + RU_RIGHT)
action_button_with_argument("shift_italy", -2, IT_LEFT + IT_LEFT + IT_BOX)
action_button_with_argument("shift_italy", -1, IT_LEFT + IT_BOX)
action_button_with_argument("shift_italy", 1, IT_BOX + IT_RIGHT)
action_button_with_argument("shift_italy", 2, IT_BOX + IT_RIGHT + IT_RIGHT)
action_button_with_argument("shift_saxony", -1, SX_LEFT + SX_BOX)
action_button_with_argument("shift_saxony", 1, SX_BOX + SX_RIGHT)
action_button_with_argument("shift_saxony", 2, SX_BOX + SX_RIGHT + SX_RIGHT)
action_button_with_argument("shift_saxony", 3, SX_BOX + SX_RIGHT + SX_RIGHT + SX_RIGHT)
action_button_with_argument("shift_saxony", 4, SX_BOX + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT)
action_button_with_argument("shift_saxony", 5, SX_BOX + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT)
action_button_with_argument("shift_saxony", 6, SX_BOX + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT + SX_RIGHT)
for (let v = 16; v >= 0; --v)
action_button_with_argument("value", v, v)
for (let p = 0; p < 20; ++p)
action_button_with_argument("supreme", p, power_image[piece_power[p]])
for (let pow of all_powers)
action_button_with_argument("power", pow, power_image[pow])
action_button("subsidy", "Subsidy")
action_button("cancel", "Cancel")
action_button("reduce", "Reduce")
action_button("peace", "Peace")
action_button("accept", "Accept")
action_button("reject", "Reject")
action_button("keep", "Kept")
confirm_action_button("break", "Broken",
"REWIND the game to force the player to keep their promise?\n\nP.S. Let them know why!")
action_button("re_enter", "Re-enter")
action_button("force_march", "Force march")
action_button("take", "Take")
action_button("give", "Give")
action_button("recruit", "Recruit")
action_button("transfer", "Transfer")
action_button("execute", "Execute")
action_button("discard", "Discard")
action_button("ignore", "Ignore")
action_button("draw", "Draw")
action_button("save", "Save")
action_button("stop", "Stop")
action_button("pass", "Pass")
action_button("next", "Next")
action_button("resume", "Resume")
confirm_action_button("dispute", "Dispute",
"Dispute order of movement on Flanders map and RESTART Austrian and Pragmatic Army movement phase?")
confirm_action_button("restart", "Restart",
"RESTART the Austrian and Pragmatic Army movement phase?")
action_button("end_political_card", "End political card")
action_button("end_place_hussars", "End place hussars")
action_button("end_setup", "End setup")
action_button("end_recruit", "End recruit")
action_button("end_movement", "End movement")
action_button("end_combat", "End combat")
action_button("end_supply", "End supply")
action_button("end_turn", "End turn")
confirm_action_button("confirm_end_movement", "End movement",
"You have UNMOVED pieces!\nAre you sure you want to END movement?")
action_button("undo", "Undo")
process_actions()
}
/* LOG */
function sub_political(_match, p1) {
let x = p1 | 0
let n = political_cards[x].title
return `${n}`
}
function sub_piece(_match, p1) {
let x = p1 | 0
let n = piece_log_name[x]
let p = power_class[piece_power[x]]
return `${n}`
}
function sub_space(_match, p1) {
let x = p1 | 0
if (x === ELIMINATED)
return "eliminated"
let n = data.cities.name[x]
return `${n}`
}
function sub_path(pieces_and_spaces) {
let ps = pieces_and_spaces[0].split(",")
let ss = pieces_and_spaces[1].split(",")
let x = ss[ss.length-1]
let ps_name = ps.map(p => piece_log_name[p]).join(" and ")
let ss_name = data.cities.name[x]
return `${ps_name} to ${ss_name}.`
}
function on_log(text) {
let p = document.createElement("div")
if (text.match(/^>>/)) {
text = text.substring(2)
p.className = "ii"
}
if (text.match(/^>/)) {
text = text.substring(1)
p.className = "i"
}
if (text.match(/^!/)) {
text = "Combat"
p.className = "combat"
}
text = text.replace(/&/g, "&")
text = text.replace(//g, ">")
text = text.replaceAll(" 1 troops", " 1 troop")
text = colorize(text)
text = text.replace(/S(\d+)/g, sub_space)
text = text.replace(/P(\d+)/g, sub_piece)
text = text.replace(/C(\d+)/g, sub_political)
text = text.replace(/-( ?[\d(])/g, "\u2212$1")
if (text.startsWith("@")) {
p.className = "move_tip"
text = sub_path(text.substring(1).split(";"))
}
else if (text.startsWith("#")) {
p.className = "h turn"
text = text.substring(2)
}
else if (text.startsWith("=")) {
p.className = "h " + power_class[text[1]]
if (text.length === 2)
text = power_name[text[1]]
else
text = text.substring(2)
}
else if (text.startsWith("%")) {
p.className = "q"
text = political_cards[text.substring(1)].flavor
}
else if (text.startsWith("{")) {
p.className = "deal"
text = text.substring(1)
}
else if (text.startsWith("}")) {
p.className = "deal rejected"
text = text.substring(1)
}
p.innerHTML = text
return p
}
/* DEAL DIALOGS */
function propose_subsidy() { send_action("propose_subsidy") }
function cancel_subsidy() { send_action("cancel_subsidy") }
function html_escape(str) {
return str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")
}
function phase_list(phase) {
let list = []
if (phase & V_POLITICS) list.push("politics")
if (phase & V_HUSSARS) list.push("hussars")
if (phase & V_MOVEMENT) list.push("movement")
if (phase & V_RETREAT) list.push("retreat")
return list.join(" ")
}
function update_subsidy_list(subs, elt, title) {
let show = false
let str = "
" + title
for (let from of all_major_powers) {
map_for_each(subs[from], (to, n) => {
show = true
str += " |
---|
" + power_image[from]
str += " | " + power_image[to]
if (n > 1)
str += " | " + n + " turns"
else
str += " | 1 turn"
})
}
if (show) {
elt.innerHTML = str
elt.style.display = "block"
} else {
elt.style.display = "none"
}
}
function update_deal_item(deal, ix) {
let [ a_power, b_power, a_turn, b_turn, a_phase, b_phase, a_promise, b_promise ] = deal
let str = " |
"
str += "" + power_image[a_power]
str += " | " + html_escape(a_promise)
str += " | " + phase_list(a_phase) + ""
str += " | " + turn_name[a_turn]
if (is_action("cancel_deal"))
str += ` | \u274c`
else
str += " | "
str += " |
"
str += "" + power_image[b_power]
str += " | " + html_escape(b_promise)
str += " | " + phase_list(b_phase) + ""
str += " | " + turn_name[b_turn]
return str
}
function cancel_deal(ix) {
let deal = view.deals[ix]
if (window.confirm(
`ANNUL deal between between ${power_name[deal[DI_A_POWER]]} and ${power_name[deal[DI_B_POWER]]}?`
))
send_message("action", [ "cancel_deal", ix, game_cookie ])
}
function update_deal_list(deals, elt, title) {
if (deals && deals.length > 0) {
//let str = " | " + title + " | phase | until"
let str = "" + title
str += deals.map(update_deal_item).join(" |
---|
")
str += " | "
elt.innerHTML = str
elt.style.display = "block"
} else {
elt.style.display = "none"
}
}
function update_deal_options() {
let a_power = window.propose_deal_form.elements.a_power.value | 0
let b_power = window.propose_deal_form.elements.b_power.value | 0
window.a_hussars_label.classList.toggle("hide", a_power !== P_AUSTRIA)
window.b_hussars_label.classList.toggle("hide", b_power !== P_AUSTRIA)
window.a_politics_label.classList.toggle("hide", all_minor_powers.includes(a_power))
window.b_politics_label.classList.toggle("hide", all_minor_powers.includes(b_power))
}
function propose_deal() {
if (!is_action("propose_deal"))
return
let form = window.propose_deal_form
window.a_phase_politics.checked = false
window.b_phase_politics.checked = false
window.a_phase_hussars.checked = false
window.b_phase_hussars.checked = false
window.a_phase_movement.checked = false
window.b_phase_movement.checked = false
window.a_phase_retreat.checked = false
window.b_phase_retreat.checked = false
form.a_power.value = view.power
switch (view.power) {
case P_FRANCE:
form.b_power.value = P_PRUSSIA
break
case P_PRUSSIA:
form.b_power.value = P_FRANCE
break
case P_PRAGMATIC:
form.b_power.value = P_AUSTRIA
break
case P_AUSTRIA:
form.b_power.value = P_PRAGMATIC
break
}
form.a_turn.value = view.turn
form.b_turn.value = view.turn
for (let opt of form.a_turn.options)
opt.disabled = (opt.value < view.turn)
for (let opt of form.b_turn.options)
opt.disabled = (opt.value < view.turn)
update_deal_options()
window.propose_deal_dlog.showModal()
}
function propose_deal_submit(evt) {
evt.preventDefault()
let form = new FormData(window.propose_deal_form)
let data = {
a_power: Number(form.get("a_power")),
b_power: Number(form.get("b_power")),
a_turn: Number(form.get("a_turn")),
b_turn: Number(form.get("b_turn")),
a_phase: form.getAll("a_phase").reduce((acc,x)=>acc|x, 0),
b_phase: form.getAll("b_phase").reduce((acc,x)=>acc|x, 0),
a_promise: form.get("a_promise"),
b_promise: form.get("b_promise"),
}
if (!data.a_promise || !data.b_promise) {
alert("Each side must promise something!")
return
}
if (player_from_power(data.a_power) === player_from_power(data.b_power)) {
alert("Cannot create deals between powers controlled by the same player.")
return
}
let deal = [ data.a_power, data.b_power, data.a_turn, data.b_turn, data.a_phase, data.b_phase, data.a_promise, data.b_promise ]
if (view.actions.propose_deal)
send_message("action", [ "propose_deal", deal, game_cookie ])
window.propose_deal_dlog.close()
}
function propose_deal_cancel(evt) {
evt.preventDefault()
window.propose_deal_dlog.close()
}
/* COMMON LIBRARY */
function array_insert(array, index, item) {
for (let i = array.length; i > index; --i)
array[i] = array[i - 1]
array[index] = item
}
function set_has(set, item) {
if (set === item) return true
if (set === undefined) return false
if (set === null) return false
let a = 0
let b = set.length - 1
while (a <= b) {
let m = (a + b) >> 1
let x = set[m]
if (item < x)
b = m - 1
else if (item > x)
a = m + 1
else
return true
}
return false
}
function set_add(set, item) {
let a = 0
let b = set.length - 1
while (a <= b) {
let m = (a + b) >> 1
let x = set[m]
if (item < x)
b = m - 1
else if (item > x)
a = m + 1
else
return
}
array_insert(set, a, item)
}
function set_add_all(set, other) {
for (let item of other)
set_add(set, item)
}
function set_union(one, two) {
let set = []
for (let item of one)
set_add(set, item)
for (let item of two)
set_add(set, item)
return set
}
function set_intersect(one, two) {
let set = []
for (let item of one)
if (set_has(two, item))
set_add(set, item)
return set
}
function map_has(map, key) {
let a = 0
let b = (map.length >> 1) - 1
while (a <= b) {
let m = (a + b) >> 1
let x = map[m<<1]
if (key < x)
b = m - 1
else if (key > x)
a = m + 1
else
return true
}
return false
}
function map_get(map, key, missing) {
let a = 0
let b = (map.length >> 1) - 1
while (a <= b) {
let m = (a + b) >> 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 missing
}
function map_for_each(map, f) {
for (let i = 0; i < map.length; i += 2)
f(map[i], map[i+1])
}
|
---|
|