Cleaning
This commit is contained in:
@@ -136,7 +136,11 @@ def _to_snake_case(name: str) -> str:
|
||||
def _to_pascal_case(name: str) -> str:
|
||||
prefix = '_' if name.startswith('_') else ''
|
||||
core = name.lstrip('_')
|
||||
return prefix + ''.join(w.capitalize() for w in re.split(r'[_\s]+', core) if w)
|
||||
# Split on camelCase boundaries first, then on underscores/spaces
|
||||
# e.g. 'myBadEnum' → 'my_Bad_Enum' → 'MyBadEnum'
|
||||
snake = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', core)
|
||||
snake = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', snake)
|
||||
return prefix + ''.join(w.capitalize() for w in re.split(r'[_\s]+', snake) if w)
|
||||
|
||||
|
||||
def _to_upper_snake_case(name: str) -> str:
|
||||
@@ -148,31 +152,63 @@ def _to_upper_snake_case(name: str) -> str:
|
||||
|
||||
|
||||
CONVENTION_NAMES: dict[str, str] = {
|
||||
'class': 'PascalCase',
|
||||
'function': 'snake_case',
|
||||
'variable': 'snake_case',
|
||||
'constant': 'UPPER_SNAKE_CASE',
|
||||
'signal': 'snake_case',
|
||||
'class': 'PascalCase',
|
||||
'enum_name': 'PascalCase',
|
||||
'enum_member': 'UPPER_SNAKE_CASE',
|
||||
'function': 'snake_case',
|
||||
'variable': 'snake_case',
|
||||
'constant': 'UPPER_SNAKE_CASE',
|
||||
'signal': 'snake_case',
|
||||
}
|
||||
|
||||
SUGGESTION_FN: dict = {
|
||||
'class': _to_pascal_case,
|
||||
'function': _to_snake_case,
|
||||
'variable': _to_snake_case,
|
||||
'constant': _to_upper_snake_case,
|
||||
'signal': _to_snake_case,
|
||||
'class': _to_pascal_case,
|
||||
'enum_name': _to_pascal_case,
|
||||
'enum_member': _to_upper_snake_case,
|
||||
'function': _to_snake_case,
|
||||
'variable': _to_snake_case,
|
||||
'constant': _to_upper_snake_case,
|
||||
'signal': _to_snake_case,
|
||||
}
|
||||
|
||||
NAMING_KINDS = frozenset({'class', 'function', 'variable', 'constant', 'signal'})
|
||||
NAMING_KINDS = frozenset({'class', 'enum_name', 'enum_member', 'function', 'variable', 'constant', 'signal'})
|
||||
|
||||
# ─── Naming check ─────────────────────────────────────────────────────────────
|
||||
|
||||
def _check_enum_member(path: str, idx: int, name: str) -> None:
|
||||
"""Flag a single enum member name if it is not UPPER_SNAKE_CASE."""
|
||||
if name and not UPPER_SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'enum_member', name))
|
||||
|
||||
|
||||
def check_naming(path: str, lines: list[str]):
|
||||
in_enum = False # True while scanning the body of a multi-line enum
|
||||
|
||||
for idx, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#') or stripped.startswith('@warning_ignore(') or not stripped:
|
||||
continue
|
||||
|
||||
# ── Enum-body lines ────────────────────────────────────────────────────
|
||||
if in_enum:
|
||||
close = stripped.find('}')
|
||||
if close != -1:
|
||||
in_enum = False
|
||||
# Any identifiers before the closing brace on this line
|
||||
before = stripped[:close]
|
||||
for part in before.split(','):
|
||||
m = re.match(r"\s*([A-Za-z0-9_]+)", part)
|
||||
if m:
|
||||
_check_enum_member(path, idx, m.group(1))
|
||||
else:
|
||||
# One (or more) members on this line: "NAME," or "NAME = val,"
|
||||
for part in stripped.split(','):
|
||||
m = re.match(r"\s*([A-Za-z0-9_]+)", part)
|
||||
if m:
|
||||
_check_enum_member(path, idx, m.group(1))
|
||||
continue
|
||||
# ── End enum-body ──────────────────────────────────────────────────────
|
||||
|
||||
match_class = re.match(r"class_name\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_class:
|
||||
name = match_class.group(1)
|
||||
@@ -208,6 +244,26 @@ def check_naming(path: str, lines: list[str]):
|
||||
if not UPPER_SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'constant', name))
|
||||
|
||||
match_enum = re.match(r"enum\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_enum:
|
||||
name = match_enum.group(1)
|
||||
if not PASCAL_CASE.match(name):
|
||||
issues.append((path, idx, 'enum_name', name))
|
||||
# Determine whether the enum body is inline or multi-line
|
||||
brace = stripped.find('{')
|
||||
if brace != -1:
|
||||
rest = stripped[brace + 1:]
|
||||
close = rest.find('}')
|
||||
if close != -1:
|
||||
# Inline enum — check members immediately
|
||||
for part in rest[:close].split(','):
|
||||
m = re.match(r"\s*([A-Za-z0-9_]+)", part)
|
||||
if m:
|
||||
_check_enum_member(path, idx, m.group(1))
|
||||
else:
|
||||
# Body continues on following lines
|
||||
in_enum = True
|
||||
|
||||
match_signal = re.match(r"signal\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_signal:
|
||||
name = match_signal.group(1)
|
||||
@@ -499,11 +555,23 @@ def categorize(kind: str) -> str:
|
||||
return 'other'
|
||||
|
||||
|
||||
_KIND_LABEL: dict[str, str] = {
|
||||
'class': 'class names',
|
||||
'enum_name': 'enum names',
|
||||
'enum_member': 'enum member names',
|
||||
'function': 'function names',
|
||||
'variable': 'variable names',
|
||||
'constant': 'constant names',
|
||||
'signal': 'signal names',
|
||||
}
|
||||
|
||||
|
||||
def format_message(kind: str, data) -> str:
|
||||
if kind in NAMING_KINDS:
|
||||
suggestion = SUGGESTION_FN[kind](data)
|
||||
conv = CONVENTION_NAMES[kind]
|
||||
return f"'{data}' should be '{suggestion}' ({kind}s must be {conv})"
|
||||
label = _KIND_LABEL.get(kind, f"{kind}s")
|
||||
return f"'{data}' should be '{suggestion}' ({label} must be {conv})"
|
||||
if kind == 'error':
|
||||
return str(data)
|
||||
template = MESSAGES.get(kind, kind)
|
||||
|
||||
@@ -404,7 +404,7 @@ VALIDATE,Confirmer,Confirmar,Confirmar,Conferma
|
||||
SUMMARY_EMAIL,Adresse email : {mail},Correo electrónico: {mail},Endereço de e-mail : {mail},Indirizzo email: {mail}
|
||||
SUMMARY_TYPE,Type de compte : {type},Tipo de cuenta: {type},Tipo de conta : {type},Tipo di account: {type}
|
||||
SUMMARY_METHOD,Méthode d'éducation : {method},Método de enseñanza: {method},Método educacional : {method},Metodo educativo: {method}
|
||||
APPONLY,Application uniquement,Solo aplicación,Somente aplicativo,Solo applicazione
|
||||
APP_ONLY,Application uniquement,Solo aplicación,Somente aplicativo,Solo applicazione
|
||||
COMPLETE,Complète (livrets et jeux papiers),Completo (cuaderno y juegos en papel),Completo (livretos e jogos de papel),Completo (libretti e giochi cartacei)
|
||||
SUMMARY_NUMBER_OF_DEVICES,Nombre d'appareils : {number},Cantidad de dispositivos: {number},Número de dispositivos : {number},Numero di dispositivi: {number}
|
||||
SUMMARY_NUMBER_OF_STUDENTS,Nombre d'élèves : {number},Cantidad de estudiantes: {number},Número de alunos : {number},Numero di studenti: {number}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 41 and column 273.
|
@@ -2,7 +2,7 @@
|
||||
class_name Garden
|
||||
extends Control
|
||||
|
||||
const BACKGROUND_PATH_MODEL: String = "res://assets/gardens/gardens/Garden_%02d.png"
|
||||
const BACKGROUND_PATH_MODEL: String = "res://assets/gardens/gardens/garden_%02d.png"
|
||||
const MAX_LESSONS: int = 5
|
||||
const PLANT_COUNT: int = 8
|
||||
# Maps lesson count → which slot indices to use
|
||||
|
||||
@@ -3,15 +3,15 @@ class_name GardenLayout
|
||||
extends Resource
|
||||
|
||||
enum FirstOrLast {
|
||||
First,
|
||||
Neither,
|
||||
Last
|
||||
FIRST,
|
||||
NEITHER,
|
||||
LAST
|
||||
}
|
||||
|
||||
@export var color: int = 0
|
||||
@export var lesson_buttons_export: Array[Dictionary] = []:
|
||||
set = set_lesson_buttons_export
|
||||
@export var is_first_or_last: FirstOrLast = FirstOrLast.Neither
|
||||
@export var is_first_or_last: FirstOrLast = FirstOrLast.NEITHER
|
||||
|
||||
var lesson_buttons: Array[GardenLayoutLessonButton] = []:
|
||||
set = set_lesson_buttons
|
||||
|
||||
@@ -2,14 +2,14 @@ class_name StudentData
|
||||
extends Resource
|
||||
|
||||
enum Level {
|
||||
Beginner,
|
||||
Reviewer,
|
||||
Adult
|
||||
BEGINNER,
|
||||
REVIEWER,
|
||||
ADULT
|
||||
}
|
||||
|
||||
@export var code: int = 0
|
||||
@export var name: String = ""
|
||||
@export var level: Level = Level.Beginner
|
||||
@export var level: Level = Level.BEGINNER
|
||||
@export var age: int = 0
|
||||
@export var last_modified: String = ""
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ extends Resource
|
||||
signal progression_changed()
|
||||
|
||||
enum Status{
|
||||
Locked,
|
||||
Unlocked,
|
||||
Completed,
|
||||
LOCKED,
|
||||
UNLOCKED,
|
||||
COMPLETED,
|
||||
}
|
||||
|
||||
static var cached_boss_gate_lessons: Array[int] = []
|
||||
@@ -42,11 +42,11 @@ func init_unlocks() -> void:
|
||||
for index: int in range(number_of_lessons):
|
||||
if not unlocks.has(index+1):
|
||||
unlocks[index + 1] = {
|
||||
"look_and_learn": Status.Locked,
|
||||
"look_and_learn": Status.LOCKED,
|
||||
"games": [
|
||||
Status.Locked,
|
||||
Status.Locked,
|
||||
Status.Locked,
|
||||
Status.LOCKED,
|
||||
Status.LOCKED,
|
||||
Status.LOCKED,
|
||||
],
|
||||
"last_duration": PackedInt32Array([0, 0, 0]),
|
||||
"total_duration": PackedInt32Array([0, 0, 0])
|
||||
@@ -54,8 +54,8 @@ func init_unlocks() -> void:
|
||||
|
||||
# Make sure that the first garden is always accessible
|
||||
if unlocks.has(1):
|
||||
if unlocks[1]["look_and_learn"] == Status.Locked:
|
||||
unlocks[1]["look_and_learn"] = Status.Unlocked
|
||||
if unlocks[1]["look_and_learn"] == Status.LOCKED:
|
||||
unlocks[1]["look_and_learn"] = Status.UNLOCKED
|
||||
_sanitize_boss_progression()
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ func ensure_data_integrity(data: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d missing → added with default values." % index)
|
||||
result[index] = {
|
||||
"games": [Status.Locked, Status.Locked, Status.Locked],
|
||||
"look_and_learn": Status.Locked,
|
||||
"games": [Status.LOCKED, Status.LOCKED, Status.LOCKED],
|
||||
"look_and_learn": Status.LOCKED,
|
||||
"last_duration": PackedInt32Array([0, 0, 0]),
|
||||
"total_duration": PackedInt32Array([0, 0, 0])
|
||||
}
|
||||
@@ -87,11 +87,11 @@ func ensure_data_integrity(data: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
if not garden.has("games"):
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: Add missing key 'games'." % index)
|
||||
garden["games"] = [Status.Locked, Status.Locked, Status.Locked]
|
||||
garden["games"] = [Status.LOCKED, Status.LOCKED, Status.LOCKED]
|
||||
if not garden.has("look_and_learn"):
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: Add missing key 'look_and_learn'." % index)
|
||||
garden["look_and_learn"] = Status.Locked
|
||||
garden["look_and_learn"] = Status.LOCKED
|
||||
if not garden.has("last_duration"):
|
||||
garden["last_duration"] = PackedInt32Array([0, 0, 0])
|
||||
if not garden.has("total_duration"):
|
||||
@@ -101,14 +101,14 @@ func ensure_data_integrity(data: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
if typeof(garden["games"]) != TYPE_ARRAY or (garden["games"] as Array).size() != 3:
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: invalid format for 'games' → reset." % index)
|
||||
garden["games"] = [Status.Locked, Status.Locked, Status.Locked]
|
||||
garden["games"] = [Status.LOCKED, Status.LOCKED, Status.LOCKED]
|
||||
|
||||
# Check value outside of possible enum values
|
||||
for game_index: int in range(3):
|
||||
if garden["games"][game_index] not in [Status.Locked, Status.Unlocked, Status.Completed]:
|
||||
garden["games"][game_index] = Status.Locked
|
||||
if garden["look_and_learn"] not in [Status.Locked, Status.Unlocked, Status.Completed]:
|
||||
garden["look_and_learn"] = Status.Locked
|
||||
if garden["games"][game_index] not in [Status.LOCKED, Status.UNLOCKED, Status.COMPLETED]:
|
||||
garden["games"][game_index] = Status.LOCKED
|
||||
if garden["look_and_learn"] not in [Status.LOCKED, Status.UNLOCKED, Status.COMPLETED]:
|
||||
garden["look_and_learn"] = Status.LOCKED
|
||||
|
||||
# Check progression rules
|
||||
for index: int in range(min_key, max_key + 1):
|
||||
@@ -119,8 +119,8 @@ func ensure_data_integrity(data: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
if result.has(index - 1):
|
||||
var prev: Dictionary = result[index - 1]
|
||||
prev_completed = (
|
||||
prev["look_and_learn"] == Status.Completed and
|
||||
(prev["games"] as Array).all(func(x: int) -> bool: return x == Status.Completed)
|
||||
prev["look_and_learn"] == Status.COMPLETED and
|
||||
(prev["games"] as Array).all(func(x: int) -> bool: return x == Status.COMPLETED)
|
||||
)
|
||||
else:
|
||||
# First garden (key 1) is always unlocked
|
||||
@@ -129,25 +129,25 @@ func ensure_data_integrity(data: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
# Case: previous garden not completed
|
||||
if not prev_completed:
|
||||
for game_index: int in range(3):
|
||||
if garden["games"][game_index] != Status.Locked or garden["look_and_learn"] != Status.Locked:
|
||||
if garden["games"][game_index] != Status.LOCKED or garden["look_and_learn"] != Status.LOCKED:
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: invalid progression (previous not finished) → reset." % index)
|
||||
garden["games"] = [Status.Locked, Status.Locked, Status.Locked]
|
||||
garden["look_and_learn"] = Status.Locked
|
||||
garden["games"] = [Status.LOCKED, Status.LOCKED, Status.LOCKED]
|
||||
garden["look_and_learn"] = Status.LOCKED
|
||||
break
|
||||
continue
|
||||
|
||||
# Case: lesson completed → unlock games if needed
|
||||
if garden["look_and_learn"] == Status.Completed:
|
||||
if garden["look_and_learn"] == Status.COMPLETED:
|
||||
for game_index: int in range(3):
|
||||
if garden["games"][game_index] == Status.Locked:
|
||||
garden["games"][game_index] = Status.Unlocked
|
||||
if garden["games"][game_index] == Status.LOCKED:
|
||||
garden["games"][game_index] = Status.UNLOCKED
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: game %d unlocked because lesson is completed" % [index, game_index + 1])
|
||||
|
||||
# Case: previous garden completed → unlock lesson if needed
|
||||
elif garden["look_and_learn"] == Status.Locked:
|
||||
garden["look_and_learn"] = Status.Unlocked
|
||||
elif garden["look_and_learn"] == Status.LOCKED:
|
||||
garden["look_and_learn"] = Status.UNLOCKED
|
||||
if not is_init:
|
||||
Log.warn("StudentProgression: Garden %d: lesson unlocked because previous garden is completed" % index)
|
||||
|
||||
@@ -229,7 +229,7 @@ func get_max_unlocked_lesson_index() -> int:
|
||||
for index: int in range(unlocks.size()):
|
||||
if is_lesson_blocked_by_boss(index + 1):
|
||||
break
|
||||
if unlocks[index + 1]["look_and_learn"] >= Status.Unlocked:
|
||||
if unlocks[index + 1]["look_and_learn"] >= Status.UNLOCKED:
|
||||
max_unlocked_level = index
|
||||
else:
|
||||
break
|
||||
@@ -238,18 +238,18 @@ func get_max_unlocked_lesson_index() -> int:
|
||||
|
||||
|
||||
func is_lesson_completed(lesson_number: int) -> bool:
|
||||
return unlocks[lesson_number]["look_and_learn"] == Status.Completed and unlocks[lesson_number]["games"][0] == Status.Completed and unlocks[lesson_number]["games"][1] == Status.Completed and unlocks[lesson_number]["games"][2] == Status.Completed
|
||||
return unlocks[lesson_number]["look_and_learn"] == Status.COMPLETED and unlocks[lesson_number]["games"][0] == Status.COMPLETED and unlocks[lesson_number]["games"][1] == Status.COMPLETED and unlocks[lesson_number]["games"][2] == Status.COMPLETED
|
||||
|
||||
|
||||
# Return true if the progression is saved or false if the look and learn was already completed
|
||||
func look_and_learn_completed(lesson_number: int) -> bool:
|
||||
if unlocks[lesson_number]["look_and_learn"] == Status.Completed:
|
||||
if unlocks[lesson_number]["look_and_learn"] == Status.COMPLETED:
|
||||
return false
|
||||
|
||||
unlocks[lesson_number]["look_and_learn"] = Status.Completed
|
||||
unlocks[lesson_number]["look_and_learn"] = Status.COMPLETED
|
||||
|
||||
for index: int in range(3):
|
||||
unlocks[lesson_number]["games"][index] = Status.Unlocked
|
||||
unlocks[lesson_number]["games"][index] = Status.UNLOCKED
|
||||
|
||||
last_modified = Time.get_datetime_string_from_system(true)
|
||||
progression_changed.emit()
|
||||
@@ -259,18 +259,18 @@ func look_and_learn_completed(lesson_number: int) -> bool:
|
||||
# Return true if the progression is saved or false if the game was already completed
|
||||
func game_completed(lesson_number: int, game_number: int) -> bool:
|
||||
# If the game is already completed, do nothing
|
||||
if unlocks[lesson_number]["games"][game_number] == Status.Completed:
|
||||
if unlocks[lesson_number]["games"][game_number] == Status.COMPLETED:
|
||||
return false
|
||||
|
||||
unlocks[lesson_number]["games"][game_number] = Status.Completed
|
||||
unlocks[lesson_number]["games"][game_number] = Status.COMPLETED
|
||||
|
||||
var all_completed: bool = true
|
||||
for index: int in range(3):
|
||||
all_completed = all_completed and unlocks[lesson_number]["games"][index] == Status.Completed
|
||||
all_completed = all_completed and unlocks[lesson_number]["games"][index] == Status.COMPLETED
|
||||
|
||||
if all_completed:
|
||||
if unlocks.has(lesson_number + 1):
|
||||
unlocks[lesson_number + 1]["look_and_learn"] = Status.Unlocked
|
||||
unlocks[lesson_number + 1]["look_and_learn"] = Status.UNLOCKED
|
||||
|
||||
last_modified = Time.get_datetime_string_from_system(true)
|
||||
progression_changed.emit()
|
||||
|
||||
@@ -2,12 +2,12 @@ class_name TeacherSettings
|
||||
extends Resource
|
||||
|
||||
enum AccountType {
|
||||
Teacher,
|
||||
Parent
|
||||
TEACHER,
|
||||
PARENT
|
||||
}
|
||||
enum EducationMethod {
|
||||
AppOnly,
|
||||
Complete
|
||||
APP_ONLY,
|
||||
COMPLETE
|
||||
}
|
||||
|
||||
const AVAILABLE_CODES: Array[int] = [123, 124, 125, 126, 132, 134, 135, 136, 142, 143, 145, 146, 152, 153, 154, 213, 214, 215, 216, 231, 234, 235, 236, 241, 243, 245, 246, 251, 253, 254, 321, 324, 325, 326, 312, 314, 315, 316, 342, 341, 345, 346, 352, 351, 354, 423, 421, 425, 426, 432, 431, 435, 436, 412, 413, 415, 416, 452, 453, 451, 523, 524, 521, 526, 532, 534, 531, 536, 542, 543, 541, 546, 512, 513, 514, 623, 624, 625, 621, 632, 634, 635, 631, 642, 643, 645, 641, 652, 653, 654]
|
||||
|
||||
+11
-11
@@ -178,7 +178,7 @@ func _apply_progression_to_gardens(transition_context: Dictionary) -> void:
|
||||
|
||||
var lesson_unlocks: Dictionary = UserDataManager.student_progression.unlocks[lesson_index]
|
||||
var is_blocked_by_boss: bool = UserDataManager.student_progression.is_lesson_blocked_by_boss(lesson_index)
|
||||
var is_lesson_unlocked: bool = lesson_unlocks["look_and_learn"] != StudentProgression.Status.Locked and not is_blocked_by_boss
|
||||
var is_lesson_unlocked: bool = lesson_unlocks["look_and_learn"] != StudentProgression.Status.LOCKED and not is_blocked_by_boss
|
||||
button.set_button_disabled(not is_lesson_unlocked)
|
||||
|
||||
if transition_context.new_lesson_unlocked and lesson_index == transition_context.newly_unlocked_lesson_number:
|
||||
@@ -352,7 +352,7 @@ func _ready() -> void:
|
||||
_scroll_to_starting_garden(transition_context)
|
||||
|
||||
await (OpeningCurtain as OpeningCurtainClass).open()
|
||||
(MusicManager as MusicManagerClass).play((MusicManager as MusicManagerClass).Track.Garden)
|
||||
(MusicManager as MusicManagerClass).play((MusicManager as MusicManagerClass).Track.GARDEN)
|
||||
|
||||
# Handles all the animation played when entering the gardens
|
||||
if transition_data:
|
||||
@@ -674,7 +674,7 @@ func _open_minigames_layout(button: LessonButton, lesson_number: int) -> void:
|
||||
current_button_global_position = button.global_position
|
||||
# Gets the current lesson unlocks
|
||||
var lesson_unlocks: Dictionary = UserDataManager.student_progression.unlocks[current_lesson_number]
|
||||
var are_minigames_locked: bool = lesson_unlocks["games"][0] == StudentProgression.Status.Locked and lesson_unlocks["games"][1] == StudentProgression.Status.Locked and lesson_unlocks["games"][2] == StudentProgression.Status.Locked
|
||||
var are_minigames_locked: bool = lesson_unlocks["games"][0] == StudentProgression.Status.LOCKED and lesson_unlocks["games"][1] == StudentProgression.Status.LOCKED and lesson_unlocks["games"][2] == StudentProgression.Status.LOCKED
|
||||
# Deactivate the mouse filters on the buttons behind the layout
|
||||
for lesson_button_item: LessonButton in current_garden.get_lesson_buttons():
|
||||
lesson_button_item.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
@@ -712,10 +712,10 @@ func _open_minigames_layout(button: LessonButton, lesson_number: int) -> void:
|
||||
|
||||
func _handle_lesson_button(lesson_number: int, status: StudentProgression.Status) -> void:
|
||||
lesson_button.text = lessons[lesson_number][0].grapheme
|
||||
lesson_button.set_button_disabled(status == StudentProgression.Status.Locked)
|
||||
lesson_button.completed = status == StudentProgression.Status.Completed
|
||||
lesson_button_particles.emitting = status == StudentProgression.Status.Unlocked
|
||||
if status == StudentProgression.Status.Completed:
|
||||
lesson_button.set_button_disabled(status == StudentProgression.Status.LOCKED)
|
||||
lesson_button.completed = status == StudentProgression.Status.COMPLETED
|
||||
lesson_button_particles.emitting = status == StudentProgression.Status.UNLOCKED
|
||||
if status == StudentProgression.Status.COMPLETED:
|
||||
if transition_data and transition_data.has("look_and_learn_completed") and transition_data.look_and_learn_completed:
|
||||
await minigame_layout_opened
|
||||
lesson_button.right()
|
||||
@@ -723,8 +723,8 @@ func _handle_lesson_button(lesson_number: int, status: StudentProgression.Status
|
||||
|
||||
func _fill_minigame_choice(minigame_layout: MinigameLayout, exercise_type: int, status: StudentProgression.Status, minigame_number: int) -> void:
|
||||
minigame_layout.icon.texture = minigames_icons[exercise_type-1]
|
||||
minigame_layout.is_disabled = status == StudentProgression.Status.Locked
|
||||
if status == StudentProgression.Status.Completed:
|
||||
minigame_layout.is_disabled = status == StudentProgression.Status.LOCKED
|
||||
if status == StudentProgression.Status.COMPLETED:
|
||||
if transition_data and transition_data.has("minigame_completed") and transition_data.minigame_completed and transition_data.has("minigame_number") and transition_data.minigame_number == minigame_number and transition_data.has("first_clear") and transition_data.first_clear:
|
||||
minigame_layout.self_modulate = unlocked_color
|
||||
await minigame_layout_opened
|
||||
@@ -732,7 +732,7 @@ func _fill_minigame_choice(minigame_layout: MinigameLayout, exercise_type: int,
|
||||
minigame_layout.right()
|
||||
else:
|
||||
minigame_layout.self_modulate.a = 0
|
||||
elif status == StudentProgression.Status.Locked:
|
||||
elif status == StudentProgression.Status.LOCKED:
|
||||
minigame_layout.self_modulate = locked_color
|
||||
else:
|
||||
minigame_layout.self_modulate = unlocked_color
|
||||
@@ -760,7 +760,7 @@ func _count_completed_minigames(lesson_number: int) -> int:
|
||||
return 0
|
||||
var completed: int = 0
|
||||
for game_status: int in UserDataManager.student_progression.unlocks[lesson_number]["games"]:
|
||||
if game_status == StudentProgression.Status.Completed:
|
||||
if game_status == StudentProgression.Status.COMPLETED:
|
||||
completed += 1
|
||||
return completed
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@ signal delete_pressed()
|
||||
signal validated()
|
||||
|
||||
enum Type {
|
||||
Silent,
|
||||
Vowel,
|
||||
Consonant,
|
||||
SILENT,
|
||||
VOWEL,
|
||||
CONSONANT,
|
||||
}
|
||||
|
||||
const TYPE_LABELS: Array[String] = ["Silent", "Vowel", "Consonant"]
|
||||
|
||||
var grapheme: String = "":
|
||||
set = set_grapheme
|
||||
var phoneme: String = "":
|
||||
set = set_phoneme
|
||||
var type: GPListElement.Type = Type.Silent:
|
||||
var type: GPListElement.Type = Type.SILENT:
|
||||
set = set_type
|
||||
var exception: bool = false:
|
||||
set = set_exception
|
||||
@@ -55,7 +57,7 @@ func set_phoneme(p_phoneme: String) -> void:
|
||||
func set_type(p_type: Type) -> void:
|
||||
type = p_type
|
||||
if type_label:
|
||||
type_label.text = Type.keys()[type]
|
||||
type_label.text = TYPE_LABELS[type]
|
||||
if type_edit:
|
||||
type_edit.selected = type
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func _ready() -> void:
|
||||
Database.db.query(query)
|
||||
if Database.db.query_result.is_empty():
|
||||
Database.db.query("CREATE TABLE ExerciseTypes (ID INTEGER PRIMARY KEY ASC AUTOINCREMENT UNIQUE NOT NULL, Type TEXT NOT NULL)")
|
||||
for exercise_name: String in Minigame.Type.keys():
|
||||
for exercise_name: String in Minigame.TYPE_NAMES:
|
||||
Database.db.query("SELECT * FROM ExerciseTypes WHERE Type = '%s'" % exercise_name)
|
||||
if Database.db.query_result.is_empty():
|
||||
Database.db.insert_row("ExerciseTypes",
|
||||
|
||||
@@ -74,10 +74,10 @@ func _on_step_completed(step: Step) -> void:
|
||||
"type":
|
||||
# Adds teacher or parent steps
|
||||
_remove_future_steps()
|
||||
if register_data.account_type == TeacherSettings.AccountType.Teacher:
|
||||
if register_data.account_type == TeacherSettings.AccountType.TEACHER:
|
||||
for scene: PackedScene in teacher_steps:
|
||||
current_steps.append(scene.instantiate())
|
||||
elif register_data.account_type == TeacherSettings.AccountType.Parent:
|
||||
elif register_data.account_type == TeacherSettings.AccountType.PARENT:
|
||||
for scene: PackedScene in parent_steps:
|
||||
current_steps.append(scene.instantiate())
|
||||
progress_bar.max_value = current_steps.size() + 3
|
||||
|
||||
@@ -14,8 +14,8 @@ func _ready() -> void:
|
||||
func _on_next() -> bool:
|
||||
var register_data: TeacherSettings = data as TeacherSettings
|
||||
if register_data:
|
||||
if register_data.account_type == TeacherSettings.AccountType.Parent:
|
||||
register_data.education_method = TeacherSettings.EducationMethod.AppOnly
|
||||
if register_data.account_type == TeacherSettings.AccountType.PARENT:
|
||||
register_data.education_method = TeacherSettings.EducationMethod.APP_ONLY
|
||||
Log.info("Register/AccountTypeStep: selected account type = %s" % TeacherSettings.AccountType.keys()[register_data.account_type])
|
||||
else:
|
||||
Log.warn("Register/AccountTypeStep: cannot continue because TeacherSettings data is missing")
|
||||
|
||||
@@ -22,7 +22,7 @@ func on_enter() -> void:
|
||||
email.text = tr("SUMMARY_EMAIL").format({"mail": teacher_settings.email})
|
||||
account_type.text = tr("SUMMARY_TYPE").format({"type": tr((TeacherSettings.AccountType.keys()[teacher_settings.account_type] as String).to_upper())})
|
||||
|
||||
if teacher_settings.account_type == TeacherSettings.AccountType.Teacher:
|
||||
if teacher_settings.account_type == TeacherSettings.AccountType.TEACHER:
|
||||
education_method.text = tr("SUMMARY_METHOD").format({"method": tr((TeacherSettings.EducationMethod.keys()[teacher_settings.education_method] as String).to_upper())})
|
||||
education_method.show()
|
||||
|
||||
@@ -41,7 +41,7 @@ func on_enter() -> void:
|
||||
for device: int in teacher_settings.students.keys():
|
||||
var device_recap: DeviceRecap = DEVICE_RECAP_SCENE.instantiate()
|
||||
|
||||
if teacher_settings.account_type == TeacherSettings.AccountType.Teacher:
|
||||
if teacher_settings.account_type == TeacherSettings.AccountType.TEACHER:
|
||||
device_recap.title = tr("DEVICE_NUMBER").format({"number": device})
|
||||
else:
|
||||
device_recap.title = tr("PLAYERS")
|
||||
|
||||
@@ -13,8 +13,8 @@ func _ready() -> void:
|
||||
func _on_next() -> bool:
|
||||
var register_data: TeacherSettings = data as TeacherSettings
|
||||
if register_data:
|
||||
if register_data.account_type == TeacherSettings.AccountType.Parent:
|
||||
register_data.education_method = TeacherSettings.EducationMethod.AppOnly
|
||||
if register_data.account_type == TeacherSettings.AccountType.PARENT:
|
||||
register_data.education_method = TeacherSettings.EducationMethod.APP_ONLY
|
||||
else:
|
||||
return false
|
||||
return true
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[gd_scene format=3 uid="uid://buhnx0oblueuq"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bxd3i06rqpxf0" path="res://sources/menus/register/steps/base_step.tscn" id="1_ksrhk"]
|
||||
[ext_resource type="Script" uid="uid://dg4qit8mffvjh" path="res://addons/godot-form-validator/control_validator.gd" id="2_03dua"]
|
||||
[ext_resource type="Script" uid="uid://ctfu2lhk6xaad" path="res://addons/godot-form-validator/control_validator.gd" id="2_03dua"]
|
||||
[ext_resource type="Script" uid="uid://ugsjyxqh1s5h" path="res://sources/menus/register/steps/teacher/method_step.gd" id="2_onyn6"]
|
||||
[ext_resource type="Script" uid="uid://dv82313fptjgb" path="res://sources/ui/tr_item_list.gd" id="2_qrbn1"]
|
||||
[ext_resource type="Script" uid="uid://dw5aercmfe4" path="res://addons/godot-form-validator/rules/required_rule.gd" id="3_qewnl"]
|
||||
[ext_resource type="Script" uid="uid://btw6a2qw2xnje" path="res://addons/godot-form-validator/rules/required_rule.gd" id="3_qewnl"]
|
||||
[ext_resource type="Script" uid="uid://7uwoocpe7ieu" path="res://sources/utils/binder/control_binder.gd" id="4_1k1yt"]
|
||||
[ext_resource type="Script" uid="uid://83q48kwp3p6p" path="res://sources/menus/register/steps/validation/item_list_validator.gd" id="4_rimto"]
|
||||
[ext_resource type="Script" uid="uid://dg17cfvs2w257" path="res://addons/godot-form-validator/rules/validator_rule.gd" id="4_wmpac"]
|
||||
[ext_resource type="Script" uid="uid://dlg1muqj8qwau" path="res://addons/godot-form-validator/rules/validator_rule.gd" id="4_wmpac"]
|
||||
[ext_resource type="LabelSettings" uid="uid://ohvlqccl2oog" path="res://resources/themes/error_label_settings.tres" id="6_sf33v"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_kw8sn"]
|
||||
@@ -16,7 +16,9 @@ fail_message = "A value is required."
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ar6ux"]
|
||||
script = ExtResource("4_rimto")
|
||||
validation_order = 1
|
||||
validation_method = 1
|
||||
skip_validation = false
|
||||
rules = Array[ExtResource("4_wmpac")]([SubResource("Resource_kw8sn")])
|
||||
|
||||
[node name="MethodStep" unique_id=304348596 instance=ExtResource("1_ksrhk")]
|
||||
|
||||
@@ -68,54 +68,54 @@ func _set_lesson_gps(value: String) -> void:
|
||||
func _on_look_and_learn_option_button_item_selected(index: int) -> void:
|
||||
unlocks[lesson_number]["look_and_learn"] = index
|
||||
|
||||
if index == StudentProgression.Status.Locked:
|
||||
if index == StudentProgression.Status.LOCKED:
|
||||
if lesson_number == 1:
|
||||
unlocks[lesson_number]["look_and_learn"] = StudentProgression.Status.Unlocked
|
||||
unlocks[lesson_number]["look_and_learn"] = StudentProgression.Status.UNLOCKED
|
||||
else:
|
||||
unlocks[lesson_number - 1]["look_and_learn"] = StudentProgression.Status.Unlocked
|
||||
unlocks[lesson_number - 1]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number - 1]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number - 1]["games"][2] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number - 1]["look_and_learn"] = StudentProgression.Status.UNLOCKED
|
||||
unlocks[lesson_number - 1]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number - 1]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number - 1]["games"][2] = StudentProgression.Status.LOCKED
|
||||
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.LOCKED
|
||||
|
||||
for lesson: int in unlocks.keys():
|
||||
if lesson > lesson_number:
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.LOCKED
|
||||
|
||||
elif index == StudentProgression.Status.Unlocked:
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.Locked
|
||||
elif index == StudentProgression.Status.UNLOCKED:
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.LOCKED
|
||||
for lesson: int in unlocks.keys():
|
||||
if lesson < lesson_number:
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.COMPLETED
|
||||
elif lesson > lesson_number:
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.Locked
|
||||
elif index == StudentProgression.Status.Completed:
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.Unlocked
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.Unlocked
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.Unlocked
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.LOCKED
|
||||
elif index == StudentProgression.Status.COMPLETED:
|
||||
unlocks[lesson_number]["games"][0] = StudentProgression.Status.UNLOCKED
|
||||
unlocks[lesson_number]["games"][1] = StudentProgression.Status.UNLOCKED
|
||||
unlocks[lesson_number]["games"][2] = StudentProgression.Status.UNLOCKED
|
||||
for lesson: int in unlocks.keys():
|
||||
if lesson < lesson_number:
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.Completed
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.COMPLETED
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.COMPLETED
|
||||
elif lesson > lesson_number:
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.Locked
|
||||
unlocks[lesson]["look_and_learn"] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][0] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][1] = StudentProgression.Status.LOCKED
|
||||
unlocks[lesson]["games"][2] = StudentProgression.Status.LOCKED
|
||||
unlocks_changed.emit()
|
||||
|
||||
@@ -90,7 +90,7 @@ func _on_back_button_pressed() -> void:
|
||||
func _get_highest_unlocked_lesson() -> int:
|
||||
var highest_unlocked_lesson: int = 0
|
||||
for lesson_number: int in progression.unlocks.keys():
|
||||
if progression.unlocks[lesson_number]["look_and_learn"] >= StudentProgression.Status.Unlocked:
|
||||
if progression.unlocks[lesson_number]["look_and_learn"] >= StudentProgression.Status.UNLOCKED:
|
||||
highest_unlocked_lesson = max(highest_unlocked_lesson, lesson_number)
|
||||
return highest_unlocked_lesson
|
||||
|
||||
|
||||
@@ -2,18 +2,32 @@ class_name Minigame
|
||||
extends Control
|
||||
|
||||
enum Type {
|
||||
jellyfish,
|
||||
crabs,
|
||||
parakeets,
|
||||
monkey,
|
||||
caterpillar,
|
||||
frog,
|
||||
turtles,
|
||||
ants,
|
||||
penguin,
|
||||
fish
|
||||
JELLYFISH,
|
||||
CRABS,
|
||||
PARAKEETS,
|
||||
MONKEY,
|
||||
CATERPILLAR,
|
||||
FROG,
|
||||
TURTLES,
|
||||
ANTS,
|
||||
PENGUIN,
|
||||
FISH,
|
||||
}
|
||||
|
||||
# String names used for file paths, database keys, and speech lookups.
|
||||
# Kept separate from enum member names so renaming members doesn't affect runtime behaviour.
|
||||
const TYPE_NAMES: Array[String] = [
|
||||
"jellyfish",
|
||||
"crabs",
|
||||
"parakeets",
|
||||
"monkey",
|
||||
"caterpillar",
|
||||
"frog",
|
||||
"turtles",
|
||||
"ants",
|
||||
"penguin",
|
||||
"fish",
|
||||
]
|
||||
const WIN_SOUND_FX: AudioStreamMP3 = preload("res://assets/sfx/sfx_game_over_win.mp3")
|
||||
const LOSE_SOUND_FX: AudioStreamMP3 = preload("res://assets/sfx/sfx_game_over_lose.mp3")
|
||||
const LABEL_COLOR_NEUTRAL: Color = Color("#e6f3e0")
|
||||
@@ -59,7 +73,7 @@ var current_lives: int = 0:
|
||||
var previous_lives: int = current_lives
|
||||
current_lives = value
|
||||
if current_lives != previous_lives:
|
||||
Log.trace("BaseMinigame: Lives changed from %d to %d (max %d) for %s" % [previous_lives, current_lives, max_number_of_lives, Type.keys()[minigame_name]])
|
||||
Log.trace("BaseMinigame: Lives changed from %d to %d (max %d) for %s" % [previous_lives, current_lives, max_number_of_lives, TYPE_NAMES[minigame_name]])
|
||||
if current_lives < previous_lives:
|
||||
consecutive_errors += previous_lives - current_lives
|
||||
if current_lives <= max_number_of_lives - errors_before_help_speech:
|
||||
@@ -104,11 +118,11 @@ func _ready() -> void:
|
||||
|
||||
# Difficulty
|
||||
if (UserDataManager as UserDataManagerClass)._student_difficulty:
|
||||
difficulty = UserDataManager.get_difficulty_for_minigame(Type.keys()[minigame_name] as String)
|
||||
difficulty = UserDataManager.get_difficulty_for_minigame(TYPE_NAMES[minigame_name] as String)
|
||||
|
||||
intro_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "intro"))
|
||||
help_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "help"))
|
||||
win_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "end"))
|
||||
intro_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name] as String, "intro"))
|
||||
help_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name] as String, "help"))
|
||||
win_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name] as String, "end"))
|
||||
lose_kalulu_speech = Database.load_external_sound(Database.get_kalulu_speech_path("minigame", "lose"))
|
||||
|
||||
if not Engine.is_editor_hint():
|
||||
@@ -125,7 +139,7 @@ func _initialize() -> void:
|
||||
|
||||
_setup_minigame()
|
||||
|
||||
Log.info("BaseMinigame: Initialize %s (lesson %d, minigame #%d, difficulty %d)" % [Type.keys()[minigame_name], lesson_nb, minigame_number, difficulty])
|
||||
Log.info("BaseMinigame: Initialize %s (lesson %d, minigame #%d, difficulty %d)" % [TYPE_NAMES[minigame_name], lesson_nb, minigame_number, difficulty])
|
||||
|
||||
if not Engine.is_editor_hint():
|
||||
await _curtains_and_kalulu()
|
||||
@@ -151,10 +165,10 @@ func _curtains_and_kalulu() -> void:
|
||||
await (OpeningCurtain as OpeningCurtainClass).open()
|
||||
|
||||
# Checks if intro needs to be played
|
||||
if not UserDataManager.is_speech_played(Type.keys()[minigame_name] as String):
|
||||
if not UserDataManager.is_speech_played(TYPE_NAMES[minigame_name] as String):
|
||||
minigame_ui.play_kalulu_speech(intro_kalulu_speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
UserDataManager.mark_speech_as_played(Type.keys()[minigame_name] as String)
|
||||
UserDataManager.mark_speech_as_played(TYPE_NAMES[minigame_name] as String)
|
||||
#endregion
|
||||
|
||||
#region Timer
|
||||
@@ -166,7 +180,7 @@ var _is_paused: bool = false
|
||||
|
||||
# Launch the minigame
|
||||
func _start() -> void:
|
||||
Log.info("BaseMinigame: Start minigame=%s lesson=%d difficulty=%d" % [Type.keys()[minigame_name], lesson_nb, difficulty])
|
||||
Log.info("BaseMinigame: Start minigame=%s lesson=%d difficulty=%d" % [TYPE_NAMES[minigame_name], lesson_nb, difficulty])
|
||||
_start_time = Time.get_ticks_msec() / 1000.0
|
||||
_elapsed_paused = 0.0
|
||||
_is_paused = false
|
||||
@@ -218,13 +232,13 @@ func _win() -> void:
|
||||
|
||||
update_scores()
|
||||
|
||||
Log.info("BaseMinigame: %s won in %d seconds with progression %d/%d and %d/%d lives" % [Type.keys()[minigame_name], _get_elapsed_time_seconds(), current_progression, max_progression, current_lives, max_number_of_lives])
|
||||
Log.info("BaseMinigame: %s won in %d seconds with progression %d/%d and %d/%d lives" % [TYPE_NAMES[minigame_name], _get_elapsed_time_seconds(), current_progression, max_progression, current_lives, max_number_of_lives])
|
||||
|
||||
# Difficulty
|
||||
if current_lives <= 0:
|
||||
UserDataManager.update_difficulty_for_minigame(Type.keys()[minigame_name] as String, false)
|
||||
UserDataManager.update_difficulty_for_minigame(TYPE_NAMES[minigame_name] as String, false)
|
||||
else:
|
||||
UserDataManager.update_difficulty_for_minigame(Type.keys()[minigame_name] as String, true)
|
||||
UserDataManager.update_difficulty_for_minigame(TYPE_NAMES[minigame_name] as String, true)
|
||||
|
||||
audio_player.stream = WIN_SOUND_FX
|
||||
audio_player.play()
|
||||
@@ -263,10 +277,10 @@ func _lose() -> void:
|
||||
|
||||
update_scores()
|
||||
|
||||
Log.info("BaseMinigame: %s Lose in %d seconds with progression %d/%d and %d/%d lives" % [Type.keys()[minigame_name], _get_elapsed_time_seconds(), current_progression, max_progression, current_lives, max_number_of_lives])
|
||||
Log.info("BaseMinigame: %s Lose in %d seconds with progression %d/%d and %d/%d lives" % [TYPE_NAMES[minigame_name], _get_elapsed_time_seconds(), current_progression, max_progression, current_lives, max_number_of_lives])
|
||||
|
||||
# Difficulty
|
||||
UserDataManager.update_difficulty_for_minigame(Type.keys()[minigame_name] as String, false)
|
||||
UserDataManager.update_difficulty_for_minigame(TYPE_NAMES[minigame_name] as String, false)
|
||||
|
||||
audio_player.stream = LOSE_SOUND_FX
|
||||
audio_player.play()
|
||||
@@ -280,7 +294,7 @@ func _lose() -> void:
|
||||
if has_method("show_adult_block"):
|
||||
call("show_adult_block")
|
||||
else:
|
||||
Log.error("BaseMinigame: Adult block requested but no handler exists for %s" % Type.keys()[minigame_name])
|
||||
Log.error("BaseMinigame: Adult block requested but no handler exists for %s" % TYPE_NAMES[minigame_name])
|
||||
return
|
||||
|
||||
_reset()
|
||||
@@ -303,8 +317,8 @@ func _save_logs() -> void:
|
||||
var logs_size: int = -1
|
||||
if logs.has("answers") and logs.get("answers", []) is Array:
|
||||
logs_size = (logs.get("answers", []) as Array).size()
|
||||
Log.info("BaseMinigame: Saving logs for %s with %d answer(s)" % [Type.keys()[minigame_name], logs_size])
|
||||
LessonLogger.save_logs(logs, UserDataManager.get_student_folder(), Type.keys()[minigame_name] as String, lesson_nb, Time.get_time_string_from_system())
|
||||
Log.info("BaseMinigame: Saving logs for %s with %d answer(s)" % [TYPE_NAMES[minigame_name], logs_size])
|
||||
LessonLogger.save_logs(logs, UserDataManager.get_student_folder(), TYPE_NAMES[minigame_name] as String, lesson_nb, Time.get_time_string_from_system())
|
||||
_reset_logs()
|
||||
|
||||
|
||||
@@ -317,7 +331,7 @@ func _log_new_response(response: Dictionary, current_stimulus: Dictionary) -> vo
|
||||
"reponse": response,
|
||||
"awaited_response": current_stimulus,
|
||||
"is_right": response == current_stimulus,
|
||||
"minigame": Type.keys()[minigame_name],
|
||||
"minigame": TYPE_NAMES[minigame_name],
|
||||
"number_of_hints": current_number_of_hints,
|
||||
"current_progression": current_progression,
|
||||
"max_progression": max_progression,
|
||||
@@ -325,7 +339,7 @@ func _log_new_response(response: Dictionary, current_stimulus: Dictionary) -> vo
|
||||
"max_number_of_lives": max_number_of_lives,
|
||||
}
|
||||
Log.trace("BaseMinigame: Log new response minigame=%s response=%s expected=%s right=%s progression=%d/%d lives=%d/%d" % [
|
||||
Type.keys()[minigame_name],
|
||||
TYPE_NAMES[minigame_name],
|
||||
str(response),
|
||||
str(current_stimulus),
|
||||
str(response_log.is_right),
|
||||
@@ -437,7 +451,7 @@ func _play_kalulu_help_speech() -> void:
|
||||
func set_current_progression(p_current_progression: int) -> void:
|
||||
var previous_progression: int = current_progression
|
||||
current_progression = p_current_progression
|
||||
Log.trace("BaseMinigame: Progression changed from %d to %d/%d for %s" % [previous_progression, current_progression, max_progression, Type.keys()[minigame_name]])
|
||||
Log.trace("BaseMinigame: Progression changed from %d to %d/%d for %s" % [previous_progression, current_progression, max_progression, TYPE_NAMES[minigame_name]])
|
||||
|
||||
consecutive_errors = 0
|
||||
is_highlighting = false
|
||||
|
||||
@@ -65,7 +65,7 @@ func _ready() -> void:
|
||||
default_label_background_color = texture_rect_text_box.self_modulate
|
||||
|
||||
# Skips the whole tutorial
|
||||
if UserDataManager.is_speech_played(Type.keys()[minigame_name] as String):
|
||||
if UserDataManager.is_speech_played(TYPE_NAMES[minigame_name]):
|
||||
tutorial_count = 2
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ func _present_next_word() -> void:
|
||||
if _is_boss_session():
|
||||
_boss_answer_start_ms = Time.get_ticks_msec()
|
||||
if tutorial_count == 0:
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "intro_test_game_first_word"))
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name], "intro_test_game_first_word"))
|
||||
minigame_ui.play_kalulu_speech(speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
|
||||
@@ -224,12 +224,12 @@ func _on_answer_dropped(is_answered_real: bool) -> void:
|
||||
words_to_present.pop_front()
|
||||
await _play_correct_answer_animation(target_button)
|
||||
if tutorial_count == 0:
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "win_test_game_first_word"))
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name], "win_test_game_first_word"))
|
||||
minigame_ui.play_kalulu_speech(speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
tutorial_count += 1
|
||||
elif tutorial_count == 1:
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "win_test_game_second_word"))
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name], "win_test_game_second_word"))
|
||||
minigame_ui.play_kalulu_speech(speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
tutorial_count += 1
|
||||
@@ -245,12 +245,12 @@ func _on_answer_dropped(is_answered_real: bool) -> void:
|
||||
wrong_fx.play()
|
||||
words_to_present_next.append(words_to_present.pop_front())
|
||||
if tutorial_count == 0:
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "lose_test_game_first_word"))
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name], "lose_test_game_first_word"))
|
||||
minigame_ui.play_kalulu_speech(speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
tutorial_count += 1
|
||||
elif tutorial_count == 1:
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(Type.keys()[minigame_name] as String, "lose_test_game_second_word"))
|
||||
var speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path(TYPE_NAMES[minigame_name], "lose_test_game_second_word"))
|
||||
minigame_ui.play_kalulu_speech(speech)
|
||||
await minigame_ui.kalulu_speech_ended
|
||||
tutorial_count += 1
|
||||
|
||||
@@ -162,7 +162,7 @@ func _on_current_progression_changed() -> void:
|
||||
func set_current_progression(p_current_progression: int) -> void:
|
||||
var previous_progression: int = current_progression
|
||||
current_progression = p_current_progression
|
||||
Log.debug("BaseMinigame: Progression changed from %d to %d/%d for %s" % [previous_progression, current_progression, max_progression, Type.keys()[minigame_name]])
|
||||
Log.debug("BaseMinigame: Progression changed from %d to %d/%d for %s" % [previous_progression, current_progression, max_progression, TYPE_NAMES[minigame_name]])
|
||||
|
||||
consecutive_errors = 0
|
||||
is_highlighting = false
|
||||
|
||||
@@ -4,8 +4,8 @@ extends Control
|
||||
signal pressed(stimulus: Dictionary)
|
||||
|
||||
enum Colors {
|
||||
Blue,
|
||||
Pink,
|
||||
BLUE,
|
||||
PINK,
|
||||
}
|
||||
|
||||
const ANIMATIONS_BODY: Array[SpriteFrames] = [
|
||||
@@ -28,7 +28,7 @@ const SCALE_FACTOR: float = 0.2
|
||||
if is_node_ready():
|
||||
_apply_visuals()
|
||||
|
||||
var _color: int = Colors.Blue
|
||||
var _color: int = Colors.BLUE
|
||||
var color: int:
|
||||
get: return _color
|
||||
set(value):
|
||||
@@ -62,7 +62,7 @@ func _ready() -> void:
|
||||
return
|
||||
|
||||
var rand: float = randf()
|
||||
color = Colors.Blue if rand < 0.7 else Colors.Pink
|
||||
color = Colors.BLUE if rand < 0.7 else Colors.PINK
|
||||
var rand_frame: int = randi_range(0, animated_sprite_body.sprite_frames.get_frame_count("idle") - 1)
|
||||
animated_sprite_body.frame = rand_frame
|
||||
animated_sprite_arms.frame = rand_frame
|
||||
@@ -71,7 +71,7 @@ func _ready() -> void:
|
||||
|
||||
func _apply_visuals() -> void:
|
||||
if boss:
|
||||
animated_sprite_body.sprite_frames = ANIMATIONS_BODY[Colors.Pink]
|
||||
animated_sprite_body.sprite_frames = ANIMATIONS_BODY[Colors.PINK]
|
||||
animated_sprite_arms.hide()
|
||||
return
|
||||
else:
|
||||
@@ -147,7 +147,7 @@ func delete() -> void:
|
||||
|
||||
func idle_boss() -> void:
|
||||
text_box_sprite_2d.hide()
|
||||
color = Colors.Pink
|
||||
color = Colors.PINK
|
||||
scale = Vector2(0.45, 0.45)
|
||||
animated_sprite_body.stop()
|
||||
animated_sprite_arms.stop()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
extends WordsMinigame
|
||||
|
||||
enum Audio {
|
||||
SendToKing,
|
||||
SendToPlank,
|
||||
SendToMonkey,
|
||||
SEND_TO_KING,
|
||||
SEND_TO_PLANK,
|
||||
SEND_TO_MONKEY,
|
||||
}
|
||||
|
||||
const MONKEY_SCENE: PackedScene = preload("res://sources/minigames/monkeys/monkey.tscn")
|
||||
@@ -123,7 +123,7 @@ func _get_coconut_from_monkey_to_king(monkey: Monkey) -> Node2D:
|
||||
await monkey.play("start_throw")
|
||||
monkey.play("finish_throw")
|
||||
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SendToKing]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SEND_TO_KING]
|
||||
audio_player.play()
|
||||
|
||||
var coconut: Coconut = monkey.coconut.duplicate()
|
||||
@@ -167,7 +167,7 @@ func _on_coconut_thrown(monkey: Monkey) -> void:
|
||||
|
||||
if _is_gp_right(monkey.stimulus):
|
||||
await king.play("start_right")
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SendToPlank]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SEND_TO_PLANK]
|
||||
audio_player.play()
|
||||
king.play("finish_right")
|
||||
var tween: Tween = create_tween()
|
||||
@@ -179,7 +179,7 @@ func _on_coconut_thrown(monkey: Monkey) -> void:
|
||||
current_word_progression += 1
|
||||
else:
|
||||
await king.play("start_wrong")
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SendToMonkey]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.SEND_TO_MONKEY]
|
||||
audio_player.play()
|
||||
king.play("finish_wrong")
|
||||
var tween: Tween = create_tween()
|
||||
|
||||
@@ -4,9 +4,9 @@ extends Node2D
|
||||
signal pressed()
|
||||
|
||||
enum Colors {
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
}
|
||||
|
||||
const ANIMATIONS: Array[SpriteFrames] = [
|
||||
@@ -21,7 +21,7 @@ const FEATHERS_ANIMATIONS: Array[SpriteFrames] = [
|
||||
]
|
||||
|
||||
@export var sad_duration: float = 2.0
|
||||
@export var color: Colors = Colors.Red:
|
||||
@export var color: Colors = Colors.RED:
|
||||
set(value):
|
||||
color = value
|
||||
if animated_sprite:
|
||||
@@ -141,7 +141,7 @@ func wrong() -> void:
|
||||
|
||||
func idle_boss() -> void:
|
||||
text_box_sprite_2d.hide()
|
||||
color = Colors.Green
|
||||
color = Colors.GREEN
|
||||
animated_sprite.play("idle_front")
|
||||
animated_sprite.stop()
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
extends Minigame
|
||||
|
||||
enum State {
|
||||
Locked,
|
||||
Idle,
|
||||
Selected1,
|
||||
Selected2,
|
||||
LOCKED,
|
||||
IDLE,
|
||||
SELECTED_1,
|
||||
SELECTED_2,
|
||||
}
|
||||
enum Audio {
|
||||
Fly,
|
||||
Happy,
|
||||
Turn,
|
||||
Win,
|
||||
FLY,
|
||||
HAPPY,
|
||||
TURN,
|
||||
WIN,
|
||||
}
|
||||
|
||||
const AUDIO_STREAMS: Array[AudioStreamMP3] = [
|
||||
@@ -32,7 +32,7 @@ const PARAKEET_SCENE: PackedScene = preload("res://sources/minigames/parakeets/p
|
||||
|
||||
var parakeets: Array[Parakeet] = []
|
||||
var selected: Array[Parakeet] = []
|
||||
var state: State = State.Locked
|
||||
var state: State = State.LOCKED
|
||||
|
||||
@onready var branches: Node2D = $GameRoot/TreeTrunk/Branches
|
||||
@onready var possible_start_positions_parent: Control = $GameRoot/FlyFrom
|
||||
@@ -114,30 +114,30 @@ func _start() -> void:
|
||||
|
||||
func _on_parakeet_pressed(parakeet: Parakeet) -> void:
|
||||
match state:
|
||||
State.Selected2, State.Locked:
|
||||
State.SELECTED_2, State.LOCKED:
|
||||
return
|
||||
|
||||
State.Selected1:
|
||||
state = State.Locked
|
||||
State.SELECTED_1:
|
||||
state = State.LOCKED
|
||||
if parakeet in selected:
|
||||
selected.erase(parakeet)
|
||||
await _turn(parakeet, true)
|
||||
state = State.Idle
|
||||
state = State.IDLE
|
||||
else:
|
||||
selected.append(parakeet)
|
||||
await _turn(parakeet, false)
|
||||
state = State.Selected2
|
||||
state = State.SELECTED_2
|
||||
_log_new_response({"pair": [selected[0].stimulus, selected[1].stimulus]}, {"pair": [selected[0].stimulus, selected[0].stimulus]})
|
||||
if selected[0].stimulus.Grapheme == selected[1].stimulus.Grapheme:
|
||||
_correct()
|
||||
else:
|
||||
_wrong()
|
||||
|
||||
State.Idle:
|
||||
state = State.Locked
|
||||
State.IDLE:
|
||||
state = State.LOCKED
|
||||
selected.append(parakeet)
|
||||
await _turn(parakeet, false)
|
||||
state = State.Selected1
|
||||
state = State.SELECTED_1
|
||||
|
||||
|
||||
func _correct() -> void:
|
||||
@@ -151,7 +151,7 @@ func _correct() -> void:
|
||||
await _fly_to(nest_positions)
|
||||
await _make_selected_coo()
|
||||
_fly_to(fly_away_positions)
|
||||
state = State.Idle
|
||||
state = State.IDLE
|
||||
selected.clear()
|
||||
|
||||
|
||||
@@ -183,14 +183,14 @@ func _present_parakeets() -> void:
|
||||
for parakeet: Parakeet in parakeets:
|
||||
coroutine.add_future(_turn.bind(parakeet, true))
|
||||
await coroutine.join_all()
|
||||
state = State.Idle
|
||||
state = State.IDLE
|
||||
|
||||
|
||||
func _make_selected_happy() -> void:
|
||||
for parakeet: Parakeet in selected:
|
||||
parakeet.right()
|
||||
parakeet.happy()
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.Happy]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.HAPPY]
|
||||
audio_player.play()
|
||||
await audio_player.finished
|
||||
|
||||
@@ -206,7 +206,7 @@ func _make_selected_sad() -> void:
|
||||
func _make_selected_coo() -> void:
|
||||
for parakeet: Parakeet in selected:
|
||||
parakeet.idle()
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.Win]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.WIN]
|
||||
audio_player.play()
|
||||
await audio_player.finished
|
||||
|
||||
@@ -216,7 +216,7 @@ func _fly_to(targets: Array[Vector2]) -> void:
|
||||
parent.move_child(selected[0], parent.get_child_count() - 1)
|
||||
parent.move_child(selected[1], parent.get_child_count() - 1)
|
||||
var coroutine: Coroutine = Coroutine.new()
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.Fly]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.FLY]
|
||||
audio_player.play()
|
||||
coroutine.add_future(audio_player.finished)
|
||||
coroutine.add_future(selected[0].fly_to.bind(targets[0], fly_duration))
|
||||
@@ -226,7 +226,7 @@ func _fly_to(targets: Array[Vector2]) -> void:
|
||||
|
||||
func _turn(parakeet: Parakeet, to_back: bool) -> void:
|
||||
if not audio_player.playing:
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.Turn]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.TURN]
|
||||
audio_player.play()
|
||||
if to_back:
|
||||
await parakeet.turn_to_back()
|
||||
@@ -237,7 +237,7 @@ func _turn(parakeet: Parakeet, to_back: bool) -> void:
|
||||
func _flying_arrival(to: Array[Vector2]) -> void:
|
||||
assert(parakeets.size() <= to.size(), "Some parakeets don't have a destination")
|
||||
var coroutine: Coroutine = Coroutine.new()
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.Fly]
|
||||
audio_player.stream = AUDIO_STREAMS[Audio.FLY]
|
||||
audio_player.play()
|
||||
coroutine.add_future(audio_player.finished)
|
||||
for index: int in range(parakeets.size()):
|
||||
|
||||
@@ -5,9 +5,9 @@ signal pressed(gp: Dictionary)
|
||||
signal animation_changed(position: Vector2)
|
||||
|
||||
enum Colors {
|
||||
Green,
|
||||
Khaki,
|
||||
Purple,
|
||||
GREEN,
|
||||
KHAKI,
|
||||
PURPLE,
|
||||
}
|
||||
|
||||
const ANIMATIONS: Array[SpriteFrames] = [
|
||||
@@ -18,7 +18,7 @@ const ANIMATIONS: Array[SpriteFrames] = [
|
||||
const TURTLE_BACK_RIGHT: CompressedTexture2D = preload("res://assets/minigames/turtles/graphic/turtle_back_right.png")
|
||||
const TURTLE_BACK_WRONG: CompressedTexture2D = preload("res://assets/minigames/turtles/graphic/turtle_back_wrong.png")
|
||||
|
||||
@export var color: Colors = Colors.Purple:
|
||||
@export var color: Colors = Colors.PURPLE:
|
||||
set(value):
|
||||
color = value
|
||||
if sprite:
|
||||
|
||||
@@ -2,8 +2,8 @@ class_name MusicManagerClass
|
||||
extends Node
|
||||
|
||||
enum Track {
|
||||
Title,
|
||||
Garden
|
||||
TITLE,
|
||||
GARDEN
|
||||
}
|
||||
|
||||
const TRACKS: Array = [
|
||||
@@ -16,7 +16,7 @@ const TRACKS: Array = [
|
||||
|
||||
func _ready() -> void:
|
||||
Log.trace("MusicManager: Ready - starting title track")
|
||||
play(Track.Title)
|
||||
play(Track.TITLE)
|
||||
|
||||
|
||||
func _on_music_player_finished() -> void:
|
||||
|
||||
@@ -2,11 +2,11 @@ class_name UserDatabaseSynchronizer
|
||||
extends Node
|
||||
|
||||
enum UpdateNeeded {
|
||||
Nothing,
|
||||
FromLocal,
|
||||
FromServer,
|
||||
DeleteLocal,
|
||||
DeleteServer
|
||||
NOTHING,
|
||||
FROM_LOCAL,
|
||||
FROM_SERVER,
|
||||
DELETE_LOCAL,
|
||||
DELETE_SERVER
|
||||
}
|
||||
|
||||
var synchronizing: bool = false
|
||||
@@ -62,13 +62,13 @@ func _determine_user_update(response_body: Dictionary) -> UpdateNeeded:
|
||||
Log.trace("UserDatabaseSynchronizer: Cannot get user from body. Canceling synchronization.")
|
||||
set_loading_bar_text("SYNCHRONIZATION_ERROR_NO_BODY_FROM_SERVER")
|
||||
stop_sync()
|
||||
return UpdateNeeded.Nothing
|
||||
return UpdateNeeded.NOTHING
|
||||
var user: Dictionary = response_body.user
|
||||
if not user.has("last_modified"):
|
||||
Log.trace("UserDatabaseSynchronizer: Cannot get last_modified from user. Canceling synchronization.")
|
||||
set_loading_bar_text("SYNCHRONIZATION_ERROR")
|
||||
stop_sync()
|
||||
return UpdateNeeded.Nothing
|
||||
return UpdateNeeded.NOTHING
|
||||
var server_unix_time_user: int = Time.get_unix_time_from_datetime_string(user.last_modified as String)
|
||||
var local_user_string_time: String = UserDataManager.teacher_settings.last_modified
|
||||
var local_unix_time_user: int = 0
|
||||
@@ -79,10 +79,10 @@ func _determine_user_update(response_body: Dictionary) -> UpdateNeeded:
|
||||
|
||||
func _compute_update_needed(local_unix_time: int, server_unix_time: int) -> UpdateNeeded:
|
||||
if local_unix_time == server_unix_time:
|
||||
return UpdateNeeded.Nothing
|
||||
return UpdateNeeded.NOTHING
|
||||
if local_unix_time > server_unix_time:
|
||||
return UpdateNeeded.FromLocal
|
||||
return UpdateNeeded.FromServer
|
||||
return UpdateNeeded.FROM_LOCAL
|
||||
return UpdateNeeded.FROM_SERVER
|
||||
|
||||
|
||||
func _determine_students_update(response_body: Dictionary, need_update_user: UpdateNeeded) -> Dictionary:
|
||||
@@ -150,7 +150,7 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
# Synchronize student data
|
||||
var local_student_unix_time: int = Time.get_unix_time_from_datetime_string(student_data.last_modified)
|
||||
student_updates["data"] = _compute_update_needed(local_student_unix_time, server_student_unix_time)
|
||||
if student_updates["data"] == UpdateNeeded.Nothing:
|
||||
if student_updates["data"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
|
||||
# Synchronize student progression
|
||||
@@ -162,7 +162,7 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
return {}
|
||||
var local_student_progression_unix_time: int = Time.get_unix_time_from_datetime_string(student_progression.last_modified)
|
||||
student_updates["progression"] = _compute_update_needed(local_student_progression_unix_time, server_student_progression_unix_time)
|
||||
if student_updates["progression"] == UpdateNeeded.Nothing:
|
||||
if student_updates["progression"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d progression data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
|
||||
# Synchronize student remediation
|
||||
@@ -170,17 +170,17 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
if student_remediation != null:
|
||||
var local_student_gp_remediation_unix_time: int = Time.get_unix_time_from_datetime_string(student_remediation.gp_last_modified)
|
||||
student_updates["remediation_gp"] = _compute_update_needed(local_student_gp_remediation_unix_time, server_student_remediation_gp_unix_time)
|
||||
if student_updates["remediation_gp"] == UpdateNeeded.Nothing:
|
||||
if student_updates["remediation_gp"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d GP remediation data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
|
||||
var local_student_syllables_remediation_unix_time: int = Time.get_unix_time_from_datetime_string(student_remediation.syllables_last_modified)
|
||||
student_updates["remediation_syllables"] = _compute_update_needed(local_student_syllables_remediation_unix_time, server_student_remediation_syllables_unix_time)
|
||||
if student_updates["remediation_syllables"] == UpdateNeeded.Nothing:
|
||||
if student_updates["remediation_syllables"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d syllables remediation data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
|
||||
var local_student_words_remediation_unix_time: int = Time.get_unix_time_from_datetime_string(student_remediation.words_last_modified)
|
||||
student_updates["remediation_words"] = _compute_update_needed(local_student_words_remediation_unix_time, server_student_remediation_words_unix_time)
|
||||
if student_updates["remediation_words"] == UpdateNeeded.Nothing:
|
||||
if student_updates["remediation_words"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d words remediation data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
|
||||
# Synchronize student confusion matrix
|
||||
@@ -188,20 +188,20 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
if student_confusion_matrix != null:
|
||||
var local_student_gp_confusion_matrix_unix_time: int = Time.get_unix_time_from_datetime_string(student_confusion_matrix.gp_last_modified)
|
||||
student_updates["confusion_matrix_gp"] = _compute_update_needed(local_student_gp_confusion_matrix_unix_time, server_student_confusion_matrix_gp_unix_time)
|
||||
if student_updates["confusion_matrix_gp"] == UpdateNeeded.Nothing:
|
||||
if student_updates["confusion_matrix_gp"] == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: Student %d GP confusion matrix data timestamp is the same in local and on server. No synchronization necessary" % code_to_check)
|
||||
else:
|
||||
if server_student_confusion_matrix_gp_unix_time > 0:
|
||||
student_updates["confusion_matrix_gp"] = UpdateNeeded.FromServer
|
||||
student_updates["confusion_matrix_gp"] = UpdateNeeded.FROM_SERVER
|
||||
|
||||
break
|
||||
if found:
|
||||
break
|
||||
if not found:
|
||||
if need_update_user == UpdateNeeded.FromServer:
|
||||
need_update_students[code_to_check]["data"] = UpdateNeeded.FromServer
|
||||
elif need_update_user == UpdateNeeded.FromLocal:
|
||||
need_update_students[code_to_check]["data"] = UpdateNeeded.DeleteServer
|
||||
if need_update_user == UpdateNeeded.FROM_SERVER:
|
||||
need_update_students[code_to_check]["data"] = UpdateNeeded.FROM_SERVER
|
||||
elif need_update_user == UpdateNeeded.FROM_LOCAL:
|
||||
need_update_students[code_to_check]["data"] = UpdateNeeded.DELETE_SERVER
|
||||
else:
|
||||
Log.warn("UserDatabaseSynchronizer: Student %d not found in local, but user doesn't need to be updated...this is theoretically not possible" % code_to_check)
|
||||
|
||||
@@ -209,12 +209,12 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
var students_in_device: Array[StudentData] = UserDataManager.teacher_settings.students[device]
|
||||
for student_data: StudentData in students_in_device:
|
||||
if not need_update_students.has(student_data.code):
|
||||
if need_update_user == UpdateNeeded.FromServer:
|
||||
if need_update_user == UpdateNeeded.FROM_SERVER:
|
||||
need_update_students[student_data.code] = {}
|
||||
need_update_students[student_data.code]["data"] = UpdateNeeded.DeleteLocal
|
||||
elif need_update_user == UpdateNeeded.FromLocal:
|
||||
need_update_students[student_data.code]["data"] = UpdateNeeded.DELETE_LOCAL
|
||||
elif need_update_user == UpdateNeeded.FROM_LOCAL:
|
||||
need_update_students[student_data.code] = {}
|
||||
need_update_students[student_data.code]["data"] = UpdateNeeded.FromLocal
|
||||
need_update_students[student_data.code]["data"] = UpdateNeeded.FROM_LOCAL
|
||||
else:
|
||||
Log.warn("UserDatabaseSynchronizer: Student %d not found in server, but user doesn't need to be updated...this is theoretically not possible" % student_data.code)
|
||||
return need_update_students
|
||||
@@ -223,13 +223,13 @@ func _determine_students_update(response_body: Dictionary, need_update_user: Upd
|
||||
func _build_message_to_server(need_update_user: UpdateNeeded, need_update_students: Dictionary[int, Dictionary]) -> Dictionary:
|
||||
var message_to_server: Dictionary = {}
|
||||
|
||||
if need_update_user == UpdateNeeded.FromLocal:
|
||||
if need_update_user == UpdateNeeded.FROM_LOCAL:
|
||||
message_to_server["user"] = {
|
||||
"account_type": UserDataManager.teacher_settings.account_type,
|
||||
"education_method": UserDataManager.teacher_settings.education_method,
|
||||
"last_modified": UserDataManager.teacher_settings.last_modified
|
||||
}
|
||||
elif need_update_user == UpdateNeeded.FromServer:
|
||||
elif need_update_user == UpdateNeeded.FROM_SERVER:
|
||||
message_to_server["user"] = {"need_update": true}
|
||||
|
||||
message_to_server["students"] = {}
|
||||
@@ -241,14 +241,14 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
if student_entry.has("data"):
|
||||
var student_update: UpdateNeeded = student_entry["data"]
|
||||
|
||||
if student_update == UpdateNeeded.DeleteLocal:
|
||||
if student_update == UpdateNeeded.DELETE_LOCAL:
|
||||
UserDataManager.delete_student(student_code)
|
||||
continue
|
||||
|
||||
elif student_update == UpdateNeeded.DeleteServer:
|
||||
elif student_update == UpdateNeeded.DELETE_SERVER:
|
||||
student_block["delete"] = true
|
||||
|
||||
elif student_update == UpdateNeeded.FromLocal:
|
||||
elif student_update == UpdateNeeded.FROM_LOCAL:
|
||||
var device_id: int = UserDataManager.teacher_settings.get_student_device(student_code)
|
||||
if device_id == -1:
|
||||
Log.error("UserDatabaseSynchronizer: Student code %s has no device ID" % student_code)
|
||||
@@ -264,7 +264,7 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
"updated_at": student_data.last_modified
|
||||
})
|
||||
|
||||
elif student_update == UpdateNeeded.FromServer:
|
||||
elif student_update == UpdateNeeded.FROM_SERVER:
|
||||
student_block["need_update"] = true
|
||||
|
||||
var student_progression: StudentProgression = UserDataManager.get_student_progression_for_code(0, student_code)
|
||||
@@ -272,16 +272,16 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
Log.trace("Cannot find progression data for student %s" % str(student_code))
|
||||
elif student_entry.has("progression"):
|
||||
var progression_block: Dictionary = {}
|
||||
if student_entry.progression == UpdateNeeded.FromLocal:
|
||||
if student_entry.progression == UpdateNeeded.FROM_LOCAL:
|
||||
progression_block = {
|
||||
"version": student_progression.version,
|
||||
"unlocked": student_progression.unlocks,
|
||||
"highest_boss_defeated": student_progression.highest_boss_defeated,
|
||||
"updated_at": student_progression.last_modified
|
||||
}
|
||||
elif student_entry.progression == UpdateNeeded.FromServer:
|
||||
elif student_entry.progression == UpdateNeeded.FROM_SERVER:
|
||||
progression_block = {"need_update": true}
|
||||
elif student_entry.progression == UpdateNeeded.DeleteServer:
|
||||
elif student_entry.progression == UpdateNeeded.DELETE_SERVER:
|
||||
progression_block = {"delete": true}
|
||||
|
||||
if progression_block.size() > 0:
|
||||
@@ -290,14 +290,14 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
var student_remediation: UserRemediation = UserDataManager.get_student_remediation_data(student_code)
|
||||
if student_entry.has("remediation_gp"):
|
||||
var gp_remediation_block: Dictionary = {}
|
||||
if student_entry.remediation_gp == UpdateNeeded.FromLocal:
|
||||
if student_entry.remediation_gp == UpdateNeeded.FROM_LOCAL:
|
||||
var tuple_list: Array = []
|
||||
for key: int in student_remediation.gps_scores.keys():
|
||||
tuple_list.append([key, student_remediation.gps_scores[key]])
|
||||
gp_remediation_block = {"score_remediation": tuple_list, "updated_at": student_remediation.gp_last_modified}
|
||||
elif student_entry.remediation_gp == UpdateNeeded.FromServer:
|
||||
elif student_entry.remediation_gp == UpdateNeeded.FROM_SERVER:
|
||||
gp_remediation_block = {"need_update": true}
|
||||
elif student_entry.remediation_gp == UpdateNeeded.DeleteServer:
|
||||
elif student_entry.remediation_gp == UpdateNeeded.DELETE_SERVER:
|
||||
gp_remediation_block = {"delete": true}
|
||||
|
||||
if gp_remediation_block.size() > 0:
|
||||
@@ -305,14 +305,14 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
|
||||
if student_entry.has("remediation_syllables"):
|
||||
var syllables_remediation_block: Dictionary = {}
|
||||
if student_entry.remediation_syllables == UpdateNeeded.FromLocal:
|
||||
if student_entry.remediation_syllables == UpdateNeeded.FROM_LOCAL:
|
||||
var tuple_list: Array = []
|
||||
for key: int in student_remediation.syllables_scores.keys():
|
||||
tuple_list.append([key, student_remediation.syllables_scores[key]])
|
||||
syllables_remediation_block = {"score_remediation": tuple_list, "updated_at": student_remediation.syllables_last_modified}
|
||||
elif student_entry.remediation_syllables == UpdateNeeded.FromServer:
|
||||
elif student_entry.remediation_syllables == UpdateNeeded.FROM_SERVER:
|
||||
syllables_remediation_block = {"need_update": true}
|
||||
elif student_entry.remediation_syllables == UpdateNeeded.DeleteServer:
|
||||
elif student_entry.remediation_syllables == UpdateNeeded.DELETE_SERVER:
|
||||
syllables_remediation_block = {"delete": true}
|
||||
|
||||
if syllables_remediation_block.size() > 0:
|
||||
@@ -320,14 +320,14 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
|
||||
if student_entry.has("remediation_words"):
|
||||
var words_remediation_block: Dictionary = {}
|
||||
if student_entry.remediation_words == UpdateNeeded.FromLocal:
|
||||
if student_entry.remediation_words == UpdateNeeded.FROM_LOCAL:
|
||||
var tuple_list: Array = []
|
||||
for key: int in student_remediation.words_scores.keys():
|
||||
tuple_list.append([key, student_remediation.words_scores[key]])
|
||||
words_remediation_block = {"score_remediation": tuple_list, "updated_at": student_remediation.words_last_modified}
|
||||
elif student_entry.remediation_words == UpdateNeeded.FromServer:
|
||||
elif student_entry.remediation_words == UpdateNeeded.FROM_SERVER:
|
||||
words_remediation_block = {"need_update": true}
|
||||
elif student_entry.remediation_words == UpdateNeeded.DeleteServer:
|
||||
elif student_entry.remediation_words == UpdateNeeded.DELETE_SERVER:
|
||||
words_remediation_block = {"delete": true}
|
||||
|
||||
if words_remediation_block.size() > 0:
|
||||
@@ -336,14 +336,14 @@ func _build_message_to_server(need_update_user: UpdateNeeded, need_update_studen
|
||||
var student_confusion_matrix: UserConfusionMatrix = UserDataManager.get_student_confusion_matrix_data(student_code)
|
||||
if student_entry.has("confusion_matrix_gp"):
|
||||
var gp_confusion_matrix_block: Dictionary = {}
|
||||
if student_entry.confusion_matrix_gp == UpdateNeeded.FromLocal:
|
||||
if student_entry.confusion_matrix_gp == UpdateNeeded.FROM_LOCAL:
|
||||
var tuple_list: Array = []
|
||||
for key: int in student_confusion_matrix.gp_scores.keys():
|
||||
tuple_list.append([key, student_confusion_matrix.gp_scores[key]])
|
||||
gp_confusion_matrix_block = {"confusion_matrix": tuple_list, "updated_at": student_confusion_matrix.gp_last_modified}
|
||||
elif student_entry.confusion_matrix_gp == UpdateNeeded.FromServer:
|
||||
elif student_entry.confusion_matrix_gp == UpdateNeeded.FROM_SERVER:
|
||||
gp_confusion_matrix_block = {"need_update": true}
|
||||
elif student_entry.confusion_matrix_gp == UpdateNeeded.DeleteServer:
|
||||
elif student_entry.confusion_matrix_gp == UpdateNeeded.DELETE_SERVER:
|
||||
gp_confusion_matrix_block = {"delete": true}
|
||||
|
||||
if gp_confusion_matrix_block.size() > 0:
|
||||
@@ -474,7 +474,7 @@ func synchronize() -> void:
|
||||
return
|
||||
|
||||
var need_update_user: UpdateNeeded = _determine_user_update(response_body)
|
||||
if need_update_user == UpdateNeeded.Nothing:
|
||||
if need_update_user == UpdateNeeded.NOTHING:
|
||||
Log.trace("UserDatabaseSynchronizer: User data timestamp is the same in local and on server. No synchronization necessary")
|
||||
if not synchronizing:
|
||||
return
|
||||
|
||||
@@ -84,8 +84,8 @@ func test_full_account_creation_login_and_deletion() -> void:
|
||||
gut.p("Step 2: Building registration payload…")
|
||||
|
||||
var register_data: TeacherSettings = TeacherSettings.new()
|
||||
register_data.account_type = TeacherSettings.AccountType.Teacher
|
||||
register_data.education_method = TeacherSettings.EducationMethod.Complete
|
||||
register_data.account_type = TeacherSettings.AccountType.TEACHER
|
||||
register_data.education_method = TeacherSettings.EducationMethod.COMPLETE
|
||||
register_data.email = _test_email
|
||||
register_data.password = TEST_PASSWORD
|
||||
register_data.language = "fr_FR"
|
||||
@@ -94,20 +94,20 @@ func test_full_account_creation_login_and_deletion() -> void:
|
||||
var student_alice: StudentData = StudentData.new()
|
||||
student_alice.code = 123
|
||||
student_alice.name = "Alice"
|
||||
student_alice.level = StudentData.Level.Beginner
|
||||
student_alice.level = StudentData.Level.BEGINNER
|
||||
student_alice.age = 7
|
||||
|
||||
var student_bob: StudentData = StudentData.new()
|
||||
student_bob.code = 124
|
||||
student_bob.name = "Bob"
|
||||
student_bob.level = StudentData.Level.Reviewer
|
||||
student_bob.level = StudentData.Level.REVIEWER
|
||||
student_bob.age = 8
|
||||
|
||||
# Device 2 — one student
|
||||
var student_charlie: StudentData = StudentData.new()
|
||||
student_charlie.code = 321
|
||||
student_charlie.name = "Charlie"
|
||||
student_charlie.level = StudentData.Level.Adult
|
||||
student_charlie.level = StudentData.Level.ADULT
|
||||
student_charlie.age = 10
|
||||
|
||||
register_data.students[1] = [student_alice, student_bob]
|
||||
@@ -166,9 +166,9 @@ func test_full_account_creation_login_and_deletion() -> void:
|
||||
|
||||
# --- Basic fields ---
|
||||
assert_eq(str(login_body.email), _test_email, "Returned email should match")
|
||||
assert_eq(login_body.account_type as int, TeacherSettings.AccountType.Teacher,
|
||||
assert_eq(login_body.account_type as int, TeacherSettings.AccountType.TEACHER,
|
||||
"Account type should be Teacher (0)")
|
||||
assert_eq(login_body.education_method as int, TeacherSettings.EducationMethod.Complete,
|
||||
assert_eq(login_body.education_method as int, TeacherSettings.EducationMethod.COMPLETE,
|
||||
"Education method should be Complete (1)")
|
||||
assert_eq(str(login_body.language), "fr_FR", "Language should be 'fr'")
|
||||
assert_true(login_body.has("token"), "Login response must include token")
|
||||
@@ -198,13 +198,13 @@ func test_full_account_creation_login_and_deletion() -> void:
|
||||
found_alice = true
|
||||
assert_eq(str(student.name), "Alice", "Student 123 should be Alice")
|
||||
assert_eq(student.age as int, 7, "Alice should be age 7")
|
||||
assert_eq(student.level as int, StudentData.Level.Beginner,
|
||||
assert_eq(student.level as int, StudentData.Level.BEGINNER,
|
||||
"Alice should be Beginner (0)")
|
||||
124:
|
||||
found_bob = true
|
||||
assert_eq(str(student.name), "Bob", "Student 124 should be Bob")
|
||||
assert_eq(student.age as int, 8, "Bob should be age 8")
|
||||
assert_eq(student.level as int, StudentData.Level.Reviewer,
|
||||
assert_eq(student.level as int, StudentData.Level.REVIEWER,
|
||||
"Bob should be Reviewer (1)")
|
||||
assert_true(found_alice, "Alice (code 123) should be present on device 1")
|
||||
assert_true(found_bob, "Bob (code 124) should be present on device 1")
|
||||
@@ -215,7 +215,7 @@ func test_full_account_creation_login_and_deletion() -> void:
|
||||
assert_eq(charlie.code as int, 321, "Device 2 student should have code 321")
|
||||
assert_eq(str(charlie.name), "Charlie", "Student 321 should be Charlie")
|
||||
assert_eq(charlie.age as int, 10, "Charlie should be age 10")
|
||||
assert_eq(charlie.level as int, StudentData.Level.Adult,
|
||||
assert_eq(charlie.level as int, StudentData.Level.ADULT,
|
||||
"Charlie should be Adult (2)")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -3,14 +3,14 @@ extends GutTest
|
||||
var default_student_data_dict: Dictionary = {
|
||||
"code": 0,
|
||||
"name": "",
|
||||
"level": StudentData.Level.Beginner,
|
||||
"level": StudentData.Level.BEGINNER,
|
||||
"age": 0,
|
||||
"last_modified": ""
|
||||
}
|
||||
var modified_student_data_dict: Dictionary = {
|
||||
"code": 123,
|
||||
"name": "Alice",
|
||||
"level": StudentData.Level.Adult,
|
||||
"level": StudentData.Level.ADULT,
|
||||
"age": 7,
|
||||
"last_modified": Time.get_datetime_string_from_unix_time(12345)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func test_to_dict() -> void:
|
||||
assert_eq_deep(default_student.to_dict(), default_student_data_dict)
|
||||
default_student.code = 123
|
||||
default_student.name = "Alice"
|
||||
default_student.level = StudentData.Level.Adult
|
||||
default_student.level = StudentData.Level.ADULT
|
||||
default_student.age = 7
|
||||
default_student.last_modified = Time.get_datetime_string_from_unix_time(12345)
|
||||
assert_eq_deep(default_student.to_dict(), modified_student_data_dict)
|
||||
|
||||
Reference in New Issue
Block a user