diff options
-rw-r--r-- | const.js | 2 | ||||
-rw-r--r-- | events.txt | 34 | ||||
-rw-r--r-- | play.css | 10 | ||||
-rw-r--r-- | play.js | 13 | ||||
-rw-r--r-- | rules.js | 448 |
5 files changed, 490 insertions, 17 deletions
@@ -34,4 +34,4 @@ const SOP_LIMITED_COMMAND = 1 const SOP_COMMAND_DECREE = 2 const SOP_EVENT_OR_COMMAND = 3 const SOP_PASS = 4 -const INELIGIBLE = 5
\ No newline at end of file +const INELIGIBLE = 5 @@ -55,16 +55,42 @@ SHADED 9 log "NOT IMPLEMENTED" EVENT 10 - log "NOT IMPLEMENTED" + current DS + prompt "Move up to 4 Delhi Sultanate Units into adjacent Provinces." + piece_undo_opt 4 is_ds_unit(p) + prompt "Move Delhi Sultanate Unit into an adjacent space." + space_no_undo 1 is_adjacent(s, piece_space(game.vm.p)) + move + endspace + endpiece + resources DS -5 SHADED 10 - log "NOT IMPLEMENTED" + prompt "Move any Qasbah to Spaces containing Governors." + piece_opt all is_qasbah(p) + space 1 has_governor(s) && !has_qasbah(s) + move + endspace + endpiece + prompt "Add up to 2 Troops in each Space with a Qasbah." + space_opt all has_qasbah(s) + place_opt DS TROOPS + place_opt DS TROOPS + endspace EVENT 11 - log "NOT IMPLEMENTED" + prompt "Place up to two Mongol Invaders in each of Mtn Passes and Punjab." + space_opt 2 (s === S_PUNJAB || s === S_MOUNTAIN_PASSES) + place MI TROOPS + place_opt MI TROOPS + endspace SHADED 11 - log "NOT IMPLEMENTED" + current DS + prompt "Remove 4 Mongol Invaders." + piece 4 is_mongol_invader(p) + remove + endpiece EVENT 12 log "NOT IMPLEMENTED" @@ -353,8 +353,8 @@ path.campaign { stroke: black; stroke-dasharray: 4 4; } } #unshaded_event { - left: 3px; - right: 3px; + left: 10px; + right: 10px; top: 200px; height: 70px; border-radius: 12px; @@ -363,11 +363,15 @@ path.campaign { stroke: black; stroke-dasharray: 4 4; } #shaded_event { left: 3px; right: 3px; - bottom: 4px; + bottom: 3px; height: 80px; border-radius: 12px; } +#this_card.card_10 #unshaded_event { height: 80px; } +#this_card.card_10 #shaded_event { height: 69px; } + + /* TOKEN IMAGES */ .token.tributary { background-color: #2a2c26 } @@ -1046,6 +1046,15 @@ function on_update() { // Influence layout_influence() + // Select Faction + action_button("ds", "Delhi Sultanate") + action_button("bk", "Bahmani Kingdom") + action_button("ve", "Vijayanagara Empire") + + confirm_action_button("choose_ds", "Delhi Sultanate", "Choose Delhi Sultanate to execute this event?") + confirm_action_button("choose_bk", "Bahmani Kingdom", "Choose Bahmani Kingdom to execute this event?") + confirm_action_button("choose_ve", "Vijayanagara Empire", "Choose Vijayanagara Empire to execute this event?") + // SOP buttons action_button("command_decree", "Command & Decree") action_button("event_command", "Event or Command") @@ -1102,10 +1111,12 @@ function on_update() { action_button("skip", "Skip") action_button("next", "Next") - action_button("undo", "Undo") + action_button("end_event", "End Event") action_button("end_of_turn", "End your turn") action_button("end_return", "End Influence Shift") + action_button("undo", "Undo") + return @@ -52,6 +52,9 @@ const SINGLE_EVENTS = [26] const first_piece = data.first_piece const last_piece = data.last_piece +const all_first_piece = 0 +const all_last_piece = 103 + const first_space = S_ANDHRA const last_space = S_PUNJAB const last_province = S_TAMILAKAM @@ -255,7 +258,7 @@ exports.setup = function (seed, scenario, _options) { } function setup_deck() { - game.deck = [ 1, 37, 2, 41, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ] + game.deck = [ 10, 37, 11, 2, 41, 3, 4, 5, 6, 7, 8, 9, 10, 1, 12, 13, 14 ] } function setup_standard() { @@ -2366,11 +2369,15 @@ function has_valid_support_attackers(s, faction) { } function gen_place_piece(faction, type) { + console.log("GEN_PLACE ", faction, type) + let can_place = false for_each_piece(faction, type, p => { if (piece_space(p) === AVAILABLE) { gen_action_piece(p) + can_place = true } }) + return can_place } function move_all_faction_piece_from(faction, type, from, to) { @@ -2447,6 +2454,34 @@ function piece_type(p) { throw "IMPOSSIBLE" } +function has_governor(s) { + return has_piece(s, DS, ELITE) +} + +function has_qasbah(s) { + return has_piece(s, DS, DISC) +} + +function is_adjacent(a, b) { + return set_has(SPACES[a].adjacent, b) +} + +function is_piece(p, faction, type) { + return p >= first_piece[faction][type] && p <= last_piece[faction][type] +} + +function is_mongol_invader(p) { + return piece_faction(p) === MI +} + +function is_ds_unit(p) { + return (is_piece(p, DS, ELITE) || is_piece(p, DS, TROOPS)) +} + +function is_qasbah(p) { + return piece_name(p) === "Qasbah" +} + /* CAVALRY */ function available_cavalry() { @@ -2647,6 +2682,34 @@ function gen_action_token(t) { gen_action("token", t) } +function gen_choose_faction(faction) { + if (game.current === faction) { + switch (faction) { + case DS: + view.actions.ds = 1 + break + case BK: + view.actions.bk = 1 + break + case VE: + view.actions.ve = 1 + break + } + } else { + switch (faction) { + case DS: + view.actions.choose_ds = 1 + break + case BK: + view.actions.choose_bk = 1 + break + case VE: + view.actions.choose_ve= 1 + break + } + } +} + /* LOGGING */ function log(msg) { @@ -3096,6 +3159,27 @@ function object_group_by(items, callback) { // === ITERATORS AND ACTION GENERATORS === +function auto_place_piece(s, faction, type) { + if (can_place_piece(s, faction, type)) { + let p = find_piece(AVAILABLE, faction, type) + if (p >= 0) { + place_piece(p, s) + return true + } + } + return false +} + +function can_place_piece(s, faction, type) { + return can_stack_piece(s, faction, type) && has_piece(AVAILABLE, faction, type) +} + +function can_stack_piece(s, faction, type) { + if (type === DISC) + return !has_piece(s, faction, type) + return true +} + function for_each_piece(faction, type, f) { let p0 = first_piece[faction][type] let p1 = last_piece[faction][type] @@ -3136,10 +3220,8 @@ function gen_any_event() { if (set_has(SINGLE_EVENTS, this_card())) { view.actions.event = 1 } else { - console.log("GEN ANY EVENT!") view.actions.unshaded = 1 view.actions.shaded = 1 - console.log(view.actions) } } @@ -3195,11 +3277,89 @@ function goto_vm(proc) { vm_exec() } +function vm_current() { + if (vm_operand(1) !== game.current) + game.state = "vm_current" + else + vm_next() +} + +states.vm_current = { + prompt() { + let list = vm_operand(1) + event_prompt("Select Faction.") + if (Array.isArray(list)) { + if (list.includes(DS)) + gen_choose_faction(DS) + if (list.includes(BK)) + gen_choose_faction(BK) + if (list.includes(VE)) + gen_choose_faction(VE) + } else { + if (list === DS) + gen_choose_faction(DS) + if (list === BK) + gen_choose_faction(BK) + if (list === VE) + gen_choose_faction(VE) + } + }, + choose_ds() { + this.ds() + }, + choose_bk() { + this.bk() + }, + choose_ve() { + this.ve() + }, + ds() { + if (game.current !== DS) + clear_undo() + game.current = DS + log_transfer(faction_name[game.current] + "...") + vm_next() + }, + bk() { + if (game.current !== BK) + clear_undo() + game.current = BK + log_transfer(faction_name[game.current] + "...") + vm_next() + }, + ve() { + if (game.current !== VE) + clear_undo() + game.current = VE + log_transfer(faction_name[game.current] + "...") + vm_next() + } +} + + function vm_exec() { vm_inst(0)() } +function vm_goto(cmd, nop, dir, step) { + let balance = 1 + while (balance > 0) { + game.vm.ip += dir + if (vm_inst(0) === cmd) + --balance + if (vm_inst(0) === nop) + ++balance + if (game.vm.ip < 0 || game.vm.ip > CODE[game.vm.fp].length) + throw "ERROR" + } + game.vm.ip += step + vm_exec() +} + function vm_inst(a) { + console.log("In vm_inst with ", a) + console.log(game.vm) + console.log(CODE[game.vm.fp]) return CODE[game.vm.fp][game.vm.ip][a] } @@ -3208,6 +3368,12 @@ function vm_log() { vm_next() } +function vm_move() { + log("Moved " + piece_name(game.vm.p) + " to S" + game.vm.s + " from S" + piece_space(game.vm.p) + ".") + place_piece(game.vm.p, game.vm.s) + vm_next() +} + function vm_next() { game.vm.ip ++ vm_exec() @@ -3220,6 +3386,28 @@ function vm_operand(a) { return x } +function vm_prompt() { + if (game.vm.prompt) + game.vm._prompt = game.vm.prompt + game.vm.prompt = game.vm.ip + vm_next() +} + +function pop_vm_prompt() { + if (game.vm._prompt) { + game.vm.prompt = game.vm._prompt + delete game.vm._prompt + } else { + game.vm.prompt = 0 + } +} + +function vm_remove() { + log("Removed " + piece_name(game.vm.p) + " from S" + piece_space(game.vm.p) + ".") + remove_piece(game.vm.p) + vm_next() +} + function vm_return() { game.state = "vm_return" } @@ -3232,6 +3420,223 @@ states.vm_return = { end_event, } +// VM: SPACE + +function vm_space() { + let n = vm_inst(3) + let f = vm_inst(4) + if (can_vm_space(n, f)) { + game.state = "vm_space" + } else { + pop_vm_prompt() + game.vm.ss = [] + game.vm.s = -1 + vm_goto(vm_endspace, vm_space, 1, 1) + } +} + +function vm_endspace() { + vm_goto(vm_space, vm_endspace, -1, 0) +} + + +function can_vm_space(n, f) { + if (game.vm.ss.length < n) + for (let s = first_space; s <= last_space; ++s) + if (!set_has(game.vm.ss, s) && f(s)) + return true + return false +} + +states.vm_space = { + prompt() { + let f = vm_inst(4) + view.who = game.vm.p + event_prompt() + for (let s = first_space; s <= last_space; ++s) + if (!set_has(game.vm.ss, s) && f(s)) + gen_action_space(s) + if (game.vm.ss.length >= vm_inst(2)) + view.actions.skip = 1 + }, + space(s) { + if (vm_inst(1)) + push_undo() + set_add(game.vm.ss, s) + game.vm.s = s + vm_next() + }, + skip() { + if (vm_inst(1)) + push_undo() + game.vm.opt = 1 + vm_goto(vm_endspace, vm_space, 1, 1) + }, +} + +// VM: PIECE ITERATOR + +function vm_piece() { + console.log("IN VM_PIECE", can_vm_piece()) + if (can_vm_piece()) { + game.state = "vm_piece" + } else { + pop_vm_prompt() + game.vm.pp = [] + game.vm.p = -1 + vm_goto(vm_endpiece, vm_piece, 1, 1) + } +} + +function vm_endpiece() { + vm_goto(vm_piece, vm_endpiece, -1, 0) +} + +function can_vm_piece() { + let f = vm_inst(4) + if (game.vm.pp.length < vm_inst(3)) + for (let p = all_first_piece; p <= all_last_piece; ++p) + if (piece_space(p) >= 0 && !set_has(game.vm.pp, p) && f(p, piece_space(p))) + return true + return false +} + +states.vm_piece = { + prompt() { + let f = vm_inst(4) + if (game.vm.s >= 0) + view.where = game.vm.s + event_prompt() + for (let p = all_first_piece; p <= all_last_piece; ++p) + if (piece_space(p) >= 0 && !set_has(game.vm.pp, p) && f(p, piece_space(p))) + gen_action_piece(p) + if (game.vm.pp.length >= vm_inst(2)) + view.actions.skip = 1 + }, + piece(p) { + if (vm_inst(1)) + push_undo() + set_add(game.vm.pp, p) + game.vm.p = p + vm_next() + }, + skip() { + if (vm_inst(1)) + push_undo() + game.vm.opt = 1 + vm_goto(vm_endpiece, vm_piece, 1, 1) + }, +} + + +// VM: PLACE PIECE + +function vm_auto_place() { + let faction = vm_operand(3) + let type = vm_operand(4) + if (auto_place_piece(game.vm.s, faction, type)) { + log("Placed " + PIECE_FACTION_TYPE_NAME[faction][type] + " in S" + game.vm.s + ".") + vm_next() + } else { + vm_place() + } +} + +function vm_place() { + if (can_vm_place()) + game.state = "vm_place" + else + vm_next() +} + +function can_vm_place_imp(s, faction, type) { + if (!can_stack_piece(s, faction, type)) + return false + if (game.current === faction) + return true + return has_piece(AVAILABLE, faction, type) +} + +function can_vm_place() { + let faction = vm_operand(3) + let type = vm_operand(4) + if (typeof faction === "object" && typeof type === "object") { + for (let f of faction) + for (let t of type) + if (can_vm_place_imp(game.vm.s, f, t)) + return true + } else if (typeof faction === "object") { + for (let f of faction) + if (can_vm_place_imp(game.vm.s, f, type)) + return true + } else if (typeof type === "object") { + for (let t of type) + if (can_vm_place_imp(game.vm.s, faction, t)) + return true + } else { + if (can_vm_place_imp(game.vm.s, faction, type)) + return true + } + return false +} + +states.vm_place = { + prompt() { + let skip = vm_inst(2) + let faction = vm_operand(3) + let type = vm_operand(4) + let where = SPACE_NAME[game.vm.s] + let possible = false + view.where = game.vm.s + + event_prompt(`Place ${PIECE_FACTION_TYPE_NAME[faction][type]} in ${where}.`) + if (gen_place_piece(faction, type)) + possible = true + if (skip || !possible) + view.actions.skip = 1 + }, + piece(p) { + if (vm_inst(1)) + push_undo() + log("Placed " + piece_name(p) + " in S" + game.vm.s + ".") + place_piece(p, game.vm.s) + vm_next() + }, + skip() { + if (vm_inst(1)) + push_undo() + if (vm_inst(2)) // only flag as optional if opcode is opt + game.vm.opt = 1 + else + log("Did not place piece in S" + game.vm.s + ".") + vm_next() + }, +} + +// VM : RESOURCES + +function vm_resources() { + game.state = "vm_resources" +} + +states.vm_resources = { + prompt() { + let f = vm_operand(1) + let n = vm_operand(2) + if (n >= 0) + event_prompt(`${faction_name[f]} +${n} Resources.`) + else + event_prompt(`${faction_name[f]} ${n} Resources.`) + gen_action_resources(f) + }, + resources(f) { + let n = vm_operand(2) + log_resources(f, n) + add_resources(f, n) + vm_next() + }, +} + // === CONST === // Factions @@ -3269,7 +3674,8 @@ const SOP_LIMITED_COMMAND = 1 const SOP_COMMAND_DECREE = 2 const SOP_EVENT_OR_COMMAND = 3 const SOP_PASS = 4 -const INELIGIBLE = 5// === CONST === +const INELIGIBLE = 5 +// === CONST === const CODE = [] @@ -3383,25 +3789,51 @@ CODE[9 * 2 + 1] = [ // EVENT 10 CODE[10 * 2 + 0] = [ - [ vm_log, "NOT IMPLEMENTED" ], + [ vm_current, DS ], + [ vm_prompt, "Move up to 4 Delhi Sultanate Units into adjacent Provinces." ], + [ vm_piece, true, 0, 4, (p,s)=>is_ds_unit(p) ], + [ vm_prompt, "Move Delhi Sultanate Unit into an adjacent space." ], + [ vm_space, false, 1, 1, (s)=>is_adjacent(s, piece_space(game.vm.p)) ], + [ vm_move ], + [ vm_endspace ], + [ vm_endpiece ], + [ vm_resources, DS, -5 ], [ vm_return ], ] // SHADED 10 CODE[10 * 2 + 1] = [ - [ vm_log, "NOT IMPLEMENTED" ], + [ vm_prompt, "Move any Qasbah to Spaces containing Governors." ], + [ vm_piece, false, 0, 999, (p,s)=>is_qasbah(p) ], + [ vm_space, true, 1, 1, (s)=>has_governor(s) && !has_qasbah(s) ], + [ vm_move ], + [ vm_endspace ], + [ vm_endpiece ], + [ vm_prompt, "Add up to 2 Troops in each Space with a Qasbah." ], + [ vm_space, true, 0, 999, (s)=>has_qasbah(s) ], + [ vm_place, false, 1, DS, TROOPS ], + [ vm_place, false, 1, DS, TROOPS ], + [ vm_endspace ], [ vm_return ], ] // EVENT 11 CODE[11 * 2 + 0] = [ - [ vm_log, "NOT IMPLEMENTED" ], + [ vm_prompt, "Place up to two Mongol Invaders in each of Mtn Passes and Punjab." ], + [ vm_space, true, 0, 2, (s)=>(s === S_PUNJAB || s === S_MOUNTAIN_PASSES) ], + [ vm_place, false, 0, MI, TROOPS ], + [ vm_place, false, 1, MI, TROOPS ], + [ vm_endspace ], [ vm_return ], ] // SHADED 11 CODE[11 * 2 + 1] = [ - [ vm_log, "NOT IMPLEMENTED" ], + [ vm_current, DS ], + [ vm_prompt, "Remove 4 Mongol Invaders." ], + [ vm_piece, false, 4, 4, (p,s)=>is_mongol_invader(p) ], + [ vm_remove ], + [ vm_endpiece ], [ vm_return ], ] |