summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data.js2
-rw-r--r--play.html4
-rw-r--r--rules.js115
-rw-r--r--ui.js66
4 files changed, 98 insertions, 89 deletions
diff --git a/data.js b/data.js
index dd77abc..2d138af 100644
--- a/data.js
+++ b/data.js
@@ -127,7 +127,7 @@ const PORTS = [];
if (order == 'Nomads')
id += " " + nomads[name]++;
if (name == 'Reynald' || name == 'Raymond')
- id += " " + home;
+ id += " (" + home + ")";
if (id in BLOCKS)
throw Error("Name clash: " + id + " order:"+order + " " + JSON.stringify(nomads));
BLOCKS[id] = {
diff --git a/play.html b/play.html
index a2d760f..febd727 100644
--- a/play.html
+++ b/play.html
@@ -112,12 +112,12 @@
opacity: 0.8;
z-index: 9;
}
-.town.where {
+.town.muster {
opacity: 0.8;
border-color: blue;
z-index: 9;
}
-.town.where.highlight {
+.town.muster.highlight {
box-shadow: 0 0 2px 4px white;
}
diff --git a/rules.js b/rules.js
index b8e85fd..d47d359 100644
--- a/rules.js
+++ b/rules.js
@@ -22,8 +22,9 @@ const DEAD = "Dead";
const F_POOL = "F. Pool";
const S_POOL = "S. Pool";
-const HIT_TEXT = "\u2605";
-const MISS_TEXT = "\u2606";
+// serif cirled numbers
+const DIE_HIT = [ 0, '\u2776', '\u2777', '\u2778', '\u2779', '\u277A', '\u277B' ];
+const DIE_MISS = [ 0, '\u2460', '\u2461', '\u2462', '\u2463', '\u2464', '\u2465' ];
const ATTACK_MARK = "*";
const RESERVE_MARK_1 = "\u2020";
@@ -180,7 +181,7 @@ function deal_cards(deck, n) {
}
function block_name(who) {
- return BLOCKS[who].name;
+ return who; // BLOCKS[who].name;
}
function block_type(who) {
@@ -414,6 +415,16 @@ function can_block_retreat_to(who, to) {
return false;
}
+function can_block_retreat(who) {
+ if (block_owner(who) == game.active) {
+ let from = game.location[who];
+ for (let to of TOWNS[from].exits)
+ if (can_block_retreat_to(who, to))
+ return true;
+ }
+ return false;
+}
+
function can_block_regroup_to(who, to) {
if (is_friendly_town(to) || is_vacant_town(to)) {
let from = game.location[who];
@@ -535,20 +546,6 @@ function reduce_block(who) {
}
}
-function filter_battle_blocks(ci, is_candidate) {
- let output = null;
- for (let b in BLOCKS) {
- if (is_candidate(b) && !game.moved[b]) {
- if (block_initiative(b) == ci) {
- if (!output)
- output = [];
- output.push(b);
- }
- }
- }
- return output;
-}
-
function count_attackers() {
let count = 0;
for (let b in BLOCKS)
@@ -987,6 +984,7 @@ states.muster_who = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move.";
view.prompt = "Muster: Move blocks to " + game.where + ".";
+ view.muster = game.where;
gen_action_undo(view);
gen_action(view, 'end_muster');
for (let b in BLOCKS)
@@ -1010,6 +1008,7 @@ states.muster_move_1 = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move.";
view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + ".";
+ view.muster = game.where;
gen_action_undo(view);
gen_action(view, 'block', game.who);
let from = game.location[game.who];
@@ -1050,6 +1049,7 @@ states.muster_move_2 = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move.";
view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + ".";
+ view.muster = game.where;
gen_action_undo(view);
let from = game.location[game.who];
let muster = game.where;
@@ -1087,6 +1087,7 @@ states.muster_move_3 = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move.";
view.prompt = "Muster: Move " + block_name(game.who) + " to " + game.where + ".";
+ view.muster = game.where;
gen_action_undo(view);
let from = game.location[game.who];
let muster = game.where;
@@ -1156,9 +1157,9 @@ function start_battle(where) {
}
function resume_battle() {
+ game.who = null;
if (game.victory)
return goto_game_over();
- game.who = null;
game.state = 'battle_round';
pump_battle_round();
}
@@ -1220,20 +1221,34 @@ function start_battle_round() {
}
function pump_battle_round() {
+ function filter_battle_blocks(ci, is_candidate) {
+ let output = null;
+ for (let b in BLOCKS) {
+ if (is_candidate(b) && !game.moved[b]) {
+ if (block_initiative(b) == ci) {
+ if (!output)
+ output = [];
+ output.push(b);
+ }
+ }
+ }
+ return output;
+ }
+
+ function battle_step(active, initiative, candidate) {
+ game.battle_list = filter_battle_blocks(initiative, candidate);
+ if (game.battle_list) {
+ game.active = active;
+ return true;
+ }
+ return false;
+ }
+
if (is_friendly_town(game.where) || is_enemy_town(game.where)) {
end_battle();
} else if (count_attackers() == 0 || count_defenders() == 0) {
start_battle_round();
} else {
- function battle_step(active, initiative, candidate) {
- game.battle_list = filter_battle_blocks(initiative, candidate);
- if (game.battle_list) {
- game.active = active;
- return true;
- }
- return false;
- }
-
let attacker = game.attacker[game.where];
let defender = ENEMY[attacker];
@@ -1253,34 +1268,39 @@ function retreat_with_block(b) {
game.state = 'retreat_in_battle';
}
-function roll_attack(verb, b) {
+function roll_attack(active, b, verb) {
game.hits = 0;
let fire = block_fire_power(b, game.where);
let printed_fire = block_printed_fire_power(b);
let rolls = [];
- let results = [];
let steps = game.steps[b];
+ let name = block_name(b) + " " + BLOCKS[b].combat;
+ if (fire > printed_fire)
+ name += "+" + (fire - printed_fire);
for (let i = 0; i < steps; ++i) {
let die = roll_d6();
- rolls.push(die);
if (die <= fire) {
- results.push(HIT_TEXT);
+ rolls.push(DIE_HIT[die]);
++game.hits;
} else {
- results.push(MISS_TEXT);
+ rolls.push(DIE_MISS[die]);
}
}
- game.flash += block_name(b) + " " + BLOCKS[b].combat;
- if (fire > printed_fire)
- game.flash += "+" + (fire - printed_fire);
- game.flash += " " + verb + "\n" + rolls.join(" ") + " = " + results.join(" ");
+
+ game.flash = name + " " + verb + " " + rolls.join(" ") + " ";
+ if (game.hits == 0)
+ game.flash += "and misses.";
+ else if (game.hits == 1)
+ game.flash += "and scores 1 hit.";
+ else
+ game.flash += "and scores " + game.hits + " hits.";
+
+ log(active[0] + ": " + name + " " + verb + " " + rolls.join("") + ".");
}
function fire_with_block(b) {
game.moved[b] = true;
- game.flash = "";
- roll_attack("fires", b);
- log(game.flash);
+ roll_attack(game.active, b, "fires");
if (game.hits > 0) {
game.active = ENEMY[game.active];
goto_battle_hits();
@@ -1334,17 +1354,17 @@ function goto_battle_hits() {
function apply_hit(who) {
game.flash = block_name(who) + " takes a hit.";
- log(game.flash);
reduce_block(who, 'combat');
game.hits--;
- if (game.hits == 1)
- game.flash += " 1 hit left.";
- else if (game.hits > 1)
- game.flash += " " + game.hits + " hits left.";
if (game.hits == 0)
resume_battle();
- else
- goto_battle_hits();
+ else {
+ game.battle_list = list_victims(game.active);
+ if (game.battle_list.length == 0)
+ resume_battle();
+ else
+ game.flash += " " + game.hits + (game.hits == 1 ? " hit left." : " hits left.");
+ }
}
function list_victims(p) {
@@ -1468,6 +1488,7 @@ states.retreat_in_battle = {
gen_action(view, 'town', to);
},
town: function (to) {
+ game.flash = block_name(game.who) + " retreats.";
logp("retreats to " + to + ".");
game.location[game.who] = to;
resume_battle();
@@ -1488,7 +1509,6 @@ function goto_regroup() {
game.active = game.attacker[game.where];
if (is_enemy_town(game.where))
game.active = ENEMY[game.active];
- log(game.active + " wins the battle in " + game.where + ".");
game.state = 'regroup';
game.turn_log = [];
}
@@ -1533,6 +1553,7 @@ states.regroup_to = {
gen_action(view, 'town', to);
},
town: function (to) {
+ let from = game.where;
game.turn_log.push([from, to]);
move_block(game.who, game.where, to);
game.who = null;
diff --git a/ui.js b/ui.js
index b78e413..8098caa 100644
--- a/ui.js
+++ b/ui.js
@@ -69,14 +69,13 @@ function on_blur_town(evt) {
function on_click_town(evt) {
let where = evt.target.town;
- if (game.actions && game.actions.town && game.actions.town.includes(where))
- socket.emit('action', 'town', where);
+ send_action('town', where);
}
const STEP_TEXT = [ 0, "I", "II", "III", "IIII" ];
const HEIR_TEXT = [ 0, '\u00b9', '\u00b2', '\u00b3', '\u2074', '\u2075' ];
-function block_name(who) { return BLOCKS[who].name; }
+function block_name(who) { return who; }
function block_home(who) { return BLOCKS[who].home; }
function block_owner(who) { return BLOCKS[who].owner; }
@@ -96,7 +95,7 @@ function on_click_secret_block(evt) {
function on_focus_map_block(evt) {
let b = evt.target.block;
let s = game.known[b][1];
- let text = block_name(b) + " (" + block_home(b) + ") ";
+ let text = block_name(b) + " ";
if (BLOCKS[b].move)
text += BLOCKS[b].move + "-";
text += STEP_TEXT[s] + "-" + BLOCKS[b].combat;
@@ -109,8 +108,8 @@ function on_blur_map_block(evt) {
function on_click_map_block(evt) {
let b = evt.target.block;
- if (game.actions && game.actions.block && game.actions.block.includes(b))
- socket.emit('action', 'block', b);
+ if (!game.battle)
+ send_action('block', b);
}
function is_battle_reserve(who, list) {
@@ -144,8 +143,7 @@ function on_blur_battle_block(evt) {
function on_click_battle_block(evt) {
let b = evt.target.block;
- if (game.actions && game.actions.block && game.actions.block.includes(b))
- socket.emit('action', 'block', b);
+ send_action('block', b);
}
function on_focus_battle_fire(evt) {
@@ -177,16 +175,15 @@ function on_blur_battle_button(evt) {
document.getElementById("status").textContent = "";
}
-function on_click_battle_hit(evt) { socket.emit('action', 'battle_hit', evt.target.block); }
-function on_click_battle_fire(evt) { socket.emit('action', 'battle_fire', evt.target.block); }
-function on_click_battle_retreat(evt) { socket.emit('action', 'battle_retreat', evt.target.block); }
-function on_click_battle_charge(evt) { socket.emit('action', 'battle_charge', evt.target.block); }
-function on_click_battle_harry(evt) { socket.emit('action', 'battle_harry', evt.target.block); }
+function on_click_battle_hit(evt) { send_action('battle_hit', evt.target.block); }
+function on_click_battle_fire(evt) { send_action('battle_fire', evt.target.block); }
+function on_click_battle_retreat(evt) { send_action('battle_retreat', evt.target.block); }
+function on_click_battle_charge(evt) { send_action('battle_charge', evt.target.block); }
+function on_click_battle_harry(evt) { send_action('battle_harry', evt.target.block); }
function on_click_card(evt) {
let c = evt.target.id.split("+")[1] | 0;
- if (game.actions && game.actions.play && game.actions.play.includes(c))
- socket.emit('action', 'play', c);
+ send_action('play', c);
}
function on_button_undo(evt) { send_action('undo'); }
@@ -277,41 +274,32 @@ function build_secret_block(b, block, secret_index) {
return element;
}
-/*
-let MAP_OFFSET_X = 30;
-let MAP_OFFSET_Y = 30;
-let MAP_HEIGHT = 1215;
-*/
-let MAP_OFFSET_X = 0;
-let MAP_OFFSET_Y = 0;
-let MAP_HEIGHT = 1275;
-
function town_x(t) {
if (map_orientation == 'tall')
- return TOWNS[t].x - MAP_OFFSET_X;
+ return TOWNS[t].x;
else
- return TOWNS[t].y - MAP_OFFSET_Y;
+ return TOWNS[t].y;
}
function town_y(t) {
if (map_orientation == 'tall')
- return TOWNS[t].y - MAP_OFFSET_Y;
+ return TOWNS[t].y;
else
- return MAP_HEIGHT - TOWNS[t].x + MAP_OFFSET_X;
+ return 1275 - TOWNS[t].x;
}
function flip_x(x, y) {
if (map_orientation == 'tall')
- return x - MAP_OFFSET_X;
+ return x;
else
- return y - MAP_OFFSET_Y;
+ return y;
}
function flip_y(x, y) {
if (map_orientation == 'tall')
- return y - MAP_OFFSET_Y;
+ return y;
else
- return MAP_HEIGHT - x + MAP_OFFSET_X;
+ return 1275 - x;
}
function build_town(t, town) {
@@ -465,11 +453,11 @@ function position_block_stacked(location, i, c, k, element) {
let block_size = 60+6;
let x, y;
if (map_orientation == 'tall') {
- x = space.x + (i - c) * 12 + k * 12;
- y = space.y + (i - c) * 16 - k * 8;
+ x = space.x + (i - c) * 16 + k * 12;
+ y = space.y + (i - c) * 16 - k * 12;
} else {
- x = space.x - (i - c) * 12 + k * 12;
- y = space.y + (i - c) * 16 + k * 8;
+ x = space.x - (i - c) * 16 + k * 12;
+ y = space.y + (i - c) * 16 + k * 12;
}
element.style.left = ((flip_x(x,y) - block_size/2)|0)+"px";
element.style.top = ((flip_y(x,y) - block_size/2)|0)+"px";
@@ -597,14 +585,14 @@ function update_map() {
for (let where in TOWNS) {
if (ui.towns[where]) {
ui.towns[where].classList.remove('highlight');
- ui.towns[where].classList.remove('where');
+ ui.towns[where].classList.remove('muster');
}
}
if (game.actions && game.actions.town)
for (let where of game.actions.town)
ui.towns[where].classList.add('highlight');
- if (game.where)
- ui.towns[game.where].classList.add('where');
+ if (game.muster)
+ ui.towns[game.where].classList.add('muster');
for (let b in BLOCKS) {
ui.known[b].classList.remove('highlight');