@ak--47/dungeon-master 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +518 -0
  2. package/dungeons/array-of-object-lookup-schema.json +327 -0
  3. package/dungeons/array-of-object-lookup.js +220 -0
  4. package/dungeons/ecommerce-schema.json +462 -0
  5. package/dungeons/ecommerce.js +447 -0
  6. package/dungeons/education-schema.json +2409 -0
  7. package/dungeons/education.js +768 -0
  8. package/dungeons/fintech-schema.json +14034 -0
  9. package/dungeons/fintech.js +696 -0
  10. package/dungeons/foobar-schema.json +403 -0
  11. package/dungeons/foobar.js +296 -0
  12. package/dungeons/food-delivery-schema.json +192 -0
  13. package/dungeons/food-delivery.js +602 -0
  14. package/dungeons/food-schema.json +1152 -0
  15. package/dungeons/food.js +754 -0
  16. package/dungeons/gaming-schema.json +1270 -0
  17. package/dungeons/gaming.js +508 -0
  18. package/dungeons/insurance-application-schema.json +204 -0
  19. package/dungeons/insurance-application.js +605 -0
  20. package/dungeons/media-schema.json +906 -0
  21. package/dungeons/media.js +790 -0
  22. package/dungeons/retention-cadence-schema.json +78 -0
  23. package/dungeons/retention-cadence.js +244 -0
  24. package/dungeons/rpg-schema.json +4526 -0
  25. package/dungeons/rpg.js +919 -0
  26. package/dungeons/sanity-schema.json +255 -0
  27. package/dungeons/sanity.js +152 -0
  28. package/dungeons/sass-schema.json +1291 -0
  29. package/dungeons/sass.js +795 -0
  30. package/dungeons/scd-schema.json +919 -0
  31. package/dungeons/scd.js +277 -0
  32. package/dungeons/simple-schema.json +608 -0
  33. package/dungeons/simple.js +285 -0
  34. package/dungeons/simplest-schema.json +1418 -0
  35. package/dungeons/simplest.js +392 -0
  36. package/dungeons/social-schema.json +1118 -0
  37. package/dungeons/social.js +686 -0
  38. package/dungeons/text-generation-schema.json +3096 -0
  39. package/dungeons/text-generation.js +812 -0
  40. package/index.js +567 -0
  41. package/lib/core/config-validator.js +395 -0
  42. package/lib/core/context.js +204 -0
  43. package/lib/core/dungeon-loader.js +337 -0
  44. package/lib/core/storage.js +379 -0
  45. package/lib/generators/adspend.js +132 -0
  46. package/lib/generators/events.js +271 -0
  47. package/lib/generators/funnels.js +407 -0
  48. package/lib/generators/mirror.js +167 -0
  49. package/lib/generators/product-lookup.js +262 -0
  50. package/lib/generators/product-names.js +195 -0
  51. package/lib/generators/profiles.js +93 -0
  52. package/lib/generators/scd.js +124 -0
  53. package/lib/generators/text.js +1192 -0
  54. package/lib/orchestrators/mixpanel-sender.js +266 -0
  55. package/lib/orchestrators/user-loop.js +335 -0
  56. package/lib/templates/abbreviated.d.ts +169 -0
  57. package/lib/templates/defaults.js +1405 -0
  58. package/lib/templates/phrases.js +2526 -0
  59. package/lib/templates/schema.d.ts +173 -0
  60. package/lib/templates/soup-presets.js +188 -0
  61. package/lib/utils/function-registry.js +302 -0
  62. package/lib/utils/json-evaluator.js +172 -0
  63. package/lib/utils/logger.js +34 -0
  64. package/lib/utils/utils.js +1490 -0
  65. package/package.json +89 -0
  66. package/types.d.ts +865 -0
@@ -0,0 +1,919 @@
1
+ import dayjs from "dayjs";
2
+ import utc from "dayjs/plugin/utc.js";
3
+ import "dotenv/config";
4
+ import * as u from "../lib/utils/utils.js";
5
+ import * as v from "ak-tools";
6
+
7
+ const SEED = "harness-gaming";
8
+ dayjs.extend(utc);
9
+ const chance = u.initChance(SEED);
10
+ const num_users = 5_000;
11
+ const days = 100;
12
+
13
+ /** @typedef {import("../types.d.ts").Dungeon} Config */
14
+
15
+ /*
16
+ * ═══════════════════════════════════════════════════════════════════════════════
17
+ * DATASET OVERVIEW
18
+ * ═══════════════════════════════════════════════════════════════════════════════
19
+ *
20
+ * D&D-Style Action RPG — a fantasy adventure game with deep strategic gameplay
21
+ * and a player-driven economy.
22
+ *
23
+ * CORE LOOP: Players create characters (6 classes, 5 races) and progress through
24
+ * quests, dungeon crawls, and combat encounters. Strategic preparation (inspect,
25
+ * search for clues, Ancient Compass) creates skill gaps. Guild membership drives
26
+ * social retention. Monetization via premium currency, lucky charms, season pass,
27
+ * and subscription tiers (Free / Premium / Elite).
28
+ *
29
+ * SCALE: 5,000 users × 120 events = 600K events over 100 days
30
+ * 20 event types, 7 funnels, guild group analytics, subscription tiers
31
+ *
32
+ * KEY SYSTEMS:
33
+ * - Quests: accept → objective → turn in (5 quest types, gold/XP rewards)
34
+ * - Dungeons: enter → find treasure → exit (50 dungeons, party-based, 3 outcomes)
35
+ * - Combat: initiate → complete (6 enemy types, level scaling, loot)
36
+ * - Economy: item purchase/sell, real money purchases, vendor types
37
+ * - Progression: level up (1-50), character classes, stat points
38
+ * - Social: guild join/leave (5-100 members, guild levels 1-20)
39
+ * - Monetization: premium currency, lucky charms, legendary chests, season pass
40
+ * - Subscriptions: Free / Premium ($9.99/mo) / Elite ($19.99/mo)
41
+ */
42
+
43
+ /*
44
+ * ═══════════════════════════════════════════════════════════════════════════════
45
+ * ANALYTICS HOOKS (8 architected patterns)
46
+ * ═══════════════════════════════════════════════════════════════════════════════
47
+ *
48
+ * 1. CONVERSION: Ancient Compass Effect
49
+ * Players who use the "Ancient Compass" item have 3x quest completion rate
50
+ * and earn 1.5x more rewards.
51
+ * → Mixpanel: Segment users by "use item" where item_type = "Ancient Compass"
52
+ * → Compare quest completion rate and average reward_gold / reward_xp
53
+ * → Look for compass_user = true on quest turned in events
54
+ *
55
+ * 2. TIME-BASED: Cursed Week (days 40-47)
56
+ * Death rates spike 5x, dungeon completion plummets, resurrection usage 4x.
57
+ * → Mixpanel: Chart "player death" count by day — clear spike days 40-47
58
+ * → Filter cause_of_death = "Curse" and cursed_week = true
59
+ * → Compare completion_status distribution before/during/after
60
+ *
61
+ * 3. RETENTION: Early Guild Joiners
62
+ * Players who join a guild within first 3 days have 80% D30 retention vs 20%.
63
+ * → Mixpanel: Create cohort of users who did "guild joined" within first 3 days
64
+ * → Compare D30 retention rate and average events per user
65
+ * → Look for guild_member_retained = true on late engagement events
66
+ *
67
+ * 4. CHURN: Death Spiral
68
+ * Players with 3+ deaths in first week have 70% churn rate (events removed).
69
+ * → Mixpanel: Segment users by count of "player death" in first 7 days
70
+ * → Bucket: 0-2, 3-4, 5+ deaths — compare events after day 7
71
+ * → Users with 3+ early deaths have 70% fewer post-week-1 events
72
+ *
73
+ * 5. PURCHASE VALUE: Lucky Charm LTV
74
+ * Lucky Charm Pack buyers become 5x higher LTV customers — higher purchase
75
+ * frequency, upgraded tiers, more total spend.
76
+ * → Mixpanel: Segment by "real money purchase" where product contains "Lucky Charm"
77
+ * → Compare total revenue per user, purchase frequency, average order value
78
+ * → Look for lucky_charm_effect = true on subsequent purchases
79
+ *
80
+ * 6. BEHAVIORS TOGETHER: Strategic Explorers
81
+ * Players who both "inspect" AND "search for clues" before dungeons have
82
+ * 85% dungeon completion (vs 45%) and 2x treasure value.
83
+ * → Mixpanel: Segment users who did BOTH "inspect" AND "search for clues"
84
+ * → Filter dungeon funnel: enter → exit, compare completion_status rates
85
+ * → Look for strategic_explorer = true on exit dungeon / find treasure events
86
+ *
87
+ * 7. TIMED RELEASE: Shadowmourne Legendary Weapon (day 45)
88
+ * 2% of players find the legendary weapon after release. They get 90% combat
89
+ * win rate (vs 60%) and complete dungeons 40% faster.
90
+ * → Mixpanel: Filter "find treasure" where treasure_type = "Shadowmourne Legendary"
91
+ * → Compare combat win rate and dungeon time_spent_mins before/after day 45
92
+ * → Look for legendary_weapon_equipped = true
93
+ *
94
+ * 8. SUBSCRIPTION TIER: Premium/Elite Advantage
95
+ * Premium: 50% better combat wins, 1.4x rewards, 45% higher dungeon completion
96
+ * Elite: 70% better combat wins, 1.8x rewards, 65% higher dungeon completion,
97
+ * bonus treasure events, reduced death rates.
98
+ * → Mixpanel: Segment by subscription_tier super property
99
+ * → Compare combat win rates, reward_gold, dungeon completion, death rates
100
+ * → Look for subscriber_advantage = "Premium" or "Elite"
101
+ *
102
+ * ───────────────────────────────────────────────────────────────────────────────
103
+ * EXPECTED METRICS SUMMARY
104
+ * ───────────────────────────────────────────────────────────────────────────────
105
+ *
106
+ * Hook | Metric | Baseline | Hook Effect | Ratio
107
+ * ──────────────────────|──────────────────────|──────────|─────────────|──────
108
+ * Ancient Compass | Quest completion | 55% | 85-90% | ~1.6x
109
+ * Cursed Week | Death rate | 8% | 40% | 5x
110
+ * Early Guild Join | D30 Retention | 20% | 80% | 4x
111
+ * Death Spiral | Retention (3+ deaths)| 100% | 30% | 0.3x
112
+ * Lucky Charm | LTV | $15 | $75 | 5x
113
+ * Strategic Explorer | Dungeon completion | 45% | 85% | ~1.9x
114
+ * Legendary Weapon | Combat win rate | 60% | 90% | 1.5x
115
+ * Premium Tier | Combat win rate | 60% | 90% | 1.5x
116
+ * Elite Tier | Combat win rate | 60% | 102% | 1.7x
117
+ *
118
+ * ───────────────────────────────────────────────────────────────────────────────
119
+ * ADVANCED ANALYSIS IDEAS
120
+ * ───────────────────────────────────────────────────────────────────────────────
121
+ *
122
+ * CROSS-HOOK PATTERNS:
123
+ *
124
+ * 1. The Ultimate Player: Users who use Ancient Compass (Hook #1), join guild
125
+ * early (Hook #3), buy Lucky Charm (Hook #5), get legendary weapon (Hook #7),
126
+ * and have Elite subscription (Hook #8) — exceptional metrics across all dims.
127
+ *
128
+ * 2. The Cursed Compass: Do Ancient Compass users survive the Cursed Week
129
+ * better than others?
130
+ *
131
+ * 3. Guild Churn Prevention: Does early guild joining (Hook #3) prevent
132
+ * death-spiral churn (Hook #4)?
133
+ *
134
+ * 4. LTV + Retention: Compare Lucky Charm buyers who joined guilds early vs.
135
+ * those who didn't.
136
+ *
137
+ * 5. Subscription Impact on Churn: Do Premium/Elite subscribers avoid the
138
+ * death spiral churn pattern?
139
+ *
140
+ * COHORT ANALYSIS:
141
+ *
142
+ * - Cohort by starting week: Users who started during Cursed Week (days 40-47)
143
+ * should show different patterns
144
+ * - Cohort by character class: Do certain classes show different hook patterns?
145
+ * - Cohort by platform: Do PC users vs. console users exhibit different
146
+ * strategic explorer behavior?
147
+ * - Cohort by subscription tier: Track retention, engagement, and monetization
148
+ * differences
149
+ *
150
+ * FUNNEL ANALYSIS:
151
+ *
152
+ * - Onboarding Funnel: How does Ancient Compass usage affect tutorial → first
153
+ * quest conversion?
154
+ * - Dungeon Funnel: Compare enter → treasure → exit completion by strategic
155
+ * explorers and subscription tier
156
+ * - Quest Funnel: Compare quest accepted → completed rates before and during
157
+ * Cursed Week
158
+ */
159
+
160
+ // Generate consistent item/location IDs for lookup tables
161
+ const dungeonIds = v.range(1, 51).map(n => `dungeon_${v.uid(6)}`);
162
+ const questIds = v.range(1, 201).map(n => `quest_${v.uid(8)}`);
163
+ const itemIds = v.range(1, 301).map(n => `item_${v.uid(7)}`);
164
+
165
+ /** @type {Config} */
166
+ const config = {
167
+ token: "",
168
+ seed: SEED,
169
+ numDays: days,
170
+ numEvents: num_users * 120,
171
+ numUsers: num_users,
172
+ hasAnonIds: false,
173
+ hasSessionIds: true,
174
+ format: "json",
175
+ gzip: true,
176
+ alsoInferFunnels: false,
177
+ hasLocation: true,
178
+ hasAndroidDevices: true,
179
+ hasIOSDevices: true,
180
+ hasDesktopDevices: true,
181
+ hasBrowser: false,
182
+ hasCampaigns: false,
183
+ isAnonymous: false,
184
+ hasAdSpend: false,
185
+ percentUsersBornInDataset: 50,
186
+
187
+ hasAvatar: true,
188
+
189
+ batchSize: 2_500_000,
190
+ concurrency: 1,
191
+ writeToDisk: false,
192
+
193
+ funnels: [
194
+ {
195
+ sequence: ["character created", "tutorial completed", "quest accepted"],
196
+ isFirstFunnel: true,
197
+ conversionRate: 75,
198
+ timeToConvert: 0.5,
199
+ },
200
+ {
201
+ // Core combat loop: most frequent player activity
202
+ sequence: ["combat initiated", "combat completed", "use item"],
203
+ conversionRate: 75,
204
+ timeToConvert: 0.5,
205
+ weight: 5,
206
+ },
207
+ {
208
+ // Dungeon crawl: enter, explore, loot, exit
209
+ sequence: ["enter dungeon", "find treasure", "exit dungeon"],
210
+ conversionRate: 60,
211
+ timeToConvert: 2,
212
+ weight: 4,
213
+ props: {
214
+ "dungeon_id": u.pickAWinner(dungeonIds),
215
+ "difficulty": ["Easy", "Medium", "Hard", "Deadly"],
216
+ }
217
+ },
218
+ {
219
+ // Quest lifecycle
220
+ sequence: ["quest accepted", "quest objective completed", "quest turned in"],
221
+ conversionRate: 55,
222
+ timeToConvert: 3,
223
+ weight: 3,
224
+ props: { "quest_id": u.pickAWinner(questIds) },
225
+ },
226
+ {
227
+ // Preparation before dungeon: inspect + search for strategic explorer hook
228
+ sequence: ["inspect", "search for clues", "enter dungeon"],
229
+ conversionRate: 50,
230
+ timeToConvert: 1,
231
+ weight: 3,
232
+ },
233
+ {
234
+ // Economy: buy gear, sell loot
235
+ sequence: ["item purchased", "use item", "item sold"],
236
+ conversionRate: 45,
237
+ timeToConvert: 6,
238
+ weight: 2,
239
+ },
240
+ {
241
+ // Social and progression
242
+ sequence: ["guild joined", "level up", "real money purchase"],
243
+ conversionRate: 25,
244
+ timeToConvert: 24,
245
+ weight: 1,
246
+ },
247
+ ],
248
+
249
+ events: [
250
+ {
251
+ event: "character created",
252
+ weight: 1,
253
+ isFirstEvent: true,
254
+ properties: {
255
+ "character_class": [
256
+ "Warrior",
257
+ "Mage",
258
+ "Rogue",
259
+ "Cleric",
260
+ "Ranger",
261
+ "Paladin"
262
+ ],
263
+ "starting_race": [
264
+ "Human",
265
+ "Elf",
266
+ "Dwarf",
267
+ "Halfling",
268
+ "Orc"
269
+ ],
270
+ }
271
+ },
272
+ {
273
+ event: "tutorial completed",
274
+ weight: 2,
275
+ properties: {
276
+ "completion_time_mins": u.weighNumRange(3, 25, 0.8, 10),
277
+ "skipped": u.pickAWinner([true, false], 0.15),
278
+ }
279
+ },
280
+ {
281
+ event: "quest accepted",
282
+ weight: 15,
283
+ properties: {
284
+ "quest_id": u.pickAWinner(questIds),
285
+ "quest_type": [
286
+ "Main Story",
287
+ "Side Quest",
288
+ "Bounty",
289
+ "Exploration",
290
+ "Escort"
291
+ ],
292
+ "recommended_level": u.weighNumRange(1, 50),
293
+ }
294
+ },
295
+ {
296
+ event: "quest objective completed",
297
+ weight: 12,
298
+ properties: {
299
+ "quest_id": u.pickAWinner(questIds),
300
+ "objective_number": u.weighNumRange(1, 5),
301
+ }
302
+ },
303
+ {
304
+ event: "quest turned in",
305
+ weight: 10,
306
+ properties: {
307
+ "quest_id": u.pickAWinner(questIds),
308
+ "reward_gold": u.weighNumRange(10, 500, 0.5, 100),
309
+ "reward_xp": u.weighNumRange(50, 2000, 0.5, 500),
310
+ }
311
+ },
312
+ {
313
+ event: "enter dungeon",
314
+ weight: 18,
315
+ properties: {
316
+ "dungeon_id": u.pickAWinner(dungeonIds),
317
+ "difficulty": ["Easy", "Medium", "Hard", "Deadly"],
318
+ "party_size": u.weighNumRange(1, 5),
319
+ }
320
+ },
321
+ {
322
+ event: "exit dungeon",
323
+ weight: 14,
324
+ properties: {
325
+ "dungeon_id": u.pickAWinner(dungeonIds),
326
+ "time_spent_mins": u.weighNumRange(5, 120, 0.6, 30),
327
+ "completion_status": ["completed", "abandoned", "died"],
328
+ }
329
+ },
330
+ {
331
+ event: "find treasure",
332
+ weight: 16,
333
+ properties: {
334
+ "treasure_type": [
335
+ "Gold",
336
+ "Weapon",
337
+ "Armor",
338
+ "Potion",
339
+ "Scroll",
340
+ "Rare Artifact"
341
+ ],
342
+ "treasure_value": u.weighNumRange(5, 1000, 1.2, 50),
343
+ }
344
+ },
345
+ {
346
+ event: "player death",
347
+ weight: 8,
348
+ properties: {
349
+ "cause_of_death": [
350
+ "Monster",
351
+ "Trap",
352
+ "Fall Damage",
353
+ "Poison",
354
+ "Friendly Fire"
355
+ ],
356
+ "player_level": u.weighNumRange(1, 50),
357
+ "resurrection_used": u.pickAWinner([true, false], 0.25),
358
+ }
359
+ },
360
+ {
361
+ event: "level up",
362
+ weight: 5,
363
+ properties: {
364
+ "new_level": u.weighNumRange(2, 50),
365
+ "stat_points_gained": u.weighNumRange(1, 5),
366
+ }
367
+ },
368
+ {
369
+ event: "item purchased",
370
+ weight: 11,
371
+ properties: {
372
+ "item_id": u.pickAWinner(itemIds),
373
+ "item_type": [
374
+ "Weapon",
375
+ "Armor",
376
+ "Potion",
377
+ "Scroll",
378
+ "Mount",
379
+ "Cosmetic"
380
+ ],
381
+ "price_gold": u.weighNumRange(10, 500, 0.8, 100),
382
+ "vendor_type": ["Town", "Dungeon", "Special Event"],
383
+ }
384
+ },
385
+ {
386
+ event: "item sold",
387
+ weight: 7,
388
+ properties: {
389
+ "item_id": u.pickAWinner(itemIds),
390
+ "item_type": [
391
+ "Weapon",
392
+ "Armor",
393
+ "Potion",
394
+ "Scroll",
395
+ "Junk"
396
+ ],
397
+ "sell_price": u.weighNumRange(5, 250, 0.5, 50),
398
+ }
399
+ },
400
+ {
401
+ event: "real money purchase",
402
+ weight: 3,
403
+ properties: {
404
+ "product": [
405
+ "Premium Currency (1000)",
406
+ "Premium Currency (5000)",
407
+ "Lucky Charm Pack",
408
+ "Legendary Weapon Chest",
409
+ "Cosmetic Bundle",
410
+ "Season Pass"
411
+ ],
412
+ "price_usd": u.pickAWinner([4.99, 9.99, 19.99, 49.99, 99.99]),
413
+ "payment_method": ["Credit Card", "PayPal", "Apple Pay", "Google Pay"],
414
+ }
415
+ },
416
+ {
417
+ event: "guild joined",
418
+ weight: 4,
419
+ properties: {
420
+ "guild_size": u.weighNumRange(5, 100),
421
+ "guild_level": u.weighNumRange(1, 20),
422
+ }
423
+ },
424
+ {
425
+ event: "guild left",
426
+ weight: 1,
427
+ properties: {
428
+ "reason": [
429
+ "Inactive",
430
+ "Found Better Guild",
431
+ "Conflict",
432
+ "Disbanded"
433
+ ],
434
+ }
435
+ },
436
+ {
437
+ event: "inspect",
438
+ weight: 9,
439
+ properties: {
440
+ "inspect_target": [
441
+ "NPC",
442
+ "Monster",
443
+ "Treasure Chest",
444
+ "Door",
445
+ "Statue",
446
+ "Bookshelf"
447
+ ],
448
+ }
449
+ },
450
+ {
451
+ event: "search for clues",
452
+ weight: 8,
453
+ properties: {
454
+ "location_type": [
455
+ "Dungeon Entrance",
456
+ "Hidden Room",
457
+ "Quest Location",
458
+ "Town Square"
459
+ ],
460
+ "clue_found": u.pickAWinner([true, false], 0.6),
461
+ }
462
+ },
463
+ {
464
+ event: "use item",
465
+ weight: 14,
466
+ properties: {
467
+ "item_id": u.pickAWinner(itemIds),
468
+ "item_type": [
469
+ "Health Potion",
470
+ "Mana Potion",
471
+ "Buff Scroll",
472
+ "Ancient Compass",
473
+ "Lucky Charm",
474
+ "Resurrection Stone"
475
+ ],
476
+ "context": ["Combat", "Exploration", "Boss Fight", "Casual"],
477
+ }
478
+ },
479
+ {
480
+ event: "combat initiated",
481
+ weight: 20,
482
+ properties: {
483
+ "enemy_type": [
484
+ "Goblin",
485
+ "Skeleton",
486
+ "Dragon",
487
+ "Demon",
488
+ "Undead",
489
+ "Beast"
490
+ ],
491
+ "enemy_level": u.weighNumRange(1, 50),
492
+ "combat_duration_sec": u.weighNumRange(10, 300, 0.7, 60),
493
+ }
494
+ },
495
+ {
496
+ event: "combat completed",
497
+ weight: 18,
498
+ properties: {
499
+ "outcome": ["Victory", "Defeat", "Fled"],
500
+ "loot_gained": u.pickAWinner([true, false], 0.7),
501
+ }
502
+ }
503
+ ],
504
+
505
+ superProps: {
506
+ platform: [
507
+ "PC",
508
+ "Mac",
509
+ "PlayStation",
510
+ "Xbox",
511
+ "Switch"
512
+ ],
513
+ graphics_quality: [
514
+ "Low",
515
+ "Medium",
516
+ "High",
517
+ "Ultra"
518
+ ],
519
+ subscription_tier: u.pickAWinner(["Free", "Free", "Free", "Premium", "Elite"]),
520
+ },
521
+
522
+ scdProps: {},
523
+
524
+ userProps: {
525
+ "preferred_playstyle": [
526
+ "Solo Explorer",
527
+ "Group Raider",
528
+ "PvP Fighter",
529
+ "Quest Completionist",
530
+ "Treasure Hunter"
531
+ ],
532
+ "total_playtime_hours": u.weighNumRange(1, 500, 1.5, 50),
533
+ "achievement_points": u.weighNumRange(0, 5000, 0.8, 500),
534
+ "favorite_class": [
535
+ "Warrior",
536
+ "Mage",
537
+ "Rogue",
538
+ "Cleric",
539
+ "Ranger",
540
+ "Paladin"
541
+ ],
542
+ },
543
+
544
+ groupKeys: [
545
+ ["guild_id", 500, ["guild joined", "guild left", "quest turned in", "combat completed"]],
546
+ ],
547
+
548
+ groupProps: {
549
+ guild_id: {
550
+ "name": () => `${chance.word()} ${chance.pickone(["Knights", "Dragons", "Warriors", "Seekers", "Legends"])}`,
551
+ "member_count": u.weighNumRange(5, 100),
552
+ "guild_level": u.weighNumRange(1, 20),
553
+ "total_wealth": u.weighNumRange(1000, 1000000, 0.5, 50000),
554
+ }
555
+ },
556
+
557
+ lookupTables: [],
558
+
559
+ /**
560
+ * 🎯 ARCHITECTED ANALYTICS HOOKS
561
+ *
562
+ * This hook function creates 8 deliberate patterns in the data:
563
+ *
564
+ * 1. CONVERSION: Ancient Compass users have 3x quest completion rate
565
+ * 2. TIME-BASED: "Cursed Week" (days 40-47) has 5x death rates & low completion
566
+ * 3. RETENTION: Early guild joiners (first 3 days) have 80% 30-day retention vs 20%
567
+ * 4. CHURN: Players with 3+ deaths in first week have 70% churn rate
568
+ * 5. PURCHASE VALUE: Lucky Charm buyers spend 5x more (LTV pattern)
569
+ * 6. BEHAVIORS TOGETHER: inspect + search before dungeon = 85% completion vs 45%
570
+ * 7. TIMED RELEASE: Legendary weapon released day 45, early adopters dominate
571
+ * 8. SUBSCRIPTION TIER: Premium/Elite users have higher engagement and success rates
572
+ */
573
+ hook: function (record, type, meta) {
574
+ const NOW = dayjs();
575
+ const DATASET_START = NOW.subtract(days, 'days');
576
+ const CURSED_WEEK_START = DATASET_START.add(40, 'days');
577
+ const CURSED_WEEK_END = DATASET_START.add(47, 'days');
578
+ const LEGENDARY_WEAPON_RELEASE = DATASET_START.add(45, 'days');
579
+
580
+ // Hook #2: TIME-BASED TREND - Cursed Week
581
+ if (type === "event") {
582
+ const EVENT_TIME = dayjs(record.time);
583
+
584
+ // During cursed week, dramatically increase death rates
585
+ if (EVENT_TIME.isAfter(CURSED_WEEK_START) && EVENT_TIME.isBefore(CURSED_WEEK_END)) {
586
+ // 50% chance to inject a death event
587
+ if (chance.bool({ likelihood: 30 })) {
588
+ const deathEvent = {
589
+ event: "player death",
590
+ time: record.time,
591
+ user_id: record.user_id,
592
+ cause_of_death: "Curse",
593
+ player_level: chance.integer({ min: 1, max: 50 }),
594
+ resurrection_used: chance.bool({ likelihood: 80 }), // More res usage during curse
595
+ cursed_week: true,
596
+ };
597
+ // Return death event instead sometimes
598
+ if (chance.bool({ likelihood: 50 })) {
599
+ return deathEvent;
600
+ }
601
+ }
602
+ }
603
+ }
604
+
605
+ // Hook #7: TIMED RELEASE - Legendary Weapon
606
+ if (type === "event") {
607
+ const EVENT_TIME = dayjs(record.time);
608
+
609
+ if (record.event === "find treasure") {
610
+ // After legendary weapon release, some lucky players get it
611
+ if (EVENT_TIME.isAfter(LEGENDARY_WEAPON_RELEASE) && chance.bool({ likelihood: 2 })) {
612
+ record.treasure_type = "Shadowmourne Legendary";
613
+ record.treasure_value = 50000;
614
+ record.legendary_drop = true;
615
+ } else {
616
+ record.legendary_drop = false;
617
+ }
618
+ }
619
+ }
620
+
621
+ // Hook #3, #4, #5, #6, #1: EVERYTHING - Complex behavioral patterns
622
+ if (type === "everything") {
623
+ const userEvents = record;
624
+ const firstEventTime = userEvents.length > 0 ? dayjs(userEvents[0].time) : null;
625
+
626
+ // Track user behaviors
627
+ let usedAncientCompass = false;
628
+ let boughtLuckyCharm = false;
629
+ let joinedGuildEarly = false;
630
+ let earlyDeaths = 0;
631
+ let hasLegendaryWeapon = false;
632
+ let inspectedBeforeDungeon = false;
633
+ let searchedBeforeDungeon = false;
634
+ let subscriptionTier = "Free"; // Hook #8: Track subscription tier
635
+
636
+ // First pass: identify user patterns
637
+ userEvents.forEach((event, idx) => {
638
+ const eventTime = dayjs(event.time);
639
+ const daysSinceStart = firstEventTime ? eventTime.diff(firstEventTime, 'days', true) : 0;
640
+
641
+ // Hook #8: Capture subscription tier from any event that has it
642
+ if (event.subscription_tier) {
643
+ subscriptionTier = event.subscription_tier;
644
+ }
645
+
646
+ // Hook #1: Track Ancient Compass usage
647
+ if (event.event === "use item" && event.item_type === "Ancient Compass") {
648
+ usedAncientCompass = true;
649
+ }
650
+
651
+ // Hook #5: Track Lucky Charm purchases
652
+ if (event.event === "real money purchase" && event.product === "Lucky Charm Pack") {
653
+ boughtLuckyCharm = true;
654
+ }
655
+
656
+ // Hook #3: Track early guild joining
657
+ if (event.event === "guild joined" && daysSinceStart < 3) {
658
+ joinedGuildEarly = true;
659
+ }
660
+
661
+ // Hook #4: Track early deaths (first 7 days)
662
+ if (event.event === "player death" && daysSinceStart < 7) {
663
+ earlyDeaths++;
664
+ }
665
+
666
+ // Hook #7: Track legendary weapon ownership
667
+ if (event.event === "find treasure" && event.legendary_drop) {
668
+ hasLegendaryWeapon = true;
669
+ }
670
+
671
+ // Hook #6: Track inspect + search patterns
672
+ if (event.event === "inspect") {
673
+ inspectedBeforeDungeon = true;
674
+ }
675
+ if (event.event === "search for clues") {
676
+ searchedBeforeDungeon = true;
677
+ }
678
+ });
679
+
680
+ // Second pass: modify events based on patterns
681
+ userEvents.forEach((event, idx) => {
682
+ const eventTime = dayjs(event.time);
683
+
684
+ // Set schema defaults for conditional properties
685
+ if (event.event === "quest turned in") {
686
+ event.compass_user = false;
687
+ event.subscriber_advantage = "Free";
688
+ }
689
+ if (event.event === "exit dungeon") {
690
+ event.strategic_explorer = false;
691
+ event.legendary_weapon_equipped = false;
692
+ event.subscriber_advantage = "Free";
693
+ }
694
+ if (event.event === "find treasure") {
695
+ event.strategic_explorer = false;
696
+ event.subscriber_advantage = "Free";
697
+ }
698
+ if (event.event === "combat completed") {
699
+ event.legendary_weapon_equipped = false;
700
+ event.subscriber_advantage = "Free";
701
+ event.near_death_survival = false;
702
+ }
703
+ if (event.event === "player death") {
704
+ event.near_death_survival = false;
705
+ event.subscriber_advantage = "Free";
706
+ }
707
+
708
+ // Hook #1: CONVERSION - Ancient Compass users complete more quests
709
+ if (usedAncientCompass && event.event === "quest turned in") {
710
+ // Triple the rewards for compass users (they're more successful)
711
+ event.reward_gold = Math.floor((event.reward_gold || 100) * 1.5);
712
+ event.reward_xp = Math.floor((event.reward_xp || 500) * 1.5);
713
+ event.compass_user = true;
714
+
715
+ // Add extra quest completions for compass users
716
+ if (chance.bool({ likelihood: 40 })) {
717
+ const extraQuest = {
718
+ event: "quest turned in",
719
+ time: eventTime.add(chance.integer({ min: 10, max: 120 }), 'minutes').toISOString(),
720
+ user_id: event.user_id,
721
+ quest_id: chance.pickone(questIds),
722
+ reward_gold: chance.integer({ min: 100, max: 500 }),
723
+ reward_xp: chance.integer({ min: 500, max: 2000 }),
724
+ compass_user: true,
725
+ };
726
+ userEvents.splice(idx + 1, 0, extraQuest);
727
+ }
728
+ }
729
+
730
+ // Hook #5: PURCHASE VALUE - Lucky charm buyers spend 5x more
731
+ if (boughtLuckyCharm) {
732
+ if (event.event === "real money purchase") {
733
+ // Increase purchase amounts
734
+ if (event.price_usd) {
735
+ const currentPrice = event.price_usd;
736
+ if (currentPrice < 49.99) {
737
+ event.price_usd = currentPrice * 2; // Upgrade tier
738
+ }
739
+ event.lucky_charm_effect = true;
740
+ }
741
+ }
742
+
743
+ // Add additional purchase events (higher LTV)
744
+ if (event.event === "item purchased" && chance.bool({ likelihood: 35 })) {
745
+ const extraPurchase = {
746
+ event: "real money purchase",
747
+ time: eventTime.add(chance.integer({ min: 1, max: 3 }), 'days').toISOString(),
748
+ user_id: event.user_id,
749
+ product: chance.pickone([
750
+ "Premium Currency (5000)",
751
+ "Legendary Weapon Chest",
752
+ "Season Pass"
753
+ ]),
754
+ price_usd: chance.pickone([19.99, 49.99, 99.99]),
755
+ payment_method: chance.pickone(["Credit Card", "PayPal"]),
756
+ lucky_charm_effect: true,
757
+ };
758
+ userEvents.splice(idx + 1, 0, extraPurchase);
759
+ }
760
+ }
761
+
762
+ // Hook #6: BEHAVIORS TOGETHER - Inspect + Search before dungeon
763
+ if (inspectedBeforeDungeon && searchedBeforeDungeon) {
764
+ if (event.event === "exit dungeon") {
765
+ // Much higher completion rate
766
+ if (event.completion_status !== "completed") {
767
+ // Convert 85% of non-completions to completions
768
+ if (chance.bool({ likelihood: 85 })) {
769
+ event.completion_status = "completed";
770
+ event.strategic_explorer = true;
771
+ }
772
+ }
773
+ }
774
+
775
+ // Find more treasure
776
+ if (event.event === "find treasure") {
777
+ event.treasure_value = Math.floor((event.treasure_value || 50) * 2);
778
+ event.strategic_explorer = true;
779
+ }
780
+ }
781
+
782
+ // Hook #7: TIMED RELEASE - Legendary weapon owners dominate
783
+ if (hasLegendaryWeapon) {
784
+ // Higher combat success
785
+ if (event.event === "combat completed") {
786
+ if (event.outcome !== "Victory") {
787
+ // Convert 90% of losses to victories
788
+ if (chance.bool({ likelihood: 90 })) {
789
+ event.outcome = "Victory";
790
+ event.legendary_weapon_equipped = true;
791
+ }
792
+ }
793
+ }
794
+
795
+ // Complete dungeons faster and more successfully
796
+ if (event.event === "exit dungeon") {
797
+ event.completion_status = "completed";
798
+ event.time_spent_mins = Math.floor((event.time_spent_mins || 60) * 0.6);
799
+ event.legendary_weapon_equipped = true;
800
+ }
801
+ }
802
+
803
+ // Hook #8: SUBSCRIPTION TIER - Premium/Elite users have better outcomes
804
+ if (subscriptionTier === "Premium" || subscriptionTier === "Elite") {
805
+ const isElite = subscriptionTier === "Elite";
806
+
807
+ // Better combat outcomes
808
+ if (event.event === "combat completed") {
809
+ if (event.outcome !== "Victory") {
810
+ const winBoost = isElite ? 70 : 50; // Elite: 70%, Premium: 50%
811
+ if (Math.random() * 100 < winBoost) {
812
+ event.outcome = "Victory";
813
+ event.loot_gained = true;
814
+ event.subscriber_advantage = subscriptionTier;
815
+ }
816
+ }
817
+ }
818
+
819
+ // Higher quest completion and rewards
820
+ if (event.event === "quest turned in") {
821
+ const rewardMultiplier = isElite ? 1.8 : 1.4; // Elite: 1.8x, Premium: 1.4x
822
+ event.reward_gold = Math.floor((event.reward_gold || 100) * rewardMultiplier);
823
+ event.reward_xp = Math.floor((event.reward_xp || 500) * rewardMultiplier);
824
+ event.subscriber_advantage = subscriptionTier;
825
+ }
826
+
827
+ // Higher dungeon completion rates
828
+ if (event.event === "exit dungeon") {
829
+ if (event.completion_status !== "completed") {
830
+ const completionBoost = isElite ? 65 : 45; // Elite: 65%, Premium: 45%
831
+ if (Math.random() * 100 < completionBoost) {
832
+ event.completion_status = "completed";
833
+ event.subscriber_advantage = subscriptionTier;
834
+ }
835
+ }
836
+ // Faster completion times
837
+ if (event.completion_status === "completed") {
838
+ const speedBoost = isElite ? 0.7 : 0.85; // Elite: 30% faster, Premium: 15% faster
839
+ event.time_spent_mins = Math.floor((event.time_spent_mins || 60) * speedBoost);
840
+ }
841
+ }
842
+
843
+ // Better treasure finds
844
+ if (event.event === "find treasure") {
845
+ const treasureBoost = isElite ? 2.0 : 1.5; // Elite: 2x, Premium: 1.5x
846
+ event.treasure_value = Math.floor((event.treasure_value || 50) * treasureBoost);
847
+ event.subscriber_advantage = subscriptionTier;
848
+ }
849
+
850
+ // Reduced death rates (subscribers have advantages)
851
+ if (event.event === "player death") {
852
+ const survivalChance = isElite ? 50 : 30; // Elite: 50% avoid death, Premium: 30%
853
+ if (Math.random() * 100 < survivalChance) {
854
+ // Convert death to combat completed with victory
855
+ event.event = "combat completed";
856
+ event.outcome = "Victory";
857
+ event.loot_gained = true;
858
+ event.subscriber_advantage = subscriptionTier;
859
+ event.near_death_survival = true;
860
+ }
861
+ }
862
+
863
+ // Elite users get bonus engagement events
864
+ if (isElite && Math.random() * 100 < 15) {
865
+ if (event.event === "quest turned in" || event.event === "exit dungeon") {
866
+ const treasureTypes = ["Rare Artifact", "Gold", "Weapon", "Armor"];
867
+ const bonusEvent = {
868
+ event: "find treasure",
869
+ time: eventTime.add(Math.floor(Math.random() * 26) + 5, 'minutes').toISOString(),
870
+ user_id: event.user_id,
871
+ treasure_type: treasureTypes[Math.floor(Math.random() * treasureTypes.length)],
872
+ treasure_value: Math.floor(Math.random() * 601) + 200,
873
+ subscriber_advantage: "Elite",
874
+ elite_bonus: true,
875
+ };
876
+ userEvents.splice(idx + 1, 0, bonusEvent);
877
+ }
878
+ }
879
+ }
880
+ });
881
+
882
+ // Hook #3: RETENTION - Early guild joiners retained
883
+ // Hook #4: CHURN - High early death = churn
884
+ const shouldChurn = (!joinedGuildEarly && earlyDeaths >= 3) ||
885
+ (earlyDeaths >= 5);
886
+
887
+ if (shouldChurn) {
888
+ // Remove 70% of events after first week (churn)
889
+ const firstWeekEnd = firstEventTime ? firstEventTime.add(7, 'days') : null;
890
+ for (let i = userEvents.length - 1; i >= 0; i--) {
891
+ const evt = userEvents[i];
892
+ if (firstWeekEnd && dayjs(evt.time).isAfter(firstWeekEnd)) {
893
+ if (chance.bool({ likelihood: 70 })) {
894
+ userEvents.splice(i, 1);
895
+ }
896
+ }
897
+ }
898
+ } else if (joinedGuildEarly) {
899
+ // Add extra engagement events for retained users
900
+ const lastEvent = userEvents[userEvents.length - 1];
901
+ if (lastEvent && chance.bool({ likelihood: 60 })) {
902
+ const retentionEvent = {
903
+ event: "combat completed",
904
+ time: dayjs(lastEvent.time).add(chance.integer({ min: 1, max: 5 }), 'days').toISOString(),
905
+ user_id: lastEvent.user_id,
906
+ outcome: "Victory",
907
+ loot_gained: true,
908
+ guild_member_retained: true,
909
+ };
910
+ userEvents.push(retentionEvent);
911
+ }
912
+ }
913
+ }
914
+
915
+ return record;
916
+ }
917
+ };
918
+
919
+ export default config;