Ranking & Reputation¶
Two parallel progression tracks:
- Military Rank — what you've done. Achievement-based, never lost.
- Personal Reputation — how you've behaved. Alignment scale that swings with combat actions.
Plus a separate per-faction reputation (covered in factions-and-teams.md).
Military Rank¶
Tiers and thresholds¶
18 ranks across five tiers (services/ranking_service.py:RANK_DEFINITIONS).
| Level | Tier | Rank | Points required | Trading bonus | Combat bonus | Max-turns bonus |
|---|---|---|---|---|---|---|
| 0 | Enlisted | Recruit | 0 | 0% | 0 | 0 |
| 1 | Enlisted | Spacer | 50 | +2% | +1 | +5 |
| 2 | Enlisted | Corporal | 150 | +3% | +2 | +10 |
| 3 | NCO | Sergeant | 300 | +5% | +4 | +15 |
| 4 | NCO | Staff Sergeant | 500 | +6% | +5 | +20 |
| 5 | NCO | Master Sergeant | 800 | +7% | +6 | +25 |
| 6 | Warrant | Warrant Officer | 1,200 | +8% | +8 | +30 |
| 7 | Warrant | Chief Warrant Officer | 1,800 | +10% | +10 | +35 |
| 8 | Officer | Ensign | 2,500 | +12% | +12 | +40 |
| 9 | Officer | Lieutenant | 3,500 | +15% | +14 | +45 |
| 10 | Officer | Commander | 5,000 | +18% | +16 | +50 |
| 11 | Officer | Captain | 7,000 | +20% | +18 | +55 |
| 12 | Officer | Senior Captain | 10,000 | +22% | +20 | +60 |
| 13 | Flag | Commodore | 14,000 | +25% | +22 | +70 |
| 14 | Flag | Rear Admiral | 20,000 | +30% | +25 | +80 |
| 15 | Flag | Vice Admiral | 28,000 | +35% | +28 | +90 |
| 16 | Flag | Admiral | 40,000 | +40% | +32 | +100 |
| 17 | Flag | Fleet Admiral | 60,000 | +50% | +40 | +120 |
Achievement gates beyond points¶
Some ranks require minimum stat thresholds in addition to points (RANK_REQUIREMENTS):
| Rank | Extra requirement |
|---|---|
| Sergeant | 25 trades, 50 sectors visited |
| Staff Sergeant | 50 trades, 10 combat victories |
| Master Sergeant | 100 trades, 25 combat victories, 100 sectors |
| Warrant Officer | 50 combat victories, 200 trades |
| Ensign | 100 combat victories, 1 planet owned |
| Lieutenant | 500 trades, 200 combat victories |
| Commander | 3 planets owned, 500 combat victories |
| Captain | 1,000 trades, 1,000 combat victories, 5 planets owned |
Earning rank points¶
Valid award reasons (VALID_REASONS):
- combat_victory — points scaled by relative rank of attacker vs defender (RankingService.calculate_combat_points).
- trading_volume — proportional to trade revenue.
- exploration — first-discovery of sectors.
- colony_establishment — claiming a planet.
- admin_grant — manual.
The combat hook in combat_service.attack_player calls:
ranking_service.award_rank_points(winner_id, points, "combat_victory")
Ranks are permanent¶
Once promoted, a player stays at that rank even if their stats fluctuate. Promotion is checked on every point award.
Old-save rank-name compatibility¶
LEGACY_RANK_MAP translates older save data to current rank IDs:
- Private → Recruit
- General → Admiral
- Major → Commander
- Colonel → Commodore
Special medals¶
Medals are the horizontal achievement layer that sits alongside the linear rank progression. The full catalog (combat / economic / exploration / diplomatic / special), award lifecycle, the Orange Cat Society lore, and the privacy model live in dedicated docs:
- Catalog + lore + UX surfaces:
./medals.md - Award dispatcher + schema + realtime events:
../../SYSTEMS/medal-service.md - Schema decision (association table over JSONB): ADR-0028
The three combat-tier medals (Bronze Cluster at 100 victories, Silver Cluster at 1,000, Quantum Cross at 10,000) are the only ones wired into code today via MedalService.check_combat_medals (services/medal_service.py). The rest of the catalog is 📐 Design-only.
Player-facing display¶
- Insignia next to player name in chat, leaderboards, team rosters.
- Trading bonus is applied at port transactions.
max_turns_bonusraises the player's turn cap (and the natural turn-pool ceiling).combat_bonusis a percent scalar applied to base damage in the canonical damage stack (base_damage × (1 + combat_bonus / 100), per../../SYSTEMS/combat-resolver.md#damage-stack-order-of-operations). Values run +1% at Spacer up to +40% at Fleet Admiral.
✅ Shipped —
combat_bonusapplied.combat_service.attack_playerreadsRankingService.get_rank_bonuses(attacker.military_rank)and applies the per-rankcombat_damage_bonus_percentasattacker_damage_mult = 1 + percent/100in the damage-roll step, per ADR-0061 S-D2.
Mid-combat rank promotion deferral (per ADR-0061 S-I4): when rank_points crosses a rank threshold during an active combat, the promotion is queued and fires at combat end. The combat_bonus value used throughout the fight is the pre-combat rank's bonus (snapshot at combat init). No mid-combat stat changes; combat math stays internally consistent round-to-round; ARIA narrates the rank-up after the fight resolves.
Source map¶
| Topic | Path |
|---|---|
| Rank tables, promotion logic | services/gameserver/src/services/ranking_service.py |
| Player rank fields | services/gameserver/src/models/player.py (military_rank, rank_points) |
| Ranking REST routes | services/gameserver/src/api/routes/ranking.py |
| Medal awards | services/gameserver/src/services/medal_service.py |
| Awarding hook | services/gameserver/src/services/combat_service.py |
Status: 🚧 Partial —
Player.military_rankandPlayer.rank_pointscolumns exist, the player-clientranking/UI (RankDisplay, RankProgress, Leaderboard, MedalShowcase) renders against them,ranking_service.pyis live behindroutes/ranking.py(rank / leaderboard / progress / medals / reputation / bounties), andtrading.pyawards rank points on buy and sell. The lifetime-stats table described above is the remaining gap, not yet plumbed end-to-end.
Personal Reputation¶
Tracks moral alignment — separate from faction standing. Eight tiers across the −1000 to +1000 range. Used to drive name color, bounty system, and station-pricing modifiers.
Tiers (personal_reputation_service.py:REPUTATION_TIERS)¶
| Score range | Tier | Name color (hex) |
|---|---|---|
| −1000 to −750 | Villain | #FF0000 |
| −749 to −500 | Criminal | #FF4400 |
| −499 to −250 | Outlaw | #FF8800 |
| −249 to −1 | Suspicious | #FFCC00 |
| 0 | Neutral | #FFFFFF |
| +1 to +249 | Lawful | #88FF88 |
| +250 to +499 | Heroic | #00FF00 |
| +500 to +1000 | Legendary | #00FFFF |
Triggers (REPUTATION_TRIGGERS)¶
| Action | Δ | Reason key |
|---|---|---|
| Attack a player with no bounty | −100 | attack_innocent |
| Kill a player in an escape pod | −500 | kill_escape_pod |
| Salvage another player's Cargo Wreck during the 1-hour grace window (non-team-member) | −25 | early_salvage (📐) |
| Pilot a ship reported stolen, per real-time day | −100 | pilot_stolen_ship (📐) |
| Caught at port docking with a stolen ship | −100 | stolen_ship_impound (📐) |
| Successfully defend against an attacker | +50 | defend_against_attacker |
| Defeat a player with active bounty | +100 | defeat_bounty_target |
| Complete legitimate trade | +1 | complete_trade |
| Destroy hostile sector drones | +10 | destroy_pirate_drones |
Adjustments are clamped to [−1000, +1000]. The service writes both the new score and the cached reputation_tier / name_color columns on Player.
Gameplay effects (get_reputation_info)¶
Mirrors the per-tier bracket table in ../../SYSTEMS/bounty-and-reputation.md#reputation-tier-table. Bracket boundaries match the tier table above:
| Score range | Tier | Effect |
|---|---|---|
| [−1000, −750] | Villain | +20% station markup, bounty hunters target |
| [−749, −500] | Criminal | +20% station markup |
| [−499, −250] | Outlaw | +10% station markup |
| [−249, −1] | Suspicious | (no station effect) |
| [0, 0] | Neutral | (no station effect) |
| [+1, +249] | Lawful | −5% station discount |
| [+250, +499] | Heroic | −5% station discount, +5% faction standing bonus |
| [+500, +1000] | Legendary | −10% station discount, +5% faction standing bonus |
Decay¶
apply_weekly_decay pulls extreme values back toward 0 by 5 points per week (positive scores decrement, negative scores increment). Once a player hits Neutral, decay stops.
Bounty interaction¶
When an attacker wins combat, BountyService.collect_bounty(attacker_id, defender_id) runs. If bounties exist, attacker collects the pool (minus a system fee) and gains reputation; if not, the attacker is treated as having attacked an innocent and loses reputation. See bounties.md for the full bounty system.
Suspect status (temporary flag)¶
Distinct from the persistent personal_reputation integer above, Suspect Status is a temporary boolean flag on Player that fires from specific actions and auto-clears on a timer. Currently the only trigger is early-window Cargo Wreck salvage (see ships.md → Cargo Wreck).
When Player.suspect_status = True:
- Name color shifts to gray/yellow across all UI surfaces (overrides the
personal_reputationtier color for the duration of the flag). - Federation-zone immunity is suspended — anyone may attack the flagged player in fed-space without the normal policing penalties; the flagged player fights back without fed-space combat-cost penalties.
- Flag auto-clears at
Player.suspect_until.
Stacking rules:
- Each suspect-triggering event sets suspect_until = max(suspect_until, now + 1h) — i.e., extends the timer to 1 hour from now if that's later than the current expiry.
- Maximum cumulative duration: 4 hours from the first triggering event. Even chronic offenders cap at 4 hours of flag.
- After clear, future events restart the cycle.
📐 Design-only — Player.suspect_status and Player.suspect_until columns are launch-target additions; not in code today.
Wanted status¶
A stronger flag than Suspect, fired by piloting a ship the registered owner has reported stolen (see ../../SYSTEMS/ship-registry.md). While Player.wanted_status = True:
- Name color renders red in all UI surfaces (overrides personal-reputation tier color and Suspect Status if both are active).
- Federation-zone immunity is suspended — anyone may attack a Wanted player in fed-space without normal policing penalties.
- NPC patrols actively hunt the player (📐 design-only — patrol AI hooks).
- An automatic bounty equal to 50% of the ship's appraised value is auto-placed on the Wanted player by the registry. See
bounties.md. - Daily personal-reputation drain —
−100 / daywhile still piloting the stolen ship. - Port-docking impound — at any port, attempting to dock with a stolen ship results in:
- Ship impounded; returned to registered owner (held at port for collection).
- Pilot fined 25% of credits (capped at 100k cr).
- Pilot personal-reputation hit
−100(separate from the daily drain). - Pilot ejected to escape pod.
Wanted Status auto-clears the moment the pilot is no longer in the stolen ship — eject voluntarily, ship destroyed, ship impounded, or registered owner retracts the stolen report.
📐 Design-only — Player.wanted_status boolean is a launch-target addition; not in code today.
✅ Shipped — system bounty auto-placement at thresholds (−500 / −750 / −1000) is live: bounty_service.SYSTEM_BOUNTY_TIERS + _get_system_bounties emit virtual bounties consumed by get_bounties_on_player and collect_bounty. 🚧 Planned — bounty-hunter NPC AI to actively hunt flagged players.
Source map¶
| Topic | Path |
|---|---|
| Tier table, adjustment logic | services/gameserver/src/services/personal_reputation_service.py |
| Player columns | services/gameserver/src/models/player.py (personal_reputation, reputation_tier, name_color) |
| Bounty service | services/gameserver/src/services/bounty_service.py |
| Combat hook | services/gameserver/src/services/combat_service.py |
How rank, personal reputation, and faction standing interact¶
These three are independent:
- Military Rank can rise even while you're a Villain (the Admiral Darkstar pattern).
- Personal Reputation sets your name color and bounty status — visible to everyone in any sector.
- Faction Standing (per-faction, see factions-and-teams.md) determines port pricing, patrol response, and territory access for each NPC faction. Movement is purely emergent — see
./factions-and-teams.md#reputation-triggers.
A player with high rank, low personal reputation, and Allied Federation standing is possible — they're feared by other players but still the Federation's top mercenary.