diff options
author | Joël Simoneau <simoneaujoel@gmail.com> | 2024-10-24 21:57:50 -0400 |
---|---|---|
committer | Joël Simoneau <simoneaujoel@gmail.com> | 2024-10-24 21:57:50 -0400 |
commit | dd868f6af39224b8741384ddf05968f6545e425c (patch) | |
tree | f9c46e8d4a61ed1b4e109a732316fe4e548b42d7 | |
parent | 9e162f6ec4e9d215b84026f4f5de40a9abedb0f0 (diff) | |
download | vijayanagara-dd868f6af39224b8741384ddf05968f6545e425c.tar.gz |
Basic Conscript
-rw-r--r-- | play.css | 8 | ||||
-rw-r--r-- | play.js | 58 | ||||
-rw-r--r-- | rules.js | 308 |
3 files changed, 341 insertions, 33 deletions
@@ -113,6 +113,14 @@ path.tip { stroke: white; stroke-dasharray: 4 4; } border: 2px solid lime; } +.space.action { + background-color: white; opacity: 0.3; stroke: white; +} + +.space.selected { + background-color: gold; opacity: 0.3; stroke: white; +} + .space.province { border-radius: 50%; } @@ -147,28 +147,7 @@ const S_VE_INF_4 = 25 const last_province = S_PUNJAB -const space_name = [ - "Andhra", - "Bengal", - "Gondwana", - "Gujarat", - "Jaunpur", - "Karnataka", - "Madhyadesh", - "Maharashtra", - "Malwa", - "Orissa", - "Rajput Kingdoms", - "Sindh", - "Tamilakam", - "Delhi", - "Mountain Passes", - "Punjab", - "Mongol Invaders", - "DS Available", - "BK Available", - "VE Available", -] +const space_name = data.space_name /* LAYOUT DATA */ // :r !node tools/parse-layout.js @@ -277,25 +256,25 @@ const layout = { }, "rects": { "available_boxes": { - "Mongol Invaders": { + "MI_available": { "x": 24, "y": 100, "w": 177, "h": 110 }, - "DS Available": { + "DS_available": { "x": 796, "y": 91, "w": 388, "h": 223 }, - "BK Available": { + "BK_available": { "x": 21, "y": 908, "w": 238, "h": 224 }, - "VE Available": { + "VE_available": { "x": 21, "y": 1405, "w": 239, @@ -525,7 +504,7 @@ function init_ui() { ui.unshaded_event.onmouseenter = on_focus_unshaded_event ui.unshaded_event.onmouseleave = on_focus_this_event - for (let s = 0; s < 20; ++s) { + for (let s = 0; s < 26; ++s) { let e = null // provinces @@ -945,6 +924,10 @@ function on_update() { layout_pieces(items, xy[0], xy[1], discs, VE) } + if (s <= S_VE_AVAILABLE) { + ui.spaces[s].classList.toggle("action", is_action("space", s)) + ui.spaces[s].classList.toggle("selected", view.where === s) + } } items.length = 0 @@ -967,8 +950,29 @@ function on_update() { layout_sop() + // Action for pieces + if (view.actions && view.actions.piece) + for (let i = 0; i < ui.pieces.length; ++i) + ui.pieces[i].classList.toggle("action", set_has(view.actions.piece, i)) + else + for (let i = 0; i < ui.pieces.length; ++i) + ui.pieces[i].classList.remove("action") + for (let i = 0; i < ui.pieces.length; ++i) + ui.pieces[i].classList.toggle("selected", view.who === i) + + // SOP buttons + action_button("command_decree", "Command & Decree") + action_button("event_command", "Event or Command") + action_button("lim_command", "Limited Command") action_button("pass", "Pass") + // DS buttons + action_button("conscript", "Conscript") + action_button("end_conscript", "End Conscript") + + action_button("next", "Next") + action_button("undo", "Undo") + return @@ -39,6 +39,10 @@ const S_VE_INF_2 = 23 const S_VE_INF_3 = 24 const S_VE_INF_4 = 25 +const first_piece = data.first_piece +const last_piece = data.last_piece +const first_space = S_ANDHRA +const last_space = S_PUNJAB const faction_name = [ "Delhi Sultanate", "Bahmani Kingdom", "Vijayanagara Empire", "Mongol Invaders" ] @@ -183,7 +187,15 @@ exports.setup = function (seed, scenario, _options) { tributary: 8191, // all 13 provinces rebel: 0, // amir/raja rebel status pieces: Array(104).fill(AVAILABLE), // piece locations + cavalry: [0, 0, 0], deck: [], + op: { + type: null, + limited: null, + free: null, + spaces: [], + where: null, + } } if (scenario === "Solo") @@ -242,7 +254,7 @@ function setup_standard() { } function setup_piece(faction, type, count, where) { - for (let p = data.first_piece[faction][type]; count > 0; ++p) { + for (let p = first_piece[faction][type]; count > 0; ++p) { if (piece_space(p) < 0) { set_piece_space(p, where) --count @@ -267,12 +279,11 @@ function goto_card() { function resume_event_card() { clear_undo() - let did_1st = (did_option(SOP_COMMAND_DECREE) || did_option(SOP_EVENT_OR_COMMAND)) - let did_2nd = (did_option(SOP_COMMAND_DECREE) || did_option(SOP_EVENT_OR_COMMAND)) - if (did_1st && did_2nd) - end_card() - else + if (game.cylinder.includes(ELIGIBLE)) goto_eligible() + else + end_card() + } function end_card() { @@ -347,6 +358,31 @@ function goto_pass() { resume_event_card() } +function goto_conscript() { + init_command("Conscript") + game.state = "conscript" + } + + function goto_conscript_space() { + push_summary() + + if (conscript_count() === 1) { + let p = find_piece(AVAILABLE, game.current, TROOPS) + log_summary_place(p) + place_piece(p, game.op.where) + end_conscript_space() + } else { + game.op.count = 0 + game.state = "conscript_space" + } + } + + function end_conscript_space() { + log_space(game.op.where, "Conscript") + pop_summary() + game.state = "conscript" + } + /* STATES */ states.eligible = { @@ -359,10 +395,254 @@ states.eligible = { view.prompt = `${data.card_title[this_card()]}: Command & Decree.` } else { view.prompt = `${data.card_title[this_card()]}: Event or Command & Decree.` + view.actions.command_decree = 1 + view.actions.event_command = 1 } + view.actions.lim_command = 1 view.actions.pass = 1 + view.actions.undo = 0 }, pass: goto_pass, + command_decree() { + push_undo() + game.cylinder[game.current] = SOP_COMMAND_DECREE + game.state = "command_decree" + }, + event_command() { + push_undo() + game.cylinder[game.current] = SOP_EVENT_OR_COMMAND + game.state = "event_command" + }, + lim_command() { + push_undo() + game.cylinder[game.current] = SOP_LIMITED_COMMAND + game.op.limited = 1 + game.state = "lim_command" + }, +} + +states.command_decree = { + inactive: "Command & Decree", + prompt() { + view.prompt = "Select a Command and a Decree!" + gen_any_command() + }, + conscript: goto_conscript, +} + +states.event_command = { + inactive: "Event or Command", + prompt() { + view.prompt = "Select the card Event or a Command!" + gen_any_command() + }, + conscript: goto_conscript, +} + +states.lim_command = { + inactive: "Limited command", + prompt() { + view.prompt = "Select a limited Command!" + gen_any_command() + }, + conscript: goto_conscript, +} + +states.conscript = { + prompt() { + view.prompt = "Conscript: Select Tributaries, Qasbah or Dehli to place troops." + + if (can_select_cmd_space(1)) { + for (let s = first_space; s <= last_space; ++s) { + if (!is_selected_cmd_space(s)) + gen_action_space(s) + } + } + + view.actions.end_conscript = prompt_end_cmd(1) + }, + space(s) { + push_undo() + select_cmd_space(s, 1) + goto_conscript_space() + }, + end_conscript: end_command, +} + +states.conscript_space = { + prompt() { + view.prompt = `Conscript: Place up to ${conscript_count()} Troops.` + view.where = game.op.where + view.actions.next = 1 + + gen_place_piece(DS, TROOPS) + }, + piece(p) { + log_summary_place(p) + place_piece(p, game.op.where) + if (++game.op.count >= conscript_count()) + end_conscript_space() + }, + next() { + end_conscript_space() + } +} + +/* COMMANDS */ + +function init_command(type) { + push_undo() + if (game.op.limited) + log_h2(faction_name[game.current] + " - Limited " + type) + else + log_h2(faction_name[game.current] + " - " + type) + game.op.type = type +} + +function gen_any_command() { + if (game.current === DS) { + view.actions.conscript = can_conscript() ? 1 : 0 + } else if (game.current === BK || game.current === VE) { + // view.actions.rally = can_rally() ? 1 : 0 + } +} + +function can_select_cmd_space(cost) { + if (!game.op.free && game.resources[game.current] < cost) + return false + if (game.op.limited) + return game.op.spaces.length === 0 + return true +} + +function select_cmd_space(s, cost) { + set_add(game.op.spaces, s) + if (!game.op.free) + game.resources[game.current] -= cost + game.op.where = s +} + +function can_conscript() { + if (!has_piece(AVAILABLE, DS, TROOPS)) + return false + return true +} + +function conscript_count() { + if (game.op.where === S_DELHI) + return 5 + if (has_piece(game.op.where, DS, DISC)) { + return 2 + } + return 1 +} + +function prompt_end_cmd(cost) { + if (!view.actions.space) { + if (game.op.limited && game.op.spaces && game.op.spaces.length > 0) + view.prompt = game.op.type + ": Done." + else if (!game.op.free && game.resources[game.current] < cost) + view.prompt = game.op.type + ": No resources." + else + view.prompt = game.op.type + ": Done." + } + return (game.op.spaces.length > 0) ? 1 : 0 +} + +function end_command() { + log_br() + game.op = null + resume_event_card() +} + +/* MISC SPACE + PIECE QUERIES */ + +function has_piece(s, faction, type) { + let first = first_piece[faction][type] + let last = last_piece[faction][type] + for (let p = first; p <= last; ++p) + if (piece_space(p) === s) + return true + return false +} + +function find_piece(s, faction, type) { + let first = first_piece[faction][type] + let last = last_piece[faction][type] + for (let p = first; p <= last; ++p) + if (piece_space(p) === s) + return p + return -1 +} + +function gen_place_piece(faction, type) { + let p0 = first_piece[faction][type] + let p1 = last_piece[faction][type] + let can_place = false + for (let p = p0; p <= p1; ++p) { + if (piece_space(p) === AVAILABLE) { + gen_action_piece(p) + can_place = true + if (type === DISC) + break + } + } + return can_place +} + +function is_selected_cmd_space(s) { + return game.op.spaces && set_has(game.op.spaces, s) +} + +function place_piece(p, s) { + if (piece_space(p) === AVAILABLE) + p = find_piece(AVAILABLE, piece_faction(p), piece_type(p)) + + set_piece_space(p, s) +} + +function piece_faction(p) { + if (p >= first_piece[MI][TROOPS] && p <= last_piece[MI][TROOPS]) + return MI + if (p >= first_piece[DS][TROOPS] && p <= last_piece[DS][TROOPS]) + return DS + if (p >= first_piece[DS][ELITE] && p <= last_piece[DS][ELITE]) + return DS + if (p >= first_piece[BK][ELITE] && p <= last_piece[BK][ELITE]) + return BK + if (p >= first_piece[VE][ELITE] && p <= last_piece[VE][ELITE]) + return VE + if (p >= first_piece[DS][DISC] && p <= last_piece[DS][DISC]) + return DS + if (p >= first_piece[BK][DISC] && p <= last_piece[BK][DISC]) + return BK + if (p >= first_piece[VE][DISC] && p <= last_piece[VE][DISC]) + return VE + throw "IMPOSSIBLE" +} + +function piece_name(p) { + return PIECE_FACTION_TYPE_NAME[piece_faction(p)][piece_type(p)] +} + +function piece_type(p) { + if (p >= first_piece[MI][TROOPS] && p <= last_piece[MI][TROOPS]) + return TROOPS + if (p >= first_piece[DS][TROOPS] && p <= last_piece[DS][TROOPS]) + return TROOPS + if (p >= first_piece[DS][ELITE] && p <= last_piece[DS][ELITE]) + return ELITE + if (p >= first_piece[BK][ELITE] && p <= last_piece[BK][ELITE]) + return ELITE + if (p >= first_piece[VE][ELITE] && p <= last_piece[VE][ELITE]) + return ELITE + if (p >= first_piece[DS][DISC] && p <= last_piece[DS][DISC]) + return DISC + if (p >= first_piece[BK][DISC] && p <= last_piece[BK][DISC]) + return DISC + if (p >= first_piece[VE][DISC] && p <= last_piece[VE][DISC]) + return DISC + throw "IMPOSSIBLE" } /* UTILS */ @@ -372,6 +652,22 @@ function add_resources(faction, n) { } +/* ACTIONS */ + +function gen_action(action, argument) { + if (!(action in view.actions)) + view.actions[action] = [] + set_add(view.actions[action], argument) +} + +function gen_action_piece(p) { + gen_action("piece", p) +} + +function gen_action_space(s) { + gen_action("space", s) +} + /* LOGGING */ function log(msg) { |