diff options
-rw-r--r-- | create.html | 16 | ||||
-rw-r--r-- | play.js | 1 | ||||
-rw-r--r-- | rules.js | 305 |
3 files changed, 196 insertions, 126 deletions
diff --git a/create.html b/create.html index 6c38f65..8244612 100644 --- a/create.html +++ b/create.html @@ -8,6 +8,18 @@ </dl> <p> -<label><input type="checkbox" name="autohit" value="true"> -Automatically apply combat hits when possible. +<label><input type="checkbox" id="delay_hits" name="delay_hits" value="true"> +Fire with all blocks before assigning hits. </label> + +<br> +<label><input type="checkbox" id="autohit" name="autohit" value="true"> +Automatically assign hits when possible. +</label> + +<script> +let cb1 = document.getElementById("autohit") +let cb2 = document.getElementById("delay_hits") +cb1.onchange = function () { if (cb1.checked) cb2.checked = false } +cb2.onchange = function () { if (cb2.checked) cb1.checked = false } +</script> @@ -693,6 +693,7 @@ function on_update() { action_button("end_disbanding", "End disbanding") action_button("end_builds", "End builds") action_button("end_pillage", "End pillage") + action_button("assign", "Assign hits") action_button("pass", "Pass") action_button("undo", "Undo") @@ -1596,6 +1596,12 @@ function end_move() { // BATTLE PHASE +function bring_on_reserves() { + for (let b = 0; b < block_count; ++b) + if (game.location[b] === game.where) + set_delete(game.reserves, b) +} + function goto_battle_phase() { if (have_contested_areas()) { game.active = game.p1 @@ -1619,6 +1625,11 @@ states.battle_phase = { }, } +function print_retreat_summary() { + if (game.turn_log && game.turn_log.length > 0) + print_turn_log_no_active("Retreated from " + area_tag(game.where) + ":") +} + function start_battle(where, reason) { game.battle_active = game.active game.battle_reason = reason @@ -1631,20 +1642,10 @@ function start_battle(where, reason) { game.where = where game.battle_round = 0 game.state = 'battle_round' - start_battle_round() -} - -function resume_battle() { - if (game.victory) - return goto_game_over() - game.state = 'battle_round' - pump_battle_round() + next_battle_round() } function end_battle() { - if (game.turn_log && game.turn_log.length > 0) - print_turn_log_no_active("Retreated from " + area_tag(game.where) + ":") - game.flash = "" game.battle_round = 0 reset_border_limits() @@ -1661,99 +1662,150 @@ function end_battle() { goto_retreat() } -function bring_on_reserves() { - for (let b = 0; b < block_count; ++b) - if (game.location[b] === game.where) - set_delete(game.reserves, b) +function next_battle_round() { + print_retreat_summary() + switch (game.battle_round) { + case 0: return goto_battle_round(1) + case 1: return goto_battle_round(2) + case 2: return goto_battle_round(3) + case 3: return end_battle() + } } -function start_battle_round() { - if (++game.battle_round <= 3) { - if (game.turn_log && game.turn_log.length > 0) - print_turn_log_no_active("Retreated from " + area_tag(game.where) + ":") - game.turn_log = [] +function goto_battle_round(new_battle_round) { + game.battle_round = new_battle_round + game.turn_log = [] - log(".h4 Battle Round " + game.battle_round) + log(".h4 Battle Round " + game.battle_round) + + reset_border_limits() + game.moved = [] - reset_border_limits() - game.moved = [] + if (game.battle_round === 1) { + for (let b of CELTIC_BLOCKS) + if (game.location[b] === game.where && !is_battle_reserve(b)) + celtic_unity_roll(b) + } - if (game.battle_round === 1) { - for (let b of CELTIC_BLOCKS) - if (game.location[b] === game.where && !is_battle_reserve(b)) - celtic_unity_roll(b) - } - if (game.battle_round === 2) { - if (count_defenders() === 0) { - log("Defending main force was eliminated.") - log("Battlefield control changed.") - game.attacker[game.where] = ENEMY[game.attacker[game.where]] - } else if (count_attackers() === 0) { - log("Attacking main force was eliminated.") - } - for (let b of CELTIC_BLOCKS) - if (game.location[b] === game.where && is_battle_reserve(b)) - celtic_unity_roll(b) - bring_on_reserves() - } - if (game.battle_round === 3) { - bring_on_reserves() + if (game.battle_round === 2) { + if (count_defenders() === 0) { + log("Defending main force was eliminated.") + log("Battlefield control changed.") + game.attacker[game.where] = ENEMY[game.attacker[game.where]] + } else if (count_attackers() === 0) { + log("Attacking main force was eliminated.") } + for (let b of CELTIC_BLOCKS) + if (game.location[b] === game.where && is_battle_reserve(b)) + celtic_unity_roll(b) + bring_on_reserves() + } - pump_battle_round() - } else { - end_battle() + if (game.battle_round === 3) { + bring_on_reserves() } + + resume_battle() } -function pump_battle_round() { - function filter_battle_blocks(ci, is_candidate) { - let output = null - for (let b = 0; b < block_count; ++b) { - if (is_candidate(b) && !set_has(game.moved, b)) { - if (block_initiative(b) === ci) { - if (!output) - output = [] - output.push(b) - } +function resume_battle() { + if (game.victory) + return goto_game_over() + + if (is_friendly_area(game.where) || is_enemy_area(game.where)) + return end_battle() + + if (count_attackers() === 0 || count_defenders() === 0) + return next_battle_round() + + game.state = 'battle_round' + pump_battle_step() +} + +function filter_battle_blocks(ci, is_candidate) { + let output = null + for (let b = 0; b < block_count; ++b) { + if (is_candidate(b) && !set_has(game.moved, b)) { + if (block_initiative(b) === ci) { + if (!output) + output = [] + output.push(b) } } - return output } + return output +} - function battle_step(active, initiative, candidate) { - game.battle_list = filter_battle_blocks(initiative, candidate) - if (game.battle_list) { +function battle_step(active, initiative, candidate) { + game.battle_list = filter_battle_blocks(initiative, candidate) + if (game.battle_list) { + if (game.active !== active) { game.active = active - return true + if (game.delay_hits && game.hits > 0) { + goto_battle_hits() + return true + } } - return false + return true } + return false +} - if (is_friendly_area(game.where) || is_enemy_area(game.where)) { - end_battle() - } else if (count_attackers() === 0 || count_defenders() === 0) { - start_battle_round() - } else { - let attacker = game.attacker[game.where] - let defender = ENEMY[attacker] +function pump_battle_step() { + let attacker = game.attacker[game.where] + let defender = ENEMY[attacker] - if (battle_step(defender, 'A', is_defender)) return - if (battle_step(attacker, 'A', is_attacker)) return - if (battle_step(defender, 'B', is_defender)) return - if (battle_step(attacker, 'B', is_attacker)) return - if (battle_step(defender, 'C', is_defender)) return - if (battle_step(attacker, 'C', is_attacker)) return + if (battle_step(defender, 'A', is_defender)) return + if (battle_step(attacker, 'A', is_attacker)) return + if (battle_step(defender, 'B', is_defender)) return + if (battle_step(attacker, 'B', is_attacker)) return + if (battle_step(defender, 'C', is_defender)) return + if (battle_step(attacker, 'C', is_attacker)) return - start_battle_round() + if (game.delay_hits && game.hits > 0) { + game.active = ENEMY[game.active] + return goto_battle_hits() } + + next_battle_round() } -function pass_with_block(b) { - game.flash = block_name(b) + " passed." - log_battle(block_name(b) + " passed.") - set_add(game.moved, b) - resume_battle() +states.battle_round = { + show_battle: true, + prompt: function (view, current) { + if (is_inactive_player(current)) + return view.prompt = "Waiting for " + game.active + " to choose a combat action." + view.prompt = "Fire, retreat, or pass with an army." + for (let b of game.battle_list) { + gen_action_block(view, b) + gen_action_battle(view, 'battle_fire', b) + gen_action_battle(view, 'battle_pass', b) + if (can_block_retreat(b)) + gen_action_battle(view, 'battle_retreat', b) + } + if (game.delay_hits && game.hits > 0) + gen_action(view, 'assign') + }, + assign: function () { + game.active = ENEMY[game.active] + if (game.hits === 1) + game.flash = `Inflicted 1 hit.` + else + game.flash = `Inflicted ${game.hits} hits.` + goto_battle_hits() + }, + block: function (who) { + fire_with_block(who) + }, + battle_fire: function (who) { + fire_with_block(who) + }, + battle_retreat: function (who) { + retreat_with_block(who) + }, + battle_pass: function (who) { + pass_with_block(who) + } } function retreat_with_block(b) { @@ -1761,6 +1813,13 @@ function retreat_with_block(b) { game.state = 'retreat_in_battle' } +function pass_with_block(b) { + game.flash = block_name(b) + " passed." + log_battle(block_name(b) + " passed.") + set_add(game.moved, b) + resume_battle() +} + function fire_with_block(b) { set_add(game.moved, b) let steps = game.steps[b] @@ -1771,60 +1830,47 @@ function fire_with_block(b) { name += "+" + (fire - printed_fire) let rolls = [] - game.hits = 0 + let hits = 0 for (let i = 0; i < steps; ++i) { let die = roll_d6() if (die <= fire) { rolls.push(DIE_HIT[die]) - ++game.hits + ++hits } else { rolls.push(DIE_MISS[die]) } } - - game.flash = name + " fired " + rolls.join(" ") + " " - if (game.hits === 0) - game.flash += "and missed." - else if (game.hits === 1) - game.flash += "and scored 1 hit." - else - game.flash += "and scored " + game.hits + " hits." + game.hits += hits log_battle(name + " fired " + rolls.join("") + ".") - if (game.hits > 0) { - game.active = ENEMY[game.active] - goto_battle_hits() + if (game.delay_hits) { + game.flash = name + " fired " + rolls.join(" ") + if (game.hits === 0) + game.flash += "." + else if (game.hits === 1) + game.flash += " for a total of 1 hit." + else if (game.hits > 1) + game.flash += " for a total of " + game.hits + " hits." } else { - resume_battle() + game.flash = name + " fired " + rolls.join(" ") + if (hits === 0) + game.flash += " and missed." + else if (hits === 1) + game.flash += " and scored 1 hit." + else + game.flash += " and scored " + hits + " hits." } -} -states.battle_round = { - show_battle: true, - prompt: function (view, current) { - if (is_inactive_player(current)) - return view.prompt = "Waiting for " + game.active + " to choose a combat action." - view.prompt = "Fire, retreat, or pass with an army." - for (let b of game.battle_list) { - gen_action_block(view, b) - gen_action_battle(view, 'battle_fire', b) - gen_action_battle(view, 'battle_pass', b) - if (can_block_retreat(b)) - gen_action_battle(view, 'battle_retreat', b) + if (hits > 0) { + if (!game.delay_hits) { + game.active = ENEMY[game.active] + goto_battle_hits() + } else { + resume_battle() } - }, - block: function (who) { - fire_with_block(who) - }, - battle_fire: function (who) { - fire_with_block(who) - }, - battle_retreat: function (who) { - retreat_with_block(who) - }, - battle_pass: function (who) { - pass_with_block(who) + } else { + resume_battle() } } @@ -1848,10 +1894,12 @@ function goto_battle_hits() { game.flash += ` Assigned ${n}.` } - if (game.battle_list.length === 0) + if (game.battle_list.length === 0) { + game.hits = 0 resume_battle() - else + } else { game.state = 'battle_hits' + } } function apply_hit(who) { @@ -1865,10 +1913,12 @@ function apply_hit(who) { resume_battle() else { game.battle_list = list_victims(game.active) - if (game.battle_list.length === 0) + if (game.battle_list.length === 0) { + game.hits = 0 resume_battle() - else + } else { game.flash += " " + game.hits + (game.hits === 1 ? " hit left." : " hits left.") + } } } @@ -1936,6 +1986,8 @@ states.retreat = { for (let b = 0; b < block_count; ++b) if (game.location[b] === game.where && block_owner(b) === game.active) eliminate_block(b, 'retreat') + if (game.victory) + return goto_game_over() print_turn_log("retreated") goto_regroup() }, @@ -1988,6 +2040,8 @@ states.retreat_to = { }, eliminate: function () { eliminate_block(game.who, 'retreat') + if (game.victory) + return goto_game_over() game.who = NOBODY game.state = 'retreat' }, @@ -2974,6 +3028,7 @@ exports.setup = function (seed, scenario, options) { log: [], undo: [], moves: 0, + hits: 0, location: [], steps: [], @@ -3004,6 +3059,8 @@ exports.setup = function (seed, scenario, options) { if (options.autohit) game.autohit = 1 + if (options.delay_hits) + game.delay_hits = 1 log(".h1 " + scenario) start_year() |