summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js253
1 files changed, 176 insertions, 77 deletions
diff --git a/rules.js b/rules.js
index 85f4745..f153cba 100644
--- a/rules.js
+++ b/rules.js
@@ -2,7 +2,9 @@
// TODO: delay pay step if there is no feed or disband to be done
-// TODO: Supply (+ lodya)
+// TODO: Lodya capability during supply!
+// TODO: 2nd edition supply rule - no reuse of transports
+// TODO: 2nd edition ravage cost
// CAPABILITIES
// TODO: Ransom (T)
@@ -404,15 +406,15 @@ function enemy_player() {
return null
}
-function get_global_assets(type) {
+function get_spoils(type) {
return pack4_get(game.assets, type)
}
-function set_global_assets(type, n) {
+function set_spoils(type, n) {
game.assets = pack4_set(game.assets, type, n)
}
-function add_global_assets(type, n) {
+function add_spoils(type, n) {
game.assets = pack4_set(game.assets, type, pack4_get(game.assets) + n)
}
@@ -806,7 +808,7 @@ function is_lord_at_friendly_locale(lord) {
return is_friendly_locale(loc)
}
-function for_each_seat(lord, fn) {
+function for_each_seat(lord, fn, repeat = false) {
let list = data.lords[lord].seats
for (let seat of list)
@@ -815,26 +817,25 @@ function for_each_seat(lord, fn) {
if (is_teutonic_lord(lord)) {
if (has_global_capability(AOW_TEUTONIC_ORDENSBURGEN)) {
for (let commandery of COMMANDERIES)
- if (!set_has(list, commandery))
+ if (repeat || !set_has(list, commandery))
fn(commandery)
}
}
if (is_russian_lord(lord)) {
if (has_global_capability(AOW_RUSSIAN_ARCHBISHOPRIC))
- if (!set_has(list, LOC_NOVGOROD))
+ if (repeat || !set_has(list, LOC_NOVGOROD))
fn(LOC_NOVGOROD)
}
if (lord === LORD_YAROSLAV) {
if (has_conquered_marker(LOC_PSKOV))
- if (!set_has(list, LOC_PSKOV))
+ if (repeat || !set_has(list, LOC_PSKOV))
fn(LOC_PSKOV)
}
}
-function is_lord_at_seat(lord) {
- let here = get_lord_locale(lord)
+function is_lord_seat(lord, here) {
let result = false
for_each_seat(lord, seat => {
if (seat === here)
@@ -843,6 +844,10 @@ function is_lord_at_seat(lord) {
return result
}
+function is_lord_at_seat(lord) {
+ return is_lord_seat(lord, get_lord_locale(lord))
+}
+
function has_free_seat(lord) {
let result = false
for_each_seat(lord, seat => {
@@ -1290,7 +1295,8 @@ exports.setup = function (seed, scenario, options) {
approach: 0,
avoid: 0,
- assets: 0,
+ spoils: 0,
+ supply: 0,
}
update_aliases()
@@ -2275,8 +2281,6 @@ function is_first_march() {
function goto_actions() {
log_h2(`L${game.command}`)
- game.state = "actions"
- game.who = game.command
game.count = 0
game.extra = 0
@@ -2307,6 +2311,15 @@ function goto_actions() {
if (this_lord_has_house_of_suzdal())
++game.extra
}
+
+ resume_actions()
+ update_supply()
+}
+
+function resume_actions() {
+ game.state = "actions"
+ game.who = game.command
+ game.where = NOWHERE
}
function spend_action(cost) {
@@ -2660,8 +2673,8 @@ function march_with_group_3() {
conquer_trade_route(to)
}
- game.where = NOWHERE
- game.state = "actions"
+ resume_actions()
+ update_supply()
}
// === ACTION: MARCH - AVOID BATTLE ===
@@ -2687,13 +2700,13 @@ function stronghold_capacity(loc) {
function spoil_prov(lord) {
log("Discarded Provender.")
add_lord_assets(lord, PROV, -1)
- add_global_assets(PROV, 1)
+ add_spoils(PROV, 1)
}
function spoil_loot(lord) {
log("Discarded Loot.")
add_lord_assets(lord, LOOT, -1)
- add_global_assets(LOOT, 1)
+ add_spoils(LOOT, 1)
}
function can_avoid_battle(to, way) {
@@ -3061,8 +3074,7 @@ function end_siege() {
set_lord_moved(lord, 1)
spend_all_actions()
-
- game.state = "actions"
+ resume_actions()
}
// === ACTION: STORM ===
@@ -3089,27 +3101,49 @@ function can_action_sally(avail) {
function goto_sally() {
log("TODO: Sally")
spend_action(1)
+ resume_actions()
}
// === ACTION: SUPPLY ===
-function can_supply_from_sea() {
+function update_supply() {
+ // TODO: Lodya - select boat OR ship (we count both here...)
+
let season = current_season()
+ let here = get_lord_locale(game.command)
+ let boats = 0
+ let carts = 0
+ let sleds = 0
+ let ships = 0
+
+ if (season === SUMMER) {
+ carts = get_shared_assets(here, CART)
+ }
if (season === SUMMER || season === RASPUTITSA) {
- let here = get_lord_locale(game.command)
- let ships = count_shared_ships() + count_shared_cogs_not_in_locale(here)
- return ships > 0
+ boats = count_shared_boats()
+ ships = count_shared_ships() + count_shared_cogs_not_in_locale(here)
+ }
+ if (season === EARLY_WINTER || season === LATE_WINTER) {
+ sleds = get_shared_assets(here, SLED)
}
+
+ if (ships > 2)
+ ships = 2
+
+ let sources = list_supply_sources(ships)
+ let reachable = filter_reachable_supply_sources(sources, boats, carts, sleds)
+ let supply_seats = filter_usable_supply_seats(reachable)
+ let supply_seaports = filter_usable_supply_seaports(reachable, ships)
+
+ game.supply = { supply_seats, supply_seaports, seats: 2, boats, carts, sleds, ships }
}
-function list_supply_sources() {
+function list_supply_sources(ships) {
let sources = []
- for_each_seat(game.command, seat => {
- set_add(sources, seat)
- })
+ for_each_seat(game.command, seat => { set_add(sources, seat) }, false)
- if (can_supply_from_sea()) {
+ if (ships > 0) {
if (game.active === TEUTONS)
for (let port of data.seaports)
set_add(sources, port)
@@ -3120,8 +3154,23 @@ function list_supply_sources() {
return sources
}
-function search_supply_rec(result, seen, sources, here, boats, carts, sleds) {
- console.log("search", "".padStart(16-(boats+carts+sleds), "-"), here)
+function filter_reachable_supply_sources(sources, boats, carts, sleds) {
+ let result = []
+ search_supply_reachable(result, [], sources, get_lord_locale(game.command), boats, carts, sleds)
+ return result
+}
+
+function search_supply_reachable(result, seen, sources, here, boats, carts, sleds) {
+ if (set_has(seen, here))
+ return
+
+ if (has_unbesieged_enemy_lord(here))
+ return
+ if (is_unbesieged_enemy_stronghold(here))
+ return
+ if (is_friendly_territory(here) && has_conquered_marker(here))
+ if (!has_siege_marker(here))
+ return
set_add(seen, here)
@@ -3130,72 +3179,120 @@ function search_supply_rec(result, seen, sources, here, boats, carts, sleds) {
if (boats > 0)
for (let next of data.locales[here].adjacent_by_waterway)
- search_supply_rec(result, seen, sources, next, boats - 1, carts, sleds)
+ search_supply_reachable(result, seen, sources, next, boats - 1, carts, sleds)
if (carts > 0)
for (let next of data.locales[here].adjacent_by_trackway)
- search_supply_rec(result, seen, sources, next, boats, carts - 1, sleds)
+ search_supply_reachable(result, seen, sources, next, boats, carts - 1, sleds)
if (sleds > 0)
for (let next of data.locales[here].adjacent)
- search_supply_rec(result, seen, sources, next, boats, carts, sleds - 1)
+ search_supply_reachable(result, seen, sources, next, boats, carts, sleds - 1)
set_delete(seen, here)
}
-function search_supply() {
- let sources = list_supply_sources()
- let start = get_lord_locale(game.command)
-
- let result = []
-
- let boats = get_global_assets(BOAT)
- let carts = get_global_assets(CART)
- let sleds = get_global_assets(SLED)
-
- console.log("SUPPLY", sources, boats, carts, sleds)
-
- search_supply_rec(result, [], sources, start, boats, carts, sleds)
+function filter_usable_supply_seats(reachable) {
+ let sources = []
+ for_each_seat(
+ game.command,
+ (seat) => {
+ if (set_has(reachable, seat))
+ sources.push(seat)
+ },
+ true
+ )
+ return sources
+}
- console.log(" => ", result)
+function filter_usable_supply_seaports(reachable, ships) {
+ if (ships > 0) {
+ let sources = []
+ if (game.active === TEUTONS) {
+ for (let port of data.seaports) {
+ if (set_has(reachable, port)) {
+ set_add(sources, port)
+ }
+ }
+ }
+ if (game.active === RUSSIANS) {
+ if (set_has(reachable, LOC_NOVGOROD)) {
+ set_add(sources, LOC_NOVGOROD)
+ }
+ }
+ return sources
+ }
+ return null
}
function can_action_supply(avail) {
if (avail < 1)
return false
- return true
+ return can_supply()
+}
+
+function can_supply() {
+ if (game.supply.seats > 0 && game.supply.supply_seats.length > 0)
+ return true
+ if (game.supply.ships > 0 && game.supply.supply_seaports.length > 0)
+ return true
+ return false
}
function goto_supply() {
push_undo()
- game.state = "supply"
+ game.state = "supply_source"
+}
- // TODO: Lodya - select boat OR ship (we count both here...)
+states.supply_source = {
+ prompt() {
+ if (!can_supply()) {
+ view.prompt = "Supply: No valid supply sources."
+ return
+ }
- let season = current_season()
- let here = get_lord_locale(game.command)
- let boats = 0, carts = 0, sleds = 0, ships = 0
- if (season === SUMMER) {
- carts = get_shared_assets(here, CART)
- }
- if (season === SUMMER || season === RASPUTITSA) {
- boats = count_shared_boats()
- ships = count_shared_ships() + count_shared_cogs_not_in_locale(here)
- }
- if (season === EARLY_WINTER || season === LATE_WINTER) {
- sleds = get_shared_assets(here, SLED)
- }
+ view.prompt = "Supply: Select supply source and route -"
+
+ if (game.supply.boats > 0)
+ view.prompt += ` ${game.supply.boats} boat`
+ if (game.supply.carts > 0)
+ view.prompt += ` ${game.supply.carts} cart`
+ if (game.supply.sleds > 0)
+ view.prompt += ` ${game.supply.sleds} sled`
+ if (game.supply.ships > 0)
+ view.prompt += ` ${game.supply.ships} ship`
+
+ if (game.supply.seats > 0)
+ for (let source of game.supply.supply_seats)
+ gen_action_locale(source)
+ if (game.supply.ships > 0)
+ for (let source of game.supply.supply_seaports)
+ gen_action_locale(source)
+ view.actions.end_supply = 1
+ },
+ locale(source) {
+ // TODO: 2nd ed - no reusing of transports!
- set_global_assets(BOAT, boats)
- set_global_assets(CART, carts)
- set_global_assets(SLED, sleds)
- set_global_assets(SHIP, ships)
-}
+ if (game.supply.supply_seats.includes(source)) {
+ log(`Supplied from seat at %${source}.`)
+ array_remove_item(game.supply.supply_seats, source)
+ game.supply.seats--
+ } else {
+ log(`Supplied from seaport at %${source}.`)
+ game.supply.ships--
+ }
-states.supply = {
- prompt() {
- view.prompt = "Supply: Select supply sources."
+ add_lord_assets(game.command, PROV, 1)
- search_supply()
+ if (!can_supply())
+ end_supply()
},
+ end_supply: end_supply,
+}
+
+function end_supply() {
+ game.supply = 0
+ spend_action(1)
+ resume_actions()
+ update_supply()
}
// === ACTION: FORAGE ===
@@ -3219,6 +3316,7 @@ function goto_forage() {
log(`Foraged at %${here}`)
add_lord_assets(game.command, PROV, 1)
spend_action(1)
+ resume_actions()
}
// === ACTION: RAVAGE ===
@@ -3329,8 +3427,7 @@ function ravage_location(here, there) {
}
spend_action(1)
-
- game.state = "actions"
+ resume_actions()
}
// === ACTION: TAX ===
@@ -3364,6 +3461,7 @@ function goto_tax() {
add_lord_assets(game.command, COIN, 1)
spend_all_actions()
+ resume_actions()
if (lord_has_capability(game.command, AOW_RUSSIAN_VELIKY_KNYAZ)) {
logi("Veliky Knyaz")
@@ -3445,7 +3543,8 @@ states.sail = {
if (overflow > 0) {
view.prompt = `Sailing with ${ships} ships and ${horses} horses. Discard loot or provender.`
- // TODO: how strict is greed?
+ // TODO: stricter greed!
+ // TODO: if 1 ship, 1 loot, 1 prov - cannot discard prov then loot!
if (loot > 0 || prov > 0) {
for (let lord of game.group) {
if (loot > 0)
@@ -3493,8 +3592,8 @@ states.sail = {
conquer_trade_route(to)
spend_all_actions()
-
- game.state = "actions"
+ resume_actions()
+ update_supply()
},
}