Faction Relationship
Diplomacy is stored as directed edges between factions, typed as ally, at war, vassal, or liege.
Diplomacy is not a field on the Faction record. It is a separate table of directed edges, each carrying one of four types: ALLY, AT_WAR, VASSAL, or LIEGE.
Directed, not symmetric
Each row records a relationship from one faction to another. Two of the four types are inverses — A's VASSAL edge to B pairs with B's LIEGE edge to A — so a hierarchy is represented by two rows, not one.
The service enforces that pairing rather than assuming it. getLiege reads the faction's own LIEGE edge, then checks the reverse direction for a matching VASSAL edge before accepting the answer; getVassals filters symmetrically. A half-written hierarchy — one row present, its counterpart missing — resolves to no relationship at all rather than to a lopsided one.
Storing both directions also means "who are my allies?" and "who considers me an ally?" are the same cheap query.
Why relationships get an approval step
Because a relationship change binds two independent groups of players, it cannot be a unilateral command. Approval Request exists for exactly the three relationship transitions that need consent — war, alliance, and vassalisation.
Effect on the rest of the model
Relationships are read by more of the codebase than any other domain concept:
- Faction Power walks a faction's vassals to compute their contribution.
- Faction Flag settings such as
alliesCanInteractWithLandandvassalageTreeCanInteractWithLandturn relationship type into land access. - Combat rules consult war status before allowing PvP between factions.
Related
Vassalage covers the liege/vassal pair specifically, since it carries mechanical weight that alliance does not.