diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-12-08 12:08:20 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-01-08 16:36:47 +0100 |
commit | f86d574e5256e11b5a41fd922033274ab7c4eb61 (patch) | |
tree | 461f6725417f3053cc9a0c640925b29c5a323df2 | |
parent | 897c0c2fcd534159b78381036076c48d2db43465 (diff) | |
download | table-battles-f86d574e5256e11b5a41fd922033274ab7c4eb61.tar.gz |
Highlight sticks to die.
-rw-r--r-- | play.html | 11 | ||||
-rw-r--r-- | play.js | 35 | ||||
-rw-r--r-- | rules.js | 94 |
3 files changed, 99 insertions, 41 deletions
@@ -116,13 +116,12 @@ main[data-scenario="5"] { .flip .slot_sticks { justify-content: start; + flex-direction: column-reverse; } .flip .slot_cubes { align-content: start; } -.table_reserve .slot_sticks { display: none } -.table_reserve .slot_cubes { display: none } .table_reserve .slot_dice { display: none } .stick { @@ -148,6 +147,10 @@ main[data-scenario="5"] { --lo-pink: hsl(359, 81%, 68%); --lo-blue: hsl(211, 78%, 42%); --lo-dkblue: hsl(240, 69%, 47%); + + --gray: hsl(0, 0%, 90%); + --lo-gray: hsl(0, 0%, 75%); + --hi-gray: hsl(0, 0%, 100%); } .red .stick { background-color: var(--red); } @@ -160,6 +163,8 @@ main[data-scenario="5"] { .blue .stick { border-color: var(--hi-blue) var(--lo-blue) var(--lo-blue) var(--hi-blue); } .dkblue .stick { border-color: var(--hi-dkblue) var(--lo-dkblue) var(--lo-dkblue) var(--hi-dkblue); } +.hit.stick { background-color: var(--gray); border-color: var(--hi-gray) var(--lo-gray) var(--lo-gray) var(--hi-gray); } + .table_pool { margin: 12px; height: 36px; @@ -238,9 +243,9 @@ main[data-scenario="5"] { } .die.action { box-shadow: 0 0 0 1px #333, 0 0 0px 3px white; } -.card.action { box-shadow: 0 0 0 3px whitesmoke; } .card.selected { box-shadow: 0 0 0px 3px gold; } .card.target { box-shadow: 0 0 0px 3px black; } +.card.action { box-shadow: 0 0 0 3px whitesmoke; } .action_type.action { border-color: white; @@ -20,6 +20,7 @@ let ui = { dice: [], sticks: [], + hit_sticks: [], cubes: [], } @@ -209,7 +210,7 @@ function create_formation_card(id) { function fill_card_row(top, parent, list) { parent.replaceChildren() for (let id of list) { - let i, n + let i, n, x if (!ui.cards[id]) ui.cards[id] = create_formation_card(id) @@ -225,7 +226,15 @@ function fill_card_row(top, parent, list) { add_cube(ui.slot_cubes[id]) n = map_get(view.sticks, id, 0) - for (let i = 0; i < n; ++i) + x = 0 + if (view.selected === id) + x = view.self + if (view.target === id) + x = view.hits + console.log("STICKS", id, x, n) + for (let i = 0; i < x; ++i) + add_hit_stick(ui.slot_sticks[id]) + for (let i = x; i < n; ++i) add_stick(ui.slot_sticks[id]) } } @@ -250,6 +259,16 @@ function add_stick(parent) { throw Error("OUT OF CUBES ERROR") } +function add_hit_stick(parent) { + for (let i = 0; i < 80; ++i) { + if (ui.hit_sticks[i].parentElement === null) { + parent.appendChild(ui.hit_sticks[i]) + return + } + } + throw Error("OUT OF CUBES ERROR") +} + function on_update() { let p1 = 0, p2 = 1 if (player === "First") @@ -269,6 +288,8 @@ function on_update() { e.remove() for (let e of ui.sticks) e.remove() + for (let e of ui.hit_sticks) + e.remove() for (let i = 0; i < view.morale[0]; ++i) add_cube(ui.morale[p1]) @@ -308,10 +329,14 @@ function on_update() { for (let e of animation_registry) animate_position(e) + action_button("attack", "Attack") action_button("bombard", "Bombard") + action_button("command", "Command") + action_button("screen", "Screen") + action_button("counterattack", "Counterattack") + action_button("absorb", "Absorb") action_button("roll", "Roll") action_button("pass", "Pass") - action_button("done", "Done") action_button("end_turn", "End turn") action_button("undo", "Undo") } @@ -321,8 +346,10 @@ for (let i = 0; i < 10; ++i) { // register_animation(ui.cubes[i], 500) } -for (let i = 0; i < 80; ++i) +for (let i = 0; i < 80; ++i) { ui.sticks[i] = create_div("stick") + ui.hit_sticks[i] = create_div("stick hit") +} for (let i = 0; i < 12; ++i) { ui.dice[i] = register_action(create_div("die d0"), "die", i) @@ -9,6 +9,7 @@ // TODO: manual "pursuit" ? // TODO: allow placing dice on full special formations? +// TODO: fizzle when action says to take cards from other dice? const data = require("./data.js") @@ -83,6 +84,10 @@ exports.view = function (state, player) { morale: game.morale, front: game.front, reserve: game.reserve, + selected: game.selected, + target: game.target, + hits: game.hits, + self: game.self, } if (game.state === "game_over") { @@ -133,8 +138,6 @@ states.game_over = { exports.setup = function (seed, scenario, options) { // TODO: "Random" - console.log("SETUP", scenario) - scenario = parseInt(scenario) scenario = data.scenarios.findIndex(s => s.number === scenario) if (scenario < 0) @@ -163,12 +166,18 @@ exports.setup = function (seed, scenario, options) { // cubes (map special formation -> count) cubes: [], - morale: [ info.players[0].morale, info.players[1].morale ], + morale: [ info.players[0].morale || -1, info.players[1].morale || -1 ], front: [ [], [], ], reserve: [ [], [] ], // dice value placed on what card placed: [], + + // current action + selected: -1, + target: -1, + hits: 0, + self: 0, } function setup_formation(front, reserve, c) { @@ -305,10 +314,10 @@ function set_dice_value(d, v) { function is_card_in_play(c) { return ( - set_has(game.players[0].front, c) || - set_has(game.players[1].front, c) || - set_has(game.players[0].reserve, c) || - set_has(game.players[1].reserve, c) + set_has(game.front[0], c) || + set_has(game.front[1], c) || + set_has(game.reserve[0], c) || + set_has(game.reserve[1], c) ) } @@ -317,24 +326,25 @@ function is_card_attack_with_target_in_play(c) { if (a.type === "Attack") { for (let t of a.target_list) if (is_card_in_play(c)) - return false + return true } } + return false } function check_impossible_to_attack_victory() { let p = player_index() - for (let c of game.players[p].front) + for (let c of game.front[p]) if (is_card_attack_with_target_in_play(c)) return false - for (let c of game.players[p].reserve) + for (let c of game.reserve[p]) if (is_card_attack_with_target_in_play(c)) return false return true } function check_morale_loss(p) { - return game.players[0].morale === 0 + return game.morale[0] === 0 } // === ROLL PHASE === @@ -790,7 +800,7 @@ states.place = { states.place_on_card = { prompt() { let card = data.cards[game.selected] - view.selected = game.selected + // XXX view.selected = game.selected view.prompt = "Place dice on " + card.name + "." gen_place_dice_select_card() @@ -811,8 +821,10 @@ states.place_on_card = { die(d) { push_undo() place_dice_take[data.cards[game.selected].dice](game.selected, d) - if (!can_place_dice(game.selected)) + if (!can_place_dice(game.selected)) { + game.selected = -1 game.state = "place" + } }, end_turn() { end_roll_phase() @@ -979,7 +991,7 @@ function can_take_any_action() { function goto_action_phase() { if (check_impossible_to_attack_victory()) { - if (player === P1) + if (player_index() === 0) goto_game_over(P2, P1 + " has no more attacks!") else goto_game_over(P1, P2 + " has no more attacks!") @@ -1076,7 +1088,7 @@ function goto_take_action(c, ix) { game.action = ix switch (a.type) { case "Attack": - game.state = "attack" + goto_attack() break case "Bombard": game.state = "bombard" @@ -1125,9 +1137,8 @@ states.bombard = { } function format_attack_result() { - let a = current_action() - let hits = get_attack_hits(game.selected, a) - let self = get_attack_self(game.selected, a) + let hits = game.hits + let self = game.self if (hits !== 1 && self > 0) return ` ${hits} hits. ${self} self.` if (hits === 1 && self > 0) @@ -1139,19 +1150,23 @@ function format_attack_result() { return "" } +function goto_attack(c) { + let a = current_action() + game.state = "attack" + game.target = find_target_of_attack(a) + game.hits = get_attack_hits(game.selected, a) + game.self = get_attack_self(game.selected, a) +} + states.attack = { prompt() { - let t = find_target_of_attack(current_action()) - view.prompt = "Attack " + card_name(t) + "." + format_attack_result() - view.selected = game.selected - gen_action_card(t) + view.prompt = "Attack " + card_name(game.target) + "." + format_attack_result() + // XXX view.selected = game.selected + gen_action_card(game.target) + view.actions.attack = 1 }, card(c) { log(card_name(game.selected) + " attacked " + card_name(c) + ".") - let a = current_action() - game.target = c - game.hits = get_attack_hits(game.selected, a) - game.self = get_attack_self(game.selected, a) if (can_opponent_react()) { clear_undo() set_opponent_active() @@ -1165,8 +1180,11 @@ states.attack = { function resume_attack() { apply_hits(game.hits) apply_self(game.self) - game.hits = game.self = 0 pay_for_action(game.selected) + + game.hits = game.self = 0 + game.selected = -1 + game.target = -1 end_action_phase() } @@ -1174,17 +1192,15 @@ states.command = { prompt() { let t = find_target_of_command(current_action()) view.prompt = "Bring " + card_name(t) + " out of reserve." - view.selected = game.selected + // XXX view.selected = game.selected gen_action_card(t) }, card(c) { log(card_name(game.selected) + " commanded " + card_name(c) + " out of reserve.") - console.log("PRE COMMAND", JSON.stringify(game)) let p = player_index() array_remove_item(game.reserve[p], c) set_add(game.front[p], c) pay_for_action(game.selected) - console.log("POST COMMAND", JSON.stringify(game)) end_action_phase() }, } @@ -1256,8 +1272,8 @@ function can_take_reaction(c, a) { states.react = { prompt() { view.prompt = card_name(game.selected) + " attacks " + card_name(game.target) + "! " + format_attack_result() - view.selected = game.selected - view.target = game.target + // XXX view.selected = game.selected + // XXX view.target = game.target let voluntary = true let p = player_index() @@ -1320,9 +1336,19 @@ function goto_screen(c, a) { log(card_name(c) + " screened.") game.reacted = 1 pay_for_action(c) + + switch (a.effect) + { + default: + throw new Error("invalid screen effect: " + a.effect) + case "": + game.hits = 0 + game.self = 0 + break + } + set_opponent_active() - pay_for_action(game.selected) - end_action_phase() + resume_attack() } function goto_absorb(c, a) { |