summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data.js78
-rw-r--r--play.html54
-rw-r--r--play.js116
-rw-r--r--rules.js572
-rw-r--r--tools/gendata.js31
5 files changed, 683 insertions, 168 deletions
diff --git a/data.js b/data.js
index 14da562..7c5fa0e 100644
--- a/data.js
+++ b/data.js
@@ -1,13 +1,15 @@
const data = {
seaports:[0,2,8,9,15,29,30,34],
+conquerable:[0,1,7,8,9,10,11,12,13,24,25,26,27,28,29,30,31,32,33,34,35,36],
+strongholds:[0,1,7,8,9,10,11,12,13,24,25,26,27,32,33,34,35,36],
locales:[
{"name":"Reval","type":"bishopric","stronghold":3,"walls":4,"vp":2,"region":"Danish Estonia","ways":[[5,25],[3,31]],"box":{"x":601,"y":3564,"w":206,"h":91}},
{"name":"Wesenberg","type":"castle","stronghold":2,"walls":4,"vp":1,"region":"Danish Estonia","ways":[[5,26],[17,30]],"box":{"x":1448,"y":3625,"w":304,"h":60}},
-{"name":"Narwia","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Danish Estonia","ways":[[7,0],[38,0],[49,0],[46,17],[6,27],[33,28]],"box":{"x":2371,"y":3549,"w":123,"h":31}},
-{"name":"Warbola","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Danish Estonia","ways":[[0,31],[4,32],[8,34]],"box":{"x":292,"y":3797,"w":142,"h":31}},
-{"name":"Harrien","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Danish Estonia","ways":[[3,32],[17,33]],"box":{"x":567,"y":3983,"w":200,"h":100}},
-{"name":"Revala","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Danish Estonia","ways":[[0,25],[1,26]],"box":{"x":1030,"y":3410,"w":200,"h":100}},
-{"name":"Wierland","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Danish Estonia","ways":[[2,27],[23,29]],"box":{"x":1999,"y":3680,"w":200,"h":100}},
+{"name":"Narwia","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Danish Estonia","ways":[[7,0],[38,0],[49,0],[46,17],[6,27],[33,28]],"box":{"x":2371,"y":3549,"w":123,"h":31}},
+{"name":"Warbola","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Danish Estonia","ways":[[0,31],[4,32],[8,34]],"box":{"x":292,"y":3797,"w":142,"h":31}},
+{"name":"Harrien","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Danish Estonia","ways":[[3,32],[17,33]],"box":{"x":567,"y":3983,"w":200,"h":100}},
+{"name":"Revala","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Danish Estonia","ways":[[0,25],[1,26]],"box":{"x":1030,"y":3410,"w":200,"h":100}},
+{"name":"Wierland","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Danish Estonia","ways":[[2,27],[23,29]],"box":{"x":1999,"y":3680,"w":200,"h":100}},
{"name":"Dorpat","type":"bishopric","stronghold":3,"walls":4,"vp":2,"region":"Crusader Livonia","ways":[[2,0],[38,0],[49,0],[11,2],[12,2,3],[22,3],[23,35]],"box":{"x":1625,"y":4589,"w":253,"h":91}},
{"name":"Leal","type":"bishopric","stronghold":3,"walls":4,"vp":2,"region":"Crusader Livonia","ways":[[3,34],[15,36]],"box":{"x":108,"y":4266,"w":205,"h":91}},
{"name":"Riga","type":"bishopric","stronghold":3,"walls":4,"vp":2,"region":"Crusader Livonia","ways":[[13,8]],"box":{"x":273,"y":6231,"w":205,"h":91}},
@@ -15,17 +17,17 @@ locales:[
{"name":"Fellin","type":"castle","stronghold":2,"walls":4,"vp":1,"region":"Crusader Livonia","ways":[[7,2],[12,2],[15,7],[17,7],[20,37]],"box":{"x":1013,"y":4583,"w":184,"h":61}},
{"name":"Odenpäh","type":"castle","stronghold":2,"walls":4,"vp":1,"region":"Crusader Livonia","ways":[[7,2,3],[11,2],[22,3],[14,45]],"box":{"x":1378,"y":5103,"w":250,"h":61}},
{"name":"Wenden","type":"castle","stronghold":2,"walls":4,"vp":1,"region":"Crusader Livonia","ways":[[9,8],[10,9],[19,39],[21,40]],"box":{"x":909,"y":5759,"w":232,"h":60}},
-{"name":"Kirrumpäh","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[10,44],[12,45],[32,46]],"box":{"x":1877,"y":5389,"w":175,"h":30}},
-{"name":"Pernau","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[11,7],[17,7],[8,36]],"box":{"x":517,"y":4580,"w":118,"h":30}},
-{"name":"Rositten","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[21,41],[18,42]],"box":{"x":2046,"y":6307,"w":146,"h":30}},
-{"name":"Jerwen","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[11,7],[15,7],[1,30],[4,33]],"box":{"x":1064,"y":3946,"w":200,"h":100}},
-{"name":"Lettgallia","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[16,42],[10,43],[21,43],[39,49],[32,50]],"box":{"x":2048,"y":5777,"w":200,"h":100}},
-{"name":"Metsepole","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[20,38],[13,39]],"box":{"x":509,"y":5226,"w":200,"h":100}},
-{"name":"Sackala","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[11,37],[19,38]],"box":{"x":617,"y":4769,"w":200,"h":100}},
-{"name":"Tolowa","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[13,40],[16,41],[10,43],[18,43]],"box":{"x":1541,"y":5933,"w":200,"h":100}},
-{"name":"Ugaunia","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[7,3],[12,3],[49,47],[32,48]],"box":{"x":1957,"y":4940,"w":200,"h":100}},
-{"name":"Waiga","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Crusader Livonia","ways":[[6,29],[7,35]],"box":{"x":1535,"y":4113,"w":200,"h":100}},
-{"name":"Novgorod","type":"archbishopric","stronghold":3,"walls":3,"vp":3,"region":"Novgorodan Rus","ways":[[27,6],[47,6],[31,23],[41,61],[40,62]],"box":{"x":4318,"y":4315,"w":333,"h":112}},
+{"name":"Kirrumpäh","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[10,44],[12,45],[32,46]],"box":{"x":1877,"y":5389,"w":175,"h":30}},
+{"name":"Pernau","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[11,7],[17,7],[8,36]],"box":{"x":517,"y":4580,"w":118,"h":30}},
+{"name":"Rositten","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[21,41],[18,42]],"box":{"x":2046,"y":6307,"w":146,"h":30}},
+{"name":"Jerwen","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[11,7],[15,7],[1,30],[4,33]],"box":{"x":1064,"y":3946,"w":200,"h":100}},
+{"name":"Lettgallia","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[16,42],[10,43],[21,43],[39,49],[32,50]],"box":{"x":2048,"y":5777,"w":200,"h":100}},
+{"name":"Metsepole","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[20,38],[13,39]],"box":{"x":509,"y":5226,"w":200,"h":100}},
+{"name":"Sackala","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[11,37],[19,38]],"box":{"x":617,"y":4769,"w":200,"h":100}},
+{"name":"Tolowa","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[13,40],[16,41],[10,43],[18,43]],"box":{"x":1541,"y":5933,"w":200,"h":100}},
+{"name":"Ugaunia","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[7,3],[12,3],[49,47],[32,48]],"box":{"x":1957,"y":4940,"w":200,"h":100}},
+{"name":"Waiga","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Crusader Livonia","ways":[[6,29],[7,35]],"box":{"x":1535,"y":4113,"w":200,"h":100}},
+{"name":"Novgorod","type":"novgorod","stronghold":3,"walls":3,"vp":3,"region":"Novgorodan Rus","ways":[[27,6],[47,6],[31,23],[41,61],[40,62]],"box":{"x":4318,"y":4315,"w":333,"h":112}},
{"name":"Ladoga","type":"city","stronghold":3,"walls":3,"vp":2,"region":"Novgorodan Rus","ways":[[31,22],[30,24],[44,58]],"box":{"x":4619,"y":2817,"w":238,"h":90}},
{"name":"Pskov","type":"city","stronghold":3,"walls":3,"vp":2,"region":"Novgorodan Rus","ways":[[49,4],[39,10],[32,51],[52,67],[37,68]],"box":{"x":2680,"y":5263,"w":205,"h":91}},
{"name":"Rusa","type":"city","stronghold":3,"walls":3,"vp":2,"region":"Novgorodan Rus","ways":[[24,6],[47,6],[28,13]],"box":{"x":4329,"y":5166,"w":205,"h":92}},
@@ -38,22 +40,22 @@ locales:[
{"name":"Koporye","type":"fort","stronghold":1,"walls":3,"vp":1,"region":"Novgorodan Rus","ways":[[33,52],[51,53]],"box":{"x":3133,"y":3160,"w":241,"h":62}},
{"name":"Porkhov","type":"fort","stronghold":1,"walls":3,"vp":1,"region":"Novgorodan Rus","ways":[[47,16],[37,69],[48,70]],"box":{"x":3515,"y":5467,"w":241,"h":63}},
{"name":"Velikiye Luki","type":"fort","stronghold":1,"walls":3,"vp":1,"region":"Novgorodan Rus","ways":[[28,14],[50,71]],"box":{"x":3706,"y":6347,"w":351,"h":61}},
-{"name":"Dubrovno","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[47,15],[26,68],[35,69]],"box":{"x":3153,"y":5214,"w":161,"h":31}},
-{"name":"Gdov","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[2,0],[7,0],[49,0,1],[46,64]],"box":{"x":2427,"y":4149,"w":88,"h":30}},
-{"name":"Ostrov","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[26,10],[50,11],[18,49]],"box":{"x":2746,"y":5717,"w":115,"h":30}},
-{"name":"Sablia","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[42,20],[24,62],[47,63]],"box":{"x":3788,"y":4541,"w":104,"h":31}},
-{"name":"Tesovo","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[42,21],[43,60],[24,61]],"box":{"x":3936,"y":4102,"w":121,"h":32}},
-{"name":"Zheltsy","type":"town","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[33,19],[40,20],[41,21],[46,65]],"box":{"x":3501,"y":4176,"w":128,"h":30}},
-{"name":"Ingria","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[51,55],[44,59],[41,60]],"box":{"x":3820,"y":3639,"w":200,"h":100}},
-{"name":"Izhora","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[30,57],[25,58],[43,59]],"box":{"x":4074,"y":3323,"w":200,"h":100}},
-{"name":"Karelia","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[30,56]],"box":{"x":3833,"y":2408,"w":200,"h":100}},
-{"name":"Plyussa River","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[2,17],[38,64],[42,65],[52,66]],"box":{"x":2829,"y":4234,"w":200,"h":100}},
-{"name":"Shelon River","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[24,6],[27,6],[37,15],[35,16],[40,63]],"box":{"x":3654,"y":4864,"w":200,"h":100}},
-{"name":"Sorot River","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[50,12],[35,70]],"box":{"x":3299,"y":5781,"w":200,"h":100}},
-{"name":"Uzmen","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[2,0],[7,0],[38,0,1],[26,4],[52,5],[22,47]],"box":{"x":2112,"y":4692,"w":200,"h":100}},
-{"name":"Velikaya River","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[39,11],[48,12],[36,71]],"box":{"x":3029,"y":6090,"w":200,"h":100}},
-{"name":"Vod","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[34,53],[30,54],[43,55]],"box":{"x":3488,"y":3345,"w":200,"h":100}},
-{"name":"Zhelcha River","type":"region","stronghold":0,"walls":0,"vp":0.5,"region":"Novgorodan Rus","ways":[[49,5],[46,66],[26,67]],"box":{"x":2782,"y":4586,"w":200,"h":100}},
+{"name":"Dubrovno","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[47,15],[26,68],[35,69]],"box":{"x":3153,"y":5214,"w":161,"h":31}},
+{"name":"Gdov","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[2,0],[7,0],[49,0,1],[46,64]],"box":{"x":2427,"y":4149,"w":88,"h":30}},
+{"name":"Ostrov","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[26,10],[50,11],[18,49]],"box":{"x":2746,"y":5717,"w":115,"h":30}},
+{"name":"Sablia","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[42,20],[24,62],[47,63]],"box":{"x":3788,"y":4541,"w":104,"h":31}},
+{"name":"Tesovo","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[42,21],[43,60],[24,61]],"box":{"x":3936,"y":4102,"w":121,"h":32}},
+{"name":"Zheltsy","type":"town","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[33,19],[40,20],[41,21],[46,65]],"box":{"x":3501,"y":4176,"w":128,"h":30}},
+{"name":"Ingria","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[51,55],[44,59],[41,60]],"box":{"x":3820,"y":3639,"w":200,"h":100}},
+{"name":"Izhora","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[30,57],[25,58],[43,59]],"box":{"x":4074,"y":3323,"w":200,"h":100}},
+{"name":"Karelia","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[30,56]],"box":{"x":3833,"y":2408,"w":200,"h":100}},
+{"name":"Plyussa River","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[2,17],[38,64],[42,65],[52,66]],"box":{"x":2829,"y":4234,"w":200,"h":100}},
+{"name":"Shelon River","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[24,6],[27,6],[37,15],[35,16],[40,63]],"box":{"x":3654,"y":4864,"w":200,"h":100}},
+{"name":"Sorot River","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[50,12],[35,70]],"box":{"x":3299,"y":5781,"w":200,"h":100}},
+{"name":"Uzmen","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[2,0],[7,0],[38,0,1],[26,4],[52,5],[22,47]],"box":{"x":2112,"y":4692,"w":200,"h":100}},
+{"name":"Velikaya River","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[39,11],[48,12],[36,71]],"box":{"x":3029,"y":6090,"w":200,"h":100}},
+{"name":"Vod","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[34,53],[30,54],[43,55]],"box":{"x":3488,"y":3345,"w":200,"h":100}},
+{"name":"Zhelcha River","type":"region","stronghold":0,"walls":0,"vp":0,"region":"Novgorodan Rus","ways":[[49,5],[46,66],[26,67]],"box":{"x":2782,"y":4586,"w":200,"h":100}},
],
ways:[
{"type":"waterway","locales":[2,7,38,49],"name":"Pleipat W"},
@@ -198,9 +200,9 @@ cards:[
{"name":"T16","event":"Famine","capability":"Ransom","lords":null},
{"name":"T17","event":"Dietrich von Grüningen","capability":"Stonemasons","lords":[0,1,2,3,4,5]},
{"name":"T18","event":"Swedish Crusade","capability":"Cogs","lords":[0,1,3]},
-{"name":"TNo","event":"No Event","capability":null,"lords":null},
-{"name":"TNo","event":"No Event","capability":null,"lords":null},
-{"name":"TNo","event":"No Event","capability":null,"lords":null},
+{"name":"T0","event":"No Event","capability":null,"lords":null},
+{"name":"T0","event":"No Event","capability":null,"lords":null},
+{"name":"T0","event":"No Event","capability":null,"lords":null},
{"name":"R1","event":"Bridge","capability":"Luchniki","lords":[8,9,10,11]},
{"name":"R2","event":"Marsh","capability":"Luchniki","lords":[8,9,10,11]},
{"name":"R3","event":"Pogost","capability":"Streltsy","lords":[6,7,8,9,11]},
@@ -219,9 +221,9 @@ cards:[
{"name":"R16","event":"Tempest","capability":"Lodya","lords":[6,7,8,9,10,11]},
{"name":"R17","event":"Dietrich von Grüningen","capability":"Veliky Knyaz","lords":[6,7,8,9,10,11]},
{"name":"R18","event":"Bountiful Harvest","capability":"Stone Kremlin","lords":[6,7,8,9,10,11]},
-{"name":"RNo","event":"No Event","capability":null,"lords":null},
-{"name":"RNo","event":"No Event","capability":null,"lords":null},
-{"name":"RNo","event":"No Event","capability":null,"lords":null},
+{"name":"R0","event":"No Event","capability":null,"lords":null},
+{"name":"R0","event":"No Event","capability":null,"lords":null},
+{"name":"R0","event":"No Event","capability":null,"lords":null},
],
}
if (typeof module !== 'undefined') module.exports = data
diff --git a/play.html b/play.html
index 6b6026f..11635b6 100644
--- a/play.html
+++ b/play.html
@@ -25,7 +25,8 @@ header.your_turn { background-color: orange; }
#log { background-color: whitesmoke; }
#log .h1 { background-color: silver; font-weight: bold; padding-top:2px; padding-bottom:2px; text-align: center; }
#log .h2 { background-color: gainsboro; padding-top:2px; padding-bottom:2px; text-align: center; }
-#log .h3 { background-color: lavender; padding-top:2px; padding-bottom:2px; text-align: center; }
+#log .h3 { text-decoration: underline; }
+#log .h4 { text-decoration: underline; }
#log > .i { padding-left: 20px; }
#log > .ii { padding-left: 32px; }
#log > div > .i { padding-left: 12px; }
@@ -123,7 +124,7 @@ body.shift .mat .card:hover {
z-index: 200;
}
-.mat .forces, .mat .routed, .mat .assets, .mat .vassals {
+.mat .forces, .mat .routed, .mat .assets, .mat .vassals, #veche {
position: absolute;
display: flex;
flex-wrap: wrap;
@@ -136,7 +137,7 @@ body.shift .mat .card:hover {
//background-color: #f004;
}
-.mat .forces {
+.mat .forces, #veche {
justify-content: center;
}
@@ -473,7 +474,7 @@ body.shift .mat .card:hover {
.locale_extra.fort { border-radius: 30% 30% 0 0 }
.locale_extra.bishopric { border-radius: 50% 50% 20% 20% }
.locale_extra.city { border-radius: 50% 50% 0 0 }
-.locale_extra.archbishopric { border-radius: 50% 50% 25% 25%; }
+.locale_extra.novgorod { border-radius: 50% 50% 25% 25%; }
.locale.tip, .locale_extra.tip {
background-color: #ff06;
@@ -573,7 +574,7 @@ body.shift .mat .card:hover {
/* PIECES */
-#legate, .cylinder, .service_marker {
+#legate, .cylinder, .service_marker, .marker {
transition-property: top, left;
transition-duration: 700ms;
transition-timing-function: ease;
@@ -816,23 +817,25 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
.card.aow_40{background-image:url(cards.1x/aow_russian_none.jpg)}
.card.aow_41{background-image:url(cards.1x/aow_russian_none.jpg)}
-.card.cc_russian_back{background-image:url(cards.1x/cc_russian_back.jpg)}
-.card.cc_russian_aleksandr{background-image:url(cards.1x/cc_russian_aleksandr.jpg)}
-.card.cc_russian_andrey{background-image:url(cards.1x/cc_russian_andrey.jpg)}
-.card.cc_russian_domash{background-image:url(cards.1x/cc_russian_domash.jpg)}
-.card.cc_russian_gavrilo{background-image:url(cards.1x/cc_russian_gavrilo.jpg)}
-.card.cc_russian_karelians{background-image:url(cards.1x/cc_russian_karelians.jpg)}
-.card.cc_russian_vladislav{background-image:url(cards.1x/cc_russian_vladislav.jpg)}
-.card.cc_russian_pass{background-image:url(cards.1x/cc_russian_pass.jpg)}
-
-.card.cc_teutonic_back{background-image:url(cards.1x/cc_teutonic_back.jpg)}
-.card.cc_teutonic_andreas{background-image:url(cards.1x/cc_teutonic_andreas.jpg)}
-.card.cc_teutonic_heinrich{background-image:url(cards.1x/cc_teutonic_heinrich.jpg)}
-.card.cc_teutonic_hermann{background-image:url(cards.1x/cc_teutonic_hermann.jpg)}
-.card.cc_teutonic_knud_and_abel{background-image:url(cards.1x/cc_teutonic_knud_and_abel.jpg)}
-.card.cc_teutonic_rudolf{background-image:url(cards.1x/cc_teutonic_rudolf.jpg)}
-.card.cc_teutonic_yaroslav{background-image:url(cards.1x/cc_teutonic_yaroslav.jpg)}
-.card.cc_teutonic_pass{background-image:url(cards.1x/cc_teutonic_pass.jpg)}
+.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)}
+.card.cc_lord_9{background-image:url(cards.1x/cc_russian_gavrilo.jpg)}
+.card.cc_lord_10{background-image:url(cards.1x/cc_russian_karelians.jpg)}
+.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)}
@@ -920,10 +923,13 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
<div class="debug menu_separator"></div>
<div class="debug menu_item" onclick="send_save()">&#x1F41E; Save</div>
<div class="debug menu_item" onclick="send_restore()">&#x1F41E; Restore</div>
+ <div class="debug menu_item" onclick="send_restart('Pleskau (Quickstart)')">&#x26a0; Pleskau (Quickstart)</div>
<div class="debug menu_item" onclick="send_restart('Pleskau')">&#x26a0; Pleskau</div>
<div class="debug menu_item" onclick="send_restart('Watland')">&#x26a0; Watland</div>
<div class="debug menu_item" onclick="send_restart('Peipus')">&#x26a0; Peipus</div>
- <div class="debug menu_item" onclick="send_restart('Quickstart')">&#x26a0; Quickstart</div>
+ <div class="debug menu_item" onclick="send_restart('Return of the Prince')">&#x26a0; Return of the Prince</div>
+ <div class="debug menu_item" onclick="send_restart('Return of the Prince (Nicolle)')">&#x26a0; Return of the Prince (Nicolle)</div>
+ <div class="debug menu_item" onclick="send_restart('Crusade on Novgorod')">&#x26a0; Crusade on Novgorod</div>
</div>
</div>
<div class="icon_button" onclick="toggle_pieces()"><img src="/images/earth-africa-europe.svg"></div>
@@ -947,7 +953,7 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
<div class="role_user">-</div>
</div>
</div>
- <div class="card_info"><div id="last_card" class="card russian cc_russian_back"></div></div>
+ <div class="card_info"><div id="command" class="card teutonic aow_back"></div></div>
</div>
<div id="log"></div>
</aside>
diff --git a/play.js b/play.js
index e6f05dd..8295c8b 100644
--- a/play.js
+++ b/play.js
@@ -6,6 +6,24 @@ const round = Math.round
const floor = Math.floor
const ceil = Math.ceil
+// unit types
+const KNIGHTS = 0
+const SERGEANTS = 1
+const LIGHT_HORSE = 2
+const ASIATIC_HORSE = 3
+const MEN_AT_ARMS = 4
+const MILITIA = 5
+const SERFS = 6
+
+// asset types
+const PROV = 0
+const COIN = 1
+const LOOT = 2
+const CART = 3
+const SLED = 4
+const BOAT = 5
+const SHIP = 6
+
function pack1_get(word, n) {
return (word >>> n) & 1
}
@@ -38,37 +56,43 @@ const asset_type_count = 7
const asset_type_name = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ]
const asset_type_x3 = [ 1, 1, 1, 0, 0, 0, 0 ]
-const first_teutonic_region = 0
-const last_teutonic_region = 23
-const first_russian_region = 24
-const last_russian_region = 52
+const first_p1_locale = 0
+const last_p1_locale = 23
+const first_p2_locale = 24
+const last_p2_locale = 52
-function is_teutonic_region(loc) {
- return loc >= first_teutonic_region && loc <= last_teutonic_region
+function is_p1_locale(loc) {
+ return loc >= first_p1_locale && loc <= last_p1_locale
}
-function is_russian_region(loc) {
- return loc >= first_russian_region && loc <= last_russian_region
+function is_p2_locale(loc) {
+ return loc >= first_p2_locale && loc <= last_p2_locale
}
-function count_teutonic_vp() {
+function count_vp1() {
let vp = 0
+ for (let loc of view.castles)
+ if (is_p2_locale(loc))
+ vp += 2
for (let loc of view.conquered)
- if (is_russian_region(loc))
+ if (is_p2_locale(loc))
vp += data.locales[loc].vp << 1
for (let loc of view.ravaged)
- if (is_russian_region(loc))
+ if (is_p2_locale(loc))
vp += 1
return vp
}
-function count_russian_vp() {
+function count_vp2() {
let vp = view.veche_vp * 2
+ for (let loc of view.castles)
+ if (is_p1_locale(loc))
+ vp += 2
for (let loc of view.conquered)
- if (is_teutonic_region(loc))
+ if (is_p1_locale(loc))
vp += data.locales[loc].vp << 1
for (let loc of view.ravaged)
- if (is_teutonic_region(loc))
+ if (is_p1_locale(loc))
vp += 1
return vp
}
@@ -177,6 +201,7 @@ const ui = {
arts_of_war_list: document.getElementById("arts_of_war_list"),
p1_global: document.getElementById("p1_global"),
p2_global: document.getElementById("p2_global"),
+ command: document.getElementById("command"),
turn: document.getElementById("turn"),
vp1: document.getElementById("vp1"),
vp2: document.getElementById("vp2"),
@@ -196,7 +221,7 @@ const extra_size_100 = {
traderoute: [ 72, 42 ],
bishopric: [ 84, 60 ],
city: [ 132, 72 ],
- archbishopric: [ 156, 96 ],
+ novgorod: [ 156, 96 ],
}
const extra_size = {
@@ -206,7 +231,7 @@ const extra_size = {
traderoute: [ 54, 32 ],
bishopric: [ 63, 45 ],
city: [ 100, 54 ],
- archbishopric: [ 117, 72 ],
+ novgorod: [ 117, 72 ],
}
function toggle_pieces() {
@@ -233,7 +258,6 @@ function on_click_cylinder(evt) {
}
function on_click_arts_of_war(evt) {
-console.log("AOW CLICK", evt.target.dataset.arts_of_war)
if (evt.button === 0) {
let id = evt.target.dataset.arts_of_war | 0
send_action('arts_of_war', id)
@@ -380,6 +404,11 @@ function add_asset(parent, type, n) {
build_div(parent, "asset " + asset_type_name[type] + " x"+n, "asset", type)
}
+function add_veche_vp(parent) {
+ // TODO: reuse pool of elements?
+ build_div(parent, "marker square conquered russian")
+}
+
function update_forces(parent, forces) {
parent.replaceChildren()
for (let i = 0; i < force_type_count; ++i) {
@@ -465,6 +494,29 @@ function update_lord(ix) {
ui.lord_mat[ix].classList.toggle("selected", ix === view.who)
}
+function update_veche() {
+ ui.veche.replaceChildren()
+
+ console.log("update_veche", view.veche_coin, view.veche_vp)
+
+ let n = view.veche_coin
+ while (n >= 3) {
+ add_asset(ui.veche, COIN, 3)
+ n -= 3
+ }
+ while (n >= 2) {
+ add_asset(ui.veche, COIN, 2)
+ n -= 2
+ }
+ while (n >= 1) {
+ add_asset(ui.veche, COIN, 1)
+ n -= 1
+ }
+
+ for (let i = 0; i < view.veche_vp; ++i)
+ add_veche_vp(ui.veche)
+}
+
function update_locale(loc) {
ui.locale[loc].classList.toggle("action", is_locale_action(loc))
if (ui.locale_extra[loc])
@@ -534,8 +586,26 @@ function on_update() {
}
}
- for (let loc = 0; loc < data.locales.length; ++loc) {
+ for (let loc = 0; loc < data.locales.length; ++loc)
update_locale(loc)
+
+ update_veche()
+
+ if ((view.turn & 1) === 0) {
+ if (player === "Russians")
+ ui.command.className = `card russian aow_back`
+ else
+ ui.command.className = `card teutonic aow_back`
+ } else if (view.command < 0) {
+ if (player === "Russians")
+ ui.command.className = `card russian cc_back`
+ else
+ ui.command.className = `card teutonic cc_back`
+ } else {
+ if (view.command < 6)
+ ui.command.className = `card russian cc_lord_${view.command}`
+ else
+ ui.command.className = `card teutonic cc_lord_${view.command}`
}
if (view.turn & 1)
@@ -543,8 +613,8 @@ function on_update() {
else
ui.turn.className = `marker circle turn levy t${view.turn>>1}`
- let vp1 = count_teutonic_vp()
- let vp2 = count_russian_vp()
+ let vp1 = count_vp1()
+ let vp2 = count_vp2()
if ((vp1 >> 1) === (vp2 >> 1)) {
if (vp1 & 1)
ui.vp1.className = `marker circle victory teutonic stack v${vp1>>1} half`
@@ -575,9 +645,15 @@ function on_update() {
action_button("capability", "Capability")
action_button("done", "Done")
+ action_button("unfed", "Unfed")
+ action_button("end_feed", "End feed")
+ action_button("end_pay", "End pay")
+ action_button("end_disband", "End disband")
+ action_button("end_actions", "End actions")
action_button("end_levy", "End levy")
action_button("end_muster", "End muster")
action_button("end_setup", "End setup")
+
action_button("undo", "Undo")
}
diff --git a/rules.js b/rules.js
index 5f280d2..386f466 100644
--- a/rules.js
+++ b/rules.js
@@ -1,5 +1,8 @@
"use strict"
+const TODO = false
+
+const BOTH = "Both"
const TEUTONS = "Teutons"
const RUSSIANS = "Russians"
@@ -27,7 +30,7 @@ exports.scenarios = [
"Watland",
"Peipus",
"Return of the Prince",
- "Return of the Prince (Nicolle Variant)",
+ "Return of the Prince (Nicolle)",
"Crusade on Novgorod",
"Pleskau (Quickstart)",
]
@@ -52,16 +55,23 @@ const SHIP = 6
const data = require("./data.js")
+function find_arts_of_war(name) { return data.cards.findIndex(x => x.name === name) }
function find_lord(name) { return data.lords.findIndex(x => x.name === name) }
-function find_locale(name) { return data.locales.findIndex(x => x?.name === name) }
+function find_locale(name) { return data.locales.findIndex(x => x.name === name) }
const lord_name = data.lords.map(lord => lord.name)
+const vassal_name = data.vassals.map(vassal => vassal.name)
const lord_count = data.lords.length
const vassal_count = data.vassals.length
const last_vassal = vassal_count - 1
const last_lord = lord_count - 1
+const first_p1_locale = 0
+const last_p1_locale = 23
+const first_p2_locale = 24
+const last_p2_locale = 52
+
const LORD_ANDREAS = find_lord("Andreas")
const LORD_HEINRICH = find_lord("Heinrich")
const LORD_HERMANN = find_lord("Hermann")
@@ -100,6 +110,10 @@ const LOC_PORKHOV = find_locale("Porkhov")
const LOC_VELIKIYE_LUKI = find_locale("Velikiye Luki")
const LOC_DUBROVNO = find_locale("Dubrovno")
+const LOC_VOD = find_locale("Vod")
+const LOC_ZHELTSY = find_locale("Zheltsy")
+const LOC_TESOVO = find_locale("Tesovo")
+const LOC_SABLIA = find_locale("Sablia")
const NOBODY = -1
const NOWHERE = -1
@@ -113,11 +127,14 @@ 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
]
const TURN_NAME = [
+ null,
"1 - Summer 1240",
"2 - Summer 1240",
"3 - Early Winter 1240",
@@ -134,6 +151,7 @@ const TURN_NAME = [
"14 - Late Winter 1242",
"15 - Rasputitsa 1242",
"16 - Rasputitsa 1242",
+ null
]
const USABLE_TRANSPORT = [
@@ -245,9 +263,15 @@ function set_lord_locale(lord, locale) {
}
function set_lord_service(lord, service) {
+ if (service < 0) service = 0
+ if (service > 16) service = 16
game.lords.service[lord] = service
}
+function add_lord_service(lord, n) {
+ set_lord_service(lord, get_lord_service(lord) + n)
+}
+
function set_lord_assets(lord, n, x) {
if (x < 0) x = 0
if (x > 8) x = 8
@@ -298,6 +322,24 @@ function set_lord_vassal_service(lord, n, x) {
// === GAME STATE HELPERS ===
+function count_lord_forces(lord) {
+ return get_lord_forces(lord, KNIGHTS)
+ + get_lord_forces(lord, SERGEANTS)
+ + get_lord_forces(lord, LIGHT_HORSE)
+ + get_lord_forces(lord, ASIATIC_HORSE)
+ + get_lord_forces(lord, MEN_AT_ARMS)
+ + get_lord_forces(lord, MILITIA)
+ + get_lord_forces(lord, SERFS)
+}
+
+function is_campaign_phase() {
+ return (game.turn & 1) === 1
+}
+
+function is_levy_phase() {
+ return (game.turn & 1) === 0
+}
+
function is_card_in_use(c) {
if (set_has(game.global_cards, c))
return true
@@ -329,6 +371,14 @@ function is_vassal_ready(vassal) {
return game.vassals[vassal] === 0
}
+function is_friendly_lord(lord) {
+ return lord >= first_friendly_lord && lord <= last_friendly_lord
+}
+
+function is_enemy_lord(lord) {
+ return lord >= first_enemy_lord && lord <= last_enemy_lord
+}
+
function is_lord_at_friendly_locale(lord) {
let loc = get_lord_locale(lord)
return is_friendly_locale(loc)
@@ -353,10 +403,8 @@ function can_add_transport(who, what) {
return get_lord_assets(who, what) < 8
}
-function roll_die(reason) {
- let die = random(6) + 1
- log(`Rolled ${die}${reason}.`)
- return die
+function roll_die() {
+ return random(6) + 1
}
// === SETUP ===
@@ -398,6 +446,8 @@ function muster_lord(lord, locale, service) {
function muster_vassal(lord, vassal) {
let info = data.vassals[vassal]
+ logi(`${vassal_name[vassal]}`)
+
game.vassals[vassal] = 1
add_lord_forces(lord, KNIGHTS, info.forces.knights | 0)
@@ -442,8 +492,10 @@ exports.setup = function (seed, scenario, options) {
veche_coin: 0,
conquered: [],
ravaged: [],
+ castles: [],
global_cards: [],
+ command: NOBODY,
who: NOBODY,
where: NOWHERE,
what: NOTHING,
@@ -467,14 +519,14 @@ exports.setup = function (seed, scenario, options) {
case "Return of the Prince":
setup_return_of_the_prince()
break
- case "Return of the Prince (Nicolle Variant)":
+ case "Return of the Prince (Nicolle)":
setup_return_of_the_prince_nicolle()
break
case "Crusade on Novgorod":
setup_crusade_on_novgorod()
break
case "Pleskau (Quickstart)":
- setup_quickstart()
+ setup_pleskau_quickstart()
break
}
@@ -483,23 +535,22 @@ exports.setup = function (seed, scenario, options) {
function setup_pleskau() {
game.turn = 1 << 1
+
game.veche_vp = 1
+
muster_lord(LORD_HERMANN, LOC_DORPAT, 4)
muster_lord(LORD_KNUD_ABEL, LOC_REVAL, 3)
muster_lord(LORD_YAROSLAV, LOC_ODENPAH, 2)
muster_lord(LORD_GAVRILO, LOC_PSKOV, 4)
muster_lord(LORD_VLADISLAV, LOC_NEVA, 3)
+
setup_lord_on_calendar(LORD_RUDOLF, 1)
setup_lord_on_calendar(LORD_DOMASH, 1)
}
-function setup_quickstart() {
- setup_pleskau()
- // TODO: automated muster
-}
-
function setup_watland() {
game.turn = 4 << 1
+
game.veche_vp = 1
game.veche_coin = 1
@@ -522,6 +573,181 @@ function setup_watland() {
setup_lord_on_calendar(LORD_HERMANN, 8)
}
+function setup_peipus() {
+ game.turn = 13 << 1
+
+ game.veche_vp = 4
+ game.veche_coin = 3
+
+ set_add(game.castles, LOC_KOPORYE)
+ set_add(game.conquered, LOC_IZBORSK)
+ set_add(game.conquered, LOC_PSKOV)
+ set_add(game.ravaged, LOC_VOD)
+ set_add(game.ravaged, LOC_ZHELTSY)
+ set_add(game.ravaged, LOC_TESOVO)
+ set_add(game.ravaged, LOC_SABLIA)
+ set_add(game.ravaged, LOC_PSKOV)
+ set_add(game.ravaged, LOC_DUBROVNO)
+
+ muster_lord(LORD_HERMANN, LOC_DORPAT, 16)
+ muster_lord(LORD_YAROSLAV, LOC_PSKOV, 14)
+ muster_lord(LORD_ALEKSANDR, LOC_NOVGOROD, 16)
+ muster_lord(LORD_ANDREY, LOC_NOVGOROD, 16)
+ muster_lord(LORD_DOMASH, LOC_NOVGOROD, 16)
+ muster_lord(LORD_KARELIANS, LOC_NOVGOROD, 14)
+
+ setup_lord_on_calendar(LORD_HEINRICH, 13)
+ setup_lord_on_calendar(LORD_KNUD_ABEL, 13)
+ setup_lord_on_calendar(LORD_RUDOLF, 13)
+ setup_lord_on_calendar(LORD_GAVRILO, 13)
+ setup_lord_on_calendar(LORD_VLADISLAV, 15)
+}
+
+function setup_return_of_the_prince() {
+ game.turn = 9 << 1
+
+ game.veche_vp = 3
+ game.veche_coin = 2
+
+ set_add(game.castles, LOC_KOPORYE)
+ set_add(game.conquered, LOC_KAIBOLOVO)
+ set_add(game.conquered, LOC_KOPORYE)
+ set_add(game.conquered, LOC_IZBORSK)
+ set_add(game.conquered, LOC_PSKOV)
+ set_add(game.ravaged, LOC_VOD)
+ set_add(game.ravaged, LOC_ZHELTSY)
+ set_add(game.ravaged, LOC_TESOVO)
+ set_add(game.ravaged, LOC_SABLIA)
+ set_add(game.ravaged, LOC_PSKOV)
+ set_add(game.ravaged, LOC_DUBROVNO)
+
+ muster_lord(LORD_ANDREAS, LOC_KOPORYE, 12)
+ muster_lord(LORD_ALEKSANDR, LOC_NOVGOROD, 14)
+
+ setup_lord_on_calendar(LORD_HERMANN, 9)
+ setup_lord_on_calendar(LORD_RUDOLF, 9)
+ setup_lord_on_calendar(LORD_YAROSLAV, 9)
+ setup_lord_on_calendar(LORD_ANDREY, 9)
+ setup_lord_on_calendar(LORD_KARELIANS, 9)
+ setup_lord_on_calendar(LORD_VLADISLAV, 10)
+ setup_lord_on_calendar(LORD_HEINRICH, 11)
+ setup_lord_on_calendar(LORD_KNUD_ABEL, 11)
+ setup_lord_on_calendar(LORD_DOMASH, 11)
+ setup_lord_on_calendar(LORD_GAVRILO, 13)
+}
+
+function setup_return_of_the_prince_nicolle() {
+ game.turn = 9 << 1
+
+ game.veche_vp = 3
+ game.veche_coin = 2
+
+ set_add(game.castles, LOC_KOPORYE)
+ set_add(game.conquered, LOC_KAIBOLOVO)
+ set_add(game.conquered, LOC_KOPORYE)
+ set_add(game.ravaged, LOC_VOD)
+ set_add(game.ravaged, LOC_ZHELTSY)
+ set_add(game.ravaged, LOC_TESOVO)
+ set_add(game.ravaged, LOC_SABLIA)
+
+ muster_lord(LORD_ANDREAS, LOC_RIGA, 12)
+ muster_lord(LORD_HERMANN, LOC_DORPAT, 12)
+ muster_lord(LORD_KNUD_ABEL, LOC_KOPORYE, 11)
+ muster_lord(LORD_ALEKSANDR, LOC_NOVGOROD, 14)
+ muster_lord(LORD_GAVRILO, LOC_PSKOV, 12)
+
+ setup_lord_on_calendar(LORD_RUDOLF, 9)
+ setup_lord_on_calendar(LORD_YAROSLAV, 9)
+ setup_lord_on_calendar(LORD_ANDREY, 9)
+ setup_lord_on_calendar(LORD_KARELIANS, 9)
+ setup_lord_on_calendar(LORD_VLADISLAV, 10)
+ setup_lord_on_calendar(LORD_HEINRICH, 11)
+ setup_lord_on_calendar(LORD_DOMASH, 11)
+}
+
+function setup_crusade_on_novgorod() {
+ game.turn = 1 << 1
+
+ game.veche_vp = 1
+ game.veche_coin = 0
+
+ muster_lord(LORD_HERMANN, LOC_DORPAT, 4)
+ muster_lord(LORD_KNUD_ABEL, LOC_REVAL, 3)
+ muster_lord(LORD_YAROSLAV, LOC_ODENPAH, 2)
+ muster_lord(LORD_GAVRILO, LOC_PSKOV, 4)
+ muster_lord(LORD_VLADISLAV, LOC_NEVA, 3)
+
+ setup_lord_on_calendar(LORD_ANDREAS, 1)
+ setup_lord_on_calendar(LORD_HEINRICH, 1)
+ setup_lord_on_calendar(LORD_RUDOLF, 1)
+ setup_lord_on_calendar(LORD_DOMASH, 1)
+ setup_lord_on_calendar(LORD_KARELIANS, 3)
+ setup_lord_on_calendar(LORD_ALEKSANDR, 5)
+ setup_lord_on_calendar(LORD_ANDREY, 5)
+}
+
+function setup_pleskau_quickstart() {
+ setup_pleskau()
+
+ add_lord_assets(LORD_HERMANN, CART, 1)
+ add_lord_assets(LORD_YAROSLAV, CART, 1)
+
+ add_lord_assets(LORD_GAVRILO, BOAT, 1)
+ add_lord_assets(LORD_GAVRILO, CART, 1)
+ add_lord_assets(LORD_VLADISLAV, BOAT, 1)
+
+ log_h2("Teutons Muster")
+
+ log_h3("Knud & Abel")
+ logi(`Rudolf at %${LOC_WENDEN}`)
+ muster_lord(LORD_RUDOLF, LOC_WENDEN)
+ logii("Cart")
+ add_lord_assets(LORD_RUDOLF, CART, 1)
+
+ logi("Boat")
+ add_lord_assets(LORD_KNUD_ABEL, BOAT, 1)
+
+ log_h3("Hermann")
+ muster_vassal(LORD_HERMANN, data.lords[LORD_HERMANN].vassals[0])
+ logi("Capability T4")
+ set_lord_capability(LORD_HERMANN, 0, find_arts_of_war("T4"))
+ logi("Capability T14")
+ set_lord_capability(LORD_HERMANN, 1, find_arts_of_war("T14"))
+
+ log_h3("Yaroslav")
+ logi("Capability T3")
+ set_lord_capability(LORD_YAROSLAV, 0, find_arts_of_war("T3"))
+
+ set_add(game.global_cards, find_arts_of_war("T13"))
+ game.legate = LOC_DORPAT
+
+ log_h2("Russians Muster")
+
+ log_h3("Vladislav")
+ logi("Capability R8")
+ set_add(game.global_cards, find_arts_of_war("R8"))
+ logi(`Domash at %${LOC_NOVGOROD}`)
+ muster_lord(LORD_DOMASH, LOC_NOVGOROD)
+ logii("Boat")
+ add_lord_assets(LORD_DOMASH, BOAT, 2)
+ logii("Cart")
+ add_lord_assets(LORD_DOMASH, CART, 2)
+
+ log_h3("Gavrilo")
+ muster_vassal(LORD_GAVRILO, data.lords[LORD_GAVRILO].vassals[0])
+ logi("Capability R2")
+ set_lord_capability(LORD_GAVRILO, 0, find_arts_of_war("R2"))
+ logi("Capability R6")
+ set_lord_capability(LORD_GAVRILO, 1, find_arts_of_war("R6"))
+
+ game.veche_coin += 1
+
+ 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()
+}
+
states.setup_lords = {
prompt() {
view.prompt = "Setup your Lords."
@@ -541,6 +767,7 @@ states.setup_lords = {
},
lord(lord) {
push_undo()
+ log(`${lord_name[lord]} at %${get_lord_locale(lord)}`)
push_state('muster_lord_transport')
set_lord_moved(lord, 1)
game.who = lord
@@ -548,19 +775,15 @@ states.setup_lords = {
},
end_setup() {
clear_undo()
- end_setup()
+ end_setup_lords()
},
}
function end_setup_lords() {
- for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord)
- set_lord_moved(lord, 0)
- if (game.active === P1) {
- set_active_enemy()
- } else {
- set_active_enemy()
+ game.lords.moved = 0
+ set_active_enemy()
+ if (game.active === P1)
goto_levy_arts_of_war()
- }
}
// === LEVY: ARTS OF WAR ===
@@ -571,56 +794,27 @@ function goto_levy_arts_of_war() {
end_levy_arts_of_war()
}
-function end_levy_arts_of_war() {
- goto_levy_pay()
-}
-
states.levy_arts_of_war = {
}
-// === LEVY: PAY ===
-
-function goto_levy_pay() {
- game.state = 'levy_pay'
- end_levy_pay()
-}
-
-function end_levy_pay() {
- goto_levy_disband()
-}
-
-states.levy_pay = {
-}
-
-// === LEVY: DISBAND ===
-
-function goto_levy_disband() {
- game.state = 'levy_disband'
- end_levy_disband()
-}
-
-function end_levy_disband() {
- goto_levy_muster()
-}
-
-states.levy_disband = {
+function end_levy_arts_of_war() {
+ goto_pay()
}
// === LEVY: MUSTER ===
function goto_levy_muster() {
+ log_h2(game.active + " Muster")
game.state = 'levy_muster'
}
function end_levy_muster() {
- for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord)
- set_lord_moved(lord, 0)
- if (game.active === P1) {
- set_active_enemy()
- } else {
- set_active_enemy()
+ game.lords.moved = 0
+ set_active_enemy()
+ if (game.active === P1)
goto_levy_call_to_arms()
- }
+ else
+ goto_levy_muster()
}
states.levy_muster = {
@@ -640,6 +834,7 @@ states.levy_muster = {
},
lord(lord) {
push_undo()
+ log_h3(`${lord_name[lord]} at %${get_lord_locale(lord)}`)
push_state('levy_muster_lord')
game.who = lord
game.count = data.lords[lord].lordship
@@ -659,9 +854,9 @@ states.levy_muster_lord = {
// Roll to muster Ready Lord at Seat
for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) {
- if (lord === ALEKSANDR)
+ if (lord === LORD_ALEKSANDR)
continue
- if (lord === ANDREY && game.who !== ALEKSANDR)
+ if (lord === LORD_ANDREY && game.who !== LORD_ALEKSANDR)
continue
if (is_lord_ready(lord))
// TODO: has available seat
@@ -698,40 +893,46 @@ states.levy_muster_lord = {
lord(other) {
clear_undo()
--game.count
- let die = roll_die(` to muster ${lord_name[other]}`)
- // TODO: roll for lord
- if (die <= data.lords[other].fealty) {
- logi(`Success!`)
+ let die = roll_die()
+ let fealty = data.lords[other].fealty
+ if (die <= fealty) {
+ logi(`${lord_name[other]} rolled ${die} <= ${fealty}`)
push_state('muster_lord_at_seat')
game.who = other
} else {
- logi(`Failed.`)
+ logi(`${lord_name[other]} rolled ${die} > ${fealty}`)
+ logii(`failed`)
}
},
vassal(vassal) {
push_undo()
+ logi(vassal_names[vassal])
--game.count
muster_vassal(game.who, vassal)
},
ship() {
push_undo()
+ logi("Ship")
--game.count
add_lord_assets(game.who, SHIP, 1)
},
boat() {
push_undo()
+ logi("Boat")
--game.count
add_lord_assets(game.who, BOAT, 1)
},
cart() {
push_undo()
+ logi("Cart")
--game.count
add_lord_assets(game.who, CART, 1)
},
sled() {
push_undo()
+ logi("Sled")
--game.count
add_lord_assets(game.who, SLED, 1)
},
@@ -757,7 +958,7 @@ states.muster_lord_at_seat = {
},
locale(loc) {
push_undo()
- logi(`Mustered at %${loc}.`)
+ logii(`at %${loc}`)
set_lord_moved(game.who, 1)
muster_lord(game.who, loc)
game.state = 'muster_lord_transport'
@@ -783,24 +984,28 @@ states.muster_lord_transport = {
},
ship() {
push_undo()
+ logii("Ship")
add_lord_assets(game.who, SHIP, 1)
if (--game.count === 0)
pop_state()
},
boat() {
push_undo()
+ logii("Boat")
add_lord_assets(game.who, BOAT, 1)
if (--game.count === 0)
pop_state()
},
cart() {
push_undo()
+ logii("Cart")
add_lord_assets(game.who, CART, 1)
if (--game.count === 0)
pop_state()
},
sled() {
push_undo()
+ logii("Sled")
add_lord_assets(game.who, SLED, 1)
if (--game.count === 0)
pop_state()
@@ -819,6 +1024,7 @@ states.muster_capability = {
},
arts_of_war(c) {
push_undo()
+ logi(`Capability #${c}`)
if (!data.cards[c].lords) {
set_add(game.global_cards, c)
} else {
@@ -843,17 +1049,224 @@ function goto_levy_call_to_arms() {
end_levy_call_to_arms()
}
-function end_levy_call_to_arms() {
- goto_campaign_plan()
+states.levy_call_to_arms = {
}
-states.levy_call_to_arms = {
+function end_levy_call_to_arms() {
+ goto_campaign_plan()
}
-// === CAMPAIGN ===
+// === CAMPAIGN: PLAN ===
function goto_campaign_plan() {
+ game.active = BOTH
game.state = 'campaign_plan'
+ game.p1_plan = []
+ game.p2_plan = []
+}
+
+states.campaign_plan = {
+}
+
+function end_campaign_plan() {
+ set_active(P1)
+ goto_command_activation()
+}
+
+// === 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) {
+ game.command = NOBODY
+ goto_end_campaign()
+ } else {
+ game.who = game.command
+ goto_actions()
+ }
+}
+
+// === CAMPAIGN: ACTIONS ===
+
+function goto_actions() {
+ game.state = 'actions'
+ game.count = data.lords[game.command].command
+}
+
+function end_actions() {
+ set_active(P1)
+ goto_feed()
+}
+
+states.actions = {
+ prompt() {
+ view.prompt = `${lord_name[game.who]} has ${game.count}x actions.`
+ view.actions.end_actions = 1
+ },
+}
+
+// === CAMPAIGN: FEED ===
+
+function has_friendly_lord_who_moved_or_fought() {
+ for (let lord of game.moved)
+ if (is_friendly_lord(lord))
+ return true
+ return false
+}
+
+function goto_feed() {
+ game.state = 'feed'
+ if (!has_friendly_lord_who_moved_or_fought())
+ end_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))
+ gen_action_lord(lord)
+ view.actions.end_feed = 1
+ },
+ lord(lord) {
+ push_undo()
+ game.who = lord
+ game.count = (count_lord_forces(lord) / 6 | 0) + 1
+ game.state = 'feed_lord'
+ },
+ end_feed() {
+ clear_undo()
+ end_feed()
+ },
+}
+
+states.feed_lord = {
+ prompt() {
+ view.prompt = "You must Feed ${lord_name[game.who]} ${game.count}x Loot or Provender."
+ // TODO: find loot or prov!
+ view.actions.unfed = 1
+ },
+ loot(lord) {
+ logi(`Fed ${lord_name[game.who]} with Loot from ${lord_name[lord]}.`)
+ add_lord_assets(lord, LOOT, -1)
+ if (--game.count === 0)
+ game.state = 'feed'
+ },
+ prov(lord) {
+ logi(`Fed ${lord_name[game.who]} with Provender from ${lord_name[lord]}.`)
+ add_lord_assets(lord, PROV, -1)
+ if (--game.count === 0)
+ game.state = 'feed'
+ },
+ unfed() {
+ logi(`Did not feed ${lord_name[game.who]}.`)
+ add_lord_service(game.who, -1)
+ game.state = 'feed'
+ },
+}
+
+function end_feed() {
+ set_active_enemy()
+ if (game.active === P1)
+ goto_pay()
+}
+
+// === LEVY & CAMPAIGN: PAY ===
+
+function goto_pay() {
+ game.state = 'pay'
+ if (TODO)
+ end_pay()
+}
+
+states.pay = {
+ prompt() {
+ view.prompt = "You may Pay your Lords."
+ for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord)
+ if (is_lord_on_map(lord))
+ gen_action_lord(lord)
+ view.actions.end_pay = 1
+ },
+ lord(lord) {
+ push_undo()
+ push_state('pay_lord')
+ game.who = lord
+ },
+ end_pay() {
+ end_pay()
+ },
+}
+
+states.pay_lord = {
+ prompt() {
+ view.prompt = `You may Pay ${lord_name[game.who]} with Coin or Loot.`
+ if (game.active === RUSSIANS) {
+ if (game.veche_coin > 0)
+ view.actions.veche_coin = 1
+ }
+ },
+ loot(lord) {
+ logi(`Paid ${lord_name[game.who]} with Loot from ${lord_name[lord]}.`)
+ add_lord_assets(lord, LOOT, -1)
+ add_lord_service(game.who, 1)
+ pop_state()
+ },
+ coin(lord) {
+ logi(`Paid ${lord_name[game.who]} with Coin from ${lord_name[lord]}.`)
+ add_lord_assets(lord, COIN, -1)
+ add_lord_service(game.who, 1)
+ pop_state()
+ },
+ veche_coin() {
+ logi(`Paid ${lord_name[game.who]} with Coin from Veche.`)
+ game.veche_coin --
+ add_lord_service(game.who, 1)
+ pop_state()
+ }
+}
+
+function end_pay() {
+ set_active_enemy()
+ if (game.active === P1)
+ goto_disband()
+}
+
+// === LEVY & CAMPAIGN: DISBAND ===
+
+function goto_disband() {
+ game.state = 'disband'
+ // TODO
+}
+
+states.disband = {
+ prompt() {
+ view.prompt = "You must Disband Lords at their Service limit."
+ view.actions.end_disband = 1
+ },
+ end_disband() {
+ end_disband()
+ },
+}
+
+function end_disband() {
+ set_active_enemy()
+ if (game.active === P1) {
+ if (is_levy_phase())
+ goto_levy_muster()
+ else
+ goto_remove_markers()
+ }
+}
+
+// === CAMPAIGN: REMOVE MARKERS ===
+
+function goto_remove_markers() {
+ game.lords.moved = 0
+ set_active_enemy()
+ goto_command_activation()
}
// === GAME OVER ===
@@ -905,6 +1318,10 @@ function logi(msg) {
game.log.push(">" + msg)
}
+function logii(msg) {
+ game.log.push(">>" + msg)
+}
+
function log_h1(msg) {
log_br()
log(".h1 " + msg)
@@ -920,7 +1337,6 @@ function log_h2(msg) {
function log_h3(msg) {
log_br()
log(".h3 " + msg)
- log_br()
}
function log_h4(msg) {
@@ -956,8 +1372,11 @@ function gen_action_arts_of_war(c) {
}
exports.view = function(state, current) {
- game = state
+ load_state(state)
+
view = {
+ prompt: null,
+ actions: null,
log: game.log,
turn: game.turn,
lords: game.lords,
@@ -968,18 +1387,21 @@ exports.view = function(state, current) {
global_cards: game.global_cards,
conquered: game.conquered,
ravaged: game.ravaged,
+ castles: game.castles,
+ command: game.command,
who: game.who,
where: game.where,
}
+
if (game.state === 'game_over') {
view.prompt = game.victory
- } else if (current === 'Observer' || game.active !== current) {
+ } else if (current === 'Observer' || (current !== game.active && current !== BOTH)) {
let inactive = states[game.state].inactive || game.state
view.prompt = `Waiting for ${game.active} \u2014 ${inactive}...`
} else {
view.actions = {}
if (states[game.state])
- states[game.state].prompt()
+ states[game.state].prompt(current)
else
view.prompt = "Unknown state: " + game.state
if (view.actions.undo === undefined) {
@@ -997,7 +1419,7 @@ exports.action = function (state, current, action, arg) {
Object.seal(game) // XXX: don't allow adding properties
let S = states[game.state]
if (S && action in S) {
- S[action](arg)
+ S[action](arg, current)
} else {
if (action === 'undo' && game.undo && game.undo.length > 0)
pop_undo()
diff --git a/tools/gendata.js b/tools/gendata.js
index d72e43b..54da50a 100644
--- a/tools/gendata.js
+++ b/tools/gendata.js
@@ -94,18 +94,18 @@ var trackways = []
const scale = 1
const vp_map = {
- archbishopric: 3,
+ novgorod: 3,
city: 2,
fort: 1,
bishopric: 2,
castle: 1,
traderoute: 1,
- town: 0.5,
- region: 0.5,
+ town: 0,
+ region: 0,
}
const wall_map = {
- archbishopric: 3,
+ novgorod: 3,
city: 3,
fort: 3,
traderoute: 0,
@@ -115,6 +115,9 @@ const wall_map = {
region: 0,
}
+let conquerable = []
+let strongholds = []
+
function defloc(region, stronghold, type, name) {
let [x, y, w, h] = boxes[name]
x = Math.round(x * scale)
@@ -124,6 +127,10 @@ function defloc(region, stronghold, type, name) {
locmap[name] = locales.length
let vp = vp_map[type]
let walls = wall_map[type]
+ if (vp > 0)
+ conquerable.push(locales.length)
+ if (stronghold > 0)
+ strongholds.push(locales.length)
locales.push({ name, type, stronghold, walls, vp, region, ways: [], box: { x, y, w, h } })
}
@@ -174,7 +181,7 @@ defloc("Crusader Livonia", 0, "region", "Tolowa")
defloc("Crusader Livonia", 0, "region", "Ugaunia")
defloc("Crusader Livonia", 0, "region", "Waiga")
-defloc("Novgorodan Rus", 3, "archbishopric", "Novgorod")
+defloc("Novgorodan Rus", 3, "novgorod", "Novgorod")
defloc("Novgorodan Rus", 3, "city", "Ladoga")
defloc("Novgorodan Rus", 3, "city", "Pskov")
defloc("Novgorodan Rus", 3, "city", "Rusa")
@@ -622,9 +629,9 @@ arts_of_war_event("T15", "Mindaugas")
arts_of_war_event("T16", "Famine")
arts_of_war_event("T17", "Dietrich von Grüningen")
arts_of_war_event("T18", "Swedish Crusade")
-arts_of_war_event("TNo", "No Event")
-arts_of_war_event("TNo", "No Event")
-arts_of_war_event("TNo", "No Event")
+arts_of_war_event("T0", "No Event")
+arts_of_war_event("T0", "No Event")
+arts_of_war_event("T0", "No Event")
arts_of_war_capability("T1", "Treaty of Stensby", [ "Heinrich", "Knud & Abel" ])
arts_of_war_capability("T2", "Raiders", "any")
@@ -663,9 +670,9 @@ arts_of_war_event("R15", "Death of the Pope")
arts_of_war_event("R16", "Tempest")
arts_of_war_event("R17", "Dietrich von Grüningen")
arts_of_war_event("R18", "Bountiful Harvest")
-arts_of_war_event("RNo", "No Event")
-arts_of_war_event("RNo", "No Event")
-arts_of_war_event("RNo", "No Event")
+arts_of_war_event("R0", "No Event")
+arts_of_war_event("R0", "No Event")
+arts_of_war_event("R0", "No Event")
arts_of_war_capability("R1", "Luchniki", [ "Vladislav", "Karelians", "Gavrilo", "Domash" ])
arts_of_war_capability("R2", "Luchniki", [ "Vladislav", "Karelians", "Gavrilo", "Domash" ])
@@ -803,6 +810,8 @@ script.push("montage -mode concatenate -tile 2x " + vassal_service.Russian.join(
print("const data = {")
print("seaports:" + JSON.stringify(seaports) + ",")
+print("conquerable:" + JSON.stringify(conquerable) + ",")
+print("strongholds:" + JSON.stringify(strongholds) + ",")
dumplist("locales", locales)
dumplist("ways", ways)
dumplist("lords", lords)