summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-10-23 20:49:49 +0200
committerTor Andersson <tor@ccxvii.net>2022-11-30 13:26:51 +0100
commitdbf5c2ef44b6ea1ff9e37098517f8bbbda2bb479 (patch)
tree37621e19b8d761ba4635a74d8d7c716c4d0c1df5
parentb957187e791d042ff54ecc194a2d089f195fece0 (diff)
downloadcrusader-rex-dbf5c2ef44b6ea1ff9e37098517f8bbbda2bb479.tar.gz
Delay hits option.
-rw-r--r--create.html12
-rw-r--r--play.js1
-rw-r--r--rules.js119
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 @@
-<p>
-Optional rules:
<dl>
<dt>
<label>
@@ -8,3 +6,13 @@ Iron Bridge
</label>
<dd>The road between Antioch and Harim has a move limit of 3.
</dl>
+
+<dl>
+<dt>
+<label>
+<input type="checkbox" id="delay_hits" name="delay_hits" value="true">
+Delay hit assignment
+</label>
+<dd>Let all blocks with the same initiative fire before assigning hits.
+<dd>This option is recommended for games that are not played live.
+</dl>
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
}