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¶
Earned via MedalService (services/medal_service.py). Combat medals:
- Bronze Star — 100 victories.
- Silver Star — 1,000.
- Quantum Cross — 10,000.
🚧 Planned — economic / exploration / diplomatic / special medals (Trader's Merit, Pathfinder, Orange Cat Society…). Only combat medals are wired in medal_service.check_combat_medals.
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_bonusmodifies attack outcomes.
🐛 Bug —
combat_bonusnot applied.ranking_service.py:RANK_DEFINITIONSexposes a per-rankcombat_bonusvalue (+1 at Spacer up to +40 at Fleet Admiral), butcombat_service.py:attack_playerdoes not read it during damage resolution. The bonus is documented and stored but doesn't reach the dice. Wiring this into the attack roll is the design target.
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, and the player-clientranking/UI (RankDisplay, RankProgress, Leaderboard, MedalShowcase) renders against them. The full ranking service, REST routes, lifetime-stats table, and trading-bonus integration described above are 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 |
| 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)¶
| Score | Effect |
|---|---|
| ≤ −500 | +20% station price markup, bounty hunters target you |
| ≤ −250 | +10% station price markup |
| ≥ +250 | −5% station price discount |
| ≥ +500 | −10% station price 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.
🚧 Planned — auto-bounty placement at thresholds (−500 / −750 / −1000), bounty redemption-by-reform, and bounty-hunter NPC AI. Infrastructure exists (models/Bounty, BountyService); auto-placement and redemption flows are not all hooked up.
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 mission access, port pricing, and territory access for each NPC faction.
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.