summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoël Simoneau <simoneaujoel@gmail.com>2024-10-24 21:57:50 -0400
committerJoël Simoneau <simoneaujoel@gmail.com>2024-10-24 21:57:50 -0400
commitdd868f6af39224b8741384ddf05968f6545e425c (patch)
treef9c46e8d4a61ed1b4e109a732316fe4e548b42d7
parent9e162f6ec4e9d215b84026f4f5de40a9abedb0f0 (diff)
downloadvijayanagara-dd868f6af39224b8741384ddf05968f6545e425c.tar.gz
Basic Conscript
-rw-r--r--play.css8
-rw-r--r--play.js58
-rw-r--r--rules.js308
3 files changed, 341 insertions, 33 deletions
diff --git a/play.css b/play.css
index 2272704..d6c5100 100644
--- a/play.css
+++ b/play.css
@@ -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%;
}
diff --git a/play.js b/play.js
index 2dd8d7f..a0f037b 100644
--- a/play.js
+++ b/play.js
@@ -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
diff --git a/rules.js b/rules.js
index 918ec81..b905027 100644
--- a/rules.js
+++ b/rules.js
@@ -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) {