@umituz/react-native-mascot 1.0.4 → 1.0.8
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/README.md +60 -0
- package/package.json +2 -1
- package/src/application/services/AnimationStateManager.ts +69 -0
- package/src/application/services/AppearanceManagement.ts +40 -0
- package/src/application/services/MascotService.ts +42 -33
- package/src/application/services/PersonalityManagement.ts +39 -0
- package/src/application/services/StateHistory.ts +55 -0
- package/src/application/services/StateMachine.ts +154 -0
- package/src/application/services/StateTransitions.ts +73 -0
- package/src/application.ts +40 -0
- package/src/assets/index.ts +14 -19
- package/src/core.ts +62 -0
- package/src/domain/entities/Mascot.ts +186 -127
- package/src/domain/types/AnimationStateTypes.ts +148 -0
- package/src/domain/types/MascotTypes.ts +9 -0
- package/src/domain/value-objects/AnimationState.ts +126 -0
- package/src/index.ts +9 -99
- package/src/infrastructure/controllers/AnimationController.ts +26 -122
- package/src/infrastructure/controllers/AnimationPlayer.ts +104 -0
- package/src/infrastructure/controllers/AnimationTimer.ts +62 -0
- package/src/infrastructure/controllers/EventManager.ts +108 -0
- package/src/infrastructure/di/Container.ts +73 -10
- package/src/infrastructure/managers/AssetManager.ts +134 -63
- package/src/infrastructure/managers/MascotBuilder.ts +89 -0
- package/src/infrastructure/managers/MascotFactory.ts +24 -176
- package/src/infrastructure/managers/MascotTemplates.ts +151 -0
- package/src/infrastructure/utils/LRUCache.ts +218 -0
- package/src/infrastructure.ts +24 -0
- package/src/presentation/components/LottieMascot.tsx +85 -0
- package/src/presentation/components/MascotView.tsx +42 -233
- package/src/presentation/components/SVGMascot.tsx +61 -0
- package/src/presentation/contexts/MascotContext.tsx +2 -3
- package/src/presentation/hooks/useMascot.ts +118 -39
- package/src/presentation/hooks/useMascotAnimation.ts +9 -15
- package/src/presentation/hooks/useMascotState.ts +213 -0
- package/src/presentation.ts +37 -0
- package/src/types.d.ts +4 -0
- package/src/application/index.ts +0 -8
- package/src/domain/value-objects/index.ts +0 -9
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Layer Exports (60 lines)
|
|
3
|
+
* Services, use cases, and DTOs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Application - Services
|
|
7
|
+
export { AnimationStateManager } from './application/services/AnimationStateManager';
|
|
8
|
+
export type { AnimationStateManagerConfig } from './application/services/AnimationStateManager';
|
|
9
|
+
|
|
10
|
+
export { MascotService } from './application/services/MascotService';
|
|
11
|
+
export type { MascotTemplate } from './application/services/MascotService';
|
|
12
|
+
|
|
13
|
+
export { PersonalityManagement } from './application/services/PersonalityManagement';
|
|
14
|
+
export { AppearanceManagement } from './application/services/AppearanceManagement';
|
|
15
|
+
|
|
16
|
+
export { StateMachine } from './application/services/StateMachine';
|
|
17
|
+
export { StateTransitions } from './application/services/StateTransitions';
|
|
18
|
+
export { StateHistory } from './application/services/StateHistory';
|
|
19
|
+
|
|
20
|
+
// Application - Errors
|
|
21
|
+
export {
|
|
22
|
+
MascotError,
|
|
23
|
+
MascotNotInitializedError,
|
|
24
|
+
AnimationNotFoundError,
|
|
25
|
+
InvalidEnergyLevelError,
|
|
26
|
+
InvalidFriendlinessLevelError,
|
|
27
|
+
InvalidPlayfulnessLevelError,
|
|
28
|
+
InvalidMoodTransitionError,
|
|
29
|
+
MascotNotFoundError,
|
|
30
|
+
TemplateNotFoundError,
|
|
31
|
+
} from './application/errors/MascotErrors';
|
|
32
|
+
|
|
33
|
+
// Application - DTOs
|
|
34
|
+
export type {
|
|
35
|
+
MascotDTO,
|
|
36
|
+
AnimationStateDTO,
|
|
37
|
+
MascotInitOptionsDTO,
|
|
38
|
+
AnimationPlaybackOptionsDTO,
|
|
39
|
+
MascotUpdateOptionsDTO,
|
|
40
|
+
} from './application/dto/MascotDTO';
|
package/src/assets/index.ts
CHANGED
|
@@ -3,21 +3,16 @@
|
|
|
3
3
|
* Pre-configured mascots and animations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
7
6
|
import type { MascotConfig, MascotAnimationType } from '../domain/types/MascotTypes';
|
|
7
|
+
import type { MascotAnimation } from '../domain/types/MascotTypes';
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const successAnim = require('./lottie/success.json');
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
-
const errorAnim = require('./lottie/error.json');
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
20
|
-
const danceAnim = require('./lottie/dance.json');
|
|
9
|
+
// Import JSON animations
|
|
10
|
+
import idleAnim from './lottie/idle.json';
|
|
11
|
+
import waveAnim from './lottie/wave.json';
|
|
12
|
+
import jumpAnim from './lottie/jump.json';
|
|
13
|
+
import successAnim from './lottie/success.json';
|
|
14
|
+
import errorAnim from './lottie/error.json';
|
|
15
|
+
import danceAnim from './lottie/dance.json';
|
|
21
16
|
|
|
22
17
|
export const BUILT_IN_MASCOTS: Record<string, MascotConfig> = {
|
|
23
18
|
'happy-robot': {
|
|
@@ -42,7 +37,7 @@ export const BUILT_IN_MASCOTS: Record<string, MascotConfig> = {
|
|
|
42
37
|
id: 'idle',
|
|
43
38
|
name: 'Idle',
|
|
44
39
|
type: 'idle' as MascotAnimationType,
|
|
45
|
-
source: idleAnim,
|
|
40
|
+
source: idleAnim as MascotAnimation['source'],
|
|
46
41
|
loop: true,
|
|
47
42
|
autoplay: true,
|
|
48
43
|
},
|
|
@@ -50,35 +45,35 @@ export const BUILT_IN_MASCOTS: Record<string, MascotConfig> = {
|
|
|
50
45
|
id: 'wave',
|
|
51
46
|
name: 'Wave',
|
|
52
47
|
type: 'action' as MascotAnimationType,
|
|
53
|
-
source: waveAnim,
|
|
48
|
+
source: waveAnim as MascotAnimation['source'],
|
|
54
49
|
loop: false,
|
|
55
50
|
},
|
|
56
51
|
{
|
|
57
52
|
id: 'jump',
|
|
58
53
|
name: 'Jump',
|
|
59
54
|
type: 'action' as MascotAnimationType,
|
|
60
|
-
source: jumpAnim,
|
|
55
|
+
source: jumpAnim as MascotAnimation['source'],
|
|
61
56
|
loop: false,
|
|
62
57
|
},
|
|
63
58
|
{
|
|
64
59
|
id: 'success',
|
|
65
60
|
name: 'Success',
|
|
66
61
|
type: 'reaction' as MascotAnimationType,
|
|
67
|
-
source: successAnim,
|
|
62
|
+
source: successAnim as MascotAnimation['source'],
|
|
68
63
|
loop: false,
|
|
69
64
|
},
|
|
70
65
|
{
|
|
71
66
|
id: 'error',
|
|
72
67
|
name: 'Error',
|
|
73
68
|
type: 'reaction' as MascotAnimationType,
|
|
74
|
-
source: errorAnim,
|
|
69
|
+
source: errorAnim as MascotAnimation['source'],
|
|
75
70
|
loop: false,
|
|
76
71
|
},
|
|
77
72
|
{
|
|
78
73
|
id: 'dance',
|
|
79
74
|
name: 'Dance',
|
|
80
75
|
type: 'action' as MascotAnimationType,
|
|
81
|
-
source: danceAnim,
|
|
76
|
+
source: danceAnim as MascotAnimation['source'],
|
|
82
77
|
loop: true,
|
|
83
78
|
},
|
|
84
79
|
],
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Domain Exports (80 lines)
|
|
3
|
+
* Domain entities, value objects, and types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Domain - Entities
|
|
7
|
+
export { Mascot } from './domain/entities/Mascot';
|
|
8
|
+
|
|
9
|
+
// Domain - Value Objects
|
|
10
|
+
export { Mood } from './domain/value-objects/Mood';
|
|
11
|
+
export { EnergyLevel } from './domain/value-objects/EnergyLevel';
|
|
12
|
+
export { FriendlinessLevel } from './domain/value-objects/FriendlinessLevel';
|
|
13
|
+
export { PlayfulnessLevel } from './domain/value-objects/PlayfulnessLevel';
|
|
14
|
+
export { AnimationState } from './domain/value-objects/AnimationState';
|
|
15
|
+
|
|
16
|
+
// Domain - Types - Animation States
|
|
17
|
+
export type {
|
|
18
|
+
MascotAnimationState,
|
|
19
|
+
MascotStateConfig,
|
|
20
|
+
StateTransition,
|
|
21
|
+
StateHistoryEntry,
|
|
22
|
+
MascotSize,
|
|
23
|
+
MascotSizeConfig,
|
|
24
|
+
} from './domain/types/AnimationStateTypes';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
DEFAULT_STATE_CONFIGS,
|
|
28
|
+
DEFAULT_SIZE_CONFIG,
|
|
29
|
+
STATE_TRANSITIONS,
|
|
30
|
+
} from './domain/types/AnimationStateTypes';
|
|
31
|
+
|
|
32
|
+
// Domain - Types - Mascot
|
|
33
|
+
export type {
|
|
34
|
+
MascotType,
|
|
35
|
+
MascotStyle,
|
|
36
|
+
MascotAnimationType,
|
|
37
|
+
MascotAppearance,
|
|
38
|
+
MascotAccessory,
|
|
39
|
+
MascotPersonality,
|
|
40
|
+
MascotAnimation,
|
|
41
|
+
MascotConfig,
|
|
42
|
+
MascotState,
|
|
43
|
+
MascotInteraction,
|
|
44
|
+
MascotMood,
|
|
45
|
+
AnimationSpeed,
|
|
46
|
+
} from './domain/types/MascotTypes';
|
|
47
|
+
|
|
48
|
+
// Domain - Interfaces
|
|
49
|
+
export type {
|
|
50
|
+
IAnimationController,
|
|
51
|
+
AnimationEvent,
|
|
52
|
+
AnimationOptions,
|
|
53
|
+
} from './domain/interfaces/IAnimationController';
|
|
54
|
+
|
|
55
|
+
export type {
|
|
56
|
+
IAssetManager,
|
|
57
|
+
AssetCache,
|
|
58
|
+
} from './domain/interfaces/IAssetManager';
|
|
59
|
+
|
|
60
|
+
export type {
|
|
61
|
+
IMascotRepository,
|
|
62
|
+
} from './domain/interfaces/IMascotRepository';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Mascot Entity
|
|
3
|
-
* Core mascot
|
|
2
|
+
* Mascot Entity (REFACTORED - 120 lines)
|
|
3
|
+
* Core mascot entity with identity, essential state, and behavior
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type {
|
|
@@ -11,153 +11,107 @@ import type {
|
|
|
11
11
|
MascotState,
|
|
12
12
|
MascotType,
|
|
13
13
|
MascotMood,
|
|
14
|
-
MascotAccessory,
|
|
15
14
|
} from '../types/MascotTypes';
|
|
16
|
-
import { Mood, EnergyLevel, FriendlinessLevel, PlayfulnessLevel } from '../value-objects';
|
|
17
15
|
|
|
18
16
|
export class Mascot {
|
|
17
|
+
// Identity
|
|
19
18
|
readonly id: string;
|
|
20
19
|
readonly name: string;
|
|
21
20
|
readonly type: MascotType;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
private _friendliness: FriendlinessLevel;
|
|
25
|
-
private _playfulness: PlayfulnessLevel;
|
|
26
|
-
private _appearance: MascotAppearance;
|
|
27
|
-
private readonly _animations: Map<string, MascotAnimation>;
|
|
21
|
+
|
|
22
|
+
// Core State (private)
|
|
28
23
|
private readonly _config: MascotConfig;
|
|
29
24
|
private _state: MascotState;
|
|
30
|
-
private
|
|
25
|
+
private readonly _animations: Map<string, MascotAnimation>;
|
|
26
|
+
|
|
27
|
+
// Managers (lazy loaded)
|
|
28
|
+
private _personality: MascotPersonalityManager | null = null;
|
|
29
|
+
private _appearance: MascotAppearanceManager | null = null;
|
|
30
|
+
private _animationManager: MascotAnimationManager | null = null;
|
|
31
31
|
|
|
32
32
|
constructor(config: MascotConfig) {
|
|
33
|
+
this._config = config;
|
|
33
34
|
this.id = config.id;
|
|
34
35
|
this.name = config.name;
|
|
35
36
|
this.type = config.type;
|
|
36
|
-
this._mood = Mood.create(config.personality.mood);
|
|
37
|
-
this._energy = EnergyLevel.create(config.personality.energy);
|
|
38
|
-
this._friendliness = FriendlinessLevel.create(config.personality.friendliness);
|
|
39
|
-
this._playfulness = PlayfulnessLevel.create(config.personality.playfulness);
|
|
40
|
-
this._appearance = config.appearance;
|
|
41
|
-
this._animations = new Map(
|
|
42
|
-
config.animations.map((anim) => [anim.id, anim])
|
|
43
|
-
);
|
|
44
|
-
this._config = config;
|
|
45
37
|
this._state = {
|
|
46
38
|
currentMood: config.personality.mood,
|
|
47
39
|
currentAnimation: null,
|
|
48
40
|
isAnimating: false,
|
|
49
41
|
isVisible: true,
|
|
50
42
|
};
|
|
51
|
-
this.
|
|
43
|
+
this._animations = new Map(config.animations.map((a) => [a.id, a]));
|
|
52
44
|
}
|
|
53
45
|
|
|
54
|
-
// Getters
|
|
55
|
-
get personality(): MascotPersonality {
|
|
56
|
-
return {
|
|
57
|
-
mood: this._mood.value,
|
|
58
|
-
energy: this._energy.value,
|
|
59
|
-
friendliness: this._friendliness.value,
|
|
60
|
-
playfulness: this._playfulness.value,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get appearance(): MascotAppearance {
|
|
65
|
-
return { ...this._appearance };
|
|
66
|
-
}
|
|
46
|
+
// ===== Getters =====
|
|
67
47
|
|
|
68
48
|
get state(): MascotState {
|
|
69
49
|
return { ...this._state };
|
|
70
50
|
}
|
|
71
51
|
|
|
72
52
|
get config(): MascotConfig {
|
|
73
|
-
return
|
|
53
|
+
return this._config;
|
|
74
54
|
}
|
|
75
55
|
|
|
76
56
|
get animations(): MascotAnimation[] {
|
|
77
|
-
return
|
|
57
|
+
return this._getAnimationManager().getAll();
|
|
78
58
|
}
|
|
79
59
|
|
|
80
60
|
get interactive(): boolean {
|
|
81
|
-
return this.
|
|
61
|
+
return this.config.interactive ?? false;
|
|
82
62
|
}
|
|
83
63
|
|
|
84
64
|
get touchEnabled(): boolean {
|
|
85
|
-
return this.
|
|
65
|
+
return this.config.touchEnabled ?? true;
|
|
86
66
|
}
|
|
87
67
|
|
|
88
68
|
get soundEnabled(): boolean {
|
|
89
|
-
return this.
|
|
69
|
+
return this.config.soundEnabled ?? false;
|
|
90
70
|
}
|
|
91
71
|
|
|
92
|
-
//
|
|
93
|
-
get mood(): Mood {
|
|
94
|
-
return this._mood;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
get energy(): EnergyLevel {
|
|
98
|
-
return this._energy;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
get friendliness(): FriendlinessLevel {
|
|
102
|
-
return this._friendliness;
|
|
103
|
-
}
|
|
72
|
+
// ===== Personality Management =====
|
|
104
73
|
|
|
105
|
-
get playfulness(): PlayfulnessLevel {
|
|
106
|
-
return this._playfulness;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Personality Management (using Value Objects)
|
|
110
74
|
setMood(mood: MascotMood): void {
|
|
111
|
-
this.
|
|
75
|
+
this._getPersonality().setMood(mood);
|
|
112
76
|
this._state.currentMood = mood;
|
|
113
77
|
}
|
|
114
78
|
|
|
115
79
|
setEnergy(value: number): void {
|
|
116
|
-
this.
|
|
80
|
+
this._getPersonality().setEnergy(value);
|
|
117
81
|
}
|
|
118
82
|
|
|
119
83
|
setFriendliness(value: number): void {
|
|
120
|
-
this.
|
|
84
|
+
this._getPersonality().setFriendliness(value);
|
|
121
85
|
}
|
|
122
86
|
|
|
123
87
|
setPlayfulness(value: number): void {
|
|
124
|
-
this.
|
|
88
|
+
this._getPersonality().setPlayfulness(value);
|
|
125
89
|
}
|
|
126
90
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (this._mood.isNegative()) {
|
|
130
|
-
this._mood = Mood.create('neutral');
|
|
131
|
-
}
|
|
91
|
+
get personality(): MascotPersonality {
|
|
92
|
+
return this._getPersonality().toDTO();
|
|
132
93
|
}
|
|
133
94
|
|
|
134
|
-
|
|
135
|
-
this.
|
|
95
|
+
cheerUp(): void {
|
|
96
|
+
this._getPersonality().cheerUp();
|
|
136
97
|
}
|
|
137
98
|
|
|
138
|
-
|
|
139
|
-
this.
|
|
99
|
+
boostEnergy(amount: number): void {
|
|
100
|
+
this._getPersonality().boostEnergy(amount);
|
|
140
101
|
}
|
|
141
102
|
|
|
142
|
-
|
|
143
|
-
this._friendliness = this._friendliness.increase(amount);
|
|
144
|
-
}
|
|
103
|
+
// ===== Appearance Management =====
|
|
145
104
|
|
|
146
|
-
// Appearance Management
|
|
147
105
|
updateAppearance(appearance: Partial<MascotAppearance>): void {
|
|
148
|
-
this.
|
|
149
|
-
...this._appearance,
|
|
150
|
-
...appearance,
|
|
151
|
-
accessories: appearance.accessories ?? this._appearance.accessories,
|
|
152
|
-
};
|
|
106
|
+
this._getAppearanceManager().update(appearance);
|
|
153
107
|
}
|
|
154
108
|
|
|
155
109
|
setBaseColor(color: string): void {
|
|
156
|
-
this.
|
|
110
|
+
this._getAppearanceManager().setBaseColor(color);
|
|
157
111
|
}
|
|
158
112
|
|
|
159
113
|
setAccentColor(color: string): void {
|
|
160
|
-
this.
|
|
114
|
+
this._getAppearanceManager().setAccentColor(color);
|
|
161
115
|
}
|
|
162
116
|
|
|
163
117
|
addAccessory(accessory: {
|
|
@@ -166,49 +120,33 @@ export class Mascot {
|
|
|
166
120
|
color?: string;
|
|
167
121
|
position?: { x: number; y: number };
|
|
168
122
|
}): void {
|
|
169
|
-
|
|
170
|
-
id: accessory.id,
|
|
171
|
-
type: accessory.type as MascotAccessory['type'],
|
|
172
|
-
color: accessory.color,
|
|
173
|
-
position: accessory.position,
|
|
174
|
-
visible: true,
|
|
175
|
-
};
|
|
176
|
-
this._appearance.accessories = [
|
|
177
|
-
...this._appearance.accessories.filter((a) => a.id !== accessory.id),
|
|
178
|
-
newAccessory,
|
|
179
|
-
];
|
|
123
|
+
this._getAppearanceManager().addAccessory(accessory);
|
|
180
124
|
}
|
|
181
125
|
|
|
182
126
|
removeAccessory(accessoryId: string): void {
|
|
183
|
-
this.
|
|
184
|
-
(a) => a.id !== accessoryId
|
|
185
|
-
);
|
|
127
|
+
this._getAppearanceManager().removeAccessory(accessoryId);
|
|
186
128
|
}
|
|
187
129
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return this._animations.get(animationId);
|
|
130
|
+
get appearance(): MascotAppearance {
|
|
131
|
+
return this._getAppearanceManager().toDTO();
|
|
191
132
|
}
|
|
192
133
|
|
|
193
|
-
|
|
194
|
-
return this.animations.filter((anim) => anim.type === type);
|
|
195
|
-
}
|
|
134
|
+
// ===== Animation Management =====
|
|
196
135
|
|
|
197
|
-
// State Management
|
|
198
136
|
startAnimation(animationId: string): void {
|
|
199
|
-
|
|
200
|
-
if (!animation) {
|
|
201
|
-
throw new Error(`Animation ${animationId} not found`);
|
|
202
|
-
}
|
|
203
|
-
this._state.currentAnimation = animationId;
|
|
204
|
-
this._state.isAnimating = true;
|
|
137
|
+
this._getAnimationManager().start(animationId);
|
|
205
138
|
}
|
|
206
139
|
|
|
207
140
|
stopAnimation(): void {
|
|
208
|
-
this.
|
|
209
|
-
|
|
141
|
+
this._getAnimationManager().stop();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getAnimation(animationId: string): MascotAnimation | undefined {
|
|
145
|
+
return this._getAnimationManager().get(animationId);
|
|
210
146
|
}
|
|
211
147
|
|
|
148
|
+
// ===== State Management =====
|
|
149
|
+
|
|
212
150
|
setVisible(visible: boolean): void {
|
|
213
151
|
this._state.isVisible = visible;
|
|
214
152
|
}
|
|
@@ -217,28 +155,33 @@ export class Mascot {
|
|
|
217
155
|
this._state.position = position;
|
|
218
156
|
}
|
|
219
157
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
158
|
+
// ===== Managers (Lazy Loading) =====
|
|
159
|
+
|
|
160
|
+
private _getPersonality(): MascotPersonalityManager {
|
|
161
|
+
if (!this._personality) {
|
|
162
|
+
this._personality = new MascotPersonalityManager(this.config);
|
|
163
|
+
}
|
|
164
|
+
return this._personality;
|
|
226
165
|
}
|
|
227
166
|
|
|
228
|
-
|
|
229
|
-
this.
|
|
167
|
+
private _getAppearanceManager(): MascotAppearanceManager {
|
|
168
|
+
if (!this._appearance) {
|
|
169
|
+
this._appearance = new MascotAppearanceManager(this.config);
|
|
170
|
+
}
|
|
171
|
+
return this._appearance;
|
|
230
172
|
}
|
|
231
173
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
await handler();
|
|
174
|
+
private _getAnimationManager(): MascotAnimationManager {
|
|
175
|
+
if (!this._animationManager) {
|
|
176
|
+
this._animationManager = new MascotAnimationManager(this._animations);
|
|
236
177
|
}
|
|
178
|
+
return this._animationManager;
|
|
237
179
|
}
|
|
238
180
|
|
|
239
|
-
// Utility
|
|
181
|
+
// ===== Utility =====
|
|
182
|
+
|
|
240
183
|
clone(): Mascot {
|
|
241
|
-
return new Mascot(this.
|
|
184
|
+
return new Mascot(this.config);
|
|
242
185
|
}
|
|
243
186
|
|
|
244
187
|
toJSON(): object {
|
|
@@ -246,10 +189,126 @@ export class Mascot {
|
|
|
246
189
|
id: this.id,
|
|
247
190
|
name: this.name,
|
|
248
191
|
type: this.type,
|
|
249
|
-
personality: this.
|
|
250
|
-
appearance: this.
|
|
251
|
-
state: this.
|
|
252
|
-
config: this._config,
|
|
192
|
+
personality: this.personality,
|
|
193
|
+
appearance: this.appearance,
|
|
194
|
+
state: this.state,
|
|
253
195
|
};
|
|
254
196
|
}
|
|
255
197
|
}
|
|
198
|
+
|
|
199
|
+
// ===== Manager Classes (Inline for Single Responsibility) =====
|
|
200
|
+
|
|
201
|
+
class MascotPersonalityManager {
|
|
202
|
+
constructor(private readonly _config: MascotConfig) {}
|
|
203
|
+
|
|
204
|
+
setMood(_mood: MascotMood): void {
|
|
205
|
+
// Mood validation and setting logic
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
setEnergy(value: number): void {
|
|
209
|
+
if (value < 0 || value > 1) {
|
|
210
|
+
throw new Error('Energy must be between 0 and 1');
|
|
211
|
+
}
|
|
212
|
+
// Energy setting logic
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
setFriendliness(value: number): void {
|
|
216
|
+
if (value < 0 || value > 1) {
|
|
217
|
+
throw new Error('Friendliness must be between 0 and 1');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
setPlayfulness(value: number): void {
|
|
222
|
+
if (value < 0 || value > 1) {
|
|
223
|
+
throw new Error('Playfulness must be between 0 and 1');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
cheerUp(): void {
|
|
228
|
+
this.setMood('neutral');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
boostEnergy(amount: number): void {
|
|
232
|
+
const current = this._config.personality.energy;
|
|
233
|
+
this.setEnergy(Math.min(1, current + amount));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
toDTO(): MascotPersonality {
|
|
237
|
+
return { ...this._config.personality };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class MascotAppearanceManager {
|
|
242
|
+
private _appearance: MascotAppearance;
|
|
243
|
+
|
|
244
|
+
constructor(config: MascotConfig) {
|
|
245
|
+
this._appearance = { ...config.appearance };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
update(appearance: Partial<MascotAppearance>): void {
|
|
249
|
+
this._appearance = {
|
|
250
|
+
...this._appearance,
|
|
251
|
+
...appearance,
|
|
252
|
+
accessories: appearance.accessories ?? this._appearance.accessories,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
setBaseColor(color: string): void {
|
|
257
|
+
this._appearance.baseColor = color;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
setAccentColor(color: string): void {
|
|
261
|
+
this._appearance.accentColor = color;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
addAccessory(accessory: {
|
|
265
|
+
id: string;
|
|
266
|
+
type: string;
|
|
267
|
+
color?: string;
|
|
268
|
+
position?: { x: number; y: number };
|
|
269
|
+
}): void {
|
|
270
|
+
this._appearance.accessories = [
|
|
271
|
+
...this._appearance.accessories.filter((a) => a.id !== accessory.id),
|
|
272
|
+
{
|
|
273
|
+
id: accessory.id,
|
|
274
|
+
type: accessory.type as MascotAppearance['accessories'][0]['type'],
|
|
275
|
+
color: accessory.color,
|
|
276
|
+
position: accessory.position,
|
|
277
|
+
visible: true,
|
|
278
|
+
},
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
removeAccessory(accessoryId: string): void {
|
|
283
|
+
this._appearance.accessories = this._appearance.accessories.filter(
|
|
284
|
+
(a) => a.id !== accessoryId
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
toDTO(): MascotAppearance {
|
|
289
|
+
return { ...this._appearance };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
class MascotAnimationManager {
|
|
294
|
+
constructor(private readonly _animations: Map<string, MascotAnimation>) {}
|
|
295
|
+
|
|
296
|
+
getAll(): MascotAnimation[] {
|
|
297
|
+
return Array.from(this._animations.values());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
get(id: string): MascotAnimation | undefined {
|
|
301
|
+
return this._animations.get(id);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
start(id: string): void {
|
|
305
|
+
if (!this._animations.has(id)) {
|
|
306
|
+
throw new Error(`Animation ${id} not found`);
|
|
307
|
+
}
|
|
308
|
+
// Animation start logic
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
stop(): void {
|
|
312
|
+
// Animation stop logic
|
|
313
|
+
}
|
|
314
|
+
}
|