/** * Galaxy Strike Online โ€” NPC Reputation System (GDD ยง15.3) * Scale: -2000 to +2000 per faction * Factions: Federation, Pirate Syndicate, Merchant Guild, Rogue AI, Void Cult */ const FACTIONS = { federation: { name: 'Federation', icon: '๐ŸŒ', color: '#42a5f5', desc: 'The governing body of settled space.' }, pirate_syndicate:{ name: 'Pirate Syndicate', icon: '๐Ÿ’€', color: '#ef5350', desc: 'Organised raiders โ€” hostile by default.' }, merchant_guild: { name: 'Merchant Guild', icon: '๐Ÿ’ผ', color: '#ffd700', desc: 'Trade consortium controlling major routes.' }, rogue_ai: { name: 'Rogue AI Collective',icon: '๐Ÿค–', color: '#ab47bc', desc: 'Autonomous machines of unknown origin.' }, void_cult: { name: 'Void Cult', icon: '๐ŸŒ‘', color: '#78909c', desc: 'Enigmatic zealots from deep space.' }, }; const THRESHOLDS = [ { min: 1500, label: 'Exalted', color: '#ffd700' }, { min: 1000, label: 'Honored', color: '#4caf50' }, { min: 500, label: 'Friendly', color: '#66bb6a' }, { min: 1, label: 'Neutral', color: '#aaa' }, { min: -499, label: 'Unfriendly',color: '#ff9800' }, { min: -999, label: 'Hostile', color: '#f44336' }, { min: -2000,label: 'Hated', color: '#b71c1c' }, ]; function getStanding(rep) { for (const t of THRESHOLDS) if (rep >= t.min) return t; return THRESHOLDS[THRESHOLDS.length - 1]; } class ReputationSystem { initReputation(playerData) { if (playerData.reputation) return; const rep = {}; for (const id of Object.keys(FACTIONS)) { rep[id] = id === 'pirate_syndicate' ? -500 : 0; // Pirates start hostile } playerData.reputation = rep; } adjustReputation(playerData, factionId, delta, seasonBonuses = {}) { this.initReputation(playerData); if (!FACTIONS[factionId]) return; // Apply void cult season bonus if (factionId === 'void_cult' && seasonBonuses.voidCultRepBonus) { delta = Math.round(delta * (1 + seasonBonuses.voidCultRepBonus)); } const current = playerData.reputation[factionId] || 0; playerData.reputation[factionId] = Math.max(-2000, Math.min(2000, current + delta)); // Opposing factions lose rep when gaining with others const opposites = { federation: 'pirate_syndicate', pirate_syndicate: 'federation' }; const opp = opposites[factionId]; if (opp && delta > 0) { playerData.reputation[opp] = Math.max(-2000, (playerData.reputation[opp]||0) - Math.floor(delta * 0.5)); } return playerData.reputation[factionId]; } /** * Get active reputation bonuses to apply in other systems. * Returns { marketFeeDiscount, craftingSpeedBonus, pvpAttackBonus } */ getActiveEffects(playerData) { this.initReputation(playerData); const rep = playerData.reputation; const effects = { marketFeeDiscount: 0, craftingSpeedBonus: 0, pvpAttackBonus: 0 }; // Merchant Guild: market fee reduction const guildRep = rep['merchant_guild'] || 0; if (guildRep >= 1500) effects.marketFeeDiscount = 0.01; // Exalted: fee 2%->1% else if (guildRep >= 750) effects.marketFeeDiscount = 0.005; // Honored: fee 2%->1.5% // Rogue AI: crafting speed bonus const aiRep = rep['rogue_ai'] || 0; if (aiRep >= 1500) effects.craftingSpeedBonus = 0.20; // Exalted: +20% else if (aiRep >= 750) effects.craftingSpeedBonus = 0.10; // Honored: +10% // Pirate Syndicate: PvP attack bonus const pirateRep = rep['pirate_syndicate'] || 0; if (pirateRep >= 750) effects.pvpAttackBonus = 0.05; // Honored+: +5% return effects; } getReputationData(playerData) { this.initReputation(playerData); return Object.entries(FACTIONS).map(([id, f]) => { const rep = playerData.reputation[id] || 0; const standing = getStanding(rep); return { id, ...f, reputation: rep, standing: standing.label, standingColor: standing.color }; }); } getFactions() { return FACTIONS; } } module.exports = { ReputationSystem, FACTIONS };