diff --git a/objects/block/block.gd b/objects/block/block.gd index 5c72b9d..d854f5b 100644 --- a/objects/block/block.gd +++ b/objects/block/block.gd @@ -1,9 +1,13 @@ -extends Area2D +## 表达式中的一个可被放入 [Card] 的卡槽,或是字符固定的一个格子。 -class_name Block +class_name Block extends Area2D -signal occupied_card_changed(block: Block) ## 当被占用的 Card 发生改变后发出。 +## 当被占用的 [Card] 发生改变后发出。 +## [br][br] +## [param block] 为发出该信号的 [Block] 对象。 +signal occupied_card_changed(block: Block) + const SHAKE_AMOUNT := 3.0 ## 振动时的振幅(单位为像素)。 @@ -19,22 +23,22 @@ var quest_pos := -1 ## 在表达式中对应字符位置的下标。 -## 是否为空槽。 +## 是否为空槽,即能否被 [Card] 占用。 func is_empty() -> bool: return not self.is_fixed and self.occupied_card == null -## 设置固定的字符为 value。 -## -## 应当只在 is_fixed 为 true 时被调用。 +## 设置固定的字符为 [param value]。 +## [br][br] +## 应当只在 [member is_fixed] 为 [code]true[/code] 时被调用。 func set_word(value: String) -> void: assert(self.is_fixed, "void Block::set_word(value: String) should only be called when is_fixed is true.") $Word.set_word(value) -## 设置占用的 Card 为 card。 -## -## 应当只在 is_fixed 为 false 时被调用。 +## 设置占用的 [Card] 为 [param card]。 +## [br][br] +## 应当只在 [member is_fixed] 为 [code]false[/code] 时被调用。 func set_card(card: Card) -> void: assert(not self.is_fixed, "void Block::set_card(card: Card) should only be called when is_fixed is false.") if self.occupied_card != card: @@ -43,10 +47,10 @@ func set_card(card: Card) -> void: ## 获取当前字符。 -## -## 当 is_fixed 为 true 时,从子节点 Word 返回固定的字符。 -## -## 当 is_fixed 为 false 时,返回占用的 Card 的字符。 +## [br][br] +## 当 [member is_fixed] 为 [code]true[/code] 时,从子节点 [Word] 返回固定的字符。 +## [br][br] +## 当 [member is_fixed] 为 [code]false[/code] 时,返回占用的 [Card] 的字符。 func get_word() -> String: if self.is_fixed: return $Word.get_word() @@ -70,12 +74,13 @@ func set_is_golden_frame(value: bool) -> void: $GoalFrameSprite.visible = not self.is_fixed and self.is_golden_frame +## 若 [param v] 为 [code]true[/code],且有占用的 [Card],则将其设置为通关状态(即调用 [method Card.set_victory])。 func set_victory(v: bool) -> void: if v and not self.is_fixed and self.occupied_card != null: self.occupied_card.set_victory(v) -## 设置字符颜色为 value。 +## 设置字符颜色为 [param value]。 func set_color(value: Color) -> void: if self.is_fixed: $Word.set_color(value) @@ -85,8 +90,7 @@ func set_color(value: Color) -> void: -## 开始震动。 -## is_frame_red 表示是否将框改为红色。 +## 开启震动。若 [param is_frame_red] 为 [code]true[/code],则将框改为红色。 func shake(is_frame_red: bool) -> void: # 开启震动 $ShakeTimer.start() diff --git a/objects/card/card.gd b/objects/card/card.gd index b7cb0ca..7f8a2df 100644 --- a/objects/card/card.gd +++ b/objects/card/card.gd @@ -1,21 +1,18 @@ -extends Area2D +## 写有单个字符的卡牌方块。由 [CardBase] 生成,可以被填入表达式的空格(即有空位的 [Block])中。 +class_name Card extends Area2D -class_name Card -var is_dragging := false ## 是否正在被拖拽。 - var current_block: Block = null ## 当前所占用的 Block 对象。 - +var is_dragging := false ## 是否正在被鼠标拖拽。 +var is_shaking := false ## 是否正在震动。 var is_victory := false ## 是否已通关。 - var shake_amount : float ## 震动幅度(单位为像素)。 -var is_shaking := false ## 是否正在震动。 -## 获取当前最近的,Area 相交的,且未被占用的 Block。 -## +## 获取当前最近的,[Area2D] 相交的,且未被占用的 [Block]。 +## [br][br] ## 距离按两个中心点间的距离计算。 func get_top_prior_entered_block() -> Block: var top_block: Block = null @@ -27,8 +24,8 @@ func get_top_prior_entered_block() -> Block: ## 进行被拾取时的处理。 -## -## 一般在生成 Card 时,以及鼠标移到 Card 上点击鼠标左键时调用。 +## [br][br] +## 一般在生成 [Card] 时,以及鼠标移到 [Card] 上点击鼠标左键时调用。 func pick_up(): self.z_index += 1 self.is_dragging = true @@ -38,8 +35,8 @@ func pick_up(): ## 进行被放下时的处理。 -## -## 一般在 Card 被拖动的过程中,点击鼠标右键时调用。 +## [br][br] +## 一般在 [Card] 被拖动的过程中,点击鼠标右键时调用。 func put_down(): self.z_index -= 1 var entered_block := get_top_prior_entered_block() @@ -56,11 +53,9 @@ func put_down(): queue_free() -## 使 Card 开始震动。 -## -## * is_letter_red:是否要将字符颜色改为红色 -## * amount:震动幅度 -## * duration:震动持续时长 +## 使 [Card] 开始震动。 [param amount] 和 [param duration] 为震动幅度和震动持续时间。 +## [br][br] +## 若 [param is_letter_red] 为 [code]true[/code],则将字符颜色改为红色。 func shake(is_letter_red: bool, amount: float, duration: float) -> void: $ShakeTimer.wait_time = duration self.shake_amount = amount @@ -75,18 +70,18 @@ func shake(is_letter_red: bool, amount: float, duration: float) -> void: $HighlightSprite.visible = false -## 设置字符颜色。 -func set_color(value: Color) -> void: # 设置颜色函数 +## 设置字符颜色为 [param value]。 +func set_color(value: Color) -> void: $Word.set_color(value) # 设置文字颜色 -## 设置是否为胜利状态。 -func set_victory(v: bool): # 设置胜利状态函数 +## 若 [param v] 为 [code]true[/code],则进行通关时的处理。 +func set_victory(v: bool): if v: $CollisionShape2D.set_deferred("disabled", true) # 如果胜利,禁用碰撞形状 -## 设置字符为 value,同时更新卡背颜色。 +## 设置字符为 [param value],同时更新卡背颜色。 func set_word(value: String) -> void: $Word.set_word(value) var card_type := "card-%s" % ExprValidator.get_char_type_as_str(get_word()).to_lower() # 根据单词类型获取卡牌类型 @@ -130,7 +125,7 @@ func _input_event(_viewport: Object, event: InputEvent, _shape_idx: int) -> void if event.button_index == MOUSE_BUTTON_LEFT: if event.is_pressed(): # 如果按下左键,则拾取 Card pick_up() - elif self.is_dragging: # 否则则是放开左键,此时若 Card 正在拖拽,则放下 + elif self.is_dragging: # 否则则是放开左键,此时若 [Card] 正在拖拽,则放下 put_down() elif event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed(): # 按下右键时 diff --git a/objects/word/word.gd b/objects/word/word.gd index 0090792..09a9eee 100644 --- a/objects/word/word.gd +++ b/objects/word/word.gd @@ -1,64 +1,76 @@ -extends AnimatedSprite2D +## 单个字符的动画,可自定义颜色。 +class_name Word extends AnimatedSprite2D -class_name Word +## 显示的字符下标。 +## [br][br] +## 当前显示的字符应为 [code]LETTER_NAME[text_id][/code]。 +@export var text_id := 0 -@export var text_id := 0 -@export var color := Color.BLACK +@export var color := Color.BLACK ## 当前字符颜色。 -const STEP := 30 +const STEP := 30 ## 在字符动画的资源文件夹([code]res://objects/word/sprites[/code])中,同一个字符相邻帧之间的文件编号差。 const LETTER_NAME = [" ", "=", "P", "b", "D", "I1", "I2", "I3", "I4", "I5", "R", "Q", "d", "q", "(", "*", "+", "<", ">", ")", "0", "1", "2", "3", "4", -"5", "6", "7", "8", "9"] +"5", "6", "7", "8", "9"] ## 可取的字符列表。 -## 内建,不要用。 +## 获取字符 [param letter] 在 [member LETTER_NAME] 中对应的下标。 +## [br][br] +## 若不存在,返回 [code]-1[/code]。 func get_letter_id(letter: String) -> int: for i in range(len(LETTER_NAME)): if LETTER_NAME[i] == letter: return i - assert(false, "Letter " + letter + " not found") + push_warning("Letter %s not found in LETTER_NAME." % letter) return -1 -## 内建,不要用。 +## 设置 [member text_id] 为 [param value] 并更新动画。 func set_text_id(value: int) -> void: assert(value >= 0, "text id < 0") text_id = value update_animation() + +## 根据当前的 [member text_id] 更新动画。 func update_animation() -> void: ImageLib.update_animation(self, text_id + 1, 3, STEP, "res://objects/word/sprites/sprite%d.png", Color.BLACK, color) -## 更改字母为 text。 +## 设置字符为 [param text]。 +## [br][br] +## [b]注意[/b]:若 [param text] 为 [code]_[/code] 或 [code].[/code],则会设置为 [code] [/code]。但不推荐使用该做法。 func set_word(text: String) -> void: if text == "_" or text == ".": + push_warning("set_word(%s) is not recommended." % text) set_text_id(0) else: set_text_id(get_letter_id(text)) -## 获得当前字母。 + +## 获取当前字符。 func get_word() -> String: return LETTER_NAME[text_id] -func _ready(): - update_animation() - +## 设置字符颜色为 [param value] 并更新动画。 func set_color(value: Color) -> void: color = value update_animation() + ## 设置是否为关卡通过状态。 -## 已弃用,请使用 set_color 或 set_color_from_name 代替。 +## [br][br] +## 将废弃,请使用 [method set_color] 代替。 +## [br][br] ## @deprecated func set_victory(v: bool) -> void: push_warning("void set_victory(v: bool) is deprecated.") @@ -66,3 +78,8 @@ func set_victory(v: bool) -> void: set_color(ImageLib.PALETTE["golden"]) else: set_color(ImageLib.PALETTE["default"]) + + + +func _ready(): + update_animation() diff --git a/scripts/expr_validator.gd b/scripts/expr_validator.gd index bdf841a..75513b6 100644 --- a/scripts/expr_validator.gd +++ b/scripts/expr_validator.gd @@ -1,17 +1,16 @@ -## 用于判定表达式 `expr` 是否满足通关要求的脚本。 -## 一般情况下只需要使用 `Array check(expr: String, req_pos: Array)` 即可,其他不需要使用。 +## 用于判定表达式 [code]expr[/code] 是否满足通关要求的脚本。 class_name ExprValidator -static func is_alpha(ch: String) -> bool: ## 判断字符 ch 是否为字母。 +static func is_alpha(ch: String) -> bool: ## 判断字符 [param ch] 是否为字母。 return (ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z') -static func is_eye(ch: String) -> bool: ## 判断字符 ch 是否为眼睛。 +static func is_eye(ch: String) -> bool: ## 判断字符 [param ch] 是否为眼睛。 return ch == "*" or ch == "=" -static func is_left_mouth(ch: String) -> bool: ## 判断字符 ch 是否为左边的嘴巴。 +static func is_left_mouth(ch: String) -> bool: ## 判断字符 [param ch] 是否为左边的嘴巴。 return ch == "q" or ch == "d" or ch == "<" -static func is_right_mouth(ch: String) -> bool: ## 判断字符 ch 是否为右边的嘴巴。 +static func is_right_mouth(ch: String) -> bool: ## 判断字符 [param ch] 是否为右边的嘴巴。 return ch == "P" or ch == "b" or ch == "D" or ch == ">" -static func is_smile(string: String) -> bool: ## 判断长度为 2 的字符串 string 是否为笑脸。 +static func is_smile(string: String) -> bool: ## 判断长度为 2 的字符串 [param string] 是否为笑脸。 return (is_left_mouth(string[0]) and is_eye(string[1])) or (is_eye(string[0]) and is_right_mouth(string[1])) """ @@ -39,6 +38,9 @@ Q 1 1 1 1 1 0 Q/0 0 0 1 0 1 """ +## 用于判断相邻字符是否合法。 +## [br][br] +## [code]IS_PAIR_VALID[x][y][/code] 表示若表达式中 [enum CharType] 为 [code]x[/code] 的字符,右边相邻的字符 [enum CharType] 为 [code]y[/code] 的情况下是否合法。 const IS_PAIR_VALID := [ [0, 0, 1, 0, 1, 1], [0, 1, 1, 0, 1, 1], @@ -46,52 +48,79 @@ const IS_PAIR_VALID := [ [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 0, 0] -] +] -enum {OP, COMP, BRACL, BRACR, VAR, CONST} +## 字符类型。 +enum CharType { + OP, ## 算术运算符,包括 [code]*[/code] 和 [code]+[/code]。 + COMP, ## 比较运算符,包括 [code]<[/code],[code]=[/code] 和 [code]>[/code]。 + BRACL, ## 左括号。 + BRACR, ## 右括号。 + VAR, ## 变量,即字母。 + CONST ## 常量,0 或 1。 +} -static func get_char_type(ch: String) -> int: +## 获取 [param ch] 的 [enum CharType]。 +static func get_char_type(ch: String) -> CharType: if ch == "*" or ch == "+": - return OP + return CharType.OP elif ch == "<" or ch == "=" or ch == ">": - return COMP + return CharType.COMP elif ch == "(": - return BRACL + return CharType.BRACL elif ch == ")": - return BRACR + return CharType.BRACR elif ch == "0" or ch == "1": - return CONST + return CharType.CONST elif is_alpha(ch): - return VAR + return CharType.VAR else: push_error("get_char_type(%s) is undefined" % ch) return -1 -static func get_char_type_enum_name(value: int) -> String: - if value == OP: + +## 获取值等于 [param value] 的 [enum CharType] 名称。 +static func get_char_type_enum_name(value: CharType) -> String: + if value == CharType.OP: return "OP" - elif value == COMP: + elif value == CharType.COMP: return "COMP" - elif value == BRACL: + elif value == CharType.BRACL: return "BRACL" - elif value == BRACR: + elif value == CharType.BRACR: return "BRACR" - elif value == VAR: + elif value == CharType.VAR: return "VAR" - elif value == CONST: + elif value == CharType.CONST: return "CONST" else: push_error("get_char_type_enum_name(%d) is undefined" % value) return "ERR" + +## 获取 [param ch] 的 [enum CharType] 名称。 static func get_char_type_as_str(ch: String) -> String: return get_char_type_enum_name(get_char_type(ch)) - + +## 判断长度为 2 的字符串 [param string] 中间是否有隐含的乘号。 +## [br][br] +## 如 [code]A([/code] 中有隐含的乘号,等价于 [code]A*([/code]。 +## [br][br] +## 示例: +## [codeblock] +## has_implict_prod("A(") # true +## has_implict_prod(")(") # true +## has_implict_prod("AB") # true +## has_implict_prod("A+") # false +## has_implict_prod("+1") # false +## [/codeblock] static func has_implict_prod(string: String) -> bool: - return get_char_type(string[0]) in [BRACR, VAR, CONST] and get_char_type(string[1]) in [BRACL, VAR, CONST] - + return get_char_type(string[0]) in [CharType.BRACR, CharType.VAR, CharType.CONST] and get_char_type(string[1]) in [CharType.BRACL, CharType.VAR, CharType.CONST] + + +## 获取 [param ch] 在后缀运算中的优先级。 static func get_priority(ch: String) -> int: if ch == "*": return 4 @@ -109,7 +138,7 @@ static func get_priority(ch: String) -> int: -## 将中缀表达式 expr 转为后缀表达式并返回(不判断 expr 的合法性)。 +## 将中缀表达式 [param expr] 转为后缀表达式并返回(不判断 [param expr] 的合法性)。 static func infix_to_suffix(expr: String) -> String: var opt_stack: Array = [] var res := "" @@ -117,7 +146,7 @@ static func infix_to_suffix(expr: String) -> String: for ch in expr: if pre_ch != "@" and has_implict_prod(pre_ch + ch): - if not opt_stack.is_empty() and (get_char_type("*") != COMP or get_char_type(opt_stack.back()) != COMP): + if not opt_stack.is_empty() and (get_char_type("*") != CharType.COMP or get_char_type(opt_stack.back()) != CharType.COMP): while not opt_stack.is_empty() and get_priority("*") <= get_priority(opt_stack.back()): res += opt_stack.back() opt_stack.pop_back() @@ -126,7 +155,7 @@ static func infix_to_suffix(expr: String) -> String: #print("ch=", ch, " stk=", opt_stack, " res=", res) - if get_char_type(ch) in [VAR, CONST]: + if get_char_type(ch) in [CharType.VAR, CharType.CONST]: res += ch elif ch == "(": opt_stack.push_back(ch) @@ -136,12 +165,12 @@ static func infix_to_suffix(expr: String) -> String: opt_stack.pop_back() opt_stack.pop_back() else: - if get_char_type(pre_ch) != COMP or get_char_type(ch) != COMP: + if get_char_type(pre_ch) != CharType.COMP or get_char_type(ch) != CharType.COMP: while not opt_stack.is_empty() and get_priority(ch) <= get_priority(opt_stack.back()): res += opt_stack.back() opt_stack.pop_back() opt_stack.push_back(ch) - if not res.is_empty() and get_char_type(res[len(res) - 1]) == COMP: + if not res.is_empty() and get_char_type(res[len(res) - 1]) == CharType.COMP: res += "@" pre_ch = ch @@ -149,15 +178,14 @@ static func infix_to_suffix(expr: String) -> String: res += opt_stack.back() opt_stack.pop_back() - if get_char_type(res[len(res) - 1]) == COMP: + if get_char_type(res[len(res) - 1]) == CharType.COMP: res += "@" #prints(expr, res) return res - -## 计算后缀表达式 expr 的值,其中变量的值给定,放在字典 var_values 中(不判断 expr 的合法性)。 +## 计算后缀表达式 [param expr] 的值,其中变量的值给定,放在字典 [param var_values] 中(不判断 [param expr] 的合法性)。 static func calculate_value(expr: String, var_values: Dictionary) -> bool: var stack: Array = [] var val := false @@ -170,12 +198,12 @@ static func calculate_value(expr: String, var_values: Dictionary) -> bool: val = false - elif get_char_type(ch) in [VAR, CONST]: + elif get_char_type(ch) in [CharType.VAR, CharType.CONST]: if is_alpha(ch): stack.push_back(var_values[ch]) else: stack.push_back(ch == "1") - elif get_char_type(ch) == COMP: + elif get_char_type(ch) == CharType.COMP: if ch == "<": val = val or (stack[len(stack) - 2] < stack[len(stack) - 1]) elif ch == "=": @@ -195,13 +223,14 @@ static func calculate_value(expr: String, var_values: Dictionary) -> bool: assert(stack.size() == 1, "suffix expr is invalid.") return stack[0] -## 判断表达式 expr 是否合法(不检查括号匹配)。 -## -## 合法返回 [],否则返回不合法的下标。 + +## 判断表达式 [param expr] 是否合法。 +## [br][br] +## 合法返回空数组 [code][][/code],否则返回不合法的下标列表。 static func check_valid(expr: String) -> Array: - if get_char_type(expr[0]) != BRACL and get_char_type(expr[0]) not in [VAR, CONST]: + if get_char_type(expr[0]) != CharType.BRACL and get_char_type(expr[0]) not in [CharType.VAR, CharType.CONST]: return [0] - if get_char_type(expr[len(expr) - 1]) != BRACR and get_char_type(expr[len(expr) - 1]) not in [VAR, CONST]: + if get_char_type(expr[len(expr) - 1]) != CharType.BRACR and get_char_type(expr[len(expr) - 1]) not in [CharType.VAR, CharType.CONST]: return [len(expr) - 1] for i in range(len(expr) - 1): if not IS_PAIR_VALID[get_char_type(expr[i])][get_char_type(expr[i + 1])]: @@ -222,9 +251,9 @@ static func check_valid(expr: String) -> Array: return [] -## 判断表达式 expr 是否满足笑脸要求。 -## -## 合法返回 [],否则返回不为笑脸的字符下标列表。 +## 判断表达式 [param expr] 是否满足笑脸要求。 +## [br][br] +## 合法返回空数组 [code][][/code],否则返回不为笑脸的字符下标列表。 static func check_smile(expr: String, req_pos: Array) -> Array: var res := [] for i in req_pos: @@ -234,8 +263,8 @@ static func check_smile(expr: String, req_pos: Array) -> Array: ## 判断表达式 expr 是否恒为真(不检查是否合法)。 -## -## 合法返回空字典,否则一种使得表达式为假的变量取值。 +## [br][br] +## 合法返回空字典 [code]{}[/code],否则返回一种使得表达式为假的变量取值。 static func check_always_true(expr: String) -> Dictionary: var var_values: Dictionary = {} # var_values[var] 获取 var 的值。类型:Dictionary[String, bool] var var_names := [] # 变量名称列表 @@ -257,15 +286,18 @@ static func check_always_true(expr: String) -> Dictionary: return {} -## 测试表达式 expr 是否符合通关要求,并返回相关信息。 -## -## 返回的是 [msg, additional_info]: [String, *], -## 若表达式不合法,返回 ["INVALID", pos],其中 pos: Array 是出错的位置。 -## 若不满足笑脸要求,返回 ["SMILE_UNSATISFIED", pos],其中 pos: int 是没有笑脸的位置。 -## 若表达式不恒为正,返回 ["NOT_ALWAYS_TRUE", var_values],其中 var_values: Dictionary[String, bool] 是一种使得表达式为假的变量取值。 -## 否则符合要求,返回 ["OK", 200]。 - -static func check(expr: String, req_pos: Array) -> Array: +## 测试表达式 [param expr] 是否符合通关要求,并返回相关信息。 +## [br][br] +## [param req_pos: Array[int]] 为要求是笑脸的表达式下标列表。 +## [br][br] +## 若表达式不合法,返回 [code]["INVALID", pos][/code],其中 [code]pos: Array[/code] 是出错的位置。 +## [br][br] +## 若不满足笑脸要求,返回 [code]["SMILE_UNSATISFIED", pos][/code],其中 [code]pos: int[/code] 是没有笑脸的位置。 +## [br][br] +## 若表达式不恒为正,返回 [code]["NOT_ALWAYS_TRUE", var_values][/code],其中 [code]var_values: Dictionary[String, bool][/code] 是一种使得表达式为假的变量取值。 +## [br][br] +## 否则符合要求,返回 [code]["OK", 200][/code]。 +static func check(expr: String, req_pos: Array) -> Array: var tmp @@ -287,9 +319,9 @@ static func check(expr: String, req_pos: Array) -> Array: return ["OK", 200] -static func test(): +# static func test(): #check("q*Pq*bd*b=D", []) - check("0=P<>P", []) + # check("0=P<>P", []) #assert(check("(1=P)+(0=P)", []) == ["OK", 200], "9") #assert(check("Q+P=P+Q", [3, 4]) == ["OK", 200], "1") #assert(check("Q+P=P+Q", [4]) == ["OK", 200], "2") diff --git a/scripts/level_data.gd b/scripts/level_data.gd index 5d46398..f9f41df 100644 --- a/scripts/level_data.gd +++ b/scripts/level_data.gd @@ -1,168 +1,188 @@ +## 章节和关卡数据。 class_name LevelData + + +## 各章节的名称。 +## [br][br] +## [code]CHAP_NAMES[chapter_id]["name-en"][/code] 返回第 [param chapter_id] 章节的英文名称(包含 [code]Ch.[/code] 前缀)。 const CHAP_NAMES := [ - { - "name-en": "Ch. 1 =P", - }, - { - "name-en": "Ch. 2 Add and Multiply", - }, - { - "name-en": "Ch. 3 ()", - }, - { - "name-en": "Ch. 4 Equal?", - }, - { - "name-en": "Ch. 5 <>", - } + { + "name-en": "Ch. 1 =P", + }, + { + "name-en": "Ch. 2 Add and Multiply", + }, + { + "name-en": "Ch. 3 ()", + }, + { + "name-en": "Ch. 4 Equal?", + }, + { + "name-en": "Ch. 5 <>", + } ] + +## 各关卡的关卡数据。 +## [br][br] +## [code]LEVEL_DATA[chapter_id][level_id] : Dictionary[/code] 返回第 [param chapter_id] 章节的第 [param level_id] 关卡的关卡数据,以下记为 [code]data[/code]。其中: +## [br][br] +## [code]data["name-en"][/code] 为关卡英文名。 +## [br][br] +## [code]data["question"][/code] 为关卡表达式,用 [code][][/code] 表示无要求的空槽,用 [code]{}[/code] 表示要求为笑脸的空槽。 +## [br][br] +## [code]data["choices"][/code] 为关卡选项。 const LEVEL_DATA := [ - [ - { - "name-en": "=P", - "question": "P [] []", - "choices": "= P" - }, + [ + { + "name-en": "=P", + "question": "P [] []", + "choices": "= P" + }, { - "name-en": "Smile", - "question": "P {} {}", - "choices": "= P P" - }, - { - "name-en": "Another Smile", - "question": "[] = {}", - "choices": "R R P D D" - }, + "name-en": "Smile", + "question": "P {} {}", + "choices": "= P P" + }, + { + "name-en": "Another Smile", + "question": "[] = {}", + "choices": "R R P D D" + }, { - "name-en": "Reverse", - "question": "{} [] []", - "choices": "D D = d d" - }, + "name-en": "Reverse", + "question": "{} [] []", + "choices": "D D = d d" + }, { "name-en": "Reverse Again", "question": "[] {} {}", "choices": "d d = R R b b" }, { - "name-en": "Snake", - "question": "[] = {} = {} = [] = {} = {}", - "choices": "dddddd QQQQQQ RRRRRR DDDDDD PPPPPP qqqqqq" - } - ], - - [ - { - "name-en": "0+0=0, 0+1=1", - "question": "[] [] {} {} d", - "choices": "= + 0 d" - }, - { - "name-en": "1+1=1", - "question": "[] [] {} {} 1", - "choices": "= + d 1" - }, - { - "name-en": "Swap", - "question": "Q + [] = {} + []", - "choices": "P P P P Q Q Q Q" - }, - { - "name-en": "Always True", - "question": "1 [] {} = {} [] []", - "choices": "1++PPdd" - }, - { - "name-en": "Make Me Laugh", - "question": "1 {} {} = []", - "choices": "XDD" - }, - { - "name-en": "Reset", - "question": "0 [] [] {} []", - "choices": "XD=0" - }, - { - "name-en": "Not Necessary", - "question": "[] {} {} {} {} []", - "choices": "QQQQQQ DDXX=" - }, - { - "name-en": "[EX] Not Really Challenging", - "question": "1 [] [] [] {} [] [] [] {} [] {} [] []", - "choices": "== ++++++ 11 P q b R" - }, - { - "name-en": "[EX] Golden Experience", - "question": "{} {} {} {} {} {} {} {} {} {}", - "choices": "PP DD qq dd question XXX =" - } - ], + "name-en": "Snake", + "question": "[] = {} = {} = [] = {} = {}", + "choices": "dddddd QQQQQQ RRRRRR DDDDDD PPPPPP qqqqqq" + } + ], + + [ + { + "name-en": "0+0=0, 0+1=1", + "question": "[] [] {} {} d", + "choices": "= + 0 d" + }, + { + "name-en": "1+1=1", + "question": "[] [] {} {} 1", + "choices": "= + d 1" + }, + { + "name-en": "Swap", + "question": "Q + [] = {} + []", + "choices": "P P P P Q Q Q Q" + }, + { + "name-en": "Always True", + "question": "1 [] {} = {} [] []", + "choices": "1++PPdd" + }, + { + "name-en": "Make Me Laugh", + "question": "1 {} {} = []", + "choices": "XDD" + }, + { + "name-en": "Reset", + "question": "0 [] [] {} []", + "choices": "XD=0" + }, + { + "name-en": "Not Necessary", + "question": "[] {} {} {} {} []", + "choices": "QQQQQQ DDXX=" + }, + { + "name-en": "[EX] Not Really Challenging", + "question": "1 [] [] [] {} [] [] [] {} [] {} [] []", + "choices": "== ++++++ 11 P q b R" + }, + { + "name-en": "[EX] Golden Experience", + "question": "{} {} {} {} {} {} {} {} {} {}", + "choices": "PP DD qq dd question XXX =" + } + ], - [ - { - "name-en": "He Goes First", - "question": "[] [] + [] = ( [] {} {} ) [] []", - "choices": "PP QQ RR X +" - }, - { - "name-en": "Still, He Goes First", - "question": "[] + [] [] = [] [] ( [] {} {} )", - "choices": "PP QQ RR X +" - }, - { - "name-en": "It's My Turn", - "question": "P ([] [] []) = {} [] [] [] []", - "choices": "PP QQ RR ++" - }, - { - "name-en": "[EX] Really Challenging", - "question": "[] [] [] [] [] {} {} [] [] [] [] {} {} + [] []", - "choices": "PPP QQ DD (()) ++ = X" - } - ], + [ + { + "name-en": "He Goes First", + "question": "[] [] + [] = ( [] {} {} ) [] []", + "choices": "PP QQ RR X +" + }, + { + "name-en": "Still, He Goes First", + "question": "[] + [] [] = [] [] ( [] {} {} )", + "choices": "PP QQ RR X +" + }, + { + "name-en": "It's My Turn", + "question": "P ([] [] []) = {} [] [] [] []", + "choices": "PP QQ RR ++" + }, + { + "name-en": "[EX] Really Challenging", + "question": "[] [] [] [] [] {} {} [] [] [] [] {} {} + [] []", + "choices": "PPP QQ DD (()) ++ = X" + } + ], - [ - { - "name-en": "Why?", - "question": "[] [] {} = {} [] {} = []", - "choices": "PP qq ++ 1" - }, - { - "name-en": "Where is the Equation?", - "question": "[] [] {} {} [] + [] [] {} {} []", - "choices": "(())==01PP" - }, + [ + { + "name-en": "Why?", + "question": "[] [] {} = {} [] {} = []", + "choices": "PP qq ++ 1" + }, + { + "name-en": "Where is the Equation?", + "question": "[] [] {} {} [] + [] [] {} {} []", + "choices": "(())==01PP" + }, - { - "name-en": "Untitled", - "question": "[] [] {} {} [] [] [] []", - "choices": "=PPQQ+()" - } - ], + { + "name-en": "Untitled", + "question": "[] [] {} {} [] [] [] []", + "choices": "=PPQQ+()" + } + ], - [ - { - "name-en": "<", - "question": "0 {} {} [] 1", - "choices": "XD<" - }, - { - "name-en": "<=", - "question": "[] X [] {} {} [] + []", - "choices": "QQPP<=" - }, - { - "name-en": "<>", - "question": "0 {} P [] [] []", - "choices": "<>=P" - } - ] + [ + { + "name-en": "<", + "question": "0 {} {} [] 1", + "choices": "XD<" + }, + { + "name-en": "<=", + "question": "[] X [] {} {} [] + []", + "choices": "QQPP<=" + }, + { + "name-en": "<>", + "question": "0 {} P [] [] []", + "choices": "<>=P" + } + ] ] + +## 获取第 [param chapter_id] 章节的关卡数量。 static func get_chapter_level_count(chapter_id: int) -> int: - return len(LEVEL_DATA[chapter_id]) + return len(LEVEL_DATA[chapter_id]) + +## 获取章节数量。 static func get_chapter_count() -> int: - return len(LEVEL_DATA) \ No newline at end of file + return len(LEVEL_DATA)