diff options
-rw-r--r-- | data.js | 38 | ||||
-rw-r--r-- | play.css | 1 | ||||
-rw-r--r-- | play.js | 94 | ||||
-rw-r--r-- | rules.js | 470 | ||||
-rw-r--r-- | tools/gendata.js | 42 |
5 files changed, 480 insertions, 165 deletions
@@ -196,25 +196,25 @@ lords:[ {"side":"Lancaster","name":"Warwick L","full_name":"Richard Neville","title":"Earl of Warwick","seats":[51],"marshal":1,"influence":5,"lordship":3,"command":2,"valour":2,"forces":{"retinue":1,"men_at_arms":3,"longbowmen":3},"assets":{"cart":2,"prov":2,"coin":2},"ships":2,"id":"lord_warwick_l"}, ], vassals:[ -{"service":1,"name":"Norfolk","seat":"Arundel","influence":0}, -{"service":1,"name":"Stanley","seat":"Derby","influence":0}, -{"service":3,"name":"Fauconberg","seat":"Dover","influence":2}, -{"service":2,"name":"Devon","seat":"Exeter","influence":-1}, -{"service":3,"name":"Suffolk","seat":"Ipswich","influence":1}, -{"service":1,"name":"Bonville","seat":"Launceston","influence":1}, -{"service":1,"name":"Dudley","seat":"Leicester","influence":0}, -{"service":1,"name":"Beaumont","seat":"Arundel","influence":-2}, -{"service":2,"name":"Oxford","seat":"Oxford","influence":0}, -{"service":2,"name":"Shrewsbury","seat":"Shrewsbury","influence":-1}, -{"service":1,"name":"Essex","seat":"St Albans","influence":0}, -{"service":3,"name":"Westmoreland","seat":"Appleby","influence":0}, -{"service":3,"name":"Worcester","seat":"Worcester","influence":0}, -{"service":0,"name":"Trollope","seat":"none","influence":0,"capability":"Andrew Trollope"}, -{"service":0,"name":"Clifford","seat":"none","influence":0,"capability":"My father's blood"}, -{"service":0,"name":"Edward","seat":"none","influence":0,"capability":"Edward"}, -{"service":0,"name":"Thomas Stanley","seat":"none","influence":0,"capability":"Thomas Stanley"}, -{"service":0,"name":"Montagu","seat":"none","influence":0,"capability":"Alice Montagu"}, -{"service":0,"name":"Hastings","seat":"none","influence":0,"capability":"Hastings"}, +{"service":1,"name":"Norfolk","seat":[14],"influence":0}, +{"service":1,"name":"Stanley","seat":[44],"influence":0}, +{"service":3,"name":"Fauconberg","seat":[17],"influence":2}, +{"service":2,"name":"Devon","seat":[21],"influence":-1}, +{"service":3,"name":"Suffolk","seat":[35],"influence":1}, +{"service":1,"name":"Bonville","seat":[23],"influence":1}, +{"service":1,"name":"Dudley","seat":[42],"influence":0}, +{"service":1,"name":"Beaumont","seat":[49],"influence":-2}, +{"service":2,"name":"Oxford","seat":[28],"influence":0}, +{"service":2,"name":"Shrewsbury","seat":[10],"influence":-1}, +{"service":1,"name":"Essex","seat":[31],"influence":0}, +{"service":3,"name":"Westmoreland","seat":[2],"influence":0}, +{"service":3,"name":"Worcester","seat":[46],"influence":0}, +{"service":0,"name":"Trollope","seat":[],"influence":0,"capability":"Andrew Trollope"}, +{"service":0,"name":"Clifford","seat":[],"influence":0,"capability":"My father's blood"}, +{"service":0,"name":"Edward","seat":[],"influence":0,"capability":"Edward"}, +{"service":0,"name":"Thomas Stanley","seat":[],"influence":0,"capability":"Thomas Stanley"}, +{"service":0,"name":"Montagu","seat":[],"influence":0,"capability":"Alice Montagu"}, +{"service":0,"name":"Hastings","seat":[],"influence":0,"capability":"Hastings"}, ], cards:[ {"name":"Y1","event":"Leeward Battle Line","when":"hold","capability":"Culverins and falconets","this_lord":true,"lords":[0,1,2,3,4,5,6,7,8,9,10,11,12,13]}, @@ -25,6 +25,7 @@ header.your_turn { background-color: orange; } #log .card_tip { font-style: italic; } #log .card_tip:hover { text-decoration: underline; } #log .lord_tip:hover { cursor: pointer; text-decoration: underline; } +#log .lord_vassal:hover { cursor: pointer; text-decoration: underline; } #log .locale_tip:hover { cursor: pointer; text-decoration: underline; } .action { @@ -36,6 +36,12 @@ function pack4_get(word, n) { return (word >>> n) & 15 } +function pack8_get(word, n) { + n = n << 4 + return (word >>> n) & 255 +} + + // === CONSTANTS (matching those in rules.js) === function find_lord(name) { return data.lords.findIndex((x) => x.name === name) } @@ -90,6 +96,9 @@ const last_aow_card = last_lancaster_card const first_locale = 0 const last_locale = data.locales.length - 1 +const first_vassal = 0 +const last_vassal = data.vassals.length - 1 + const Y1 = find_card("Y1") const Y2 = find_card("Y2") const Y3 = find_card("Y3") @@ -186,8 +195,8 @@ const asset_type_count = 4 const asset_action_name = [ "prov", "coin", "cart", "ship" ] const asset_type_x34 = [ 1, 1, 1, 0] -const VASSAL_READY = 1 -const VASSAL_MUSTERED = 2 +const VASSAL_UNAVAILABLE = -1 +const VASSAL_READY = -2 const NOWHERE = -1 const CALENDAR = 100 @@ -375,6 +384,31 @@ function count_favour(type) { return n; } +function is_vassal_ready(vassal) { + return pack8_get(view.pieces.vassals[vassal], 0) === VASSAL_READY +} + + +function is_vassal_unavailable(vassal) { + return view.pieces.vassals[vassal] === VASSAL_UNAVAILABLE +} + + +function get_vassal_locale(vassal) { + return pack8_get(view.pieces.vassal[vassal], 1) +} + +function get_vassals_with_lord(lord) { + let results = [] + for (let x = first_vassal; x < last_vassal; x++) { + if (pack8_get(view.pieces.vassal[x], 0) === lord) { + results.push(x) + } + } + + return results +} + function is_york_locale(loc) { return loc >= first_york_locale && loc <= last_york_locale } @@ -596,7 +630,7 @@ const ui = { } let locale_layout = [] -let calendar_layout_service = [] +let calendar_layout_vassal = [] let calendar_layout_cylinder = [] function clean_name(name) { @@ -776,6 +810,7 @@ function build_map() { e.style.backgroundSize = small + "px" register_action(e, "vassal", ix) register_tooltip(e, data.vassalbox[ix].name) + e.classList.toggle("hide", !is_vassal_ready(ix)) document.getElementById("pieces").appendChild(e) }) @@ -940,7 +975,7 @@ function layout_locale_cylinders(loc) { function layout_calendar() { for (let loc = 1; loc <= 16; ++loc) { let [cx, cy] = calendar_xy[loc] - let list = calendar_layout_service[loc] + let list = calendar_layout_vassal[loc] for (let i = 0; i < list.length; ++i) { let e = list[i] let x = cx, y = cy, z = 60 - i @@ -990,6 +1025,22 @@ function layout_track() { } } +function add_vassal(parent, vassal, lord, routed) { + let elt + if (routed) { + if (is_action(routed_force_action_name[VASSAL], lord)) + elt = get_cached_element("action unit " + force_action_name[VASSAL] + " " + data.vassals[vassal].name, routed_force_action_name[VASSAL], lord) + else + elt = get_cached_element("action unit " + force_action_name[VASSAL] + " " + data.vassals[vassal].name, routed_force_action_name[VASSAL], lord) + } else { + if (is_action(routed_force_action_name[VASSAL], lord)) + elt = get_cached_element("action unit " + force_action_name[VASSAL] + " " + data.vassals[vassal].name, force_action_name[VASSAL], lord) + else + elt = get_cached_element("action unit " + force_action_name[VASSAL] + " " + data.vassals[vassal].name, force_action_name[VASSAL], lord) + } + parent.appendChild(elt) +} + function add_force(parent, type, lord, routed) { let elt if (routed) { @@ -1018,9 +1069,14 @@ function add_asset(parent, type, n, lord) { function update_forces(parent, forces, lord_ix, routed) { parent.replaceChildren() for (let i = 0; i < force_type_count; ++i) { - let n = pack4_get(forces, i) - for (let k = 0; k < n; ++k) { - add_force(parent, i, lord_ix, routed) + if (i === VASSAL) { + get_vassals_with_lord(lord_ix) + .forEach(v => add_vassal(v, parent)) + } else { + let n = pack4_get(forces, i) + for (let k = 0; k < n; ++k) { + add_force(parent, i, lord_ix, routed) + } } } } @@ -1362,12 +1418,27 @@ function update_court() { lcourt.appendChild(ui.lord_mat[lord]) } +function update_calendar_vassals() { + for (let v in data.vassals) { + let info = data.vassals[v] + let e = ui.map_vassals[v] + e.classList.add("hide") + if (!is_vassal_ready(v) && get_vassal_locale(v) !== 0) { + calendar_layout_vassal[get_vassal_locale(v) - CALENDAR] = e + e.classList.remove("hide") + e.classList.toggle("action", is_action("vassal", v)) + e.classList.add("vassal" + clean_name(info.name)) + e.classList.add("back", is_vassal_unavailable(v)) + } + } +} + function on_update() { restart_cache() for (let i = 0; i <= 16; ++i) { calendar_layout_cylinder[i] = [] - calendar_layout_service[i] = [] + calendar_layout_vassal[i] = [] } for (let i = 0; i < data.locales.length; ++i) @@ -1382,6 +1453,7 @@ function on_update() { } } + update_calendar_vassals() layout_calendar() //layout_track() @@ -1588,6 +1660,11 @@ function sub_lord_name(match, p1) { return `<span class="lord_tip" onclick="on_click_lord_tip(${x})">${n}</span>` } +function sub_vassal_name(match, x) { + let n = data.vassals[x].name + return `<span class="vassal_tip" >${n}</span>` +} + function on_log(text) { let p = document.createElement("div") @@ -1609,6 +1686,7 @@ function on_log(text) { text = text.replace(/E(\d+)/g, sub_card_event) text = text.replace(/L(\d+)/g, sub_lord_name) text = text.replace(/%(\d+)/g, sub_locale_name) + text = text.replace(/V(\d+)/g, sub_vassal_name) if (text.match(/^\.h1/)) { text = text.substring(4) @@ -119,6 +119,14 @@ function 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 @@ -134,6 +142,8 @@ const last_lancaster_locale = 73 const first_locale = 0 const last_locale = data.locales.length - 1 +const first_vassal = 0 +const last_vassal = vassal_count - 1 const first_york_card = 0 @@ -323,6 +333,25 @@ const LOC_SCARBOROUGH = find_locale("Scarborough") const LOC_RAVENSPUR = find_locale("Ravenspur") +const VASSAL_BEAUMONT = find_vassal('Beaumont') +const VASSAL_BONVILLE = find_vassal('Bonville') +const VASSAL_DEVON = find_vassal('Devon') +const VASSAL_DUDLEY = find_vassal('Dudley') +const VASSAL_ESSEX = find_vassal('Essex') +const VASSAL_FAUCONBERG = find_vassal('Fauconberg') +const VASSAL_NORFOLK = find_vassal('Norfolk') +const VASSAL_OXFORD = find_vassal('Oxford') +const VASSAL_SHREWSBURY = find_vassal('Shrewsbury') +const VASSAL_STANLEY1 = find_vassal('Stanley1') +const VASSAL_SUFFOLK = find_vassal('Suffolk') +const VASSAL_WESTMORLD = find_vassal('Westmorld') +const VASSAL_WORCESTER = find_vassal('Worcester') +const VASSAL_CLIFFORD = find_vassal('Clifford') +const VASSAL_EDWARD = find_vassal('Edward') +const VASSAL_HASTINGS = find_vassal('Hastings') +const VASSAL_MONTAGU = find_vassal('Montagu') +const VASSAL_STANLEY2 = find_vassal('Stanley2') +const VASSAL_TROLLOPE = find_vassal('Trollope') const AOW_LANCASTER_CULVERINS_AND_FALCONETS = [ L1, L2 ] // TODO const AOW_LANCASTER_MUSTERD_MY_SOLDIERS = L3 // TODO @@ -473,9 +502,9 @@ const EVENT_YORK_PATRICK_DE_LA_MOTE = Y37 // TODO // Check all push/clear_undo -const VASSAL_UNAVAILABLE = 0 -const VASSAL_READY = 1 -const VASSAL_MUSTERED = 2 +const VASSAL_UNAVAILABLE = -1 +const VASSAL_READY = -2 +//const VASSAL_MUSTERED = -3 const NOBODY = -1 const NOWHERE = -1 @@ -718,11 +747,18 @@ function get_lord_routed_forces(lord, n) { } function lord_has_unrouted_units(lord) { - return game.pieces.forces[lord] !== 0 + return game.pieces.forces[lord] !== 0 || get_vassals_with_lord(lord).some(v => game.battle.routed_vassals[lord] === 0 || !game.battle.routed_vassals[lord].includes(v)) } function lord_has_routed_units(lord) { - return game.pieces.routed[lord] !== 0 + return game.pieces.routed[lord] !== 0 || (game.battle.routed_vassals[lord] !== 0 && game.battle.routed_vassals[lord].length > 0) +} + +function rout_vassal(lord, vassal) { + if (game.battle.routed_vassals[lord] == 0) + game.battle.routed_vassals[lord] = [] + + set_add(game.battle.routed_vassals[lord], vassal) } function set_lord_locale(lord, locale) { @@ -732,6 +768,8 @@ function set_lord_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] } @@ -1061,12 +1099,92 @@ function is_lord_ready(lord) { return loc >= CALENDAR && loc <= CALENDAR + (game.turn >> 1) } +function setup_vassals(excludes = []) { + for (let x = first_vassal; x < last_vassal; x++) { + if (!excludes.includes(x) && data.vassals[x].capability === undefined) { + set_vassal_ready(x) + set_vassal_on_map(data.vassals[x].seat[0]) + } + } +} + +function set_vassal_on_map(vassal, loc) { + game.pieces.vassal[vassal] = pack8_set(game.pieces.vassal[vassal], 1, loc) +} + +function get_vassal_locale(vassal) { + return pack8_get(game.pieces.vassal[vassal], 1) +} + +function get_vassals_with_lord(lord) { + let results = [] + for (let x = first_vassal; x < last_vassal; x++) { + if (pack8_get(game.pieces.vassal[x], 0) === lord) { + results.push(x) + } + } + + return results +} + +function set_vassal_ready(vassal) { + game.pieces.vassal[vassal] = pack8_set(game.pieces.vassals[vassal], 0, VASSAL_READY) +} + +function set_vassal_on_calendar(vassal, turn) { + game.pieces.vassal[vassal] = pack8_set(game.pieces.vassal[vassal], 1, turn + CALENDAR) +} + +function set_vassal_with_lord(vassal, lord) { + game.pieces.vassal[vassal] = pack8_set(game.pieces.vassal[vassal], 0, lord) +} + +function set_vassal_unavailable(vassal) { + game.pieces.vassal[vassal] = pack8_set(game.pieces.vassals[vassal], 0, VASSAL_UNAVAILABLE) +} + +function muster_vassal(vassal, lord) { + set_vassal_with_lord(vassal, lord) + if (data.vassals[vassal].service !== 0) + set_vassal_on_calendar(vassal, current_turn() + (6 - data.vassals[vassal].service)) +} + +function disband_vassal(vassal) { + let new_turn = current_turn() + (6 - data.vassals[vassal].service) + set_vassal_unavailable(vassal) + set_vassal_on_calendar(vassal, new_turn) + log(`Disbanded V${vassal} to turn ${current_turn() + (6 - data.vassals[vassal].service)}.`) + +} + +function pay_vassal(vassal) { + if (current_turn() < 15) + set_vassal_on_calendar(vassal, current_turn() + 1) +} + +function get_ready_vassals() { + let favor = game.active === YORK ? game.pieces.favoury : game.pieces.favourl + let results = [] + + for (let x = first_vassal; x < last_vassal; x++) { + if (is_vassal_ready(x) && favor.includes(x)) { + results.push(x) + } + } + + return results +} + +function is_vassal_unavailable(vassal) { + return pack8_get(game.pieces.vassals[vassal], 0) === VASSAL_UNAVAILABLE +} + function is_vassal_ready(vassal) { - return game.pieces.vassals[vassal] === VASSAL_READY + return pack8_get(game.pieces.vassals[vassal], 0) === VASSAL_READY } function is_vassal_mustered(vassal) { - return game.pieces.vassals[vassal] === VASSAL_MUSTERED + return pack8_get(game.pieces.vassals[vassal]) >= 0 } function is_york_lord(lord) { @@ -1504,30 +1622,6 @@ function muster_lord(lord, locale) { muster_lord_forces(lord) } -/* -function disband_vassal(vassal) { - let info = data.vassals[vassal] - let lord = data.vassals[vassal].lord - - add_lord_forces(lord, KNIGHTS, -(info.forces.knights | 0)) - add_lord_forces(lord, SERGEANTS, -(info.forces.sergeants | 0)) - add_lord_forces(lord, LIGHT_HORSE, -(info.forces.light_horse | 0)) - add_lord_forces(lord, ASIATIC_HORSE, -(info.forces.asiatic_horse | 0)) - add_lord_forces(lord, MEN_AT_ARMS, -(info.forces.men_at_arms | 0)) - add_lord_forces(lord, MILITIA, -(info.forces.militia | 0)) - add_lord_forces(lord, SERFS, -(info.forces.serfs | 0)) - - game.pieces.vassals[vassal] = VASSAL_READY - - if (!lord_has_unrouted_units(lord)) { - disband_lord(lord) - } -} */ - -function muster_vassal(lord, vassal) { - game.pieces.vassals[vassal] = VASSAL_MUSTERED - muster_vassal_forces(lord, vassal) -} function draw_card(deck) { let i = random(deck.length) @@ -1674,6 +1768,8 @@ function setup_Ia() { add_favoury_marker(LOC_LUDLOW) add_favoury_marker(LOC_BURGUNDY) add_favoury_marker(LOC_IRELAND) + + setup_vassals() } @@ -1720,6 +1816,9 @@ function setup_Ib() { 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() { @@ -1768,6 +1867,7 @@ function setup_Ic() { add_favoury_marker(LOC_BURGUNDY) add_favoury_marker(LOC_IRELAND) + setup_vassals() } @@ -1808,6 +1908,8 @@ function setup_II() { add_favoury_marker(LOC_EXETER) add_favoury_marker(LOC_BURGUNDY) + setup_vassals([VASSAL_DEVON, VASSAL_OXFORD]) + } @@ -1836,6 +1938,8 @@ function setup_III() { add_favoury_marker(LOC_ARUNDEL) add_favoury_marker(LOC_YORK) add_favoury_marker(LOC_GLOUCESTER) + + setup_vassals([VASSAL_OXFORD, VASSAL_NORFOLK]) } @@ -1858,6 +1962,8 @@ function setup_ItoIII() { set_lord_cylinder_on_calendar(LORD_WARWICK_Y, 3) set_lord_cylinder_on_calendar(LORD_RUTLAND, 5) + setup_vassals() + } // setup will be used in some scenarios @@ -2449,10 +2555,11 @@ states.levy_muster_lord = { } // Muster Ready Vassal Forces - /* for (let vassal of data.lords[game.who].vassals) { - if (is_vassal_ready(vassal)) + if (is_friendly_locale(get_lord_locale(game.who))) { + for (let vassal of get_ready_vassals()) { gen_action_vassal(vassal) - }*/ + } + } // Add Transport if (is_seaport(get_lord_locale(game.who)) && get_lord_assets(game.who, SHIP) < 2) @@ -2493,8 +2600,7 @@ states.levy_muster_lord = { vassal(vassal) { push_undo() - muster_vassal(game.who, vassal) - resume_levy_muster_lord() + goto_muster_vassal(vassal) }, take_ship() { @@ -3315,6 +3421,38 @@ states.parley = { // 2) The other vassal marker is placed, face down, on the calendar, a number of boxes right to current turn + 6 - service // (a service 3 disbanding in turn 8 will come back turn 11) +function goto_muster_vassal(vassal) { + game.what = vassal + push_state("levy_vassal") + init_influence_check(game.who) + game.check.push({cost: 0, modifier: data.vassals[vassal].influence * (game.active === LANCASTER? -1 : 1), source: "vassal"}) +} + +function end_muster_vassal() { + pop_state() + end_influence_check() + resume_levy_muster_lord() +} + +states.levy_vassal = { + inactive: "Levy Vassal", + prompt() { + view.prompt = `Levy Vassal V${game.what}. ` + + prompt_influence_check() + }, + spend1:add_influence_check_modifier_1, + spend3:add_influence_check_modifier_2, + check() { + let results = do_influence_check() + log(`Attempt to levy V${game.what} ${results.success ? "Successful" : "Failed"}: (${range(results.rating)}) ${results.success ? HIT[results.roll] : MISS[results.roll]}`) + + if (success) { + muster_vassal(game.who, game.what) + } + end_muster_vassal() + } +} // === ACTION: MARCH === @@ -3459,19 +3597,16 @@ function march_with_group_2() { } function march_with_group_3() { - let here = get_lord_locale(game.command) // Disbanded in battle! - if (here === NOWHERE) { + if (!is_lord_on_map(game.command)) { game.march = 0 spend_all_actions() resume_command() return } - game.march = 0 - resume_command() } @@ -3988,6 +4123,7 @@ function init_battle(here) { -1, -1, -1, ], valour: Array(lord_count).fill(0), + routed_vassals: Array(lord_count).fill([]), engagements: [], reserves: [], retreated: 0, @@ -4896,9 +5032,9 @@ function prompt_hit_forces() { if (get_lord_forces(lord, MILITIA) > 0) gen_action_militia(lord) -// get_vassals_with_lord(lord) -// .filter(v => !game.battle.routed_vassals[lord].includes(v)) -// .forEach(gen_action_vassal) + get_vassals_with_lord(lord) + .filter(v => !game.battle.routed_vassals[lord].includes(v)) + .forEach(gen_action_vassal) }) } @@ -4972,8 +5108,12 @@ function will_lord_rout(lord) { } function rout_unit(lord, type) { - add_lord_forces(lord, type, -1) - add_lord_routed_forces(lord, type, 1) + if (type === VASSAL) { + rout_vassal(lord, special) + } else { + add_lord_forces(lord, type, -1) + add_lord_routed_forces(lord, type, 1) + } } function which_lord_capability(lord, list) { @@ -5181,9 +5321,8 @@ function goto_battle_influence() { if (game.battle.loser !== BOTH) { set_active_loser() - // Need to add Vassals to this calculation let influence = get_defeated_lords() - .map(l => data.lords[l].influence) + .map(l => data.lords[l].influence + get_vassals_with_lord(l).length) .reduce((p, c) => p+c,0) reduce_influence(influence) @@ -5427,6 +5566,15 @@ states.death_or_disband = { function goto_battle_aftermath() { set_active(game.battle.attacker) + // Routed Vassals get disbanded + for (let lord = first_lord; lord <= last_lord; lord++) { + if (is_lord_on_map(lord) && game.battle.routed_vassals[lord] !== 0) { + for (let vassal of game.battle.routed_vassals[lord]) { + disband_vassal(vassal) + } + } + } + // Events discard_events("hold") @@ -5691,32 +5839,165 @@ states.pay = { } function end_pay() { - - // NOTE: We can combine Pay & Disband steps because disband is mandatory only. game.who = NOBODY set_active_enemy() if (game.active === P2) { goto_pay() } else - goto_levy_muster() - -// goto_disband() -} + goto_pay_lords() -// === LEVY & CAMPAIGN: DISBAND === +} -function has_friendly_lord_who_must_disband() { +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_disband() { - game.state = "disband" - if (!has_friendly_lord_who_must_disband()) - end_disband() +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() { + prompt_held_event() + let done = true + + 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 + } + } else { + view.actions.disband = 1 + view.actions.pay = 1 + } + }, + lord(lord) { + game.who = lord + }, + disband() { + disband_lord(game.who) + game.who = NOBODY + }, + pay() { + reduce_influence(is_exile_box(get_lord_locale(game.who)) ? 2 : 1) + set_lord_moved(game.who, 0) + game.who = NOBODY + }, + 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(v) && is_friendly_lord(get_lord_with_vassal(v)) && get_vassal_locale(v) === CALENDAR + current_turn()) { + vassal_to_pay = true + } + } + if (vassal_to_pay) { + game.state = "pay_vassals" + game.what = NOTHING + } else { + end_pay_vassals() + } +} + +function end_pay_vassals() { + set_active_enemy() + + if (game.active === P1) { + //goto_muster_exiles() + goto_ready_vassals() + } 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 === NOTHING) { + + for (let v = first_vassal; v < last_vassal; v++) { + if (is_vassal_mustered(v) && is_friendly_lord(get_lord_with_vassal(v)) && get_vassal_locale(v) === CALENDAR + current_turn()) { + gen_action_vassal(v) + done = false + } + } + + if (done) { + view.actions.done = 1 + } + } else { + view.actions.pay = 1 + view.actions.disband = 1 + } + }, + vassal(v) { + game.what = v + }, + pay() { + pay_vassal(game.what) + reduce_influence(1) + game.what = NOBODY + }, + disband() { + 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 (is_vassal_unavailable(vassal) && get_vassal_locale(vassal) === CALENDAR + current_turn()) { + set_vassal_ready(vassal) + set_vassal_on_map(vassal, data.vassals[vassal].seat[0]) + } + } + + goto_levy_muster() } +// === LEVY & CAMPAIGN: DISBAND === + function disband_lord(lord, permanently = false) { let here = get_lord_locale(lord) let turn = current_turn() @@ -5742,67 +6023,12 @@ function disband_lord(lord, permanently = false) { set_lord_moved(lord, 0) - // for (let v of data.lords[lord].vassals) - // game.pieces.vassals[v] = VASSAL_UNAVAILABLE -} - -states.disband = { - inactive: "Disband", - prompt() { - view.prompt = "Disband: You must Disband Lords at their Service limit." - - prompt_held_event() - - let done = true - for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { - } - if (done) - view.actions.end_disband = 1 - }, - service_bad(lord) { - this.lord(lord) - }, - lord(lord) { - if (is_lord_besieged(lord) && can_ransom_lord_siege(lord)) { - clear_undo() - goto_ransom(lord) - } else { - push_undo() - disband_lord(lord) - } - }, - end_disband() { - end_disband() - }, - card: action_held_event, -} - -function end_ransom_disband() { - // do nothing -} - -function end_disband() { - clear_undo() - - if (is_campaign_phase()) { - if (check_campaign_victory()) - return - } - - set_active_enemy() - if (is_campaign_phase()) { - if (is_active_command()) - goto_remove_markers() - else - goto_feed() - } else { - if (game.active === P1) - goto_levy_muster() - else - goto_feed() + for (let v of get_vassals_with_lord(lord)) { + disband_vassal(v) } } + // === CAMPAIGN: REMOVE MARKERS === function goto_remove_markers() { @@ -6427,6 +6653,16 @@ function pack4_set(word, n, x) { return (word & ~(15 << n)) | (x << n) } +function pack8_get(word, n) { + n = n << 4 + return (word >>> n) & 255 +} + +function pack8_set(word, n, x) { + n = n << 4 + return (word & ~(255 << n)) | (x << n) +} + // === COMMON LIBRARY === function clear_undo() { diff --git a/tools/gendata.js b/tools/gendata.js index 38e240d..a05c325 100644 --- a/tools/gendata.js +++ b/tools/gendata.js @@ -804,7 +804,7 @@ locales.forEach(loc => { }) function seats(list) { - return list.split(", ").map(name => locmap[name]).sort(cmpnum) + return list.split(", ").filter(name => name !== "").map(name => locmap[name]).sort(cmpnum) } let lords = [ @@ -1684,26 +1684,26 @@ lords.forEach(lord => { lord.id = "lord_" + clean_name(lord.name) }) -vassal(1, "Norfolk", "Arundel", 0) -vassal(1, "Stanley", "Derby", 0) -vassal(3, "Fauconberg", "Dover", 2) -vassal(2, "Devon", "Exeter", -1) -vassal(3, "Suffolk", "Ipswich", 1) -vassal(1, "Bonville", "Launceston", 1) -vassal(1, "Dudley", "Leicester", 0) -vassal(1, "Beaumont", "Arundel", -2) -vassal(2, "Oxford", "Oxford", 0) -vassal(2, "Shrewsbury", "Shrewsbury", -1) -vassal(1, "Essex", "St Albans", 0) -vassal(3, "Westmoreland", "Appleby", 0) -vassal(3, "Worcester", "Worcester", 0) - -vassal(0, "Trollope", "none", 0, "Andrew Trollope") -vassal(0, "Clifford", "none", 0, "My father's blood") -vassal(0, "Edward", "none", 0, "Edward") -vassal(0, "Thomas Stanley", "none", 0, "Thomas Stanley") -vassal(0, "Montagu", "none", 0, "Alice Montagu") -vassal(0, "Hastings", "none", 0, "Hastings") +vassal(1, "Norfolk", seats("Arundel"), 0) +vassal(1, "Stanley", seats("Derby"), 0) +vassal(3, "Fauconberg", seats("Dover"), 2) +vassal(2, "Devon", seats("Exeter"), -1) +vassal(3, "Suffolk", seats("Ipswich"), 1) +vassal(1, "Bonville", seats("Launceston"), 1) +vassal(1, "Dudley", seats("Leicester"), 0) +vassal(1, "Beaumont", seats("Lincoln"), -2) +vassal(2, "Oxford", seats("Oxford"), 0) +vassal(2, "Shrewsbury", seats("Shrewsbury"), -1) +vassal(1, "Essex", seats("St Albans"), 0) +vassal(3, "Westmoreland", seats("Appleby"), 0) +vassal(3, "Worcester", seats("Worcester"), 0) + +vassal(0, "Trollope", seats(''), 0, "Andrew Trollope") +vassal(0, "Clifford", seats(''), 0, "My father's blood") +vassal(0, "Edward", seats(''), 0, "Edward") +vassal(0, "Thomas Stanley", seats(''), 0, "Thomas Stanley") +vassal(0, "Montagu", seats(''), 0, "Alice Montagu") +vassal(0, "Hastings", seats(''), 0, "Hastings") print("const data = {") print("seaports:" + JSON.stringify(seaports) + ",") |