From aac59e22580aae1bfdfc7c1712599a06b47fd34b Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 14 Nov 2022 01:00:21 +0100 Subject: More rules. --- data.js | 78 ++++---- play.html | 54 +++--- play.js | 116 +++++++++-- rules.js | 572 +++++++++++++++++++++++++++++++++++++++++++++++-------- tools/gendata.js | 31 +-- 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; } + - + + +
@@ -947,7 +953,7 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; }
-
-
+
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) -- cgit v1.2.3