Skip to content

Factions

Factions are configurable groups with per-player reputation, named ranks, and opposing-faction decay. Players gain or lose reputation via quest rewards or Molang calls, and climb through ranks that unlock rewards along the way.

New in Beta 2   Factions are loaded from config/journey/factions/.

Per-player reputation

Clamped integers bounded by configurable min / max / default values. Each player has their own score with each faction.

Named ranks with rewards

A sorted rank ladder where each rank unlocks the next with a reputation threshold and runs a reward list on first-time unlock.

Opposing decay

Gain with one faction and rivals take proportional damage. Default ratio is -0.5. Perfect for forcing narrative choices.

Visibility gating

hidden: true + a Molang visibility_condition keeps unstarted storylines out of the Content Book until the player qualifies.


Here’s a ranger guild with seven ranks and an opposing shadow faction:

File: config/journey/factions/rangers_guild.json

{
"id": "rangers_guild",
"name": "<green>Rangers Guild",
"description": [
"<gray>Protectors of the wilderness and",
"<gray>guardians of Pokemon safety."
],
"icon": "minecraft:oak_sapling",
"default_reputation": 0,
"min_reputation": -10000,
"max_reputation": 10000,
"hidden": false,
"ranks": [
{ "id": "hostile", "name": "Hostile", "threshold": -5000, "rewards": [] },
{ "id": "neutral", "name": "Neutral", "threshold": 0, "rewards": [] },
{ "id": "friendly", "name": "Friendly", "threshold": 1000,
"rewards": [
{ "type": "command", "data": { "command": "give {player} minecraft:bread 8" } }
]
},
{ "id": "honored", "name": "Honored", "threshold": 3000,
"rewards": [
{ "type": "currency", "data": { "currency": "impactor:pokedollars", "amount": 5000 } }
]
},
{ "id": "exalted", "name": "Exalted", "threshold": 7000, "rewards": [] }
],
"opposing_factions": [
{ "faction": "team_shadow", "ratio": -0.5 }
]
}

Reputation gained here decays Team Shadow’s reputation by half. Hit 1000 with the rangers and a loaf of bread hits your inventory. Hit 3000 and you’re honored for 5000 Pokedollars.


FieldTypeDefaultDescription
idStringRequiredRegistry key referenced by the reputation reward type and Molang.
nameString""Display name. Supports MiniMessage.
descriptionString or String[][]Single strings auto-wrap to a one-element list.
iconStringminecraft:paperContent Book icon.
default_reputationInt0Starting value for players who have never interacted.
min_reputationInt-10000Hard floor.
max_reputationInt10000Hard ceiling.
ranksRank[][]Sorted ascending by threshold on load.
opposing_factionsOpposingFaction[][]Reputation gain here decays these.
hiddenBooleanfalseHides from Content Book unless visibility_condition unhides it.
visibility_conditionMolang""Per-player expression. Falsy = hidden when hidden=true.
FieldNotes
idMust be unique within the faction.
nameDisplay name shown in the rank-up toast.
thresholdMinimum reputation to qualify. Ranks sort ascending on load.
rewardsPolymorphic reward list — same types as tasks.
{ "faction": "team_shadow", "ratio": -0.5 }

The ratio is multiplied against the reputation change and applied to the listed faction. Default is -0.5 when omitted. Mutually-opposing factions are safe — Journey tracks which factions have already been touched in a single change and won’t revisit them.


Factions plug into the task / contract reward system via the reputation reward type:

{
"type": "reputation",
"data": {
"faction": "rangers_guild",
"amount": 100
}
}

The amount is signed — pass negatives to subtract reputation. Rank-up rewards, notifications, and opposing decay all run automatically when this reward fires.


There are two ways to change reputation, and they behave differently:

  • Gameplay path (q.player.add_reputation or the reputation reward) — clamps, evaluates rank changes, runs rank-up rewards on first unlock, sends the rank-up toast, and cascades decay to opposing_factions.
  • Admin path (q.player.set_reputation) — hard clamps and writes. No rank evaluation, no rewards, no toast, no decay. Use it for resets and admin seeding.

Use these from task filters, NPC dialogue, zone scripts, and cutscenes:

FunctionEffect
q.player.reputation(factionId)Read; returns default_reputation if unknown.
q.player.reputation_rank(factionId)Current rank id, or empty string.
q.player.add_reputation(factionId, amount)Gameplay path; returns new value.
q.player.set_reputation(factionId, amount)Admin path; returns clamped value.
q.player.faction_rank_index(factionId)Current rank index, or -1.
q.player.faction_count()Total registered factions.

Two reputation conditions also exist for task / trigger gating:

  • reputation — compare a faction’s reputation to a numeric value.
  • reputation_rank — compare the current rank id to a target string.

  • List page — the Content Book Factions tab. Hidden factions with no visibility_condition are filtered out; the rest evaluate their Molang condition per player.
  • Detail page — click a faction to open a 6-row detail view with a header (icon, info, current rank, progress bar) and a full rank grid.

The rank progress bar uses a linear ratio between the player’s current rank threshold and the next. Max-rank players show as full.


There is no dedicated /journey faction subcommand in this build. Reputation is manipulated via:

  • The reputation reward type on any quest, contract, or rank reward list.
  • Molang calls from NPC dialogue, scripts, or cutscenes.

Three defaults ship in config/journey/factions/:

FileThemeOpposing
rangers_guild.json7-rank protector guildteam_shadow at -0.5
team_shadow.json7-rank villain factionrangers_guild at -0.5
merchants_consortium.json7-rank neutral trader networknone

All three use default_reputation: 0, bounds [-10000, 10000], and hidden: false.


  • Faction deleted while a player has reputation — reads return 0 for an unknown id, but the stored value survives in player data.
  • Two ranks with the same threshold sort stably ascending; whichever appears later in the list wins when resolving the current rank.
  • Decay rounds toward negative infinity. Adding +1 with ratio: -0.5 decays the opposing faction by -1. Adding -1 with the same ratio leaves it alone.
  • Empty faction id on a reputation reward logs a warning and no-ops.
  • hidden: true + visibility_condition — the condition takes precedence. A hidden faction with a valid expression still appears when the expression resolves true.

  • Use round-number thresholds. Players track rank progress more easily when the numbers are human-readable: 0, 1000, 3000, 7000.
  • Pair factions via opposing_factions. Create real narrative choices — gaining with one side should cost something with the other.
  • Keep default_reputation at 0 unless a new player should start with a specific relationship (a reviled villain group, an allied home guild).
  • Gate story factions with visibility_condition. q.player.has_completed_task('journey:intro_quest_5') keeps unstarted storylines out of the Content Book.
  • Use reputation_rank conditions for task visibility. Gate quests on "honored" or better without hardcoding threshold numbers.