diff options
-rw-r--r-- | data.js | 2 | ||||
-rw-r--r-- | play.html | 4 | ||||
-rw-r--r-- | rules.js | 115 | ||||
-rw-r--r-- | ui.js | 66 |
4 files changed, 98 insertions, 89 deletions
@@ -127,7 +127,7 @@ const PORTS = []; if (order == 'Nomads') id += " " + nomads[name]++; if (name == 'Reynald' || name == 'Raymond') - id += " " + home; + id += " (" + home + ")"; if (id in BLOCKS) throw Error("Name clash: " + id + " order:"+order + " " + JSON.stringify(nomads)); BLOCKS[id] = { @@ -112,12 +112,12 @@ opacity: 0.8; z-index: 9; } -.town.where { +.town.muster { opacity: 0.8; border-color: blue; z-index: 9; } -.town.where.highlight { +.town.muster.highlight { box-shadow: 0 0 2px 4px white; } @@ -22,8 +22,9 @@ const DEAD = "Dead"; const F_POOL = "F. Pool"; const S_POOL = "S. Pool"; -const HIT_TEXT = "\u2605"; -const MISS_TEXT = "\u2606"; +// serif cirled numbers +const DIE_HIT = [ 0, '\u2776', '\u2777', '\u2778', '\u2779', '\u277A', '\u277B' ]; +const DIE_MISS = [ 0, '\u2460', '\u2461', '\u2462', '\u2463', '\u2464', '\u2465' ]; const ATTACK_MARK = "*"; const RESERVE_MARK_1 = "\u2020"; @@ -180,7 +181,7 @@ function deal_cards(deck, n) { } function block_name(who) { - return BLOCKS[who].name; + return who; // BLOCKS[who].name; } function block_type(who) { @@ -414,6 +415,16 @@ function can_block_retreat_to(who, to) { return false; } +function can_block_retreat(who) { + if (block_owner(who) == game.active) { + let from = game.location[who]; + for (let to of TOWNS[from].exits) + if (can_block_retreat_to(who, to)) + return true; + } + return false; +} + function can_block_regroup_to(who, to) { if (is_friendly_town(to) || is_vacant_town(to)) { let from = game.location[who]; @@ -535,20 +546,6 @@ function reduce_block(who) { } } -function filter_battle_blocks(ci, is_candidate) { - let output = null; - for (let b in BLOCKS) { - if (is_candidate(b) && !game.moved[b]) { - if (block_initiative(b) == ci) { - if (!output) - output = []; - output.push(b); - } - } - } - return output; -} - function count_attackers() { let count = 0; for (let b in BLOCKS) @@ -987,6 +984,7 @@ states.muster_who = { if (is_inactive_player(current)) return view.prompt = "Waiting for " + game.active + " to move."; view.prompt = "Muster: Move blocks to " + game.where + "."; + view.muster = game.where; gen_action_undo(view); gen_action(view, 'end_muster'); for (let b in BLOCKS) @@ -1010,6 +1008,7 @@ states.muster_move_1 = { if (is_inactive_player(current)) return view.prompt = "Waiting for " + game.active + " to move."; view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + "."; + view.muster = game.where; gen_action_undo(view); gen_action(view, 'block', game.who); let from = game.location[game.who]; @@ -1050,6 +1049,7 @@ states.muster_move_2 = { if (is_inactive_player(current)) return view.prompt = "Waiting for " + game.active + " to move."; view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + "."; + view.muster = game.where; gen_action_undo(view); let from = game.location[game.who]; let muster = game.where; @@ -1087,6 +1087,7 @@ states.muster_move_3 = { if (is_inactive_player(current)) return view.prompt = "Waiting for " + game.active + " to move."; view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + "."; + view.muster = game.where; gen_action_undo(view); let from = game.location[game.who]; let muster = game.where; @@ -1156,9 +1157,9 @@ function start_battle(where) { } function resume_battle() { + game.who = null; if (game.victory) return goto_game_over(); - game.who = null; game.state = 'battle_round'; pump_battle_round(); } @@ -1220,20 +1221,34 @@ function start_battle_round() { } function pump_battle_round() { + function filter_battle_blocks(ci, is_candidate) { + let output = null; + for (let b in BLOCKS) { + if (is_candidate(b) && !game.moved[b]) { + if (block_initiative(b) == ci) { + if (!output) + output = []; + output.push(b); + } + } + } + return output; + } + + function battle_step(active, initiative, candidate) { + game.battle_list = filter_battle_blocks(initiative, candidate); + if (game.battle_list) { + game.active = active; + return true; + } + return false; + } + if (is_friendly_town(game.where) || is_enemy_town(game.where)) { end_battle(); } else if (count_attackers() == 0 || count_defenders() == 0) { start_battle_round(); } else { - function battle_step(active, initiative, candidate) { - game.battle_list = filter_battle_blocks(initiative, candidate); - if (game.battle_list) { - game.active = active; - return true; - } - return false; - } - let attacker = game.attacker[game.where]; let defender = ENEMY[attacker]; @@ -1253,34 +1268,39 @@ function retreat_with_block(b) { game.state = 'retreat_in_battle'; } -function roll_attack(verb, b) { +function roll_attack(active, b, verb) { game.hits = 0; let fire = block_fire_power(b, game.where); let printed_fire = block_printed_fire_power(b); let rolls = []; - let results = []; let steps = game.steps[b]; + let name = block_name(b) + " " + BLOCKS[b].combat; + if (fire > printed_fire) + name += "+" + (fire - printed_fire); for (let i = 0; i < steps; ++i) { let die = roll_d6(); - rolls.push(die); if (die <= fire) { - results.push(HIT_TEXT); + rolls.push(DIE_HIT[die]); ++game.hits; } else { - results.push(MISS_TEXT); + rolls.push(DIE_MISS[die]); } } - game.flash += block_name(b) + " " + BLOCKS[b].combat; - if (fire > printed_fire) - game.flash += "+" + (fire - printed_fire); - game.flash += " " + verb + "\n" + rolls.join(" ") + " = " + results.join(" "); + + game.flash = name + " " + verb + " " + rolls.join(" ") + " "; + if (game.hits == 0) + game.flash += "and misses."; + else if (game.hits == 1) + game.flash += "and scores 1 hit."; + else + game.flash += "and scores " + game.hits + " hits."; + + log(active[0] + ": " + name + " " + verb + " " + rolls.join("") + "."); } function fire_with_block(b) { game.moved[b] = true; - game.flash = ""; - roll_attack("fires", b); - log(game.flash); + roll_attack(game.active, b, "fires"); if (game.hits > 0) { game.active = ENEMY[game.active]; goto_battle_hits(); @@ -1334,17 +1354,17 @@ function goto_battle_hits() { function apply_hit(who) { game.flash = block_name(who) + " takes a hit."; - log(game.flash); reduce_block(who, 'combat'); game.hits--; - if (game.hits == 1) - game.flash += " 1 hit left."; - else if (game.hits > 1) - game.flash += " " + game.hits + " hits left."; if (game.hits == 0) resume_battle(); - else - goto_battle_hits(); + else { + game.battle_list = list_victims(game.active); + if (game.battle_list.length == 0) + resume_battle(); + else + game.flash += " " + game.hits + (game.hits == 1 ? " hit left." : " hits left."); + } } function list_victims(p) { @@ -1468,6 +1488,7 @@ states.retreat_in_battle = { gen_action(view, 'town', to); }, town: function (to) { + game.flash = block_name(game.who) + " retreats."; logp("retreats to " + to + "."); game.location[game.who] = to; resume_battle(); @@ -1488,7 +1509,6 @@ function goto_regroup() { game.active = game.attacker[game.where]; if (is_enemy_town(game.where)) game.active = ENEMY[game.active]; - log(game.active + " wins the battle in " + game.where + "."); game.state = 'regroup'; game.turn_log = []; } @@ -1533,6 +1553,7 @@ states.regroup_to = { gen_action(view, 'town', to); }, town: function (to) { + let from = game.where; game.turn_log.push([from, to]); move_block(game.who, game.where, to); game.who = null; @@ -69,14 +69,13 @@ function on_blur_town(evt) { function on_click_town(evt) { let where = evt.target.town; - if (game.actions && game.actions.town && game.actions.town.includes(where)) - socket.emit('action', 'town', where); + send_action('town', where); } const STEP_TEXT = [ 0, "I", "II", "III", "IIII" ]; const HEIR_TEXT = [ 0, '\u00b9', '\u00b2', '\u00b3', '\u2074', '\u2075' ]; -function block_name(who) { return BLOCKS[who].name; } +function block_name(who) { return who; } function block_home(who) { return BLOCKS[who].home; } function block_owner(who) { return BLOCKS[who].owner; } @@ -96,7 +95,7 @@ function on_click_secret_block(evt) { function on_focus_map_block(evt) { let b = evt.target.block; let s = game.known[b][1]; - let text = block_name(b) + " (" + block_home(b) + ") "; + let text = block_name(b) + " "; if (BLOCKS[b].move) text += BLOCKS[b].move + "-"; text += STEP_TEXT[s] + "-" + BLOCKS[b].combat; @@ -109,8 +108,8 @@ function on_blur_map_block(evt) { function on_click_map_block(evt) { let b = evt.target.block; - if (game.actions && game.actions.block && game.actions.block.includes(b)) - socket.emit('action', 'block', b); + if (!game.battle) + send_action('block', b); } function is_battle_reserve(who, list) { @@ -144,8 +143,7 @@ function on_blur_battle_block(evt) { function on_click_battle_block(evt) { let b = evt.target.block; - if (game.actions && game.actions.block && game.actions.block.includes(b)) - socket.emit('action', 'block', b); + send_action('block', b); } function on_focus_battle_fire(evt) { @@ -177,16 +175,15 @@ function on_blur_battle_button(evt) { document.getElementById("status").textContent = ""; } -function on_click_battle_hit(evt) { socket.emit('action', 'battle_hit', evt.target.block); } -function on_click_battle_fire(evt) { socket.emit('action', 'battle_fire', evt.target.block); } -function on_click_battle_retreat(evt) { socket.emit('action', 'battle_retreat', evt.target.block); } -function on_click_battle_charge(evt) { socket.emit('action', 'battle_charge', evt.target.block); } -function on_click_battle_harry(evt) { socket.emit('action', 'battle_harry', evt.target.block); } +function on_click_battle_hit(evt) { send_action('battle_hit', evt.target.block); } +function on_click_battle_fire(evt) { send_action('battle_fire', evt.target.block); } +function on_click_battle_retreat(evt) { send_action('battle_retreat', evt.target.block); } +function on_click_battle_charge(evt) { send_action('battle_charge', evt.target.block); } +function on_click_battle_harry(evt) { send_action('battle_harry', evt.target.block); } function on_click_card(evt) { let c = evt.target.id.split("+")[1] | 0; - if (game.actions && game.actions.play && game.actions.play.includes(c)) - socket.emit('action', 'play', c); + send_action('play', c); } function on_button_undo(evt) { send_action('undo'); } @@ -277,41 +274,32 @@ function build_secret_block(b, block, secret_index) { return element; } -/* -let MAP_OFFSET_X = 30; -let MAP_OFFSET_Y = 30; -let MAP_HEIGHT = 1215; -*/ -let MAP_OFFSET_X = 0; -let MAP_OFFSET_Y = 0; -let MAP_HEIGHT = 1275; - function town_x(t) { if (map_orientation == 'tall') - return TOWNS[t].x - MAP_OFFSET_X; + return TOWNS[t].x; else - return TOWNS[t].y - MAP_OFFSET_Y; + return TOWNS[t].y; } function town_y(t) { if (map_orientation == 'tall') - return TOWNS[t].y - MAP_OFFSET_Y; + return TOWNS[t].y; else - return MAP_HEIGHT - TOWNS[t].x + MAP_OFFSET_X; + return 1275 - TOWNS[t].x; } function flip_x(x, y) { if (map_orientation == 'tall') - return x - MAP_OFFSET_X; + return x; else - return y - MAP_OFFSET_Y; + return y; } function flip_y(x, y) { if (map_orientation == 'tall') - return y - MAP_OFFSET_Y; + return y; else - return MAP_HEIGHT - x + MAP_OFFSET_X; + return 1275 - x; } function build_town(t, town) { @@ -465,11 +453,11 @@ function position_block_stacked(location, i, c, k, element) { let block_size = 60+6; let x, y; if (map_orientation == 'tall') { - x = space.x + (i - c) * 12 + k * 12; - y = space.y + (i - c) * 16 - k * 8; + x = space.x + (i - c) * 16 + k * 12; + y = space.y + (i - c) * 16 - k * 12; } else { - x = space.x - (i - c) * 12 + k * 12; - y = space.y + (i - c) * 16 + k * 8; + x = space.x - (i - c) * 16 + k * 12; + y = space.y + (i - c) * 16 + k * 12; } element.style.left = ((flip_x(x,y) - block_size/2)|0)+"px"; element.style.top = ((flip_y(x,y) - block_size/2)|0)+"px"; @@ -597,14 +585,14 @@ function update_map() { for (let where in TOWNS) { if (ui.towns[where]) { ui.towns[where].classList.remove('highlight'); - ui.towns[where].classList.remove('where'); + ui.towns[where].classList.remove('muster'); } } if (game.actions && game.actions.town) for (let where of game.actions.town) ui.towns[where].classList.add('highlight'); - if (game.where) - ui.towns[game.where].classList.add('where'); + if (game.muster) + ui.towns[game.where].classList.add('muster'); for (let b in BLOCKS) { ui.known[b].classList.remove('highlight'); |