summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-03-05 20:34:55 +0100
committerTor Andersson <tor@ccxvii.net>2023-05-03 18:48:15 +0200
commit2d66908b20d43a39974f99a0ec74212eaa5031ed (patch)
tree12d8197c8110604fbfb61afac5ff21d400bedfed
parent71c0e5bc691ce148515ebf4d6e2fba96c85c2ccb (diff)
downloadandean-abyss-2d66908b20d43a39974f99a0ec74212eaa5031ed.tar.gz
Patrol, Sweep & Assault.
TODO: LimOp Patrol - only move to one destination.
-rw-r--r--data.js2
-rw-r--r--play.css13
-rw-r--r--play.js21
-rw-r--r--rules.js552
-rw-r--r--tools/gendata.js10
5 files changed, 500 insertions, 98 deletions
diff --git a/data.js b/data.js
index 0feac9c..d1a3bb5 100644
--- a/data.js
+++ b/data.js
@@ -1,2 +1,2 @@
-const data = {"first_space":0,"first_pop":1,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":31,"first_loc":32,"last_loc":49,"last_space":49,"coastal_spaces":[11,12,13,17],"card_name":[null,"1st Division","Ospina & Mora","Tapias","Caño Limón - Coveñas","Occidental & Ecopetrol","Oil Spill","7th Special Forces","Fuerza Aérea Colombiana","High Mountain Battalions","Blackhawks","National Defense & Security Council","Plan Colombia","Plan Meteoro","Tres Esquinas","War Tax","Coffee Prices","Madrid Donors","NSPD-18","General Offensive","Mono Jojoy","Raúl Reyes","Alfonso Cano","DoD Contractors","Operación Jaque","Ejército de Liberación Nacional","Gramaje","Misil Antiaéreo","Hugo Chávez","Kill Zone","Peace Commission","Betancourt","Secuestrados","Sucumbíos","Airdropped AKs","Crop Substitution","Zona de Convivencia","Former Military","National Coordination Center","Soldados Campesinos","Demobilization","Mancuso","Senado & Cámara","Calima Front","Colombia Nueva","Los Derechos Humanos","Limpieza","Pinto & del Rosario","Unión Sindical Obrera","Bloques","Carabineros","Pipeline Repairs","Castaño","Criminal Air Force","Deserters & Defectors","DEA Agents","Drogas La Rebaja","Op Millennium","General Serrano","Salcedo","The Chess Player","Air Bridge","Amazonía","Narco-War","Cocaine Labs","Poppies","Tingo María","Mexican Traffickers","Narco-Subs","Riverines & Fast Boats","Ayahuasca Tourism","Darién","Sicarios","Propaganda!","Propaganda!","Propaganda!","Propaganda!"],"card_order":[null,[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,3,2],[0,1,3,2],[0,1,3,2],[0,2,1,3],[0,2,1,3],[0,2,1,3],[0,2,3,1],[0,2,3,1],[0,2,3,1],[0,3,1,2],[0,3,1,2],[0,3,1,2],[0,3,2,1],[0,3,2,1],[0,3,2,1],[1,0,2,3],[1,0,2,3],[1,0,2,3],[1,0,3,2],[1,0,3,2],[1,0,3,2],[1,2,0,3],[1,2,0,3],[1,2,0,3],[1,2,3,0],[1,2,3,0],[1,2,3,0],[1,3,0,2],[1,3,0,2],[1,3,0,2],[1,3,2,0],[1,3,2,0],[1,3,2,0],[2,0,1,3],[2,0,1,3],[2,0,1,3],[2,0,3,1],[2,0,3,1],[2,0,3,1],[2,1,0,3],[2,1,0,3],[2,1,0,3],[2,1,3,0],[2,1,3,0],[2,1,3,0],[2,3,0,1],[2,3,0,1],[2,3,0,1],[2,3,1,0],[2,3,1,0],[2,3,1,0],[3,0,1,2],[3,0,1,2],[3,0,1,2],[3,0,2,1],[3,0,2,1],[3,0,2,1],[3,1,0,2],[3,1,0,2],[3,1,0,2],[3,1,2,0],[3,1,2,0],[3,1,2,0],[3,2,0,1],[3,2,0,1],[3,2,0,1],[3,2,1,0],[3,2,1,0],[3,2,1,0],[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,2,3]],"space_name":["Bogotá","Cali","Medellín","Bucaramanga","Ibagué","Santa Marta","Cartagena","Cúcuta","Neiva","Pasto","Sincelejo","Atlántico","Chocó","Nariño","Meta West","Guaviare","Putumayo","Cesar","Antioquia","Santander","Huila","Arauca","Meta East","Vichada","Guainía","Vaupés","Amazonas","Panamá","Ecuador","Brasil","Perú","Venezuela","Santa Marta / Cartagena","Cali / Pasto","Neiva / Pasto","Bogotá / San José","Pasto / Tumaco","Cali / Buenaventura","Cartagena / Sincelejo","Sincelejo / Medellín","Medellín / Ibagué","Ibagué / Cali","Bucaramanga / Ibagué / Bogotá","Bogotá / Neiva","Cúcuta / Arauca","Bogotá / Yopal","Santa Marta / Ayacucho","Bucaramanga / Ayacucho","Sincelejo / Ayacucho","Cúcuta / Ayacucho"],"spaces":[{"type":"city","id":"Bogota","pop":8,"adjacent":[14,19,20,22,35,42,43,45]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,33,37,41]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,39,40]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,42,47]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,40,41,42]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,32,46]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,32,38]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,31,44,49]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,34,43]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,28,33,34,36]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,38,39,48]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,32,38,46,47,48,49]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,27,37,39,40,41]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,28,33,36,37]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,35,43]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,35]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,28,30,34]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,31,46,47,48,49]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,39,40,42,46,47,48,49]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,31,42,44,45,46,47,48,49]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,33,34,41,42,43]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,31,44,45]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,35,45]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24,31]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25,29,31]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26,29]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25,29,30]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,36]},{"type":"foreign","id":"Brasil","pop":0,"adjacent":[24,25,26]},{"type":"foreign","id":"Peru","pop":0,"adjacent":[16,26]},{"type":"foreign","id":"Venezuela","pop":0,"adjacent":[7,17,19,21,23,24,44,49]},{"type":"road","id":"Santa Marta / Cartagena","econ":1,"adjacent":[5,6,11]},{"type":"road","id":"Cali / Pasto","econ":1,"adjacent":[1,9,13,20]},{"type":"road","id":"Neiva / Pasto","econ":1,"adjacent":[8,9,16,20]},{"type":"road","id":"Bogota / San Jose","econ":1,"adjacent":[14,15,22]},{"type":"road","id":"Pasto / Tumaco","econ":1,"adjacent":[9,13,28]},{"type":"pipeline","id":"Cali / Buenaventura","econ":1,"adjacent":[1,12,13]},{"type":"pipeline","id":"Cartagena / Sincelejo","econ":1,"adjacent":[6,10,11]},{"type":"pipeline","id":"Sincelejo / Medellin","econ":2,"adjacent":[2,10,12,18]},{"type":"pipeline","id":"Medellin / Ibague","econ":1,"adjacent":[2,4,12,18]},{"type":"pipeline","id":"Ibague / Cali","econ":1,"adjacent":[1,4,12,20]},{"type":"pipeline","id":"Bucaramanga / Ibague / Bogota","econ":2,"adjacent":[3,4,18,19,20]},{"type":"pipeline","id":"Bogota / Neiva","econ":2,"adjacent":[8,14,20]},{"type":"pipeline","id":"Cucuta / Arauca","econ":3,"adjacent":[7,19,21,21,31]},{"type":"pipeline","id":"Bogota / Yopal","econ":2,"adjacent":[19,21,22]},{"type":"pipeline","id":"Santa Marta / Ayacucho","econ":2,"adjacent":[5,11,17,18,19,47,48,49]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,46,48,49]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,46,47,49]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,31,46,47,48]}],"first_piece":[[0,3,3,33,0],[63,72],[102,108],[126,141]],"last_piece":[[2,2,32,62,-1],[71,101],[107,125],[140,152]]}
+const data = {"first_space":0,"first_pop":1,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":31,"first_loc":32,"last_loc":49,"last_space":49,"coastal_spaces":[11,12,13,17],"card_name":[null,"1st Division","Ospina & Mora","Tapias","Caño Limón - Coveñas","Occidental & Ecopetrol","Oil Spill","7th Special Forces","Fuerza Aérea Colombiana","High Mountain Battalions","Blackhawks","National Defense & Security Council","Plan Colombia","Plan Meteoro","Tres Esquinas","War Tax","Coffee Prices","Madrid Donors","NSPD-18","General Offensive","Mono Jojoy","Raúl Reyes","Alfonso Cano","DoD Contractors","Operación Jaque","Ejército de Liberación Nacional","Gramaje","Misil Antiaéreo","Hugo Chávez","Kill Zone","Peace Commission","Betancourt","Secuestrados","Sucumbíos","Airdropped AKs","Crop Substitution","Zona de Convivencia","Former Military","National Coordination Center","Soldados Campesinos","Demobilization","Mancuso","Senado & Cámara","Calima Front","Colombia Nueva","Los Derechos Humanos","Limpieza","Pinto & del Rosario","Unión Sindical Obrera","Bloques","Carabineros","Pipeline Repairs","Castaño","Criminal Air Force","Deserters & Defectors","DEA Agents","Drogas La Rebaja","Op Millennium","General Serrano","Salcedo","The Chess Player","Air Bridge","Amazonía","Narco-War","Cocaine Labs","Poppies","Tingo María","Mexican Traffickers","Narco-Subs","Riverines & Fast Boats","Ayahuasca Tourism","Darién","Sicarios","Propaganda!","Propaganda!","Propaganda!","Propaganda!"],"card_order":[null,[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,3,2],[0,1,3,2],[0,1,3,2],[0,2,1,3],[0,2,1,3],[0,2,1,3],[0,2,3,1],[0,2,3,1],[0,2,3,1],[0,3,1,2],[0,3,1,2],[0,3,1,2],[0,3,2,1],[0,3,2,1],[0,3,2,1],[1,0,2,3],[1,0,2,3],[1,0,2,3],[1,0,3,2],[1,0,3,2],[1,0,3,2],[1,2,0,3],[1,2,0,3],[1,2,0,3],[1,2,3,0],[1,2,3,0],[1,2,3,0],[1,3,0,2],[1,3,0,2],[1,3,0,2],[1,3,2,0],[1,3,2,0],[1,3,2,0],[2,0,1,3],[2,0,1,3],[2,0,1,3],[2,0,3,1],[2,0,3,1],[2,0,3,1],[2,1,0,3],[2,1,0,3],[2,1,0,3],[2,1,3,0],[2,1,3,0],[2,1,3,0],[2,3,0,1],[2,3,0,1],[2,3,0,1],[2,3,1,0],[2,3,1,0],[2,3,1,0],[3,0,1,2],[3,0,1,2],[3,0,1,2],[3,0,2,1],[3,0,2,1],[3,0,2,1],[3,1,0,2],[3,1,0,2],[3,1,0,2],[3,1,2,0],[3,1,2,0],[3,1,2,0],[3,2,0,1],[3,2,0,1],[3,2,0,1],[3,2,1,0],[3,2,1,0],[3,2,1,0],[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,2,3]],"space_name":["Bogotá","Cali","Medellín","Bucaramanga","Ibagué","Santa Marta","Cartagena","Cúcuta","Neiva","Pasto","Sincelejo","Atlántico","Chocó","Nariño","Meta West","Guaviare","Putumayo","Cesar","Antioquia","Santander","Huila","Arauca","Meta East","Vichada","Guainía","Vaupés","Amazonas","Panamá","Ecuador","Brasil","Perú","Venezuela","Santa Marta / Cartagena","Cali / Pasto","Neiva / Pasto","Bogotá / San José","Pasto / Tumaco","Cali / Buenaventura","Cartagena / Sincelejo","Sincelejo / Medellín","Medellín / Ibagué","Ibagué / Cali","Bucaramanga / Ibagué / Bogotá","Bogotá / Neiva","Cúcuta / Arauca","Bogotá / Yopal","Santa Marta / Ayacucho","Bucaramanga / Ayacucho","Sincelejo / Ayacucho","Cúcuta / Ayacucho"],"spaces":[{"type":"city","id":"Bogota","pop":8,"adjacent":[14,19,20,22,35,42,43,45]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,33,37,41]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,39,40]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,42,47]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,40,41,42]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,32,46]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,32,38]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,31,44,49]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,34,43]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,28,33,34,36]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,38,39,48]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,32,38,46,47,48,49]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,27,37,39,40,41]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,28,33,36,37]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,35,43]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,35]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,28,30,34]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,31,46,47,48,49]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,39,40,42,46,47,48,49]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,31,42,44,45,46,47,48,49]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,33,34,41,42,43]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,31,44,45]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,35,45]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24,31]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25,29,31]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26,29]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25,29,30]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,36]},{"type":"foreign","id":"Brasil","pop":0,"adjacent":[24,25,26]},{"type":"foreign","id":"Peru","pop":0,"adjacent":[16,26]},{"type":"foreign","id":"Venezuela","pop":0,"adjacent":[7,17,19,21,23,24,44,49]},{"type":"road","id":"Santa Marta / Cartagena","econ":1,"adjacent":[5,6,11]},{"type":"road","id":"Cali / Pasto","econ":1,"adjacent":[1,9,13,20]},{"type":"road","id":"Neiva / Pasto","econ":1,"adjacent":[8,9,16,20]},{"type":"road","id":"Bogota / San Jose","econ":1,"adjacent":[0,14,15,22]},{"type":"road","id":"Pasto / Tumaco","econ":1,"adjacent":[9,13,28]},{"type":"pipeline","id":"Cali / Buenaventura","econ":1,"adjacent":[1,12,13]},{"type":"pipeline","id":"Cartagena / Sincelejo","econ":1,"adjacent":[6,10,11]},{"type":"pipeline","id":"Sincelejo / Medellin","econ":2,"adjacent":[2,10,12,18]},{"type":"pipeline","id":"Medellin / Ibague","econ":1,"adjacent":[2,4,12,18]},{"type":"pipeline","id":"Ibague / Cali","econ":1,"adjacent":[1,4,12,20]},{"type":"pipeline","id":"Bucaramanga / Ibague / Bogota","econ":2,"adjacent":[0,3,4,18,19,20]},{"type":"pipeline","id":"Bogota / Neiva","econ":2,"adjacent":[0,8,14,20]},{"type":"pipeline","id":"Cucuta / Arauca","econ":3,"adjacent":[7,19,21,21,31]},{"type":"pipeline","id":"Bogota / Yopal","econ":2,"adjacent":[0,19,21,22]},{"type":"pipeline","id":"Santa Marta / Ayacucho","econ":2,"adjacent":[5,11,17,18,19,47,48,49]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,46,48,49]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,46,47,49]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,31,46,47,48]}],"adjacent_patrol":[[35,42,43,45],[33,37,41],[39,40],[42,47],[40,41,42],[32,46],[32,38],[44,49],[34,43],[33,34,36],[38,39,48],[5,6,10,32,38,46,47,48,49],[1,2,4,10,37,39,40,41],[1,9,33,36,37],[0,8,35,43],[35],[8,9,34],[5,46,47,48,49],[2,3,4,10,39,40,42,46,47,48,49],[0,3,7,42,44,45,46,47,48,49],[0,1,4,8,9,33,34,41,42,43],[44,45],[0,35,45],[],[],[],[],[],[9,36],[],[],[7,44,49],[5,6],[1,9],[8,9],[0],[9],[1],[6,10],[2,10],[2,4],[1,4],[0,3,4],[0,8],[7],[0],[5,47,48,49],[3,46,48,49],[10,46,47,49],[7,46,47,48]],"first_piece":[[0,3,3,33,0],[63,72],[102,108],[126,141]],"last_piece":[[2,2,32,62,-1],[71,101],[107,125],[140,152]]}
if (typeof module !== 'undefined') module.exports = data
diff --git a/play.css b/play.css
index 2cf2615..f0c5d60 100644
--- a/play.css
+++ b/play.css
@@ -94,6 +94,15 @@ path.selected { stroke: yellow; }
drop-shadow(2px 0 0 white)
;
}
+.piece.selected {
+ pointer-events: auto;
+ filter:
+ drop-shadow(0 -2px 0 yellow)
+ drop-shadow(0 2px 0 yellow)
+ drop-shadow(-2px 0 0 yellow)
+ drop-shadow(2px 0 0 yellow)
+ ;
+}
.token {
pointer-events: none;
@@ -109,6 +118,10 @@ path.selected { stroke: yellow; }
height: 45px;
}
+.token.terror {
+ transition: none;
+}
+
.token.action {
pointer-events: auto;
}
diff --git a/play.js b/play.js
index f7aff9f..22dcf85 100644
--- a/play.js
+++ b/play.js
@@ -1,5 +1,4 @@
-// TODO: FARC zone
-// TODO: terror markers
+// TODO: show selected op spaces with highlight / pawn ?
// Factions
const GOVT = 0
@@ -231,10 +230,10 @@ function get_layout_radius(s) {
}
function init_ui() {
- register_action(ui.resources[GOVT], "pay", GOVT)
- register_action(ui.resources[FARC], "pay", FARC)
- register_action(ui.resources[AUC], "pay", AUC)
- register_action(ui.resources[CARTELS], "pay", CARTELS)
+ register_action(ui.resources[GOVT], "resources", GOVT)
+ register_action(ui.resources[FARC], "resources", FARC)
+ register_action(ui.resources[AUC], "resources", AUC)
+ register_action(ui.resources[CARTELS], "resources", CARTELS)
for (let i = 0; i < data.spaces.length; ++i) {
let id = data.spaces[i].id
@@ -654,10 +653,10 @@ function on_update() {
ui.header.classList.toggle("cartels", view.current === CARTELS)
ui.header.classList.toggle("farc", view.current === FARC)
- ui.resources[GOVT].classList.toggle("action", is_action("pay", GOVT))
- ui.resources[FARC].classList.toggle("action", is_action("pay", FARC))
- ui.resources[AUC].classList.toggle("action", is_action("pay", AUC))
- ui.resources[CARTELS].classList.toggle("action", is_action("pay", CARTELS))
+ ui.resources[GOVT].classList.toggle("action", is_action("resources", GOVT))
+ ui.resources[FARC].classList.toggle("action", is_action("resources", FARC))
+ ui.resources[AUC].classList.toggle("action", is_action("resources", AUC))
+ ui.resources[CARTELS].classList.toggle("action", is_action("resources", CARTELS))
ui.player[GOVT].classList.toggle("hide", view.scenario < 3)
ui.player[AUC].classList.toggle("hide", view.scenario < 4)
@@ -800,6 +799,8 @@ function on_update() {
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)
action_button("remove", "Remove")
// menu trade
diff --git a/rules.js b/rules.js
index f88dc89..8dd00a4 100644
--- a/rules.js
+++ b/rules.js
@@ -1,5 +1,7 @@
"use strict"
+// TODO: Automatic "All done" message. (limited / no more resources / no more available options).
+
let states = {}
let game = null
let view = null
@@ -429,6 +431,30 @@ function has_underground_guerrilla(s, faction) {
return false
}
+function has_any_guerrilla(s) {
+ return (
+ has_piece(s, FARC, GUERRILLA) ||
+ has_piece(s, AUC, GUERRILLA) ||
+ has_piece(s, CARTELS, GUERRILLA)
+ )
+}
+
+function has_any_active_guerrilla(s) {
+ return (
+ has_active_guerrilla(s, FARC) ||
+ has_active_guerrilla(s, AUC) ||
+ has_active_guerrilla(s, CARTELS)
+ )
+}
+
+function has_any_underground_guerrilla(s) {
+ return (
+ has_underground_guerrilla(s, FARC) ||
+ has_underground_guerrilla(s, AUC) ||
+ has_underground_guerrilla(s, CARTELS)
+ )
+}
+
function count_bases(s) {
return (
count_pieces(s, GOVT, BASE) +
@@ -485,6 +511,14 @@ function is_pop(s) {
return s >= first_pop && s <= last_pop
}
+function is_mountain(s) {
+ return data.spaces[s].type === "mountain"
+}
+
+function is_forest(s) {
+ return data.spaces[s].type === "forest"
+}
+
function is_adjacent(a, b) {
return set_has(data.spaces[a].adjacent, b)
}
@@ -493,6 +527,10 @@ function is_active(p) {
return !is_underground(p)
}
+function is_farc_zone(s) {
+ return set_has(game.farc_zones, s)
+}
+
function is_guerrilla(p) {
for (let faction = 1; faction < 4; ++faction) {
let p0 = first_piece[faction][GUERRILLA]
@@ -592,7 +630,7 @@ function for_each_piece(faction, type, f) {
f(p)
}
-function gen_piece_in_space(faction, type, space) {
+function gen_piece_in_space(space, faction, type) {
for_each_piece(faction, type, p => {
if (game.pieces[p] === space)
gen_action_piece(p)
@@ -617,7 +655,7 @@ function gen_place_piece(space, faction, type) {
gen_action_piece(p)
}
-function gen_activate_guerrilla(s, faction) {
+function gen_underground_guerrillas(s, faction) {
for_each_piece(faction, GUERRILLA, p => {
if (game.pieces[p] === s)
if (is_underground(p))
@@ -625,16 +663,24 @@ function gen_activate_guerrilla(s, faction) {
})
}
+function gen_active_guerrillas(s, faction) {
+ for_each_piece(faction, GUERRILLA, p => {
+ if (game.pieces[p] === s)
+ if (is_active(p))
+ gen_action_piece(p)
+ })
+}
+
function gen_remove_piece(s, faction) {
if (faction === GOVT) {
- gen_piece_in_space(GOVT, TROOPS, game.op.where)
- gen_piece_in_space(GOVT, POLICE, game.op.where)
+ gen_piece_in_space(game.op.where, GOVT, TROOPS)
+ gen_piece_in_space(game.op.where, GOVT, POLICE)
if (!has_piece(game.op.where, GOVT, TROOPS) && !has_piece(game.op.where, GOVT, POLICE))
- gen_piece_in_space(GOVT, BASE, game.op.where)
+ gen_piece_in_space(game.op.where, GOVT, BASE)
} else {
- gen_piece_in_space(faction, GUERRILLA, game.op.where)
+ gen_piece_in_space(game.op.where, faction, GUERRILLA)
if (!has_piece(game.op.where, faction, GUERRILLA))
- gen_piece_in_space(faction, BASE, game.op.where)
+ gen_piece_in_space(game.op.where, faction, BASE)
}
}
@@ -1013,12 +1059,7 @@ function operation(state) {
states.train = operation({
prompt() {
- view.prompt = "Train: Place cubes first, then replace with Base or buy Civic Action."
-
- if (can_use_special_activity()) {
- view.actions.air_lift = 1
- view.actions.eradicate = 1
- }
+ view.prompt = "Train: Select spaces to place cubes."
// Any Departments or Cities
if (can_select_op_space(3)) {
@@ -1028,16 +1069,8 @@ states.train = operation({
gen_action_space(s)
}
- // place base
- view.actions.base = 1
-
- // buy civic action
- if (game.resources[game.current] >= 3)
- view.actions.civic = 1
- else
- view.actions.civic = 0
-
- gen_operation_common()
+ if (game.op.spaces.length > 0)
+ view.actions.next = 1
},
space(s) {
push_undo()
@@ -1050,22 +1083,11 @@ states.train = operation({
game.op.where = s
game.op.count = 6
},
- base() {
- push_undo()
- game.state = "train_base"
- game.op.where = -1
- },
- civic() {
+ next() {
push_undo()
- game.state = "train_civic"
- game.op.where = -1
- },
- done: end_operation,
+ game.state = "train_base_or_civic"
+ }
})
- },
- air_lift: goto_air_lift,
- eradicate: goto_eradicate,
-}
states.train_place = operation({
prompt() {
@@ -1092,14 +1114,36 @@ states.train_place = operation({
},
})
+states.train_base_or_civic = operation({
+ prompt() {
+ view.prompt = "Train: Build Base or buy Civic Action in one selected space?"
+ view.actions.base = 1
+ if (game.resources[game.current] >= 3)
+ view.actions.civic = 1
+ else
+ view.actions.civic = 0
+ view.actions.done = 1
+ },
+ base() {
+ push_undo()
+ game.state = "train_base"
+ game.op.where = -1
+ },
+ civic() {
+ push_undo()
+ game.state = "train_civic"
+ game.op.where = -1
+ },
+ done: end_operation,
+})
+
states.train_base = operation({
prompt() {
if (game.op.where < 0) {
view.prompt = `Train: Replace 3 cubes with a Base.`
- for (let s = first_space; s <= last_dept; ++s) {
- if (can_select_op_space(3) || is_selected_op_space(s))
- if (count_bases(s) < 2 && count_cubes(s) >= 3)
- gen_action_space(s)
+ for (let s of game.op.spaces) {
+ if (count_bases(s) < 2 && count_cubes(s) >= 3)
+ gen_action_space(s)
}
} else {
if (game.op.count < 0) {
@@ -1107,8 +1151,8 @@ states.train_base = operation({
view.actions.done = 1
} else if (game.op.count > 0) {
view.prompt = `Train: ${game.op.count} cubes with a Base.`
- gen_piece_in_space(GOVT, POLICE, game.op.where)
- gen_piece_in_space(GOVT, TROOPS, game.op.where)
+ gen_piece_in_space(game.op.where, GOVT, POLICE)
+ gen_piece_in_space(game.op.where, GOVT, TROOPS)
} else {
view.prompt = `Train: Place Base.`
gen_place_piece(game.op.where, GOVT, BASE)
@@ -1117,8 +1161,6 @@ states.train_base = operation({
},
space(s) {
push_undo()
- if (!is_selected_op_space(s))
- select_op_space(s, 3)
game.op.where = s
game.op.count = 3
},
@@ -1140,26 +1182,25 @@ states.train_civic = operation({
if (game.op.where < 0) {
view.prompt = `Train: Buy Civic Action.`
if (res >= 3) {
- for (let s = first_space; s <= last_dept; ++s) {
- if (can_select_op_space(6) || is_selected_op_space(s))
- if (can_govt_civic_action(s))
- gen_action_space(s)
+ for (let s of game.op.spaces) {
+ if (can_govt_civic_action(s))
+ gen_action_space(s)
}
}
} else {
view.prompt = `Train: Buy Civic Action in ${space_name[game.op.where]}.`
view.where = game.op.where
- if (res >= 3 && can_govt_civic_action(game.op.where))
+ if (res >= 3 && can_govt_civic_action(game.op.where)) {
+ //gen_action("resources", game.current)
gen_action_space(game.op.where)
- else
+ } else {
view.prompt = `Train: All done.`
+ }
view.actions.done = 1
}
},
space(s) {
push_undo()
- if (!is_selected_op_space(s))
- select_op_space(s, 3)
game.op.where = s
game.resources[game.current] -= 3
game.support[game.op.where] += 1
@@ -1167,8 +1208,372 @@ states.train_civic = operation({
done: end_operation,
})
+// OPERATION: PATROL
+
+function goto_patrol() {
+ if (!game.op.free)
+ game.resources[game.current] -= 3
+ if (game.op.limited)
+ game.state = "limited_patrol"
+ else
+ game.state = "patrol"
+ game.op.who = -1
+ game.op.pieces = []
+}
+
+states.limited_patrol = operation({
+ prompt() {
+ view.prompt = "Patrol: ... TODO LimOp Patrol ..."
+ view.actions.next = 1
+ },
+ next: goto_patrol_activate,
+})
+
+function gen_patrol_move(start) {
+ let queue = [ start ]
+ view.actions.space = []
+ while (queue.length > 0) {
+ let here = queue.shift()
+ for (let next of data.adjacent_patrol[here]) {
+ // already seen
+ if (set_has(view.actions.space, next))
+ continue
+
+ set_add(view.actions.space, next)
+
+ // must stop if guerrilla
+ if (has_any_guerrilla(next))
+ continue
+
+ queue.push(next)
+ }
+ }
+}
+
+states.patrol = operation({
+ prompt() {
+ view.prompt = "Patrol: ..."
+ view.who = game.op.who
+
+ if (game.op.who < 0) {
+ for_each_piece(GOVT, TROOPS, p => {
+ if (game.pieces[p] !== AVAILABLE && !set_has(game.op.pieces, p))
+ gen_action_piece(p)
+ })
+ for_each_piece(GOVT, POLICE, p => {
+ if (game.pieces[p] !== AVAILABLE && !set_has(game.op.pieces, p))
+ gen_action_piece(p)
+ })
+
+ } else {
+ gen_action_piece(game.op.who)
+ gen_patrol_move(game.pieces[game.op.who])
+ let from = game.pieces[game.op.who]
+ for (let to of data.spaces[from].adjacent) {
+ if (is_city(to) || is_loc(to))
+ gen_action_space(to)
+ }
+ }
+
+ view.actions.next = 1
+ },
+ piece(p) {
+ if (game.op.who < 0) {
+ push_undo()
+ game.op.who = p
+ set_add(game.op.pieces, p)
+ } else {
+ pop_undo()
+ }
+ },
+ space(s) {
+ move_piece(game.op.who, s)
+ game.op.who = -1
+ update_control()
+ },
+ next: goto_patrol_activate,
+})
+
+function goto_patrol_activate() {
+ push_undo()
+ game.state = "patrol_activate"
+ game.op.count = []
+ for (let s = first_loc; s <= last_loc; ++s)
+ game.op.count[s - first_loc] = count_cubes(s)
+}
+
+states.patrol_activate = operation({
+ prompt() {
+ view.prompt = "Patrol: Activate 1 Guerrilla for each cube in LoCs."
+
+ // for each cube in each loc, activate 1 guerrilla
+ for (let s = first_loc; s <= last_loc; ++s) {
+ let n = game.op.count[s - first_loc]
+ if (n > 0) {
+ gen_underground_guerrillas(s, FARC)
+ gen_underground_guerrillas(s, AUC)
+ gen_underground_guerrillas(s, CARTELS)
+ }
+ }
+
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+ let s = game.pieces[p]
+ set_active(p)
+ game.op.count[s - first_loc]--
+ },
+ next() {
+ push_undo()
+ game.state = "patrol_assault"
+ },
+})
+
+states.patrol_assault = operation({
+ prompt() {
+ view.prompt = "Patrol: Assault in one LoC?"
+
+ if (game.op.limited) {
+ for (let s of game.op.spaces)
+ if (can_assault_space(s))
+ gen_action_space(s)
+ } else {
+ for (let s = first_loc; s <= last_loc; ++s)
+ if (can_assault_space(s))
+ gen_action_space(s)
+ }
+
+ view.actions.done = 1
+ },
+ space(s) {
+ push_undo()
+ log(`Assault S${s}.`)
+
+ game.state = "patrol_assault_space"
+ game.op.where = s
+ game.op.count = count_cubes(s)
+ },
+ done: end_operation
+})
+
+states.patrol_assault_space = operation({
+ prompt() {
+ view.prompt = `Patrol: Remove up to ${game.op.count} enemy pieces in ${space_name[game.op.where]}.`
+ view.where = game.op.where
+
+ if (game.op.count > 0) {
+ gen_assault_piece(game.op.where, FARC)
+ gen_assault_piece(game.op.where, AUC)
+ gen_assault_piece(game.op.where, CARTELS)
+ }
+
+ if (!view.actions.piece)
+ view.prompt = "Patrol: All done."
+
+ view.actions.done = 1
+ },
+ piece(p) {
+ push_undo()
+ remove_piece(p)
+ update_control()
+ },
+ done: end_operation,
+})
+
+// OPERATION: SWEEP
+
+function can_sweep_move(here) {
+ if (has_piece(here, GOVT, TROOPS) || has_piece(here, GOVT, POLICE))
+ return true
+ for (let next of data.spaces[here].adjacent) {
+ if (has_piece(next, GOVT, TROOPS))
+ return true
+ if (is_loc(next) && !has_any_guerrilla(next)) {
+ for (let nextnext of data.spaces[next].adjacent) {
+ if (nextnext !== here)
+ if (has_piece(nextnext, GOVT, TROOPS))
+ return true
+ }
+ }
+ }
+ return false
+}
+
+function gen_sweep_move(here) {
+ for (let next of data.spaces[here].adjacent) {
+ gen_piece_in_space(next, GOVT, TROOPS)
+ if (is_loc(next) && !has_any_guerrilla(next)) {
+ for (let nextnext of data.spaces[next].adjacent) {
+ if (nextnext !== here)
+ gen_piece_in_space(nextnext, GOVT, TROOPS)
+ }
+ }
+ }
+}
+
+states.sweep = operation({
+ prompt() {
+ view.prompt = "Sweep: Select Cities and Departments."
+
+ if (can_select_op_space(3)) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (is_selected_op_space(s))
+ continue
+ if (is_farc_zone(s))
+ continue
+ if (can_sweep_move(s))
+ gen_action_space(s)
+ }
+ }
+
+ if (!view.actions.space)
+ view.prompt = "Sweep: All done."
+
+ gen_operation_common()
+ },
+ space(s) {
+ push_undo()
+
+ logi(`S${s}.`)
+
+ select_op_space(s, 3)
+
+ game.state = "sweep_move"
+ game.op.where = s
+ },
+ done: end_operation,
+})
+
+states.sweep_move = operation({
+ prompt() {
+ view.prompt = `Sweep: Move Troops into ${space_name[game.op.where]}.`
+ view.where = game.op.where
+ gen_sweep_move(game.op.where)
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+ place_piece(p, game.op.where)
+ update_control()
+ },
+ next() {
+ push_undo()
+ game.state = "sweep_activate"
+ game.op.count = count_pieces(game.op.where, GOVT, TROOPS)
+ if (is_forest(game.op.where))
+ game.op.count >>= 1
+ },
+})
+
+states.sweep_activate = operation({
+ prompt() {
+ view.prompt = `Sweep: Activate up to ${game.op.count} Guerrillas in ${space_name[game.op.where]}.`
+ view.where = game.op.where
+
+ gen_underground_guerrillas(game.op.where, FARC)
+ gen_underground_guerrillas(game.op.where, AUC)
+ gen_underground_guerrillas(game.op.where, CARTELS)
+
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+ set_active(p)
+ if (--game.op.count === 0 || !has_any_underground_guerrilla(game.op.where))
+ game.state = "sweep"
+ },
+ next() {
+ push_undo()
+ game.state = "sweep"
+ },
+})
+
+// OPERATION: ASSAULT
+
+function can_assault_space(s) {
+ for (let faction = 1; faction < 4; ++faction) {
+ if (has_piece(s, faction, GUERRILLA)) {
+ if (has_active_guerrilla(s, faction))
+ return true
+ } else {
+ if (has_piece(s, faction, BASE))
+ return true
+ }
+ }
+ return false
+}
+
+function gen_assault_piece(s, faction) {
+ if (has_piece(s, faction, GUERRILLA))
+ gen_active_guerrillas(s, faction)
+ else
+ gen_piece_in_space(s, faction, BASE)
}
+states.assault = operation({
+ prompt() {
+ view.prompt = "Assault: Select space to eliminate enemy forces."
+
+ if (can_select_op_space(3)) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (is_selected_op_space(s))
+ continue
+ let n = count_pieces(s, GOVT, TROOPS)
+ if (n >= (is_mountain(s) ? 2 : 1)) {
+ if (can_assault_space(s))
+ gen_action_space(s)
+ }
+ }
+ }
+
+ if (!view.actions.space)
+ view.prompt = "Assault: All done."
+
+ gen_operation_common()
+ },
+ space(s) {
+ push_undo()
+
+ logi(`S${s}.`)
+
+ select_op_space(s, 3)
+
+ game.state = "assault_space"
+ game.op.where = s
+ game.op.count = count_pieces(s, GOVT, TROOPS)
+ if (is_city(s) || is_loc(s))
+ game.op.count += count_pieces(s, GOVT, POLICE)
+ if (is_mountain(s))
+ game.op.count >>= 1
+ },
+ done: end_operation,
+})
+
+states.assault_space = operation({
+ prompt() {
+ view.prompt = `Assault: Remove up to ${game.op.count} enemy pieces in ${space_name[game.op.where]}.`
+ view.where = game.op.where
+
+ gen_assault_piece(game.op.where, FARC)
+ gen_assault_piece(game.op.where, AUC)
+ gen_assault_piece(game.op.where, CARTELS)
+
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+ remove_piece(p)
+ update_control()
+ if (--game.op.count === 0 || !can_assault_space(game.op.where))
+ game.state = "assault"
+ },
+ next() {
+ push_undo()
+ game.state = "assault"
+ },
+})
+
// OPERATION: RALLY
function rally_count() {
@@ -1186,18 +1591,6 @@ states.rally = operation({
else
view.prompt = "Rally: Select City or Department."
- if (can_use_special_activity()) {
- if (game.current === FARC)
- view.actions.extort = 1
- if (game.current === AUC)
- view.actions.extort = 1
- if (game.current === CARTELS) {
- view.actions.cultivate = 1
- view.actions.process = 1
- view.actions.bribe = 1
- }
- }
-
// Departments or Cities
if (can_select_op_space(1)) {
for (let s = first_space; s <= last_dept; ++s) {
@@ -1230,18 +1623,14 @@ states.rally = operation({
select_op_space(s, 1)
- game.state = "rally_place"
+ game.state = "rally_space"
game.op.where = s
game.op.count = rally_count()
},
done: end_operation,
- extort: goto_extort,
- cultivate: goto_cultivate,
- process: goto_process,
- bribe: goto_bribe,
})
-states.rally_place = operation({
+states.rally_space = operation({
prompt() {
view.prompt = `Rally: Place up to ${game.op.count} Guerrillas.`
view.where = game.op.where
@@ -1291,7 +1680,7 @@ states.rally_base = operation({
prompt() {
if (game.op.count > 0) {
view.prompt = `Rally: Replace ${game.op.count} Guerrillas with a Base.`
- gen_piece_in_space(game.current, GUERRILLA, game.op.where)
+ gen_piece_in_space(game.op.where, game.current, GUERRILLA)
} else {
view.prompt = `Rally: Place a Base.`
gen_place_piece(game.op.where, game.current, BASE)
@@ -1354,18 +1743,6 @@ states.march = operation({
prompt() {
view.prompt = `March: Move Guerrillas to Cities/Departments/LoCs.`
- if (can_use_special_activity()) {
- if (game.current === FARC)
- view.actions.extort = 1
- if (game.current === AUC)
- view.actions.extort = 1
- if (game.current === CARTELS) {
- view.actions.cultivate = 1
- view.actions.process = 1
- view.actions.bribe = 1
- }
- }
-
if (can_select_op_space(1)) {
for (let s = first_space; s <= last_dept; ++s) {
if (is_selected_op_space(s))
@@ -1614,7 +1991,7 @@ function goto_ambush() {
states.ambush = {
prompt() {
view.prompt = `Ambush in ${space_name[game.op.where]}: Activate an Underground Guerrilla.`
- gen_activate_guerrilla(game.op.where, game.current)
+ gen_underground_guerrillas(game.op.where, game.current)
},
piece(p) {
set_active(p)
@@ -1649,6 +2026,9 @@ states.terror = operation({
}
}
+ if (!view.actions.space)
+ view.prompt = "Terror: All done."
+
gen_operation_common()
},
space(s) {
@@ -1670,7 +2050,7 @@ states.terror = operation({
states.terror_space = operation({
prompt() {
view.prompt = `Terror in ${space_name[game.op.where]}: Activate an Underground Guerrilla.`
- gen_activate_guerrilla(game.op.where, game.current)
+ gen_underground_guerrillas(game.op.where, game.current)
},
piece(p) {
set_active(p)
diff --git a/tools/gendata.js b/tools/gendata.js
index 4fd79a2..4d773e9 100644
--- a/tools/gendata.js
+++ b/tools/gendata.js
@@ -156,7 +156,7 @@ function def_loc(type, econ, cities, depts) {
console.log("not a space: " + n)
let loc_spaces = loc_names.map(n => space_name.indexOf(n))
let ix = spaces.length
- spaces.push({ type, id: to_ascii(name), econ, adjacent: loc_spaces.filter(x=>x>0) })
+ spaces.push({ type, id: to_ascii(name), econ, adjacent: loc_spaces.filter(x => x >= 0) })
for (let loc of loc_spaces)
if (loc >= 0)
add(spaces[loc].adjacent, ix)
@@ -414,6 +414,14 @@ data.card_order = card_order
data.space_name = space_name
data.spaces = spaces
+data.adjacent_patrol = []
+for (let s = 0; s < spaces.length; ++s) {
+ let ap = data.adjacent_patrol[s] = []
+ for (let next of spaces[s].adjacent)
+ if ((next <= data.last_city) || (next >= data.first_loc && next <= data.last_loc))
+ ap.push(next)
+}
+
let pc_index = 0
let pc_first = data.first_piece = [ [ 0, 0, 0, 0, 0 ], [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]
let pc_last = data.last_piece = [ [ -1, -1, -1, -1, -1 ], [ -1, -1 ], [ -1, -1 ], [ -1, -1 ] ]