summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data.js2
-rw-r--r--play.html2
-rw-r--r--play.js75
-rw-r--r--rules.js325
-rw-r--r--tools/gendata.js8
5 files changed, 335 insertions, 77 deletions
diff --git a/data.js b/data.js
index ca1dcbf..a16a55d 100644
--- a/data.js
+++ b/data.js
@@ -1,2 +1,2 @@
-const data = {"areas":[{"id":12,"name":"Oran","type":1,"zone":"V","x":430.6,"y":588.8},{"id":13,"name":"Algiers","type":1,"zone":"IV","x":1185.6,"y":346.8},{"id":14,"name":"Constantine","type":1,"zone":"II","x":2066.6,"y":315.8},{"id":9,"name":"France","type":3,"zone":null,"x":1605,"y":185.3,"w":266,"h":212.4},{"id":10,"name":"Morocco","type":3,"zone":null,"x":109,"y":1765,"w":94,"h":94},{"id":11,"name":"Tunisia","type":3,"zone":null,"x":2499.9,"y":1667.6,"w":94,"h":94},{"id":15,"name":"Barika","type":0,"zone":"I","x":1708.1,"y":1117.9},{"id":16,"name":"Batna","type":2,"zone":"I","x":2185.6,"y":1390.9},{"id":17,"name":"Biskra","type":2,"zone":"I","x":1853.6,"y":1620.9},{"id":18,"name":"Tebessa","type":0,"zone":"I","x":2299.6,"y":1120.9},{"id":19,"name":"Setif","type":0,"zone":"II","x":1917.6,"y":800.8},{"id":20,"name":"Souk Ahras","type":0,"zone":"II","x":2347.6,"y":848.9},{"id":21,"name":"Phillippeville","type":0,"zone":"II","x":2200.6,"y":584.9},{"id":22,"name":"Tizi Ouzou","type":0,"zone":"III","x":1473.6,"y":578.9},{"id":23,"name":"Bordj Bou Arreridj","type":0,"zone":"III","x":1465.6,"y":832.9},{"id":24,"name":"Bougie","type":0,"zone":"III","x":1703.6,"y":616.9},{"id":25,"name":"Medea","type":0,"zone":"IV","x":1212,"y":727},{"id":26,"name":"Orleansville","type":0,"zone":"IV","x":982,"y":780.2},{"id":27,"name":"Mecheria","type":2,"zone":"V","x":234,"y":1485.7},{"id":28,"name":"Tlemcen","type":0,"zone":"V","x":173,"y":1140.9},{"id":29,"name":"Sidi Bel Abbes","type":0,"zone":"V","x":476,"y":1038},{"id":30,"name":"Mostaganem","type":0,"zone":"V","x":741,"y":863.9},{"id":31,"name":"Saida","type":2,"zone":"V","x":501,"y":1419.1},{"id":32,"name":"Mascara","type":2,"zone":"V","x":785,"y":1302.9},{"id":33,"name":"Ain Sefra","type":2,"zone":"V","x":752,"y":1670.9},{"id":34,"name":"Laghouat","type":2,"zone":"V","x":1191,"y":1615.6},{"id":35,"name":"Sidi Aissa","type":2,"zone":"VI","x":1385,"y":1186},{"id":36,"name":"Ain Qussera","type":0,"zone":"VI","x":1070.6,"y":1235.6}],"zones":{"V":["Oran","Mecheria","Tlemcen","Sidi Bel Abbes","Mostaganem","Saida","Mascara","Ain Sefra","Laghouat"],"IV":["Algiers","Medea","Orleansville"],"II":["Constantine","Setif","Souk Ahras","Phillippeville"],"I":["Barika","Batna","Biskra","Tebessa"],"III":["Tizi Ouzou","Bordj Bou Arreridj","Bougie"],"VI":["Sidi Aissa","Ain Qussera"]},"locations":{"DEPLOY":1,"ELIMINATED":2,"I":3,"II":4,"III":5,"IV":6,"V":7,"VI":8,"France":9,"Morocco":10,"Tunisia":11,"Oran":12,"Algiers":13,"Constantine":14,"Barika":15,"Batna":16,"Biskra":17,"Tebessa":18,"Setif":19,"Souk Ahras":20,"Phillippeville":21,"Tizi Ouzou":22,"Bordj Bou Arreridj":23,"Bougie":24,"Medea":25,"Orleansville":26,"Mecheria":27,"Tlemcen":28,"Sidi Bel Abbes":29,"Mostaganem":30,"Saida":31,"Mascara":32,"Ain Sefra":33,"Laghouat":34,"Sidi Aissa":35,"Ain Qussera":36},"units":[{"side":1,"type":0,"class":"fr_xx_2"},{"side":1,"type":0,"class":"fr_xx_4"},{"side":1,"type":0,"class":"fr_xx_9"},{"side":1,"type":0,"class":"fr_xx_12"},{"side":1,"type":0,"class":"fr_xx_13"},{"side":1,"type":0,"class":"fr_xx_14"},{"side":1,"type":0,"class":"fr_xx_19"},{"side":1,"type":0,"class":"fr_xx_20"},{"side":1,"type":0,"class":"fr_xx_21"},{"side":1,"type":0,"class":"fr_xx_29"},{"side":1,"type":0,"class":"fr_xx_27"},{"side":1,"type":0,"class":"fr_xx_25"},{"side":1,"type":0,"class":"fr_xx_7"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_inf"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"}]}
+const data = {"areas":[{"id":12,"name":"Oran","type":1,"zone":"V","x":430.6,"y":588.8},{"id":13,"name":"Algiers","type":1,"zone":"IV","x":1185.6,"y":346.8},{"id":14,"name":"Constantine","type":1,"zone":"II","x":2066.6,"y":315.8},{"id":9,"name":"France","type":3,"zone":null,"x":1605,"y":185.3,"w":266,"h":212.4},{"id":10,"name":"Morocco","type":3,"zone":null,"x":109,"y":1765,"w":94,"h":94},{"id":11,"name":"Tunisia","type":3,"zone":null,"x":2499.9,"y":1667.6,"w":94,"h":94},{"id":15,"name":"Barika","type":0,"zone":"I","x":1708.1,"y":1117.9},{"id":16,"name":"Batna","type":2,"zone":"I","x":2185.6,"y":1390.9},{"id":17,"name":"Biskra","type":2,"zone":"I","x":1853.6,"y":1620.9},{"id":18,"name":"Tebessa","type":0,"zone":"I","x":2299.6,"y":1120.9},{"id":19,"name":"Setif","type":0,"zone":"II","x":1917.6,"y":800.8},{"id":20,"name":"Souk Ahras","type":0,"zone":"II","x":2347.6,"y":848.9},{"id":21,"name":"Phillippeville","type":0,"zone":"II","x":2200.6,"y":584.9},{"id":22,"name":"Tizi Ouzou","type":0,"zone":"III","x":1473.6,"y":578.9},{"id":23,"name":"Bordj Bou Arreridj","type":0,"zone":"III","x":1465.6,"y":832.9},{"id":24,"name":"Bougie","type":0,"zone":"III","x":1703.6,"y":616.9},{"id":25,"name":"Medea","type":0,"zone":"IV","x":1212,"y":727},{"id":26,"name":"Orleansville","type":0,"zone":"IV","x":982,"y":780.2},{"id":27,"name":"Mecheria","type":2,"zone":"V","x":234,"y":1485.7},{"id":28,"name":"Tlemcen","type":0,"zone":"V","x":173,"y":1140.9},{"id":29,"name":"Sidi Bel Abbes","type":0,"zone":"V","x":476,"y":1038},{"id":30,"name":"Mostaganem","type":0,"zone":"V","x":741,"y":863.9},{"id":31,"name":"Saida","type":2,"zone":"V","x":501,"y":1419.1},{"id":32,"name":"Mascara","type":2,"zone":"V","x":785,"y":1302.9},{"id":33,"name":"Ain Sefra","type":2,"zone":"V","x":752,"y":1670.9},{"id":34,"name":"Laghouat","type":2,"zone":"V","x":1191,"y":1615.6},{"id":35,"name":"Sidi Aissa","type":2,"zone":"VI","x":1385,"y":1186},{"id":36,"name":"Ain Qussera","type":0,"zone":"VI","x":1070.6,"y":1235.6}],"zones":{"V":["Oran","Mecheria","Tlemcen","Sidi Bel Abbes","Mostaganem","Saida","Mascara","Ain Sefra","Laghouat"],"IV":["Algiers","Medea","Orleansville"],"II":["Constantine","Setif","Souk Ahras","Phillippeville"],"I":["Barika","Batna","Biskra","Tebessa"],"III":["Tizi Ouzou","Bordj Bou Arreridj","Bougie"],"VI":["Sidi Aissa","Ain Qussera"]},"locations":{"DEPLOY":1,"ELIMINATED":2,"I":3,"II":4,"III":5,"IV":6,"V":7,"VI":8,"France":9,"Morocco":10,"Tunisia":11,"Oran":12,"Algiers":13,"Constantine":14,"Barika":15,"Batna":16,"Biskra":17,"Tebessa":18,"Setif":19,"Souk Ahras":20,"Phillippeville":21,"Tizi Ouzou":22,"Bordj Bou Arreridj":23,"Bougie":24,"Medea":25,"Orleansville":26,"Mecheria":27,"Tlemcen":28,"Sidi Bel Abbes":29,"Mostaganem":30,"Saida":31,"Mascara":32,"Ain Sefra":33,"Laghouat":34,"Sidi Aissa":35,"Ain Qussera":36},"free_deploy_locations":[3,4,5,6,7,8],"units":[{"side":1,"type":0,"class":"fr_xx_2"},{"side":1,"type":0,"class":"fr_xx_4"},{"side":1,"type":0,"class":"fr_xx_9"},{"side":1,"type":0,"class":"fr_xx_12"},{"side":1,"type":0,"class":"fr_xx_13"},{"side":1,"type":0,"class":"fr_xx_14"},{"side":1,"type":0,"class":"fr_xx_19"},{"side":1,"type":0,"class":"fr_xx_20"},{"side":1,"type":0,"class":"fr_xx_21"},{"side":1,"type":0,"class":"fr_xx_29"},{"side":1,"type":0,"class":"fr_xx_27"},{"side":1,"type":0,"class":"fr_xx_25"},{"side":1,"type":0,"class":"fr_xx_7"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":1,"class":"fr_x"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_para"},{"side":1,"type":2,"class":"fr_elite_x_inf"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":2,"class":"fr_elite_x_marine"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":3,"class":"alg_x"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":1,"type":4,"class":"alg_police"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":5,"class":"fln_failek"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":6,"class":"fln_band"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":7,"class":"fln_cadre"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"},{"side":0,"type":8,"class":"fln_front"}]}
if (typeof module !== 'undefined') module.exports = data
diff --git a/play.html b/play.html
index 798fc85..4719dec 100644
--- a/play.html
+++ b/play.html
@@ -49,7 +49,7 @@ main { background-color: dimgray }
.box{position:absolute;box-sizing:border-box;border:4px solid transparent}
.box.loc{border-radius:50%}
.box.action{border-color:lemonchiffon;}
-.box.loc.action{background-color:#fff5;border-color:white;}
+.box.loc.action{border-color:lemonchiffon;}
.box.selected{border-color:yellow;}
.box.tip { border-color: white; }
diff --git a/play.js b/play.js
index d668379..28bc157 100644
--- a/play.js
+++ b/play.js
@@ -21,6 +21,27 @@ const BOX_NAMES = ["UG", "OPS", "PTL", "OC"]
const unit_count = 120
+function is_gov_unit(u) { return (u >= 0 && u <= 39) }
+function is_fln_unit(u) { return (u >= 40 && u <= 119) }
+
+function set_has(set, item) {
+ if (!set)
+ return false
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return true
+ }
+ return false
+}
+
let ui = {
board: document.getElementById("map"),
map: document.getElementById("map"),
@@ -73,7 +94,7 @@ const UNIT_LOC_SHIFT = 3
const UNIT_LOC_MASK = 255 << UNIT_LOC_SHIFT
function is_unit_neutralized(u) {
- return (game.units[u] & UNIT_NEUTRALIZED_MASK) === UNIT_NEUTRALIZED_MASK
+ return (view.units[u] & UNIT_NEUTRALIZED_MASK) === UNIT_NEUTRALIZED_MASK
}
function unit_loc(u) {
@@ -96,6 +117,24 @@ function is_unit_not_dispersed(u) {
return (view.units[u] & UNIT_DISPERSED_MASK) !== UNIT_DISPERSED_MASK
}
+function is_unit_moved(u) {
+ return set_has(view.moved, u)
+}
+
+function is_unit_action(unit) {
+ return !!(view.actions && view.actions.unit && view.actions.unit.includes(unit))
+}
+
+function is_unit_selected(unit) {
+ if (Array.isArray(view.selected))
+ return view.selected.includes(unit)
+ return view.selected === unit
+}
+
+function is_loc_action(x) {
+ return !!(view.actions && view.actions.loc && view.actions.loc.includes(x))
+}
+
let action_register = []
function register_action(e, action, id) {
@@ -136,9 +175,10 @@ let on_init_once = false
function build_units() {
function build_unit(u) {
+ let side = is_gov_unit(u) ? "gov" : "fln"
let elt = ui.units[u] = document.createElement("div")
let klass = data.units[u].class
- elt.className = `counter unit u${u} ${klass}`
+ elt.className = `counter unit ${side} u${u} ${klass}`
elt.addEventListener("mousedown", on_click_unit)
// elt.addEventListener("mouseenter", on_focus_unit)
// elt.addEventListener("mouseleave", on_blur)
@@ -207,10 +247,12 @@ function on_init() {
// Areas
for (let i = 0; i < data.areas.length; ++i) {
+ let id = data.areas[i].id
let name = data.areas[i].name
let type = data.areas[i].type
let e = document.createElement("div")
e.id = `area-${name}`
+ e.my_id = id
e.className = "box"
e.style.left = data.areas[i].x / SCALE + "px"
e.style.top = data.areas[i].y / SCALE + "px"
@@ -225,12 +267,12 @@ function on_init() {
if (type !== COUNTRY) {
for (let j = 0; j < 4; ++j) {
- let e = ui.locations[i * 4 + j] = document.createElement("div")
+ let e = ui.locations[id * 4 + j] = document.createElement("div")
let box_name = BOX_NAMES[j]
e.id = `ops-${name}-${box_name}`
e.className = "box stack loc"
e.addEventListener("mousedown", on_click_loc)
- e.loc = i * 4 + j
+ e.dataset.loc = i * 4 + j
e.style.left = (data.areas[i].x + (j % 2) * 99) / SCALE + "px"
e.style.top = (data.areas[i].y + Math.floor(j / 2) * 99) / SCALE + "px"
e.style.width = 94 / SCALE + "px"
@@ -243,8 +285,17 @@ function on_init() {
build_units()
}
-function update_map() {
+function update_unit(e, u) {
+ e.classList.toggle("disrupted", is_unit_neutralized(u))
+ e.classList.toggle("airmobile", is_unit_airmobile(u))
+ e.classList.toggle("dispersed", is_unit_dispersed(u))
+ e.classList.toggle("action", !view.battle && is_unit_action(u))
+ e.classList.toggle("selected", !view.battle && is_unit_selected(u))
+ e.classList.toggle("moved", is_unit_moved(u))
+ e.classList.toggle("eliminated", unit_loc(u) === ELIMINATED)
+}
+function update_map() {
ui.tracker[view.turn].appendChild(ui.markers.turn)
ui.tracker[view.fln_ap].appendChild(ui.markers.fln_ap)
ui.tracker[view.fln_psl].appendChild(ui.markers.fln_psl)
@@ -262,8 +313,10 @@ function update_map() {
if (loc) {
e.loc = loc
- if (loc === DEPLOY) {
- if (!ui.fln_supply.contains(e))
+ if (data.free_deploy_locations.includes(loc) || loc === DEPLOY) {
+ if (is_gov_unit(u) && !ui.gov_supply.contains(e))
+ ui.gov_supply.appendChild(e)
+ if (is_fln_unit(u) && !ui.fln_supply.contains(e))
ui.fln_supply.appendChild(e)
} else if (loc === ELIMINATED) {
@@ -273,10 +326,18 @@ function update_map() {
if (!ui.locations[loc].contains(e))
ui.locations[loc].appendChild(e)
}
+ update_unit(e, u)
} else {
e.remove()
}
}
+
+ for (let i = 0; i < ui.locations.length; ++i) {
+ let e = ui.locations[i]
+ if (e) {
+ e.classList.toggle("action", is_loc_action(ui.locations[i].loc))
+ }
+ }
}
function on_update() {
diff --git a/rules.js b/rules.js
index f70b35b..d3bdec1 100644
--- a/rules.js
+++ b/rules.js
@@ -5,6 +5,10 @@ const GOV = "Government"
const BOTH = "Both"
const unit_count = 120
+const first_gov_unit = 0
+const last_gov_unit = 39
+const first_fln_unit = 40
+const last_fln_unit = 119
const FR_XX = 0
const FR_X = 1
@@ -25,9 +29,73 @@ var game = null
var view = null
const {
- areas, zones, locations, units
+ areas, zones, locations, units, free_deploy_locations
} = require("./data.js")
+var first_friendly_unit, last_friendly_unit
+var first_enemy_unit, last_enemy_unit
+
+function set_active_player() {
+ clear_undo()
+ if (game.active !== game.phasing) {
+ game.active = game.phasing
+ update_aliases()
+ }
+}
+
+function set_passive_player() {
+ clear_undo()
+ let nonphasing = (game.phasing === GOV ? FLN : GOV)
+ if (game.active !== nonphasing) {
+ game.active = nonphasing
+ update_aliases()
+ }
+}
+
+function set_enemy_player() {
+ if (is_active_player())
+ set_passive_player()
+ else
+ set_active_player()
+}
+
+function is_active_player() {
+ return game.active === game.phasing
+}
+
+function is_passive_player() {
+ return game.active !== game.phasing
+}
+
+function is_gov_player() {
+ return game.active === GOV
+}
+
+function is_fln_player() {
+ return game.active === FLN
+}
+
+function update_aliases() {
+ if (game.active === GOV) {
+ first_friendly_unit = first_gov_unit
+ last_friendly_unit = last_gov_unit
+ first_enemy_unit = first_fln_unit
+ last_enemy_unit = last_fln_unit
+ } else {
+ first_friendly_unit = first_fln_unit
+ last_friendly_unit = last_fln_unit
+ first_enemy_unit = first_gov_unit
+ last_enemy_unit = last_gov_unit
+ }
+}
+
+function load_state(state) {
+ if (game !== state) {
+ game = state
+ update_aliases()
+ }
+}
+
// === UNIT STATE ===
// location (8 bits), dispersed (1 bit), airmobile (1 bit), neutralized (1 bit)
@@ -159,42 +227,28 @@ function find_free_unit_by_type(type) {
throw new Error("cannot find free unit of type: " + type)
}
-// function find_unit(name) {
-// for (let u = 0; u < unit_count; ++u)
-// if (unit_name[u] === name)
-// return u
-// throw new Error("cannot find named block: " + name + unit_name)
-// }
-
-// function is_axis_unit(u) {
-// return u >= first_axis_unit && u <= last_axis_unit
-// }
-
-// function is_german_unit(u) {
-// return (u >= 14 && u <= 33)
-// }
+// === ITERATORS ===
-// function is_elite_unit(u) {
-// return unit_elite[u]
-// }
-
-// function is_artillery_unit(u) {
-// return unit_class[u] === ARTILLERY
-// }
-
-// function unit_cv(u) {
-// if (is_elite_unit(u))
-// return unit_steps(u) * 2
-// return unit_steps(u)
-// }
+function for_each_friendly_unit_in_loc(x, fn) {
+ for (let u = first_friendly_unit; u <= last_friendly_unit; ++u)
+ if (unit_loc(u) === x)
+ fn(u)
+}
-// function unit_hp_per_step(u) {
-// return is_elite_unit(u) ? 2 : 1
-// }
+function for_each_friendly_unit_in_locs(xs, fn) {
+ for (let u = first_friendly_unit; u <= last_friendly_unit; ++u)
+ for (let x of xs)
+ if (unit_loc(u) === x)
+ fn(u)
+}
-// function unit_hp(u) {
-// return unit_steps(u) * unit_hp_per_step(u)
-// }
+function has_friendly_unit_in_locs(xs) {
+ for (let u = first_friendly_unit; u <= last_friendly_unit; ++u)
+ for (let x of xs)
+ if (unit_loc(u) === x)
+ return true
+ return false
+}
// === PUBLIC FUNCTIONS ===
@@ -208,8 +262,16 @@ function gen_action(action, argument) {
view.actions[action].push(argument)
}
+function gen_action_unit(u) {
+ gen_action('unit', u)
+}
+
+function gen_action_loc(x) {
+ gen_action('loc', x)
+}
+
exports.action = function (state, player, action, arg) {
- game = state
+ load_state(state)
let S = states[game.state]
if (action in S)
S[action](arg, player)
@@ -221,13 +283,13 @@ exports.action = function (state, player, action, arg) {
}
exports.view = function(state, player) {
- game = state
+ load_state(state)
view = {
log: game.log,
prompt: null,
scenario: game.scenario,
- current: game.current,
+ phasing: game.phasing,
turn: game.turn,
fln_ap: game.fln_ap,
@@ -245,6 +307,9 @@ exports.view = function(state, player) {
units: game.units,
}
+ if (player === game.active)
+ view.selected = game.selected
+
if (game.state === "game_over") {
view.prompt = game.victory
} else if (player !== game.active && game.active !== BOTH) {
@@ -263,7 +328,7 @@ exports.view = function(state, player) {
}
exports.resign = function (state, player) {
- game = state
+ load_state(state)
if (game.state !== 'game_over') {
if (player === FLN_NAME)
goto_game_over(GOV, "FLN resigned.")
@@ -275,7 +340,6 @@ exports.resign = function (state, player) {
function goto_game_over(result, victory) {
game.state = "game_over"
- game.current = -1
game.active = "None"
game.result = result
game.victory = victory
@@ -293,15 +357,16 @@ states.game_over = {
// === SETUP ===
exports.setup = function (seed, scenario, options) {
- game = {
+ load_state({
seed: seed,
log: [],
undo: [],
- active: null,
+
+ state: null,
selected: -1,
+ phasing: GOV,
+ active: GOV,
- current: 0,
- state: null,
scenario: null,
turn: 0,
@@ -329,7 +394,7 @@ exports.setup = function (seed, scenario, options) {
// logging
summary: null,
- }
+ })
game.scenario = scenario
setup_scenario(scenario)
@@ -429,38 +494,85 @@ function setup_scenario(scenario_name) {
log(`Government PSL=${game.gov_psl}`)
SETUP[scenario_name]()
+ game.phasing = GOV
}
function goto_scenario_setup() {
- game.active = GOV
+ set_active_player()
game.state = "scenario_setup"
- log_h1("Deployment")
+ log_h2(`${game.active} Deployment`)
+ game.selected = []
+ game.summary = {}
}
states.scenario_setup = {
inactive: "setup",
prompt() {
view.prompt = `Setup: ${game.active} Deployment.`
- gen_action("end_deployment")
+ let done = true
+ for_each_friendly_unit_in_locs(free_deploy_locations, u => {
+ gen_action_unit(u)
+ done = false
+ })
+ if (done)
+ gen_action('end_deployment')
+ if (game.selected.length > 0) {
+ for (let i = 0; i < areas.length; ++i) {
+ let loc = areas[i].id
+ for (let j = 0; j < 4; ++j) {
+ gen_action_loc(loc * 4 + j)
+ }
+ }
+ }
+ // XXX
+ gen_action("restart")
+ },
+ unit(u) {
+ set_toggle(game.selected, u)
+ },
+ loc(to) {
+ console.log("loc", to)
+ let list = game.selected
+ game.selected = []
+ push_undo()
+ game.summary[to] = (game.summary[to] | 0) + list.length
+ for (let who of list)
+ set_unit_loc(who, to)
},
end_deployment() {
log(`Deployed`)
- // let keys = Object.keys(game.summary).map(Number).sort((a,b)=>a-b)
- // for (let x of keys)
- // log(`>${game.summary[x]} at #${x}`)
- // game.summary = null
+ let keys = Object.keys(game.summary).map(Number).sort((a,b)=>a-b)
+ for (let x of keys)
+ log(`>${game.summary[x]} at #${x}`)
+ game.summary = null
end_scenario_setup()
+ },
+ restart() {
+ // XXX debug
+ log("Restarting...")
+ goto_restart()
}
}
function end_scenario_setup() {
- game.turn = 1
- goto_random_event()
+ set_enemy_player()
+ if (has_friendly_unit_in_locs(free_deploy_locations)) {
+ goto_scenario_setup()
+ } else {
+ game.selected = -1
+ game.summary = null
+ begin_game()
+ }
}
// === FLOW OF PLAY ===
+function begin_game() {
+ game.turn = 1
+ goto_random_event()
+}
+
function goto_random_event() {
game.active = BOTH
game.state = "random_event"
@@ -751,6 +863,98 @@ function roll_2d6() {
return roll_d6() + roll_d6()
}
+// Array remove and insert (faster than splice)
+
+function array_remove(array, index) {
+ let n = array.length
+ for (let i = index + 1; i < n; ++i)
+ array[i - 1] = array[i]
+ array.length = n - 1
+}
+
+function array_remove_item(array, item) {
+ let n = array.length
+ for (let i = 0; i < n; ++i)
+ if (array[i] === item)
+ return array_remove(array, i)
+}
+
+// insert item at index (faster than splice)
+function array_insert(array, index, item) {
+ for (let i = array.length; i > index; --i)
+ array[i] = array[i - 1]
+ array[index] = item
+ return array
+}
+
+function set_clear(set) {
+ set.length = 0
+}
+
+function set_has(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return true
+ }
+ return false
+}
+
+function set_add(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return set
+ }
+ return array_insert(set, a, item)
+}
+
+function set_delete(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return array_remove(set, m)
+ }
+ return set
+}
+
+function set_toggle(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return array_remove(set, m)
+ }
+ return array_insert(set, a, item)
+}
+
// Fast deep copy for objects without cycles
function object_copy(original) {
if (Array.isArray(original)) {
@@ -777,18 +981,3 @@ function object_copy(original) {
}
}
-// Array remove and insert (faster than splice)
-
-function array_remove(array, index) {
- let n = array.length
- for (let i = index + 1; i < n; ++i)
- array[i - 1] = array[i]
- array.length = n - 1
-}
-
-function array_remove_item(array, item) {
- let n = array.length
- for (let i = 0; i < n; ++i)
- if (array[i] === item)
- return array_remove(array, i)
-}
diff --git a/tools/gendata.js b/tools/gendata.js
index 83b8493..0f49c71 100644
--- a/tools/gendata.js
+++ b/tools/gendata.js
@@ -83,9 +83,16 @@ def_area("Laghouat", REMOTE, "V", 1191, 1615.6)
def_area("Sidi Aissa", REMOTE, "VI", 1385, 1186)
def_area("Ain Qussera", RURAL, "VI", 1070.6, 1235.6)
+
+let free_deploy_locations = []
+for (let l of ["I", "II", "III", "IV", "V", "VI"]) {
+ free_deploy_locations.push(locations[l])
+}
+
data.areas = areas
data.zones = zones
data.locations = locations
+data.free_deploy_locations = free_deploy_locations
let units = []
@@ -130,6 +137,7 @@ def_unit(GOV, EL_X, "fr_elite_x_marine", 3)
def_unit(GOV, AL_X, "alg_x", 6)
def_unit(GOV, POL, "alg_police", 10)
+console.log("last_gov_unit =", units.length)
def_unit(FLN, FAILEK, "fln_failek", 10)
def_unit(FLN, BAND, "fln_band", 24)