diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-06-05 13:31:15 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2023-07-16 13:06:00 +0200 |
commit | 51f8e9aefed01f36127246b15563d7e9cc1801b8 (patch) | |
tree | 28d0c16b9247d1f33f49d99a874514114b6637d8 | |
parent | fa3fc7fd0184fc8500cbe21cee7440433d1d1c4c (diff) | |
download | field-cloth-gold-51f8e9aefed01f36127246b15563d7e9cc1801b8.tar.gz |
All in one.
-rw-r--r-- | play.html | 188 | ||||
-rw-r--r-- | play.js | 253 | ||||
-rw-r--r-- | rules.js | 395 |
3 files changed, 700 insertions, 136 deletions
@@ -12,64 +12,141 @@ <script defer src="play.js"></script> <style> -main { background-color: dimgray } +main { + background-color: #666; + background-image: url(background.png); + box-shadow: inset 0 0 8px #0008; +} + +#log { + background-color: oldlace; +} -#role_Red .role_name { background-color: salmon; } -#role_Blue .role_name { background-color: skyblue; } +#role_Red .role_name { background-color: hsl(359,100%,75%); } +#role_Blue .role_name { background-color: hsl(220,100%,80%); } #mapwrap { - width: 1104px; - height: 854px; - box-shadow: 0px 1px 10px #0008; - margin: 12px auto; + width: 827px; + margin: 0px auto; } #map { - width: 1100px; - height: 850px; - background-size: 1100px 850px; - background-image: url(map100.jpg); - border: 2px solid #333; + width: 825px; + height: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px + 80px + 12px); +} + +#board, #hand, .court { + box-sizing: border-box; + position: absolute; + width: 825px; + border: 2px solid #0004; + border-color: #fff4 #0004 #0004 #fff4; + box-shadow: 0 0 0 1px #222, 1px 1px 8px -2px #000; + border-radius: 8px; +} + +#hand, .court { + height: 80px; +} + +#board { + height: 638px; + background-size: 825px 638px; + background-position: center; + background-image: url(map75.jpg); + border-color: #fff8 #2228 #2228 #fff8; } +#court1 { top: 12px } +#board { top: calc(12px + 80px + 12px) } +#court2 { top: calc(12px + 80px + 12px + 638px + 12px) } +#hand { top: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px) } +#mapwrap { height: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px + 80px) } + +.red.court { background-color: hsl(359,35%,45%) } +.blue.court { background-color: hsl(220,35%,45%) } + +X#hand { background-color: hsl(46, 63%, 56%) } +#hand { background-color: hsl(46, 35%, 45%) } +X#hand { background-image: url(bg_gold3.png); background-size: 256px 256px; } +X#hand { background-color: #666 } + @media (min-resolution: 97dpi) { - #map { background-image: url(map200.jpg) } + #board { background-image: url(map150.jpg) } } #map div { position: absolute; transition-property: top, left; - transition-duration: 1s; + transition-duration: 500ms; transition-timing-function: ease; } +.action { + cursor: pointer; +} + .token { - width: 58px; - height: 64px; - background-size: 58px 64px; + width: 41px; + height: 44px; + background-size: 41px 44px; filter: drop-shadow(0px 2px 4px #0008); } +.token.action { + filter: + drop-shadow(0px 2px 0px white) + drop-shadow(2px 0px 0px white) + drop-shadow(-2px 0px 0px white) + drop-shadow(0px -2px 0px white) +} + +.token.selected { + filter: + drop-shadow(0px 2px 0px yellow) + drop-shadow(2px 0px 0px yellow) + drop-shadow(-2px 0px 0px yellow) + drop-shadow(0px -2px 0px yellow) +} + .tile { - width: 75px; - height: 75px; + width: 52px; + height: 52px; background-repeat: no-repeat; - background-size: 50px 50px; + background-size: 38px 38px; background-position: center; border-width: 2px; border-style: solid; box-shadow: 0 0 0 1px #222, 1px 2px 4px #0008; } +.tile.action { + box-shadow: 0 0 0 1px #222, 0 0 0 4px white; +} + +.tile.selected { + box-shadow: 0 0 0 1px #222, 0 0 0 4px yellow; +} + +.space { + box-sizing: border-box; + border-radius: 50%; + border: 3px solid transparent; +} + +.space.action { + border: 3px solid white; +} + .token.white { background-image: url(images/token_white.svg) } .token.red { background-image: url(images/token_red.svg) } .token.blue { background-image: url(images/token_blue.svg) } -.tile.white { background-image: url(images/tile_white.png) } -.tile.red { background-image: url(images/tile_red.png) } -.tile.blue { background-image: url(images/tile_blue.png) } -.tile.gold { background-image: url(images/tile_gold.png) } -.tile.green { background-image: url(images/tile_green.png) } +.tile.white { background-image: url(images/white.png) } +.tile.red { background-image: url(images/red.png) } +.tile.blue { background-image: url(images/blue.png) } +.tile.gold { background-image: url(images/gold.png) } +.tile.green { background-image: url(images/green.png) } .tile.red { background-color: hsl(0,90%,49%); border-color: hsl(0,90%,59%) hsl(0,90%,39%) hsl(0,90%,39%) hsl(0,90%,59%); } .tile.white { background-color: hsl(0,0%,94%); border-color: hsl(0,0%,100%) hsl(0,0%,84%) hsl(0,0%,84%) hsl(0,0%,100%); } @@ -77,38 +154,6 @@ main { background-color: dimgray } .tile.gold { background-color: hsl(50,81%,59%); border-color: hsl(50,81%,69%) hsl(50,81%,49%) hsl(50,81%,49%) hsl(50,81%,69%); } .tile.green { background-color: hsl(125,21%,33%); border-color: hsl(125,21%,43%) hsl(125,21%,23%) hsl(125,21%,23%) hsl(125,21%,43%); } -.panel { - max-width: 1100px; - margin: 24px auto; - background-color: #555; - border: 2px solid #333; - box-shadow: 0 1px 10px #0008; -} - -#red_court_header { background-color: #644 } -#red_court { background-color: #655 } - -#blue_court_header { background-color: #446 } -#blue_court { background-color: #556 } - -.panel_header { - background-color: #444; - color: white; - user-select: none; - font-weight: bold; - text-align: center; - padding: 3px 1em; -} - -.panel_body { - display: flex; - justify-content: start; - flex-wrap: wrap; - padding: 16px; - gap: 16px; - min-height: 80px; -} - </style> </head> <body> @@ -149,36 +194,17 @@ main { background-color: dimgray } <main> -<div id="mapwrap" class=""> +<div id="mapwrap"> <div id="map"> -<div id="token_white" class="token white" style="left:90px;top:135px;"></div> -<div id="token_red1" class="token red" style="left:200px"></div> -<div id="token_red2" class="token red" style="left:300px"></div> -<div id="token_red3" class="token red" style="left:400px"></div> -<div id="token_blue1" class="token blue" style="left:500px"></div> -<div id="token_blue2" class="token blue" style="left:600px"></div> -<div id="token_blue3" class="token blue" style="left:700px"></div> +<div id="court1" class="red court"></div> +<div id="board"></div> +<div id="court2" class="blue court"></div> +<div id="hand" class="blue hand"></div> </div> </div> -<div id="hand_panel" class="panel"> -<div id="hand_header" class="panel_header">Hand</div> -<div id="hand" class="panel_body"> -</div> -</div> - -<div id="red_court_panel" class="panel"> -<div id="red_court_header" class="panel_header">Red Court</div> -<div id="red_court" class="panel_body"> -</div></div> - -<div id="blue_court_panel" class="panel"> -<div id="blue_court_header" class="panel_header">Blue Court</div> -<div id="blue_court" class="panel_body"> -</div></div> - </main> <footer id="status"></footer> @@ -0,0 +1,253 @@ +"use strict" + +const FIRST_TILE = 1 +const TILE_BLUE = 1 +const TILE_RED = 13 +const TILE_GOLD = 25 +const TILE_WHITE = 37 +const TILE_GREEN = 49 +const LAST_TILE = 54 + +const TILE_W = 56 +const TILE_SPACE = TILE_W + 12 + +function calc_tile_spacing(n) { + if (n > 12) + return 744 / (n-1) + return TILE_SPACE +} + +const BOARD_X = 0 +const BOARD_Y = 0 + (12 + 80 + 12) + +const COURT1_X = 12 +const COURT1_Y = 12 + (12) +const COURT2_X = 12 +const COURT2_Y = 12 + (12 + 80 + 12 + 638 + 12) +const HAND_X = 12 +const HAND_Y = 12 + (12 + 80 + 12 + 638 + 12 + 80 + 12) + +const TOKEN_DX = 3 +const TOKEN_DY = -5 +const TILE_DX = 3 +const TILE_DY = 3 + +const LAYOUT_OVAL = [ + [156,165,45,45], + [262,165,45,45], + [369,165,45,45], + [475,165,45,45], + [582,165,45,45], + [688,165,45,45], + [66,164,45,45], + + // dragon at home + [66,105,46,45], + + // off-board + [75,400,62,62], + [175,400,62,62], + [50,500,62,62], + [150,500,62,62], +] + +const LAYOUT_SQUARE = [ + [147,224,61,61], + [253,224,61,61], + [360,225,61,61], + [466,225,61,61], + [573,225,61,61], + [679,225,61,61], +] + +const LAYOUT_SCORE_0 = [358,332,45,45] +const LAYOUT_SCORE_39 = [748,547,45,45] + +const SCORE_X0 = LAYOUT_SCORE_0[0] + TOKEN_DX + BOARD_X +const SCORE_Y0 = LAYOUT_SCORE_0[1] + TOKEN_DY + BOARD_Y +const SCORE_DX = (LAYOUT_SCORE_39[0] - LAYOUT_SCORE_0[0]) / 7 +const SCORE_DY = (LAYOUT_SCORE_39[1] - LAYOUT_SCORE_0[1]) / 4 + +let ui = { + board: document.getElementById("map"), + court1: document.getElementById("court1"), + court2: document.getElementById("court2"), + red_score: null, + blue_score: null, + oval_spaces: [], + tokens: [], + tiles: [ null ], +} + +let action_register = [] + +function register_action(e, action, id) { + e.my_action = action + e.my_id = id + e.onmousedown = on_click_action + action_register.push(e) +} + +function on_click_action(evt) { + if (evt.button === 0) + if (send_action(evt.target.my_action, evt.target.my_id)) + evt.stopPropagation() +} + +function is_action(action, arg) { + if (arg === undefined) + return !!(view.actions && view.actions[action] === 1) + return !!(view.actions && view.actions[action] && view.actions[action].includes(arg)) +} + +function create(t, p, ...c) { + let e = document.createElement(t) + Object.assign(e, p) + e.append(c) + if (p.my_action) + register_action(e, p.my_action, p.my_id) + return e +} + +function create_token(p) { + let e = create("div", p) + ui.board.appendChild(e) + return e +} + +function create_tile(p) { + let e = create("div", p) + return e +} + +let on_init_once = false + +function on_init() { + if (on_init_once) + return + on_init_once = true + + for (let i = 0; i < 7; ++i) { + ui.oval_spaces[i] = create("div", { className: "oval space", my_action: "space", my_id: i }) + ui.oval_spaces[i].style.left = (BOARD_X + LAYOUT_OVAL[i][0]) + "px" + ui.oval_spaces[i].style.top = (BOARD_Y + LAYOUT_OVAL[i][1]) + "px" + ui.oval_spaces[i].style.width = LAYOUT_OVAL[i][2] + "px" + ui.oval_spaces[i].style.height = LAYOUT_OVAL[i][3] + "px" + ui.board.append(ui.oval_spaces[i]) + } + + ui.red_score = create_token({ className: "token red" }) + ui.blue_score = create_token({ className: "token blue" }) + + ui.tokens[0] = create_token({ className: "token white", my_action: "token", my_id: 0 }) + ui.tokens[1] = create_token({ className: "token red", my_action: "token", my_id: 1 }) + ui.tokens[2] = create_token({ className: "token red", my_action: "token", my_id: 2 }) + ui.tokens[3] = create_token({ className: "token blue", my_action: "token", my_id: 3 }) + ui.tokens[4] = create_token({ className: "token blue", my_action: "token", my_id: 4 }) + + for (let i = TILE_BLUE; i < TILE_BLUE + 12; ++i) + ui.tiles[i] = create_tile({ className: "tile blue", my_action: "tile", my_id: i }) + for (let i = TILE_RED; i < TILE_RED + 12; ++i) + ui.tiles[i] = create_tile({ className: "tile red", my_action: "tile", my_id: i }) + for (let i = TILE_GOLD; i < TILE_GOLD + 12; ++i) + ui.tiles[i] = create_tile({ className: "tile gold", my_action: "tile", my_id: i }) + for (let i = TILE_WHITE; i < TILE_WHITE + 12; ++i) + ui.tiles[i] = create_tile({ className: "tile white", my_action: "tile", my_id: i }) + for (let i = TILE_GREEN; i < TILE_GREEN + 6; ++i) + ui.tiles[i] = create_tile({ className: "tile green", my_action: "tile", my_id: i }) +} + +function show(elt) { + if (elt.parentElement !== ui.board) + ui.board.appendChild(elt) +} + +function hide(elt) { + if (elt.parentElement === ui.board) + elt.remove() +} + +function on_update() { + on_init() + + for (let i = 1; i <= 54; ++i) { + if (view.hand.includes(i) || view.red_court.includes(i) || view.blue_court.includes(i) || view.squares.includes(i)) + show(ui.tiles[i]) + else + hide(ui.tiles[i]) + } + + for (let i = 0; i < 6; ++i) { + let k = view.squares[i] + if (k > 0) { + let x = LAYOUT_SQUARE[i][0] + TILE_DX + BOARD_X + let y = LAYOUT_SQUARE[i][1] + TILE_DY + BOARD_Y + ui.tiles[k].style.left = x + "px" + ui.tiles[k].style.top = y + "px" + } + } + + for (let i = 0; i < 5; ++i) { + let s = view.tokens[i] + let x = LAYOUT_OVAL[s][0] + TOKEN_DX + BOARD_X + let y = LAYOUT_OVAL[s][1] + TOKEN_DY + BOARD_Y + ui.tokens[i].style.left = x + "px" + ui.tokens[i].style.top = y + "px" + ui.tokens[i].classList.toggle("selected", i === view.selected_token) + } + + let tile_space = calc_tile_spacing(view.red_court.length) + for (let i = 0; i < view.red_court.length; ++i) { + let k = view.red_court[i] + let x = COURT1_X + tile_space * i + let y = COURT1_Y + ui.tiles[k].style.left = x + "px" + ui.tiles[k].style.top = y + "px" + ui.tiles[k].style.zIndex = i + } + + tile_space = calc_tile_spacing(view.blue_court.length) + for (let i = 0; i < view.blue_court.length; ++i) { + let k = view.blue_court[i] + let x = COURT2_X + tile_space * i + let y = COURT2_Y + ui.tiles[k].style.left = x + "px" + ui.tiles[k].style.top = y + "px" + ui.tiles[k].style.zIndex = i + } + + tile_space = calc_tile_spacing(view.hand.length) + for (let i = 0; i < view.hand.length; ++i) { + let k = view.hand[i] + let x = HAND_X + tile_space * i + let y = HAND_Y + ui.tiles[k].style.left = x + "px" + ui.tiles[k].style.top = y + "px" + ui.tiles[k].style.zIndex = i + } + + let rs = view.red_score + let bs = view.blue_score + let rs_x = rs % 8 + let bs_x = bs % 8 + let rs_y = rs >> 3 + let bs_y = bs >> 3 + + ui.red_score.style.left = (SCORE_X0 + SCORE_DX * rs_x) + "px" + ui.blue_score.style.left = (SCORE_X0 + SCORE_DX * bs_x) + "px" + if (rs === bs) { + ui.red_score.style.top = (SCORE_Y0 + SCORE_DY * rs_y - 21) + "px" + ui.blue_score.style.top = (SCORE_Y0 + SCORE_DY * bs_y + 8) + "px" + } else { + ui.red_score.style.top = (SCORE_Y0 + SCORE_DY * rs_y) + "px" + ui.blue_score.style.top = (SCORE_Y0 + SCORE_DY * bs_y) + "px" + } + + for (let e of action_register) + e.classList.toggle("action", is_action(e.my_action, e.my_id)) + + action_button("done", "Done") + action_button("undo", "Undo") +} + +scroll_with_middle_mouse("main") @@ -3,9 +3,9 @@ const RED = "Red" const BLUE = "Blue" -const states = {} - -var game, view +var states = {} +var game = null +var view = null exports.scenarios = [ "Standard" ] @@ -17,23 +17,31 @@ const TOKEN_RED_2 = 2 const TOKEN_BLUE_1 = 3 const TOKEN_BLUE_2 = 4 +const FIRST_SPACE = 0 const S_DARKNESS = 0 const S_GOLD = 1 const S_BLUE = 2 const S_WHITE = 3 const S_RED = 4 const S_PURPLE = 5 -const S_DRAGON = 6 -const S_OFF_BOARD = 7 +const LAST_TILE_SPACE = 5 +const S_DRAGON_1 = 6 +const LAST_OVAL_SPACE = 6 + +const S_DRAGON_2 = 7 +const S_OFF_BOARD_1 = 8 +const S_OFF_BOARD_2 = 9 +const S_OFF_BOARD_3 = 10 +const S_OFF_BOARD_4 = 11 -const TILE_NONE = 0 const FIRST_TILE = 1 +const TILE_NONE = 0 const TILE_BLUE = 1 const TILE_RED = 13 const TILE_GOLD = 25 const TILE_WHITE = 37 const TILE_GREEN = 49 -const LAST_TILE = 55 +const LAST_TILE = 54 function gen_action(action, argument) { if (!(action in view.actions)) @@ -58,22 +66,13 @@ exports.action = function (state, player, action, arg) { let S = states[game.state] if (action in S) S[action](arg, player) + else if (action === "undo" && game.undo && game.undo.length > 0) + pop_undo() else throw new Error("Invalid action: " + action) return game } -exports.resign = function (state, player) { - game = state - if (game.state !== 'game_over') { - if (player === RED) - goto_game_over(BLUE, "Red resigned.") - if (player === BLUE) - goto_game_over(RED, "Blue resigned.") - } - return game -} - exports.view = function(state, player) { game = state @@ -114,6 +113,37 @@ exports.view = function(state, player) { return view; } +exports.resign = function (state, player) { + game = state + if (game.state !== 'game_over') { + if (player === RED) + goto_game_over(BLUE, "Red resigned.") + if (player === BLUE) + goto_game_over(RED, "Blue resigned.") + } + return game +} + +function goto_game_over(result, victory) { + game.state = "game_over" + game.active = "None" + game.result = result + game.victory = victory + log_br() + log(game.victory) +} + +states.game_over = { + get inactive() { + return game.victory + }, + prompt() { + view.prompt = game.victory + }, +} + +// === PREPARATION === + exports.setup = function (seed, scenario, options) { game = { seed: seed, @@ -123,8 +153,8 @@ exports.setup = function (seed, scenario, options) { red_score: 0, blue_score: 0, - tokens: [ S_DRAGON, S_OFF_BOARD, S_OFF_BOARD, S_OFF_BOARD, S_OFF_BOARD ], - squares: [ 0, 0, 0, 0, 0, 0 ], + tokens: [ S_DRAGON_2, S_OFF_BOARD_1, S_OFF_BOARD_2, S_OFF_BOARD_3, S_OFF_BOARD_4 ], + squares: [ TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE ], darkness: [], red_court: [], blue_court: [], @@ -148,7 +178,7 @@ exports.setup = function (seed, scenario, options) { shuffle(game.darkness) - for (let i = 0; i < 6; ++i) + for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i) game.squares[i] = game.darkness.pop() if (random(2) === 0) @@ -161,24 +191,112 @@ exports.setup = function (seed, scenario, options) { return game } +// === HANDS AND COURTS === + +function rival_court() { + if (game.active === RED) + return game.blue_court + return game.red_court +} + +function own_court() { + if (game.active === RED) + return game.red_court + return game.blue_court +} + +function own_hand() { + if (game.active === RED) + return game.red_hand + return game.blue_hand +} + +function own_score() { + if (game.active === RED) + return game.red_score + return game.blue_score +} + +function gift_tile_in_space(to) { + // Gift associated tile to rival. + if (to <= LAST_TILE_SPACE) { + let tile = game.squares[to] + log("Gifted " + tile) + game.squares[to] = TILE_NONE + rival_court().push(tile) + } +} + +function score_own_points(n) { + log("Scored " + n) + if (game.active === RED) + game.red_score += n + if (game.active === BLUE) + game.blue_score += n + return (game.red_score >= 30) || (game.blue_score >= 30) +} + +function count_tiles(list, type) { + let n = 0 + for (let tile of list) + if (tile >= type && tile < type + 12) + ++n + return n +} + +function reveal_tiles_into_court(type) { + let hand = own_hand() + let court = own_court() + for (let i = 0; i < hand.length;) { + let tile = hand[i] + if (tile >= type && tile < type + 12) { + logi("Revealed " + tile) + array_remove(hand, i) + court.push(tile) + } else { + ++i + } + } +} + +function remove_tiles_from_court(type) { + let court = own_court() + for (let i = 0; i < court.length;) { + let tile = court[i] + if (tile >= type && tile < type + 12) { + logi("Removed " + tile) + array_remove(court, i) + } else { + ++i + } + } +} + +function is_oval_space_empty(s) { + for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i) + if (game.tokens[i] === s) + return false + return true +} + // === FLOW OF PLAY === states.move_token = { prompt() { view.prompt = "Move one of your tokens to an oval space." if (game.active === RED) { - if (game.tokens[TOKEN_RED_1] === S_OFF_BOARD) { + if (game.tokens[TOKEN_RED_1] >= S_OFF_BOARD_1) { gen_action_token(TOKEN_RED_1) - } else if (game.tokens[TOKEN_RED_2] === S_OFF_BOARD) { + } else if (game.tokens[TOKEN_RED_2] >= S_OFF_BOARD_1) { gen_action_token(TOKEN_RED_2) } else { gen_action_token(TOKEN_RED_1) gen_action_token(TOKEN_RED_2) } } else { - if (game.tokens[TOKEN_BLUE_1] === S_OFF_BOARD) { + if (game.tokens[TOKEN_BLUE_1] >= S_OFF_BOARD_1) { gen_action_token(TOKEN_BLUE_1) - } else if (game.tokens[TOKEN_BLUE_2] === S_OFF_BOARD) { + } else if (game.tokens[TOKEN_BLUE_2] >= S_OFF_BOARD_1) { gen_action_token(TOKEN_BLUE_2) } else { gen_action_token(TOKEN_BLUE_1) @@ -193,65 +311,232 @@ states.move_token = { }, } -function is_oval_space_empty(s) { - for (let i = 0; i < 5; ++i) - if (game.tokens[i] === s) - return false - return true -} - states.move_token_to = { prompt() { view.prompt = "Move your token to an oval space." view.selected_token = game.selected_token - for (let i = 0; i < 7; ++i) + for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i) if (is_oval_space_empty(i)) gen_action_space(i) + gen_action_token(game.selected_token) + }, + space(to) { + // Remember whence we came. + game.from = game.tokens[game.selected_token] + + log_h2(game.active + " MOVED TO " + to) + + // Move the token. + game.tokens[game.selected_token] = to + + // Gift associated tile to rival. + gift_tile_in_space(to) + + // Take action. + switch (to) { + case S_DRAGON_1: + return goto_dragon() + case S_DARKNESS: + return goto_secrecy() + case S_GOLD: + return goto_cloth_of_gold() + case S_BLUE: + return goto_banquets_and_feasts() + case S_WHITE: + return goto_godliness_and_piety() + case S_RED: + return goto_tournaments() + case S_PURPLE: + return goto_purple() + } + }, + token(t) { + pop_undo() }, - space(space) { - game.tokens[game.selected_token] = space - // take action! +} + +function end_turn() { + clear_undo() + + // Return Dragon if needed. + if (is_oval_space_empty(S_DRAGON_1)) + game.tokens[TOKEN_DRAGON] = S_DRAGON_2 + + // Refill Tiles if needed. + for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i) { + if (is_oval_space_empty(i) && game.squares[i] === 0) { + let tile = game.darkness.pop() + game.squares[i] = tile + if (game.darkness.length === 0) + return goto_end_of_the_contest() + } } + + // Play passes to the rival player. + if (game.active === RED) + game.active = BLUE + else + game.active = RED + game.state = "move_token" } // === THE ACTIONS: DRAGON === + +function goto_dragon() { + log("Dragon") + game.state = "dragon" +} + +states.dragon = { + prompt() { + view.selected_token = TOKEN_DRAGON + for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i) + if (i !== game.from && is_oval_space_empty(i)) + gen_action_space(i) + }, + space(to) { + game.tokens[TOKEN_DRAGON] = to + gift_tile_in_space(to) + end_turn() + }, +} + // === THE ACTIONS: SECRECY === + +const SECRECY_PER_ROW = [ 1, 2, 2, 3, 0 ] + +function gain_tile_from_darkness() { + let tile = game.darkness.pop() + own_hand().push(tile) + return game.darkness.length === 0 +} + +function goto_secrecy() { + log("Secrecy") + let score = own_score() + let row = (score >> 8) + let n = SECRECY_PER_ROW[row] + for (let i = 0; i < n; ++i) { + log("Gained Tile from Darkness.") + if (gain_tile_from_darkness()) { + return goto_end_of_the_contest() + } + } + end_turn() +} + // === THE ACTIONS: CLOTH OF GOLD === + +function goto_cloth_of_gold() { + log("Cloth of Gold") + reveal_tiles_into_court(TILE_GOLD) + let own = count_tiles(own_court(), TILE_GOLD) + let rival = count_tiles(rival_court(), TILE_GOLD) + if (own > rival) { + if (score_own_points(2)) + return goto_end_of_the_contest() + } + end_turn() +} + // === THE ACTIONS: BANQUETS AND FEASTS === + +function goto_banquets_and_feasts() { + log("Banquets & Feasts") + reveal_tiles_into_court(TILE_BLUE) + let own = count_tiles(own_court(), TILE_BLUE) + let score = 0 + if (own === 1) + score = 1 + else if (own === 2) + score = 3 + else if (own >= 3) + score = 6 + remove_tiles_from_court(TILE_BLUE) + if (score_own_points(score)) + return goto_end_of_the_contest() + end_turn() +} + // === THE ACTIONS: GODLINESS AND PIETY === + +function goto_godliness_and_piety() { + end_turn() +} + // === THE ACTIONS: TOURNAMENTS === + +function goto_tournaments() { + end_turn() +} + // === THE ACTIONS: COLLECTIONS === + +function goto_purple() { + end_turn() +} + // === END OF THE CONTEST === // === COMMON LIBRARY === +function log(msg) { + game.log.push(msg) +} + +function log_br() { + if (game.log.length > 0 && game.log[game.log.length - 1] !== "") + game.log.push("") +} + +function logi(msg) { + game.log.push(">" + msg) +} + +function log_h1(msg) { + log_br() + log(".h1 " + msg) + log_br() +} + +function log_h2(msg) { + log_br() + log(".h2 " + msg) + log_br() +} + function clear_undo() { - if (game.undo.length > 0) - game.undo = [] + if (game.undo) { + game.undo.length = 0 + } } function push_undo() { - let copy = {} - for (let k in game) { - let v = game[k] - if (k === "undo") - continue - else if (k === "log") - v = v.length - else if (typeof v === "object" && v !== null) - v = object_copy(v) - copy[k] = v + if (game.undo) { + let copy = {} + for (let k in game) { + let v = game[k] + if (k === "undo") + continue + else if (k === "log") + v = v.length + else if (typeof v === "object" && v !== null) + v = object_copy(v) + copy[k] = v + } + game.undo.push(copy) } - game.undo.push(copy) } function pop_undo() { - let save_log = game.log - let save_undo = game.undo - game = save_undo.pop() - save_log.length = game.log - game.log = save_log - game.undo = save_undo + if (game.undo) { + let save_log = game.log + let save_undo = game.undo + game = save_undo.pop() + save_log.length = game.log + game.log = save_log + game.undo = save_undo + } } function random(range) { |