Shop Combat & Karma System
Shop Combat & Karma System
Status: Designed Author: Nathan + Claude Last Updated: 2026-04-07 Implements Pillar: Risk/reward decisions, moral choice, replayability
Overview
Players can choose to rob the merchant at any shop node instead of buying. This triggers a unique combat encounter against the Merchant (and bodyguards at higher karma debt). Winning gives all shop items for free. Losing ends the run. A persistent karma system tracks robberies across runs, escalating consequences: higher prices, bodyguards, locked items, and altered narrative. Redemption is possible through legitimate purchases and shrine atonement.
The system adds a high-stakes moral dimension to every shop visit — do you play fair and invest gold, or risk everything for free loot?
Player Fantasy
The temptation of the shortcut. Every shop visit becomes a tense decision: "I could just take it all." The player who robs feels the thrill of getting away with it — but the creeping dread of consequences. The player who resists feels virtuous, but always wonders "what if?" The player deep in karma debt feels hunted — the merchant remembers, and the world pushes back.
Reference: Undertale's genocide route (consequences persist), Slay the Spire's risk/reward node choices, Balatro's "do I push my luck" tension.
Detailed Design
Core Flow
At any shop node, the player sees three options:
- Buy — normal shop behavior (browse, purchase with gold)
- Rob — initiate combat against the Merchant
- Leave — exit shop, proceed to next floor (no purchase, no fight)
Robbery Combat
When the player clicks "Rob":
- Warning dialog: "The Merchant won't go quietly. Are you sure?" with karma indicator
- If confirmed, transition to combat scene with the Merchant (+ bodyguards based on karma)
- Win: all shop items (relics, consumables, heal) added to player inventory
- Relics respect the 8-relic cap — if full, swap picker shown
- Consumables respect slot limit — excess discarded
- Heal option auto-applied
- Karma decreases by 10
- Lose: run ends (game over flow, skill points still awarded)
- During combat, player can Flee (replaces "Score" button on turn 1 only)
- Flee = leave shop with nothing, no karma change, merchant remembers (dialogue only)
The Merchant (Enemy)
Unique enemy not in the normal pool. The Merchant is a defensive fighter — shields up, dirty tricks, calls for help.
| Stat | Act 1 | Act 2 | Act 3 |
|---|---|---|---|
| HP | 80 | 120 | 160 |
| Shield/turn | 8 | 12 | 16 |
| Turns allowed | 5 | 5 | 4 |
| Tier | ELITE | ELITE | ELITE |
Merchant Interference Actions (unique to this enemy):
| Action | Effect | Weight |
|---|---|---|
| POCKET_SAND | Blind player for 1 turn (hides enemy intent) | 20% |
| STEAL_GOLD | Player loses 5-15 gold | 25% |
| PRICE_CURSE | Next shop in this run: prices +30% | 15% |
| DEFEND | Gain shield (8/12/16 by act) | 25% |
| SMOKE_BOMB | Scramble 2 dice to random 1-3 (like CHAOS but fewer dice) | 15% |
Merchant Passive — "Coin Purse": Immune to Burn (can't set a merchant on fire — bad for business). First 3 damage per hit absorbed (like Thick Hide but weaker).
Merchant Sprite: Hooded figure, uses existing shop_bg merchant silhouette style.
Bodyguards (Karma-Scaled)
At higher karma debt, the Merchant hires protection:
| Karma | Bodyguards | Type |
|---|---|---|
| 0 to -9 (first robbery) | None | Solo merchant |
| -10 to -19 (2 robberies) | 1 Thug | Basic enemy, 40 HP, LOCK_HOLD + ATTACK |
| -20 to -29 (3 robberies) | 1 Enforcer | Elite enemy, 70 HP, STEAL_REROLL + HEAVY_ATTACK |
| -30+ (4+ robberies) | 1 Enforcer + 1 Thug | Both present, merchant gets +2 attack |
Bodyguards use existing enemy sprites (pick from basic/elite pool) with "Hired" prefix in display name.
Karma System
Persistence: Stored in user://karma.save, independent of run data (survives across runs like skill tree).
Karma Value: Integer, starts at 0. Negative = criminal, positive = virtuous.
| Action | Karma Change |
|---|---|
| Rob the merchant (win) | -10 |
| Rob the merchant (flee) | 0 (no change) |
| Buy any item at full price | +1 |
| Buy any item at Merchant Prince discount | +1 (still counts) |
| Shrine Atonement (pay 50g) | +15 |
| Complete a run without robbing | +2 |
Karma Consequences (applied at shop generation):
| Karma Range | Effect |
|---|---|
| +10 or higher | "Loyal Customer" — merchant occasionally offers 1 item at 20% discount |
| 0 to +9 | Normal (no modifier) |
| -1 to -9 | Prices +15%. Merchant dialogue: wary |
| -10 to -19 | Prices +30%. 1 Thug bodyguard. Merchant dialogue: hostile |
| -20 to -29 | Prices +50%. 1 Enforcer bodyguard. 1 random item locked (can't buy). Dialogue: threatening |
| -30 or lower | Prices +75%. 1 Enforcer + 1 Thug. 2 items locked. Merchant is armed (+2 attack). Dialogue: "You again…" |
Redemption: Karma naturally drifts toward 0 through legitimate play. A player at -30 karma needs ~30 purchases or 2 shrine atonements to return to neutral. This takes 5-8 runs of honest shopping.
Shrine Atonement
When karma < -5, shrines gain an additional blessing option:
- "Atonement": Pay 50 gold. Karma +15. Flavor text: "The shrine absolves a fraction of your sins."
- Appears alongside normal shrine blessings (heal, reroll, consumable)
- Only available when karma is negative
Narrative Integration
Merchant Dialogue changes based on karma (shown as flavor text at top of shop screen):
| Karma | Dialogue Pool |
|---|---|
| +10+ | "Ah, my favorite customer! I saved something special for you." |
| 0 to +9 | "Welcome, traveler. Browse at your leisure." |
| -1 to -9 | "…Keep your hands where I can see them." |
| -10 to -19 | "I've hired protection. Try anything and you'll regret it." |
| -20 to -29 | "You. I know what you are. My enforcer is watching." |
| -30+ | "Back for more? This time I'm ready. draws weapon" |
First Robbery Cutscene (plays once via CutsceneManager):
- "The merchant's eyes widen as you reach for your dice. This isn't a negotiation."
- After victory: "You gather the merchant's wares. Somewhere, a ledger records your debt."
Karma Warning on Game Over/Victory:
- If karma < -10, defeat screen shows: "The merchant's guild will remember this."
- If karma < -20, victory screen shows: "Your reputation precedes you. Merchants whisper your name."
States and Transitions
SHOP_ENTER
|-- Player browses items --> SHOPPING (normal flow)
|-- Player clicks "Rob" --> ROBBERY_WARNING
|-- Player clicks "Leave" --> MAP (no purchase)
ROBBERY_WARNING
|-- Player confirms --> ROBBERY_COMBAT
|-- Player cancels --> SHOPPING
ROBBERY_COMBAT
|-- Player wins --> LOOT_ALL (items added, karma -10)
|-- Player loses --> GAME_OVER
|-- Player flees (turn 1 only) --> MAP (nothing gained, dialogue noted)
LOOT_ALL
|-- Relic cap check --> SWAP_PICKER (if at cap) or MAP
Interactions with Other Systems
| System | Interface | Direction |
|---|---|---|
| Combat | Reuses DiceCombatManager with Merchant enemy data | This → Combat |
| Shop | Rob button added to shop UI, price modifier from karma | This ↔ Shop |
| Economy | Gold stolen by STEAL_GOLD, prices modified by karma | This → Economy |
| Persistence | karma.save file, loaded on game start | This ↔ Disk |
| Shrine | Atonement blessing option when karma < -5 | This → Shrine |
| Narrative | Merchant dialogue pool, robbery cutscene, karma warnings | This → Narrative |
| Achievement | New achievements for robbery/redemption | This → Achievements |
| Wanderer Archetype | "Shops always have relic" still applies to robbery loot | Archetype → This |
Formulas
Merchant HP Scaling
merchant_hp = BASE_MERCHANT_HP[act] + abs(karma) * 2
Where BASE_MERCHANT_HP = {1: 80, 2: 120, 3: 160}.
At -30 karma, Act 3 merchant has 160 + 60 = 220 HP (approaching boss territory).
Price Modifier
karma_price_mult = 1.0 + max(0, abs(min(karma, 0))) * 0.025
At karma 0: 1.0x. At -10: 1.25x. At -20: 1.5x. At -30: 1.75x.
Capped at 2.0x (karma -40 or lower).
Merchant Shield Per Turn
shield_per_turn = BASE_SHIELD[act] + max(0, abs(min(karma, 0)) / 10) * 4
At karma -30, Act 1: 8 + 12 = 20 shield/turn.
Loot Value
total_loot_value = sum(item_prices_in_shop)
Typically 200-500g worth of items per shop. This is the risk/reward calculation: spend 0g for ~300g worth of items, but risk game over + karma debt.
Karma Decay (Natural Drift)
Karma does NOT auto-decay. It only changes through player actions (buy, rob, atone). This means consequences are permanent until actively redeemed.
Edge Cases
| Situation | Resolution |
|---|---|
| Rob at relic cap (8/8) | Swap picker shown for each relic. Can skip individual relics. |
| Rob with full consumable slots | Excess consumables discarded. Player warned before combat. |
| Rob with 0 gold and STEAL_GOLD triggers | Gold can't go negative. Merchant steals 0 but action still plays. |
| Wanderer + Rob | Wanderer's "shops always have relic" still applies — loot includes the guaranteed relic. |
| Merchant Prince keystone + karma prices | Merchant Prince discount applies AFTER karma price increase. At -20 karma + Merchant Prince: 1.5x * 0.7 = 1.05x (nearly neutral — build synergy!). |
| Rob the guaranteed F4 shop on first run | Allowed. First-run tutorial tips warn about consequences. |
| Flee during robbery | Only available on turn 1. After turn 1, must fight to conclusion. |
| Player at karma -30 enters shop but doesn't rob | Still faces +75% prices and locked items. Must buy or leave. |
| All shop items locked by karma | At least 1 item always remains purchasable (heal option can't be locked). |
| Shrine atonement when karma is 0+ | Atonement option hidden. Normal blessings only. |
| Rob twice in same run (F4 + F9) | Both count. Karma -20 after run. Second shop already has bodyguards from first robbery this run (tracked in run_data). |
| Player dies to Merchant bodyguard | Normal game over flow. Skill points + achievements still awarded. |
| Merchant killed by burn damage | Works normally. Burn isn't blocked by "Coin Purse" passive (only direct damage reduced). Wait — design says immune to burn. So burn just doesn't apply. |
Dependencies
| System | Type | Notes |
|---|---|---|
| Combat (DiceCombatManager) | Hard | Robbery IS a combat encounter |
| Shop (shop_screen.gd) | Hard | Rob button, price modifiers, loot collection |
| Enemy Database | Hard | Merchant + Thug + Enforcer enemy definitions |
| Persistence | Hard | karma.save file |
| Shrine | Soft | Atonement option (shrine works without karma) |
| Narrative | Soft | Dialogue flavor (shop works without it) |
| Achievement | Soft | New achievements (system works without them) |
Tuning Knobs
| Knob | Default | Safe Range | What Breaks |
|---|---|---|---|
| BASE_MERCHANT_HP | {80, 120, 160} | 60-200 per act | Too low: free loot. Too high: never worth robbing. |
| KARMA_PER_ROBBERY | -10 | -5 to -20 | Too small: no consequences. Too large: one mistake = permanent debt. |
| KARMA_PER_PURCHASE | +1 | +1 to +3 | Too high: karma trivially redeemable. |
| ATONEMENT_COST | 50g | 30-100g | Too cheap: karma meaningless. Too expensive: never used. |
| ATONEMENT_KARMA | +15 | +5 to +25 | Too high: one shrine fixes everything. |
| PRICE_MULT_PER_KARMA | 0.025 per point | 0.01-0.05 | Too high: shops unusable after 1 robbery. |
| PRICE_MULT_CAP | 2.0x | 1.5-3.0x | Too high: shops become impossible. |
| MERCHANT_SHIELD_BASE | {8, 12, 16} | 4-20 | Too low: merchant is a pushover. Too high: tedious fight. |
| BODYGUARD_KARMA_THRESHOLDS | {-10, -20, -30} | varies | Spacing determines how fast consequences escalate. |
| FLEE_TURN_LIMIT | 1 (turn 1 only) | 1-2 | More turns = less risk = less tension. |
| LOYAL_CUSTOMER_THRESHOLD | +10 | +5 to +20 | Too low: trivial to reach. |
| LOYAL_DISCOUNT | 20% on 1 item | 10-30% | Too high: always-buy builds get free value. |
Visual/Audio Requirements
Visual:
- Merchant sprite: hooded figure with coin purse (1-bit style, existing art direction)
- Thug sprite: reuse basic enemy sprite with dark tint
- Enforcer sprite: reuse elite enemy sprite with gold tint
- "Rob" button: red text, danger styling, positioned below "Leave" button
- Karma indicator: small icon near gold display on shop screen (skull for negative, star for positive)
- Price markup: original price shown with strikethrough, karma-inflated price in red
Audio:
- Robbery initiation: tense SFX (reuse enemy_snipe or new "confrontation" sound)
- Merchant defeat: coin shower SFX (reuse coin sounds from shop purchase)
- Karma warning: low rumble on shop enter when karma < -10
- Atonement: shrine chime + ascending tone
UI Requirements
Shop Screen Changes:
- "Rob" button: bottom of shop UI, red text, small (doesn't dominate the layout)
- Karma indicator: top-right corner, shows skull icon + number when negative
- Price display: when karma < 0, show base price crossed out + inflated price
- Locked items: greyed out with "[LOCKED]" label and padlock icon
- Merchant dialogue: flavor text banner at top of shop, changes with karma
Combat Screen Changes:
- Merchant name displays as "The Merchant" (not a generic enemy name)
- "Flee" button replaces "Score" on turn 1 of robbery combat only
- Bodyguard names: "Hired Thug", "Hired Enforcer"
Confirmation Dialog:
- "Rob the Merchant?" + karma display + bodyguard warning
- "This will anger the Merchant's Guild." (if karma already negative)
- Confirm / Cancel buttons
Acceptance Criteria
- Player can click "Rob" at any shop to initiate combat with the Merchant
- Winning robbery grants all shop items (respecting caps, with swap picker)
- Losing robbery triggers normal game over flow
- Flee option available on turn 1 only, returns to map with no loot
- Karma persists across runs in
user://karma.save - Karma decreases by 10 on successful robbery
- Karma increases by 1 per purchase, +15 per shrine atonement
- Shop prices scale with negative karma (verified at -10, -20, -30)
- Bodyguards appear at correct karma thresholds
- Merchant HP scales with act + karma level
- Merchant uses unique interference actions (POCKET_SAND, STEAL_GOLD, etc.)
- Shrine shows Atonement option when karma < -5
- Merchant dialogue changes with karma level
- Locked items appear at karma -20 and below (heal always available)
- Loyal Customer discount appears at karma +10
- Merchant Prince keystone stacks correctly with karma price modifier
- Wanderer relic guarantee applies to robbery loot
- Two robberies in one run (F4 + F9) both apply karma and bodyguard escalation
- First robbery cutscene plays once via CutsceneManager
New Achievements
| ID | Name | SP | Trigger |
|---|---|---|---|
first_robbery | Sticky Fingers | 3 | Rob the merchant for the first time |
rob_3_times | Most Wanted | 5 | Reach karma -30 |
redeem_karma | Reformed | 5 | Return to karma 0 after being below -10 |
loyal_customer | Preferred Buyer | 3 | Reach karma +10 (Loyal Customer status) |
rob_and_win | Crime Pays | 5 | Win a full run after robbing a merchant in that run |
Open Questions
Should karma affect non-shop systems?IMPLEMENTED — karma now affects:- Guardian choices (Kill/Save dialogue, resistance)
- Shop prices (existing)
- Multiple endings (determines Good/Bad/True ending)
- Memory fragment collection (karma gates truth)
- Should there be a "Pacifist" achievement? Win without ever robbing (karma >= 0 after 10+ runs)
- How does this interact with the Player-Echo Boss? Could the echo boss inherit karma state — a "good" echo boss is weaker but the merchant helps you, an "evil" echo boss is stronger
- Multiplayer expansion: If co-op is ever added, does one player's robbery affect the other's karma?
- Visual karma indicator in hub? A wanted poster that appears on the camp wall when karma < -10