diff options
-rw-r--r-- | rules.js | 12927 |
1 files changed, 6467 insertions, 6460 deletions
@@ -1,15 +1,87 @@ "use strict" +// TODO: "approach" pause when about to move into intercept range? +// TODO: log end victory conditions at scenario start +// Check all push/clear_undo +// TODO : Check BUG done visible in Death of Disband despite being false +// TODO: check flank attack + +let game = null +let view = null +let states = {} + +// === CONSTANTS === + const data = require("./data.js") +function find_card(name) { + let ix = data.cards.findIndex(x => x.name === name) + if (ix < 0) + throw "CANNOT FIND CARD: " + name + return ix +} + +function find_lord(name) { + let ix = data.lords.findIndex(x => x.name === name) + if (ix < 0) + throw "CANNOT FIND LORD: " + name + return ix +} + +function find_locale(name) { + let ix = data.locales.findIndex(x => x.name === name) + if (ix < 0) + throw "CANNOT FIND LOCALE: " + name + return ix +} + +function find_vassal(name) { + let ix = data.vassals.findIndex(x => x.name === name) + if (ix < 0) + throw "CANNOT FIND VASSAL: " + name + return ix +} + const BOTH = "Both" const LANCASTER = "Lancaster" const YORK = "York" -// TODO: "approach" pause when about to move into intercept range? +exports.roles = [ LANCASTER, YORK ] -var P1 = null -var P2 = null +const NOBODY = -1 +const NOWHERE = -1 +const NOTHING = -1 + +const CALENDAR = 100 + +const VASSAL_READY = 29 +const VASSAL_CALENDAR = 30 +const VASSAL_OUT_OF_PLAY = 31 + +const SUMMER = 0 +const SPRING = 1 +const WINTER = 2 +const AUTUMN = 3 + +const SEASONS = [ + null, + WINTER, + SPRING, + SUMMER, + AUTUMN, + WINTER, + WINTER, + SPRING, + SUMMER, + AUTUMN, + WINTER, + WINTER, + SPRING, + SUMMER, + AUTUMN, + WINTER, + null, +] const INFLUENCE_TURNS = [ 1, 4, 6, 9, 11, 14 ] const GROW_TURNS = [ 4, 9, 14 ] @@ -18,42 +90,6 @@ const WASTE_TURNS = [ 5, 10 ] const HIT = [ "0", "\u2776", "\u2777", "\u2778", "\u2779", "\u277A", "\u277B" ] const MISS = [ "0", "\u2460", "\u2461", "\u2462", "\u2463", "\u2464", "\u2465" ] -function range(x) { - switch (x) { - case 0: return "0" - case 1: return "1" - case 2: return "1-2" - case 3: return "1-3" - case 4: return "1-4" - case 5: return "1-5" - case 6: return "Automatic success" - } -} - -let game = null -let view = null -let states = {} - -exports.roles = [ LANCASTER, YORK ] - -exports.scenarios = [ - "Ia. Henry VI", - "Ib. Towton", - "Ic. Somerset's Return", - "II. Warwicks' Rebellion", - "III. My Kingdom for a Horse", -// TODO "I-III. Wars of the Roses", -] - -const scenario_last_turn = { - "Ia. Henry VI": 15, - "Ib. Towton": 2, - "Ic. Somerset's Return": 8, - "II. Warwicks' Rebellion": 15, - "III. My Kingdom for a Horse": 15, - "I-III. Wars of the Roses": 15, -} - // unit types const RETINUE = 0 const VASSAL = 1 @@ -84,34 +120,6 @@ const D1 = 3 // defenders const D2 = 4 const D3 = 5 -function find_card(name) { - let ix = data.cards.findIndex(x => x.name === name) - if (ix < 0) - throw "CANNOT FIND CARD: " + name - return ix -} - -function find_lord(name) { - let ix = data.lords.findIndex(x => x.name === name) - if (ix < 0) - throw "CANNOT FIND LORD: " + name - return ix -} - -function find_locale(name) { - let ix = data.locales.findIndex(x => x.name === name) - if (ix < 0) - throw "CANNOT FIND LOCALE: " + name - return ix -} - -function find_vassal(name) { - let ix = data.vassals.findIndex(x => x.name === name) - if (ix < 0) - throw "CANNOT FIND VASSAL: " + name - return ix -} - const lord_name = data.lords.map(lord => lord.name) const lord_count = data.lords.length @@ -131,6 +139,150 @@ const last_york_card = 36 const first_lancaster_card = 37 const last_lancaster_card = 73 +function is_york_card(c) { + return c >= first_york_card && c <= last_york_card +} + +function is_lancaster_card(c) { + return c >= first_lancaster_card && c <= last_lancaster_card +} + +const first_york_lord = 0 +const last_york_lord = 13 +const first_lancaster_lord = 14 +const last_lancaster_lord = 27 + +const YORK_LORD_MASK = 0x1fff +const LANCASTER_LORD_MASK = YORK_LORD_MASK << 14 + +function is_york_lord(lord) { + return lord >= first_york_lord && lord <= last_york_lord +} + +function is_lancaster_lord(lord) { + return lord >= first_lancaster_lord && lord <= last_lancaster_lord +} + +function is_marshal(lord) { + switch (lord) { + case LORD_MARGARET: + return true + case LORD_HENRY_VI: + return true + case LORD_HENRY_TUDOR: + return true + case LORD_EDWARD_IV: + return true + case LORD_GLOUCESTER_2: + return true + case LORD_RICHARD_III: + return true + case LORD_YORK: + return true + default: + return false + } +} + +function is_lieutenant(lord) { + switch (lord) { + case LORD_WARWICK_L: + return true + case LORD_SOMERSET_1: + return true + case LORD_GLOUCESTER_1: + return true + case LORD_WARWICK_Y: + return true + default: + return false + } +} + +function is_seaport(loc) { + return set_has(data.seaports, loc) +} + +function is_stronghold(loc) { + return data.locales[loc].type !== "exile" +} + +function is_exile(loc) { + return data.locales[loc].type === "exile" +} + +function is_city(loc) { + return data.locales[loc].type === "city" +} + +function is_town(loc) { + return data.locales[loc].type === "town" +} + +function is_wales(loc) { + return data.locales[loc].region === "Wales" +} + +function is_south(loc) { + return data.locales[loc].region === "South" +} + +function is_north(loc) { + return data.locales[loc].region === "North" +} + +function is_fortress(loc) { + return data.locales[loc].type === "fortress" +} + +function is_sea(loc) { + return data.locales[loc].type === "sea" +} + +function find_ports(here) { + if ((lord_has_capability(game.group, AOW_YORK_GREAT_SHIPS) || lord_has_capability(game.group, AOW_LANCASTER_GREAT_SHIPS))) + return data.all_ports + if (here === data.sea_1) return data.port_1 + if (here === data.sea_2) return data.port_2 + if (here === data.sea_3) return data.port_3 + if (here === data.exile_1) return data.port_1 + if (here === data.exile_2) return data.port_2 + if (here === data.exile_3) return data.port_3 + if (here === data.exile_4) return data.port_1 + if (set_has(data.port_1, here)) return data.port_1 + if (set_has(data.port_2, here)) return data.port_2 + if (set_has(data.port_3, here)) return data.port_3 + return null +} + +function find_sail_locales(here) { + if (here === data.sea_1) return data.way_sea_1 + if (here === data.sea_2) return data.way_sea_2 + if (here === data.sea_3) return data.way_sea_3 + if (here === data.exile_1) return data.way_exile_1 + if (here === data.exile_2) return data.way_exile_2 + if (here === data.exile_3) return data.way_exile_3 + if (set_has(data.port_1, here)) return data.way_port_1 + if (set_has(data.port_2, here)) return data.way_port_2 + if (set_has(data.port_3, here)) return data.way_port_3 + return null +} + +function make_locale_list(pred) { + let list = [] + for (let loc = first_locale; loc <= last_locale; ++loc) + if (pred(loc)) + list.push(loc) + return list +} + +const all_north_locales = make_locale_list(is_north) +const all_south_locales = make_locale_list(is_south) +const all_wales_locales = make_locale_list(is_wales) +const all_city_locales = make_locale_list(is_city) +const all_town_locales = make_locale_list(is_town) +const all_fortress_locales = make_locale_list(is_fortress) + const Y1 = find_card("Y1") const Y2 = find_card("Y2") const Y3 = find_card("Y3") @@ -320,16 +472,11 @@ const VASSAL_MONTAGU = find_vassal("Montagu") const VASSAL_THOMAS_STANLEY = find_vassal("Thomas Stanley") const VASSAL_TROLLOPE = find_vassal("Trollope") -// === === === === FROM NEVSKY === === === === - -// TODO: log end victory conditions at scenario start - const AOW_LANCASTER_CULVERINS_AND_FALCONETS = [L1 , L2] const AOW_LANCASTER_MUSTERD_MY_SOLDIERS = L3 const AOW_LANCASTER_HERALDS = L4 const AOW_LANCASTER_CHURCH_BLESSINGS = L5 -const AOW_LANCASTER_GREAT_SHIPS = L6 // TODO Resolve bug about not correctly -// targeting ports on other seats and further +const AOW_LANCASTER_GREAT_SHIPS = L6 // TODO Resolve bug about not correctly targeting ports on other seats and further const AOW_LANCASTER_HARBINGERS = L7 const AOW_LANCASTER_HAY_WAINS = L8 const AOW_LANCASTER_QUARTERMASTERS = L9 @@ -375,7 +522,7 @@ const AOW_YORK_YORKISTS_NEVER_WAIT = Y11 const AOW_YORK_SOLDIERS_OF_FORTUNE = Y12 const AOW_YORK_SCOURERS = Y13 const AOW_YORK_BURGUNDIANS = [ Y14, Y23 ] -const AOW_YORK_NAVAL_BLOCKADE = Y15 // TODO DEBUG pop_state() going into parley and not after province selection +const AOW_YORK_NAVAL_BLOCKADE = Y15 // TODO DEBUG pop_state() going into parley and not after province selection const AOW_YORK_BELOVED_WARWICK = Y16 const AOW_YORK_ALICE_MONTAGU = Y17 const AOW_YORK_IRISHMEN = Y18 @@ -478,107 +625,95 @@ const EVENT_YORK_PRIVY_COUNCIL = Y35 const EVENT_YORK_SWIFT_MANEUVER = Y36 const EVENT_YORK_PATRICK_DE_LA_MOTE = Y37 -// Check all push/clear_undo -// TODO : Check BUG done visible in Death of Disband despite being false +// === STATE: ACTIVE PLAYER === -const NOBODY = -1 -const NOWHERE = -1 -const NOTHING = -1 +var P1 = null +var P2 = null -const CALENDAR = 100 +var first_friendly_lord = 0 +var last_friendly_lord = 13 +var first_enemy_lord = 14 +var last_enemy_lord = 27 -const VASSAL_READY = 29 -const VASSAL_CALENDAR = 30 -const VASSAL_OUT_OF_PLAY = 31 +function update_aliases() { + if (game.active === YORK) { + first_friendly_lord = 0 + last_friendly_lord = 13 + first_enemy_lord = 14 + last_enemy_lord = 27 + } else if (game.active === LANCASTER) { + first_friendly_lord = 14 + last_friendly_lord = 27 + first_enemy_lord = 0 + last_enemy_lord = 13 + } else { + first_friendly_lord = -1 + last_friendly_lord = -1 + first_enemy_lord = -1 + last_enemy_lord = -1 + } + if (game.rebel === YORK) { + P1 = YORK + P2 = LANCASTER + } else { + P1 = LANCASTER + P2 = YORK + } +} -const SUMMER = 0 -const SPRING = 1 -const WINTER = 2 -const AUTUMN = 3 +function load_state(state) { + if (game !== state) { + game = state + update_aliases() + } +} -const SEASONS = [ - null, - WINTER, - SPRING, - SUMMER, - AUTUMN, - WINTER, - WINTER, - SPRING, - SUMMER, - AUTUMN, - WINTER, - WINTER, - SPRING, - SUMMER, - AUTUMN, - WINTER, - null, -] +function set_active(new_active) { + if (game.active !== new_active) { + clear_undo() + game.active = new_active + update_aliases() + } +} -const TURN_NAME = [ - null, - "1 - January/February/March", - "2 - April/May", - "3 - June/July", - "4 - August/September/October", - "5 - November/December", - "6 - January/February/March", - "7 - April/May", - "8 - June/July", - "9 - August/September/October", - "10 - November/December", - "11 - January/February/March", - "12 - April/May", - "13 - June/July", - "14 - August/September/October", - "15 - November/December", - null, -] +function set_active_enemy() { + if (game.active === YORK) + set_active(LANCASTER) + else + set_active(YORK) +} -function find_ports(here) { - if ((lord_has_capability(game.group, AOW_YORK_GREAT_SHIPS) || lord_has_capability(game.group, AOW_LANCASTER_GREAT_SHIPS))) - return data.all_ports - if (here === data.sea_1) return data.port_1 - if (here === data.sea_2) return data.port_2 - if (here === data.sea_3) return data.port_3 - if (here === data.exile_1) return data.port_1 - if (here === data.exile_2) return data.port_2 - if (here === data.exile_3) return data.port_3 - if (here === data.exile_4) return data.port_1 - if (set_has(data.port_1, here)) return data.port_1 - if (set_has(data.port_2, here)) return data.port_2 - if (set_has(data.port_3, here)) return data.port_3 - return null +function set_active_command() { + if (is_york_lord(game.command)) + set_active(YORK) + else + set_active(LANCASTER) } -function find_sail_locales(here) { - if (here === data.sea_1) return data.way_sea_1 - if (here === data.sea_2) return data.way_sea_2 - if (here === data.sea_3) return data.way_sea_3 - if (here === data.exile_1) return data.way_exile_1 - if (here === data.exile_2) return data.way_exile_2 - if (here === data.exile_3) return data.way_exile_3 - if (set_has(data.port_1, here)) return data.way_port_1 - if (set_has(data.port_2, here)) return data.way_port_2 - if (set_has(data.port_3, here)) return data.way_port_3 - return null +function reduce_influence(amt) { + if (game.active === YORK) + reduce_york_influence(amt) + else + reduce_lancaster_influence(amt) } -function make_locale_list(pred) { - let list = [] - for (let loc = first_locale; loc <= last_locale; ++loc) - if (pred(loc)) - list.push(loc) - return list +function reduce_york_influence(amt) { + game.influence += amt } -const all_north_locales = make_locale_list(is_north) -const all_south_locales = make_locale_list(is_south) -const all_wales_locales = make_locale_list(is_wales) -const all_city_locales = make_locale_list(is_city) -const all_town_locales = make_locale_list(is_town) -const all_fortress_locales = make_locale_list(is_fortress) +function increase_york_influence(amt) { + game.influence -= amt +} + +function reduce_lancaster_influence(amt) { + game.influence -= amt +} + +function increase_lancaster_influence(amt) { + game.influence += amt +} + +// === STATE: TURN === function current_turn() { return game.turn >> 1 @@ -588,114 +723,154 @@ function current_season() { return SEASONS[game.turn >> 1] } +function is_campaign_phase() { + return (game.turn & 1) === 1 +} + +function is_levy_phase() { + return (game.turn & 1) === 0 +} + function current_turn_name() { return String(game.turn >> 1) } +function max_plan_length() { + switch (current_season()) { + case SUMMER: + return 7 + case SPRING: + return 6 + case WINTER: + return 4 + case AUTUMN: + return 6 + } +} + +// === STATE: CARDS === + function current_hand() { if (game.active === YORK) return game.hand_y return game.hand_l } -function is_campaign_phase() { - return (game.turn & 1) === 1 +function is_friendly_card(c) { + if (game.active === YORK) + return is_york_card(c) + return is_lancaster_card(c) } -function is_levy_phase() { - return (game.turn & 1) === 0 +function has_card_in_hand(c) { + if (game.active === YORK) + return set_has(game.hand_y, c) + return set_has(game.hand_l, c) } -// === GAME STATE === - -const first_york_lord = 0 -const last_york_lord = 13 -const first_lancaster_lord = 14 -const last_lancaster_lord = 27 +function could_play_card(c) { + if (!game.hidden) { + // TODO: check capabilities on lords revealed in battle if hidden + if (map_has_value(game.pieces.capabilities, c)) + return false + } + if (set_has(game.events, c)) + return false + if (is_york_card(c)) + return game.hand_y.length > 0 + if (is_lancaster_card(c)) + return game.hand_l.length > 0 + return true +} -let first_friendly_lord = 0 -let last_friendly_lord = 13 -let first_enemy_lord = 14 -let last_enemy_lord = 27 +function count_cards_in_plan(plan, lord) { + let n = 0 + for (let c of plan) + if (c === lord) + ++n + return n +} -function update_aliases() { - if (game.active === YORK) { - first_friendly_lord = 0 - last_friendly_lord = 13 - first_enemy_lord = 14 - last_enemy_lord = 27 - } else if (game.active === LANCASTER) { - first_friendly_lord = 14 - last_friendly_lord = 27 - first_enemy_lord = 0 - last_enemy_lord = 13 - } else { - first_friendly_lord = -1 - last_friendly_lord = -1 - first_enemy_lord = -1 - last_enemy_lord = -1 - } - if (game.rebel === YORK) { - P1 = YORK - P2 = LANCASTER - } else { - P1 = LANCASTER - P2 = YORK - } +function is_card_in_use(c) { + if (set_has(game.hand_y, c)) + return true + if (set_has(game.hand_l, c)) + return true + if (set_has(game.events, c)) + return true + if (map_has_value(game.pieces.capabilities, c)) + return true + return false } -function load_state(state) { - if (game !== state) { - game = state - update_aliases() - } +function list_deck() { + let deck = [] + let first_card = game.active === YORK ? first_york_card : first_lancaster_card + let last_card = game.active === YORK ? last_york_card : last_lancaster_card + for (let c = first_card; c <= last_card; ++c) + if (!is_card_in_use(c) && is_card_in_scenario(c)) + deck.push(c) + return deck } -function push_state(next) { - if (!states[next]) - throw Error("No such state: " + next) - game.stack.push([ game.state, game.who, game.count ]) - game.state = next +function draw_card(deck) { + clear_undo() + let i = random(deck.length) + let c = deck[i] + set_delete(deck, c) + return c } -function pop_state() { - [ game.state, game.who, game.count ] = game.stack.pop() +function draw_two_cards() { + let deck = list_deck() + return [ draw_card(deck), draw_card(deck) ] } -function set_active(new_active) { - if (game.active !== new_active) { - clear_undo() - game.active = new_active - update_aliases() +function discard_events(when) { + for (let i = 0; i < game.events.length; ) { + let c = game.events[i] + if (data.cards[c].when === when) + array_remove(game.events, i) + else + ++i } } -function set_active_enemy() { - set_active(enemy_player()) +function discard_extra_levy_events() { + for (let i = 0; i < game.events.length; ) { + let c = game.events[i] + if (c === Y20) + array_remove(game.events, i) + else + ++i + } } -function enemy_player() { - if (game.active === YORK) - return LANCASTER - if (game.active === LANCASTER) - return YORK - return null +function discard_friendly_events(when) { + for (let i = 0; i < game.events.length; ) { + let c = game.events[i] + if (is_friendly_card(c) && data.cards[c].when === when) + array_remove(game.events, i) + else + ++i + } } -function has_any_spoils() { - return game.spoils && game.spoils[PROV] + game.spoils[COIN] + game.spoils[CART] + game.spoils[SHIP] > 0 +function is_event_in_play(c) { + return set_has(game.events, c) } -function get_spoils(type) { - if (game.spoils) - return game.spoils[type] - return 0 +// === STATE: LORD === + +function get_lord_locale(lord) { + return map_get(game.pieces.locale, lord, NOWHERE) } -function add_spoils(type, n) { - if (!game.spoils) - game.spoils = [ 0, 0, 0, 0, 0, 0, 0 ] - game.spoils[type] += n +function set_lord_locale(lord, locale) { + if (locale === NOWHERE) + map_delete(game.pieces.locale, lord) + else + map_set(game.pieces.locale, lord, locale) } function get_lord_calendar(lord) { @@ -712,10 +887,6 @@ function set_lord_calendar(lord, turn) { set_lord_locale(lord, CALENDAR + turn) } -function get_lord_locale(lord) { - return map_get(game.pieces.locale, lord, NOWHERE) -} - function get_lord_capability(lord, n) { return map2_get(game.pieces.capabilities, lord, n, NOTHING) } @@ -731,46 +902,6 @@ function get_lord_assets(lord, n) { return map_get_pack4(game.pieces.assets, lord, n) } -function get_lord_forces(lord, n) { - return map_get_pack4(game.pieces.forces, lord, n) -} - -function get_lord_routed_forces(lord, n) { - return map_get_pack4(game.pieces.routed, lord, n) -} - -function lord_has_unrouted_units(lord) { - for (let x = 0; x < FORCE_TYPE_COUNT; ++x) - if (get_lord_forces(lord, x) > 0) - return true - let result = false - for_each_vassal_with_lord(lord, v => { - if (!set_has(game.battle.routed_vassals, v)) - result = true - }) - return result -} - -function rout_vassal(_lord, vassal) { - set_add(game.battle.routed_vassals, vassal) -} - -function set_lord_locale(lord, locale) { - if (locale === NOWHERE) - map_delete(game.pieces.locale, lord) - else - map_set(game.pieces.locale, lord, locale) -} - -function get_force_name(lord, n, x) { - if (n === RETINUE) { - return `${lord_name[lord]}'s Retinue` - } else if (n === VASSAL) { - return `Vassal ${data.vassals[x].name}` - } - return FORCE_TYPE_NAME[n] -} - function set_lord_assets(lord, n, x) { if (x < 0) x = 0 @@ -783,6 +914,18 @@ function add_lord_assets(lord, n, x) { set_lord_assets(lord, n, get_lord_assets(lord, n) + x) } +function drop_prov(lord) { + add_lord_assets(lord, PROV, -1) +} + +function drop_cart(lord) { + add_lord_assets(lord, CART, -1) +} + +function get_lord_forces(lord, n) { + return map_get_pack4(game.pieces.forces, lord, n) +} + function set_lord_forces(lord, n, x) { if (x < 0) x = 0 @@ -795,6 +938,10 @@ function add_lord_forces(lord, n, x) { set_lord_forces(lord, n, get_lord_forces(lord, n) + x) } +function get_lord_routed_forces(lord, n) { + return map_get_pack4(game.pieces.routed, lord, n) +} + function set_lord_routed_forces(lord, n, x) { if (x < 0) x = 0 @@ -807,10 +954,6 @@ function add_lord_routed_forces(lord, n, x) { set_lord_routed_forces(lord, n, get_lord_routed_forces(lord, n) + x) } -function clear_lords_moved() { - map_clear(game.pieces.moved) -} - function get_lord_moved(lord) { return map_get(game.pieces.moved, lord, 0) } @@ -819,6 +962,10 @@ function set_lord_moved(lord, x) { map_set(game.pieces.moved, lord, x) } +function clear_lords_moved() { + map_clear(game.pieces.moved) +} + function set_lord_fought(lord) { set_lord_moved(lord, 1) // TODO: is this needed? @@ -849,36 +996,31 @@ function pay_lord(lord) { set_lord_moved(lord, n) } -function get_lord_array_position(lord) { - for (let p = 0; p < 12; ++p) - if (game.battle.array[p] === lord) - return p - return -1 +function is_lord_on_map(lord) { + let loc = get_lord_locale(lord) + return loc !== NOWHERE && loc < CALENDAR } -// === GAME STATE HELPERS === +function is_lord_in_play(lord) { + return get_lord_locale(lord) !== NOWHERE +} -function roll_die() { - clear_undo() - return random(6) + 1 +function is_lord_on_calendar(lord) { + let loc = get_lord_locale(lord) + return loc >= CALENDAR } -function get_shared_assets(loc, what) { - let m = 0 - let n = 0 - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (get_lord_locale(lord) === loc) - n += get_lord_assets(lord, what) - if (game.state === "supply_source" && lord_has_capability(lord, AOW_LANCASTER_HAY_WAINS) && what === CART) { - m = get_lord_assets(lord, CART) - n += m - } - if (game.state === "supply_source" && (lord_has_capability(lord, AOW_YORK_GREAT_SHIPS) || lord_has_capability(lord, AOW_YORK_GREAT_SHIPS)) && what === SHIP) { - m = get_lord_assets(lord, SHIP) - n += m - } - } - return n +function is_lord_ready(lord) { + let loc = get_lord_locale(lord) + return loc >= CALENDAR && loc <= CALENDAR + (game.turn >> 1) +} + +function is_friendly_lord(lord) { + return lord >= first_friendly_lord && lord <= last_friendly_lord +} + +function is_enemy_lord(lord) { + return lord >= first_enemy_lord && lord <= last_enemy_lord } function count_lord_all_forces(lord) { @@ -892,16 +1034,56 @@ function count_lord_all_forces(lord) { } function count_lord_ships(lord) { - let ships = get_lord_assets(lord, SHIP) - return ships + return get_lord_assets(lord, SHIP) } -function count_group_ships() { +function count_lord_transport(lord) { + return get_lord_assets(lord, CART) +} + +function lord_has_unrouted_units(lord) { + for (let x = 0; x < FORCE_TYPE_COUNT; ++x) + if (get_lord_forces(lord, x) > 0) + return true + let result = false + for_each_vassal_with_lord(lord, v => { + if (!set_has(game.battle.routed_vassals, v)) + result = true + }) + return result +} + +function find_lord_with_capability_card(c) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (lord_has_capability_card(lord, c)) + return lord + return NOBODY +} + +function get_force_name(lord, n, x) { + if (n === RETINUE) + return `${lord_name[lord]}'s Retinue` + if (n === VASSAL) + return `Vassal ${data.vassals[x].name}` + return FORCE_TYPE_NAME[n] +} + +// === STATE: LORD (SHARED) === + +function get_shared_assets(loc, what) { + let m = 0 let n = 0 - for (let lord of game.group) { - n += count_lord_ships(lord) - if (lord_has_capability(lord, AOW_YORK_GREAT_SHIPS) || lord_has_capability(lord, AOW_LANCASTER_GREAT_SHIPS)) - n += count_lord_ships(lord) + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (get_lord_locale(lord) === loc) + n += get_lord_assets(lord, what) + if (game.state === "supply_source" && lord_has_capability(lord, AOW_LANCASTER_HAY_WAINS) && what === CART) { + m = get_lord_assets(lord, CART) + n += m + } + if (game.state === "supply_source" && (lord_has_capability(lord, AOW_YORK_GREAT_SHIPS) || lord_has_capability(lord, AOW_YORK_GREAT_SHIPS)) && what === SHIP) { + m = get_lord_assets(lord, SHIP) + n += m + } } return n } @@ -919,8 +1101,14 @@ function count_group_assets(type, group = game.group) { return n } -function count_lord_transport(lord) { - return get_lord_assets(lord, CART) +function count_group_ships() { + let n = 0 + for (let lord of game.group) { + n += count_lord_ships(lord) + if (lord_has_capability(lord, AOW_YORK_GREAT_SHIPS) || lord_has_capability(lord, AOW_LANCASTER_GREAT_SHIPS)) + n += count_lord_ships(lord) + } + return n } function count_group_transport(group = game.group) { @@ -934,138 +1122,7 @@ function count_group_lords() { return game.group.length } -function max_plan_length() { - switch (current_season()) { - case SUMMER: - return 7 - case SPRING: - return 6 - case WINTER: - return 4 - case AUTUMN: - return 6 - } -} - -function count_cards_in_plan(plan, lord) { - let n = 0 - for (let c of plan) - if (c === lord) - ++n - return n -} - -function is_marshal(lord) { - switch (lord) { - case LORD_MARGARET: - return true - case LORD_HENRY_VI: - return true - case LORD_HENRY_TUDOR: - return true - case LORD_EDWARD_IV: - return true - case LORD_GLOUCESTER_2: - return true - case LORD_RICHARD_III: - return true - case LORD_YORK: - return true - default: - return false - } -} - -function is_lieutenant(lord) { - switch (lord) { - case LORD_WARWICK_L: - return true - case LORD_SOMERSET_1: - return true - case LORD_GLOUCESTER_1: - return true - case LORD_WARWICK_Y: - return true - default: - return false - } -} - -function is_york_card(c) { - return c >= first_york_card && c <= last_york_card -} - -function is_lancaster_card(c) { - return c >= first_lancaster_card && c <= last_lancaster_card -} - -function is_card_in_use(c) { - if (set_has(game.hand_y, c)) - return true - if (set_has(game.hand_l, c)) - return true - if (set_has(game.events, c)) - return true - if (map_has_value(game.pieces.capabilities, c)) - return true - return false -} - -function is_card_in_scenario(_c) { - // TODO: Cards setup - return true -} - -function list_deck() { - let deck = [] - let first_card = game.active === YORK ? first_york_card : first_lancaster_card - let last_card = game.active === YORK ? last_york_card : last_lancaster_card - for (let c = first_card; c <= last_card; ++c) - if (!is_card_in_use(c) && is_card_in_scenario(c)) - deck.push(c) - return deck -} - -function is_friendly_card(c) { - if (game.active === YORK) - return is_york_card(c) - return is_lancaster_card(c) -} - -function has_card_in_hand(c) { - if (game.active === YORK) - return set_has(game.hand_y, c) - return set_has(game.hand_l, c) -} - -// Used in CULVERINS AND FALCONETS -function find_lord_with_capability_card(c) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (lord_has_capability_card(lord, c)) - return lord - return NOBODY -} - -function is_lord_on_map(lord) { - let loc = get_lord_locale(lord) - return loc !== NOWHERE && loc < CALENDAR -} - -function is_lord_in_play(lord) { - return get_lord_locale(lord) !== NOWHERE -} - -function is_lord_on_calendar(lord) { - let loc = get_lord_locale(lord) - return loc >= CALENDAR -} - -function is_lord_ready(lord) { - let loc = get_lord_locale(lord) - return loc >= CALENDAR && loc <= CALENDAR + (game.turn >> 1) -} - -// === VASSAL STATE === +// === STATE: VASSAL === function set_vassal_lord_and_service(vassal, lord, service) { game.pieces.vassals[vassal] = lord + (service << 5) @@ -1145,120 +1202,11 @@ function pay_vassal(vassal) { set_vassal_lord_and_service(vassal, get_vassal_lord(vassal), current_turn() + 1) } -function is_york_lord(lord) { - return lord >= first_york_lord && lord <= last_york_lord -} - -function is_lancaster_lord(lord) { - return lord >= first_lancaster_lord && lord <= last_lancaster_lord -} - -function is_friendly_lord(lord) { - return lord >= first_friendly_lord && lord <= last_friendly_lord -} - -function is_enemy_lord(lord) { - return lord >= first_enemy_lord && lord <= last_enemy_lord -} - -function is_lord_at_friendly_locale(lord) { - let loc = get_lord_locale(lord) - return is_friendly_locale(loc) -} - -function has_locale_to_muster(lord) { - // Can muster at own seat without enemy lord. - if (!has_enemy_lord(data.lords[lord].seat)) - return true - - // Else, can muster at any friendly seat (of a friendly lord who is also in play) - for (let other = first_friendly_lord; other <= last_friendly_lord; other++) - if (is_lord_in_play(other) && is_friendly_locale(data.lords[other].seat)) - return true - - // Tough luck! - return false -} - -function has_friendly_lord(loc) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (get_lord_locale(lord) === loc) - return true - return false -} - -function is_adjacent_friendly_port_english_channel(loc) { - for (let next of data.locales[loc].adjacent) { - if (is_friendly_locale(next) && data.port_2.includes(next)) - return true - } -} - -function has_enemy_lord(loc) { - for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) - if (get_lord_locale(lord) === loc) - return true - return false -} - -function has_unbesieged_enemy_lord(loc) { - for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) - if (get_lord_locale(lord) === loc) - return true - return false -} - -function is_seaport(loc) { - return set_has(data.seaports, loc) -} - -function is_stronghold(loc) { - return data.locales[loc].type !== "exile" -} - -function is_exile(loc) { - return data.locales[loc].type === "exile" -} - -function is_city(loc) { - return data.locales[loc].type === "city" -} - -function is_town(loc) { - return data.locales[loc].type === "town" -} - -function is_wales(loc) { - return data.locales[loc].region === "Wales" -} - -function is_south(loc) { - return data.locales[loc].region === "South" -} - -function is_north(loc) { - return data.locales[loc].region === "North" -} - -function is_lord_in_wales(lord) { - return is_wales(get_lord_locale(lord)) -} - -function is_lord_in_south(lord) { - return is_south(get_lord_locale(lord)) -} - -function is_lord_in_north(lord) { - return is_north(get_lord_locale(lord)) -} - -function is_fortress(loc) { - return data.locales[loc].type === "fortress" +function rout_vassal(_lord, vassal) { + set_add(game.battle.routed_vassals, vassal) } -function is_sea(loc) { - return data.locales[loc].type === "sea" -} +// === STATE: LOCALE === function is_favour_friendly(loc) { if (game.active === YORK) @@ -1298,6 +1246,44 @@ function remove_favoury_marker(loc) { set_delete(game.pieces.favoury, loc) } +function shift_favour_away(loc) { + if (game.active === YORK) + shift_favour_toward_lancaster(loc) + else + shift_favour_toward_york(loc) +} + +function shift_favour_toward(loc) { + if (game.active === YORK) + shift_favour_toward_york(loc) + else + shift_favour_toward_lancaster(loc) +} + +function shift_favour_toward_york(loc) { + if (has_favourl_marker(loc)) + remove_favourl_marker(loc) + else + add_favoury_marker(loc) +} + +function shift_favour_toward_lancaster(loc) { + if (has_favoury_marker(loc)) + remove_favoury_marker(loc) + else + add_favourl_marker(loc) +} + +function set_favour_enemy(loc) { + if (game.active === YORK) { + remove_favoury_marker(loc) + add_favourl_marker(loc) + } else { + remove_favourl_marker(loc) + add_favoury_marker(loc) + } +} + function has_exhausted_marker(loc) { return set_has(game.pieces.exhausted, loc) } @@ -1341,1201 +1327,303 @@ function deplete_locale(loc) { } } +function is_neutral_locale(loc) { + return !has_favourl_marker(loc) && !has_favoury_marker(loc) +} + +function has_favour_in_locale(side, loc) { + if (side === YORK) + return has_favoury_marker(loc) + else + return has_favourl_marker(loc) +} + function is_friendly_locale(loc) { if (loc !== NOWHERE && loc < CALENDAR) { if (has_enemy_lord(loc)) return false - if (is_favour_friendly(loc)) { + if (is_favour_friendly(loc)) return true - } } return false } -function can_add_troops(_lordwho, locale) { - if (!has_exhausted_marker(locale) && !is_exile(locale)) - return true - return false -} - -function can_add_troops_coa(lordwho, locale) { - for (let next of data.locales[locale].adjacent) { - if (is_friendly_locale(next) && lord_has_capability(lordwho, AOW_LANCASTER_COMMISION_OF_ARRAY) && (!has_exhausted_marker(locale) && !is_exile(locale))) +function is_adjacent_friendly_port_english_channel(loc) { + for (let next of data.locales[loc].adjacent) { + if (is_friendly_locale(next) && data.port_2.includes(next)) return true } - return false } -function can_add_troops_beloved_warwick(lordwho, locale) { - return ( - lord_has_capability(lordwho, AOW_YORK_BELOVED_WARWICK) && - !has_exhausted_marker(locale) && - !is_exile(locale) - ) -} +// === STATE: LORD & LOCALE === -function can_add_troops_irishmen(lordwho, locale) { - return ( - lord_has_capability(lordwho, AOW_YORK_IRISHMEN) && - !has_exhausted_marker(locale) && - (locale === LOC_IRELAND || data.port_3.includes(locale)) - ) -} - -function can_add_troops_sof(lordwho, locale) { - if ( - lord_has_capability(lordwho, AOW_YORK_SOLDIERS_OF_FORTUNE) && - !has_exhausted_marker(locale) && - !is_exile(locale) && - get_shared_assets(locale, COIN) > 0 - ) { - let number = 6 - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - number -= get_lord_forces(lord, MERCENARIES) - if (number >= 1) - return true - } - return false +function is_lord_at_friendly_locale(lord) { + return is_friendly_locale(get_lord_locale(lord)) } -function can_add_transport(who, what) { - return get_lord_assets(who, what) < 100 +function is_lord_in_wales(lord) { + return is_wales(get_lord_locale(lord)) } -function reduce_influence(amt) { - if (game.active === YORK) - reduce_york_influence(amt) - else - reduce_lancaster_influence(amt) +function is_lord_in_south(lord) { + return is_south(get_lord_locale(lord)) } -function reduce_york_influence(amt) { - game.influence += amt +function is_lord_in_north(lord) { + return is_north(get_lord_locale(lord)) } -function increase_york_influence(amt) { - game.influence -= amt +function has_friendly_lord(loc) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (get_lord_locale(lord) === loc) + return true + return false } -function reduce_lancaster_influence(amt) { - game.influence -= amt +function has_enemy_lord(loc) { + for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) + if (get_lord_locale(lord) === loc) + return true + return false } -function increase_lancaster_influence(amt) { - game.influence += amt +function has_unbesieged_enemy_lord(loc) { + for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) + if (get_lord_locale(lord) === loc) + return true + return false } -function shift_favour_away(loc) { - if (game.active === YORK) - shift_favour_toward_lancaster(loc) - else - shift_favour_toward_york(loc) +function is_lord_in_or_adjacent_to_north(lord) { + let here = get_lord_locale(lord) + if (is_north(here)) + return true + for (let loc of data.locales[here].adjacent) + if (is_north(loc)) + return true + return false } -function shift_favour_toward(loc) { - if (game.active === YORK) - shift_favour_toward_york(loc) - else - shift_favour_toward_lancaster(loc) +function is_lord_in_or_adjacent_to_south(lord) { + let here = get_lord_locale(lord) + if (is_south(here)) + return true + for (let loc of data.locales[here].adjacent) + if (is_south(loc)) + return true + return false } -function shift_favour_toward_york(loc) { - if (has_favourl_marker(loc)) - remove_favourl_marker(loc) - else - add_favoury_marker(loc) +function is_lord_in_or_adjacent_to_wales(lord) { + let here = get_lord_locale(lord) + if (is_wales(here)) + return true + for (let loc of data.locales[here].adjacent) + if (is_wales(loc)) + return true + return false } -function shift_favour_toward_lancaster(loc) { - if (has_favoury_marker(loc)) - remove_favoury_marker(loc) +// Captain capability (lieutenant/marshall only if no other) +function other_marshal_or_lieutenant(loc) { + let here = loc + let n = 0 + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (lord !== game.command) { + if (get_lord_locale(lord) === here && (is_marshal(lord) || is_lieutenant(lord))) + n += 1 + } + if (n === 0) + return false else - add_favourl_marker(loc) + return true } -function set_favour_enemy(loc) { - if (game.active === YORK) { - remove_favoury_marker(loc) - add_favourl_marker(loc) - } else { - remove_favourl_marker(loc) - add_favoury_marker(loc) - } +function has_adjacent_enemy(loc) { + for (let next of data.locales[loc].adjacent) + if (has_unbesieged_enemy_lord(next)) + return true + return false } -// === SETUP === - -function muster_lord_forces(lord) { - let info = data.lords[lord] - set_lord_forces(lord, RETINUE, info.forces.retinue | 0) - set_lord_forces(lord, MEN_AT_ARMS, info.forces.men_at_arms | 0) - set_lord_forces(lord, LONGBOWMEN, info.forces.longbowmen | 0) - set_lord_forces(lord, MILITIA, info.forces.militia | 0) +function has_adjacent_friendly(loc) { + for (let next of data.locales[loc].adjacent) + if (has_friendly_lord(next)) + return true + return false } -function muster_lord(lord, locale) { - let info = data.lords[lord] - - set_lord_locale(lord, locale) - - set_lord_assets(lord, PROV, info.assets.prov | 0) - set_lord_assets(lord, COIN, info.assets.coin | 0) +// === 1.4 INFLUENCE === - set_lord_assets(lord, CART, info.assets.cart | 0) - set_lord_assets(lord, SHIP, info.ships | 0) +// Bonus score but still maxed at 5 +function influence_capabilities(lord, score) { + let here = get_lord_locale(game.group) + if (game.active === YORK && is_event_in_play(EVENT_YORK_YORKIST_PARADE)) + score += 2 + if (game.active === YORK && is_event_in_play(EVENT_YORK_PRIVY_COUNCIL)) + score += 1 + if (game.state === "parley" && ((is_event_in_play(EVENT_YORK_RICHARD_OF_YORK) && game.active === YORK) || lord_has_capability(game.group, AOW_LANCASTER_IN_THE_NAME_OF_THE_KING))) + score += 1 + if (get_lord_locale(LORD_MARGARET) === here && lord_has_capability(game.group, AOW_LANCASTER_LOYAL_SOMERSET)) + score += 1 + if (lord_has_capability(lord, AOW_YORK_YORKS_FAVOURED_SON)) + score += 1 + if ( + get_lord_locale(LORD_WARWICK_L) === here && + lord_has_capability(game.group, AOW_LANCASTER_MARRIED_TO_A_NEVILLE) && + is_friendly_locale(here) + ) + score += 2 + if (has_favoury_marker(here) && lord_has_capability(lord, AOW_YORK_FAIR_ARBITER)) + score += 1 + if (lord_has_capability(lord, AOW_YORK_FALLEN_BROTHER) && !is_lord_in_play(LORD_CLARENCE)) + score += 2 - muster_lord_forces(lord) + return score } -function draw_card(deck) { - clear_undo() - let i = random(deck.length) - let c = deck[i] - set_delete(deck, c) - return c -} +// Cards that allows automatic success +function automatic_success(lord, score) { -function discard_events(when) { - for (let i = 0; i < game.events.length; ) { - let c = game.events[i] - if (data.cards[c].when === when) - array_remove(game.events, i) - else - ++i - } -} + if (lord_has_capability(lord, AOW_LANCASTER_TWO_ROSES)) + score = 6 + if (game.active === LANCASTER + && is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND) + && game.state === "levy_muster_vassal") + score = 6 + if (game.active === LANCASTER + && game.flags.parliament_votes === 1 + && game.state === "parley") + score = 6 + if (game.active === YORK + && game.flags.jack_cade > 0 + && game.state === "parley") + score = 6 + if (game.active === YORK + && game.flags.succession === 1 + && game.state === "parley") + score = 6 + if (is_campaign_phase() + && game.command === LORD_DEVON + && get_lord_locale(LORD_DEVON) === LOC_EXETER + && is_event_in_play(EVENT_YORK_DORSET) + && game.state === "parley") + score = 6 -function discard_extra_levy_events() { - for (let i = 0; i < game.events.length; ) { - let c = game.events[i] - if (c === Y20) - array_remove(game.events, i) - else - ++i - } + return score } -function discard_friendly_events(when) { - for (let i = 0; i < game.events.length; ) { - let c = game.events[i] - if (is_friendly_card(c) && data.cards[c].when === when) - array_remove(game.events, i) - else - ++i +// Initiate influence check with cards influencing the cost overriding all others +// (even automatic at no cost) +function init_influence_check(lord) { + game.check = [] + game.check.push({ cost: 1, modifier: 0, source: "base" }) + game.check.push({ cost: 0, modifier: data.lords[lord].influence, source: "lord" }) + if (game.active === LANCASTER + && is_event_in_play(EVENT_YORK_AN_HONEST_TALE_SPEEDS_BEST) + && game.state === "parley"){ + game.check.push({ cost: 1, modifier: 0, source:"An Honest tale speeds best"}) } -} - -exports.setup = function (seed, scenario, options) { - game = { - seed, - scenario, - hidden: options.hidden ? 1 : 0, - - log: [], - undo: [], - - active: null, - rebel: null, - state: "setup_lords", - stack: [], - victory_check: 0, - influence: 0, - - hand_y: [], - hand_l: [], - plan_y: [], - plan_l: [], - - turn: 0, - events: [], // this levy/this campaign cards - - pieces: { - // per lord data - locale: [], - assets: [], - forces: [], - routed: [], - capabilities: [], // TODO map card -> lord instead of lord+slot -> card - moved: [], - in_exile: 0, - - // per vassal data - vassals: Array(vassal_count).fill(VASSAL_OUT_OF_PLAY), - - // per locale data - depleted: [], - exhausted: [], - favourl: [], - favoury: [], - propaganda: [], - }, - - flags: { - first_action: 0, - first_march_highway: 0, - free_levy: 0, - free_parley_henry:0, - free_parley_gloucester:0, - burgundians:0, - charity:0, - bloody:0, - london_for_york:0, - surprise_landing:0, - parliament_votes:0, - succession:0, - jack_cade:0, - commons_militia:0, - swap_battle_attacker:0, - supply_depot:0, - loyalty_and_trust:0, - warden_of_the_marches:0, - naval_blockade:0 - }, - - command: NOBODY, - actions: 0, - group: 0, - intercept_group: 0, - who: NOBODY, - where: NOWHERE, - what: NOTHING, - which: NOTHING, - count: 0, - - supply: 0, - march: 0, - battle: 0, - spoils: 0, - parley: 0, + if (game.active === LANCASTER + && is_event_in_play(EVENT_LANCASTER_PARLIAMENT_VOTES) + && game.flags.parliament_votes === 1 + && game.state === "parley") { + game.check.push({ cost: -1, modifier: 0, source:"Parliament Votes"}) } - - log_h1(scenario) - - switch (scenario) { - default: - case "Ia. Henry VI": - setup_Ia() - break - case "Ib. Towton": - setup_Ib() - break - case "Ic. Somerset's Return": - setup_Ic() - break - case "II. Warwicks' Rebellion": - setup_II() - break - case "III. My Kingdom for a Horse": - setup_III() - break - case "I-III. Wars of the Roses": - setup_ItoIII() - break + if (game.active === YORK + && is_event_in_play(EVENT_YORK_SUCCESSION) + && game.flags.succession === 1 + && game.state === "parley") { + game.check.push({ cost: -1, modifier: 0, source:"Succession"}) } - - update_aliases() - - goto_setup_lords() - - return game -} - -function setup_Ia() { - game.turn = 1 << 1 - - game.rebel = YORK - game.active = YORK - game.victory_check = 40 - game.influence = 0 - muster_lord(LORD_YORK, LOC_ELY) - muster_lord(LORD_MARCH, LOC_LUDLOW) - muster_lord(LORD_HENRY_VI, LOC_LONDON) - muster_lord(LORD_SOMERSET_1, LOC_LONDON) - - set_lord_calendar(LORD_NORTHUMBERLAND_L, 2) - set_lord_calendar(LORD_EXETER_1, 3) - set_lord_calendar(LORD_BUCKINGHAM, 5) - set_lord_calendar(LORD_SALISBURY, 2) - set_lord_calendar(LORD_WARWICK_Y, 3) - set_lord_calendar(LORD_RUTLAND, 5) - - add_favourl_marker(LOC_LONDON) - add_favourl_marker(LOC_WELLS) - add_favourl_marker(LOC_SCOTLAND) - add_favourl_marker(LOC_FRANCE) - - add_favoury_marker(LOC_ELY) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_BURGUNDY) - add_favoury_marker(LOC_IRELAND) - - setup_vassals() -} - -function setup_Ib() { - game.turn = 1 << 1 - - game.rebel = YORK - game.active = YORK - game.victory_check = 45 - game.influence = 0 - muster_lord(LORD_NORFOLK, LOC_LONDON) - muster_lord(LORD_WARWICK_Y, LOC_LONDON) - muster_lord(LORD_MARCH, LOC_LUDLOW) - muster_lord(LORD_EXETER_1, LOC_NEWCASTLE) - muster_lord(LORD_SOMERSET_1, LOC_NEWCASTLE) - muster_lord(LORD_NORTHUMBERLAND_L, LOC_CARLISLE) - - add_favourl_marker(LOC_ST_ALBANS) - add_favourl_marker(LOC_SCARBOROUGH) - add_favourl_marker(LOC_NEWCASTLE) - add_favourl_marker(LOC_BAMBURGH) - add_favourl_marker(LOC_HEXHAM) - add_favourl_marker(LOC_APPLEBY) - add_favourl_marker(LOC_CARLISLE) - add_favourl_marker(LOC_SCOTLAND) - add_favourl_marker(LOC_FRANCE) - - add_favoury_marker(LOC_LONDON) - add_favoury_marker(LOC_CALAIS) - add_favoury_marker(LOC_GLOUCESTER) - add_favoury_marker(LOC_HEREFORD) - add_favoury_marker(LOC_OXFORD) - add_favoury_marker(LOC_SALISBURY) - add_favoury_marker(LOC_WINCHESTER) - add_favoury_marker(LOC_GUILDFORD) - add_favoury_marker(LOC_ARUNDEL) - add_favoury_marker(LOC_HASTINGS) - add_favoury_marker(LOC_DOVER) - add_favoury_marker(LOC_ROCHESTER) - add_favoury_marker(LOC_CANTERBURY) - add_favoury_marker(LOC_SOUTHAMPTON) - add_favoury_marker(LOC_BURGUNDY) - add_favoury_marker(LOC_IRELAND) - - setup_vassals([ VASSAL_FAUCONBERG, VASSAL_NORFOLK ]) - muster_vassal(VASSAL_FAUCONBERG, LORD_MARCH) -} - -function setup_Ic() { - game.turn = 5 << 1 - - game.rebel = YORK - game.active = YORK - game.victory_check = 40 - game.influence = 6 - muster_lord(LORD_WARWICK_Y, LOC_LONDON) - muster_lord(LORD_MARCH, LOC_LONDON) - muster_lord(LORD_SOMERSET_1, LOC_BAMBURGH) - - set_lord_calendar(LORD_HENRY_VI, 5) - - add_favourl_marker(LOC_SCARBOROUGH) - add_favourl_marker(LOC_NEWCASTLE) - add_favourl_marker(LOC_BAMBURGH) - add_favourl_marker(LOC_HEXHAM) - add_favourl_marker(LOC_APPLEBY) - add_favourl_marker(LOC_CARLISLE) - add_favourl_marker(LOC_HARLECH) - add_favourl_marker(LOC_PEMBROKE) - add_favourl_marker(LOC_CARDIFF) - add_favourl_marker(LOC_CHESTER) - add_favourl_marker(LOC_LANCASTER) - add_favourl_marker(LOC_SCOTLAND) - add_favourl_marker(LOC_FRANCE) - - add_favoury_marker(LOC_LONDON) - add_favoury_marker(LOC_CALAIS) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_HEREFORD) - add_favoury_marker(LOC_SALISBURY) - add_favoury_marker(LOC_WINCHESTER) - add_favoury_marker(LOC_GUILDFORD) - add_favoury_marker(LOC_ARUNDEL) - add_favoury_marker(LOC_HASTINGS) - add_favoury_marker(LOC_DOVER) - add_favoury_marker(LOC_ROCHESTER) - add_favoury_marker(LOC_CANTERBURY) - add_favoury_marker(LOC_SOUTHAMPTON) - add_favoury_marker(LOC_BURGUNDY) - add_favoury_marker(LOC_IRELAND) - - setup_vassals() -} - -function setup_II() { - game.turn = 1 << 1 - - game.rebel = LANCASTER - game.active = LANCASTER - game.victory_check = 40 - game.influence = 0 - muster_lord(LORD_EDWARD_IV, LOC_LONDON) - muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) - muster_lord(LORD_WARWICK_L, LOC_CALAIS) - muster_lord(LORD_CLARENCE, LOC_YORK) - muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) - - set_lord_calendar(LORD_DEVON, 1) - set_lord_calendar(LORD_GLOUCESTER_1, 9) - set_lord_calendar(LORD_NORTHUMBERLAND_Y1, 9) - set_lord_calendar(LORD_MARGARET, 9) - set_lord_in_exile(LORD_MARGARET) - set_lord_calendar(LORD_SOMERSET_2, 9) - set_lord_in_exile(LORD_SOMERSET_2) - set_lord_calendar(LORD_OXFORD, 9) - set_lord_in_exile(LORD_OXFORD) - set_lord_calendar(LORD_EXETER_2, 9) - set_lord_in_exile(LORD_EXETER_2) - - add_favourl_marker(LOC_CALAIS) - add_favourl_marker(LOC_YORK) - add_favourl_marker(LOC_HARLECH) - add_favourl_marker(LOC_COVENTRY) - add_favourl_marker(LOC_WELLS) - add_favourl_marker(LOC_FRANCE) - - add_favoury_marker(LOC_LONDON) - add_favoury_marker(LOC_ELY) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_CARLISLE) - add_favoury_marker(LOC_PEMBROKE) - add_favoury_marker(LOC_EXETER) - add_favoury_marker(LOC_BURGUNDY) - - setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) - - // TODO: Add Foreign Haven rule - // TODO: Add Skaky Allies rules } -function setup_III() { - game.turn = 1 << 1 - - game.rebel = LANCASTER - game.active = LANCASTER - game.victory_check = 40 - game.influence = 0 - muster_lord(LORD_RICHARD_III, LOC_LONDON) - muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) - muster_lord(LORD_NORFOLK, LOC_ARUNDEL) - muster_lord(LORD_HENRY_TUDOR, LOC_FRANCE) - muster_lord(LORD_JASPER_TUDOR_2, LOC_FRANCE) - muster_lord(LORD_OXFORD, LOC_FRANCE) - - add_favourl_marker(LOC_FRANCE) - add_favourl_marker(LOC_OXFORD) - add_favourl_marker(LOC_HARLECH) - add_favourl_marker(LOC_PEMBROKE) - - add_favoury_marker(LOC_BURGUNDY) - add_favoury_marker(LOC_LONDON) - add_favoury_marker(LOC_CALAIS) - add_favoury_marker(LOC_CARLISLE) - add_favoury_marker(LOC_ARUNDEL) - add_favoury_marker(LOC_YORK) - add_favoury_marker(LOC_GLOUCESTER) - - setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) -} - -function setup_ItoIII() { - game.turn = 1 << 1 - - game.rebel = YORK - game.active = YORK - game.victory_check = 45 - game.influence = 0 - muster_lord(LORD_YORK, LOC_ELY) - muster_lord(LORD_MARCH, LOC_LUDLOW) - muster_lord(LORD_HENRY_VI, LOC_LONDON) - muster_lord(LORD_SOMERSET_1, LOC_WELLS) - - set_lord_calendar(LORD_NORTHUMBERLAND_L, 1) - set_lord_calendar(LORD_EXETER_1, 3) - set_lord_calendar(LORD_BUCKINGHAM, 5) - set_lord_calendar(LORD_SALISBURY, 2) - set_lord_calendar(LORD_WARWICK_Y, 3) - set_lord_calendar(LORD_RUTLAND, 5) - - add_favourl_marker(LOC_LONDON) - add_favourl_marker(LOC_WELLS) - add_favourl_marker(LOC_SCOTLAND) - add_favourl_marker(LOC_FRANCE) - - add_favoury_marker(LOC_ELY) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_BURGUNDY) - add_favoury_marker(LOC_IRELAND) - - setup_vassals() +function end_influence_check() { + game.check = 0 } -// === CAMPAIGN GAME -- NOT IMPLEMENTED YET === -/* +function count_influence_score() { + let score = game.check.reduce((p, c) => p + c.modifier, 0) + let lord = 0 + if (is_levy_phase()) + lord = game.who + if (is_campaign_phase()) + lord = game.command -function should_remove_Y28_event_card() { - return game.scenario !== "I-III. Wars of the Roses" -} + // Space for whose lord has been selected for SUSPICION EVENT -function has_Y28_happened() { - //TODO: Scenario IIY and IIL when Y28 happens. - return false -} + score = (lord, score) -function add_card_scenario(c) { - // TODO: Add card in scenario -} + if (score > 5) + score = 5 + if (score < 1) + score = 1 -function remove_card_scenario(c) { - //TODO: Remove card in scenario + score = automatic_success(lord, score) + return score } -function setup_II_Y() { - game.turn = 1 << 1 - game.scenario = "IIY. The Kingmaker" - game.rebel = LANCASTER - game.active = LANCASTER - game.victory_check = 45 - game.influence = 0 - - for (let lord = first_lord; lord <= last_lord; lord++) { - if (is_lord_in_play(lord)) { - disband_lord(lord, false) +function count_influence_cost() { + if (game.state === "parley") { + if (is_campaign_phase()) { + if (game.command === LORD_DEVON && get_lord_locale(LORD_DEVON) === LOC_EXETER && is_event_in_play(EVENT_YORK_DORSET)) + return 0 } - } - for (let loc = first_locale; loc <= last_locale; loc++) { - remove_exhausted_marker(loc) - remove_depleted_marker(loc) - remove_favourl_marker(loc) - remove_favoury_marker(loc) - } - discard_events("this_levy") - discard_events("hold") - discard_events("this_campaign") - - // Setup - // Yorkist setup - // TODO: Add cards Y1-Y13, Y25, Y26, Y27, Y29, Y30 - - if (is_lord_in_play(LORD_RUTLAND) && main_york_heir !== LORD_RUTLAND) { - muster_lord(LORD_RUTLAND, LOC_CANTERBURY) - add_favoury_marker(LOC_CANTERBURY) - } - - set_lord_calendar(LORD_DEVON, 1) - set_lord_calendar(LORD_GLOUCESTER_1, 9) - set_lord_calendar(LORD_NORTHUMBERLAND_Y1, 9) - - if (main_york_heir === LORD_YORK) { - muster_lord(LORD_YORK, LOC_CANTERBURY) - add_favoury_marker(LOC_LONDON) - if (is_lord_in_play(LORD_MARCH)) { - muster_lord(LORD_MARCH, LOC_LUDLOW) + if (is_levy_phase()) { + if (game.flags.jack_cade > 0) + return 0 } - // TODO: Add cards Y14, Y18, Y19, Y20 - } - - if (main_york_heir === LORD_MARCH) { - muster_lord(LORD_EDWARD_IV, LOC_LONDON) - // Removed because he can't appear in scenario III - disband_lord(LORD_MARCH, true) - // TODO: Add cards Y23, Y24, Y28, Y31 - } - - if (main_york_heir === LORD_RUTLAND) { - muster_lord(LORD_RUTLAND, LOC_LONDON) - // TODO: Add cards Y20, Y21, Y28, Y35 - } - - // If < 2 heirs, muster Pembroke - if ((main_york_heir === LORD_RUTLAND || main_york_heir === LORD_GLOUCESTER_1) - || (main_york_heir === LORD_EDWARD_IV && !is_lord_in_play(LORD_RUTLAND))) { - muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) - } - - // Lancaster setup - // TODO: Add cards L1-L3, L5-L13, L23, L24, L25, L29, L30, L36 - - if (main_lancaster_heir === LORD_HENRY_VI) { - set_lord_calendar(LORD_HENRY_VI, 9) - set_lord_in_exile(LORD_HENRY_VI) - // TODO: Add L17, L18, L20, L21 - } - if (main_lancaster_heir === LORD_MARGARET) { - set_lord_calendar(LORD_MARGARET, 9) - set_lord_in_exile(LORD_MARGARET) - - // TODO: Add L27, L28, L31 + L26 Special rule - } - if (main_lancaster_heir === LORD_SOMERSET_1 || main_lancaster_heir === LORD_SOMERSET_2) { - // TODO: Add cards L20, L21, L27 - } - - if (is_lord_in_play(LORD_SOMERSET_1)) { - set_lord_calendar(LORD_SOMERSET_1, 9) - set_lord_in_exile(LORD_SOMERSET_1) } - else if (is_lord_in_play(LORD_SOMERSET_2)) { - set_lord_calendar(LORD_SOMERSET_2, 9) - set_lord_in_exile(LORD_SOMERSET_2) - } - - muster_lord(LORD_WARWICK_L, LOC_CALAIS) - muster_lord(LORD_CLARENCE, LOC_YORK) - muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) - set_lord_calendar(LORD_OXFORD, 9) - set_lord_in_exile(LORD_OXFORD) - set_lord_calendar(LORD_EXETER_2, 9) - set_lord_in_exile(LORD_EXETER_2) - - add_favourl_marker(LOC_CALAIS) - add_favourl_marker(LOC_YORK) - add_favourl_marker(LOC_HARLECH) - add_favourl_marker(LOC_COVENTRY) - add_favourl_marker(LOC_WELLS) - - add_favoury_marker(LOC_LONDON) - add_favoury_marker(LOC_ELY) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_CARLISLE) - add_favoury_marker(LOC_PEMBROKE) - add_favoury_marker(LOC_EXETER) - - // Exile box setup - add_favourl_marker(LOC_FRANCE) - add_favoury_marker(LOC_BURGUNDY) - - setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) - - // TODO: Add Foreign Haven rule - // TODO: Add Skaky Allies rules - // TODO: Natural causes rule - + return game.check.reduce((p, c) => p + c.cost, 0) } -function setup_II_L() { - game.turn = 1 << 1 - game.scenario = "IIL. Lancastrian Legitimacy Fades" - game.rebel = YORK - game.active = YORK - game.victory_check = 40 - game.influence = 0 - - for (let lord = first_lord; lord <= last_lord; lord++) { - if (is_lord_in_play(lord)) { - disband_lord(lord, false) - } - } - for (let loc = first_locale; loc <= last_locale; loc++) { - remove_exhausted_marker(loc) - remove_depleted_marker(loc) - remove_favourl_marker(loc) - remove_favoury_marker(loc) - } - discard_events("this_levy") - discard_events("hold") - discard_events("this_campaign") - - // Setup - // Lancaster setup - // TODO: Add cards L1-L3, L5-L13, L18, L19, L20, L21, L25, L29, L34 - - if (main_lancaster_heir === LORD_HENRY_VI) { - muster_lord(LORD_HENRY_VI, LOC_LONDON) - // TODO: Add L15, L17 - if (is_lord_in_play(LORD_SOMERSET_1)) { - muster_lord(LORD_SOMERSET_1, LOC_WELLS) - } - if (is_lord_in_play(LORD_SOMERSET_2)) { - muster_lord(LORD_SOMERSET_2, LOC_WELLS) - } - } - - if (main_lancaster_heir === LORD_MARGARET) { - set_lord_calendar(LORD_MARGARET, 1) - // TODO: Add L27, L31 + L26 Special rule - if (is_lord_in_play(LORD_SOMERSET_1)) { - muster_lord(LORD_SOMERSET_1, LOC_WELLS) - } - if (is_lord_in_play(LORD_SOMERSET_2)) { - muster_lord(LORD_SOMERSET_2, LOC_WELLS) - } - } - if (main_lancaster_heir === LORD_SOMERSET_1 || main_lancaster_heir === LORD_SOMERSET_2) { - // TODO: Add cards L16, L27 - muster_lord(LORD_SOMERSET_1, LOC_LONDON) - if (main_lancaster_heir === LORD_SOMERSET_2) { - // Somerset 2 cylinder replaced by Somerset 1 cylinder - disband_lord(LORD_SOMERSET_2, true) - } - } - - // Yorkist setup - // TODO: Add cards Y1-Y13, Y15, Y16, Y17, Y22, Y28, Y29, Y31, Y34 - - if (main_york_heir === LORD_YORK) { - set_lord_calendar(LORD_YORK, 7) - set_lord_in_exile(LORD_YORK) - // TODO: Add cards Y14, Y20 - } - - if (main_york_heir === LORD_MARCH) { - set_lord_calendar(LORD_MARCH, 7) - set_lord_in_exile(LORD_MARCH) - // TODO: Add cards Y20, 21 - } - - if (main_york_heir === LORD_RUTLAND) { - set_lord_calendar(LORD_MARCH, 7) - // TODO: Add cards Y20, Y21 - } - - if (main_york_heir === LORD_GLOUCESTER_1) { - // TODO: Add cards Y25, Y30 - } - - if (is_lord_in_play(LORD_MARCH) && main_york_heir !== LORD_MARCH) { - set_lord_calendar(LORD_MARCH, 7) - set_lord_in_exile(LORD_MARCH) - } - - if (is_lord_in_play(LORD_RUTLAND) && main_york_heir !== LORD_RUTLAND) { - set_lord_calendar(LORD_RUTLAND, 7) - set_lord_in_exile(LORD_RUTLAND) - } - if (is_lord_in_play(LORD_GLOUCESTER_1) && main_york_heir !== LORD_GLOUCESTER_1) { - set_lord_calendar(LORD_GLOUCESTER_1, 7) - set_lord_in_exile(LORD_GLOUCESTER_1) - } - - muster_lord(LORD_WARWICK_Y, LOC_CALAIS) - muster_lord(LORD_SALISBURY, LOC_YORK) - muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) - muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) - set_lord_calendar(LORD_DEVON, 1) - set_lord_calendar(LORD_OXFORD, 2) - set_lord_calendar(LORD_EXETER_2, 2) - set_lord_calendar(LORD_NORTHUMBERLAND_L, 8) - - add_favourl_marker(LOC_LONDON) - add_favourl_marker(LOC_HARLECH) - add_favourl_marker(LOC_OXFORD) - add_favourl_marker(LOC_WELLS) - add_favourl_marker(LOC_EXETER) - add_favourl_marker(LOC_CARLISLE) - - add_favoury_marker(LOC_CALAIS) - add_favoury_marker(LOC_YORK) - add_favoury_marker(LOC_ELY) - add_favoury_marker(LOC_LUDLOW) - add_favoury_marker(LOC_PEMBROKE) - - // Exile box setup - add_favourl_marker(LOC_FRANCE) - add_favoury_marker(LOC_BURGUNDY) - - setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) +function do_influence_check() { + reduce_influence(count_influence_cost()) + let rating = count_influence_score() + let roll = roll_die() + let success - // TODO: Add Foreign Haven rule - // TODO: Add Shaky Allies rules - // TODO: Natural causes rule + if (roll === 1 || rating === 6) + success = true + else if (roll === 6) + success = false + else + success = roll <= rating + // TODO: print log message here instead of returning object + return { success: success, rating: rating, roll: roll } } -function setup_III_Y() { - game.turn = 1 << 1 - game.scenario = "IIIY. New Rivals" - game.rebel = LANCASTER - game.active = LANCASTER - game.victory_check = 45 - game.influence = 0 - - if (!is_lord_in_play(LORD_YORK)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_RUTLAND)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_HENRY_VI)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_SOMERSET_1)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2)) { - game.influence -= 8 - } - - for (let lord = first_lord; lord <= last_lord; lord++) { - if (is_lord_in_play(lord)) { - disband_lord(lord, false) - } - } - for (let loc = first_locale; loc <= last_locale; loc++) { - remove_exhausted_marker(loc) - remove_depleted_marker(loc) - remove_favourl_marker(loc) - remove_favoury_marker(loc) - } - discard_events("this_levy") - discard_events("hold") - discard_events("this_campaign") - - // Yorkist Setup - // TODO: Add Y1-Y13, Y36 - - if (has_Y28_happened()) { - if (is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III))) { - // If Gloucester (any) and Rutland, Rutland dies - disband_lord(LORD_RUTLAND, true) - } - } - - if (main_york_heir === LORD_RUTLAND && (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2))) { - // If Rutland is lone heir, Rutland dies - disband_lord(LORD_RUTLAND, true) - //Warwick becomes king - muster_lord(LORD_WARWICK_Y, LOC_LONDON) - add_favoury_marker(LOC_LONDON) - muster_lord(LORD_SALISBURY, LOC_YORK) - add_favoury_marker(LOC_YORK) - - // TODO: Add Y16, Y17, Y22 - } - - // If only 1 is alive - if (main_york_heir === LORD_YORK && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1)) { - muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) - add_favoury_marker(LOC_CARLISLE) - - // TODO: Add Y37 - } - if ((main_york_heir === LORD_MARCH || main_york_heir === LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1)) { - muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) - add_favoury_marker(LOC_CARLISLE) - // TODO: Add Y37 - } - if (main_york_heir === LORD_GLOUCESTER_1 || main_york_heir === LORD_RICHARD_III) { - muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) - add_favoury_marker(LOC_CARLISLE) - // TODO: Add Y37 - } - muster_lord(LORD_NORFOLK, LOC_ARUNDEL) - add_favoury_marker(LOC_ARUNDEL) - - if (main_york_heir === LORD_YORK) { - // TODO: Add Y14, Y21 - if (is_lord_in_play(LORD_MARCH)) { - muster_lord(LORD_MARCH, LOC_LUDLOW) - add_favoury_marker(LOC_LUDLOW) - // Add Y20 - // Only 2 heirs can stay - disband_lord(LORD_RUTLAND, true) - disband_lord(LORD_GLOUCESTER_1, true) - } - if (!is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_RUTLAND)) { - muster_lord(LORD_RUTLAND, LOC_CANTERBURY) - add_favoury_marker(LOC_CANTERBURY) - // TODO: Add Y20 - } - if (is_lord_in_play(LORD_GLOUCESTER_1)) { - muster_lord(LORD_GLOUCESTER_1, LOC_GLOUCESTER) - add_favoury_marker(LOC_GLOUCESTER) - // TODO: Y34 - } - } - if (main_york_heir === LORD_MARCH || main_york_heir === LORD_EDWARD_IV) { - muster_lord(LORD_EDWARD_IV, LOC_LONDON) - add_favoury_marker(LOC_LONDON) - - // If Edward IV is on the map, remove March - disband_lord(LORD_MARCH, true) - // TODO: Add Y23, Y24 - if (is_lord_in_play(LORD_RUTLAND)) { - muster_lord(LORD_RUTLAND, LOC_CANTERBURY) - add_favoury_marker(LOC_CANTERBURY) - // TODO: Add Y31 - } - if (is_lord_in_play(LORD_GLOUCESTER_1)) { - muster_lord(LORD_GLOUCESTER_1, LOC_GLOUCESTER) - add_favoury_marker(LOC_GLOUCESTER) - // TODO: Add Y28, Y34 - } - - } - if (main_york_heir === LORD_RUTLAND) { - muster_lord(LORD_RUTLAND, LOC_LONDON) - add_favoury_marker(LOC_LONDON) - // TODO: Add Y20, Y21 - if (is_lord_in_play(LORD_GLOUCESTER_1)) { - muster_lord(LORD_GLOUCESTER_2, LOC_LONDON) - // If Rutland is King, golden gloucester 2 arrives and gloucester 1 is gone - disband_lord(LORD_GLOUCESTER_1, true) - // TODO: Add Y34 - } - } - if (main_york_heir === LORD_GLOUCESTER_1) { - muster_lord(LORD_RICHARD_III, LOC_LONDON) - add_favoury_marker(LOC_LONDON) - // if Richard III is here, both gloucester are gone - disband_lord(LORD_GLOUCESTER_1, true) - disband_lord(LORD_GLOUCESTER_2, true) - // TODO: Add Y32, Y33 - } - - // Lancaster setup - // TODO: Add L1-L13, L34, L35, L36, L37 - - if (main_lancaster_heir === LORD_HENRY_VI || main_lancaster_heir === LORD_MARGARET) { - muster_lord(LORD_MARGARET, LOC_FRANCE) - // TODO: Add L27, L31 + L26 Edward - // Only one heir - disband_lord(LORD_HENRY_VI, true) - disband_lord(LORD_SOMERSET_1, true) - disband_lord(LORD_SOMERSET_2, true) - } - // If Margaret not here and Edward IV not king - if (!is_lord_on_map(LORD_MARGARET) && main_york_heir !== LORD_EDWARD_IV) { - muster_lord(LORD_HENRY_TUDOR, LOC_FRANCE) - // TODO: Add L32, L35 - disband_lord(LORD_SOMERSET_1, true) - disband_lord(LORD_SOMERSET_2, true) - } - if (!is_lord_on_map(LORD_MARGARET) && !is_lord_on_map(LORD_HENRY_TUDOR)) { - muster_lord(LORD_WARWICK_L, LOC_CALAIS) - add_favourl_marker(LOC_CALAIS) - // TODO: Add L23, L30 - } - - if (is_lord_on_map(LORD_MARGARET) || is_lord_on_map(LORD_HENRY_TUDOR)) { - muster_lord(LORD_OXFORD, LOC_FRANCE) - add_favourl_marker(LOC_OXFORD) - muster_lord(LORD_JASPER_TUDOR_2, LOC_FRANCE) - add_favoury_marker(LOC_PEMBROKE) - } - else if (is_lord_on_map(LORD_WARWICK_L)) { - muster_lord(LORD_OXFORD, LOC_CALAIS) - add_favourl_marker(LOC_OXFORD) - muster_lord(LORD_JASPER_TUDOR_2, LOC_CALAIS) - add_favoury_marker(LOC_PEMBROKE) - } - else { - throw Error("Error Lancastrian setup III.Y") - } - - // Exile box setup - add_favourl_marker(LOC_FRANCE) - add_favoury_marker(LOC_BURGUNDY) - - setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) +function add_influence_check_modifier_1() { + game.check.push({ cost: 1, modifier: 1, source: "add" }) } -function setup_III_L() { - game.turn = 1 << 1 - game.scenario = "IIIL. Yorkists Last Stand" - game.rebel = YORK - game.active = YORK - game.victory_check = 45 - game.influence = 0 - - if (!is_lord_in_play(LORD_YORK)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_RUTLAND)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) { - game.influence += 8 - } - if (!is_lord_in_play(LORD_HENRY_VI)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_SOMERSET_1)) { - game.influence -= 8 - } - if (!is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2)) { - game.influence -= 8 - } - - for (let lord = first_lord; lord <= last_lord; lord++) { - if (is_lord_in_play(lord)) { - disband_lord(lord, false) - } - } - for (let loc = first_locale; loc <= last_locale; loc++) { - remove_exhausted_marker(loc) - remove_depleted_marker(loc) - remove_favourl_marker(loc) - remove_favoury_marker(loc) - } - discard_events("this_levy") - discard_events("hold") - discard_events("this_campaign") - - // Lancaster Setup - // TODO: Add L1-L13, L25, L34, L36 - - if (main_lancaster_heir === LORD_HENRY_VI) { - muster_lord(LORD_HENRY_VI, LOC_LONDON) - // TOOD: Add L15, L17 - } - if (main_lancaster_heir === LORD_MARGARET) { - muster_lord(LORD_MARGARET, LOC_LONDON) - // TODO: Add L27, L31 - } - if (main_lancaster_heir === LORD_SOMERSET_1) { - muster_lord(LORD_SOMERSET_1, LOC_LONDON) - add_favourl_marker(LOC_WELLS) - // TODO: Add L18, L20, L27 - } - // Should never happen but as a failsafe - if (main_lancaster_heir === LORD_SOMERSET_2) { - muster_lord(LORD_SOMERSET_1, LOC_LONDON) - add_favourl_marker(LOC_WELLS) - disband_lord(LORD_SOMERSET_2, true) - // TODO: Add L18, L20, L27 - } - muster_lord(LORD_OXFORD, LOC_OXFORD) - muster_lord(LORD_JASPER_TUDOR_2, LOC_PEMBROKE) - add_favourl_marker(LOC_OXFORD) - add_favourl_marker(LOC_PEMBROKE) - add_favourl_marker(LOC_LONDON) - - // York Setup - // TOOD: Add Y1-Y13, Y36 - - if (has_Y28_happened()) { - if (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III)) { - // If Gloucester (any), all other yorkist heir dies - disband_lord(LORD_YORK, true) - disband_lord(LORD_RUTLAND, true) - disband_lord(LORD_MARCH, true) - disband_lord(LORD_EDWARD_IV, true) - disband_lord(LORD_GLOUCESTER_1, true) - disband_lord(LORD_RICHARD_III, true) - muster_lord(LORD_GLOUCESTER_2, LOC_BURGUNDY) - // TODO: Add Y35 - } - } - - if (main_york_heir === LORD_YORK) { - muster_lord(LORD_YORK, LOC_BURGUNDY) - add_favoury_marker(LOC_ELY) - // TODO: Add Y14, Y18 - if (is_lord_in_play(LORD_MARCH)) { - // Only next highest heir alive - disband_lord(LORD_RUTLAND, true) - disband_lord(LORD_GLOUCESTER_1, true) - disband_lord(LORD_GLOUCESTER_2, true) - muster_lord(LORD_MARCH, LOC_BURGUNDY) - add_favoury_marker(LOC_LUDLOW) - //TODO: Add Y20 - } - else if (!is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_RUTLAND)) { - // Only next highest heir alive - disband_lord(LORD_GLOUCESTER_1, true) - disband_lord(LORD_GLOUCESTER_2, true) - muster_lord(LORD_RUTLAND, LOC_BURGUNDY) - add_favoury_marker(LOC_CANTERBURY) - //TODO: Add Y20 - } - else if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2))) { - // Final Scenario, and no succession rule - disband_lord(LORD_GLOUCESTER_2, true) - muster_lord(LORD_GLOUCESTER_1, LOC_BURGUNDY) - add_favoury_marker(LOC_GLOUCESTER) - // TODO: Add Y4 - } - else { - // If York alone - muster_lord(LORD_SALISBURY, LOC_BURGUNDY) - add_favoury_marker(LOC_YORK) - //TODO: Add Y17, Y22 - } - } - if (main_york_heir === LORD_MARCH || main_york_heir === LORD_RUTLAND) { - // If March or Rutland is highest heir, Warwick takes the lead - disband_lord(LORD_MARCH, true) - disband_lord(LORD_RUTLAND, true) - muster_lord(LORD_WARWICK_Y, LOC_CALAIS) - add_favoury_marker(LOC_CALAIS) - //TODO: Add Y16 - } - - if (main_york_heir === LORD_WARWICK_Y) { - muster_lord(LORD_NORFOLK, LOC_CALAIS) - muster_lord(LORD_SALISBURY, LOC_CALAIS) - add_favoury_marker(LOC_CALAIS) - //TODO: Add Y17, Y22 - } - else ( - muster_lord(LORD_NORFOLK, LOC_BURGUNDY) - ) - - if (main_york_heir === LORD_GLOUCESTER_1) { - disband_lord(LORD_GLOUCESTER_1, true) - muster_lord(LORD_GLOUCESTER_2, LOC_BURGUNDY) - muster_lord(LORD_SALISBURY, LOC_BURGUNDY) - //TODO: Add Y17, Y22 - } +function add_influence_check_modifier_2() { + game.check.push({ cost: 3, modifier: 2, source: "add" }) +} - add_favoury_marker(LOC_ARUNDEL) +function add_influence_check_distance(distance) { + let idx = game.check.findIndex(i => i.source === "distance") - // Exile box setup - add_favourl_marker(LOC_FRANCE) - add_favoury_marker(LOC_BURGUNDY) + if (idx !== NOTHING) + game.check.splice(idx, 1) - setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) + game.check.push({ cost: distance, modifier: 0, source: "distance" }) } -// FULL SCENARIO HEIR -function get_main_york_heir() { - if (is_lord_in_play(LORD_YORK)) - return LORD_YORK - if (!is_lord_in_play(LORD_YORK) && is_lord_in_play(LORD_MARCH)) - return LORD_MARCH - if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_EDWARD_IV)) - return LORD_EDWARD_IV - if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && is_lord_in_play(LORD_RUTLAND)) - return LORD_RUTLAND - if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III))) - return LORD_GLOUCESTER_1 - if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) - return LORD_WARWICK_Y -} +function prompt_influence_check() { + if (!game.check.some(c => c.source === "add")) { + view.actions.spend1 = 1 + view.actions.spend3 = 1 + } + if (game.where !== NOWHERE) + gen_action_locale(game.where) + view.actions.check = 1 -function get_main_lancaster_heir() { - if (is_lord_in_play(LORD_HENRY_VI)) - return LORD_HENRY_VI - if (!is_lord_in_play(LORD_HENRY_VI) && is_lord_in_play(LORD_MARGARET)) - return LORD_MARGARET - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && is_lord_in_play(LORD_SOMERSET_1)) - return LORD_SOMERSET_1 - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && is_lord_in_play(LORD_SOMERSET_2)) - return LORD_SOMERSET_2 - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2) && is_lord_in_play(LORD_HENRY_TUDOR)) - return LORD_HENRY_TUDOR - if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2) && !is_lord_in_play(LORD_HENRY_TUDOR) && is_lord_in_play(LORD_WARWICK_L)) - return LORD_WARWICK_L + view.prompt += `Cost: ${count_influence_cost()} - Range (${range(count_influence_score())})` } -*/ +// === 2.0 SETUP === function goto_setup_lords() { // setup will be used in some scenarios @@ -2566,1969 +1654,689 @@ function end_setup_lords() { } } -// === EVENTS === - -function is_event_in_play(c) { - return set_has(game.events, c) -} - -function is_leeward_battle_line_in_play(lord) { - if (is_archery_step()) { - if (is_event_in_play(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) - && !is_event_in_play(EVENT_YORK_LEEWARD_BATTLE_LINE) - && is_york_lord(lord)) { - logevent(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) - return true - - } - if (is_event_in_play(EVENT_YORK_LEEWARD_BATTLE_LINE) - && !is_event_in_play(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) - && is_lancaster_lord(lord)) { - logevent(EVENT_YORK_LEEWARD_BATTLE_LINE) - return true - } +// === 3.1 LEVY: ARTS OF WAR (FIRST TURN) === - } - return false +function discard_card_capability(c) { + log(`${game.active} discarded C${c}.`) } -function is_caltrops_in_play() { - if (game.active === YORK) - return is_event_in_play(EVENT_YORK_CALTROPS) +function discard_card_event(c) { + log(`${game.active} discarded E${c}.`) } -function is_regroup_in_play() { +function goto_levy_arts_of_war_first() { if (game.active === YORK) - return is_event_in_play(EVENT_YORK_REGROUP) -} - -function is_swift_maneuver_in_play() { - return is_event_in_play(EVENT_YORK_SWIFT_MANEUVER) -} - -function is_york_dominating_north() { - let dom = 0 - for (let loc of all_north_locales) { - if (has_favoury_marker(loc)) { - dom++ - } - } - if (dom > 5) - return true - - return false -} - -function is_york_dominating_south() { - let dom = 0 - for (let loc of all_south_locales) { - if (has_favoury_marker(loc)) { - dom++ - } - } - if (dom > 9) - return true - if (dom > 4 - && (lord_has_capability(LORD_MARCH, AOW_YORK_SOUTHERNERS) - || lord_has_capability(LORD_RUTLAND, AOW_YORK_SOUTHERNERS) - || lord_has_capability(LORD_YORK, AOW_YORK_SOUTHERNERS))) - return true - - return false -} - -function is_york_dominating_wales() { - let dom = 0 - for (let loc of all_wales_locales) { - if (has_favoury_marker(loc)) { - dom++ - } - } - if (dom > 5) - return true - if (dom > 2 - && (lord_has_capability(LORD_MARCH, AOW_YORK_WELSHMEN) - || lord_has_capability(LORD_YORK, AOW_YORK_WELSHMEN))) - return true - - return false -} - -function is_lord_in_or_adjacent_to_north(lord) { - let here = get_lord_locale(lord) - if (is_north(here)) - return true - for (let loc of data.locales[here].adjacent) - if (is_north(loc)) - return true - return false -} - -function is_lord_in_or_adjacent_to_south(lord) { - let here = get_lord_locale(lord) - if (is_south(here)) - return true - for (let loc of data.locales[here].adjacent) - if (is_south(loc)) - return true - return false -} - -function is_lord_in_or_adjacent_to_wales(lord) { - let here = get_lord_locale(lord) - if (is_wales(here)) - return true - for (let loc of data.locales[here].adjacent) - if (is_wales(loc)) - return true - return false -} - -function is_jack_cade_eligible(lord) { - if (!is_event_in_play(EVENT_YORK_JACK_CADE)) - return false - if (is_lord_in_or_adjacent_to_south(lord) && is_york_dominating_south()) - return true - if (is_lord_in_or_adjacent_to_north(lord) && is_york_dominating_north()) - return true - if (is_lord_in_or_adjacent_to_wales(lord) && is_york_dominating_wales()) - return true - return false -} - -function goto_immediate_event(c) { - switch (c) { - // This Levy / Campaign - case EVENT_LANCASTER_BE_SENT_FOR: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_SEAMANSHIP: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_FORCED_MARCHES: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_RISING_WAGES: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_NEW_ACT_OF_PARLIAMENT: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_MY_CROWN_IS_IN_MY_HEART: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_PARLIAMENT_VOTES: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_FRENCH_FLEET: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_BUCKINGHAMS_PLOT: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_MARGARET_BEAUFORT: - set_add(game.events, c) - return end_immediate_event() - case EVENT_LANCASTER_THE_EARL_OF_RICHMOND: - set_add(game.events, c) - return end_immediate_event() - - case EVENT_YORK_JACK_CADE: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_SEAMANSHIP: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_YORKISTS_BLOCK_PARLIAMENT: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_EXILE_PACT: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_RICHARD_OF_YORK: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_THE_COMMONS: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_SUCCESSION: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_LOYALTY_AND_TRUST: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_OWAIN_GLYNDWR: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_GLOUCESTER_AS_HEIR: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_DORSET: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_THE_KINGS_NAME: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_EDWARD_V: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_AN_HONEST_TALE_SPEEDS_BEST: - set_add(game.events, c) - return end_immediate_event() - case EVENT_YORK_PRIVY_COUNCIL: - set_add(game.events, c) - return end_immediate_event() - - // Immediate effect - // Discard - Immediate Events - case EVENT_LANCASTER_SCOTS: - return goto_lancaster_event_scots() - case EVENT_LANCASTER_HENRY_PRESSURES_PARLIAMENT: - return goto_lancaster_event_henry_pressures_parliament() - case EVENT_LANCASTER_HENRYS_PROCLAMATION: - return goto_lancaster_event_henrys_proclamation() - case EVENT_LANCASTER_FRENCH_TROOPS: - return goto_lancaster_event_french_troops() - case EVENT_LANCASTER_WARWICKS_PROPAGANDA: - return goto_warwicks_propaganda() - case EVENT_LANCASTER_WARWICKS_PROPAGANDA2: - return goto_warwicks_propaganda() - case EVENT_LANCASTER_WELSH_REBELLION: - return goto_lancaster_event_welsh_rebellion() - case EVENT_LANCASTER_HENRY_RELEASED: - return goto_lancaster_event_henry_released() - case EVENT_LANCASTER_LUNIVERSELLE_ARAGNE: - return goto_lancaster_event_luniverselle_aragne() - case EVENT_LANCASTER_TO_WILFUL_DISOBEDIANCE: - return goto_lancaster_event_to_wilful_disobediance() - case EVENT_LANCASTER_FRENCH_WAR_LOANS: - return goto_lancaster_event_french_war_loans() - case EVENT_LANCASTER_ROBINS_REBELLION: - return goto_lancaster_event_robins_rebellion() - case EVENT_LANCASTER_TUDOR_BANNERS: - return goto_lancaster_event_tudor_banners() - case EVENT_YORK_TAX_COLLECTORS: - return goto_york_event_tax_collectors() - case EVENT_YORK_LONDON_FOR_YORK: - return goto_york_event_london_for_york() - case EVENT_YORK_SHEWOLF_OF_FRANCE: - return goto_york_event_shewolf_of_france() - case EVENT_YORK_SIR_RICHARD_LEIGH: - return goto_york_event_sir_richard_leigh() - case EVENT_YORK_CHARLES_THE_BOLD: - return goto_york_event_charles_the_bold() - case EVENT_YORK_DUBIOUS_CLARENCE: - return goto_dubious_clarence() - case EVENT_YORK_YORKIST_NORTH: - return goto_york_event_yorkist_north() - case EVENT_YORK_EARL_RIVERS: - return goto_york_event_earl_rivers() - default: - log("NOT IMPLEMENTED") - return end_immediate_event() - } -} - -function end_immediate_event() { - resume_levy_arts_of_war() -} - -// === EVENTS: UNIQUE IMMEDIATE EVENTS === - -// === EVENTS: LANCASTER SCOTS EVENT === - -function goto_lancaster_event_scots() { - game.state = "scots" - game.count = [] - game.who = NOBODY + log_h2("York Arts of War") + else + log_h2("Lancaster Arts of War") + game.state = "levy_arts_of_war_first" + game.what = draw_two_cards() } -function end_lancaster_event_scots() { - game.count = 0 - game.who = NOBODY - end_immediate_event() +function resume_levy_arts_of_war_first() { + if (game.what.length === 0) + end_levy_arts_of_war_first() } -states.scots = { - inactive: "Scots", +states.levy_arts_of_war_first = { + inactive: "Arts of War", prompt() { - view.prompt = "Scots: You may add 1 Men-at-Arms and 1 Militia to each Lord." - for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; lord++) { - if (is_lord_on_map(lord) && map_get(game.count, lord, 0) < 3) { + let c = game.what[0] + view.arts_of_war = game.what + view.what = c + let discard = true + for (let lord of data.cards[c].lords) { + if (is_lord_on_map(lord) && !lord_already_has_capability(lord, c)) { gen_action_lord(lord) + discard = false } } - - if (game.who !== NOBODY) { - let troops = map_get(game.count, game.who, 0) - if ((troops & 1) === 0) - view.actions.add_militia = 1 - if ((troops & 2) === 0) - view.actions.add_men_at_arms = 1 + if (discard) { + view.prompt = `Arts of War: Discard ${data.cards[c].capability}.` + view.actions.discard = 1 + } else { + view.prompt = `Arts of War: Assign ${data.cards[c].capability} to a Lord.` } - view.actions.done = 1 - }, - done() { - end_lancaster_event_scots() - }, - add_militia() { - add_lord_forces(game.who, MILITIA, 1) - let troops = map_get(game.count, game.who, 0) - map_set(game.count, game.who, troops + 1) - if (troops !== 0) - game.who = NOBODY - }, - add_men_at_arms() { - add_lord_forces(game.who, MEN_AT_ARMS, 1) - let troops = map_get(game.count, game.who, 0) - map_set(game.count, game.who, troops + 2) - if (troops !== 0) - game.who = NOBODY }, lord(lord) { push_undo() - game.who = lord + let c = game.what.shift() + log(`${game.active} deployed Capability.`) + add_lord_capability(lord, c) + capability_muster_effects(lord, c) + resume_levy_arts_of_war_first() + }, + discard() { + push_undo() + let c = game.what.shift() + discard_card_capability(c) + resume_levy_arts_of_war_first() }, } -// === EVENTS: LANCASTER HENRY PRESSURES PARLIAMENT EVENT === - -function goto_lancaster_event_henry_pressures_parliament() { - let count = 0 - for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { - if (is_vassal_mustered_with_york_lord(vassal)) { - count++ - } - } - - if (count > 0) { - logi(`Removed ${count} York influence.`) - reduce_york_influence(count) - } - - end_immediate_event() -} - -// === EVENTS: LANCASTER HENRY'S PROCLAMATION EVENT === - -function goto_lancaster_event_henrys_proclamation() { - for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { - if (is_vassal_mustered_with_york_lord(vassal)) { - set_vassal_lord_and_service(vassal, get_vassal_lord(vassal), current_turn()) - logi(`Vassal ${data.vassals[vassal].name} moved to current turn`) - - } - } - end_immediate_event() +function end_levy_arts_of_war_first() { + game.what = NOTHING + set_active_enemy() + if (game.active === P2) + goto_levy_arts_of_war_first() + else + goto_levy_muster() } -// === EVENTS: LANCASTER FRENCH TROOPS EVENT === +// === 3.1 LEVY: ARTS OF WAR === -function goto_lancaster_event_french_troops() { - let can_play = false - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_on_map(lord) && data.seaports.includes(get_lord_locale(lord))) { - can_play = true - } - } - if (can_play) { - game.state = "french_troops" - game.who = NOBODY - game.count = 0 - } else { - end_immediate_event() - } +function goto_levy_arts_of_war() { + if (game.active === YORK) + log_h2("York Arts of War") + else + log_h2("Lancaster Arts of War") + game.what = draw_two_cards() + resume_levy_arts_of_war() } -function end_lancaster_event_french_troops() { - game.who = NOBODY - game.count = 0 - end_immediate_event() +function resume_levy_arts_of_war() { + game.state = "levy_arts_of_war" + if (game.what.length === 0) + end_levy_arts_of_war() } -states.french_troops = { - inactive: "French Troops", +states.levy_arts_of_war = { + inactive: "Arts of War", prompt() { - - view.prompt = `Add 2 Men at Arms and 2 Militia to a Lord at a port.` - if (game.who === NOBODY) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_on_map(lord) && is_seaport(get_lord_locale(lord))) { - gen_action_lord(lord) - } - } - } else { - view.prompt = `Add ${2-pack2_get(game.count, 0)} Men at Arms and ${2-pack2_get(game.count, 1)} Militia to ${lord_name[game.who]}.` - if (pack2_get(game.count, 0) < 2) - view.actions.add_men_at_arms = 1 - if (pack2_get(game.count, 1) < 2) - view.actions.add_militia = 1 + let c = game.what[0] + view.arts_of_war = [ c ] + view.what = c + switch (data.cards[c].when) { + case "this_levy": + case "this_campaign": + case "now": + view.prompt = `Arts of War: Play ${data.cards[c].event}.` + view.actions.play = 1 + break + case "hold": + view.prompt = `Arts of War: Hold ${data.cards[c].event}.` + view.actions.hold = 1 + break + case "never": + view.prompt = `Arts of War: Discard ${data.cards[c].event}.` + view.actions.discard = 1 + break } - - view.actions.done = 1 }, - add_men_at_arms() { - push_undo() - add_lord_forces(game.who, MEN_AT_ARMS, 1) - let c = pack2_get(game.count, 0) - game.count = pack2_set(game.count, 0, c+1) + play() { + let c = game.what.shift() + log(`${game.active} played E${c}.`) + goto_immediate_event(c) }, - add_militia() { - push_undo() - add_lord_forces(game.who, MILITIA, 1) - let c = pack2_get(game.count, 1) - game.count = pack2_set(game.count, 1, c+1) + hold() { + let c = game.what.shift() + log(`${game.active} Held Event.`) + if (game.active === YORK) + set_add(game.hand_y, c) + else + set_add(game.hand_l, c) + resume_levy_arts_of_war() }, - lord(lord) { - push_undo() - game.who = lord + discard() { + let c = game.what.shift() + discard_card_event(c) + resume_levy_arts_of_war() }, - done() { - end_lancaster_event_french_troops() - } -} - -// === EVENTS: WARWICKS PROPAGANDA === - -function add_propaganda_target(loc) { - set_add(game.pieces.propaganda, loc) } -function remove_propaganda_target(loc) { - set_delete(game.pieces.propaganda, loc) +function end_levy_arts_of_war() { + game.what = NOTHING + set_active_enemy() + if (game.active === P2) + goto_levy_arts_of_war() + else + goto_pay() } -function is_propaganda_target(loc) { - return set_has(game.pieces.propaganda, loc) -} +// === 3.2 LEVY: PAY === -function goto_warwicks_propaganda() { - let can_play = false - for (let loc = first_locale; loc <= last_locale; ++loc) { - if (has_favoury_marker(loc)) { - can_play = true +function reset_unpaid_lords() { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_unfed(lord)) { + set_lord_unfed(lord, Math.ceil(count_lord_all_forces(lord) / 6)) } } - - if (can_play) { - game.state = "warwicks_propaganda" - game.who = NOBODY - game.count = 0 - } else { - end_immediate_event() - } } -states.warwicks_propaganda = { - inactive: "Warwick's Propaganda", - prompt() { - view.prompt = `Select up to ${3-game.count} Yorkists Locales.` - for (let loc = first_locale; loc <= last_locale; loc++) { - if (game.count < 3 && has_favoury_marker(loc) && !is_exile(loc) && !is_propaganda_target(loc)) { - gen_action_locale(loc) - } +function goto_pay() { + log_br() + let n = 0 + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + let here = get_lord_locale(lord) + if (is_lord_on_map(lord) && + !is_lord_on_calendar(lord) && + lord_has_capability(lord, AOW_LANCASTER_MADAME_LA_GRANDE) && + (((is_friendly_locale(here)) && data.port_2.includes(here)) || + is_adjacent_friendly_port_english_channel(here))) { + add_lord_assets(lord, COIN, 1) + } + if ( + game.active === LANCASTER && + is_lord_on_map(lord) && + lord_has_capability(LORD_NORTHUMBERLAND_L, AOW_LANCASTER_PERCYS_POWER) && + is_lord_in_north(LORD_NORTHUMBERLAND_L) && + is_lord_in_north(lord) + ) { + set_lord_unfed(lord, 0) + } else { + n = Math.ceil(count_lord_all_forces(lord) / 6) + set_lord_unfed(lord, n) } - view.actions.done = 1 - }, - locale(loc) { - push_undo() - add_propaganda_target(loc) - game.count++ - }, - done() { - goto_yorkist_choice() } + game.state = "pay" } -function goto_yorkist_choice() { - game.who = NOBODY - set_active_enemy() - game.state = "warwicks_propaganda_yorkist_choice" -} - -states.warwicks_propaganda_yorkist_choice = { - inactive: "Yorkists to choose to Pay or Remove favour", +states.pay = { + inactive: "Pay", prompt() { - view.prompt = `For each Stronghold, Pay 2 influence or Remove favour.` + view.prompt = "Pay: You must Pay your Lord's Troops" let done = true - if (game.who === NOBODY) { - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_propaganda_target(loc)) { - gen_action_locale(loc) - done = false + + // Pay from own mat + if (done) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_unfed(lord)) { + if (get_lord_assets(lord, COIN) > 0) { + gen_action_coin(lord) + done = false + } } } - if (done) { - view.actions.done = 1 - } - } else { - view.actions.remove_favour = 1 - view.actions.pay = 1 } - }, - locale(loc) { - game.who = loc - }, - remove_favour() { - push_undo() - remove_favoury_marker(game.who) - remove_propaganda_target(game.who) - logi(`Removed favour in ${game.who}`) - game.who = NOBODY - }, - pay() { - push_undo() - reduce_influence(2) - logi(`Paid 2 to keep ${game.who}`) - remove_propaganda_target(game.who) - game.who = NOBODY - }, - done() { - end_warwicks_propaganda() - }, -} - -function end_warwicks_propaganda() { - game.who = NOBODY - game.count = 0 - set_active_enemy() - end_immediate_event() -} - -// === EVENTS: WELSH REBELLION === - -function goto_lancaster_event_welsh_rebellion() { - let can_play_remove_troops = false - let can_play_remove_favour = false - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (is_lord_on_map(lord) && is_lord_in_wales(lord)) { - set_lord_moved(lord, 1) - can_play_remove_troops = true - } - } - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_wales(loc) && has_favoury_marker(loc)) - can_play_remove_favour = true - } - - if (can_play_remove_troops) { - push_state("welsh_rebellion_remove_troops") - game.who = NOBODY - game.count = 0 - } - else if (can_play_remove_favour) { - push_state("welsh_rebellion_remove_favour") - game.who = NOBODY - game.count = 0 - } - else { - end_immediate_event() - } -} -states.welsh_rebellion_remove_troops = { - inactive: "Welsh Rebellion \u2014 Remove troops", - prompt() { - view.prompt = `Remove 2 Troops from each enemy Lord in Wales.` - let done = true - if (game.who === NOBODY) { - for (let lord = first_enemy_lord; lord <= last_enemy_lord; lord++) { - if (is_lord_on_map(lord) && is_lord_in_wales(lord) && get_lord_moved(lord)) { + // Sharing + if (done) { + view.prompt = "Pay: You must Pay Lords with Shared Coin." + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_unfed(lord) && can_pay_from_shared(lord)) { gen_action_lord(lord) done = false } } - if (done) { - view.actions.done = 1 - } } - else if (game.who !== NOBODY) { - if (game.count >= 0) { - view.prompt = `Remove ${game.count} Troops from ${data.lords[game.who].name}.` - if (game.count === 0) { - set_lord_moved(game.who, 0) - view.actions.done = 1 - } - else { - if (get_lord_forces(game.who, BURGUNDIANS) > 0) - gen_action_burgundians(game.who) - if (get_lord_forces(game.who, MERCENARIES) > 0) - gen_action_mercenaries(game.who) - if (get_lord_forces(game.who, LONGBOWMEN) > 0) - gen_action_longbowmen(game.who) - if (get_lord_forces(game.who, MEN_AT_ARMS) > 0) - gen_action_men_at_arms(game.who) - if (get_lord_forces(game.who, MILITIA) > 0) - gen_action_militia(game.who) + + if (done) { + view.prompt = "Pay: You must Pillage and/or Disband." + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_unfed(lord)) { + view.actions.pillage = 1 + done = false } } } + + // All done! + if (done) { + view.prompt = "Pay: All done." + view.actions.end_pay = 1 + } + }, + coin(lord) { + push_undo() + add_lord_assets(lord, COIN, -1) + pay_lord(lord) }, lord(lord) { push_undo() game.who = lord - game.count = 2 - }, - burgundians(lord) { - add_lord_forces(lord, BURGUNDIANS, -1) - game.count-- - }, - mercenaries(lord) { - add_lord_forces(lord, MERCENARIES, -1) - game.count-- - }, - longbowmen(lord) { - add_lord_forces(lord, LONGBOWMEN, -1) - game.count-- - }, - men_at_arms(lord) { - add_lord_forces(lord, MEN_AT_ARMS, -1) - game.count-- + game.state = "pay_lord_shared" }, - militia(lord) { - add_lord_forces(lord, MILITIA, -1) - game.count-- + pillage() { + push_undo() + reset_unpaid_lords() + goto_pillage_coin() }, - done() { - end_welsh_rebellion() + end_pay() { + end_pay() }, + card: action_held_event, } -states.welsh_rebellion_remove_favour = { - inactive: "Welsh Rebellion \u2014 Remove Favour", +states.pay_lord_shared = { + inactive: "Pay", prompt() { - view.prompt = `Select up to ${2-game.count} Locales in Wales.` - for (let loc = first_locale; loc <= last_locale; loc++) { - if (game.count < 2 && is_wales(loc) && has_favoury_marker(loc)) { - gen_action_locale(loc) + view.prompt = `Pay: You must Feed ${lord_name[game.who]} with Shared Coin.` + let loc = get_lord_locale(game.who) + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (get_lord_locale(lord) === loc) { + if (get_lord_assets(lord, COIN) > 0) + gen_action_coin(lord) } } - view.actions.done = 1 }, - locale(loc) { + coin(lord) { push_undo() - remove_favoury_marker(loc) - logi(`Removed favour at ${data.locales[loc].name}`) - game.count++ - }, - done() { - end_immediate_event() + add_lord_assets(lord, COIN, -1) + pay_lord(game.who) + resume_pay_lord_shared() }, } -function end_welsh_rebellion() { - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (is_lord_on_map(lord) && is_lord_in_wales(lord) && !lord_has_unrouted_units(lord)) - disband_lord(lord, false) - } - game.count = 0 - game.who = NOBODY - end_immediate_event() -} - -// === EVENTS: HENRY RELEASED === -function goto_lancaster_event_henry_released() { - if (has_favourl_marker(LOC_LONDON)) { - logi(`Henry Released : 5 Influence for Lancaster`) - increase_lancaster_influence(5) - } - end_immediate_event() -} - -// === EVENTS : L'UNIVERSELLE ARAGNE === - -function goto_lancaster_event_luniverselle_aragne() { - let can_play = false - for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { - if (is_vassal_mustered_with_york_lord(vassal)) { - can_play = true - } - } - if (can_play) { - game.state = "aragne" +function resume_pay_lord_shared() { + if (!is_lord_unfed(game.who) || !can_pay_from_shared(game.who)) { game.who = NOBODY - game.count = 0 - } else { - logi("No Effect") - end_immediate_event() + game.state = "pay" } } -states.aragne = { - inactive: "L'universelle Aragne", - prompt() { - view.prompt = "Select up to 2 Vassals" - if (game.who === NOBODY && game.count < 2) { - for (let v = first_vassal; v <= last_vassal; v++) { - if (!get_vassal_moved(v) && is_vassal_mustered_with_york_lord(v)) { - gen_action_vassal(v) - } - } - } - view.actions.done = 1 - }, - vassal(v) { - push_undo() - game.who = v - game.count++ - set_vassal_moved(v, 1) - logi(`Vassal ${data.vassals[v].name} selected`) - game.who = NOBODY - }, - done() { - goto_yorkist_aragne() - }, -} - -function goto_yorkist_aragne() { +function end_pay() { game.who = NOBODY set_active_enemy() - game.state = "yorkist_aragne" + if (game.active === P2) { + goto_pay() + } else + goto_pay_lords() } -states.yorkist_aragne = { - inactive: "Influence checks", - prompt() { - view.prompt = `For Each vassal, influence check : failure disbands it` - let done = true - if (game.who === NOBODY) { - for (let v = first_vassal; v <= last_vassal; v++) { - if (get_vassal_moved(v) && is_vassal_mustered_with_friendly_lord(v)) { - gen_action_vassal(v) - done = false - } - } - if (done) { - view.actions.done = 1 - } - } - }, - vassal(other) { - push_undo() - goto_aragne_save(other) - }, - done() { - end_universelle_aragne() - }, -} +// === 3.2.1 PAY TROOPS (PILLAGE) === -function goto_aragne_save(other) { - game.who = other - game.where = NOWHERE - get_vassal_lord(other) - init_influence_check(get_vassal_lord(other)) - game.check.push({ - cost: 0, - modifier: data.vassals[other].influence * (game.active === LANCASTER ? -1 : 1), - source: "vassal", - }) - push_state("aragne_save") +function goto_pillage_food() { + push_state("pillage") + game.what = PROV } -states.aragne_save = { - inactive: `Influence check`, - prompt() { - view.prompt = `Influence check : Failure disbands ${data.vassals[game.who].name}` - prompt_influence_check() - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - logi(`Attempt to save ${data.vassals[game.who].name} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - - if (results.success) { - set_vassal_moved(game.who, 0) - game.who = NOBODY - end_influence_check() - push_state("yorkist_aragne") - } else { - set_vassal_moved(game.who, 0) - disband_vassal(game.who) - if (game.who === VASSAL_HASTINGS) { - discard_card_capability(AOW_YORK_HASTINGS) - logi(`Hastings Discarded`) - } - game.who = NOBODY - end_influence_check() - push_state("yorkist_aragne") - } - }, +function goto_pillage_coin() { + push_state("pillage") + game.what = COIN } -function end_universelle_aragne() { - game.who = NOBODY - game.count = 0 - end_immediate_event() +function end_pillage() { + pop_state() } -// === EVENTS : TO WILFUL DISOBEDIANCE === - -function goto_lancaster_event_to_wilful_disobediance() { - let can_play = false - for (let loc = first_locale; loc <= last_locale; loc++){ - if (has_favoury_marker(loc) && !has_enemy_lord(loc) && !has_adjacent_enemy(loc) && (has_friendly_lord(loc) || has_adjacent_friendly(loc))) { - can_play = true - } - } - if (can_play) { - game.state = "wilful_disobediance" - game.who = NOBODY - game.count = 0 - } else { - end_immediate_event() - logi(`No Effect`) - } - -} -states.wilful_disobediance = { - inactive: "to wilful disobediance", - prompt() { - view.prompt = `Select up to ${2-game.count} Yorkists Locales.` - for (let loc = first_locale; loc <= last_locale; loc++) { - if ( - game.count < 2 && - has_favoury_marker(loc) && - !has_enemy_lord(loc) && - !has_adjacent_enemy(loc) && - (has_friendly_lord(loc) || has_adjacent_friendly(loc)) - ) { - gen_action_locale(loc) - } - } - view.actions.done = 1 - }, - locale(loc) { - push_undo() - remove_favoury_marker(loc) - game.count++ - logi(`Favour removed at ${loc}`) - }, - done() { - logi(`No Effect`) - end_immediate_event() - } +function can_pillage(loc) { + return !is_exile(loc) && !has_exhausted_marker(loc) } -// === EVENTS : FRENCH WAR LOANS === +states.pillage = { + inactive: "Pillage", + prompt() { + view.prompt = `Pillage: Pillage the locales where your ${game.what === PROV ? "unfed" : "unpaid"} lords are.` -function goto_lancaster_event_french_war_loans() { - for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; ++lord) { - if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { - add_lord_assets(lord, PROV, 1) - add_lord_assets(lord, COIN, 1) - logi(`1 Coin and 1 Provender added to ${data.lords[lord].name}`) + let done = true + for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { + if (is_lord_on_map(x) && is_lord_unfed(x) && can_pillage(get_lord_locale(x))) { + gen_action_locale(get_lord_locale(x)) + done = false + } } - } - end_immediate_event() -} - -// === EVENTS : ROBINS REBELLION === -function goto_lancaster_event_robins_rebellion() { - let can_play = false - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_north(loc) && !has_favourl_marker(loc)) { - can_play = true + if (done) { + view.prompt = `Pillage: Unable to Pillage, you must disband your ${game.what === PROV ? "unfed" : "unpaid"} lords.` + for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { + if (is_lord_on_map(x) && is_lord_unfed(x)) { + gen_action_lord(x) + done = false + } + } } - } - if (can_play) { - game.state = "robins_rebellion" - game.who = NOBODY - game.count = 0 - } else { - logi(`No Effect`) - end_immediate_event() - } -} -states.robins_rebellion = { - inactive: "Robin's Rebellion", - prompt() { - view.prompt = `Select up to ${3-game.count} Locales in North.` - for (let loc = first_locale; loc <= last_locale; loc++) { - if (game.count < 3 && is_north(loc) && !has_favourl_marker(loc)) { - gen_action_locale(loc) - } + if (done) { + view.prompt = `Pillage: Done.` + view.actions.done = 1 } - view.actions.done = 1 }, locale(loc) { - push_undo() - shift_favour_toward(loc) - logi(`Placed/Removed favour at ${data.locales[loc].name}`) - game.count++ + // pillage the Locale + game.where = loc + goto_pillage_locale() + }, + lord(lord) { + disband_influence_penalty(lord) + disband_lord(lord) }, done() { - end_immediate_event() - } + end_pillage() + }, } -// === EVENTS: TUDOR BANNERS === - -function tudor_banner_eligible() { - if (is_lord_on_map(LORD_HENRY_TUDOR) && !is_lord_on_calendar(LORD_HENRY_TUDOR)) { - for (let next of data.locales[get_lord_locale(LORD_HENRY_TUDOR)].adjacent) { - if (can_parley_at(next)) - return true - } - } - else - return false +function goto_pillage_locale() { + push_state("pillage_locale") } -function goto_lancaster_event_tudor_banners() { - let can_play = false - if (tudor_banner_eligible()) { - can_play = true - } - if (can_play) { - game.state = "tudor_banners" - game.who = NOBODY - } else { - logi(`No Effect`) - end_immediate_event() - } +function end_pillage_locale() { + pop_state() + game.where = NOWHERE + end_pillage() } -states.tudor_banners = { - inactive: "Tudor banners", +states.pillage_locale = { + inactive: "Pillage", prompt() { - view.prompt = `Select locales adjacent to Henry to make them Lancastrian` - for (let next of data.locales[get_lord_locale(LORD_HENRY_TUDOR)].adjacent) { - if (!has_enemy_lord(next) && !has_favourl_marker(next)) { - gen_action_locale(next) - } else { - view.actions.done = 1 + view.prompt = `Pillage: Choose Lord to Pillage ${data.locales[game.where].name}.` + + for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { + if (get_lord_locale(x) === game.where && is_lord_unfed(x)) { + gen_action_lord(x) } } }, - locale(loc) { - push_undo() - remove_favoury_marker(loc) - add_favourl_marker(loc) - logi(`Placed Lancastrian favour at ${data.locales[loc].name}`) - }, - done() { - end_immediate_event() - } -} -// === EVENTS: TAX COLLECTORS === + lord(lord) { + // Pillage + // Same values as Taxing. + let num = get_tax_amount(game.where) + add_lord_assets(lord, COIN, num) + add_lord_assets(lord, PROV, num) + reduce_influence(4 * num) -function goto_york_event_tax_collectors() { - game.who = NOBODY - game.count = 0 - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { - set_lord_moved(lord, 1) - } - } - push_state("tax_collectors") -} + add_exhausted_marker(game.where) + set_favour_enemy(game.where) + for (let next of data.locales[game.where].adjacent) + shift_favour_away(next) -function can_action_tax_collectors(lord) { - let here = get_lord_locale(lord) - if (can_tax_collectors_at(here, lord)) - return true - return search_tax_collectors(false, here, lord) + end_pillage_locale() + }, } -function can_tax_collectors_at(here, lord) { - if (is_friendly_locale(here) && !has_exhausted_marker(here)) { - // London, Calais, and Harlech - if (here === LOC_LONDON || here === LOC_CALAIS || here === LOC_HARLECH) - return true +// === 3.2.2 PAY LORDS === - // Own seat - if (here === data.lords[lord].seat) +function has_friendly_lord_who_must_pay_troops() { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (is_lord_unfed(lord)) return true - - // vassal seats - for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) - if (is_vassal_mustered_with(vassal, lord)) - if (here === data.vassals[vassal].seat) - return true - } return false } -function search_tax_collectors(result, start, lord) { - let ships = get_shared_assets(start, SHIP) - - search_seen.fill(0) - search_seen[start] = 1 - - let queue = [ start ] - while (queue.length > 0) { - let here = queue.shift() - - if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { - if (can_tax_collectors_at(here, lord)) { - if (result) - set_add(result, here) - else - return true - } - } +function goto_pay_lords() { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_on_map(lord)) + set_lord_unfed(lord, 1) + } - if (is_friendly_locale(here)) { - for (let next of data.locales[here].adjacent) { - if (!search_seen[next]) { - search_seen[next] = 1 - queue.push(next) - } - } - if (ships > 0 && is_seaport(here)) { - for (let next of find_ports(here)) { - if (!search_seen[next]) { - search_seen[next] = 1 - queue.push(next) - } - } - } - } + if (has_friendly_lord_who_must_pay_troops()) { + game.who = NOBODY + game.state = "pay_lords" + } else { + end_pay_lords() } +} - if (result) - return result +function end_pay_lords() { + set_active_enemy() + + if (game.active === P2) + goto_pay_lords() else - return false + goto_pay_vassals() } -states.tax_collectors = { - inactive: "Tax Collectors", +states.pay_lords = { + inactive: "Pay Lords", prompt() { - view.prompt = "Tax Collectors : You may tax for Double coin with each lord" - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (can_action_tax_collectors(lord) && get_lord_moved(lord)) { - gen_action_lord(lord) + view.prompt = "Pay Lords in Influence or Disband them." + prompt_held_event() + let done = true + game.count = 0 + if (game.who === NOBODY) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_on_map(lord) && is_lord_unfed(lord)) { + gen_action_lord(lord) + done = false + } } - } - view.actions.done = 1 - }, + if (done) { + view.actions.done = 1 + } + if (!done) + view.actions.pay_all = 1 + } else { + view.actions.disband = 1 + view.actions.pay = 1 + } }, lord(lord) { push_undo() - game.where = NOWHERE game.who = lord - push_state("double_tax_collectors") - init_influence_check(lord) }, - done() { - end_tax_collectors() - }, -} - -states.double_tax_collectors = { - inactive: "Tax Collectors", - prompt() { - view.prompt = "Tax: Select the location to tax for double." - if (game.where === NOWHERE) { - for (let loc of search_tax_collectors([], get_lord_locale(game.who), game.who)) - gen_action_locale(loc) - } else { - view.prompt = `Tax: Attempting to tax ${data.locales[game.where].name}. ` - prompt_influence_check() - } - }, - locale(loc) { - game.where = loc - if (loc === data.lords[game.who].seat) { - // Auto succeed without influence check at Lords seat. - deplete_locale(game.where) - - log(`Taxed %${game.where}.`) - add_lord_assets(game.who, COIN, get_tax_amount(game.where)*2) - set_lord_moved(game.who, 0) - end_tax_lord() - } + disband() { + push_undo() + disband_lord(game.who) + game.who = NOBODY }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - logi(`Tax : ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - - if (lord_has_capability(game.who, AOW_YORK_SO_WISE_SO_YOUNG)) { - log(`C${AOW_YORK_SO_WISE_SO_YOUNG}.`) - add_lord_assets(game.who, COIN, 1) - } - - if (results.success) { - deplete_locale(game.where) - log(`Taxed %${game.where}.`) - add_lord_assets(game.who, COIN, get_tax_amount(game.where)*2) - - if ( - game.command === LORD_DEVON && - (game.where === LOC_EXETER || - game.where === LOC_LAUNCESTON || - game.where === LOC_PLYMOUTH || - game.where === LOC_WELLS || - game.where === LOC_DORCHESTER) - ) - add_lord_assets(game.command, COIN, 4) - } else { - log(`Tax of %${game.where} failed.`) - - } + pay() { + push_undo() + reduce_influence(is_exile(get_lord_locale(game.who)) ? 2 : 1) set_lord_moved(game.who, 0) game.who = NOBODY - push_state("tax_collectors") }, -} - -function end_tax_lord() { - game.where = NOWHERE - game.who = NOBODY - pop_state() -} - -function end_tax_collectors() { - game.where = NOWHERE - game.who = NOBODY - game.count = 0 - end_immediate_event() -} - -// === EVENTS: LONDON FOR YORK === - -function goto_york_event_london_for_york() { - let can_play = false - if (has_favoury_marker(LOC_LONDON)) { - can_play = true - } - if (can_play) { + pay_all() { + push_undo() + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_on_map(lord) && is_lord_unfed(lord)) { + ++game.count + set_lord_moved(lord, 0) + if (is_exile(get_lord_locale(lord))) { + ++game.count + } + reduce_influence(game.count) + game.count = 0 + } + } game.who = NOBODY - game.state = "london_for_york" - } else { - logi(`No Effect`) - end_immediate_event() - } -} -states.london_for_york = { - inactive: "London For York", - prompt() { - view.prompt = `Select London to add a second favour marker` - gen_action_locale(LOC_LONDON) }, - locale(loc) { - push_undo() - game.flags.london_for_york = 1 - logi(`Second marker placed at ${data.locales[loc].name}`) - logi(`Immune to Lancastrian parley unless aided by event`) - end_immediate_event() + card: action_held_event, + done() { + end_pay_lords() }, } -function check_london_protected() { -// TODO IF HENRY/MARGARET ARE MUSTERED IT DOES NOT CHANGE FAVOUR -// ONLY L17/L18 and Pillage will cancel that event -//(it is annuled when london go to neutral - if (game.state === "pillage") { - return false - } - if (game.flags.london_for_york === 1 && game.where === LOC_LONDON) { - return true - } - else { - return false - } -} +// === 3.2.3 PAY VASSALS === -// === EVENTS: SHE-WOLF OF FRANCE === +function goto_pay_vassals() { + let vassal_to_pay = false -function goto_york_event_shewolf_of_france() { - let can_play = false for (let v = first_vassal; v <= last_vassal; v++) { - if (is_vassal_mustered_with_friendly_lord(v)) { - set_vassal_moved(v, 1) - can_play = true + if ( + is_vassal_mustered_with_friendly_lord(v) && + get_vassal_service(v) === current_turn() + ) { + vassal_to_pay = true } } - if (can_play) { - game.state = "she_wolf" - game.who = NOBODY + if (vassal_to_pay) { + game.state = "pay_vassals" + game.what = NOBODY } else { - logi(`No Effect`) - end_immediate_event() + end_pay_vassals() } } -// TO TRACK VASSALS DURING EVENTS -function get_vassal_moved(v) { - return map_get(game.pieces.moved, v, 0) -} - -function set_vassal_moved(v, x) { - map_set(game.pieces.moved, v, x) -} +function end_pay_vassals() { + set_active_enemy() -function pay_vassal_shewolf(vassal) { - if (current_turn() < 16) - set_vassal_lord_and_service(vassal, get_vassal_lord(vassal),get_vassal_service(vassal) + 1) + if (game.active === P1) { + goto_muster_exiles() + } else { + goto_pay_vassals() + } } -states.she_wolf = { - inactive: "She-Wolf of France", +states.pay_vassals = { + inactive: "Pay Vassals", prompt() { let done = true - view.prompt = "You may shift your vassals one calendar box." - if (game.who === NOBODY) { + view.prompt = "You may pay or disband vassals in the next calendar box." + if (game.what === NOBODY) { for (let v = first_vassal; v <= last_vassal; v++) { - if (get_vassal_moved(v) && is_vassal_mustered_with_friendly_lord(v)) { + if ( + is_vassal_mustered_with_friendly_lord(v) && + get_vassal_service(v) === current_turn() + ) { gen_action_vassal(v) done = false } } + if (done) { view.actions.done = 1 } + if (!done) + view.actions.pay_all = 1 + } else { + view.actions.pay = 1 + view.actions.disband = 1 } }, vassal(v) { push_undo() - game.who = v - pay_vassal_shewolf(game.who) - set_vassal_moved(v, 0) - logi(`Vassal ${data.vassals[v].name} shifted one calendar box`) - game.who = NOBODY - }, - done() { - end_immediate_event() - }, -} - -// === EVENTS : RICHARD LEIGH === -function goto_york_event_sir_richard_leigh() { - let can_play = false - if (!has_favoury_marker(LOC_LONDON)) { - can_play = true - } - if (can_play) { - game.state = "richard_leigh" - game.who = LOC_LONDON - game.count = 0 - } else { - logi(`No Effect`) - end_immediate_event() - } -} - -states.richard_leigh = { - inactive: "Richard Leigh", - prompt() { - view.prompt = `Select London, shift it once in your favour` - if (game.who === LOC_LONDON && !has_favoury_marker(LOC_LONDON)) { - gen_action_locale(LOC_LONDON) - } else { - view.actions.done = 1 - } + game.what = v }, - locale(loc) { + pay() { push_undo() - shift_favour_toward(loc) - logi(`London shifted once in your favour`) - game.who = NOBODY - }, - done() { - end_immediate_event() - } -} - -// === EVENTS: CHARLES THE BOLD === - -function goto_york_event_charles_the_bold() { - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { - add_lord_assets(lord, PROV, 1) - add_lord_assets(lord, COIN, 1) - logi(`1 Coin and 1 Provender added to ${data.lords[lord].name}`) - } - } - end_immediate_event() -} - -// === EVENTS: DUBIOUS CLARENCE === - -function goto_dubious_clarence() { - let can_play = false - if ((is_lord_on_map(LORD_EDWARD_IV) && !is_lord_on_calendar(LORD_EDWARD_IV)) - && is_lord_on_map(LORD_CLARENCE) && !is_lord_on_calendar(LORD_CLARENCE)) - can_play = true - - if (can_play) { - game.state = "dubious_clarence" - game.who = LORD_EDWARD_IV - init_influence_check(LORD_EDWARD_IV) - } else { - logi(`No Effect`) - end_immediate_event() - } -} - -states.dubious_clarence = { - inactive: "Dubious Clarence", - prompt() { - view.prompt = `You may Influence check with Edward to disband Clarence ` - prompt_influence_check() - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - log(`Attempt to disband Clarence ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - - if (results.success) { - disband_lord(LORD_CLARENCE, false) - end_immediate_event() - } else { - end_immediate_event() - } + pay_vassal(game.what) + reduce_influence(1) + game.what = NOBODY }, -} - -// === EVENTS: YORKIST NORTH === - -function goto_york_event_yorkist_north() { - let influence_gained = 0 - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (is_lord_on_map(lord) && !is_lord_on_calendar(lord) && is_lord_in_north(lord)) - influence_gained++ - } - for (let loc = first_locale; loc <= last_locale; loc++) { - if (loc !== NOWHERE && loc < CALENDAR && has_favoury_marker(loc) && is_north(loc)) { - influence_gained++ - } - } - logi(`Yorkist North : ${influence_gained} Influence for Yorkists`) - increase_york_influence(influence_gained) - end_immediate_event() -} - -// === EARL RIVERS === - -function goto_york_event_earl_rivers() { - game.state = "earl_rivers" - game.count = [] - game.who = NOBODY -} - -function end_york_event_earl_rivers() { - game.count = 0 - game.who = NOBODY - end_immediate_event() -} - -states.earl_rivers = { - inactive: "Earl Rivers", - prompt() { - view.prompt = "Earl Rivers: Add up to 2 Militia to each lord" - view.actions.done = 1 - for (let lord = first_york_lord; lord <= last_york_lord; lord++) { - if (is_lord_on_map(lord) && map_get(game.count, lord, 0) < 3) { - gen_action_lord(lord) + pay_all() { + push_undo() + for (let v = first_vassal; v <= last_vassal; v++) { + if (is_vassal_mustered_with_friendly_lord(v) + && get_vassal_service(v) === current_turn()) { + pay_vassal(v) + reduce_influence(1) + game.what = NOBODY } } - - if (game.who !== NOBODY) { - let troops = map_get(game.count, game.who, 0) - if ((troops & 1) === 0) - view.actions.add_militia = 1 - } - if (game.who !== NOBODY) { - let troops = map_get(game.count, game.who, 0) - if ((troops & 1) === 0) - view.actions.add_militia2 = 1 - } - }, - done() { - end_york_event_earl_rivers() }, - add_militia() { + disband() { push_undo() - add_lord_forces(game.who, MILITIA, 1) - let troops = map_get(game.count, game.who, 0) - map_set(game.count, game.who, troops + 1) - if (troops > 1) - game.who = NOBODY + disband_vassal(game.what) + game.what = NOBODY }, - add_militia2() { - push_undo() - add_lord_forces(game.who, MILITIA, 2) - let troops = map_get(game.count, game.who, 0) - map_set(game.count, game.who, troops + 1) - if (troops > 1) - game.who = NOBODY + done() { + end_pay_vassals() }, - lord(lord) { - push_undo() - game.who = lord - } } -// === EVENTS: SHIFT LORD OR SERVICE (IMMEDIATE) === -/* -function prompt_shift_lord_on_calendar(boxes) { - if (game.who !== NOBODY) { - // Shift in direction beneficial to active player. - if (is_friendly_lord(game.who)) { - if (is_lord_on_calendar(game.who)) - gen_action_calendar(get_lord_calendar(game.who) - boxes) - else - gen_action_calendar(get_lord_calendar(game.who) + boxes) - } else { - if (is_lord_on_calendar(game.who)) - gen_action_calendar(get_lord_calendar(game.who) + boxes) - else - gen_action_calendar(get_lord_calendar(game.who) - boxes) - } - } -} -*/ -// === EVENTS: HOLD === +// === 3.2.4 DISBAND === -function play_held_event(c) { - log(`Played E${c}.`) - if (c >= first_york_card && c <= last_york_card) - set_delete(game.hand_y, c) - else - set_delete(game.hand_l, c) +function disband_lord(lord, permanently = false) { + let turn = current_turn() + let extra = 6 - /* Hold events with This Levy/Campaign */ - if ( - c === EVENT_YORK_YORKIST_PARADE || - c === EVENT_YORK_PARLIAMENTS_TRUCE || - c === EVENT_LANCASTER_PARLIAMENTS_TRUCE - ) { - set_add(game.events, c) + if (permanently) { + log(`Removed L${lord}.`) + set_lord_locale(lord, NOWHERE) + } else if (lord_has_capability(lord, AOW_YORK_ENGLAND_IS_MY_HOME)) { + set_lord_calendar(lord, turn + (extra - data.lords[lord].influence)) + log(`Disbanded L${lord} to turn ${current_turn() + 1}.`) } -} - -// TODO: use or remove this function -function end_held_event() { - pop_state() - game.what = NOTHING -} - -function prompt_held_event() { - for (let c of current_hand()) - if (can_play_held_event(c)) - gen_action_card(c) -} - -function prompt_held_event_intercept() { - for (let c of current_hand()) - if (can_play_held_event_intercept(c)) - gen_action_card(c) -} - -function can_play_held_event(c) { - switch (c) { - case EVENT_LANCASTER_ASPIELLES: - return can_play_l_aspielles() - case EVENT_LANCASTER_REBEL_SUPPLY_DEPOT: - return can_play_rebel_supply_depot() - case EVENT_LANCASTER_SURPRISE_LANDING: - return can_play_surprise_landing() - case EVENT_LANCASTER_PARLIAMENTS_TRUCE: - return can_play_parliaments_truce() - case EVENT_YORK_PARLIAMENTS_TRUCE: - return can_play_parliaments_truce() - case EVENT_YORK_ASPIELLES: - return can_play_y_aspielles() - case EVENT_YORK_YORKIST_PARADE: - return can_play_yorkist_parade() - case EVENT_YORK_SUN_IN_SPLENDOUR: - return can_play_sun_in_splendour() + else { + set_lord_calendar(lord, turn + (extra - data.lords[lord].influence)) + log(`Disbanded L${lord} to turn ${get_lord_calendar(lord)}.`) } - return false -} -function can_play_held_event_intercept(c) { - switch (c) { - case EVENT_LANCASTER_FLANK_ATTACK: - return can_play_flank_attack() - case EVENT_YORK_FLANK_ATTACK: - return can_play_flank_attack() - } - return false -} + discard_lord_capability_n(lord, 0) + discard_lord_capability_n(lord, 1) -function action_held_event(c) { - push_undo() - play_held_event(c) - game.what = c - goto_held_event(c) -} + for (let x = 0; x < ASSET_TYPE_COUNT; ++x) + set_lord_assets(lord, x, 0) -function goto_held_event(c) { - switch (c) { - case EVENT_YORK_ESCAPE_SHIP: - goto_play_escape_ship() - break - case EVENT_YORK_ASPIELLES: - goto_play_aspielles() - break - case EVENT_LANCASTER_ESCAPE_SHIP: - // TODO? - break - case EVENT_LANCASTER_TALBOT_TO_THE_RESCUE: - // TODO? - break - case EVENT_LANCASTER_WARDEN_OF_THE_MARCHES: - goto_play_warden_of_the_marches() - break - case EVENT_LANCASTER_REBEL_SUPPLY_DEPOT: - goto_rebel_supply_depot() - break - case EVENT_LANCASTER_SURPRISE_LANDING: - goto_play_surprise_landing() - break - case EVENT_LANCASTER_ASPIELLES: - goto_play_aspielles() - break - case EVENT_YORK_SUN_IN_SPLENDOUR: - goto_play_sun_in_splendour() - break + for (let x = 0; x < FORCE_TYPE_COUNT; ++x) { + set_lord_forces(lord, x, 0) + if (get_lord_routed_forces(lord, x) > 0) { + set_lord_routed_forces(lord, x, 0) + } } -} - -// === EVENTS: HOLD - UNIQUE === -function can_play_l_aspielles() { - if (game.hand_y.length > 0 || game.hidden) { - return true - } - return false -} + set_lord_moved(lord, 0) -function can_play_y_aspielles() { - if (game.hand_l.length > 0 || game.hidden) { - return true - } - return false + for_each_vassal_with_lord(lord, v => { + disband_vassal(v) + }) } -function can_play_parliaments_truce() { - return game.state === "campaign" -} +// === 3.3.1 MUSTER EXILES === -function can_play_rebel_supply_depot() { - if (game.active === YORK) - return false - if (game.group) { - for (let lord of game.group) { - if (get_lord_moved(lord) && is_seaport(get_lord_locale(game.command))) { - return true - } +function goto_muster_exiles() { + for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { + if ((get_lord_locale(x) === current_turn() + CALENDAR && get_lord_in_exile(x)) + || (is_lancaster_lord(x) && is_lord_on_calendar((get_lord_locale(x)) && get_lord_in_exile(x) && is_event_in_play(EVENT_LANCASTER_BE_SENT_FOR)))) { + game.state = "muster_exiles" + return } } - return false -} - -function can_play_surprise_landing() { - let here = get_lord_locale(game.command) - if (game.flags.surprise_landing === 0 || !is_seaport(here) || here === LOC_CALAIS || here === LOC_PEMBROKE || here === LOC_HARLECH || here === LANCASTER || is_sea(here)) - return false - return true + end_muster_exiles() } -function can_play_yorkist_parade() { - if (game.active === YORK && is_favour_friendly(LOC_LONDON) && (get_lord_locale(LORD_WARWICK_Y) === LOC_LONDON || get_lord_locale(LORD_YORK) === LOC_LONDON)) { - return true - } - return false -} +function end_muster_exiles() { + set_active_enemy() -function can_play_sun_in_splendour() { - if (is_levy_phase() && is_lord_on_calendar(LORD_EDWARD_IV)) { - return true + if (game.active === P1) { + if (!check_disband_victory()) { + goto_ready_vassals() + } + } else { + goto_muster_exiles() } - return false -} - -function can_play_flank_attack() { - return game.state === "intercept" && game.who !== NOBODY && !is_truce_in_effect() -} - -// === EVENTS : HOLD - SUN IN SPLENDOUR - -function goto_play_sun_in_splendour() { - push_state("sun_in_splendour") } -states.sun_in_splendour = { - inactive: "Sun in splendour", +states.muster_exiles = { + inactive: "Muster Exiles", prompt() { + view.prompt = "Muster Exiled Lords." let done = true - view.prompt = "Sun in Splendour: Muster Edward IV in any friendly locale with no enemy lord" - if (is_lord_on_calendar(LORD_EDWARD_IV)) { - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_friendly_locale(loc)) { + + if (game.who === NOBODY) { + for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { + if ((get_lord_locale(x) === current_turn() + CALENDAR && get_lord_in_exile(x)) + || (is_lancaster_lord(x) && is_lord_on_calendar((get_lord_locale(x)) && get_lord_in_exile(x) && is_event_in_play(EVENT_LANCASTER_BE_SENT_FOR)))) { + gen_action_lord(x) done = false - gen_action_locale(loc) } } - } - if (done) { - view.actions.done = 1 - } - }, - locale(loc) { - push_undo() - muster_lord(LORD_EDWARD_IV, loc) - logi(`Mustered Edward IV at ${data.locales[loc].name}`) - }, - done() { - end_sun_in_splendour() - }, -} - -function end_sun_in_splendour() { - game.who = NOBODY - pop_state() -} - -// === EVENTS : HOLD - REBEL SUPPLY DEPOT === - -function goto_rebel_supply_depot() { - game.flags.supply_depot = 1 - add_spoils(PROV, 4) - push_state("rebel_supply_depot") -} - -states.rebel_supply_depot = { - inactive: "Rebel Supply depot", - prompt() { - if (has_any_spoils()) { - view.prompt = "Divide " + list_spoils() + "." - let here = get_lord_locale(game.command) - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (get_lord_locale(lord) === here) - prompt_select_lord(lord) - if (game.who !== NOBODY) - prompt_spoils() - } } else { - view.prompt = "Rebel Supply Depot: All done." - view.actions.end_spoils = 1 + for (let loc of data.exile_boxes) + if (has_favour_in_locale(game.active, loc)) + gen_action_locale(loc) } - }, - lord: action_select_lord, - take_prov() { - push_undo_without_who() - take_spoils(PROV) - }, - end_spoils() { - end_rebel_supply_depot() - }, -} - -function end_rebel_supply_depot() { - game.who = NOBODY - game.spoils = 0 - resume_command() -} -// === EVENTS: HOLD - ASPIELLES === - -function goto_play_aspielles() { - push_state("aspielles") - game.who = NOBODY -} - -states.aspielles = { - inactive: "Aspielles", - prompt() { - view.prompt = "Aspielles: You may see enemy held cards" - if (game.hidden) { - view.prompt += " and an enemy lord to see his mat" - } - prompt_held_event() - if (game.hidden) { - for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) - gen_action_lord(lord) - } - if (game.active === YORK) { - view.hand = game.hand_l - log("Lancaster hand shown to the York player") - } - if (game.active === LANCASTER) { - view.hand = game.hand_y - log("York hand shown to the Lancaster player") + if (done) { + view.actions.done = true } - view.actions.done = 1 - }, lord(lord) { - log(`${lord_name[lord]} Spied`) - view.reveal |= (1 << lord) + game.who = lord + }, + locale(loc) { + muster_lord_in_exile(game.who, loc) + game.who = NOBODY }, - card: action_held_event, done() { - if (is_campaign_phase()) { - resume_command() - } else { - pop_state() - } + end_muster_exiles() }, } -// === EVENTS: HOLD - SURPRISE LANDING === - -function goto_play_surprise_landing() { - push_state("surprise_landing") - game.flags.surprise_landing = 2 - game.who = NOBODY -} - -states.surprise_landing = { - inactive: "Surprise Landing", - prompt() { - view.prompt = "Surprise Landing : You may march once (no path)." - prompt_held_event() - - view.group = game.group - let here = get_lord_locale(game.command) - // 4.3.2 Marshals MAY take other lords - if ( - is_marshal(game.command) || - (lord_has_capability(game.command, AOW_YORK_CAPTAIN) && !other_marshal_or_lieutenant(here)) - ) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (lord !== game.command) - if (get_lord_locale(lord) === here) - gen_action_lord(lord) - } - - // Lieutenant may not take marshall - if (is_lieutenant(game.command)) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (lord !== game.command) - if (get_lord_locale(lord) === here && !is_marshal(lord)) { - gen_action_lord(lord) - } - } - - prompt_march() - }, - lord(lord) { - set_toggle(game.group, lord) - }, - locale: goto_march, - card: action_held_event, +function muster_lord_in_exile(lord, exile_box) { + remove_lord_from_exile(lord) + muster_lord(lord, exile_box) } -// === CAPABILITIES === - -// When a lord levy a capability, its + Lordship -// effects and muster vassal applies instantly -function capability_muster_effects(lord, c) { - if (c === AOW_LANCASTER_MONTAGU) - muster_vassal(VASSAL_MONTAGU, lord) - - if (c === AOW_LANCASTER_MY_FATHERS_BLOOD) - muster_vassal(VASSAL_CLIFFORD, lord) - - if (c === AOW_LANCASTER_ANDREW_TROLLOPE) - muster_vassal(VASSAL_TROLLOPE, lord) +// === 3.3.2 READY VASSALS === - if (c === AOW_LANCASTER_EDWARD) - muster_vassal(VASSAL_EDWARD, lord) - - if (c === AOW_LANCASTER_THOMAS_STANLEY) { - muster_vassal(VASSAL_THOMAS_STANLEY, lord) - game.flags.free_levy = 1 - } - - if (c === AOW_YORK_HASTINGS) { - add_lord_forces(lord, MEN_AT_ARMS, 2) - muster_vassal(VASSAL_HASTINGS, lord) - } - if (c === AOW_YORK_FAIR_ARBITER && is_friendly_locale(get_lord_locale(LORD_SALISBURY))) { - game.count += 1 - } - if (c === AOW_YORK_FALLEN_BROTHER && !is_lord_in_play(LORD_CLARENCE)) { - game.count += 1 - } - - if (c === AOW_YORK_BURGUNDIANS) { - if (is_seaport(get_lord_locale(lord) && !is_exile(get_lord_locale(lord)))) { - add_lord_forces(lord, BURGUNDIANS, 2) - logi(AOW_YORK_BURGUNDIANS) - game.flags.burgundians = 1 - } - else { - game.flags.burgundians = 0 +function goto_ready_vassals() { + for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { + if (get_vassal_service(vassal) === current_turn()) { + set_vassal_lord_and_service(vassal, VASSAL_READY, 0) } } -} - -function lordship_effects(lord) { - if (is_friendly_locale(get_lord_locale(lord)) && lord_has_capability(lord, AOW_YORK_FAIR_ARBITER)) - game.count += 1 - if (lord_has_capability(lord, AOW_YORK_FALLEN_BROTHER) && !is_lord_in_play(LORD_CLARENCE)) - game.count += 1 - if (is_event_in_play(EVENT_YORK_EDWARD_V) && (lord === LORD_GLOUCESTER_1 || lord === LORD_GLOUCESTER_2)) - game.count += 3 - if (is_lancaster_lord(lord) && is_event_in_play(EVENT_LANCASTER_PARLIAMENT_VOTES)) { - game.flags.parliament_votes = 1 - } - if (is_york_lord(lord) && is_jack_cade_eligible(lord)) { - game.flags.jack_cade = 2 - } - if (is_york_lord(lord) && is_event_in_play(EVENT_YORK_SUCCESSION)) { - game.flags.succession = 1 - } -} - -// === LEVY: ARTS OF WAR (FIRST TURN) === - -function draw_two_cards() { - let deck = list_deck() - return [ draw_card(deck), draw_card(deck) ] -} - -function discard_card_capability(c) { - log(`${game.active} discarded C${c}.`) -} - -function discard_card_event(c) { - log(`${game.active} discarded E${c}.`) -} - -function goto_levy_arts_of_war_first() { - if (game.active === YORK) - log_h2("York Arts of War") - else - log_h2("Lancaster Arts of War") - game.state = "levy_arts_of_war_first" - game.what = draw_two_cards() -} - -function resume_levy_arts_of_war_first() { - if (game.what.length === 0) - end_levy_arts_of_war_first() -} - -states.levy_arts_of_war_first = { - inactive: "Arts of War", - prompt() { - let c = game.what[0] - view.arts_of_war = game.what - view.what = c - let discard = true - for (let lord of data.cards[c].lords) { - if (is_lord_on_map(lord) && !lord_already_has_capability(lord, c)) { - gen_action_lord(lord) - discard = false - } - } - if (discard) { - view.prompt = `Arts of War: Discard ${data.cards[c].capability}.` - view.actions.discard = 1 - } else { - view.prompt = `Arts of War: Assign ${data.cards[c].capability} to a Lord.` - } - }, - lord(lord) { - push_undo() - let c = game.what.shift() - log(`${game.active} deployed Capability.`) - add_lord_capability(lord, c) - capability_muster_effects(lord, c) - resume_levy_arts_of_war_first() - }, - discard() { - push_undo() - let c = game.what.shift() - discard_card_capability(c) - resume_levy_arts_of_war_first() - }, -} - -function end_levy_arts_of_war_first() { - game.what = NOTHING - set_active_enemy() - if (game.active === P2) - goto_levy_arts_of_war_first() - else - goto_levy_muster() -} -// === LEVY: ARTS OF WAR === - -function goto_levy_arts_of_war() { - if (game.active === YORK) - log_h2("York Arts of War") - else - log_h2("Lancaster Arts of War") - game.what = draw_two_cards() - resume_levy_arts_of_war() -} - -function resume_levy_arts_of_war() { - game.state = "levy_arts_of_war" - if (game.what.length === 0) - end_levy_arts_of_war() + goto_levy_muster() } -states.levy_arts_of_war = { - inactive: "Arts of War", - prompt() { - let c = game.what[0] - view.arts_of_war = [ c ] - view.what = c - switch (data.cards[c].when) { - case "this_levy": - case "this_campaign": - case "now": - view.prompt = `Arts of War: Play ${data.cards[c].event}.` - view.actions.play = 1 - break - case "hold": - view.prompt = `Arts of War: Hold ${data.cards[c].event}.` - view.actions.hold = 1 - break - case "never": - view.prompt = `Arts of War: Discard ${data.cards[c].event}.` - view.actions.discard = 1 - break - } - }, - play() { - let c = game.what.shift() - log(`${game.active} played E${c}.`) - goto_immediate_event(c) - }, - hold() { - let c = game.what.shift() - log(`${game.active} Held Event.`) - if (game.active === YORK) - set_add(game.hand_y, c) - else - set_add(game.hand_l, c) - resume_levy_arts_of_war() - }, - discard() { - let c = game.what.shift() - discard_card_event(c) - resume_levy_arts_of_war() - }, -} +// === 3.4 MUSTER === -function end_levy_arts_of_war() { - game.what = NOTHING - set_active_enemy() - if (game.active === P2) - goto_levy_arts_of_war() - else - goto_pay() +function reset_flags() { + // to avoid some flags affecting campaign + game.flags.jack_cade = 0 + game.flags.parliament_votes = 0 } -// === LEVY: MUSTER === - function goto_levy_muster() { for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { clear_lords_moved() @@ -4560,12 +2368,6 @@ function end_levy_muster() { goto_levy_discard_events() } -function reset_flags() { - // to avoid some flags affecting campaign - game.flags.jack_cade = 0 - game.flags.parliament_votes = 0 -} - function can_lord_muster(lord) { // already mustered (except free levy)! TODO : re-check parley henry if ships are levied and at exile if (get_lord_moved(lord) @@ -4586,6 +2388,20 @@ function can_lord_muster(lord) { return false } +function has_locale_to_muster(lord) { + // Can muster at own seat without enemy lord. + if (!has_enemy_lord(data.lords[lord].seat)) + return true + + // Else, can muster at any friendly seat (of a friendly lord who is also in play) + for (let other = first_friendly_lord; other <= last_friendly_lord; other++) + if (is_lord_in_play(other) && is_friendly_locale(data.lords[other].seat)) + return true + + // Tough luck! + return false +} + states.levy_muster = { inactive: "Muster", prompt() { @@ -4843,357 +2659,7 @@ states.levy_muster_lord = { }, } -// === EVENT : THE KINGS NAME === - -function eligible_kings_name() { - if ( - (!is_lord_on_calendar(LORD_GLOUCESTER_1) && is_lord_on_map(LORD_GLOUCESTER_1)) || - (!is_lord_on_calendar(LORD_GLOUCESTER_2) && is_lord_on_map(LORD_GLOUCESTER_2)) - ) { - if (is_event_in_play(EVENT_YORK_THE_KINGS_NAME) && game.active === LANCASTER) - return true - } - return false -} - -function goto_kings_name(action) { - game.what = action - set_active_enemy() - push_state("kings_name") -} - -states.kings_name = { - inactive: `King's name`, - prompt() { - view.prompt = `King's Name: You pay may 1 Influence to cancel last ${game.what} action` - view.actions.pass = 1 - view.actions.pay = 1 - }, - pay() { - push_undo() - reduce_influence(1) - goto_kings_name_cancel() - }, - pass() { - set_active_enemy() - pop_state() - if (game.state === "levy_muster_lord_attempt") { - push_state("muster_lord_at_seat") - } - if (game.state === "levy_muster_vassal") { - muster_vassal(game.which, game.who) - pop_state() - } - if (game.state === "muster_capability") { - add_lord_capability(game.who, game.which) - capability_muster_effects(game.who, game.which) - pop_state() - } - if (game.state === "parley") { - game.what = NOTHING - game.where = NOWHERE - pop_state() - } - resume_levy_muster_lord() - } -} - -function goto_kings_name_cancel() { - switch(game.what) { - case "Levy Lord": - pop_state() - end_levy_muster_lord_attempt() - break - case "Levy Cart": - add_lord_assets(game.who, CART, -2) - pop_state() - resume_levy_muster_lord() - break - case "Levy Ship": - add_lord_assets(game.who, SHIP, -1) - pop_state() - resume_levy_muster_lord() - break - case "Levy Vassal": - game.which = NOTHING - end_levy_muster_vassal() - break - case "Levy Troops": - kings_name_reset_troops() - pop_state() - resume_levy_muster_lord() - break - case "Parley": - shift_favour_toward_york(game.where) - pop_state() - game.where = NOWHERE - end_parley() - break - case "Capability": - game.which = NOTHING - pop_state() - pop_state() - resume_levy_muster_lord() - break - case "Levy Beloved Warwick": - add_lord_forces(game.who, MILITIA, -5) - pop_state() - resume_levy_muster_lord() - break - default: - throw Error("No King's name cancel state found") - } - log(`${game.what} action cancelled`) - logevent(`${EVENT_YORK_THE_KINGS_NAME}`) - set_active_enemy() -} - -function kings_name_reset_troops() { - if (!lord_has_capability(game.who, AOW_LANCASTER_QUARTERMASTERS)) { - remove_depleted_marker(get_lord_locale(game.who)) - remove_exhausted_marker(get_lord_locale(game.who)) - } - - let here = get_lord_locale(game.who) - let here_type = data.locales[here].type - switch (here_type) { - case "calais": - add_lord_forces(game.who, MEN_AT_ARMS, -2) - add_lord_forces(game.who, LONGBOWMEN, -1) - break - case "london": - add_lord_forces(game.who, MEN_AT_ARMS, -1) - add_lord_forces(game.who, LONGBOWMEN, -1) - add_lord_forces(game.who, MILITIA, -1) - break - case "harlech": - add_lord_forces(game.who, MEN_AT_ARMS, -1) - add_lord_forces(game.who, LONGBOWMEN, -2) - break - case "city": - add_lord_forces(game.who, LONGBOWMEN, -1) - add_lord_forces(game.who, MILITIA, -1) - break - case "town": - add_lord_forces(game.who, MILITIA, -2) - break - case "fortress": - add_lord_forces(game.who, MEN_AT_ARMS, -1) - add_lord_forces(game.who, MILITIA, -1) - break - } -} - -// === EVENT : RISING WAGES === - -states.rising_wages = { - inactive: "Rising Wages", - prompt() { - let here = get_lord_locale(game.who) - view.prompt = "Rising Wages: Pay 1 extra coin to levy troops" - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - let loc = get_lord_locale(lord) - if (here === loc && (get_lord_assets(lord, COIN) > 0)) { - gen_action_coin(lord) - } - } - }, - coin(lord) { - push_undo() - add_lord_assets(lord, COIN, -1) - logi(`${EVENT_LANCASTER_RISING_WAGES}`) - log("York paid 1 Coin to Levy troops") - pop_state() - }, -} - -// Check if the levy troops is at vassal seat - -function chamberlains_eligible_levy(locale) { - for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) - if (is_vassal_mustered_with(vassal, game.who) && lord_has_capability(game.who, AOW_LANCASTER_CHAMBERLAINS)) { - if (locale === data.vassals[vassal].seat) - return true - } -} - -// === EVENT: THE COMMONS === - -states.the_commons = { - inactive: "The Commons", - prompt() { - view.prompt = `Add up to ${game.flags.commons_militia} Militias.` - if (game.flags.commons_militia > 0) { - view.actions.add_militia = 1 - } - view.actions.done = 1 - }, - add_militia() { - push_undo() - add_lord_forces(game.who, MILITIA, 1) - --game.flags.commons_militia - }, - done() { - push_undo() - end_the_commons() - - } -} - -function end_the_commons() { - game.flags.commons_militia = 0 - pop_state() - resume_levy_muster_lord() -} - -// === CAPABILITY: SOLDIERS OF FORTUNE - -states.soldier_of_fortune = { - inactive: "Levy Troops", - prompt() { - view.prompt = `Pay 1 Coin for Mercenaries ${lord_name[game.who]}.` - let done = true - if (done) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_unfed(lord) && can_pay_from_shared(lord)) { - if (get_lord_assets(lord, COIN) > 0) { - gen_action_coin(lord) - done = false - } - } - } - } - // Done - if (done) { - view.prompt = "Soldiers of fortune: Done." - view.actions.end_sof = 1 - } - }, - coin(lord) { - push_undo() - let here = get_lord_locale(game.who) - let here_type = data.locales[here].type - let number = get_lord_forces(game.who, MERCENARIES) - let merc = 0 - if (!lord_has_capability(game.who, AOW_YORK_WOODWILLES)) - deplete_locale(here) - - switch (here_type) { - case "calais": - add_lord_forces(game.who, MEN_AT_ARMS, 2) - add_lord_forces(game.who, LONGBOWMEN, 1) - break - case "london": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, LONGBOWMEN, 1) - add_lord_forces(game.who, MILITIA, 1) - break - case "harlech": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, LONGBOWMEN, 2) - break - case "city": - add_lord_forces(game.who, LONGBOWMEN, 1) - add_lord_forces(game.who, MILITIA, 1) - break - case "town": - add_lord_forces(game.who, MILITIA, 2) - break - case "fortress": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, MILITIA, 1) - break - } - if (game.flags.free_levy === 1) { - ++game.count - game.flags.free_levy = 0 - } - if (number === 5) - merc = 1 - else - merc = 2 - add_lord_assets(lord, COIN, -1) - add_lord_forces(game.who, MERCENARIES, merc) - set_lord_unfed(game.who, 0) - }, - end_sof() { - end_soldiers_of_fortune() - }, - card: action_held_event, -} - -function end_soldiers_of_fortune() { - pop_state() - resume_levy_muster_lord() -} - - -// === CAPABILITY: COMMISSION OF ARRAY - -states.commission_of_array = { - inactive: "Levy Troops", - prompt() { - view.prompt = `Lord troops adjacent to ${lord_name[game.who]}.` - let done = true - let here = get_lord_locale(game.who) - if (done) { - for (let next of data.locales[here].adjacent) { - if (is_friendly_locale(next) && lord_has_capability(game.who, AOW_LANCASTER_COMMISION_OF_ARRAY) && (!has_exhausted_marker(next) && !is_exile(next))) { - done = false - gen_action_locale(next) - } - } - } - // Done - if (done) { - view.prompt = "Commission of Array: Done." - view.actions.end_coa = 1 - } - }, - locale(loc) { - push_undo() - let loc_type = data.locales[loc].type - deplete_locale(loc) - - switch (loc_type) { - case "calais": - add_lord_forces(game.who, MEN_AT_ARMS, 2) - add_lord_forces(game.who, LONGBOWMEN, 1) - break - case "london": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, LONGBOWMEN, 1) - add_lord_forces(game.who, MILITIA, 1) - break - case "harlech": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, LONGBOWMEN, 2) - break - case "city": - add_lord_forces(game.who, LONGBOWMEN, 1) - add_lord_forces(game.who, MILITIA, 1) - break - case "town": - add_lord_forces(game.who, MILITIA, 2) - break - case "fortress": - add_lord_forces(game.who, MEN_AT_ARMS, 1) - add_lord_forces(game.who, MILITIA, 1) - break - } - if (game.flags.free_levy === 1) { - ++game.count - game.flags.free_levy = 0 - } - end_commission_of_array() - }, - card: action_held_event, -} - -function end_commission_of_array() { - pop_state() - resume_levy_muster_lord() -} +// === 3.4.2 LEVY LORD === states.muster_lord_at_seat = { inactive: "Muster", @@ -5274,6 +2740,143 @@ function end_muster_lord_at_seat() { end_levy_muster_lord_attempt() } +// === 3.4.3 LEVY VASSAL === + +function eligible_vassal(vassal) { + if (!is_vassal_ready(vassal)) { + return false + } + if ( + !is_favour_friendly(data.vassals[vassal].seat) && + (game.who !== LORD_HENRY_TUDOR || !is_event_in_play(EVENT_LANCASTER_MARGARET_BEAUFORT)) + ) { + return false + } + if (!is_favour_friendly(data.vassals[vassal].seat)) + return false + if ( + game.active === LANCASTER && + is_event_in_play(EVENT_YORK_YORKISTS_BLOCK_PARLIAMENT) && + !(is_event_in_play(EVENT_LANCASTER_MARGARET_BEAUFORT) && !is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND)) + ) { + return false + } + return true +} + +function goto_levy_muster_vassal(vassal) { + game.where = NOWHERE + let influence_cost = 0 + if (game.active === YORK && is_event_in_play(EVENT_LANCASTER_BUCKINGHAMS_PLOT)) + influence_cost += 2 + + push_state("levy_muster_vassal") + init_influence_check(game.who) + game.check.push({ + cost: influence_cost, + modifier: data.vassals[vassal].influence * (game.active === LANCASTER ? -1 : 1), + source: "vassal", + }) +} + +function end_levy_muster_vassal() { + pop_state() + end_influence_check() + resume_levy_muster_lord() +} + +states.levy_muster_vassal = { + inactive: "Levy Vassal", + prompt() { + view.prompt = `Levy Vassal ${data.vassals[game.which].name}. ` + prompt_influence_check() + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + + if (lord_has_capability(game.who, AOW_LANCASTER_TWO_ROSES)) { + log(`Automatic Success. C${AOW_LANCASTER_TWO_ROSES}.`) + } + else if (game.active === LANCASTER && is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND) && game.state === "levy_muster_vassal") { + log(`Automatic Success. C${EVENT_LANCASTER_THE_EARL_OF_RICHMOND}.`) + } + else { + log(`Attempt to levy V${game.which} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + } + + if (results.success) { + if (eligible_kings_name()) { + goto_kings_name("Levy Vassal") + } + else { + muster_vassal(game.which, game.who) + end_levy_muster_vassal() + } + } + else { + end_levy_muster_vassal() + } + }, +} + +// === 3.4.4 LEVY TROOPS === + +function can_add_troops(_lordwho, locale) { + if (!has_exhausted_marker(locale) && !is_exile(locale)) + return true + return false +} + +function can_add_troops_coa(lordwho, locale) { + for (let next of data.locales[locale].adjacent) { + if (is_friendly_locale(next) && lord_has_capability(lordwho, AOW_LANCASTER_COMMISION_OF_ARRAY) && (!has_exhausted_marker(locale) && !is_exile(locale))) + return true + } + return false +} + +function can_add_troops_beloved_warwick(lordwho, locale) { + return ( + lord_has_capability(lordwho, AOW_YORK_BELOVED_WARWICK) && + !has_exhausted_marker(locale) && + !is_exile(locale) + ) +} + +function can_add_troops_irishmen(lordwho, locale) { + return ( + lord_has_capability(lordwho, AOW_YORK_IRISHMEN) && + !has_exhausted_marker(locale) && + (locale === LOC_IRELAND || data.port_3.includes(locale)) + ) +} + +function can_add_troops_sof(lordwho, locale) { + if ( + lord_has_capability(lordwho, AOW_YORK_SOLDIERS_OF_FORTUNE) && + !has_exhausted_marker(locale) && + !is_exile(locale) && + get_shared_assets(locale, COIN) > 0 + ) { + let number = 6 + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + number -= get_lord_forces(lord, MERCENARIES) + if (number >= 1) + return true + } + return false +} + +// === 3.4.5 LEVY TRANSPORT + +function can_add_transport(who, what) { + return get_lord_assets(who, what) < 100 +} + +// === 3.4.6 LEVY CAPABILITY === + function lord_has_capability_card(lord, c) { if (get_lord_capability(lord, 0) === c) return true @@ -5379,7 +2982,7 @@ states.muster_capability = { }, } -// === LEVY: DISCARD EVENTS === +// === 3.4 MUSTER - DISCARD EVENTS === function goto_levy_discard_events() { // Discard "This Levy" events from play. @@ -5388,7 +2991,7 @@ function goto_levy_discard_events() { goto_campaign_plan() } -// === CAMPAIGN: PLAN === +// === 4.1 CAMPAIGN: PLAN === function goto_campaign_plan() { game.turn++ @@ -5464,7 +3067,21 @@ function end_campaign_plan() { goto_command_activation() } -// === CAMPAIGN: COMMAND ACTIVATION === +// === 4.2 CAMPAIGN: COMMAND === + +// First action vs actions that take full command card +function is_first_action() { + return game.flags.first_action +} + +// If march on a highway, set the flag so the lord can go through +// a second highway at no cost +function is_first_march_highway() { + if (game.flags.first_march_highway === 1) + return true + else + return false +} function goto_command_activation() { if (game.plan_y.length === 0 && game.plan_l.length === 0) { @@ -5502,29 +3119,6 @@ function goto_command_activation() { } } -// === CAMPAIGN: ACTIONS === - -function set_active_command() { - if (game.command >= first_york_lord && game.command <= last_york_lord) - set_active(YORK) - else - set_active(LANCASTER) -} - -// First action vs actions that take full command card -function is_first_action() { - return game.flags.first_action -} - -// If march on a highway, set the flag so the lord can go through -// a second highway at no cost -function is_first_march_highway() { - if (game.flags.first_march_highway === 1) - return true - else - return false -} - function goto_command() { game.actions = data.lords[game.command].command if (lord_has_capability(game.command, AOW_YORK_THOMAS_BOURCHIER) && is_city(get_lord_locale(game.command))) @@ -5582,21 +3176,6 @@ function end_command() { goto_feed() } -// Captain capability (lieutenant/marshall only if no other) -function other_marshal_or_lieutenant(loc) { - let here = loc - let n = 0 - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (lord !== game.command) { - if (get_lord_locale(lord) === here && (is_marshal(lord) || is_lieutenant(lord))) - n += 1 - } - if (n === 0) - return false - else - return true -} - states.command = { inactive: "Command", prompt() { @@ -5694,177 +3273,637 @@ states.command = { }, } -// === INFLUENCE CHECKS === +// === 4.5 ACTION: SUPPLY (SEARCH) === -// Bonus score but still maxed at 5 -function influence_capabilities(lord, score) { - let here = get_lord_locale(game.group) - if (game.active === YORK && is_event_in_play(EVENT_YORK_YORKIST_PARADE)) - score += 2 - if (game.active === YORK && is_event_in_play(EVENT_YORK_PRIVY_COUNCIL)) - score += 1 - if (game.state === "parley" && ((is_event_in_play(EVENT_YORK_RICHARD_OF_YORK) && game.active === YORK) || lord_has_capability(game.group, AOW_LANCASTER_IN_THE_NAME_OF_THE_KING))) - score += 1 - if (get_lord_locale(LORD_MARGARET) === here && lord_has_capability(game.group, AOW_LANCASTER_LOYAL_SOMERSET)) - score += 1 - if (lord_has_capability(lord, AOW_YORK_YORKS_FAVOURED_SON)) - score += 1 - if ( - get_lord_locale(LORD_WARWICK_L) === here && - lord_has_capability(game.group, AOW_LANCASTER_MARRIED_TO_A_NEVILLE) && - is_friendly_locale(here) +function can_supply_at(loc, ships) { + // if theoretically possible to supply (does not check carts or ships) + if (is_stronghold(loc) && is_friendly_locale(loc)) { + if (ships > 0 && is_seaport(loc)) + return true + if (!has_exhausted_marker(loc)) + return true + } + return false +} + +function search_supply_by_way(result, start, carts, ships) { + search_dist.fill(0) + search_seen.fill(0) + search_seen[start] = 1 + + let queue = [ start ] + while (queue.length > 0) { + let here = queue.shift() + let dist = search_dist[here] + + if (can_supply_at(here, ships)) { + if (result) + map_set(result, here, dist) + else + return true + } + + if (is_friendly_locale(here)) { + let next_dist = dist + 1 + if (next_dist <= carts) { + for (let next of data.locales[here].adjacent) { + if (!search_seen[next]) { + search_seen[next] = 1 + search_dist[next] = next_dist + queue.push(next) + } + } + } + } + } + + if (result) + return result + return false +} + +function search_supply_by_sea(result, here) { + // Search via sea from Exile box. + if (is_friendly_locale(here)) { + for (let next of find_ports(here)) { + if (can_supply_at(next, 1)) { + if (result) + map_set(result, next, 0) + else + return true + } + } + } + if (result) + return result + return false +} + +function search_supply(result) { + let here = get_lord_locale(game.command) + let carts = get_shared_assets(here, CART) + let ships = get_shared_assets(here, SHIP) + if (ships > 0 && is_exile(here)) + result = search_supply_by_sea(result, here) + result = search_supply_by_way(result, here, carts, ships) + return result +} + +// === 4.5 ACTION: SUPPLY === + +function command_has_harbingers() { + return ( + lord_has_capability(game.command, AOW_LANCASTER_HARBINGERS) || + lord_has_capability(game.command, AOW_YORK_HARBINGERS) ) - score += 2 - if (has_favoury_marker(here) && lord_has_capability(lord, AOW_YORK_FAIR_ARBITER)) - score += 1 - if (lord_has_capability(lord, AOW_YORK_FALLEN_BROTHER) && !is_lord_in_play(LORD_CLARENCE)) - score += 2 +} - return score +function chamberlains_eligible_supply(source) { + for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) + if ( + is_vassal_mustered_with(vassal, game.command) && + lord_has_capability(game.command, AOW_LANCASTER_CHAMBERLAINS) + ) { + if (source === data.vassals[vassal].seat) + return true + } } -// Cards that allows automatic success -function automatic_success(lord, score) { +function command_has_stafford_branch(loc) { + if (lord_has_capability(game.command, AOW_YORK_STAFFORD_BRANCH)) { + return ( + loc === LOC_EXETER || + loc === LOC_LAUNCESTON || + loc === LOC_PLYMOUTH || + loc === LOC_WELLS || + loc === LOC_DORCHESTER + ) + } + return false +} - if (lord_has_capability(lord, AOW_LANCASTER_TWO_ROSES)) - score = 6 - if (game.active === LANCASTER - && is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND) - && game.state === "levy_muster_vassal") - score = 6 - if (game.active === LANCASTER - && game.flags.parliament_votes === 1 - && game.state === "parley") - score = 6 - if (game.active === YORK - && game.flags.jack_cade > 0 - && game.state === "parley") - score = 6 - if (game.active === YORK - && game.flags.succession === 1 - && game.state === "parley") - score = 6 - if (is_campaign_phase() - && game.command === LORD_DEVON - && get_lord_locale(LORD_DEVON) === LOC_EXETER - && is_event_in_play(EVENT_YORK_DORSET) - && game.state === "parley") - score = 6 +function init_supply() { + game.supply = search_supply([]) +} - return score +function can_action_supply() { + if (game.actions < 1) + return false + return search_supply(false) } -// Initiate influence check with cards influencing the cost overriding all others -// (even automatic at no cost) -function init_influence_check(lord) { - game.check = [] - game.check.push({ cost: 1, modifier: 0, source: "base" }) - game.check.push({ cost: 0, modifier: data.lords[lord].influence, source: "lord" }) - if (game.active === LANCASTER - && is_event_in_play(EVENT_YORK_AN_HONEST_TALE_SPEEDS_BEST) - && game.state === "parley"){ - game.check.push({ cost: 1, modifier: 0, source:"An Honest tale speeds best"}) +function goto_supply() { + push_undo() + log(`Supplied`) + game.state = "supply_source" + init_supply() +} + +function modify_supply(loc, supply) { + let here = get_lord_locale(game.command) + let carts = get_shared_assets(here, CART) + + // Must carry supply over land with one cart per provender per way + let distance = map_get(game.supply, loc, 0) + if (distance > 0) + supply = Math.min(supply, Math.floor(carts / distance)) + + // Harbingers event doubles supply received + if (command_has_harbingers()) + supply = supply * 2 + + return supply +} + +function get_port_supply_amount(loc) { + if (is_seaport(loc)) { + let here = get_lord_locale(game.command) + let ships = get_shared_assets(here, SHIP) + return modify_supply(loc, ships) } - if (game.active === LANCASTER - && is_event_in_play(EVENT_LANCASTER_PARLIAMENT_VOTES) - && game.flags.parliament_votes === 1 - && game.state === "parley") { - game.check.push({ cost: -1, modifier: 0, source:"Parliament Votes"}) + return 0 +} + +function get_stronghold_supply_amount(loc) { + if (!has_exhausted_marker(loc)) { + let supply + + if (loc === LOC_LONDON || loc === LOC_CALAIS) + supply = 3 + else if (is_city(loc)) + supply = 2 + else + supply = 1 + + if (command_has_stafford_branch(loc)) + supply += 1 + + return modify_supply(loc, supply) } - if (game.active === YORK - && is_event_in_play(EVENT_YORK_SUCCESSION) - && game.flags.succession === 1 - && game.state === "parley") { - game.check.push({ cost: -1, modifier: 0, source:"Succession"}) + return 0 +} + +states.supply_source = { + inactive: "Supply", + prompt() { + view.prompt = "Supply: Select Supply Source." + + let here = get_lord_locale(game.command) + let carts = get_shared_assets(here, CART) + let ships = get_shared_assets(here, SHIP) + + if (carts > 0) + view.prompt += ` ${carts} Cart.` + if (ships > 0) + view.prompt += ` ${ships} Ship.` + + for (let i = 0; i < game.supply.length; i += 2) + gen_action_locale(game.supply[i]) + }, + locale(loc) { + push_undo() + + let port_supply = get_port_supply_amount(loc) + let stronghold_supply = get_stronghold_supply_amount(loc) + + if (stronghold_supply > 0 && port_supply === 0) { + use_stronghold_supply(loc, stronghold_supply) + return + } + + if (port_supply > 0 && stronghold_supply === 0) { + use_port_supply(loc, port_supply) + return + } + + game.where = loc + game.state = "select_supply_type" + }, +} + +function quartermasters_eligible_supply(source) { + for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) + if ( + is_vassal_mustered_with(vassal, game.command) && + lord_has_capability(game.command, AOW_LANCASTER_CHAMBERLAINS) + ) { + if (source === data.vassals[vassal].seat) + return true + } +} + +function use_stronghold_supply(source, amount) { + logi(`${amount} from Stronghold at %${source}`) + add_lord_assets(game.command, PROV, amount) + if (chamberlains_eligible_supply(source)) { + end_supply() + } + else { + deplete_locale(source) + end_supply() } } -function end_influence_check() { - game.check = 0 +function use_port_supply(source, amount) { + logi(`${amount} from Port at %${source}`) + add_lord_assets(game.command, PROV, amount) + end_supply() } -function count_influence_score() { - let score = game.check.reduce((p, c) => p + c.modifier, 0) - let lord = 0 - if (is_levy_phase()) - lord = game.who - if (is_campaign_phase()) - lord = game.command +function end_supply() { + spend_action(1) + resume_command() + game.supply = 0 + game.where = NOWHERE +} - // Space for whose lord has been selected for SUSPICION EVENT +states.select_supply_type = { + inactive: "Supply", + prompt() { + let port = get_port_supply_amount(game.where) + let stronghold = get_stronghold_supply_amount(game.where) - score = (lord, score) + view.prompt = `Supply: ${stronghold} from Stronghold or ${port} from Port?` + view.actions.stronghold = 1 + view.actions.port = 1 + }, + stronghold() { + use_stronghold_supply(game.where, get_stronghold_supply_amount(game.where)) + }, + port() { + if (check_naval_blockade("supply", game.where)) { + roll_naval_blockade() + } + else { + use_port_supply(game.where, get_port_supply_amount(game.where)) + } + }, +} - if (score > 5) - score = 5 - if (score < 1) - score = 1 +// === 4.6.1 ACTION: SAIL === - score = automatic_success(lord, score) - return score +function has_enough_available_ships_for_army() { + let ships = count_group_ships() + let army = count_lord_all_forces(game.group) + let needed_ships = army / 6 + return needed_ships <= ships } -function count_influence_cost() { - if (game.state === "parley") { - if (is_campaign_phase()) { - if (game.command === LORD_DEVON && get_lord_locale(LORD_DEVON) === LOC_EXETER && is_event_in_play(EVENT_YORK_DORSET)) - return 0 +function is_seamanship_in_play() { + if (game.active === LANCASTER && is_event_in_play(EVENT_LANCASTER_SEAMANSHIP)) + return true + if (game.active === YORK && is_event_in_play(EVENT_YORK_SEAMANSHIP)) + return true + return false +} + +function can_sail_to(to) { + if (is_wales_forbidden(to)) + return false + if (has_enemy_lord(to)) { + if (is_truce_in_effect()) + return false + if (!lord_has_capability(game.command, AOW_LANCASTER_HIGH_ADMIRAL)) + return false + } + return true +} + +function can_action_sail() { + // Must use whole action except if seamanship in play + + if (is_lancaster_lord(game.command)) { + if (!is_first_action() && !is_event_in_play(EVENT_LANCASTER_SEAMANSHIP)) + return false + } + + if (is_york_lord(game.command)) { + if ((is_event_in_play(EVENT_LANCASTER_FRENCH_FLEET) || !is_first_action() && !is_event_in_play(EVENT_YORK_SEAMANSHIP))) + return false + } + + if (game.actions === 0) + return false + + // at a seaport + let here = get_lord_locale(game.command) + if (!is_seaport(here)) + return false + + // with enough ships to carry all the army + if (!has_enough_available_ships_for_army()) + return false + + // and a valid destination + for (let to of find_sail_locales(here)) { + if (to === here) + continue + if (can_sail_to(to)) + return true + } + + return false +} + +function goto_sail() { + push_undo() + game.state = "sail" +} + +states.sail = { + inactive: "Sail", + prompt() { + view.group = game.group + + let here = get_lord_locale(game.command) + let ships = count_group_ships() + let prov = count_group_assets(PROV) + let cart = count_group_assets(CART) + + let overflow_prov = (prov / 2 - ships) * 2 + let overflow_cart = (cart / 2 - ships) * 2 + + if (overflow_prov <= 0 && overflow_cart <= 0) { + view.prompt = `Sail: Select a destination Port.` + for (let to of find_sail_locales(here)) { + if (to === here) + continue + if (can_sail_to(to)) + gen_action_locale(to) + } + } else if (overflow_cart > 0) { + view.prompt = `Sailing with ${ships} Ships. Please discard ${overflow_cart} Cart` + if (cart > 0) { + for (let lord of game.group) { + if (get_lord_assets(lord, CART) > 0) + gen_action_cart(lord) + } + } + } else if (overflow_prov > 0) { + view.prompt = `Sailing with ${ships} Ships. Please discard ${overflow_prov} Provender` + if (prov > 0) { + for (let lord of game.group) { + if (get_lord_assets(lord, PROV) > 0) + gen_action_prov(lord) + } + } + } else { + view.prompt = "ERROR" } - if (is_levy_phase()) { - if (game.flags.jack_cade > 0) - return 0 + }, + prov: drop_prov, + cart: drop_cart, + locale(to) { + if (check_naval_blockade("sail", get_lord_locale(game.command)) || check_naval_blockade("sail", to)) { + roll_naval_blockade() + game.where = to + } + else { + log(`Sailed to %${to}${format_group_move()}.`) + + for (let lord of game.group) { + set_lord_locale(lord, to) + set_lord_moved(lord, 1) + levy_burgundians(lord) + } + + if (is_seamanship_in_play()) + spend_action(1) + else + spend_all_actions() + + // you can go to unbesieged enemy lord with norfolk capability + if (has_unbesieged_enemy_lord(to)) + goto_confirm_approach_sail() + else { + game.flags.surprise_landing = 1 + resume_command() + } } + }, +} + +function goto_confirm_approach_sail() { + game.state = "confirm_approach_sail" +} + +states.confirm_approach_sail = { + inactive: "Sail", + prompt() { + view.prompt = `Sail: Confirm Approach to enemy Lord.` + view.group = game.group + view.actions.approach = 1 + }, + approach() { + push_undo() + goto_battle() + }, +} + +// === 4.6.2 ACTION: FORAGE === + +function can_action_forage() { + if (game.actions < 1) + return false + let here = get_lord_locale(game.command) + if (has_exhausted_marker(here) || is_sea(here)) + return false + return true +} + +function goto_forage() { + push_undo() + let here = get_lord_locale(game.command) + if (!has_adjacent_enemy(here) && is_neutral_locale(here)) { + let die = roll_die() + if (die <= 4) { + add_lord_assets(game.command, PROV, 1) + log(`${HIT[die]}, Foraged at %${here}`) + deplete_locale(here) + } else { + log(`${MISS[die]}, Forage Failure`) + } + } else if (has_adjacent_enemy(here) || is_favour_enemy(here, game.active)) { + let die = roll_die() + if (die <= 3) { + add_lord_assets(game.command, PROV, 1) + log(`${HIT[die]}, Foraged at %${here}`) + deplete_locale(here) + } else { + log(`${MISS[die]}, Forage Failure`) + } + } else { + add_lord_assets(game.command, PROV, 1) + log(`Foraged at %${here}`) + deplete_locale(here) } - return game.check.reduce((p, c) => p + c.cost, 0) + if (lord_has_capability(game.command, AOW_YORK_SCOURERS)) { + add_lord_assets(game.command, PROV, 1) + log(`1 Extra Provender (Scourers)`) + } + + spend_action(1) + resume_command() } -function do_influence_check() { - reduce_influence(count_influence_cost()) - let rating = count_influence_score() - let roll = roll_die() - let success +// === 4.6.3 ACTION: TAX === - if (roll === 1 || rating === 6) - success = true - else if (roll === 6) - success = false +function can_tax_at(here) { + if (is_friendly_locale(here) && !has_exhausted_marker(here)) { + // London, Calais, and Harlech + if (here === LOC_LONDON || here === LOC_CALAIS || here === LOC_HARLECH) + return true + + // Own seat + if (here === data.lords[game.command].seat) + return true + + // vassal seats + for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) + if (is_vassal_mustered_with(vassal, game.command)) + if (here === data.vassals[vassal].seat) + return true + } + return false +} + +// adjacent friendly locales to an eligible stronghold (can_tax_at) +function search_tax(result, start) { + let ships = get_shared_assets(start, SHIP) + + search_seen.fill(0) + search_seen[start] = 1 + + let queue = [ start ] + while (queue.length > 0) { + let here = queue.shift() + + if (can_tax_at(here)) { + if (result) + set_add(result, here) + else + return true + } + + if (is_friendly_locale(here)) { + for (let next of data.locales[here].adjacent) { + if (!search_seen[next]) { + search_seen[next] = 1 + queue.push(next) + } + } + if (ships > 0 && is_seaport(here)) { + for (let next of find_ports(here)) { + if (!search_seen[next]) { + search_seen[next] = 1 + queue.push(next) + } + } + } + } + } + if (result) + return result else - success = roll <= rating + return false +} - // TODO: print log message here instead of returning object - return { success: success, rating: rating, roll: roll } +function can_action_tax() { + if (game.actions < 1) + return false + let here = get_lord_locale(game.command) + if (can_tax_at(here)) + return true + return search_tax(false, here) } -function add_influence_check_modifier_1() { - game.check.push({ cost: 1, modifier: 1, source: "add" }) +function goto_tax() { + push_undo() + push_state("tax") + init_influence_check(game.command) + game.where = NOWHERE } -function add_influence_check_modifier_2() { - game.check.push({ cost: 3, modifier: 2, source: "add" }) +function end_tax() { + pop_state() + game.where = NOWHERE + spend_action(1) + resume_command() } -function add_influence_check_distance(distance) { - let idx = game.check.findIndex(i => i.source === "distance") +function get_tax_amount(loc) { + let tax - if (idx !== NOTHING) - game.check.splice(idx, 1) + if (loc === LOC_LONDON || loc === LOC_CALAIS) + tax = 3 + else if (is_city(loc)) + tax = 2 + else + tax = 1 - game.check.push({ cost: distance, modifier: 0, source: "distance" }) + if (command_has_stafford_branch(loc)) + tax += 1 + + return tax } -function prompt_influence_check() { - if (!game.check.some(c => c.source === "add")) { - view.actions.spend1 = 1 - view.actions.spend3 = 1 - } - if (game.where !== NOWHERE) - gen_action_locale(game.where) - view.actions.check = 1 +states.tax = { + inactive: "Tax", + prompt() { + view.prompt = "Tax: Select the location to tax." + if (game.where === NOWHERE) { + for (let loc of search_tax([], get_lord_locale(game.command))) + gen_action_locale(loc) + } else { + view.prompt = `Tax: Attempting to tax ${data.locales[game.where].name}. ` + prompt_influence_check() + } + }, + locale(loc) { + game.where = loc + if (loc === data.lords[game.command].seat) { + // Auto succeed without influence check at Lords seat. + deplete_locale(game.where) - view.prompt += `Cost: ${count_influence_cost()} - Range (${range(count_influence_score())})` + log(`Taxed automatically successful at %${game.where}.`) + add_lord_assets(game.command, COIN, get_tax_amount(game.where)) + end_tax() + } + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + logi(`Tax : ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (lord_has_capability(game.command, AOW_YORK_SO_WISE_SO_YOUNG)) { + log(`C${AOW_YORK_SO_WISE_SO_YOUNG}.`) + add_lord_assets(game.command, COIN, 1) + } + + if (results.success) { + deplete_locale(game.where) + + log(`Taxed %${game.where}.`) + add_lord_assets(game.command, COIN, get_tax_amount(game.where)) + + if ( + game.command === LORD_DEVON && + (game.where === LOC_EXETER || + game.where === LOC_LAUNCESTON || + game.where === LOC_PLYMOUTH || + game.where === LOC_WELLS || + game.where === LOC_DORCHESTER) + ) + add_lord_assets(game.command, COIN, 1) + } else { + log(`Tax of %${game.where} failed.`) + } + end_tax() + }, } -// === ACTION: PARLEY === +// === 4.6.4 ACTION: PARLEY === // TODO : FIX Parley through strongholds overseas function can_parley_at(loc) { @@ -6110,88 +4149,7 @@ states.parley = { } } -// === ACTION: LEVY VASSAL === - -function eligible_vassal(vassal) { - if (!is_vassal_ready(vassal)) { - return false - } - if ( - !is_favour_friendly(data.vassals[vassal].seat) && - (game.who !== LORD_HENRY_TUDOR || !is_event_in_play(EVENT_LANCASTER_MARGARET_BEAUFORT)) - ) { - return false - } - if (!is_favour_friendly(data.vassals[vassal].seat)) - return false - if ( - game.active === LANCASTER && - is_event_in_play(EVENT_YORK_YORKISTS_BLOCK_PARLIAMENT) && - !(is_event_in_play(EVENT_LANCASTER_MARGARET_BEAUFORT) && !is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND)) - ) { - return false - } - return true -} - -function goto_levy_muster_vassal(vassal) { - game.where = NOWHERE - let influence_cost = 0 - if (game.active === YORK && is_event_in_play(EVENT_LANCASTER_BUCKINGHAMS_PLOT)) - influence_cost += 2 - - push_state("levy_muster_vassal") - init_influence_check(game.who) - game.check.push({ - cost: influence_cost, - modifier: data.vassals[vassal].influence * (game.active === LANCASTER ? -1 : 1), - source: "vassal", - }) -} - -function end_levy_muster_vassal() { - pop_state() - end_influence_check() - resume_levy_muster_lord() -} - -states.levy_muster_vassal = { - inactive: "Levy Vassal", - prompt() { - view.prompt = `Levy Vassal ${data.vassals[game.which].name}. ` - prompt_influence_check() - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - - if (lord_has_capability(game.who, AOW_LANCASTER_TWO_ROSES)) { - log(`Automatic Success. C${AOW_LANCASTER_TWO_ROSES}.`) - } - else if (game.active === LANCASTER && is_event_in_play(EVENT_LANCASTER_THE_EARL_OF_RICHMOND) && game.state === "levy_muster_vassal") { - log(`Automatic Success. C${EVENT_LANCASTER_THE_EARL_OF_RICHMOND}.`) - } - else { - log(`Attempt to levy V${game.which} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - } - - if (results.success) { - if (eligible_kings_name()) { - goto_kings_name("Levy Vassal") - } - else { - muster_vassal(game.which, game.who) - end_levy_muster_vassal() - } - } - else { - end_levy_muster_vassal() - } - }, -} - -// === ACTION: MARCH === +// === 4.3 ACTION: MARCH === function get_way_type(from, to) { return map_get(data.ways[from], to, undefined) @@ -6371,7 +4329,7 @@ function end_march() { resume_command() } -// === Interception === +// === 4.3.4 INTERCEPT === function can_intercept_to(to) { // TODO: forbid lancaster intercept into york moving to york, and vice versa @@ -6404,6 +4362,22 @@ function end_intercept() { goto_kings_parley() } +function can_play_held_event_intercept(c) { + switch (c) { + case EVENT_LANCASTER_FLANK_ATTACK: + return can_play_flank_attack() + case EVENT_YORK_FLANK_ATTACK: + return can_play_flank_attack() + } + return false +} + +function prompt_held_event_intercept() { + for (let c of current_hand()) + if (can_play_held_event_intercept(c)) + gen_action_card(c) +} + states.intercept = { inactive: "Intercept", prompt() { @@ -6527,7 +4501,13 @@ function for_each_friendly_lord_in_locale(loc, f) { f(lord) } -// === King's Parley / Parliament's Truce -- cancel Approach === +// === MARCH EVENT: FLANK ATTACK === + +function can_play_flank_attack() { + return game.state === "intercept" && game.who !== NOBODY && !is_truce_in_effect() +} + +// === MARCH EVENT: KING'S PARLEY === // TODO: merge states into one question @@ -6575,6 +4555,12 @@ function end_kings_parley() { goto_parliaments_truce() } +// === MARCH EVENT: PARLIAMENT'S TRUCE === + +function can_play_parliaments_truce() { + return game.state === "campaign" +} + function is_truce_in_effect() { return ( is_event_in_play(EVENT_YORK_PARLIAMENTS_TRUCE) || @@ -6640,6 +4626,8 @@ function end_parliaments_truce() { goto_blocked_ford() } +// === MARCH EVENT: BLOCKED FORD === + function goto_blocked_ford() { let here = get_lord_locale(game.command) @@ -6679,7 +4667,7 @@ states.blocked_ford = { }, } -// === Exile === +// === 4.3.5 APPROACH - EXILE === function goto_exiles() { let here = get_lord_locale(game.command) @@ -6746,7 +4734,23 @@ function remove_lord_from_exile(lord) { game.pieces.in_exile = pack1_set(game.pieces.in_exile, lord, 0) } -// === ACTION: MARCH - DIVIDE SPOILS AFTER AVOID BATTLE === +// === 4.3.5 APPROACH - EXILE (SPOILS) === + +function has_any_spoils() { + return game.spoils && game.spoils[PROV] + game.spoils[COIN] + game.spoils[CART] + game.spoils[SHIP] > 0 +} + +function get_spoils(type) { + if (game.spoils) + return game.spoils[type] + return 0 +} + +function add_spoils(type, n) { + if (!game.spoils) + game.spoils = [ 0, 0, 0, 0, 0, 0, 0 ] + game.spoils[type] += n +} function list_spoils() { let list = [] @@ -6774,1176 +4778,138 @@ function take_spoils(type) { game.who = NOBODY } -// === ACTION: SUPPLY (SEARCHING) === +// === 4.4 BATTLE === -function can_supply_at(loc, ships) { - // if theoretically possible to supply (does not check carts or ships) - if (is_stronghold(loc) && is_friendly_locale(loc)) { - if (ships > 0 && is_seaport(loc)) - return true - if (!has_exhausted_marker(loc)) - return true - } - return false -} - -function search_supply_by_way(result, start, carts, ships) { - search_dist.fill(0) - search_seen.fill(0) - search_seen[start] = 1 - - let queue = [ start ] - while (queue.length > 0) { - let here = queue.shift() - let dist = search_dist[here] - - if (can_supply_at(here, ships)) { - if (result) - map_set(result, here, dist) - else - return true - } - - if (is_friendly_locale(here)) { - let next_dist = dist + 1 - if (next_dist <= carts) { - for (let next of data.locales[here].adjacent) { - if (!search_seen[next]) { - search_seen[next] = 1 - search_dist[next] = next_dist - queue.push(next) - } - } - } - } - } - - if (result) - return result - return false -} - -function search_supply_by_sea(result, here) { - // Search via sea from Exile box. - if (is_friendly_locale(here)) { - for (let next of find_ports(here)) { - if (can_supply_at(next, 1)) { - if (result) - map_set(result, next, 0) - else - return true - } - } - } - if (result) - return result - return false -} - -function search_supply(result) { - let here = get_lord_locale(game.command) - let carts = get_shared_assets(here, CART) - let ships = get_shared_assets(here, SHIP) - if (ships > 0 && is_exile(here)) - result = search_supply_by_sea(result, here) - result = search_supply_by_way(result, here, carts, ships) - return result -} - -// === ACTION: SUPPLY === - -function command_has_harbingers() { - return ( - lord_has_capability(game.command, AOW_LANCASTER_HARBINGERS) || - lord_has_capability(game.command, AOW_YORK_HARBINGERS) - ) -} - -function chamberlains_eligible_supply(source) { - for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) - if ( - is_vassal_mustered_with(vassal, game.command) && - lord_has_capability(game.command, AOW_LANCASTER_CHAMBERLAINS) - ) { - if (source === data.vassals[vassal].seat) - return true - } -} - -function command_has_stafford_branch(loc) { - if (lord_has_capability(game.command, AOW_YORK_STAFFORD_BRANCH)) { - return ( - loc === LOC_EXETER || - loc === LOC_LAUNCESTON || - loc === LOC_PLYMOUTH || - loc === LOC_WELLS || - loc === LOC_DORCHESTER - ) - } - return false -} - -function init_supply() { - game.supply = search_supply([]) -} - -function can_action_supply() { - if (game.actions < 1) - return false - return search_supply(false) -} - -function goto_supply() { - push_undo() - log(`Supplied`) - game.state = "supply_source" - init_supply() -} - -function modify_supply(loc, supply) { - let here = get_lord_locale(game.command) - let carts = get_shared_assets(here, CART) - - // Must carry supply over land with one cart per provender per way - let distance = map_get(game.supply, loc, 0) - if (distance > 0) - supply = Math.min(supply, Math.floor(carts / distance)) - - // Harbingers event doubles supply received - if (command_has_harbingers()) - supply = supply * 2 - - return supply -} - -function get_port_supply_amount(loc) { - if (is_seaport(loc)) { - let here = get_lord_locale(game.command) - let ships = get_shared_assets(here, SHIP) - return modify_supply(loc, ships) - } - return 0 -} - -function get_stronghold_supply_amount(loc) { - if (!has_exhausted_marker(loc)) { - let supply - - if (loc === LOC_LONDON || loc === LOC_CALAIS) - supply = 3 - else if (is_city(loc)) - supply = 2 - else - supply = 1 - - if (command_has_stafford_branch(loc)) - supply += 1 - - return modify_supply(loc, supply) - } - return 0 -} - -states.supply_source = { - inactive: "Supply", - prompt() { - view.prompt = "Supply: Select Supply Source." - - let here = get_lord_locale(game.command) - let carts = get_shared_assets(here, CART) - let ships = get_shared_assets(here, SHIP) - - if (carts > 0) - view.prompt += ` ${carts} Cart.` - if (ships > 0) - view.prompt += ` ${ships} Ship.` - - for (let i = 0; i < game.supply.length; i += 2) - gen_action_locale(game.supply[i]) - }, - locale(loc) { - push_undo() - - let port_supply = get_port_supply_amount(loc) - let stronghold_supply = get_stronghold_supply_amount(loc) - - if (stronghold_supply > 0 && port_supply === 0) { - use_stronghold_supply(loc, stronghold_supply) - return - } - - if (port_supply > 0 && stronghold_supply === 0) { - use_port_supply(loc, port_supply) - return - } - - game.where = loc - game.state = "select_supply_type" - }, -} - -function quartermasters_eligible_supply(source) { - for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) - if ( - is_vassal_mustered_with(vassal, game.command) && - lord_has_capability(game.command, AOW_LANCASTER_CHAMBERLAINS) - ) { - if (source === data.vassals[vassal].seat) - return true - } -} - -function use_stronghold_supply(source, amount) { - logi(`${amount} from Stronghold at %${source}`) - add_lord_assets(game.command, PROV, amount) - if (chamberlains_eligible_supply(source)) { - end_supply() - } - else { - deplete_locale(source) - end_supply() - } -} - -function use_port_supply(source, amount) { - logi(`${amount} from Port at %${source}`) - add_lord_assets(game.command, PROV, amount) - end_supply() -} - -function end_supply() { - spend_action(1) - resume_command() - game.supply = 0 - game.where = NOWHERE -} - -states.select_supply_type = { - inactive: "Supply", - prompt() { - let port = get_port_supply_amount(game.where) - let stronghold = get_stronghold_supply_amount(game.where) - - view.prompt = `Supply: ${stronghold} from Stronghold or ${port} from Port?` - view.actions.stronghold = 1 - view.actions.port = 1 - }, - stronghold() { - use_stronghold_supply(game.where, get_stronghold_supply_amount(game.where)) - }, - port() { - if (check_naval_blockade("supply", game.where)) { - roll_naval_blockade() - } - else { - use_port_supply(game.where, get_port_supply_amount(game.where)) - } - }, -} - -// === ACTION: FORAGE === - -function can_action_forage() { - if (game.actions < 1) - return false - let here = get_lord_locale(game.command) - if (has_exhausted_marker(here) || is_sea(here)) - return false - return true -} - -function has_adjacent_enemy(loc) { - for (let next of data.locales[loc].adjacent) - if (has_unbesieged_enemy_lord(next)) - return true - return false -} - -function has_adjacent_friendly(loc) { - for (let next of data.locales[loc].adjacent) - if (has_friendly_lord(next)) - return true - return false -} - -function goto_forage() { - push_undo() - let here = get_lord_locale(game.command) - if (!has_adjacent_enemy(here) && is_neutral_locale(here)) { - let die = roll_die() - if (die <= 4) { - add_lord_assets(game.command, PROV, 1) - log(`${HIT[die]}, Foraged at %${here}`) - deplete_locale(here) - } else { - log(`${MISS[die]}, Forage Failure`) - } - } else if (has_adjacent_enemy(here) || is_favour_enemy(here, game.active)) { - let die = roll_die() - if (die <= 3) { - add_lord_assets(game.command, PROV, 1) - log(`${HIT[die]}, Foraged at %${here}`) - deplete_locale(here) - } else { - log(`${MISS[die]}, Forage Failure`) - } - } else { - add_lord_assets(game.command, PROV, 1) - log(`Foraged at %${here}`) - deplete_locale(here) - } - if (lord_has_capability(game.command, AOW_YORK_SCOURERS)) { - add_lord_assets(game.command, PROV, 1) - log(`1 Extra Provender (Scourers)`) - } - - spend_action(1) - resume_command() +function get_lord_array_position(lord) { + for (let p = 0; p < 12; ++p) + if (game.battle.array[p] === lord) + return p + return -1 } -// === ACTION: TAX === - -function can_tax_at(here) { - if (is_friendly_locale(here) && !has_exhausted_marker(here)) { - // London, Calais, and Harlech - if (here === LOC_LONDON || here === LOC_CALAIS || here === LOC_HARLECH) - return true - - // Own seat - if (here === data.lords[game.command].seat) - return true - - // vassal seats - for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) - if (is_vassal_mustered_with(vassal, game.command)) - if (here === data.vassals[vassal].seat) - return true - } - return false +function set_active_attacker() { + set_active(game.battle.attacker) } -// adjacent friendly locales to an eligible stronghold (can_tax_at) -function search_tax(result, start) { - let ships = get_shared_assets(start, SHIP) - - search_seen.fill(0) - search_seen[start] = 1 - - let queue = [ start ] - while (queue.length > 0) { - let here = queue.shift() - - if (can_tax_at(here)) { - if (result) - set_add(result, here) - else - return true - } - - if (is_friendly_locale(here)) { - for (let next of data.locales[here].adjacent) { - if (!search_seen[next]) { - search_seen[next] = 1 - queue.push(next) - } - } - if (ships > 0 && is_seaport(here)) { - for (let next of find_ports(here)) { - if (!search_seen[next]) { - search_seen[next] = 1 - queue.push(next) - } - } - } - } - } - if (result) - return result +function set_active_defender() { + if (game.battle.attacker === P1) + set_active(P2) else - return false + set_active(P1) } -function can_action_tax() { - if (game.actions < 1) - return false - let here = get_lord_locale(game.command) - if (can_tax_at(here)) +function ravine_check(lord,pos) { + // TODO: if no lord in pos and no ravine? -- if (lord !== NOBODY) + if (game.battle.array[pos] === lord) return true - return search_tax(false, here) -} - -function goto_tax() { - push_undo() - push_state("tax") - init_influence_check(game.command) - game.where = NOWHERE -} - -function end_tax() { - pop_state() - game.where = NOWHERE - spend_action(1) - resume_command() -} - -function get_tax_amount(loc) { - let tax - - if (loc === LOC_LONDON || loc === LOC_CALAIS) - tax = 3 - else if (is_city(loc)) - tax = 2 - else - tax = 1 - - if (command_has_stafford_branch(loc)) - tax += 1 - - return tax -} - -states.tax = { - inactive: "Tax", - prompt() { - view.prompt = "Tax: Select the location to tax." - if (game.where === NOWHERE) { - for (let loc of search_tax([], get_lord_locale(game.command))) - gen_action_locale(loc) - } else { - view.prompt = `Tax: Attempting to tax ${data.locales[game.where].name}. ` - prompt_influence_check() - } - }, - locale(loc) { - game.where = loc - if (loc === data.lords[game.command].seat) { - // Auto succeed without influence check at Lords seat. - deplete_locale(game.where) - - log(`Taxed automatically successful at %${game.where}.`) - add_lord_assets(game.command, COIN, get_tax_amount(game.where)) - end_tax() - } - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - logi(`Tax : ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - - if (lord_has_capability(game.command, AOW_YORK_SO_WISE_SO_YOUNG)) { - log(`C${AOW_YORK_SO_WISE_SO_YOUNG}.`) - add_lord_assets(game.command, COIN, 1) - } - - if (results.success) { - deplete_locale(game.where) - - log(`Taxed %${game.where}.`) - add_lord_assets(game.command, COIN, get_tax_amount(game.where)) - - if ( - game.command === LORD_DEVON && - (game.where === LOC_EXETER || - game.where === LOC_LAUNCESTON || - game.where === LOC_PLYMOUTH || - game.where === LOC_WELLS || - game.where === LOC_DORCHESTER) - ) - add_lord_assets(game.command, COIN, 1) - } else { - log(`Tax of %${game.where} failed.`) - } - end_tax() - }, -} - -// === ACTION: SAIL === - -function drop_prov(lord) { - add_lord_assets(lord, PROV, -1) -} - -function drop_cart(lord) { - add_lord_assets(lord, CART, -1) -} - -function has_enough_available_ships_for_army() { - let ships = count_group_ships() - let army = count_lord_all_forces(game.group) - let needed_ships = army / 6 - return needed_ships <= ships -} - -function can_sail_to(to) { - if (is_wales_forbidden(to)) - return false - if (has_enemy_lord(to)) { - if (is_truce_in_effect()) - return false - if (!lord_has_capability(game.command, AOW_LANCASTER_HIGH_ADMIRAL)) - return false - } - return true -} - -function can_action_sail() { - // Must use whole action except if seamanship in play - - if (is_lancaster_lord(game.command)) { - if (!is_first_action() && !is_event_in_play(EVENT_LANCASTER_SEAMANSHIP)) - return false - } - - if (is_york_lord(game.command)) { - if ((is_event_in_play(EVENT_LANCASTER_FRENCH_FLEET) || !is_first_action() && !is_event_in_play(EVENT_YORK_SEAMANSHIP))) - return false - } - - if (game.actions === 0) - return false - - // at a seaport - let here = get_lord_locale(game.command) - if (!is_seaport(here)) - return false - - // with enough ships to carry all the army - if (!has_enough_available_ships_for_army()) - return false - - // and a valid destination - for (let to of find_sail_locales(here)) { - if (to === here) - continue - if (can_sail_to(to)) - return true - } - return false } -function is_seamanship_in_play() { - if (game.active === LANCASTER && is_event_in_play(EVENT_LANCASTER_SEAMANSHIP)) - return true - if (game.active === YORK && is_event_in_play(EVENT_YORK_SEAMANSHIP)) +function filled(pos) { + if (game.battle.array[pos] !== NOBODY && !ravine_check(game.battle.ravine, pos)) { return true - return false -} - -function goto_sail() { - push_undo() - game.state = "sail" -} - -states.sail = { - inactive: "Sail", - prompt() { - view.group = game.group - - let here = get_lord_locale(game.command) - let ships = count_group_ships() - let prov = count_group_assets(PROV) - let cart = count_group_assets(CART) - - let overflow_prov = (prov / 2 - ships) * 2 - let overflow_cart = (cart / 2 - ships) * 2 - - if (overflow_prov <= 0 && overflow_cart <= 0) { - view.prompt = `Sail: Select a destination Port.` - for (let to of find_sail_locales(here)) { - if (to === here) - continue - if (can_sail_to(to)) - gen_action_locale(to) - } - } else if (overflow_cart > 0) { - view.prompt = `Sailing with ${ships} Ships. Please discard ${overflow_cart} Cart` - if (cart > 0) { - for (let lord of game.group) { - if (get_lord_assets(lord, CART) > 0) - gen_action_cart(lord) - } - } - } else if (overflow_prov > 0) { - view.prompt = `Sailing with ${ships} Ships. Please discard ${overflow_prov} Provender` - if (prov > 0) { - for (let lord of game.group) { - if (get_lord_assets(lord, PROV) > 0) - gen_action_prov(lord) - } - } - } else { - view.prompt = "ERROR" - } - }, - prov: drop_prov, - cart: drop_cart, - locale(to) { - if (check_naval_blockade("sail", get_lord_locale(game.command)) || check_naval_blockade("sail", to)) { - roll_naval_blockade() - game.where = to - } - else { - log(`Sailed to %${to}${format_group_move()}.`) - - for (let lord of game.group) { - set_lord_locale(lord, to) - set_lord_moved(lord, 1) - levy_burgundians(lord) - } - - if (is_seamanship_in_play()) - spend_action(1) - else - spend_all_actions() - - // you can go to unbesieged enemy lord with norfolk capability - if (has_unbesieged_enemy_lord(to)) - goto_confirm_approach_sail() - else { - game.flags.surprise_landing = 1 - resume_command() - } - } - }, -} - -function goto_confirm_approach_sail() { - game.state = "confirm_approach_sail" -} - -states.confirm_approach_sail = { - inactive: "Sail", - prompt() { - view.prompt = `Sail: Confirm Approach to enemy Lord.` - view.group = game.group - view.actions.approach = 1 - }, - approach() { - push_undo() - goto_battle() - }, -} - -// === CAPABILITY : WE DONE DEEDS OF CHARITY === - -function tow_extra_ip() { - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { - if (lord_has_capability(lord, AOW_YORK_WE_DONE_DEEDS_OF_CHARITY) && (get_lord_assets(lord, PROV) > 0 || get_shared_assets(lord, PROV) > 0)) - return true } return false } -states.tow_extra_ip = { - inactive: "We done needs of charity", - prompt() { - let loc = 0 - view.prompt = "We done deeds of charity: spend up to two Provender to add influence points." - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (lord_has_capability(lord, AOW_YORK_WE_DONE_DEEDS_OF_CHARITY)) { - loc = get_lord_locale(lord) - } - } - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (game.flags.charity < 2 && get_lord_locale(lord) === loc && (get_lord_assets(lord, PROV) > 0)) { - gen_action_prov(lord) - } - } - view.actions.done = 1 - }, - prov(lord) { - push_undo() - add_lord_assets(lord, PROV, -1) - game.flags.charity += 1 - }, - done() { - increase_york_influence(game.flags.charity) - logi(`${AOW_YORK_WE_DONE_DEEDS_OF_CHARITY}`) - log("York paid " + game.flags.charity + " provender to add " + game.flags.charity + " Influence Points") - game.flags.charity = 0 - goto_disembark() - }, -} - -// === CAPABILITY : MERCHANTS === - -function can_action_merchants() { - let loc = get_lord_locale(game.command) - if (game.actions <= 0) - return false - - if (lord_has_capability(game.command, AOW_LANCASTER_MERCHANTS) && count_deplete(loc) > 0) - return true - else - return false -} - -function goto_merchants() { - game.count = count_deplete(get_lord_locale(game.command)) - game.state = "merchants" - init_influence_check(game.command) -} - -states.merchants = { - inactive: "Merchants", - prompt() { - view.prompt = "Merchants: Succeed an influence check to remove Depleted markers" - prompt_influence_check() - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - log(`Attempt to C${AOW_LANCASTER_MERCHANTS} with %${game.command} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - if (results.success) { - merchants_success() - } else { - end_merchants_attempt() - } - } -} -function merchants_success() { - game.state = "merchants_success" -} - -states.merchants_success = { - inactive: "Merchants Success", - prompt() { - view.prompt = `Remove Depleted/Exhausted markers` - deplete_merchants() - if (game.count === 0) { - view.actions.done = 1 - } - }, - locale(loc) { - push_undo() - remove_depleted_marker(loc) - remove_exhausted_marker(loc) - --game.count - if (game.count === 0) { - end_merchants_attempt() - } - }, - done(){ - end_merchants_attempt() - } -} +const battle_strike_positions = [ D1, D2, D3, A1, A2, A3 ] -function end_merchants_attempt() { - spend_action(1) - game.count = 0 - push_undo() - end_influence_check() - resume_command() -} +const battle_steps = [ + { name: "Archery", hits: count_archery_hits }, + { name: "Melee", hits: count_melee_hits }, +] -function deplete_merchants() { - let here = get_lord_locale(game.command) - for (let next of data.locales[here].adjacent) { - if (has_exhausted_marker(next) || has_depleted_marker(next)) - gen_action_locale(next) - } - if (has_exhausted_marker(here) || has_depleted_marker(here)) - gen_action_locale(here) -} +function count_archery_hits(lord) { + let hits = 0 + hits += get_lord_forces(lord, LONGBOWMEN) << 2 + hits += get_lord_forces(lord, BURGUNDIANS) << 2 + hits += get_lord_forces(lord, MILITIA) + hits += get_lord_forces(lord, MERCENARIES) -function count_deplete(loc) { - game.count = 0 - for (let next of data.locales[loc].adjacent) { - if (has_exhausted_marker(next) || has_depleted_marker(next)) { - ++game.count - } - } - if (has_exhausted_marker(loc) || has_depleted_marker(loc)) { - ++game.count + if (is_leeward_battle_line_in_play(lord)) { + // TODO: rounding? + return hits/2 } - if (game.count > 1) - return game.count = 2 - else - return game.count -} - -// === CAPABILITY : BURGUNDIANS === -function levy_burgundians(lord) { - if (is_seaport(get_lord_locale(lord)) && !is_exile(get_lord_locale(lord)) && lord_has_capability(lord, AOW_YORK_BURGUNDIANS) && game.flags.burgundians === 0) { - add_lord_forces(lord, BURGUNDIANS, 2) - logi(AOW_YORK_BURGUNDIANS) - game.flags.burgundians = 1 - } + return hits } -// === EVENT ACTION : EXILE PACT === - -function can_action_exile_pact() { - if (game.actions <= 0 || !is_event_in_play(EVENT_YORK_EXILE_PACT)) - return false +function count_melee_hits(lord) { + let hits = 0 + hits += /*retinue*/ 3 << 1 + //hits += count_vassals_with_lord(lord) << 2 + if (lord_has_capability(lord, AOW_LANCASTER_CHEVALIERS)) + hits += get_lord_forces(lord, MEN_AT_ARMS) << 2 else - return true -} - -function goto_exile_pact() { - push_undo() - game.state = "exile_pact" -} - -states.exile_pact = { - inactive: "Exile pact", - prompt() { - view.prompt = "Exile Pact : place your cylinder in one of your Exile boxes" - for (let loc of data.exile_boxes) { - if (has_favour_in_locale(game.active, loc)) - gen_action_locale(loc) - } - }, - locale(loc) { - push_undo() - set_lord_locale(game.command, loc) - end_exile_pact() - } -} - -function end_exile_pact() { - spend_action(1) - push_undo() - resume_command() -} - -// === CAPABILITY : NAVAL BLOCKADE - -function parley_through_sea(start, locale) { - // Only entered in levy - let ships = get_shared_assets(start, SHIP) - - if (ships === 0) { - game.flags.naval_blockade = -1 - } - - search_dist.fill(0) - search_seen.fill(0) - search_seen[start] = 1 - - let queue = [ start ] - while (queue.length > 0) { - let here = queue.shift() - let dist = search_dist[here] - let next_dist = dist + 1 - - if (is_friendly_locale(here)) { - for (let next of data.locales[here].adjacent) { - if (!search_seen[next]) { - search_seen[next] = 1 - search_dist[next] = next_dist - queue.push(next) - if (next === locale) { - game.flags.naval_blockade = -1 - } - } - } - } - } - queue = [ start ] - while (queue.length > 0) { - let here = queue.shift() - let dist = search_dist[here] - let next_dist = dist + 1 - - if (is_friendly_locale(here)) { - if (ships > 0 && is_seaport(here)) { - for (let next of find_ports(here)) { - if (!search_seen[next]) { - search_seen[next] = 1 - search_dist[next] = next_dist - queue.push(next) - if (next === locale && game.flags.naval_blockade !== -1) { - game.flags.naval_blockade = 1 - } - } - } - } - } - } -} - -function check_naval_blockade(action, locale) { - let ports = [data.port_1, data.port_2, data.port_3] - game.what = action - - if (!lord_has_capability(LORD_WARWICK_Y, AOW_YORK_NAVAL_BLOCKADE) || !is_seaport(get_lord_locale(LORD_WARWICK_Y)) || is_exile(get_lord_locale(LORD_WARWICK_Y))) { - return false - } - - if (action === "levy parley") { - parley_through_sea(get_lord_locale(game.who), locale) - if (game.flags.naval_blockade !== 1) { - return false - } - } + hits += get_lord_forces(lord, MEN_AT_ARMS) << 1 + hits += get_lord_forces(lord, MILITIA) + hits += get_lord_forces(lord, MERCENARIES) + hits += get_lord_forces(lord, BURGUNDIANS) << 1 - if (action === "campaign parley" && data.locales[locale].adjacent.includes(get_lord_locale(game.command))) { - return false - } - - for (let port of ports) { - if (set_has(port, get_lord_locale(LORD_WARWICK_Y)) && set_has(port, locale)) { - return true - } + if (lord === game.battle.caltrops) { + hits += 2 } -} - -function roll_naval_blockade() { - push_state("naval_blockade") -} - -// Parley, and Tax -states.naval_blockade = { - inactive: "Naval Blockade", - prompt() { - view.prompt = `Naval Blockade : Warwick block this action except on a 1-2` - view.actions.roll = 1 - }, - roll() { - let threshold = 2 - let roll = roll_die() - let success = threshold >= roll - log(`Attempt to counter Naval Blockade ${success ? "Failed" : "Successful"}: (1-2) ${success ? HIT[roll] : MISS[roll]}`) - if (success) { - logi(`Successfully overran C${AOW_YORK_NAVAL_BLOCKADE}`) - if (game.what === "levy parley") { - game.flags.naval_blockade = -1 - } - if (game.what === "campaign parley") { - game.flags.naval_blockade = -1 - } - if (game.what === "levy ship") { - add_lord_assets(game.who, SHIP, 1) - } - if (game.what === "supply") { - use_port_supply(game.where, get_port_supply_amount(game.where)) - } - if (game.what === "sail") { - log(`Sailed to %${game.where}${format_group_move()}.`) - for (let lord of game.group) { - set_lord_locale(lord, game.where) - set_lord_moved(lord, 1) - } - if (is_seamanship_in_play()) - spend_action(1) - else - spend_all_actions() - game.flags.surprise_landing = 1 - resume_command() - } - } - else { - logi(`Failed C${AOW_YORK_NAVAL_BLOCKADE}`) - if (game.what === "levy parley") { - pop_state() - } - if (game.what === "campaign parley") { - pop_state() - } - } - if (game.what === "levy parley") { - pop_state() - resume_levy_muster_lord() - } - if (game.what === "campaign parley") { - pop_state() - --game.count - resume_command() - } - if (game.what === "levy ship") { - pop_state() - resume_levy_muster_lord() - } - if (game.what === "supply" && !success) { - end_supply() - } - if (game.what === "sail" && !success) { - resume_command() - } - game.what = NOTHING - }, -} - -// === CAPABILITY : AGITATORS === - -function can_action_agitators() { - let here = get_lord_locale(game.command) - if (game.actions <= 0) - return false - if (lord_has_capability(game.command, AOW_YORK_AGITATORS)) { - for (let next of data.locales[here].adjacent) { - if (!has_exhausted_marker(next) && !is_friendly_locale(next)) - return true - } - } - else - return false + return hits } -function goto_agitators() { - game.count = count_deplete(get_lord_locale(game.command)) - game.state = "agitators" +function count_lord_hits(lord) { + return battle_steps[game.battle.step].hits(lord) } -states.agitators = { - inactive: "Agitators", - prompt() { - view.prompt = "Agitators: Add a depleted marker or flip to exhausted adjacent" - deplete_agitators() - }, - locale(loc) { - push_undo() - if (has_depleted_marker(loc)) { - add_exhausted_marker(loc) - } - else { - add_depleted_marker(loc) - } - end_agitators() - }, +function format_strike_step() { + return battle_steps[game.battle.step].name } -function deplete_agitators() { - let here = get_lord_locale(game.command) - for (let next of data.locales[here].adjacent) { - if (!has_exhausted_marker(next) && !is_friendly_locale(next)) - gen_action_locale(next) +function format_hits() { + if (game.battle.ahits > 0) { + return `${game.battle.ahits} Hit${game.battle.ahits > 1 ? "s" : ""}` + } else if (game.battle.dhits > 0) { + return `${game.battle.dhits} Hit${game.battle.dhits > 1 ? "s" : ""}` } } -function end_agitators() { - spend_action(1) - push_undo() - resume_command() -} - -// === CAPABILITY : HERALDS === - -function can_action_heralds() { - if (game.actions <= 0) - return false - - if (!is_first_action()) - return false - // at a seaport - let here = get_lord_locale(game.command) - if (!is_seaport(here)) - return false - - if (!lord_has_capability(game.command, AOW_LANCASTER_HERALDS)) - return false - - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_on_calendar(lord)) - return true - } +function is_battle_over() { + set_active_attacker() + if (has_no_unrouted_forces()) + return true + set_active_defender() + if (has_no_unrouted_forces()) + return true return false } -function goto_heralds() { - game.state = "heralds" -} - -states.heralds = { - inactive: "Heralds", - prompt() { - view.prompt = "Heralds: Choose a Lord on calendar to shift him to next turn box" - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_on_calendar(lord)) - gen_action_lord(lord) - } - }, - lord(other) { - goto_heralds_attempt(other) - }, -} - -function goto_heralds_attempt(lord) { - game.what = lord - push_state("heralds_attempt") - init_influence_check(game.command) +function has_no_unrouted_forces() { + // All unrouted lords are either in battle array or in reserves + for (let p = 0; p < 6; ++p) + if (is_friendly_lord(game.battle.array[p])) + return false + for (let lord of game.battle.reserves) + if (is_friendly_lord(lord)) + return false + return true } -states.heralds_attempt = { - inactive: "Heralds Attempt", - prompt() { - view.prompt = `Attempt to shift ${lord_name[game.what]} to next Turn Box. ` - prompt_influence_check() - }, - spend1: add_influence_check_modifier_1, - spend3: add_influence_check_modifier_2, - check() { - let results = do_influence_check() - log(`Attempt to shift L${game.what} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) - - if (results.success) { - game.who = game.what - set_lord_calendar(game.who, current_turn() + 1) - end_heralds_attempt() - } else { - end_heralds_attempt() - } - }, +function is_attacker() { + return game.active === game.battle.attacker } -function end_heralds_attempt() { - spend_all_actions() - pop_state() - end_influence_check() - resume_command() +function is_defender() { + return game.active !== game.battle.attacker } -// === BATTLE === - -function set_active_attacker() { - set_active(game.battle.attacker) +function is_archery_step() { + return game.battle.step === 0 } -function set_active_defender() { - if (game.battle.attacker === P1) - set_active(P2) - else - set_active(P1) +function is_melee_step() { + return game.battle.step === 1 } -function goto_battle() { - start_battle() +function has_strike(pos) { + return game.battle.ah[pos] > 0 } -function init_battle(here) { - game.battle = { - where: here, - round: 1, - step: 0, - relief: 0, - attacker: game.active, - ambush: 0, - loser: 0, - fought: 0, // flag all lords who participated - array: [ - -1, -1, -1, - -1, -1, -1, - ], - valour: Array(lord_count).fill(0), - routed_vassals: [], - engagements: [], - reserves: [], - retreated: 0, - fled: [], - routed: [], - target: NOBODY, - strikers: 0, - a_hits: 0, - d_hits: 0, - fc: -1, - } -} // Capabilities adding troops at start of the battle function add_battle_capability_troops() { @@ -8022,12 +4988,37 @@ function remove_battle_capability_troops() { } } -function start_battle() { +function goto_battle() { let here = get_lord_locale(game.command) log_h3(`Battle at %${here}`) - init_battle(game.where) + game.battle = { + where: game.where, + round: 1, + step: 0, + relief: 0, + attacker: game.active, + ambush: 0, + loser: 0, + fought: 0, // flag all lords who participated + array: [ + -1, -1, -1, + -1, -1, -1, + ], + valour: Array(lord_count).fill(0), + routed_vassals: [], + engagements: [], + reserves: [], + retreated: 0, + fled: [], + routed: [], + target: NOBODY, + strikers: 0, + a_hits: 0, + d_hits: 0, + fc: -1, + } // Troops by capability @@ -8084,7 +5075,7 @@ function start_battle() { goto_array_defender() } -// === BATTLE: BATTLE ARRAY === +// === 4.4.1 BATTLE ARRAY === // 0) Defender decides to stand for Battle, not Exile // 1) Defender decides how he wants to array his lords @@ -8232,32 +5223,32 @@ states.array_defender = { end_array: end_array_defender, } -// === BATTLE: EVENTS === +// === 4.4.1 BATTLE ARRAY: EVENTS === -function goto_attacker_events() { - set_active_attacker() +function goto_defender_events() { + set_active_defender() log_br() if (can_play_battle_events()) - game.state = "attacker_events" + game.state = "defender_events" else - end_attacker_events() + end_defender_events() } -function end_attacker_events() { - goto_battle_rounds() +function end_defender_events() { + goto_attacker_events() } -function goto_defender_events() { - set_active_defender() +function goto_attacker_events() { + set_active_attacker() log_br() if (can_play_battle_events()) - game.state = "defender_events" + game.state = "attacker_events" else - end_defender_events() + end_attacker_events() } -function end_defender_events() { - goto_attacker_events() +function end_attacker_events() { + goto_battle_rounds() } function resume_battle_events() { @@ -8268,19 +5259,30 @@ function resume_battle_events() { goto_defender_events() } -function could_play_card(c) { - if (!game.hidden) { - // TODO: check capabilities on lords revealed in battle if hidden - if (map_has_value(game.pieces.capabilities, c)) - return false - } - if (set_has(game.events, c)) - return false - if (is_york_card(c)) - return game.hand_y.length > 0 - if (is_lancaster_card(c)) - return game.hand_l.length > 0 - return true +states.defender_events = { + inactive: "Defender Events", + prompt() { + view.prompt = "Defender may play Events." + prompt_battle_events() + + // defender only events + }, + card: action_battle_events, + done() { + end_defender_events() + }, +} + +states.attacker_events = { + inactive: "Attacker Events", + prompt() { + view.prompt = "Attacker may play Events." + prompt_battle_events() + }, + card: action_battle_events, + done() { + end_attacker_events() + }, } function can_play_battle_events() { @@ -8315,7 +5317,7 @@ function prompt_battle_events() { // both attacker and defender events if (game.active === LANCASTER) { gen_action_card_if_held(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) - if (can_play_suspicion()) + if (can_play_suspicion()) gen_action_card_if_held(EVENT_LANCASTER_SUSPICION) if (can_play_for_trust_not_him()) gen_action_card_if_held(EVENT_LANCASTER_FOR_TRUST_NOT_HIM) @@ -8333,204 +5335,6 @@ function prompt_battle_events() { view.actions.done = 1 } -function prompt_battle_events_death() { - // both attacker and defender events - if (game.active === LANCASTER) { - if (can_play_escape_ship()) - gen_action_card_if_held(EVENT_LANCASTER_ESCAPE_SHIP) - if (can_play_warden_of_the_marches()) - gen_action_card_if_held(EVENT_LANCASTER_WARDEN_OF_THE_MARCHES) - if (can_play_talbot_to_the_rescue()) - gen_action_card_if_held(EVENT_LANCASTER_TALBOT_TO_THE_RESCUE) - } - if (game.active === YORK) { - if (can_play_escape_ship()) - gen_action_card_if_held(EVENT_YORK_ESCAPE_SHIP) - } - view.actions.done = 1 -} - -// === EVENT : WARDEN OF THE MARCHES === - -function can_play_warden_of_the_marches() { - // TOOD : Maybe a bug with blocked ford/exile ? - let can_play = false - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_friendly_locale(loc) && is_north(loc) && loc !== game.battle.where) { - can_play = true - } - } - if (!can_play) { - return false - } - // if blocked ford then flee - if (is_north(game.where)) - return true - // if battle - if (is_north(game.battle.where)) - return true - return false -} - -function goto_play_warden_of_the_marches() { - push_undo() - push_state("warden_of_the_marches") -} - -states.warden_of_the_marches = { - inactive: "Warden of the Marches", - prompt() { - let done = true - view.prompt = "Warden of the Marches: Select a friendly locale in the North to move routed lords there" - for (let loc = first_locale; loc <= last_locale; loc++) { - if (is_friendly_locale(loc) && is_north(loc) && loc !== game.battle.where) { - done = false - gen_action_locale(loc) - } - } - if (done) { - view.actions.done = 1 - } - }, - locale(loc) { - push_undo() - for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; lord++) { - if (get_lord_locale(lord) === game.battle.where) { - set_lord_locale(lord, loc) - if (!lord_has_unrouted_troops(lord)) { - disband_lord(lord, false) - } - else { - set_lord_forces(lord, RETINUE, 1) - } - for (let x = 0; x < FORCE_TYPE_COUNT; ++x) { - set_lord_forces(lord, x, 0) - if (get_lord_routed_forces(lord, x) > 0) { - set_lord_routed_forces(lord, x, 0) - } - } - for_each_vassal_with_lord(lord, v => { - if (set_has(game.battle.routed_vassals, v)) { - array_remove(game.battle.routed_vassals, v) - disband_vassal(v) - } - }) - } - } - logi(`Moved to ${data.locales[loc].name}`) - end_warden_of_the_marches() - }, - done() { - end_warden_of_the_marches() - }, -} - -function end_warden_of_the_marches() { - game.flags.warden_of_the_marches = 1 - game.who = NOBODY - pop_state() -} - -// === EVENT : ESCAPE SHIP === - -function can_play_escape_ship() { - return can_escape_at(game.battle.where) -} - -function can_escape_at(here) { - if (game.active === YORK && has_favoury_marker(here) && is_seaport(here)) - return true - if (game.active === LANCASTER && has_favourl_marker(here) && is_seaport(here)) - return true - if (search_escape_route(here)) - return true - return false -} - -function search_escape_route(start) { - search_seen.fill(0) - search_seen[start] = 1 - let queue = [start] - - while (queue.length > 0) { - let here = queue.shift() - - // Check if the current locale is a seaport - if (is_seaport(here)) { - return true - } - - if (is_friendly_locale(here)) { - for (let next of data.locales[here].adjacent) { - if (!search_seen[next]) { - search_seen[next] = 1 - queue.push(next) - } - } - } - } - return false -} - -function goto_play_escape_ship() { - push_state("escape_ship") - game.who = NOBODY -} - -states.escape_ship = { - inactive: `Escape ship`, - prompt() { - view.prompt = "Escape Ship: Your lords go to Exile." - view.actions.escape_ship = 1 - }, - escape_ship() { - push_undo() - for (let lord of game.battle.fled) { - log(`${lord_name[lord]} went to exile.`) - exile_lord(lord) - set_delete(game.battle.fled, lord) - } - for (let lord of game.battle.routed) { - log(`${lord_name[lord]} went to exile.`) - exile_lord(lord) - set_delete(game.battle.routed, lord) - } - goto_battle_aftermath() - }, -} - -// === EVENT : TALBOT TO THE RESCUE === - -function can_play_talbot_to_the_rescue() { - return true -} - -states.attacker_events = { - inactive: "Attacker Events", - prompt() { - view.prompt = "Attacker may play Events." - prompt_battle_events() - }, - card: action_battle_events, - done() { - end_attacker_events() - }, -} - -states.defender_events = { - inactive: "Defender Events", - prompt() { - view.prompt = "Defender may play Events." - prompt_battle_events() - - // defender only events - }, - card: action_battle_events, - done() { - end_defender_events() - }, -} - function action_battle_events(c) { game.what = c set_delete(current_hand(), c) @@ -8562,7 +5366,7 @@ function action_battle_events(c) { } } -// === EVENT : RAVINE === +// === BATTLE EVENT: RAVINE === states.ravine = { inactive: "Ravine", @@ -8588,7 +5392,22 @@ states.ravine = { }, } -// === EVENT : CALTROPS === +// === BATTLE EVENT: REGROUP === + +function is_regroup_in_play() { + if (game.active === YORK) + return is_event_in_play(EVENT_YORK_REGROUP) + return false +} + +// TODO + +// === BATTLE EVENT: CALTROPS === + +function is_caltrops_in_play() { + if (game.active === YORK) + return is_event_in_play(EVENT_YORK_CALTROPS) +} states.caltrops = { inactive: "Caltrops", @@ -8609,7 +5428,7 @@ states.caltrops = { }, } -// === EVENT : SUSPICION === +// === BATTLE EVENT: SUSPICION === function can_play_suspicion() { if (highest_friendly_influence() >= lowest_enemy_influence()) { @@ -8724,7 +5543,7 @@ states.influence_check_suspicion = { }, } -// === EVENT : FOR TRUST NOT HIM === +// === BATTLE EVENT: FOR TRUST NOT HIM === function can_play_for_trust_not_him() { for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { @@ -8824,7 +5643,126 @@ function end_for_trust_not_him() { resume_battle_events() } -// === BATTLE: FLEE === +// === BATTLE EVENT: LEEWARD BATTLE LINE === + +function is_leeward_battle_line_in_play(lord) { + if (is_archery_step()) { + if (is_event_in_play(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) + && !is_event_in_play(EVENT_YORK_LEEWARD_BATTLE_LINE) + && is_york_lord(lord)) { + logevent(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) + return true + } + if (is_event_in_play(EVENT_YORK_LEEWARD_BATTLE_LINE) + && !is_event_in_play(EVENT_LANCASTER_LEEWARD_BATTLE_LINE) + && is_lancaster_lord(lord)) { + logevent(EVENT_YORK_LEEWARD_BATTLE_LINE) + return true + } + } + return false +} + +// === BATTLE EVENT: CULVERINS AND FALCONETS === + +function goto_culverins() { + let can_play = false + for (let lord of game.battle.array) { + if (is_lancaster_lord(lord) && lord_has_capability(lord, AOW_LANCASTER_CULVERINS_AND_FALCONETS)) + can_play = true + if (is_york_lord(lord) && lord_has_capability(lord, AOW_YORK_CULVERINS_AND_FALCONETS)) + can_play = true + } + if (can_play) { + set_active_defender() + game.state = "culverins_and_falconets" + game.who = NOBODY + } + else { + goto_engagement_total_hits() + } +} + +function artillery_hits(ahits) { + if (is_attacker()) { + game.battle.attacker_artillery = ahits*2 + } + if (is_defender()) { + game.battle.defender_artillery = ahits*2 + } +} + +states.culverins_and_falconets = { + inactive: "Culverins and Falconets", + prompt() { + let done = true + view.prompt = `Use Culverin and Falconets ?` + for (let lord of game.battle.array) { + if (lord !== NOBODY) { + if (is_friendly_lord(lord) && (lord_has_capability(lord, AOW_YORK_CULVERINS_AND_FALCONETS))) { + gen_action_card(AOW_YORK_CULVERINS_AND_FALCONETS) + done = false + } + if (is_friendly_lord(lord) && (lord_has_capability(lord, AOW_LANCASTER_CULVERINS_AND_FALCONETS))) { + gen_action_card(AOW_LANCASTER_CULVERINS_AND_FALCONETS) + done = false + } + } + } + if (done) { + view.prompt = "Culverins and Falconets : Done" + } + view.actions.done = 1 + }, + card(c) { + let die = roll_die() + let lord = find_lord_with_capability_card(c) + if (is_event_in_play(EVENT_YORK_PATRICK_DE_LA_MOTE) && game.active === YORK) { + let die2 = roll_die() + die += die2 + } + logi(`${data.lords[lord].name} Artillery does ${die} hits`) + artillery_hits(die) + discard_lord_capability(lord, c) + }, + done() { + if (is_defender()) { + set_active_enemy() + } + else { + goto_engagement_total_hits() + } + } +} + +// === BATTLE EVENT: SWIFT MANEUVER === + +function is_swift_maneuver_in_play() { + return is_event_in_play(EVENT_YORK_SWIFT_MANEUVER) +} + +states.swift_maneuver = { + inactive: "Swift Maneuver", + prompt() { + view.prompt = "Swift Maneuver: You may end the round now" + view.actions.end_battle_round = 1 + view.actions.pass = 1 + }, + end_battle_round() { + logevent(`${EVENT_YORK_SWIFT_MANEUVER}`) + log("Ended Action Round.") + set_active_enemy() + goto_end_battle_round() + }, + pass() { + logevent(`${EVENT_YORK_SWIFT_MANEUVER}`) + log("Passed.") + set_active_enemy() + pop_state() + }, +} + +// === 4.4.2 BATTLE ROUNDS === function goto_battle_rounds() { set_active_defender() @@ -8832,6 +5770,8 @@ function goto_battle_rounds() { goto_flee() } +// === 4.4.2 BATTLE ROUNDS: FLEE === + function goto_flee() { game.state = "flee_battle" } @@ -8881,7 +5821,7 @@ states.flee_battle = { }, } -// === BATTLE: REPOSITION === +// === 4.4.2 BATTLE ROUNDS: REPOSITION === function slide_array(from, to) { game.battle.array[to] = game.battle.array[from] @@ -9043,107 +5983,8 @@ states.reposition_center = { }, } -// === BATTLE: STRIKE === - -function filled(pos) { - if (game.battle.array[pos] !== NOBODY && !ravine_check(game.battle.ravine, pos)) { - return true - } - return false -} -const battle_strike_positions = [ D1, D2, D3, A1, A2, A3 ] - -const battle_steps = [ - { name: "Archery", hits: count_archery_hits }, - { name: "Melee", hits: count_melee_hits }, -] - -function count_archery_hits(lord) { - let hits = 0 - hits += get_lord_forces(lord, LONGBOWMEN) << 2 - hits += get_lord_forces(lord, BURGUNDIANS) << 2 - hits += get_lord_forces(lord, MILITIA) - hits += get_lord_forces(lord, MERCENARIES) - - if (is_leeward_battle_line_in_play(lord)) { - return hits/2 - } - - return hits -} - -function count_melee_hits(lord) { - let hits = 0 - hits += /*retinue*/ 3 << 1 - //hits += count_vassals_with_lord(lord) << 2 - if (lord_has_capability(lord, AOW_LANCASTER_CHEVALIERS)) - hits += get_lord_forces(lord, MEN_AT_ARMS) << 2 - else - hits += get_lord_forces(lord, MEN_AT_ARMS) << 1 - hits += get_lord_forces(lord, MILITIA) - hits += get_lord_forces(lord, MERCENARIES) - hits += get_lord_forces(lord, BURGUNDIANS) << 1 - - if (lord === game.battle.caltrops) { - hits += 2 - } - - return hits -} - -function count_lord_hits(lord) { - return battle_steps[game.battle.step].hits(lord) -} - -function is_battle_over() { - set_active_attacker() - if (has_no_unrouted_forces()) - return true - set_active_defender() - if (has_no_unrouted_forces()) - return true - return false -} - -function has_no_unrouted_forces() { - // All unrouted lords are either in battle array or in reserves - for (let p = 0; p < 6; ++p) - if (is_friendly_lord(game.battle.array[p])) - return false - for (let lord of game.battle.reserves) - if (is_friendly_lord(lord)) - return false - return true -} - -function is_attacker() { - return game.active === game.battle.attacker -} - -function is_defender() { - return game.active !== game.battle.attacker -} - -function is_archery_step() { - return game.battle.step === 0 -} - -function is_melee_step() { - return game.battle.step === 1 -} - -function has_strike(pos) { - return game.battle.ah[pos] > 0 -} - -// === BATTLE: ENGAGEMENTS - -function ravine_check(lord,pos) { - if (game.battle.array[pos] === lord) - return true - return false -} +// === 4.4.2 BATTLE ROUNDS: ENGAGE / STRIKE === function determine_engagements() { let center = [ A2, D2 ] @@ -9167,33 +6008,6 @@ function determine_engagements() { return results } -// === BATTLE: STRIKE === - -// for each battle step: -// generate strikes for each lord -// while strikes remain: -// create list of strike groups (choose left/right both rows) -// select strike group -// create target group (choose if sally) -// total strikes and roll for walls -// while hits remain: -// assign hit to unit in target group -// if lord routs: -// forget choice of left/right strike group in current row -// create new target group (choose if left/right/sally) - -function format_strike_step() { - return battle_steps[game.battle.step].name -} - -function format_hits() { - if (game.battle.ahits > 0) { - return `${game.battle.ahits} Hit${game.battle.ahits > 1 ? "s" : ""}` - } else if (game.battle.dhits > 0) { - return `${game.battle.dhits} Hit${game.battle.dhits > 1 ? "s" : ""}` - } -} - function goto_first_engagement() { game.battle.step = 0 game.battle.engagements = determine_engagements() @@ -9289,79 +6103,20 @@ function resume_engagement() { } } -// === BATTLE: CULVERINS AND FALCONETS === - -function goto_culverins() { - let can_play = false - for (let lord of game.battle.array) { - if (is_lancaster_lord(lord) && lord_has_capability(lord, AOW_LANCASTER_CULVERINS_AND_FALCONETS)) - can_play = true - if (is_york_lord(lord) && lord_has_capability(lord, AOW_YORK_CULVERINS_AND_FALCONETS)) - can_play = true - } - if (can_play) { - set_active_defender() - game.state = "culverins_and_falconets" - game.who = NOBODY - } - else { - goto_engagement_total_hits() - } -} - -states.culverins_and_falconets = { - inactive: "Culverins and Falconets", - prompt() { - let done = true - view.prompt = `Use Culverin and Falconets ?` - for (let lord of game.battle.array) { - if (lord !== NOBODY) { - if (is_friendly_lord(lord) && (lord_has_capability(lord, AOW_YORK_CULVERINS_AND_FALCONETS))) { - gen_action_card(AOW_YORK_CULVERINS_AND_FALCONETS) - done = false - } - if (is_friendly_lord(lord) && (lord_has_capability(lord, AOW_LANCASTER_CULVERINS_AND_FALCONETS))) { - gen_action_card(AOW_LANCASTER_CULVERINS_AND_FALCONETS) - done = false - } - } - } - if (done) { - view.prompt = "Culverins and Falconets : Done" - } - view.actions.done = 1 - }, - card(c) { - let die = roll_die() - let lord = find_lord_with_capability_card(c) - if (is_event_in_play(EVENT_YORK_PATRICK_DE_LA_MOTE) && game.active === YORK) { - let die2 = roll_die() - die += die2 - } - logi(`${data.lords[lord].name} Artillery does ${die} hits`) - artillery_hits(die) - discard_lord_capability(lord, c) - }, - done() { - if (is_defender()) { - set_active_enemy() - } - else { - goto_engagement_total_hits() - } - } -} - -function artillery_hits(ahits) { - if (is_attacker()) { - game.battle.attacker_artillery = ahits*2 - } - if (is_defender()) { - game.battle.defender_artillery = ahits*2 - } -} +// === 4.4.2 BATTLE ROUNDS: TOTAL HITS (ROUND UP) === -// === BATTLE: TOTAL HITS (ROUND UP) === +// for each battle step: +// generate strikes for each lord +// while strikes remain: +// create list of strike groups (choose left/right both rows) +// select strike group +// create target group (choose if sally) +// total strikes and roll for walls +// while hits remain: +// assign hit to unit in target group +// if lord routs: +// forget choice of left/right strike group in current row +// create new target group (choose if left/right/sally) function goto_engagement_total_hits() { let ahits = 0 @@ -9420,7 +6175,7 @@ function log_hits(total, name) { logi(`No ${name}s`) } -// === BATTLE: ASSIGN HITS TO UNITS / ROLL BY HIT / ROUT === +// === 4.4.2 BATTLE ROUNDS: APPLY HITS / PROTECTION / ROLL BY HIT / ROUT === function goto_defender_assign_hits() { set_active_defender() @@ -9675,10 +6430,11 @@ function action_assign_hits(lord, type, special) { game.where = special } else { rout_unit(lord, type, special) - // Switf Maneuver event + // Swift Maneuver event if (is_swift_maneuver_in_play() && type === RETINUE) { set_active_enemy() push_state("swift_maneuver") + // TODO: add return here? } finish_action_assign_hits(lord) } @@ -9728,33 +6484,12 @@ states.spend_valour = { }, } -states.swift_maneuver = { - inactive: "Swift Maneuver", - prompt() { - view.prompt = "Swift Maneuver: You may end the round now" - view.actions.end_battle_round = 1 - view.actions.pass = 1 - }, - end_battle_round() { - logevent(`${EVENT_YORK_SWIFT_MANEUVER}`) - log("Ended Action Round.") - set_active_enemy() - goto_end_battle_round() - }, - pass() { - logevent(`${EVENT_YORK_SWIFT_MANEUVER}`) - log("Passed.") - set_active_enemy() - pop_state() - }, -} +// === BATTLE: NEW ROUND === function goto_end_battle_round() { end_battle_round() } -// === BATTLE: NEW ROUND === - function end_battle_round() { game.battle.ravine = NOBODY let attacker_loser = null @@ -9785,11 +6520,12 @@ function end_battle_round() { game.battle.ambush = 0 + // TODO: goto_battle_rounds() instead? set_active_defender() goto_flee() } -// === ENDING THE BATTLE === +// === 4.4.3 ENDING THE BATTLE === // Ending the Battle - optimized from rules as written // Loser retreat / withdraw / remove @@ -9803,10 +6539,10 @@ function set_active_loser() { } function set_active_victor() { - if (game.battle.loser === P1) - set_active(P2) + if (game.battle.loser === YORK) + set_active(LANCASTER) else - set_active(P1) + set_active(YORK) } function end_battle() { @@ -9830,6 +6566,8 @@ function has_defeated_lords() { return false } +// === 4.4.3 ENDING THE BATTLE: INFLUENCE === + function goto_battle_influence() { if (game.battle.loser !== BOTH) { set_active_loser() @@ -9849,7 +6587,7 @@ function goto_battle_influence() { } } -// === ENDING THE BATTLE: LOSSES === +// === 4.4.3 ENDING THE BATTLE: LOSSES === function has_battle_losses() { for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) @@ -9937,7 +6675,7 @@ states.battle_losses = { }, } -// === ENDING THE BATTLE: SPOILS (VICTOR) === +// === 4.4.3 ENDING THE BATTLE: SPOILS === function log_spoils() { if (game.spoils[PROV] > 0) @@ -9946,17 +6684,6 @@ function log_spoils() { logi(game.spoils[CART] + " Cart") } -function is_neutral_locale(loc) { - return !has_favourl_marker(loc) && !has_favoury_marker(loc) -} - -function has_favour_in_locale(side, loc) { - if (side === YORK) - return has_favoury_marker(loc) - else - return has_favourl_marker(loc) -} - function calculate_spoils() { let n_prov = 0 let n_cart = 0 @@ -10054,6 +6781,8 @@ states.battle_spoils = { }, } +// === 4.4.3 ENDING THE BATTLE: DEATH CHECK AND DISBAND === + function goto_death_or_disband() { remove_battle_capability_troops() if (has_defeated_lords()) { @@ -10077,6 +6806,23 @@ function end_death_or_disband() { } } +function prompt_battle_events_death() { + // both attacker and defender events + if (game.active === LANCASTER) { + if (can_play_escape_ship()) + gen_action_card_if_held(EVENT_LANCASTER_ESCAPE_SHIP) + if (can_play_warden_of_the_marches()) + gen_action_card_if_held(EVENT_LANCASTER_WARDEN_OF_THE_MARCHES) + if (can_play_talbot_to_the_rescue()) + gen_action_card_if_held(EVENT_LANCASTER_TALBOT_TO_THE_RESCUE) + } + if (game.active === YORK) { + if (can_play_escape_ship()) + gen_action_card_if_held(EVENT_YORK_ESCAPE_SHIP) + } + view.actions.done = 1 +} + states.death_or_disband = { inactive: "Death or Disband", prompt() { @@ -10110,7 +6856,7 @@ states.death_or_disband = { let threshold = 2 let modifier = 0 - // === CAPABILITY : BLOODY THOU ART, BLOODY WILL BE THE END === // + // CAPABILITY: BLOODY THOU ART, BLOODY WILL BE THE END if (is_lancaster_lord(lord) && game.flags.bloody === 1) { logcap(AOW_YORK_BLOODY_THOU_ART) disband_lord(lord, true) @@ -10135,7 +6881,166 @@ states.death_or_disband = { card: action_held_event, } -// === ENDING THE BATTLE: VASSAL DISBAND === +// === DEATH CHECK EVENT: ESCAPE SHIP === + +function can_play_escape_ship() { + return can_escape_at(game.battle.where) +} + +function can_escape_at(here) { + if (game.active === YORK && has_favoury_marker(here) && is_seaport(here)) + return true + if (game.active === LANCASTER && has_favourl_marker(here) && is_seaport(here)) + return true + if (search_escape_route(here)) + return true + return false +} + +function search_escape_route(start) { + search_seen.fill(0) + search_seen[start] = 1 + let queue = [start] + + while (queue.length > 0) { + let here = queue.shift() + + // Check if the current locale is a seaport + if (is_seaport(here)) { + return true + } + + if (is_friendly_locale(here)) { + for (let next of data.locales[here].adjacent) { + if (!search_seen[next]) { + search_seen[next] = 1 + queue.push(next) + } + } + } + } + return false +} + +function goto_play_escape_ship() { + push_state("escape_ship") + game.who = NOBODY +} + +states.escape_ship = { + inactive: `Escape ship`, + prompt() { + view.prompt = "Escape Ship: Your lords go to Exile." + view.actions.escape_ship = 1 + }, + escape_ship() { + push_undo() + for (let lord of game.battle.fled) { + log(`${lord_name[lord]} went to exile.`) + exile_lord(lord) + set_delete(game.battle.fled, lord) + } + for (let lord of game.battle.routed) { + log(`${lord_name[lord]} went to exile.`) + exile_lord(lord) + set_delete(game.battle.routed, lord) + } + goto_battle_aftermath() + }, +} + +// === DEATH CHECK EVENT: TALBOT TO THE RESCUE === + +function can_play_talbot_to_the_rescue() { + return true +} + +function goto_play_talbot_to_the_rescue() { + throw "TODO" +} + +// === DEATH CHECK EVENT: WARDEN OF THE MARCHES === + +function can_play_warden_of_the_marches() { + // TOOD : Maybe a bug with blocked ford/exile ? + let can_play = false + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_friendly_locale(loc) && is_north(loc) && loc !== game.battle.where) { + can_play = true + } + } + if (!can_play) { + return false + } + // if blocked ford then flee + if (is_north(game.where)) + return true + // if battle + if (is_north(game.battle.where)) + return true + return false +} + +function goto_play_warden_of_the_marches() { + push_undo() + push_state("warden_of_the_marches") +} + +states.warden_of_the_marches = { + inactive: "Warden of the Marches", + prompt() { + let done = true + view.prompt = "Warden of the Marches: Select a friendly locale in the North to move routed lords there" + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_friendly_locale(loc) && is_north(loc) && loc !== game.battle.where) { + done = false + gen_action_locale(loc) + } + } + if (done) { + view.actions.done = 1 + } + }, + locale(loc) { + push_undo() + for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; lord++) { + if (get_lord_locale(lord) === game.battle.where) { + set_lord_locale(lord, loc) + if (!lord_has_unrouted_troops(lord)) { + disband_lord(lord, false) + } + else { + set_lord_forces(lord, RETINUE, 1) + } + for (let x = 0; x < FORCE_TYPE_COUNT; ++x) { + set_lord_forces(lord, x, 0) + if (get_lord_routed_forces(lord, x) > 0) { + set_lord_routed_forces(lord, x, 0) + } + } + for_each_vassal_with_lord(lord, v => { + if (set_has(game.battle.routed_vassals, v)) { + array_remove(game.battle.routed_vassals, v) + disband_vassal(v) + } + }) + } + } + logi(`Moved to ${data.locales[loc].name}`) + end_warden_of_the_marches() + }, + done() { + end_warden_of_the_marches() + }, +} + +function end_warden_of_the_marches() { + game.flags.warden_of_the_marches = 1 + game.who = NOBODY + pop_state() +} + +// === 4.4.4 ENDING THE BATTLE: AFTERMATH === function goto_battle_aftermath() { set_active(game.battle.attacker) @@ -10164,12 +7069,13 @@ function goto_battle_aftermath() { end_march() } -// === CAMPAIGN: FEED === +// === 4.7 FEED === function can_feed_from_shared(lord) { let loc = get_lord_locale(lord) return get_shared_assets(loc, PROV) > 0 } + function can_pay_from_shared(lord) { let loc = get_lord_locale(lord) return get_shared_assets(loc, COIN) > 0 @@ -10312,739 +7218,22 @@ function end_feed() { goto_remove_markers() } -// === LEVY & CAMPAIGN: PAY === - -function reset_unpaid_lords() { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_unfed(lord)) { - set_lord_unfed(lord, Math.ceil(count_lord_all_forces(lord) / 6)) - } - } -} - -function goto_pay() { - log_br() - let n = 0 - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - let here = get_lord_locale(lord) - if (is_lord_on_map(lord) && - !is_lord_on_calendar(lord) && - lord_has_capability(lord, AOW_LANCASTER_MADAME_LA_GRANDE) && - (((is_friendly_locale(here)) && data.port_2.includes(here)) || - is_adjacent_friendly_port_english_channel(here))) { - add_lord_assets(lord, COIN, 1) - } - if ( - game.active === LANCASTER && - is_lord_on_map(lord) && - lord_has_capability(LORD_NORTHUMBERLAND_L, AOW_LANCASTER_PERCYS_POWER) && - is_lord_in_north(LORD_NORTHUMBERLAND_L) && - is_lord_in_north(lord) - ) { - set_lord_unfed(lord, 0) - } else { - n = Math.ceil(count_lord_all_forces(lord) / 6) - set_lord_unfed(lord, n) - } - } - game.state = "pay" -} - -states.pay = { - inactive: "Pay", - prompt() { - view.prompt = "Pay: You must Pay your Lord's Troops" - let done = true - - // Pay from own mat - if (done) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_unfed(lord)) { - if (get_lord_assets(lord, COIN) > 0) { - gen_action_coin(lord) - done = false - } - } - } - } - - // Sharing - if (done) { - view.prompt = "Pay: You must Pay Lords with Shared Coin." - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_unfed(lord) && can_pay_from_shared(lord)) { - gen_action_lord(lord) - done = false - } - } - } - - if (done) { - view.prompt = "Pay: You must Pillage and/or Disband." - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (is_lord_unfed(lord)) { - view.actions.pillage = 1 - done = false - } - } - } - - // All done! - if (done) { - view.prompt = "Pay: All done." - view.actions.end_pay = 1 - } - }, - coin(lord) { - push_undo() - add_lord_assets(lord, COIN, -1) - pay_lord(lord) - }, - lord(lord) { - push_undo() - game.who = lord - game.state = "pay_lord_shared" - }, - pillage() { - push_undo() - reset_unpaid_lords() - goto_pillage_coin() - }, - end_pay() { - end_pay() - }, - card: action_held_event, -} - -states.pay_lord_shared = { - inactive: "Pay", - prompt() { - view.prompt = `Pay: You must Feed ${lord_name[game.who]} with Shared Coin.` - let loc = get_lord_locale(game.who) - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - if (get_lord_locale(lord) === loc) { - if (get_lord_assets(lord, COIN) > 0) - gen_action_coin(lord) - } - } - }, - coin(lord) { - push_undo() - add_lord_assets(lord, COIN, -1) - pay_lord(game.who) - resume_pay_lord_shared() - }, -} - -function resume_pay_lord_shared() { - if (!is_lord_unfed(game.who) || !can_pay_from_shared(game.who)) { - game.who = NOBODY - game.state = "pay" - } -} - -function end_pay() { - game.who = NOBODY - set_active_enemy() - if (game.active === P2) { - goto_pay() - } else - goto_pay_lords() -} - -function has_friendly_lord_who_must_pay_troops() { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) - if (is_lord_unfed(lord)) - return true - return false -} - -function goto_pay_lords() { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_on_map(lord)) - set_lord_unfed(lord, 1) - } - - if (has_friendly_lord_who_must_pay_troops()) { - game.who = NOBODY - game.state = "pay_lords" - } else { - end_pay_lords() - } -} - -function end_pay_lords() { - set_active_enemy() - - if (game.active === P2) - goto_pay_lords() - else - goto_pay_vassals() -} - -states.pay_lords = { - inactive: "Pay Lords", - prompt() { - view.prompt = "Pay Lords in Influence or Disband them." - prompt_held_event() - let done = true - game.count = 0 - if (game.who === NOBODY) { - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_on_map(lord) && is_lord_unfed(lord)) { - gen_action_lord(lord) - done = false - } - } - if (done) { - view.actions.done = 1 - } - if (!done) - view.actions.pay_all = 1 - } else { - view.actions.disband = 1 - view.actions.pay = 1 - } }, - lord(lord) { - push_undo() - game.who = lord - }, - disband() { - push_undo() - disband_lord(game.who) - game.who = NOBODY - }, - pay() { - push_undo() - reduce_influence(is_exile(get_lord_locale(game.who)) ? 2 : 1) - set_lord_moved(game.who, 0) - game.who = NOBODY - }, - pay_all() { - push_undo() - for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { - if (is_lord_on_map(lord) && is_lord_unfed(lord)) { - ++game.count - set_lord_moved(lord, 0) - if (is_exile(get_lord_locale(lord))) { - ++game.count - } - reduce_influence(game.count) - game.count = 0 - } - } - game.who = NOBODY - - }, - card: action_held_event, - done() { - end_pay_lords() - }, -} - -function goto_pay_vassals() { - let vassal_to_pay = false - - for (let v = first_vassal; v <= last_vassal; v++) { - if ( - is_vassal_mustered_with_friendly_lord(v) && - get_vassal_service(v) === current_turn() - ) { - vassal_to_pay = true - } - } - if (vassal_to_pay) { - game.state = "pay_vassals" - game.what = NOBODY - } else { - end_pay_vassals() - } -} - -function end_pay_vassals() { - set_active_enemy() - - if (game.active === P1) { - goto_muster_exiles() - } else { - goto_pay_vassals() - } -} - -states.pay_vassals = { - inactive: "Pay Vassals", - prompt() { - let done = true - view.prompt = "You may pay or disband vassals in the next calendar box." - if (game.what === NOBODY) { - for (let v = first_vassal; v <= last_vassal; v++) { - if ( - is_vassal_mustered_with_friendly_lord(v) && - get_vassal_service(v) === current_turn() - ) { - gen_action_vassal(v) - done = false - } - } - - if (done) { - view.actions.done = 1 - } - if (!done) - view.actions.pay_all = 1 - } else { - view.actions.pay = 1 - view.actions.disband = 1 - } - }, - vassal(v) { - push_undo() - game.what = v - }, - pay() { - push_undo() - pay_vassal(game.what) - reduce_influence(1) - game.what = NOBODY - }, - pay_all() { - push_undo() - for (let v = first_vassal; v <= last_vassal; v++) { - if (is_vassal_mustered_with_friendly_lord(v) - && get_vassal_service(v) === current_turn()) { - pay_vassal(v) - reduce_influence(1) - game.what = NOBODY - } - } - }, - disband() { - push_undo() - disband_vassal(game.what) - game.what = NOBODY - }, - done() { - end_pay_vassals() - }, -} - -function goto_ready_vassals() { - for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { - if (get_vassal_service(vassal) === current_turn()) { - set_vassal_lord_and_service(vassal, VASSAL_READY, 0) - } - } - - goto_levy_muster() -} - -function goto_muster_exiles() { - for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { - if ((get_lord_locale(x) === current_turn() + CALENDAR && get_lord_in_exile(x)) - || (is_lancaster_lord(x) && is_lord_on_calendar((get_lord_locale(x)) && get_lord_in_exile(x) && is_event_in_play(EVENT_LANCASTER_BE_SENT_FOR)))) { - game.state = "muster_exiles" - return - } - } - end_muster_exiles() -} - -function end_muster_exiles() { - set_active_enemy() - - if (game.active === P1) { - if (!check_disband_victory()) { - goto_ready_vassals() - } - } else { - goto_muster_exiles() - } -} - -states.muster_exiles = { - inactive: "Muster Exiles", - prompt() { - view.prompt = "Muster Exiled Lords." - let done = true - - if (game.who === NOBODY) { - for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { - if ((get_lord_locale(x) === current_turn() + CALENDAR && get_lord_in_exile(x)) - || (is_lancaster_lord(x) && is_lord_on_calendar((get_lord_locale(x)) && get_lord_in_exile(x) && is_event_in_play(EVENT_LANCASTER_BE_SENT_FOR)))) { - gen_action_lord(x) - done = false - } - } - } else { - for (let loc of data.exile_boxes) - if (has_favour_in_locale(game.active, loc)) - gen_action_locale(loc) - } - - if (done) { - view.actions.done = true - } - }, - lord(lord) { - game.who = lord - }, - locale(loc) { - muster_lord_in_exile(game.who, loc) - game.who = NOBODY - }, - done() { - end_muster_exiles() - }, -} - -function muster_lord_in_exile(lord, exile_box) { - remove_lord_from_exile(lord) - muster_lord(lord, exile_box) -} - -// === PILLAGE === - -function goto_pillage_food() { - push_state("pillage") - game.what = PROV -} - -function goto_pillage_coin() { - push_state("pillage") - game.what = COIN -} - -function end_pillage() { - pop_state() -} - -function can_pillage(loc) { - return !is_exile(loc) && !has_exhausted_marker(loc) -} - -states.pillage = { - inactive: "Pillage", - prompt() { - view.prompt = `Pillage: Pillage the locales where your ${game.what === PROV ? "unfed" : "unpaid"} lords are.` - - let done = true - for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { - if (is_lord_on_map(x) && is_lord_unfed(x) && can_pillage(get_lord_locale(x))) { - gen_action_locale(get_lord_locale(x)) - done = false - } - } - - if (done) { - view.prompt = `Pillage: Unable to Pillage, you must disband your ${game.what === PROV ? "unfed" : "unpaid"} lords.` - for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { - if (is_lord_on_map(x) && is_lord_unfed(x)) { - gen_action_lord(x) - done = false - } - } - } - - if (done) { - view.prompt = `Pillage: Done.` - view.actions.done = 1 - } - }, - locale(loc) { - // pillage the Locale - game.where = loc - goto_pillage_locale() - }, - lord(lord) { - disband_influence_penalty(lord) - disband_lord(lord) - }, - done() { - end_pillage() - }, -} - -function goto_pillage_locale() { - push_state("pillage_locale") -} - -function end_pillage_locale() { - pop_state() - game.where = NOWHERE - end_pillage() -} - -states.pillage_locale = { - inactive: "Pillage", - prompt() { - view.prompt = `Pillage: Choose Lord to Pillage ${data.locales[game.where].name}.` - - for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { - if (get_lord_locale(x) === game.where && is_lord_unfed(x)) { - gen_action_lord(x) - } - } - }, - lord(lord) { - // Pillage - // Same values as Taxing. - let num = get_tax_amount(game.where) - add_lord_assets(lord, COIN, num) - add_lord_assets(lord, PROV, num) - reduce_influence(4 * num) - - add_exhausted_marker(game.where) - set_favour_enemy(game.where) - for (let next of data.locales[game.where].adjacent) - shift_favour_away(next) - - end_pillage_locale() - }, -} - -// === LEVY & CAMPAIGN: DISBAND === - -function disband_lord(lord, permanently = false) { - let turn = current_turn() - let extra = 6 - - if (permanently) { - log(`Removed L${lord}.`) - set_lord_locale(lord, NOWHERE) - } else if (lord_has_capability(lord, AOW_YORK_ENGLAND_IS_MY_HOME)) { - set_lord_calendar(lord, turn + (extra - data.lords[lord].influence)) - log(`Disbanded L${lord} to turn ${current_turn() + 1}.`) - } - else { - set_lord_calendar(lord, turn + (extra - data.lords[lord].influence)) - log(`Disbanded L${lord} to turn ${get_lord_calendar(lord)}.`) - } - - discard_lord_capability_n(lord, 0) - discard_lord_capability_n(lord, 1) - - for (let x = 0; x < ASSET_TYPE_COUNT; ++x) - set_lord_assets(lord, x, 0) - - for (let x = 0; x < FORCE_TYPE_COUNT; ++x) { - set_lord_forces(lord, x, 0) - if (get_lord_routed_forces(lord, x) > 0) { - set_lord_routed_forces(lord, x, 0) - } - } - - set_lord_moved(lord, 0) - - for_each_vassal_with_lord(lord, v => { - disband_vassal(v) - }) -} - -// === CAMPAIGN: REMOVE MARKERS === +// === 4.7 FEED: REMOVE MARKERS === function goto_remove_markers() { clear_lords_moved() goto_command_activation() } -// === END CAMPAIGN: GAME END === - -function check_campaign_victory_york(inc_exiles = false) { - for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; ++lord) - if ( - is_lord_on_map(lord) || - (inc_exiles && get_lord_locale(lord) === CALENDAR + current_turn() + 1 && get_lord_in_exile(lord)) - ) - return false - return true -} - -function check_campaign_victory_lancaster(inc_exiles = false) { - for (let lord = first_york_lord; lord <= last_york_lord; ++lord) - if ( - is_lord_on_map(lord) || - (inc_exiles && get_lord_locale(lord) === CALENDAR + current_turn() + 1 && get_lord_in_exile(lord)) - ) - return false - return true -} - -function check_campaign_victory() { - let york_v = check_campaign_victory_york(true) - let lancaster_v = check_campaign_victory_lancaster(true) - - if (york_v && lancaster_v) { - goto_game_over("Draw", "The game ended in a draw.") - return true - } else if (york_v) { - goto_game_over(YORK, `${YORK} won a Campaign Victory!`) - return true - } else if (lancaster_v) { - goto_game_over(LANCASTER, `${LANCASTER} won a Campaign Victory!`) - return true - } - - return false -} - -function check_disband_victory() { - let york_v = check_campaign_victory_york() - let lancaster_v = check_campaign_victory_lancaster() - - if (york_v && lancaster_v) { - goto_game_over("Draw", "The game ended in a draw.") - return true - } else if (york_v) { - goto_game_over(YORK, `${YORK} won a Campaign Victory!`) - return true - } else if (lancaster_v) { - goto_game_over(LANCASTER, `${LANCASTER} won a Campaign Victory!`) - return true - } - - return false -} - -function check_scenario_end_victory() { - if (current_turn() === scenario_last_turn[game.scenario]) { - // Scenario End Victory - - if (game.ip === 0) - goto_game_over("Draw", "The game ended in a draw.") - else if (game.ip > 0) - goto_game_over(LANCASTER, `${LANCASTER} won with ${game.ip} Influence.`) - else - goto_game_over(YORK, `${YORK} won with ${Math.abs(game.ip)} Influence.`) - - return true - } - return false -} - -function check_threshold_victory() { - // This needs to change to account for graduated victory thresholds in some scenarios. - - if (Math.abs(game.ip) > game.victory_check) { - if (game.ip > 0) - goto_game_over(LANCASTER, `${LANCASTER} won with ${game.ip} Influence.`) - else - goto_game_over(YORK, `${YORK} won with ${Math.abs(game.ip)} Influence.`) - - return true - } - - return false -} +// === 4.8 END CAMPAIGN === function goto_end_campaign() { log_h1("End Campaign") set_active(P1) - tides_of_war() -} - -function goto_game_end() { - // GAME END - - if (!(check_scenario_end_victory() || check_campaign_victory() || check_threshold_victory())) { - if (set_has(GROW_TURNS, current_turn())) { - do_grow() - } else if (set_has(WASTE_TURNS, current_turn())) { - do_waste() - } else { - goto_reset() - } - } -} - -function do_grow() { - log("Grow:") - logi("Changing all Depleted locales to Normal.") - logi("Changing all Exhausted locales to Depleted.") - - for (let x = first_locale; x <= last_locale; x++) { - refresh_locale(x) - } - goto_reset() -} - -function do_waste() { - log("Waste:") - logi("Removing half of all lords provinder, carts, and ships.") - logi("Resetting Lords Coin and Troops to initial values.") - for (let x = first_lord; x <= last_lord; x++) { - if (is_lord_on_map(x)) { - do_lord_waste(x) - } - } - - goto_reset() -} - -function do_lord_waste(lord) { - remove_half(lord, PROV) - remove_half(lord, CART) - remove_half(lord, SHIP) - set_lord_assets(lord, COIN, data.lords[lord].assets.coin) - muster_lord_forces(lord) -} - -function remove_half(lord, type) { - set_lord_assets(lord, type, Math.ceil(get_lord_assets(lord, type) / 2)) -} - -// === END CAMPAIGN: RESET (DISCARD ARTS OF WAR) === - -function goto_reset() { - game.state = "reset" - - // Discard "This Campaign" events from play. - discard_friendly_events("this_campaign") -} - -states.reset = { - inactive: "Reset", - prompt() { - view.prompt = "Reset: You may discard any held Arts of War cards desired." - if (game.active === YORK) { - for (let c = first_york_card; c <= last_york_card; ++c) { - if (set_has(game.hand_y, c)) { - gen_action_card(c) - } - } - } - if (game.active === LANCASTER) { - for (let c = first_lancaster_card; c <= last_lancaster_card; ++c) { - if (set_has(game.hand_l, c)) { - gen_action_card(c) - } - } - } - view.actions.end_discard = 1 - }, - card(c) { - push_undo() - if (set_has(game.hand_y, c)) { - log("Discarded Held card.") - set_delete(game.hand_y, c) - } else if (set_has(game.hand_l, c)) { - log("Discarded Held card.") - set_delete(game.hand_l, c) - } - }, - end_discard() { - end_reset() - }, -} - -function end_reset() { - set_active_enemy() - if (game.active === P2) - goto_reset() - else - goto_advance_campaign() + goto_tides_of_war() } -// === END CAMPAIGN: TIDES OF WAR === +// === 4.8.1 END CAMPAIGN: TIDES OF WAR === function tides_calc() { log_h3(`Tides of War`) @@ -11322,7 +7511,7 @@ function tides_calc() { game.influence -= domy } -function tides_of_war() { +function goto_tides_of_war() { if (lord_has_capability(LORD_BUCKINGHAM, AOW_LANCASTER_STAFFORD_ESTATES)) { add_lord_assets(LORD_BUCKINGHAM, COIN, 1) add_lord_assets(LORD_BUCKINGHAM, PROV, 1) @@ -11336,7 +7525,7 @@ function tides_of_war() { goto_disembark() } -// === END CAMPAIGN: DISEMBARK === +// === 4.8.2 END CAMPAIGN: DISEMBARK === function has_lords_at_sea() { for (let x = first_friendly_lord; x <= last_friendly_lord; x++) { @@ -11369,7 +7558,7 @@ function end_disembark() { } else { set_active(P1) - goto_game_end() + goto_victory_check() } } @@ -11469,193 +7658,3867 @@ function goto_advance_campaign() { goto_levy_arts_of_war() } -// === GAME OVER === +// === 4.8.3 END CAMPAIGN: VICTORY CHECK === -function goto_game_over(result, victory) { - game.state = "game_over" - game.active = "None" - game.result = result - game.victory = victory - log_h1("Game Over") - log(game.victory) +function goto_victory_check() { + if (!(check_scenario_end_victory() || check_campaign_victory() || check_threshold_victory())) { + if (set_has(GROW_TURNS, current_turn())) { + do_grow() + } else if (set_has(WASTE_TURNS, current_turn())) { + do_waste() + } else { + goto_reset() + } + } +} + +// === 4.8.4 END CAMPAIGN: GROW === + +function do_grow() { + log("Grow:") + logi("Changing all Depleted locales to Normal.") + logi("Changing all Exhausted locales to Depleted.") + + for (let x = first_locale; x <= last_locale; x++) { + refresh_locale(x) + } + goto_reset() +} + +// === 4.8.5 END CAMPAIGN: WASTE === + +function do_waste() { + log("Waste:") + logi("Removing half of all lords provinder, carts, and ships.") + logi("Resetting Lords Coin and Troops to initial values.") + for (let x = first_lord; x <= last_lord; x++) { + if (is_lord_on_map(x)) { + do_lord_waste(x) + } + } + + goto_reset() +} + +function do_lord_waste(lord) { + remove_half(lord, PROV) + remove_half(lord, CART) + remove_half(lord, SHIP) + set_lord_assets(lord, COIN, data.lords[lord].assets.coin) + muster_lord_forces(lord) +} + +function remove_half(lord, type) { + set_lord_assets(lord, type, Math.ceil(get_lord_assets(lord, type) / 2)) +} + +// === 4.8.6 END CAMPAIGN: RESET (DISCARD ARTS OF WAR) === + +function goto_reset() { + game.state = "reset" + + // Discard "This Campaign" events from play. + discard_friendly_events("this_campaign") +} + +states.reset = { + inactive: "Reset", + prompt() { + view.prompt = "Reset: You may discard any held Arts of War cards desired." + if (game.active === YORK) { + for (let c = first_york_card; c <= last_york_card; ++c) { + if (set_has(game.hand_y, c)) { + gen_action_card(c) + } + } + } + if (game.active === LANCASTER) { + for (let c = first_lancaster_card; c <= last_lancaster_card; ++c) { + if (set_has(game.hand_l, c)) { + gen_action_card(c) + } + } + } + view.actions.end_discard = 1 + }, + card(c) { + push_undo() + if (set_has(game.hand_y, c)) { + log("Discarded Held card.") + set_delete(game.hand_y, c) + } else if (set_has(game.hand_l, c)) { + log("Discarded Held card.") + set_delete(game.hand_l, c) + } + }, + end_discard() { + end_reset() + }, +} + +function end_reset() { + set_active_enemy() + if (game.active === P2) + goto_reset() + else + goto_advance_campaign() +} + +// === 5.1 CAMPAIGN VICTORY === + +function check_campaign_victory_york(inc_exiles = false) { + for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; ++lord) + if ( + is_lord_on_map(lord) || + (inc_exiles && get_lord_locale(lord) === CALENDAR + current_turn() + 1 && get_lord_in_exile(lord)) + ) + return false return true } -states.game_over = { - get inactive() { - return game.victory +function check_campaign_victory_lancaster(inc_exiles = false) { + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) + if ( + is_lord_on_map(lord) || + (inc_exiles && get_lord_locale(lord) === CALENDAR + current_turn() + 1 && get_lord_in_exile(lord)) + ) + return false + return true +} + +function check_campaign_victory() { + let york_v = check_campaign_victory_york(true) + let lancaster_v = check_campaign_victory_lancaster(true) + + if (york_v && lancaster_v) { + goto_game_over("Draw", "The game ended in a draw.") + return true + } else if (york_v) { + goto_game_over(YORK, `${YORK} won a Campaign Victory!`) + return true + } else if (lancaster_v) { + goto_game_over(LANCASTER, `${LANCASTER} won a Campaign Victory!`) + return true + } + + return false +} + +function check_disband_victory() { + let york_v = check_campaign_victory_york() + let lancaster_v = check_campaign_victory_lancaster() + + if (york_v && lancaster_v) { + goto_game_over("Draw", "The game ended in a draw.") + return true + } else if (york_v) { + goto_game_over(YORK, `${YORK} won a Campaign Victory!`) + return true + } else if (lancaster_v) { + goto_game_over(LANCASTER, `${LANCASTER} won a Campaign Victory!`) + return true + } + + return false +} + +// === 5.2 THRESHOLD VICTORY === + +function check_threshold_victory() { + // This needs to change to account for graduated victory thresholds in some scenarios. + + if (Math.abs(game.ip) > game.victory_check) { + if (game.ip > 0) + goto_game_over(LANCASTER, `${LANCASTER} won with ${game.ip} Influence.`) + else + goto_game_over(YORK, `${YORK} won with ${Math.abs(game.ip)} Influence.`) + + return true + } + + return false +} + +// === 5.3 SCENARIO END VICTORY === + +function check_scenario_end_victory() { + if (current_turn() === scenario_last_turn[game.scenario]) { + // Scenario End Victory + + if (game.ip === 0) + goto_game_over("Draw", "The game ended in a draw.") + else if (game.ip > 0) + goto_game_over(LANCASTER, `${LANCASTER} won with ${game.ip} Influence.`) + else + goto_game_over(YORK, `${YORK} won with ${Math.abs(game.ip)} Influence.`) + + return true + } + return false +} + +// === 6.0 SCENARIOS === + +exports.scenarios = [ + "Ia. Henry VI", + "Ib. Towton", + "Ic. Somerset's Return", + "II. Warwicks' Rebellion", + "III. My Kingdom for a Horse", +// TODO "I-III. Wars of the Roses", +] + +const scenario_last_turn = { + "Ia. Henry VI": 15, + "Ib. Towton": 2, + "Ic. Somerset's Return": 8, + "II. Warwicks' Rebellion": 15, + "III. My Kingdom for a Horse": 15, + "I-III. Wars of the Roses": 15, +} + +function is_card_in_scenario(_c) { + // TODO: Cards setup + return true +} + +function muster_lord_forces(lord) { + let info = data.lords[lord] + set_lord_forces(lord, RETINUE, info.forces.retinue | 0) + set_lord_forces(lord, MEN_AT_ARMS, info.forces.men_at_arms | 0) + set_lord_forces(lord, LONGBOWMEN, info.forces.longbowmen | 0) + set_lord_forces(lord, MILITIA, info.forces.militia | 0) +} + +function muster_lord(lord, locale) { + let info = data.lords[lord] + + set_lord_locale(lord, locale) + + set_lord_assets(lord, PROV, info.assets.prov | 0) + set_lord_assets(lord, COIN, info.assets.coin | 0) + + set_lord_assets(lord, CART, info.assets.cart | 0) + set_lord_assets(lord, SHIP, info.ships | 0) + + muster_lord_forces(lord) +} + +exports.setup = function (seed, scenario, options) { + game = { + seed, + scenario, + hidden: options.hidden ? 1 : 0, + + log: [], + undo: [], + + active: null, + rebel: null, + state: "setup_lords", + stack: [], + victory_check: 0, + influence: 0, + + hand_y: [], + hand_l: [], + plan_y: [], + plan_l: [], + + turn: 0, + events: [], // this levy/this campaign cards + + pieces: { + // per lord data + locale: [], + assets: [], + forces: [], + routed: [], + capabilities: [], // TODO map card -> lord instead of lord+slot -> card + moved: [], + in_exile: 0, + + // per vassal data + vassals: Array(vassal_count).fill(VASSAL_OUT_OF_PLAY), + + // per locale data + depleted: [], + exhausted: [], + favourl: [], + favoury: [], + propaganda: [], + }, + + flags: { + first_action: 0, + first_march_highway: 0, + free_levy: 0, + free_parley_henry:0, + free_parley_gloucester:0, + burgundians:0, + charity:0, + bloody:0, + london_for_york:0, + surprise_landing:0, + parliament_votes:0, + succession:0, + jack_cade:0, + commons_militia:0, + swap_battle_attacker:0, + supply_depot:0, + loyalty_and_trust:0, + warden_of_the_marches:0, + naval_blockade:0 + }, + + command: NOBODY, + actions: 0, + group: 0, + intercept_group: 0, + who: NOBODY, + where: NOWHERE, + what: NOTHING, + which: NOTHING, + count: 0, + + supply: 0, + march: 0, + battle: 0, + spoils: 0, + parley: 0, + } + + log_h1(scenario) + + switch (scenario) { + default: + case "Ia. Henry VI": + setup_Ia() + break + case "Ib. Towton": + setup_Ib() + break + case "Ic. Somerset's Return": + setup_Ic() + break + case "II. Warwicks' Rebellion": + setup_II() + break + case "III. My Kingdom for a Horse": + setup_III() + break + case "I-III. Wars of the Roses": + setup_ItoIII() + break + } + + update_aliases() + + goto_setup_lords() + + return game +} + +function setup_Ia() { + game.turn = 1 << 1 + + game.rebel = YORK + game.active = YORK + game.victory_check = 40 + game.influence = 0 + muster_lord(LORD_YORK, LOC_ELY) + muster_lord(LORD_MARCH, LOC_LUDLOW) + muster_lord(LORD_HENRY_VI, LOC_LONDON) + muster_lord(LORD_SOMERSET_1, LOC_LONDON) + + set_lord_calendar(LORD_NORTHUMBERLAND_L, 2) + set_lord_calendar(LORD_EXETER_1, 3) + set_lord_calendar(LORD_BUCKINGHAM, 5) + set_lord_calendar(LORD_SALISBURY, 2) + set_lord_calendar(LORD_WARWICK_Y, 3) + set_lord_calendar(LORD_RUTLAND, 5) + + add_favourl_marker(LOC_LONDON) + add_favourl_marker(LOC_WELLS) + add_favourl_marker(LOC_SCOTLAND) + add_favourl_marker(LOC_FRANCE) + + add_favoury_marker(LOC_ELY) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_BURGUNDY) + add_favoury_marker(LOC_IRELAND) + + setup_vassals() +} + +function setup_Ib() { + game.turn = 1 << 1 + + game.rebel = YORK + game.active = YORK + game.victory_check = 45 + game.influence = 0 + muster_lord(LORD_NORFOLK, LOC_LONDON) + muster_lord(LORD_WARWICK_Y, LOC_LONDON) + muster_lord(LORD_MARCH, LOC_LUDLOW) + muster_lord(LORD_EXETER_1, LOC_NEWCASTLE) + muster_lord(LORD_SOMERSET_1, LOC_NEWCASTLE) + muster_lord(LORD_NORTHUMBERLAND_L, LOC_CARLISLE) + + add_favourl_marker(LOC_ST_ALBANS) + add_favourl_marker(LOC_SCARBOROUGH) + add_favourl_marker(LOC_NEWCASTLE) + add_favourl_marker(LOC_BAMBURGH) + add_favourl_marker(LOC_HEXHAM) + add_favourl_marker(LOC_APPLEBY) + add_favourl_marker(LOC_CARLISLE) + add_favourl_marker(LOC_SCOTLAND) + add_favourl_marker(LOC_FRANCE) + + add_favoury_marker(LOC_LONDON) + add_favoury_marker(LOC_CALAIS) + add_favoury_marker(LOC_GLOUCESTER) + add_favoury_marker(LOC_HEREFORD) + add_favoury_marker(LOC_OXFORD) + add_favoury_marker(LOC_SALISBURY) + add_favoury_marker(LOC_WINCHESTER) + add_favoury_marker(LOC_GUILDFORD) + add_favoury_marker(LOC_ARUNDEL) + add_favoury_marker(LOC_HASTINGS) + add_favoury_marker(LOC_DOVER) + add_favoury_marker(LOC_ROCHESTER) + add_favoury_marker(LOC_CANTERBURY) + add_favoury_marker(LOC_SOUTHAMPTON) + add_favoury_marker(LOC_BURGUNDY) + add_favoury_marker(LOC_IRELAND) + + setup_vassals([ VASSAL_FAUCONBERG, VASSAL_NORFOLK ]) + muster_vassal(VASSAL_FAUCONBERG, LORD_MARCH) +} + +function setup_Ic() { + game.turn = 5 << 1 + + game.rebel = YORK + game.active = YORK + game.victory_check = 40 + game.influence = 6 + muster_lord(LORD_WARWICK_Y, LOC_LONDON) + muster_lord(LORD_MARCH, LOC_LONDON) + muster_lord(LORD_SOMERSET_1, LOC_BAMBURGH) + + set_lord_calendar(LORD_HENRY_VI, 5) + + add_favourl_marker(LOC_SCARBOROUGH) + add_favourl_marker(LOC_NEWCASTLE) + add_favourl_marker(LOC_BAMBURGH) + add_favourl_marker(LOC_HEXHAM) + add_favourl_marker(LOC_APPLEBY) + add_favourl_marker(LOC_CARLISLE) + add_favourl_marker(LOC_HARLECH) + add_favourl_marker(LOC_PEMBROKE) + add_favourl_marker(LOC_CARDIFF) + add_favourl_marker(LOC_CHESTER) + add_favourl_marker(LOC_LANCASTER) + add_favourl_marker(LOC_SCOTLAND) + add_favourl_marker(LOC_FRANCE) + + add_favoury_marker(LOC_LONDON) + add_favoury_marker(LOC_CALAIS) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_HEREFORD) + add_favoury_marker(LOC_SALISBURY) + add_favoury_marker(LOC_WINCHESTER) + add_favoury_marker(LOC_GUILDFORD) + add_favoury_marker(LOC_ARUNDEL) + add_favoury_marker(LOC_HASTINGS) + add_favoury_marker(LOC_DOVER) + add_favoury_marker(LOC_ROCHESTER) + add_favoury_marker(LOC_CANTERBURY) + add_favoury_marker(LOC_SOUTHAMPTON) + add_favoury_marker(LOC_BURGUNDY) + add_favoury_marker(LOC_IRELAND) + + setup_vassals() +} + +function setup_II() { + game.turn = 1 << 1 + + game.rebel = LANCASTER + game.active = LANCASTER + game.victory_check = 40 + game.influence = 0 + muster_lord(LORD_EDWARD_IV, LOC_LONDON) + muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) + muster_lord(LORD_WARWICK_L, LOC_CALAIS) + muster_lord(LORD_CLARENCE, LOC_YORK) + muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) + + set_lord_calendar(LORD_DEVON, 1) + set_lord_calendar(LORD_GLOUCESTER_1, 9) + set_lord_calendar(LORD_NORTHUMBERLAND_Y1, 9) + set_lord_calendar(LORD_MARGARET, 9) + set_lord_in_exile(LORD_MARGARET) + set_lord_calendar(LORD_SOMERSET_2, 9) + set_lord_in_exile(LORD_SOMERSET_2) + set_lord_calendar(LORD_OXFORD, 9) + set_lord_in_exile(LORD_OXFORD) + set_lord_calendar(LORD_EXETER_2, 9) + set_lord_in_exile(LORD_EXETER_2) + + add_favourl_marker(LOC_CALAIS) + add_favourl_marker(LOC_YORK) + add_favourl_marker(LOC_HARLECH) + add_favourl_marker(LOC_COVENTRY) + add_favourl_marker(LOC_WELLS) + add_favourl_marker(LOC_FRANCE) + + add_favoury_marker(LOC_LONDON) + add_favoury_marker(LOC_ELY) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_CARLISLE) + add_favoury_marker(LOC_PEMBROKE) + add_favoury_marker(LOC_EXETER) + add_favoury_marker(LOC_BURGUNDY) + + setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) + + // TODO: Add Foreign Haven rule + // TODO: Add Skaky Allies rules +} + +function setup_III() { + game.turn = 1 << 1 + + game.rebel = LANCASTER + game.active = LANCASTER + game.victory_check = 40 + game.influence = 0 + muster_lord(LORD_RICHARD_III, LOC_LONDON) + muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) + muster_lord(LORD_NORFOLK, LOC_ARUNDEL) + muster_lord(LORD_HENRY_TUDOR, LOC_FRANCE) + muster_lord(LORD_JASPER_TUDOR_2, LOC_FRANCE) + muster_lord(LORD_OXFORD, LOC_FRANCE) + + add_favourl_marker(LOC_FRANCE) + add_favourl_marker(LOC_OXFORD) + add_favourl_marker(LOC_HARLECH) + add_favourl_marker(LOC_PEMBROKE) + + add_favoury_marker(LOC_BURGUNDY) + add_favoury_marker(LOC_LONDON) + add_favoury_marker(LOC_CALAIS) + add_favoury_marker(LOC_CARLISLE) + add_favoury_marker(LOC_ARUNDEL) + add_favoury_marker(LOC_YORK) + add_favoury_marker(LOC_GLOUCESTER) + + setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) +} + +function setup_ItoIII() { + game.turn = 1 << 1 + + game.rebel = YORK + game.active = YORK + game.victory_check = 45 + game.influence = 0 + muster_lord(LORD_YORK, LOC_ELY) + muster_lord(LORD_MARCH, LOC_LUDLOW) + muster_lord(LORD_HENRY_VI, LOC_LONDON) + muster_lord(LORD_SOMERSET_1, LOC_WELLS) + + set_lord_calendar(LORD_NORTHUMBERLAND_L, 1) + set_lord_calendar(LORD_EXETER_1, 3) + set_lord_calendar(LORD_BUCKINGHAM, 5) + set_lord_calendar(LORD_SALISBURY, 2) + set_lord_calendar(LORD_WARWICK_Y, 3) + set_lord_calendar(LORD_RUTLAND, 5) + + add_favourl_marker(LOC_LONDON) + add_favourl_marker(LOC_WELLS) + add_favourl_marker(LOC_SCOTLAND) + add_favourl_marker(LOC_FRANCE) + + add_favoury_marker(LOC_ELY) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_BURGUNDY) + add_favoury_marker(LOC_IRELAND) + + setup_vassals() +} + +// === 6.0 CAMPAIGN === +/* + +function should_remove_Y28_event_card() { + return game.scenario !== "I-III. Wars of the Roses" +} + +function has_Y28_happened() { + //TODO: Scenario IIY and IIL when Y28 happens. + return false +} + +function add_card_scenario(c) { + // TODO: Add card in scenario +} + +function remove_card_scenario(c) { + //TODO: Remove card in scenario +} + +function setup_II_Y() { + game.turn = 1 << 1 + game.scenario = "IIY. The Kingmaker" + game.rebel = LANCASTER + game.active = LANCASTER + game.victory_check = 45 + game.influence = 0 + + for (let lord = first_lord; lord <= last_lord; lord++) { + if (is_lord_in_play(lord)) { + disband_lord(lord, false) + } + } + for (let loc = first_locale; loc <= last_locale; loc++) { + remove_exhausted_marker(loc) + remove_depleted_marker(loc) + remove_favourl_marker(loc) + remove_favoury_marker(loc) + } + discard_events("this_levy") + discard_events("hold") + discard_events("this_campaign") + + // Setup + // Yorkist setup + // TODO: Add cards Y1-Y13, Y25, Y26, Y27, Y29, Y30 + + if (is_lord_in_play(LORD_RUTLAND) && main_york_heir !== LORD_RUTLAND) { + muster_lord(LORD_RUTLAND, LOC_CANTERBURY) + add_favoury_marker(LOC_CANTERBURY) + } + + set_lord_calendar(LORD_DEVON, 1) + set_lord_calendar(LORD_GLOUCESTER_1, 9) + set_lord_calendar(LORD_NORTHUMBERLAND_Y1, 9) + + if (main_york_heir === LORD_YORK) { + muster_lord(LORD_YORK, LOC_CANTERBURY) + add_favoury_marker(LOC_LONDON) + if (is_lord_in_play(LORD_MARCH)) { + muster_lord(LORD_MARCH, LOC_LUDLOW) + } + // TODO: Add cards Y14, Y18, Y19, Y20 + } + + if (main_york_heir === LORD_MARCH) { + muster_lord(LORD_EDWARD_IV, LOC_LONDON) + // Removed because he can't appear in scenario III + disband_lord(LORD_MARCH, true) + // TODO: Add cards Y23, Y24, Y28, Y31 + } + + if (main_york_heir === LORD_RUTLAND) { + muster_lord(LORD_RUTLAND, LOC_LONDON) + // TODO: Add cards Y20, Y21, Y28, Y35 + } + + // If < 2 heirs, muster Pembroke + if ((main_york_heir === LORD_RUTLAND || main_york_heir === LORD_GLOUCESTER_1) + || (main_york_heir === LORD_EDWARD_IV && !is_lord_in_play(LORD_RUTLAND))) { + muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) + } + + // Lancaster setup + // TODO: Add cards L1-L3, L5-L13, L23, L24, L25, L29, L30, L36 + + if (main_lancaster_heir === LORD_HENRY_VI) { + set_lord_calendar(LORD_HENRY_VI, 9) + set_lord_in_exile(LORD_HENRY_VI) + // TODO: Add L17, L18, L20, L21 + } + if (main_lancaster_heir === LORD_MARGARET) { + set_lord_calendar(LORD_MARGARET, 9) + set_lord_in_exile(LORD_MARGARET) + + // TODO: Add L27, L28, L31 + L26 Special rule + } + if (main_lancaster_heir === LORD_SOMERSET_1 || main_lancaster_heir === LORD_SOMERSET_2) { + // TODO: Add cards L20, L21, L27 + } + + if (is_lord_in_play(LORD_SOMERSET_1)) { + set_lord_calendar(LORD_SOMERSET_1, 9) + set_lord_in_exile(LORD_SOMERSET_1) + } + else if (is_lord_in_play(LORD_SOMERSET_2)) { + set_lord_calendar(LORD_SOMERSET_2, 9) + set_lord_in_exile(LORD_SOMERSET_2) + } + + muster_lord(LORD_WARWICK_L, LOC_CALAIS) + muster_lord(LORD_CLARENCE, LOC_YORK) + muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) + set_lord_calendar(LORD_OXFORD, 9) + set_lord_in_exile(LORD_OXFORD) + set_lord_calendar(LORD_EXETER_2, 9) + set_lord_in_exile(LORD_EXETER_2) + + add_favourl_marker(LOC_CALAIS) + add_favourl_marker(LOC_YORK) + add_favourl_marker(LOC_HARLECH) + add_favourl_marker(LOC_COVENTRY) + add_favourl_marker(LOC_WELLS) + + add_favoury_marker(LOC_LONDON) + add_favoury_marker(LOC_ELY) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_CARLISLE) + add_favoury_marker(LOC_PEMBROKE) + add_favoury_marker(LOC_EXETER) + + // Exile box setup + add_favourl_marker(LOC_FRANCE) + add_favoury_marker(LOC_BURGUNDY) + + setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) + + // TODO: Add Foreign Haven rule + // TODO: Add Skaky Allies rules + // TODO: Natural causes rule + +} + +function setup_II_L() { + game.turn = 1 << 1 + game.scenario = "IIL. Lancastrian Legitimacy Fades" + game.rebel = YORK + game.active = YORK + game.victory_check = 40 + game.influence = 0 + + for (let lord = first_lord; lord <= last_lord; lord++) { + if (is_lord_in_play(lord)) { + disband_lord(lord, false) + } + } + for (let loc = first_locale; loc <= last_locale; loc++) { + remove_exhausted_marker(loc) + remove_depleted_marker(loc) + remove_favourl_marker(loc) + remove_favoury_marker(loc) + } + discard_events("this_levy") + discard_events("hold") + discard_events("this_campaign") + + // Setup + // Lancaster setup + // TODO: Add cards L1-L3, L5-L13, L18, L19, L20, L21, L25, L29, L34 + + if (main_lancaster_heir === LORD_HENRY_VI) { + muster_lord(LORD_HENRY_VI, LOC_LONDON) + // TODO: Add L15, L17 + if (is_lord_in_play(LORD_SOMERSET_1)) { + muster_lord(LORD_SOMERSET_1, LOC_WELLS) + } + if (is_lord_in_play(LORD_SOMERSET_2)) { + muster_lord(LORD_SOMERSET_2, LOC_WELLS) + } + } + + if (main_lancaster_heir === LORD_MARGARET) { + set_lord_calendar(LORD_MARGARET, 1) + // TODO: Add L27, L31 + L26 Special rule + if (is_lord_in_play(LORD_SOMERSET_1)) { + muster_lord(LORD_SOMERSET_1, LOC_WELLS) + } + if (is_lord_in_play(LORD_SOMERSET_2)) { + muster_lord(LORD_SOMERSET_2, LOC_WELLS) + } + } + if (main_lancaster_heir === LORD_SOMERSET_1 || main_lancaster_heir === LORD_SOMERSET_2) { + // TODO: Add cards L16, L27 + muster_lord(LORD_SOMERSET_1, LOC_LONDON) + if (main_lancaster_heir === LORD_SOMERSET_2) { + // Somerset 2 cylinder replaced by Somerset 1 cylinder + disband_lord(LORD_SOMERSET_2, true) + } + } + + // Yorkist setup + // TODO: Add cards Y1-Y13, Y15, Y16, Y17, Y22, Y28, Y29, Y31, Y34 + + if (main_york_heir === LORD_YORK) { + set_lord_calendar(LORD_YORK, 7) + set_lord_in_exile(LORD_YORK) + // TODO: Add cards Y14, Y20 + } + + if (main_york_heir === LORD_MARCH) { + set_lord_calendar(LORD_MARCH, 7) + set_lord_in_exile(LORD_MARCH) + // TODO: Add cards Y20, 21 + } + + if (main_york_heir === LORD_RUTLAND) { + set_lord_calendar(LORD_MARCH, 7) + // TODO: Add cards Y20, Y21 + } + + if (main_york_heir === LORD_GLOUCESTER_1) { + // TODO: Add cards Y25, Y30 + } + + if (is_lord_in_play(LORD_MARCH) && main_york_heir !== LORD_MARCH) { + set_lord_calendar(LORD_MARCH, 7) + set_lord_in_exile(LORD_MARCH) + } + + if (is_lord_in_play(LORD_RUTLAND) && main_york_heir !== LORD_RUTLAND) { + set_lord_calendar(LORD_RUTLAND, 7) + set_lord_in_exile(LORD_RUTLAND) + } + if (is_lord_in_play(LORD_GLOUCESTER_1) && main_york_heir !== LORD_GLOUCESTER_1) { + set_lord_calendar(LORD_GLOUCESTER_1, 7) + set_lord_in_exile(LORD_GLOUCESTER_1) + } + + muster_lord(LORD_WARWICK_Y, LOC_CALAIS) + muster_lord(LORD_SALISBURY, LOC_YORK) + muster_lord(LORD_PEMBROKE, LOC_PEMBROKE) + muster_lord(LORD_JASPER_TUDOR_1, LOC_HARLECH) + set_lord_calendar(LORD_DEVON, 1) + set_lord_calendar(LORD_OXFORD, 2) + set_lord_calendar(LORD_EXETER_2, 2) + set_lord_calendar(LORD_NORTHUMBERLAND_L, 8) + + add_favourl_marker(LOC_LONDON) + add_favourl_marker(LOC_HARLECH) + add_favourl_marker(LOC_OXFORD) + add_favourl_marker(LOC_WELLS) + add_favourl_marker(LOC_EXETER) + add_favourl_marker(LOC_CARLISLE) + + add_favoury_marker(LOC_CALAIS) + add_favoury_marker(LOC_YORK) + add_favoury_marker(LOC_ELY) + add_favoury_marker(LOC_LUDLOW) + add_favoury_marker(LOC_PEMBROKE) + + // Exile box setup + add_favourl_marker(LOC_FRANCE) + add_favoury_marker(LOC_BURGUNDY) + + setup_vassals([ VASSAL_DEVON, VASSAL_OXFORD ]) + + // TODO: Add Foreign Haven rule + // TODO: Add Shaky Allies rules + // TODO: Natural causes rule + +} + +function setup_III_Y() { + game.turn = 1 << 1 + game.scenario = "IIIY. New Rivals" + game.rebel = LANCASTER + game.active = LANCASTER + game.victory_check = 45 + game.influence = 0 + + if (!is_lord_in_play(LORD_YORK)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_RUTLAND)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_HENRY_VI)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_SOMERSET_1)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2)) { + game.influence -= 8 + } + + for (let lord = first_lord; lord <= last_lord; lord++) { + if (is_lord_in_play(lord)) { + disband_lord(lord, false) + } + } + for (let loc = first_locale; loc <= last_locale; loc++) { + remove_exhausted_marker(loc) + remove_depleted_marker(loc) + remove_favourl_marker(loc) + remove_favoury_marker(loc) + } + discard_events("this_levy") + discard_events("hold") + discard_events("this_campaign") + + // Yorkist Setup + // TODO: Add Y1-Y13, Y36 + + if (has_Y28_happened()) { + if (is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III))) { + // If Gloucester (any) and Rutland, Rutland dies + disband_lord(LORD_RUTLAND, true) + } + } + + if (main_york_heir === LORD_RUTLAND && (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2))) { + // If Rutland is lone heir, Rutland dies + disband_lord(LORD_RUTLAND, true) + //Warwick becomes king + muster_lord(LORD_WARWICK_Y, LOC_LONDON) + add_favoury_marker(LOC_LONDON) + muster_lord(LORD_SALISBURY, LOC_YORK) + add_favoury_marker(LOC_YORK) + + // TODO: Add Y16, Y17, Y22 + } + + // If only 1 is alive + if (main_york_heir === LORD_YORK && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1)) { + muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) + add_favoury_marker(LOC_CARLISLE) + + // TODO: Add Y37 + } + if ((main_york_heir === LORD_MARCH || main_york_heir === LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1)) { + muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) + add_favoury_marker(LOC_CARLISLE) + // TODO: Add Y37 + } + if (main_york_heir === LORD_GLOUCESTER_1 || main_york_heir === LORD_RICHARD_III) { + muster_lord(LORD_NORTHUMBERLAND_Y2, LOC_CARLISLE) + add_favoury_marker(LOC_CARLISLE) + // TODO: Add Y37 + } + muster_lord(LORD_NORFOLK, LOC_ARUNDEL) + add_favoury_marker(LOC_ARUNDEL) + + if (main_york_heir === LORD_YORK) { + // TODO: Add Y14, Y21 + if (is_lord_in_play(LORD_MARCH)) { + muster_lord(LORD_MARCH, LOC_LUDLOW) + add_favoury_marker(LOC_LUDLOW) + // Add Y20 + // Only 2 heirs can stay + disband_lord(LORD_RUTLAND, true) + disband_lord(LORD_GLOUCESTER_1, true) + } + if (!is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_RUTLAND)) { + muster_lord(LORD_RUTLAND, LOC_CANTERBURY) + add_favoury_marker(LOC_CANTERBURY) + // TODO: Add Y20 + } + if (is_lord_in_play(LORD_GLOUCESTER_1)) { + muster_lord(LORD_GLOUCESTER_1, LOC_GLOUCESTER) + add_favoury_marker(LOC_GLOUCESTER) + // TODO: Y34 + } + } + if (main_york_heir === LORD_MARCH || main_york_heir === LORD_EDWARD_IV) { + muster_lord(LORD_EDWARD_IV, LOC_LONDON) + add_favoury_marker(LOC_LONDON) + + // If Edward IV is on the map, remove March + disband_lord(LORD_MARCH, true) + // TODO: Add Y23, Y24 + if (is_lord_in_play(LORD_RUTLAND)) { + muster_lord(LORD_RUTLAND, LOC_CANTERBURY) + add_favoury_marker(LOC_CANTERBURY) + // TODO: Add Y31 + } + if (is_lord_in_play(LORD_GLOUCESTER_1)) { + muster_lord(LORD_GLOUCESTER_1, LOC_GLOUCESTER) + add_favoury_marker(LOC_GLOUCESTER) + // TODO: Add Y28, Y34 + } + + } + if (main_york_heir === LORD_RUTLAND) { + muster_lord(LORD_RUTLAND, LOC_LONDON) + add_favoury_marker(LOC_LONDON) + // TODO: Add Y20, Y21 + if (is_lord_in_play(LORD_GLOUCESTER_1)) { + muster_lord(LORD_GLOUCESTER_2, LOC_LONDON) + // If Rutland is King, golden gloucester 2 arrives and gloucester 1 is gone + disband_lord(LORD_GLOUCESTER_1, true) + // TODO: Add Y34 + } + } + if (main_york_heir === LORD_GLOUCESTER_1) { + muster_lord(LORD_RICHARD_III, LOC_LONDON) + add_favoury_marker(LOC_LONDON) + // if Richard III is here, both gloucester are gone + disband_lord(LORD_GLOUCESTER_1, true) + disband_lord(LORD_GLOUCESTER_2, true) + // TODO: Add Y32, Y33 + } + + // Lancaster setup + // TODO: Add L1-L13, L34, L35, L36, L37 + + if (main_lancaster_heir === LORD_HENRY_VI || main_lancaster_heir === LORD_MARGARET) { + muster_lord(LORD_MARGARET, LOC_FRANCE) + // TODO: Add L27, L31 + L26 Edward + // Only one heir + disband_lord(LORD_HENRY_VI, true) + disband_lord(LORD_SOMERSET_1, true) + disband_lord(LORD_SOMERSET_2, true) + } + // If Margaret not here and Edward IV not king + if (!is_lord_on_map(LORD_MARGARET) && main_york_heir !== LORD_EDWARD_IV) { + muster_lord(LORD_HENRY_TUDOR, LOC_FRANCE) + // TODO: Add L32, L35 + disband_lord(LORD_SOMERSET_1, true) + disband_lord(LORD_SOMERSET_2, true) + } + if (!is_lord_on_map(LORD_MARGARET) && !is_lord_on_map(LORD_HENRY_TUDOR)) { + muster_lord(LORD_WARWICK_L, LOC_CALAIS) + add_favourl_marker(LOC_CALAIS) + // TODO: Add L23, L30 + } + + if (is_lord_on_map(LORD_MARGARET) || is_lord_on_map(LORD_HENRY_TUDOR)) { + muster_lord(LORD_OXFORD, LOC_FRANCE) + add_favourl_marker(LOC_OXFORD) + muster_lord(LORD_JASPER_TUDOR_2, LOC_FRANCE) + add_favoury_marker(LOC_PEMBROKE) + } + else if (is_lord_on_map(LORD_WARWICK_L)) { + muster_lord(LORD_OXFORD, LOC_CALAIS) + add_favourl_marker(LOC_OXFORD) + muster_lord(LORD_JASPER_TUDOR_2, LOC_CALAIS) + add_favoury_marker(LOC_PEMBROKE) + } + else { + throw Error("Error Lancastrian setup III.Y") + } + + // Exile box setup + add_favourl_marker(LOC_FRANCE) + add_favoury_marker(LOC_BURGUNDY) + + setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) +} + +function setup_III_L() { + game.turn = 1 << 1 + game.scenario = "IIIL. Yorkists Last Stand" + game.rebel = YORK + game.active = YORK + game.victory_check = 45 + game.influence = 0 + + if (!is_lord_in_play(LORD_YORK)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_RUTLAND)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) { + game.influence += 8 + } + if (!is_lord_in_play(LORD_HENRY_VI)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_SOMERSET_1)) { + game.influence -= 8 + } + if (!is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2)) { + game.influence -= 8 + } + + for (let lord = first_lord; lord <= last_lord; lord++) { + if (is_lord_in_play(lord)) { + disband_lord(lord, false) + } + } + for (let loc = first_locale; loc <= last_locale; loc++) { + remove_exhausted_marker(loc) + remove_depleted_marker(loc) + remove_favourl_marker(loc) + remove_favoury_marker(loc) + } + discard_events("this_levy") + discard_events("hold") + discard_events("this_campaign") + + // Lancaster Setup + // TODO: Add L1-L13, L25, L34, L36 + + if (main_lancaster_heir === LORD_HENRY_VI) { + muster_lord(LORD_HENRY_VI, LOC_LONDON) + // TOOD: Add L15, L17 + } + if (main_lancaster_heir === LORD_MARGARET) { + muster_lord(LORD_MARGARET, LOC_LONDON) + // TODO: Add L27, L31 + } + if (main_lancaster_heir === LORD_SOMERSET_1) { + muster_lord(LORD_SOMERSET_1, LOC_LONDON) + add_favourl_marker(LOC_WELLS) + // TODO: Add L18, L20, L27 + } + // Should never happen but as a failsafe + if (main_lancaster_heir === LORD_SOMERSET_2) { + muster_lord(LORD_SOMERSET_1, LOC_LONDON) + add_favourl_marker(LOC_WELLS) + disband_lord(LORD_SOMERSET_2, true) + // TODO: Add L18, L20, L27 + } + muster_lord(LORD_OXFORD, LOC_OXFORD) + muster_lord(LORD_JASPER_TUDOR_2, LOC_PEMBROKE) + add_favourl_marker(LOC_OXFORD) + add_favourl_marker(LOC_PEMBROKE) + add_favourl_marker(LOC_LONDON) + + // York Setup + // TOOD: Add Y1-Y13, Y36 + + if (has_Y28_happened()) { + if (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III)) { + // If Gloucester (any), all other yorkist heir dies + disband_lord(LORD_YORK, true) + disband_lord(LORD_RUTLAND, true) + disband_lord(LORD_MARCH, true) + disband_lord(LORD_EDWARD_IV, true) + disband_lord(LORD_GLOUCESTER_1, true) + disband_lord(LORD_RICHARD_III, true) + muster_lord(LORD_GLOUCESTER_2, LOC_BURGUNDY) + // TODO: Add Y35 + } + } + + if (main_york_heir === LORD_YORK) { + muster_lord(LORD_YORK, LOC_BURGUNDY) + add_favoury_marker(LOC_ELY) + // TODO: Add Y14, Y18 + if (is_lord_in_play(LORD_MARCH)) { + // Only next highest heir alive + disband_lord(LORD_RUTLAND, true) + disband_lord(LORD_GLOUCESTER_1, true) + disband_lord(LORD_GLOUCESTER_2, true) + muster_lord(LORD_MARCH, LOC_BURGUNDY) + add_favoury_marker(LOC_LUDLOW) + //TODO: Add Y20 + } + else if (!is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_RUTLAND)) { + // Only next highest heir alive + disband_lord(LORD_GLOUCESTER_1, true) + disband_lord(LORD_GLOUCESTER_2, true) + muster_lord(LORD_RUTLAND, LOC_BURGUNDY) + add_favoury_marker(LOC_CANTERBURY) + //TODO: Add Y20 + } + else if (!is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2))) { + // Final Scenario, and no succession rule + disband_lord(LORD_GLOUCESTER_2, true) + muster_lord(LORD_GLOUCESTER_1, LOC_BURGUNDY) + add_favoury_marker(LOC_GLOUCESTER) + // TODO: Add Y4 + } + else { + // If York alone + muster_lord(LORD_SALISBURY, LOC_BURGUNDY) + add_favoury_marker(LOC_YORK) + //TODO: Add Y17, Y22 + } + } + if (main_york_heir === LORD_MARCH || main_york_heir === LORD_RUTLAND) { + // If March or Rutland is highest heir, Warwick takes the lead + disband_lord(LORD_MARCH, true) + disband_lord(LORD_RUTLAND, true) + muster_lord(LORD_WARWICK_Y, LOC_CALAIS) + add_favoury_marker(LOC_CALAIS) + //TODO: Add Y16 + } + + if (main_york_heir === LORD_WARWICK_Y) { + muster_lord(LORD_NORFOLK, LOC_CALAIS) + muster_lord(LORD_SALISBURY, LOC_CALAIS) + add_favoury_marker(LOC_CALAIS) + //TODO: Add Y17, Y22 + } + else ( + muster_lord(LORD_NORFOLK, LOC_BURGUNDY) + ) + + if (main_york_heir === LORD_GLOUCESTER_1) { + disband_lord(LORD_GLOUCESTER_1, true) + muster_lord(LORD_GLOUCESTER_2, LOC_BURGUNDY) + muster_lord(LORD_SALISBURY, LOC_BURGUNDY) + //TODO: Add Y17, Y22 + } + + add_favoury_marker(LOC_ARUNDEL) + + // Exile box setup + add_favourl_marker(LOC_FRANCE) + add_favoury_marker(LOC_BURGUNDY) + + setup_vassals([ VASSAL_OXFORD, VASSAL_NORFOLK ]) +} + +// FULL SCENARIO HEIR +function get_main_york_heir() { + if (is_lord_in_play(LORD_YORK)) + return LORD_YORK + if (!is_lord_in_play(LORD_YORK) && is_lord_in_play(LORD_MARCH)) + return LORD_MARCH + if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && is_lord_in_play(LORD_EDWARD_IV)) + return LORD_EDWARD_IV + if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && is_lord_in_play(LORD_RUTLAND)) + return LORD_RUTLAND + if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && (is_lord_in_play(LORD_GLOUCESTER_1) || is_lord_in_play(LORD_GLOUCESTER_2) || is_lord_in_play(LORD_RICHARD_III))) + return LORD_GLOUCESTER_1 + if (!is_lord_in_play(LORD_YORK) && !is_lord_in_play(LORD_MARCH) && !is_lord_in_play(LORD_EDWARD_IV) && !is_lord_in_play(LORD_RUTLAND) && !is_lord_in_play(LORD_GLOUCESTER_1) && !is_lord_in_play(LORD_GLOUCESTER_2) && !is_lord_in_play(LORD_RICHARD_III)) + return LORD_WARWICK_Y +} + +function get_main_lancaster_heir() { + if (is_lord_in_play(LORD_HENRY_VI)) + return LORD_HENRY_VI + if (!is_lord_in_play(LORD_HENRY_VI) && is_lord_in_play(LORD_MARGARET)) + return LORD_MARGARET + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && is_lord_in_play(LORD_SOMERSET_1)) + return LORD_SOMERSET_1 + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && is_lord_in_play(LORD_SOMERSET_2)) + return LORD_SOMERSET_2 + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2) && is_lord_in_play(LORD_HENRY_TUDOR)) + return LORD_HENRY_TUDOR + if (!is_lord_in_play(LORD_HENRY_VI) && !is_lord_in_play(LORD_MARGARET) && !is_lord_in_play(LORD_SOMERSET_1) && !is_lord_in_play(LORD_SOMERSET_2) && !is_lord_in_play(LORD_HENRY_TUDOR) && is_lord_in_play(LORD_WARWICK_L)) + return LORD_WARWICK_L +} + +*/ + +// === CAPABILITY: MUSTER EFFECTS === + +// When a lord levies a capability, its muster vassal applies instantly. +function capability_muster_effects(lord, c) { + if (c === AOW_LANCASTER_MONTAGU) + muster_vassal(VASSAL_MONTAGU, lord) + + if (c === AOW_LANCASTER_MY_FATHERS_BLOOD) + muster_vassal(VASSAL_CLIFFORD, lord) + + if (c === AOW_LANCASTER_ANDREW_TROLLOPE) + muster_vassal(VASSAL_TROLLOPE, lord) + + if (c === AOW_LANCASTER_EDWARD) + muster_vassal(VASSAL_EDWARD, lord) + + if (c === AOW_LANCASTER_THOMAS_STANLEY) { + muster_vassal(VASSAL_THOMAS_STANLEY, lord) + game.flags.free_levy = 1 + } + + if (c === AOW_YORK_HASTINGS) { + add_lord_forces(lord, MEN_AT_ARMS, 2) + muster_vassal(VASSAL_HASTINGS, lord) + } + if (c === AOW_YORK_FAIR_ARBITER && is_friendly_locale(get_lord_locale(LORD_SALISBURY))) { + game.count += 1 + } + if (c === AOW_YORK_FALLEN_BROTHER && !is_lord_in_play(LORD_CLARENCE)) { + game.count += 1 + } + + if (c === AOW_YORK_BURGUNDIANS) { + if (is_seaport(get_lord_locale(lord) && !is_exile(get_lord_locale(lord)))) { + add_lord_forces(lord, BURGUNDIANS, 2) + logi(AOW_YORK_BURGUNDIANS) + game.flags.burgundians = 1 + } + else { + game.flags.burgundians = 0 + } + } +} + +// === CAPABILITY: LORDSHIP EFFECTS === + +// When a lord levies a capability, its +Lordship effects apply instantly. +function lordship_effects(lord) { + if (is_friendly_locale(get_lord_locale(lord)) && lord_has_capability(lord, AOW_YORK_FAIR_ARBITER)) + game.count += 1 + if (lord_has_capability(lord, AOW_YORK_FALLEN_BROTHER) && !is_lord_in_play(LORD_CLARENCE)) + game.count += 1 + if (is_event_in_play(EVENT_YORK_EDWARD_V) && (lord === LORD_GLOUCESTER_1 || lord === LORD_GLOUCESTER_2)) + game.count += 3 + if (is_lancaster_lord(lord) && is_event_in_play(EVENT_LANCASTER_PARLIAMENT_VOTES)) { + game.flags.parliament_votes = 1 + } + if (is_york_lord(lord) && is_jack_cade_eligible(lord)) { + game.flags.jack_cade = 2 + } + if (is_york_lord(lord) && is_event_in_play(EVENT_YORK_SUCCESSION)) { + game.flags.succession = 1 + } +} + +// === CAPABILITY: SOLDIERS OF FORTUNE === + +states.soldier_of_fortune = { + inactive: "Levy Troops", + prompt() { + view.prompt = `Pay 1 Coin for Mercenaries ${lord_name[game.who]}.` + let done = true + if (done) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_unfed(lord) && can_pay_from_shared(lord)) { + if (get_lord_assets(lord, COIN) > 0) { + gen_action_coin(lord) + done = false + } + } + } + } + // Done + if (done) { + view.prompt = "Soldiers of fortune: Done." + view.actions.end_sof = 1 + } }, + coin(lord) { + push_undo() + let here = get_lord_locale(game.who) + let here_type = data.locales[here].type + let number = get_lord_forces(game.who, MERCENARIES) + let merc = 0 + if (!lord_has_capability(game.who, AOW_YORK_WOODWILLES)) + deplete_locale(here) + + switch (here_type) { + case "calais": + add_lord_forces(game.who, MEN_AT_ARMS, 2) + add_lord_forces(game.who, LONGBOWMEN, 1) + break + case "london": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, LONGBOWMEN, 1) + add_lord_forces(game.who, MILITIA, 1) + break + case "harlech": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, LONGBOWMEN, 2) + break + case "city": + add_lord_forces(game.who, LONGBOWMEN, 1) + add_lord_forces(game.who, MILITIA, 1) + break + case "town": + add_lord_forces(game.who, MILITIA, 2) + break + case "fortress": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, MILITIA, 1) + break + } + if (game.flags.free_levy === 1) { + ++game.count + game.flags.free_levy = 0 + } + if (number === 5) + merc = 1 + else + merc = 2 + add_lord_assets(lord, COIN, -1) + add_lord_forces(game.who, MERCENARIES, merc) + set_lord_unfed(game.who, 0) + }, + end_sof() { + end_soldiers_of_fortune() + }, + card: action_held_event, +} + +function end_soldiers_of_fortune() { + pop_state() + resume_levy_muster_lord() +} + +// === CAPABILITY: COMMISSION OF ARRAY === + +states.commission_of_array = { + inactive: "Levy Troops", prompt() { - view.prompt = game.victory + view.prompt = `Lord troops adjacent to ${lord_name[game.who]}.` + let done = true + let here = get_lord_locale(game.who) + if (done) { + for (let next of data.locales[here].adjacent) { + if (is_friendly_locale(next) && lord_has_capability(game.who, AOW_LANCASTER_COMMISION_OF_ARRAY) && (!has_exhausted_marker(next) && !is_exile(next))) { + done = false + gen_action_locale(next) + } + } + } + // Done + if (done) { + view.prompt = "Commission of Array: Done." + view.actions.end_coa = 1 + } }, + locale(loc) { + push_undo() + let loc_type = data.locales[loc].type + deplete_locale(loc) + + switch (loc_type) { + case "calais": + add_lord_forces(game.who, MEN_AT_ARMS, 2) + add_lord_forces(game.who, LONGBOWMEN, 1) + break + case "london": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, LONGBOWMEN, 1) + add_lord_forces(game.who, MILITIA, 1) + break + case "harlech": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, LONGBOWMEN, 2) + break + case "city": + add_lord_forces(game.who, LONGBOWMEN, 1) + add_lord_forces(game.who, MILITIA, 1) + break + case "town": + add_lord_forces(game.who, MILITIA, 2) + break + case "fortress": + add_lord_forces(game.who, MEN_AT_ARMS, 1) + add_lord_forces(game.who, MILITIA, 1) + break + } + if (game.flags.free_levy === 1) { + ++game.count + game.flags.free_levy = 0 + } + end_commission_of_array() + }, + card: action_held_event, } -// === UNCOMMON TEMPLATE === +function end_commission_of_array() { + pop_state() + resume_levy_muster_lord() +} -function log_br() { - if (game.log.length > 0 && game.log[game.log.length - 1] !== "") - game.log.push("") +// === CAPABILITY: WE DONE DEEDS OF CHARITY === + +function tow_extra_ip() { + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (lord_has_capability(lord, AOW_YORK_WE_DONE_DEEDS_OF_CHARITY) && (get_lord_assets(lord, PROV) > 0 || get_shared_assets(lord, PROV) > 0)) + return true + } + return false } -function log(msg) { - game.log.push(msg) +states.tow_extra_ip = { + inactive: "We done needs of charity", + prompt() { + let loc = 0 + view.prompt = "We done deeds of charity: spend up to two Provender to add influence points." + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (lord_has_capability(lord, AOW_YORK_WE_DONE_DEEDS_OF_CHARITY)) { + loc = get_lord_locale(lord) + } + } + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (game.flags.charity < 2 && get_lord_locale(lord) === loc && (get_lord_assets(lord, PROV) > 0)) { + gen_action_prov(lord) + } + } + view.actions.done = 1 + }, + prov(lord) { + push_undo() + add_lord_assets(lord, PROV, -1) + game.flags.charity += 1 + }, + done() { + increase_york_influence(game.flags.charity) + logi(`${AOW_YORK_WE_DONE_DEEDS_OF_CHARITY}`) + log("York paid " + game.flags.charity + " provender to add " + game.flags.charity + " Influence Points") + game.flags.charity = 0 + goto_disembark() + }, } -function logevent(cap) { - game.log.push(`E${cap}.`) +// === CAPABILITY: MERCHANTS === + +function can_action_merchants() { + let loc = get_lord_locale(game.command) + if (game.actions <= 0) + return false + + if (lord_has_capability(game.command, AOW_LANCASTER_MERCHANTS) && count_deplete(loc) > 0) + return true + else + return false } -function logcap(cap) { - game.log.push(`C${cap}.`) +function goto_merchants() { + game.count = count_deplete(get_lord_locale(game.command)) + game.state = "merchants" + init_influence_check(game.command) } -function logi(msg) { - game.log.push(">" + msg) +states.merchants = { + inactive: "Merchants", + prompt() { + view.prompt = "Merchants: Succeed an influence check to remove Depleted markers" + prompt_influence_check() + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + log(`Attempt to C${AOW_LANCASTER_MERCHANTS} with %${game.command} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + if (results.success) { + merchants_success() + } else { + end_merchants_attempt() + } + } +} +function merchants_success() { + game.state = "merchants_success" } -function log_h1(msg) { - log_br() - log(".h1 " + msg) - log_br() +states.merchants_success = { + inactive: "Merchants Success", + prompt() { + view.prompt = `Remove Depleted/Exhausted markers` + deplete_merchants() + if (game.count === 0) { + view.actions.done = 1 + } + }, + locale(loc) { + push_undo() + remove_depleted_marker(loc) + remove_exhausted_marker(loc) + --game.count + if (game.count === 0) { + end_merchants_attempt() + } + }, + done(){ + end_merchants_attempt() + } } -function log_h2(msg) { - log_br() - if (game.active === YORK) - log(".h2t " + msg) +function end_merchants_attempt() { + spend_action(1) + game.count = 0 + push_undo() + end_influence_check() + resume_command() +} + +function deplete_merchants() { + let here = get_lord_locale(game.command) + for (let next of data.locales[here].adjacent) { + if (has_exhausted_marker(next) || has_depleted_marker(next)) + gen_action_locale(next) + } + if (has_exhausted_marker(here) || has_depleted_marker(here)) + gen_action_locale(here) +} + +function count_deplete(loc) { + game.count = 0 + for (let next of data.locales[loc].adjacent) { + if (has_exhausted_marker(next) || has_depleted_marker(next)) { + ++game.count + } + } + if (has_exhausted_marker(loc) || has_depleted_marker(loc)) { + ++game.count + } + if (game.count > 1) + return game.count = 2 else - log(".h2r " + msg) - log_br() + return game.count } -function log_h3(msg) { - log_br() - if (game.active === YORK) - log(".h3t " + msg) +// === CAPABILITY: BURGUNDIANS === + +function levy_burgundians(lord) { + if (is_seaport(get_lord_locale(lord)) && !is_exile(get_lord_locale(lord)) && lord_has_capability(lord, AOW_YORK_BURGUNDIANS) && game.flags.burgundians === 0) { + add_lord_forces(lord, BURGUNDIANS, 2) + logi(AOW_YORK_BURGUNDIANS) + game.flags.burgundians = 1 + } +} + +// === CAPABILITY: NAVAL BLOCKADE === + +function parley_through_sea(start, locale) { + // Only entered in levy + let ships = get_shared_assets(start, SHIP) + + if (ships === 0) { + game.flags.naval_blockade = -1 + } + + search_dist.fill(0) + search_seen.fill(0) + search_seen[start] = 1 + + let queue = [ start ] + while (queue.length > 0) { + let here = queue.shift() + let dist = search_dist[here] + let next_dist = dist + 1 + + if (is_friendly_locale(here)) { + for (let next of data.locales[here].adjacent) { + if (!search_seen[next]) { + search_seen[next] = 1 + search_dist[next] = next_dist + queue.push(next) + if (next === locale) { + game.flags.naval_blockade = -1 + } + } + } + } + } + queue = [ start ] + while (queue.length > 0) { + let here = queue.shift() + let dist = search_dist[here] + let next_dist = dist + 1 + + if (is_friendly_locale(here)) { + if (ships > 0 && is_seaport(here)) { + for (let next of find_ports(here)) { + if (!search_seen[next]) { + search_seen[next] = 1 + search_dist[next] = next_dist + queue.push(next) + if (next === locale && game.flags.naval_blockade !== -1) { + game.flags.naval_blockade = 1 + } + } + } + } + } + } +} + +function check_naval_blockade(action, locale) { + let ports = [data.port_1, data.port_2, data.port_3] + game.what = action + + if (!lord_has_capability(LORD_WARWICK_Y, AOW_YORK_NAVAL_BLOCKADE) || !is_seaport(get_lord_locale(LORD_WARWICK_Y)) || is_exile(get_lord_locale(LORD_WARWICK_Y))) { + return false + } + + if (action === "levy parley") { + parley_through_sea(get_lord_locale(game.who), locale) + if (game.flags.naval_blockade !== 1) { + return false + } + } + + if (action === "campaign parley" && data.locales[locale].adjacent.includes(get_lord_locale(game.command))) { + return false + } + + for (let port of ports) { + if (set_has(port, get_lord_locale(LORD_WARWICK_Y)) && set_has(port, locale)) { + return true + } + } +} + +function roll_naval_blockade() { + push_state("naval_blockade") +} + +// Parley, and Tax +states.naval_blockade = { + inactive: "Naval Blockade", + prompt() { + view.prompt = `Naval Blockade : Warwick block this action except on a 1-2` + view.actions.roll = 1 + }, + roll() { + let threshold = 2 + let roll = roll_die() + let success = threshold >= roll + log(`Attempt to counter Naval Blockade ${success ? "Failed" : "Successful"}: (1-2) ${success ? HIT[roll] : MISS[roll]}`) + if (success) { + logi(`Successfully overran C${AOW_YORK_NAVAL_BLOCKADE}`) + if (game.what === "levy parley") { + game.flags.naval_blockade = -1 + } + if (game.what === "campaign parley") { + game.flags.naval_blockade = -1 + } + if (game.what === "levy ship") { + add_lord_assets(game.who, SHIP, 1) + } + if (game.what === "supply") { + use_port_supply(game.where, get_port_supply_amount(game.where)) + } + if (game.what === "sail") { + log(`Sailed to %${game.where}${format_group_move()}.`) + for (let lord of game.group) { + set_lord_locale(lord, game.where) + set_lord_moved(lord, 1) + } + if (is_seamanship_in_play()) + spend_action(1) + else + spend_all_actions() + + game.flags.surprise_landing = 1 + resume_command() + } + } + else { + logi(`Failed C${AOW_YORK_NAVAL_BLOCKADE}`) + if (game.what === "levy parley") { + pop_state() + } + if (game.what === "campaign parley") { + pop_state() + } + } + if (game.what === "levy parley") { + pop_state() + resume_levy_muster_lord() + } + if (game.what === "campaign parley") { + pop_state() + --game.count + resume_command() + } + if (game.what === "levy ship") { + pop_state() + resume_levy_muster_lord() + } + if (game.what === "supply" && !success) { + end_supply() + } + if (game.what === "sail" && !success) { + resume_command() + } + game.what = NOTHING + }, +} + +// === CAPABILITY: AGITATORS === + +function can_action_agitators() { + let here = get_lord_locale(game.command) + if (game.actions <= 0) + return false + if (lord_has_capability(game.command, AOW_YORK_AGITATORS)) { + for (let next of data.locales[here].adjacent) { + if (!has_exhausted_marker(next) && !is_friendly_locale(next)) + return true + } + } else - log(".h3r " + msg) - log_br() + return false } -function log_h4(msg) { - log_br() - log(".h4 " + msg) +function goto_agitators() { + game.count = count_deplete(get_lord_locale(game.command)) + game.state = "agitators" } -function log_h5(msg) { - log_br() - log(".h5 " + msg) +states.agitators = { + inactive: "Agitators", + prompt() { + view.prompt = "Agitators: Add a depleted marker or flip to exhausted adjacent" + deplete_agitators() + }, + locale(loc) { + push_undo() + if (has_depleted_marker(loc)) { + add_exhausted_marker(loc) + } + else { + add_depleted_marker(loc) + } + end_agitators() + }, } -function gen_action(action, argument) { - if (!(action in view.actions)) - view.actions[action] = [] - set_add(view.actions[action], argument) +function deplete_agitators() { + let here = get_lord_locale(game.command) + for (let next of data.locales[here].adjacent) { + if (!has_exhausted_marker(next) && !is_friendly_locale(next)) + gen_action_locale(next) + } } -function gen_action_card_if_held(c) { - if (has_card_in_hand(c)) - gen_action_card(c) +function end_agitators() { + spend_action(1) + push_undo() + resume_command() } -function prompt_select_lord(lord) { - if (lord !== game.who) - gen_action_lord(lord) +// === CAPABILITY: HERALDS === + +function can_action_heralds() { + if (game.actions <= 0) + return false + + if (!is_first_action()) + return false + // at a seaport + let here = get_lord_locale(game.command) + if (!is_seaport(here)) + return false + + if (!lord_has_capability(game.command, AOW_LANCASTER_HERALDS)) + return false + + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_on_calendar(lord)) + return true + } + return false } -function action_select_lord(lord) { - if (game.who === lord) +function goto_heralds() { + game.state = "heralds" +} + +states.heralds = { + inactive: "Heralds", + prompt() { + view.prompt = "Heralds: Choose a Lord on calendar to shift him to next turn box" + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (is_lord_on_calendar(lord)) + gen_action_lord(lord) + } + }, + lord(other) { + goto_heralds_attempt(other) + }, +} + +function goto_heralds_attempt(lord) { + game.what = lord + push_state("heralds_attempt") + init_influence_check(game.command) +} + +states.heralds_attempt = { + inactive: "Heralds Attempt", + prompt() { + view.prompt = `Attempt to shift ${lord_name[game.what]} to next Turn Box. ` + prompt_influence_check() + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + log(`Attempt to shift L${game.what} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (results.success) { + game.who = game.what + set_lord_calendar(game.who, current_turn() + 1) + end_heralds_attempt() + } else { + end_heralds_attempt() + } + }, +} + +function end_heralds_attempt() { + spend_all_actions() + pop_state() + end_influence_check() + resume_command() +} + +// === EVENTS: IMMEDIATE === + +function goto_immediate_event(c) { + switch (c) { + // This Levy / Campaign + case EVENT_LANCASTER_BE_SENT_FOR: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_SEAMANSHIP: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_FORCED_MARCHES: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_RISING_WAGES: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_NEW_ACT_OF_PARLIAMENT: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_MY_CROWN_IS_IN_MY_HEART: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_PARLIAMENT_VOTES: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_FRENCH_FLEET: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_BUCKINGHAMS_PLOT: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_MARGARET_BEAUFORT: + set_add(game.events, c) + return end_immediate_event() + case EVENT_LANCASTER_THE_EARL_OF_RICHMOND: + set_add(game.events, c) + return end_immediate_event() + + case EVENT_YORK_JACK_CADE: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_SEAMANSHIP: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_YORKISTS_BLOCK_PARLIAMENT: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_EXILE_PACT: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_RICHARD_OF_YORK: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_THE_COMMONS: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_SUCCESSION: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_LOYALTY_AND_TRUST: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_OWAIN_GLYNDWR: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_GLOUCESTER_AS_HEIR: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_DORSET: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_THE_KINGS_NAME: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_EDWARD_V: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_AN_HONEST_TALE_SPEEDS_BEST: + set_add(game.events, c) + return end_immediate_event() + case EVENT_YORK_PRIVY_COUNCIL: + set_add(game.events, c) + return end_immediate_event() + + // Immediate effect + // Discard - Immediate Events + case EVENT_LANCASTER_SCOTS: + return goto_lancaster_event_scots() + case EVENT_LANCASTER_HENRY_PRESSURES_PARLIAMENT: + return goto_lancaster_event_henry_pressures_parliament() + case EVENT_LANCASTER_HENRYS_PROCLAMATION: + return goto_lancaster_event_henrys_proclamation() + case EVENT_LANCASTER_FRENCH_TROOPS: + return goto_lancaster_event_french_troops() + case EVENT_LANCASTER_WARWICKS_PROPAGANDA: + return goto_warwicks_propaganda() + case EVENT_LANCASTER_WARWICKS_PROPAGANDA2: + return goto_warwicks_propaganda() + case EVENT_LANCASTER_WELSH_REBELLION: + return goto_lancaster_event_welsh_rebellion() + case EVENT_LANCASTER_HENRY_RELEASED: + return goto_lancaster_event_henry_released() + case EVENT_LANCASTER_LUNIVERSELLE_ARAGNE: + return goto_lancaster_event_luniverselle_aragne() + case EVENT_LANCASTER_TO_WILFUL_DISOBEDIANCE: + return goto_lancaster_event_to_wilful_disobediance() + case EVENT_LANCASTER_FRENCH_WAR_LOANS: + return goto_lancaster_event_french_war_loans() + case EVENT_LANCASTER_ROBINS_REBELLION: + return goto_lancaster_event_robins_rebellion() + case EVENT_LANCASTER_TUDOR_BANNERS: + return goto_lancaster_event_tudor_banners() + case EVENT_YORK_TAX_COLLECTORS: + return goto_york_event_tax_collectors() + case EVENT_YORK_LONDON_FOR_YORK: + return goto_york_event_london_for_york() + case EVENT_YORK_SHEWOLF_OF_FRANCE: + return goto_york_event_shewolf_of_france() + case EVENT_YORK_SIR_RICHARD_LEIGH: + return goto_york_event_sir_richard_leigh() + case EVENT_YORK_CHARLES_THE_BOLD: + return goto_york_event_charles_the_bold() + case EVENT_YORK_DUBIOUS_CLARENCE: + return goto_dubious_clarence() + case EVENT_YORK_YORKIST_NORTH: + return goto_york_event_yorkist_north() + case EVENT_YORK_EARL_RIVERS: + return goto_york_event_earl_rivers() + default: + log("NOT IMPLEMENTED") + return end_immediate_event() + } +} + +function end_immediate_event() { + resume_levy_arts_of_war() +} + +// === EVENT: JACK CADE === + +function is_york_dominating_north() { + let dom = 0 + for (let loc of all_north_locales) { + if (has_favoury_marker(loc)) { + dom++ + } + } + if (dom > 5) + return true + return false +} + +function is_york_dominating_south() { + let dom = 0 + for (let loc of all_south_locales) { + if (has_favoury_marker(loc)) { + dom++ + } + } + if (dom > 9) + return true + if (dom > 4 + && (lord_has_capability(LORD_MARCH, AOW_YORK_SOUTHERNERS) + || lord_has_capability(LORD_RUTLAND, AOW_YORK_SOUTHERNERS) + || lord_has_capability(LORD_YORK, AOW_YORK_SOUTHERNERS))) + return true + return false +} + +function is_york_dominating_wales() { + let dom = 0 + for (let loc of all_wales_locales) { + if (has_favoury_marker(loc)) { + dom++ + } + } + if (dom > 5) + return true + if (dom > 2 + && (lord_has_capability(LORD_MARCH, AOW_YORK_WELSHMEN) + || lord_has_capability(LORD_YORK, AOW_YORK_WELSHMEN))) + return true + return false +} + +function is_jack_cade_eligible(lord) { + if (!is_event_in_play(EVENT_YORK_JACK_CADE)) + return false + if (is_lord_in_or_adjacent_to_south(lord) && is_york_dominating_south()) + return true + if (is_lord_in_or_adjacent_to_north(lord) && is_york_dominating_north()) + return true + if (is_lord_in_or_adjacent_to_wales(lord) && is_york_dominating_wales()) + return true + return false +} + +// === EVENT: LANCASTER SCOTS === + +function goto_lancaster_event_scots() { + game.state = "scots" + game.count = [] + game.who = NOBODY +} + +function end_lancaster_event_scots() { + game.count = 0 + game.who = NOBODY + end_immediate_event() +} + +states.scots = { + inactive: "Scots", + prompt() { + view.prompt = "Scots: You may add 1 Men-at-Arms and 1 Militia to each Lord." + for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; lord++) { + if (is_lord_on_map(lord) && map_get(game.count, lord, 0) < 3) { + gen_action_lord(lord) + } + } + + if (game.who !== NOBODY) { + let troops = map_get(game.count, game.who, 0) + if ((troops & 1) === 0) + view.actions.add_militia = 1 + if ((troops & 2) === 0) + view.actions.add_men_at_arms = 1 + } + view.actions.done = 1 + }, + done() { + end_lancaster_event_scots() + }, + add_militia() { + add_lord_forces(game.who, MILITIA, 1) + let troops = map_get(game.count, game.who, 0) + map_set(game.count, game.who, troops + 1) + if (troops !== 0) + game.who = NOBODY + }, + add_men_at_arms() { + add_lord_forces(game.who, MEN_AT_ARMS, 1) + let troops = map_get(game.count, game.who, 0) + map_set(game.count, game.who, troops + 2) + if (troops !== 0) + game.who = NOBODY + }, + lord(lord) { + push_undo() + game.who = lord + }, +} + +// === EVENT: LANCASTER HENRY PRESSURES PARLIAMENT === + +function goto_lancaster_event_henry_pressures_parliament() { + let count = 0 + for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { + if (is_vassal_mustered_with_york_lord(vassal)) { + count++ + } + } + + if (count > 0) { + logi(`Removed ${count} York influence.`) + reduce_york_influence(count) + } + + end_immediate_event() +} + +// === EVENT: LANCASTER HENRY'S PROCLAMATION === + +function goto_lancaster_event_henrys_proclamation() { + for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { + if (is_vassal_mustered_with_york_lord(vassal)) { + set_vassal_lord_and_service(vassal, get_vassal_lord(vassal), current_turn()) + logi(`Vassal ${data.vassals[vassal].name} moved to current turn`) + + } + } + end_immediate_event() +} + +// === EVENT: LANCASTER FRENCH TROOPS === + +function goto_lancaster_event_french_troops() { + let can_play = false + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_on_map(lord) && data.seaports.includes(get_lord_locale(lord))) { + can_play = true + } + } + if (can_play) { + game.state = "french_troops" game.who = NOBODY + game.count = 0 + } else { + end_immediate_event() + } +} + +function end_lancaster_event_french_troops() { + game.who = NOBODY + game.count = 0 + end_immediate_event() +} + +states.french_troops = { + inactive: "French Troops", + prompt() { + + view.prompt = `Add 2 Men at Arms and 2 Militia to a Lord at a port.` + if (game.who === NOBODY) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; lord++) { + if (is_lord_on_map(lord) && is_seaport(get_lord_locale(lord))) { + gen_action_lord(lord) + } + } + } else { + view.prompt = `Add ${2-pack2_get(game.count, 0)} Men at Arms and ${2-pack2_get(game.count, 1)} Militia to ${lord_name[game.who]}.` + if (pack2_get(game.count, 0) < 2) + view.actions.add_men_at_arms = 1 + if (pack2_get(game.count, 1) < 2) + view.actions.add_militia = 1 + } + + view.actions.done = 1 + }, + add_men_at_arms() { + push_undo() + add_lord_forces(game.who, MEN_AT_ARMS, 1) + let c = pack2_get(game.count, 0) + game.count = pack2_set(game.count, 0, c+1) + }, + add_militia() { + push_undo() + add_lord_forces(game.who, MILITIA, 1) + let c = pack2_get(game.count, 1) + game.count = pack2_set(game.count, 1, c+1) + }, + lord(lord) { + push_undo() + game.who = lord + }, + done() { + end_lancaster_event_french_troops() + } +} + +// === EVENT: WARWICKS PROPAGANDA === + +function add_propaganda_target(loc) { + set_add(game.pieces.propaganda, loc) +} + +function remove_propaganda_target(loc) { + set_delete(game.pieces.propaganda, loc) +} + +function is_propaganda_target(loc) { + return set_has(game.pieces.propaganda, loc) +} + +function goto_warwicks_propaganda() { + let can_play = false + for (let loc = first_locale; loc <= last_locale; ++loc) { + if (has_favoury_marker(loc)) { + can_play = true + } + } + + if (can_play) { + game.state = "warwicks_propaganda" + game.who = NOBODY + game.count = 0 + } else { + end_immediate_event() + } +} + +states.warwicks_propaganda = { + inactive: "Warwick's Propaganda", + prompt() { + view.prompt = `Select up to ${3-game.count} Yorkists Locales.` + for (let loc = first_locale; loc <= last_locale; loc++) { + if (game.count < 3 && has_favoury_marker(loc) && !is_exile(loc) && !is_propaganda_target(loc)) { + gen_action_locale(loc) + } + } + view.actions.done = 1 + }, + locale(loc) { + push_undo() + add_propaganda_target(loc) + game.count++ + }, + done() { + goto_yorkist_choice() + } +} + +function goto_yorkist_choice() { + game.who = NOBODY + set_active_enemy() + game.state = "warwicks_propaganda_yorkist_choice" +} + +states.warwicks_propaganda_yorkist_choice = { + inactive: "Yorkists to choose to Pay or Remove favour", + prompt() { + view.prompt = `For each Stronghold, Pay 2 influence or Remove favour.` + let done = true + if (game.who === NOBODY) { + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_propaganda_target(loc)) { + gen_action_locale(loc) + done = false + } + } + if (done) { + view.actions.done = 1 + } + } else { + view.actions.remove_favour = 1 + view.actions.pay = 1 + } + }, + locale(loc) { + game.who = loc + }, + remove_favour() { + push_undo() + remove_favoury_marker(game.who) + remove_propaganda_target(game.who) + logi(`Removed favour in ${game.who}`) + game.who = NOBODY + }, + pay() { + push_undo() + reduce_influence(2) + logi(`Paid 2 to keep ${game.who}`) + remove_propaganda_target(game.who) + game.who = NOBODY + }, + done() { + end_warwicks_propaganda() + }, +} + +function end_warwicks_propaganda() { + game.who = NOBODY + game.count = 0 + set_active_enemy() + end_immediate_event() +} + +// === EVENT: WELSH REBELLION === + +function goto_lancaster_event_welsh_rebellion() { + let can_play_remove_troops = false + let can_play_remove_favour = false + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (is_lord_on_map(lord) && is_lord_in_wales(lord)) { + set_lord_moved(lord, 1) + can_play_remove_troops = true + } + } + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_wales(loc) && has_favoury_marker(loc)) + can_play_remove_favour = true + } + + if (can_play_remove_troops) { + push_state("welsh_rebellion_remove_troops") + game.who = NOBODY + game.count = 0 + } + else if (can_play_remove_favour) { + push_state("welsh_rebellion_remove_favour") + game.who = NOBODY + game.count = 0 + } + else { + end_immediate_event() + } +} + +states.welsh_rebellion_remove_troops = { + inactive: "Welsh Rebellion \u2014 Remove troops", + prompt() { + view.prompt = `Remove 2 Troops from each enemy Lord in Wales.` + let done = true + if (game.who === NOBODY) { + for (let lord = first_enemy_lord; lord <= last_enemy_lord; lord++) { + if (is_lord_on_map(lord) && is_lord_in_wales(lord) && get_lord_moved(lord)) { + gen_action_lord(lord) + done = false + } + } + if (done) { + view.actions.done = 1 + } + } + else if (game.who !== NOBODY) { + if (game.count >= 0) { + view.prompt = `Remove ${game.count} Troops from ${data.lords[game.who].name}.` + if (game.count === 0) { + set_lord_moved(game.who, 0) + view.actions.done = 1 + } + else { + if (get_lord_forces(game.who, BURGUNDIANS) > 0) + gen_action_burgundians(game.who) + if (get_lord_forces(game.who, MERCENARIES) > 0) + gen_action_mercenaries(game.who) + if (get_lord_forces(game.who, LONGBOWMEN) > 0) + gen_action_longbowmen(game.who) + if (get_lord_forces(game.who, MEN_AT_ARMS) > 0) + gen_action_men_at_arms(game.who) + if (get_lord_forces(game.who, MILITIA) > 0) + gen_action_militia(game.who) + } + } + } + }, + lord(lord) { + push_undo() + game.who = lord + game.count = 2 + }, + burgundians(lord) { + add_lord_forces(lord, BURGUNDIANS, -1) + game.count-- + }, + mercenaries(lord) { + add_lord_forces(lord, MERCENARIES, -1) + game.count-- + }, + longbowmen(lord) { + add_lord_forces(lord, LONGBOWMEN, -1) + game.count-- + }, + men_at_arms(lord) { + add_lord_forces(lord, MEN_AT_ARMS, -1) + game.count-- + }, + militia(lord) { + add_lord_forces(lord, MILITIA, -1) + game.count-- + }, + done() { + end_welsh_rebellion() + }, +} + +states.welsh_rebellion_remove_favour = { + inactive: "Welsh Rebellion \u2014 Remove Favour", + prompt() { + view.prompt = `Select up to ${2-game.count} Locales in Wales.` + for (let loc = first_locale; loc <= last_locale; loc++) { + if (game.count < 2 && is_wales(loc) && has_favoury_marker(loc)) { + gen_action_locale(loc) + } + } + view.actions.done = 1 + }, + locale(loc) { + push_undo() + remove_favoury_marker(loc) + logi(`Removed favour at ${data.locales[loc].name}`) + game.count++ + }, + done() { + end_immediate_event() + }, +} + +function end_welsh_rebellion() { + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (is_lord_on_map(lord) && is_lord_in_wales(lord) && !lord_has_unrouted_units(lord)) + disband_lord(lord, false) + } + game.count = 0 + game.who = NOBODY + end_immediate_event() +} + +// === EVENT: HENRY RELEASED === +function goto_lancaster_event_henry_released() { + if (has_favourl_marker(LOC_LONDON)) { + logi(`Henry Released : 5 Influence for Lancaster`) + increase_lancaster_influence(5) + } + end_immediate_event() +} + +// === EVENT: L'UNIVERSELLE ARAGNE === + +function goto_lancaster_event_luniverselle_aragne() { + let can_play = false + for (let vassal = first_vassal; vassal <= last_vassal; vassal++) { + if (is_vassal_mustered_with_york_lord(vassal)) { + can_play = true + } + } + if (can_play) { + game.state = "aragne" + game.who = NOBODY + game.count = 0 + } else { + logi("No Effect") + end_immediate_event() + } +} + +states.aragne = { + inactive: "L'universelle Aragne", + prompt() { + view.prompt = "Select up to 2 Vassals" + if (game.who === NOBODY && game.count < 2) { + for (let v = first_vassal; v <= last_vassal; v++) { + if (!get_vassal_moved(v) && is_vassal_mustered_with_york_lord(v)) { + gen_action_vassal(v) + } + } + } + view.actions.done = 1 + }, + vassal(v) { + push_undo() + game.who = v + game.count++ + set_vassal_moved(v, 1) + logi(`Vassal ${data.vassals[v].name} selected`) + game.who = NOBODY + }, + done() { + goto_yorkist_aragne() + }, +} + +function goto_yorkist_aragne() { + game.who = NOBODY + set_active_enemy() + game.state = "yorkist_aragne" +} + +states.yorkist_aragne = { + inactive: "Influence checks", + prompt() { + view.prompt = `For Each vassal, influence check : failure disbands it` + let done = true + if (game.who === NOBODY) { + for (let v = first_vassal; v <= last_vassal; v++) { + if (get_vassal_moved(v) && is_vassal_mustered_with_friendly_lord(v)) { + gen_action_vassal(v) + done = false + } + } + if (done) { + view.actions.done = 1 + } + } + }, + vassal(other) { + push_undo() + goto_aragne_save(other) + }, + done() { + end_universelle_aragne() + }, +} + +function goto_aragne_save(other) { + game.who = other + game.where = NOWHERE + get_vassal_lord(other) + init_influence_check(get_vassal_lord(other)) + game.check.push({ + cost: 0, + modifier: data.vassals[other].influence * (game.active === LANCASTER ? -1 : 1), + source: "vassal", + }) + push_state("aragne_save") +} + +states.aragne_save = { + inactive: `Influence check`, + prompt() { + view.prompt = `Influence check : Failure disbands ${data.vassals[game.who].name}` + prompt_influence_check() + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + logi(`Attempt to save ${data.vassals[game.who].name} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (results.success) { + set_vassal_moved(game.who, 0) + game.who = NOBODY + end_influence_check() + push_state("yorkist_aragne") + } else { + set_vassal_moved(game.who, 0) + disband_vassal(game.who) + if (game.who === VASSAL_HASTINGS) { + discard_card_capability(AOW_YORK_HASTINGS) + logi(`Hastings Discarded`) + } + game.who = NOBODY + end_influence_check() + push_state("yorkist_aragne") + } + }, +} + +function end_universelle_aragne() { + game.who = NOBODY + game.count = 0 + end_immediate_event() +} + +// === EVENT: TO WILFUL DISOBEDIANCE === + +function goto_lancaster_event_to_wilful_disobediance() { + let can_play = false + for (let loc = first_locale; loc <= last_locale; loc++){ + if (has_favoury_marker(loc) && !has_enemy_lord(loc) && !has_adjacent_enemy(loc) && (has_friendly_lord(loc) || has_adjacent_friendly(loc))) { + can_play = true + } + } + if (can_play) { + game.state = "wilful_disobediance" + game.who = NOBODY + game.count = 0 + } else { + end_immediate_event() + logi(`No Effect`) + } + +} +states.wilful_disobediance = { + inactive: "to wilful disobediance", + prompt() { + view.prompt = `Select up to ${2-game.count} Yorkists Locales.` + for (let loc = first_locale; loc <= last_locale; loc++) { + if ( + game.count < 2 && + has_favoury_marker(loc) && + !has_enemy_lord(loc) && + !has_adjacent_enemy(loc) && + (has_friendly_lord(loc) || has_adjacent_friendly(loc)) + ) { + gen_action_locale(loc) + } + } + view.actions.done = 1 + }, + locale(loc) { + push_undo() + remove_favoury_marker(loc) + game.count++ + logi(`Favour removed at ${loc}`) + }, + done() { + logi(`No Effect`) + end_immediate_event() + } +} + +// === EVENT: FRENCH WAR LOANS === + +function goto_lancaster_event_french_war_loans() { + for (let lord = first_lancaster_lord; lord <= last_lancaster_lord; ++lord) { + if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { + add_lord_assets(lord, PROV, 1) + add_lord_assets(lord, COIN, 1) + logi(`1 Coin and 1 Provender added to ${data.lords[lord].name}`) + } + } + end_immediate_event() +} + +// === EVENT: ROBINS REBELLION === + +function goto_lancaster_event_robins_rebellion() { + let can_play = false + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_north(loc) && !has_favourl_marker(loc)) { + can_play = true + } + } + if (can_play) { + game.state = "robins_rebellion" + game.who = NOBODY + game.count = 0 + } else { + logi(`No Effect`) + end_immediate_event() + } +} + +states.robins_rebellion = { + inactive: "Robin's Rebellion", + prompt() { + view.prompt = `Select up to ${3-game.count} Locales in North.` + for (let loc = first_locale; loc <= last_locale; loc++) { + if (game.count < 3 && is_north(loc) && !has_favourl_marker(loc)) { + gen_action_locale(loc) + } + } + view.actions.done = 1 + }, + locale(loc) { + push_undo() + shift_favour_toward(loc) + logi(`Placed/Removed favour at ${data.locales[loc].name}`) + game.count++ + }, + done() { + end_immediate_event() + } +} + +// === EVENT: TUDOR BANNERS === + +function tudor_banner_eligible() { + if (is_lord_on_map(LORD_HENRY_TUDOR) && !is_lord_on_calendar(LORD_HENRY_TUDOR)) { + for (let next of data.locales[get_lord_locale(LORD_HENRY_TUDOR)].adjacent) { + if (can_parley_at(next)) + return true + } + } else + return false +} + +function goto_lancaster_event_tudor_banners() { + let can_play = false + if (tudor_banner_eligible()) { + can_play = true + } + if (can_play) { + game.state = "tudor_banners" + game.who = NOBODY + } else { + logi(`No Effect`) + end_immediate_event() + } +} + +states.tudor_banners = { + inactive: "Tudor banners", + prompt() { + view.prompt = `Select locales adjacent to Henry to make them Lancastrian` + for (let next of data.locales[get_lord_locale(LORD_HENRY_TUDOR)].adjacent) { + if (!has_enemy_lord(next) && !has_favourl_marker(next)) { + gen_action_locale(next) + } else { + view.actions.done = 1 + } + } + }, + locale(loc) { + push_undo() + remove_favoury_marker(loc) + add_favourl_marker(loc) + logi(`Placed Lancastrian favour at ${data.locales[loc].name}`) + }, + done() { + end_immediate_event() + } +} + +// === EVENT: TAX COLLECTORS === + +function goto_york_event_tax_collectors() { + game.who = NOBODY + game.count = 0 + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { + set_lord_moved(lord, 1) + } + } + push_state("tax_collectors") +} + +function can_action_tax_collectors(lord) { + let here = get_lord_locale(lord) + if (can_tax_collectors_at(here, lord)) + return true + return search_tax_collectors(false, here, lord) +} + +function can_tax_collectors_at(here, lord) { + if (is_friendly_locale(here) && !has_exhausted_marker(here)) { + // London, Calais, and Harlech + if (here === LOC_LONDON || here === LOC_CALAIS || here === LOC_HARLECH) + return true + + // Own seat + if (here === data.lords[lord].seat) + return true + + // vassal seats + for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) + if (is_vassal_mustered_with(vassal, lord)) + if (here === data.vassals[vassal].seat) + return true + } + return false +} + +function search_tax_collectors(result, start, lord) { + let ships = get_shared_assets(start, SHIP) + + search_seen.fill(0) + search_seen[start] = 1 + + let queue = [ start ] + while (queue.length > 0) { + let here = queue.shift() + + if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { + if (can_tax_collectors_at(here, lord)) { + if (result) + set_add(result, here) + else + return true + } + } + + if (is_friendly_locale(here)) { + for (let next of data.locales[here].adjacent) { + if (!search_seen[next]) { + search_seen[next] = 1 + queue.push(next) + } + } + if (ships > 0 && is_seaport(here)) { + for (let next of find_ports(here)) { + if (!search_seen[next]) { + search_seen[next] = 1 + queue.push(next) + } + } + } + } + } + + if (result) + return result + else + return false +} + +states.tax_collectors = { + inactive: "Tax Collectors", + prompt() { + view.prompt = "Tax Collectors : You may tax for Double coin with each lord" + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (can_action_tax_collectors(lord) && get_lord_moved(lord)) { + gen_action_lord(lord) + } + } + view.actions.done = 1 + }, + lord(lord) { + push_undo() + game.where = NOWHERE game.who = lord + push_state("double_tax_collectors") + init_influence_check(lord) + }, + done() { + end_tax_collectors() + }, } -function gen_action_locale(locale) { - gen_action("locale", locale) +states.double_tax_collectors = { + inactive: "Tax Collectors", + prompt() { + view.prompt = "Tax: Select the location to tax for double." + if (game.where === NOWHERE) { + for (let loc of search_tax_collectors([], get_lord_locale(game.who), game.who)) + gen_action_locale(loc) + } else { + view.prompt = `Tax: Attempting to tax ${data.locales[game.where].name}. ` + prompt_influence_check() + } + }, + locale(loc) { + game.where = loc + if (loc === data.lords[game.who].seat) { + // Auto succeed without influence check at Lords seat. + deplete_locale(game.where) + + log(`Taxed %${game.where}.`) + add_lord_assets(game.who, COIN, get_tax_amount(game.where)*2) + set_lord_moved(game.who, 0) + end_tax_lord() + } + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + logi(`Tax : ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (lord_has_capability(game.who, AOW_YORK_SO_WISE_SO_YOUNG)) { + log(`C${AOW_YORK_SO_WISE_SO_YOUNG}.`) + add_lord_assets(game.who, COIN, 1) + } + + if (results.success) { + deplete_locale(game.where) + log(`Taxed %${game.where}.`) + add_lord_assets(game.who, COIN, get_tax_amount(game.where)*2) + + if ( + game.command === LORD_DEVON && + (game.where === LOC_EXETER || + game.where === LOC_LAUNCESTON || + game.where === LOC_PLYMOUTH || + game.where === LOC_WELLS || + game.where === LOC_DORCHESTER) + ) + add_lord_assets(game.command, COIN, 4) + } else { + log(`Tax of %${game.where} failed.`) + + } + set_lord_moved(game.who, 0) + game.who = NOBODY + push_state("tax_collectors") + }, } -function gen_action_lord(lord) { - gen_action("lord", lord) +function end_tax_lord() { + game.where = NOWHERE + game.who = NOBODY + pop_state() } -function gen_action_array(pos) { - gen_action("array", pos) +function end_tax_collectors() { + game.where = NOWHERE + game.who = NOBODY + game.count = 0 + end_immediate_event() } -function gen_action_vassal(vassal) { - gen_action("vassal", vassal) +// === EVENT: LONDON FOR YORK === + +function goto_york_event_london_for_york() { + let can_play = false + if (has_favoury_marker(LOC_LONDON)) { + can_play = true + } + if (can_play) { + game.who = NOBODY + game.state = "london_for_york" + } else { + logi(`No Effect`) + end_immediate_event() + } } -function gen_action_card(card_or_list) { - if (Array.isArray(card_or_list)) - for (let c of card_or_list) - gen_action("card", c) +states.london_for_york = { + inactive: "London For York", + prompt() { + view.prompt = `Select London to add a second favour marker` + gen_action_locale(LOC_LONDON) + }, + locale(loc) { + push_undo() + game.flags.london_for_york = 1 + logi(`Second marker placed at ${data.locales[loc].name}`) + logi(`Immune to Lancastrian parley unless aided by event`) + end_immediate_event() + }, +} + +function check_london_protected() { +// TODO IF HENRY/MARGARET ARE MUSTERED IT DOES NOT CHANGE FAVOUR +// ONLY L17/L18 and Pillage will cancel that event +//(it is annuled when london go to neutral + if (game.state === "pillage") { + return false + } + if (game.flags.london_for_york === 1 && game.where === LOC_LONDON) { + return true + } + else { + return false + } +} + +// === EVENT: SHE-WOLF OF FRANCE === + +function goto_york_event_shewolf_of_france() { + let can_play = false + for (let v = first_vassal; v <= last_vassal; v++) { + if (is_vassal_mustered_with_friendly_lord(v)) { + set_vassal_moved(v, 1) + can_play = true + } + } + if (can_play) { + game.state = "she_wolf" + game.who = NOBODY + } else { + logi(`No Effect`) + end_immediate_event() + } +} + +// TO TRACK VASSALS DURING EVENTS + +function get_vassal_moved(v) { + return map_get(game.pieces.moved, v, 0) +} + +function set_vassal_moved(v, x) { + map_set(game.pieces.moved, v, x) +} + +function pay_vassal_shewolf(vassal) { + if (current_turn() < 16) + set_vassal_lord_and_service(vassal, get_vassal_lord(vassal),get_vassal_service(vassal) + 1) +} + +states.she_wolf = { + inactive: "She-Wolf of France", + prompt() { + let done = true + view.prompt = "You may shift your vassals one calendar box." + if (game.who === NOBODY) { + for (let v = first_vassal; v <= last_vassal; v++) { + if (get_vassal_moved(v) && is_vassal_mustered_with_friendly_lord(v)) { + gen_action_vassal(v) + done = false + } + } + if (done) { + view.actions.done = 1 + } + } + }, + vassal(v) { + push_undo() + game.who = v + pay_vassal_shewolf(game.who) + set_vassal_moved(v, 0) + logi(`Vassal ${data.vassals[v].name} shifted one calendar box`) + game.who = NOBODY + }, + done() { + end_immediate_event() + }, +} + +// === EVENT: RICHARD LEIGH === + +function goto_york_event_sir_richard_leigh() { + let can_play = false + if (!has_favoury_marker(LOC_LONDON)) { + can_play = true + } + if (can_play) { + game.state = "richard_leigh" + game.who = LOC_LONDON + game.count = 0 + } else { + logi(`No Effect`) + end_immediate_event() + } +} + +states.richard_leigh = { + inactive: "Richard Leigh", + prompt() { + view.prompt = `Select London, shift it once in your favour` + if (game.who === LOC_LONDON && !has_favoury_marker(LOC_LONDON)) { + gen_action_locale(LOC_LONDON) + } else { + view.actions.done = 1 + } + }, + locale(loc) { + push_undo() + shift_favour_toward(loc) + logi(`London shifted once in your favour`) + game.who = NOBODY + }, + done() { + end_immediate_event() + } +} + +// === EVENT: CHARLES THE BOLD === + +function goto_york_event_charles_the_bold() { + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (is_lord_on_map(lord) && !is_lord_on_calendar(lord)) { + add_lord_assets(lord, PROV, 1) + add_lord_assets(lord, COIN, 1) + logi(`1 Coin and 1 Provender added to ${data.lords[lord].name}`) + } + } + end_immediate_event() +} + +// === EVENT: DUBIOUS CLARENCE === + +function goto_dubious_clarence() { + let can_play = false + if ((is_lord_on_map(LORD_EDWARD_IV) && !is_lord_on_calendar(LORD_EDWARD_IV)) + && is_lord_on_map(LORD_CLARENCE) && !is_lord_on_calendar(LORD_CLARENCE)) + can_play = true + + if (can_play) { + game.state = "dubious_clarence" + game.who = LORD_EDWARD_IV + init_influence_check(LORD_EDWARD_IV) + } else { + logi(`No Effect`) + end_immediate_event() + } +} + +states.dubious_clarence = { + inactive: "Dubious Clarence", + prompt() { + view.prompt = `You may Influence check with Edward to disband Clarence ` + prompt_influence_check() + }, + spend1: add_influence_check_modifier_1, + spend3: add_influence_check_modifier_2, + check() { + let results = do_influence_check() + log(`Attempt to disband Clarence ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (results.success) { + disband_lord(LORD_CLARENCE, false) + end_immediate_event() + } else { + end_immediate_event() + } + }, +} + +// === EVENT: YORKIST NORTH === + +function goto_york_event_yorkist_north() { + let influence_gained = 0 + for (let lord = first_york_lord; lord <= last_york_lord; ++lord) { + if (is_lord_on_map(lord) && !is_lord_on_calendar(lord) && is_lord_in_north(lord)) + influence_gained++ + } + for (let loc = first_locale; loc <= last_locale; loc++) { + if (loc !== NOWHERE && loc < CALENDAR && has_favoury_marker(loc) && is_north(loc)) { + influence_gained++ + } + } + logi(`Yorkist North : ${influence_gained} Influence for Yorkists`) + increase_york_influence(influence_gained) + end_immediate_event() +} + +// === EVENT: EARL RIVERS === + +function goto_york_event_earl_rivers() { + game.state = "earl_rivers" + game.count = [] + game.who = NOBODY +} + +function end_york_event_earl_rivers() { + game.count = 0 + game.who = NOBODY + end_immediate_event() +} + +states.earl_rivers = { + inactive: "Earl Rivers", + prompt() { + view.prompt = "Earl Rivers: Add up to 2 Militia to each lord" + view.actions.done = 1 + for (let lord = first_york_lord; lord <= last_york_lord; lord++) { + if (is_lord_on_map(lord) && map_get(game.count, lord, 0) < 3) { + gen_action_lord(lord) + } + } + + if (game.who !== NOBODY) { + let troops = map_get(game.count, game.who, 0) + if ((troops & 1) === 0) + view.actions.add_militia = 1 + } + if (game.who !== NOBODY) { + let troops = map_get(game.count, game.who, 0) + if ((troops & 1) === 0) + view.actions.add_militia2 = 1 + } + }, + done() { + end_york_event_earl_rivers() + }, + add_militia() { + push_undo() + add_lord_forces(game.who, MILITIA, 1) + let troops = map_get(game.count, game.who, 0) + map_set(game.count, game.who, troops + 1) + if (troops > 1) + game.who = NOBODY + }, + add_militia2() { + push_undo() + add_lord_forces(game.who, MILITIA, 2) + let troops = map_get(game.count, game.who, 0) + map_set(game.count, game.who, troops + 1) + if (troops > 1) + game.who = NOBODY + }, + lord(lord) { + push_undo() + game.who = lord + } +} + +// === EVENT: THE KINGS NAME === + +function eligible_kings_name() { + if ( + (!is_lord_on_calendar(LORD_GLOUCESTER_1) && is_lord_on_map(LORD_GLOUCESTER_1)) || + (!is_lord_on_calendar(LORD_GLOUCESTER_2) && is_lord_on_map(LORD_GLOUCESTER_2)) + ) { + if (is_event_in_play(EVENT_YORK_THE_KINGS_NAME) && game.active === LANCASTER) + return true + } + return false +} + +function goto_kings_name(action) { + game.what = action + set_active_enemy() + push_state("kings_name") +} + +states.kings_name = { + inactive: `King's name`, + prompt() { + view.prompt = `King's Name: You pay may 1 Influence to cancel last ${game.what} action` + view.actions.pass = 1 + view.actions.pay = 1 + }, + pay() { + push_undo() + reduce_influence(1) + goto_kings_name_cancel() + }, + pass() { + set_active_enemy() + pop_state() + if (game.state === "levy_muster_lord_attempt") { + push_state("muster_lord_at_seat") + } + if (game.state === "levy_muster_vassal") { + muster_vassal(game.which, game.who) + pop_state() + } + if (game.state === "muster_capability") { + add_lord_capability(game.who, game.which) + capability_muster_effects(game.who, game.which) + pop_state() + } + if (game.state === "parley") { + game.what = NOTHING + game.where = NOWHERE + pop_state() + } + resume_levy_muster_lord() + } +} + +function goto_kings_name_cancel() { + switch(game.what) { + case "Levy Lord": + pop_state() + end_levy_muster_lord_attempt() + break + case "Levy Cart": + add_lord_assets(game.who, CART, -2) + pop_state() + resume_levy_muster_lord() + break + case "Levy Ship": + add_lord_assets(game.who, SHIP, -1) + pop_state() + resume_levy_muster_lord() + break + case "Levy Vassal": + game.which = NOTHING + end_levy_muster_vassal() + break + case "Levy Troops": + kings_name_reset_troops() + pop_state() + resume_levy_muster_lord() + break + case "Parley": + shift_favour_toward_york(game.where) + pop_state() + game.where = NOWHERE + end_parley() + break + case "Capability": + game.which = NOTHING + pop_state() + pop_state() + resume_levy_muster_lord() + break + case "Levy Beloved Warwick": + add_lord_forces(game.who, MILITIA, -5) + pop_state() + resume_levy_muster_lord() + break + default: + throw Error("No King's name cancel state found") + } + log(`${game.what} action cancelled`) + logevent(`${EVENT_YORK_THE_KINGS_NAME}`) + set_active_enemy() +} + +function kings_name_reset_troops() { + if (!lord_has_capability(game.who, AOW_LANCASTER_QUARTERMASTERS)) { + remove_depleted_marker(get_lord_locale(game.who)) + remove_exhausted_marker(get_lord_locale(game.who)) + } + + let here = get_lord_locale(game.who) + let here_type = data.locales[here].type + switch (here_type) { + case "calais": + add_lord_forces(game.who, MEN_AT_ARMS, -2) + add_lord_forces(game.who, LONGBOWMEN, -1) + break + case "london": + add_lord_forces(game.who, MEN_AT_ARMS, -1) + add_lord_forces(game.who, LONGBOWMEN, -1) + add_lord_forces(game.who, MILITIA, -1) + break + case "harlech": + add_lord_forces(game.who, MEN_AT_ARMS, -1) + add_lord_forces(game.who, LONGBOWMEN, -2) + break + case "city": + add_lord_forces(game.who, LONGBOWMEN, -1) + add_lord_forces(game.who, MILITIA, -1) + break + case "town": + add_lord_forces(game.who, MILITIA, -2) + break + case "fortress": + add_lord_forces(game.who, MEN_AT_ARMS, -1) + add_lord_forces(game.who, MILITIA, -1) + break + } +} + +// === EVENT: RISING WAGES === + +states.rising_wages = { + inactive: "Rising Wages", + prompt() { + let here = get_lord_locale(game.who) + view.prompt = "Rising Wages: Pay 1 extra coin to levy troops" + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + let loc = get_lord_locale(lord) + if (here === loc && (get_lord_assets(lord, COIN) > 0)) { + gen_action_coin(lord) + } + } + }, + coin(lord) { + push_undo() + add_lord_assets(lord, COIN, -1) + logi(`${EVENT_LANCASTER_RISING_WAGES}`) + log("York paid 1 Coin to Levy troops") + pop_state() + }, +} + +// Check if the levy troops is at vassal seat + +function chamberlains_eligible_levy(locale) { + for (let vassal = first_vassal; vassal <= last_vassal; ++vassal) + if (is_vassal_mustered_with(vassal, game.who) && lord_has_capability(game.who, AOW_LANCASTER_CHAMBERLAINS)) { + if (locale === data.vassals[vassal].seat) + return true + } +} + +// === EVENT: THE COMMONS === + +states.the_commons = { + inactive: "The Commons", + prompt() { + view.prompt = `Add up to ${game.flags.commons_militia} Militias.` + if (game.flags.commons_militia > 0) { + view.actions.add_militia = 1 + } + view.actions.done = 1 + }, + add_militia() { + push_undo() + add_lord_forces(game.who, MILITIA, 1) + --game.flags.commons_militia + }, + done() { + push_undo() + end_the_commons() + + } +} + +function end_the_commons() { + game.flags.commons_militia = 0 + pop_state() + resume_levy_muster_lord() +} + +// === EVENT (AS ACTION): EXILE PACT === + +function can_action_exile_pact() { + if (game.actions <= 0 || !is_event_in_play(EVENT_YORK_EXILE_PACT)) + return false else - gen_action("card", card_or_list) + return true } -function gen_action_plan(lord) { - gen_action("plan", lord) +function goto_exile_pact() { + push_undo() + game.state = "exile_pact" } -function gen_action_prov(lord) { - gen_action("prov", lord) +states.exile_pact = { + inactive: "Exile pact", + prompt() { + view.prompt = "Exile Pact : place your cylinder in one of your Exile boxes" + for (let loc of data.exile_boxes) { + if (has_favour_in_locale(game.active, loc)) + gen_action_locale(loc) + } + }, + locale(loc) { + push_undo() + set_lord_locale(game.command, loc) + end_exile_pact() + } } -function gen_action_coin(lord) { - gen_action("coin", lord) +function end_exile_pact() { + spend_action(1) + push_undo() + resume_command() } -function gen_action_cart(lord) { - gen_action("cart", lord) +// === EVENTS: HELD === + +function play_held_event(c) { + log(`Played E${c}.`) + if (c >= first_york_card && c <= last_york_card) + set_delete(game.hand_y, c) + else + set_delete(game.hand_l, c) + + /* Hold events with This Levy/Campaign */ + if ( + c === EVENT_YORK_YORKIST_PARADE || + c === EVENT_YORK_PARLIAMENTS_TRUCE || + c === EVENT_LANCASTER_PARLIAMENTS_TRUCE + ) { + set_add(game.events, c) + } } -function gen_action_mercenaries(lord) { - gen_action("mercenaries", lord) +// TODO: use or remove this function +function end_held_event() { + pop_state() + game.what = NOTHING } -function gen_action_burgundians(lord) { - gen_action("burgundians", lord) +function prompt_held_event() { + for (let c of current_hand()) + if (can_play_held_event(c)) + gen_action_card(c) } -function gen_action_longbowmen(lord) { - gen_action("longbowmen", lord) +function can_play_held_event(c) { + switch (c) { + case EVENT_LANCASTER_ASPIELLES: + return can_play_l_aspielles() + case EVENT_LANCASTER_REBEL_SUPPLY_DEPOT: + return can_play_rebel_supply_depot() + case EVENT_LANCASTER_SURPRISE_LANDING: + return can_play_surprise_landing() + case EVENT_LANCASTER_PARLIAMENTS_TRUCE: + return can_play_parliaments_truce() + case EVENT_YORK_PARLIAMENTS_TRUCE: + return can_play_parliaments_truce() + case EVENT_YORK_ASPIELLES: + return can_play_y_aspielles() + case EVENT_YORK_YORKIST_PARADE: + return can_play_yorkist_parade() + case EVENT_YORK_SUN_IN_SPLENDOUR: + return can_play_sun_in_splendour() + } + return false } -function gen_action_retinue(lord) { - gen_action("retinue", lord) +function action_held_event(c) { + push_undo() + play_held_event(c) + game.what = c + goto_held_event(c) } -function gen_action_men_at_arms(lord) { - gen_action("men_at_arms", lord) +function goto_held_event(c) { + switch (c) { + case EVENT_YORK_ESCAPE_SHIP: + goto_play_escape_ship() + break + case EVENT_YORK_ASPIELLES: + goto_play_aspielles() + break + case EVENT_LANCASTER_ESCAPE_SHIP: + goto_play_escape_ship() + break + case EVENT_LANCASTER_TALBOT_TO_THE_RESCUE: + goto_play_talbot_to_the_rescue() + break + case EVENT_LANCASTER_WARDEN_OF_THE_MARCHES: + goto_play_warden_of_the_marches() + break + case EVENT_LANCASTER_REBEL_SUPPLY_DEPOT: + goto_play_rebel_supply_depot() + break + case EVENT_LANCASTER_SURPRISE_LANDING: + goto_play_surprise_landing() + break + case EVENT_LANCASTER_ASPIELLES: + goto_play_aspielles() + break + case EVENT_YORK_SUN_IN_SPLENDOUR: + goto_play_sun_in_splendour() + break + } } -function gen_action_militia(lord) { - gen_action("militia", lord) +// === HELD EVENT (LEVY): YORKIST PARADE === + +function can_play_yorkist_parade() { + // TODO: only during levy? + if (game.active === YORK && is_favour_friendly(LOC_LONDON) && (get_lord_locale(LORD_WARWICK_Y) === LOC_LONDON || get_lord_locale(LORD_YORK) === LOC_LONDON)) { + return true + } + return false } -function gen_action_routed_mercenaries(lord) { - gen_action("routed_mercenaries", lord) +// === HELD EVENT (LEVY): SUN IN SPLENDOUR === + +function can_play_sun_in_splendour() { + if (is_levy_phase() && is_lord_on_calendar(LORD_EDWARD_IV)) { + return true + } + return false } -function gen_action_routed_longbowmen(lord) { - gen_action("routed_longbowmen", lord) +function goto_play_sun_in_splendour() { + push_state("sun_in_splendour") } -function gen_action_routed_burgundians(lord) { - gen_action("routed_burgundians", lord) +states.sun_in_splendour = { + inactive: "Sun in splendour", + prompt() { + let done = true + view.prompt = "Sun in Splendour: Muster Edward IV in any friendly locale with no enemy lord" + if (is_lord_on_calendar(LORD_EDWARD_IV)) { + for (let loc = first_locale; loc <= last_locale; loc++) { + if (is_friendly_locale(loc)) { + done = false + gen_action_locale(loc) + } + } + } + if (done) { + view.actions.done = 1 + } + }, + locale(loc) { + push_undo() + muster_lord(LORD_EDWARD_IV, loc) + logi(`Mustered Edward IV at ${data.locales[loc].name}`) + }, + done() { + end_sun_in_splendour() + }, } -function gen_action_routed_men_at_arms(lord) { - gen_action("routed_men_at_arms", lord) +function end_sun_in_splendour() { + game.who = NOBODY + pop_state() } -function gen_action_routed_militia(lord) { - gen_action("routed_militia", lord) +// === HELD EVENT: ASPIELLES === + +function can_play_l_aspielles() { + if (game.hand_y.length > 0 || game.hidden) { + return true + } + return false } -const YORK_LORD_MASK = 0x1fff -const LANCASTER_LORD_MASK = YORK_LORD_MASK << 14 +function can_play_y_aspielles() { + if (game.hand_l.length > 0 || game.hidden) { + return true + } + return false +} + +function goto_play_aspielles() { + push_state("aspielles") + game.who = NOBODY +} + +states.aspielles = { + inactive: "Aspielles", + prompt() { + view.prompt = "Aspielles: You may see enemy held cards" + if (game.hidden) { + view.prompt += " and an enemy lord to see his mat" + } + prompt_held_event() + if (game.hidden) { + for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) + gen_action_lord(lord) + } + if (game.active === YORK) { + view.hand = game.hand_l + log("Lancaster hand shown to the York player") + } + if (game.active === LANCASTER) { + view.hand = game.hand_y + log("York hand shown to the Lancaster player") + } + view.actions.done = 1 + + }, + lord(lord) { + log(`${lord_name[lord]} Spied`) + view.reveal |= (1 << lord) + }, + card: action_held_event, + done() { + if (is_campaign_phase()) { + resume_command() + } else { + pop_state() + } + }, +} + +// === HELD EVENT: REBEL SUPPLY DEPOT === + +function can_play_rebel_supply_depot() { + if (game.active === YORK) + return false + if (game.group) { + for (let lord of game.group) { + if (get_lord_moved(lord) && is_seaport(get_lord_locale(game.command))) { + return true + } + } + } + return false +} + +function goto_play_rebel_supply_depot() { + game.flags.supply_depot = 1 + add_spoils(PROV, 4) + push_state("rebel_supply_depot") +} + +states.rebel_supply_depot = { + inactive: "Rebel Supply depot", + prompt() { + if (has_any_spoils()) { + view.prompt = "Divide " + list_spoils() + "." + let here = get_lord_locale(game.command) + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + if (get_lord_locale(lord) === here) + prompt_select_lord(lord) + if (game.who !== NOBODY) + prompt_spoils() + } + } else { + view.prompt = "Rebel Supply Depot: All done." + view.actions.end_spoils = 1 + } + }, + lord: action_select_lord, + take_prov() { + push_undo_without_who() + take_spoils(PROV) + }, + end_spoils() { + end_rebel_supply_depot() + }, +} + +function end_rebel_supply_depot() { + game.who = NOBODY + game.spoils = 0 + resume_command() +} + +// === HELD EVENT: SURPRISE LANDING === + +function can_play_surprise_landing() { + let here = get_lord_locale(game.command) + if (game.flags.surprise_landing === 0 || !is_seaport(here) || here === LOC_CALAIS || here === LOC_PEMBROKE || here === LOC_HARLECH || here === LANCASTER || is_sea(here)) + return false + return true +} + +function goto_play_surprise_landing() { + push_state("surprise_landing") + game.flags.surprise_landing = 2 + game.who = NOBODY +} + +states.surprise_landing = { + inactive: "Surprise Landing", + prompt() { + view.prompt = "Surprise Landing : You may march once (no path)." + prompt_held_event() + + view.group = game.group + let here = get_lord_locale(game.command) + // 4.3.2 Marshals MAY take other lords + if ( + is_marshal(game.command) || + (lord_has_capability(game.command, AOW_YORK_CAPTAIN) && !other_marshal_or_lieutenant(here)) + ) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (lord !== game.command) + if (get_lord_locale(lord) === here) + gen_action_lord(lord) + } + + // Lieutenant may not take marshall + if (is_lieutenant(game.command)) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (lord !== game.command) + if (get_lord_locale(lord) === here && !is_marshal(lord)) { + gen_action_lord(lord) + } + } + + prompt_march() + }, + lord(lord) { + set_toggle(game.group, lord) + }, + locale: goto_march, + card: action_held_event, +} + +// === LOGGING === + +function range(x) { + switch (x) { + case 0: return "0" + case 1: return "1" + case 2: return "1-2" + case 3: return "1-3" + case 4: return "1-4" + case 5: return "1-5" + case 6: return "Automatic success" + } +} + +function log_br() { + if (game.log.length > 0 && game.log[game.log.length - 1] !== "") + game.log.push("") +} + +function log(msg) { + game.log.push(msg) +} + +function logevent(cap) { + game.log.push(`E${cap}.`) +} + +function logcap(cap) { + game.log.push(`C${cap}.`) +} + +function logi(msg) { + game.log.push(">" + msg) +} + +function log_h1(msg) { + log_br() + log(".h1 " + msg) + log_br() +} + +function log_h2(msg) { + log_br() + if (game.active === YORK) + log(".h2t " + msg) + else + log(".h2r " + msg) + log_br() +} + +function log_h3(msg) { + log_br() + if (game.active === YORK) + log(".h3t " + msg) + else + log(".h3r " + msg) + log_br() +} + +function log_h4(msg) { + log_br() + log(".h4 " + msg) +} + +function log_h5(msg) { + log_br() + log(".h5 " + msg) +} + +// === VIEW & ACTION === exports.view = function (state, current) { load_state(state) @@ -11746,39 +11609,146 @@ exports.action = function (state, current, action, arg) { return game } -// === COMMON TEMPLATE === +function gen_action(action, argument) { + if (!(action in view.actions)) + view.actions[action] = [] + set_add(view.actions[action], argument) +} -// Packed array of small numbers in one word +function gen_action_card_if_held(c) { + if (has_card_in_hand(c)) + gen_action_card(c) +} -function pack1_get(word, n) { - return (word >>> n) & 1 +function prompt_select_lord(lord) { + if (lord !== game.who) + gen_action_lord(lord) } -function pack2_get(word, n) { - n = n << 1 - return (word >>> n) & 3 +function action_select_lord(lord) { + if (game.who === lord) + game.who = NOBODY + else + game.who = lord } -function pack4_get(word, n) { - n = n << 2 - return (word >>> n) & 15 +function gen_action_locale(locale) { + gen_action("locale", locale) } -function pack1_set(word, n, x) { - return (word & ~(1 << n)) | (x << n) +function gen_action_lord(lord) { + gen_action("lord", lord) } -function pack2_set(word, n, x) { - n = n << 1 - return (word & ~(3 << n)) | (x << n) +function gen_action_array(pos) { + gen_action("array", pos) } -function pack4_set(word, n, x) { - n = n << 2 - return (word & ~(15 << n)) | (x << n) +function gen_action_vassal(vassal) { + gen_action("vassal", vassal) +} + +function gen_action_card(card_or_list) { + if (Array.isArray(card_or_list)) + for (let c of card_or_list) + gen_action("card", c) + else + gen_action("card", card_or_list) +} + +function gen_action_plan(lord) { + gen_action("plan", lord) +} + +function gen_action_prov(lord) { + gen_action("prov", lord) +} + +function gen_action_coin(lord) { + gen_action("coin", lord) +} + +function gen_action_cart(lord) { + gen_action("cart", lord) +} + +function gen_action_mercenaries(lord) { + gen_action("mercenaries", lord) +} + +function gen_action_burgundians(lord) { + gen_action("burgundians", lord) +} + +function gen_action_longbowmen(lord) { + gen_action("longbowmen", lord) +} + +function gen_action_retinue(lord) { + gen_action("retinue", 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_routed_mercenaries(lord) { + gen_action("routed_mercenaries", lord) +} + +function gen_action_routed_longbowmen(lord) { + gen_action("routed_longbowmen", lord) +} + +function gen_action_routed_burgundians(lord) { + gen_action("routed_burgundians", 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) +} + +// === GAME OVER === + +function goto_game_over(result, victory) { + game.state = "game_over" + game.active = "None" + game.result = result + game.victory = victory + log_h1("Game Over") + log(game.victory) + return true +} + +states.game_over = { + get inactive() { + return game.victory + }, + prompt() { + view.prompt = game.victory + }, } -// === COMMON LIBRARY === +// === UTILITY FUNCTIONS === + +function push_state(next) { + if (!states[next]) + throw Error("No such state: " + next) + game.stack.push([ game.state, game.who, game.count ]) + game.state = next +} + +function pop_state() { + [ game.state, game.who, game.count ] = game.stack.pop() +} function clear_undo() { if (game.undo.length > 0) @@ -11816,6 +11786,11 @@ function pop_undo() { game.undo = save_undo } +function roll_die() { + clear_undo() + return random(6) + 1 +} + function random(range) { // An MLCG using integer arithmetic with doubles. // https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf @@ -11849,6 +11824,36 @@ function object_copy(original) { } } +// Packed array of small numbers in one word + +function pack1_get(word, n) { + return (word >>> n) & 1 +} + +function pack2_get(word, n) { + n = n << 1 + return (word >>> n) & 3 +} + +function pack4_get(word, n) { + n = n << 2 + return (word >>> n) & 15 +} + +function pack1_set(word, n, x) { + return (word & ~(1 << n)) | (x << n) +} + +function pack2_set(word, n, x) { + n = n << 1 + return (word & ~(3 << n)) | (x << n) +} + +function pack4_set(word, n, x) { + n = n << 2 + return (word & ~(15 << n)) | (x << n) +} + // Array remove and insert (faster than splice) function array_remove_item(array, item) { @@ -12060,6 +12065,8 @@ function map_delete(map, item) { } } +// === FUZZING ASSERTS === + const mutually_exclusive_lords = [ [LORD_EXETER_1, LORD_EXETER_2], [LORD_SOMERSET_1, LORD_SOMERSET_2], |