Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
31d049c
feat: Implemented Scheduler System for the gameplay pause mechanic
janfb-codev Jan 14, 2026
e230fc7
fix: updated minigame animation to use gameplay-scene deltaTime inste…
janfb-codev Jan 15, 2026
f959303
feat: added playUIAudio in the audio-player to separate nonPausable a…
janfb-codev Jan 16, 2026
1706270
Merge branch 'develop' into feature/FM-806
janfb-codev Jan 16, 2026
a3f34e3
chore: fixed unit testing affected by scheduler change
janfb-codev Jan 20, 2026
f2b7b4d
chore: removed comments
janfb-codev Jan 20, 2026
ea939ee
chore: added comments regarding scheduler class
janfb-codev Jan 20, 2026
50e743e
fix: removed visibility change handling on stone-handler and prompt-text
janfb-codev Jan 21, 2026
6ba6ec6
Merge branch 'develop' into feature/FM-806
janfb-codev Jan 22, 2026
d767eef
fix: fix for Timer Rotation not resuming after pausing before the tim…
janfb-codev Jan 22, 2026
d44fafe
chore: added TODO for scheduler after migrating to Core Modules
janfb-codev Jan 22, 2026
b74d323
chore: added unit testing for scheduler
janfb-codev Jan 22, 2026
380ca10
chore: moved importing scheduler to its alias @services/scheduler
janfb-codev Jan 22, 2026
61fa02d
chore: removed unused code
janfb-codev Jan 22, 2026
26f7c56
chore: added comments in audio-player regarding the new functionalities
janfb-codev Jan 22, 2026
58eacac
chore: fixed unit testing regarding changes on how to import scheduler
janfb-codev Jan 22, 2026
fb2358b
fix: fix for audio are missing when switching scenes
janfb-codev Jan 28, 2026
9638236
fix: fix on non tutorial levels the timeRef is incorrect
janfb-codev Jan 28, 2026
ddebbdc
fix: added pause event for the prompt text
janfb-codev Jan 29, 2026
484d510
chore: edited some comments
janfb-codev Jan 29, 2026
58f4043
chore: removed unused code
janfb-codev Jan 29, 2026
bf6e01d
chore: added safe delay for setInterval in Scheduler
janfb-codev Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
"^@gamepuzzles(.*)$": "<rootDir>/src/gamepuzzles$1",
"@gameSettingsService/*": ["<rootDir>/src/gameSettingsService/$1"],
"@tutorials/*": ["<rootDir>/src/tutorials/$1"],
"@services/(.*)": "<rootDir>/src/services/$1",
"^@miniGames(.*)$": ["<rootDir>/src/miniGame/miniGames/$1"],
"^@miniGameStateService(.*)$": ["<rootDir>/src/miniGame/miniGameStateService/$1"],
"@curiouslearning/analytics": "<rootDir>/node_modules/@curiouslearning/analytics/dist/index.js"
Expand Down
38 changes: 19 additions & 19 deletions src/common/stone-config/stone-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,19 @@ describe('StoneConfig', () => {
stoneConfig.initialize();
stoneConfig['animationStartTime'] = 0;
now = 0;
stoneConfig.draw();
stoneConfig.draw(0);
expect(stoneConfig.getX()).toBe(0);

// 25% through animation
now = 250;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 25; // Explicitly set frame to match elapsed time
const expected25X = targetX * (1 - Math.cos(Math.PI * 0.25)) / 2;
expect(stoneConfig.getX()).toBeCloseTo(expected25X, 0);

// Complete animation
now = 1000;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 100; // Explicitly set frame for completion
expect(stoneConfig.getX()).toBe(targetX);
});
Expand All @@ -109,19 +109,19 @@ describe('StoneConfig', () => {
stoneConfig.initialize();
stoneConfig['animationStartTime'] = 0;
now = 0;
stoneConfig.draw();
stoneConfig.draw(0);
expect(stoneConfig.getY()).toBe(0);

// 25% through animation
now = 250;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 25; // Explicitly set frame to match elapsed time
const expected25Y = targetY * (1 - Math.cos(Math.PI * 0.25)) / 2;
expect(stoneConfig.getY()).toBeCloseTo(expected25Y, 0);

// Complete animation
now = 1000;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 100; // Explicitly set frame for completion
expect(stoneConfig.getY()).toBe(targetY);
});
Expand All @@ -137,15 +137,15 @@ describe('StoneConfig', () => {
stoneConfig.initialize();
stoneConfig['animationStartTime'] = 0;
now = 0;
stoneConfig.draw();
stoneConfig.draw(0);
expect(stoneConfig.getX()).toBe(0);
expect(stoneConfig.getY()).toBe(0);

// Check multiple points in animation
const checkPoints = [0.25, 0.5, 0.75];
checkPoints.forEach(progress => {
now = progress * 1000;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = progress * 100; // Set frame to match progress

const expectedX = targetX * (1 - Math.cos(Math.PI * progress)) / 2;
Expand All @@ -168,7 +168,7 @@ describe('StoneConfig', () => {
const drawImageSpy = jest.spyOn(mockContext, 'drawImage');
const fillTextSpy = jest.spyOn(mockContext, 'fillText');

stoneConfig.draw();
stoneConfig.draw(0);

// Verify essential drawing operations
expect(drawImageSpy).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -196,32 +196,32 @@ describe('StoneConfig', () => {
now = 0;

// First draw to start animation
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 0;
expect(stoneConfig.frame).toBe(0);

// 25% through animation (250ms / 1000ms * 100 = 25)
now = 250;
stoneConfig.draw();
stoneConfig.draw(0);
// Manually set frame since we're mocking time
stoneConfig.frame = Math.floor((now / 1000) * 100);
expect(stoneConfig.frame).toBe(25);

// 50% through animation
now = 500;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = Math.floor((now / 1000) * 100);
expect(stoneConfig.frame).toBe(50);

// Complete animation
now = 1000;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 100;
expect(stoneConfig.frame).toBe(100);

// Past completion
now = 1500;
stoneConfig.draw();
stoneConfig.draw(0);
expect(stoneConfig.frame).toBe(100); // Should stay at 100
});

Expand All @@ -231,25 +231,25 @@ describe('StoneConfig', () => {
now = 0;

// First draw to start animation
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 0;
expect(stoneConfig.frame).toBe(0);

// Near completion
now = 990;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = Math.floor((now / 1000) * 100);
expect(stoneConfig.frame).toBe(99);

// Just past completion
now = 1010;
stoneConfig.draw();
stoneConfig.draw(0);
stoneConfig.frame = 100;
expect(stoneConfig.frame).toBe(100);

// Well past completion
now = 2000;
stoneConfig.draw();
stoneConfig.draw(0);
expect(stoneConfig.frame).toBe(100);
});
});
Expand All @@ -264,7 +264,7 @@ describe('StoneConfig', () => {
const drawImageSpy = jest.spyOn(mockContext, 'drawImage');

stoneConfig.dispose();
stoneConfig.draw();
stoneConfig.draw(0);

expect(drawImageSpy).not.toHaveBeenCalled();
});
Expand Down
11 changes: 3 additions & 8 deletions src/common/stone-config/stone-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export class StoneConfig {
public frame: number = 0;
public isDisposed: boolean = false;
// Performance optimization: Use time-based animation for smoother movement
private animationStartTime: number = 0;
private animationDuration: number = 1500; // 1.5 second animation
public scale = gameSettingsService.getDevicePixelRatioValue();
constructor(context, canvasWidth, canvasHeight, stoneLetter, xPos, yPos, img) {
Expand All @@ -43,7 +42,6 @@ export class StoneConfig {
public initialize() {
this.frame = 0;
this.isDisposed = false;
this.animationStartTime = 0;
}

public get isAnimating(): boolean {
Expand Down Expand Up @@ -110,16 +108,13 @@ export class StoneConfig {
* - Uses time-based animation for smooth movement
* - Only applies effects when necessary
*/
draw(shouldResize: boolean = false) {
draw(deltaTime: number, shouldResize: boolean = false) {
if (this.isDisposed || !this.img || !this.context) return;

// Update animation based on actual time elapsed
if (this.frame < 100) {
if (this.animationStartTime === 0) {
this.animationStartTime = performance.now();
}
const elapsed = performance.now() - this.animationStartTime;
this.frame = Math.min(100, (elapsed / this.animationDuration) * 100);
this.frame += (deltaTime / this.animationDuration) * 100;
this.frame = Math.min(100, this.frame);
}
//shouldResize is used when stone letters are grouped together when playing word puzzle game types.
const x = this.getX() - (shouldResize ? this.imageCenterOffsetX * 1.25 : this.imageCenterOffsetX);
Expand Down
2 changes: 2 additions & 0 deletions src/components/__mocks__/audio-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const mockAudioPlayerInstance = {
stopFeedbackAudio: jest.fn(),
preloadPromptAudio: jest.fn(),
handlePlayPromptAudioClickEvent: jest.fn(),
pauseAllAudios: jest.fn(),
resumeAllAudios: jest.fn(),
audioContext: {
createBufferSource: jest.fn(() => ({
connect: jest.fn(),
Expand Down
Loading