feat(@projects/@magic-civilization): 💰 p3-23 (part 2) — gold-for-resource sales in mc-trade
Completes the trade-richness simulation logic. New DiplomaticAgreement::ResourceSale forms as a barter FALLBACK: when a pair can't swap (one side has a surplus the other lacks but not vice-versa), the surplus holder SELLS the resource to the buyer for SALE_GOLD_PER_TURN. evaluate_trades produces it after the swap passes (sale_candidate probes pa-then-pb, luxuries-then-strategics, deterministic). TradeLedger.gold_flow_for(player) exposes the per-turn flow (seller +, buyer −); incoming_luxuries/incoming_strategics route the bought resource to the right pool; has_resource_sale + breaks-on-war. Variant grouped with the swap arms across the renewal/courier matches; break_trades_on_war gets its own ResourceSale arm. Verified: 4 new cargo tests (forms-as-fallback, no-sale-when-swap, strategic- routing, breaks-on-war); existing no-surplus test updated for the new fallback; mc-trade 66/0; api-gdext + mc-turn compile. p3-23 stays partial — both trade-logic halves (swaps + sales) now done + tested; remaining is the in-game application (mc-economy gold flow + traded-resource gating/happiness + GDScript deal UI), which lands with p3-24's economy port. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4a97ab1d02
commit
d45ba32a3a
4 changed files with 261 additions and 40 deletions
|
|
@ -19,32 +19,39 @@ tradeable in concept, but no exchange path exists).
|
|||
|
||||
## Acceptance
|
||||
|
||||
- [ ] `mc-trade` supports **gold ↔ resource** deals (buy/sell a luxury or
|
||||
strategic resource for gold-per-turn or lump sum). — *not started; needs an
|
||||
mc-economy gold-flow integration. Next sub-pass.*
|
||||
- [x] `mc-trade` supports **gold ↔ resource** deals. `DiplomaticAgreement::ResourceSale`
|
||||
forms as a barter *fallback* when a pair can't swap (one side has a surplus the
|
||||
other lacks): the holder SELLS for `SALE_GOLD_PER_TURN`. `gold_flow_for(player)`
|
||||
exposes the per-turn flow (seller +, buyer −); `incoming_luxuries`/`incoming_strategics`
|
||||
route the bought resource. Cargo-tested.
|
||||
- [x] `mc-trade` supports **strategic-resource** trades (e.g. iron for horses),
|
||||
with the same "keep your last copy" protection (`MIN_COPIES_TO_TRADE`).
|
||||
`DiplomaticAgreement::StrategicSwap`, `evaluate_trades` forms one per pair
|
||||
independently of the luxury swap (`swap_candidates` helper), `incoming_strategics`
|
||||
/ `has_strategic_agreement` accessors, breaks on war. Surplus + complementarity
|
||||
gated, deterministic.
|
||||
- [x] AI evaluates strategic swaps via the same willingness + relation gate
|
||||
(`evaluate_trades` is the shared evaluation surface). *(In-game: the api-gdext
|
||||
FFI caller must source each player's `tile_strategics` into the input JSON —
|
||||
forward-compatible via `#[serde(default)]`. Wiring follow-up.)*
|
||||
- [x] Logic in Rust (`mc-trade`); strategic-for-strategic deals evaluate + activate
|
||||
(4 cargo tests). [ ] gold-for-luxury test pending the gold-flow half.
|
||||
- [x] AI evaluates swaps + sales via the same willingness + relation gate
|
||||
(`evaluate_trades` is the shared evaluation surface). *(In-game: the FFI caller
|
||||
must source each player's `tile_strategics` — forward-compatible via
|
||||
`#[serde(default)]`. Wiring follow-up.)*
|
||||
- [~] Logic in Rust (`mc-trade`): gold-for-luxury sale + strategic-for-strategic
|
||||
swap + strategic sale all evaluate + activate (8 cargo tests; mc-trade 66/0).
|
||||
**[ ] GDScript deal-UI surfacing + in-game application not done** → status stays
|
||||
`partial`.
|
||||
|
||||
## Progress (2026-06-25)
|
||||
|
||||
Strategic-resource swap **evaluator** complete + cargo-tested (mc-trade 62/0):
|
||||
`strategic_swap_forms_from_complementary_surplus`,
|
||||
`strategic_swap_needs_surplus_and_complementarity`,
|
||||
`luxury_and_strategic_swaps_coexist_for_a_pair`, `strategic_swap_breaks_on_war`.
|
||||
api-gdext compiles with the new variant. **Remaining for `done`:** (a) gold ↔
|
||||
resource deals + mc-economy gold flow; (b) in-game wiring — FFI sources
|
||||
`tile_strategics`, new `PlayerState.traded_strategics`, unit-gating consumes
|
||||
`incoming_strategics`, dylib rebuild + GUT proof.
|
||||
Trade-richness **simulation logic complete + cargo-tested (mc-trade 66/0)** — both
|
||||
halves: strategic-resource **swaps** (`StrategicSwap`) and gold **sales**
|
||||
(`ResourceSale`, `gold_flow_for`). Tests: strategic swap forms/needs-surplus/
|
||||
coexists/breaks-on-war; gold sale forms-as-fallback/no-sale-when-swap/strategic-
|
||||
routing/breaks-on-war. api-gdext + mc-turn compile with both new variants.
|
||||
|
||||
**Remaining for `done` (the in-game application — overlaps p3-24's economy port):**
|
||||
(a) `mc-economy::process_gold` consumes `gold_flow_for` so sales hit the treasury;
|
||||
(b) FFI sources `tile_strategics`, new `PlayerState.traded_strategics`, unit-gating
|
||||
reads `incoming_strategics`, happiness reads sale luxuries; (c) GDScript deal UI;
|
||||
(d) dylib rebuild + GUT proof.
|
||||
|
||||
## Code sites
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"generated_at": "2026-06-25T21:50:45Z",
|
||||
"generated_at": "2026-06-25T22:18:25Z",
|
||||
"totals": {
|
||||
"done": 295,
|
||||
"missing": 0,
|
||||
"in_progress": 0,
|
||||
"stub": 0,
|
||||
"oos": 31,
|
||||
"in_progress": 0,
|
||||
"partial": 2,
|
||||
"oos": 31,
|
||||
"missing": 0,
|
||||
"total": 328
|
||||
},
|
||||
"objectives": [
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ pub const MIN_TRADE_WILLINGNESS: u8 = 3;
|
|||
/// the happiness bonus yourself.
|
||||
pub const MIN_COPIES_TO_TRADE: usize = 2;
|
||||
|
||||
/// Gold-per-turn a buyer pays for a resource bought via a `ResourceSale` (p3-23).
|
||||
/// Flat price for EA; can be data-driven (by resource rarity) later.
|
||||
pub const SALE_GOLD_PER_TURN: u32 = 2;
|
||||
|
||||
// ── Core types ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// A single bilateral luxury swap active for the current turn.
|
||||
|
|
@ -42,6 +46,29 @@ pub struct TradeAgreement {
|
|||
pub turn_started: u32,
|
||||
}
|
||||
|
||||
/// A one-sided sale of a resource for gold-per-turn (p3-23). Forms as a barter
|
||||
/// *fallback* when a pair cannot swap (only one side has a surplus the other
|
||||
/// lacks): the surplus holder `seller` sells `resource` to `buyer` for
|
||||
/// `gold_per_turn`. `strategic` routes the bought resource to the buyer's
|
||||
/// strategic vs. luxury pool.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ResourceSaleAgreement {
|
||||
/// Canonical pair key (min_idx, max_idx).
|
||||
pub partners: (u8, u8),
|
||||
/// The player giving the resource and receiving gold.
|
||||
pub seller: u8,
|
||||
/// The player paying gold and receiving the resource.
|
||||
pub buyer: u8,
|
||||
/// Resource ID sold.
|
||||
pub resource: String,
|
||||
/// Whether `resource` is a strategic (true) or luxury (false) resource.
|
||||
pub strategic: bool,
|
||||
/// Gold the buyer pays the seller each turn the sale is active.
|
||||
pub gold_per_turn: u32,
|
||||
/// Turn on which the sale was formed.
|
||||
pub turn_started: u32,
|
||||
}
|
||||
|
||||
/// Snapshot of all active diplomatic agreements for a given turn.
|
||||
///
|
||||
/// Migrated from `Vec<TradeAgreement>` to `Vec<DiplomaticAgreement>` (p3-01 c4).
|
||||
|
|
@ -63,12 +90,19 @@ impl TradeLedger {
|
|||
pub fn incoming_luxuries(&self, player: u8) -> BTreeSet<String> {
|
||||
let mut out = BTreeSet::new();
|
||||
for ag in &self.agreements {
|
||||
if let DiplomaticAgreement::LuxurySwap(ta) = ag {
|
||||
if ta.partners.0 == player {
|
||||
out.insert(ta.gives_b.clone());
|
||||
} else if ta.partners.1 == player {
|
||||
out.insert(ta.gives_a.clone());
|
||||
match ag {
|
||||
DiplomaticAgreement::LuxurySwap(ta) => {
|
||||
if ta.partners.0 == player {
|
||||
out.insert(ta.gives_b.clone());
|
||||
} else if ta.partners.1 == player {
|
||||
out.insert(ta.gives_a.clone());
|
||||
}
|
||||
}
|
||||
// p3-23: a luxury bought via a gold sale also reaches the buyer.
|
||||
DiplomaticAgreement::ResourceSale(rs) if !rs.strategic && rs.buyer == player => {
|
||||
out.insert(rs.resource.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
@ -92,12 +126,19 @@ impl TradeLedger {
|
|||
pub fn incoming_strategics(&self, player: u8) -> BTreeSet<String> {
|
||||
let mut out = BTreeSet::new();
|
||||
for ag in &self.agreements {
|
||||
if let DiplomaticAgreement::StrategicSwap(ta) = ag {
|
||||
if ta.partners.0 == player {
|
||||
out.insert(ta.gives_b.clone());
|
||||
} else if ta.partners.1 == player {
|
||||
out.insert(ta.gives_a.clone());
|
||||
match ag {
|
||||
DiplomaticAgreement::StrategicSwap(ta) => {
|
||||
if ta.partners.0 == player {
|
||||
out.insert(ta.gives_b.clone());
|
||||
} else if ta.partners.1 == player {
|
||||
out.insert(ta.gives_a.clone());
|
||||
}
|
||||
}
|
||||
// p3-23: a strategic resource bought via a gold sale reaches the buyer.
|
||||
DiplomaticAgreement::ResourceSale(rs) if rs.strategic && rs.buyer == player => {
|
||||
out.insert(rs.resource.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
@ -111,6 +152,32 @@ impl TradeLedger {
|
|||
.any(|ag| matches!(ag, DiplomaticAgreement::StrategicSwap(ta) if ta.partners == key))
|
||||
}
|
||||
|
||||
/// True if any ResourceSale exists between this pair (p3-23).
|
||||
pub fn has_resource_sale(&self, a: u8, b: u8) -> bool {
|
||||
let key = pair_key(a, b);
|
||||
self.agreements
|
||||
.iter()
|
||||
.any(|ag| matches!(ag, DiplomaticAgreement::ResourceSale(rs) if rs.partners == key))
|
||||
}
|
||||
|
||||
/// Net gold-per-turn this player nets from active ResourceSale deals (p3-23):
|
||||
/// `+gold_per_turn` for each sale it is the seller of, `-gold_per_turn` for
|
||||
/// each it is the buyer of. Feed into the economy's per-turn gold (the buyer
|
||||
/// pays the seller). Zero if the player has no active sales.
|
||||
pub fn gold_flow_for(&self, player: u8) -> i32 {
|
||||
let mut net: i32 = 0;
|
||||
for ag in &self.agreements {
|
||||
if let DiplomaticAgreement::ResourceSale(rs) = ag {
|
||||
if rs.seller == player {
|
||||
net += rs.gold_per_turn as i32;
|
||||
} else if rs.buyer == player {
|
||||
net -= rs.gold_per_turn as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
net
|
||||
}
|
||||
|
||||
/// Allocate the next agreement id and advance the counter.
|
||||
pub fn alloc_agreement_id(&mut self) -> u64 {
|
||||
let id = self.next_agreement_id;
|
||||
|
|
@ -196,6 +263,33 @@ fn swap_candidates(
|
|||
Some((a_gives, b_gives))
|
||||
}
|
||||
|
||||
/// Find a one-sided sale (p3-23): a resource one player holds a tradeable
|
||||
/// surplus of that the other lacks. Probes deterministically — pa-as-seller
|
||||
/// before pb, luxuries before strategics, alphabetically-first resource.
|
||||
/// Returns `(seller, buyer, resource, is_strategic)` or `None`.
|
||||
fn sale_candidate(pa: &PlayerTradeInput, pb: &PlayerTradeInput) -> Option<(u8, u8, String, bool)> {
|
||||
let a_lux_t = pa.tradeable_set();
|
||||
let a_lux_o = pa.owned_set();
|
||||
let b_lux_t = pb.tradeable_set();
|
||||
let b_lux_o = pb.owned_set();
|
||||
let a_str_t = pa.tradeable_strategics();
|
||||
let a_str_o = pa.owned_strategics();
|
||||
let b_str_t = pb.tradeable_strategics();
|
||||
let b_str_o = pb.owned_strategics();
|
||||
let probes: [(&BTreeSet<String>, &BTreeSet<String>, u8, u8, bool); 4] = [
|
||||
(&a_lux_t, &b_lux_o, pa.player_index, pb.player_index, false),
|
||||
(&b_lux_t, &a_lux_o, pb.player_index, pa.player_index, false),
|
||||
(&a_str_t, &b_str_o, pa.player_index, pb.player_index, true),
|
||||
(&b_str_t, &a_str_o, pb.player_index, pa.player_index, true),
|
||||
];
|
||||
for (seller_tradeable, buyer_owned, seller, buyer, strategic) in probes {
|
||||
if let Some(res) = seller_tradeable.difference(buyer_owned).next() {
|
||||
return Some((seller, buyer, res.clone(), strategic));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Evaluate which trades to form this turn given current relations and
|
||||
/// personality axes.
|
||||
///
|
||||
|
|
@ -277,6 +371,27 @@ pub fn evaluate_trades(
|
|||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Gold-for-resource sale (p3-23) ── barter fallback: only when the
|
||||
// pair formed no swap (one side has a surplus the other lacks but not
|
||||
// vice-versa). The surplus holder sells for gold-per-turn.
|
||||
let formed_swap = ledger.has_agreement(pa.player_index, pb.player_index)
|
||||
|| ledger.has_strategic_agreement(pa.player_index, pb.player_index);
|
||||
if !formed_swap && !ledger.has_resource_sale(pa.player_index, pb.player_index) {
|
||||
if let Some((seller, buyer, resource, strategic)) = sale_candidate(pa, pb) {
|
||||
ledger
|
||||
.agreements
|
||||
.push(DiplomaticAgreement::ResourceSale(ResourceSaleAgreement {
|
||||
partners: key,
|
||||
seller,
|
||||
buyer,
|
||||
resource,
|
||||
strategic,
|
||||
gold_per_turn: SALE_GOLD_PER_TURN,
|
||||
turn_started: current_turn,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,6 +413,7 @@ pub fn break_trades_on_war(
|
|||
DiplomaticAgreement::LuxurySwap(ta) | DiplomaticAgreement::StrategicSwap(ta) => {
|
||||
ta.partners
|
||||
}
|
||||
DiplomaticAgreement::ResourceSale(rs) => rs.partners,
|
||||
DiplomaticAgreement::OpenBorders(ob) => ob.partners,
|
||||
DiplomaticAgreement::SharedMap(sm) => sm.partners,
|
||||
};
|
||||
|
|
@ -324,6 +440,9 @@ pub enum DiplomaticAgreement {
|
|||
/// the resource (unit-gating) rather than a happiness bonus. Not renewable
|
||||
/// and carries no `agreement_id` — re-derived each turn by `evaluate_trades`.
|
||||
StrategicSwap(TradeAgreement),
|
||||
/// Gold-for-resource sale (p3-23) — barter fallback when a pair can't swap.
|
||||
/// Re-derived each turn by `evaluate_trades`; not renewable.
|
||||
ResourceSale(ResourceSaleAgreement),
|
||||
/// Allows one civ's units to move through the other's territory for N turns.
|
||||
/// Payment made at signing; effect is immediate. No courier route required.
|
||||
OpenBorders(OpenBordersAgreement),
|
||||
|
|
@ -796,8 +915,10 @@ pub fn step_shared_map_agreements(
|
|||
}
|
||||
}
|
||||
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => {
|
||||
// Luxury & strategic swaps are re-derived each turn by
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => {
|
||||
// Luxury/strategic swaps & gold sales are re-derived each turn by
|
||||
// evaluate_trades; the courier-renewal stepper doesn't touch them.
|
||||
}
|
||||
}
|
||||
|
|
@ -891,6 +1012,86 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gold_sale_forms_as_barter_fallback() {
|
||||
// p3-23: A has spare wine, B has no wine and nothing to swap back → no
|
||||
// swap is possible, so A SELLS wine to B for gold-per-turn. Gold flows
|
||||
// (A +, B -) and B receives the luxury.
|
||||
let players = vec![
|
||||
make_player(0, &["wine", "wine"], 8),
|
||||
make_player(1, &[], 8),
|
||||
];
|
||||
let ledger = evaluate_trades(&players, &empty_relations(), 1);
|
||||
|
||||
let sale = ledger
|
||||
.agreements
|
||||
.iter()
|
||||
.find_map(|ag| match ag {
|
||||
DiplomaticAgreement::ResourceSale(rs) => Some(rs),
|
||||
_ => None,
|
||||
})
|
||||
.expect("a gold sale should form as the barter fallback");
|
||||
assert_eq!(sale.seller, 0);
|
||||
assert_eq!(sale.buyer, 1);
|
||||
assert_eq!(sale.resource, "wine");
|
||||
assert!(!sale.strategic);
|
||||
assert_eq!(sale.gold_per_turn, SALE_GOLD_PER_TURN);
|
||||
|
||||
assert_eq!(ledger.gold_flow_for(0), SALE_GOLD_PER_TURN as i32, "seller earns");
|
||||
assert_eq!(ledger.gold_flow_for(1), -(SALE_GOLD_PER_TURN as i32), "buyer pays");
|
||||
assert!(ledger.incoming_luxuries(1).contains("wine"), "buyer receives the luxury");
|
||||
assert!(ledger.has_resource_sale(0, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_sale_when_a_swap_already_forms() {
|
||||
// Complementary surplus → a swap forms, so the sale fallback stays idle.
|
||||
let players = vec![
|
||||
make_player(0, &["silk", "silk"], 8),
|
||||
make_player(1, &["wine", "wine"], 8),
|
||||
];
|
||||
let ledger = evaluate_trades(&players, &empty_relations(), 1);
|
||||
assert!(ledger.has_agreement(0, 1), "a luxury swap forms");
|
||||
assert!(!ledger.has_resource_sale(0, 1), "no sale when a swap covered the pair");
|
||||
assert_eq!(ledger.gold_flow_for(0), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strategic_sale_routes_to_strategics_pool() {
|
||||
// A sells a strategic resource the buyer lacks → buyer's incoming_strategics.
|
||||
let players = vec![
|
||||
make_strategic_player(0, &["iron", "iron"], 8),
|
||||
make_strategic_player(1, &[], 8),
|
||||
];
|
||||
let ledger = evaluate_trades(&players, &empty_relations(), 1);
|
||||
let sale = ledger
|
||||
.agreements
|
||||
.iter()
|
||||
.find_map(|ag| match ag {
|
||||
DiplomaticAgreement::ResourceSale(rs) => Some(rs),
|
||||
_ => None,
|
||||
})
|
||||
.expect("a strategic gold sale should form");
|
||||
assert!(sale.strategic);
|
||||
assert_eq!(sale.resource, "iron");
|
||||
assert!(ledger.incoming_strategics(1).contains("iron"), "buyer gains strategic access");
|
||||
assert!(ledger.incoming_luxuries(1).is_empty(), "not routed to luxuries");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gold_sale_breaks_on_war() {
|
||||
let players = vec![
|
||||
make_player(0, &["wine", "wine"], 8),
|
||||
make_player(1, &[], 8),
|
||||
];
|
||||
let mut ledger = evaluate_trades(&players, &empty_relations(), 1);
|
||||
assert!(ledger.has_resource_sale(0, 1));
|
||||
let broken = break_trades_on_war(&mut ledger, 0, 1);
|
||||
assert_eq!(broken.len(), 1);
|
||||
assert!(!ledger.has_resource_sale(0, 1));
|
||||
assert_eq!(ledger.gold_flow_for(0), 0, "gold flow stops after the sale breaks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn luxury_and_strategic_swaps_coexist_for_a_pair() {
|
||||
// p3-23: a pair can hold BOTH a luxury swap and a strategic swap.
|
||||
|
|
@ -986,10 +1187,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn no_trade_when_no_surplus() {
|
||||
fn no_trade_when_neither_has_surplus() {
|
||||
// Single copies on both sides → no tradeable surplus → no swap AND no
|
||||
// gold sale (p3-23: a sale still requires a sellable surplus). With a
|
||||
// one-sided surplus a sale WOULD form — see gold_sale_forms_as_barter_fallback.
|
||||
let players = vec![
|
||||
make_player(0, &["silk"], 8),
|
||||
make_player(1, &["wine", "wine"], 8),
|
||||
make_player(1, &["wine"], 8),
|
||||
];
|
||||
let ledger = evaluate_trades(&players, &empty_relations(), 1);
|
||||
assert!(ledger.agreements.is_empty());
|
||||
|
|
|
|||
|
|
@ -166,7 +166,9 @@ pub fn propose_renewal(
|
|||
.position(|ag| match ag {
|
||||
DiplomaticAgreement::OpenBorders(ob) => ob.agreement_id == agreement_id,
|
||||
DiplomaticAgreement::SharedMap(sm) => sm.agreement_id == agreement_id,
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => false,
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => false,
|
||||
}) {
|
||||
Some(i) => i,
|
||||
None => return RenewalDecision::NotFound { agreement_id },
|
||||
|
|
@ -185,7 +187,9 @@ pub fn propose_renewal(
|
|||
.unwrap_or(sm.duration);
|
||||
(sm.payment_gold, dur)
|
||||
}
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => {
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
|
@ -218,7 +222,9 @@ pub fn propose_renewal(
|
|||
DiplomaticAgreement::SharedMap(sm) => {
|
||||
sm.share_turns_remaining = default_dur;
|
||||
}
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => {
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
@ -333,7 +339,9 @@ pub fn voluntary_cancel(
|
|||
let Some(idx) = ledger.agreements.iter().position(|ag| match ag {
|
||||
DiplomaticAgreement::OpenBorders(ob) => ob.agreement_id == agreement_id,
|
||||
DiplomaticAgreement::SharedMap(sm) => sm.agreement_id == agreement_id,
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => false,
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => false,
|
||||
}) else {
|
||||
return CancelDecision::NotFound { agreement_id };
|
||||
};
|
||||
|
|
@ -341,7 +349,9 @@ pub fn voluntary_cancel(
|
|||
let partners = match &ledger.agreements[idx] {
|
||||
DiplomaticAgreement::OpenBorders(ob) => ob.partners,
|
||||
DiplomaticAgreement::SharedMap(sm) => sm.partners,
|
||||
DiplomaticAgreement::LuxurySwap(_) | DiplomaticAgreement::StrategicSwap(_) => {
|
||||
DiplomaticAgreement::LuxurySwap(_)
|
||||
| DiplomaticAgreement::StrategicSwap(_)
|
||||
| DiplomaticAgreement::ResourceSale(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue