summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data.js584
-rw-r--r--data.ts663
-rw-r--r--play.ts6
-rw-r--r--rules.js163
-rw-r--r--rules.ts223
-rw-r--r--types.d.ts46
6 files changed, 1002 insertions, 683 deletions
diff --git a/data.js b/data.js
index d60e7ee..c41aee7 100644
--- a/data.js
+++ b/data.js
@@ -30,329 +30,601 @@ const CLOSEST_TO_VICTORY = 'v';
const TOWARDS_CENTER = 10;
const AWAY_FROM_CENTER = 11;
const ANY = 'any';
+const SELF = 'self';
+const OTHER_PLAYERS = 'other';
+const TRASH = 'trash';
+function create_effect(type, target, value) {
+ return {
+ type,
+ target,
+ value,
+ };
+}
const data = {
cards: [
{},
{
id: 1,
+ effects: [
+ create_effect('track', FOREIGN_AID, 2),
+ create_effect('track', SOVIET_SUPPORT, -1),
+ ],
strength: 1,
title: 'CLANDESTINE FRENCH ARMS',
type: 'pc',
},
{
id: 2,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('front', ANY, 3),
+ create_effect('track', LIBERTY, -2),
+ ],
strength: 2,
title: 'POPULAR ARMY OF THE REPUBLIC',
type: 'pc',
},
{
id: 3,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 3),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'MEXICAN GUNS',
type: 'pc',
},
{
id: 4,
+ effects: [
+ create_effect('front', MADRID, 3),
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 1,
title: 'BATTLE OF GUADALAJARA',
type: 'pc',
},
{
id: 5,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: '"SI ME OUIERES ESCRIBIR"',
type: 'pc',
},
{
id: 6,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', GOVERNMENT, 1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'XYZ LINE',
type: 'pc',
},
{
id: 7,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', FOREIGN_AID, 2),
+ create_effect('track', COLLECTIVIZATION, -1),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'INDALECIO PRIETO',
type: 'pc',
},
{
id: 8,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', ANY, 3),
+ ],
strength: 1,
title: "PEOPLE'S OLYMPIAD",
type: 'pc',
},
{
id: 9,
+ effects: [
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', COLLECTIVIZATION, -2),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ ],
strength: 1,
title: 'FOUR ANARCHIST MINISTERS',
type: 'pc',
},
{
id: 10,
+ effects: [
+ create_effect('track', FOREIGN_AID, 4),
+ create_effect('add_to_tableau', SELF, 1),
+ ],
strength: 1,
title: 'GUERNICA',
type: 'pc',
},
{
id: 11,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 1),
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', FOREIGN_AID, 2),
+ ],
strength: 1,
title: 'ERNEST HEMINGWAY',
type: 'pc',
},
{
id: 12,
+ effects: [
+ create_effect('track', SOVIET_SUPPORT, -3),
+ create_effect('track', COLLECTIVIZATION, -3),
+ ],
strength: 1,
title: 'HUESCA OFFENSIVE',
type: 'pc',
},
{
id: 13,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('track', SOVIET_SUPPORT, -1),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'PABLO NERUDA',
type: 'pc',
},
{
id: 14,
+ effects: [
+ create_effect('front', NORTHERN, 2),
+ create_effect('track', COLLECTIVIZATION, -1),
+ create_effect('track', GOVERNMENT, 1),
+ ],
strength: 1,
title: 'EUSKO GUDAROSTEA',
type: 'pc',
},
{
id: 15,
+ effects: [
+ create_effect('track', FOREIGN_AID, 2),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: 'JUAN NEGRÍN',
type: 'pc',
},
{
id: 16,
+ effects: [],
strength: 1,
title: 'PUBLICIZE FASCIST WAR CRIMES',
type: 'pc',
},
{
id: 17,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('track', GOVERNMENT, 1),
+ ],
strength: 1,
title: 'AGRARIAN REFORM',
type: 'pc',
},
{
id: 18,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, -3),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'IMPOSE FACTORY MANAGERS',
type: 'pc',
},
{
id: 19,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('track', GOVERNMENT, -1),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: '¡NO PASARÁN!',
type: 'pc',
},
{
id: 20,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ ],
strength: 2,
title: 'RUSSIAN FIGHTERS',
type: 'pc',
},
{
id: 21,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', COLLECTIVIZATION, -2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'ENRIQUE LÍSTER',
type: 'pc',
},
{
id: 22,
+ effects: [
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ ],
strength: 2,
title: 'LARGO CABALLERO',
type: 'pc',
},
{
id: 23,
+ effects: [
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 2,
title: 'SOVIET TANKS',
type: 'pc',
},
{
id: 24,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', LIBERTY, -1),
+ create_effect('track', GOVERNMENT, -2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'DOLORES IBÁRRURI',
type: 'pc',
},
{
id: 25,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: 'PAUL ROBESON',
type: 'pc',
},
{
id: 26,
+ effects: [
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', GOVERNMENT, -1),
+ ],
strength: 1,
title: 'MADRID DEFENSE COUNCIL',
type: 'pc',
},
{
id: 27,
+ effects: [
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 3),
+ create_effect('track', FOREIGN_AID, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: "STALIN GETS THE REPUBLIC'S GOLD",
type: 'pc',
},
{
id: 28,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', SOUTHERN, 3),
+ create_effect('track', FOREIGN_AID, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'INTERNATIONAL BRIGADES',
type: 'pc',
},
{
id: 29,
+ effects: [
+ create_effect('track', GOVERNMENT, -2),
+ ],
strength: 1,
title: 'BAN WOMEN FROM THE FRONT',
type: 'pc',
},
{
id: 30,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', FOREIGN_AID, -3),
+ ],
strength: 1,
title: 'ABRAHAM LINCOLN BRIGADE',
type: 'pc',
},
{
id: 31,
+ effects: [
+ create_effect('attack', ANY, -2),
+ create_effect('track', SOVIET_SUPPORT, 3),
+ create_effect('track', GOVERNMENT, -2),
+ ],
strength: 2,
title: 'OUTLAW THE POUM',
type: 'pc',
},
{
id: 32,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ ],
strength: 1,
title: 'DISBAND THE CONTROL PATROLS',
type: 'pc',
},
{
id: 33,
+ effects: [
+ create_effect('attack', ARAGON, -1),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ create_effect('track', LIBERTY, -4),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 1,
title: 'MAY DAYS',
type: 'pc',
},
{
id: 34,
+ effects: [
+ create_effect('front', SOUTHERN, 2),
+ create_effect('track', GOVERNMENT, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'FIFTH REGIMENT',
type: 'pc',
},
{
id: 35,
+ effects: [
+ create_effect('front', ANY, 2),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'THÄLMANN BATTALION',
type: 'pc',
},
{
id: 36,
+ effects: [
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', COLLECTIVIZATION, -3),
+ create_effect('draw_card', SELF, 2),
+ create_effect('add_to_tableau', ANY, 1),
+ ],
strength: 1,
title: 'DE-COLLECTIVIZE AGRICULTURE',
type: 'pc',
},
{
id: 37,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', COLLECTIVIZATION, 2),
+ create_effect('track', LIBERTY, 2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'BUENAVENTURA DURRUTI',
type: 'pc',
},
{
id: 38,
+ effects: [
+ create_effect('track', LIBERTY, 3),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 1),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 2,
title: 'MUJERES LIBRES',
type: 'pc',
},
{
id: 39,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 1),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'IRON COLUMN',
type: 'pc',
},
{
id: 40,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 3),
+ create_effect('track', COLLECTIVIZATION, 2),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'ASTURIAN MINERS',
type: 'pc',
},
{
id: 41,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', COLLECTIVIZATION, 3),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: 'CNT-FAI',
type: 'pc',
},
{
id: 42,
+ effects: [
+ create_effect('track', LIBERTY, 2),
+ create_effect('front', ARAGON, 2),
+ create_effect('front', MADRID, 1),
+ ],
strength: 2,
title: 'DURRUTI COLUMN',
type: 'pc',
},
{
id: 43,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', ARAGON, 1),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: 'GEORGE ORWELL',
type: 'pc',
},
{
id: 44,
+ effects: [
+ create_effect('front', MADRID, 2),
+ create_effect('track', LIBERTY, 1),
+ create_effect('track', FOREIGN_AID, -1),
+ ],
strength: 1,
title: 'F.I.J.L.',
type: 'pc',
},
{
id: 45,
+ effects: [
+ create_effect('front', ANY, 2),
+ create_effect('track', FOREIGN_AID, -3)
+ ],
strength: 1,
title: 'ARM THE UNIONS',
type: 'pc',
},
{
id: 46,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', LIBERTY, 2),
+ ],
strength: 1,
title: 'GUERRILLAS',
type: 'pc',
},
{
id: 47,
+ effects: [
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', LIBERTY, 2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'RADICAL EDUCATION',
type: 'pc',
},
{
id: 48,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ ],
strength: 1,
title: 'MATTEOTTI BATTALION',
type: 'pc',
},
{
id: 49,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 4),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'COLLECTIVIZE AGRICULTURE',
type: 'pc',
},
{
id: 50,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 1),
+ create_effect('track', FOREIGN_AID, -1),
+ ],
strength: 1,
title: 'ARMORED VEHICLES',
type: 'pc',
},
{
id: 51,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 3),
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ ],
strength: 1,
title: 'INDUSTRIAL DEMOCRACY',
type: 'pc',
},
{
id: 52,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ ],
strength: 2,
title: 'AFFINITY GROUPS',
type: 'pc',
},
{
id: 53,
+ effects: [
+ create_effect('track', LIBERTY, 1),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'GENDER-INCLUSIVE MILITIA',
type: 'pc',
},
{
id: 54,
+ effects: [
+ create_effect('track', SOVIET_SUPPORT, -1),
+ create_effect('add_to_tableau', ANY, 1),
+ ],
strength: 1,
title: 'FEDERICA MONTSENY',
type: 'pc',
@@ -360,21 +632,9 @@ const data = {
{
id: 55,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: 4,
- },
- {
- target: ARAGON,
- type: 'attack',
- value: 1,
- },
- {
- target: SOVIET_SUPPORT,
- type: 'track',
- value: -2,
- },
+ create_effect('attack', SOUTHERN, -4),
+ create_effect('attack', ARAGON, -1),
+ create_effect('track', SOVIET_SUPPORT, -2),
],
title: 'SPANISH LEGION',
type: 'ec',
@@ -383,21 +643,9 @@ const data = {
{
id: 56,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: 3,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: 2,
- },
- {
- target: MORALE_BONUS,
- type: 'bonus',
- value: OFF,
- },
+ create_effect('attack', SOUTHERN, -3),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('bonus', MORALE_BONUS, OFF),
],
title: 'BRITISH TREACHERY AT GIBRALTAR',
type: 'ec',
@@ -406,21 +654,9 @@ const data = {
{
id: 57,
effects: [
- {
- target: MADRID,
- type: 'attack',
- value: -5,
- },
- {
- target: FOREIGN_AID,
- type: 'track',
- value: -2,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', MADRID, -5),
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'PARACUELLOS MASSACRES',
type: 'ec',
@@ -429,21 +665,9 @@ const data = {
{
id: 58,
effects: [
- {
- target: 'n',
- type: 'attack',
- value: -5,
- },
- {
- target: 'v',
- type: 'attack',
- value: -1,
- },
- {
- target: COLLECTIVIZATION,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', NORTHERN, -5),
+ create_effect('attack', CLOSEST_TO_VICTORY, -1),
+ create_effect('track', COLLECTIVIZATION, -1),
],
title: 'CARLISTS',
type: 'ec',
@@ -452,21 +676,9 @@ const data = {
{
id: 59,
effects: [
- {
- target: MADRID,
- type: 'attack',
- value: -4,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -2,
- },
- {
- target: LIBERTY,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', MADRID, -4),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('track', LIBERTY, -1),
],
title: 'ASSASSINATION OF GARCIA LORCA',
type: 'ec',
@@ -475,21 +687,9 @@ const data = {
{
id: 60,
effects: [
- {
- target: NORTHERN,
- type: 'attack',
- value: -3,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -3,
- },
- {
- target: LIBERTY,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', NORTHERN, -3),
+ create_effect('attack', CLOSEST_TO_VICTORY, -3),
+ create_effect('track', LIBERTY, -1),
],
title: 'GENERAL SANJURIO',
type: 'ec',
@@ -498,21 +698,9 @@ const data = {
{
id: 61,
effects: [
- {
- target: ARAGON,
- type: 'attack',
- value: -4,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -2,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', ARAGON, -2),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'FAILED INVASION OF MALLORCA',
type: 'ec',
@@ -521,21 +709,9 @@ const data = {
{
id: 62,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: -5,
- },
- {
- target: MORALE_BONUS,
- type: 'bonus',
- value: OFF,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', SOUTHERN, -5),
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'AIRLIFT OF THE ARMY OF AFRICA',
type: 'ec',
@@ -611,82 +787,34 @@ const data = {
id: LIBERTY,
name: 'Liberty',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: MADRID,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', MADRID, -1),
null,
- {
- type: 'track',
- target: SOVIET_SUPPORT,
- value: 1,
- },
+ create_effect('track', SOVIET_SUPPORT, 1),
null,
null,
null,
- {
- type: 'track',
- target: GOVERNMENT,
- value: TOWARDS_CENTER,
- },
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('front', ANY, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: COLLECTIVIZATION,
name: 'Collectivization',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: ARAGON,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', ARAGON, -1),
null,
- {
- type: 'track',
- target: FOREIGN_AID,
- value: 1,
- },
+ create_effect('track', FOREIGN_AID, 1),
null,
null,
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: LIBERTY,
- value: 1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', LIBERTY, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
@@ -694,114 +822,50 @@ const data = {
name: 'Government',
triggers: [
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
null,
null,
- {
- type: 'track',
- target: SOVIET_SUPPORT,
- value: 1,
- },
+ create_effect('track', SOVIET_SUPPORT, 1),
null,
null,
- {
- type: 'track',
- target: FOREIGN_AID,
- value: 1,
- },
+ create_effect('track', FOREIGN_AID, 1),
null,
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: SOVIET_SUPPORT,
name: 'Soviet Support',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: SOUTHERN,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', SOUTHERN, -1),
null,
null,
null,
- {
- type: 'track',
- target: LIBERTY,
- value: -1,
- },
+ create_effect('track', LIBERTY, -1),
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: GOVERNMENT,
- value: -1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', GOVERNMENT, -1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: FOREIGN_AID,
name: 'Foreign Aid',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: NORTHERN,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', NORTHERN, -1),
null,
null,
null,
- {
- type: 'track',
- target: LIBERTY,
- value: -1,
- },
+ create_effect('track', LIBERTY, -1),
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: GOVERNMENT,
- value: 1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', GOVERNMENT, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
],
diff --git a/data.ts b/data.ts
index 3b4e29d..d2d2172 100644
--- a/data.ts
+++ b/data.ts
@@ -1,49 +1,4 @@
-export interface CardBase {
- id: number;
- title: string;
-}
-
-export interface CardEffect {
- type: 'attack' | 'track' | 'bonus' | 'hero_points';
- target: string | number;
- value: number;
-}
-
-export interface EventCard extends CardBase {
- type: 'ec';
- year: number;
- effects: CardEffect[];
-}
-
-export interface PlayerCard extends CardBase {
- type: 'pc';
- strength: number;
-}
-
-export type Card = EventCard | PlayerCard;
-
-export interface StaticData {
- cards: Card[];
- fronts: Array<{
- id: string;
- name: string;
- left: number;
- top: number;
- }>;
- medaillons: Array<{
- id: number;
- name: string;
- }>;
- tracks: Array<{
- id: number;
- name: string;
- triggers: Array<null | {
- type: 'attack' | 'track' | 'bonus' | 'front' | 'medaillon';
- target: string | number;
- value: number;
- }>;
- }>;
-}
+import { Card, Effect, StaticData } from './types';
const LIBERTY = 0;
const COLLECTIVIZATION = 1;
@@ -70,6 +25,9 @@ const TOWARDS_CENTER = 10;
const AWAY_FROM_CENTER = 11;
const ANY = 'any';
+const SELF = 'self';
+const OTHER_PLAYERS = 'other';
+const TRASH = 'trash';
export {
LIBERTY,
@@ -84,329 +42,630 @@ export {
PLAYER_WITH_MOST_HERO_POINTS,
};
+function create_effect(
+ type: Effect['type'],
+ target: Effect['target'],
+ value: Effect['value']
+): Effect {
+ return {
+ type,
+ target,
+ value,
+ };
+}
+
const data: StaticData = {
cards: [
{} as Card,
{
id: 1,
+ effects: [
+ create_effect('track', FOREIGN_AID, 2),
+ // create_effect('track', FOREIGN_AID, 2), // Conditional support
+ create_effect('track', SOVIET_SUPPORT, -1),
+ ],
strength: 1,
title: 'CLANDESTINE FRENCH ARMS',
type: 'pc',
},
{
id: 2,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('front', ANY, 3),
+ create_effect('track', LIBERTY, -2),
+ ],
strength: 2,
title: 'POPULAR ARMY OF THE REPUBLIC',
type: 'pc',
},
{
id: 3,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 3),
+ // create_effect('track', FOREIGN_AID, 2), // conditional effect
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'MEXICAN GUNS',
type: 'pc',
},
{
id: 4,
+ effects: [
+ create_effect('front', MADRID, 3),
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 1,
title: 'BATTLE OF GUADALAJARA',
type: 'pc',
},
{
id: 5,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: '"SI ME OUIERES ESCRIBIR"',
type: 'pc',
},
{
id: 6,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ // create_effect('front', ANY, ON), // REMOVE attacks from front
+ create_effect('track', GOVERNMENT, 1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'XYZ LINE',
type: 'pc',
},
{
id: 7,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', FOREIGN_AID, 2),
+ create_effect('track', COLLECTIVIZATION, -1),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'INDALECIO PRIETO',
type: 'pc',
},
{
id: 8,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', ANY, 3),
+ ],
strength: 1,
title: "PEOPLE'S OLYMPIAD",
type: 'pc',
},
{
id: 9,
+ effects: [
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', COLLECTIVIZATION, -2),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ ],
strength: 1,
title: 'FOUR ANARCHIST MINISTERS',
type: 'pc',
},
{
id: 10,
+ effects: [
+ create_effect('track', FOREIGN_AID, 4),
+ // create_effect('track', FOREIGN_AID, 3) // conditional draw cards
+ create_effect('add_to_tableau', SELF, 1),
+ ],
strength: 1,
title: 'GUERNICA',
type: 'pc',
},
{
id: 11,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 1),
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', FOREIGN_AID, 2),
+ ],
strength: 1,
title: 'ERNEST HEMINGWAY',
type: 'pc',
},
{
id: 12,
+ effects: [
+ // create_effect('hero_points', OTHER_PLAYERS, -2), // take from other player
+ create_effect('track', SOVIET_SUPPORT, -3),
+ create_effect('track', COLLECTIVIZATION, -3),
+ ],
strength: 1,
title: 'HUESCA OFFENSIVE',
type: 'pc',
},
{
id: 13,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('track', SOVIET_SUPPORT, -1),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'PABLO NERUDA',
type: 'pc',
},
{
id: 14,
+ effects: [
+ create_effect('front', NORTHERN, 2),
+ create_effect('track', COLLECTIVIZATION, -1),
+ create_effect('track', GOVERNMENT, 1),
+ ],
strength: 1,
title: 'EUSKO GUDAROSTEA',
type: 'pc',
},
{
id: 15,
+ effects: [
+ // create_effect('track', ) Choose track,
+ create_effect('track', FOREIGN_AID, 2),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: 'JUAN NEGRÍN',
type: 'pc',
},
{
id: 16,
+ effects: [
+ // create_effect('front', ANY, -2) // move attacks
+ // create_effect('track', FOREIGN_AID, 4) // conditional
+ ],
strength: 1,
title: 'PUBLICIZE FASCIST WAR CRIMES',
type: 'pc',
},
{
id: 17,
+ effects: [
+ create_effect('track', FOREIGN_AID, 1),
+ create_effect('track', GOVERNMENT, 1),
+ // create_effect('track', COLLECTIVIZATION,-4) // conditional
+ ],
strength: 1,
title: 'AGRARIAN REFORM',
type: 'pc',
},
{
id: 18,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, -3),
+ create_effect('track', GOVERNMENT, 2),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'IMPOSE FACTORY MANAGERS',
type: 'pc',
},
{
id: 19,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('track', GOVERNMENT, -1),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: '¡NO PASARÁN!',
type: 'pc',
},
{
id: 20,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ // create_effect('front') // conditional on track
+ ],
strength: 2,
title: 'RUSSIAN FIGHTERS',
type: 'pc',
},
{
id: 21,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', COLLECTIVIZATION, -2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'ENRIQUE LÍSTER',
type: 'pc',
},
{
id: 22,
+ effects: [
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ // create_effect('track', GOVERNMENT, -2) // conditional
+ ],
strength: 2,
title: 'LARGO CABALLERO',
type: 'pc',
},
{
id: 23,
+ effects: [
+ // create_effect('front', ANY, 4) // conditional on track
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 2,
title: 'SOVIET TANKS',
type: 'pc',
},
{
id: 24,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', LIBERTY, -1),
+ create_effect('track', GOVERNMENT, -2),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'DOLORES IBÁRRURI',
type: 'pc',
},
{
id: 25,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: 'PAUL ROBESON',
type: 'pc',
},
{
id: 26,
+ effects: [
+ // create_effect('front', MADRID, 1), // ability active
+ create_effect('track', LIBERTY, -2),
+ create_effect('track', GOVERNMENT, -1),
+ ],
strength: 1,
title: 'MADRID DEFENSE COUNCIL',
type: 'pc',
},
{
id: 27,
+ effects: [
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 3),
+ create_effect('track', FOREIGN_AID, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: "STALIN GETS THE REPUBLIC'S GOLD",
type: 'pc',
},
{
id: 28,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', SOUTHERN, 3),
+ create_effect('track', FOREIGN_AID, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'INTERNATIONAL BRIGADES',
type: 'pc',
},
{
id: 29,
+ effects: [
+ create_effect('track', GOVERNMENT, -2),
+ // create_effect('track', LIBERTY, -3) // conditional
+ ],
strength: 1,
title: 'BAN WOMEN FROM THE FRONT',
type: 'pc',
},
{
id: 30,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', FOREIGN_AID, -3),
+ ],
strength: 1,
title: 'ABRAHAM LINCOLN BRIGADE',
type: 'pc',
},
{
id: 31,
+ effects: [
+ create_effect('attack', ANY, -2),
+ create_effect('track', SOVIET_SUPPORT, 3),
+ create_effect('track', GOVERNMENT, -2),
+ ],
strength: 2,
title: 'OUTLAW THE POUM',
type: 'pc',
},
{
id: 32,
+ effects: [
+ // create_effect('hero_points', OTHER_PLAYERS, 1) // take 1 from others
+ create_effect('front', ANY, 1),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ // create_effect('track') // choose one of two tracks
+ ],
strength: 1,
title: 'DISBAND THE CONTROL PATROLS',
type: 'pc',
},
{
id: 33,
+ effects: [
+ create_effect('attack', ARAGON, -1),
+ create_effect('track', SOVIET_SUPPORT, 1),
+ create_effect('track', LIBERTY, -4),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 1,
title: 'MAY DAYS',
type: 'pc',
},
{
id: 34,
+ effects: [
+ create_effect('front', SOUTHERN, 2),
+ create_effect('track', GOVERNMENT, -1),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'FIFTH REGIMENT',
type: 'pc',
},
{
id: 35,
+ effects: [
+ create_effect('front', ANY, 2),
+ // create_effect('track', SOVIET_SUPPORT,2) // conditional
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'THÄLMANN BATTALION',
type: 'pc',
},
{
id: 36,
+ effects: [
+ create_effect('track', SOVIET_SUPPORT, 2),
+ create_effect('track', COLLECTIVIZATION, -3),
+ create_effect('draw_card', SELF, 2),
+ create_effect('add_to_tableau', ANY, 1),
+ ],
strength: 1,
title: 'DE-COLLECTIVIZE AGRICULTURE',
type: 'pc',
},
{
id: 37,
+ effects: [
+ create_effect('front', ANY, 1),
+ create_effect('track', COLLECTIVIZATION,2),
+ create_effect('track', LIBERTY, 2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 3,
title: 'BUENAVENTURA DURRUTI',
type: 'pc',
},
{
id: 38,
+ effects: [
+ create_effect('track', LIBERTY, 3),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 1),
+ create_effect('swap_card_tableau_hand', ANY, 1),
+ ],
strength: 2,
title: 'MUJERES LIBRES',
type: 'pc',
},
{
id: 39,
+ effects: [
+ // create_effect('move attacks ')
+ create_effect('track', COLLECTIVIZATION, 1),
+ create_effect('draw_card', SELF, 2),
+ ],
strength: 1,
title: 'IRON COLUMN',
type: 'pc',
},
{
id: 40,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 3),
+ create_effect('track', COLLECTIVIZATION, 2),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 2,
title: 'ASTURIAN MINERS',
type: 'pc',
},
{
id: 41,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', COLLECTIVIZATION, 3),
+ create_effect('return_card', TRASH, 1),
+ ],
strength: 2,
title: 'CNT-FAI',
type: 'pc',
},
{
id: 42,
+ effects: [
+ create_effect('track', LIBERTY, 2),
+ create_effect('front', ARAGON, 2),
+ create_effect('front', MADRID, 1),
+ // create_effect('ability') // activate ability
+ ],
strength: 2,
title: 'DURRUTI COLUMN',
type: 'pc',
},
{
id: 43,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('front', ARAGON, 1),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ create_effect('draw_card', OTHER_PLAYERS, 1),
+ ],
strength: 1,
title: 'GEORGE ORWELL',
type: 'pc',
},
{
id: 44,
+ effects: [
+ // create_effect('hero_points', OTHER_PLAYERS, -1) // take from other player
+ create_effect('front', MADRID, 2),
+ create_effect('track', LIBERTY, 1),
+ create_effect('track', FOREIGN_AID, -1),
+ ],
strength: 1,
title: 'F.I.J.L.',
type: 'pc',
},
{
id: 45,
+ effects: [
+ create_effect('front', ANY, 2),
+ // create_effect('track', COLLECTIVIZATION, 1) // conditional
+ create_effect('track', FOREIGN_AID, -3)
+ ],
strength: 1,
title: 'ARM THE UNIONS',
type: 'pc',
},
{
id: 46,
+ effects: [
+ create_effect('bonus', ANY, ON),
+ create_effect('track', LIBERTY, 2),
+ // create_effect('special') // Peek at Fascist cards
+ ],
strength: 1,
title: 'GUERRILLAS',
type: 'pc',
},
{
id: 47,
+ effects: [
+ create_effect('remove_blank_marker', ANY, 1),
+ create_effect('track', LIBERTY, 2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'RADICAL EDUCATION',
type: 'pc',
},
{
id: 48,
+ effects: [
+ create_effect('front', CLOSEST_TO_DEFEAT, 2),
+ create_effect('track', SOVIET_SUPPORT, -2),
+ create_effect('draw_card', SELF, 3),
+ ],
strength: 1,
title: 'MATTEOTTI BATTALION',
type: 'pc',
},
{
id: 49,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 4),
+ create_effect('draw_card', SELF, 1),
+ ],
strength: 1,
title: 'COLLECTIVIZE AGRICULTURE',
type: 'pc',
},
{
id: 50,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 1),
+ // create_effect('front', ARAGON, 3) // conditional
+ create_effect('track', FOREIGN_AID, -1),
+ ],
strength: 1,
title: 'ARMORED VEHICLES',
type: 'pc',
},
{
id: 51,
+ effects: [
+ create_effect('track', COLLECTIVIZATION, 3),
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ ],
strength: 1,
title: 'INDUSTRIAL DEMOCRACY',
type: 'pc',
},
{
id: 52,
+ effects: [
+ create_effect('front', ANY, 1),
+ // create_effect('track') // choose
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ ],
strength: 2,
title: 'AFFINITY GROUPS',
type: 'pc',
},
{
id: 53,
+ effects: [
+ create_effect('track', LIBERTY, 1),
+ // create_effect('front', ANY, 3) // conditional
+ create_effect('hero_points', SELF, 1),
+ ],
strength: 1,
title: 'GENDER-INCLUSIVE MILITIA',
type: 'pc',
},
{
id: 54,
+ effects: [
+ // create_effect('track', LIBERTY, 3) // conditional
+ create_effect('track', SOVIET_SUPPORT, -1),
+ create_effect('add_to_tableau', ANY, 1),
+ ],
strength: 1,
title: 'FEDERICA MONTSENY',
type: 'pc',
@@ -414,21 +673,9 @@ const data: StaticData = {
{
id: 55,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: 4,
- },
- {
- target: ARAGON,
- type: 'attack',
- value: 1,
- },
- {
- target: SOVIET_SUPPORT,
- type: 'track',
- value: -2,
- },
+ create_effect('attack', SOUTHERN, -4),
+ create_effect('attack', ARAGON, -1),
+ create_effect('track', SOVIET_SUPPORT, -2),
],
title: 'SPANISH LEGION',
type: 'ec',
@@ -437,21 +684,9 @@ const data: StaticData = {
{
id: 56,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: 3,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: 2,
- },
- {
- target: MORALE_BONUS,
- type: 'bonus',
- value: OFF,
- },
+ create_effect('attack', SOUTHERN, -3),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('bonus', MORALE_BONUS, OFF),
],
title: 'BRITISH TREACHERY AT GIBRALTAR',
type: 'ec',
@@ -460,21 +695,9 @@ const data: StaticData = {
{
id: 57,
effects: [
- {
- target: MADRID,
- type: 'attack',
- value: -5,
- },
- {
- target: FOREIGN_AID,
- type: 'track',
- value: -2,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', MADRID, -5),
+ create_effect('track', FOREIGN_AID, -2),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'PARACUELLOS MASSACRES',
type: 'ec',
@@ -483,21 +706,9 @@ const data: StaticData = {
{
id: 58,
effects: [
- {
- target: 'n',
- type: 'attack',
- value: -5,
- },
- {
- target: 'v',
- type: 'attack',
- value: -1,
- },
- {
- target: COLLECTIVIZATION,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', NORTHERN, -5),
+ create_effect('attack', CLOSEST_TO_VICTORY, -1),
+ create_effect('track', COLLECTIVIZATION, -1),
],
title: 'CARLISTS',
type: 'ec',
@@ -506,21 +717,9 @@ const data: StaticData = {
{
id: 59,
effects: [
- {
- target: MADRID,
- type: 'attack',
- value: -4,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -2,
- },
- {
- target: LIBERTY,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', MADRID, -4),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('track', LIBERTY, -1),
],
title: 'ASSASSINATION OF GARCIA LORCA',
type: 'ec',
@@ -529,21 +728,9 @@ const data: StaticData = {
{
id: 60,
effects: [
- {
- target: NORTHERN,
- type: 'attack',
- value: -3,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -3,
- },
- {
- target: LIBERTY,
- type: 'track',
- value: -1,
- },
+ create_effect('attack', NORTHERN, -3),
+ create_effect('attack', CLOSEST_TO_VICTORY, -3),
+ create_effect('track', LIBERTY, -1),
],
title: 'GENERAL SANJURIO',
type: 'ec',
@@ -552,21 +739,9 @@ const data: StaticData = {
{
id: 61,
effects: [
- {
- target: ARAGON,
- type: 'attack',
- value: -4,
- },
- {
- target: CLOSEST_TO_VICTORY,
- type: 'attack',
- value: -2,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', ARAGON, -2),
+ create_effect('attack', CLOSEST_TO_VICTORY, -2),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'FAILED INVASION OF MALLORCA',
type: 'ec',
@@ -575,21 +750,9 @@ const data: StaticData = {
{
id: 62,
effects: [
- {
- target: SOUTHERN,
- type: 'attack',
- value: -5,
- },
- {
- target: MORALE_BONUS,
- type: 'bonus',
- value: OFF,
- },
- {
- target: PLAYER_WITH_MOST_HERO_POINTS,
- type: 'hero_points',
- value: -1,
- },
+ create_effect('attack', SOUTHERN, -5),
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('hero_points', PLAYER_WITH_MOST_HERO_POINTS, -1),
],
title: 'AIRLIFT OF THE ARMY OF AFRICA',
type: 'ec',
@@ -665,82 +828,34 @@ const data: StaticData = {
id: LIBERTY,
name: 'Liberty',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: MADRID,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', MADRID, -1),
null,
- {
- type: 'track',
- target: SOVIET_SUPPORT,
- value: 1,
- },
+ create_effect('track', SOVIET_SUPPORT, 1),
null,
null,
null,
- {
- type: 'track',
- target: GOVERNMENT,
- value: TOWARDS_CENTER,
- },
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
+ create_effect('track', GOVERNMENT, TOWARDS_CENTER),
+ create_effect('front', ANY, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: COLLECTIVIZATION,
name: 'Collectivization',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: ARAGON,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', ARAGON, -1),
null,
- {
- type: 'track',
- target: FOREIGN_AID,
- value: 1,
- },
+ create_effect('track', FOREIGN_AID, 1),
null,
null,
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: LIBERTY,
- value: 1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', LIBERTY, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
@@ -748,114 +863,50 @@ const data: StaticData = {
name: 'Government',
triggers: [
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
null,
null,
- {
- type: 'track',
- target: SOVIET_SUPPORT,
- value: 1,
- },
+ create_effect('track', SOVIET_SUPPORT, 1),
null,
null,
- {
- type: 'track',
- target: FOREIGN_AID,
- value: 1,
- },
+ create_effect('track', FOREIGN_AID, 1),
null,
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: SOVIET_SUPPORT,
name: 'Soviet Support',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: SOUTHERN,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', SOUTHERN, -1),
null,
null,
null,
- {
- type: 'track',
- target: LIBERTY,
- value: -1,
- },
+ create_effect('track', LIBERTY, -1),
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: GOVERNMENT,
- value: -1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', GOVERNMENT, -1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
{
id: FOREIGN_AID,
name: 'Foreign Aid',
triggers: [
- {
- type: 'bonus',
- target: MORALE_BONUS,
- value: OFF,
- },
- {
- type: 'attack',
- target: NORTHERN,
- value: -1,
- },
+ create_effect('bonus', MORALE_BONUS, OFF),
+ create_effect('attack', NORTHERN, -1),
null,
null,
null,
- {
- type: 'track',
- target: LIBERTY,
- value: -1,
- },
+ create_effect('track', LIBERTY, -1),
null,
- {
- type: 'front',
- target: ANY,
- value: 1,
- },
- {
- type: 'track',
- target: GOVERNMENT,
- value: 1,
- },
+ create_effect('front', ANY, 1),
+ create_effect('track', GOVERNMENT, 1),
null,
- {
- type: 'medaillon',
- target: ANY,
- value: 1,
- },
+ create_effect('medaillon', ANY, 1),
],
},
],
diff --git a/play.ts b/play.ts
index c0b144f..644ec89 100644
--- a/play.ts
+++ b/play.ts
@@ -1,10 +1,6 @@
'use strict';
-import {
-
- StaticData,
-} from './data';
-import { View } from './types';
+import { StaticData, View } from './types';
declare function action_button(action: string, text: string): void;
// declare function register_action(element: HTMLElement, type: string, s: number): void;
diff --git a/rules.js b/rules.js
index 3fa056a..62592e5 100644
--- a/rules.js
+++ b/rules.js
@@ -33,21 +33,7 @@ const front_names = {
d: 'the Front closest to Defeat',
v: 'the Front closest to Victory',
};
-const tracks = [
- data_1.LIBERTY,
- data_1.COLLECTIVIZATION,
- data_1.GOVERNMENT,
- data_1.SOVIET_SUPPORT,
- data_1.FOREIGN_AID,
-];
-const track_names = {
- [data_1.LIBERTY]: 'Liberty',
- [data_1.COLLECTIVIZATION]: 'Collectivization',
- [data_1.GOVERNMENT]: 'Government',
- [data_1.SOVIET_SUPPORT]: 'Soviet Support',
- [data_1.FOREIGN_AID]: 'Foreign Aid',
-};
-const { cards, } = data_1.default;
+const { cards, tracks, } = data_1.default;
const faction_cards = {
[ANARCHISTS_ID]: make_list(37, 54),
[COMMUNISTS_ID]: make_list(19, 36),
@@ -311,6 +297,7 @@ function setup(seed, _scenario, _options) {
[MODERATES_ID]: [],
},
tracks: [5, 5, 6, 3, 3],
+ triggered_track_effects: [[], [], [], [], []],
log: [],
undo: [],
turn: 1,
@@ -341,12 +328,7 @@ function start_turn() {
game.current_events.push(cardId);
const card = cards[cardId];
log_h3('Fascist Event: ' + card.title);
- game.engine = card.effects.map((_effect, index) => ({
- t: leaf_node,
- s: 'resolve_event',
- p: game.initiative,
- a: index,
- }));
+ game.engine = card.effects.map((effect) => resolve_effect(effect, game.initiative));
game.engine.push({
t: 'f',
f: 'setup_choose_card',
@@ -375,12 +357,34 @@ states.add_glory = {
resolve_active_and_proceed();
},
};
+states.attack_front = {
+ inactive: 'attack a Front',
+ prompt() {
+ const node = get_active_node();
+ const front = node.a.f;
+ view.prompt = 'Attack ' + front_names[front];
+ if (front === 'd' || front === 'v') {
+ const fronts = get_fronts_closest_to(front);
+ fronts.forEach((id) => gen_action('front', id));
+ }
+ else {
+ gen_action('front', front);
+ }
+ },
+ front(f) {
+ const node = get_active_node();
+ const value = node.a.v;
+ game.fronts[f] += value;
+ log_h3(`${Math.abs(value)} attacks added to ${front_names[f]}`);
+ resolve_active_and_proceed();
+ },
+};
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
view.prompt = 'Choose area of the board to affect';
- for (let track_id of tracks) {
- gen_action_standee(track_id);
+ for (let track of tracks) {
+ gen_action_standee(track.id);
}
},
standee(track_id) {
@@ -414,7 +418,7 @@ states.move_track_up_or_down = {
const node = get_active_node();
move_track(node.a.track_id, node.a.strength);
resolve_active_and_proceed();
- }
+ },
};
states.choose_card = {
inactive: 'choose a card',
@@ -433,6 +437,28 @@ states.choose_card = {
resolve_active_and_proceed();
},
};
+states.lose_hero_points = {
+ inactive: 'choose a Player',
+ prompt() {
+ view.prompt = 'Lose Hero Points';
+ },
+};
+states.move_track = {
+ inactive: 'move a Track',
+ prompt() {
+ const node = get_active_node();
+ const track = node.a.t;
+ const value = node.a.v;
+ view.prompt = `Move ${tracks[track].name} ${value > 0 ? 'up' : 'down'}`;
+ gen_action_standee(track);
+ },
+ standee(s) {
+ const node = get_active_node();
+ const value = node.a.v;
+ move_track(s, value);
+ resolve_active_and_proceed();
+ },
+};
states.player_turn = {
inactive: 'play their turn',
prompt() {
@@ -519,15 +545,7 @@ states.resolve_event = {
const node = get_active_node(game.engine);
const effect = card.effects[node.a];
view.prompt = get_event_prompt(effect);
- if (effect.type === 'attack' &&
- (effect.target === 'd' || effect.target === 'v')) {
- const fronts = get_fronts_closest_to(effect.target);
- fronts.forEach((id) => gen_action('front', id));
- }
- else if (effect.type === 'attack') {
- gen_action('front', effect.target);
- }
- else if (effect.type === 'track') {
+ if (effect.type === 'track') {
gen_action('standee', effect.target);
}
else if (effect.type === 'hero_points' &&
@@ -538,20 +556,6 @@ states.resolve_event = {
}
}
},
- front(f) {
- const card = get_current_event();
- const value = card.effects[get_active_node_args()].value;
- game.fronts[f] -= value;
- log_h3(`${value} attacks added to ${front_names[f]}`);
- resolve_active_and_proceed();
- },
- standee(s) {
- const effect = get_current_event().effects[get_active_node_args()];
- const value = effect.value;
- game.tracks[s] += value;
- log_h3(`${track_names[effect.target]} decreased by ${Math.abs(value)}`);
- resolve_active_and_proceed();
- },
Anarchist() {
lose_hero_point(ANARCHISTS_ID, 1);
resolve_active_and_proceed();
@@ -607,7 +611,66 @@ function resolve_fascist_test() {
log_h2('Fascist test is resolved');
next();
}
-function move_track(track, change) {
+function move_track(track_id, change) {
+ const current_value = game.tracks[track_id];
+ let new_value = current_value + change;
+ new_value = Math.max(new_value, track_id === data_1.GOVERNMENT ? 1 : 0);
+ new_value = Math.min(new_value, 10);
+ game.tracks[track_id] = new_value;
+ log(`${game.active} moves ${get_track_name(track_id)} to ${new_value}`);
+ const triggered_spaces = change > 0
+ ? make_list(current_value + 1, new_value).reverse()
+ : make_list(new_value, current_value - 1);
+ triggered_spaces.forEach((space_id) => {
+ const trigger = tracks[track_id].triggers[space_id];
+ if (trigger !== null &&
+ !game.triggered_track_effects[track_id].includes(space_id)) {
+ if (space_id !== 0) {
+ game.triggered_track_effects[track_id].push(space_id);
+ }
+ const node = resolve_effect(trigger);
+ if (node !== null) {
+ insert_after_active_node(node);
+ }
+ }
+ });
+}
+function resolve_effect(effect, faction = get_active_faction()) {
+ if (effect.type === 'attack') {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'attack_front',
+ a: {
+ f: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ if (effect.type === 'track') {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'move_track',
+ a: {
+ t: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ if (effect.type === 'hero_points' &&
+ effect.target === data_1.PLAYER_WITH_MOST_HERO_POINTS) {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'lose_hero_points',
+ a: {
+ p: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ return null;
}
function draw_card(deck) {
clear_undo();
@@ -646,7 +709,7 @@ function get_event_prompt(effect) {
case 'hero_points':
return 'Select player with most Hero points';
case 'track':
- return 'Decrease ' + track_names[effect.target];
+ return 'Decrease ' + tracks[effect.target].name;
}
return prompt;
}
@@ -730,7 +793,7 @@ function get_factions_with_most_hero_poins() {
return faction_ids;
}
function get_track_name(track_id) {
- return track_names[track_id];
+ return tracks[track_id].name;
}
function make_list(first, last) {
let list = [];
diff --git a/rules.ts b/rules.ts
index cbe7f5a..0b4901f 100644
--- a/rules.ts
+++ b/rules.ts
@@ -2,12 +2,15 @@
import {
CardId,
+ Effect,
EngineNode,
+ EventCard,
FactionId,
FunctionNode,
Game,
LeafNode,
Player,
+ PlayerCard,
States,
View,
} from './types';
@@ -22,15 +25,12 @@ import data, {
// TEAMWORK_BONUS,
// OFF,
ON,
- EventCard,
- CardEffect,
LIBERTY,
COLLECTIVIZATION,
GOVERNMENT,
SOVIET_SUPPORT,
FOREIGN_AID,
PLAYER_WITH_MOST_HERO_POINTS,
- PlayerCard,
// StaticData,
// PLAYER_WITH_MOST_HERO_POINTS,
} from './data';
@@ -75,25 +75,10 @@ const front_names: Record<string, string> = {
v: 'the Front closest to Victory',
};
-const tracks = [
- LIBERTY,
- COLLECTIVIZATION,
- GOVERNMENT,
- SOVIET_SUPPORT,
- FOREIGN_AID,
-];
-
-const track_names: Record<number, string> = {
- [LIBERTY]: 'Liberty',
- [COLLECTIVIZATION]: 'Collectivization',
- [GOVERNMENT]: 'Government',
- [SOVIET_SUPPORT]: 'Soviet Support',
- [FOREIGN_AID]: 'Foreign Aid',
-};
-
const {
cards,
- // fronts
+ // fronts,
+ tracks,
} = data;
const faction_cards = {
@@ -453,6 +438,7 @@ export function setup(seed: number, _scenario: string, _options: unknown) {
[MODERATES_ID]: [],
},
tracks: [5, 5, 6, 3, 3],
+ triggered_track_effects: [[], [], [], [], []],
log: [],
undo: [],
turn: 1,
@@ -492,12 +478,9 @@ function start_turn() {
const card = cards[cardId] as EventCard;
log_h3('Fascist Event: ' + card.title);
- game.engine = card.effects.map((_effect, index) => ({
- t: leaf_node,
- s: 'resolve_event',
- p: game.initiative,
- a: index,
- }));
+ game.engine = card.effects.map((effect) =>
+ resolve_effect(effect, game.initiative)
+ );
game.engine.push({
t: 'f',
f: 'setup_choose_card',
@@ -534,12 +517,34 @@ states.add_glory = {
},
};
+states.attack_front = {
+ inactive: 'attack a Front',
+ prompt() {
+ const node = get_active_node();
+ const front = node.a.f;
+ view.prompt = 'Attack ' + front_names[front];
+ if (front === 'd' || front === 'v') {
+ const fronts = get_fronts_closest_to(front);
+ fronts.forEach((id) => gen_action('front', id));
+ } else {
+ gen_action('front', front);
+ }
+ },
+ front(f: string) {
+ const node = get_active_node();
+ const value = node.a.v;
+ game.fronts[f] += value;
+ log_h3(`${Math.abs(value)} attacks added to ${front_names[f]}`);
+ resolve_active_and_proceed();
+ },
+};
+
states.choose_area_ap = {
inactive: 'choose area to use Action Points',
prompt() {
view.prompt = 'Choose area of the board to affect';
- for (let track_id of tracks) {
- gen_action_standee(track_id);
+ for (let track of tracks) {
+ gen_action_standee(track.id);
}
},
standee(track_id: number) {
@@ -561,7 +566,7 @@ states.move_track_up_or_down = {
inactive: 'move a track',
prompt() {
const node = get_active_node();
- view.prompt = `Move ${get_track_name(node.a.track_id)} up or down`
+ view.prompt = `Move ${get_track_name(node.a.track_id)} up or down`;
gen_action('up');
gen_action('down');
},
@@ -574,8 +579,8 @@ states.move_track_up_or_down = {
const node = get_active_node();
move_track(node.a.track_id, node.a.strength);
resolve_active_and_proceed();
- }
-}
+ },
+};
states.choose_card = {
inactive: 'choose a card',
@@ -594,6 +599,31 @@ states.choose_card = {
},
};
+states.lose_hero_points = {
+ inactive: 'choose a Player',
+ prompt() {
+ view.prompt = 'Lose Hero Points';
+ },
+};
+
+states.move_track = {
+ inactive: 'move a Track',
+ prompt() {
+ const node = get_active_node();
+ const track = node.a.t;
+ const value = node.a.v;
+ view.prompt = `Move ${tracks[track].name} ${value > 0 ? 'up' : 'down'}`;
+ // return 'Decrease ' + tracks[effect.target].name;
+ gen_action_standee(track)
+ },
+ standee(s: number) {
+ const node = get_active_node();
+ const value = node.a.v;
+ move_track(s, value)
+ resolve_active_and_proceed();
+ },
+};
+
states.player_turn = {
inactive: 'play their turn',
prompt() {
@@ -682,18 +712,10 @@ states.resolve_event = {
prompt() {
const card = get_current_event();
const node = get_active_node(game.engine) as LeafNode;
- const effect: CardEffect = card.effects[node.a];
+ const effect: Effect = card.effects[node.a];
view.prompt = get_event_prompt(effect);
- if (
- effect.type === 'attack' &&
- (effect.target === 'd' || effect.target === 'v')
- ) {
- const fronts = get_fronts_closest_to(effect.target);
- fronts.forEach((id) => gen_action('front', id));
- } else if (effect.type === 'attack') {
- gen_action('front', effect.target);
- } else if (effect.type === 'track') {
+if (effect.type === 'track') {
gen_action('standee', effect.target);
} else if (
effect.type === 'hero_points' &&
@@ -706,20 +728,7 @@ states.resolve_event = {
}
// for (let p = 0; p < 5; ++p) gen_action('standee', p);
},
- front(f: string) {
- const card = get_current_event();
- const value = card.effects[get_active_node_args()].value;
- game.fronts[f] -= value;
- log_h3(`${value} attacks added to ${front_names[f]}`);
- resolve_active_and_proceed();
- },
- standee(s: string) {
- const effect = get_current_event().effects[get_active_node_args()];
- const value = effect.value;
- game.tracks[s] += value;
- log_h3(`${track_names[effect.target]} decreased by ${Math.abs(value)}`);
- resolve_active_and_proceed();
- },
+
Anarchist() {
lose_hero_point(ANARCHISTS_ID, 1);
resolve_active_and_proceed();
@@ -803,8 +812,100 @@ function resolve_fascist_test() {
next();
}
-function move_track(track: number, change: number) {
-
+function move_track(track_id: number, change: number) {
+ // Check if track can be moved
+ // Updata values
+ // Check all triggers
+ // Add states for triggers
+ const current_value = game.tracks[track_id];
+ let new_value = current_value + change;
+ new_value = Math.max(new_value, track_id === GOVERNMENT ? 1 : 0);
+ new_value = Math.min(new_value, 10);
+ game.tracks[track_id] = new_value;
+ log(`${game.active} moves ${get_track_name(track_id)} to ${new_value}`);
+ const triggered_spaces =
+ change > 0
+ ? make_list(current_value + 1, new_value).reverse()
+ : make_list(new_value, current_value - 1);
+
+ triggered_spaces.forEach((space_id) => {
+ const trigger = tracks[track_id].triggers[space_id];
+ if (
+ trigger !== null &&
+ !game.triggered_track_effects[track_id].includes(space_id)
+ ) {
+ if (space_id !== 0) {
+ game.triggered_track_effects[track_id].push(space_id);
+ }
+ const node = resolve_effect(trigger);
+ if (node !== null) {
+ insert_after_active_node(node);
+ }
+ }
+ });
+}
+
+function resolve_effect(
+ effect: Effect,
+ faction: FactionId = get_active_faction()
+): EngineNode | null {
+ if (effect.type === 'attack') {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'attack_front',
+ a: {
+ f: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ if (effect.type === 'track') {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'move_track',
+ a: {
+ t: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ if (
+ effect.type === 'hero_points' &&
+ effect.target === PLAYER_WITH_MOST_HERO_POINTS
+ ) {
+ return {
+ t: leaf_node,
+ p: faction,
+ s: 'lose_hero_points',
+ a: {
+ p: effect.target,
+ v: effect.value,
+ },
+ };
+ }
+ return null;
+
+ // if (
+ // effect.type === 'attack' &&
+ // (effect.target === 'd' || effect.target === 'v')
+ // ) {
+ // const fronts = get_fronts_closest_to(effect.target);
+ // fronts.forEach((id) => gen_action('front', id));
+ // } else if (effect.type === 'attack') {
+ // gen_action('front', effect.target);
+ // } else if (effect.type === 'track') {
+ // gen_action('standee', effect.target);
+ // } else if (
+ // effect.type === 'hero_points' &&
+ // effect.target === PLAYER_WITH_MOST_HERO_POINTS
+ // ) {
+ // const factions = get_factions_with_most_hero_poins();
+ // for (let faction_id of factions) {
+ // gen_action(get_player(faction_id));
+ // }
+ // }
}
// #endregion
@@ -874,7 +975,7 @@ function get_current_event(): EventCard {
// }
// }
-function get_event_prompt(effect: CardEffect) {
+function get_event_prompt(effect: Effect) {
let prompt = '';
switch (effect.type) {
case 'attack':
@@ -884,7 +985,7 @@ function get_event_prompt(effect: CardEffect) {
case 'hero_points':
return 'Select player with most Hero points';
case 'track':
- return 'Decrease ' + track_names[effect.target];
+ return 'Decrease ' + tracks[effect.target].name;
}
return prompt;
}
@@ -1067,10 +1168,10 @@ function get_factions_with_most_hero_poins() {
}
function get_track_name(track_id: number): string {
- return track_names[track_id];
+ return tracks[track_id].name;
}
-function make_list(first: number, last: number) {
+function make_list(first: number, last: number): number[] {
let list = [];
for (let i = first; i <= last; i++) list.push(i);
return list;
diff --git a/types.d.ts b/types.d.ts
index a79c951..b470acc 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -37,7 +37,7 @@ export interface Game {
medaillons: Array<number | null>;
tableaus: Record<FactionId, CardId[]>;
tracks: number[];
-
+ triggered_track_effects: number[][];
result?: string;
victory?: string;
@@ -93,4 +93,48 @@ export interface LeafNode {
p: FactionId; // Player
a?: any; // args
r?: 0 | 1; // 1 if resolved
+}
+
+export interface CardBase {
+ id: number;
+ title: string;
+}
+
+export type Card = EventCard | PlayerCard;
+
+export interface EventCard extends CardBase {
+ type: 'ec';
+ year: number;
+ effects: Effect[];
+}
+
+export interface PlayerCard extends CardBase {
+ type: 'pc';
+ strength: number;
+ effects: Effect[];
+}
+
+export interface Effect {
+ type: 'attack' | 'track' | 'bonus' | 'hero_points' | 'front' | 'medaillon' | 'draw_card' | 'swap_card_tableau_hand' | 'add_to_tableau' | 'remove_blank_marker' | 'return_card';
+ target: string | number;
+ value: number;
+}
+
+export interface StaticData {
+ cards: Card[];
+ fronts: Array<{
+ id: string;
+ name: string;
+ left: number;
+ top: number;
+ }>;
+ medaillons: Array<{
+ id: number;
+ name: string;
+ }>;
+ tracks: Array<{
+ id: number;
+ name: string;
+ triggers: Array<null | Effect>;
+ }>;
} \ No newline at end of file