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