summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js790
1 files changed, 673 insertions, 117 deletions
diff --git a/rules.js b/rules.js
index d8e921d..831377d 100644
--- a/rules.js
+++ b/rules.js
@@ -28,22 +28,25 @@ const BASE = 0
const GUERRILLA = 1
const TROOPS = 2
const POLICE = 3
+const CUBE = 4
const space_name = data.space_name
const first_piece = data.first_piece
const last_piece = data.last_piece
+const first_space = data.first_space
+const last_space = data.last_space
const first_pop = data.first_pop
const first_city = data.first_city
const last_city = data.last_city
const first_dept = data.first_dept
const last_pop = data.last_pop
const last_dept = data.last_dept
-const first_foreign = data.first_foreign
-const last_foreign = data.last_foreign
const first_loc = data.first_loc
const last_loc = data.last_loc
+const first_foreign = data.first_foreign
+const last_foreign = data.last_foreign
// Sequence of Play options
const ELIGIBLE = 0
@@ -179,8 +182,8 @@ exports.setup = function (seed, scenario, options) {
current: 0,
state: null,
- op_spaces: null,
- sa_spaces: null,
+ op: null,
+ sa: null,
deck: [],
president: 0,
@@ -293,6 +296,9 @@ function setup_standard() {
place_piece(CARTELS, BASE, 1, META_WEST)
place_piece(CARTELS, BASE, 1, GUAVIARE)
place_piece(CARTELS, BASE, 2, PUTUMAYO)
+
+ // XXX
+ set_support(BOGOTA, ACTIVE_OPPOSITION)
}
function setup_quick() {
@@ -386,7 +392,7 @@ function remove_piece(faction, type, count, where) {
}
}
-function count_pieces_imp(s, faction, type) {
+function count_pieces(s, faction, type) {
let first = first_piece[faction][type]
let last = last_piece[faction][type]
let n = 0
@@ -396,19 +402,53 @@ function count_pieces_imp(s, faction, type) {
return n
}
+function has_piece(s, faction, type) {
+ let first = first_piece[faction][type]
+ let last = last_piece[faction][type]
+ for (let p = first; p <= last; ++p)
+ if (game.pieces[p] === s)
+ return true
+ return false
+}
+
+function has_active_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 && !is_underground(p))
+ return true
+ return false
+}
+
+function count_bases(s) {
+ return (
+ count_pieces(s, GOVT, BASE) +
+ count_pieces(s, FARC, BASE) +
+ count_pieces(s, AUC, BASE) +
+ count_pieces(s, CARTELS, BASE)
+ )
+}
+
+function count_cubes(s) {
+ return (
+ count_pieces(s, GOVT, TROOPS) +
+ count_pieces(s, GOVT, POLICE)
+ )
+}
+
function update_control() {
game.govt_control = 0
game.farc_control = 0
- for (let s = 0; s <= last_dept; ++s) {
- let g = count_pieces_imp(s, GOVT, BASE) +
- count_pieces_imp(s, GOVT, TROOPS) +
- count_pieces_imp(s, GOVT, POLICE)
- let f = count_pieces_imp(s, FARC, BASE) +
- count_pieces_imp(s, FARC, GUERRILLA)
- let a = count_pieces_imp(s, AUC, BASE) +
- count_pieces_imp(s, AUC, GUERRILLA)
- let c = count_pieces_imp(s, CARTELS, BASE) +
- count_pieces_imp(s, CARTELS, GUERRILLA)
+ for (let s = first_space; s <= last_dept; ++s) {
+ let g = count_pieces(s, GOVT, BASE) +
+ count_pieces(s, GOVT, TROOPS) +
+ count_pieces(s, GOVT, POLICE)
+ let f = count_pieces(s, FARC, BASE) +
+ count_pieces(s, FARC, GUERRILLA)
+ let a = count_pieces(s, AUC, BASE) +
+ count_pieces(s, AUC, GUERRILLA)
+ let c = count_pieces(s, CARTELS, BASE) +
+ count_pieces(s, CARTELS, GUERRILLA)
if (g > a + c + f)
game.govt_control |= (1 << s)
else if (f > g + a + c)
@@ -417,11 +457,19 @@ function update_control() {
}
function is_city(s) {
- return s <= last_city
+ return s >= first_city && s <= last_city
}
-function has_govt_base(s) {
- return set_has(game.govt.bases, s)
+function is_dept(s) {
+ return s >= first_dept && s <= last_dept
+}
+
+function is_loc(s) {
+ return s >= first_loc && s <= last_loc
+}
+
+function is_adjacent(a, b) {
+ return set_has(data.spaces[a].adjacent, b)
}
function is_underground(p) {
@@ -451,12 +499,42 @@ function set_active(p) {
}
}
+function has_govt_control(s) {
+ return game.govt_control & (1 << s)
+}
+
+function has_farc_control(s) {
+ return game.farc_control & (1 << s)
+}
+
+function can_govt_civic_action(s) {
+ return game.support[s] < 2 && has_govt_control(s) && has_piece(s, GOVT, TROOPS) && has_piece(s, GOVT, POLICE)
+}
+
+function for_each_piece(faction, type, f) {
+ let p0 = first_piece[faction][type]
+ let p1 = last_piece[faction][type]
+ for (let p = p0; p <= p1; ++p)
+ f(p)
+}
+
+function gen_piece_in_space(faction, type, space) {
+ for_each_piece(faction, type, p => {
+ if (game.pieces[p] === space)
+ gen_action("piece", p)
+ })
+}
+
// === SEQUENCE OF PLAY ===
function this_card() {
return game.deck[0]
}
+function is_final_card() {
+ return false
+}
+
function goto_card() {
if (this_card() > 72)
goto_propaganda_card()
@@ -587,7 +665,7 @@ states.eligible2 = {
goto_pass()
break
case SOP_2ND_LIMOP:
- goto_op_only()
+ goto_limop()
break
case SOP_2ND_LIMOP_OR_EVENT:
goto_limop_or_event()
@@ -635,43 +713,100 @@ function goto_event() {
function goto_op_only() {
log_h2(faction_name[game.current] + " - Op Only")
- game.state = "op"
- game.op_spaces = []
- game.sa_spaces = null
+ goto_operation()
}
function goto_op_and_sa() {
log_h2(faction_name[game.current] + " - Op + Special")
- game.state = "op"
- game.op_spaces = []
- game.sa_spaces = []
+ goto_operation()
}
function goto_limop() {
log_h2(faction_name[game.current] + " - LimOp")
+ goto_operation()
+}
+
+function goto_operation() {
game.state = "op"
- game.op_spaces = []
- game.sa_spaces = null
+ game.op = {
+ spaces: [],
+ pieces: [],
+ count: 0,
+ }
+}
+
+function end_operation() {
+ game.op = null
+ resume_event_card()
}
function can_use_special_activity() {
- let faction = current_faction()
- if (faction.cylinder === SOP_1ST_OP_AND_SA || faction.cylinder === SOP_2ND_OP_AND_SA)
+ if (game.cylinder[game.current] === SOP_1ST_OP_AND_SA || game.cylinder[game.current] === SOP_2ND_OP_AND_SA)
+ return true
+ return false
+}
+
+function is_limop() {
+ if (game.cylinder[game.current] === SOP_2ND_LIMOP || game.cylinder[game.current] === SOP_2ND_LIMOP_OR_EVENT)
return true
return false
}
+function action_remove() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "remove"
+}
+
+states.remove = {
+ prompt() {
+ view.prompt = "Remove pieces to Available Forces."
+ for (let p = first_piece[game.current][BASE]; p <= last_piece[game.current][BASE]; ++p)
+ if (game.pieces[p] !== AVAILABLE)
+ gen_action("piece", p)
+ if (game.current === GOVT) {
+ for (let p = first_piece[game.current][TROOPS]; p <= last_piece[game.current][TROOPS]; ++p)
+ if (game.pieces[p] !== AVAILABLE)
+ gen_action("piece", p)
+ for (let p = first_piece[game.current][POLICE]; p <= last_piece[game.current][POLICE]; ++p)
+ if (game.pieces[p] !== AVAILABLE)
+ gen_action("piece", p)
+ } else {
+ for (let p = first_piece[game.current][GUERRILLA]; p <= last_piece[game.current][GUERRILLA]; ++p)
+ if (game.pieces[p] !== AVAILABLE)
+ gen_action("piece", p)
+ }
+ view.actions.done = 1
+ },
+ piece(p) {
+ game.pieces[p] = AVAILABLE
+ update_control()
+ },
+ done() {
+ game.state = game.save_state
+ game.save_state = 0
+ },
+}
+
+// === OPERATIONS ===
+
states.op = {
prompt() {
view.prompt = "Choose an Operation."
if (game.current === GOVT) {
view.actions.train = 1
view.actions.patrol = 1
- view.actions.sweep = 1
+ if (is_final_card())
+ view.actions.sweep = 0
+ else
+ view.actions.sweep = 1
view.actions.assault = 1
} else {
view.actions.rally = 1
- view.actions.march = 1
+ if (is_final_card())
+ view.actions.march = 0
+ else
+ view.actions.march = 1
view.actions.attack = 1
view.actions.terror = 1
}
@@ -708,6 +843,7 @@ states.op = {
push_undo()
log_h3("March")
game.state = "march"
+ game.op.pieces = []
},
attack() {
push_undo()
@@ -719,101 +855,545 @@ states.op = {
log_h3("Terror")
game.state = "terror"
},
+}
+
+function gen_operation_common() {
+ if (game.op.spaces.length > 0)
+ view.actions.done = 1
+ else
+ view.actions.done = 0
+ view.actions.remove = 1
+}
+
+// OPERATION: TRAIN
+
+states.train = {
+ prompt() {
+ view.prompt = "Train: Place cubes first, then replace with Base or buy Civic Action."
+
+ if (can_use_special_activity()) {
+ view.actions.air_lift = 1
+ view.actions.eradicate = 1
+ }
+
+ // Any Departments or Cities
+ if (game.resources[game.current] >= 3) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (is_city(s) || has_piece(s, GOVT, BASE))
+ if (!set_has(game.op.spaces, s))
+ gen_action("space", s)
+ }
+ }
+
+ // place base
+ view.actions.base = 1
+
+ // buy civic action
+ view.actions.civic = 1
- remove() {
+ gen_operation_common()
+ },
+ space(s) {
+ push_undo()
+
+ logi(`S${s}.`)
+
+ game.resources[game.current] -= 3
+ set_add(game.op.spaces, s)
+
+ game.state = "train_place"
+ game.op.where = s
+ game.op.count = 6
+ },
+ base() {
+ push_undo()
+ game.state = "train_base"
+ game.op.where = -1
+ },
+ civic() {
push_undo()
- game.save_state = game.state
- game.state = "remove"
+ game.state = "train_civic"
+ game.op.where = -1
},
+ done() {
+ end_operation()
+ },
+ air_lift: goto_air_lift,
+ eradicate: goto_eradicate,
}
-function for_each_own_piece(f) {
- f(p)
+states.train_place = {
+ prompt() {
+ view.prompt = `Train in ${space_name[game.op.where]}: Place up to ${game.op.count} cubes.`
+ view.where = game.op.where
+
+ if (game.op.count > 0) {
+ gen_piece_in_space(GOVT, POLICE, AVAILABLE)
+ gen_piece_in_space(GOVT, TROOPS, AVAILABLE)
+ }
+
+ view.actions.next = 1
+ view.actions.remove = 1
+ },
+ piece(p) {
+ push_undo()
+ game.pieces[p] = game.op.where
+ if (--game.op.count == 0)
+ game.state = "train"
+ update_control()
+ },
+ next() {
+ game.op.count = 0
+ game.state = "train"
+ },
}
-states.remove = {
+states.train_base = {
prompt() {
- view.prompt = "Remove pieces to Available Forces."
- for_each_own_piece(p => {
- if (game.pieces[p] !== AVAILABLE)
- gen_action("piece", p)
- })
- view.actions.done = 1
+ if (game.op.where < 0) {
+ view.prompt = `Train: Replace 3 cubes with a Base.`
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (set_has(game.op.spaces, s) || game.resources[game.current] >= 3)
+ if (count_bases(s) < 2 && count_cubes(s) >= 3)
+ gen_action("space", s)
+ }
+ } else {
+ if (game.op.count < 0) {
+ view.prompt = `Train: All done.`
+ view.actions.done = 1
+ } else if (game.op.count > 0) {
+ view.prompt = `Train: Replace ${game.op.count} cubes with a Base.`
+ gen_piece_in_space(GOVT, POLICE, game.op.where)
+ gen_piece_in_space(GOVT, TROOPS, game.op.where)
+ } else {
+ view.prompt = `Train: Place Base.`
+ gen_piece_in_space(GOVT, BASE, AVAILABLE)
+ }
+ }
+ },
+ space(s) {
+ push_undo()
+ if (!set_has(game.op.spaces, s)) {
+ game.resources[game.current] -= 3
+ set_add(game.op.spaces, s)
+ }
+ game.op.where = s
+ game.op.count = 3
},
piece(p) {
push_undo()
- game.pieces[p] = AVAILABLE
+ if (game.op.count > 0)
+ game.pieces[p] = AVAILABLE
+ else
+ game.pieces[p] = game.op.where
+ --game.op.count
+ update_control()
},
- done() {
- game.state = game.save_state
- game.save_state = 0
- }
+ done: end_operation,
}
-states.train = {
+states.train_civic = {
prompt() {
- let faction = current_faction()
+ let res = game.resources[game.current]
+ if (game.op.where < 0) {
+ view.prompt = `Train: Buy Civic Action.`
+ if (res >= 3) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (set_has(game.op.spaces, s) || res >= 6)
+ if (can_govt_civic_action(s))
+ gen_action("space", s)
+ }
+ }
+ } else {
+ view.prompt = `Train: Buy Civic Action in ${space_name[game.op.where]}.`
+ view.where = game.op.where
+ if (res >= 3 && can_govt_civic_action(game.op.where))
+ gen_action("space", game.op.where)
+ else
+ view.prompt = `Train: All done.`
+ view.actions.done = 1
+ }
+ },
+ space(s) {
+ push_undo()
+ if (!set_has(game.op.spaces, s)) {
+ game.resources[game.current] -= 3
+ set_add(game.op.spaces, s)
+ }
+ game.op.where = s
+ game.resources[game.current] -= 3
+ game.support[game.op.where] += 1
+ },
+ done: end_operation,
+}
- view.prompt = "Train: Select spaces."
+// OPERATION: RALLY
+
+function rally_count() {
+ if (has_piece(game.op.where, game.current, BASE))
+ return data.spaces[game.op.where].pop + count_pieces(game.op.where, game.current, BASE)
+ return 1
+}
+
+states.rally = {
+ prompt() {
+ if (game.current === FARC)
+ view.prompt = "Rally: Select City or Department without Support."
+ else if (game.current === AUC)
+ view.prompt = "Rally: Select City or Department without Opposition."
+ else
+ view.prompt = "Rally: Select City or Department."
if (can_use_special_activity()) {
- view.actions.air_lift = 1
- view.actions.eradicate = 1
+ if (game.current === FARC)
+ view.actions.extort = 1
+ if (game.current === AUC)
+ view.actions.extort = 1
+ if (game.current === CARTELS) {
+ view.actions.cultivate = 1
+ view.actions.process = 1
+ view.actions.bribe = 1
+ }
}
- // Any Departments or Cities
- if (faction.resources >= 3) {
- for (let s = 0; s <= last_dept; ++s) {
- if (!set_has(game.op_spaces, s))
- gen_action("space", s)
+ // Departments or Cities
+ if (game.resources[game.current] >= 1) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (set_has(game.op.spaces, s))
+ continue
+
+ // FARC: without Support
+ if (game.current === FARC)
+ if (game.support[s] > 0)
+ continue
+
+ // AUC: without Opposition
+ if (game.current === AUC)
+ if (game.support[s] < 0)
+ continue
+
+ gen_action("space", s)
}
}
+
+ gen_operation_common()
},
- air_lift() {
+ space(s) {
push_undo()
- game.state = "air_lift"
+
+ logi(`S${s}.`)
+ game.resources[game.current] -= 1
+ set_add(game.op.spaces, s)
+
+ game.state = "rally_place"
+ game.op.where = s
+ game.op.count = rally_count()
},
- eradicate() {
+ done: end_operation,
+ extort: goto_extort,
+ cultivate: goto_cultivate,
+ process: goto_process,
+ bribe: goto_bribe,
+}
+
+states.rally_place = {
+ prompt() {
+ view.prompt = `Rally: Place up to ${game.op.count} Guerrillas.`
+ view.where = game.op.where
+ view.actions.remove = 1
+
+ if (game.op.count === rally_count()) {
+ view.actions.base = 0
+ view.actions.flip = 0
+ view.actions.move = 0
+ if (count_pieces(game.op.where, game.current, GUERRILLA) >= 2) {
+ if (count_bases(game.op.where) < 2)
+ view.actions.base = 1
+ }
+ if (has_piece(game.op.where, game.current, BASE)) {
+ if (has_active_guerrilla(game.op.where, game.current))
+ view.actions.flip = 1
+ view.actions.move = 1
+ }
+ }
+
+ view.actions.next = 1
+
+ gen_piece_in_space(game.current, GUERRILLA, AVAILABLE)
+ },
+ piece(p) {
push_undo()
- game.state = "eradicate"
+ set_underground(p)
+ game.pieces[p] = game.op.where
+ if (--game.op.count === 0)
+ game.state = "rally"
+ update_control()
},
- space(s) {
+ base() {
push_undo()
- logi(`S${s}.`)
- let faction = current_faction()
- faction.resources -= 3
- set_add(game.op_spaces, s)
- game.where = s
- if (is_city(s) || has_govt_base(s)) {
- game.state = "train_place_cubes"
- game.selected = -1
- game.count = 6
+ logi("Base.")
+ game.state = "rally_base"
+ game.op.count = 2
+ },
+ flip() {
+ push_undo()
+ logi("Flipped.")
+ for_each_piece(game.current, GUERRILLA, p => {
+ if (game.pieces[p] === game.op.where)
+ set_underground(p)
+ })
+ game.state = "rally"
+ },
+ move() {
+ push_undo()
+ logi("Moved.")
+ game.state = "rally_move"
+ game.op.count = 0
+ },
+ next() {
+ push_undo()
+ game.state = "rally"
+ },
+}
+
+states.rally_base = {
+ prompt() {
+ view.prompt = `Rally: Replace ${game.op.count} Guerrillas with a Base.`
+ if (game.op.count > 0)
+ gen_piece_in_space(game.current, GUERRILLA, game.op.where)
+ else
+ gen_piece_in_space(game.current, BASE, AVAILABLE)
+ },
+ piece(p) {
+ push_undo()
+ if (game.op.count > 0) {
+ set_underground(p)
+ game.pieces[p] = AVAILABLE
+ --game.op.count
} else {
- game.state = "train_base_or_civic"
+ game.pieces[p] = game.op.where
+ game.state = "rally"
}
+ update_control()
},
}
-function gen_select_available(action, list) {
- for (let i = list.length; i-- > 0; ) {
- if (list[i] === AVAILABLE) {
- gen_action(action, i)
- return
- }
+states.rally_move = {
+ prompt() {
+ view.where = game.op.where
+ view.actions.remove = 1
+
+ view.prompt = `Rally: Move any Guerrillas to ${space_name[game.op.where]}.`
+
+ for_each_piece(game.current, GUERRILLA, p => {
+ if (game.pieces[p] !== game.op.where && game.pieces[p] !== AVAILABLE)
+ gen_action("piece", p)
+ })
+
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+ game.pieces[p] = game.op.where
+ game.op.count++
+ update_control()
+ },
+ next() {
+ push_undo()
+ game.op.count = 0
+ game.state = "rally"
}
- for (let i = 0; i < list.length; ++i)
- gen_action(action, i)
}
-states.train_place_cubes = {
+// OPERATION: MARCH
+
+function can_march_to(to) {
+ for (let from of data.spaces[to].adjacent)
+ if (has_piece(from, game.current, GUERRILLA))
+ return true
+ return false
+}
+
+states.march = {
prompt() {
- view.prompt = `Train in ${space_name[game.where]}: Place up to ${game.count} cubes.`
+ view.prompt = `March: Move Guerrillas to Cities/Departments/LoCs.`
+
+ if (can_use_special_activity()) {
+ if (game.current === FARC)
+ view.actions.extort = 1
+ if (game.current === AUC)
+ view.actions.extort = 1
+ if (game.current === CARTELS) {
+ view.actions.cultivate = 1
+ view.actions.process = 1
+ view.actions.bribe = 1
+ }
+ }
+
+ // TODO: check if move is possible
- if (game.selected < 0) {
- gen_select_available("govt_police", game.govt.police)
- gen_select_available("govt_troops", game.govt.troops)
+ if (game.resources[game.current] >= 1) {
+ for (let s = first_space; s <= last_dept; ++s) {
+ if (set_has(game.op.spaces, s))
+ continue
+ if (can_march_to(s))
+ gen_action("space", s)
+ }
}
+
+ for (let s = first_loc; s <= last_loc; ++s) {
+ if (set_has(game.op.spaces, s))
+ continue
+ if (can_march_to(s))
+ gen_action("space", s)
+ }
+
+ gen_operation_common()
},
+ space(s) {
+ push_undo()
+
+ logi(`S${s}.`)
+
+ if (s <= last_dept)
+ game.resources[game.current] -= 1
+
+ set_add(game.op.spaces, s)
+
+ game.state = "march_move"
+ game.op.where = s
+ game.op.march = []
+ },
+ done: end_operation,
+}
+
+function may_activate_marching_guerrillas() {
+ if (is_loc(game.op.where))
+ return true
+ if (game.support[game.op.where] > 0)
+ return true
+ if (game.current === AUC && game.support[game.op.where] < 0)
+ return true
+ return false
+}
+
+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)
+ count += count_pieces(game.op.where, GOVT, POLICE)
+ if (game.current === AUC)
+ count += count_pieces(game.op.where, FARC, GUERRILLA)
+ if (count > 3)
+ for (let p of group)
+ set_active(p)
+ }
+}
+
+states.march_move = {
+ prompt() {
+ view.prompt = `March: Move Guerrillas to ${space_name[game.op.where]}.`
+ view.where = game.op.where
+
+ for_each_piece(game.current, GUERRILLA, p => {
+ // May not move more than once
+ if (set_has(game.op.pieces, p))
+ return // continue
+
+ let s = game.pieces[p]
+ if (is_adjacent(game.op.where, s))
+ gen_action("piece", p)
+ })
+
+ if (game.op.march.length > 0)
+ view.actions.next = 1
+ },
+ piece(p) {
+ push_undo()
+
+ let from = game.pieces[p]
+ let group = map_get(game.op.march, from, null)
+ if (!group)
+ group = []
+ set_add(group, p)
+ map_set(game.op.march, from, group)
+
+ activate_marching_guerrillas(group)
+
+ set_add(game.op.pieces, p)
+
+ game.pieces[p] = game.op.where
+
+ update_control()
+ },
+ next() {
+ push_undo()
+ game.op.march = 0
+ game.state = "march"
+ },
+}
+
+// === SPECIAL ACTIVITIES ===
+
+function goto_air_lift() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "air_lift"
+}
+
+function goto_air_strike() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "air_strike"
+}
+
+function goto_eradicate() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "eradicate"
+}
+
+function goto_extort() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "extort"
+}
+
+function goto_ambush() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "ambush"
+}
+
+function goto_kidnap() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "kidnap"
+}
+
+function goto_assassinate() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "assassinate"
+}
+
+function goto_cultivate() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "cultivate"
+}
+
+function goto_process() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "process"
+}
+
+function goto_bribe() {
+ push_undo()
+ game.save_state = game.state
+ game.state = "bribe"
}
// === GAME OVER ===
@@ -958,13 +1538,18 @@ exports.view = function (state, role) {
exports.action = function (state, role, action, arg) {
load_game(state)
- // Object.seal(game) // XXX: don't allow adding properties
+
+ // 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) {
S[action](arg)
} else {
if (action === "undo" && game.undo && game.undo.length > 0)
pop_undo()
+ else if (action === "remove")
+ action_remove()
else
throw new Error("Invalid action: " + action)
}
@@ -977,36 +1562,6 @@ exports.is_checkpoint = function (a, b) {
// === COMMON LIBRARY ===
-// Packed array of small numbers in one word
-
-function pack1_get(word, n) {
- return (word >>> n) & 1
-}
-
-function pack2_get(word, n) {
- n = n << 1
- return (word >>> n) & 3
-}
-
-function pack4_get(word, n) {
- n = n << 2
- return (word >>> n) & 15
-}
-
-function pack1_set(word, n, x) {
- return (word & ~(1 << n)) | (x << n)
-}
-
-function pack2_set(word, n, x) {
- n = n << 1
- return (word & ~(3 << n)) | (x << n)
-}
-
-function pack4_set(word, n, x) {
- n = n << 2
- return (word & ~(15 << n)) | (x << n)
-}
-
function clear_undo() {
if (game.undo.length > 0)
game.undo = []
@@ -1254,3 +1809,4 @@ function map_delete(map, item) {
}
}
}
+