Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Game: Create a new quiz feature #2919

Merged
merged 87 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
90694b3
add new action show_quiz
reginateh Mar 20, 2024
2ecf439
add quiz parser & quiz manager
reginateh Mar 20, 2024
9758022
reformat
reginateh Mar 20, 2024
8a0546a
CSE Machine: Remove old components and make compact components the de…
CZX123 Mar 17, 2024
491a4f3
bumping js-slang (#2852)
martin-henz Mar 17, 2024
59143df
add quiz manager
Mar 22, 2024
583f14e
create quiz result type
Mar 22, 2024
a8d37b9
hide dialogue box while showing quiz
Mar 23, 2024
eab42e5
modify character hidding feature
Mar 23, 2024
91f7976
track and display quiz results
Mar 23, 2024
023c9cb
debugging for speaker displaying
Mar 25, 2024
6d34b75
midify UI for quiz displaying
Mar 25, 2024
fd701c9
update UI for quiz display
Mar 26, 2024
85ef1fb
attempt to rebase
Mar 26, 2024
f7eaed7
small bug fix
Mar 26, 2024
c7044b5
solve quiz speaker issue
Mar 29, 2024
35dd94c
remove default reaction in QuizParser and handle no reaction situatio…
reginateh Mar 23, 2024
a740601
add a new property (boolean array) in Quiz type to record quiz result
reginateh Mar 23, 2024
0891160
rearrange quiz result
reginateh Mar 27, 2024
fe704ae
minor changes & comment
reginateh Mar 27, 2024
112b8a3
remove redundant file and console logs
reginateh Mar 29, 2024
0515c9c
save quiz status as attempted/completed arrays
reginateh Mar 29, 2024
60c8c00
add conditions to check whether a quiz is attempted/completed
reginateh Mar 29, 2024
8ac29d5
modify UI again
Apr 1, 2024
116e0f3
disable keyboard input in a quiz
reginateh Apr 1, 2024
4a9b71a
move question prompt text to QuizConstants
reginateh Apr 1, 2024
e47a20a
QuizParser error handling & add new "speaker" property to QuizType & …
reginateh Apr 1, 2024
3f93602
remove console log
reginateh Apr 1, 2024
2d6573e
display quiz result message to dialogue log
reginateh Apr 1, 2024
55d4c54
format code
reginateh Apr 1, 2024
1554798
Merge branch 'master' into game-quiz
reginateh Apr 1, 2024
7655f78
add documentation
Apr 1, 2024
a8de1e3
move quiz result message to QuizConstant
reginateh Apr 2, 2024
65e8912
change quiz condition names
reginateh Apr 2, 2024
01824e6
Add a prompt before a quiz & proceed to the next dialogue line when a…
reginateh Apr 5, 2024
a5020e4
dialogue line bug fix
Apr 5, 2024
04974d3
reformatting
Apr 10, 2024
0b8e464
Merge branch 'master' into game-quiz2
martin-henz Apr 13, 2024
2842b84
Merge branch 'master' into game-quiz2
martin-henz Apr 13, 2024
d1ba1bc
Merge branch 'master' into game-quiz2
RichDom2185 Apr 13, 2024
722bb5f
Merge branch 'master' into game-quiz2
RichDom2185 Apr 13, 2024
43c4979
add saving quizzes score
reginateh Apr 14, 2024
01ea0e0
add validation for quiz conditions parameters
reginateh Apr 14, 2024
7175104
add new condition quizScoreAtLeast to check the status of quiz scores
reginateh Apr 14, 2024
152dd68
support Interpolation of player's name in quiz questions
reginateh Apr 14, 2024
ff6eb38
minor change
reginateh Apr 14, 2024
5572aef
support interpolation of quiz scores in dialogue lines
reginateh Apr 14, 2024
66c68b2
remove default result message & minor changes
reginateh Apr 14, 2024
1df31b9
format
reginateh Apr 14, 2024
6119365
minor change
reginateh Apr 14, 2024
c107fb9
Merge branch 'game-quiz2' of https://github.com/source-academy/fronte…
reginateh Apr 14, 2024
9d8d378
Merge branch 'master' into game-quiz2
CYX22222003 Apr 15, 2024
a82e4bf
Merge branch 'master' into game-quiz2
CYX22222003 Apr 15, 2024
47bf5b2
Merge branch 'master' into game-quiz2
CYX22222003 Apr 16, 2024
a5ec8c9
Merge branch 'master' into game-quiz2
CYX22222003 Apr 19, 2024
d096489
Merge branch 'master' into game-quiz2
CYX22222003 Apr 19, 2024
3491f65
Merge branch 'master' into game-quiz2
CYX22222003 Apr 21, 2024
3c4697b
Merge branch 'master' into game-quiz2
CYX22222003 Apr 23, 2024
12a4190
Merge branch 'master' into game-quiz2
CYX22222003 May 2, 2024
ab6341e
Merge branch 'master' into game-quiz2
RichDom2185 May 3, 2024
aea3aed
Merge branch 'master' into game-quiz2
CYX22222003 May 5, 2024
9205153
Merge branch 'master' into game-quiz2
RichDom2185 May 5, 2024
7c38472
Merge branch 'master' into game-quiz2
RichDom2185 May 6, 2024
6953459
Update GameDialogueManager.ts
CYX22222003 May 12, 2024
e005eb7
Update GameSaveTypes.ts
CYX22222003 May 12, 2024
cacd2f2
Update GameStateManager.ts
CYX22222003 May 12, 2024
0b232ea
Update src/features/game/state/GameStateManager.ts
CYX22222003 May 12, 2024
725f9f8
Merge branch 'master' into game-quiz2
CYX22222003 May 12, 2024
b2eb34a
Merge branch 'master' into game-quiz2
CYX22222003 May 13, 2024
6de3681
Merge branch 'master' into game-quiz2
CYX22222003 May 13, 2024
10d5a83
Merge branch 'master' into game-quiz2
CYX22222003 May 14, 2024
e72fe77
Merge branch 'master' into game-quiz2
CYX22222003 May 16, 2024
54ec50e
Update src/features/game/dialogue/GameDialogueManager.ts
CYX22222003 May 16, 2024
1feee8b
Update GameQuizManager.ts
CYX22222003 May 16, 2024
7c5b4bd
Update GameDialogueSpeakerRenderer.ts
CYX22222003 May 16, 2024
4960cb9
Update GameQuizSpeakerRenderer.ts
CYX22222003 May 16, 2024
c455348
Update src/features/game/dialogue/GameDialogueRenderer.ts
CYX22222003 May 16, 2024
e21f292
Update GameQuizManager.ts
CYX22222003 May 16, 2024
e33cb15
Update GameQuizManager and GameQuizConstants
May 16, 2024
3ba8991
Modifying showNextLine method in GameGlobalAPI
May 16, 2024
b3f268f
Reformatting
May 16, 2024
4e639f4
Update src/features/game/parser/QuizParser.ts
CYX22222003 May 20, 2024
e79f24a
Merge branch 'master' into game-quiz2
CYX22222003 Jun 26, 2024
475c502
Merge branch 'master' into game-quiz2
CYX22222003 Jul 9, 2024
a4df53a
Merge branch 'master' into game-quiz2
martin-henz Jul 19, 2024
86da80b
Update Prompt field for game object
Aug 4, 2024
21ad801
Merge branch 'master' into game-quiz2
CYX22222003 Aug 4, 2024
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
10 changes: 10 additions & 0 deletions src/features/game/action/GameActionConditionChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ export default class ActionConditionChecker {
return GameGlobalAPI.getInstance().isObjectiveComplete(conditionParams.id) === boolean;
case GameStateStorage.TasklistState:
return GameGlobalAPI.getInstance().isTaskComplete(conditionParams.id) === boolean;
case GameStateStorage.AttemptedQuizState:
return GameGlobalAPI.getInstance().isQuizAttempted(conditionParams.id) === boolean;
case GameStateStorage.PassedQuizState:
return GameGlobalAPI.getInstance().isQuizComplete(conditionParams.id) === boolean;
case GameStateStorage.QuizScoreState:
return (
GameGlobalAPI.getInstance().getQuizScore(conditionParams.id) >=
parseInt(conditionParams.score) ===
boolean
);
default:
return true;
}
Expand Down
6 changes: 6 additions & 0 deletions src/features/game/action/GameActionExecuter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ export default class GameActionExecuter {
case GameActionType.Delay:
await sleep(actionParams.duration);
return;
case GameActionType.ShowQuiz:
globalAPI.enableKeyboardInput(false);
await globalAPI.showQuiz(actionParams.id);
globalAPI.enableKeyboardInput(true);
lhw-1 marked this conversation as resolved.
Show resolved Hide resolved
return;
default:
return;
}
Expand Down Expand Up @@ -141,6 +146,7 @@ export default class GameActionExecuter {
case GameActionType.PlaySFX:
case GameActionType.ShowObjectLayer:
case GameActionType.Delay:
case GameActionType.ShowQuiz:
return false;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/features/game/action/GameActionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export enum GameActionType {
ShowObjectLayer = 'ShowObjectLayer',
NavigateToAssessment = 'NavigateToAssessment',
UpdateAssessmentStatus = 'UpdateAssessmentStatus',
Delay = 'Delay'
Delay = 'Delay',
ShowQuiz = 'ShowQuiz'
}

/**
Expand Down
39 changes: 37 additions & 2 deletions src/features/game/dialogue/GameDialogueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ export default class DialogueManager {
});
}

private async showNextLine(resolve: () => void) {
public async showNextLine(resolve: () => void) {
lhw-1 marked this conversation as resolved.
Show resolved Hide resolved
GameGlobalAPI.getInstance().playSound(SoundAssets.dialogueAdvance.key);
const { line, speakerDetail, actionIds, prompt } =
await this.getDialogueGenerator().generateNextLine();
const lineWithName = line.replace('{name}', this.getUsername());
const lineWithQuizScores = this.makeLineWithQuizScores(line);
const lineWithName = lineWithQuizScores.replace('{name}', this.getUsername());
this.getDialogueRenderer().changeText(lineWithName);
this.getSpeakerRenderer().changeSpeakerTo(speakerDetail);

Expand All @@ -79,6 +80,7 @@ export default class DialogueManager {

// Disable interactions while processing actions
GameGlobalAPI.getInstance().enableSprite(this.getDialogueRenderer().getDialogueBox(), false);
this.getInputManager().enableKeyboardInput(false);
lhw-1 marked this conversation as resolved.
Show resolved Hide resolved

if (prompt) {
// disable keyboard input to prevent continue dialogue
Expand All @@ -94,6 +96,7 @@ export default class DialogueManager {
}
await GameGlobalAPI.getInstance().processGameActionsInSamePhase(actionIds);
GameGlobalAPI.getInstance().enableSprite(this.getDialogueRenderer().getDialogueBox(), true);
this.getInputManager().enableKeyboardInput(true);

if (!line) {
// clear keyboard listeners when dialogue ends
Expand All @@ -102,6 +105,38 @@ export default class DialogueManager {
}
}

/**
* Hide all dialogue boxes, speaker boxes and speaker sprites
* */
public async hideAll() {
await this.getDialogueRenderer().hide();
await this.getSpeakerRenderer().hide();
}

/**
* Make all dialogue boxes, speaker boxes and speaker sprites visible
* */
public async showAll() {
await this.getDialogueRenderer().show();
await this.getSpeakerRenderer().show();
}

/**
* Find patterns of quiz score interpolation in a dialogue line,
* and replace them by actual scores.
* The pattern: "{<quizId>.score}"
*
* @param line
* @returns {string} the given line with all quiz score interpolation replaced by actual scores.
*/
public makeLineWithQuizScores(line: string) {
const quizScores = line.matchAll(/\{(.+?)\.score\}/g);
for (const match of quizScores) {
line = line.replace(match[0], GameGlobalAPI.getInstance().getQuizScore(match[1]).toString());
}
return line;
}

private getDialogueGenerator = () => this.dialogueGenerator as DialogueGenerator;
private getDialogueRenderer = () => this.dialogueRenderer as DialogueRenderer;
private getSpeakerRenderer = () => this.speakerRenderer as DialogueSpeakerRenderer;
Expand Down
18 changes: 18 additions & 0 deletions src/features/game/dialogue/GameDialogueRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ class DialogueRenderer {
fadeAndDestroy(gameManager, this.getDialogueContainer());
}

/**
* Hide the dialoguebox
*/
public async hide() {
this.typewriter.container.setVisible(false);
this.dialogueBox.setVisible(false);
this.blinkingDiamond.container.setVisible(false);
}

/**
* Make the dialoguebox visible
*/
public async show() {
this.typewriter.container.setVisible(true);
this.dialogueBox.setVisible(true);
this.blinkingDiamond.container.setVisible(true);
}

/**
* Change the text written in the box
*/
Expand Down
22 changes: 22 additions & 0 deletions src/features/game/dialogue/GameDialogueSpeakerRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import DialogueConstants, { speakerTextStyle } from './GameDialogueConstants';
*/
export default class DialogueSpeakerRenderer {
private currentSpeakerId?: string;
private speakerSprite?: Phaser.GameObjects.Image;
private speakerSpriteBox?: Phaser.GameObjects.Container;

/**
* Changes the speaker shown in the speaker box and the speaker rendered on screen
Expand Down Expand Up @@ -63,6 +65,7 @@ export default class DialogueSpeakerRenderer {
expression,
speakerPosition
);
this.speakerSprite = speakerSprite;
GameGlobalAPI.getInstance().addToLayer(Layer.Speaker, speakerSprite);
}

Expand Down Expand Up @@ -90,8 +93,27 @@ export default class DialogueSpeakerRenderer {

container.add([rectangle, speakerText]);
speakerText.text = StringUtils.capitalize(text);
this.speakerSpriteBox = container;
return container;
}

/**
* Hide the speaker box and sprite
*/
public async hide() {
this.getSpeakerSprite().setVisible(false);
this.getSpeakerSpriteBox().setVisible(false);
}

/**
* Show the hidden speaker box and sprite
*/
public async show() {
this.getSpeakerSprite().setVisible(true);
this.getSpeakerSpriteBox().setVisible(true);
}

public getUsername = () => SourceAcademyGame.getInstance().getAccountInfo().name;
private getSpeakerSprite = () => this.speakerSprite as Phaser.GameObjects.Image;
private getSpeakerSpriteBox = () => this.speakerSpriteBox as Phaser.GameObjects.Container;
}
6 changes: 5 additions & 1 deletion src/features/game/layer/GameLayerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export enum Layer {
Escape,
Selector,
Dashboard,
WorkerMessage
WorkerMessage,
QuizSpeakerBox,
QuizSpeaker
}

// Back to Front
Expand All @@ -23,9 +25,11 @@ export const defaultLayerSequence = [
Layer.BBox,
Layer.Character,
Layer.Speaker,
Layer.QuizSpeaker,
Layer.PopUp,
Layer.Dialogue,
Layer.SpeakerBox,
Layer.QuizSpeakerBox,
Layer.Effects,
Layer.Dashboard,
Layer.Escape,
Expand Down
7 changes: 7 additions & 0 deletions src/features/game/location/GameMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AssetKey, ItemId } from '../commons/CommonTypes';
import { Dialogue } from '../dialogue/GameDialogueTypes';
import { GameMode } from '../mode/GameModeTypes';
import { ObjectProperty } from '../objects/GameObjectTypes';
import { Quiz } from '../quiz/GameQuizType';
import { mandatory } from '../utils/GameUtils';
import { AnyId, GameItemType, GameLocation, LocationId } from './GameMapTypes';

Expand Down Expand Up @@ -36,6 +37,7 @@ class GameMap {
private actions: Map<ItemId, GameAction>;
private gameStartActions: ItemId[];
private checkpointCompleteActions: ItemId[];
private quizzes: Map<ItemId, Quiz>;

constructor() {
this.soundAssets = [];
Expand All @@ -47,6 +49,7 @@ class GameMap {
this.boundingBoxes = new Map<ItemId, BBoxProperty>();
this.characters = new Map<ItemId, Character>();
this.actions = new Map<ItemId, GameAction>();
this.quizzes = new Map<ItemId, Quiz>();

this.gameStartActions = [];
this.checkpointCompleteActions = [];
Expand Down Expand Up @@ -120,6 +123,10 @@ class GameMap {
return this.actions;
}

public getQuizMap(): Map<ItemId, Quiz> {
return this.quizzes;
}

public getSoundAssets(): SoundAsset[] {
return this.soundAssets;
}
Expand Down
3 changes: 2 additions & 1 deletion src/features/game/location/GameMapTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ export enum GameItemType {
characters = 'characters',
actions = 'actions',
bgmKey = 'bgmKey',
collectibles = 'collectibles'
collectibles = 'collectibles',
quizzes = 'quizzes'
}
5 changes: 5 additions & 0 deletions src/features/game/parser/ActionParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ export default class ActionParser {
case GameActionType.Delay:
actionParamObj.duration = parseInt(actionParams[0]) * 1000;
break;

case GameActionType.ShowQuiz:
actionParamObj.id = actionParams[0];
Parser.validator.assertItemType(GameItemType.quizzes, actionParams[0], actionType);
break;
}

const actionId = Parser.generateActionId();
Expand Down
36 changes: 36 additions & 0 deletions src/features/game/parser/ConditionParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ActionCondition } from '../action/GameActionTypes';
import { GameItemType } from '../location/GameMapTypes';
import { GameStateStorage } from '../state/GameStateTypes';
import StringUtils from '../utils/StringUtils';
import Parser from './Parser';
Expand Down Expand Up @@ -51,6 +52,41 @@ export default class ConditionParser {
},
boolean: !hasExclamation
};

case GameStateStorage.AttemptedQuizState:
Parser.validator.assertItemType(GameItemType.quizzes, condParams[0]);
return {
state: GameStateStorage.AttemptedQuizState,
conditionParams: {
id: condParams[0]
},
boolean: !hasExclamation
};

case GameStateStorage.PassedQuizState:
Parser.validator.assertItemType(GameItemType.quizzes, condParams[0]);
return {
state: GameStateStorage.PassedQuizState,
conditionParams: {
id: condParams[0]
},
boolean: !hasExclamation
};

case GameStateStorage.QuizScoreState:
Parser.validator.assertItemType(GameItemType.quizzes, condParams[0]);
if (Number.isNaN(parseInt(condParams[1]))) {
throw new Error('Parsing error: quiz score condition requires number as second param');
}
return {
state: GameStateStorage.QuizScoreState,
conditionParams: {
id: condParams[0],
score: condParams[1]
},
boolean: !hasExclamation
};

default:
throw new Error('Parsing error: Invalid condition param');
}
Expand Down
12 changes: 11 additions & 1 deletion src/features/game/parser/DialogueParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dialogue, DialogueLine, PartName } from '../dialogue/GameDialogueTypes';
import { Dialogue, DialogueLine, DialogueObject, PartName } from '../dialogue/GameDialogueTypes';
import { GameItemType } from '../location/GameMapTypes';
import { mapValues } from '../utils/GameUtils';
import StringUtils from '../utils/StringUtils';
Expand Down Expand Up @@ -143,6 +143,16 @@ export default class DialogueParser {
}
return dialogueLines;
}
/**
* This function parses a diaglogue written in a quiz as reaction
* and returns a DialogueObject.
* Itis only called by the QuizParser.
*
* @param {Array<string>} dialogueBody the lines inside a dialogue
*/
public static parseQuizReaction(dialogueBody: string[]): DialogueObject {
return this.parseDialogueContent(dialogueBody);
}
}

const isInteger = (line: string) => new RegExp(/^[0-9]+$/).test(line);
Expand Down
4 changes: 4 additions & 0 deletions src/features/game/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DialoguesParser from './DialogueParser';
import LocationsParser from './LocationDetailsParser';
import LocationParser from './LocationParser';
import ParserValidator, { GameEntityType } from './ParserValidator';
import QuizParser from './QuizParser';
import TasksParser from './TasksParser';

/**
Expand Down Expand Up @@ -94,6 +95,9 @@ class Parser {
case 'dialogues':
DialoguesParser.parse(body);
break;
case 'quizzes':
QuizParser.parse(body);
break;
default:
return false;
}
Expand Down
8 changes: 6 additions & 2 deletions src/features/game/parser/ParserConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ const stringToActionTypeMap = {
show_object_layer: GameActionType.ShowObjectLayer,
navigate_to_assessment: GameActionType.NavigateToAssessment,
update_assessment_status: GameActionType.UpdateAssessmentStatus,
delay: GameActionType.Delay
delay: GameActionType.Delay,
show_quiz: GameActionType.ShowQuiz
};

const stringToGameStateStorageMap = {
checklist: GameStateStorage.ChecklistState,
tasklist: GameStateStorage.TasklistState,
userstate: GameStateStorage.UserState
userstate: GameStateStorage.UserState,
attemptedQuiz: GameStateStorage.AttemptedQuizState,
passedQuiz: GameStateStorage.PassedQuizState,
quizScore: GameStateStorage.QuizScoreState
};

const stringToUserStateTypeMap = {
Expand Down
Loading
Loading