From 097fb37a0f8cdc02d34bfa51760300b1177f9ea1 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 22 Apr 2022 20:52:42 +0200 Subject: Client. --- create.html | 4 + play.css | 859 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ play.html | 437 ++++++++++++++++++++++++++++++ play.js | 885 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2185 insertions(+) create mode 100644 play.css create mode 100644 play.html create mode 100644 play.js diff --git a/create.html b/create.html index e69de29..16a9da7 100644 --- a/create.html +++ b/create.html @@ -0,0 +1,4 @@ +

+ diff --git a/play.css b/play.css new file mode 100644 index 0000000..f301ab8 --- /dev/null +++ b/play.css @@ -0,0 +1,859 @@ +main { background-color: slategray; } +header { background-color: silver; } +#role_Gray { background-color: #b7b2b0; } +#role_Blue { background-color: #95b4ca; } +#role_Tan { background-color: #e7cea7; } +#role_Red { background-color: #d18e95; } +#role_Black { background-color: #7b7979; } + +#log { background-color: ivory; } +#log div { padding-left: 20px; text-indent: -12px; } +#log div.i { padding-left: 32px; text-indent: -12px; font-style: italic; } +#log .turn, #log .dc { font-style: italic; text-align: right; margin: 0; text-indent: 0; padding: 4px 8px; } +#log .turn.Gray { background-color: #b7b2b0; } +#log .turn.Blue { background-color: #95b4ca; } +#log .turn.Tan { background-color: #e7cea7; } +#log .turn.Red { background-color: #d18e95; } +#log .turn.Black { background-color: #7b7979; } +#log .dc.unsuccessful { background-color: #856781; color: lavenderblush; } +#log .dc.Afghan { background-color: #5bbc93; } +#log .dc.British { background-color: #e2a6ca; } +#log .dc.Russian { background-color: #fff69a; } +#log .tip { color: blue; } +#log .tip:hover { text-decoration: underline; cursor: pointer; } + +main { + position: relative; + scrollbar-width: auto; +} + +.action { + cursor: pointer; +} + +#tooltip { + pointer-events: none; + position: fixed; + z-index: 100; + right: 240px; + top: 60px; + box-shadow: 0 0 20px black; +} + +aside.hide + #tooltip { + right: 30px +} + +#tooltip.focus { display: none; } +body.shift #tooltip.focus { display: block; } + +#deck_info { + position: absolute; + right: 8px; + bottom: 5px; + white-space: pre-line; + font-family: "Source Serif SmText", "Georgia", serif; + font-size: 12px; + font-style: italic; +} + +#popup { + position: fixed; + user-select: none; + background-color: white; + left: 10px; + top: 100px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.3); + z-index: 300; + min-width: 20ex; + white-space: nowrap; + display: none; + border: 1px solid black; +} +#popup div { padding: 3pt 8pt; color: gray; display: none; } +#popup div.enabled { color: black; display: block; } +#popup div.enabled:hover { background-color: black; color: white; } +#popup div.always { display: block; } +#popup #menu_label { border-bottom: 1px solid silver; } + +#banner { + cursor: pointer; + display: flex; + justify-content: space-between; + user-select: none; + border-bottom: 1px solid black; + background-color: #856781; + height: 40px; +} + +#banner > div { + flex-grow: 1; +} + +.icon { + background-repeat: no-repeat; + background-size: auto 25px; + background-position: center; + filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3)); +} + +#favored_suit_banner.Political { background-image: url(icons/suit_political.svg) } +#favored_suit_banner.Intelligence { background-image: url(icons/suit_intelligence.svg) } +#favored_suit_banner.Economic { background-image: url(icons/suit_economic.svg) } +#favored_suit_banner.Military { background-image: url(icons/suit_military.svg) } + +#map_banner { background-image: url(icons/treasure-map.svg) } +#hand_banner { background-image: url(icons/hand.svg) } + +.role { + position: relative; +} + +.role_name { + cursor: pointer; +} + +.role_rupees_icon, +.role_rupees_text, +.role_loyalty_icon, +.role_loyalty_text, +.role_cylinders_icon, +.role_cylinders_text { + width: 24px; + height: 24px; + position: absolute; + cursor: pointer; + top: 2px; + text-align: right; + background-repeat: no-repeat; +} + +.role_rupees_text, +.role_loyalty_text, +.role_cylinders_text { + opacity: 0.8; +} + +.role_loyalty_icon { + background-position: 0 -1px; + background-size: 24px 24px; + opacity: 0.4; +} + +.role_cylinders_icon { + width: 16px; + background-position: 0 3px; + background-size: 16px 16px; + background-image: url(icons/cylinder.svg); + opacity: 0.3; +} + +.role_rupees_icon { + width: 17px; + background-position: 0 3px; + background-size: 17px 17px; + background-image: url(icons/rupee.svg); + opacity: 0.4; +} + +.role_loyalty_icon.Afghan { background-image: url(icons/eagle.svg) } +.role_loyalty_icon.British { background-image: url(icons/lion.svg) } +.role_loyalty_icon.Russian { background-image: url(icons/bear.svg) } + +.role_loyalty_icon { right: 5px; } +.role_loyalty_text { right: 32px; } +.role_cylinders_icon { right: 55px; } +.role_cylinders_text { right: 74px; } +.role_rupees_icon { right: 100px; } +.role_rupees_text { right: 119px; } + +/* CARDS */ + +.card { + position: relative; + display: block; + width: 186px; + height: 258px; + border-radius: 10px; + background-color: #fefcf0; + background-size: cover; + background-repeat: no-repeat; + border: 1px solid #4d452b; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); +} + +#tooltip.card { + width: 372px; + height: 516px; + border-radius: 20px; +} + +.card.action { + box-shadow: 0 0 0 3px yellow; +} + +.card .spyrow { + position: absolute; + display: flex; + flex-wrap: wrap; + width: 168px; + bottom: 108px; + right: 8px; + gap: 4px; + justify-content: end; +} + +/* if discarded */ +#global_events .card.event, +.hand .card.event { + height: 178px; + border-radius: 10px 10px 0 0; +} + +/* if played */ +#global_events .card_109.event, +#global_events .card_115.event, +.player_court .card.event { + margin-top: 71px; + background-position: bottom; + height: 187px; + border-radius: 0 0 10px 10px; +} + +#global_events .card_109.event, +#global_events .card_115.event { + margin-top: 0; +} + +.card .card_action { + display: none; + position: absolute; + width: 36px; + height: 37px; + border: 3px solid transparent; + top: 190px; + border-radius: 8px; +} + +.card .card_action.action { display: block; } + +.card .card_action.n1 { left: 133px; } +.card .card_action.n2 { left: 85px; } + +.card.passive .card_action.n1 { left: 128px; } + +.card.three .card_action.n1 { left: 119px; } +.card.three .card_action.n2 { left: 72px; } +.card.three .card_action.n3 { left: 25px; } + +.card.card_61 .card_action.n1 { left: 72px; } +.card.card_64 .card_action.n1 { left: 72px; } +.card.card_84 .card_action.n1 { left: 72px; } +.card.card_69 .card_action.n1 { left: 96px; } +.card.card_69 .card_action.n2 { left: 48px; } +.card.card_56 .card_action.n1 { top: 192px; } +.card.card_41 .card_action.n1 { top: 198px; } +.card.card_21 .card_action.n1 { top: 194px; } + +.card.Political .card_action { border-color: #8d198f } +.card.Intelligence .card_action { border-color: #3871c1 } +.card.Economic .card_action { border-color: #cd700f } +.card.Military .card_action { border-color: #bf1b2c } + +/* +.card.Political .card_action { box-shadow: 0 0 3px #8d198f } +.card.Intelligence .card_action { box-shadow: 0 0 3px #8d198f } +.card.Economic .card_action { box-shadow: 0 0 3px #8d198f } +.card.Military .card_action { box-shadow: 0 0 3px #8d198f } +*/ + +/* PIECES */ + +#board div { + transition: top 500ms, left 500ms; +} + +.coin { + position: absolute; + pointer-events: none; + width: 50px; + height: 50px; + background-size: contain; + background-repeat: no-repeat; + background-image: url(pieces/rupee.svg); + color: ivory; + font-weight: bold; + font-size: 32px; + line-height: 50px; + text-align: center; + text-shadow: 0 0 8px black; + user-select: none; + filter: drop-shadow(0 2px 3px rgba(0,0,0,0.5)); +} + +.cylinder { + width: 30px; + height: 30px; + background-size: contain; + background-repeat: no-repeat; + filter: drop-shadow(0 2px 3px rgba(0,0,0,0.5)); +} + +#board .cylinder { + position: absolute; +} + +.cylinder.p0 { background-image: url(pieces/cylinder_gray.svg) } +.cylinder.p1 { background-image: url(pieces/cylinder_blue.svg) } +.cylinder.p2 { background-image: url(pieces/cylinder_tan.svg) } +.cylinder.p3 { background-image: url(pieces/cylinder_red.svg) } +.cylinder.p4 { background-image: url(pieces/cylinder_black.svg) } + +.block { + position: absolute; + width: 35px; + height: 45px; + background-size: contain; + background-repeat: no-repeat; + filter: drop-shadow(0 2px 3px rgba(0,0,0,0.5)); +} + +.block.road { + position: absolute; + width: 50px; + height: 30px; +} + +.block.Afghan { background-image: url(pieces/block_afghan.svg) } +.block.British { background-image: url(pieces/block_british.svg) } +.block.Russian { background-image: url(pieces/block_russian.svg) } +.block.army.Afghan { background-image: url(pieces/army_afghan.svg) } +.block.army.British { background-image: url(pieces/army_british.svg) } +.block.army.Russian { background-image: url(pieces/army_russian.svg) } +.block.road.Afghan { background-image: url(pieces/road_afghan.svg) } +.block.road.British { background-image: url(pieces/road_british.svg) } +.block.road.Russian { background-image: url(pieces/road_russian.svg) } + +.Afghan.block.action { + filter: + drop-shadow(0 -2px 0 turquoise) + drop-shadow(0 2px 0 turquoise) + drop-shadow(-2px 0 0 turquoise) + drop-shadow(2px 0 0 turquoise) +} +.British.block.action { + filter: + drop-shadow(0 -2px 0 hotpink) + drop-shadow(0 2px 0 hotpink) + drop-shadow(-2px 0 0 hotpink) + drop-shadow(2px 0 0 hotpink) +} +.Russian.block.action { + filter: + drop-shadow(0 -2px 0 orange) + drop-shadow(0 2px 0 orange) + drop-shadow(-2px 0 0 orange) + drop-shadow(2px 0 0 orange) +} + +.cylinder.action { + filter: + drop-shadow(0 -2px 0 yellow) + drop-shadow(0 2px 0 yellow) + drop-shadow(-2px 0 0 yellow) + drop-shadow(2px 0 0 yellow) +} + +.block.selected { + filter: + drop-shadow(0 -2px 0 deepskyblue) + drop-shadow(0 2px 0 deepskyblue) + drop-shadow(-2px 0 0 deepskyblue) + drop-shadow(2px 0 0 deepskyblue) +} + +.cylinder.selected { + filter: + drop-shadow(0 -2px 0 deepskyblue) + drop-shadow(0 2px 0 deepskyblue) + drop-shadow(-2px 0 0 deepskyblue) + drop-shadow(2px 0 0 deepskyblue) +} + +/* FAVORED SUIT MARKER AND SPACES */ + +#favored_suit_marker { + position: absolute; + width: 32px; + height: 70px; + background-image: url(pieces/favored_suit_marker.svg); + background-size: contain; + background-repeat: no-repeat; + left: 42px; + filter: drop-shadow(0 2px 3px rgba(0,0,0,0.5)); + transition: 500ms; + transform-origin: bottom center; +} + +#favored_suit_marker.Political { top: 112px; } +#favored_suit_marker.Intelligence { top: 200px; } +#favored_suit_marker.Economic { top: 289px; } +#favored_suit_marker.Military { top: 378px; } + +.suit { + position: absolute; + border: 3px solid transparent; + border-radius: 50%; + left: 31px; + width: 48px; + height: 48px; +} + +#suit_political { top: 129px; } +#suit_intelligence { top: 216px; } +#suit_economic { top: 305px; } +#suit_military { top: 394px; } + +#suit_political.action { border-color: orchid; } +#suit_intelligence.action { border-color: deepskyblue; } +#suit_economic.action { border-color: orange; } +#suit_military.action { border-color: orangered; } + +/* MARKET BOARD */ + +#market { + width: 1280px; + height: 630px; + background-color: #e7cea7; + background-repeat: no-repeat; + background-size: cover; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); + margin: 0px auto 10px auto; +} + +#market_a { + /* TRBL */ + padding: 74px 0 0 26px ; + display: flex; + gap: 20px; +} + +#market_b { + padding: 16px 0 0 26px ; + display: flex; + gap: 20px; +} + +.market_slot { + position: relative; + width: 188px; + height: 260px; +} + +#market .coin { + top: 70px; + left: -8px; +} + +/* MAP BOARD */ + +#board { + position: relative; + width: 1280px; + height: 630px; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); + margin: 0px auto 0px auto; + background-color: ivory; + background-repeat: no-repeat; + background-size: cover; +} + +#board svg { + position: absolute; +} + +.border, .region { + fill: none; + stroke: none; +} + +.region.action { + fill: transparent; + stroke: #66a2b4; + stroke-width: 5px; + opacity: 0.7; +} + +.border.action { + fill: transparent; + stroke: #b88a40; + stroke-width: 5px; + opacity: 0.7; +} + +.rule { + position: absolute; + background-repeat: no-repeat; + background-size: 100%; + background-position: center; + background-color: #bf935b; + border: 1px solid #4d452b; + border-radius: 50%; + width: 50px; + height: 50px; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); + margin: 0 auto 8px auto; +} + +.rule.Transcaspia { left:229px; top:127px; } +.rule.Punjab { left:904px; top:281px; } +.rule.Persia { left:181px; top:400px; } +.rule.Kandahar { left:707px; top:412px; } +.rule.Kabul { left:648px; top:138px; } +.rule.Herat { left:431px; top:358px; } + +.rule.Persia { background-image: url(pieces/ruler_persia.svg) } +.rule.Transcaspia { background-image: url(pieces/ruler_transcaspia.svg) } +.rule.Herat { background-image: url(pieces/ruler_herat.svg) } +.rule.Kabul { background-image: url(pieces/ruler_kabul.svg) } +.rule.Kandahar { background-image: url(pieces/ruler_kandahar.svg) } +.rule.Punjab { background-image: url(pieces/ruler_punjab.svg) } + +.rule.Gray { background-color: #b7b2b0; } +.rule.Blue { background-color: #95b4ca; } +.rule.Tan { background-color: #e7cea7; } +.rule.Red { background-color: #d18e95; } +.rule.Black { background-color: #7b7979; } + +/* PLAYER AREAS */ + +#global_events { + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + padding: 0 25px; + gap: 15px; + margin: 15px auto; + max-width: min(calc(100% - 30px), 1260px); +} + +.hand { + display: flex; + flex-wrap: wrap; + box-sizing: border-box; + justify-content: start; + padding: 15px; + margin: 15px auto 0 auto; + gap: 15px; + min-height: 260px; + max-width: min(calc(100% - 20px), 1260px); +} + +.hand.hide { + display: none; +} + +.player_area { + box-sizing: border-box; + margin: 15px auto; + min-width: min(100%, 1280px); + width: fit-content; + max-width: 100%; +} + +.player_court { + display: flex; + padding: 10px 15px; + min-height: 283px; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); + justify-content: start; + flex-wrap: wrap; + gap: 15px; + background-repeat: no-repeat; + background-size: 100%; + background-position: center bottom; +} + +#player_court_0 { background-color: #b7b2b0; background-image: url(backgrounds/mountains_gray.jpg) } +#player_court_1 { background-color: #95b4ca; background-image: url(backgrounds/mountains_blue.jpg) } +#player_court_2 { background-color: #e7cea7; background-image: url(backgrounds/mountains_tan.jpg) } +#player_court_3 { background-color: #d18e95; background-image: url(backgrounds/mountains_red.jpg) } +#player_court_4 { background-color: #7b7979; background-image: url(backgrounds/mountains_black.jpg) } + +#player_hand_0 { background-image: linear-gradient(transparent, #b7b2b080) } +#player_hand_1 { background-image: linear-gradient(transparent, #95b4ca80) } +#player_hand_2 { background-image: linear-gradient(transparent, #e7cea780) } +#player_hand_3 { background-image: linear-gradient(transparent, #d18e9580) } +#player_hand_4 { background-image: linear-gradient(transparent, #7b797980) } + +.player_pool { + display: inline-flex; + justify-content: center; + align-content: start; + flex-wrap: wrap; + gap: 7px; + width: 209px; +} + +.player_dial { + position: relative; + width: 207px; + height: 207px; + border: 1px solid #4d452b; + border-radius: 50%; + background-size: 100%; + background-repeat: no-repeat; + box-shadow: 0px 2px 3px 1px rgba(0,0,0,0.5); +} + +.player_dial .coin { + top: 19px; + right: 0px; +} + +.player_hand_size { + position: absolute; + user-select: none; + top: 19px; + left: -3px; + width: 36px; + height: 50px; + background-repeat: no-repeat; + background-size: 100%; + background-color: #fefcf0; + background-image: url(icons/card_back.png); + color: ivory; + border: 1px solid #4d452b; + border-radius: 3px; + font-size: 32px; + line-height: 47px; + font-weight: bold; + text-align: center; + text-shadow: 0 0 8px black; + text-align: center; + box-shadow: 0 2px 3px rgba(0,0,0,0.5); +} + +.player_dial .prize { + position: absolute; + user-select: none; + top: 94px; + left: 135px; + height: 26px; + font-family: "Source Serif"; + font-size: 16px; + line-height: 25px; + color: black; + font-weight: bold; +} + +.gift_2, .gift_4, .gift_6 { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + width: 42px; + height: 42px; + border: 3px solid transparent; + border-radius: 50%; +} +.gift_2.action, .gift_4.action, .gift_6.action { + border-color: white; +} +.gift_2 { top: 75px; left: 8px; } +.gift_4 { top: 120px; left: 21px; } +.gift_6 { top: 147px; left: 60px; } + +/* IMAGES */ + +#market { background-image: url(market.1x.jpg) } +#board { background-image: url(board_fg.svg), url(board_bg.1x.jpg) } + +.player_dial.p0 { background-image: url(dials/loyalty_gray.1x.opt.png) } +.player_dial.p1 { background-image: url(dials/loyalty_blue.1x.opt.png) } +.player_dial.p2 { background-image: url(dials/loyalty_tan.1x.opt.png) } +.player_dial.p3 { background-image: url(dials/loyalty_red.1x.opt.png) } +.player_dial.p4 { background-image: url(dials/loyalty_black.1x.opt.png) } +.player_dial.Afghan.p0 { background-image: url(dials/loyalty_gray.1x.opt.png), url(dials/loyalty_afghan.1x.opt.png) } +.player_dial.Afghan.p1 { background-image: url(dials/loyalty_blue.1x.opt.png), url(dials/loyalty_afghan.1x.opt.png) } +.player_dial.Afghan.p2 { background-image: url(dials/loyalty_tan.1x.opt.png), url(dials/loyalty_afghan.1x.opt.png) } +.player_dial.Afghan.p3 { background-image: url(dials/loyalty_red.1x.opt.png), url(dials/loyalty_afghan.1x.opt.png) } +.player_dial.Afghan.p4 { background-image: url(dials/loyalty_black.1x.opt.png), url(dials/loyalty_afghan.1x.opt.png) } +.player_dial.British.p0 { background-image: url(dials/loyalty_gray.1x.opt.png), url(dials/loyalty_british.1x.opt.png) } +.player_dial.British.p1 { background-image: url(dials/loyalty_blue.1x.opt.png), url(dials/loyalty_british.1x.opt.png) } +.player_dial.British.p2 { background-image: url(dials/loyalty_tan.1x.opt.png), url(dials/loyalty_british.1x.opt.png) } +.player_dial.British.p3 { background-image: url(dials/loyalty_red.1x.opt.png), url(dials/loyalty_british.1x.opt.png) } +.player_dial.British.p4 { background-image: url(dials/loyalty_black.1x.opt.png), url(dials/loyalty_british.1x.opt.png) } +.player_dial.Russian.p0 { background-image: url(dials/loyalty_gray.1x.opt.png), url(dials/loyalty_russian.1x.opt.png) } +.player_dial.Russian.p1 { background-image: url(dials/loyalty_blue.1x.opt.png), url(dials/loyalty_russian.1x.opt.png) } +.player_dial.Russian.p2 { background-image: url(dials/loyalty_tan.1x.opt.png), url(dials/loyalty_russian.1x.opt.png) } +.player_dial.Russian.p3 { background-image: url(dials/loyalty_red.1x.opt.png), url(dials/loyalty_russian.1x.opt.png) } +.player_dial.Russian.p4 { background-image: url(dials/loyalty_black.1x.opt.png), url(dials/loyalty_russian.1x.opt.png) } + +@media (min-resolution: 97dpi) { + #market { background-image: url(market.2x.jpg) } + #board { background-image: url(board_fg.svg), url(board_bg.2x.jpg) } + + .player_dial.p0 { background-image: url(dials/loyalty_gray.2x.opt.png) } + .player_dial.p1 { background-image: url(dials/loyalty_blue.2x.opt.png) } + .player_dial.p2 { background-image: url(dials/loyalty_tan.2x.opt.png) } + .player_dial.p3 { background-image: url(dials/loyalty_red.2x.opt.png) } + .player_dial.p4 { background-image: url(dials/loyalty_black.2x.opt.png) } + .player_dial.Afghan.p0 { background-image: url(dials/loyalty_gray.2x.opt.png), url(dials/loyalty_afghan.2x.opt.png) } + .player_dial.Afghan.p1 { background-image: url(dials/loyalty_blue.2x.opt.png), url(dials/loyalty_afghan.2x.opt.png) } + .player_dial.Afghan.p2 { background-image: url(dials/loyalty_tan.2x.opt.png), url(dials/loyalty_afghan.2x.opt.png) } + .player_dial.Afghan.p3 { background-image: url(dials/loyalty_red.2x.opt.png), url(dials/loyalty_afghan.2x.opt.png) } + .player_dial.Afghan.p4 { background-image: url(dials/loyalty_black.2x.opt.png), url(dials/loyalty_afghan.2x.opt.png) } + .player_dial.British.p0 { background-image: url(dials/loyalty_gray.2x.opt.png), url(dials/loyalty_british.2x.opt.png) } + .player_dial.British.p1 { background-image: url(dials/loyalty_blue.2x.opt.png), url(dials/loyalty_british.2x.opt.png) } + .player_dial.British.p2 { background-image: url(dials/loyalty_tan.2x.opt.pn.opt.png url(dials/loyalty_british.2x.opt.png) } + .player_dial.British.p3 { background-image: url(dials/loyalty_red.2x.opt.png), url(dials/loyalty_british.2x.opt.png) } + .player_dial.British.p4 { background-image: url(dials/loyalty_black.2x.opt.png), url(dials/loyalty_british.2x.opt.png) } + .player_dial.Russian.p0 { background-image: url(dials/loyalty_gray.2x.opt.png), url(dials/loyalty_russian.2x.opt.png) } + .player_dial.Russian.p1 { background-image: url(dials/loyalty_blue.2x.opt.png), url(dials/loyalty_russian.2x.opt.png) } + .player_dial.Russian.p2 { background-image: url(dials/loyalty_tan.2x.opt.png), url(dials/loyalty_russian.2x.opt.png) } + .player_dial.Russian.p3 { background-image: url(dials/loyalty_red.2x.opt.png), url(dials/loyalty_russian.2x.opt.png) } + .player_dial.Russian.p4 { background-image: url(dials/loyalty_black.2x.opt.png), url(dials/loyalty_russian.2x.opt.png) } +} + +/* CARD IMAGES */ + +.card_1{background-image:url(cards/card_1.jpg)} +.card_2{background-image:url(cards/card_2.jpg)} +.card_3{background-image:url(cards/card_3.jpg)} +.card_4{background-image:url(cards/card_4.jpg)} +.card_5{background-image:url(cards/card_5.jpg)} +.card_6{background-image:url(cards/card_6.jpg)} +.card_7{background-image:url(cards/card_7.jpg)} +.card_8{background-image:url(cards/card_8.jpg)} +.card_9{background-image:url(cards/card_9.jpg)} +.card_10{background-image:url(cards/card_10.jpg)} +.card_11{background-image:url(cards/card_11.jpg)} +.card_12{background-image:url(cards/card_12.jpg)} +.card_13{background-image:url(cards/card_13.jpg)} +.card_14{background-image:url(cards/card_14.jpg)} +.card_15{background-image:url(cards/card_15.jpg)} +.card_16{background-image:url(cards/card_16.jpg)} +.card_17{background-image:url(cards/card_17.jpg)} +.card_18{background-image:url(cards/card_18.jpg)} +.card_19{background-image:url(cards/card_19.jpg)} +.card_20{background-image:url(cards/card_20.jpg)} +.card_21{background-image:url(cards/card_21.jpg)} +.card_22{background-image:url(cards/card_22.jpg)} +.card_23{background-image:url(cards/card_23.jpg)} +.card_24{background-image:url(cards/card_24.jpg)} +.card_25{background-image:url(cards/card_25.jpg)} +.card_26{background-image:url(cards/card_26.jpg)} +.card_27{background-image:url(cards/card_27.jpg)} +.card_28{background-image:url(cards/card_28.jpg)} +.card_29{background-image:url(cards/card_29.jpg)} +.card_30{background-image:url(cards/card_30.jpg)} +.card_31{background-image:url(cards/card_31.jpg)} +.card_32{background-image:url(cards/card_32.jpg)} +.card_33{background-image:url(cards/card_33.jpg)} +.card_34{background-image:url(cards/card_34.jpg)} +.card_35{background-image:url(cards/card_35.jpg)} +.card_36{background-image:url(cards/card_36.jpg)} +.card_37{background-image:url(cards/card_37.jpg)} +.card_38{background-image:url(cards/card_38.jpg)} +.card_39{background-image:url(cards/card_39.jpg)} +.card_40{background-image:url(cards/card_40.jpg)} +.card_41{background-image:url(cards/card_41.jpg)} +.card_42{background-image:url(cards/card_42.jpg)} +.card_43{background-image:url(cards/card_43.jpg)} +.card_44{background-image:url(cards/card_44.jpg)} +.card_45{background-image:url(cards/card_45.jpg)} +.card_46{background-image:url(cards/card_46.jpg)} +.card_47{background-image:url(cards/card_47.jpg)} +.card_48{background-image:url(cards/card_48.jpg)} +.card_49{background-image:url(cards/card_49.jpg)} +.card_50{background-image:url(cards/card_50.jpg)} +.card_51{background-image:url(cards/card_51.jpg)} +.card_52{background-image:url(cards/card_52.jpg)} +.card_53{background-image:url(cards/card_53.jpg)} +.card_54{background-image:url(cards/card_54.jpg)} +.card_55{background-image:url(cards/card_55.jpg)} +.card_56{background-image:url(cards/card_56.jpg)} +.card_57{background-image:url(cards/card_57.jpg)} +.card_58{background-image:url(cards/card_58.jpg)} +.card_59{background-image:url(cards/card_59.jpg)} +.card_60{background-image:url(cards/card_60.jpg)} +.card_61{background-image:url(cards/card_61.jpg)} +.card_62{background-image:url(cards/card_62.jpg)} +.card_63{background-image:url(cards/card_63.jpg)} +.card_64{background-image:url(cards/card_64.jpg)} +.card_65{background-image:url(cards/card_65.jpg)} +.card_66{background-image:url(cards/card_66.jpg)} +.card_67{background-image:url(cards/card_67.jpg)} +.card_68{background-image:url(cards/card_68.jpg)} +.card_69{background-image:url(cards/card_69.jpg)} +.card_70{background-image:url(cards/card_70.jpg)} +.card_71{background-image:url(cards/card_71.jpg)} +.card_72{background-image:url(cards/card_72.jpg)} +.card_73{background-image:url(cards/card_73.jpg)} +.card_74{background-image:url(cards/card_74.jpg)} +.card_75{background-image:url(cards/card_75.jpg)} +.card_76{background-image:url(cards/card_76.jpg)} +.card_77{background-image:url(cards/card_77.jpg)} +.card_78{background-image:url(cards/card_78.jpg)} +.card_79{background-image:url(cards/card_79.jpg)} +.card_80{background-image:url(cards/card_80.jpg)} +.card_81{background-image:url(cards/card_81.jpg)} +.card_82{background-image:url(cards/card_82.jpg)} +.card_83{background-image:url(cards/card_83.jpg)} +.card_84{background-image:url(cards/card_84.jpg)} +.card_85{background-image:url(cards/card_85.jpg)} +.card_86{background-image:url(cards/card_86.jpg)} +.card_87{background-image:url(cards/card_87.jpg)} +.card_88{background-image:url(cards/card_88.jpg)} +.card_89{background-image:url(cards/card_89.jpg)} +.card_90{background-image:url(cards/card_90.jpg)} +.card_91{background-image:url(cards/card_91.jpg)} +.card_92{background-image:url(cards/card_92.jpg)} +.card_93{background-image:url(cards/card_93.jpg)} +.card_94{background-image:url(cards/card_94.jpg)} +.card_95{background-image:url(cards/card_95.jpg)} +.card_96{background-image:url(cards/card_96.jpg)} +.card_97{background-image:url(cards/card_97.jpg)} +.card_98{background-image:url(cards/card_98.jpg)} +.card_99{background-image:url(cards/card_99.jpg)} +.card_100{background-image:url(cards/card_100.jpg)} +.card_101{background-image:url(cards/card_101.jpg)} +.card_102{background-image:url(cards/card_102.jpg)} +.card_103{background-image:url(cards/card_103.jpg)} +.card_104{background-image:url(cards/card_104.jpg)} +.card_105{background-image:url(cards/card_105.jpg)} +.card_106{background-image:url(cards/card_106.jpg)} +.card_107{background-image:url(cards/card_107.jpg)} +.card_108{background-image:url(cards/card_108.jpg)} +.card_109{background-image:url(cards/card_109.jpg)} +.card_110{background-image:url(cards/card_110.jpg)} +.card_111{background-image:url(cards/card_111.jpg)} +.card_112{background-image:url(cards/card_112.jpg)} +.card_113{background-image:url(cards/card_113.jpg)} +.card_114{background-image:url(cards/card_114.jpg)} +.card_115{background-image:url(cards/card_115.jpg)} +.card_116{background-image:url(cards/card_116.jpg)} +.card_back{background-image:url(cards/card_back_116.jpg)} + +/* MOBILE PHONE LAYOUT */ + +@media (max-width: 640px) { + #tooltip { + top: 75px; + left: 0; + right: 0; + bottom: 0; + margin: auto; + } +} + +@media (max-width: 400px) or (max-height: 590px) { + #tooltip.card { + width: 248px; + height: 344px; + border-radius: 13px; + } +} diff --git a/play.html b/play.html new file mode 100644 index 0000000..452a3af --- /dev/null +++ b/play.html @@ -0,0 +1,437 @@ + + + + + + +PAX PAMIR + + + + + + + + + + +

+ +
+ +
+
+
+
+ + + +
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+
+
3
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + diff --git a/play.js b/play.js new file mode 100644 index 0000000..20e2a85 --- /dev/null +++ b/play.js @@ -0,0 +1,885 @@ +"use strict"; + +// CONSTANTS + +const player_names = [ "Gray", "Blue", "Tan", "Red", "Black" ]; +const player_index = Object.fromEntries(Object.entries(player_names).map(([k,v])=>[v,k|0])); + +const Persia = 201; +const Transcaspia = 202; +const Herat = 203; +const Kabul = 204; +const Kandahar = 205; +const Punjab = 206; + +const Persia_Transcaspia = 301; +const Persia_Herat = 302; +const Transcaspia_Herat = 303; +const Transcaspia_Kabul = 304; +const Herat_Kabul = 305; +const Herat_Kandahar = 306; +const Kabul_Kandahar = 307; +const Kabul_Punjab = 308; +const Kandahar_Punjab = 309; + +const Gift2 = 400; +const Gift4 = 401; +const Gift6 = 402; +const Safe_House = 500; + +const region_index = { + "Persia": Persia, + "Transcaspia": Transcaspia, + "Herat": Herat, + "Kabul": Kabul, + "Kandahar": Kandahar, + "Punjab": Punjab, +}; + +const region_names = { + [Persia]: "Persia", + [Transcaspia]: "Transcaspia", + [Herat]: "Herat", + [Kabul]: "Kabul", + [Kandahar]: "Kandahar", + [Punjab]: "Punjab", +}; + +cards.forEach(card => { + if (card) { + card.region = region_index[card.region]; + if (card.name === 'EVENT') + card.name = card.if_discarded + " / " + card.if_purchased; + } +}); + +const event_cards = { + new_tactics: 105, + koh_i_noor: 106, + courtly_manners: 107, + rumor: 108, + conflict_fatigue: 109, + nationalism: 110, + nation_building: 112, + pashtunwali_values: 115, + embarrassment_of_riches: 106, + disregard_for_customs: 107, +}; + +const VP_OFFSET = [ + [-16, -16], + [-32, 0], + [0, 0], + [-16, 16], + [16, 16], +]; + +const VP_TRACK = [ + [ 91, 43 ], + [ 183, 43 ], + [ 273, 43 ], + [ 363, 43 ], + [ 454, 43 ], + [ 545, 43 ], + [ 635, 43 ], + [ 726, 43 ], + [ 816, 43 ], + [ 906, 43 ], + [ 996, 43 ], + [ 1035, 78 ], + [ 1035, 169 ], + [ 1035, 259 ], + [ 1035, 350 ], + [ 1035, 441 ], + [ 1035, 531 ], + [ 996, 563 ], + [ 906, 563 ], + [ 816, 563 ], + [ 726, 563 ], + [ 635, 563 ], + [ 545, 563 ], + [ 454, 563 ], +]; + +// GAME STATE + +function player_cylinders(p) { + return 36 + p * 10; +} + +function ruler_of_region(r) { + let ruler = -1; + + let n_afghan = 0; + let n_british = 0; + let n_russian = 0; + for (let i = 0; i < 12; ++i) { + if (view.pieces[i] === r) + n_afghan ++; + if (view.pieces[i+12] === r) + n_british ++; + if (view.pieces[i+24] === r) + n_russian ++; + } + + let max_ruling = Math.max(n_afghan, n_british, n_russian); + + for (let p = 0; p < view.players.length; ++p) { + let n_tribes = 0; + let x = 36 + p * 10; + for (let i = x; i < x + 10; ++i) + if (view.pieces[i] === r) + n_tribes++; + + let n_ruling = n_tribes; + if (view.players[p].loyalty === 'Afghan') + n_ruling += n_afghan; + if (view.players[p].loyalty === 'British') + n_ruling += n_british; + if (view.players[p].loyalty === 'Russian') + n_ruling += n_russian; + + if (n_ruling === max_ruling) { + ruler = -1; + } else if (n_ruling > max_ruling) { + max_ruling = n_ruling; + if (n_tribes > 0) + ruler = p; + else + ruler = -1; + } + } + + return ruler; +} + +function count_influence_points(p) { + let n = 1 + view.players[p].prizes; + let x = player_cylinders(p); + + if (!view.events.embarrassment_of_riches) { + let gv = view.players[p].events.koh_i_noor ? 2 : 1; + for (let i = x; i < x + 10; ++i) { + let s = view.pieces[i]; + if (s === Gift2 || s === Gift4 || s === Gift6) + n += gv; + } + } + + if (!view.players[p].events.rumor) { + let court = view.players[p].court; + for (let i = 0; i < court.length; ++i) + if (cards[court[i]].patriot) + ++n; + } + + return n; +} + +function count_cylinders_in_play(p) { + let n = 0; + let x = player_cylinders(p); + for (let i = x; i < x + 10; ++i) + if (view.pieces[i] > 0) + ++n; + return n; +} + +function is_piece_army(i) { + return (view.pieces[i] >= 201 && view.pieces[i] <= 206); +} + +function is_piece_road(i) { + return (view.pieces[i] >= 301 && view.pieces[i] <= 309); +} + +function is_card_action(action, card) { + if (view.actions && view.actions[action] && view.actions[action].includes(card)) + return true; + return false; +} + +function is_place_gift_action(i) { + if (view.actions && view.actions.place_gift && view.actions.place_gift.includes(i)) + return true; + return false; +} + +function is_suit_action(suit) { + if (view.actions && view.actions.suit && view.actions.suit.includes(suit)) + return true; + return false; +} + +function is_piece_action(i) { + if (view.actions && view.actions.piece && view.actions.piece.includes(i)) + return true; + return false; +} + +function is_space_action(i) { + if (view.actions && view.actions.space && view.actions.space.includes(i)) + return true; + return false; +} + +// UI ELEMENTS + +let ui = { + pieces: [], + spaces: [], + cards: [], + spyrows: [], + market_card: [[],[]], + market_coin: [[],[]], + card_action_index: { battle: [], betray: [], build: [], gift: [], move: [], tax: [] }, + card_action_element: { battle: [], betray: [], build: [], gift: [], move: [], tax: [] }, + player: [], +} + +function scroll_to_map() { + ui.board.scrollIntoView({behavior:'smooth'}); +} + +function scroll_to_market() { + ui.market.scrollIntoView({behavior:'smooth'}); +} + +function scroll_to_player(p) { + ui.player[p].area.scrollIntoView({behavior:'smooth'}); +} + +let open_toggle = true; +function toggle_open_hands() { + open_toggle = !open_toggle; + for (let p = 0; p < view.players.length; ++p) + if (p !== player_index[player]) + ui.player[p].hand.classList.toggle("hide", open_toggle); +} + +function on_blur() { + ui.status.textContent = ""; + ui.tooltip.classList = "hide"; +} + +function on_focus_card_tip(c) { + ui.tooltip.classList = "card card_" + c; +} + +function on_click_card_tip(c) { + ui.cards[c].scrollIntoView({behavior:'smooth'}); +} + +function on_focus_card(evt) { + let c = evt.target.card; + if (!evt.target.classList.contains("card_back")) { + ui.status.textContent = `${evt.target.card} - ${cards[c].name}`; + ui.tooltip.classList = "focus card card_" + c; + } +} + +function on_click_space(evt) { + send_action('space', evt.target.space); + evt.stopPropagation(); +} + +function on_click_block(evt) { + send_action('piece', evt.target.piece); + evt.stopPropagation(); +} + +function on_click_cylinder(evt) { + send_action('piece', evt.target.piece); + evt.stopPropagation(); +} + +function toggle_hand(p) { + ui.player[p].hand.classList.toggle("hide"); +} + +// CARD MENU + +const card_action_menu = [ + 'play_left', + 'play_right', +]; + +let current_popup_card = 0; + +function show_popup_menu(evt, list) { + document.querySelectorAll("#popup div").forEach(e => e.classList.remove('enabled')); + for (let item of list) { + let e = document.getElementById("menu_" + item); + e.classList.add('enabled'); + } + let popup = document.getElementById("popup"); + popup.style.display = 'block'; + popup.style.left = (evt.clientX-50) + "px"; + popup.style.top = (evt.clientY-12) + "px"; + ui.cards[current_popup_card].classList.add("selected"); + ui.popup_label.textContent = cards[current_popup_card].name; +} + +function hide_popup_menu() { + let popup = document.getElementById("popup"); + popup.style.display = 'none'; + if (current_popup_card) { + ui.cards[current_popup_card].classList.remove("selected"); + current_popup_card = 0; + } +} + +function popup_action(action) { + send_action(action, current_popup_card); + hide_popup_menu(); +} + +function on_click_card(evt) { + let c = evt.target.card; + if (is_card_action('card', c)) { + send_action('card', c); + } else { + let menu = card_action_menu.filter(a => is_card_action(a, c)); + if (menu.length > 0) { + current_popup_card = c; + show_popup_menu(evt, menu); + } + } +} + +// LOG + +function sub_card_name(match, p1) { + let c = p1 | 0; + let name = cards[c].name; + return `${name}`; +} + +function on_log(text) { + let p = document.createElement("div"); + if (text.match(/^>/)) { + text = text.substring(1); + p.className = 'i'; + } + text = text.replace(/&/g, "&"); + text = text.replace(//g, ">"); + text = text.replace(/#(\d+)/g, sub_card_name); + if (text.match(/^.turn/)) { + text = text.substring(6); + p.className = 'turn ' + text; + } + let m; + if ((m = text.match(/^.dc.(\w+) (.*)/))) { + text = m[2]; + p.className = 'dc ' + m[1]; + } + p.innerHTML = text; + return p; +} + +// LAYOUT + +function layout_block_pool() { + function place_block_pool(i, x, y) { + ui.pieces[i].style = `top:${27+y*48}px;left:${1070+26+x*(26+35)}px`; + } + for (let k = 0, i = 0; i < 12; ++i) { + if (view.pieces[i] === 0) { + place_block_pool(i, 0, k); + ++k; + } + } + for (let k = 0, i = 12; i < 24; ++i) { + if (view.pieces[i] === 0) { + place_block_pool(i, 1, k); + ++k; + } + } + for (let k = 0, i = 24; i < 36; ++i) { + if (view.pieces[i] === 0) { + place_block_pool(i, 2, k); + ++k; + } + } +} + +function layout_armies(list, xc, yc, maxcol) { + function place_army(y, x, i) { + ui.pieces[i].style = `top:${yc+y*16+x*1}px;left:${xc+x*26-y*16}px`; + } + let ncol = Math.min(maxcol, list.length); + let nrow = Math.ceil(list.length / ncol); + let i = 0; + for (let row = 0; row < nrow; ++row) + for (let col = 0; col < ncol && i < list.length; ++col) + place_army(row-nrow+1, col - (ncol/2) - ((nrow-1)/4), list[i++]); +} + +function layout_tribes(list, xc, yc, maxcol) { + function place_tribe(y, x, i) { + ui.pieces[i].style = `top:${yc+y*16+x*4}px;left:${xc+x*32-y*20}px`; + } + let ncol = Math.min(maxcol, Math.ceil(Math.sqrt(list.length))); + let nrow = Math.ceil(list.length / ncol); + let i = 0; + for (let row = 0; row < nrow; ++row) + for (let col = 0; col < ncol && i < list.length; ++col) + place_tribe(row, col - (ncol/2) + ((nrow-1)/4), list[i++]); +} + +function layout_region(r, xc, yc, maxcol) { + let list = []; + for (let i = 0; i < 36; ++i) + if (view.pieces[i] === r) + list.push(i); + layout_armies(list, xc - 2, yc - 50, maxcol); + + list = []; + for (let i = 36; i < view.pieces.length; ++i) + if (view.pieces[i] === r) + list.push(i); + layout_tribes(list, xc, yc + 20, maxcol); +} + +function layout_border(r, xc, yc, line) { + xc -= 24; + yc -= 12; + function place_piece_border(i, k) { + let x, y; + switch (line) { + case 0: x = k * 18; y = k * 7; break; + case 1: x = k * 4; y = k * 16; break; + case 2: x = k * -4; y = k * 16; break; + case 3: x = k * -12; y = k * 14; break; + } + ui.pieces[i].style = `top:${yc+y}px;left:${xc+x}px`; + } + let n = 0; + for (let i = 0; i < view.pieces.length; ++i) { + if (view.pieces[i] === r) + ++n; + } + for (let k = (-(n-1)/2), i = 0; i < view.pieces.length; ++i) { + if (view.pieces[i] === r) { + place_piece_border(i, k); + ++k; + } + } +} + +// UPDATE UI + +let once = true; + +function on_update() { + if (once) { + build_ui(); + once = false; + } + + function update_event_cards(node, events) { + for (let evt in events) + node.appendChild(ui.cards[event_cards[evt]]); + } + + let ruler = [ + ruler_of_region(Persia), + ruler_of_region(Transcaspia), + ruler_of_region(Herat), + ruler_of_region(Kabul), + ruler_of_region(Kandahar), + ruler_of_region(Punjab) + ]; + + ui.prompt.innerHTML = view.prompt.replace(/#(\d+)/g, sub_card_name); + + ui.deck_info.textContent = `${view.cards[0]}x Draw Deck, ${view.cards[1]}x Dominance Check`; + + action_button("loyalty_afghan", "Afghan"); + action_button("loyalty_british", "British"); + action_button("loyalty_russian", "Russian"); + + action_button("courtly_manners", "Courtly Manners"); + action_button("beg", "Beg"); + action_button("pay", "Pay"); + action_button("waive", "Waive"); + action_button("accept", "Accept"); + + for (let i = 0; i < 10; ++i) + action_button("offer_" + i, i); + + action_button("refuse", "Refuse"); + + action_button("player_0", "Gray"); + action_button("player_1", "Blue"); + action_button("player_2", "Tan"); + action_button("player_3", "Red"); + action_button("player_4", "Black"); + + action_button("pass", "Pass"); + action_button("next", "Next"); + confirm_action_button("end_turn_pass", "End turn", + "Are you sure you want to END TURN while you still have actions?"); + action_button("end_turn", "End turn"); + action_button("undo", "Undo"); + + ui.favored1.className = view.favored; + ui.favored2.className = view.favored + " icon"; + + for (let row = 0; row < 2; ++row) { + for (let col = 0; col < 6; ++col) { + let ce = ui.cards[view.market_cards[row][col]]; + if (ce) + ce.classList.remove("card_back"); + let me = ui.market_card[row][col]; + if (me.firstChild !== ce) { + if (me.firstChild) + me.removeChild(me.firstChild); + if (ce) + me.appendChild(ce); + } + let coins = view.market_coins[row][col]; + if (coins > 0) { + ui.market_coin[row][col].textContent = coins; + ui.market_coin[row][col].className = "coin"; + } else { + ui.market_coin[row][col].textContent = ""; + ui.market_coin[row][col].className = "coin hide"; + } + } + } + + for (let i = 1; i < cards.length; ++i) { + ui.cards[i].classList.toggle('action', is_card_action('card', i)); + } + + for (let i = 201; i <= 206; ++i) { + ui.spaces[i].classList.toggle('action', is_space_action(i)); + ui.spaces[i].classList.toggle('selected', view.where === i); + } + for (let i = 301; i <= 309; ++i) + ui.spaces[i].classList.toggle('action', is_space_action(i)); + + for (let i = 0; i < 36; ++i) { + ui.pieces[i].classList.toggle('action', is_piece_action(i)); + ui.pieces[i].classList.toggle('selected', view.selected === i); + ui.pieces[i].classList.toggle('road', is_piece_road(i)); + ui.pieces[i].classList.toggle('army', is_piece_army(i)); + } + + for (let p = 0; p < view.players.length; ++p) { + let pp = view.players[p]; + let me = ui.player[p].court; + while (me.firstChild) + me.removeChild(me.firstChild); + me.appendChild(ui.player[p].pool); + update_event_cards(me, view.players[p].events); + for (let i = 0; i < pp.court.length; ++i) { + let ce = ui.cards[pp.court[i]]; + me.appendChild(ce); + ce.classList.remove("card_back"); + } + + if (p == player_index[player]) { + ui.player[p].gift_2.classList.toggle('action', is_place_gift_action(2)); + ui.player[p].gift_4.classList.toggle('action', is_place_gift_action(4)); + ui.player[p].gift_6.classList.toggle('action', is_place_gift_action(6)); + } + + me = ui.global_events; + while (me.firstChild) + me.removeChild(me.firstChild); + update_event_cards(me, view.events); + + me = ui.player[p].hand; + while (me.firstChild) + me.removeChild(me.firstChild); + if (p === player_index[player]) + me.classList.remove("hide"); + for (let i = 0; i < pp.hand.length; ++i) { + let ce = ui.cards[pp.hand[i]]; + if (p !== player_index[player] && !view.open) + ce.classList.add("card_back"); + else + ce.classList.remove("card_back"); + me.appendChild(ce); + } + + if (view.players[p].coins == 0) { + ui.player[p].coin.classList.add("hide"); + } else { + ui.player[p].coin.classList.remove("hide"); + ui.player[p].coin.textContent = view.players[p].coins; + } + + if (view.players[p].prizes == 0) { + ui.player[p].prize.classList.add("hide"); + } else { + ui.player[p].prize.classList.remove("hide"); + if (view.players[p].prizes === 1) + ui.player[p].prize.textContent = view.players[p].prizes + " prize"; + else + ui.player[p].prize.textContent = view.players[p].prizes + " prizes"; + } + + ui.player[p].dial.className = "player_dial " + view.players[p].loyalty + " p" + p; + + ui.player[p].role_loy_icon.className = "role_loyalty_icon " + view.players[p].loyalty; + ui.player[p].role_loy_text.textContent = count_influence_points(p); + ui.player[p].role_cyl_text.textContent = count_cylinders_in_play(p); + ui.player[p].role_rup_text.textContent = view.players[p].coins; + + ui.player[p].hand_size.textContent = view.players[p].hand.length; + + ui.player[p].score.style.left = (VP_OFFSET[p][0] + VP_TRACK[view.players[p].vp][0]) + "px"; + ui.player[p].score.style.top = (VP_OFFSET[p][1] + VP_TRACK[view.players[p].vp][1]) + "px"; + + for (let i = 0; i < 10; ++i) { + let x = 36 + p * 10 + i; + let s = view.pieces[x]; + if (s === 0 || s === Safe_House) + ui.player[p].pool.appendChild(ui.pieces[x]); + else if (s === Gift2) + ui.player[p].gift_2.appendChild(ui.pieces[x]); + else if (s === Gift4) + ui.player[p].gift_4.appendChild(ui.pieces[x]); + else if (s === Gift6) + ui.player[p].gift_6.appendChild(ui.pieces[x]); + else if (s <= 100) + ui.spyrows[s].appendChild(ui.pieces[x]); + else + ui.board.appendChild(ui.pieces[x]); + ui.pieces[x].classList.toggle('action', is_piece_action(x)); + ui.pieces[x].classList.toggle('selected', view.selected === x); + ui.pieces[x].style = ""; + } + } + + for (let i = 0; i < 6; ++i) + if (ruler[i] === -1) + ui.rule[i].classList = "hide"; + else + ui.rule[i].classList = `rule ${region_names[i+Persia]} ${player_names[ruler[i]]}`; + + ui.suit_political.classList.toggle('action', is_suit_action('Political')); + ui.suit_intelligence.classList.toggle('action', is_suit_action('Intelligence')); + ui.suit_economic.classList.toggle('action', is_suit_action('Economic')); + ui.suit_military.classList.toggle('action', is_suit_action('Military')); + + layout_block_pool(); + + layout_region(Persia, 206-16, 426, 5); + layout_region(Transcaspia, 254, 152, 10); + layout_region(Herat, 456, 383, 6); + layout_region(Kabul, 673, 163, 12); + layout_region(Kandahar, 732-23, 437, 6); + layout_region(Punjab, 929, 306, 3); + + layout_border(Persia_Transcaspia, 188, 320, 0); + layout_border(Persia_Herat, 313, 441, 1); + layout_border(Transcaspia_Herat, 371, 297, 3); + layout_border(Transcaspia_Kabul, 477, 164, 1); + layout_border(Herat_Kabul, 527, 297, 0); + layout_border(Herat_Kandahar, 598, 441, 2); + layout_border(Kabul_Kandahar, 699, 332, 0); + layout_border(Kabul_Punjab, 859, 211, 2); + layout_border(Kandahar_Punjab, 836, 438, 1); + + for (let action in ui.card_action_index) { + for (let i = 0; i < ui.card_action_index[action].length; ++i) { + let c = ui.card_action_index[action][i]; + let e = ui.card_action_element[action][i]; + e.classList.toggle("action", is_card_action(action, c)); + } + } +} + +// BUILD UI + +function build_ui() { + let passive_cards = [1,3,5,15,17,21,24,41,42,43,51,54,56,66,68,70,72,78,83,91,97,99]; + + function build_player_ui(p) { + return { + role: document.getElementById("role_" + player_names[p]), + role_rup_text: document.getElementById("rupees_" + p + "_text"), + role_cyl_text: document.getElementById("cylinders_" + p + "_text"), + role_loy_text: document.getElementById("loyalty_" + p + "_text"), + role_loy_icon: document.getElementById("loyalty_" + p + "_icon"), + score: document.getElementById("player_score_" + p), + area: document.getElementById("player_area_" + p), + hand_size: document.getElementById("player_hand_size_" + p), + hand: document.getElementById("player_hand_" + p), + court: document.getElementById("player_court_" + p), + pool: document.getElementById("player_pool_" + p), + dial: document.getElementById("player_dial_" + p), + coin: document.getElementById("player_coin_" + p), + prize: document.getElementById("player_prize_" + p), + gift_2: document.getElementById("player_gift_" + p + "_2"), + gift_4: document.getElementById("player_gift_" + p + "_4"), + gift_6: document.getElementById("player_gift_" + p + "_6"), + } + } + + function build_card_action(card, action, i, x) { + let e = document.createElement("div"); + e.className = `card_action ${action} n${x}`; + e.addEventListener("click", () => send_action(action, i)); + ui.card_action_index[action].push(i); + ui.card_action_element[action].push(e); + card.appendChild(e); + } + + function build_space(i, n) { + ui.spaces[i] = document.getElementById("svgmap").getElementById(n); + ui.spaces[i].space = i; + ui.spaces[i].addEventListener("click", on_click_space); + } + + for (let c = 1; c < cards.length; ++c) { + let e = document.createElement("div"); + e.card = c; + if (c <= 100) { + let info = cards[c]; + e.className = "card card_" + c + " " + info.suit; + let n = 0; + if (info.gift) ++n, build_card_action(e, 'gift', c, info.gift); + if (info.move) ++n, build_card_action(e, 'move', c, info.move); + if (info.betray) ++n, build_card_action(e, 'betray', c, info.betray); + if (info.battle) ++n, build_card_action(e, 'battle', c, info.battle); + if (info.build) ++n, build_card_action(e, 'build', c, info.build); + if (info.tax) ++n, build_card_action(e, 'tax', c, info.tax); + if (passive_cards.includes(c)) + e.classList.add("passive"); + if (n === 3) + e.classList.add("three"); + } else { + e.className = "event card card_" + c; + } + e.addEventListener("click", on_click_card); + e.addEventListener("mouseenter", on_focus_card); + e.addEventListener("mouseleave", on_blur); + ui.cards[c] = e; + let ee = document.createElement("div"); + ee.className = "spyrow"; + e.appendChild(ee); + ui.spyrows[c] = ee; + } + + for (let row = 0; row < 2; ++row) { + for (let col = 0; col < 6; ++col) { + ui.market_card[row][col] = document.getElementById("market_card_" + row + "_" + col); + ui.market_coin[row][col] = document.getElementById("market_coin_" + row + "_" + col); + } + } + + for (let p = 0; p < 5; ++p) { + ui.player[p] = build_player_ui(p); + + ui.player[p].hand_size.addEventListener("click", + () => toggle_hand(p)); + + for (let i = 0; i < 10; ++i) { + let x = 36 + p * 10 + i; + ui.pieces[x] = document.createElement("div"); + ui.pieces[x].piece = x; + ui.pieces[x].className = "cylinder p" + p; + ui.pieces[x].addEventListener("click", on_click_cylinder); + ui.player[p].pool.appendChild(ui.pieces[x]); + } + + ui.player[p].gift_2.addEventListener("click", () => send_action('place_gift', 2)); + ui.player[p].gift_4.addEventListener("click", () => send_action('place_gift', 4)); + ui.player[p].gift_6.addEventListener("click", () => send_action('place_gift', 6)); + } + + ui.rule = [ + document.querySelector(`#board .rule.Persia`), + document.querySelector(`#board .rule.Transcaspia`), + document.querySelector(`#board .rule.Herat`), + document.querySelector(`#board .rule.Kabul`), + document.querySelector(`#board .rule.Kandahar`), + document.querySelector(`#board .rule.Punjab`), + ]; + + ui.prompt = document.getElementById("prompt"); + ui.deck_info = document.getElementById("deck_info"); + ui.board = document.getElementById("board"); + ui.market = document.getElementById("market"); + ui.status = document.getElementById("status"); + ui.tooltip = document.getElementById("tooltip"); + ui.favored1 = document.getElementById("favored_suit_marker"); + ui.favored2 = document.getElementById("favored_suit_banner"); + ui.popup_label = document.getElementById("popup_label"); + ui.global_events = document.getElementById("global_events"); + + ui.suit_political = document.getElementById("suit_political"); + ui.suit_intelligence = document.getElementById("suit_intelligence"); + ui.suit_economic = document.getElementById("suit_economic"); + ui.suit_military = document.getElementById("suit_military"); + + ui.suit_political.addEventListener("click", () => send_action('suit', 'Political')); + ui.suit_intelligence.addEventListener("click", () => send_action('suit', 'Intelligence')); + ui.suit_economic.addEventListener("click", () => send_action('suit', 'Economic')); + ui.suit_military.addEventListener("click", () => send_action('suit', 'Military')); + + build_space(Transcaspia, "Transcaspia"); + build_space(Kabul, "Kabul"); + build_space(Punjab, "Punjab"); + build_space(Persia, "Persia"); + build_space(Herat, "Herat"); + build_space(Kandahar, "Kandahar"); + build_space(Persia_Transcaspia, "Persia/Transcaspia"); + build_space(Persia_Herat, "Persia/Herat"); + build_space(Transcaspia_Herat, "Transcaspia/Herat"); + build_space(Transcaspia_Kabul, "Transcaspia/Kabul"); + build_space(Herat_Kabul, "Herat/Kabul"); + build_space(Herat_Kandahar, "Herat/Kandahar"); + build_space(Kabul_Kandahar, "Kabul/Kandahar"); + build_space(Kabul_Punjab, "Kabul/Punjab"); + build_space(Kandahar_Punjab, "Kandahar/Punjab"); + + function make_block(p, faction) { + let div = document.createElement("div"); + div.className = faction + " block"; + div.piece = p; + div.addEventListener("click", on_click_block); + ui.board.appendChild(div); + return div; + } + + for (let i = 0; i < 12; ++i) ui.pieces[i] = make_block(i, "Afghan"); + for (let i = 12; i < 24; ++i) ui.pieces[i] = make_block(i, "British"); + for (let i = 24; i < 36; ++i) ui.pieces[i] = make_block(i, "Russian"); + + // Sort player roles so active player is on top! + let top = player === 'Observer' ? 0 : player_index[player]; + let alist = document.getElementById("player_area_list"); + let rlist = document.getElementById("roles"); + for (let p = top; p < view.players.length; ++p) { + alist.appendChild(ui.player[p].area); + rlist.appendChild(ui.player[p].role); + ui.player[p].area.classList.remove("hide"); + ui.player[p].role.classList.remove("hide"); + ui.player[p].score.classList.remove("hide"); + } + for (let p = 0; p < top; ++p) { + alist.appendChild(ui.player[p].area); + rlist.appendChild(ui.player[p].role); + ui.player[p].area.classList.remove("hide"); + ui.player[p].role.classList.remove("hide"); + ui.player[p].score.classList.remove("hide"); + } + + if (player !== 'Observer') + ui.player[top].hand_size.classList.add("hide"); +} + +function debug() { + function rr(k,v) { return k === 'log' || k === 'players' || k === 'actions' ? undefined: v; } + console.log("VIEW", JSON.stringify(view, rr, 0)); + console.log("ACTIONS", JSON.stringify(view.actions, rr, 0)); + for (let i = 0; i < view.players.length; ++i) + console.log("PLAYER", i, JSON.stringify(view.players[i], rr, 0)); +} -- cgit v1.2.3