From 8a5916ce20fc16e9655463d3d142056401a04031 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 19 Jun 2023 14:50:33 +0200 Subject: Some battles. --- rules.js | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 301 insertions(+), 46 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 2228f8a..3120fdb 100644 --- a/rules.js +++ b/rules.js @@ -131,6 +131,13 @@ const BARBARIAN_COUNT = [ 0, 50, 30, 40, 50 ] const first_barbarian = [ 0, 10, 20, 30, 40 ] const last_barbarian = [ 9, 19, 29, 39, 49 ] +const GENERAL_NAME = [ + "Red 0", "Red 1", "Red 2", "Red 3", "Red 4", "Red 5", + "Blue 0", "Blue 1", "Blue 2", "Blue 3", "Blue 4", "Blue 5", + "Yellow 0", "Yellow 1", "Yellow 2", "Yellow 3", "Yellow 4", "Yellow 5", + "Green 0", "Green 1", "Green 2", "Green 3", "Green 4", "Green 5", +] + const BARBARIAN_NAME = [ "Alamanni", "Franks", @@ -336,6 +343,13 @@ function current_hand() { return game.hand[game.current] } function current_draw() { return game.draw[game.current] } function current_discard() { return game.discard[game.current] } +function get_barbarian_tribe(id) { + for (let tribe = 0; tribe < 5; ++tribe) + if (id >= first_barbarian[tribe] && id <= last_barbarian[tribe]) + return tribe + return -1 +} + // === BOARD STATE === function is_no_place_governor(province) { @@ -563,7 +577,7 @@ function is_enemy_governor(id) { } function is_capital_free_of_enemy(where) { - return is_enemy_general(get_capital_general(where)) + return !is_enemy_general(get_capital_general(where)) } function get_province_governor(where) { @@ -626,6 +640,10 @@ function has_enemy_army_in_capital(where) { function has_enemy_army_in_province(where) { if (some_general((id, loc) => loc === where && is_enemy_general(id))) return true + if (is_province(where) && some_barbarian((id, loc) => loc === where)) + return true + if (!is_province(where) && some_barbarian((id, loc, active) => loc === where && active)) + return true return false } @@ -753,6 +771,12 @@ function roll_dice(count, target) { return hits } +function eliminate_barbarian(id) { + let tribe = get_barbarian_tribe(id) + set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) + set_barbarian_inactive(id) +} + // === SETUP === states.setup_province = { @@ -1068,7 +1092,7 @@ states.take_actions_governor = { // Place Militia if (!has_militia(where) && is_capital_free_of_enemy(where)) { if (pip >= 2) - view.actions.militia = 1 + view.actions.place_militia = 1 } // Hold Games @@ -1084,11 +1108,13 @@ states.take_actions_governor = { view.actions.build_improvement = 1 } - // TODO: Initiate Battle with Militia not stacked with General - if (has_militia(where) && !is_own_general(get_capital_general(where))) { - if (has_enemy_army_in_province(where)) - if (mip >= 1) - view.actions.initiate_battle = 1 + // Initiate Battle with Militia not stacked with General + if (!has_militia_battled(where)) { + if (has_militia(where) && !is_own_general(get_capital_general(where))) { + if (has_enemy_army_in_province(where)) + if (mip >= 1) + view.actions.initiate_battle = 1 + } } } }, @@ -1127,6 +1153,12 @@ states.take_actions_governor = { spend_ip(POPULACE, support + 1) game.support[where] = support + 1 }, + place_militia() { + push_undo() + let where = get_governor_location(game.who) + spend_ip(POPULACE, 2) + add_militia(where) + }, hold_games() { push_undo() let where = get_governor_location(game.who) @@ -1141,8 +1173,10 @@ states.take_actions_governor = { }, initiate_battle() { push_undo() + spend_ip(MILITARY, 1) game.state = "initiate_battle" game.misc = { attacker: -1, where: get_governor_location(game.who) } + set_militia_battled(get_governor_location(game.who)) }, } @@ -1265,8 +1299,10 @@ states.take_actions_general = { }, initiate_battle() { push_undo() + spend_ip(MILITARY, 1) game.state = "initiate_battle" game.misc = { attacker: game.who, where: get_general_location(game.who) } + set_general_battled(game.who) }, } @@ -1526,76 +1562,286 @@ states.initiate_battle = { }) if (is_enemy_province(game.misc.where)) { - if (has_militia(game.misc.where) && is_capital_free_of_enemy(where)) + if (has_militia(game.misc.where) && get_capital_general(game.misc.where) < 0) gen_action_militia(game.misc.where) } }, general(id) { push_undo() - log("Initiate Battle vs Gen" + id) + log("Initiate Battle vs " + GENERAL_NAME[id] + " in S" + game.misc.where) + game.misc.type = "general" game.misc.target = id - game.state = "battle_general" + game.state = "battle" }, barbarian(id) { push_undo() - log("Initiate Battle vs B" + id) - game.misc.target = id - game.state = "battle_barbarians" + let tribe = get_barbarian_tribe(id) + log("Initiate Battle vs " + BARBARIAN_NAME[id] + " in S" + game.misc.where) + game.misc.type = "barbarian" + game.misc.target = tribe + game.state = "battle" }, rival_emperor(id) { push_undo() - log("Initiate Battle vs B" + id) + log("Initiate Battle vs Rival Emperor in S" + game.misc.where) + game.misc.type = "rival_emperor" game.misc.target = id - game.state = "battle_rival_emperor" + game.state = "battle" }, militia(where) { push_undo() - log("Initiate Battle vs Militia" + id) + log("Initiate Battle vs Militia in S" + game.misc.where) + game.misc.type = "militia" game.misc.target = -1 - game.state = "battle_militia" + game.state = "battle" }, } -states.battle_general = { +states.battle = { prompt() { - prompt("Battle vs General.") + prompt("Battle!") + // TODO: play "Flanking Maneuver" view.actions.roll = 1 }, roll() { - clear_undo() + // clear_undo() + + game.misc.dhits = roll_attacker_dice() + game.misc.dtaken = 0 + + game.misc.ahits = roll_defender_dice() + game.misc.ataken = 0 + + // TODO: castra + + if (game.misc.ahits > 0) + game.state = "assign_hits_on_attacker" + else if (game.misc.dhits > 0) + game.state = "assign_hits_on_defender" + else + end_battle() }, } -states.battle_barbarians = { - prompt() { - prompt("Battle vs Barbarians.") - view.actions.roll = 1 - }, - roll() { - clear_undo() - }, +function roll_general_dice(general) { + let army = ARMY + general + let n = 0 + + log(GENERAL_NAME[general]) + + if (is_general_inside_capital(general) && has_militia(game.misc.where)) { + log("Militia") + n += roll_dice(1, 5) + } + + let full_strength = 0 + let reduced = 0 + for (let id = 0; id < 33; ++id) { + if (get_legion_location(id) === army) { + if (is_legion_reduced(id)) + reduced += 1 + else + full_strength += 1 + } + } + if (full_strength > 0) { + log("Legions") + n += roll_dice(full_strength, 3) + } + if (reduced > 0) { + log("Reduced Legions") + n += roll_dice(reduced, 5) + } + + let barbarians = 0 + for (let id = 0; id < game.barbarians.length; ++id) + if (get_barbarian_location(id) === army) + barbarians += 1 + if (barbarians > 0) { + log("Barbarians") + n += roll_dice(barbarians, 4) + } + + return n +} + +function roll_militia_dice() { + log("Militia") + return roll_dice(1, 5) +} + +function roll_rival_emperor_dice(rival_emperor) { + log("Rival Emperor") + return roll_dice(3, 4) +} + +function roll_barbarian_dice(tribe) { + log(BARBARIAN_NAME[tribe]) + let prov = is_province(game.misc.where) + let n = 0 + for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) + if (get_barbarian_location(id) === game.misc.where) + if (prov || is_barbarian_active(id)) + n += 1 + return roll_dice(n, 4) +} + +function roll_attacker_dice() { + log_h3("ATTACKER") + if (game.misc.attacker < 0) + return roll_militia_dice() + else + return roll_general_dice(game.misc.attacker) +} + +function roll_defender_dice() { + log_h3("DEFENDER") + switch (game.misc.type) { + case "militia": + return roll_militia_dice() + case "rival_emperor": + return roll_rival_emperor_dice(game.misc.target) + case "barbarians": + return roll_barbarian_dice(game.misc.target) + case "general": + return roll_general_dice(game.misc.target) + } + return 0 +} + +function gen_hits_militia() { + if (has_militia(game.misc.where)) { + gen_action_militia(game.misc.where) + return true + } + return false } -states.battle_rival_emperor = { +function gen_hits_general(general) { + let army = ARMY + general + + // TODO: castra + + if (is_general_inside_capital(general) && has_militia(game.misc.where)) { + gen_action_militia(game.misc.where) + return false + } + + for (let id = 0; id < game.barbarians.length; ++id) { + if (get_barbarian_location(id) === army) { + gen_action_barbarian(id) + return false + } + } + + // NOTE: reduce all legions before eliminating any + for (let id = 0; id < 33; ++id) { + if (get_legion_location(id) === army && !is_legion_reduced(id)) { + gen_action_legion(id) + return false + } + } + + for (let id = 0; id < 33; ++id) { + if (get_legion_location(id) === army && is_legion_reduced(id)) { + gen_action_legion(id) + return false + } + } + + return true +} + +states.assign_hits_on_attacker = { prompt() { - prompt("Battle vs Rival Emperor.") - view.actions.roll = 1 + prompt("Assign " + (game.misc.ahits - game.misc.ataken) + " hits!") + + let done = true + + if (game.misc.ataken < game.misc.ahits) { + if (game.misc.attacker < 0) + done = gen_hits_militia() + else + done = gen_hits_general(game.misc.attacker) + } + + if (done) + view.actions.done = 1 }, - roll() { - clear_undo() + done() { + if (game.misc.dhits > 0) + game.state = "assign_hits_on_defender" + else + end_battle() + }, + militia(where) { + game.misc.ataken += 1 + remove_militia(where) + }, + legion(id) { + game.misc.ataken += 1 + if (is_legion_reduced(id)) + set_legion_location(id, AVAILABLE) + else + set_legion_reduced(id) + }, + barbarian(id) { + game.misc.ataken += 1 + eliminate_barbarian(id) }, } -states.battle_militia = { +states.assign_hits_on_defender = { prompt() { - prompt("Battle vs Militia.") - view.actions.roll = 1 + prompt("Assign " + (game.misc.dhits - game.misc.dtaken) + " hits!") + + let done = true + + if (game.misc.dtaken < game.misc.dhits) { + switch (game.misc.type) { + case "militia": + done = gen_hits_militia() + break + case "rival_emperor": + done = gen_hits_rival_emperor(game.misc.target) + break + case "barbarians": + done = gen_hits_barbarians(game.misc.target) + break + case "general": + done = gen_hits_general(game.misc.target) + break + } + } + + if (done) + view.actions.done = 1 }, - roll() { - clear_undo() + done() { + end_battle() + }, + militia(where) { + game.misc.dtaken += 1 + remove_militia(where) + }, + legion(id) { + game.misc.dtaken += 1 + if (is_legion_reduced(id)) + set_legion_location(id, AVAILABLE) + else + set_legion_reduced(id) + }, + barbarian(id) { + game.misc.dtaken += 1 + eliminate_barbarian(id) }, } +function end_battle() { + // TODO: retreat / advance into capital + game.state = "take_actions_general" +} + // === SUPPORT CHECK === function goto_support_check() { @@ -2003,6 +2249,11 @@ function log_h2(msg) { log_br() } +function log_h3(msg) { + log_br() + log(".h3 " + msg) +} + function logi(msg) { game.log.push(">" + msg) } @@ -2023,20 +2274,24 @@ function gen_action(action, argument) { set_add(view.actions[action], argument) } -function gen_action_general(ix) { - gen_action("general", ix) +function gen_action_general(id) { + gen_action("general", id) +} + +function gen_action_governor(id) { + gen_action("governor", id) } -function gen_action_governor(ix) { - gen_action("governor", ix) +function gen_action_legion(id) { + gen_action("legion", id) } -function gen_action_legion(ix) { - gen_action("legion", ix) +function gen_action_barbarian(id) { + gen_action("barbarian", id) } -function gen_action_barbarian(ix) { - gen_action("barbarian", ix) +function gen_action_militia(where) { + gen_action("militia", where) } function gen_action_region(where) { -- cgit v1.2.3