Nomenclatures corrections
This commit is contained in:
@@ -57,10 +57,7 @@ def split_params(param_string: str):
|
||||
return params
|
||||
|
||||
EXCLUDED_DIRS = {"addons", ".git", ".github"}
|
||||
|
||||
PASCAL_CASE = re.compile(r"^[A-Z][A-Za-z0-9]*$")
|
||||
SNAKE_CASE = re.compile(r"^_?[a-z][a-z0-9_]*$")
|
||||
UPPER_SNAKE_CASE = re.compile(r"^_?[A-Z][A-Z0-9_]*$")
|
||||
EXCLUDED_FILES = {os.path.normpath("script_templates/Node/default.gd")}
|
||||
|
||||
issues = []
|
||||
MESSAGES = {
|
||||
@@ -69,63 +66,317 @@ MESSAGES = {
|
||||
'variable': 'is a variable name and should be in snake_case',
|
||||
'constant': 'is a constant name and should be in UPPER_SNAKE_CASE',
|
||||
'signal': 'is a signal name and should be in snake_case',
|
||||
'annotation_order': 'annotations must be on the first line and include only \@tool, \@icon or \@static_unload',
|
||||
'class_position': 'class_name must appear after annotations (if any)',
|
||||
'extends_position': 'extends must appear after annotation and class_name',
|
||||
'extends_missing': 'extends is required and must follow annotation and class_name',
|
||||
'func_blank': 'functions must be preceded by exactly two empty lines (or only one if preceded by a #region comment)',
|
||||
'signal_position': 'signals must come right after extends',
|
||||
'signal_format': 'signals must end with ()',
|
||||
'signal_blank': 'signals must not be separated by empty lines',
|
||||
'enum_position': 'enums must come after signals and be grouped together',
|
||||
'enum_format': "enum declaration should be 'enum Name {'",
|
||||
'enum_member_blank': 'enum members must not be separated by empty lines',
|
||||
'enum_member_indent': 'enum members must be indented with a single tab',
|
||||
'enum_no_close': 'enum declaration is missing a closing }',
|
||||
'enum_blank': 'enums must be preceded by one empty line and must not be separated by empty lines',
|
||||
'const_position': 'constants must come after enums and be grouped together',
|
||||
'const_blank': 'constants must be preceded by one empty line and must not be separated by empty lines',
|
||||
'static_position': 'static variables must come after constants and be grouped together',
|
||||
'static_blank': 'static variables must be preceded by one empty line and must not be separated by empty lines',
|
||||
'export_position': 'export variables must come after static variables and be grouped together',
|
||||
'export_blank': 'export variables must be preceded by one empty line and must not be separated by empty lines',
|
||||
'var_position': 'variables must come after export variables and be grouped together',
|
||||
'var_blank': 'variables must be preceded by one empty line and must not be separated by empty lines',
|
||||
'onready_position': '@onready variables must come after other variables and be grouped together',
|
||||
'onready_blank': '@onready variables must be preceded by one empty line and must not be separated by empty lines',
|
||||
}
|
||||
|
||||
# Naming conventions
|
||||
PASCAL_CASE = re.compile(r"^[A-Z][A-Za-z0-9]*$")
|
||||
SNAKE_CASE = re.compile(r"^_?[a-z][a-z0-9_]*$")
|
||||
UPPER_SNAKE_CASE = re.compile(r"^_?[A-Z][A-Z0-9_]*$")
|
||||
REGION_RE = re.compile(r"#\s*(region|endregion)\b", re.IGNORECASE)
|
||||
|
||||
def check_naming(path: str, lines: list[str]):
|
||||
for idx, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#') or stripped.startswith('@warning_ignore(') or not stripped:
|
||||
continue
|
||||
match_class = re.match(r"class_name\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_class:
|
||||
name = match_class.group(1)
|
||||
if not PASCAL_CASE.match(name):
|
||||
issues.append((path, idx, 'class', name))
|
||||
match_func = re.match(r"(?:static\s+)?func\s+([A-Za-z0-9_]+)\s*(\([^)]*\))?", stripped)
|
||||
if match_func:
|
||||
name = match_func.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'function', name))
|
||||
params = match_func.group(2)
|
||||
if params:
|
||||
params = params.strip('()')
|
||||
for param in split_params(params):
|
||||
param_name = param.split(':')[0].split('=')[0].strip()
|
||||
if param_name and not SNAKE_CASE.match(param_name):
|
||||
issues.append((path, idx, 'variable', param_name))
|
||||
match_export = re.match(r"(?:@export\s+)?var\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_export:
|
||||
name = match_export.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'variable', name))
|
||||
match_const = re.match(r"const\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_const:
|
||||
name = match_const.group(1)
|
||||
if not UPPER_SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'constant', name))
|
||||
match_signal = re.match(r"signal\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_signal:
|
||||
name = match_signal.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'signal', name))
|
||||
match_for = re.match(r"for\s+([A-Za-z0-9_]+)(?:\s*:\s*[^\s]+)?\s+in\b", stripped)
|
||||
if match_for:
|
||||
name = match_for.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'variable', name))
|
||||
|
||||
# Content order
|
||||
ANNOTATION_LINE_RE = re.compile(
|
||||
r"^(?:@(tool|icon|static_unload)(?:\([^\n]*\))?)(?:,\s*@(tool|icon|static_unload)(?:\([^\n]*\))?)*$"
|
||||
)
|
||||
ALLOWED_ANNOTATION_RE = re.compile(r"@(tool|icon|static_unload)\b")
|
||||
|
||||
def check_content_order(path: str, lines: list[str]):
|
||||
content = [
|
||||
(line.rstrip('\n'), idx)
|
||||
for idx, line in enumerate(lines, 1)
|
||||
if not line.startswith(' ') # Line is not indented
|
||||
and not line.lstrip().startswith('#')
|
||||
and not line.lstrip().startswith('@warning_ignore(')
|
||||
]
|
||||
idx = 0
|
||||
n = len(content)
|
||||
|
||||
# 1) annotations
|
||||
if idx < n and ANNOTATION_LINE_RE.fullmatch(content[idx][0].strip()):
|
||||
idx += 1
|
||||
else:
|
||||
for j in range(idx, n):
|
||||
if ALLOWED_ANNOTATION_RE.search(content[j][0]):
|
||||
issues.append((path, content[j][1], 'annotation_order', content[j][0].strip()))
|
||||
break
|
||||
|
||||
# 2) class_name (optional)
|
||||
for j in range(idx, n):
|
||||
if content[j][0].strip().startswith('class_name'):
|
||||
if j != idx:
|
||||
issues.append((path, content[j][1], 'class_position', 'class_name'))
|
||||
else:
|
||||
idx += 1
|
||||
break
|
||||
|
||||
# 3) extends (required)
|
||||
extends_pos = None
|
||||
for j in range(idx, n):
|
||||
if content[j][0].strip().startswith('extends'):
|
||||
extends_pos = j
|
||||
break
|
||||
if extends_pos is None:
|
||||
issues.append((path, 0, 'extends_missing', 'extends'))
|
||||
return
|
||||
if extends_pos != idx:
|
||||
issues.append((path, content[extends_pos][1], 'extends_position', 'extends'))
|
||||
idx = extends_pos + 1
|
||||
|
||||
order = ['signal', 'enum', 'const', 'static var', '@export', 'var', '@onready var']
|
||||
order_index = {name: i for i, name in enumerate(order)}
|
||||
seen: set[str] = set()
|
||||
current_order = -1
|
||||
prev_token: str | None = content[extends_pos][0].strip()
|
||||
j = idx
|
||||
while j < n:
|
||||
line, line_no = content[j]
|
||||
stripped = line.strip()
|
||||
if stripped == '':
|
||||
prev_token = ''
|
||||
j += 1
|
||||
continue
|
||||
if re.match(r'(?:static\s+)?func\b', stripped):
|
||||
break
|
||||
token = None
|
||||
if stripped.startswith('signal'):
|
||||
token = 'signal'
|
||||
if not re.fullmatch(r"signal\s+\w+\([^)]*\)", stripped):
|
||||
issues.append((path, line_no, 'signal_format', stripped))
|
||||
if 'signal' in seen and prev_token == '':
|
||||
issues.append((path, content[j - 1][1], 'signal_blank', 'signal'))
|
||||
seen.add('signal')
|
||||
elif stripped.startswith('enum'):
|
||||
token = 'enum'
|
||||
if not re.fullmatch(r"enum\s+\w+\s*{", stripped):
|
||||
issues.append((path, line_no, 'enum_format', stripped))
|
||||
if 'enum' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'enum_blank', 'enum'))
|
||||
else:
|
||||
if prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'enum_blank', 'enum'))
|
||||
k = j + 1
|
||||
while k < n and content[k][0].strip() != '}':
|
||||
member, m_line_no = content[k]
|
||||
if member.strip() == '':
|
||||
issues.append((path, m_line_no, 'enum_member_blank', 'enum'))
|
||||
if not member.startswith('\t') or member.startswith('\t\t'):
|
||||
issues.append((path, m_line_no, 'enum_member_indent', member.strip()))
|
||||
k += 1
|
||||
if k >= n:
|
||||
issues.append((path, line_no, 'enum_no_close', 'enum'))
|
||||
return
|
||||
j = k
|
||||
prev_token = '}'
|
||||
seen.add('enum')
|
||||
curr_order = order_index['enum']
|
||||
if curr_order < current_order:
|
||||
issues.append((path, line_no, 'enum_position', 'enum'))
|
||||
else:
|
||||
current_order = max(current_order, curr_order)
|
||||
j += 1
|
||||
continue
|
||||
elif stripped.startswith('const '):
|
||||
token = 'const'
|
||||
if 'const' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'const_blank', 'const'))
|
||||
else:
|
||||
if prev_token != 'const' and prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'const_blank', 'const'))
|
||||
seen.add('const')
|
||||
elif stripped.startswith('static var '):
|
||||
token = 'static var'
|
||||
if 'static var' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'static_blank', 'static var'))
|
||||
else:
|
||||
if prev_token != 'static var' and prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'static_blank', 'static var'))
|
||||
seen.add('static var')
|
||||
elif stripped.startswith('@export'):
|
||||
token = '@export'
|
||||
if '@export' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'export_blank', '@export'))
|
||||
else:
|
||||
if prev_token != '@export' and prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'export_blank', '@export'))
|
||||
seen.add('@export')
|
||||
elif stripped.startswith('var '):
|
||||
token = 'var'
|
||||
if 'var' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'var_blank', 'var'))
|
||||
else:
|
||||
if prev_token != 'var' and prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'var_blank', 'var'))
|
||||
seen.add('var')
|
||||
elif stripped.startswith('@onready var '):
|
||||
token = '@onready var'
|
||||
if '@onready var' not in seen:
|
||||
if prev_token != '':
|
||||
issues.append((path, line_no, 'onready_blank', '@onready var'))
|
||||
else:
|
||||
if prev_token != '@onready var' and prev_token != '}' and prev_token != ']':
|
||||
issues.append((path, line_no, 'onready_blank', '@onready var'))
|
||||
seen.add('@onready var')
|
||||
else:
|
||||
prev_token = stripped
|
||||
j += 1
|
||||
continue
|
||||
curr_order = order_index[token]
|
||||
key_map = {
|
||||
'signal': 'signal_position',
|
||||
'enum': 'enum_position',
|
||||
'const': 'const_position',
|
||||
'static var': 'static_position',
|
||||
'@export': 'export_position',
|
||||
'var': 'var_position',
|
||||
'@onready var': 'onready_position',
|
||||
}
|
||||
if curr_order < current_order:
|
||||
issues.append((path, line_no, key_map[token], token))
|
||||
else:
|
||||
current_order = max(current_order, curr_order)
|
||||
|
||||
prev_token = token
|
||||
j += 1
|
||||
|
||||
|
||||
# Spacing for functions
|
||||
def check_func_spacing(path: str):
|
||||
with open(path, 'r', encoding='utf-8') as file:
|
||||
lines = file.readlines()
|
||||
for idx, line in enumerate(lines):
|
||||
stripped_line = line.lstrip()
|
||||
if re.match(r'(?:static\s+)?func\b', stripped_line):
|
||||
if re.match(r'(?:static\s+)?func\s*\(', stripped_line):
|
||||
continue
|
||||
start_idx = idx
|
||||
# Skip annotations and regular comments above the function
|
||||
while start_idx > 0:
|
||||
prev_line = lines[start_idx - 1].lstrip()
|
||||
if prev_line.startswith('@warning_ignore('):
|
||||
start_idx -= 1
|
||||
elif prev_line.startswith('#') and not REGION_RE.match(prev_line):
|
||||
start_idx -= 1
|
||||
else:
|
||||
break
|
||||
test_index = start_idx - 1
|
||||
blank_count = 0
|
||||
while test_index >= 0:
|
||||
stripped = lines[test_index].lstrip()
|
||||
if stripped == '':
|
||||
blank_count += 1
|
||||
test_index -= 1
|
||||
continue
|
||||
if stripped.startswith('@warning_ignore('):
|
||||
test_index -= 1
|
||||
continue
|
||||
if stripped.startswith('#') and not REGION_RE.match(stripped):
|
||||
test_index -= 1
|
||||
continue
|
||||
break
|
||||
region_above = test_index >= 0 and REGION_RE.match(lines[test_index].lstrip())
|
||||
if region_above:
|
||||
if blank_count > 1:
|
||||
issues.append((path, idx + 1, 'func_blank', 'func'))
|
||||
continue
|
||||
elif blank_count != 2:
|
||||
if not (blank_count == 1 and test_index >= 0 and lines[test_index].lstrip().startswith('#region')):
|
||||
issues.append((path, idx + 1, 'func_blank', 'func'))
|
||||
|
||||
|
||||
# Main function
|
||||
for root, dirs, files in os.walk('.', topdown=True):
|
||||
rel_root = os.path.relpath(root, '.')
|
||||
if any(rel_root == excluded or rel_root.startswith(f"{excluded}{os.sep}") for excluded in EXCLUDED_DIRS):
|
||||
dirs[:] = []
|
||||
continue
|
||||
for fname in files:
|
||||
if fname.endswith('.gd'):
|
||||
path = os.path.join(root, fname)
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as file:
|
||||
lines = file.readlines()
|
||||
except Exception as e:
|
||||
issues.append((path, 0, 'error', f'Could not read file: {e}'))
|
||||
continue
|
||||
for idx, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('#') or not stripped:
|
||||
continue
|
||||
match_class = re.match(r"class_name\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_class:
|
||||
name = match_class.group(1)
|
||||
if not PASCAL_CASE.match(name):
|
||||
issues.append((path, idx, 'class', name))
|
||||
match_func = re.match(r"func\s+([A-Za-z0-9_]+)\s*(\([^)]*\))?", stripped)
|
||||
if match_func:
|
||||
name = match_func.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'function', name))
|
||||
params = match_func.group(2)
|
||||
if params:
|
||||
params = params.strip('()')
|
||||
for param in split_params(params):
|
||||
param_name = param.split(':')[0].split('=')[0].strip()
|
||||
if param_name and not SNAKE_CASE.match(param_name):
|
||||
issues.append((path, idx, 'variable', param_name))
|
||||
match_export = re.match(r"(?:@export\s+)?var\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_export:
|
||||
name = match_export.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'variable', name))
|
||||
match_const = re.match(r"const\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_const:
|
||||
name = match_const.group(1)
|
||||
if not UPPER_SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'constant', name))
|
||||
match_signal = re.match(r"signal\s+([A-Za-z0-9_]+)", stripped)
|
||||
if match_signal:
|
||||
name = match_signal.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'signal', name))
|
||||
match_for = re.match(r"for\s+([A-Za-z0-9_]+)(?:\s*:\s*[^\s]+)?\s+in\b", stripped)
|
||||
if match_for:
|
||||
name = match_for.group(1)
|
||||
if not SNAKE_CASE.match(name):
|
||||
issues.append((path, idx, 'variable', name))
|
||||
if not fname.endswith('.gd'):
|
||||
continue
|
||||
path = os.path.join(root, fname)
|
||||
rel_path = os.path.normpath(os.path.relpath(path, '.'))
|
||||
if rel_path in EXCLUDED_FILES:
|
||||
continue
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as file:
|
||||
lines = file.readlines()
|
||||
except Exception as e:
|
||||
issues.append((path, 0, 'error', f'Could not read file: {e}'))
|
||||
continue
|
||||
check_naming(path, lines)
|
||||
check_content_order(path, lines)
|
||||
check_func_spacing(path)
|
||||
|
||||
if issues:
|
||||
print("### \u274c GDScript Naming Convention Check Failed\n")
|
||||
|
||||
@@ -8,6 +8,7 @@ const CONFIG_PATH: String = "user://environment.cfg"
|
||||
var env_selector: OptionButton
|
||||
var current_environment: EnvType = EnvType.DEV
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
# Create OptionButton
|
||||
env_selector = OptionButton.new()
|
||||
@@ -25,27 +26,32 @@ func _enter_tree() -> void:
|
||||
# Reflect the current environment in UI
|
||||
env_selector.select(current_environment)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
# Clean up the UI and save state
|
||||
remove_control_from_container(EditorPlugin.CONTAINER_TOOLBAR, env_selector)
|
||||
env_selector.queue_free()
|
||||
|
||||
|
||||
func _on_env_selected(index: int) -> void:
|
||||
current_environment = index
|
||||
save_environment()
|
||||
print("Environment switched to:", _environment_to_string(current_environment))
|
||||
|
||||
|
||||
func _environment_to_string(env: EnvType) -> String:
|
||||
match env:
|
||||
EnvType.DEV: return "DEV"
|
||||
EnvType.PROD: return "PROD"
|
||||
_: return "Unknown"
|
||||
|
||||
|
||||
func save_environment() -> void:
|
||||
var config = ConfigFile.new()
|
||||
config.set_value("environment", "current", str(current_environment))
|
||||
config.save(CONFIG_PATH)
|
||||
|
||||
|
||||
func load_environment() -> void:
|
||||
var config = ConfigFile.new()
|
||||
if config.load(CONFIG_PATH) == OK:
|
||||
|
||||
@@ -2,6 +2,7 @@ extends PanelContainer
|
||||
|
||||
@onready var form_validator_control: FormValidator = $MarginContainer/FormValidator
|
||||
|
||||
|
||||
func _on_form_validator_control_control_validated(control, passed, messages) -> void:
|
||||
var error_label = _get_error_label(control)
|
||||
var valid_label = _get_valid_label(control)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name AlphanumericRule
|
||||
extends ValidatorRule
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name BooleanRule
|
||||
extends ValidatorRule
|
||||
|
||||
@export var target_value: bool
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name DoesNotMatchRule
|
||||
extends ValidatorRule
|
||||
|
||||
@export var pattern: String
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name EqualsRule
|
||||
extends ValidatorRule
|
||||
|
||||
@export var target_value: String
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name NotBlankRule
|
||||
extends ValidatorRule
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends RefCounted
|
||||
class_name RuleResult
|
||||
extends RefCounted
|
||||
|
||||
var passed: bool = false
|
||||
var message: String = ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends Resource
|
||||
class_name ValidatorRule
|
||||
extends Resource
|
||||
|
||||
@export var fail_message: String = ""
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
extends ZIPReader
|
||||
class_name FolderUnzipper
|
||||
extends ZIPReader
|
||||
|
||||
signal file_count(count: int)
|
||||
signal file_copied(count: int, name: String)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
extends ZIPPacker
|
||||
class_name FolderZipper
|
||||
extends ZIPPacker
|
||||
|
||||
|
||||
func write_folder_recursive(abs_path: String, rel_path: String) -> Error:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
extends Resource
|
||||
class_name ProfToolSave
|
||||
extends Resource
|
||||
|
||||
@export var selected_language: String = ""
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@tool
|
||||
extends Resource
|
||||
class_name DeviceSettings
|
||||
|
||||
extends Resource
|
||||
|
||||
const SUPPORTED_LOCALES: Array[String] = [
|
||||
# French France
|
||||
@@ -26,25 +25,21 @@ const SUPPORTED_LOCALES: Array[String] = [
|
||||
@export var device_id: int
|
||||
@export var language_versions: Dictionary = {} # locale: datetime
|
||||
@export var game_version: String = "0.0.1"
|
||||
|
||||
@export var master_volume: float = 0.0:
|
||||
set(volume):
|
||||
master_volume = volume
|
||||
var ind: int = AudioServer.get_bus_index("Master")
|
||||
AudioServer.set_bus_volume_db(ind, volume)
|
||||
|
||||
@export var music_volume: float = 0.0:
|
||||
set(volume):
|
||||
music_volume = volume
|
||||
var ind: int = AudioServer.get_bus_index("Music")
|
||||
AudioServer.set_bus_volume_db(ind, volume)
|
||||
|
||||
@export var voice_volume: float = 0.0:
|
||||
set(volume):
|
||||
voice_volume = volume
|
||||
var ind: int = AudioServer.get_bus_index("Voice")
|
||||
AudioServer.set_bus_volume_db(ind, volume)
|
||||
|
||||
@export var effects_volume: float = 0.0:
|
||||
set(volume):
|
||||
effects_volume = volume
|
||||
|
||||
+15
-17
@@ -1,15 +1,28 @@
|
||||
@tool
|
||||
extends Control
|
||||
class_name Garden
|
||||
extends Control
|
||||
|
||||
enum FlowerSizes{
|
||||
NOT_STARTED,
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
LARGE
|
||||
}
|
||||
|
||||
const FLOWER_PATH_MODEL: String = "res://assets/gardens/flowers/plant_%02d_%02d_%s.png"
|
||||
const BACKGROUND_PATH_MODEL: String = "res://assets/gardens/gardens/garden_%02d_open.png"
|
||||
|
||||
@export var garden_layout: GardenLayout:
|
||||
set = set_garden_layout
|
||||
|
||||
@export var garden_colors: Array[Color] = []
|
||||
|
||||
var flowers: Array[GardenLayout.Flower] = []
|
||||
var flowers_sizes: Array[FlowerSizes] = []
|
||||
var color: Color
|
||||
var current_progression: float = 0.0
|
||||
var max_progression: float = 0.0
|
||||
var garden_index: int = -1
|
||||
|
||||
@onready var buttons: Control = $Buttons
|
||||
@onready var flower_controls: Array[TextureRect] = [
|
||||
%Flower1,
|
||||
@@ -26,21 +39,6 @@ const BACKGROUND_PATH_MODEL: String = "res://assets/gardens/gardens/garden_%02d_
|
||||
%Button4,
|
||||
]
|
||||
|
||||
enum FlowerSizes{
|
||||
NOT_STARTED,
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
LARGE
|
||||
}
|
||||
|
||||
var flowers: Array[GardenLayout.Flower] = []
|
||||
var flowers_sizes: Array[FlowerSizes] = []
|
||||
var color: Color
|
||||
|
||||
var current_progression: float = 0.0
|
||||
var max_progression: float = 0.0
|
||||
|
||||
var garden_index: int = -1
|
||||
|
||||
func get_button_size() -> Vector2:
|
||||
return lesson_button_controls[0].get_size()
|
||||
|
||||
@@ -1,46 +1,6 @@
|
||||
@tool
|
||||
extends Resource
|
||||
class_name GardenLayout
|
||||
|
||||
|
||||
class Flower:
|
||||
var color: int = 0
|
||||
var type: int = 0
|
||||
var position: Vector2 = Vector2i.ZERO
|
||||
|
||||
func _init(p_color: int = 0, p_type: int = 0, p_position: Vector2i = Vector2i.ZERO) -> void:
|
||||
color = p_color
|
||||
type = p_type
|
||||
position = p_position
|
||||
|
||||
static func from_dict(d: Dictionary) -> Flower:
|
||||
return Flower.new(d.color as int, d.type as int, d.position as Vector2i)
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
return {
|
||||
color = color,
|
||||
type = type,
|
||||
position = position,
|
||||
}
|
||||
|
||||
|
||||
class GardenLayoutLessonButton:
|
||||
var position: Vector2i = Vector2i.ZERO
|
||||
var path_out_position: Vector2i = Vector2i.ZERO
|
||||
|
||||
func _init(p_position: Vector2i = Vector2i.ZERO, p_path_out_position: Vector2i = Vector2i.ZERO) -> void:
|
||||
position = p_position
|
||||
path_out_position = p_path_out_position
|
||||
|
||||
static func from_dict(d: Dictionary) -> GardenLayoutLessonButton:
|
||||
return GardenLayoutLessonButton.new(d.position as Vector2i, d.path_out_position as Vector2i)
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
return {
|
||||
position = position,
|
||||
path_out_position = path_out_position
|
||||
}
|
||||
|
||||
extends Resource
|
||||
|
||||
enum FirstOrLast {
|
||||
First,
|
||||
@@ -48,18 +8,18 @@ enum FirstOrLast {
|
||||
Last
|
||||
}
|
||||
|
||||
|
||||
@export var color: int = 0
|
||||
var flowers: Array[Flower] = []:
|
||||
set = set_flowers
|
||||
@export var flowers_export: Array[Dictionary] = []:
|
||||
set = set_flowers_export
|
||||
var lesson_buttons: Array[GardenLayoutLessonButton] = []:
|
||||
set = set_lesson_buttons
|
||||
@export var lesson_buttons_export: Array[Dictionary] = []:
|
||||
set = set_lesson_buttons_export
|
||||
@export var is_first_or_last: FirstOrLast = FirstOrLast.Neither
|
||||
|
||||
var flowers: Array[Flower] = []:
|
||||
set = set_flowers
|
||||
var lesson_buttons: Array[GardenLayoutLessonButton] = []:
|
||||
set = set_lesson_buttons
|
||||
|
||||
|
||||
func set_flowers_export(p_flowers_export: Array[Dictionary]) -> void:
|
||||
flowers_export = p_flowers_export
|
||||
@@ -87,3 +47,48 @@ func set_lesson_buttons(p_lesson_buttons: Array[GardenLayoutLessonButton]) -> vo
|
||||
lesson_buttons_export.clear()
|
||||
for lesson_button: GardenLayoutLessonButton in lesson_buttons:
|
||||
lesson_buttons_export.append(lesson_button.to_dict())
|
||||
|
||||
|
||||
class Flower:
|
||||
var color: int = 0
|
||||
var type: int = 0
|
||||
var position: Vector2 = Vector2i.ZERO
|
||||
|
||||
|
||||
func _init(p_color: int = 0, p_type: int = 0, p_position: Vector2i = Vector2i.ZERO) -> void:
|
||||
color = p_color
|
||||
type = p_type
|
||||
position = p_position
|
||||
|
||||
|
||||
static func from_dict(d: Dictionary) -> Flower:
|
||||
return Flower.new(d.color as int, d.type as int, d.position as Vector2i)
|
||||
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
return {
|
||||
color = color,
|
||||
type = type,
|
||||
position = position,
|
||||
}
|
||||
|
||||
|
||||
class GardenLayoutLessonButton:
|
||||
var position: Vector2i = Vector2i.ZERO
|
||||
var path_out_position: Vector2i = Vector2i.ZERO
|
||||
|
||||
|
||||
func _init(p_position: Vector2i = Vector2i.ZERO, p_path_out_position: Vector2i = Vector2i.ZERO) -> void:
|
||||
position = p_position
|
||||
path_out_position = p_path_out_position
|
||||
|
||||
|
||||
static func from_dict(d: Dictionary) -> GardenLayoutLessonButton:
|
||||
return GardenLayoutLessonButton.new(d.position as Vector2i, d.path_out_position as Vector2i)
|
||||
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
return {
|
||||
position = position,
|
||||
path_out_position = path_out_position
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@tool
|
||||
extends Resource
|
||||
class_name GardensLayout
|
||||
|
||||
extends Resource
|
||||
|
||||
@export var gardens: Array[GardenLayout] = []
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends Resource
|
||||
class_name StudentData
|
||||
|
||||
extends Resource
|
||||
|
||||
enum Level {
|
||||
Beginner,
|
||||
@@ -14,6 +13,7 @@ enum Level {
|
||||
@export var age: int = 0
|
||||
@export var last_modified: String = ""
|
||||
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
return {
|
||||
"code": code,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Resource
|
||||
class_name StudentProgression
|
||||
extends Resource
|
||||
|
||||
signal unlocks_changed()
|
||||
|
||||
@@ -17,6 +17,7 @@ enum Status{
|
||||
func _init() -> void:
|
||||
init_unlocks()
|
||||
|
||||
|
||||
# Make sure the unlocks are correct
|
||||
func init_unlocks() -> void:
|
||||
if not unlocks:
|
||||
@@ -73,6 +74,7 @@ func look_and_learn_completed(lesson_number: int) -> bool:
|
||||
unlocks_changed.emit()
|
||||
return true
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
extends Resource
|
||||
class_name TeacherSettings
|
||||
|
||||
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]
|
||||
|
||||
extends Resource
|
||||
|
||||
enum AccountType {
|
||||
Teacher,
|
||||
Parent
|
||||
}
|
||||
|
||||
enum EducationMethod {
|
||||
AppOnly,
|
||||
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]
|
||||
|
||||
@export var account_type: AccountType
|
||||
@export var education_method: EducationMethod
|
||||
var devices_count: int
|
||||
@export var students: Dictionary[int, Array] = {} # int (device): Array[StudentData]
|
||||
@export var email: String
|
||||
var password: String
|
||||
@export var token: String
|
||||
@export var last_modified: String = ""
|
||||
|
||||
var devices_count: int
|
||||
var password: String
|
||||
|
||||
|
||||
func update_from_dict(dict: Dictionary) -> void:
|
||||
if dict.has("account_type"):
|
||||
account_type = dict.account_type
|
||||
@@ -104,6 +104,7 @@ func get_new_code() -> int:
|
||||
var code: int = codes.pick_random()
|
||||
return code
|
||||
|
||||
|
||||
func to_dict() -> Dictionary:
|
||||
var dict: Dictionary = {
|
||||
"account_type": account_type,
|
||||
@@ -122,6 +123,7 @@ func to_dict() -> Dictionary:
|
||||
|
||||
return dict
|
||||
|
||||
|
||||
func get_number_of_students() -> int:
|
||||
if not students:
|
||||
return 0
|
||||
@@ -130,12 +132,14 @@ func get_number_of_students() -> int:
|
||||
result += data.size()
|
||||
return result
|
||||
|
||||
|
||||
func get_all_students_data() -> Array[StudentData]:
|
||||
var result: Array[StudentData] = []
|
||||
for data: Array[StudentData] in students.values():
|
||||
result.append_array(data)
|
||||
return result
|
||||
|
||||
|
||||
func delete_student(student_code: int) -> void:
|
||||
for device: int in students.keys():
|
||||
for index: int in range(students[device].size()):
|
||||
@@ -146,12 +150,14 @@ func delete_student(student_code: int) -> void:
|
||||
return
|
||||
Logger.warn("TeacherSettings: Trying to delete student, but code %d not found" % student_code)
|
||||
|
||||
|
||||
func get_student_with_code(student_code: int) -> StudentData:
|
||||
for student_data: StudentData in get_all_students_data():
|
||||
if student_data.code == student_code:
|
||||
return student_data
|
||||
return null
|
||||
|
||||
|
||||
func get_student_device(student_code: int) -> int:
|
||||
for device: int in students.keys():
|
||||
for index: int in range(students[device].size()):
|
||||
@@ -159,6 +165,7 @@ func get_student_device(student_code: int) -> int:
|
||||
return device
|
||||
return -1
|
||||
|
||||
|
||||
func set_data_student_with_code(student_code: int, new_device_id: int, new_name: String, new_age: int, new_last_modified: String) -> void:
|
||||
update_student_device(student_code, new_device_id)
|
||||
for student_data: StudentData in students[new_device_id]:
|
||||
|
||||
@@ -39,11 +39,9 @@ func append_and_trim(target: Dictionary[int, PackedInt32Array], expected_id: int
|
||||
|
||||
# Key is the ID of the expected answer
|
||||
# Value is a PackedInt32Array whose entries are the IDs of the answers
|
||||
@export
|
||||
var gp_scores: Dictionary[int, PackedInt32Array] = {}
|
||||
@export var gp_scores: Dictionary[int, PackedInt32Array] = {}
|
||||
@export var gp_last_modified: String = ""
|
||||
|
||||
@export
|
||||
var gp_last_modified: String = ""
|
||||
|
||||
# Gets (a copy of) the data of a GP
|
||||
func get_gp_scores(id: int) -> PackedInt32Array:
|
||||
@@ -52,6 +50,7 @@ func get_gp_scores(id: int) -> PackedInt32Array:
|
||||
return PackedInt32Array(gp_scores[id])
|
||||
return PackedInt32Array()
|
||||
|
||||
|
||||
# Updates the confusion matrix from a minigame scores
|
||||
func update_gp_scores(minigame_scores: Dictionary[int, PackedInt32Array]) -> void:
|
||||
if not minigame_scores or minigame_scores.is_empty():
|
||||
@@ -63,6 +62,7 @@ func update_gp_scores(minigame_scores: Dictionary[int, PackedInt32Array]) -> voi
|
||||
set_gp_last_modified(Time.get_datetime_string_from_system(true))
|
||||
score_changed.emit()
|
||||
|
||||
|
||||
func set_gp_scores(new_scores: Dictionary[int, PackedInt32Array]) -> void:
|
||||
var cleaned: Dictionary[int, PackedInt32Array] = {}
|
||||
for expected_id: int in new_scores.keys():
|
||||
@@ -71,6 +71,7 @@ func set_gp_scores(new_scores: Dictionary[int, PackedInt32Array]) -> void:
|
||||
gp_scores = cleaned
|
||||
set_gp_last_modified(Time.get_datetime_string_from_system(true))
|
||||
|
||||
|
||||
func set_gp_last_modified(new_date: String) -> void:
|
||||
gp_last_modified = new_date
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
extends Resource
|
||||
class_name UserDifficulty
|
||||
extends Resource
|
||||
|
||||
signal difficulty_changed()
|
||||
|
||||
# Stores the user history for all the minigames
|
||||
# Key -> minigame_name: String
|
||||
# Value -> UserMinigameHistory
|
||||
@export
|
||||
var minigames_histories: Dictionary = {}
|
||||
@export var minigames_histories: Dictionary = {}
|
||||
|
||||
|
||||
# Gets the difficulty of the user for the given minigame
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
extends Resource
|
||||
class_name UserMinigameHistory
|
||||
extends Resource
|
||||
|
||||
const MIN_DIFFICULTY: int = 0
|
||||
const MAX_DIFFICULTY: int = 4
|
||||
const CONSECUTIVE_WINS_TO_PROMOTE: int = 2
|
||||
const CONSECUTIVE_LOSSES_TO_DEMOTE: int = 2
|
||||
|
||||
@export
|
||||
var difficulty: int = 0
|
||||
@export
|
||||
var consecutives_losses: int = 0
|
||||
@export
|
||||
var consecutives_wins: int = 0
|
||||
@export
|
||||
var history: Array[bool] = []
|
||||
@export var difficulty: int = 0
|
||||
@export var consecutives_losses: int = 0
|
||||
@export var consecutives_wins: int = 0
|
||||
@export var history: Array[bool] = []
|
||||
|
||||
|
||||
func add_game(is_won: bool) -> void:
|
||||
history.append(is_won)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
class_name UserRemediation
|
||||
extends Resource
|
||||
|
||||
## A remediation score indicates how much extra practice an item needs.
|
||||
## It is a cumulative value that becomes more negative when the student struggles
|
||||
## and rises back toward 0 as they succeed. Items at or below a defined
|
||||
@@ -19,11 +18,9 @@ const REMEDIATION_SCORE: int = -2
|
||||
|
||||
# Key is the ID of the GP
|
||||
# Value is the score of the GP
|
||||
@export
|
||||
var gps_scores: Dictionary[int, int] = {}
|
||||
@export var gps_scores: Dictionary[int, int] = {}
|
||||
@export var gp_last_modified: String = ""
|
||||
|
||||
@export
|
||||
var gp_last_modified: String = ""
|
||||
|
||||
# Gets the score of a GP if it is below or equals to the remediation score
|
||||
func get_gp_score(id: int) -> int:
|
||||
@@ -31,6 +28,7 @@ func get_gp_score(id: int) -> int:
|
||||
return gps_scores[id] if gps_scores[id] <= REMEDIATION_SCORE else 0
|
||||
return 0
|
||||
|
||||
|
||||
# Updates the gp scores from a minigame scores
|
||||
func update_gp_scores(minigame_scores: Dictionary) -> void:
|
||||
if not minigame_scores:
|
||||
@@ -47,9 +45,11 @@ func update_gp_scores(minigame_scores: Dictionary) -> void:
|
||||
set_gp_last_modified(Time.get_datetime_string_from_system(true))
|
||||
score_changed.emit()
|
||||
|
||||
|
||||
func set_gp_scores(new_scores: Dictionary[int, int]) -> void:
|
||||
gps_scores = new_scores
|
||||
|
||||
|
||||
func set_gp_last_modified(new_date: String) -> void:
|
||||
gp_last_modified = new_date
|
||||
|
||||
@@ -59,11 +59,9 @@ func set_gp_last_modified(new_date: String) -> void:
|
||||
|
||||
# Key is the ID of the syllable
|
||||
# Value is the score of the syllable
|
||||
@export
|
||||
var syllables_scores: Dictionary[int, int] = {}
|
||||
@export var syllables_scores: Dictionary[int, int] = {}
|
||||
@export var syllables_last_modified: String = ""
|
||||
|
||||
@export
|
||||
var syllables_last_modified: String = ""
|
||||
|
||||
# Gets the score of a syllable if it is below or equals to the remediation score
|
||||
func get_syllable_score(id: int) -> int:
|
||||
@@ -71,6 +69,7 @@ func get_syllable_score(id: int) -> int:
|
||||
return syllables_scores[id] if syllables_scores[id] <= REMEDIATION_SCORE else 0
|
||||
return 0
|
||||
|
||||
|
||||
# Updates the syllables scores from a minigame scores
|
||||
func update_syllables_scores(minigame_scores: Dictionary) -> void:
|
||||
if not minigame_scores:
|
||||
@@ -87,9 +86,11 @@ func update_syllables_scores(minigame_scores: Dictionary) -> void:
|
||||
set_syllables_last_modified(Time.get_datetime_string_from_system(true))
|
||||
score_changed.emit()
|
||||
|
||||
|
||||
func set_syllables_scores(new_scores: Dictionary[int, int]) -> void:
|
||||
syllables_scores = new_scores
|
||||
|
||||
|
||||
func set_syllables_last_modified(new_date: String) -> void:
|
||||
syllables_last_modified = new_date
|
||||
|
||||
@@ -99,11 +100,9 @@ func set_syllables_last_modified(new_date: String) -> void:
|
||||
|
||||
# Key is the ID of the word
|
||||
# Value is the score of the word
|
||||
@export
|
||||
var words_scores: Dictionary[int, int] = {}
|
||||
@export var words_scores: Dictionary[int, int] = {}
|
||||
@export var words_last_modified: String = ""
|
||||
|
||||
@export
|
||||
var words_last_modified: String = ""
|
||||
|
||||
# Gets the score of a word if it is below or equals to the remediation score
|
||||
func get_word_score(id: int) -> int:
|
||||
@@ -111,6 +110,7 @@ func get_word_score(id: int) -> int:
|
||||
return words_scores[id] if words_scores[id] <= REMEDIATION_SCORE else 0
|
||||
return 0
|
||||
|
||||
|
||||
# Updates the words scores from a minigame scores
|
||||
func update_words_scores(minigame_scores: Dictionary) -> void:
|
||||
if not minigame_scores:
|
||||
@@ -127,9 +127,11 @@ func update_words_scores(minigame_scores: Dictionary) -> void:
|
||||
set_words_last_modified(Time.get_datetime_string_from_system(true))
|
||||
score_changed.emit()
|
||||
|
||||
|
||||
func set_words_scores(new_scores: Dictionary[int, int]) -> void:
|
||||
words_scores = new_scores
|
||||
|
||||
|
||||
func set_words_last_modified(new_date: String) -> void:
|
||||
words_last_modified = new_date
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
extends Resource
|
||||
class_name UserSpeeches
|
||||
extends Resource
|
||||
|
||||
signal speeches_changed
|
||||
signal speeches_changed()
|
||||
|
||||
# Contains the list of speeches already played
|
||||
@export var speeches_played: Array[String] = []
|
||||
|
||||
|
||||
func add_speech(speech: String) -> void:
|
||||
if not speeches_played.has(speech):
|
||||
speeches_played.append(speech)
|
||||
speeches_changed.emit()
|
||||
|
||||
|
||||
func is_speech_played(speech: String) -> bool:
|
||||
return speeches_played.has(speech)
|
||||
|
||||
@@ -22,13 +22,20 @@ extends _BASE_
|
||||
## and any further detail.
|
||||
|
||||
#05. signals
|
||||
|
||||
#06. enums
|
||||
|
||||
#07. constants
|
||||
|
||||
#08. static variables
|
||||
|
||||
#09. @export variables
|
||||
|
||||
#10. remaning regular variables
|
||||
|
||||
#11. @onready variables
|
||||
|
||||
|
||||
#12. static_init
|
||||
# Called automatically when the class is loaded, after the static variables have been initialized
|
||||
static func _static_init():
|
||||
@@ -38,6 +45,8 @@ static func _static_init():
|
||||
|
||||
|
||||
#13. remaining static methods
|
||||
|
||||
|
||||
#14-1. overridden built-in virtual methods:
|
||||
# Called upon creating the object in memory.
|
||||
func _init():
|
||||
@@ -66,6 +75,11 @@ func _physics_process(float) -> void:
|
||||
|
||||
#14-2. remaining virtual methods
|
||||
|
||||
|
||||
#15. overridden custom methods
|
||||
|
||||
|
||||
#16. remaining methods
|
||||
|
||||
|
||||
#17. subclasses
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Control
|
||||
class_name FlowerVFX
|
||||
extends Control
|
||||
|
||||
@export var sounds: Array[AudioStream] = []
|
||||
|
||||
@@ -8,9 +8,9 @@ class_name FlowerVFX
|
||||
$Particles2,
|
||||
$Particles3
|
||||
]
|
||||
|
||||
@onready var audio_stream_player: AudioStreamPlayer2D = $AudioStreamPlayer2D
|
||||
|
||||
|
||||
func play() -> void:
|
||||
audio_stream_player.stream = sounds.pick_random()
|
||||
audio_stream_player.play()
|
||||
|
||||
+15
-23
@@ -1,30 +1,39 @@
|
||||
extends Control
|
||||
class_name Gardens
|
||||
extends Control
|
||||
|
||||
signal minigame_layout_opened()
|
||||
|
||||
# Namespace
|
||||
const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
|
||||
const GARDEN_SCENE: PackedScene = preload("res://resources/gardens/garden.tscn")
|
||||
const LOOK_AND_LEARN_SCENE: PackedScene = preload("res://sources/look_and_learn/look_and_learn.tscn")
|
||||
const FLOWER_FVX: PackedScene = preload("res://sources/gardens/flower_particle.tscn")
|
||||
|
||||
const GARDEN_SIZE: int = 2400
|
||||
|
||||
static var transition_data: Dictionary = {}
|
||||
|
||||
@export_category("Layout")
|
||||
@export var gardens_layout: GardensLayout:
|
||||
set = set_gardens_layout
|
||||
@export var starting_garden: int = -1
|
||||
|
||||
@export_category("Colors")
|
||||
@export var unlocked_color: Color = Color("1c2662") #blue
|
||||
@export var locked_color: Color = Color("1d2229") #black
|
||||
|
||||
@export_group("Minigames")
|
||||
@export var minigames_scenes: Array[PackedScene] = []
|
||||
@export var minigames_icons: Array[Texture] = []
|
||||
|
||||
var lessons: Dictionary = {}
|
||||
var points: Array[Array] = []
|
||||
var is_scrolling: bool = false
|
||||
var scroll_beginning_garden: int = 0
|
||||
var scroll_tween: Tween
|
||||
var is_locked: bool = false
|
||||
var in_minigame_selection: bool = false
|
||||
var current_lesson_number: int = -1
|
||||
var current_garden: Garden
|
||||
var current_button_global_position: Vector2 = Vector2.ZERO
|
||||
var current_button: LessonButton
|
||||
|
||||
@onready var garden_parent: HBoxContainer = %GardenParent
|
||||
@onready var locked_line: Line2D = $ScrollContainer/LockedLine
|
||||
@onready var unlocked_line: Line2D = $ScrollContainer/UnlockedLine
|
||||
@@ -48,26 +57,10 @@ const GARDEN_SIZE: int = 2400
|
||||
@onready var lock: Control = %Lock
|
||||
@onready var kalulu: KALULU = %Kalulu
|
||||
@onready var kalulu_button: CanvasItem = %KaluluButton
|
||||
|
||||
@onready var intro_speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path("gardens_screen", "intro"))
|
||||
@onready var help_few_plants_speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path("gardens_screen", "help_few_plants"))
|
||||
@onready var help_many_plants_speech: AudioStreamMP3 = Database.load_external_sound(Database.get_kalulu_speech_path("gardens_screen", "help_many_plants"))
|
||||
|
||||
var lessons: Dictionary = {}
|
||||
var points: Array[Array] = []
|
||||
var is_scrolling: bool = false
|
||||
var scroll_beginning_garden: int = 0
|
||||
var scroll_tween: Tween
|
||||
var is_locked: bool = false
|
||||
|
||||
var in_minigame_selection: bool = false
|
||||
var current_lesson_number: int = -1
|
||||
var current_garden: Garden
|
||||
var current_button_global_position: Vector2 = Vector2.ZERO
|
||||
var current_button: LessonButton
|
||||
|
||||
static var transition_data: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
||||
@@ -382,7 +375,6 @@ func _ready() -> void:
|
||||
await kalulu.play_kalulu_speech(intro_speech)
|
||||
kalulu_button.show()
|
||||
UserDataManager.mark_speech_as_played("gardens")
|
||||
|
||||
|
||||
|
||||
static func compute_lessons_distribution(total_lessons: int, garden_layouts: Array[GardenLayout]) -> Array[int]:
|
||||
|
||||
@@ -5,7 +5,6 @@ const GARDEN_TEXTURES_NB: int = 20
|
||||
const FLOWER_TYPES_NB: int = 5
|
||||
const GARDENS_LAYOUT_RESOURCE_PATH: String = "res://resources/gardens/gardens_layout.tres"
|
||||
|
||||
|
||||
var dragging_element: Variant
|
||||
var drag_data: Dictionary = {}
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
extends TextureRect
|
||||
class_name MinigameLayout
|
||||
extends TextureRect
|
||||
|
||||
signal pressed()
|
||||
|
||||
var is_disabled: bool = false
|
||||
|
||||
@onready var icon: TextureRect = $TextureRect
|
||||
@onready var area: Area2D = $Area2D
|
||||
@onready var right_fx: RightFX = $TextureRect/RightFX
|
||||
|
||||
var is_disabled: bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
area.connect("input_event", _on_click)
|
||||
|
||||
|
||||
func _on_click(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
|
||||
if event.is_action_pressed("left_click") and not is_disabled:
|
||||
pressed.emit()
|
||||
|
||||
|
||||
func right() -> void:
|
||||
right_fx.play()
|
||||
|
||||
+1
-2
@@ -1,12 +1,11 @@
|
||||
extends AnimatedSprite2D
|
||||
|
||||
|
||||
func _on_animation_finished() -> void:
|
||||
|
||||
if animation in ["Hide", "Show"]:
|
||||
return
|
||||
|
||||
var rand: float = randf()
|
||||
|
||||
match animation:
|
||||
"Idle1", "Idle2":
|
||||
if rand < 0.5:
|
||||
|
||||
@@ -2,10 +2,10 @@ extends Control
|
||||
|
||||
const ELEMENT_SCENE: PackedScene = preload("res://sources/language_tool/fish_word_list_element.tscn")
|
||||
|
||||
@onready var elements_container: VBoxContainer = %ElementsContainer
|
||||
|
||||
var word_list: Array = []
|
||||
|
||||
@onready var elements_container: VBoxContainer = %ElementsContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var query: String = "SELECT name FROM sqlite_master WHERE type='table' AND name='Pseudowords'"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends MarginContainer
|
||||
class_name FishWordListElement
|
||||
|
||||
extends MarginContainer
|
||||
|
||||
@export var word_id: int = 1:
|
||||
set = set_word_id
|
||||
@@ -10,13 +9,13 @@ class_name FishWordListElement
|
||||
set = set_lesson_nb
|
||||
@export var pseudoword_id: int = -1
|
||||
|
||||
var is_in_line_edit_changed: bool = false
|
||||
var word: String = ""
|
||||
|
||||
@onready var option_button: OptionButton = %OptionButton
|
||||
@onready var line_edit: LineEdit = %LineEdit
|
||||
@onready var lesson_label: Label = %LessonLabel
|
||||
|
||||
var is_in_line_edit_changed: bool = false
|
||||
var word: String = ""
|
||||
|
||||
|
||||
func set_word_id(p_word_id: int) -> void:
|
||||
word_id = p_word_id
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
extends Node
|
||||
## Global data for prof_tool only
|
||||
|
||||
# For prof_tool only
|
||||
var main_menu_selected_tab: int = 0
|
||||
|
||||
var device_colors: Array[Color] = [
|
||||
Color("9670e0"),
|
||||
Color("ffe823"),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
extends Control
|
||||
class_name GPImageAndSoundDescriptions
|
||||
|
||||
@onready var description_container: VBoxContainer = %DescriptionsContainer
|
||||
extends Control
|
||||
|
||||
const DESCRIPTION_LINE_SCENE: PackedScene = preload("res://sources/language_tool/image_and_sound_gp_description.tscn")
|
||||
|
||||
@onready var description_container: VBoxContainer = %DescriptionsContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(Database.BASE_PATH + Database.language + Database.LOOK_AND_LEARN_IMAGES)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
extends Control
|
||||
|
||||
var undo_redo: UndoRedo = UndoRedo.new()
|
||||
var element_scene: PackedScene = preload("res://sources/language_tool/gp_list_element.tscn")
|
||||
|
||||
@onready var elements_container: VBoxContainer = %ElementsContainer
|
||||
@onready var error_label: Label = %ErrorLabel
|
||||
|
||||
var undo_redo: UndoRedo = UndoRedo.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var query: String = "Select * FROM GPs ORDER BY GPs.Grapheme"
|
||||
@@ -79,7 +78,6 @@ func _on_back_button_pressed() -> void:
|
||||
get_tree().change_scene_to_file("res://sources/language_tool/prof_tool_menu.tscn")
|
||||
|
||||
|
||||
|
||||
func _on_grapheme_gui_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("left_click"):
|
||||
_reorder_by("grapheme")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends OptionButton
|
||||
class_name GPListButton
|
||||
extends OptionButton
|
||||
|
||||
signal gp_selected(id: int)
|
||||
signal new_selected()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends MarginContainer
|
||||
class_name GPListElement
|
||||
extends MarginContainer
|
||||
|
||||
signal delete_pressed()
|
||||
signal validated()
|
||||
@@ -10,18 +10,6 @@ enum Type {
|
||||
Consonant,
|
||||
}
|
||||
|
||||
@onready var exception_checkbox: CheckBox = %ExceptionCheckBox
|
||||
@onready var exception_edit_checkbox: CheckBox = %ExceptionEditCheckBox
|
||||
@onready var grapheme_label: Label = $%Grapheme
|
||||
@onready var phoneme_label: Label = $%Phoneme
|
||||
@onready var type_label: Label = $%Type
|
||||
@onready var grapheme_edit: LineEdit = $%GraphemeEdit
|
||||
@onready var phoneme_edit: LineEdit = $%PhonemeEdit
|
||||
@onready var type_edit: OptionButton = $%TypeEdit
|
||||
@onready var tab_container: TabContainer = $%TabContainer
|
||||
|
||||
|
||||
|
||||
var grapheme: String = "":
|
||||
set = set_grapheme
|
||||
var phoneme: String = "":
|
||||
@@ -37,6 +25,16 @@ var undo_redo: UndoRedo:
|
||||
undo_redo = UndoRedo.new()
|
||||
return undo_redo
|
||||
|
||||
@onready var exception_checkbox: CheckBox = %ExceptionCheckBox
|
||||
@onready var exception_edit_checkbox: CheckBox = %ExceptionEditCheckBox
|
||||
@onready var grapheme_label: Label = $%Grapheme
|
||||
@onready var phoneme_label: Label = $%Phoneme
|
||||
@onready var type_label: Label = $%Type
|
||||
@onready var grapheme_edit: LineEdit = $%GraphemeEdit
|
||||
@onready var phoneme_edit: LineEdit = $%PhonemeEdit
|
||||
@onready var type_edit: OptionButton = $%TypeEdit
|
||||
@onready var tab_container: TabContainer = $%TabContainer
|
||||
|
||||
|
||||
func set_grapheme(p_grapheme: String) -> void:
|
||||
grapheme = p_grapheme
|
||||
@@ -61,6 +59,7 @@ func set_type(p_type: Type) -> void:
|
||||
if type_edit:
|
||||
type_edit.selected = type
|
||||
|
||||
|
||||
func set_exception(p_exception: bool) -> void:
|
||||
exception = p_exception
|
||||
if exception_checkbox:
|
||||
|
||||
@@ -4,14 +4,12 @@ signal gp_selected(grapheme_ind: int, gp_id: int, text: String)
|
||||
signal focus_changed(has_focus: bool)
|
||||
signal new_gp_asked()
|
||||
|
||||
@onready var container: VBoxContainer = $VBoxContainer
|
||||
@onready var button: Button = $Button
|
||||
|
||||
|
||||
var grapheme_ind: int = -1
|
||||
var ind_selected: int = -1:
|
||||
set = set_ind_selected
|
||||
|
||||
@onready var container: VBoxContainer = $VBoxContainer
|
||||
@onready var button: Button = $Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
extends Control
|
||||
|
||||
@onready var description_container: VBoxContainer = %DescriptionsContainer
|
||||
|
||||
const DESCRIPTION_LINE_SCENE: PackedScene = preload("res://sources/language_tool/video_gp_description.tscn")
|
||||
|
||||
@onready var description_container: VBoxContainer = %DescriptionsContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var _description_line: VideoGPDescription = DESCRIPTION_LINE_SCENE.instantiate()
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
extends HBoxContainer
|
||||
class_name ImageAndSoundGPDescription
|
||||
extends HBoxContainer
|
||||
|
||||
signal delete
|
||||
signal deleted()
|
||||
|
||||
var gp: Dictionary = {}
|
||||
var get_image_path: Callable = Database.get_gp_look_and_learn_image_path
|
||||
var get_sound_path: Callable = Database.get_gp_look_and_learn_sound_path
|
||||
|
||||
@onready var gp_menu_button: MenuButton = %GPMenuButton
|
||||
@onready var image_preview: TextureRect = %ImagePreview
|
||||
@@ -13,10 +17,6 @@ signal delete
|
||||
@onready var image_clear_button: MarginContainer = %ImageClearButton
|
||||
@onready var sound_clear_button: MarginContainer = %SoundClearButton
|
||||
|
||||
var gp: Dictionary = {}
|
||||
var get_image_path: Callable = Database.get_gp_look_and_learn_image_path
|
||||
var get_sound_path: Callable = Database.get_gp_look_and_learn_sound_path
|
||||
|
||||
|
||||
func _image_file_selected(file_path: String) -> void:
|
||||
file_dialog.files_selected.connect(_image_file_selected.bind(file_dialog))
|
||||
@@ -114,7 +114,7 @@ func _on_sound_upload_button_pressed() -> void:
|
||||
|
||||
|
||||
func _on_button_pressed() -> void:
|
||||
delete.emit()
|
||||
deleted.emit()
|
||||
|
||||
|
||||
func _on_sound_preview_pressed() -> void:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends GPImageAndSoundDescriptions
|
||||
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
DirAccess.make_dir_recursive_absolute(Database.BASE_PATH + Database.language + Database.LANGUAGE_SOUNDS)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends PanelContainer
|
||||
class_name KaluluSpeech
|
||||
extends PanelContainer
|
||||
|
||||
@export var speech_category: String
|
||||
@export var speech_name: String:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends PanelContainer
|
||||
class_name KaluluTitle
|
||||
|
||||
extends PanelContainer
|
||||
|
||||
@export var title: String = "":
|
||||
set = _set_title
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends Control
|
||||
|
||||
|
||||
const TITLE_SCENE: PackedScene = preload("res://sources/language_tool/kalulu_speech_title.tscn")
|
||||
const SPEECH_SCENE: PackedScene = preload("res://sources/language_tool/kalulu_speech.tscn")
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
extends MarginContainer
|
||||
class_name LessonContainer
|
||||
|
||||
var gp_label_scene: PackedScene = preload("res://sources/language_tool/lesson_gp_label.tscn")
|
||||
extends MarginContainer
|
||||
|
||||
signal lesson_dropped(before: bool, number: int, dropped_number: int)
|
||||
|
||||
var gp_label_scene: PackedScene = preload("res://sources/language_tool/lesson_gp_label.tscn")
|
||||
var number: int = 0:
|
||||
set = set_number
|
||||
|
||||
|
||||
@onready var gp_container: HBoxContainer = $%GPContainer
|
||||
@onready var number_label: Label = $%NumberLabel
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
extends PanelContainer
|
||||
class_name LessonExerciseContainer
|
||||
extends PanelContainer
|
||||
|
||||
@export var lesson_number: int = -1:
|
||||
set = _set_lesson_number
|
||||
|
||||
var sentences_by_lesson: Dictionary = {}
|
||||
|
||||
@onready var lesson_id_label: Label = %LessonIDLabel
|
||||
@onready var lesson_gps: HBoxContainer = %LessonGPs
|
||||
@onready var exercise_buttons: Array[OptionButton] = [%ExerciseButton1, %ExerciseButton2, %ExerciseButton3]
|
||||
@@ -13,8 +15,6 @@ class_name LessonExerciseContainer
|
||||
@onready var number_of_words: Label = %NumberOfWords
|
||||
@onready var number_of_sentences: Label = %NumberOfSentences
|
||||
|
||||
var sentences_by_lesson: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Database.db.query("Select * FROM ExerciseTypes")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Label
|
||||
class_name LessonGPLabel
|
||||
extends Label
|
||||
|
||||
signal gp_dropped(before: bool, data: Dictionary)
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
extends Control
|
||||
|
||||
@onready var lessons_container: VBoxContainer = $%LessonsContainer
|
||||
@onready var unused_gp_container: GridContainer = $%UnusedGPContainer
|
||||
|
||||
var lesson_container_scene: PackedScene = preload("res://sources/language_tool/lesson_container.tscn")
|
||||
var gp_label_scene: PackedScene = preload("res://sources/language_tool/lesson_gp_label.tscn")
|
||||
|
||||
var lessons: Dictionary[int, LessonContainer] = {}
|
||||
|
||||
@onready var lessons_container: VBoxContainer = $%LessonsContainer
|
||||
@onready var unused_gp_container: GridContainer = $%UnusedGPContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Database.db.query("Select Grapheme, Phoneme, LessonNb, GPID FROM Lessons
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends MarginContainer
|
||||
class_name ListTitle
|
||||
extends MarginContainer
|
||||
|
||||
signal add_pressed()
|
||||
signal save_pressed()
|
||||
@@ -7,10 +7,11 @@ signal back_pressed()
|
||||
signal new_search(new_text: String)
|
||||
signal import_path_selected(path: String, match_to_file: bool)
|
||||
|
||||
var my_button: Button
|
||||
|
||||
@onready var title_label: Label = %TitleLabel
|
||||
@onready var file_dialog: FileDialog = $FileDialog
|
||||
|
||||
var my_button: Button
|
||||
|
||||
func _ready() -> void:
|
||||
my_button = file_dialog.add_button("Match list to file (delete elements not in file)", true, "act")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends MarginContainer
|
||||
class_name PlusButton
|
||||
extends MarginContainer
|
||||
|
||||
signal pressed()
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
extends Control
|
||||
|
||||
enum FileCheckResult {
|
||||
OK,
|
||||
ERROR_NOT_FOUND,
|
||||
ERROR_CASE_MISMATCH
|
||||
}
|
||||
|
||||
const BASE_PATH: String = "user://language_resources/"
|
||||
const SAVE_FILE_PATH: String = "user://prof_tool_save.tres"
|
||||
|
||||
var save_file: ProfToolSave
|
||||
|
||||
@onready var language_select_button: OptionButton = %LanguageSelectButton
|
||||
@@ -140,6 +147,7 @@ var integrity_checking: bool = false
|
||||
var integrity_log_path: String = "user://database-integrity-log.txt"
|
||||
var total_integrity_warnings: int = 0
|
||||
|
||||
|
||||
func _check_db_integrity() -> void:
|
||||
if integrity_checking:
|
||||
return
|
||||
@@ -258,6 +266,7 @@ func _check_db_integrity() -> void:
|
||||
var file_path: String = ProjectSettings.globalize_path(integrity_log_path)
|
||||
Logger.trace("ProfToolMenu: Logs saved at " + file_path)
|
||||
OS.shell_open(file_path)
|
||||
|
||||
#endregion
|
||||
|
||||
func log_message(message: String) -> bool:
|
||||
@@ -281,11 +290,6 @@ func log_message(message: String) -> bool:
|
||||
integrity_checking = false
|
||||
return false
|
||||
|
||||
enum FileCheckResult {
|
||||
OK,
|
||||
ERROR_NOT_FOUND,
|
||||
ERROR_CASE_MISMATCH
|
||||
}
|
||||
|
||||
func file_exists_case_sensitive(path: String) -> Dictionary:
|
||||
var result: Dictionary = {
|
||||
@@ -325,6 +329,7 @@ func file_exists_case_sensitive(path: String) -> Dictionary:
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func _get_available_languages() -> Array[String]:
|
||||
var available_languages: Array[String] = []
|
||||
var dir: DirAccess = DirAccess.open(BASE_PATH)
|
||||
@@ -523,8 +528,8 @@ func _on_open_folder_button_pressed() -> void:
|
||||
func _on_tab_container_tab_changed(tab: int) -> void:
|
||||
Globals.main_menu_selected_tab = tab
|
||||
|
||||
|
||||
#region Book Generation
|
||||
|
||||
func create_book() -> void:
|
||||
var lang_path: String = BASE_PATH.path_join(Database.language)
|
||||
var file_names: Dictionary[String, String] = {
|
||||
@@ -635,6 +640,7 @@ func create_book() -> void:
|
||||
error_label.text = "📘 Export data of the booklet finished to path: " + output_path
|
||||
Logger.trace("ProfToolMenu: " + error_label.text)
|
||||
|
||||
|
||||
# Fonction qui ajoute une ligne au dictionnaire
|
||||
func add_row(dict: Dictionary[String, PackedStringArray], row_data: Dictionary[String, String], categorie: String, all_headers: Array) -> void:
|
||||
# Nombre de lignes déjà enregistrées (doit être égal pour chaque colonne)
|
||||
@@ -688,6 +694,7 @@ func parse_csv_line(line: String) -> PackedStringArray:
|
||||
result.append(current)
|
||||
return result
|
||||
|
||||
|
||||
# Transforme une ligne pour l'écriture CSV, avec échappement
|
||||
func escape_csv_line(fields: PackedStringArray) -> String:
|
||||
var output: String = ""
|
||||
@@ -700,10 +707,12 @@ func escape_csv_line(fields: PackedStringArray) -> String:
|
||||
output += ","
|
||||
return output
|
||||
|
||||
|
||||
# Normalise les noms de colonnes (ex: writing page -> Writing page)
|
||||
func normalize_header(header_name: String) -> String:
|
||||
return header_name.strip_edges()[0].to_upper() + header_name.strip_edges().substr(1, -1).to_lower()
|
||||
|
||||
|
||||
# Lit une "ligne logique" complète d’un CSV (même si elle est sur plusieurs lignes à cause des guillemets)
|
||||
func read_csv_record(file: FileAccess) -> String:
|
||||
var record: String = ""
|
||||
@@ -727,5 +736,4 @@ func read_csv_record(file: FileAccess) -> String:
|
||||
|
||||
return record
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
extends HBoxContainer
|
||||
class_name SegmentBuild
|
||||
extends HBoxContainer
|
||||
|
||||
signal modify()
|
||||
signal delete()
|
||||
signal modified()
|
||||
signal deleted()
|
||||
|
||||
var points: Array[Vector2] = []
|
||||
|
||||
@onready var number_of_points_labels: Label = %NumberOfPointsLabel
|
||||
@onready var color_rect: ColorRect = %ColorRect
|
||||
|
||||
var points: Array[Vector2] = []
|
||||
|
||||
|
||||
func add_point(point: Vector2) -> void:
|
||||
points.append(point)
|
||||
@@ -35,9 +35,9 @@ func set_color(color: Color) -> void:
|
||||
|
||||
|
||||
func _on_modify_button_pressed() -> void:
|
||||
modify.emit()
|
||||
modified.emit()
|
||||
|
||||
|
||||
func _on_delete_button_pressed() -> void:
|
||||
delete.emit()
|
||||
deleted.emit()
|
||||
queue_free()
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
extends HBoxContainer
|
||||
class_name SegmentContainer
|
||||
extends HBoxContainer
|
||||
|
||||
signal changed()
|
||||
|
||||
const SEGMENT_BUILD_SCENE: PackedScene = preload("res://sources/language_tool/segment_build.tscn")
|
||||
const POINT_BUTTON_SCENE: PackedScene = preload("res://sources/language_tool/segment_point_button.tscn")
|
||||
|
||||
@export var points_per_lines: int = 25
|
||||
@export var points_per_gradient: int = 7
|
||||
|
||||
var gradient: Gradient
|
||||
var current_segment: SegmentBuild
|
||||
var current_button: SegmentPointButton
|
||||
var buttons: Array[SegmentPointButton] = []
|
||||
|
||||
@onready var grapheme_label: Label = %GraphemeLabel
|
||||
@onready var buttons_parent: Control = %ButtonsParent
|
||||
@onready var segments_container: VBoxContainer = %SegmentsContainer
|
||||
@onready var lines: Node2D = %Lines
|
||||
|
||||
const SEGMENT_BUILD_SCENE: PackedScene = preload("res://sources/language_tool/segment_build.tscn")
|
||||
const POINT_BUTTON_SCENE: PackedScene = preload("res://sources/language_tool/segment_point_button.tscn")
|
||||
|
||||
var gradient: Gradient
|
||||
|
||||
var current_segment: SegmentBuild
|
||||
var current_button: SegmentPointButton
|
||||
var buttons: Array[SegmentPointButton] = []
|
||||
|
||||
|
||||
func reset() -> void:
|
||||
var to_free: Array[Node] = lines.get_children()
|
||||
@@ -126,8 +125,8 @@ func _on_add_segment_button_pressed() -> void:
|
||||
segments_container.add_child(new_segment)
|
||||
|
||||
# Connect signals for modifying and deleting the segment
|
||||
new_segment.modify.connect(_on_segment_modify.bind(new_segment))
|
||||
new_segment.delete.connect(_on_segment_delete.bind(new_segment))
|
||||
new_segment.modified.connect(_on_segment_modified.bind(new_segment))
|
||||
new_segment.deleted.connect(_on_segment_deleted.bind(new_segment))
|
||||
|
||||
# Store the new segment as the currently selected one
|
||||
current_segment = new_segment
|
||||
@@ -156,15 +155,13 @@ func _on_add_segment_button_pressed() -> void:
|
||||
changed.emit()
|
||||
|
||||
|
||||
func _on_segment_modify(segment: SegmentBuild) -> void:
|
||||
func _on_segment_modified(segment: SegmentBuild) -> void:
|
||||
current_segment = segment
|
||||
|
||||
match_segment_with_buttons()
|
||||
|
||||
changed.emit()
|
||||
|
||||
|
||||
func _on_segment_delete(segment: SegmentBuild) -> void:
|
||||
func _on_segment_deleted(segment: SegmentBuild) -> void:
|
||||
if segment == current_segment:
|
||||
var possible_segments: Array[Node] = segments_container.get_children()
|
||||
if not possible_segments.is_empty():
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Control
|
||||
class_name SegmentPointButton
|
||||
extends Control
|
||||
|
||||
signal point_down()
|
||||
signal point_up()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
extends WordList
|
||||
|
||||
var not_found_list: String = ""
|
||||
|
||||
@onready var export_not_found_button: Button = %ExportNotFoundButton
|
||||
@onready var file_dialog_export: FileDialog = $FileDialogExport
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends WordListElement
|
||||
class_name SentenceListElement
|
||||
extends WordListElement
|
||||
|
||||
signal not_found(text: String)
|
||||
|
||||
@@ -13,8 +13,6 @@ func _ready() -> void:
|
||||
graphemes_label.hide()
|
||||
|
||||
|
||||
|
||||
|
||||
func update_lesson() -> void:
|
||||
var highest_min_lesson: int = -1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
extends "res://sources/language_tool/word_list.gd"
|
||||
extends WordList
|
||||
|
||||
|
||||
func _on_list_title_import_path_selected(path: String, match_to_file: bool) -> void:
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
extends Control
|
||||
|
||||
const EXTENSION: String = ".csv"
|
||||
|
||||
@export var gradient: Gradient
|
||||
|
||||
var letters: Array[String] = []
|
||||
var current_letter: int = -1
|
||||
|
||||
@onready var lower_container: SegmentContainer = %Lower
|
||||
@onready var upper_container: SegmentContainer = %Upper
|
||||
@onready var letter_picker: OptionButton = %LetterPicker
|
||||
@onready var save_ok: TextureRect = %SaveOk
|
||||
@onready var copy_from: MenuButton = %CopyFrom
|
||||
|
||||
const EXTENSION: String = ".csv"
|
||||
|
||||
var letters: Array[String] = []
|
||||
var current_letter: int = -1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
copy_from.get_popup().id_pressed.connect(_on_copy_from_id_pressed)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
extends HBoxContainer
|
||||
class_name VideoGPDescription
|
||||
extends HBoxContainer
|
||||
|
||||
@warning_ignore("unused_signal")
|
||||
signal delete
|
||||
signal deleted()
|
||||
|
||||
var gp: Dictionary = {}
|
||||
|
||||
@onready var gp_menu_button: MenuButton = %GPMenuButton
|
||||
@onready var video_player: VideoStreamPlayer = %VideoStreamPlayer
|
||||
@onready var video_upload_button: PlusButton = %VideoUploadButton
|
||||
@onready var file_dialog: FileDialog = $FileDialog
|
||||
|
||||
var gp: Dictionary = {}
|
||||
|
||||
|
||||
func _video_file_selected(file_path: String) -> void:
|
||||
file_dialog.files_selected.connect(_video_file_selected.bind(file_dialog))
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
extends Control
|
||||
class_name WordList
|
||||
extends Control
|
||||
|
||||
@export var element_scene: PackedScene = preload("res://sources/language_tool/word_list_element.tscn")
|
||||
|
||||
var undo_redo: UndoRedo = UndoRedo.new()
|
||||
var in_new_gp_mode: bool = false:
|
||||
set = set_in_new_gp_mode
|
||||
var _element: WordListElement
|
||||
var sub_elements_list: Dictionary = {}
|
||||
var new_gp_asked_element: WordListElement
|
||||
var new_gp_asked_ind: int
|
||||
|
||||
@onready var elements_container: VBoxContainer = %ElementsContainer
|
||||
@onready var new_gp_layer: CanvasLayer = $NewGPLayer
|
||||
@onready var new_gp: Variant = %NewGP
|
||||
@@ -12,14 +20,6 @@ class_name WordList
|
||||
@onready var graphemes_title: Label = %Graphemes
|
||||
@onready var error_label: Label = %ErrorLabel
|
||||
|
||||
var undo_redo: UndoRedo = UndoRedo.new()
|
||||
var in_new_gp_mode: bool = false:
|
||||
set = set_in_new_gp_mode
|
||||
var _element: WordListElement
|
||||
var sub_elements_list: Dictionary = {}
|
||||
var new_gp_asked_element: WordListElement
|
||||
var new_gp_asked_ind: int
|
||||
|
||||
|
||||
func create_sub_elements_list() -> void:
|
||||
sub_elements_list.clear()
|
||||
@@ -190,7 +190,6 @@ func _on_save_button_pressed() -> void:
|
||||
undo_redo.clear_history()
|
||||
|
||||
|
||||
|
||||
func _on_back_button_pressed() -> void:
|
||||
get_tree().change_scene_to_file("res://sources/language_tool/prof_tool_menu.tscn")
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends MarginContainer
|
||||
class_name WordListElement
|
||||
extends MarginContainer
|
||||
|
||||
signal delete_pressed()
|
||||
signal new_gp_asked(i: int)
|
||||
@@ -17,21 +17,6 @@ const PLUS_BUTTON_SCENE: PackedScene = preload("res://sources/language_tool/plus
|
||||
@export var relational_table: String = "GPsInWords"
|
||||
@export var sub_table_id: String = "GPID"
|
||||
|
||||
@onready var word_label: Label = %Word
|
||||
@onready var graphemes_label: Label = %Graphemes
|
||||
@onready var word_edit: LineEdit = %WordEdit
|
||||
@onready var tab_container: TabContainer = $TabContainer
|
||||
@onready var lesson_label: Label = %Lesson
|
||||
@onready var exception_checkbox: CheckBox = %ExceptionCheckBox
|
||||
@onready var exception_edit_checkbox: CheckBox = %ExceptionEditCheckBox
|
||||
@onready var reading_checkbox: CheckBox = %ReadingCheckBox
|
||||
@onready var writing_checkbox: CheckBox = %WritingCheckBox
|
||||
@onready var reading_edit_checkbox: CheckBox = %ReadingEditCheckBox
|
||||
@onready var writing_edit_checkbox: CheckBox = %WritingEditCheckBox
|
||||
@onready var graphemes_edit_container: HBoxContainer = %GraphemesEditContainer
|
||||
@onready var add_gp_button: MarginContainer = %AddGPButton
|
||||
@onready var remove_gp_button: MarginContainer = %RemoveGPButton2
|
||||
|
||||
var word: String = "":
|
||||
set = set_word
|
||||
var lesson: int = 0:
|
||||
@@ -53,6 +38,21 @@ var writing: int = 0:
|
||||
set = set_writing
|
||||
var sub_elements_list: Dictionary = {}
|
||||
|
||||
@onready var word_label: Label = %Word
|
||||
@onready var graphemes_label: Label = %Graphemes
|
||||
@onready var word_edit: LineEdit = %WordEdit
|
||||
@onready var tab_container: TabContainer = $TabContainer
|
||||
@onready var lesson_label: Label = %Lesson
|
||||
@onready var exception_checkbox: CheckBox = %ExceptionCheckBox
|
||||
@onready var exception_edit_checkbox: CheckBox = %ExceptionEditCheckBox
|
||||
@onready var reading_checkbox: CheckBox = %ReadingCheckBox
|
||||
@onready var writing_checkbox: CheckBox = %WritingCheckBox
|
||||
@onready var reading_edit_checkbox: CheckBox = %ReadingEditCheckBox
|
||||
@onready var writing_edit_checkbox: CheckBox = %WritingEditCheckBox
|
||||
@onready var graphemes_edit_container: HBoxContainer = %GraphemesEditContainer
|
||||
@onready var add_gp_button: MarginContainer = %AddGPButton
|
||||
@onready var remove_gp_button: MarginContainer = %RemoveGPButton2
|
||||
|
||||
|
||||
func set_exception(p_exception: bool) -> void:
|
||||
exception = p_exception
|
||||
@@ -61,6 +61,7 @@ func set_exception(p_exception: bool) -> void:
|
||||
if exception_edit_checkbox:
|
||||
exception_edit_checkbox.button_pressed = bool(exception)
|
||||
|
||||
|
||||
func set_reading(p_reading: bool) -> void:
|
||||
reading = p_reading
|
||||
if reading_checkbox:
|
||||
@@ -68,6 +69,7 @@ func set_reading(p_reading: bool) -> void:
|
||||
if reading_edit_checkbox:
|
||||
reading_edit_checkbox.button_pressed = bool(reading)
|
||||
|
||||
|
||||
func set_writing(p_writing: bool) -> void:
|
||||
writing = p_writing
|
||||
if writing_checkbox:
|
||||
@@ -367,7 +369,6 @@ func _on_add_gp_button_pressed(element: Node) -> void:
|
||||
add_gp_list_button(gp_id, ind_gp_id + 1)
|
||||
|
||||
|
||||
|
||||
func _on_empty_add_gp_button_pressed() -> void:
|
||||
var gp_id: int = sub_elements_list.keys()[0]
|
||||
unvalidated_gp_ids.append(gp_id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends TextureButton
|
||||
class_name LessonButton
|
||||
extends TextureButton
|
||||
|
||||
@export_color_no_alpha var base_color: Color:
|
||||
set = _set_base_color
|
||||
@@ -15,11 +15,12 @@ class_name LessonButton
|
||||
@onready var placeholder: TextureRect = %Placeholder
|
||||
@onready var right_fx: RightFX = %RightFX
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_set_base_color(base_color)
|
||||
_set_completed_color(completed_color)
|
||||
_set_text(text)
|
||||
|
||||
|
||||
|
||||
func show_placeholder(is_shown: bool) -> void:
|
||||
placeholder.visible = is_shown
|
||||
@@ -30,6 +31,7 @@ func right() -> void:
|
||||
right_fx.play()
|
||||
await right_fx.finished
|
||||
|
||||
|
||||
func _set_base_color(color: Color) -> void:
|
||||
base_color = color
|
||||
if center and not completed:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Node2D
|
||||
class_name LetterSegment
|
||||
extends Node2D
|
||||
|
||||
signal finished()
|
||||
|
||||
@@ -25,6 +25,7 @@ func start() -> void:
|
||||
func stop() -> void:
|
||||
finished.emit()
|
||||
|
||||
|
||||
func demo() -> void:
|
||||
tracing_path.demo()
|
||||
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
extends Control
|
||||
class_name LookAndLearn
|
||||
extends Control
|
||||
|
||||
static var transition_data: Dictionary = {}
|
||||
|
||||
@export var lesson_nb: int = 1
|
||||
@export var current_button_pressed: int = 0
|
||||
|
||||
var gp_list: Array[Dictionary] = []
|
||||
var current_video: int = 0
|
||||
var videos: Array[VideoStream] = []
|
||||
var current_image_and_sound: int = 0
|
||||
var images: Array[Texture] = []
|
||||
var sounds: Array[AudioStream] = []
|
||||
var gardens_data: Dictionary = {}
|
||||
var current_tracing: int = 0
|
||||
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
@onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||
@onready var video_player: VideoStreamPlayer = %VideoStreamPlayer
|
||||
@@ -13,21 +24,6 @@ class_name LookAndLearn
|
||||
@onready var grapheme_particles: GPUParticles2D = $GraphemeParticles
|
||||
|
||||
|
||||
var gp_list: Array[Dictionary] = []
|
||||
var current_video: int = 0
|
||||
var videos: Array[VideoStream] = []
|
||||
|
||||
var current_image_and_sound: int = 0
|
||||
var images: Array[Texture] = []
|
||||
var sounds: Array[AudioStream] = []
|
||||
|
||||
static var transition_data: Dictionary = {}
|
||||
var gardens_data: Dictionary = {}
|
||||
|
||||
|
||||
var current_tracing: int = 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
MusicManager.stop()
|
||||
|
||||
@@ -157,7 +153,6 @@ func _on_tracing_manager_finished() -> void:
|
||||
_back_to_gardens()
|
||||
|
||||
|
||||
|
||||
func _back_to_gardens() -> void:
|
||||
await OpeningCurtain.close()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Node2D
|
||||
class_name TracingEffects
|
||||
extends Node2D
|
||||
|
||||
@onready var particles_effect: GPUParticles2D = $TracingParticles
|
||||
@onready var sound_effect: AudioStreamPlayer = $TracingAudioStreamPlayer
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
extends Control
|
||||
class_name TracingManager
|
||||
extends Control
|
||||
|
||||
signal finished()
|
||||
|
||||
const LETTER_SEGMENT_CLASS: PackedScene = preload("res://sources/look_and_learn/letter_segment.tscn")
|
||||
const EXTENSION: String = ".csv"
|
||||
|
||||
@export var label_settings: LabelSettings
|
||||
|
||||
@onready var lower_labels: HBoxContainer = %LowerLabels
|
||||
@onready var upper_labels: HBoxContainer = %UpperLabels
|
||||
|
||||
const EXTENSION: String = ".csv"
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
place_segments(upper_labels.get_children())
|
||||
@@ -34,8 +34,6 @@ func reset() -> void:
|
||||
await get_tree().process_frame
|
||||
|
||||
|
||||
# --- Setup ---
|
||||
|
||||
func setup(grapheme: String) -> void:
|
||||
await reset()
|
||||
for letter: String in grapheme:
|
||||
@@ -118,10 +116,6 @@ func _real_path(path: String) -> String:
|
||||
return Database.BASE_PATH.path_join(Database.language).path_join(Database.TRACING_DATA_FOLDER).path_join(path) + EXTENSION
|
||||
|
||||
|
||||
|
||||
# --- Start ---
|
||||
|
||||
|
||||
func start() -> void:
|
||||
if upper_labels.get_child_count(false) > 0:
|
||||
lower_labels.visible = false
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
extends Path2D
|
||||
class_name TracingPath
|
||||
extends Path2D
|
||||
|
||||
signal finished
|
||||
signal demo_finished
|
||||
signal finished()
|
||||
signal demo_finished()
|
||||
|
||||
@export var points_per_curve: int = 25
|
||||
@export var hand_min_travel_time: float = 0.5
|
||||
@export var hand_max_travel_time: float = 2.0
|
||||
@export var distance: float = 100.0
|
||||
|
||||
@export var color_gradient: Gradient
|
||||
|
||||
var curve_points: PackedVector2Array
|
||||
var is_playing: bool = false
|
||||
var is_in_demo: bool = false
|
||||
var touch_positions: Array[Vector2] = []
|
||||
var should_play_effects: bool = false
|
||||
|
||||
@onready var line: Line2D = $Line2D
|
||||
@onready var guide: PathFollow2D = $GuidePathFollow
|
||||
@onready var hand: PathFollow2D = $HandPathFollow2D
|
||||
@onready var guide_sprite: Sprite2D = $GuidePathFollow/Guide
|
||||
@onready var hand_sprite: Sprite2D = $HandPathFollow2D/Hand
|
||||
|
||||
var curve_points: PackedVector2Array
|
||||
@onready var remaining_curve: Curve2D = Curve2D.new()
|
||||
|
||||
var is_playing: bool = false
|
||||
var is_in_demo: bool = false
|
||||
var touch_positions: Array[Vector2] = []
|
||||
var should_play_effects: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
guide_sprite.visible = false
|
||||
|
||||
@@ -11,10 +11,11 @@ const SYMBOLS_NAMES: Dictionary[String, String] = {
|
||||
"6": "TRIANGLE",
|
||||
}
|
||||
|
||||
var password: String = ""
|
||||
|
||||
@onready var code_keyboard: CodeKeyboard = %CodeKeyboard
|
||||
@onready var password_label: Label = %PasswordLabel
|
||||
|
||||
var password: String = ""
|
||||
|
||||
func _ready() -> void:
|
||||
_reset_password()
|
||||
|
||||
@@ -10,7 +10,6 @@ const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
@onready var kalulu: KALULU = $Kalulu
|
||||
@onready var kalulu_button: TextureButton = %KaluluButton
|
||||
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||
|
||||
@onready var garden_buttons: Array[TextureButton] = [
|
||||
%GardenButton1,
|
||||
%GardenButton2,
|
||||
@@ -33,7 +32,6 @@ const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
%GardenButton19,
|
||||
%GardenButton20,
|
||||
]
|
||||
|
||||
@onready var particles: Array[GPUParticles2D] = [
|
||||
%Particles1,
|
||||
%Particles2,
|
||||
@@ -56,18 +54,18 @@ const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
%Particles19,
|
||||
%Particles20,
|
||||
]
|
||||
|
||||
@onready var tutorial_speeches: Array[AudioStream]= [
|
||||
Database.load_external_sound(Database.get_kalulu_speech_path("brain_screen", "intro_1")),
|
||||
Database.load_external_sound(Database.get_kalulu_speech_path("brain_screen", "intro_2")),
|
||||
Database.load_external_sound(Database.get_kalulu_speech_path("brain_screen", "intro_3"))
|
||||
]
|
||||
|
||||
@onready var help_speech: AudioStream = Database.load_external_sound(Database.get_kalulu_speech_path("brain_screen", "help"))
|
||||
|
||||
|
||||
static func _compute_lessons_distribution(total_lessons: int, garden_layouts: Array[GardenLayout]) -> Array[int]:
|
||||
return Gardens.compute_lessons_distribution(total_lessons, garden_layouts)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
UserDataManager.start_synchronization_timer()
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
extends Control
|
||||
class_name CodeKeyboard
|
||||
extends Control
|
||||
|
||||
signal button_pressed(key: String, password: Array[String])
|
||||
signal password_entered(password: String)
|
||||
|
||||
var password: Array[String] = []
|
||||
|
||||
@onready var password_visualizer: PasswordVisualizer = %PasswordVisualizer
|
||||
@onready var buttons: GridContainer = %Buttons
|
||||
@onready var sound_player: AudioStreamPlayer = $ButtonSoundPlayer
|
||||
|
||||
var password: Array[String] = []
|
||||
|
||||
func _ready() -> void:
|
||||
for button: TextureButton in buttons.get_children(false):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
class_name PasswordVisualizer
|
||||
extends HBoxContainer
|
||||
|
||||
const ICONS_TEXTURES: Dictionary[String, CompressedTexture2D] = {
|
||||
"1": preload("res://assets/menus/login/symbol_01.png"),
|
||||
@@ -24,12 +24,14 @@ const ICONS_TEXTURES: Dictionary[String, CompressedTexture2D] = {
|
||||
|
||||
@onready var icons: Array[TextureRect] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_draw_password()
|
||||
for icon: TextureRect in icons:
|
||||
icon.custom_minimum_size.x = key_size
|
||||
icon.custom_minimum_size.y = key_size
|
||||
|
||||
|
||||
func _draw_password() -> void:
|
||||
|
||||
if not icons:
|
||||
|
||||
@@ -5,9 +5,11 @@ const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
|
||||
|
||||
@onready var container: GridContainer = %GridContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
if not UserDataManager.teacher_settings:
|
||||
return
|
||||
@@ -23,6 +25,7 @@ func _refresh() -> void:
|
||||
button.pressed.connect(_device_button_pressed.bind(device))
|
||||
OpeningCurtain.open()
|
||||
|
||||
|
||||
func _device_button_pressed(device: int) -> void:
|
||||
Logger.trace("DeviceSelection: User selected device %d" % device)
|
||||
if UserDataManager.set_device_id(device):
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
extends Control
|
||||
class_name PackageDownloader
|
||||
extends Control
|
||||
|
||||
const MAIN_MENU_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
||||
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
|
||||
const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
|
||||
const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources"
|
||||
|
||||
const ERROR_MESSAGES: Array[String] = [
|
||||
"DISCONNECTED_ERROR",
|
||||
"NO_INTERNET_ACCESS",
|
||||
@@ -13,6 +12,12 @@ const ERROR_MESSAGES: Array[String] = [
|
||||
"INVALID_LANGUAGE_DIRECTORY",
|
||||
]
|
||||
|
||||
var device_language: String
|
||||
var current_language_path: String
|
||||
var mutex: Mutex
|
||||
var thread: Thread
|
||||
var server_language_version: Dictionary = {}
|
||||
var current_language_version: Dictionary = {}
|
||||
|
||||
@onready var http_request: HTTPRequest = $HTTPRequest
|
||||
@onready var checking_label: Label = %CheckingLabel
|
||||
@@ -25,13 +30,6 @@ const ERROR_MESSAGES: Array[String] = [
|
||||
@onready var error_label: Label = %ErrorLabel
|
||||
@onready var error_popup: ConfirmPopup = $ErrorPopup
|
||||
|
||||
var device_language: String
|
||||
var current_language_path: String
|
||||
var mutex: Mutex
|
||||
var thread: Thread
|
||||
|
||||
var server_language_version: Dictionary = {}
|
||||
var current_language_version: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
|
||||
@@ -5,9 +5,12 @@ const BACK_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
||||
const NEXT_SCENE_PATH: String = "res://sources/menus/brain/brain.tscn"
|
||||
const TEACHER_SCENE_PATH: String = "res://sources/menus/settings/teacher_settings.tscn"
|
||||
const PACKAGE_LOADER_SCENE_PATH: String = "res://sources/menus/language_selection/package_downloader.tscn"
|
||||
|
||||
const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
|
||||
var help_speech: AudioStream
|
||||
var wrong_password_speech: AudioStream
|
||||
var right_password_speech: AudioStream
|
||||
|
||||
@onready var kalulu: KALULU = $Kalulu
|
||||
@onready var music_player: AudioStreamPlayer = $MusicStreamPlayer
|
||||
@onready var device_number_label: Label = $DeviceNumber
|
||||
@@ -16,9 +19,6 @@ const KALULU := preload("res://sources/minigames/base/kalulu.gd")
|
||||
@onready var teacher_help_label: Label = %TeacherHelpLabel
|
||||
@onready var kalulu_button: CanvasItem = %KaluluButton
|
||||
|
||||
var help_speech: AudioStream
|
||||
var wrong_password_speech: AudioStream
|
||||
var right_password_speech: AudioStream
|
||||
|
||||
func _ready() -> void:
|
||||
UserDataManager.stop_synchronization_timer()
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
@tool
|
||||
extends TextureButton
|
||||
class_name DeviceButton
|
||||
|
||||
@onready var background: TextureRect = $Background
|
||||
@onready var label: Label = $Label
|
||||
|
||||
extends TextureButton
|
||||
|
||||
@export_range(1, 99) var number: int:
|
||||
set(value):
|
||||
number = value
|
||||
if label:
|
||||
label.text = str(value)
|
||||
|
||||
@export var background_color: Color:
|
||||
set(value):
|
||||
background_color = value
|
||||
if background:
|
||||
background.self_modulate = background_color
|
||||
|
||||
@onready var background: TextureRect = $Background
|
||||
@onready var label: Label = $Label
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
label.text = str(number)
|
||||
background.self_modulate = background_color
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
extends Control
|
||||
|
||||
@onready var speech_player: AudioStreamPlayer = $SpeechPlayer
|
||||
@onready var sprite: AnimatedSprite2D = $Sprite
|
||||
|
||||
var tuto_speech: AudioStreamMP3
|
||||
var feedback_speech: AudioStreamMP3
|
||||
|
||||
var feedback_speech: AudioStreamMP3
|
||||
var is_speaking: bool = false:
|
||||
set(value):
|
||||
is_speaking = value
|
||||
@@ -16,6 +12,9 @@ var is_speaking: bool = false:
|
||||
sprite.play("Tc_Idle1")
|
||||
var elapsed_time: float = 0.0
|
||||
|
||||
@onready var speech_player: AudioStreamPlayer = $SpeechPlayer
|
||||
@onready var sprite: AnimatedSprite2D = $Sprite
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
tuto_speech = Database.load_external_sound(Database.get_kalulu_speech_path("title_screen", "tuto_welcome_oneshot"))
|
||||
@@ -26,27 +25,23 @@ func _ready() -> void:
|
||||
func _process(delta: float) -> void:
|
||||
if not visible:
|
||||
return
|
||||
|
||||
if not sprite.is_playing():
|
||||
sprite.play("Tc_Idle1")
|
||||
|
||||
if not is_speaking:
|
||||
elapsed_time += delta
|
||||
if elapsed_time > 20:
|
||||
start_speech()
|
||||
|
||||
|
||||
func _play_speech(speech: AudioStream) -> void:
|
||||
if not speech:
|
||||
Logger.warn("Kalulu: Speech not found")
|
||||
is_speaking = false
|
||||
return
|
||||
|
||||
is_speaking = true
|
||||
|
||||
speech_player.stream = speech
|
||||
speech_player.play()
|
||||
await speech_player.finished
|
||||
|
||||
is_speaking = false
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ extends OptionButton
|
||||
|
||||
var items: Array[String] = []
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Adds the supported locales to the field
|
||||
var idx: int = 0
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
extends Control
|
||||
|
||||
signal logged_in
|
||||
signal logged_in()
|
||||
|
||||
@onready var validator: FormValidator = %LoginFormValidator
|
||||
@onready var email_field: LineEdit = %EmailField
|
||||
@onready var password_field: LineEdit = %PasswordField
|
||||
@onready var login_message: Label = %LoginError
|
||||
|
||||
@onready var device_id_container: VBoxContainer = %DeviceIDContainer
|
||||
@onready var device_id_field: SpinBox = %DeviceIDField
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
extends Control
|
||||
|
||||
const KALULU := preload("res://sources/menus/main/kalulu.gd")
|
||||
|
||||
const ADULT_CHECK_SCENE_PATH: String = "res://sources/menus/adult_check/adult_check.tscn"
|
||||
const PACKAGE_LOADER_SCENE_PATH: String = "res://sources/menus/language_selection/package_downloader.tscn"
|
||||
|
||||
@onready var version_label: Label = $Informations/BuildVersionValue
|
||||
@onready var teacher_label: Label = $Informations/TeacherValue
|
||||
@onready var device_id_label: Label = $Informations/DeviceIDValue
|
||||
|
||||
@onready var kalulu: KALULU = $Kalulu
|
||||
@onready var play_button: Button = %PlayButton
|
||||
@onready var interface_left: MarginContainer = %InterfaceLeft
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
extends ProgressBar
|
||||
class_name RegisterProgressBar
|
||||
extends ProgressBar
|
||||
|
||||
|
||||
func set_value_with_tween(new_value: float) -> void:
|
||||
create_tween().tween_property(self, "value", new_value, 0.15).set_trans(Tween.TRANS_LINEAR)
|
||||
|
||||
@@ -3,6 +3,8 @@ extends Control
|
||||
const MAIN_MENU_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
||||
const NEXT_SCENE_PATH: String = "res://sources/menus/language_selection/package_downloader.tscn"
|
||||
|
||||
var current_steps: Array[Step] = []
|
||||
|
||||
@onready var teacher_steps: Array[PackedScene] = [
|
||||
preload("res://sources/menus/register/steps/teacher/method_step.tscn"),
|
||||
preload("res://sources/menus/register/steps/teacher/devices_count_step.tscn")
|
||||
@@ -18,15 +20,13 @@ const NEXT_SCENE_PATH: String = "res://sources/menus/language_selection/package_
|
||||
@onready var account_type_step: PackedScene = preload("res://sources/menus/register/steps/account_type_step.tscn")
|
||||
@onready var students_step: PackedScene = preload("res://sources/menus/register/steps/teacher/students_count_step.tscn")
|
||||
@onready var player_step: PackedScene = preload("res://sources/menus/register/steps/parent/player_step.tscn")
|
||||
|
||||
var current_steps: Array[Step] = []
|
||||
|
||||
@onready var register_data: TeacherSettings = TeacherSettings.new()
|
||||
@onready var progress_bar: RegisterProgressBar = %ProgressBar
|
||||
@onready var steps: Control = %Steps
|
||||
@onready var popup: TextureRect = %Popup
|
||||
@onready var popup_info_label: Label = %PopupInfo
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
current_steps = [account_type_step.instantiate()]
|
||||
_go_to_step(int(progress_bar.value))
|
||||
@@ -129,5 +129,6 @@ func _remove_future_steps() -> void:
|
||||
# Resize the array to remove unwanted steps
|
||||
current_steps.resize(int(progress_bar.value + 1))
|
||||
|
||||
|
||||
func _on_popup_button_pressed() -> void:
|
||||
popup.hide()
|
||||
|
||||
@@ -3,6 +3,7 @@ extends Step
|
||||
|
||||
@onready var type: ItemList = %TypeSelect
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
type.clear()
|
||||
type.add_item(tr("TEACHER"))
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
@tool
|
||||
extends Control
|
||||
class_name Step
|
||||
extends Control
|
||||
|
||||
signal back(step: Step)
|
||||
signal next(step: Step)
|
||||
|
||||
@export var step_name: String
|
||||
@export_multiline var question: String
|
||||
@export_multiline var infos: String
|
||||
@export var data: Resource
|
||||
|
||||
@onready var question_label: Label = %QuestionLabel
|
||||
@onready var info_label: Label = %InfoLabel
|
||||
@onready var form_validator: FormValidator = %FormValidator
|
||||
@onready var form_binder: FormBinder = %FormBinder
|
||||
@onready var form_container: Control = %FormContainer
|
||||
|
||||
@export var step_name: String
|
||||
@export_multiline var question: String
|
||||
@export_multiline var infos: String
|
||||
@export var data: Resource
|
||||
|
||||
func on_enter() -> void:
|
||||
form_binder.read(data)
|
||||
@@ -25,6 +26,7 @@ func on_enter() -> void:
|
||||
else:
|
||||
info_label.visible = false
|
||||
|
||||
|
||||
func _on_back() -> bool:
|
||||
return true
|
||||
|
||||
@@ -32,6 +34,7 @@ func _on_back() -> bool:
|
||||
func _on_next() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# Display error messages
|
||||
func _on_form_validator_control_validated(control: Control, passed: bool, messages: PackedStringArray) -> void:
|
||||
var label: Label = find_child(control.name as String + "Error", true, false) as Label
|
||||
|
||||
@@ -3,6 +3,7 @@ extends Step
|
||||
|
||||
@onready var api_email_field_error: Label = %APIEmailFieldError
|
||||
|
||||
|
||||
func _on_validate_button_pressed() -> void:
|
||||
api_email_field_error.visible = false
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Control
|
||||
class_name DeviceRecap
|
||||
extends Control
|
||||
|
||||
const STUDENT_PANEL_SCENE: PackedScene = preload("res://sources/menus/settings/student_panel.tscn")
|
||||
|
||||
@@ -9,6 +9,7 @@ const STUDENT_PANEL_SCENE: PackedScene = preload("res://sources/menus/settings/s
|
||||
@onready var title_label: Label = %Title
|
||||
@onready var students_container: GridContainer = %StudentsContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
||||
if title:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends Step
|
||||
class_name RecapStep
|
||||
extends Step
|
||||
|
||||
const DEVICE_RECAP_SCENE: PackedScene = preload("res://sources/menus/register/steps/device_recap.tscn")
|
||||
|
||||
@@ -11,6 +11,7 @@ const DEVICE_RECAP_SCENE: PackedScene = preload("res://sources/menus/register/st
|
||||
@onready var devices_count: Label = %DevicesCount
|
||||
@onready var students_count: Label = %StudentsCount
|
||||
|
||||
|
||||
func on_enter() -> void:
|
||||
super.on_enter()
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
@tool
|
||||
extends Step
|
||||
class_name StudentsCountStep
|
||||
extends Step
|
||||
|
||||
@export var device_id: int
|
||||
|
||||
@onready var students_count_field: SpinBox = %StudentsCountField
|
||||
|
||||
|
||||
func _on_back() -> bool:
|
||||
var register_data: TeacherSettings = data as TeacherSettings
|
||||
if register_data:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
@tool
|
||||
extends ValidatorRule
|
||||
class_name ConfirmRule
|
||||
extends ValidatorRule
|
||||
|
||||
@export_node_path("Control") var confirm_control_path: NodePath
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
fail_message = "Value must be the same in both fields."
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@tool
|
||||
extends Validator
|
||||
class_name ItemListValidator
|
||||
extends Validator
|
||||
|
||||
|
||||
func get_value(control: Control) -> Variant:
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
extends TabBar
|
||||
class_name DeviceTab
|
||||
extends TabBar
|
||||
|
||||
signal student_pressed(code: int)
|
||||
|
||||
const STUDENT_PANEL_SCENE: PackedScene = preload("res://sources/menus/settings/student_panel.tscn")
|
||||
|
||||
@onready var students_container: GridContainer = %StudentsContainer
|
||||
|
||||
@export var device_id: int
|
||||
@export var students: Array[StudentData] = []
|
||||
|
||||
@onready var students_container: GridContainer = %StudentsContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
refresh()
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
extends MarginContainer
|
||||
class_name LessonUnlock
|
||||
extends MarginContainer
|
||||
|
||||
signal unlocks_changed
|
||||
signal unlocks_changed()
|
||||
|
||||
@export var lesson_number: int:
|
||||
set = _set_lesson_number
|
||||
|
||||
@export var lesson_gps: String:
|
||||
set = _set_lesson_gps
|
||||
|
||||
@export var unlocks: Dictionary = {}
|
||||
|
||||
@onready var lesson_label: Label = %LessonLabel
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
extends Control
|
||||
class_name LessonUnlocks
|
||||
extends Control
|
||||
|
||||
signal student_deleted(code: int)
|
||||
|
||||
const DEVICE_BUTTON_SCENE: PackedScene = preload("res://sources/menus/main/device_button.tscn")
|
||||
const LESSON_UNLOCK_SCENE: PackedScene = preload("res://sources/menus/settings/lesson_unlock.tscn")
|
||||
|
||||
@onready var lesson_container: VBoxContainer = %LessonContainer
|
||||
@onready var name_line_edit: LineEdit = %NameLineEdit
|
||||
@onready var device_selection_container: PanelContainer = %DeviceSelectionContainer
|
||||
@onready var container: GridContainer = %GridContainer
|
||||
|
||||
var teacher_settings: SettingsTeacherSettings = null
|
||||
|
||||
@export var device: int:
|
||||
set = _on_device_changed
|
||||
@export var student: int:
|
||||
set = _on_student_changed
|
||||
|
||||
var progression: StudentProgression
|
||||
var teacher_settings: SettingsTeacherSettings = null
|
||||
|
||||
@onready var lesson_container: VBoxContainer = %LessonContainer
|
||||
@onready var name_line_edit: LineEdit = %NameLineEdit
|
||||
@onready var device_selection_container: PanelContainer = %DeviceSelectionContainer
|
||||
@onready var container: GridContainer = %GridContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
name_line_edit.connect("text_submitted", _on_name_changed)
|
||||
|
||||
@@ -7,6 +7,7 @@ extends TextureButton
|
||||
@onready var voice_volume_slider: HSlider = %VoiceVolumeSlider
|
||||
@onready var effects_volume_slider: HSlider = %EffectsVolumeSlider
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_master_volume_slider(UserDataManager.get_master_volume())
|
||||
set_music_volume_slider(UserDataManager.get_music_volume())
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
extends Control
|
||||
class_name StudentPanel
|
||||
extends Control
|
||||
|
||||
signal pressed
|
||||
signal pressed()
|
||||
|
||||
@export var student_count: int
|
||||
@export var student_data: StudentData
|
||||
|
||||
@onready var name_label: Label = %NameLabel
|
||||
@onready var password_visualizer: PasswordVisualizer = %PasswordVisualizer
|
||||
|
||||
@export var student_count: int
|
||||
@export var student_data: StudentData
|
||||
|
||||
func _ready() -> void:
|
||||
if not student_data:
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
extends Control
|
||||
class_name SettingsTeacherSettings
|
||||
extends Control
|
||||
|
||||
const MAIN_MENU_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
||||
const LOGIN_MENU_PATH: String = "res://sources/menus/login/login.tscn"
|
||||
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
|
||||
|
||||
const DEVICE_TAB_SCENE: PackedScene = preload("res://sources/menus/settings/device_tab.tscn")
|
||||
|
||||
var last_device_id: int = -1
|
||||
|
||||
@onready var devices_tab_container: TabContainer = %DevicesTabContainer
|
||||
@onready var lesson_unlocks: LessonUnlocks = $LessonUnlocks
|
||||
@onready var delete_popup: ConfirmPopup = %DeletePopup
|
||||
@onready var loading_popup: LoadingPopup = %LoadingPopup
|
||||
|
||||
|
||||
@onready var account_type_option_button: OptionButton = %AccountTypeOptionButton
|
||||
@onready var education_method_option_button: OptionButton = %EducationMethodOptionButton
|
||||
|
||||
@onready var add_device_button: Button = %AddDeviceButton
|
||||
@onready var add_student_button: Button = %AddStudentButton
|
||||
@onready var label_internet_mandatory: Label = %LabelInternetMandatory
|
||||
|
||||
@onready var add_device_popup: CanvasLayer = %AddDevicePopup
|
||||
@onready var add_student_popup: CanvasLayer = %AddStudentPopup
|
||||
@onready var delete_student_popup: CanvasLayer = %DeleteStudentPopup
|
||||
|
||||
var last_device_id: int = -1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
refresh_devices_tabs()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
extends Area2D
|
||||
class_name Ant
|
||||
extends Area2D
|
||||
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
@onready var anchor: Node2D = $Anchor
|
||||
|
||||
@@ -4,9 +4,12 @@ extends Minigame
|
||||
const BLANK_SCENE: PackedScene = preload("res://sources/minigames/ants/blank.tscn")
|
||||
const ANT_SCENE: PackedScene = preload("res://sources/minigames/ants/ant.tscn")
|
||||
const WORD_SCENE: PackedScene = preload("res://sources/minigames/ants/word.tscn")
|
||||
|
||||
const LABEL_SETTINGS: LabelSettings = preload("res://resources/themes/minigames_label_settings.tres")
|
||||
|
||||
var current_sentence: Dictionary = {}
|
||||
var answer_input_done: Array[bool] = []
|
||||
var answers: Dictionary[String, String] # Expected, current
|
||||
|
||||
@onready var sentence_container: HFlowContainer = %Sentence
|
||||
@onready var ants_spawn: Node2D = %AntsSpawn
|
||||
@onready var ants_start: Node2D = %AntsStart
|
||||
@@ -15,10 +18,6 @@ const LABEL_SETTINGS: LabelSettings = preload("res://resources/themes/minigames_
|
||||
@onready var ants: Node2D = %Ants
|
||||
@onready var words: Node2D = %Words
|
||||
|
||||
var current_sentence: Dictionary = {}
|
||||
var answer_input_done: Array[bool] = []
|
||||
var answers: Dictionary[String, String] # Expected, current
|
||||
|
||||
|
||||
func _find_stimuli_and_distractions() -> void:
|
||||
var sentences_list: Array = Database.get_sentences_for_lesson(lesson_nb, difficulty + 2, 50)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
extends TextureRect
|
||||
|
||||
class_name Blank
|
||||
extends TextureRect
|
||||
|
||||
const IMAGES: Array[String] = [
|
||||
"res://assets/minigames/ants/graphics/hole_02.png",
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
extends TextureButton
|
||||
|
||||
class_name Word
|
||||
extends TextureButton
|
||||
|
||||
signal answer(stimulus: String, expected_stimulus: String)
|
||||
signal no_answer()
|
||||
|
||||
@onready var area: Area2D = $Area2D
|
||||
@onready var label: Label = %Label
|
||||
@onready var right_fx: RightFX = $RightFX
|
||||
@onready var wrong_fx: WrongFX = $WrongFX
|
||||
|
||||
var stimulus: String:
|
||||
set = _set_stimulus
|
||||
var follow_mouse: bool = false
|
||||
var current_anchor: CanvasItem
|
||||
|
||||
@onready var area: Area2D = $Area2D
|
||||
@onready var label: Label = %Label
|
||||
@onready var right_fx: RightFX = $RightFX
|
||||
@onready var wrong_fx: WrongFX = $WrongFX
|
||||
|
||||
|
||||
func right() -> void:
|
||||
right_fx.play()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user