summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data.js2
-rw-r--r--play.css3
-rw-r--r--play.js49
-rw-r--r--rules.js438
-rw-r--r--tools/gendata.js6
5 files changed, 448 insertions, 50 deletions
diff --git a/data.js b/data.js
index 4c72a23..ecf291b 100644
--- a/data.js
+++ b/data.js
@@ -1,2 +1,2 @@
-const data = {"first_space":0,"first_pop":0,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":28,"first_loc":29,"last_loc":46,"last_space":46,"coastal_spaces":[11,12,13,17],"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]],"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_flavor":[null,"Jointness","COIN experts take charge","CO tightens civil-military bonds","Profitable pipeline","Oil company security","Rebels blamed","Infrastructure protection training","COIN strike aircraft","Elites guard high-altitude corridors","US helos delivered","Military-police jointness","US \"War on Drugs\"","Transport protection units","Forward base","Defense budget shot in the arm","They're up","Aid conference generous","US \"War on Terror\" takes on FARC",null,"KIA puts FARC in disarray","FARC Deputy killed","FARC leader killed in military strike","US provides aircrew","Dramatic hostage rescue","ELN and FARC jockey","FARC protection rejected","FARC MANPADs deemed a myth","Caracas controls border","Army sniffs out FARC trap","FARC accused in Commissioner's killing","Sympathy for famous hostage","Fed up with hostage-taking","Ecuadoran buffer zone","Insurgents scammed by Russian criminals","Government initiative","ELN gets its DMZ","Ties that bind","New command fights paramilitaries","Local forces platoons","Negotiated reintegration","AUC No.2 extradited","Unity behind Presidential war policy","Suspect leftists massacred","Anti-corruption campaign","Officers disciplined","Ruthless elimination","Human rights investigators","AUC targets oil labor organizers","Militias defy Castaño","National police field forces","Speedy patching","AUC leader's memoir a best seller","Insurgent access to small aircraft",null,"Law enforcement assistance","Cali cartel's drugstore chain seized","Colombian-US strike at Bernal syndicate","National Police hammer cartels","Cartel informant","Kingpin strategy scores","Peruvian coca supply controlled","Brasília's Op Cobra blocks border","Rival syndicates go for the throat","FARC taps suppliers","Growers and Government eradication focus on heroin source","Coca crop fails","Major shipment busted en route","Submersibles seized",null,"Eco-tourism helps trade balance","Arms traffic interdicted","Hired drug guns unreliable"],"card_flavor_shaded":[null,"Service parochialism","COIN strategy eludes Army","Civil-military rivalries fester","Pipeline draws attacks","Industry thought exploitative","Multinationals make mess","US training ineffective","Budget diverted to expensive jets","Equipment not delivered","Delivery of US helos delayed","Military-police rivalry","US aid focuses on drug war","Transport security deemphasized","Base overrun","Middle class resents cost of war","They're down","EU aid focuses on reconstruction","US focused on Mid-East and South Asia",null,"Military strategist","FARC Deputy channels foreign support","Ideologue","Plane down - hostage search and evasion","Hostage rescue goes awry","ELN and FARC coordinate ops","Schedule of fees","MANPADs feared","Caracas aids rebels","Tactics lure enemy in","Peace bid","Hostage negotiations forum for FARC","Ransoming highly profitable","Cross-border war","Covert weapons delivery","FARC proposals lauded",null,"Ex-officers advise paramilitaries","Sympathizers alert AUC","Local forces augment autodefensas","Talks a ruse, fighters recycled","AUC drug lord","Insurgent sympathies","Brutality blamed on Army","Political campaign divisive","International human rights cartel",null,"Prosecutors killed","Labor backs FARC","Independent militias join AUC","National police corruption","Security concerns hinder maintenance","Charismatic AUC political leader",null,null,"Más Yanquis","Retail empire","Investigation penetrated","Officials on cartel payroll","Cali cartel security chief","Cali's Gilberto Rodríguez Orejuela expands empire","Colombian coca growers fill Peruvian void","Jungle landing strips",null,"Well-oiled industry",null,"Hearty coca variety","New routes to US market","Littoral stealth",null,"Eco-tourists taken","Border sanctuary","Unemployed ready to work for syndicates"],"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","Ecuador","Panamá","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,32,39,40,42]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,30,34,38]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,36,37]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,39,44]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,37,38,39]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,29,43]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,29,35]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,41,46]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,31,40]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,27,30,31,33]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,35,36,45]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,29,35,43,44,45,46]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,28,34,36,37,38]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,27,30,33,34]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,32,40]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,32]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,27,31]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,43,44,45,46]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,36,37,39,43,44,45,46]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,39,41,42,43,44,45,46]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,30,31,38,39,40]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,41,42]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,32,42]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,33]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"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,27]},{"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]},{"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,44,45,46]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,43,45,46]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,43,44,46]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,43,44,45]}],"adjacent_patrol":[[32,39,40,42],[30,34,38],[36,37],[39,44],[37,38,39],[29,43],[29,35],[41,46],[31,40],[30,31,33],[35,36,45],[5,6,10,29,35,43,44,45,46],[1,2,4,10,34,36,37,38],[1,9,30,33,34],[0,8,32,40],[32],[8,9,31],[5,43,44,45,46],[2,3,4,10,36,37,39,43,44,45,46],[0,3,7,39,41,42,43,44,45,46],[0,1,4,8,9,30,31,38,39,40],[41,42],[0,32,42],[],[],[],[],[9,33],[],[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,44,45,46],[3,43,45,46],[10,43,44,46],[7,43,44,45]],"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":0,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":28,"first_loc":29,"last_loc":46,"last_space":46,"coastal_spaces":[11,12,13,17],"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]],"card_title":[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_flavor":[null,"Jointness","COIN experts take charge","CO tightens civil-military bonds","Profitable pipeline","Oil company security","Rebels blamed","Infrastructure protection training","COIN strike aircraft","Elites guard high-altitude corridors","US helos delivered","Military-police jointness","US \"War on Drugs\"","Transport protection units","Forward base","Defense budget shot in the arm","They're up","Aid conference generous","US \"War on Terror\" takes on FARC",null,"KIA puts FARC in disarray","FARC Deputy killed","FARC leader killed in military strike","US provides aircrew","Dramatic hostage rescue","ELN and FARC jockey","FARC protection rejected","FARC MANPADs deemed a myth","Caracas controls border","Army sniffs out FARC trap","FARC accused in Commissioner's killing","Sympathy for famous hostage","Fed up with hostage-taking","Ecuadoran buffer zone","Insurgents scammed by Russian criminals","Government initiative","ELN gets its DMZ","Ties that bind","New command fights paramilitaries","Local forces platoons","Negotiated reintegration","AUC No.2 extradited","Unity behind Presidential war policy","Suspect leftists massacred","Anti-corruption campaign","Officers disciplined","Ruthless elimination","Human rights investigators","AUC targets oil labor organizers","Militias defy Castaño","National police field forces","Speedy patching","AUC leader's memoir a best seller","Insurgent access to small aircraft",null,"Law enforcement assistance","Cali cartel's drugstore chain seized","Colombian-US strike at Bernal syndicate","National Police hammer cartels","Cartel informant","Kingpin strategy scores","Peruvian coca supply controlled","Brasília's Op Cobra blocks border","Rival syndicates go for the throat","FARC taps suppliers","Growers and Government eradication focus on heroin source","Coca crop fails","Major shipment busted en route","Submersibles seized",null,"Eco-tourism helps trade balance","Arms traffic interdicted","Hired drug guns unreliable"],"card_flavor_shaded":[null,"Service parochialism","COIN strategy eludes Army","Civil-military rivalries fester","Pipeline draws attacks","Industry thought exploitative","Multinationals make mess","US training ineffective","Budget diverted to expensive jets","Equipment not delivered","Delivery of US helos delayed","Military-police rivalry","US aid focuses on drug war","Transport security deemphasized","Base overrun","Middle class resents cost of war","They're down","EU aid focuses on reconstruction","US focused on Mid-East and South Asia",null,"Military strategist","FARC Deputy channels foreign support","Ideologue","Plane down - hostage search and evasion","Hostage rescue goes awry","ELN and FARC coordinate ops","Schedule of fees","MANPADs feared","Caracas aids rebels","Tactics lure enemy in","Peace bid","Hostage negotiations forum for FARC","Ransoming highly profitable","Cross-border war","Covert weapons delivery","FARC proposals lauded",null,"Ex-officers advise paramilitaries","Sympathizers alert AUC","Local forces augment autodefensas","Talks a ruse, fighters recycled","AUC drug lord","Insurgent sympathies","Brutality blamed on Army","Political campaign divisive","International human rights cartel",null,"Prosecutors killed","Labor backs FARC","Independent militias join AUC","National police corruption","Security concerns hinder maintenance","Charismatic AUC political leader",null,null,"Más Yanquis","Retail empire","Investigation penetrated","Officials on cartel payroll","Cali cartel security chief","Cali's Gilberto Rodríguez Orejuela expands empire","Colombian coca growers fill Peruvian void","Jungle landing strips",null,"Well-oiled industry",null,"Hearty coca variety","New routes to US market","Littoral stealth",null,"Eco-tourists taken","Border sanctuary","Unemployed ready to work for syndicates"],"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","Ecuador","Panamá","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,32,39,40,42]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,30,34,38]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,36,37]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,39,44]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,37,38,39]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,29,43]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,29,35]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,41,46]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,31,40]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,27,30,31,33]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,35,36,45]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,29,35,43,44,45,46]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,28,34,36,37,38]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,27,30,33,34]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,32,40]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,32]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,27,31]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,43,44,45,46]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,36,37,39,43,44,45,46]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,39,41,42,43,44,45,46]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,30,31,38,39,40]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,41,42]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,32,42]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,33]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"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,27]},{"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]},{"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,44,45,46]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,43,45,46]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,43,44,46]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,43,44,45]}],"adjacent_patrol":[[32,39,40,42],[30,34,38],[36,37],[39,44],[37,38,39],[29,43],[29,35],[41,46],[31,40],[30,31,33],[35,36,45],[5,6,10,29,35,43,44,45,46],[1,2,4,10,34,36,37,38],[1,9,30,33,34],[0,8,32,40],[32],[8,9,31],[5,43,44,45,46],[2,3,4,10,36,37,39,43,44,45,46],[0,3,7,39,41,42,43,44,45,46],[0,1,4,8,9,30,31,38,39,40],[41,42],[0,32,42],[],[],[],[],[9,33],[],[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,44,45,46],[3,43,45,46],[10,43,44,46],[7,43,44,45]],"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 2032c7e..c45d870 100644
--- a/play.css
+++ b/play.css
@@ -67,6 +67,7 @@ path.action.forest { fill: limegreen; stroke: limegreen; }
path.action.grassland { fill: greenyellow; stroke: greenyellow; }
path.action.mountain { fill: chocolate; stroke: chocolate; }
path.selected { stroke: yellow; }
+path.tip { stroke: yellow; }
.box{position:absolute;box-sizing:border-box;border:4px solid transparent}
.box.city{border-radius:50%}
@@ -75,6 +76,7 @@ path.selected { stroke: yellow; }
.box.city.action{background-color:#6495ed55;border-color:deepskyblue;}
.box.loc.action{background-color:#fff5;border-color:white;}
.box.selected{border-color:yellow;}
+.box.tip { border-color: yellow; }
#capabilities .token {
position: static;
@@ -175,6 +177,7 @@ path.selected { stroke: yellow; }
height: 50px;
}
.token.shipment { background-color: #01904b; border-color: #01904b; box-shadow: 0 0 0 1px #002c00, 0px 1px 4px #0008; }
+.token.shipment.action { box-shadow: 0 0 0 3px white; }
#token_total_support { background-color: #006cb7; border-color: #348cda #004d95 #004d95 #348cda; box-shadow: 0 0 0 1px #000d55, 0px 1px 4px #0008; }
#token_prop_card { background-color: #80643e; border-color: #a0835c #614721 #614721 #a0835c; box-shadow: 0 0 0 1px #291200, 0px 1px 4px #0008; }
diff --git a/play.js b/play.js
index 99cc0ef..e9bcf42 100644
--- a/play.js
+++ b/play.js
@@ -451,6 +451,9 @@ function layout_pieces(list, xorig, yorig) {
e.style.left = (xo + x) + "px"
e.style.top = (yo + y) + "px"
e.style.zIndex = z
+ e.my_x = x + 15
+ e.my_y = y + 24
+ e.my_z = z
}
let z = 50
let i = 0
@@ -461,10 +464,19 @@ function layout_pieces(list, xorig, yorig) {
}
function place_piece(p, x, y, z) {
- p.style.top = y + "px"
p.style.left = x + "px"
+ p.style.top = y + "px"
if (z)
p.style.zIndex = z
+ p.my_x = x
+ p.my_y = y
+ p.my_z = z
+}
+
+function place_piece_under(p, other) {
+ p.style.left = (other.my_x - 26) + "px"
+ p.style.top = (other.my_y - 22) + "px"
+ p.style.zIndex = (other.my_z - 1)
}
function layout_space_bases(list, xc, yc, r) {
@@ -823,8 +835,6 @@ function on_update() {
filter_piece_list(list, s, GOVT, TROOPS)
filter_piece_list(list, s, GOVT, POLICE)
- // TODO: shipments with other faction/piece, not space
-
tix = layout_terror(tix, s, map_get(view.terror, s, 0) * 1)
let xy = get_layout_xy(s)
@@ -851,9 +861,24 @@ function on_update() {
ui.terror[tix].className = "hide"
list.length = 0
- for (let i = 0; i < 4; ++i)
- if (view.shipments[i] === AVAILABLE)
+ for (let i = 0; i < 4; ++i) {
+ let shx = view.shipments[i]
+ if (shx < 0) {
list.push(ui.shipments[i])
+ } else if ((shx & 3) === 0) {
+ let holder = ui.pieces[shx >> 2]
+ place_piece_under(ui.shipments[i], holder)
+ } else {
+ // abandoned shipment!
+ let xy = get_center_xy(shx >> 2)
+ place_piece(ui.shipments[i], xy[0] - 26, xy[1] - 72, 0)
+ }
+ if (view.actions && view.actions.shipment && set_has(view.actions.shipment, i))
+ ui.shipments[i].classList.add("action")
+ else
+ ui.shipments[i].classList.remove("action")
+ ui.shipments[i].classList.toggle("selected", view.selected_shipment === i)
+ }
layout_available_bases(list, 1532, 1722, 2, 2, 89, 69)
list.length = 0
@@ -947,9 +972,21 @@ function on_blur_card_tip(c) {
document.getElementById("card_tip").className = "hide"
}
+function on_focus_space_tip(s) {
+ ui.spaces[s].classList.add("tip")
+}
+
+function on_blur_space_tip(s) {
+ ui.spaces[s].classList.remove("tip")
+}
+
+function on_click_space_tip(s) {
+ ui.spaces[s].scrollIntoView({ block:"center", inline:"center", behavior:"smooth" })
+}
+
function sub_card(match, p1) {
let x = p1 | 0
- let n = data.card_name[x]
+ let n = data.card_title[x]
return `<span class="tip" onmouseenter="on_focus_card_tip(${x})" onmouseleave="on_blur_card_tip(${x})" onclick="on_click_card_tip(${x})">${n}</span>`
}
diff --git a/rules.js b/rules.js
index 3f8669d..4da376b 100644
--- a/rules.js
+++ b/rules.js
@@ -2,10 +2,11 @@
// TODO: Automatic "All done" message. (limited / no more resources / no more available options).
// TODO: auto-next at end of Special Activity / operation space ?
+// rough undo granularity (save only at start of op/activity in space)
+// TODO: resume_...activity - end automatically when no more possible
// TODO: for (s = ... last_space) to for_each_space (including/excluding foreign countries depending on events)
// TODO: how granular undo (one at start of each space, each step, or each piece?)
-// TODO: resume_...activity - end automatically when no more possible
let states = {}
let game = null
@@ -221,6 +222,7 @@ exports.setup = function (seed, scenario, options) {
op: null,
sa: null,
+ transfer: 0,
deck: [],
president: 0,
@@ -446,10 +448,38 @@ function count_pieces(s, faction, type) {
return n
}
+function add_resources(faction, n) {
+ game.resources[faction] = Math.max(0, Math.min(99, game.resources[faction] + n))
+}
+
+function add_aid(n) {
+ game.aid = Math.max(0, Math.min(29, game.aid + n))
+}
+
function is_police(p) {
return p >= first_piece[GOVT][POLICE] && p <= last_piece[GOVT][POLICE]
}
+function is_troops(p) {
+ return p >= first_piece[GOVT][TROOPS] && p <= last_piece[GOVT][TROOPS]
+}
+
+function is_faction_guerrilla(p, faction) {
+ return p >= first_piece[faction][GUERRILLA] && p <= last_piece[faction][GUERRILLA]
+}
+
+function is_farc_guerrilla(p) {
+ return p >= first_piece[FARC][GUERRILLA] && p <= last_piece[FARC][GUERRILLA]
+}
+
+function is_auc_guerrilla(p) {
+ return p >= first_piece[AUC][GUERRILLA] && p <= last_piece[AUC][GUERRILLA]
+}
+
+function is_cartels_guerrilla(p) {
+ return p >= first_piece[CARTELS][GUERRILLA] && p <= last_piece[CARTELS][GUERRILLA]
+}
+
function is_base(p) {
if (p >= first_piece[GOVT][BASE] && p <= last_piece[GOVT][BASE])
return true
@@ -484,6 +514,28 @@ function target_faction(p) {
return 0
}
+function piece_faction(p) {
+ if (p >= first_piece[GOVT][TROOPS] && p <= last_piece[GOVT][TROOPS])
+ return GOVT
+ if (p >= first_piece[GOVT][POLICE] && p <= last_piece[GOVT][POLICE])
+ return GOVT
+ if (p >= first_piece[GOVT][BASE] && p <= last_piece[GOVT][BASE])
+ return GOVT
+ if (p >= first_piece[FARC][GUERRILLA] && p <= last_piece[FARC][GUERRILLA])
+ return FARC
+ if (p >= first_piece[FARC][BASE] && p <= last_piece[FARC][BASE])
+ return FARC
+ if (p >= first_piece[AUC][GUERRILLA] && p <= last_piece[AUC][GUERRILLA])
+ return AUC
+ if (p >= first_piece[AUC][BASE] && p <= last_piece[AUC][BASE])
+ return AUC
+ if (p >= first_piece[CARTELS][GUERRILLA] && p <= last_piece[CARTELS][GUERRILLA])
+ return CARTELS
+ if (p >= first_piece[CARTELS][BASE] && p <= last_piece[CARTELS][BASE])
+ return CARTELS
+ return 0
+}
+
function has_targeted_faction(faction) {
let bit = 1 << faction
return game.op.targeted & bit
@@ -555,6 +607,15 @@ function find_underground_guerrilla(s, faction) {
return -1
}
+function find_guerrilla(s, faction) {
+ let first = first_piece[faction][GUERRILLA]
+ let last = last_piece[faction][GUERRILLA]
+ for (let p = first; p <= last; ++p)
+ if (game.pieces[p] === s)
+ return p
+ return -1
+}
+
function has_any_guerrilla(s) {
return (
has_piece(s, FARC, GUERRILLA) ||
@@ -727,8 +788,11 @@ function set_active(p) {
}
function remove_piece(p) {
+ let s = game.pieces[p]
+ drop_held_shipments(p)
set_underground(p)
game.pieces[p] = AVAILABLE
+ // auto_transfer_dropped_shipments(s)
}
function place_piece(p, s) {
@@ -740,6 +804,125 @@ function move_piece(p, s) {
game.pieces[p] = s
}
+function place_shipment(sh, p) {
+ game.shipments[sh] = p << 2
+}
+
+function remove_shipment(sh) {
+ game.shipments[sh] = -1
+}
+
+function drop_shipment(sh) {
+ let p = game.shipments[sh] >> 2
+ let s = game.pieces[p]
+ let f = piece_faction(p)
+ game.shipments[sh] = (s << 2) | f
+}
+
+function is_shipment_held_by_piece(sh, p) {
+ return game.shipments[sh] === p << 2
+}
+
+function is_shipment_held_by_faction(sh, f) {
+ let x = game.shipments[sh]
+ return x >= 0 && is_faction_guerrilla(x >> 2, f)
+}
+
+function is_any_shipment_held_by_faction(faction) {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_held_by_faction(sh, faction))
+ return true
+ return false
+}
+
+function is_any_shipment_held_by_any_faction() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (sh >= 0)
+ return true
+ return false
+}
+
+function is_shipment_dropped(sh) {
+ return game.shipments[sh] >= 0 && (game.shipments[sh] & 3) > 0
+}
+
+function get_dropped_shipment_faction(sh) {
+ return game.shipments[sh] & 3
+}
+
+function get_dropped_shipment_space(sh) {
+ return game.shipments[sh] >> 2
+}
+
+function remove_dropped_shipments() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ remove_shipment(sh)
+}
+
+function drop_held_shipments(p) {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_held_by_piece(sh, p))
+ drop_shipment(sh)
+}
+
+function has_dropped_shipments() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ return true
+ return false
+}
+
+function can_transfer_shipments() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ if (has_any_guerrilla(get_dropped_shipment_space(sh)))
+ return true
+ return false
+}
+
+function auto_transfer_dropped_shipments(s) {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ auto_transfer_shipment(sh)
+}
+
+function auto_transfer_shipment(sh) {
+ let f = get_dropped_shipment_faction(sh)
+ let s = get_dropped_shipment_space(sh)
+ let a, b, p
+
+ p = find_guerrilla(s, f)
+ if (p >= 0) {
+ place_shipment(sh, p)
+ return
+ }
+
+ if (f === FARC) {
+ a = find_guerrilla(s, AUC)
+ b = find_guerrilla(s, CARTELS)
+ }
+ if (f === AUC) {
+ a = find_guerrilla(s, FARC)
+ b = find_guerrilla(s, CARTELS)
+ }
+ if (f === CARTELS) {
+ a = find_guerrilla(s, FARC)
+ b = find_guerrilla(s, AUC)
+ }
+
+ if (a >= 0 && b < 0) {
+ place_shipment(sh, a)
+ return
+ }
+ if (a < 0 && b >= 0) {
+ place_shipment(sh, b)
+ return
+ }
+
+ // nobody to pick it up...
+}
+
function count_terror_and_sabotage() {
let n = (game.sabotage.length >> 1)
for (let i = 1; i < game.terror.length; i += 2)
@@ -941,7 +1124,7 @@ function goto_eligible2() {
states.eligible1 = {
inactive: "1st Eligible",
prompt() {
- view.prompt = "1st Eligible: Choose a Sequence of Play option."
+ view.prompt = `${data.card_title[this_card()]}: Choose a Sequence of Play option.`
gen_action("sop", SOP_1ST_OP_ONLY)
gen_action("sop", SOP_1ST_OP_AND_SA)
gen_action("sop", SOP_1ST_EVENT)
@@ -970,7 +1153,7 @@ states.eligible1 = {
states.eligible2 = {
inactive: "2nd Eligible",
prompt() {
- view.prompt = "2nd Eligible: Choose a Sequence of Play option."
+ view.prompt = `${data.card_title[this_card()]}: Choose a Sequence of Play option.`
if (did_option(SOP_1ST_OP_ONLY))
gen_action("sop", SOP_2ND_LIMOP)
if (did_option(SOP_1ST_OP_AND_SA))
@@ -1002,9 +1185,9 @@ states.eligible2 = {
function goto_pass() {
log_h2(faction_name[game.current] + " - Pass")
if (game.current === GOVT)
- game.resources[game.current] += 3
+ add_resources(game.current, 3)
else
- game.resources[game.current] += 1
+ add_resources(game.current, 1)
resume_event_card()
}
@@ -1015,7 +1198,7 @@ function goto_limop_or_event() {
states.limop_or_event = {
prompt() {
- view.prompt = "2nd Eligible: Event or Limited Operation?"
+ view.prompt = `${data.card_title[this_card()]}: Event or Limited Operation?`
view.actions.event = 1
view.actions.limop = 1
},
@@ -1037,24 +1220,30 @@ function goto_event() {
function goto_op_only() {
log_h2(faction_name[game.current] + " - Op Only")
- goto_operation(0, 0, 0)
+ goto_operation(0, 0, 0, 1)
}
function goto_op_and_sa() {
log_h2(faction_name[game.current] + " - Op + Special")
- goto_operation(0, 0, 1)
+ goto_operation(0, 0, 1, 0)
}
function goto_limop() {
log_h2(faction_name[game.current] + " - LimOp")
- goto_operation(0, 1, 0)
+ goto_operation(0, 1, 0, 1)
+}
+
+function goto_ship_limop() {
+ log_h2(faction_name[game.current] + " - Ship")
+ goto_operation(0, 1, 0, 0)
}
-function goto_operation(free, limited, special) {
+function goto_operation(free, limited, special, ship) {
game.state = "op"
game.op = {
free,
limited,
+ ship,
spaces: [],
targeted: 0,
pieces: 0,
@@ -1063,11 +1252,23 @@ function goto_operation(free, limited, special) {
game.sa = special
}
+function can_ship() {
+ // TODO: ship by other faction?
+ return game.op.ship && is_any_shipment_held_by_faction(game.current)
+}
+
function end_operation() {
- game.op = null
- resume_event_card()
+ if (can_ship()) {
+ push_undo()
+ game.state = "ship"
+ } else {
+ game.op = null
+ resume_event_card()
+ }
}
+// === REMOVE PIECES (VOLUNTARILY) ===
+
function action_remove() {
push_undo()
game.save_state = game.state
@@ -1101,6 +1302,130 @@ states.remove = {
done() {
game.state = game.save_state
delete game.save_state
+ transfer_or_remove_shipments()
+ },
+}
+
+// === CLAIM SHIPMENTS ===
+
+function transfer_or_remove_shipments()
+{
+ auto_transfer_dropped_shipments()
+ if (has_dropped_shipments()) {
+ if (can_transfer_shipments())
+ goto_transfer_shipments()
+ else
+ remove_dropped_shipments()
+ }
+}
+
+function transfer_or_drug_bust_shipments()
+{
+ auto_transfer_dropped_shipments()
+ if (has_dropped_shipments()) {
+ if (can_transfer_shipments())
+ goto_transfer_shipments()
+ else
+ goto_drug_bust()
+ }
+}
+
+function goto_transfer_shipments() {
+ game.transfer = {
+ active: game.active,
+ state: game.state,
+ shipment: 0,
+ }
+ resume_transfer_shipments()
+}
+
+function resume_transfer_shipments() {
+ for (let sh = 0; sh < 4; ++sh) {
+ if (is_shipment_dropped(sh)) {
+ game.active = get_dropped_shipment_faction(sh)
+ game.state = "transfer_shipment"
+ game.transfer.shipment = sh
+ return
+ }
+ }
+ game.active = game.active
+ game.state = game.transfer.state
+ game.transfer = 0
+}
+
+states.transfer_shipment = {
+ prompt() {
+ view.prompt = "Transfer Shipment to another Guerrilla."
+ let s = get_dropped_shipment_space(game.transfer.shipment)
+
+ gen_piece_in_space(s, FARC, GUERRILLA)
+ gen_piece_in_space(s, AUC, GUERRILLA)
+ gen_piece_in_space(s, CARTELS, GUERRILLA)
+
+ view.actions.undo = 0
+ },
+ piece(p) {
+ push_undo()
+ place_shipment(game.transfer.shipment, p)
+ resume_transfer_shipments()
+ },
+}
+
+function goto_drug_bust() {
+ game.transfer = game.state
+ resume_drug_bust()
+}
+
+function resume_drug_bust() {
+ if (has_dropped_shipments()) {
+ game.state = "drug_bust"
+ } else {
+ game.state = game.transfer
+ game.transfer = 0
+ }
+}
+
+states.drug_bust = {
+ prompt() {
+ view.prompt = "Drug Bust: Gain 6 resources per removed Shipment."
+ gen_action_resources(GOVT)
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ gen_action_shipment(sh)
+ },
+ shipment(sh) {
+ add_resources(GOVT, 6)
+ remove_shipment(sh)
+ resume_drug_bust()
+ },
+ resources(_) {
+ for (let sh = 0; sh < 4; ++sh) {
+ if (is_shipment_dropped(sh)) {
+ add_resources(GOVT, 6)
+ remove_shipment(sh)
+ }
+ }
+ resume_drug_bust()
+ },
+}
+
+states.ship = {
+ prompt() {
+ view.prompt = "Ship: Remove Shipment for a free, extra Limited Operation?"
+ view.actions.end_op = 1
+ // TODO: ask another faction?
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_held_by_faction(sh, game.current))
+ gen_action_shipment(sh)
+ },
+ shipment(sh) {
+ push_undo()
+ remove_shipment(sh)
+ goto_ship_limop()
+ },
+ end_operation() {
+ game.op.ship = 0
+ end_operation()
},
}
@@ -1676,11 +2001,14 @@ states.patrol_assault_space = {
remove_piece(p)
update_control()
- if (--game.op.count === 0 || !can_assault_space(game.op.where))
+ if (--game.op.count === 0 || !can_assault_space(game.op.where)) {
end_patrol_assault_space()
+ transfer_or_drug_bust_shipments()
+ }
},
next() {
end_patrol_assault_space()
+ transfer_or_drug_bust_shipments()
},
}
@@ -1961,14 +2289,15 @@ states.assault_space = {
remove_piece(p)
update_control()
- // TODO: 3.2.5 Drug Bust
-
- if (--game.op.count === 0 || !can_assault_space(game.op.where))
+ if (--game.op.count === 0 || !can_assault_space(game.op.where)) {
game.state = "assault"
+ transfer_or_drug_bust_shipments()
+ }
},
next() {
push_undo()
game.state = "assault"
+ transfer_or_drug_bust_shipments()
},
}
@@ -2100,6 +2429,7 @@ states.rally_base = {
} else {
place_piece(p, game.op.where)
game.state = "rally"
+ transfer_or_remove_shipments()
}
update_control()
},
@@ -2208,7 +2538,6 @@ function may_activate_marching_guerrillas() {
}
function activate_marching_guerrillas(group) {
- console.log("MARCH", group)
if (may_activate_marching_guerrillas()) {
let count = group.length
count += count_pieces(game.op.where, GOVT, TROOPS)
@@ -2232,6 +2561,7 @@ states.march_move = {
return // continue
let s = game.pieces[p]
+ if (s > 0)
if (is_adjacent(game.op.where, s))
gen_action_piece(p)
})
@@ -2420,12 +2750,15 @@ states.attack_remove = {
// TODO: Captured Goods
- if (--game.op.count === 0 || !has_exposed_enemy_piece(game.op.where, game.current))
+ if (--game.op.count === 0 || !has_exposed_enemy_piece(game.op.where, game.current)) {
game.state = "attack"
+ transfer_or_remove_shipments()
+ }
},
next() {
game.state = "attack"
game.op.count = 0
+ transfer_or_remove_shipments()
}
}
@@ -2656,6 +2989,7 @@ states.air_strike = {
remove_piece(p)
update_control()
end_special_activity()
+ transfer_or_remove_shipments()
}
}
@@ -2823,7 +3157,7 @@ states.extort1 = {
let p = find_underground_guerrilla(s, game.current)
set_active(p)
set_add(game.sa.spaces, s)
- game.resources[game.current] += 1
+ add_resources(game.current, 1)
},
end_activity() {
push_undo()
@@ -2855,7 +3189,7 @@ states.extort2 = {
},
piece(p) {
set_active(p)
- game.resources[game.current] += 1
+ add_resources(game.current, 1)
game.sa.where = -1
},
space(s) {
@@ -2944,7 +3278,7 @@ function transfer_resources(from, to, n) {
if (n > game.resources[from])
n = game.resources[from]
game.resources[from] -= n
- game.resources[to] += n
+ add_resources(to, n)
}
states.kidnap_space = {
@@ -3068,6 +3402,9 @@ states.assassinate_space = {
end_special_activity()
else
game.state = "assassinate"
+
+ // TODO: Commandeer
+ transfer_or_remove_shipments()
},
}
@@ -3136,14 +3473,14 @@ states.cultivate_move = {
function has_available_shipment() {
for (let i = 0; i < 4; ++i)
- if (game.shipments[i] === AVAILABLE)
+ if (game.shipments[i] < 0)
return true
return false
}
function find_available_shipment() {
for (let i = 0; i < 4; ++i)
- if (game.shipments[i] === AVAILABLE)
+ if (game.shipments[i] < 0)
return i
return -1
}
@@ -3154,6 +3491,7 @@ function goto_process() {
game.sa = {
save: game.state,
spaces: [],
+ count: 0,
where: -1,
}
game.state = "process"
@@ -3172,6 +3510,7 @@ states.process = {
log(`Process in S${s}.`)
set_add(game.sa.spaces, s)
game.sa.where = s
+ game.sa.count = 2
game.state = "process_space"
},
end_activity() {
@@ -3184,25 +3523,37 @@ states.process_space = {
prompt() {
view.prompt = `Process: Remove Base or place Shipment with Guerrilla in ${space_name[game.sa.where]}.`
view.where = game.sa.where
- gen_piece_in_space(game.sa.where, CARTELS, BASE)
- if (has_available_shipment()) {
- gen_piece_in_space(game.sa.where, CARTELS, GUERRILLA)
- gen_piece_in_space(game.sa.where, FARC, GUERRILLA)
- gen_piece_in_space(game.sa.where, AUC, GUERRILLA)
+ if (game.sa.count === 2 || game.sa.count === 0)
+ gen_piece_in_space(game.sa.where, CARTELS, BASE)
+ if (game.sa.count > 0) {
+ if (has_available_shipment()) {
+ gen_piece_in_space(game.sa.where, CARTELS, GUERRILLA)
+ gen_piece_in_space(game.sa.where, FARC, GUERRILLA)
+ gen_piece_in_space(game.sa.where, AUC, GUERRILLA)
+ }
}
+ view.actions.next = 1
},
piece(p) {
- push_undo()
if (is_base(p)) {
- game.resources[CARTELS] += 3
+ log("Removed Base.")
+ add_resources(CARTELS, 3)
remove_piece(p)
update_control()
+ game.sa.count = 0
+ if (!has_piece(game.sa.where, CARTELS, BASE))
+ game.state = "process"
} else {
- let i = find_available_shipment()
- game.shipments[i] = p
+ log("Placed Shipment.")
+ let sh = find_available_shipment()
+ place_shipment(sh, p)
+ if (--game.sa.count === 0 || !has_available_shipment())
+ game.state = "process"
}
- game.state = "process"
},
+ next() {
+ game.state = "process"
+ }
}
// SPECIAL ACTIVITY: BRIBE
@@ -3287,8 +3638,6 @@ states.bribe_space = {
gen_piece_in_space(game.sa.where, AUC, GUERRILLA)
}
- // TODO: Contraband
-
if (did_maximum_damage(game.sa.targeted))
view.actions.next = 1
else
@@ -3298,10 +3647,15 @@ states.bribe_space = {
//push_undo()
remove_piece(p)
update_control()
- if (game.sa.targeted || is_base(p))
+ if (game.sa.targeted || is_base(p)) {
resume_bribe()
- else
+ transfer_or_remove_shipments()
+ } else {
game.sa.targeted |= target_faction(p)
+ }
+
+ // TODO: Contraband
+ transfer_or_remove_shipments()
},
flip() {
//push_undo()
@@ -3311,6 +3665,7 @@ states.bribe_space = {
},
next() {
resume_bribe()
+ transfer_or_remove_shipments()
},
}
@@ -3425,6 +3780,10 @@ function gen_action_resources(faction) {
gen_action("resources", faction)
}
+function gen_action_shipment(sh) {
+ gen_action("shipment", sh)
+}
+
function is_current_role(role) {
switch (role) {
case NAME_GOVT_AUC:
@@ -3528,7 +3887,7 @@ exports.action = function (state, role, action, arg) {
load_game(state)
// XXX - don't allow adding properties
- Object.seal(game) // XXX: don't allow adding properties
+ // Object.seal(game) // XXX: don't allow adding properties
let S = states[game.state]
if (S && action in S) {
@@ -3555,7 +3914,7 @@ exports.is_checkpoint = function (a, b) {
states.event = {
prompt() {
let c = this_card()
- view.prompt = `${data.card_name[c]}: Choose effect.`
+ view.prompt = `${data.card_title[c]}: Choose effect.`
view.actions.shaded = 1
view.actions.unshaded = 1
},
@@ -3870,4 +4229,3 @@ function map_delete(map, item) {
}
}
}
-
diff --git a/tools/gendata.js b/tools/gendata.js
index d11820b..0878d11 100644
--- a/tools/gendata.js
+++ b/tools/gendata.js
@@ -18,13 +18,13 @@ function to_ascii(s) {
const IMAP = { G: GOVT, F: FARC, A: AUC, C: CARTELS }
let order = null
-let card_name = [ null ]
+let card_title = [ null ]
let card_order = [ null ]
function def_order(s) {
order = [ IMAP[s[0]], IMAP[s[1]], IMAP[s[2]], IMAP[s[3]] ]
}
function def_card(number, name) {
- card_name[number] = name
+ card_title[number] = name
card_order[number] = order
}
def_order("GFAC")
@@ -480,7 +480,7 @@ for (let i = 0; i < spaces.length; ++i) {
}
data.card_order = card_order
-data.card_name = card_name
+data.card_title = card_title
data.card_flavor = card_flavor
data.card_flavor_shaded = card_flavor_shaded
data.space_name = space_name