Compare commits
10 commits
8a2a393f4a
...
8129cefb00
Author | SHA1 | Date | |
---|---|---|---|
8129cefb00 | |||
feb158f85d | |||
ec2d9d2af5 | |||
3107c40c75 | |||
17259992e9 | |||
b90fbb5e7c | |||
cde8565f5d | |||
66df0a9b44 | |||
f76eca5b9c | |||
921e3351ef |
15 changed files with 358 additions and 121 deletions
|
@ -1,5 +1,5 @@
|
||||||
# Gordle
|
# Gordle
|
||||||
|
|
||||||
Exactly what the title implies - A [Godot engine](https://godotengine.org/) 4.2.1 clone of the trendy game [Wordle](https://www.nytimes.com/games/wordle/index.html). Don't get your expections high.
|
Exactly what the title implies - A [Godot engine](https://godotengine.org/) 4.3 clone of the trendy game [Wordle](https://www.nytimes.com/games/wordle/index.html). Don't get your expections high.
|
||||||
|
|
||||||
Play it at https://thearst3rd.com/games/gordle or https://thearst3rd.itch.io/gordle
|
Play it at https://thearst3rd.com/games/gordle or https://thearst3rd.itch.io/gordle
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe Bold Italic.ttf-f7950aa2883
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe Bold.ttf-b0636941e64913a7c7
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe Italic.ttf-7c4cebb6b89daf9a
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe Light Italic.ttf-3bc639f66f
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe Light.ttf-af51ede3c574e1f3e
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/Louis George Cafe.ttf-7069fa0ad08fa81628e2f55
|
||||||
Rendering=null
|
Rendering=null
|
||||||
antialiasing=1
|
antialiasing=1
|
||||||
generate_mipmaps=false
|
generate_mipmaps=false
|
||||||
|
disable_embedded_bitmaps=true
|
||||||
multichannel_signed_distance_field=false
|
multichannel_signed_distance_field=false
|
||||||
msdf_pixel_range=8
|
msdf_pixel_range=8
|
||||||
msdf_size=48
|
msdf_size=48
|
||||||
|
|
|
@ -12,7 +12,7 @@ config_version=5
|
||||||
|
|
||||||
config/name="gordle"
|
config/name="gordle"
|
||||||
run/main_scene="res://src/menu.tscn"
|
run/main_scene="res://src/menu.tscn"
|
||||||
config/features=PackedStringArray("4.2")
|
config/features=PackedStringArray("4.3")
|
||||||
config/icon="res://icon.png"
|
config/icon="res://icon.png"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
|
@ -136,20 +136,21 @@ layout_mode = 2
|
||||||
|
|
||||||
[node name="WordLabel" type="Label" parent="C/V/Help/Options"]
|
[node name="WordLabel" type="Label" parent="C/V/Help/Options"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "A five letter word, e.g. \"APPLE\""
|
text = "A 3-8 letter word, e.g. \"APPLE\""
|
||||||
label_settings = SubResource("LabelSettings_6at68")
|
label_settings = SubResource("LabelSettings_6at68")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="CodeLabel" type="Label" parent="C/V/Help/Options"]
|
[node name="CodeLabel" type="Label" parent="C/V/Help/Options"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "An encoded word, e.g. \"a00d57a\""
|
text = "An encoded word beginning with \"$\",
|
||||||
|
e.g. \"$bco92\""
|
||||||
label_settings = SubResource("LabelSettings_6at68")
|
label_settings = SubResource("LabelSettings_6at68")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="DateLabel" type="Label" parent="C/V/Help/Options"]
|
[node name="DateLabel" type="Label" parent="C/V/Help/Options"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "A date in YYYY-MM-DD format, e.g.
|
text = "A date in YYYY-MM-DD format,
|
||||||
\"2024-01-23\""
|
e.g. \"2024-01-23\""
|
||||||
label_settings = SubResource("LabelSettings_6at68")
|
label_settings = SubResource("LabelSettings_6at68")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
|
131
src/global.gd
131
src/global.gd
|
@ -7,27 +7,36 @@ enum GameMode {
|
||||||
CUSTOM,
|
CUSTOM,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIN_LENGTH := 5
|
const MIN_WORD_LENGTH := 3
|
||||||
const MAX_LENGTH := 5
|
const MAX_WORD_LENGTH := 8
|
||||||
|
const DEFAULT_WORD_LENGTH := 5
|
||||||
const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt"
|
const GENERABLE_WORDS_FILENAME = "res://words/popular-filtered.txt"
|
||||||
const ALL_WORDS_FILENAME := "res://words/enable1.txt"
|
const ALL_WORDS_FILENAME := "res://words/enable1.txt"
|
||||||
|
|
||||||
|
# !$'()*+,-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz <- All URL-encodable characters
|
||||||
|
const LARGE_BASE_CHARS := "!'()*+,-.123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz" # <- Without some similar looking characters
|
||||||
|
const LARGE_BASE := 68 # Number of characters in above array
|
||||||
|
const ENCODING_INDICATOR := "$"
|
||||||
|
|
||||||
var date_regex = RegEx.create_from_string("^[0-9]+-[0-9]+-[0-9]+$")
|
var date_regex = RegEx.create_from_string("^[0-9]+-[0-9]+-[0-9]+$")
|
||||||
|
|
||||||
var game_mode := GameMode.DAILY
|
var game_mode := GameMode.DAILY
|
||||||
var custom_word := ""
|
var custom_word := ""
|
||||||
var custom_date_str := ""
|
var custom_date_str := ""
|
||||||
|
var custom_word_length := -1
|
||||||
|
|
||||||
var generable_words: Dictionary
|
var generable_words: Dictionary
|
||||||
var all_words: Dictionary
|
var all_words: Dictionary
|
||||||
|
|
||||||
var allow_future := false
|
var allow_future := false
|
||||||
|
|
||||||
|
var strict_mode := false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
randomize()
|
randomize()
|
||||||
generable_words = parse_words(GENERABLE_WORDS_FILENAME, MIN_LENGTH, MAX_LENGTH)
|
generable_words = parse_words(GENERABLE_WORDS_FILENAME, MIN_WORD_LENGTH, MAX_WORD_LENGTH)
|
||||||
all_words = parse_words(ALL_WORDS_FILENAME, MIN_LENGTH, MAX_LENGTH)
|
all_words = parse_words(ALL_WORDS_FILENAME, MIN_WORD_LENGTH, MAX_WORD_LENGTH)
|
||||||
|
|
||||||
detect_params()
|
detect_params()
|
||||||
|
|
||||||
|
@ -66,7 +75,7 @@ func generate_word(length: int, random_seed = null) -> String:
|
||||||
|
|
||||||
func is_valid_word(word: String) -> bool:
|
func is_valid_word(word: String) -> bool:
|
||||||
var length := word.length()
|
var length := word.length()
|
||||||
if length < MIN_LENGTH or length > MAX_LENGTH:
|
if length < MIN_WORD_LENGTH or length > MAX_WORD_LENGTH:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var words_list := all_words[length] as Array
|
var words_list := all_words[length] as Array
|
||||||
|
@ -112,19 +121,29 @@ func detect_params() -> void:
|
||||||
|
|
||||||
## Checks if the given string is:
|
## Checks if the given string is:
|
||||||
##
|
##
|
||||||
## * A normal five letter word, or
|
|
||||||
## * A valid encoded word, or
|
## * A valid encoded word, or
|
||||||
|
## * A normal alphabetical word, or
|
||||||
## * A valid date in YYYY-MM-DD encoding
|
## * A valid date in YYYY-MM-DD encoding
|
||||||
## * Will only allow this date to be in the present or past, unless `allow_future` is set to true
|
## * Will only allow this date to be in the present or past, unless `allow_future` is set to true
|
||||||
##
|
##
|
||||||
## Returns true if successful, and will set `custom_word` and possibly `custom_date_str` values accordingly. Returns
|
## Returns true if successful, and will set `custom_word` and possibly `custom_date_str` values accordingly. Returns
|
||||||
## false if unsuccessful and will erase `custom_word` and `custom_date_str`.
|
## false if unsuccessful and will erase `custom_word` and `custom_date_str`.
|
||||||
func parse_custom(value: String) -> bool:
|
func parse_custom(value: String) -> bool:
|
||||||
if value.length() == 5:
|
custom_word = ""
|
||||||
|
custom_date_str = ""
|
||||||
|
|
||||||
|
var decoded := decode_word(value)
|
||||||
|
var decoded_len := decoded.length()
|
||||||
|
if decoded_len >= MIN_WORD_LENGTH and decoded_len <= MAX_WORD_LENGTH:
|
||||||
|
custom_word = decoded
|
||||||
|
return true
|
||||||
|
|
||||||
|
var value_len := value.length()
|
||||||
|
if value_len >= MIN_WORD_LENGTH and value_len <= MAX_WORD_LENGTH:
|
||||||
# ...is there an is_alpha function that I missed?
|
# ...is there an is_alpha function that I missed?
|
||||||
var word := value.to_upper()
|
var word := value.to_upper()
|
||||||
var valid := true
|
var valid := true
|
||||||
for i in range(5):
|
for i in range(value_len):
|
||||||
var code := word.unicode_at(i)
|
var code := word.unicode_at(i)
|
||||||
if code < 65 or code > 90: # 65 = ascii "A", 90 = ascii "Z"
|
if code < 65 or code > 90: # 65 = ascii "A", 90 = ascii "Z"
|
||||||
valid = false
|
valid = false
|
||||||
|
@ -133,11 +152,6 @@ func parse_custom(value: String) -> bool:
|
||||||
custom_word = word
|
custom_word = word
|
||||||
return true
|
return true
|
||||||
|
|
||||||
var decoded := decode_word(value)
|
|
||||||
if not decoded.is_empty():
|
|
||||||
custom_word = decoded
|
|
||||||
return true
|
|
||||||
|
|
||||||
if date_regex.search(value):
|
if date_regex.search(value):
|
||||||
var parsed_date := Time.get_datetime_dict_from_datetime_string(value, false)
|
var parsed_date := Time.get_datetime_dict_from_datetime_string(value, false)
|
||||||
parsed_date["hour"] = 0
|
parsed_date["hour"] = 0
|
||||||
|
@ -148,16 +162,14 @@ func parse_custom(value: String) -> bool:
|
||||||
if new_time != 0 or (parsed_date["year"] == 1970 and parsed_date["month"] == 1 and parsed_date["day"] == 1):
|
if new_time != 0 or (parsed_date["year"] == 1970 and parsed_date["month"] == 1 and parsed_date["day"] == 1):
|
||||||
valid = true
|
valid = true
|
||||||
if not allow_future:
|
if not allow_future:
|
||||||
var current_time := Time.get_unix_time_from_datetime_string(Time.get_date_string_from_system(true))
|
var current_time := Time.get_unix_time_from_datetime_string(Time.get_date_string_from_system(false))
|
||||||
if new_time > current_time:
|
if new_time > current_time:
|
||||||
valid = false
|
valid = false
|
||||||
if valid:
|
if valid:
|
||||||
custom_word = generate_word(5, new_time)
|
custom_word = generate_word(DEFAULT_WORD_LENGTH, new_time)
|
||||||
custom_date_str = Time.get_date_string_from_unix_time(new_time)
|
custom_date_str = Time.get_date_string_from_unix_time(new_time)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
custom_word = ""
|
|
||||||
custom_date_str = ""
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,60 +190,81 @@ func popcnt(num: int) -> int:
|
||||||
|
|
||||||
## Encodes a word using a very basic "encryption" method. Returns an empty string if invalid word
|
## Encodes a word using a very basic "encryption" method. Returns an empty string if invalid word
|
||||||
func encode_word(word: String) -> String:
|
func encode_word(word: String) -> String:
|
||||||
if word.length() != 5:
|
|
||||||
return ""
|
|
||||||
word = word.to_upper()
|
word = word.to_upper()
|
||||||
|
|
||||||
# Encode word in base 26
|
# Encode word in base 26
|
||||||
var num := 0
|
var num := 1 # Start with a single 1 bit
|
||||||
for i in range(word.length()):
|
for i in range(word.length()):
|
||||||
var letter_ind := word[i].unicode_at(0) - 65 # 65 = ascii "A"
|
num *= 26
|
||||||
|
var letter_ind := word.unicode_at(i) - 65 # 65 = ascii "A"
|
||||||
if letter_ind < 0 or letter_ind >= 26:
|
if letter_ind < 0 or letter_ind >= 26:
|
||||||
return ""
|
return ""
|
||||||
num += letter_ind * (26 ** i)
|
num += letter_ind
|
||||||
# Add an offset so that "aaaaa" isn't just zeroes
|
|
||||||
num += 123456
|
|
||||||
|
|
||||||
# Shuffle bits around
|
# Bit count for redundancy
|
||||||
# uvwxyz -> vuxwzy -> zyvuxw
|
var count := popcnt(num)
|
||||||
num = ((num & 0xf0f0f0) >> 4) | ((num & 0x0f0f0f) << 4)
|
num = (num << 6) + count
|
||||||
num = ((num & 0xffff00) >> 8) | ((num & 0x0000ff) << 16)
|
|
||||||
# Count bits and add as checksum
|
|
||||||
var bits := popcnt(num)
|
|
||||||
num = num | (bits << 24)
|
|
||||||
|
|
||||||
return String.num_int64(num, 16)
|
return ENCODING_INDICATOR + large_encode(num)
|
||||||
|
|
||||||
|
|
||||||
## Decodes an encoded word. Returns an empty string if invalid
|
## Decodes an encoded word. Returns an empty string if invalid
|
||||||
func decode_word(encoded_word: String) -> String:
|
func decode_word(encoded_word: String) -> String:
|
||||||
if not encoded_word.is_valid_hex_number():
|
if not encoded_word.begins_with(ENCODING_INDICATOR):
|
||||||
return ""
|
|
||||||
var encoded_word_num := encoded_word.hex_to_int()
|
|
||||||
|
|
||||||
var bits := encoded_word_num >> 24
|
|
||||||
var num := encoded_word_num & 0xffffff
|
|
||||||
|
|
||||||
# Verify bit count
|
|
||||||
if popcnt(num) != bits:
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Unshuffle bits
|
var large_base_str := encoded_word.substr(1)
|
||||||
num = ((num << 8) & 0xffff00) | ((num >> 16) & 0x0000ff)
|
var full_num := large_decode(large_base_str)
|
||||||
num = ((num << 4) & 0xf0f0f0) | ((num >> 4) & 0x0f0f0f)
|
|
||||||
|
|
||||||
num -= 123456
|
# Check the bit count
|
||||||
|
var count := full_num & 0x3F # 6 bits
|
||||||
|
var num := full_num >> 6
|
||||||
|
if popcnt(num) != count:
|
||||||
|
return ""
|
||||||
|
|
||||||
if num < 0 or num >= (26 ** 5): # (26 ** 5) - 1 = "ZZZZZ"
|
if num <= 0:
|
||||||
# Someone is trying to be sneaky! Or more likely I just messed up.
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Decode base 26 word
|
# Decode base 26 word
|
||||||
var word := ""
|
var word := ""
|
||||||
for i in range(5):
|
while num > 1:
|
||||||
var letter_ind := num % 26
|
var letter_ind := num % 26
|
||||||
word += char(letter_ind + 65) # 65 = ascii "A"
|
word = char(letter_ind + 65) + word # 65 = ascii "A"
|
||||||
@warning_ignore("integer_division")
|
@warning_ignore("integer_division")
|
||||||
num = num / 26
|
num = num / 26
|
||||||
|
|
||||||
|
if num != 1:
|
||||||
|
return ""
|
||||||
|
|
||||||
return word
|
return word
|
||||||
|
|
||||||
|
|
||||||
|
## Encodes an integer as a large-base string
|
||||||
|
func large_encode(num: int) -> String:
|
||||||
|
if num <= 0: # Negative numbers not supported since they are not needed
|
||||||
|
return LARGE_BASE_CHARS[0]
|
||||||
|
var lint := ""
|
||||||
|
while num > 0:
|
||||||
|
lint = LARGE_BASE_CHARS[num % LARGE_BASE] + lint
|
||||||
|
@warning_ignore("integer_division")
|
||||||
|
num = num / LARGE_BASE
|
||||||
|
return lint
|
||||||
|
|
||||||
|
|
||||||
|
## Checks if the given string is a valid large-base integer. (Sorta, it only checks if the character composition is good)
|
||||||
|
func is_valid_large_base_int(lint: String) -> bool:
|
||||||
|
for chr in lint:
|
||||||
|
if chr not in LARGE_BASE_CHARS:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
## Converts the given large-base integer back into a normal int. Returns -1 if invalid
|
||||||
|
func large_decode(lint: String) -> int:
|
||||||
|
var num := 0
|
||||||
|
for chr in lint:
|
||||||
|
var index := LARGE_BASE_CHARS.find(chr)
|
||||||
|
if index < 0:
|
||||||
|
return -1
|
||||||
|
num = (num * LARGE_BASE) + index
|
||||||
|
return num
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
[sub_resource type="FontFile" id="1"]
|
[sub_resource type="FontFile" id="1"]
|
||||||
fallbacks = Array[Font]([ExtResource("1")])
|
fallbacks = Array[Font]([ExtResource("1")])
|
||||||
|
subpixel_positioning = 0
|
||||||
|
msdf_pixel_range = 14
|
||||||
|
msdf_size = 128
|
||||||
cache/0/52/0/ascent = 0.0
|
cache/0/52/0/ascent = 0.0
|
||||||
cache/0/52/0/descent = 0.0
|
cache/0/52/0/descent = 0.0
|
||||||
cache/0/52/0/underline_position = 0.0
|
cache/0/52/0/underline_position = 0.0
|
||||||
|
|
128
src/main.gd
128
src/main.gd
|
@ -1,7 +1,6 @@
|
||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
|
|
||||||
const letter_count := 5
|
|
||||||
const guess_count := 6
|
const guess_count := 6
|
||||||
|
|
||||||
const Letter := preload("res://src/letter.tscn")
|
const Letter := preload("res://src/letter.tscn")
|
||||||
|
@ -13,6 +12,7 @@ const Letter := preload("res://src/letter.tscn")
|
||||||
# An array of arrays of the letter nodes
|
# An array of arrays of the letter nodes
|
||||||
var letters := []
|
var letters := []
|
||||||
var keyboard_buttons := {}
|
var keyboard_buttons := {}
|
||||||
|
var letter_count := Global.DEFAULT_WORD_LENGTH
|
||||||
|
|
||||||
var target_word: String
|
var target_word: String
|
||||||
var current_guess: int
|
var current_guess: int
|
||||||
|
@ -20,6 +20,18 @@ var ended: bool
|
||||||
var won: bool
|
var won: bool
|
||||||
var input_guess: String
|
var input_guess: String
|
||||||
|
|
||||||
|
# Array from position -> 1 character string, if the letter is green, else null if not green yet. In strict mode, once a
|
||||||
|
# green letter is found, it has to be used in all subsequent guesses.
|
||||||
|
var strict_green_requirements: Array = [] # Array[String or null]
|
||||||
|
# Array of guessed letters which were green or yellow. In strict mode, these letters must be used in subsequent guesses.
|
||||||
|
var strict_letter_requirements: Array[String] = []
|
||||||
|
# Array from position index -> Array of letters which have been found yellow at that index. In strict mode, these yellow
|
||||||
|
# must not be used at this position in subsequent guesses.
|
||||||
|
var strict_yellow_restrictions: Array[Array] = [] # Array[Array[String]]
|
||||||
|
# Array of guessed letters which aren't in the target word. In strict mode, these letters cannot be used in subsequent
|
||||||
|
# guesses more times than they have been seen as green or yellow letters.
|
||||||
|
var strict_gray_restrictions: Array[String] = []
|
||||||
|
|
||||||
@onready var title: Label = %Title
|
@onready var title: Label = %Title
|
||||||
@onready var subtitle: Label = %Subtitle
|
@onready var subtitle: Label = %Subtitle
|
||||||
@onready var letter_grid: GridContainer = %LetterGrid
|
@onready var letter_grid: GridContainer = %LetterGrid
|
||||||
|
@ -33,20 +45,12 @@ var input_guess: String
|
||||||
@onready var info_text_animation: AnimationPlayer = %InfoTextAnimation
|
@onready var info_text_animation: AnimationPlayer = %InfoTextAnimation
|
||||||
@onready var copied_text_animation: AnimationPlayer = %CopiedTextAnimation
|
@onready var copied_text_animation: AnimationPlayer = %CopiedTextAnimation
|
||||||
|
|
||||||
|
@onready var strict_button: CheckButton = %StrictButton
|
||||||
|
|
||||||
@onready var error_text_color_default := info_text.label_settings.font_color
|
@onready var error_text_color_default := info_text.label_settings.font_color
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
letter_grid.columns = letter_count
|
|
||||||
for _i in range(guess_count):
|
|
||||||
var letter_array := []
|
|
||||||
for _j in range(letter_count):
|
|
||||||
var letter := Letter.instantiate()
|
|
||||||
letter_array.append(letter)
|
|
||||||
letter_grid.add_child(letter)
|
|
||||||
letters.append(letter_array)
|
|
||||||
|
|
||||||
|
|
||||||
if Global.game_mode != Global.GameMode.CUSTOM:
|
if Global.game_mode != Global.GameMode.CUSTOM:
|
||||||
Global.custom_word = ""
|
Global.custom_word = ""
|
||||||
Global.custom_date_str = ""
|
Global.custom_date_str = ""
|
||||||
|
@ -62,7 +66,7 @@ func _ready() -> void:
|
||||||
else:
|
else:
|
||||||
var random_seed = null
|
var random_seed = null
|
||||||
if Global.game_mode == Global.GameMode.DAILY:
|
if Global.game_mode == Global.GameMode.DAILY:
|
||||||
var current_time := Time.get_date_string_from_system(true)
|
var current_time := Time.get_date_string_from_system(false)
|
||||||
random_seed = Time.get_unix_time_from_datetime_string(current_time)
|
random_seed = Time.get_unix_time_from_datetime_string(current_time)
|
||||||
|
|
||||||
title.text = "Daily " + title.text
|
title.text = "Daily " + title.text
|
||||||
|
@ -70,11 +74,31 @@ func _ready() -> void:
|
||||||
elif Global.game_mode == Global.GameMode.RANDOM:
|
elif Global.game_mode == Global.GameMode.RANDOM:
|
||||||
title.text = "Random " + title.text
|
title.text = "Random " + title.text
|
||||||
|
|
||||||
|
if Global.custom_word_length > 0:
|
||||||
|
letter_count = Global.custom_word_length
|
||||||
target_word = Global.generate_word(letter_count, random_seed)
|
target_word = Global.generate_word(letter_count, random_seed)
|
||||||
|
|
||||||
if Global.game_mode != Global.GameMode.DAILY:
|
if Global.game_mode != Global.GameMode.DAILY:
|
||||||
subtitle.text = Global.encode_word(target_word)
|
subtitle.text = Global.encode_word(target_word)
|
||||||
|
|
||||||
|
letter_count = target_word.length()
|
||||||
|
letter_grid.columns = letter_count
|
||||||
|
for _i in range(guess_count):
|
||||||
|
var letter_array := []
|
||||||
|
for _j in range(letter_count):
|
||||||
|
var letter := Letter.instantiate()
|
||||||
|
letter_array.append(letter)
|
||||||
|
letter_grid.add_child(letter)
|
||||||
|
letters.append(letter_array)
|
||||||
|
|
||||||
|
strict_green_requirements = []
|
||||||
|
strict_green_requirements.resize(letter_count)
|
||||||
|
strict_letter_requirements = []
|
||||||
|
strict_yellow_restrictions = []
|
||||||
|
for _i in range(letter_count):
|
||||||
|
strict_yellow_restrictions.push_back([])
|
||||||
|
strict_gray_restrictions = []
|
||||||
|
|
||||||
current_guess = 0
|
current_guess = 0
|
||||||
ended = false
|
ended = false
|
||||||
won = false
|
won = false
|
||||||
|
@ -88,6 +112,8 @@ func _ready() -> void:
|
||||||
|
|
||||||
share_button.hide()
|
share_button.hide()
|
||||||
|
|
||||||
|
strict_button.button_pressed = Global.strict_mode
|
||||||
|
|
||||||
|
|
||||||
func type_letter(letter: String) -> void:
|
func type_letter(letter: String) -> void:
|
||||||
if ended or input_guess.length() >= letter_count:
|
if ended or input_guess.length() >= letter_count:
|
||||||
|
@ -132,18 +158,73 @@ func guess_entered() -> void:
|
||||||
return
|
return
|
||||||
input_guess = input_guess.to_upper()
|
input_guess = input_guess.to_upper()
|
||||||
if input_guess.length() != letter_count:
|
if input_guess.length() != letter_count:
|
||||||
show_error("Word must be five characters.")
|
show_error("Word must be %d characters." % [letter_count])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if Global.strict_mode:
|
||||||
|
# Strict mode requirements:
|
||||||
|
#
|
||||||
|
# * Letters that have been previously green must be used again
|
||||||
|
# * Letters that have been previously yellow cannot be used again in the same position
|
||||||
|
# * Every letter that has been previously yellow must be used somewhere
|
||||||
|
# * Every letter that has been gray cannot be used, except if it has been found to be green or yellow, in which
|
||||||
|
# case it must be used exactly the correct number of times
|
||||||
|
#
|
||||||
|
# It is possible to rearrange this block to reduce the number of conditions, but doing it this way makes the
|
||||||
|
# error messages more intuitive in my opinion
|
||||||
|
for i in range(letter_count):
|
||||||
|
var letter := input_guess[i]
|
||||||
|
var green = strict_green_requirements[i] # String | null
|
||||||
|
if green and (letter != green):
|
||||||
|
show_error("Letter %d must be %s." % [i + 1, green])
|
||||||
|
return
|
||||||
|
var yellows: Array[String] = []
|
||||||
|
yellows.assign(strict_yellow_restrictions[i])
|
||||||
|
if letter in yellows:
|
||||||
|
show_error("Letter %d cannot be %s." % [i + 1, letter])
|
||||||
|
return
|
||||||
|
var input_letters := Array(input_guess.split(""))
|
||||||
|
var used_requirements: Array[String] = []
|
||||||
|
for present_letter in strict_letter_requirements:
|
||||||
|
if present_letter not in input_letters:
|
||||||
|
var count := strict_letter_requirements.count(present_letter)
|
||||||
|
var count_str := (" %dx" % count) if count > 1 else ""
|
||||||
|
show_error("Letter %s must be used%s." % [present_letter, count_str])
|
||||||
|
return
|
||||||
|
used_requirements.push_back(present_letter)
|
||||||
|
input_letters.erase(present_letter)
|
||||||
|
for i in letter_count:
|
||||||
|
var letter := input_guess[i]
|
||||||
|
if letter in strict_gray_restrictions:
|
||||||
|
if letter not in used_requirements:
|
||||||
|
var count := strict_letter_requirements.count(letter)
|
||||||
|
# We love nested ternaries
|
||||||
|
var count_str = (" more than %d time%s" % [count, "" if count == 1 else "s"]) if count > 0 else ""
|
||||||
|
show_error("Letter %s cannot be used%s." % [letter, count_str])
|
||||||
|
return
|
||||||
|
used_requirements.erase(letter)
|
||||||
|
|
||||||
if not Global.is_valid_word(input_guess) and input_guess != target_word:
|
if not Global.is_valid_word(input_guess) and input_guess != target_word:
|
||||||
show_error("Not a recognized word.")
|
show_error("Not a recognized word.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# This is a valid guess. Strict mode can no longer be changed!
|
||||||
|
strict_button.disabled = true
|
||||||
|
|
||||||
var letters_remaining = []
|
var letters_remaining = []
|
||||||
for letter in target_word:
|
for letter in target_word:
|
||||||
letters_remaining.append(letter)
|
letters_remaining.append(letter)
|
||||||
|
|
||||||
hide_info()
|
hide_info()
|
||||||
|
|
||||||
|
if Global.strict_mode:
|
||||||
|
strict_letter_requirements = []
|
||||||
|
var target_letters := Array(target_word.split(""))
|
||||||
|
for letter in input_guess:
|
||||||
|
if letter in target_letters:
|
||||||
|
strict_letter_requirements.push_back(letter)
|
||||||
|
target_letters.erase(letter)
|
||||||
|
|
||||||
# Mark all greens
|
# Mark all greens
|
||||||
for i in range(letter_count):
|
for i in range(letter_count):
|
||||||
var guess_letter := input_guess[i]
|
var guess_letter := input_guess[i]
|
||||||
|
@ -152,6 +233,8 @@ func guess_entered() -> void:
|
||||||
var letter_instance := letters[current_guess][i] as ColorRect
|
var letter_instance := letters[current_guess][i] as ColorRect
|
||||||
letter_instance.get_node("Label").text = guess_letter
|
letter_instance.get_node("Label").text = guess_letter
|
||||||
if guess_letter == target_letter:
|
if guess_letter == target_letter:
|
||||||
|
if Global.strict_mode:
|
||||||
|
strict_green_requirements[i] = guess_letter
|
||||||
letter_instance.color = color_correct
|
letter_instance.color = color_correct
|
||||||
keyboard_buttons[target_letter].modulate = color_correct
|
keyboard_buttons[target_letter].modulate = color_correct
|
||||||
# Remove that one letter from remaining letters (is there a function for this?)
|
# Remove that one letter from remaining letters (is there a function for this?)
|
||||||
|
@ -175,9 +258,13 @@ func guess_entered() -> void:
|
||||||
break
|
break
|
||||||
if found:
|
if found:
|
||||||
letter_instance.color = color_misplaced
|
letter_instance.color = color_misplaced
|
||||||
|
if Global.strict_mode:
|
||||||
|
strict_yellow_restrictions[i].push_back(guess_letter)
|
||||||
if keyboard_buttons[guess_letter].modulate != color_correct:
|
if keyboard_buttons[guess_letter].modulate != color_correct:
|
||||||
keyboard_buttons[guess_letter].modulate = color_misplaced
|
keyboard_buttons[guess_letter].modulate = color_misplaced
|
||||||
else:
|
else:
|
||||||
|
if Global.strict_mode:
|
||||||
|
strict_gray_restrictions.push_back(guess_letter)
|
||||||
letter_instance.color = color_incorrect
|
letter_instance.color = color_incorrect
|
||||||
if keyboard_buttons[guess_letter].modulate not in [color_correct, color_misplaced]:
|
if keyboard_buttons[guess_letter].modulate not in [color_correct, color_misplaced]:
|
||||||
keyboard_buttons[guess_letter].modulate = color_incorrect
|
keyboard_buttons[guess_letter].modulate = color_incorrect
|
||||||
|
@ -236,14 +323,17 @@ func _on_ButtonBksp_pressed() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _on_share_button_pressed() -> void:
|
func _on_share_button_pressed() -> void:
|
||||||
var text := "%s %s\n" % [title.text, subtitle.text]
|
var text := "%s %s " % [title.text, subtitle.text]
|
||||||
if won:
|
if won:
|
||||||
text += "%d/%d\n\n" % [current_guess, guess_count]
|
text += "%d/%d" % [current_guess, guess_count]
|
||||||
elif ended:
|
elif ended:
|
||||||
text += "X/%d\n\n" % [guess_count]
|
text += "X/%d" % [guess_count]
|
||||||
else:
|
else:
|
||||||
# Can probably bail early but I might as allow it
|
# Can probably bail early but I might as allow it
|
||||||
text += "?/%d\n\n" % [guess_count]
|
text += "?/%d" % [guess_count]
|
||||||
|
if strict_button.button_pressed:
|
||||||
|
text += "*"
|
||||||
|
text += "\n\n"
|
||||||
|
|
||||||
for i in range(current_guess):
|
for i in range(current_guess):
|
||||||
for j in range(letter_count):
|
for j in range(letter_count):
|
||||||
|
@ -260,3 +350,7 @@ func _on_share_button_pressed() -> void:
|
||||||
DisplayServer.clipboard_set(text)
|
DisplayServer.clipboard_set(text)
|
||||||
copied_text_animation.stop()
|
copied_text_animation.stop()
|
||||||
copied_text_animation.play("Copied")
|
copied_text_animation.play("Copied")
|
||||||
|
|
||||||
|
|
||||||
|
func _on_strict_button_toggled(toggled_on: bool) -> void:
|
||||||
|
Global.strict_mode = toggled_on
|
||||||
|
|
129
src/main.tscn
129
src/main.tscn
|
@ -102,23 +102,23 @@ fallbacks = Array[Font]([ExtResource("1")])
|
||||||
subpixel_positioning = 0
|
subpixel_positioning = 0
|
||||||
msdf_pixel_range = 14
|
msdf_pixel_range = 14
|
||||||
msdf_size = 128
|
msdf_size = 128
|
||||||
|
cache/0/18/0/ascent = 0.0
|
||||||
|
cache/0/18/0/descent = 0.0
|
||||||
|
cache/0/18/0/underline_position = 0.0
|
||||||
|
cache/0/18/0/underline_thickness = 0.0
|
||||||
|
cache/0/18/0/scale = 1.0
|
||||||
|
cache/0/18/0/kerning_overrides/18/0 = Vector2(0, 0)
|
||||||
|
cache/0/18/0/kerning_overrides/16/0 = Vector2(0, 0)
|
||||||
cache/0/16/0/ascent = 0.0
|
cache/0/16/0/ascent = 0.0
|
||||||
cache/0/16/0/descent = 0.0
|
cache/0/16/0/descent = 0.0
|
||||||
cache/0/16/0/underline_position = 0.0
|
cache/0/16/0/underline_position = 0.0
|
||||||
cache/0/16/0/underline_thickness = 0.0
|
cache/0/16/0/underline_thickness = 0.0
|
||||||
cache/0/16/0/scale = 1.0
|
cache/0/16/0/scale = 1.0
|
||||||
|
cache/0/16/0/kerning_overrides/18/0 = Vector2(0, 0)
|
||||||
cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0)
|
cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0)
|
||||||
cache/0/16/0/kerning_overrides/24/0 = Vector2(0, 0)
|
|
||||||
cache/0/24/0/ascent = 0.0
|
|
||||||
cache/0/24/0/descent = 0.0
|
|
||||||
cache/0/24/0/underline_position = 0.0
|
|
||||||
cache/0/24/0/underline_thickness = 0.0
|
|
||||||
cache/0/24/0/scale = 1.0
|
|
||||||
cache/0/24/0/kerning_overrides/16/0 = Vector2(0, 0)
|
|
||||||
cache/0/24/0/kerning_overrides/24/0 = Vector2(0, 0)
|
|
||||||
|
|
||||||
[sub_resource type="LabelSettings" id="LabelSettings_vxdpn"]
|
[sub_resource type="LabelSettings" id="LabelSettings_vxdpn"]
|
||||||
font_size = 24
|
font_size = 18
|
||||||
font_color = Color(0.968627, 1, 0, 1)
|
font_color = Color(0.968627, 1, 0, 1)
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_xl3cv"]
|
[sub_resource type="Animation" id="Animation_xl3cv"]
|
||||||
|
@ -146,7 +146,7 @@ tracks/1/keys = {
|
||||||
"times": PackedFloat32Array(0, 0.3, 4),
|
"times": PackedFloat32Array(0, 0.3, 4),
|
||||||
"transitions": PackedFloat32Array(1, 1, 1),
|
"transitions": PackedFloat32Array(1, 1, 1),
|
||||||
"update": 0,
|
"update": 0,
|
||||||
"values": [Vector2(126, 31), Vector2(126, 51), Vector2(126, 51)]
|
"values": [Vector2(266, 14), Vector2(308, 14), Vector2(308, 14)]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_sgitb"]
|
[sub_resource type="AnimationLibrary" id="AnimationLibrary_sgitb"]
|
||||||
|
@ -181,11 +181,13 @@ theme_override_constants/separation = 0
|
||||||
|
|
||||||
[node name="Title" type="Label" parent="C/V/Header"]
|
[node name="Title" type="Label" parent="C/V/Header"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(0, 112)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_fonts/font = SubResource("1")
|
theme_override_fonts/font = SubResource("1")
|
||||||
text = "Gordle"
|
text = "Gordle"
|
||||||
label_settings = SubResource("LabelSettings_h2dux")
|
label_settings = SubResource("LabelSettings_h2dux")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
[node name="Subtitle" type="Label" parent="C/V/Header"]
|
[node name="Subtitle" type="Label" parent="C/V/Header"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
@ -233,63 +235,73 @@ layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
[node name="ButtonQ" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonQ" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "Q"
|
text = "Q"
|
||||||
|
|
||||||
[node name="ButtonW" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonW" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "W"
|
text = "W"
|
||||||
|
|
||||||
[node name="ButtonE" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonE" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "E"
|
text = "E"
|
||||||
|
|
||||||
[node name="ButtonR" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonR" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "R"
|
text = "R"
|
||||||
|
|
||||||
[node name="ButtonT" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonT" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "T"
|
text = "T"
|
||||||
|
|
||||||
[node name="ButtonY" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonY" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "Y"
|
text = "Y"
|
||||||
|
|
||||||
[node name="ButtonU" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonU" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "U"
|
text = "U"
|
||||||
|
|
||||||
[node name="ButtonI" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonI" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "I"
|
text = "I"
|
||||||
|
|
||||||
[node name="ButtonO" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonO" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "O"
|
text = "O"
|
||||||
|
|
||||||
[node name="ButtonP" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
[node name="ButtonP" type="Button" parent="C/V/KeyboardVBox/HRow1"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "P"
|
text = "P"
|
||||||
|
|
||||||
[node name="HRow2" type="HBoxContainer" parent="C/V/KeyboardVBox"]
|
[node name="HRow2" type="HBoxContainer" parent="C/V/KeyboardVBox"]
|
||||||
|
@ -297,57 +309,66 @@ layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
[node name="ButtonA" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonA" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "A"
|
text = "A"
|
||||||
|
|
||||||
[node name="ButtonS" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonS" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "S"
|
text = "S"
|
||||||
|
|
||||||
[node name="ButtonD" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonD" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "D"
|
text = "D"
|
||||||
|
|
||||||
[node name="ButtonF" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonF" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "F"
|
text = "F"
|
||||||
|
|
||||||
[node name="ButtonG" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonG" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "G"
|
text = "G"
|
||||||
|
|
||||||
[node name="ButtonH" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonH" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "H"
|
text = "H"
|
||||||
|
|
||||||
[node name="ButtonJ" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonJ" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "J"
|
text = "J"
|
||||||
|
|
||||||
[node name="ButtonK" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonK" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "K"
|
text = "K"
|
||||||
|
|
||||||
[node name="ButtonL" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
[node name="ButtonL" type="Button" parent="C/V/KeyboardVBox/HRow2"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "L"
|
text = "L"
|
||||||
|
|
||||||
[node name="HRow3" type="HBoxContainer" parent="C/V/KeyboardVBox"]
|
[node name="HRow3" type="HBoxContainer" parent="C/V/KeyboardVBox"]
|
||||||
|
@ -355,56 +376,64 @@ layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
[node name="ButtonZ" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonZ" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "Z"
|
text = "Z"
|
||||||
|
|
||||||
[node name="ButtonX" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonX" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "X"
|
text = "X"
|
||||||
|
|
||||||
[node name="ButtonC" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonC" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "C"
|
text = "C"
|
||||||
|
|
||||||
[node name="ButtonV" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonV" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "V"
|
text = "V"
|
||||||
|
|
||||||
[node name="ButtonB" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonB" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "B"
|
text = "B"
|
||||||
|
|
||||||
[node name="ButtonN" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonN" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "N"
|
text = "N"
|
||||||
|
|
||||||
[node name="ButtonM" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonM" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(58, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 44
|
||||||
text = "M"
|
text = "M"
|
||||||
|
|
||||||
[node name="ButtonBksp" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
[node name="ButtonBksp" type="Button" parent="C/V/KeyboardVBox/HRow3"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
custom_minimum_size = Vector2(50, 0)
|
custom_minimum_size = Vector2(86, 58)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
text = "bksp"
|
text = "bksp"
|
||||||
|
|
||||||
[node name="GuessButton" type="Button" parent="C/V/KeyboardVBox"]
|
[node name="GuessButton" type="Button" parent="C/V/KeyboardVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(120, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
focus_mode = 0
|
focus_mode = 0
|
||||||
|
@ -413,6 +442,7 @@ text = "Guess"
|
||||||
|
|
||||||
[node name="ShareButton" type="Button" parent="C/V/KeyboardVBox/GuessButton"]
|
[node name="ShareButton" type="Button" parent="C/V/KeyboardVBox/GuessButton"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(120, 0)
|
||||||
layout_mode = 0
|
layout_mode = 0
|
||||||
offset_left = 180.0
|
offset_left = 180.0
|
||||||
offset_right = 283.0
|
offset_right = 283.0
|
||||||
|
@ -422,16 +452,15 @@ text = "Share"
|
||||||
|
|
||||||
[node name="CopiedText" type="Label" parent="C/V/KeyboardVBox/GuessButton"]
|
[node name="CopiedText" type="Label" parent="C/V/KeyboardVBox/GuessButton"]
|
||||||
modulate = Color(1, 1, 1, 0)
|
modulate = Color(1, 1, 1, 0)
|
||||||
layout_mode = 0
|
layout_mode = 1
|
||||||
offset_left = 126.0
|
offset_left = 266.0
|
||||||
offset_top = 31.0
|
offset_top = 14.0
|
||||||
offset_right = 347.0
|
offset_right = 331.0
|
||||||
offset_bottom = 59.0
|
offset_bottom = 35.0
|
||||||
theme_override_colors/font_color = Color(1, 0.309804, 0.309804, 1)
|
theme_override_colors/font_color = Color(1, 0.309804, 0.309804, 1)
|
||||||
theme_override_fonts/font = SubResource("FontFile_6m14o")
|
theme_override_fonts/font = SubResource("FontFile_6m14o")
|
||||||
text = "Copied to clipboard!"
|
text = "Copied!"
|
||||||
label_settings = SubResource("LabelSettings_vxdpn")
|
label_settings = SubResource("LabelSettings_vxdpn")
|
||||||
horizontal_alignment = 1
|
|
||||||
|
|
||||||
[node name="CopiedTextAnimation" type="AnimationPlayer" parent="C/V/KeyboardVBox/GuessButton"]
|
[node name="CopiedTextAnimation" type="AnimationPlayer" parent="C/V/KeyboardVBox/GuessButton"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
@ -452,7 +481,19 @@ grow_horizontal = 0
|
||||||
theme_override_font_sizes/font_size = 28
|
theme_override_font_sizes/font_size = 28
|
||||||
text = "Menu"
|
text = "Menu"
|
||||||
|
|
||||||
|
[node name="StrictButton" type="CheckButton" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 0
|
||||||
|
offset_left = 10.0
|
||||||
|
offset_top = 10.0
|
||||||
|
offset_right = 206.0
|
||||||
|
offset_bottom = 50.0
|
||||||
|
focus_mode = 0
|
||||||
|
theme_override_font_sizes/font_size = 28
|
||||||
|
text = "Strict Mode"
|
||||||
|
|
||||||
[connection signal="pressed" from="C/V/KeyboardVBox/HRow3/ButtonBksp" to="." method="_on_ButtonBksp_pressed"]
|
[connection signal="pressed" from="C/V/KeyboardVBox/HRow3/ButtonBksp" to="." method="_on_ButtonBksp_pressed"]
|
||||||
[connection signal="pressed" from="C/V/KeyboardVBox/GuessButton" to="." method="_on_GuessButton_pressed"]
|
[connection signal="pressed" from="C/V/KeyboardVBox/GuessButton" to="." method="_on_GuessButton_pressed"]
|
||||||
[connection signal="pressed" from="C/V/KeyboardVBox/GuessButton/ShareButton" to="." method="_on_share_button_pressed"]
|
[connection signal="pressed" from="C/V/KeyboardVBox/GuessButton/ShareButton" to="." method="_on_share_button_pressed"]
|
||||||
[connection signal="pressed" from="MenuButton" to="." method="_on_MenuButton_pressed"]
|
[connection signal="pressed" from="MenuButton" to="." method="_on_MenuButton_pressed"]
|
||||||
|
[connection signal="toggled" from="StrictButton" to="." method="_on_strict_button_toggled"]
|
||||||
|
|
17
src/menu.gd
17
src/menu.gd
|
@ -4,22 +4,33 @@ extends Control
|
||||||
@onready var quit_button: Button = %QuitButton
|
@onready var quit_button: Button = %QuitButton
|
||||||
@onready var credits: ColorRect = %Credits
|
@onready var credits: ColorRect = %Credits
|
||||||
|
|
||||||
|
@onready var random_buttons = [%Random3, %Random4, %Random6, %Random7, %Random8]
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if OS.has_feature("web"):
|
if OS.has_feature("web"):
|
||||||
quit_button.hide()
|
quit_button.hide()
|
||||||
|
|
||||||
|
for button in random_buttons:
|
||||||
|
button.pressed.connect(start_random_game.bind(int(button.text[0])))
|
||||||
|
|
||||||
|
|
||||||
|
func start_random_game(length := -1) -> void:
|
||||||
|
Global.game_mode = Global.GameMode.RANDOM
|
||||||
|
Global.custom_word_length = length
|
||||||
|
var error := get_tree().change_scene_to_file("res://src/main.tscn")
|
||||||
|
assert(not error)
|
||||||
|
|
||||||
|
|
||||||
func _on_DailyButton_pressed() -> void:
|
func _on_DailyButton_pressed() -> void:
|
||||||
Global.game_mode = Global.GameMode.DAILY
|
Global.game_mode = Global.GameMode.DAILY
|
||||||
|
Global.custom_word_length = -1
|
||||||
var error := get_tree().change_scene_to_file("res://src/main.tscn")
|
var error := get_tree().change_scene_to_file("res://src/main.tscn")
|
||||||
assert(not error)
|
assert(not error)
|
||||||
|
|
||||||
|
|
||||||
func _on_RandomButton_pressed() -> void:
|
func _on_RandomButton_pressed() -> void:
|
||||||
Global.game_mode = Global.GameMode.RANDOM
|
start_random_game()
|
||||||
var error := get_tree().change_scene_to_file("res://src/main.tscn")
|
|
||||||
assert(not error)
|
|
||||||
|
|
||||||
|
|
||||||
func _on_custom_button_pressed() -> void:
|
func _on_custom_button_pressed() -> void:
|
||||||
|
|
|
@ -92,20 +92,22 @@ horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="V2" type="VBoxContainer" parent="C/V"]
|
[node name="V2" type="VBoxContainer" parent="C/V"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 30
|
theme_override_constants/separation = 30
|
||||||
|
|
||||||
[node name="V" type="VBoxContainer" parent="C/V/V2"]
|
[node name="V" type="VBoxContainer" parent="C/V/V2"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="DailyButton" type="Button" parent="C/V/V2/V"]
|
[node name="DailyButton" type="Button" parent="C/V/V2/V"]
|
||||||
|
custom_minimum_size = Vector2(280, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
text = "Daily Gordle"
|
text = "Daily Gordle"
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="C/V/V2/V"]
|
[node name="Label" type="Label" parent="C/V/V2/V"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_fonts/font = SubResource("2")
|
theme_override_fonts/font = SubResource("2")
|
||||||
text = "Changes every day
|
text = "Changes at midnight local time"
|
||||||
at midnight GMT"
|
|
||||||
label_settings = SubResource("LabelSettings_17upj")
|
label_settings = SubResource("LabelSettings_17upj")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
@ -114,9 +116,49 @@ custom_minimum_size = Vector2(310, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="RandomButton" type="Button" parent="C/V/V2/V2"]
|
[node name="RandomButton" type="Button" parent="C/V/V2/V2"]
|
||||||
|
custom_minimum_size = Vector2(280, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
text = "Random Gordle"
|
text = "Random Gordle"
|
||||||
|
|
||||||
|
[node name="H2" type="HBoxContainer" parent="C/V/V2/V2"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="Random3" type="Button" parent="C/V/V2/V2/H2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "3dle"
|
||||||
|
|
||||||
|
[node name="Random4" type="Button" parent="C/V/V2/V2/H2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "4dle"
|
||||||
|
|
||||||
|
[node name="Spacer" type="Control" parent="C/V/V2/V2/H2"]
|
||||||
|
custom_minimum_size = Vector2(20, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="Random6" type="Button" parent="C/V/V2/V2/H2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "6dle"
|
||||||
|
|
||||||
|
[node name="Random7" type="Button" parent="C/V/V2/V2/H2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "7dle"
|
||||||
|
|
||||||
|
[node name="Random8" type="Button" parent="C/V/V2/V2/H2"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_font_sizes/font_size = 24
|
||||||
|
text = "8dle"
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="C/V/V2/V2"]
|
[node name="Label" type="Label" parent="C/V/V2/V2"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_fonts/font = SubResource("2")
|
theme_override_fonts/font = SubResource("2")
|
||||||
|
@ -129,7 +171,9 @@ custom_minimum_size = Vector2(310, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="CustomButton" type="Button" parent="C/V/V2/V3"]
|
[node name="CustomButton" type="Button" parent="C/V/V2/V3"]
|
||||||
|
custom_minimum_size = Vector2(280, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
text = "Custom Gordle"
|
text = "Custom Gordle"
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="C/V/V2/V3"]
|
[node name="Label" type="Label" parent="C/V/V2/V3"]
|
||||||
|
@ -140,12 +184,16 @@ label_settings = SubResource("LabelSettings_cdviw")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="CreditsButton" type="Button" parent="C/V/V2"]
|
[node name="CreditsButton" type="Button" parent="C/V/V2"]
|
||||||
|
custom_minimum_size = Vector2(280, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
text = "Licenses"
|
text = "Licenses"
|
||||||
|
|
||||||
[node name="QuitButton" type="Button" parent="C/V/V2"]
|
[node name="QuitButton" type="Button" parent="C/V/V2"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
custom_minimum_size = Vector2(280, 0)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
text = "Quit"
|
text = "Quit"
|
||||||
|
|
||||||
[node name="Credits" type="ColorRect" parent="."]
|
[node name="Credits" type="ColorRect" parent="."]
|
||||||
|
|
Loading…
Add table
Reference in a new issue