summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-04-23 13:29:24 +0200
committerTor Andersson <tor@ccxvii.net>2023-05-03 18:48:16 +0200
commitc627af2462954bd4f000d9a4fc43fe63ed1a2358 (patch)
treedfe2b158de7da58604197d9b4febc5bb5c13c1a1 /rules.js
parent4fdda6328e67b25db6ff30c0f18fb7d5ffef97b9 (diff)
downloadandean-abyss-c627af2462954bd4f000d9a4fc43fe63ed1a2358.tar.gz
WIP Clean up Shipment Transfer timing.
At end of each Operation Space. At end of each Special Activity (per space for Assassinate/Bribe). At end of each Event (never in the middle). Clean up Transfer mechanisms. Force current player to actively select Shipment before control swaps. Transfer all shipments of current player before swapping again. Captured Goods, Contraband, and Commandeer - steal shipments! Only Drug Bust / Captured Goods / Commandeer / Contraband if no option. If all guerrillas are dead in Drug Bust, or if no enemy guerrillas in the other cases. Auto-transfer to own faction if possible. Make Shipment removal optional. 4.5.3 Errata and new Shipment transfer code.
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js425
1 files changed, 316 insertions, 109 deletions
diff --git a/rules.js b/rules.js
index f508804..7ea466e 100644
--- a/rules.js
+++ b/rules.js
@@ -6,6 +6,10 @@
// TODO: can_...operation - for space = ... check them all / can_rally - check that it is dept/city etc
+// Captured Good - may not remove
+// Commandeer - may not remove
+// Contraband - if removed - cartels decide who gets it
+
const AUTOMATIC = true
let states = {}
@@ -1384,6 +1388,11 @@ function move_piece(p, s) {
function place_piece(p, s) {
if (piece_space(p) === AVAILABLE)
p = find_piece(AVAILABLE, piece_faction(p), piece_type(p))
+
+ // NOTE: drop any held shipments if we automatically remove a piece to place it elsewhere
+ else if (piece_space(p) !== s && is_any_guerrilla(p))
+ drop_held_shipments(p)
+
set_underground(p)
set_piece_space(p, s)
update_control()
@@ -1538,6 +1547,13 @@ function drop_held_shipments(p) {
drop_shipment(sh)
}
+function find_stealable_shipment() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (can_steal_shipment(sh))
+ return sh
+ return -1
+}
+
function has_dropped_shipments() {
for (let sh = 0; sh < 4; ++sh)
if (is_shipment_dropped(sh))
@@ -1545,6 +1561,53 @@ function has_dropped_shipments() {
return false
}
+function has_dropped_shipments_of_current() {
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh) && get_dropped_shipment_faction(sh) === game.current)
+ return true
+ return false
+}
+
+function can_steal_dropped_shipments() {
+ /* Allow stealing if owning faction has no guerrillas to transfer to. */
+ /* i.e. force transfer to executing faction. */
+ for (let sh = 0; sh < 4; ++sh)
+ if (can_steal_shipment(sh))
+ return true
+ return false
+}
+
+function can_steal_shipment(sh) {
+ /* Steal if we're the ONLY faction remaining in a space. */
+ /*
+ @Volko
+ My recollection of my original intent--and maybe we should just revert to
+ that, in anticipation of an AA reprint--is that a Faction would be able
+ to hold on to its Shipment until that Faction had no more Guerrillas in
+ the space. As desired, including when the owner loses its last Guerrilla
+ there, the owning Faction could choose to either remove the Shipment
+ or give it to someone else (if anyone else's Guerrillas there). Attack,
+ Assassinate, and Bribe could snatch the Shipment only if it got removed.
+ */
+ if (is_shipment_dropped(sh)) {
+ let s = get_dropped_shipment_space(sh)
+ if (game.current === FARC)
+ return has_piece(s, FARC, GUERRILLA) && !has_piece(s, AUC, GUERRILLA) && !has_piece(s, CARTELS, GUERRILLA)
+ if (game.current === AUC)
+ return !has_piece(s, FARC, GUERRILLA) && has_piece(s, AUC, GUERRILLA) && !has_piece(s, CARTELS, GUERRILLA)
+ if (game.current === CARTELS)
+ return !has_piece(s, FARC, GUERRILLA) && !has_piece(s, AUC, GUERRILLA) && has_piece(s, CARTELS, GUERRILLA)
+ }
+ return false
+}
+
+function can_drug_bust_dropped_shipments(s) {
+ if (has_dropped_shipments()) {
+ return !has_piece(s, FARC, GUERRILLA) && !has_piece(s, AUC, GUERRILLA) && !has_piece(s, CARTELS, GUERRILLA)
+ }
+ return false
+}
+
function can_transfer_dropped_shipments() {
for (let sh = 0; sh < 4; ++sh)
if (is_shipment_dropped(sh))
@@ -1560,27 +1623,34 @@ function auto_transfer_dropped_shipments() {
}
function auto_transfer_dropped_shipment_imp(sh) {
- // Transfer shipment automatically if there's only one faction present.
- // NOTE: Don't transfer to own faction automatically,
let owner = get_dropped_shipment_faction(sh)
let s = get_dropped_shipment_space(sh)
let a = find_piece(s, FARC, GUERRILLA)
let b = find_piece(s, AUC, GUERRILLA)
let c = find_piece(s, CARTELS, GUERRILLA)
- if (a >= 0 && b < 0 && c < 0) {
- if (owner !== FARC)
- log_transfer("FARC took Shipment in S" + s + ".")
+
+ // Keep it with own faction if possible (to speed up play)
+ if (owner === FARC && a >= 0)
place_shipment(sh, a)
- }
- if (a < 0 && b >= 0 && c < 0) {
- if (owner !== AUC)
- log_transfer("AUC took Shipment in S" + s + ".")
+ else if (owner === AUC && b >= 0)
place_shipment(sh, b)
- }
- if (a < 0 && b < 0 && c >= 0) {
- if (owner !== CARTELS)
- log_transfer("Cartels took Shipment in S" + s + ".")
+ else if (owner === CARTELS && c >= 0)
place_shipment(sh, c)
+
+ else if (!may_remove_shipment()) {
+ // If only one faction in space and not allowed to remove.
+ if (a >= 0 && b < 0 && c < 0) {
+ log_transfer("FARC took Shipment in S" + s + ".")
+ place_shipment(sh, a)
+ }
+ else if (a < 0 && b >= 0 && c < 0) {
+ log_transfer("AUC took Shipment in S" + s + ".")
+ place_shipment(sh, b)
+ }
+ else if (a < 0 && b < 0 && c >= 0) {
+ log_transfer("Cartels took Shipment in S" + s + ".")
+ place_shipment(sh, c)
+ }
}
}
@@ -1759,8 +1829,8 @@ states.remove_pieces = {
game.state = game.transfer.state
game.summary = game.transfer.summary
delete game.transfer
- transfer_or_remove_shipments()
log_br()
+ transfer_or_remove_shipments()
},
}
@@ -2124,16 +2194,6 @@ function transfer_or_remove_shipments() {
}
}
-function transfer_or_drug_bust_shipments() {
- auto_transfer_dropped_shipments()
- if (has_dropped_shipments()) {
- if (can_transfer_dropped_shipments())
- goto_transfer_dropped_shipments()
- else
- goto_drug_bust()
- }
-}
-
function goto_transfer_dropped_shipments() {
game.transfer = {
current: game.current,
@@ -2144,32 +2204,77 @@ function goto_transfer_dropped_shipments() {
}
function resume_transfer_dropped_shipments() {
- for (let sh = 0; sh < 4; ++sh) {
- if (is_shipment_dropped(sh)) {
- game.current = get_dropped_shipment_faction(sh)
- game.state = "transfer_dropped_shipments"
- game.transfer.shipment = sh
+ if (has_dropped_shipments_of_current())
+ game.state = "transfer_dropped_shipments_current"
+ else if (has_dropped_shipments())
+ game.state = "transfer_dropped_shipments"
+ else
+ end_negotiation()
+}
- // Clear undo if swapping to another player!
- if (game.current !== game.transfer.current)
- clear_undo()
+states.transfer_dropped_shipments_current = {
+ disable_negotiation: true,
+ inactive: "Transfer Shipment",
+ prompt() {
+ view.prompt = "Transfer Shipment to another Guerrilla."
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh) && get_dropped_shipment_faction(sh) === game.current)
+ gen_action_shipment(sh)
+ },
+ shipment(sh) {
+ game.state = "transfer_dropped_shipment_to"
+ game.transfer.shipment = sh
+ },
+}
- return
+states.transfer_dropped_shipments = {
+ disable_negotiation: true,
+ inactive: "Transfer Shipment",
+ prompt() {
+ view.prompt = "Transfer Shipment to another Guerrilla."
+ for (let sh = 0; sh < 4; ++sh)
+ if (is_shipment_dropped(sh))
+ gen_action_shipment(sh)
+ },
+ shipment(sh) {
+ let faction = get_dropped_shipment_faction(sh)
+ if (game.current !== faction) {
+ clear_undo()
+ game.current = faction
}
- }
- end_negotiation()
+ game.state = "transfer_dropped_shipment_to"
+ game.transfer.shipment = sh
+ },
}
-states.transfer_dropped_shipments = {
+function may_remove_shipment() {
+ // Captured Goods
+ if (typeof game.op === "object" && game.op.type === "Attack")
+ return false
+ // Commandeer
+ if (typeof game.sa === "object" && game.sa.commandeer)
+ return false
+ return true
+}
+
+states.transfer_dropped_shipment_to = {
disable_negotiation: true,
+ inactive: "Transfer Shipment",
prompt() {
view.prompt = "Transfer Shipment to another Guerrilla."
+ view.selected_shipment = game.transfer.shipment
+
let s = get_dropped_shipment_space(game.transfer.shipment)
gen_piece_in_space(s, FARC, GUERRILLA)
gen_piece_in_space(s, AUC, GUERRILLA)
gen_piece_in_space(s, CARTELS, GUERRILLA)
+ // Disable removal for Captured Goods and Commandeer.
+ // Must transfer to executing faction instead of removing.
+ if (may_remove_shipment())
+ view.actions.remove = 1
+
view.actions.undo = 0
},
piece(p) {
@@ -2177,25 +2282,62 @@ states.transfer_dropped_shipments = {
place_shipment(game.transfer.shipment, p)
resume_transfer_dropped_shipments()
},
+ remove() {
+ // Contraband - Cartels decide who get it or if it is removed
+ if (typeof game.sa === "object" && game.sa.contraband) {
+ game.current = CARTELS
+ game.state = "transfer_dropped_shipment_contraband"
+ return
+ }
+
+ let sh = game.transfer.shipment
+ let s = get_dropped_shipment_space(sh)
+ if (game.op && (game.op.type === "Assault" || game.op.type === "Patrol")) {
+ log_transfer("Drug Bust - Removed Shipment in S" + s + " for +6 Aid.")
+ add_aid(6)
+ } else {
+ log_transfer("Removed Shipment in S" + s + ".")
+ }
+ remove_shipment(sh)
+ resume_transfer_dropped_shipments()
+ },
}
-// === DRUG BUST ===
+states.transfer_dropped_shipment_contraband = {
+ disable_negotiation: true,
+ inactive: "Transfer Shipment",
+ prompt() {
+ view.prompt = "Contraband: Transfer Shipment to any Guerrilla."
+ view.selected_shipment = game.transfer.shipment
-function goto_drug_bust() {
- game.transfer = game.state
- resume_drug_bust()
-}
+ let s = get_dropped_shipment_space(game.transfer.shipment)
-function resume_drug_bust() {
- if (has_dropped_shipments()) {
- game.state = "drug_bust"
- } else {
- game.state = game.transfer
- delete game.transfer
- }
+ gen_piece_in_space(s, FARC, GUERRILLA)
+ gen_piece_in_space(s, AUC, GUERRILLA)
+ gen_piece_in_space(s, CARTELS, GUERRILLA)
+
+ view.actions.remove = 1
+
+ view.actions.undo = 0
+ },
+ piece(p) {
+ log_transfer(`Contraband - ${piece_faction_name(p)} took Shipment in S${piece_space(p)}.`)
+ place_shipment(game.transfer.shipment, p)
+ resume_transfer_dropped_shipments()
+ },
+ remove() {
+ let sh = game.transfer.shipment
+ let s = get_dropped_shipment_space(sh)
+ log_transfer("Contraband - removed Shipment in S" + s + ".")
+ remove_shipment(sh)
+ resume_transfer_dropped_shipments()
+ },
}
+// === DRUG BUST (PATROL AND ASSAULT) ===
+
states.drug_bust = {
+ inactive: "Drug Bust",
prompt() {
view.prompt = "Drug Bust: Add +6 Aid per Shipment removed by Assault."
for (let sh = 0; sh < 4; ++sh)
@@ -2203,11 +2345,16 @@ states.drug_bust = {
gen_action_shipment(sh)
},
shipment(sh) {
- log_space(get_dropped_shipment_space(sh), "Drug Bust")
+ let s = get_dropped_shipment_space(sh)
+ log_transfer("Drug Bust - Removed Shipment in S" + s + " for +6 Aid.")
remove_shipment(sh)
- logi_aid(6)
add_aid(6)
- resume_drug_bust()
+ if (!has_dropped_shipments()) {
+ if (game.op.type === "Patrol")
+ resume_patrol_assault()
+ else
+ resume_assault()
+ }
},
}
@@ -3143,6 +3290,7 @@ function resume_patrol_assault() {
game.state = "patrol_assault"
else
game.state = "patrol_done"
+ transfer_or_drug_bust_shipments()
}
function can_patrol_assault() {
@@ -3223,8 +3371,10 @@ states.patrol_assault_space = {
function end_patrol_assault_space() {
log_space(game.op.where, "Assault")
pop_summary()
- resume_patrol_assault()
- transfer_or_drug_bust_shipments()
+ if (can_drug_bust_dropped_shipments(game.op.where))
+ game.state = "drug_bust"
+ else
+ resume_patrol_assault()
}
states.patrol_done = {
@@ -3384,10 +3534,11 @@ states.sweep = {
}
function resume_sweep() {
- if (game.vm)
+ if (game.vm) {
end_operation()
- else
- game.state = "sweep"
+ return
+ }
+ game.state = "sweep"
}
function goto_sweep_space(allow_move) {
@@ -3658,6 +3809,15 @@ states.assault = {
end_assault: end_operation,
}
+function resume_assault() {
+ if (game.vm) {
+ end_operation()
+ } else {
+ game.state = "assault"
+ transfer_or_remove_shipments() // NOTE: Unless removed by Drug Bust
+ }
+}
+
function goto_assault_space() {
push_summary()
game.op.targeted = 0
@@ -3701,11 +3861,10 @@ states.assault_space = {
function end_assault_space() {
log_space(game.op.where, "Assault")
pop_summary()
- if (game.vm)
- end_operation()
+ if (can_drug_bust_dropped_shipments(game.op.where))
+ game.state = "drug_bust"
else
- game.state = "assault"
- transfer_or_drug_bust_shipments()
+ resume_assault()
}
// OPERATION: RALLY
@@ -3789,12 +3948,15 @@ states.rally = {
}
function resume_rally() {
+ if (game.vm) {
+ end_operation()
+ return
+ }
if (game.op.elite_backing)
game.state = "elite_backing_done"
- else if (game.vm)
- end_operation()
else
game.state = "rally"
+ transfer_or_remove_shipments()
}
function goto_rally_space() {
@@ -3877,7 +4039,6 @@ states.rally_base_remove = {
if (auto_place_piece(game.op.where, game.current, BASE)) {
log_summary("Placed " + piece_faction_type_name[game.current][BASE])
end_rally_space()
- transfer_or_remove_shipments()
} else {
game.state = "rally_base_place"
}
@@ -3894,7 +4055,6 @@ states.rally_base_place = {
log_summary("Placed " + piece_faction_type_name[game.current][BASE] + " from S" + piece_space(p))
place_piece(p, game.op.where)
end_rally_space()
- transfer_or_remove_shipments()
},
}
@@ -4186,10 +4346,12 @@ states.attack = {
}
function resume_attack() {
- if (game.vm)
+ if (game.vm) {
vm_next()
- else
- game.state = "attack"
+ return
+ }
+ game.state = "attack"
+ transfer_or_remove_shipments() // NOTE: Unless taken by Captured Goods
}
function goto_attack_space() {
@@ -4311,19 +4473,33 @@ states.attack_remove = {
game.op.targeted |= target_faction(p)
remove_piece(p)
- if (--game.op.count === 0 || !has_attack_target(game.op.where)) {
+ if (--game.op.count === 0 || !has_attack_target(game.op.where))
end_attack_space()
- transfer_or_remove_shipments()
- }
},
skip() {
end_attack_space()
- transfer_or_remove_shipments()
}
}
function end_attack_space() {
- resume_attack()
+ if (can_steal_dropped_shipments())
+ game.state = "captured_goods"
+ else
+ resume_attack()
+}
+
+states.captured_goods = {
+ prompt() {
+ view.prompt = "Captured Goods: Take removed Shipment."
+ view.selected_shipment = find_stealable_shipment()
+ gen_piece_in_space(game.op.where, game.current, GUERRILLA)
+ },
+ piece(p) {
+ log_transfer("Captured Goods - " + piece_faction_name(p) + " took Shipment in S" + piece_space(p) + ".")
+ place_shipment(find_stealable_shipment(), p)
+ if (!can_steal_dropped_shipments())
+ resume_attack()
+ },
}
// OPERATION: TERROR
@@ -4405,7 +4581,7 @@ states.terror = {
}
function do_terror_space(s) {
- // Note: can come here from both "terror" and "kidnap" states
+ // NOTE: can come here from both "terror" and "kidnap" states
if (is_loc(s))
select_op_space(s, 0)
else
@@ -4686,7 +4862,6 @@ states.air_strike = {
piece(p) {
log_space(piece_space(p), "Air Strike")
logi("Removed " + piece_name(p))
-
remove_piece(p)
end_special_activity()
}
@@ -4762,11 +4937,9 @@ states.eradicate_base = {
function goto_eradicate_shift() {
if (can_eradicate_shift()) {
game.state = "eradicate_shift"
- } else if (AUTOMATIC && auto_place_piece(game.sa.where, FARC, GUERRILLA)) {
+ } else if (auto_place_piece(game.sa.where, FARC, GUERRILLA)) {
logi("Placed FARC Guerrilla")
end_eradicate()
- } else if (can_place_piece(game.sa.where, FARC, GUERRILLA)) {
- game.state = "eradicate_place"
} else {
end_eradicate()
}
@@ -4798,18 +4971,6 @@ states.eradicate_shift = {
},
}
-states.eradicate_place = {
- prompt() {
- view.prompt = `Eradicate: Place available FARC Guerrilla in ${space_name[game.sa.where]}.`
- gen_piece_in_space(AVAILABLE, FARC, GUERRILLA)
- },
- piece(p) {
- logi("Placed FARC Guerrilla")
- place_piece(p, game.sa.where)
- end_eradicate()
- },
-}
-
function end_eradicate() {
end_special_activity()
}
@@ -5146,6 +5307,7 @@ function goto_assassinate() {
push_undo()
move_cylinder_to_special_activity()
game.sa = {
+ commandeer: 1,
save: game.state,
spaces: [],
where: -1,
@@ -5163,6 +5325,15 @@ function can_assassinate_in_space(s) {
return false
}
+function resume_assassinate() {
+ if (can_assassinate_again()) {
+ game.state = "assassinate"
+ transfer_or_remove_shipments() // NOTE: Unless taken by Commandeer
+ } else {
+ end_special_activity()
+ }
+}
+
states.assassinate = {
prompt() {
view.prompt = "Assassinate: Select up to 3 spaces where Terror."
@@ -5209,13 +5380,28 @@ states.assassinate_space = {
piece(p) {
logi("Removed " + piece_name(p))
remove_piece(p)
+ end_assassinate_space()
+ },
+}
- if (can_assassinate_again())
- game.state = "assassinate"
- else
- end_special_activity()
+function end_assassinate_space() {
+ if (can_steal_dropped_shipments())
+ game.state = "commandeer"
+ else
+ resume_assassinate()
+}
- transfer_or_remove_shipments()
+states.commandeer = {
+ prompt() {
+ view.prompt = "Commandeer: Take removed Shipment."
+ view.selected_shipment = find_stealable_shipment()
+ gen_piece_in_space(game.sa.where, game.current, GUERRILLA)
+ },
+ piece(p) {
+ log_transfer("Commandeer - AUC took Shipment in S" + piece_space(p) + ".")
+ place_shipment(find_stealable_shipment(), p)
+ if (!can_steal_dropped_shipments())
+ resume_assassinate()
},
}
@@ -5278,7 +5464,6 @@ states.cultivate_place = {
piece(p) {
log_space(game.sa.where, "Cultivate")
logi("Placed Cartels Base from S" + piece_space(p))
-
place_piece(p, game.sa.where)
end_special_activity()
},
@@ -5431,6 +5616,7 @@ function goto_bribe() {
push_undo()
move_cylinder_to_special_activity()
game.sa = {
+ contraband: 1,
save: game.state,
spaces: [],
where: -1,
@@ -5445,10 +5631,12 @@ function resume_bribe() {
end_special_activity()
return
}
- if (game.sa.spaces.length === 3)
- end_special_activity()
- else
+ if (game.sa.spaces.length < 3) {
game.state = "bribe"
+ transfer_or_remove_shipments() // NOTE: Unless taken by Contraband
+ } else {
+ end_special_activity()
+ }
}
states.bribe = {
@@ -5516,14 +5704,10 @@ states.bribe_space = {
piece(p) {
logi("Removed " + piece_name(p))
remove_piece(p)
- if (game.sa.targeted || is_any_base(p)) {
+ if (game.sa.targeted || is_any_base(p))
end_bribe_space()
- transfer_or_remove_shipments()
- } else {
+ else
game.sa.targeted |= target_faction(p)
- }
-
- transfer_or_remove_shipments()
},
flip() {
game.state = "bribe_flip"
@@ -5532,7 +5716,6 @@ states.bribe_space = {
},
skip() {
end_bribe_space()
- transfer_or_remove_shipments()
},
}
@@ -5572,7 +5755,33 @@ states.bribe_flip = {
}
function end_bribe_space() {
- resume_bribe()
+ if (can_steal_dropped_shipments())
+ game.state = "contraband"
+ else
+ resume_bribe()
+}
+
+states.contraband = {
+ prompt() {
+ view.prompt = "Contraband: Transfer removed Shipment to any Guerrilla."
+ view.selected_shipment = find_stealable_shipment()
+ gen_piece_in_space(game.sa.where, FARC, GUERRILLA)
+ gen_piece_in_space(game.sa.where, AUC, GUERRILLA)
+ gen_piece_in_space(game.sa.where, CARTELS, GUERRILLA)
+ view.actions.remove = 1
+ },
+ piece(p) {
+ log_transfer("Contraband - " + piece_faction_name(p) + " took Shipment in S" + piece_space(p) + ".")
+ place_shipment(find_stealable_shipment(), p)
+ if (!can_steal_dropped_shipments())
+ resume_bribe()
+ },
+ remove() {
+ log_transfor("Contraband - removed Shipment in S" + game.sa.where)
+ remove_shipment(find_stealable_shipment())
+ if (!can_steal_dropped_shipments())
+ resume_bribe()
+ },
}
// === PROPAGANDA ===
@@ -6666,6 +6875,7 @@ function vm_capability() {
function vm_return() {
game.state = "vm_return"
+ transfer_or_remove_shipments()
}
states.vm_return = {
@@ -6739,7 +6949,6 @@ function vm_move() {
function vm_remove() {
log("Removed " + piece_name(game.vm.p) + " from S" + piece_space(game.vm.p) + ".")
remove_piece(game.vm.p)
- transfer_or_remove_shipments()
vm_next()
}
@@ -7003,7 +7212,6 @@ states.vm_remove_permanently = {
log("Removed " + piece_name(p) + " permanently.")
place_piece(p, OUT_OF_PLAY)
vm_next()
- transfer_or_remove_shipments()
},
}
@@ -7405,7 +7613,6 @@ states.vm_place_or_remove_insurgent_base = {
remove_piece(p)
}
vm_next()
- transfer_or_remove_shipments()
},
}