@contractspec/module.learning-journey 1.57.0 → 1.58.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.
- package/dist/browser/contracts/index.js +578 -0
- package/dist/browser/contracts/models.js +193 -0
- package/dist/browser/contracts/onboarding.js +417 -0
- package/dist/browser/contracts/operations.js +326 -0
- package/dist/browser/contracts/shared.js +5 -0
- package/dist/browser/docs/index.js +124 -0
- package/dist/browser/docs/learning-journey.docblock.js +124 -0
- package/dist/browser/engines/index.js +526 -0
- package/dist/browser/engines/srs.js +198 -0
- package/dist/browser/engines/streak.js +159 -0
- package/dist/browser/engines/xp.js +171 -0
- package/dist/browser/entities/ai.js +343 -0
- package/dist/browser/entities/course.js +276 -0
- package/dist/browser/entities/flashcard.js +222 -0
- package/dist/browser/entities/gamification.js +340 -0
- package/dist/browser/entities/index.js +2136 -0
- package/dist/browser/entities/learner.js +329 -0
- package/dist/browser/entities/onboarding.js +301 -0
- package/dist/browser/entities/quiz.js +304 -0
- package/dist/browser/events.js +423 -0
- package/dist/browser/index.js +3833 -0
- package/dist/browser/learning-journey.capability.js +40 -0
- package/dist/browser/learning-journey.feature.js +56 -0
- package/dist/browser/track-spec.js +0 -0
- package/dist/contracts/index.d.ts +5 -5
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +578 -5
- package/dist/contracts/models.d.ts +426 -431
- package/dist/contracts/models.d.ts.map +1 -1
- package/dist/contracts/models.js +178 -372
- package/dist/contracts/onboarding.d.ts +621 -627
- package/dist/contracts/onboarding.d.ts.map +1 -1
- package/dist/contracts/onboarding.js +404 -388
- package/dist/contracts/operations.d.ts +243 -249
- package/dist/contracts/operations.d.ts.map +1 -1
- package/dist/contracts/operations.js +324 -148
- package/dist/contracts/shared.d.ts +1 -4
- package/dist/contracts/shared.d.ts.map +1 -1
- package/dist/contracts/shared.js +6 -6
- package/dist/docs/index.d.ts +2 -1
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +125 -1
- package/dist/docs/learning-journey.docblock.d.ts +2 -1
- package/dist/docs/learning-journey.docblock.d.ts.map +1 -0
- package/dist/docs/learning-journey.docblock.js +47 -58
- package/dist/engines/index.d.ts +4 -4
- package/dist/engines/index.d.ts.map +1 -0
- package/dist/engines/index.js +526 -4
- package/dist/engines/srs.d.ts +89 -92
- package/dist/engines/srs.d.ts.map +1 -1
- package/dist/engines/srs.js +197 -217
- package/dist/engines/streak.d.ts +84 -87
- package/dist/engines/streak.d.ts.map +1 -1
- package/dist/engines/streak.js +158 -192
- package/dist/engines/xp.d.ts +80 -83
- package/dist/engines/xp.d.ts.map +1 -1
- package/dist/engines/xp.js +170 -211
- package/dist/entities/ai.d.ts +199 -204
- package/dist/entities/ai.d.ts.map +1 -1
- package/dist/entities/ai.js +336 -368
- package/dist/entities/course.d.ts +149 -154
- package/dist/entities/course.d.ts.map +1 -1
- package/dist/entities/course.js +267 -306
- package/dist/entities/flashcard.d.ts +144 -149
- package/dist/entities/flashcard.d.ts.map +1 -1
- package/dist/entities/flashcard.js +217 -243
- package/dist/entities/gamification.d.ts +197 -202
- package/dist/entities/gamification.d.ts.map +1 -1
- package/dist/entities/gamification.js +331 -382
- package/dist/entities/index.d.ts +613 -618
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +2135 -43
- package/dist/entities/learner.d.ts +191 -196
- package/dist/entities/learner.d.ts.map +1 -1
- package/dist/entities/learner.js +322 -357
- package/dist/entities/onboarding.d.ts +164 -169
- package/dist/entities/onboarding.d.ts.map +1 -1
- package/dist/entities/onboarding.js +296 -301
- package/dist/entities/quiz.d.ts +184 -189
- package/dist/entities/quiz.d.ts.map +1 -1
- package/dist/entities/quiz.js +296 -361
- package/dist/events.d.ts +608 -614
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +421 -687
- package/dist/index.d.ts +8 -20
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3834 -22
- package/dist/learning-journey.capability.d.ts +3 -8
- package/dist/learning-journey.capability.d.ts.map +1 -1
- package/dist/learning-journey.capability.js +41 -46
- package/dist/learning-journey.feature.d.ts +1 -6
- package/dist/learning-journey.feature.d.ts.map +1 -1
- package/dist/learning-journey.feature.js +55 -155
- package/dist/node/contracts/index.js +578 -0
- package/dist/node/contracts/models.js +193 -0
- package/dist/node/contracts/onboarding.js +417 -0
- package/dist/node/contracts/operations.js +326 -0
- package/dist/node/contracts/shared.js +5 -0
- package/dist/node/docs/index.js +124 -0
- package/dist/node/docs/learning-journey.docblock.js +124 -0
- package/dist/node/engines/index.js +526 -0
- package/dist/node/engines/srs.js +198 -0
- package/dist/node/engines/streak.js +159 -0
- package/dist/node/engines/xp.js +171 -0
- package/dist/node/entities/ai.js +343 -0
- package/dist/node/entities/course.js +276 -0
- package/dist/node/entities/flashcard.js +222 -0
- package/dist/node/entities/gamification.js +340 -0
- package/dist/node/entities/index.js +2136 -0
- package/dist/node/entities/learner.js +329 -0
- package/dist/node/entities/onboarding.js +301 -0
- package/dist/node/entities/quiz.js +304 -0
- package/dist/node/events.js +423 -0
- package/dist/node/index.js +3833 -0
- package/dist/node/learning-journey.capability.js +40 -0
- package/dist/node/learning-journey.feature.js +56 -0
- package/dist/node/track-spec.js +0 -0
- package/dist/track-spec.d.ts +115 -118
- package/dist/track-spec.d.ts.map +1 -1
- package/dist/track-spec.js +1 -0
- package/package.json +237 -60
- package/dist/contracts/models.js.map +0 -1
- package/dist/contracts/onboarding.js.map +0 -1
- package/dist/contracts/operations.js.map +0 -1
- package/dist/contracts/shared.js.map +0 -1
- package/dist/docs/learning-journey.docblock.js.map +0 -1
- package/dist/engines/srs.js.map +0 -1
- package/dist/engines/streak.js.map +0 -1
- package/dist/engines/xp.js.map +0 -1
- package/dist/entities/ai.js.map +0 -1
- package/dist/entities/course.js.map +0 -1
- package/dist/entities/flashcard.js.map +0 -1
- package/dist/entities/gamification.js.map +0 -1
- package/dist/entities/index.js.map +0 -1
- package/dist/entities/learner.js.map +0 -1
- package/dist/entities/onboarding.js.map +0 -1
- package/dist/entities/quiz.js.map +0 -1
- package/dist/events.js.map +0 -1
- package/dist/learning-journey.capability.js.map +0 -1
- package/dist/learning-journey.feature.js.map +0 -1
package/dist/engines/xp.d.ts
CHANGED
|
@@ -1,97 +1,94 @@
|
|
|
1
|
-
//#region src/engines/xp.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* XP (Experience Points) Engine
|
|
4
3
|
*
|
|
5
4
|
* Calculates XP rewards for various learning activities.
|
|
6
5
|
*/
|
|
7
|
-
type XPActivityType = 'lesson_complete' | 'quiz_pass' | 'quiz_perfect' | 'flashcard_review' | 'course_complete' | 'module_complete' | 'streak_bonus' | 'achievement_unlock' | 'daily_goal_complete' | 'first_lesson' | 'onboarding_step' | 'onboarding_complete';
|
|
8
|
-
interface XPCalculationInput {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
export type XPActivityType = 'lesson_complete' | 'quiz_pass' | 'quiz_perfect' | 'flashcard_review' | 'course_complete' | 'module_complete' | 'streak_bonus' | 'achievement_unlock' | 'daily_goal_complete' | 'first_lesson' | 'onboarding_step' | 'onboarding_complete';
|
|
7
|
+
export interface XPCalculationInput {
|
|
8
|
+
/** Type of activity */
|
|
9
|
+
activity: XPActivityType;
|
|
10
|
+
/** Base XP for the activity (from content config) */
|
|
11
|
+
baseXp?: number;
|
|
12
|
+
/** Score achieved (0-100) for scored activities */
|
|
13
|
+
score?: number;
|
|
14
|
+
/** Current streak (for streak bonuses) */
|
|
15
|
+
currentStreak?: number;
|
|
16
|
+
/** Time spent in seconds */
|
|
17
|
+
timeSpent?: number;
|
|
18
|
+
/** Attempt number (for quizzes) */
|
|
19
|
+
attemptNumber?: number;
|
|
20
|
+
/** Whether this is a retry */
|
|
21
|
+
isRetry?: boolean;
|
|
23
22
|
}
|
|
24
|
-
interface XPResult {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
export interface XPResult {
|
|
24
|
+
/** Total XP earned */
|
|
25
|
+
totalXp: number;
|
|
26
|
+
/** Base XP before bonuses */
|
|
27
|
+
baseXp: number;
|
|
28
|
+
/** Breakdown of XP sources */
|
|
29
|
+
breakdown: XPBreakdown[];
|
|
31
30
|
}
|
|
32
|
-
interface XPBreakdown {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
export interface XPBreakdown {
|
|
32
|
+
/** Source of XP */
|
|
33
|
+
source: string;
|
|
34
|
+
/** XP amount */
|
|
35
|
+
amount: number;
|
|
36
|
+
/** Multiplier applied */
|
|
37
|
+
multiplier?: number;
|
|
39
38
|
}
|
|
40
|
-
interface XPConfig {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
39
|
+
export interface XPConfig {
|
|
40
|
+
/** Base XP values for each activity */
|
|
41
|
+
baseValues: Record<XPActivityType, number>;
|
|
42
|
+
/** Score thresholds for bonus XP */
|
|
43
|
+
scoreThresholds: {
|
|
44
|
+
min: number;
|
|
45
|
+
multiplier: number;
|
|
46
|
+
}[];
|
|
47
|
+
/** Streak bonus tiers */
|
|
48
|
+
streakTiers: {
|
|
49
|
+
days: number;
|
|
50
|
+
bonus: number;
|
|
51
|
+
}[];
|
|
52
|
+
/** Perfect score bonus multiplier */
|
|
53
|
+
perfectScoreMultiplier: number;
|
|
54
|
+
/** First attempt bonus */
|
|
55
|
+
firstAttemptBonus: number;
|
|
56
|
+
/** Retry penalty multiplier */
|
|
57
|
+
retryPenalty: number;
|
|
58
|
+
/** Speed bonus (complete under expected time) */
|
|
59
|
+
speedBonusMultiplier: number;
|
|
60
|
+
/** Speed bonus threshold (percentage of expected time) */
|
|
61
|
+
speedBonusThreshold: number;
|
|
63
62
|
}
|
|
64
|
-
declare const DEFAULT_XP_CONFIG: XPConfig;
|
|
65
|
-
declare class XPEngine {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
63
|
+
export declare const DEFAULT_XP_CONFIG: XPConfig;
|
|
64
|
+
export declare class XPEngine {
|
|
65
|
+
private config;
|
|
66
|
+
constructor(config?: Partial<XPConfig>);
|
|
67
|
+
/**
|
|
68
|
+
* Calculate XP for an activity.
|
|
69
|
+
*/
|
|
70
|
+
calculate(input: XPCalculationInput): XPResult;
|
|
71
|
+
/**
|
|
72
|
+
* Calculate streak bonus XP.
|
|
73
|
+
*/
|
|
74
|
+
calculateStreakBonus(currentStreak: number): XPResult;
|
|
75
|
+
/**
|
|
76
|
+
* Calculate XP needed for a level.
|
|
77
|
+
*/
|
|
78
|
+
getXpForLevel(level: number): number;
|
|
79
|
+
/**
|
|
80
|
+
* Get level from total XP.
|
|
81
|
+
*/
|
|
82
|
+
getLevelFromXp(totalXp: number): {
|
|
83
|
+
level: number;
|
|
84
|
+
xpInLevel: number;
|
|
85
|
+
xpForNextLevel: number;
|
|
86
|
+
};
|
|
87
|
+
private getScoreMultiplier;
|
|
88
|
+
private getStreakBonus;
|
|
90
89
|
}
|
|
91
90
|
/**
|
|
92
91
|
* Default XP engine instance.
|
|
93
92
|
*/
|
|
94
|
-
declare const xpEngine: XPEngine;
|
|
95
|
-
//#endregion
|
|
96
|
-
export { DEFAULT_XP_CONFIG, XPActivityType, XPBreakdown, XPCalculationInput, XPConfig, XPEngine, XPResult, xpEngine };
|
|
93
|
+
export declare const xpEngine: XPEngine;
|
|
97
94
|
//# sourceMappingURL=xp.d.ts.map
|
package/dist/engines/xp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xp.d.ts","
|
|
1
|
+
{"version":3,"file":"xp.d.ts","sourceRoot":"","sources":["../../src/engines/xp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,WAAW,GACX,cAAc,GACd,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,oBAAoB,GACpB,qBAAqB,GACrB,cAAc,GACd,iBAAiB,GACjB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,uBAAuB;IACvB,QAAQ,EAAE,cAAc,CAAC;IACzB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAC3C,oCAAoC;IACpC,eAAe,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvD,yBAAyB;IACzB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,qCAAqC;IACrC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,0BAA0B;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAID,eAAO,MAAM,iBAAiB,EAAE,QAqC/B,CAAC;AAIF,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAW;gBAEb,MAAM,GAAE,OAAO,CAAC,QAAQ,CAAM;IAW1C;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,QAAQ;IAmF9C;;OAEG;IACH,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ;IAcrD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAUpC;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;QAC/B,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;KACxB;IAqBD,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;CAQvB;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ,UAAiB,CAAC"}
|
package/dist/engines/xp.js
CHANGED
|
@@ -1,213 +1,172 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
days: 365,
|
|
42
|
-
bonus: 50
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
days: 180,
|
|
46
|
-
bonus: 30
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
days: 90,
|
|
50
|
-
bonus: 20
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
days: 30,
|
|
54
|
-
bonus: 15
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
days: 14,
|
|
58
|
-
bonus: 10
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
days: 7,
|
|
62
|
-
bonus: 5
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
days: 3,
|
|
66
|
-
bonus: 2
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
days: 1,
|
|
70
|
-
bonus: 0
|
|
71
|
-
}
|
|
72
|
-
],
|
|
73
|
-
perfectScoreMultiplier: 1.5,
|
|
74
|
-
firstAttemptBonus: 10,
|
|
75
|
-
retryPenalty: .5,
|
|
76
|
-
speedBonusMultiplier: 1.2,
|
|
77
|
-
speedBonusThreshold: .8
|
|
1
|
+
// @bun
|
|
2
|
+
// src/engines/xp.ts
|
|
3
|
+
var DEFAULT_XP_CONFIG = {
|
|
4
|
+
baseValues: {
|
|
5
|
+
lesson_complete: 10,
|
|
6
|
+
quiz_pass: 20,
|
|
7
|
+
quiz_perfect: 50,
|
|
8
|
+
flashcard_review: 1,
|
|
9
|
+
course_complete: 200,
|
|
10
|
+
module_complete: 50,
|
|
11
|
+
streak_bonus: 5,
|
|
12
|
+
achievement_unlock: 0,
|
|
13
|
+
daily_goal_complete: 15,
|
|
14
|
+
first_lesson: 25,
|
|
15
|
+
onboarding_step: 5,
|
|
16
|
+
onboarding_complete: 50
|
|
17
|
+
},
|
|
18
|
+
scoreThresholds: [
|
|
19
|
+
{ min: 90, multiplier: 1.5 },
|
|
20
|
+
{ min: 80, multiplier: 1.25 },
|
|
21
|
+
{ min: 70, multiplier: 1 },
|
|
22
|
+
{ min: 60, multiplier: 0.75 },
|
|
23
|
+
{ min: 0, multiplier: 0.5 }
|
|
24
|
+
],
|
|
25
|
+
streakTiers: [
|
|
26
|
+
{ days: 365, bonus: 50 },
|
|
27
|
+
{ days: 180, bonus: 30 },
|
|
28
|
+
{ days: 90, bonus: 20 },
|
|
29
|
+
{ days: 30, bonus: 15 },
|
|
30
|
+
{ days: 14, bonus: 10 },
|
|
31
|
+
{ days: 7, bonus: 5 },
|
|
32
|
+
{ days: 3, bonus: 2 },
|
|
33
|
+
{ days: 1, bonus: 0 }
|
|
34
|
+
],
|
|
35
|
+
perfectScoreMultiplier: 1.5,
|
|
36
|
+
firstAttemptBonus: 10,
|
|
37
|
+
retryPenalty: 0.5,
|
|
38
|
+
speedBonusMultiplier: 1.2,
|
|
39
|
+
speedBonusThreshold: 0.8
|
|
78
40
|
};
|
|
79
|
-
var XPEngine = class {
|
|
80
|
-
config;
|
|
81
|
-
constructor(config = {}) {
|
|
82
|
-
this.config = {
|
|
83
|
-
...DEFAULT_XP_CONFIG,
|
|
84
|
-
...config,
|
|
85
|
-
baseValues: {
|
|
86
|
-
...DEFAULT_XP_CONFIG.baseValues,
|
|
87
|
-
...config.baseValues
|
|
88
|
-
},
|
|
89
|
-
scoreThresholds: config.scoreThresholds || DEFAULT_XP_CONFIG.scoreThresholds,
|
|
90
|
-
streakTiers: config.streakTiers || DEFAULT_XP_CONFIG.streakTiers
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Calculate XP for an activity.
|
|
95
|
-
*/
|
|
96
|
-
calculate(input) {
|
|
97
|
-
const breakdown = [];
|
|
98
|
-
const baseXp = input.baseXp ?? this.config.baseValues[input.activity];
|
|
99
|
-
let totalXp = baseXp;
|
|
100
|
-
breakdown.push({
|
|
101
|
-
source: "base",
|
|
102
|
-
amount: baseXp
|
|
103
|
-
});
|
|
104
|
-
if (input.score !== void 0) {
|
|
105
|
-
const scoreMultiplier = this.getScoreMultiplier(input.score);
|
|
106
|
-
if (scoreMultiplier !== 1) {
|
|
107
|
-
const scoreBonus = Math.round(baseXp * (scoreMultiplier - 1));
|
|
108
|
-
totalXp += scoreBonus;
|
|
109
|
-
breakdown.push({
|
|
110
|
-
source: "score_bonus",
|
|
111
|
-
amount: scoreBonus,
|
|
112
|
-
multiplier: scoreMultiplier
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
if (input.score === 100) {
|
|
116
|
-
const perfectBonus = Math.round(baseXp * (this.config.perfectScoreMultiplier - 1));
|
|
117
|
-
totalXp += perfectBonus;
|
|
118
|
-
breakdown.push({
|
|
119
|
-
source: "perfect_score",
|
|
120
|
-
amount: perfectBonus,
|
|
121
|
-
multiplier: this.config.perfectScoreMultiplier
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (input.attemptNumber === 1 && !input.isRetry) {
|
|
126
|
-
totalXp += this.config.firstAttemptBonus;
|
|
127
|
-
breakdown.push({
|
|
128
|
-
source: "first_attempt",
|
|
129
|
-
amount: this.config.firstAttemptBonus
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
if (input.isRetry) {
|
|
133
|
-
const penalty = Math.round(totalXp * (1 - this.config.retryPenalty));
|
|
134
|
-
totalXp -= penalty;
|
|
135
|
-
breakdown.push({
|
|
136
|
-
source: "retry_penalty",
|
|
137
|
-
amount: -penalty,
|
|
138
|
-
multiplier: this.config.retryPenalty
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
if (input.currentStreak && input.currentStreak > 0) {
|
|
142
|
-
const streakBonus = this.getStreakBonus(input.currentStreak);
|
|
143
|
-
if (streakBonus > 0) {
|
|
144
|
-
totalXp += streakBonus;
|
|
145
|
-
breakdown.push({
|
|
146
|
-
source: "streak_bonus",
|
|
147
|
-
amount: streakBonus
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (baseXp > 0) totalXp = Math.max(1, totalXp);
|
|
152
|
-
return {
|
|
153
|
-
totalXp: Math.round(totalXp),
|
|
154
|
-
baseXp,
|
|
155
|
-
breakdown
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Calculate streak bonus XP.
|
|
160
|
-
*/
|
|
161
|
-
calculateStreakBonus(currentStreak) {
|
|
162
|
-
const bonus = this.getStreakBonus(currentStreak);
|
|
163
|
-
return {
|
|
164
|
-
totalXp: bonus,
|
|
165
|
-
baseXp: bonus,
|
|
166
|
-
breakdown: [{
|
|
167
|
-
source: "streak_bonus",
|
|
168
|
-
amount: bonus
|
|
169
|
-
}]
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Calculate XP needed for a level.
|
|
174
|
-
*/
|
|
175
|
-
getXpForLevel(level) {
|
|
176
|
-
if (level <= 1) return 0;
|
|
177
|
-
return Math.round(100 * Math.pow(level - 1, 1.5));
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Get level from total XP.
|
|
181
|
-
*/
|
|
182
|
-
getLevelFromXp(totalXp) {
|
|
183
|
-
let level = 1;
|
|
184
|
-
let xpRequired = this.getXpForLevel(level + 1);
|
|
185
|
-
while (totalXp >= xpRequired && level < 1e3) {
|
|
186
|
-
level++;
|
|
187
|
-
xpRequired = this.getXpForLevel(level + 1);
|
|
188
|
-
}
|
|
189
|
-
const xpForCurrentLevel = this.getXpForLevel(level);
|
|
190
|
-
const xpForNextLevel = this.getXpForLevel(level + 1);
|
|
191
|
-
return {
|
|
192
|
-
level,
|
|
193
|
-
xpInLevel: totalXp - xpForCurrentLevel,
|
|
194
|
-
xpForNextLevel: xpForNextLevel - xpForCurrentLevel
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
getScoreMultiplier(score) {
|
|
198
|
-
for (const threshold of this.config.scoreThresholds) if (score >= threshold.min) return threshold.multiplier;
|
|
199
|
-
return 1;
|
|
200
|
-
}
|
|
201
|
-
getStreakBonus(streak) {
|
|
202
|
-
for (const tier of this.config.streakTiers) if (streak >= tier.days) return tier.bonus;
|
|
203
|
-
return 0;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
/**
|
|
207
|
-
* Default XP engine instance.
|
|
208
|
-
*/
|
|
209
|
-
const xpEngine = new XPEngine();
|
|
210
41
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
42
|
+
class XPEngine {
|
|
43
|
+
config;
|
|
44
|
+
constructor(config = {}) {
|
|
45
|
+
this.config = {
|
|
46
|
+
...DEFAULT_XP_CONFIG,
|
|
47
|
+
...config,
|
|
48
|
+
baseValues: { ...DEFAULT_XP_CONFIG.baseValues, ...config.baseValues },
|
|
49
|
+
scoreThresholds: config.scoreThresholds || DEFAULT_XP_CONFIG.scoreThresholds,
|
|
50
|
+
streakTiers: config.streakTiers || DEFAULT_XP_CONFIG.streakTiers
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
calculate(input) {
|
|
54
|
+
const breakdown = [];
|
|
55
|
+
const baseXp = input.baseXp ?? this.config.baseValues[input.activity];
|
|
56
|
+
let totalXp = baseXp;
|
|
57
|
+
breakdown.push({
|
|
58
|
+
source: "base",
|
|
59
|
+
amount: baseXp
|
|
60
|
+
});
|
|
61
|
+
if (input.score !== undefined) {
|
|
62
|
+
const scoreMultiplier = this.getScoreMultiplier(input.score);
|
|
63
|
+
if (scoreMultiplier !== 1) {
|
|
64
|
+
const scoreBonus = Math.round(baseXp * (scoreMultiplier - 1));
|
|
65
|
+
totalXp += scoreBonus;
|
|
66
|
+
breakdown.push({
|
|
67
|
+
source: "score_bonus",
|
|
68
|
+
amount: scoreBonus,
|
|
69
|
+
multiplier: scoreMultiplier
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (input.score === 100) {
|
|
73
|
+
const perfectBonus = Math.round(baseXp * (this.config.perfectScoreMultiplier - 1));
|
|
74
|
+
totalXp += perfectBonus;
|
|
75
|
+
breakdown.push({
|
|
76
|
+
source: "perfect_score",
|
|
77
|
+
amount: perfectBonus,
|
|
78
|
+
multiplier: this.config.perfectScoreMultiplier
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (input.attemptNumber === 1 && !input.isRetry) {
|
|
83
|
+
totalXp += this.config.firstAttemptBonus;
|
|
84
|
+
breakdown.push({
|
|
85
|
+
source: "first_attempt",
|
|
86
|
+
amount: this.config.firstAttemptBonus
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (input.isRetry) {
|
|
90
|
+
const penalty = Math.round(totalXp * (1 - this.config.retryPenalty));
|
|
91
|
+
totalXp -= penalty;
|
|
92
|
+
breakdown.push({
|
|
93
|
+
source: "retry_penalty",
|
|
94
|
+
amount: -penalty,
|
|
95
|
+
multiplier: this.config.retryPenalty
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (input.currentStreak && input.currentStreak > 0) {
|
|
99
|
+
const streakBonus = this.getStreakBonus(input.currentStreak);
|
|
100
|
+
if (streakBonus > 0) {
|
|
101
|
+
totalXp += streakBonus;
|
|
102
|
+
breakdown.push({
|
|
103
|
+
source: "streak_bonus",
|
|
104
|
+
amount: streakBonus
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (baseXp > 0) {
|
|
109
|
+
totalXp = Math.max(1, totalXp);
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
totalXp: Math.round(totalXp),
|
|
113
|
+
baseXp,
|
|
114
|
+
breakdown
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
calculateStreakBonus(currentStreak) {
|
|
118
|
+
const bonus = this.getStreakBonus(currentStreak);
|
|
119
|
+
return {
|
|
120
|
+
totalXp: bonus,
|
|
121
|
+
baseXp: bonus,
|
|
122
|
+
breakdown: [
|
|
123
|
+
{
|
|
124
|
+
source: "streak_bonus",
|
|
125
|
+
amount: bonus
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
getXpForLevel(level) {
|
|
131
|
+
if (level <= 1)
|
|
132
|
+
return 0;
|
|
133
|
+
return Math.round(100 * Math.pow(level - 1, 1.5));
|
|
134
|
+
}
|
|
135
|
+
getLevelFromXp(totalXp) {
|
|
136
|
+
let level = 1;
|
|
137
|
+
let xpRequired = this.getXpForLevel(level + 1);
|
|
138
|
+
while (totalXp >= xpRequired && level < 1000) {
|
|
139
|
+
level++;
|
|
140
|
+
xpRequired = this.getXpForLevel(level + 1);
|
|
141
|
+
}
|
|
142
|
+
const xpForCurrentLevel = this.getXpForLevel(level);
|
|
143
|
+
const xpForNextLevel = this.getXpForLevel(level + 1);
|
|
144
|
+
return {
|
|
145
|
+
level,
|
|
146
|
+
xpInLevel: totalXp - xpForCurrentLevel,
|
|
147
|
+
xpForNextLevel: xpForNextLevel - xpForCurrentLevel
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
getScoreMultiplier(score) {
|
|
151
|
+
for (const threshold of this.config.scoreThresholds) {
|
|
152
|
+
if (score >= threshold.min) {
|
|
153
|
+
return threshold.multiplier;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return 1;
|
|
157
|
+
}
|
|
158
|
+
getStreakBonus(streak) {
|
|
159
|
+
for (const tier of this.config.streakTiers) {
|
|
160
|
+
if (streak >= tier.days) {
|
|
161
|
+
return tier.bonus;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
var xpEngine = new XPEngine;
|
|
168
|
+
export {
|
|
169
|
+
xpEngine,
|
|
170
|
+
XPEngine,
|
|
171
|
+
DEFAULT_XP_CONFIG
|
|
172
|
+
};
|