From dbf5c2ef44b6ea1ff9e37098517f8bbbda2bb479 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 23 Oct 2022 20:49:49 +0200 Subject: Delay hits option. --- create.html | 12 +++++- play.js | 1 + rules.js | 119 +++++++++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 104 insertions(+), 28 deletions(-) diff --git a/create.html b/create.html index a9b870a..a6a2b53 100644 --- a/create.html +++ b/create.html @@ -1,5 +1,3 @@ -

-Optional rules:

The road between Antioch and Harim has a move limit of 3.
+ +
+
+ +
Let all blocks with the same initiative fire before assigning hits. +
This option is recommended for games that are not played live. +
diff --git a/play.js b/play.js index a9cddba..ae68ea2 100644 --- a/play.js +++ b/play.js @@ -861,6 +861,7 @@ function on_update() { action_button("end_retreat", "End retreat") action_button("end_regroup", "End regroup") action_button("end_move_phase", "End move phase") + action_button("assign", "Assign hits") action_button("pass", "Pass") action_button("next", "Next") action_button("undo", "Undo") diff --git a/rules.js b/rules.js index 281ba00..5d33bb6 100644 --- a/rules.js +++ b/rules.js @@ -2186,6 +2186,7 @@ function start_combat() { game.halfhit = NOBODY game.storming = [] game.sallying = [] + game.hits = 0 game.show_castle = 0 game.show_field = 0 @@ -2744,12 +2745,26 @@ function filter_battle_blocks(ci, is_candidate) { function battle_step(active, initiative, candidate) { game.battle_list = filter_battle_blocks(initiative, candidate) if (game.battle_list) { - game.active = active + if (game.active !== active) { + game.active = active + if (game.delay_hits && game.hits > 0) { + goto_battle_hits() + } + } return true } return false } +function goto_battle_hits() { + if (game.state === 'field_battle') + goto_field_battle_hits() + else if (game.state === 'siege_battle') + goto_siege_battle_hits() + else + throw new Error("invalid battle state") +} + function pump_battle_step(is_candidate_attacker, is_candidate_defender) { let attacker = game.attacker[game.where] let defender = enemy(attacker) @@ -2770,6 +2785,11 @@ function pump_battle_step(is_candidate_attacker, is_candidate_defender) { if (battle_step(attacker, 'C', is_candidate_attacker)) return } + if (game.delay_hits && game.hits > 0) { + game.active = enemy(game.active) + return goto_battle_hits() + } + next_combat_round() } @@ -2788,6 +2808,7 @@ function resume_field_battle() { return } + let save_active = game.active game.active = game.attacker[game.where] if (is_friendly_field(game.where)) { @@ -2822,6 +2843,7 @@ function resume_field_battle() { return next_combat_round() } + game.active = save_active game.state = 'field_battle' game.show_field = 1 pump_battle_step(is_field_attacker, is_field_defender) @@ -2858,6 +2880,16 @@ states.field_battle = { if (block_owner(b) === FRANKS && block_initiative(b) === 'B') gen_action(view, 'charge', 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_field_battle_hits() }, block: field_fire_with_block, fire: field_fire_with_block, @@ -2876,6 +2908,7 @@ function goto_siege_battle() { } function resume_siege_battle() { + let save_active = game.active game.active = game.attacker[game.where] if (is_friendly_town(game.where)) { @@ -2906,6 +2939,7 @@ function resume_siege_battle() { return next_combat_round() } + game.active = save_active game.state = 'siege_battle' pump_battle_step(is_siege_attacker, is_siege_defender) } @@ -2922,6 +2956,16 @@ states.siege_battle = { if (set_has(game.storming, b)) gen_action(view, '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_siege_battle_hits() }, block: siege_fire_with_block, fire: siege_fire_with_block, @@ -2931,12 +2975,13 @@ states.siege_battle = { // FIELD BATTLE HITS function goto_field_battle_hits() { - game.active = enemy(game.active) game.battle_list = list_field_victims() - if (game.battle_list.length === 0) + if (game.battle_list.length === 0) { + game.hits = 0 resume_field_battle() - else + } else { game.state = 'field_battle_hits' + } } function list_field_victims() { @@ -2982,22 +3027,25 @@ function apply_field_battle_hit(who) { resume_field_battle() else { game.battle_list = list_field_victims() - if (game.battle_list.length === 0) + if (game.battle_list.length === 0) { + game.hits = 0 resume_field_battle() - else + } else { game.flash += " " + game.hits + (game.hits === 1 ? " hit left." : " hits left.") + } } } // SIEGE BATTLE HITS function goto_siege_battle_hits() { - game.active = enemy(game.active) game.battle_list = list_siege_victims() - if (game.battle_list.length === 0) + if (game.battle_list.length === 0) { + game.hits = 0 resume_siege_battle() - else + } else { game.state = 'siege_battle_hits' + } } function list_siege_victims() { @@ -3059,6 +3107,7 @@ function apply_siege_battle_hit(who) { } else { game.battle_list = list_siege_victims() if (game.battle_list.length === 0) { + game.hits = 0 resume_siege_battle() } else { game.flash += " " + game.hits + (game.hits === 1 ? " hit left." : " hits left.") @@ -3069,17 +3118,18 @@ function apply_siege_battle_hit(who) { // BATTLE ACTIONS function roll_attack(active, b, verb, is_charge) { - game.hits = 0 let fire = block_fire_power(b, game.where) + is_charge - let rolls = [] let steps = game.steps[b] let name = block_name(b) + " " + BLOCKS[b].initiative + BLOCKS[b].fire_power + + let rolls = [] + let hits = 0 let self = 0 for (let i = 0; i < steps; ++i) { let die = roll_d6() if (die <= fire) { rolls.push(DIE_HIT[die]) - ++game.hits + ++hits } else { if (is_charge && die === 6) { rolls.push(DIE_SELF) @@ -3089,14 +3139,27 @@ function roll_attack(active, b, verb, is_charge) { } } } + game.hits += hits - game.flash = name + " " + verb + " " + 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." + log(active[0] + ": " + name + " " + verb + " " + rolls.join("") + ".") + + if (game.delay_hits) { + game.flash = name + " " + verb + " " + rolls.join(" ") + if (game.hits === 0) + game.flash += "." + else if (game.hits === 1) + game.flash += " for a total of 1 hit." + else + game.flash += " for a total of " + game.hits + " hits." + } else { + game.flash = name + " " + verb + " " + 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." + } if (self > 0) { if (self === 1) @@ -3107,14 +3170,13 @@ function roll_attack(active, b, verb, is_charge) { while (self-- > 0) reduce_block(b) } - - log(active[0] + ": " + name + " " + verb + " " + rolls.join("") + ".") } function field_fire_with_block(b) { set_add(game.moved, b) roll_attack(game.active, b, "fired", 0) - if (game.hits > 0) { + if (!game.delay_hits && game.hits > 0) { + game.active = enemy(game.active) goto_field_battle_hits() } else { resume_field_battle() @@ -3124,7 +3186,8 @@ function field_fire_with_block(b) { function siege_fire_with_block(b) { set_add(game.moved, b) roll_attack(game.active, b, "fired", 0) - if (game.hits > 0) { + if (!game.delay_hits && game.hits > 0) { + game.active = enemy(game.active) goto_siege_battle_hits() } else { resume_siege_battle() @@ -3134,7 +3197,8 @@ function siege_fire_with_block(b) { function charge_with_block(b) { set_add(game.moved, b) roll_attack(game.active, b, "charged", 1) - if (game.hits > 0) { + if (!game.delay_hits && game.hits > 0) { + game.active = enemy(game.active) goto_field_battle_hits() } else { resume_field_battle() @@ -3162,7 +3226,8 @@ function harry_with_block(b) { game.harry = game.active // remember to retreat after hits have been applied game.who = b roll_attack(game.active, b, "harried", 0) - if (game.hits > 0) { + if (!game.delay_hits && game.hits > 0) { + game.active = enemy(game.active) goto_field_battle_hits() } else { resume_field_battle() @@ -3727,13 +3792,15 @@ exports.setup = function (seed, scenario, options) { log(".h1 Crusader Rex") log("") - if (options && options.iron_bridge) { game.iron_bridge = 1 log("Iron Bridge:\nThe road between Antioch and Harim has a move limit of 3.") log("") } + if (options && options.delay_hits) + game.delay_hits = 1 + setup_game() return game } -- cgit v1.2.3