Per-player reputation
Clamped integers bounded by configurable min / max / default values. Each player has their own score with each faction.
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.
| Field | Type | Default | Description |
|---|---|---|---|
id | String | Required | Registry key referenced by the reputation reward type and Molang. |
name | String | "" | Display name. Supports MiniMessage. |
description | String or String[] | [] | Single strings auto-wrap to a one-element list. |
icon | String | minecraft:paper | Content Book icon. |
default_reputation | Int | 0 | Starting value for players who have never interacted. |
min_reputation | Int | -10000 | Hard floor. |
max_reputation | Int | 10000 | Hard ceiling. |
ranks | Rank[] | [] | Sorted ascending by threshold on load. |
opposing_factions | OpposingFaction[] | [] | Reputation gain here decays these. |
hidden | Boolean | false | Hides from Content Book unless visibility_condition unhides it. |
visibility_condition | Molang | "" | Per-player expression. Falsy = hidden when hidden=true. |
| Field | Notes |
|---|---|
id | Must be unique within the faction. |
name | Display name shown in the rank-up toast. |
threshold | Minimum reputation to qualify. Ranks sort ascending on load. |
rewards | Polymorphic 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:
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.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:
| Function | Effect |
|---|---|
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.visibility_condition are filtered out; the rest evaluate their Molang condition per player.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:
reputation reward type on any quest, contract, or rank reward list.Three defaults ship in config/journey/factions/:
| File | Theme | Opposing |
|---|---|---|
rangers_guild.json | 7-rank protector guild | team_shadow at -0.5 |
team_shadow.json | 7-rank villain faction | rangers_guild at -0.5 |
merchants_consortium.json | 7-rank neutral trader network | none |
All three use default_reputation: 0, bounds [-10000, 10000], and hidden: false.
0 for an unknown id, but the stored value survives in player data.+1 with ratio: -0.5 decays the opposing faction by -1. Adding -1 with the same ratio leaves it alone.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.opposing_factions. Create real narrative choices — gaining with one side should cost something with the other.default_reputation at 0 unless a new player should start with a specific relationship (a reviled villain group, an allied home guild).visibility_condition. q.player.has_completed_task('journey:intro_quest_5') keeps unstarted storylines out of the Content Book.reputation_rank conditions for task visibility. Gate quests on "honored" or better without hardcoding threshold numbers.