summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-10-05 14:06:07 +0200
committerTor Andersson <tor@ccxvii.net>2022-11-17 12:53:18 +0100
commit67beb5feb4b7454692b9e4511d060fea0f35c445 (patch)
tree38aa086b9ffc971191373f87139f272533b8c8ee
parenta99a00ffd7cd22f3344e64a7436f91216d547d72 (diff)
downloadhammer-of-the-scots-67beb5feb4b7454692b9e4511d060fea0f35c445.tar.gz
Delayed hit option.
-rw-r--r--create.html16
-rw-r--r--play.js1
-rw-r--r--rules.js305
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>
diff --git a/play.js b/play.js
index 75c7c3d..26b2407 100644
--- a/play.js
+++ b/play.js
@@ -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")
diff --git a/rules.js b/rules.js
index c722e55..8e80964 100644
--- a/rules.js
+++ b/rules.js
@@ -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()