summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.html1
-rw-r--r--rules.js289
-rw-r--r--ui.js11
3 files changed, 215 insertions, 86 deletions
diff --git a/play.html b/play.html
index 7d357cd..a0b49a8 100644
--- a/play.html
+++ b/play.html
@@ -26,6 +26,7 @@
.role_info {
padding: 10px 20px;
background-color: gainsboro;
+ white-space: pre-wrap;
}
.last_played {
background-color: silver;
diff --git a/rules.js b/rules.js
index caab28e..a444a5c 100644
--- a/rules.js
+++ b/rules.js
@@ -4,11 +4,8 @@
// Diary: 2021-04-24 - Saturday - Art, UI, preparation phase.
// Diary: 2021-04-25 - Sunday - Supply, movement and battle.
// Diary: 2021-04-26 - Monday Evening - Redid piece layout. Transport armies on fleets.
-// Diary: 2021-05-01 - Saturday Evening - Added undo. Started simple events.
-
-// TODO: rewrite battle and movement states to common with is_friendly/etc
-// TODO: undo in preparation phase
-// TODO: separate land/port moves?
+// Diary: 2021-05-01 - Saturday Evening - Added undo. Tribute, Ostracism, and simple greek events.
+// Diary: 2021-05-02 - Sunday - Movement events.
exports.scenarios = [
"Default"
@@ -25,7 +22,14 @@ const ATHENAI = "Athenai";
const SPARTA = "Sparta";
const PELLA = "Pella";
+// Greek event numbers
+const LEONIDAS = 8;
+const EVANGELION = 10;
const MOLON_LABE = 12;
+
+// Persian event numbers
+const CAVALRY_OF_MARDONIUS = 1;
+const THE_GREAT_KING = 7;
const SUDDEN_DEATH_OF_THE_GREAT_KING = 11;
const PERSIAN_EVENT_NAMES = {
@@ -91,6 +95,8 @@ const PORTS = [
"Thebai",
];
+const SPARTA_CARDS = [ 6, 8, 11, 15 ];
+
const CITIES_WITH_ROADS = [
"Abydos",
"Athenai",
@@ -349,6 +355,45 @@ function is_greek_control(where) {
return count_greek_armies(where) > 0;
}
+function persian_can_land_move() {
+ for (let city of CITIES_WITH_ROADS)
+ if (count_persian_armies(city) > 0)
+ return true;
+ return false;
+}
+
+function greek_can_land_move() {
+ for (let city of CITIES_WITH_ROADS) {
+ if (city == SPARTA && game.trigger.festival)
+ continue;
+ if (count_greek_armies(city) > 0)
+ return true;
+ }
+ return false;
+}
+
+function persian_can_naval_move() {
+ for (let port of PORTS)
+ if (count_persian_fleets(port) > 0)
+ return true;
+ return false;
+}
+
+function greek_can_naval_move() {
+ for (let port of PORTS)
+ if (count_greek_fleets(port) > 0)
+ return true;
+ return false;
+}
+
+function persian_can_move() {
+ return persian_can_land_move() || have_persian_naval_move();
+}
+
+function greek_can_move() {
+ return greek_can_land_move() || have_greek_naval_move();
+}
+
function gen_greek_cities(view) {
for (let city of CITIES)
if (is_greek_control(city))
@@ -373,6 +418,21 @@ function gen_persian_armies(view) {
gen_action(view, 'city', city);
}
+function gen_greek_land_movement(view) {
+ for (let city of CITIES_WITH_ROADS) {
+ if (city == SPARTA && game.trigger.festival)
+ continue;
+ if (count_greek_armies(city) > 0)
+ gen_action(view, 'city', city);
+ }
+}
+
+function gen_persian_land_movement(view) {
+ for (let city of CITIES_WITH_ROADS)
+ if (count_persian_armies(city) > 0)
+ gen_action(view, 'city', city);
+}
+
function gen_greek_fleets(view) {
for (let port of PORTS)
if (count_greek_fleets(port) > 0)
@@ -687,15 +747,17 @@ states.persian_operation = {
return view.prompt = "Persian Operation Phase.";
view.prompt = "Persian Operation Phase: Play a card or pass.";
for (let card of game.persian.hand) {
- gen_action(view, 'card_move', card);
+ if (persian_can_move())
+ gen_action(view, 'card_move', card);
if (can_play_persian_event(card))
gen_action(view, 'card_event', card);
}
gen_action(view, 'pass');
},
card_move: function (card) {
+ push_undo();
play_card("Persia", game.persian.hand, card, " for movement.");
- game.state = 'persian_movement';
+ goto_persian_movement(true, true, 0);
},
card_event: function (card) {
play_persian_event(card);
@@ -713,15 +775,17 @@ states.greek_operation = {
return view.prompt = "Greek Operation Phase.";
view.prompt = "Greek Operation Phase: Play a card or pass.";
for (let card of game.greek.hand) {
- gen_action(view, 'card_move', card);
+ if (greek_can_move())
+ gen_action(view, 'card_move', card);
if (can_play_greek_event(card))
gen_action(view, 'card_event', card);
}
gen_action(view, 'pass');
},
card_move: function (card) {
+ push_undo();
play_card("Greece", game.greek.hand, card, " for movement.");
- game.state = 'greek_movement';
+ goto_greek_movement(true, true, 0);
},
card_event: function (card) {
play_greek_event(card);
@@ -734,9 +798,7 @@ states.greek_operation = {
}
function end_persian_movement() {
- let event = game.event;
- game.event = null;
- switch (event) {
+ switch (game.event) {
default:
end_persian_operation();
break;
@@ -744,10 +806,7 @@ function end_persian_movement() {
}
function end_greek_movement() {
- let event = game.event;
- console.log("end_greek_movement event=", game.event);
- game.event = null;
- switch (event) {
+ switch (game.event) {
case MOLON_LABE:
end_persian_operation();
break;
@@ -758,6 +817,10 @@ function end_greek_movement() {
}
function end_persian_operation() {
+ clear_undo();
+ game.event = 0;
+ game.land_movement = 0;
+ game.naval_movement = 0;
game.move_list = null;
game.transport = 0;
game.attacker = 0;
@@ -767,6 +830,10 @@ function end_persian_operation() {
}
function end_greek_operation() {
+ clear_undo();
+ game.event = 0;
+ game.land_movement = 0;
+ game.naval_movement = 0;
game.move_list = null;
game.transport = 0;
game.attacker = 0;
@@ -807,60 +874,33 @@ function list_greek_land_moves(seen, from) {
list_greek_land_moves(seen, to);
}
-function goto_persian_land_movement_event(event) {
- game.event = event;
+function goto_persian_movement(land, naval, event) {
game.active = PERSIA;
- game.state = 'persian_land_movement_event';
-}
-
-function goto_greek_land_movement_event(event) {
+ game.state = 'persian_movement';
+ game.land_movement = land;
+ game.naval_movement = naval;
game.event = event;
- game.active = GREECE;
- game.state = 'greek_land_movement_event';
}
-states.persian_land_movement_event = {
- prompt: function (view, current) {
- if (is_inactive_player(current))
- return view.prompt = PERSIAN_EVENT_NAMES[game.event] + ".";
- view.prompt = PERSIAN_EVENT_NAMES[game.event] + ": Choose an origin.";
- gen_persian_armies(view);
- gen_action(view, 'pass');
- },
- city: function (space) {
- push_undo();
- goto_persian_land_movement(space);
- },
- pass: function () {
- end_land_movement_event();
- },
-}
-
-states.greek_land_movement_event = {
- prompt: function (view, current) {
- if (is_inactive_player(current))
- return view.prompt = GREEK_EVENT_NAMES[game.event] + ".";
- view.prompt = GREEK_EVENT_NAMES[game.event] + ": Choose an origin.";
- gen_greek_armies(view);
- gen_action(view, 'pass');
- },
- city: function (space) {
- push_undo();
- goto_greek_land_movement(space);
- },
- pass: function () {
- end_land_movement_event();
- },
+function goto_greek_movement(land, naval, event) {
+ game.active = GREECE;
+ game.state = 'greek_movement';
+ game.land_movement = land;
+ game.naval_movement = naval;
+ game.event = event;
}
states.persian_movement = {
prompt: function (view, current) {
+ let name = game.event ? PERSIAN_EVENT_NAMES[game.event] : "Persian Movement";
if (is_inactive_player(current))
- return view.prompt = "Persian Movement.";
- view.prompt = "Persian Movement: Choose an origin.";
- gen_persian_armies(view);
- gen_persian_fleets(view);
- gen_action(view, 'pass');
+ return view.prompt = name + "."
+ view.prompt = name + ": Choose an origin.";
+ if (game.land_movement)
+ gen_persian_land_movement(view);
+ if (game.naval_movement)
+ gen_persian_fleets(view);
+ gen_action_undo(view);
},
city: function (space) {
goto_persian_land_movement(space);
@@ -869,19 +909,20 @@ states.persian_movement = {
game.from = space;
game.state = 'persian_naval_movement';
},
- pass: function () {
- end_persian_movement();
- },
+ undo: pop_undo,
}
states.greek_movement = {
prompt: function (view, current) {
+ let name = game.event ? GREEK_EVENT_NAMES[game.event] : "Greek Movement";
if (is_inactive_player(current))
- return view.prompt = "Greek Movement.";
- view.prompt = "Greek Movement: Choose an origin.";
- gen_greek_armies(view);
- gen_greek_fleets(view);
- gen_action(view, 'pass');
+ return view.prompt = name + "."
+ view.prompt = name + ": Choose an origin.";
+ if (game.land_movement)
+ gen_greek_land_movement(view);
+ if (game.naval_movement)
+ gen_greek_fleets(view);
+ gen_action_undo(view);
},
city: function (space) {
goto_greek_land_movement(space);
@@ -890,23 +931,30 @@ states.greek_movement = {
game.from = space;
game.state = 'greek_naval_movement';
},
- pass: function () {
- end_greek_movement();
- },
+ undo: pop_undo,
}
function goto_persian_land_movement(space) {
+ push_undo();
game.from = space;
game.state = 'persian_land_movement';
list_persian_land_moves(game.move_list = {}, game.from);
}
function goto_greek_land_movement(space) {
+ push_undo();
game.from = space;
game.state = 'greek_land_movement';
list_greek_land_moves(game.move_list = {}, game.from);
}
+function goto_greek_land_movement_leonidas(space) {
+ push_undo();
+ game.from = space;
+ game.state = 'greek_land_movement_leonidas';
+ list_greek_land_moves(game.move_list = {}, game.from);
+}
+
states.persian_land_movement = {
prompt: function (view, current) {
if (is_inactive_player(current))
@@ -925,9 +973,6 @@ states.persian_land_movement = {
game.where = to;
game.state = 'persian_land_movement_confirm';
},
- pass: function () {
- end_persian_movement();
- },
undo: pop_undo,
}
@@ -949,8 +994,25 @@ states.greek_land_movement = {
game.where = to;
game.state = 'greek_land_movement_confirm';
},
- pass: function () {
- end_greek_movement();
+ undo: pop_undo,
+}
+
+states.greek_land_movement_leonidas = {
+ prompt: function (view, current) {
+ if (is_inactive_player(current))
+ return view.prompt = "Greek Land Movement.";
+ view.prompt = "Greek Land Movement: Select a destination.";
+ for (let to in game.move_list)
+ if (to != game.from)
+ gen_action(view, 'city', to);
+ gen_action(view, 'undo');
+ },
+ city: function (to) {
+ push_undo();
+ log("Greece moves " + 1 + " armies from " + game.from + " to " + to + ".");
+ move_greek_army(game.from, to, 1);
+ game.where = to;
+ game.state = 'greek_land_movement_confirm';
},
undo: pop_undo,
}
@@ -1016,9 +1078,6 @@ states.persian_naval_movement = {
game.where = to;
game.state = 'persian_naval_movement_confirm';
},
- pass: function () {
- end_persian_movement();
- },
undo: function () {
game.from = 0;
game.state = 'persian_movement';
@@ -1031,6 +1090,10 @@ states.greek_naval_movement = {
return view.prompt = "Greek Naval Movement.";
view.prompt = "Greek Naval Movement: Select fleets to move, armies to transport, and then a destination.";
view.naval_movement = game.from;
+ if (game.trigger.festival && game.from == SPARTA)
+ view.naval_transport = 0;
+ else
+ view.naval_transport = 1;
for (let port of PORTS)
if (port != game.from)
gen_action(view, 'port', port);
@@ -1046,9 +1109,6 @@ states.greek_naval_movement = {
game.where = to;
game.state = 'greek_naval_movement_confirm';
},
- pass: function () {
- end_greek_movement();
- },
undo: function () {
game.from = 0;
game.state = 'greek_movement';
@@ -1627,9 +1687,12 @@ function play_persian_event_card(card) {
function can_play_persian_event(card) {
switch (card) {
+ case 1: return can_play_cavalry_of_mardonius();
case 2: return can_play_tribute_of_earth_and_water();
case 3: return can_play_tribute_of_earth_and_water();
+ case 4: return true; // carneia_festival
case 6: return can_play_ostracism();
+ case 7: return can_play_the_great_king();
case 13: return can_play_tribute_of_earth_and_water();
}
return false;
@@ -1638,14 +1701,38 @@ function can_play_persian_event(card) {
function play_persian_event(card) {
play_persian_event_card(card);
switch (card) {
+ case 1: return play_cavalry_of_mardonius();
case 2: return play_tribute_of_earth_and_water();
case 3: return play_tribute_of_earth_and_water();
+ case 4: return play_carneia_festival();
case 6: return play_ostracism();
+ case 7: return play_the_great_king();
case 13: return play_tribute_of_earth_and_water();
}
end_persian_operation();
}
+function play_carneia_festival() {
+ game.trigger.festival = 1;
+ end_persian_operation();
+}
+
+function can_play_cavalry_of_mardonius() {
+ return persian_can_land_move();
+}
+
+function play_cavalry_of_mardonius() {
+ goto_persian_movement(true, false, CAVALRY_OF_MARDONIUS);
+}
+
+function can_play_the_great_king() {
+ return persian_can_land_move();
+}
+
+function play_the_great_king() {
+ goto_persian_movement(true, false, THE_GREAT_KING);
+}
+
function can_play_tribute_of_earth_and_water() {
if (count_persian_armies(RESERVE) > 0)
for (let city of CITIES)
@@ -1693,10 +1780,14 @@ function play_greek_event_card(card) {
}
function can_play_greek_event(card) {
+ if (game.trigger.festival && SPARTA_CARDS.includes(card))
+ return false;
switch (card) {
case 2: return can_play_ionian_revolt();
case 3: return can_play_wrath_of_poseidon();
case 7: return can_play_oracle_of_delphi();
+ case 8: return can_play_leonidas();
+ case 10: return can_play_evangelion();
case 11: return can_play_melas_zomos();
case 12: return can_play_molon_labe();
case 14: return can_play_support_from_syracuse();
@@ -1711,6 +1802,8 @@ function play_greek_event(card) {
case 2: return play_ionian_revolt();
case 3: return play_wrath_of_poseidon();
case 7: return play_oracle_of_delphi();
+ case 8: return play_leonidas();
+ case 10: return play_evangelion();
case 11: return play_melas_zomos();
case 12: return play_molon_labe();
case 14: return play_support_from_syracuse();
@@ -1777,6 +1870,34 @@ states.support_from_syracuse = {
undo: pop_undo,
}
+function can_play_leonidas() {
+ return greek_can_land_move();
+}
+
+function play_leonidas() {
+ game.state = 'leonidas';
+}
+
+states.leonidas = {
+ prompt: function (view, current) {
+ if (is_inactive_player(current))
+ return view.prompt = "Leonidas.";
+ view.prompt = "Leonidas: Select one Greek army and move it.";
+ gen_greek_land_movement(view);
+ },
+ city: function (space) {
+ goto_greek_land_movement_leonidas(space);
+ },
+}
+
+function can_play_evangelion() {
+ return greek_can_land_move();
+}
+
+function play_evangelion() {
+ goto_greek_movement(true, false, EVANGELION);
+}
+
function can_play_melas_zomos() {
return count_greek_armies(RESERVE) > 0 && is_greek_control(SPARTA);
}
@@ -2111,6 +2232,7 @@ function goto_scoring_phase() {
}
function end_campaign() {
+ game.trigger.festival = 0;
if (game.campaign == 5) {
if (game.vp < 0) {
game.victory = $("Greece wins with " + (-game.vp) + " points.");
@@ -2183,6 +2305,7 @@ exports.setup = function (scenario, players) {
themistocles: 0,
leonidas: 0,
hellespont: 0,
+ festival: 0,
},
// transient action state
diff --git a/ui.js b/ui.js
index 67357c7..1d1fc47 100644
--- a/ui.js
+++ b/ui.js
@@ -88,7 +88,7 @@ function on_click_army(evt) {
}
update_ui();
}
- if (game.naval_movement && ui.player) {
+ if (game.naval_transport && ui.player) {
let here = (ui.player == PERSIA ? ui.persian_army : ui.greek_army)[game.naval_movement];
if (here.includes(evt.target)) {
if (ui.selected_armies && ui.selected_armies.includes(evt.target)) {
@@ -226,9 +226,14 @@ function build_ui() {
}
function greek_info() {
+ let text = "";
if (game.g_cards == 1)
- return "1 card in hand";
- return game.g_cards + " cards in hand";
+ text += "1 card in hand";
+ else
+ text += game.g_cards + " cards in hand";
+ if (game.trigger.festival)
+ text += "\nCarneia Festival!";
+ return text;
}
function persian_info() {