diff options
author | Tor Andersson <tor@ccxvii.net> | 2022-12-27 01:30:55 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2023-02-18 13:02:38 +0100 |
commit | 1a1c5531d9729d744584ca150c929befb6c0908f (patch) | |
tree | 3c1347766cc90e655e10ede2b035833ff34f9204 | |
parent | a346336ed81fdeca831b326043b82ab865b214cf (diff) | |
download | nevsky-1a1c5531d9729d744584ca150c929befb6c0908f.tar.gz |
Take hits.
-rw-r--r-- | play.js | 53 | ||||
-rw-r--r-- | rules.js | 249 |
2 files changed, 259 insertions, 43 deletions
@@ -49,6 +49,9 @@ const MEN_AT_ARMS = 4 const MILITIA = 5 const SERFS = 6 +const force_action_name = [ "knights", "sergeants", "light_horse", "asiatic_horse", "men_at_arms", "militia", "serfs" ] +const routed_force_action_name = [ "routed_knights", "routed_sergeants", "routed_light_horse", "routed_asiatic_horse", "routed_men_at_arms", "routed_militia", "routed_serfs" ] + // asset types const PROV = 0 const COIN = 1 @@ -70,6 +73,26 @@ const on_click_asset = [ (evt) => evt.button === 0 && send_action('ship', evt.target.my_id), ] +const on_click_force = [ + (evt) => evt.button === 0 && send_action('knights', evt.target.my_id), + (evt) => evt.button === 0 && send_action('sergeants', evt.target.my_id), + (evt) => evt.button === 0 && send_action('light_horse', evt.target.my_id), + (evt) => evt.button === 0 && send_action('asiatic_horse', evt.target.my_id), + (evt) => evt.button === 0 && send_action('men_at_arms', evt.target.my_id), + (evt) => evt.button === 0 && send_action('militia', evt.target.my_id), + (evt) => evt.button === 0 && send_action('serfs', evt.target.my_id), +] + +const on_click_routed_force = [ + (evt) => evt.button === 0 && send_action('routed_knights', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_sergeants', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_light_horse', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_asiatic_horse', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_men_at_arms', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_militia', evt.target.my_id), + (evt) => evt.button === 0 && send_action('routed_serfs', evt.target.my_id), +] + function on_click_veche_coin(evt) { if (evt.button === 0) send_action('veche_coin') @@ -178,6 +201,14 @@ function is_battle_array_action(ix) { return !!(view.actions && view.actions.array && set_has(view.actions.array, ix)) } +function is_routed_force_action(lord, action) { + return !!(view.actions && view.actions[action] && set_has(view.actions[action], lord)) +} + +function is_force_action(lord, action) { + return !!(view.actions && view.actions[action] && set_has(view.actions[action], lord)) +} + function is_asset_action(lord, action) { return !!(view.actions && view.actions[action] && set_has(view.actions[action], lord)) } @@ -903,9 +934,19 @@ function layout_calendar() { } } -function add_force(parent, type) { +function add_force(parent, type, lord, routed) { // TODO: reuse pool of elements? - build_div(parent, "unit " + force_type_name[type], type) + if (routed) { + if (is_routed_force_action(lord, routed_force_action_name[type])) + build_div(parent, "action unit " + force_type_name[type], lord, on_click_routed_force[type]) + else + build_div(parent, "unit " + force_type_name[type], lord, on_click_routed_force[type]) + } else { + if (is_force_action(lord, force_action_name[type])) + build_div(parent, "action unit " + force_type_name[type], lord, on_click_force[type]) + else + build_div(parent, "unit " + force_type_name[type], lord, on_click_force[type]) + } } function add_asset(parent, type, n, lord) { @@ -928,12 +969,12 @@ function add_veche_vp(parent) { build_div(parent, "marker square conquered russian") } -function update_forces(parent, forces) { +function update_forces(parent, forces, lord_ix, routed) { parent.replaceChildren() for (let i = 0; i < force_type_count; ++i) { let n = pack4_get(forces, i) for (let k = 0; k < n; ++k) { - add_force(parent, i) + add_force(parent, i, lord_ix, routed) } } } @@ -984,8 +1025,8 @@ function update_vassals(ready_parent, mustered_parent, lord_ix) { function update_lord_mat(ix) { update_assets(ix, ui.assets[ix], view.pieces.assets[ix]) update_vassals(ui.ready_vassals[ix], ui.mustered_vassals[ix], ix) - update_forces(ui.forces[ix], view.pieces.forces[ix]) - update_forces(ui.routed[ix], view.pieces.routed[ix]) + update_forces(ui.forces[ix], view.pieces.forces[ix], ix, false) + update_forces(ui.routed[ix], view.pieces.routed[ix], ix, true) } function is_lord_mat_selected(ix) { @@ -83,6 +83,10 @@ const MEN_AT_ARMS = 4 const MILITIA = 5 const SERFS = 6 +const FORCE_TYPE_NAME = [ "knights", "sergeants", "light horse", "asiatic horse", "men-at-arms", "militia", "serfs" ] +const FORCE_PROTECTION = [ 4, 3, 1, 1, 3, 1, 0 ] +const FORCE_EVADE = [ 0, 0, 0, 3, 0, 0, 0 ] + // asset types const PROV = 0 const COIN = 1 @@ -92,7 +96,7 @@ const SLED = 4 const BOAT = 5 const SHIP = 6 -const asset_type_name = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ] +const ASSET_TYPE_NAME = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ] // battle array const A1 = 0 // attackers @@ -3828,7 +3832,7 @@ function list_spoils() { for (let type = 0; type < 7; ++type) { let n = get_spoils(type) if (n > 0) - list.push(`${n} ${asset_type_name[type]}`) + list.push(`${n} ${ASSET_TYPE_NAME[type]}`) } if (list.length > 0) return list.join(", ") @@ -5216,18 +5220,17 @@ function has_rear_strike_choice() { return GROUPS[s][1][r] !== 0 } - -function goto_start_strike() { - game.battle.step = 0 - goto_strike() +function format_group(g) { + return g.map(p=>lord_name[game.battle.array[p]]).join(", ") } -function goto_next_strike() { - game.battle.step++ - if (game.battle.step >= 6) - goto_new_round() +function format_hits() { + if (game.battle.h2 > 0 && game.battle.h1 > 0) + return `${game.battle.h2} crossbow hits and ${game.battle.h1} hits` + else if (game.battle.h2 > 0) + return `${game.battle.h2} crossbow hits` else - goto_strike() + return `${game.battle.h1} hits` } function debug_battle_array(f, r) { @@ -5312,14 +5315,28 @@ function count_storm_hits(ix, lord, step) { } } +function goto_start_strike() { + game.battle.step = 0 + goto_strike() +} + +function goto_next_strike() { + game.battle.step++ + if (game.battle.step >= 6) + goto_new_round() + else + goto_strike() +} + function goto_strike() { let s = game.battle.step & 1 - if (s) set_active_attacker() else set_active_defender() + log_h4(battle_step_name[game.battle.step]) + // TODO: garrison hits game.battle.ah1 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] game.battle.ah2 = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] @@ -5350,6 +5367,7 @@ function goto_select_strike_group() { if (game.battle.groups.length === 0) goto_next_strike() else + // TODO: select if 1 game.state = "select_strike_group" } @@ -5398,53 +5416,154 @@ function select_strike_group(i) { game.battle.h2 = (game.battle.h2 >> 1) } + log(`${format_group(game.battle.sg)} struck ${format_group(game.battle.hg)} for ${format_hits()}`) + set_active_enemy() - goto_select_hit_group() + goto_select_hit_lord() return } -function goto_select_hit_group() { - if (game.battle.hg.length > 0) { - game.state = "select_hit_group" +function goto_select_hit_lord() { + if (game.battle.hg.length > 1) { + game.state = "select_hit_lord" } else { - game.who = game.battle.array[game.battle.hg[0]] - game.state = "hit_lord" + select_hit_lord(game.battle.array[game.battle.hg[0]]) } } -function format_hits() { - if (game.battle.h2 > 0 && game.battle.h1 > 0) - return `${game.battle.h2} crossbow hits and ${game.battle.h1} hits` - else if (game.battle.h2 > 0) - return `${game.battle.h2} crossbow hits` - else - return `${game.battle.h1} hits` -} - -states.select_hit_group = { +states.select_hit_lord = { prompt() { view.prompt = `${battle_step_name[game.battle.step]}: Select Lord to take ${format_hits()}.` for (let pos of game.battle.hg) gen_action_battle_lord(game.battle.array[pos]) }, battle_lord(lord) { - game.who = lord - game.state = "hit_lord" + select_hit_lord(lord) }, } +function select_hit_lord(lord) { + // TODO: walls + game.who = lord + game.state = "hit_lord" +} + +function has_lord_routed(lord) { + return game.pieces.forces[lord] === 0 +} + +function rout_lord(lord) { + log(`L${lord} routed!`) + for (let p = 0; p < 12; ++p) + if (game.battle.array[p] === lord) + game.battle.array[p] = NOBODY + set_add(game.battle.routed, lord) +} + +function resume_hit_lord() { + if ((game.battle.h1 === 0 && game.battle.h2 === 0) || has_lord_routed(game.who)) + end_hit_lord() +} + +function rout_unit(lord, type) { + add_lord_forces(lord, type, -1) + add_lord_routed_forces(lord, type, 1) +} + +function action_hit_lord(lord, type) { + let protection = FORCE_PROTECTION[type] + let evade = FORCE_EVADE[type] + + // TODO: manual choice + let ap = (game.battle.h2 > 0) ? 2 : 0 + + if (evade > 0 && !game.battle.storm) { + let die = roll_die() + if (die <= evade) { + log(`${FORCE_TYPE_NAME[type]} evade ${die} <= ${evade}`) + } else { + log(`${FORCE_TYPE_NAME[type]} evade ${die} > ${evade}`) + rout_unit(lord, type) + } + } else if (protection > 0) { + let die = roll_die() + if (die <= protection - ap) { + log(`${FORCE_TYPE_NAME[type]} protection ${die} <= ${protection - ap}`) + } else { + log(`${FORCE_TYPE_NAME[type]} protection ${die} > ${protection - ap}`) + rout_unit(lord, type) + } + } else { + log(`${FORCE_TYPE_NAME[type]} unprotected`) + rout_unit(lord, type) + } + + // TODO: manual choice + if (game.battle.h2) + game.battle.h2-- + else + game.battle.h1-- + + if (has_lord_routed(lord)) { + rout_lord(lord) + // TODO: remaining hits! + } + + resume_hit_lord() +} + states.hit_lord = { prompt() { - view.prompt = `${battle_step_name[game.battle.step]}: Assign ${format_hits()}.` - view.actions.pass = 1 + view.prompt = `${battle_step_name[game.battle.step]}: Assign ${format_hits()} to units.` + view.who = game.who + + // TODO: h1 or h2 + + let lord = game.who + if (get_lord_forces(lord, KNIGHTS) > 0) + gen_action_knights(lord) + if (get_lord_forces(lord, SERGEANTS) > 0) + gen_action_sergeants(lord) + if (get_lord_forces(lord, LIGHT_HORSE) > 0) + gen_action_light_horse(lord) + if (get_lord_forces(lord, ASIATIC_HORSE) > 0) + gen_action_asiatic_horse(lord) + if (get_lord_forces(lord, MEN_AT_ARMS) > 0) + gen_action_men_at_arms(lord) + if (get_lord_forces(lord, MILITIA) > 0) + gen_action_militia(lord) + if (get_lord_forces(lord, SERFS) > 0) + gen_action_serfs(lord) }, - pass() { - game.who = NOBODY - set_active_enemy() - goto_select_strike_group() + knights(lord) { + action_hit_lord(lord, KNIGHTS) + }, + sergeants(lord) { + action_hit_lord(lord, SERGEANTS) + }, + light_horse(lord) { + action_hit_lord(lord, LIGHT_HORSE) + }, + asiatic_horse(lord) { + action_hit_lord(lord, ASIATIC_HORSE) + }, + men_at_arms(lord) { + action_hit_lord(lord, MEN_AT_ARMS) + }, + militia(lord) { + action_hit_lord(lord, MILITIA) + }, + serfs(lord) { + action_hit_lord(lord, SERFS) }, } +function end_hit_lord() { + game.who = NOBODY + set_active_enemy() + goto_select_strike_group() +} + // === BATTLE: NEW ROUND === function goto_new_round() { @@ -6568,7 +6687,7 @@ function prompt_wastage(lord) { function action_wastage(lord, type) { push_undo() - log(`L${lord} discarded ${asset_type_name[type]}.`) + log(`L${lord} discarded ${ASSET_TYPE_NAME[type]}.`) set_lord_moved(lord, 0) add_lord_assets(lord, type, -1) } @@ -6874,6 +6993,62 @@ function gen_action_ship(lord) { gen_action("ship", lord) } +function gen_action_knights(lord) { + gen_action("knights", lord) +} + +function gen_action_sergeants(lord) { + gen_action("sergeants", lord) +} + +function gen_action_light_horse(lord) { + gen_action("light_horse", lord) +} + +function gen_action_asiatic_horse(lord) { + gen_action("asiatic_horse", lord) +} + +function gen_action_men_at_arms(lord) { + gen_action("men_at_arms", lord) +} + +function gen_action_militia(lord) { + gen_action("militia", lord) +} + +function gen_action_serfs(lord) { + gen_action("serfs", lord) +} + +function gen_action_routed_knights(lord) { + gen_action("routed_knights", lord) +} + +function gen_action_routed_sergeants(lord) { + gen_action("routed_sergeants", lord) +} + +function gen_action_routed_light_horse(lord) { + gen_action("routed_light_horse", lord) +} + +function gen_action_routed_asiatic_horse(lord) { + gen_action("routed_asiatic_horse", lord) +} + +function gen_action_routed_men_at_arms(lord) { + gen_action("routed_men_at_arms", lord) +} + +function gen_action_routed_militia(lord) { + gen_action("routed_militia", lord) +} + +function gen_action_routed_serfs(lord) { + gen_action("routed_serfs", lord) +} + exports.view = function (state, current) { load_state(state) |