diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-03-05 20:34:55 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2023-05-03 18:48:15 +0200 |
commit | 2d66908b20d43a39974f99a0ec74212eaa5031ed (patch) | |
tree | 12d8197c8110604fbfb61afac5ff21d400bedfed | |
parent | 71c0e5bc691ce148515ebf4d6e2fba96c85c2ccb (diff) | |
download | andean-abyss-2d66908b20d43a39974f99a0ec74212eaa5031ed.tar.gz |
Patrol, Sweep & Assault.
TODO: LimOp Patrol - only move to one destination.
-rw-r--r-- | data.js | 2 | ||||
-rw-r--r-- | play.css | 13 | ||||
-rw-r--r-- | play.js | 21 | ||||
-rw-r--r-- | rules.js | 552 | ||||
-rw-r--r-- | tools/gendata.js | 10 |
5 files changed, 500 insertions, 98 deletions
@@ -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 @@ -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; } @@ -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 @@ -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 ] ] |