Reward and Loot Systems
Learning Objectives: By the end of this lesson, you will be able to:
- Design reward schedules that motivate without manipulating
- Create loot tables with appropriate drop rates
- Implement pity systems that reduce frustration
- Design achievement systems that guide and recognize players
- Understand the psychology of rewards and use it ethically
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
Remember the last time a game gave you exactly what you wanted? How did it feel? Now think about the 50th time you killed a boss hoping for a rare drop and got nothing. Both experiences are shaped by reward system design.
Rewards are the heart of player motivation. Design them well, and players feel valued. Design them poorly, and players feel manipulated or bored.
Lesson Overview
- Duration: 90-120 minutes
- Prerequisites: Lessons 1-6 (Foundations and Core Systems)
- Materials Needed: Spreadsheet software, probability calculator
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Reward Psychology - Why rewards work and can be problematic
- Reward Schedules - Fixed, variable, and hybrid approaches
- Loot Tables - Designing drop rates and distributions
- Pity Systems - Preventing extreme bad luck
- Achievement Design - Goals that guide and celebrate
- Ethical Considerations - Rewards without manipulation
Success Criteria:
By the end of this session, you should be able to design a complete loot system with tables, pity mechanics, and ethical boundaries.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
The Psychology of Rewards
Rewards tap into fundamental human psychology:
- Dopamine Response: Anticipation of reward releases dopamine, not just receiving it
- Variable Reinforcement: Unpredictable rewards are more engaging than predictable ones
- Loss Aversion: Fear of losing potential rewards motivates strongly
- Sunk Cost: Investment increases perceived value
- Social Comparison: Others’ rewards affect our satisfaction
Real-World Analogy: Consider fishing. The uncertainty of what you might catch keeps you casting. A fish guaranteed every cast would be boring. No fish ever would be frustrating. The balance creates engagement.
Why This Matters:
- Rewards are the primary motivation for continued play
- Poorly designed rewards cause addiction or frustration
- Ethical reward design respects player psychology
- Good rewards feel earned, not manipulated
Key Terminology
- Reward Schedule: Pattern of when rewards are given
- Drop Rate: Probability of receiving a specific item
- Loot Table: Distribution of possible rewards from a source
- Pity System: Mechanism guaranteeing rewards after unlucky streak
- Soft Pity: Gradually increasing rates after no drops
- Hard Pity: Guaranteed drop after N attempts
4. Detailed Explanation and Examples (25-30 minutes)
Reward Schedules
Based on B.F. Skinner’s operant conditioning research:
Fixed Ratio (FR)
Reward after every N actions.
Actions: ■ ■ ■ ■ ■ → Reward → ■ ■ ■ ■ ■ → Reward
Examples:
- Kill 10 enemies = 1 token
- Complete 5 quests = bonus XP
- Play 3 games = daily reward
Characteristics:
- Predictable, grindable
- Can feel like a chore
- Players can “clock out” at reward points
Variable Ratio (VR)
Reward with probability after each action.
Actions: ■ ■ → Reward → ■ ■ ■ ■ ■ ■ → Reward → ■ → Reward
Examples:
- 10% chance per kill to drop rare item
- Critical hits (15% chance)
- Loot boxes
Characteristics:
- Highly engaging (most addictive)
- Creates anticipation
- Can be frustrating with bad luck
- Most susceptible to abuse
Fixed Interval (FI)
Reward available after N time passes.
Time: 0h → 4h → 8h → 12h → 16h → 20h → 24h
Reward: ◆ ◆ ◆ ◆
Examples:
- Daily login bonus
- Shop refresh every 4 hours
- Energy refill
Characteristics:
- Encourages regular returns
- Can feel restrictive
- FOMO (fear of missing out) risk
Variable Interval (VI)
Reward available at random time intervals.
Time: ──────◆────────────◆──◆─────────────◆────
(unpredictable)
Examples:
- Random world events
- Surprise sales
- Hidden treasure spawns
Characteristics:
- Creates constant anticipation
- Less predictable play patterns
- Can feel chaotic
Loot Tables
Loot tables define what can drop and how likely each item is.
Basic Structure
| Item | Rarity | Drop Rate | Cumulative |
|------|--------|-----------|------------|
| Common Sword | Common | 50% | 50% |
| Steel Sword | Uncommon | 30% | 80% |
| Silver Sword | Rare | 15% | 95% |
| Dragon Slayer | Legendary | 5% | 100% |
Weighted Random Selection
import random
def roll_loot_table(table):
"""
table = [('item_name', weight), ...]
"""
total_weight = sum(weight for _, weight in table)
roll = random.uniform(0, total_weight)
cumulative = 0
for item, weight in table:
cumulative += weight
if roll <= cumulative:
return item
return table[-1][0] # Fallback to last item
# Example
monster_drops = [
('gold', 60), # 60% chance
('potion', 25), # 25% chance
('rare_gem', 10), # 10% chance
('legendary', 5), # 5% chance
]
# Simulate 1000 kills
results = {}
for _ in range(1000):
item = roll_loot_table(monster_drops)
results[item] = results.get(item, 0) + 1
print(results)
# Expected ~600 gold, ~250 potion, ~100 rare_gem, ~50 legendary
Guaranteed + Bonus Structure
def boss_loot(boss_difficulty):
loot = []
# Guaranteed drops
loot.append(f"gold_{boss_difficulty * 100}")
loot.append("boss_token")
# Guaranteed equipment (from pool)
equipment_pool = ['helmet', 'chest', 'gloves', 'boots', 'weapon']
loot.append(random.choice(equipment_pool))
# Bonus rare drop (chance)
if random.random() < 0.10: # 10% chance
loot.append("rare_material")
# Jackpot drop (very rare)
if random.random() < 0.01: # 1% chance
loot.append("legendary_drop")
return loot
Pity Systems
Pity systems prevent players from experiencing extremely long unlucky streaks.
Soft Pity
Gradually increase drop rates after no drops.
def calculate_drop_rate(base_rate, attempts_since_drop, soft_pity_start=75):
if attempts_since_drop < soft_pity_start:
return base_rate
else:
bonus = (attempts_since_drop - soft_pity_start) * 0.05
return min(base_rate + bonus, 1.0) # Cap at 100%
# Example: Legendary at 2% base
for attempts in [0, 50, 75, 80, 85, 90]:
rate = calculate_drop_rate(0.02, attempts)
print(f"Attempts: {attempts}, Rate: {rate:.1%}")
# Output:
# Attempts: 0, Rate: 2.0%
# Attempts: 50, Rate: 2.0%
# Attempts: 75, Rate: 2.0% (soft pity kicks in after this)
# Attempts: 80, Rate: 27.0%
# Attempts: 85, Rate: 52.0%
# Attempts: 90, Rate: 77.0%
Hard Pity
Guarantee drop after N attempts.
class GachaWithPity:
def __init__(self, base_rate=0.02, hard_pity=90):
self.base_rate = base_rate
self.hard_pity = hard_pity
self.counter = 0
def pull(self):
self.counter += 1
# Hard pity guarantee
if self.counter >= self.hard_pity:
self.counter = 0
return "LEGENDARY (pity)"
# Normal roll
if random.random() < self.base_rate:
self.counter = 0
return "LEGENDARY (lucky)"
return "common"
Duplicate Protection
Prevent same item from dropping repeatedly.
class LootWithDupeProtection:
def __init__(self, item_pool):
self.pool = item_pool.copy()
self.owned = set()
def drop(self):
available = [item for item in self.pool if item not in self.owned]
if not available:
# All owned - convert to currency or random
return "duplicate_currency"
# Weighted toward unowned
item = random.choice(available)
self.owned.add(item)
return item
Achievement Design
Achievements serve multiple purposes:
- Guide: Point players to features they might miss
- Challenge: Provide optional goals for mastery
- Celebrate: Recognize player accomplishments
- Share: Create social moments
Achievement Categories
| Category | Purpose | Example |
|---|---|---|
| Progress | Mark milestones | Complete Chapter 1 |
| Mastery | Reward skill | Beat boss without damage |
| Collection | Encourage exploration | Find all hidden items |
| Social | Promote community | Play with 10 friends |
| Discovery | Reveal secrets | Find easter egg |
| Endurance | Reward dedication | Play 100 hours |
Common Misconceptions
-
Misconception: “More random = more engaging” Reality: Extreme randomness causes frustration. Controlled randomness is better.
-
Misconception: “Players want easy rewards” Reality: Players want EARNED rewards. Too easy feels worthless.
-
Misconception: “Rare should mean frustrating to get” Reality: Rare should mean special, not painful. Pity systems help.
5. Live Coding Demonstration (20-25 minutes)
Example 1: Complete Loot System
import random
class LootSystem:
def __init__(self):
self.rarity_weights = {
'common': 60,
'uncommon': 25,
'rare': 12,
'epic': 2.5,
'legendary': 0.5
}
self.items_by_rarity = {
'common': ['iron_sword', 'leather_armor', 'health_potion'],
'uncommon': ['steel_sword', 'chain_mail', 'mana_potion'],
'rare': ['enchanted_blade', 'plate_armor', 'elixir'],
'epic': ['dragon_tooth', 'shadow_cloak', 'phoenix_feather'],
'legendary': ['excalibur', 'aegis', 'holy_grail']
}
# Pity tracking
self.pulls_since_legendary = 0
self.soft_pity_start = 50
self.hard_pity = 100
def calculate_legendary_rate(self):
base = self.rarity_weights['legendary'] / 100
if self.pulls_since_legendary >= self.hard_pity:
return 1.0 # Guaranteed
elif self.pulls_since_legendary >= self.soft_pity_start:
bonus = (self.pulls_since_legendary - self.soft_pity_start) * 0.01
return min(base + bonus, 1.0)
return base
def roll_rarity(self):
# Check legendary with pity
if random.random() < self.calculate_legendary_rate():
self.pulls_since_legendary = 0
return 'legendary'
self.pulls_since_legendary += 1
# Roll other rarities
total = sum(v for k, v in self.rarity_weights.items() if k != 'legendary')
roll = random.uniform(0, total)
cumulative = 0
for rarity, weight in self.rarity_weights.items():
if rarity == 'legendary':
continue
cumulative += weight
if roll <= cumulative:
return rarity
return 'common'
def drop_loot(self, source_level=1, luck_bonus=0):
rarity = self.roll_rarity()
# Apply luck bonus (increases rare+ chance slightly)
if luck_bonus > 0 and rarity in ['common', 'uncommon']:
if random.random() < luck_bonus * 0.01:
rarity = 'rare'
item = random.choice(self.items_by_rarity[rarity])
return {
'item': item,
'rarity': rarity,
'level': source_level,
'pity_counter': self.pulls_since_legendary
}
# Test the system
loot = LootSystem()
# Simulate 200 drops
results = {'common': 0, 'uncommon': 0, 'rare': 0, 'epic': 0, 'legendary': 0}
for i in range(200):
drop = loot.drop_loot()
results[drop['rarity']] += 1
if drop['rarity'] == 'legendary':
print(f"LEGENDARY at pull {i+1}! (pity was {drop['pity_counter']})")
print(f"\nResults over 200 pulls: {results}")
Example 2: Achievement System
class AchievementSystem:
def __init__(self):
self.achievements = {
# Progress achievements
'first_blood': {
'name': 'First Blood',
'description': 'Defeat your first enemy',
'type': 'progress',
'requirement': {'enemies_killed': 1},
'reward': {'gold': 100, 'xp': 50}
},
'centurion': {
'name': 'Centurion',
'description': 'Defeat 100 enemies',
'type': 'progress',
'requirement': {'enemies_killed': 100},
'reward': {'gold': 1000, 'title': 'Centurion'}
},
# Mastery achievements
'perfect_boss': {
'name': 'Untouchable',
'description': 'Defeat a boss without taking damage',
'type': 'mastery',
'requirement': {'perfect_boss_kills': 1},
'reward': {'achievement_points': 50, 'cosmetic': 'golden_aura'}
},
# Collection achievements
'collector': {
'name': 'Collector',
'description': 'Find all hidden treasures in Act 1',
'type': 'collection',
'requirement': {'act1_treasures': 10},
'reward': {'gold': 5000, 'item': 'treasure_map_act2'}
},
# Discovery achievements
'secret_room': {
'name': 'Secret Finder',
'description': 'Discover the hidden room',
'type': 'discovery',
'requirement': {'secret_rooms_found': 1},
'reward': {'achievement_points': 25}
}
}
self.player_stats = {
'enemies_killed': 0,
'perfect_boss_kills': 0,
'act1_treasures': 0,
'secret_rooms_found': 0
}
self.unlocked = set()
def update_stat(self, stat, value=1):
if stat in self.player_stats:
self.player_stats[stat] += value
self._check_achievements()
def _check_achievements(self):
newly_unlocked = []
for ach_id, ach in self.achievements.items():
if ach_id in self.unlocked:
continue
requirement_met = True
for stat, needed in ach['requirement'].items():
if self.player_stats.get(stat, 0) < needed:
requirement_met = False
break
if requirement_met:
self.unlocked.add(ach_id)
newly_unlocked.append(ach)
return newly_unlocked
def get_progress(self, ach_id):
ach = self.achievements.get(ach_id)
if not ach:
return None
progress = {}
for stat, needed in ach['requirement'].items():
current = self.player_stats.get(stat, 0)
progress[stat] = {'current': current, 'needed': needed,
'percent': min(100, int(current/needed * 100))}
return progress
# Test
ach_system = AchievementSystem()
# Simulate gameplay
for i in range(150):
ach_system.update_stat('enemies_killed')
unlocked = ach_system._check_achievements()
for ach in unlocked:
print(f"🏆 ACHIEVEMENT UNLOCKED: {ach['name']}")
print(f" Reward: {ach['reward']}")
6. Hands-On Practice (30-40 minutes)
Exercise 1: Loot Table Design (10 minutes)
Objective: Create a balanced loot table
Scenario: Design drops for a world boss that:
- Is fought once per week
- Should give meaningful progression
- Has a rare mount drop (1% target)
- Should never feel completely unrewarding
Deliverables:
- Complete loot table with rates
- Guaranteed drops
- Bonus chance drops
- Pity system for the mount
Exercise 2: Reward Schedule Analysis (15 minutes)
Objective: Analyze and improve reward scheduling
Scenario: A mobile game has these rewards:
- Login bonus: Same 50 gold every day
- Level up: 100 gold every level
- Quest: 10-1000 gold randomly
Players say the game is “boring” early and “frustrating” late.
Requirements:
- Identify the problem with each reward type
- Propose improved versions of each
- Add one new reward type to address gaps
- Explain the psychology behind your changes
Exercise 3: Ethical Evaluation (15 minutes)
Objective: Apply ethical principles to reward design
Scenario: Evaluate these reward mechanisms:
- Limited-time banner: Special character available for 2 weeks only, 0.5% drop rate
- Login streak: Rewards increase daily, reset to zero if you miss a day
- Battle pass: Free and premium tracks, premium costs $10, takes 100 hours to complete
- Loot boxes: Randomized cosmetics, real money purchase, no pity system
For each mechanism:
- Rate ethicality (1-5, 5 being most ethical)
- Identify potential manipulation
- Propose improvements to make it more ethical
- Decide: Would you implement this?
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
-
Problem: Players feel rewards are random/unfair Cause: No pity system or bad luck protection Solution: Implement soft and hard pity
-
Problem: Rewards lose meaning over time Cause: Inflation of rewards without new sinks Solution: Scale reward utility, add new reward types
-
Problem: Players feel manipulated Cause: Exploitative variable ratio schedules Solution: Add transparency, guaranteed minimums
-
Problem: Achievement hunting feels like a checklist Cause: Too many trivial achievements Solution: Fewer, more meaningful achievements
Warning Signs
| Symptom | Likely Problem | Check This |
|---|---|---|
| Players complaining about RNG | Missing pity | Drop data, streaks |
| Rewards feel worthless | Inflation | Economy balance |
| Compulsive playing, regret | Manipulation | Variable ratio abuse |
| Ignoring achievements | Poor design | Achievement tracking rates |
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Hard pity: Usually 80-100 pulls for gacha
- Soft pity: Kicks in at 50-75% of hard pity
- Duplicate protection: After 2-3 duplicates
- Achievement rarity: Only 1-5% should get hardest ones
Ethical Guidelines
- Transparency: Publish drop rates
- Fairness: Pity systems for all random rewards
- Respect: No FOMO-based manipulation
- Value: Rewards should feel worth the time/money
- Choice: Players can opt out of gambling mechanics
9. Summary and Review (5 minutes)
Key Takeaways
- Reward schedules shape behavior - choose carefully
- Random doesn’t mean unfair - pity systems help
- Achievements guide and celebrate - use both functions
- Ethics matter - players remember manipulation
Quick Review Questions
- What’s the difference between soft and hard pity?
- Why are variable ratio schedules so engaging (and dangerous)?
- Name three purposes of achievement systems.
- How do you make a loot table feel fair?
For Instructors
Teaching Tips
- Discuss real gacha games students have played
- Calculate actual probabilities together
- Debate ethics of specific implementations
- Share stories of reward system failures/successes
Adaptation Notes
- For beginners: Focus on basic loot tables and achievements
- For advanced: Discuss behavioral economics, A/B testing
- For mobile focus: Deep dive into gacha systems
- For ethics focus: Extended case studies on manipulation