File

src/app/models/exercices-juste-point.ts

Constructor

constructor(cabri: CabriIntegrationJeuJustePoint, page: JeuJustePointPage, scenario: ScenarioJeuJustePoint, cabriService: CabriDataService, lmsService: LmsService, globalService: any, ttsService: any)

Methods

updateVariables
updateVariables()
Returns: void
Public runExerciceBasket
runExerciceBasket()
Returns: void
getConsigneTTS
getConsigneTTS(secondTry: boolean)
Returns: void
Public runExerciceOnTheRoad
runExerciceOnTheRoad()
Returns: void
firstBadResponseScenario
firstBadResponseScenario(answerStatus: AnswerStatus, responseTimeInSeconds: number, award: string, help: AnswerNeedsHelp)

Scenario of the first bad response

Returns: void
checkChallengeModeRules
checkChallengeModeRules()
Returns: void
execScenarioMathiaAnim
execScenarioMathiaAnim(promiseAnim: any, promiseScenario: any, callback: any)

Promises of scenario and mascotte animation

Returns: any
runRemediationBasket
runRemediationBasket()
Returns: any
calculateResponseTime
calculateResponseTime()

Response time foor LRS

Returns: void
mathiaDisplay
mathiaDisplay(show: boolean)

Show or hide mascotte (if show scene is not displayed)

Returns: void
runExercice
runExercice()
Returns: void
manageLRS
manageLRS(result: ResultType, toggleStarboard: boolean)
Returns: void
checkRulesLrsElimination
checkRulesLrsElimination()

Check if any user eliminated after bad response before end of the activity

Returns: boolean
getAward
getAward(award: AwardsType)
Returns: string
updateExercice
updateExercice(firstTime: boolean)
Returns: void
foundQuestionValid
foundQuestionValid(seconds: any, previousConsigne: any)
Returns: void
spacedStudentRepetition
spacedStudentRepetition()
Returns: void

true if the current question could be asked

Private debug
debug(message: any, on: boolean)
Returns: void

Properties

Public answerSubscription
answerSubscription: any
cabri
cabri: CabriIntegrationJeuJustePoint
cabriService
cabriService: CabriDataService
consigneTTS
consigneTTS: string
exoType
exoType: justePointActivity
globalService
globalService: any
isChallengeMode
isChallengeMode: boolean
lmsService
lmsService: LmsService
page
page: JeuJustePointPage
scenario
scenario: ScenarioJeuJustePoint
spacedRepetitonDate
spacedRepetitonDate: any
ttsService
ttsService: any
import { PlayTTSService } from "src/app/services/play-tts.service";
import { GlobalService } from "src/app/services/global.service";
import { addWeeks, format, parseISO } from "date-fns";
import { Subscription } from "rxjs";
import { Fraction } from "../app-utils";
import { JeuJustePointPage, justePointActivity } from "../page/jeu-juste-point/jeu-juste-point.page";
import { CabriDataService } from "../services/cabri-data.service";
import { LmsService } from "../services/lms.service";
import { CabriIntegrationJeuJustePoint, ResultOnTheRoad, UserAnswer } from "./cabri-integration-jeujustepoint";
import { LrsUtils } from "./lrs/lrsUtils";
import { ScenarioJeuJustePoint } from "./scenario-jeu-juste-point";
import { AnswerNeedsHelp, AnswerStatus } from "./activity-answer";
import { AwardsType } from "./enums/awards";

declare var window: {
	store: any;
	document: any;
	innerWidth: any;
	innerHeight: any;
	outerWidth;
	outerHeight;
	dispatchEvent: any;
	CabriSceneBuilder: any;
	SceneUpdater: any;
	Hud: any;
	Globals: any;
	params: any;
	webGLCleanup: any;
	URL: any;
};

export enum ResultType {
	success,
	successOnRetry,
	fail,
	failOnFirstAttempt
}

export class ExercicesJustePoint {
	exoType: justePointActivity;

	public answerSubscription: Subscription;
	spacedRepetitonDate: any;
	consigneTTS: string;

	constructor(
		public cabri: CabriIntegrationJeuJustePoint,
		public page: JeuJustePointPage,
		public scenario: ScenarioJeuJustePoint,
		public cabriService: CabriDataService,
		public lmsService: LmsService,
		public globalService: GlobalService,
		public ttsService: PlayTTSService
	) {}

	updateVariables() {
		this.cabriService.currentActivity.updateVariables(this.cabriService.getCurrentExercice());
		this.exoType = this.cabriService.currentActivity.currentVariables.activity;
		let expectedResult: any;
		const nbVariablesArray = Object.entries(this.cabriService.currentActivity.currentVariables).filter(
			key => key[0].includes("nb") && !key[0].includes("nbM")
		);

		this.cabriService.currentActivity.currentVariables.nb1 = Number(this.cabriService.currentActivity.currentVariables.nb1);
		this.cabriService.currentActivity.currentVariables.unitStep = this.cabriService.currentActivity.currentVariables.pas;
		if (nbVariablesArray.length > 1) {
			expectedResult = [];
			nbVariablesArray.forEach(key => {
				expectedResult.push(Number(key[1]));
			});
		} else {
			this.cabri.basketUnitStepPrecision = this.cabri.getStepPrecision(this.cabriService.currentActivity.currentVariables.unitStep);

			if (nbVariablesArray[0][1] instanceof Fraction) {
				expectedResult = nbVariablesArray[0][1];
			} else {
				expectedResult = Number(
					Number(nbVariablesArray[0][1]).toFixed(this.cabri.displayedNumberPrecision(Number(nbVariablesArray[0][1])))
				);
			}
		}

		this.cabriService.currentActivity.currentVariables.expectedResult = expectedResult;

		this.page.variable = this.cabriService.currentActivity.currentVariables;

		this.page.variable.chooseExactNumber = (this.page.variable.chooseExactNumber as any) === 0 ? false : true;
		if (!this.page.variable.chooseExactNumber && !this.page.variable.approximation) {
			this.page.variable.approximation = 1;
		}
	}

	public async runExerciceBasket() {
		this.page.checkIfSonicMode();
		this.getConsigneTTS();
		this.ttsService.playTTSEventProtected(this.consigneTTS);
		await this.cabri.displayBasketBoxRandomly();
		// WAIT USER ANSWER
		this.answerSubscription = this.cabri.waitUserAnswer.subscribe(async (userAnswer: UserAnswer) => {
			await this.ttsService.killSpeech();
			this.page.displayActivityElements(false);
			this.cabriService.participantAnswer = userAnswer?.response;
			if (userAnswer.success) {
				let scenario;
				const animation = this.page.runAnimSuccess();
				if (this.page.failed) {
					// SUCCESS 2ND TRY
					this.manageLRS(ResultType.successOnRetry);
					if (this.page.sonicAward) {
						scenario = this.scenario.playSoundWithAward(AwardsType.normal);
					} else {
						this.page.audioService.playStarSound();
						scenario = this.scenario.dynamicFeedback(AnswerStatus.VALID2ND, LrsUtils.responseTime, AwardsType.normal);
					}
					await this.execScenarioMathiaAnim(animation, scenario);
				} else {
					// SUCCESS 1ST TRY
					this.manageLRS(ResultType.success);
					if (this.page.sonicAward) {
						scenario = this.scenario.playSoundWithAward(AwardsType.shooting);
					} else {
						this.page.audioService.playStarSound();
						scenario = this.scenario.dynamicFeedback(AnswerStatus.VALID1ST, LrsUtils.responseTime, AwardsType.shooting);
					}
					await this.execScenarioMathiaAnim(animation, scenario);
				}
				this.page.failed = false;
				this.page.detectChanges();
				this.page.newQuestion();
			} else {
				this.page.audioService.playAwardMoonSound();
				if (!this.page.failed) {
					// ERROR 1ST TRY - NEED HELP
					this.manageLRS(ResultType.failOnFirstAttempt);
					const animation = this.page.runAnimError();
					const scenario = this.firstBadResponseScenario(AnswerStatus.ERROR1ST, LrsUtils.responseTime, null, AnswerNeedsHelp.YES);
					await this.execScenarioMathiaAnim(animation, scenario);
					if (this.isChallengeMode) {
						this.checkChallengeModeRules();
						this.page.failed = false;
					} else {
						await this.runRemediationBasket();
						this.page.failed = true;
					}
						if(!this.isChallengeMode){
							this.page.displayMathia(false);
							this.page.displayActivityElements(true);
							this.page.detectChanges();
							this.getConsigneTTS(true);
							this.ttsService.playTTSEventProtected(this.consigneTTS);
						}
				} else {
					// ERROR 2ND TRY
					this.page.failed = false;
					this.manageLRS(ResultType.fail);
					const animation = this.page.runAnimError();
					const scenario = this.page.scenario.badResponseMoonWithResultBasketDynamic(
						AnswerStatus.ERROR2ND,
						LrsUtils.responseTime,
						AwardsType.moon
					);
					await this.execScenarioMathiaAnim(animation, scenario);
					this.page.displayActivityElements(true);
					this.page.newQuestion();
				}
			}
		});
	}

	getConsigneTTS(secondTry?: boolean) {
		const virgule = $localize`virgule`;
		const kilometre = $localize`kilomètre`;
		switch (this.exoType) {
			case justePointActivity.basket:
				this.consigneTTS = this.cabri
					.getConsigneExerciceBasketTTS(secondTry)
					.reduce((p, n) => p + "!" + n)
					.replace(/\./g, " "+ virgule +" ");
				break;
			case justePointActivity.onTheRoad:
				// injected in cabri.buildElementActivityOnTheRoad()
				break;
		}
	}

	public async runExerciceOnTheRoad() {
		this.ttsService.playTTSEventProtected(this.consigneTTS);
		// WAIT USER ANSWER
		this.answerSubscription = this.cabri.waitUserAnswer.subscribe(async (userAnswer: UserAnswer) => {
			await this.page.ttsService.killSpeech();
			this.cabri.toggleGui(false);
			// traiter la réponse
			if (userAnswer.success) {
				let waitScenario;
				const promiseSuccess = this.page.runAnimSuccess();
				if (this.page.failed) {
					// SUCCESS 2ND TRY
					this.manageLRS(ResultType.successOnRetry, false);
					this.page.checkIfSonicMode();
					if (this.page.sonicAward) {
						waitScenario = this.scenario.playSoundWithAward(AwardsType.normal);
					} else {
						this.page.audioService.playStarSound();
						waitScenario = this.scenario.dynamicFeedback(AnswerStatus.VALID2ND, LrsUtils.responseTime, AwardsType.normal);
					}
					await this.execScenarioMathiaAnim(promiseSuccess, waitScenario);
				} else {
					// SUCCESS 1ST TRY
					this.manageLRS(ResultType.success, false);
					this.page.checkIfSonicMode();
					if (this.page.sonicAward) {
						waitScenario = this.scenario.playSoundWithAward(AwardsType.shooting);
					} else {
						this.page.audioService.playStarSound();
						waitScenario = this.scenario.dynamicFeedback(AnswerStatus.VALID1ST, LrsUtils.responseTime, AwardsType.shooting);
					}
					await this.execScenarioMathiaAnim(promiseSuccess, waitScenario);
				}
				this.page.failed = false;
				this.page.displayCM = false;
				this.page.detectChanges();
				this.page.newQuestion();
			} else {
				this.page.audioService.playAwardMoonSound();
				if (!this.page.failed) {
					// ERROR 1ST TRY - NEED HELP
					this.manageLRS(ResultType.failOnFirstAttempt, false);
					const promiseError = this.page.runAnimError(false);
					const scenario = this.firstBadResponseScenario(AnswerStatus.ERROR1ST, LrsUtils.responseTime, null, AnswerNeedsHelp.YES);
					await this.execScenarioMathiaAnim(promiseError, scenario);
					this.page.displayMathia(false);
					await this.scenario.showErrorCar(userAnswer.response as ResultOnTheRoad);
					if (this.isChallengeMode) {
						this.checkChallengeModeRules();
						this.page.failed = false;
					} else {
						if (userAnswer.reversed) {
							await this.scenario.runRemediationRoadReverse();
						} else {
							await this.scenario.runRemediationRoad();
						}
						this.cabri.toggleGui(true);
						this.page.cabri.highlightCar(false, userAnswer.response as ResultOnTheRoad);
						this.page.failed = true;
					}
					this.page.detectChanges();
					this.cabri.resetAllCarPosition(userAnswer.response as ResultOnTheRoad);
				} else {
					// ERROR 2ND TRY
					this.page.failed = false;
					this.manageLRS(ResultType.fail, false);
					await this.scenario.badResponseMoonWithResultNextQRoadDynamic(userAnswer.response as ResultOnTheRoad,
						AnswerStatus.ERROR2ND,
						LrsUtils.responseTime,
						AwardsType.moon);
					this.page.newQuestion();
				}
			}
		});
	}

	/**
	 * Scenario of the first bad response
	 */
	 firstBadResponseScenario(answerStatus: AnswerStatus, responseTimeInSeconds: number, award?: string, help?: AnswerNeedsHelp) {
		let currentScenario: Promise<void>;
		if (Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 2) {
			// MODE D2FI
			currentScenario = this.scenario.eliminationChallengeMode();
		} else {
			if (this.exoType === justePointActivity.basket) {
				currentScenario = this.scenario.dynamicFeedback(answerStatus, responseTimeInSeconds, award, help );
			} else {
				currentScenario = this.scenario.dynamicFeedback(answerStatus, responseTimeInSeconds, award, help );
			}
		}
		return currentScenario;
	}

	checkChallengeModeRules() {
		if (this.page.currentTeam.length === 1) {
			this.page.endActivity();
		} else if (this.page.currentTeam.length > 1) {
			this.page.currentEliminatedPlayerId = Number(this.page.currentUser.playerId);
			this.page.newQuestion();
		}
	}

	get isChallengeMode() {
		return Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 2;
	}

	/**
	 * Promises of scenario and mascotte animation
	 */
	execScenarioMathiaAnim(promiseAnim: Promise<any>, promiseScenario: Promise<any>, callback?): Promise<void> {
		return new Promise<void>(resolve => {
			Promise.all([promiseAnim, promiseScenario]).then(async () => {
				if (callback) {
					await callback();
				}
				resolve();
			});
		});
	}

	async runRemediationBasket(): Promise<void> {
		return new Promise<void>(async (resolve, reject) => {
			this.page.activateHelp = true;
			this.page.cabriService.unitsHelp = true;
			this.scenario.runResumedRemediationBasket().then(() => {
				if (this.page.isActivePage()) {
					this.page.validationType = "units_help_validation";
					this.page.cabriService.unitsHelp = this.page.globalService.unitsHelpCMValidation = true;
					this.page.validateAnswer(null, null, true, true).then(async validationData => {
						this.page.cabri.resetMeshesAfterRemediation();
						this.page.validationActive = false;
						this.page.globalService.unitsHelpCMValidation = false;
						this.page.detectChanges();
						if (validationData === true) {
							this.page.cabriService.hideMathia = true;
							this.page.activateHelp = false;
							this.cabriService.unitsHelp = false;
							this.page.detectChanges();
							// END
							resolve();
						} else if (validationData === false) {
							this.page.activateHelp = true;
							this.page.cabriService.hideMathia = false;
							await this.scenario.runCompleteRemediationBasket();
							this.page.activateHelp = false;
							this.page.cabri.resetMeshesAfterRemediation();
							this.page.detectChanges();
							// this.toggleUnitsTutorialCore(resolve, reject, status);
						}
						this.page.validationType = "";
						this.page.cabriService.unitsHelp = this.page.globalService.unitsHelpCMValidation = false;
						resolve();
					});
					this.page.detectChanges();
				} else {
					this.page.validationActive = false;
					this.page.globalService.unitsHelpCMValidation = false;
					this.cabriService.unitsHelp = false;
					this.page.activateHelp = false;
					resolve();
				}
			});
		});
	}

	/**
	 * Response time foor LRS
	 */
	calculateResponseTime() {
		const timestampDiff = Date.now() - LrsUtils.currentUserResponseTime;
		const millis = Math.floor(timestampDiff / 1000);
		LrsUtils.responseTime = millis;
	}

	/**
	 * Show or hide mascotte (if show scene is not displayed)
	 */
	mathiaDisplay(show: boolean) {
		if (show) {
			this.cabri.displayMeshes(false);
			this.page.displayExceptedResult = false;
			this.page.displayCM = true;
			this.cabriService.hideMathia = false;
		} else {
			this.cabri.displayMeshes(true);
			this.page.displayExceptedResult = true;
			this.page.displayCM = false;
			this.cabriService.hideMathia = false;
		}
		this.page.detectChanges();
	}

	async runExercice() {
		switch (this.exoType) {
			case justePointActivity.basket:
				await this.runExerciceBasket();
				break;
			case justePointActivity.onTheRoad:
				this.runExerciceOnTheRoad();
		}
	}

	manageLRS(result: ResultType, toggleStarboard = true) {
		this.calculateResponseTime();
		switch (result) {
			case ResultType.success:
				this.page.manageLrsOperation("passed");
				this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.shooting));
				break;
			case ResultType.successOnRetry:
				this.page.manageLrsOperation("passed-with-help");
				this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.normal));
				break;
			case ResultType.fail:
				// this.page.manageLrsOperation("failed");
				this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.moon));
				break;
			case ResultType.failOnFirstAttempt:
				const isEliminated = this.checkRulesLrsElimination();
				if (isEliminated) {
					this.page.manageLrsOperation("failed-on-first-attempt", true, true);
				} else {
					this.page.manageLrsOperation("failed-on-first-attempt");
				}
				break;
		}
		if (result !== ResultType.failOnFirstAttempt && toggleStarboard) {
			this.page.showHideAward();
		}
	}

	/**
	 * Check if any user eliminated after bad response before end of the activity
	 */
	checkRulesLrsElimination(): boolean {
		let anyUserEliminated = false;
		if (this.isChallengeMode) {
			anyUserEliminated = this.page.currentTeam.length > 1 && !this.page.endOfActivity();
		}
		return anyUserEliminated;
	}

	getAward(award: AwardsType): string {
		let currentAward = award;
		// exceptions
		if (Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 0) {
			// MODE DECOUVERTE
			if (award === AwardsType.shooting) {
				currentAward = AwardsType.normal;
			}
		}
		return currentAward;
	}

	async updateExercice(firstTime = false) {
		return new Promise((resolve, reject) => {
			// console.error("update exercice");
			const previousConsigne = this.cabri.getCurrentOperation();

			const exoType = this.updateVariables();
			// force numpad mode for certain exercises
			// update time for spaced repetition
			const date = new Date();
			const seconds = (date.getTime() - this.spacedRepetitonDate.getTime()) / 1000;

			if (this.foundQuestionValid(seconds, previousConsigne)) {
				this.spacedRepetitonDate = new Date();
				resolve(exoType);
			} else {
				setTimeout(async () => {
					resolve(await this.updateExercice());
				}, 30);
			}
		});
	}

	foundQuestionValid(seconds, previousConsigne) {
		if (this.lmsService.currentUserJourney) {
			// mode parcours
			if (!this.spacedStudentRepetition() && seconds <= 3) {
				return false;
			} else {
				if (seconds >= 3 && this.lmsService.currentUserJourney.allAskedQuestions) {
					this.lmsService.currentUserJourney.allAskedQuestions = new Array();
				}
			}
		} else {
			// mode exercice
			const posInd = this.page.indexAlreadyAskedQuestion(this.cabri.getCurrentOperation(), previousConsigne);
			if (posInd > -1 && seconds <= 3) {
				return false;
			} else if (seconds >= 3 && this.page.questionAskedDuringSession) {
				this.page.questionAskedDuringSession = new Array();
			}
		}
		return true;
	}

	/**
	 * @returns true if the current question could be asked
	 */
	spacedStudentRepetition() {
		let spacedRepetition = true;

		const countCorrectAnswers = this.page.checkQuestionAskedDuringSession(this.cabri.getCurrentOperation());
		if (countCorrectAnswers !== 0) {
			const questionAwards = this.lmsService.currentUserJourney.allAskedQuestions.filter(questionAward => {
				return (
					questionAward.question &&
					(questionAward.question.trim() === this.cabri.getCurrentOperation().trim() ||
						this.cabri.getCurrentOperation().trim() === questionAward.question.trim().split("").reverse().join("")) &&
					typeof questionAward.award === "string"
				);
			});
			const lastTimePassedQuestion = questionAwards[questionAwards.length - 1];
			if (!lastTimePassedQuestion) {
				spacedRepetition = true;
			} else {
				const lastAnswerDate = lastTimePassedQuestion.timestamp;
				const weekCount = Math.pow(2, countCorrectAnswers - 1);
				const recurrenceWeek: string = format(addWeeks(parseISO(lastAnswerDate), weekCount), "T");
				spacedRepetition = Number(format(new Date(), "T")) > Number(recurrenceWeek);
			}
			return spacedRepetition;
		} else {
			// question never been asked
			return true;
		}
	}
	private debug(message, on = true) {
		if (on) {
			console.error(message);
		}
	}
}

results matching ""

    No results matching ""