@@ -78,6 +78,24 @@ export interface GameConfig extends DifficultyConfig {
7878 // ── Challenges ──────────────────────────────────────────
7979 /** Number of challenges selected per run. */
8080 readonly challengesPerRun : number ;
81+ /** Multiplier to increase positive Incident frequency (1.0 = baseline). */
82+ readonly positiveIncidentMultiplier : number ;
83+
84+ // ── Reputation-based Coin Multiplier ───────────────────
85+ /**
86+ * Divisor used in the reputation coin multiplier formula:
87+ * multiplier = 1 + (reputation / reputationCoinDivisor)
88+ *
89+ * Higher values make reputation less impactful on coin rewards.
90+ * Default 20 means rep=20 yields a 2x multiplier.
91+ */
92+ readonly reputationCoinDivisor : number ;
93+ /**
94+ * Maximum value the reputation coin multiplier can reach.
95+ * Prevents runaway scaling in long or lucky games.
96+ * Default 3.0 means coin rewards can at most triple.
97+ */
98+ readonly maxReputationCoinMultiplier : number ;
8199}
82100
83101// ── Preset Definitions ──────────────────────────────────────
@@ -96,6 +114,9 @@ export const EASY_PRESET: Readonly<GameConfig> = {
96114 challengeBonusPoints : 15 ,
97115 synergyBonusPerNeighbor : 2 ,
98116 challengesPerRun : 2 ,
117+ positiveIncidentMultiplier : 1.2 ,
118+ reputationCoinDivisor : 20 ,
119+ maxReputationCoinMultiplier : 3.0 ,
99120} ;
100121
101122/**
@@ -112,6 +133,11 @@ export const MEDIUM_PRESET: Readonly<GameConfig> = {
112133 challengeBonusPoints : 10 ,
113134 synergyBonusPerNeighbor : 1 ,
114135 challengesPerRun : 3 ,
136+ // Increase positive incident frequency by 50% for the Medium baseline
137+ // as requested by work item CG-0MMLR20XP1IPPD03.
138+ positiveIncidentMultiplier : 1.5 ,
139+ reputationCoinDivisor : 20 ,
140+ maxReputationCoinMultiplier : 3.0 ,
115141} ;
116142
117143/**
@@ -128,6 +154,9 @@ export const HARD_PRESET: Readonly<GameConfig> = {
128154 challengeBonusPoints : 8 ,
129155 synergyBonusPerNeighbor : 1 ,
130156 challengesPerRun : 4 ,
157+ positiveIncidentMultiplier : 1 ,
158+ reputationCoinDivisor : 20 ,
159+ maxReputationCoinMultiplier : 3.0 ,
131160} ;
132161
133162// ── Preset Registry ─────────────────────────────────────────
@@ -156,3 +185,57 @@ export function getPreset(name: DifficultyName | undefined): Readonly<GameConfig
156185
157186/** All available difficulty names in display order. */
158187export const DIFFICULTY_NAMES : readonly DifficultyName [ ] = [ 'Easy' , 'Medium' , 'Hard' ] ;
188+
189+ // ── Reputation-based Coin Multiplier ────────────────────────
190+
191+ /**
192+ * Computes the reputation-based coin multiplier.
193+ *
194+ * Formula: min(1 + reputation / divisor, cap)
195+ *
196+ * Uses the additive formula so that reputation=0 still yields a 1x
197+ * baseline (no reward lost). The multiplier is capped to prevent
198+ * runaway scaling in long or lucky games.
199+ *
200+ * - reputation=0 -> 1.0x (baseline preserved)
201+ * - reputation=10 -> 1.5x (with divisor=20)
202+ * - reputation=20 -> 2.0x
203+ * - reputation=40 -> 3.0x (capped at maxMultiplier=3.0)
204+ * - reputation=60 -> 3.0x (capped)
205+ *
206+ * Negative reputation clamps the multiplier at 1.0 (no penalty via
207+ * this channel -- reputation collapse is handled elsewhere).
208+ *
209+ * @param reputation Current player reputation.
210+ * @param config Game config with reputationCoinDivisor and maxReputationCoinMultiplier.
211+ * @returns The multiplier to apply to coin rewards (always >= 1.0).
212+ */
213+ export function reputationCoinMultiplier (
214+ reputation : number ,
215+ config : Pick < GameConfig , 'reputationCoinDivisor' | 'maxReputationCoinMultiplier' > ,
216+ ) : number {
217+ if ( ! Number . isFinite ( reputation ) || reputation <= 0 ) return 1.0 ;
218+ const raw = 1 + reputation / config . reputationCoinDivisor ;
219+ return Math . min ( raw , config . maxReputationCoinMultiplier ) ;
220+ }
221+
222+ /**
223+ * Applies the reputation coin multiplier to a raw coin delta.
224+ *
225+ * Only positive coin deltas are scaled -- negative deltas (penalties)
226+ * pass through unchanged so that reputation does not amplify losses.
227+ * The result is rounded down (floored) to keep coin amounts integral.
228+ *
229+ * @param rawCoinDelta The base coin amount (positive = gain, negative = penalty).
230+ * @param reputation Current player reputation.
231+ * @param config Game config with multiplier tuning constants.
232+ * @returns The adjusted coin delta.
233+ */
234+ export function applyReputationMultiplier (
235+ rawCoinDelta : number ,
236+ reputation : number ,
237+ config : Pick < GameConfig , 'reputationCoinDivisor' | 'maxReputationCoinMultiplier' > ,
238+ ) : number {
239+ if ( rawCoinDelta <= 0 ) return rawCoinDelta ;
240+ return Math . floor ( rawCoinDelta * reputationCoinMultiplier ( reputation , config ) ) ;
241+ }
0 commit comments