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