diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 273 |
1 files changed, 259 insertions, 14 deletions
@@ -49,6 +49,10 @@ const last_province = S_TAMILAKAM const faction_name = [ "Delhi Sultanate", "Bahmani Kingdom", "Vijayanagara Empire", "Mongol Invaders" ] const faction_acronyms = [ "ds", "bk", "ve", "mi" ] +const AD = [ "0", '\u2776', '\u2777', '\u2778', '\u2779', '\u277A', '\u277B' ] +const DD = [ "0", '\u2460', '\u2461', '\u2462', '\u2463', '\u2464', '\u2465' ] + + exports.scenarios = [ "Standard", "Solo" ] exports.roles = function (scenario, _options) { @@ -107,6 +111,7 @@ exports.view = function (state, role) { rebel: game.rebel, order: game.order, who: {}, + dice: game.dice, } if (game.result) { @@ -215,6 +220,7 @@ exports.setup = function (seed, scenario, _options) { inf_shift: null, decree: null, vm: null, + dice: [0, 0, 0, 0, 0, 0] } if (scenario === "Solo") @@ -465,6 +471,7 @@ function goto_rebel() { function goto_tax() { init_decree("Tax") + game.decree.count = 1 game.state = "tax" } @@ -536,6 +543,7 @@ states.main_phase = { view.actions.end_of_turn = 1 } }, + attack: goto_attack, build: goto_build, campaign: goto_campaign, collect: goto_collect, @@ -761,7 +769,7 @@ states.strategic_assistance = { states.tax = { prompt() { - if (game.decree.n === 1) { + if (game.decree.count === 1) { view.prompt = `Tax: Collect ${tax_count()} from Controlled Prosperity and Temples.` gen_action_resources(VE) } else { @@ -774,7 +782,7 @@ states.tax = { let t = tax_count() add_resources(game.current, t) logi_resources(VE, t) - game.decree.n = 0 + game.decree.count = 0 }, end_tax: end_decree } @@ -880,11 +888,202 @@ function end_decree() { /* SHARED COMMANDS */ -function can_attack() {} +function can_attack() { + for (let s = first_space; s <= last_space; ++s) + if (can_attack_in_space(s)) + return true + return false +} -function can_attack_in_space(s) {} +function can_attack_in_space(s) { + if (!has_valid_attackers(s, game.current)) + return false -function goto_attack() {} + if (game.current === DS) { + if ( + can_attack_rebel_in_space(s, BK) || + can_attack_rebel_in_space(s, VE) || + has_piece(s, MI, TROOPS) + ) + return true + } else if (game.current === BK) { + if (count_faction_pieces(s, DS) > 0 || count_faction_pieces(s, VE) > 0) + return true + } else if (game.current === VE) { + if (count_faction_pieces(s, DS) > 0 || count_faction_pieces(s, BK) > 0) + return true + } + return false +} + +function can_attack_rebel_in_space(s, faction) { + if ( + count_pieces(s, faction, DISC) === 1 && + count_faction_pieces(s, faction) === 1 + ) + return true + + if (has_rebel(s, faction)) + return true + + return false +} + +function goto_attack() { + init_command("Attack") + game.cmd.attacker = game.current + game.state = "attack" +} + +function goto_attack_select() { + game.cmd.target = [0, 0, 0, 0] + if (game.current === DS) { + game.cmd.target[BK] += can_attack_rebel_in_space(game.cmd.where, BK) + game.cmd.target[VE] += can_attack_rebel_in_space(game.cmd.where, VE) + game.cmd.target[MI] += has_piece(game.cmd.where, MI, TROOPS) + } else if (game.current === BK) { + game.cmd.target[DS] += count_faction_pieces(game.cmd.where, DS) > 0 + game.cmd.target[VE] += count_faction_pieces(game.cmd.where, VE) > 0 + } else if (game.current === VE) { + game.cmd.target[DS] += count_faction_pieces(game.cmd.where, DS) > 0 + game.cmd.target[BK] += count_faction_pieces(game.cmd.where, BK) > 0 + } + + let n = game.cmd.target.reduce((a, c) => a + c, 0); + if (n === 1) { + game.cmd.target = game.cmd.target.indexOf(1) + game.state = "attack_space" + } else { + game.state = "attack_select" + } +} + +function roll_attack(faction_d) { + for (let d = 0; d < 6; ++d) + game.dice[d] = random(6) + 1 + + if (faction_d === BK && has_piece(game.cmd.where, BK, DISC)) + game.dice[3] = 0 +} + +function goto_attack_cavalry() { + game.cmd.clog = false + game.state = "attack_cavalry" +} + +function attack_use_cavalry(d) { + use_cavalry(game.current) + + let is_attacker = (game.current === game.cmd.attacker) + let is_a_die = (d < 4) + + if (is_a_die && is_attacker) { + charge_die(d) + } else if (!is_a_die && is_attacker) { + screen_die(d) + } else if (is_a_die && !is_attacker) { + screen_die(d) + } else if (!is_a_die && !is_attacker) { + charge_die[d] + } +} + +function charge_die(d) { + if (d < 4) { + logi(`.ad ${AD[game.dice[d]]} \u27f6 ${AD[game.dice[d]-1]} charged`) + } else { + logi(`.dd ${DD[game.dice[d]]} \u27f6 ${DD[game.dice[d]-1]} charged`) + } + game.dice[d] -= 1 +} + +function screen_die(d) { + if (d < 4) { + logi(`.ad ${AD[game.dice[d]]} screened`) + } else { + logi(`.dd ${DD[game.dice[d]]} screened`) + } + game.dice[d] = 0 +} + +states.attack = { + prompt() { + if (game.current === DS) + view.prompt = "Attack: Select Spaces with Rebels or Mongol Invaders." + else + view.prompt = "Attack: Select Provinces with enemy pieces." + + if (can_select_cmd_space(1) && can_attack()) { + for (let s = first_space; s <= last_space; ++s) { + if (!is_selected_cmd_space(s) && can_attack_in_space(s)) + gen_action_space(s) + } + } + }, + space(s) { + game.cmd.where = s + log_space(game.cmd.where, "Attack") + goto_attack_select() + } +} + +states.attack_select = { + prompt() { + view.prompt = "Attack: Choose a Faction to attack" + } +} + +states.attack_space = { + prompt() { + view.prompt = "Attack: " + + view.actions.roll = 1 + }, + roll() { + roll_attack() + log(">.ad " + game.dice.slice(0,4).map(d => AD[d]).join(" ")) + log(">.dd " + game.dice.slice(4).map(d => DD[d]).join(" ")) + log_br() + + goto_attack_cavalry() + } +} + +states.attack_cavalry = { + prompt() { + view.prompt = "Attack: Use cavalry to..." + + // game.cmd.attacker + // game.cmd.target + // game.current + // + + // STEP ? + // step 1 - attacker cavalry + // step 2 - defender calvary + + let curr_die = [0, 1, 2, 3, 4, 5] + // TODO: restrict range for MI player + + if (has_cavalry(game.current)) + for (let d of curr_die) + if (game.dice[d] > 1) + gen_action_die(d) + + view.actions.next = 1 + }, + die(d) { + if (!game.cmd.cavalry) { + log(`${faction_name[game.current]} is using Cavalry.`) + game.cmd.cavalry = true + } + + attack_use_cavalry(d) + }, + next() { + + } +} /* DELHI SULTANATE COMMANDS */ @@ -1105,6 +1304,7 @@ function goto_migrate_space() { } states.migrate = { + // TODO: Check Rebel status after migration prompt() { view.prompt = "Migrate: Select a Province as the Migrate destination." @@ -1305,13 +1505,13 @@ function collect_count() { function goto_collect() { init_decree("Collect") - game.decree.n = 1 + game.decree.count = 1 game.state = "collect" } states.collect = { prompt() { - if (game.decree.n > 0) { + if (game.decree.count > 0) { view.prompt = `Collect Tribute: Collect ${collect_count()} from half the Tributaries prosperity` gen_action_resources(DS) } else { @@ -1323,7 +1523,7 @@ states.collect = { let c = collect_count() add_resources(DS, c) logi_resources(DS, c) - game.decree.n = 0 + game.decree.count = 0 goto_cavalry(2, "collect") }, end_collect: end_decree, @@ -1444,7 +1644,7 @@ function can_build_in_space(s) { function goto_build() { init_decree("Build") - game.decree.n = 1 + game.decree.count = 1 game.state = "build" } @@ -1455,7 +1655,7 @@ states.build = { else if (game.current === VE) view.prompt = "Build: Select a Province with a Raja." - if (game.decree.n === 1) { + if (game.decree.count === 1) { for (let s = first_space; s <= last_space; ++s) { if (can_build_in_space(s)) gen_action_space(s) @@ -1473,7 +1673,7 @@ states.build = { place_piece(p, s) log_space(s, "Build") pop_summary() - game.decree.n = 0 + game.decree.count = 0 }, end_build: end_decree, } @@ -1658,12 +1858,13 @@ function trade_count() { function goto_trade() { init_decree("Trade") + game.decree.count = 1 game.state = "trade" } states.trade = { prompt() { - if (game.decree.n === 1) { + if (game.decree.count === 1) { view.prompt = `Trade: Collect ${trade_count()} from Provinces with your presence.` gen_action_resources(BK) } else { @@ -1675,7 +1876,7 @@ states.trade = { let t = trade_count() add_resources(game.current, t) logi_resources(BK, t) - game.decree.n = 0 + game.decree.count = 0 goto_cavalry(trade_cavalry_count(), "trade") }, end_trade: end_decree, @@ -1760,6 +1961,16 @@ function remove_tributary(s) { update_control() } +function has_rebel(s, faction) { + let p0 = first_piece[faction][ELITE] + let p1 = last_piece[faction][ELITE] + for (let p = p0; p <= p1; ++p) + if (piece_space(p) === s) + if (is_rebel(p)) + return true + return false +} + function is_rebel(p) { let faction = piece_faction(p) let piece_index = p - first_piece[faction][ELITE] @@ -1890,6 +2101,20 @@ function has_unmoved_piece(s, faction) { return unmoved } +function has_valid_attackers(s, faction) { + let valid_attacker = false + for_each_movable(faction, p => { + if (piece_space(p) === s) + valid_attacker = true + if (piece_space(p) === s || (has_piece(piece_space(p), faction, DISC) && SPACES[s].adjacent.includes(piece_space(p)))) + if (!game.cmd.pieces || game.cmd.pieces.length === 0) + valid_attacker = true + else if (!set_has(game.cmd.pieces, p)) + valid_attacker = true + }) + return valid_attacker +} + function gen_place_piece(faction, type) { for_each_piece(faction, type, p => { if (piece_space(p) === AVAILABLE) { @@ -2004,6 +2229,22 @@ function trade_cavalry_count() { return 3; } +function find_cavalry(faction) { + for (let c = 0; c <= LAST_CAVALRY; ++c) + if (game.cavalry[c] === faction) + return c + return -1 +} + +function has_cavalry(faction) { + return find_cavalry(faction) != -1 +} + +function use_cavalry(faction) { + let c = find_cavalry(faction) + game.cavalry[c] = AVAILABLE +} + /* INFLUENCE */ function add_influence(faction) { @@ -2127,6 +2368,10 @@ function gen_action(action, argument) { set_add(view.actions[action], argument) } +function gen_action_die(d) { + gen_action("die", d) +} + function gen_action_influence(faction) { gen_action("inf", faction) } @@ -2614,7 +2859,7 @@ function for_each_movable(faction, f) { if (faction === BK) for_each_piece(BK, ELITE, f) else if (faction === VE) - for_each_piece(BK, ELITE, f) + for_each_piece(VE, ELITE, f) else if (faction === DS) { for_each_piece(DS, TROOPS, f) for_each_piece(DS, ELITE, f) |