summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.html200
-rw-r--r--play.js188
-rw-r--r--rules.js157
3 files changed, 438 insertions, 107 deletions
diff --git a/play.html b/play.html
index 11635b6..2f7c763 100644
--- a/play.html
+++ b/play.html
@@ -42,7 +42,7 @@ header.your_turn { background-color: orange; }
font-variant-numeric: tabular-nums;
}
-/* DIALOGS */
+/* ARTS OF WAR */
#arts_of_war {
background-color: white;
@@ -75,6 +75,63 @@ header.your_turn { background-color: orange; }
gap: 12px;
}
+/* PLAN */
+
+#plan {
+ background-color: white;
+ border: 1px solid black;
+ position: fixed;
+ top: 84px;
+ left: 36px;
+ box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
+ z-index: 51;
+ user-select: none;
+}
+
+#plan_header {
+ font-weight: bold;
+ cursor: move;
+ border-bottom: 1px solid black;
+ background-color: lightsteelblue;
+ padding: 3px 1em;
+}
+
+#plan_list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ padding: 12px;
+ height: 260px;
+}
+
+#plan_actions {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 12px;
+ gap: 12px;
+ height: 130px;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+#plan_list {
+ background-color: slategray;
+}
+
+#plan_actions {
+ border-top: 2px dotted white;
+ background-color: slategray;
+}
+
+body.Russians #plan_actions .teutonic { display: none }
+body.Teutons #plan_actions .russian { display: none }
+
+#plan_actions .card {
+ width: 93px;
+ height: 130px;
+ background-size: 93px 130px;
+}
+
/* MATS */
.court {
@@ -113,7 +170,7 @@ header.your_turn { background-color: orange; }
.mat .c1, .mat .c2 {
position: absolute;
width: 186px;
- height: 261px;
+ height: 260px;
background-color: green;
}
@@ -369,9 +426,9 @@ body.shift .mat .card:hover {
}
.card {
- background-size: 186px 261px;
+ background-size: 186px 260px;
width: 186px;
- height: 261px;
+ height: 260px;
border-radius: 8px;
transition: 100ms;
}
@@ -772,7 +829,6 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
.card.russian.aow_back{background-image:url(cards.1x/aow_russian_back.jpg)}
.card.teutonic.aow_back{background-image:url(cards.1x/aow_teutonic_back.jpg)}
-
.card.aow_0{background-image:url(cards.1x/aow_teutonic_01.jpg)}
.card.aow_1{background-image:url(cards.1x/aow_teutonic_02.jpg)}
.card.aow_2{background-image:url(cards.1x/aow_teutonic_03.jpg)}
@@ -794,7 +850,6 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
.card.aow_18{background-image:url(cards.1x/aow_teutonic_none.jpg)}
.card.aow_19{background-image:url(cards.1x/aow_teutonic_none.jpg)}
.card.aow_20{background-image:url(cards.1x/aow_teutonic_none.jpg)}
-
.card.aow_21{background-image:url(cards.1x/aow_russian_01.jpg)}
.card.aow_22{background-image:url(cards.1x/aow_russian_02.jpg)}
.card.aow_23{background-image:url(cards.1x/aow_russian_03.jpg)}
@@ -819,17 +874,14 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
.card.teutonic.cc_back{background-image:url(cards.1x/cc_teutonic_back.jpg)}
.card.russian.cc_back{background-image:url(cards.1x/cc_russian_back.jpg)}
-
.card.teutonic.cc_pass{background-image:url(cards.1x/cc_teutonic_pass.jpg)}
.card.russian.cc_pass{background-image:url(cards.1x/cc_russian_pass.jpg)}
-
.card.cc_lord_0{background-image:url(cards.1x/cc_teutonic_andreas.jpg)}
.card.cc_lord_1{background-image:url(cards.1x/cc_teutonic_heinrich.jpg)}
.card.cc_lord_2{background-image:url(cards.1x/cc_teutonic_hermann.jpg)}
.card.cc_lord_3{background-image:url(cards.1x/cc_teutonic_knud_and_abel.jpg)}
.card.cc_lord_4{background-image:url(cards.1x/cc_teutonic_rudolf.jpg)}
.card.cc_lord_5{background-image:url(cards.1x/cc_teutonic_yaroslav.jpg)}
-
.card.cc_lord_6{background-image:url(cards.1x/cc_russian_aleksandr.jpg)}
.card.cc_lord_7{background-image:url(cards.1x/cc_russian_andrey.jpg)}
.card.cc_lord_8{background-image:url(cards.1x/cc_russian_domash.jpg)}
@@ -838,67 +890,69 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
.card.cc_lord_11{background-image:url(cards.1x/cc_russian_vladislav.jpg)}
@media (min-resolution: 97dpi) {
-.card.russian.aow_back{background-image:url(cards.1x/aow_russian_back.jpg)}
-.card.teutonic.aow_back{background-image:url(cards.1x/aow_teutonic_back.jpg)}
-.card.aow_0{background-image:url(cards.1x/aow_russian_01.jpg)}
-.card.aow_1{background-image:url(cards.1x/aow_russian_02.jpg)}
-.card.aow_2{background-image:url(cards.1x/aow_russian_03.jpg)}
-.card.aow_3{background-image:url(cards.1x/aow_russian_04.jpg)}
-.card.aow_4{background-image:url(cards.1x/aow_russian_05.jpg)}
-.card.aow_5{background-image:url(cards.1x/aow_russian_06.jpg)}
-.card.aow_6{background-image:url(cards.1x/aow_russian_07.jpg)}
-.card.aow_7{background-image:url(cards.1x/aow_russian_08.jpg)}
-.card.aow_8{background-image:url(cards.1x/aow_russian_09.jpg)}
-.card.aow_9{background-image:url(cards.1x/aow_russian_10.jpg)}
-.card.aow_10{background-image:url(cards.1x/aow_russian_11.jpg)}
-.card.aow_11{background-image:url(cards.1x/aow_russian_12.jpg)}
-.card.aow_12{background-image:url(cards.1x/aow_russian_13.jpg)}
-.card.aow_13{background-image:url(cards.1x/aow_russian_14.jpg)}
-.card.aow_14{background-image:url(cards.1x/aow_russian_15.jpg)}
-.card.aow_15{background-image:url(cards.1x/aow_russian_16.jpg)}
-.card.aow_16{background-image:url(cards.1x/aow_russian_17.jpg)}
-.card.aow_17{background-image:url(cards.1x/aow_russian_18.jpg)}
-.card.aow_18{background-image:url(cards.1x/aow_russian_none.jpg)}
-.card.aow_19{background-image:url(cards.1x/aow_russian_none.jpg)}
-.card.aow_20{background-image:url(cards.1x/aow_russian_none.jpg)}
-.card.aow_21{background-image:url(cards.1x/aow_teutonic_01.jpg)}
-.card.aow_22{background-image:url(cards.1x/aow_teutonic_02.jpg)}
-.card.aow_23{background-image:url(cards.1x/aow_teutonic_03.jpg)}
-.card.aow_24{background-image:url(cards.1x/aow_teutonic_04.jpg)}
-.card.aow_25{background-image:url(cards.1x/aow_teutonic_05.jpg)}
-.card.aow_26{background-image:url(cards.1x/aow_teutonic_06.jpg)}
-.card.aow_27{background-image:url(cards.1x/aow_teutonic_07.jpg)}
-.card.aow_28{background-image:url(cards.1x/aow_teutonic_08.jpg)}
-.card.aow_29{background-image:url(cards.1x/aow_teutonic_09.jpg)}
-.card.aow_30{background-image:url(cards.1x/aow_teutonic_10.jpg)}
-.card.aow_31{background-image:url(cards.1x/aow_teutonic_11.jpg)}
-.card.aow_32{background-image:url(cards.1x/aow_teutonic_12.jpg)}
-.card.aow_33{background-image:url(cards.1x/aow_teutonic_13.jpg)}
-.card.aow_34{background-image:url(cards.1x/aow_teutonic_14.jpg)}
-.card.aow_35{background-image:url(cards.1x/aow_teutonic_15.jpg)}
-.card.aow_36{background-image:url(cards.1x/aow_teutonic_16.jpg)}
-.card.aow_37{background-image:url(cards.1x/aow_teutonic_17.jpg)}
-.card.aow_38{background-image:url(cards.1x/aow_teutonic_18.jpg)}
-.card.aow_39{background-image:url(cards.1x/aow_teutonic_none.jpg)}
-.card.aow_40{background-image:url(cards.1x/aow_teutonic_none.jpg)}
-.card.aow_41{background-image:url(cards.1x/aow_teutonic_none.jpg)}
-
-.card.cc_russian_back{background-image:url(cards.2x/cc_russian_back.jpg)}
-.card.cc_russian_aleksandr{background-image:url(cards.2x/cc_russian_aleksandr.jpg)}
-.card.cc_russian_andrey{background-image:url(cards.2x/cc_russian_andrey.jpg)}
-.card.cc_russian_domash{background-image:url(cards.2x/cc_russian_domash.jpg)}
-.card.cc_russian_gavrilo{background-image:url(cards.2x/cc_russian_gavrilo.jpg)}
-.card.cc_russian_karelians{background-image:url(cards.2x/cc_russian_karelians.jpg)}
-.card.cc_russian_vladislav{background-image:url(cards.2x/cc_russian_vladislav.jpg)}
-.card.cc_russian_pass{background-image:url(cards.2x/cc_russian_pass.jpg)}
-.card.cc_teutonic_back{background-image:url(cards.2x/cc_teutonic_back.jpg)}
-.card.cc_teutonic_andreas{background-image:url(cards.2x/cc_teutonic_andreas.jpg)}
-.card.cc_teutonic_heinrich{background-image:url(cards.2x/cc_teutonic_heinrich.jpg)}
-.card.cc_teutonic_hermann{background-image:url(cards.2x/cc_teutonic_hermann.jpg)}
-.card.cc_teutonic_knud_and_abel{background-image:url(cards.2x/cc_teutonic_knud_and_abel.jpg)}
-.card.cc_teutonic_rudolf{background-image:url(cards.2x/cc_teutonic_rudolf.jpg)}
-.card.cc_teutonic_yaroslav{background-image:url(cards.2x/cc_teutonic_yaroslav.jpg)}
-.card.cc_teutonic_pass{background-image:url(cards.2x/cc_teutonic_pass.jpg)}
+
+.card.russian.aow_back{background-image:url(cards.2x/aow_russian_back.jpg)}
+.card.teutonic.aow_back{background-image:url(cards.2x/aow_teutonic_back.jpg)}
+.card.aow_0{background-image:url(cards.2x/aow_russian_01.jpg)}
+.card.aow_1{background-image:url(cards.2x/aow_russian_02.jpg)}
+.card.aow_2{background-image:url(cards.2x/aow_russian_03.jpg)}
+.card.aow_3{background-image:url(cards.2x/aow_russian_04.jpg)}
+.card.aow_4{background-image:url(cards.2x/aow_russian_05.jpg)}
+.card.aow_5{background-image:url(cards.2x/aow_russian_06.jpg)}
+.card.aow_6{background-image:url(cards.2x/aow_russian_07.jpg)}
+.card.aow_7{background-image:url(cards.2x/aow_russian_08.jpg)}
+.card.aow_8{background-image:url(cards.2x/aow_russian_09.jpg)}
+.card.aow_9{background-image:url(cards.2x/aow_russian_10.jpg)}
+.card.aow_10{background-image:url(cards.2x/aow_russian_11.jpg)}
+.card.aow_11{background-image:url(cards.2x/aow_russian_12.jpg)}
+.card.aow_12{background-image:url(cards.2x/aow_russian_13.jpg)}
+.card.aow_13{background-image:url(cards.2x/aow_russian_14.jpg)}
+.card.aow_14{background-image:url(cards.2x/aow_russian_15.jpg)}
+.card.aow_15{background-image:url(cards.2x/aow_russian_16.jpg)}
+.card.aow_16{background-image:url(cards.2x/aow_russian_17.jpg)}
+.card.aow_17{background-image:url(cards.2x/aow_russian_18.jpg)}
+.card.aow_18{background-image:url(cards.2x/aow_russian_none.jpg)}
+.card.aow_19{background-image:url(cards.2x/aow_russian_none.jpg)}
+.card.aow_20{background-image:url(cards.2x/aow_russian_none.jpg)}
+.card.aow_21{background-image:url(cards.2x/aow_teutonic_01.jpg)}
+.card.aow_22{background-image:url(cards.2x/aow_teutonic_02.jpg)}
+.card.aow_23{background-image:url(cards.2x/aow_teutonic_03.jpg)}
+.card.aow_24{background-image:url(cards.2x/aow_teutonic_04.jpg)}
+.card.aow_25{background-image:url(cards.2x/aow_teutonic_05.jpg)}
+.card.aow_26{background-image:url(cards.2x/aow_teutonic_06.jpg)}
+.card.aow_27{background-image:url(cards.2x/aow_teutonic_07.jpg)}
+.card.aow_28{background-image:url(cards.2x/aow_teutonic_08.jpg)}
+.card.aow_29{background-image:url(cards.2x/aow_teutonic_09.jpg)}
+.card.aow_30{background-image:url(cards.2x/aow_teutonic_10.jpg)}
+.card.aow_31{background-image:url(cards.2x/aow_teutonic_11.jpg)}
+.card.aow_32{background-image:url(cards.2x/aow_teutonic_12.jpg)}
+.card.aow_33{background-image:url(cards.2x/aow_teutonic_13.jpg)}
+.card.aow_34{background-image:url(cards.2x/aow_teutonic_14.jpg)}
+.card.aow_35{background-image:url(cards.2x/aow_teutonic_15.jpg)}
+.card.aow_36{background-image:url(cards.2x/aow_teutonic_16.jpg)}
+.card.aow_37{background-image:url(cards.2x/aow_teutonic_17.jpg)}
+.card.aow_38{background-image:url(cards.2x/aow_teutonic_18.jpg)}
+.card.aow_39{background-image:url(cards.2x/aow_teutonic_none.jpg)}
+.card.aow_40{background-image:url(cards.2x/aow_teutonic_none.jpg)}
+.card.aow_41{background-image:url(cards.2x/aow_teutonic_none.jpg)}
+
+.card.teutonic.cc_back{background-image:url(cards.2x/cc_teutonic_back.jpg)}
+.card.russian.cc_back{background-image:url(cards.2x/cc_russian_back.jpg)}
+.card.teutonic.cc_pass{background-image:url(cards.2x/cc_teutonic_pass.jpg)}
+.card.russian.cc_pass{background-image:url(cards.2x/cc_russian_pass.jpg)}
+.card.cc_lord_0{background-image:url(cards.2x/cc_teutonic_andreas.jpg)}
+.card.cc_lord_1{background-image:url(cards.2x/cc_teutonic_heinrich.jpg)}
+.card.cc_lord_2{background-image:url(cards.2x/cc_teutonic_hermann.jpg)}
+.card.cc_lord_3{background-image:url(cards.2x/cc_teutonic_knud_and_abel.jpg)}
+.card.cc_lord_4{background-image:url(cards.2x/cc_teutonic_rudolf.jpg)}
+.card.cc_lord_5{background-image:url(cards.2x/cc_teutonic_yaroslav.jpg)}
+.card.cc_lord_6{background-image:url(cards.2x/cc_russian_aleksandr.jpg)}
+.card.cc_lord_7{background-image:url(cards.2x/cc_russian_andrey.jpg)}
+.card.cc_lord_8{background-image:url(cards.2x/cc_russian_domash.jpg)}
+.card.cc_lord_9{background-image:url(cards.2x/cc_russian_gavrilo.jpg)}
+.card.cc_lord_10{background-image:url(cards.2x/cc_russian_karelians.jpg)}
+.card.cc_lord_11{background-image:url(cards.2x/cc_russian_vladislav.jpg)}
+
}
</style>
@@ -910,6 +964,12 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
<div id="arts_of_war_list"></div>
</div>
+<div id="plan" class="hide">
+<div id="plan_header">Plan</div>
+<div id="plan_list"></div>
+<div id="plan_actions"></div>
+</div>
+
<header>
<div id="toolbar">
<div class="menu">
diff --git a/play.js b/play.js
index b372537..44efe00 100644
--- a/play.js
+++ b/play.js
@@ -24,6 +24,31 @@ const SLED = 4
const BOAT = 5
const SHIP = 6
+const SUMMER = 0
+const EARLY_WINTER = 1
+const LATE_WINTER = 2
+const RASPUTITSA = 3
+
+const SEASONS = [
+ null,
+ SUMMER, SUMMER, EARLY_WINTER, EARLY_WINTER, LATE_WINTER, LATE_WINTER, RASPUTITSA, RASPUTITSA,
+ SUMMER, SUMMER, EARLY_WINTER, EARLY_WINTER, LATE_WINTER, LATE_WINTER, RASPUTITSA, RASPUTITSA,
+ null
+]
+
+function current_season() {
+ return SEASONS[view.turn >> 1]
+}
+
+function max_plan_length() {
+ switch (current_season()) {
+ case SUMMER: return 6
+ case EARLY_WINTER: return 4
+ case LATE_WINTER: return 4
+ case RASPUTITSA: return 5
+ }
+}
+
function pack1_get(word, n) {
return (word >>> n) & 1
}
@@ -37,6 +62,10 @@ function is_lord_action(lord) {
return !!(view.actions && view.actions.lord && view.actions.lord.includes(lord))
}
+function is_plan_action(lord) {
+ return !!(view.actions && view.actions.plan && view.actions.plan.includes(lord))
+}
+
function is_service_action(lord) {
return !!(view.actions && view.actions.service && view.actions.service.includes(lord))
}
@@ -61,6 +90,34 @@ const last_p1_locale = 23
const first_p2_locale = 24
const last_p2_locale = 52
+let used_cache = {}
+let unused_cache = {}
+
+function get_cached_element(className) {
+ if (!(className in unused_cache)) {
+ unused_cache[className] = []
+ used_cache[className] = []
+ }
+ if (unused_cache[className].length > 0) {
+ let elt = unused_cache[className].pop()
+ used_cache[className].push(elt)
+ return elt
+ }
+ let elt = document.createElement("div")
+ elt.className = className
+ used_cache[className].push(elt)
+ return elt
+}
+
+function restart_cache() {
+ for (let k in used_cache) {
+ let u = used_cache[k]
+ let uu = unused_cache[k]
+ while (u.length > 0)
+ uu.push(u.pop())
+ }
+}
+
function is_p1_locale(loc) {
return loc >= first_p1_locale && loc <= last_p1_locale
}
@@ -197,6 +254,11 @@ const ui = {
arts_of_war: [],
boxes: {},
veche: document.getElementById("veche"),
+ plan_dialog: document.getElementById("plan"),
+ plan_list: document.getElementById("plan_list"),
+ plan_actions: document.getElementById("plan_actions"),
+ plan_list_cards: [],
+ plan_action_cards: [],
arts_of_war_dialog: document.getElementById("arts_of_war"),
arts_of_war_list: document.getElementById("arts_of_war_list"),
p1_global: document.getElementById("p1_global"),
@@ -240,56 +302,63 @@ function toggle_pieces() {
function on_click_locale(evt) {
if (evt.button === 0) {
- let id = evt.target.my_locale
+ let id = evt.target.my_id
send_action('locale', id)
}
}
function on_focus_locale(evt) {
- let id = evt.target.my_locale
+ let id = evt.target.my_id
document.getElementById("status").textContent = `(${id}) ${data.locales[id].name} - ${data.locales[id].type}`
}
function on_click_cylinder(evt) {
if (evt.button === 0) {
- let id = evt.target.my_lord
+ let id = evt.target.my_id
send_action('lord', id)
}
}
function on_click_arts_of_war(evt) {
if (evt.button === 0) {
- let id = evt.target.my_arts_of_war
+ let id = evt.target.my_id
send_action('arts_of_war', id)
}
}
+function on_click_plan(evt) {
+ if (evt.button === 0) {
+ let id = evt.target.my_id
+ send_action('plan', id)
+ }
+}
+
function on_focus_cylinder(evt) {
- let id = evt.target.my_lord
+ let id = evt.target.my_id
document.getElementById("status").textContent = `(${id}) ${data.lords[id].full_name} [${data.lords[id].command}] - ${data.lords[id].title}`
}
function on_click_lord_service_marker(evt) {
if (evt.button === 0) {
- let id = evt.target.my_lord
+ let id = evt.target.my_id
send_action('lord_service', id)
}
}
function on_focus_lord_service_marker(evt) {
- let id = evt.target.my_lord
+ let id = evt.target.my_id
document.getElementById("status").textContent = `(${id}) ${data.lords[id].full_name} - ${data.lords[id].title}`
}
function on_click_vassal_service_marker(evt) {
if (evt.button === 0) {
- let id = evt.target.my_vassal
+ let id = evt.target.my_id
send_action('vassal', id)
}
}
function on_focus_vassal_service_marker(evt) {
- let id = evt.target.my_vassal
+ let id = evt.target.my_id
let vassal = data.vassals[id]
let lord = data.lords[vassal.lord]
document.getElementById("status").textContent = `(${id}) ${lord.name} / ${vassal.name}`
@@ -521,6 +590,57 @@ function update_locale(loc) {
ui.locale_extra[loc].classList.toggle("action", is_locale_action(loc))
}
+function update_plan() {
+ if (view.plan) {
+ ui.plan_dialog.classList.remove("hide")
+ for (let i = 0; i < 6; ++i) {
+ if (i < view.plan.length) {
+ let lord = view.plan[i]
+ if (lord < 0) {
+ if (player === "Teutons")
+ ui.plan_list_cards[i].className = "card teutonic cc_pass"
+ else
+ ui.plan_list_cards[i].className = "card russian cc_pass"
+ } else {
+ if (lord < 6)
+ ui.plan_list_cards[i].className = "card teutonic cc_lord_" + lord
+ else
+ ui.plan_list_cards[i].className = "card russian cc_lord_" + lord
+ }
+ } else if (i < max_plan_length()) {
+ if (player === "Teutons")
+ ui.plan_list_cards[i].className = "card teutonic cc_back"
+ else
+ ui.plan_list_cards[i].className = "card russian cc_back"
+ } else {
+ ui.plan_list_cards[i].className = "hide"
+ }
+ }
+ for (let lord = 0; lord < 12; ++lord) {
+ if (is_plan_action(lord)) {
+ ui.plan_action_cards[lord].classList.add("action")
+ ui.plan_action_cards[lord].classList.remove("disabled")
+ } else {
+ ui.plan_action_cards[lord].classList.remove("action")
+ ui.plan_action_cards[lord].classList.add("disabled")
+ }
+ }
+ if (is_plan_action(-1)) {
+ ui.plan_action_pass_p1.classList.add("action")
+ ui.plan_action_pass_p1.classList.remove("disabled")
+ ui.plan_action_pass_p2.classList.add("action")
+ ui.plan_action_pass_p2.classList.remove("disabled")
+ } else {
+ ui.plan_action_pass_p1.classList.remove("action")
+ ui.plan_action_pass_p1.classList.add("disabled")
+ ui.plan_action_pass_p2.classList.remove("action")
+ ui.plan_action_pass_p2.classList.add("disabled")
+ }
+ } else {
+ ui.plan_dialog.classList.add("hide")
+ }
+}
+
function update_arts_of_war() {
if (view.actions && view.actions.arts_of_war) {
ui.arts_of_war_dialog.classList.remove("hide")
@@ -570,6 +690,8 @@ function update_arts_of_war() {
}
function on_update() {
+ restart_cache()
+
locale_layout.fill(0)
calendar_layout.fill(0)
@@ -633,6 +755,7 @@ function on_update() {
ui.vp2.className = `marker circle victory russian v${vp2>>1}`
}
+ update_plan()
update_arts_of_war()
action_button("ship", "Ship")
@@ -644,6 +767,7 @@ function on_update() {
action_button("done", "Done")
action_button("unfed", "Unfed")
+ action_button("end_plan", "End plan")
action_button("end_feed", "End feed")
action_button("end_pay", "End pay")
action_button("end_disband", "End disband")
@@ -681,10 +805,41 @@ function build_lord_mat(lord, ix, side, name) {
function build_arts_of_war(side, c) {
let card = ui.arts_of_war[c] = document.createElement("div")
card.className = `card ${side} aow_${c}`
- card.my_arts_of_war = c
+ card.my_id = c
card.addEventListener("mousedown", on_click_arts_of_war)
}
+function build_plan() {
+ let elt
+ for (let i = 0; i < 6; ++i) {
+ elt = document.createElement("div")
+ elt.className = "hide"
+ ui.plan_list_cards.push(elt)
+ ui.plan_list.appendChild(elt)
+ }
+ for (let lord = 0; lord < 12; ++lord) {
+ let side = lord < 6 ? "teutonic" : "russian"
+ elt = document.createElement("div")
+ elt.className = `card ${side} cc_lord_${lord}`
+ elt.my_id = lord
+ elt.addEventListener("mousedown", on_click_plan)
+ ui.plan_action_cards.push(elt)
+ ui.plan_actions.appendChild(elt)
+ }
+
+ ui.plan_action_pass_p1 = elt = document.createElement("div")
+ elt.className = `card teutonic cc_pass`
+ elt.my_id = -1
+ elt.addEventListener("mousedown", on_click_plan)
+ ui.plan_actions.appendChild(elt)
+
+ ui.plan_action_pass_p2 = elt = document.createElement("div")
+ elt.className = `card russian cc_pass`
+ elt.my_id = -1
+ elt.addEventListener("mousedown", on_click_plan)
+ ui.plan_actions.appendChild(elt)
+}
+
function build_map() {
data.locales.forEach((locale, ix) => {
let e = ui.locale[ix] = document.createElement("div")
@@ -715,7 +870,7 @@ function build_map() {
e.style.top = y + "px"
e.style.width = w + "px"
e.style.height = h + "px"
- e.my_locale = ix
+ e.my_id = ix
e.addEventListener("mousedown", on_click_locale)
e.addEventListener("mouseenter", on_focus_locale)
e.addEventListener("mouseleave", on_blur)
@@ -731,7 +886,7 @@ function build_map() {
e.style.left = (cx - ew/2) + "px"
e.style.width = ew + "px"
e.style.height = eh + "px"
- e.my_locale = ix
+ e.my_id = ix
e.addEventListener("mousedown", on_click_locale)
e.addEventListener("mouseenter", on_focus_locale)
e.addEventListener("mouseleave", on_blur)
@@ -744,7 +899,7 @@ function build_map() {
data.lords.forEach((lord, ix) => {
let e = ui.lord_cylinder[ix] = document.createElement("div")
e.className = "cylinder lord " + clean_name(lord.side) + " " + clean_name(lord.name) + " hide"
- e.my_lord = ix
+ e.my_id = ix
e.addEventListener("mousedown", on_click_cylinder)
e.addEventListener("mouseenter", on_focus_cylinder)
e.addEventListener("mouseleave", on_blur)
@@ -752,7 +907,7 @@ function build_map() {
e = ui.lord_service[ix] = document.createElement("div")
e.className = "service_marker lord image" + lord.image + " " + clean_name(lord.side) + " " + clean_name(lord.name) + " hide"
- e.my_lord = ix
+ e.my_id = ix
e.addEventListener("mousedown", on_click_lord_service_marker)
e.addEventListener("mouseenter", on_focus_lord_service_marker)
e.addEventListener("mouseleave", on_blur)
@@ -767,7 +922,7 @@ function build_map() {
let lord = data.lords[vassal.lord]
let e = ui.vassal_service[ix] = document.createElement("div")
e.className = "service_marker vassal image" + vassal.image + " " + clean_name(lord.side) + " " + clean_name(vassal.name) + " hide"
- e.my_vassal = ix
+ e.my_id = ix
e.addEventListener("mousedown", on_click_vassal_service_marker)
e.addEventListener("mouseenter", on_focus_vassal_service_marker)
e.addEventListener("mouseleave", on_blur)
@@ -788,6 +943,8 @@ function build_map() {
document.getElementById("boxes").appendChild(e)
}
+ build_plan()
+
for (let c = 0; c < 21; ++c)
build_arts_of_war("teutonic", c)
for (let c = 21; c < 42; ++c)
@@ -797,4 +954,5 @@ function build_map() {
build_map()
// drag_element_with_mouse("#battle", "#battle_header")
drag_element_with_mouse("#arts_of_war", "#arts_of_war_header")
+drag_element_with_mouse("#plan", "#plan_header")
scroll_with_middle_mouse("main")
diff --git a/rules.js b/rules.js
index 386f466..138260e 100644
--- a/rules.js
+++ b/rules.js
@@ -171,6 +171,11 @@ function current_turn_name() {
// === GAME STATE ===
+const first_p1_lord = 0
+const last_p1_lord = 5
+const first_p2_lord = 6
+const last_p2_lord = 11
+
let first_friendly_lord = 0
let last_friendly_lord = 5
let first_enemy_lord = 6
@@ -182,11 +187,16 @@ function update_aliases() {
last_friendly_lord = 5
first_enemy_lord = 6
last_enemy_lord = 11
- } else {
+ } else if (game.active === P2) {
first_friendly_lord = 6
last_friendly_lord = 11
first_enemy_lord = 0
last_enemy_lord = 5
+ } else {
+ first_friendly_lord = -1
+ last_friendly_lord = -1
+ first_enemy_lord = -1
+ last_enemy_lord = -1
}
}
@@ -211,7 +221,7 @@ function pop_state() {
function set_active(new_active) {
if (game.active !== new_active) {
game.active = new_active
- update_active_aliases()
+ update_aliases()
}
}
@@ -696,6 +706,8 @@ function setup_pleskau_quickstart() {
add_lord_assets(LORD_GAVRILO, CART, 1)
add_lord_assets(LORD_VLADISLAV, BOAT, 1)
+ log_h1("Levy " + current_turn_name())
+
log_h2("Teutons Muster")
log_h3("Knud & Abel")
@@ -742,10 +754,12 @@ function setup_pleskau_quickstart() {
game.veche_coin += 1
+ goto_campaign_plan()
+
game.p1_plan = [ LORD_YAROSLAV, LORD_RUDOLF, LORD_HERMANN, LORD_HERMANN, LORD_RUDOLF, LORD_HERMANN ]
game.p2_plan = [ LORD_GAVRILO, LORD_VLADISLAV, LORD_DOMASH, LORD_GAVRILO, LORD_DOMASH, LORD_DOMASH ]
- goto_command_activation()
+ // goto_command_activation()
}
states.setup_lords = {
@@ -811,10 +825,10 @@ function goto_levy_muster() {
function end_levy_muster() {
game.lords.moved = 0
set_active_enemy()
- if (game.active === P1)
- goto_levy_call_to_arms()
- else
+ if (game.active === P2)
goto_levy_muster()
+ else
+ goto_levy_call_to_arms()
}
states.levy_muster = {
@@ -1059,13 +1073,80 @@ function end_levy_call_to_arms() {
// === CAMPAIGN: PLAN ===
function goto_campaign_plan() {
- game.active = BOTH
+ game.turn++
+
+ log_h1("Campaign " + current_turn_name())
+
+ set_active(BOTH)
game.state = 'campaign_plan'
game.p1_plan = []
game.p2_plan = []
+
+ // TODO: define lieutenants
+}
+
+function max_plan_length() {
+ switch (current_season()) {
+ case SUMMER: return 6
+ case EARLY_WINTER: return 4
+ case LATE_WINTER: return 4
+ case RASPUTITSA: return 5
+ }
+}
+
+function count_cards_in_plan(plan, lord) {
+ let n = 0
+ for (let c of plan)
+ if (c === lord)
+ ++n
+ return n
}
states.campaign_plan = {
+ prompt(current) {
+ view.prompt = "Create your plan."
+ let plan = (current === P1) ? game.p1_plan : game.p2_plan
+ view.plan = plan
+ if (plan.length < max_plan_length()) {
+ view.actions.end_plan = 0
+ if (count_cards_in_plan(plan, -1) < 3)
+ gen_action_plan(-1)
+ for (let lord = 0; lord <= last_lord; ++lord) {
+ console.log("campaign_plan", lord, current)
+ if (lord >= first_p1_lord && lord <= last_p1_lord && current !== P1)
+ continue
+ if (lord >= first_p2_lord && lord <= last_p2_lord && current !== P2)
+ continue
+ if (is_lord_on_map(lord) && count_cards_in_plan(plan, lord) < 3)
+ gen_action_plan(lord)
+ }
+ } else {
+ view.actions.end_plan = 1
+ }
+ if (plan.length > 0)
+ view.actions.undo = 1
+ else
+ view.actions.undo = 0
+ },
+ plan(lord, current) {
+ let plan = (current === P1) ? game.p1_plan : game.p2_plan
+ plan.push(lord)
+ },
+ undo(_, current) {
+ let plan = (current === P1) ? game.p1_plan : game.p2_plan
+ plan.length--
+ },
+ end_plan(_, current) {
+ console.log("active", game.active)
+ if (game.active === BOTH) {
+ if (current === P1)
+ set_active(P2)
+ else
+ set_active(P1)
+ } else {
+ end_campaign_plan()
+ }
+ },
}
function end_campaign_plan() {
@@ -1076,23 +1157,41 @@ function end_campaign_plan() {
// === CAMPAIGN: COMMAND ACTIVATION ===
function goto_command_activation() {
- if (game.active === P1)
- game.command = game.p1_plan.shift()
- else
- game.command = game.p2_plan.shift()
- if (game.command === undefined) {
+ if (game.p2_plan.length === 0) {
game.command = NOBODY
goto_end_campaign()
+ return
+ }
+
+ if (game.p2_plan.length > game.p1_plan.length) {
+ set_active(P2)
+ game.command = game.p2_plan.shift()
+ } else {
+ set_active(P1)
+ game.command = game.p1_plan.shift()
+ }
+
+ if (game.command === -1) {
+ log_h2("Pass")
+ goto_command_activation()
} else {
- game.who = game.command
goto_actions()
}
}
+function goto_end_campaign() {
+ // TODO: end game check
+ game.turn++
+ goto_levy_arts_of_war()
+}
+
// === CAMPAIGN: ACTIONS ===
function goto_actions() {
+ log_h2(lord_name[game.command])
+
game.state = 'actions'
+ game.who = game.command
game.count = data.lords[game.command].command
}
@@ -1106,13 +1205,17 @@ states.actions = {
view.prompt = `${lord_name[game.who]} has ${game.count}x actions.`
view.actions.end_actions = 1
},
+ end_actions() {
+ clear_undo()
+ end_actions()
+ },
}
// === CAMPAIGN: FEED ===
function has_friendly_lord_who_moved_or_fought() {
- for (let lord of game.moved)
- if (is_friendly_lord(lord))
+ for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord)
+ if (get_lord_moved(lord))
return true
return false
}
@@ -1126,8 +1229,8 @@ function goto_feed() {
states.feed = {
prompt() {
view.prompt = "You must Feed your who Moved or Fought."
- for (let lord of game.moved)
- if (is_friendly_lord(lord))
+ for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord)
+ if (get_lord_moved(lord))
gen_action_lord(lord)
view.actions.end_feed = 1
},
@@ -1170,7 +1273,9 @@ states.feed_lord = {
function end_feed() {
set_active_enemy()
- if (game.active === P1)
+ if (game.active === P2)
+ goto_feed()
+ else
goto_pay()
}
@@ -1230,7 +1335,9 @@ states.pay_lord = {
function end_pay() {
set_active_enemy()
- if (game.active === P1)
+ if (game.active === P2)
+ goto_pay()
+ else
goto_disband()
}
@@ -1253,7 +1360,9 @@ states.disband = {
function end_disband() {
set_active_enemy()
- if (game.active === P1) {
+ if (game.active === P2) {
+ goto_disband()
+ } else {
if (is_levy_phase())
goto_levy_muster()
else
@@ -1265,7 +1374,6 @@ function end_disband() {
function goto_remove_markers() {
game.lords.moved = 0
- set_active_enemy()
goto_command_activation()
}
@@ -1371,6 +1479,10 @@ function gen_action_arts_of_war(c) {
gen_action('arts_of_war', c)
}
+function gen_action_plan(lord) {
+ gen_action('plan', lord)
+}
+
exports.view = function(state, current) {
load_state(state)
@@ -1389,13 +1501,14 @@ exports.view = function(state, current) {
ravaged: game.ravaged,
castles: game.castles,
command: game.command,
+ plan: null,
who: game.who,
where: game.where,
}
if (game.state === 'game_over') {
view.prompt = game.victory
- } else if (current === 'Observer' || (current !== game.active && current !== BOTH)) {
+ } else if (current === 'Observer' || (game.active !== current && game.active !== BOTH)) {
let inactive = states[game.state].inactive || game.state
view.prompt = `Waiting for ${game.active} \u2014 ${inactive}...`
} else {