summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <git@mail.dweipert.de>2025-04-07 11:45:05 +0200
committerDaniel Weipert <git@mail.dweipert.de>2025-04-07 11:45:05 +0200
commitf3d360e022fce829370c7d916abe98083b14818c (patch)
tree4ac9f886b45a9482dcd42e991e85ab2e4bea3dbc
parentbd77c88efc9327805b6f6fd83fa0492ed59d0f9a (diff)
next commitHEADmain
-rw-r--r--Readme.md3
-rw-r--r--byte_stream.gd9
-rw-r--r--constants.gd58
-rw-r--r--data_models/login_character_list.gd4
-rw-r--r--entities/player.tscn8
-rw-r--r--extractor/extractor_interface.gd28
-rw-r--r--extractor/gnd_format.gd6
-rw-r--r--extractor/grf.gd25
-rw-r--r--extractor/rsm_format.gd12
-rw-r--r--extractor/rsw_format.gd18
-rw-r--r--network/character_server.gd54
-rw-r--r--network/client_info.gd19
-rw-r--r--network/login_server.gd20
-rw-r--r--network/network.gd6
-rw-r--r--network/server.gd39
-rw-r--r--packets/character_server/character_creation_failed_packet.gd19
-rw-r--r--packets/character_server/create_character_success_packet.gd22
-rw-r--r--packets/client/character_server/create_character_packet.gd54
-rw-r--r--packets/login_server/login_failed_packet.gd (renamed from packets/login_failed_packet.gd)0
-rw-r--r--packets/login_server/login_failed_packet_2.gd27
-rw-r--r--packets/login_server/login_server_login_success_packet.gd2
-rw-r--r--project.godot2
-rw-r--r--sound_manager.tscn4
-rw-r--r--ui/chat_window.tscn12
-rw-r--r--ui/login.gd103
-rw-r--r--ui/login.tscn120
-rw-r--r--ui/login/character_creation.gd134
-rw-r--r--ui/login/character_creation.tscn304
-rw-r--r--ui/login/character_selection_item.tscn10
-rw-r--r--ui/login/login_character_selection_list.gd5
-rw-r--r--ui/login/login_character_selection_list.tscn22
-rw-r--r--ui/login/stat_entry.gd18
-rw-r--r--ui/login/stat_entry.tscn24
-rw-r--r--ui/message_window.tscn12
-rw-r--r--ui/theme.tres11
-rw-r--r--ui/theme_clear.tres2
-rw-r--r--ui/window.tscn38
37 files changed, 1068 insertions, 186 deletions
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..83bbb9d
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,3 @@
+- https://wiki.herc.ws/wiki/Data_Folder_Structure
+- https://github.com/Doddler/RagnarokRebuild/blob/7933dd2d2ef512d58b9f12fd5d9f76df44d0765e/RebuildClient/Assets/Scripts/MapEditor/Editor/RagnarokModelLoader.cs#L267
+- https://github.com/Doddler/RagnarokRebuild/blob/7933dd2d2ef512d58b9f12fd5d9f76df44d0765e/RebuildClient/Assets/Scripts/MapEditor/Editor/RagnarokWorldSceneBuilder.cs#L332
diff --git a/byte_stream.gd b/byte_stream.gd
index 219e977..5477422 100644
--- a/byte_stream.gd
+++ b/byte_stream.gd
@@ -100,3 +100,12 @@ func get_string_from_utf8(length: int) -> String:
func get_string_from_ascii(length: int) -> String:
return get_buffer(length).bytes.get_string_from_ascii()
+
+func get_string_from_ro(length: int) -> String:
+ var buffer := get_buffer(length).bytes
+ Engine.print_error_messages = false
+ var string := buffer.get_string_from_multibyte_char("EUC-KR")
+ Engine.print_error_messages = true
+ if string.is_empty():
+ return buffer.get_string_from_ascii()
+ return string
diff --git a/constants.gd b/constants.gd
index e495322..28c5ab9 100644
--- a/constants.gd
+++ b/constants.gd
@@ -27,6 +27,25 @@ enum LoginFailedReason {
AlreadyOnline = 8,
}
+enum LoginFailedReason2 {
+ UnregisteredId,
+ IncorrectPassword,
+ IdExpired,
+ RejectedFromServer,
+ BlockedByGMTeam,
+ GameOutdated,
+ LoginProhibitedUntil,
+ ServerFull,
+ CompanyAccountLimitReached,
+}
+
+enum CharacterCreationFailedReason {
+ CharacterNameAlreadyInUse,
+ NotOldEnough,
+ NotAllowedToUseSlot = 3,
+ CharacterCreationFailed = 255,
+}
+
enum StatusType {
Weight,
MaximumWeight,
@@ -94,11 +113,16 @@ static var PacketDB = {
CharacterServerLoginSuccessCharacterListPacket.HEADER: CharacterServerLoginSuccessCharacterListPacket,
CharacterListSizePacket.HEADER: CharacterListSizePacket,
LoginFailedPacket.HEADER: LoginFailedPacket,
+ LoginFailedPacket2.HEADER: LoginFailedPacket2,
BlockCharacterPacket.HEADER: BlockCharacterPacket,
PinCodeStatePacket.HEADER: PinCodeStatePacket,
RequestCharacterListSuccessPacket.HEADER: RequestCharacterListSuccessPacket,
CharacterSelectionSuccessPacket.HEADER: CharacterSelectionSuccessPacket,
CharacterSelectionFailedPacket.HEADER: CharacterSelectionFailedPacket,
+ CreateCharacterPacket.HEADER: CreateCharacterPacket,
+ CreateCharacterSuccessPacket.HEADER: CreateCharacterSuccessPacket,
+ CharacterCreationFailedPacket.HEADER: CharacterCreationFailedPacket,
+
MapServerLoginSuccessPacket.HEADER: MapServerLoginSuccessPacket,
FriendListPacket.HEADER: FriendListPacket,
ServerMessagePacket.HEADER: ServerMessagePacket,
@@ -139,11 +163,11 @@ static var PacketDB = {
class FilePaths:
- const female := "¿©"
- const male := "³²"
+ const female := "여"
+ const male := "남"
- const player_head := "Àΰ£Á·/¸Ó¸®Åë"
- const player_body := "Àΰ£Á·/¸öÅë"
+ const player_head := "인간족/머리통"
+ const player_body := "인간족/몸통"
const male_head_lookup := [2, 2, 1, 7, 5, 4, 3, 6, 8, 9, 10, 12, 11]
const female_head_lookup := [2, 2, 4, 7, 1, 5, 3, 6, 12, 10, 9, 11, 8]
@@ -159,33 +183,33 @@ class FilePaths:
static func get_job_path(job_id: int) -> String:
match job_id:
Job.Novice: # NOVICE
- return "Ãʺ¸ÀÚ"
+ return "초보자"
Job.Swordman: # SWORDMAN
- return "°Ë»Ç"
+ return "검사"
Job.Magician: # MAGICIAN
- return "À§Àúµå"
+ return "마법사"
Job.Archer: # ARCHER
- return "±Ã¼Ö"
+ return "궁수"
Job.Acolyte: # ACOLYTE
- return "¼ºÁ÷ÀÚ"
+ return "성직자"
5: # MERCHANT
- return "»ÓÀÎ"
+ return "상인"
6: # THIEF
- return "µµµÏ"
+ return "도둑"
7: # KNIGHT
- return "񃯇"
+ return "기사"
8: # PRIEST
- return "¼ºÅõ»ç"
+ return "성투사"
9: # WIZARD
- return "¸¶¹Ý»Ç"
+ return "위저드"
10: # BLACKSMITH
- return "Á¦Ã¶°ø"
+ return "제철공"
11: # HUNTER
- return "ÇåÅÍ"
+ return "헌터"
# ... TODO
_: # NOVICE
- return "Ãʺ¸ÀÚ"
+ return "초보자"
static func get_player_head(gender: Gender, head_id: int) -> String:
diff --git a/data_models/login_character_list.gd b/data_models/login_character_list.gd
index 8a8d7d5..25a94c3 100644
--- a/data_models/login_character_list.gd
+++ b/data_models/login_character_list.gd
@@ -35,3 +35,7 @@ func get_info_for_slot(slot_idx: int):
return info
return null
+
+
+func add_character_information(info: CharacterInformation) -> void:
+ character_information.append(info)
diff --git a/entities/player.tscn b/entities/player.tscn
index 805cd45..b9c35b7 100644
--- a/entities/player.tscn
+++ b/entities/player.tscn
@@ -1,8 +1,8 @@
[gd_scene load_steps=4 format=3 uid="uid://b2c5mpkafk8q6"]
[ext_resource type="Script" uid="uid://caid7hva3kg2i" path="res://entities/player.gd" id="1_merdl"]
-[ext_resource type="Texture2D" uid="uid://danymuvfjf4o1" path="res://client_data/data/sprite/Àΰ£Á·/¸Ó¸®Åë/³²/16_³²/000.png" id="2_b0kkn"]
-[ext_resource type="Texture2D" uid="uid://cwqgdd00sf7pu" path="res://client_data/data/sprite/Àΰ£Á·/¸öÅë/³²/Ãʺ¸ÀÚ_³²/000.png" id="3_e4p34"]
+[ext_resource type="Texture2D" uid="uid://ci2liot5s8jnb" path="res://client_data/data/sprite/인간족/머리통/남/16_남/000.png" id="2_e4p34"]
+[ext_resource type="Texture2D" uid="uid://be3ax80esna7b" path="res://client_data/data/sprite/인간족/몸통/남/초보자_남/000.png" id="3_rfe5m"]
[node name="Player" type="CharacterBody3D"]
script = ExtResource("1_merdl")
@@ -14,9 +14,9 @@ transform = Transform3D(1, 0, 0, 0, 0.258819, 0.965926, 0, -0.965926, 0.258819,
unique_name_in_owner = true
transform = Transform3D(25, 0, 0, 0, 6.47048, 24.1481, 0, -24.1481, 6.47048, 0, 0, 0)
offset = Vector2(0, 43)
-texture = ExtResource("2_b0kkn")
+texture = ExtResource("2_e4p34")
[node name="Body" type="Sprite3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(25, 0, 0, 0, 6.47048, 24.1481, 0, -24.1481, 6.47048, 0, 0, 0)
-texture = ExtResource("3_e4p34")
+texture = ExtResource("3_rfe5m")
diff --git a/extractor/extractor_interface.gd b/extractor/extractor_interface.gd
index 3afedfd..4d74a20 100644
--- a/extractor/extractor_interface.gd
+++ b/extractor/extractor_interface.gd
@@ -2,9 +2,11 @@ extends Control
func _ready() -> void:
- #var grf = GRF.open("res://client_data/data.grf")
- #grf.extract()#"user://client_data")
- #grf.convert()#"user://client_data")
+ pass
+
+ var grf = GRF.open("res://client_data/data.grf")
+ #grf.extract("res://client_data")
+ grf.convert("res://client_data")
#Sprite.from_bytes(FileAccess.get_file_as_bytes("res://client_data/data/sprite/cursors.spr"))
#ActionFormat.from_bytes(
@@ -22,12 +24,12 @@ func _ready() -> void:
#FileAccess.get_file_as_bytes("res://client_data/data/int_land02.gnd")
#)
#)
- var rsw = RSWFormat.from_bytes(
- ByteStream.from_bytes(
- #FileAccess.get_file_as_bytes("res://client_data/data/int_land02.rsw")
- FileAccess.get_file_as_bytes("res://client_data/data/pay_dun00.rsw")
- )
- )
+ #var rsw = RSWFormat.from_bytes(
+ #ByteStream.from_bytes(
+ ##FileAccess.get_file_as_bytes("res://client_data/data/int_land02.rsw")
+ #FileAccess.get_file_as_bytes("res://client_data/data/pay_dun00.rsw")
+ #)
+ #)
#RSMFormat.from_bytes(
#ByteStream.from_bytes(
##FileAccess.get_file_as_bytes("res://client_data/data/model/prontera/chair_01.rsm")
@@ -37,7 +39,7 @@ func _ready() -> void:
#)
#)
- var scene_root := rsw.convert("pay_dun00", "res://client_data")
- var scene := PackedScene.new()
- scene.pack(scene_root)
- ResourceSaver.save(scene, "res://extractor/test/pay_dun00.tscn")
+ #var scene_root := rsw.convert("pay_dun00", "res://client_data")
+ #var scene := PackedScene.new()
+ #scene.pack(scene_root)
+ #ResourceSaver.save(scene, "res://extractor/test/pay_dun00.tscn")
diff --git a/extractor/gnd_format.gd b/extractor/gnd_format.gd
index faad9ae..c49bce6 100644
--- a/extractor/gnd_format.gd
+++ b/extractor/gnd_format.gd
@@ -91,7 +91,7 @@ static func from_bytes(bytes: ByteStream) -> GNDFormat:
gnd_format.texture_paths = []
for _n in gnd_format.texture_count:
gnd_format.texture_paths.append(
- bytes.get_string_from_ascii(gnd_format.texture_path_length)
+ bytes.get_string_from_ro(gnd_format.texture_path_length)
)
gnd_format.light_map_slice_count = bytes.decode_s32()
@@ -162,6 +162,10 @@ func convert(data_path: String) -> GridMap:
var cubes := get_cubes()
+ var cache := {}
+ # TODO: use texture_index and surface uvs as key
+ # TODO: for deduplication of cell items (as long as other sides aren't accounted for..)
+
for x in width:
for y in height:
var cube = cubes[x + y * width]
diff --git a/extractor/grf.gd b/extractor/grf.gd
index fd0e6f7..5b7046f 100644
--- a/extractor/grf.gd
+++ b/extractor/grf.gd
@@ -156,7 +156,7 @@ class FileEntry:
var file_access: FileAccess
-static func open(path: String):
+static func open(path: String) -> GRF:
var grf = GRF.new()
grf.file_access = FileAccess.open(path, FileAccess.ModeFlags.READ)
@@ -227,12 +227,17 @@ func convert(destination: String = "res://client_data"):
# BMP
if file_path.ends_with(".bmp"):
+ #continue
if not FileAccess.file_exists("%s/%s" % [destination, file_path]):
continue
# load existing bmp files, so language specific overwrites are kept
var texture: CompressedTexture2D = load("%s/%s" % [destination, file_path])
+ if not texture:
+ # TODO: check if .godot/imported file is there (alrdy sufficient?)
+ continue
+
var texture_image := texture.get_image()
texture_image.decompress()
var image := BMPTexture.convert_image(
@@ -241,18 +246,19 @@ func convert(destination: String = "res://client_data"):
)
image.save_png("%s/%s" % [destination, file_path.replace(".bmp", ".png")])
+
continue
# Sprite.spr and Action.act
- var player_head_path_part = "¸Ó¸®Åë"
- var player_body_path_part = "¸öÅë"
+ var player_head_path_part = "머리통"
+ var player_body_path_part = "몸통"
- if file_path.ends_with(".spr") and file_path.contains(player_head_path_part):
+ if file_path.ends_with(".spr"): #and (file_path.contains(player_head_path_part) or file_path.contains(player_body_path_part)):
continue
var sprite = SpriteFormat.from_bytes(file_entry.get_contents(file_access))
sprite.save_to_file(base_file_directory_path)
- elif file_path.ends_with(".act") and file_path.contains(player_head_path_part): #or file_path.contains(player_body_path_part):
+ elif file_path.ends_with(".act") and file_path.contains("cursors"): #and (file_path.contains(player_head_path_part) or file_path.contains(player_body_path_part)):
#continue
if not FileAccess.file_exists("%s/000.png.import" % base_file_directory_path):
continue
@@ -267,6 +273,7 @@ func convert(destination: String = "res://client_data"):
ResourceSaver.save(scene, "%s/actions.tscn" % base_file_directory_path)
+ continue
# Map.rsw and .gnd and .gat
if file_path.ends_with(".rsw") and (file_path.contains("pay_dun") or file_path.contains("iz_int") or file_path.contains("int_land")):
@@ -279,5 +286,9 @@ func convert(destination: String = "res://client_data"):
static func decode_string(bytes: PackedByteArray):
- # TODO: use iconv to decode EUC-KR
- return bytes.get_string_from_ascii()
+ Engine.print_error_messages = false
+ var string := bytes.get_string_from_multibyte_char("EUC-KR")
+ Engine.print_error_messages = true
+ if string.is_empty():
+ return bytes.get_string_from_ascii()
+ return string
diff --git a/extractor/rsm_format.gd b/extractor/rsm_format.gd
index c01826e..7f16368 100644
--- a/extractor/rsm_format.gd
+++ b/extractor/rsm_format.gd
@@ -92,17 +92,17 @@ static func from_bytes(bytes: ByteStream) -> RSMFormat:
rsm_format.texture_names = [] as Array[String]
for _n in rsm_format.texture_count:
- rsm_format.texture_names.append(bytes.get_string_from_ascii(40))
+ rsm_format.texture_names.append(bytes.get_string_from_ro(40))
if version.lower_than(2, 2):
- rsm_format.root_node_name = bytes.get_string_from_ascii(40)
+ rsm_format.root_node_name = bytes.get_string_from_ro(40)
if version.higher_than(2, 1): # >= 2.2
rsm_format.root_node_count = bytes.decode_u32()
rsm_format.root_node_names = [] as Array[String]
for _n in rsm_format.root_node_count:
- rsm_format.root_node_names.append(bytes.get_string_from_ascii(40))
+ rsm_format.root_node_names.append(bytes.get_string_from_ro(40))
rsm_format.node_count = bytes.decode_u32()
rsm_format.nodes = [] as Array[ModelNode]
@@ -253,7 +253,7 @@ class ModelNode:
var node = ModelNode.new()
node.node_name = bytes.get_string_from_utf8(40)
- node.parent_node_name = bytes.get_string_from_ascii(40)
+ node.parent_node_name = bytes.get_string_from_ro(40)
if version.lower_than(2, 3): # < 2.3
node.texture_count = bytes.decode_u32()
@@ -267,7 +267,7 @@ class ModelNode:
node.texture_names = [] as Array[String]
for _n in node.texture_name_count:
- node.texture_names.append(bytes.get_string_from_ascii(40))
+ node.texture_names.append(bytes.get_string_from_ro(40))
node.offset_matrix = [] as Array[Vector3]
for _in in 3:
@@ -362,7 +362,7 @@ class ModelNode:
node.translate(translation_2)
if rotation_axis != Vector3.ZERO:
- node.rotation = rotation_axis * rotation_angle
+ node.rotation = (rotation_axis * rotation_angle) * Vector3(1,-1,1)
node.scale = scale
diff --git a/extractor/rsw_format.gd b/extractor/rsw_format.gd
index 85d873c..7e70850 100644
--- a/extractor/rsw_format.gd
+++ b/extractor/rsw_format.gd
@@ -122,12 +122,14 @@ func convert(name: String, data_path: String) -> Node3D:
if not FileAccess.file_exists(model_file_path):
continue
- var rsm := RSMFormat.from_bytes(ByteStream.from_bytes(FileAccess.get_file_as_bytes(model_file_path)))
+ var rsm := RSMFormat.from_bytes(ByteStream.from_bytes(
+ FileAccess.get_file_as_bytes(model_file_path)
+ ))
var model_root := Node3D.new()
model_root.name = resource.name
model_root.position = resource.get_position() * Vector3(-1, 1, 1)
- model_root.rotation_degrees = resource.get_rotation()
+ model_root.rotation_degrees = resource.get_rotation() * Vector3(-1,1,-1)
model_root.scale = resource.get_scale()
node.add_child(model_root)
@@ -397,11 +399,11 @@ class Animated3DModel extends MapResource:
static func from_bytes(bytes: ByteStream) -> Animated3DModel:
var resource = Animated3DModel.new()
- resource.name = bytes.get_string_from_ascii(40)
+ resource.name = bytes.get_string_from_ro(40)
resource.animation_type = bytes.decode_u32()
resource.animation_speed_percent = bytes.decode_float()
resource.collision_flags = bytes.decode_u32()
- resource.model_file = bytes.get_string_from_ascii(80)
+ resource.model_file = bytes.get_string_from_ro(80)
resource.root_node_name = bytes.get_string_from_utf8(80)
resource.position_x = bytes.decode_float()
resource.position_y = bytes.decode_float()
@@ -462,7 +464,7 @@ class DynamicLightSource extends MapResource:
static func from_bytes(bytes: ByteStream) -> DynamicLightSource:
var resource = DynamicLightSource.new()
- resource.name = bytes.get_string_from_ascii(80)
+ resource.name = bytes.get_string_from_ro(80)
resource.position_x = bytes.decode_float()
resource.position_y = bytes.decode_float()
resource.position_z = bytes.decode_float()
@@ -528,8 +530,8 @@ class SpatialAudioSource extends MapResource:
static func from_bytes(bytes: ByteStream) -> SpatialAudioSource:
var resource = SpatialAudioSource.new()
- resource.name = bytes.get_string_from_ascii(80)
- resource.audio_file = bytes.get_string_from_ascii(80)
+ resource.name = bytes.get_string_from_ro(80)
+ resource.audio_file = bytes.get_string_from_ro(80)
resource.position_x = bytes.decode_float()
resource.position_y = bytes.decode_float()
resource.position_z = bytes.decode_float()
@@ -593,7 +595,7 @@ class ParticleEffectEmitter extends MapResource:
static func from_bytes(bytes: ByteStream) -> ParticleEffectEmitter:
var resource = ParticleEffectEmitter.new()
- resource.name = bytes.get_string_from_ascii(80)
+ resource.name = bytes.get_string_from_ro(80)
resource.position_x = bytes.decode_float()
resource.position_y = bytes.decode_float()
resource.position_z = bytes.decode_float()
diff --git a/network/character_server.gd b/network/character_server.gd
index 60389e4..7f0ac24 100644
--- a/network/character_server.gd
+++ b/network/character_server.gd
@@ -2,8 +2,8 @@ class_name CharacterServer
extends Server
-signal logged_in(packet: CharacterServerLoginSuccessPacket)
-signal logged_in_character_list(packet: CharacterServerLoginSuccessCharacterListPacket)
+#signal logged_in(packet: CharacterServerLoginSuccessPacket)
+#signal logged_in_character_list(packet: CharacterServerLoginSuccessCharacterListPacket)
signal requested_character_list(packet: RequestCharacterListSuccessPacket)
signal selected_character(packet: CharacterSelectionSuccessPacket)
@@ -24,25 +24,13 @@ func login(account_id: int, login_id1: int, login_id2: int, gender: Constants.Ge
peer.get_data(4) # in-between packet
- var login_response_packet = await received_packet
- if login_response_packet is CharacterServerLoginSuccessPacket:
- logged_in.emit(login_response_packet)
-
- var character_list_packet = await received_packet
- if character_list_packet is CharacterServerLoginSuccessCharacterListPacket:
- logged_in_character_list.emit(character_list_packet)
-
- var character_list_size_packet = await received_packet
- var block_character_packet = await received_packet
- var pin_code_state_packet = await received_packet
-
- return {
- "login": login_response_packet,
- "character_list": character_list_packet,
- "character_list_size": character_list_size_packet,
- "block_character": block_character_packet,
- "pin_code_state": pin_code_state_packet,
- }
+ return await wait_for_all_packets([
+ CharacterServerLoginSuccessPacket,
+ CharacterServerLoginSuccessCharacterListPacket,
+ CharacterListSizePacket,
+ BlockCharacterPacket,
+ PinCodeStatePacket,
+ ], [CharacterSelectionFailedPacket])
func request_character_list():
@@ -65,6 +53,30 @@ func select_character(slot: int):
selected_character.emit(packet)
+func create_character(
+ name: String,
+ slot: int,
+ hair_color: int,
+ hair_style: int,
+ start_job: Constants.Job,
+ gender: Constants.Gender
+):
+ var create_character_packet = CreateCharacterPacket.new()
+ create_character_packet.name = name
+ create_character_packet.slot = slot
+ create_character_packet.hair_color = hair_color
+ create_character_packet.hair_style = hair_style
+ create_character_packet.start_job = start_job
+ create_character_packet.gender = gender
+
+ send(create_character_packet)
+
+ return await wait_for_packets([
+ CreateCharacterSuccessPacket,
+ CharacterCreationFailedPacket,
+ ])
+
+
func get_keep_alive_timer() -> Timer:
var character_server_keep_alive_timer = Timer.new()
character_server_keep_alive_timer.name = "CharacterServerKeepAliveTimer"
diff --git a/network/client_info.gd b/network/client_info.gd
new file mode 100644
index 0000000..45327e8
--- /dev/null
+++ b/network/client_info.gd
@@ -0,0 +1,19 @@
+class_name ClientInfo
+
+
+var file: String
+
+
+func _init(file_path: String) -> void:
+ file = FileAccess.get_file_as_string(file_path)
+
+
+
+func get_address() -> String:
+ var regex := RegEx.create_from_string("<address>(.*)<\\/address>")
+ return regex.search(file).get_string(1)
+
+
+func get_port() -> int:
+ var regex := RegEx.create_from_string("<port>(.*)<\\/port>")
+ return regex.search(file).get_string(1) as int
diff --git a/network/login_server.gd b/network/login_server.gd
index 902c477..b2e6e1b 100644
--- a/network/login_server.gd
+++ b/network/login_server.gd
@@ -2,8 +2,8 @@ class_name LoginServer
extends Server
-signal logged_in(packet: LoginServerLoginSuccessPacket)
-signal login_failed(packet: LoginFailedPacket)
+#signal logged_in(packet: LoginServerLoginSuccessPacket)
+#signal login_failed(packet: LoginFailedPacket)
@warning_ignore("shadowed_variable_base_class")
@@ -11,20 +11,18 @@ func _init(host: String, port: int = 6900) -> void:
super._init(host, port)
-func login(username: String, password: String):
+func login(username: String, password: String) -> Packet:
var login_server_login_packet = LoginServerLoginPacket.new()
login_server_login_packet.username = username
login_server_login_packet.password = password
send(login_server_login_packet)
- var packet = await received_packet
- if packet is LoginServerLoginSuccessPacket:
- logged_in.emit(packet)
- elif packet is LoginFailedPacket:
- login_failed.emit(packet)
-
- return packet
+ return await wait_for_packets([
+ LoginServerLoginSuccessPacket,
+ LoginFailedPacket,
+ LoginFailedPacket2,
+ ])
func get_keep_alive_timer() -> Timer:
@@ -32,7 +30,7 @@ func get_keep_alive_timer() -> Timer:
login_server_keep_alive_timer.name = "LoginServerKeepAliveTimer"
login_server_keep_alive_timer.autostart = true
login_server_keep_alive_timer.one_shot = false
- login_server_keep_alive_timer.wait_time = 30.0 # 60.0
+ login_server_keep_alive_timer.wait_time = 20.0 # 30.0 # 60.0
login_server_keep_alive_timer.timeout.connect(func():
var login_server_keep_alive_packet := LoginServerKeepAlivePacket.new()
diff --git a/network/network.gd b/network/network.gd
index 63de38a..736f58b 100644
--- a/network/network.gd
+++ b/network/network.gd
@@ -1,11 +1,17 @@
extends Node
+static var client_info: ClientInfo
static var login_server: LoginServer
static var character_server: CharacterServer
static var map_server: MapServer
+func _ready() -> void:
+ if FileAccess.file_exists("res://client_data/clientinfo.xml"):
+ client_info = ClientInfo.new("res://client_data/clientinfo.xml")
+
+
func _process(_delta: float) -> void:
if login_server:
login_server.listen()
diff --git a/network/server.gd b/network/server.gd
index 56e9861..b8f8319 100644
--- a/network/server.gd
+++ b/network/server.gd
@@ -84,3 +84,42 @@ func send(packet: Packet) -> Error:
func send_raw(bytes: PackedByteArray) -> Error:
return peer.put_data(bytes)
+
+
+func wait_for_packets(packet_types: Array) -> Packet:
+ var packet: Packet
+ while true:
+ packet = await received_packet
+ for type in packet_types:
+ if is_instance_of(packet, type):
+ return packet
+
+ # code path return satisfaction
+ return packet
+
+
+func wait_for_all_packets(packet_types: Array, error_packet_types: Array = []) -> Dictionary[int, Packet]:
+ var packets: Dictionary[int, Packet] = {}
+ var packet: Packet
+ var has_error := false
+ while true:
+ packet = await received_packet
+
+ # check packets
+ for type in packet_types:
+ if is_instance_of(packet, type):
+ packets.set(type.get("HEADER"), packet)
+ break
+
+ # check for errors
+ for type in error_packet_types:
+ if is_instance_of(packet, type):
+ packets.set(type.get("HEADER"), packet)
+ has_error = true
+ break
+
+ # finish if all packages received or one package errors
+ if packets.size() == packet_types.size() or has_error:
+ break
+
+ return packets
diff --git a/packets/character_server/character_creation_failed_packet.gd b/packets/character_server/character_creation_failed_packet.gd
new file mode 100644
index 0000000..8ce5b15
--- /dev/null
+++ b/packets/character_server/character_creation_failed_packet.gd
@@ -0,0 +1,19 @@
+class_name CharacterCreationFailedPacket
+extends Packet
+
+
+const HEADER := 0x006e
+const BYTE_LENGTH := 3
+
+
+## Byte Type: u8
+## Byte Length: 1
+var reason: Constants.CharacterCreationFailedReason
+
+
+static func from_bytes(bytes: PackedByteArray):
+ var packet = CharacterCreationFailedPacket.new()
+
+ packet.reason = bytes.decode_u8(2)
+
+ return packet
diff --git a/packets/character_server/create_character_success_packet.gd b/packets/character_server/create_character_success_packet.gd
new file mode 100644
index 0000000..b7f826d
--- /dev/null
+++ b/packets/character_server/create_character_success_packet.gd
@@ -0,0 +1,22 @@
+class_name CreateCharacterSuccessPacket
+extends Packet
+
+
+const HEADER := 0x0b6f
+const BYTE_LENGTH := 0
+
+
+## Byte Type: u16
+## Byte Length: 2
+var packet_length: int
+
+var character_information: CharacterInformation
+
+
+static func from_bytes(bytes: PackedByteArray):
+ var packet = CreateCharacterSuccessPacket.new()
+
+ packet.packet_length = bytes.decode_u16(2)
+ packet.character_information = CharacterInformation.from_bytes(bytes.slice(4))
+
+ return packet
diff --git a/packets/client/character_server/create_character_packet.gd b/packets/client/character_server/create_character_packet.gd
new file mode 100644
index 0000000..a368508
--- /dev/null
+++ b/packets/client/character_server/create_character_packet.gd
@@ -0,0 +1,54 @@
+class_name CreateCharacterPacket
+extends Packet
+
+
+const HEADER := 0x0a39
+const BYTE_LENGTH := 36
+
+
+## Byte Type: u8
+## Byte Length: 24
+var name: String
+
+## Byte Type: u8
+## Byte Length: 1
+var slot: int
+
+## Byte Type: u16
+## Byte Length: 2
+var hair_color: int
+
+## Byte Type: u16
+## Byte Length: 2
+var hair_style: int
+
+## Byte Type: u16
+## Byte Length: 2
+var start_job: int
+
+## Byte Type: u8
+## Byte Length: 2
+var unknown: int
+
+## Byte Type: u8
+## Byte Length: 1
+var gender: Constants.Gender
+
+
+func to_bytes():
+ var payload = PackedByteArray([])
+
+ var name_buffer = name.to_ascii_buffer()
+ name_buffer.resize(24)
+ payload.append_array(name_buffer)
+
+ payload.resize(BYTE_LENGTH - 2)
+
+ payload.encode_u8(24, slot)
+ payload.encode_u16(25, hair_color)
+ payload.encode_u16(27, hair_style)
+ payload.encode_u16(29, start_job)
+ payload.encode_u16(31, unknown)
+ payload.encode_u8(33, gender)
+
+ return get_header() + payload
diff --git a/packets/login_failed_packet.gd b/packets/login_server/login_failed_packet.gd
index 93ebf23..93ebf23 100644
--- a/packets/login_failed_packet.gd
+++ b/packets/login_server/login_failed_packet.gd
diff --git a/packets/login_server/login_failed_packet_2.gd b/packets/login_server/login_failed_packet_2.gd
new file mode 100644
index 0000000..1c4bb4a
--- /dev/null
+++ b/packets/login_server/login_failed_packet_2.gd
@@ -0,0 +1,27 @@
+## rAthena References:
+## - clif_authfail_fd
+class_name LoginFailedPacket2
+extends Packet
+
+
+const HEADER := 0x083e
+const BYTE_LENGTH := 26
+
+
+## Byte Type: u32
+## Byte Length: 4
+@warning_ignore("enum_variable_without_default")
+var reason: Constants.LoginFailedReason2
+
+## Byte Type: u8
+## Byte Length: 20
+var unblock_time: String
+
+
+static func from_bytes(bytes: PackedByteArray) -> LoginFailedPacket2:
+ var packet = LoginFailedPacket2.new()
+
+ packet.reason = bytes.decode_u32(2)
+ packet.unblock_time = bytes.slice(6, 26).get_string_from_utf8()
+
+ return packet
diff --git a/packets/login_server/login_server_login_success_packet.gd b/packets/login_server/login_server_login_success_packet.gd
index ce6a058..f1893df 100644
--- a/packets/login_server/login_server_login_success_packet.gd
+++ b/packets/login_server/login_server_login_success_packet.gd
@@ -32,7 +32,7 @@ var ip_address: PackedByteArray
## Byte Length: 26
var last_login: PackedByteArray
-## Byte Type: u8
+## Byte Type: u8 [br]
## Byte Length: 1
var gender: Constants.Gender
diff --git a/project.godot b/project.godot
index 6d281f7..5951a79 100644
--- a/project.godot
+++ b/project.godot
@@ -12,7 +12,7 @@ config_version=5
config/name="Minerva"
run/main_scene="res://ui/login.tscn"
-config/features=PackedStringArray("4.4", "Forward Plus")
+config/features=PackedStringArray("4.5", "Forward Plus")
config/icon="uid://du8c0ll5pq5ci"
[autoload]
diff --git a/sound_manager.tscn b/sound_manager.tscn
index fe577ba..d268c41 100644
--- a/sound_manager.tscn
+++ b/sound_manager.tscn
@@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://cf02havkcqt1l"]
-[ext_resource type="AudioStream" uid="uid://pibh8ld1pcq5" path="res://client_data/data/wav/¹öư¼Ò¸®.wav" id="1_g5net"]
[ext_resource type="Script" uid="uid://r3smu0o27hwj" path="res://sound_manager.gd" id="1_mdjn0"]
+[ext_resource type="AudioStream" uid="uid://dn0464j35rina" path="res://client_data/data/wav/버튼소리.wav" id="2_mdjn0"]
[node name="SoundManager" type="Node"]
script = ExtResource("1_mdjn0")
@@ -9,4 +9,4 @@ script = ExtResource("1_mdjn0")
[node name="UI" type="Node" parent="."]
[node name="SoundButtonClick" type="AudioStreamPlayer" parent="UI"]
-stream = ExtResource("1_g5net")
+stream = ExtResource("2_mdjn0")
diff --git a/ui/chat_window.tscn b/ui/chat_window.tscn
index b430fb8..5804fd0 100644
--- a/ui/chat_window.tscn
+++ b/ui/chat_window.tscn
@@ -1,8 +1,7 @@
-[gd_scene load_steps=5 format=3 uid="uid://c8uqw08hxfqlu"]
+[gd_scene load_steps=4 format=3 uid="uid://c8uqw08hxfqlu"]
[ext_resource type="Script" uid="uid://cy5bwkc4gokw1" path="res://ui/chat_window.gd" id="1_vovuq"]
-[ext_resource type="PackedScene" uid="uid://cjcm2mai50thr" path="res://ui/bmp_texture_button.tscn" id="2_2x3wj"]
-[ext_resource type="Texture2D" uid="uid://dqq3mtjcrwqnp" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/sys_base_off.bmp" id="3_smgio"]
+[ext_resource type="Texture2D" uid="uid://dwv8ca3u5xk3o" path="res://client_data/data/texture/유저인터페이스/basic_interface/sys_base_off.png" id="2_2x3wj"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ku06j"]
bg_color = Color(0.133333, 0.133333, 0.133333, 0.784314)
@@ -45,6 +44,7 @@ layout_mode = 2
[node name="ToPlayer" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
+placeholder_text = "Player"
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
@@ -52,9 +52,11 @@ layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Send Messages here"
-[node name="Handle" parent="VBoxContainer/HBoxContainer" instance=ExtResource("2_2x3wj")]
+[node name="Handle" type="TextureButton" parent="VBoxContainer/HBoxContainer"]
+texture_filter = 1
layout_mode = 2
-texture_normal = ExtResource("3_smgio")
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("2_2x3wj")
stretch_mode = 5
[connection signal="text_submitted" from="VBoxContainer/HBoxContainer/LineEdit" to="." method="_on_line_edit_text_submitted"]
diff --git a/ui/login.gd b/ui/login.gd
index 70cc52e..1c5284e 100644
--- a/ui/login.gd
+++ b/ui/login.gd
@@ -28,11 +28,16 @@ func switch_screen(screen: Node):
func _on_login_pressed() -> void:
SoundManager.sound_button_click.play()
- Network.login_server = LoginServer.new("127.0.0.1")
+ Network.login_server = LoginServer.new(Network.client_info.get_address(), Network.client_info.get_port())
Network.login_server.establish_connection()
if Network.login_server.get_status() == Error.FAILED:
- Client.show_message_window("Couldn't connect to server at 127.0.0.1", "Connection Error", %Login/Window, SIDE_BOTTOM)
+ Client.show_message_window(
+ "Couldn't connect to server at %s:%s" % [Network.login_server.host, Network.login_server.port],
+ "Connection Error",
+ %Login/Window,
+ SIDE_BOTTOM
+ )
return
var response = await Network.login_server.login(username.text, password.text)
@@ -48,6 +53,30 @@ func _on_login_pressed() -> void:
Client.show_message_window(message, "Login Error", %Login/Window, SIDE_BOTTOM)
return
+ if response is LoginFailedPacket2:
+ var message := ""
+ if response.reason == Constants.LoginFailedReason2.UnregisteredId:
+ message = "Unregistered ID"
+ elif response.reason == Constants.LoginFailedReason2.IncorrectPassword:
+ message = "Incorrect Password"
+ elif response.reason == Constants.LoginFailedReason2.IdExpired:
+ message = "ID expired"
+ elif response.reason == Constants.LoginFailedReason2.RejectedFromServer:
+ message = "Rejected from Server"
+ elif response.reason == Constants.LoginFailedReason2.BlockedByGMTeam:
+ message = "Blocked by GM Team"
+ elif response.reason == Constants.LoginFailedReason2.GameOutdated:
+ message = "Game Outdated"
+ elif response.reason == Constants.LoginFailedReason2.LoginProhibitedUntil:
+ message = "Login prohibited until %s" % response.unblock_time
+ elif response.reason == Constants.LoginFailedReason2.ServerFull:
+ message = "Server Full"
+ elif response.reason == Constants.LoginFailedReason2.CompanyAccountLimitReached:
+ message = "CompanyAccountLimitReached"
+
+ Client.show_message_window(message, "Login Error", %Login/Window, SIDE_BOTTOM)
+ return
+
account_information = response
character_server_information = account_information.character_server_information
@@ -96,10 +125,12 @@ func _on_character_server_login_pressed(character_server_info: CharacterServerIn
account_information.gender
)
- if response.login is CharacterSelectionFailedPacket:
+ if response.has(CharacterSelectionFailedPacket.HEADER):
Client.show_message_window(
- "Connection rejected from server \"%s\" at %s" % [
- character_server_info.server_name, character_server_info.get_server_ip()
+ "Connection rejected from server \"%s\" at %s:%s" % [
+ character_server_info.server_name,
+ character_server_info.get_server_ip(),
+ character_server_info.server_port
],
"Connection Error", %CharacterServer/Window, SIDE_BOTTOM
)
@@ -107,7 +138,7 @@ func _on_character_server_login_pressed(character_server_info: CharacterServerIn
get_tree().root.add_child(Network.character_server.get_keep_alive_timer())
- var character_list: CharacterServerLoginSuccessCharacterListPacket = response.character_list
+ var character_list: CharacterServerLoginSuccessCharacterListPacket = response[CharacterServerLoginSuccessCharacterListPacket.HEADER]
var login_character_list := LoginCharacterList.from_character_list_login_packet(character_list)
%CharacterSelectionList.login_character_list = login_character_list
@@ -118,11 +149,18 @@ func _on_character_server_login_pressed(character_server_info: CharacterServerIn
var info = login_character_list.get_info_for_slot(slot_idx)
if info:
current_character_information = info
+ %CharacterSelectionCreate.visible = false
+ %CharacterLogin.visible = true
else:
current_character_information = null
+ %CharacterSelectionCreate.visible = true
+ %CharacterLogin.visible = false
)
%CharacterSelectionList.requested_login.connect(func(slot_idx: int):
- _on_character_selected_pressed(slot_idx)
+ if current_character_information:
+ _on_character_selected_pressed(slot_idx)
+ else:
+ _on_character_selection_create_pressed()
)
# pre-select first character
@@ -178,5 +216,56 @@ func _on_character_selection_back_button_pressed() -> void:
switch_screen(%CharacterServer)
+func _on_character_selection_create_pressed() -> void:
+ SoundManager.sound_button_click.play()
+ switch_screen(%CharacterCreation)
+
+ %CharacterCreationInterface.strength = 5
+ %CharacterCreationInterface.agility = 5
+ %CharacterCreationInterface.vitality = 5
+ %CharacterCreationInterface.intelligence = 5
+ %CharacterCreationInterface.dexterity = 5
+ %CharacterCreationInterface.luck = 5
+
+
func _on_character_login_pressed() -> void:
_on_character_selected_pressed(current_character_slot_idx)
+
+
+func _on_character_creation_cancel_pressed() -> void:
+ SoundManager.sound_button_click.play()
+ switch_screen(%CharacterSelection)
+
+
+func _on_character_creation_create_pressed() -> void:
+ SoundManager.sound_button_click.play()
+
+ var response = await Network.character_server.create_character(
+ %CharacterCreationInterface.character_name,
+ current_character_slot_idx,
+ 0,
+ %CharacterCreationInterface.head_id,
+ Constants.Job.Novice,
+ account_information.gender
+ )
+
+ if response is CharacterCreationFailedPacket:
+ var message := ""
+ if response.reason == Constants.CharacterCreationFailedReason.CharacterNameAlreadyInUse:
+ message = "Character Name already in use"
+ elif response.reason == Constants.CharacterCreationFailedReason.NotOldEnough:
+ message = "Not old enough"
+ elif response.reason == Constants.CharacterCreationFailedReason.NotAllowedToUseSlot:
+ message = "Not allowed to use slot"
+ elif response.reason == Constants.CharacterCreationFailedReason.CharacterCreationFailed:
+ message = "Character creation failed. Reason unknown"
+
+ Client.show_message_window(message, "Character Creation Error")
+ return
+
+ # TODO: create character
+ # TODO: reload selection list data
+
+ switch_screen(%CharacterSelection)
+ %CharacterSelectionList.login_character_list.add_character_information(response.character_information)
+ print(inst_to_dict(response.character_information))
diff --git a/ui/login.tscn b/ui/login.tscn
index e889508..04f24b1 100644
--- a/ui/login.tscn
+++ b/ui/login.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=24 format=3 uid="uid://dser74lcd3a4g"]
+[gd_scene load_steps=25 format=3 uid="uid://dser74lcd3a4g"]
[ext_resource type="Script" uid="uid://dqswsdaamfhbq" path="res://ui/login.gd" id="1_1m5cv"]
[ext_resource type="Texture2D" uid="uid://cxd6dnc7s17vg" path="res://client_data/skin/login_background.jpg" id="2_elmti"]
@@ -6,22 +6,23 @@
[ext_resource type="Theme" uid="uid://c5sm3yvuakj3b" path="res://ui/theme.tres" id="3_7ogdv"]
[ext_resource type="PackedScene" uid="uid://cjcm2mai50thr" path="res://ui/bmp_texture_button.tscn" id="3_qemc0"]
[ext_resource type="PackedScene" uid="uid://c8uqw08hxfqlu" path="res://ui/chat_window.tscn" id="4_ah2a1"]
-[ext_resource type="Texture2D" uid="uid://bern5mhol3l8y" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect.bmp" id="4_wpax4"]
-[ext_resource type="Texture2D" uid="uid://c31u8nlyugk3p" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect_b.bmp" id="5_7ogdv"]
-[ext_resource type="Texture2D" uid="uid://c5kctdb8b2msx" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect_a.bmp" id="6_l3yss"]
-[ext_resource type="Texture2D" uid="uid://l3c7ssoc4lxq" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect.png" id="7_pfdi0"]
-[ext_resource type="Texture2D" uid="uid://p7bxfof7gd0t" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect_b.png" id="8_38pxr"]
-[ext_resource type="Texture2D" uid="uid://ba8j1t53lqxr0" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/btn_connect_a.png" id="9_hqeko"]
+[ext_resource type="Texture2D" uid="uid://1vrog6v0c8dd" path="res://client_data/data/texture/유저인터페이스/login_interface/btn_connect.png" id="7_8pgsx"]
+[ext_resource type="Texture2D" uid="uid://txbfc4bmwl3o" path="res://client_data/data/texture/유저인터페이스/login_interface/btn_connect_b.png" id="8_dx2ir"]
+[ext_resource type="Texture2D" uid="uid://xj061qbyw4mp" path="res://client_data/data/texture/유저인터페이스/login_interface/btn_connect_a.png" id="9_tftjj"]
[ext_resource type="Theme" uid="uid://c6y6r8kcnbb10" path="res://ui/theme_clear.tres" id="9_toei2"]
[ext_resource type="PackedScene" uid="uid://f2urhroq21t0" path="res://ui/login/character_server_button.tscn" id="10_38pxr"]
-[ext_resource type="Texture2D" uid="uid://byn26biifjkng" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_cancel.bmp" id="11_tihdy"]
-[ext_resource type="Texture2D" uid="uid://wvnt34j5mkvy" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_cancel_b.bmp" id="12_mma3j"]
-[ext_resource type="Texture2D" uid="uid://pv886kwtlrq0" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_cancel_a.bmp" id="13_0vsp3"]
-[ext_resource type="Texture2D" uid="uid://b13jh48hyjyrt" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_next.bmp" id="14_pfdi0"]
-[ext_resource type="Texture2D" uid="uid://bo277gh8uyw1w" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_next_b.bmp" id="15_38pxr"]
-[ext_resource type="Texture2D" uid="uid://qvmuk8xa3yej" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_next_a.bmp" id="16_hqeko"]
+[ext_resource type="Texture2D" uid="uid://pjerdwsogw84" path="res://client_data/data/texture/유저인터페이스/btn_cancel.png" id="11_60jnu"]
+[ext_resource type="Texture2D" uid="uid://ba3g38r086pyk" path="res://client_data/data/texture/유저인터페이스/btn_cancel_b.png" id="12_up3vp"]
+[ext_resource type="Texture2D" uid="uid://bw3j7xm53qoqe" path="res://client_data/data/texture/유저인터페이스/btn_cancel_a.png" id="13_swwvu"]
+[ext_resource type="Texture2D" uid="uid://brkbxq02xuyw8" path="res://client_data/data/texture/유저인터페이스/btn_next.png" id="14_ohrmw"]
+[ext_resource type="Texture2D" uid="uid://dweiqt26hgws2" path="res://client_data/data/texture/유저인터페이스/btn_next_b.png" id="15_c1fiv"]
+[ext_resource type="Texture2D" uid="uid://h6r0smppndhq" path="res://client_data/data/texture/유저인터페이스/btn_next_a.png" id="16_14c6s"]
[ext_resource type="PackedScene" uid="uid://bxbprntny8duj" path="res://ui/login/login_character_selection_list.tscn" id="16_tihdy"]
[ext_resource type="PackedScene" uid="uid://swtqlba1wi3o" path="res://ui/window.tscn" id="17_mma3j"]
+[ext_resource type="Texture2D" uid="uid://cgsgfo5j5lm2t" path="res://client_data/data/texture/유저인터페이스/btn_make_b.png" id="19_dx2ir"]
+[ext_resource type="Texture2D" uid="uid://bvbne1gpgvk08" path="res://client_data/data/texture/유저인터페이스/btn_make_a.png" id="20_tftjj"]
+[ext_resource type="Texture2D" uid="uid://cnhrbacvvitjq" path="res://client_data/data/texture/유저인터페이스/btn_make.png" id="22_82qbc"]
+[ext_resource type="PackedScene" uid="uid://di8inwptr42fe" path="res://ui/login/character_creation.tscn" id="24_8pgsx"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pfdi0"]
bg_color = Color(0.977834, 0.977834, 0.977834, 1)
@@ -119,9 +120,9 @@ secret_character = "*"
layout_mode = 2
size_flags_horizontal = 8
theme = ExtResource("3_7ogdv")
-texture_normal = ExtResource("7_pfdi0")
-texture_pressed = ExtResource("8_38pxr")
-texture_hover = ExtResource("9_hqeko")
+texture_normal = ExtResource("7_8pgsx")
+texture_pressed = ExtResource("8_dx2ir")
+texture_hover = ExtResource("9_tftjj")
[node name="CharacterServer" type="CenterContainer" parent="."]
unique_name_in_owner = true
@@ -169,16 +170,16 @@ layout_mode = 2
[node name="CharacterServerBackButton" parent="CharacterServer/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer" instance=ExtResource("3_qemc0")]
layout_mode = 2
size_flags_horizontal = 0
-texture_normal = ExtResource("11_tihdy")
-texture_pressed = ExtResource("12_mma3j")
-texture_hover = ExtResource("13_0vsp3")
+texture_normal = ExtResource("11_60jnu")
+texture_pressed = ExtResource("12_up3vp")
+texture_hover = ExtResource("13_swwvu")
[node name="CharacterServerNextButton" parent="CharacterServer/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer" instance=ExtResource("3_qemc0")]
layout_mode = 2
size_flags_horizontal = 10
-texture_normal = ExtResource("14_pfdi0")
-texture_pressed = ExtResource("15_38pxr")
-texture_hover = ExtResource("16_hqeko")
+texture_normal = ExtResource("14_ohrmw")
+texture_pressed = ExtResource("15_c1fiv")
+texture_hover = ExtResource("16_14c6s")
[node name="CharacterSelection" type="CenterContainer" parent="."]
unique_name_in_owner = true
@@ -294,17 +295,72 @@ layout_mode = 2
[node name="CharacterSelectionBackButton" parent="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements" index="0" instance=ExtResource("3_qemc0")]
layout_mode = 2
size_flags_horizontal = 0
-texture_normal = ExtResource("11_tihdy")
-texture_pressed = ExtResource("12_mma3j")
-texture_hover = ExtResource("13_0vsp3")
+texture_normal = ExtResource("11_60jnu")
+texture_pressed = ExtResource("12_up3vp")
+texture_hover = ExtResource("13_swwvu")
-[node name="CharacterLogin" parent="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements" index="1" instance=ExtResource("3_qemc0")]
+[node name="HBoxContainer" type="HBoxContainer" parent="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements" index="1"]
+layout_mode = 2
+size_flags_horizontal = 8
+
+[node name="CharacterSelectionCreate" type="TextureButton" parent="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer"]
+unique_name_in_owner = true
+texture_filter = 1
+layout_mode = 2
+size_flags_horizontal = 8
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("22_82qbc")
+texture_pressed = ExtResource("19_dx2ir")
+texture_hover = ExtResource("20_tftjj")
+
+[node name="CharacterLogin" parent="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer" instance=ExtResource("3_qemc0")]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 8
+texture_normal = ExtResource("7_8pgsx")
+texture_pressed = ExtResource("8_dx2ir")
+texture_hover = ExtResource("9_tftjj")
+
+[node name="CharacterCreation" type="CenterContainer" parent="."]
+unique_name_in_owner = true
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="Window" parent="CharacterCreation" instance=ExtResource("17_mma3j")]
+layout_mode = 2
+
+[node name="MarginContainer" type="MarginContainer" parent="CharacterCreation/Window/VBoxContainer/Body" index="0"]
+layout_mode = 2
+theme_override_constants/margin_left = 16
+theme_override_constants/margin_top = 16
+theme_override_constants/margin_right = 16
+theme_override_constants/margin_bottom = 16
+
+[node name="CharacterCreationInterface" parent="CharacterCreation/Window/VBoxContainer/Body/MarginContainer" instance=ExtResource("24_8pgsx")]
unique_name_in_owner = true
layout_mode = 2
+
+[node name="CharacterCreationCancel" type="TextureButton" parent="CharacterCreation/Window/VBoxContainer/ButtonBar/ButtonBarElements" index="0"]
+texture_filter = 1
+layout_mode = 2
+size_flags_horizontal = 0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("11_60jnu")
+texture_pressed = ExtResource("12_up3vp")
+texture_hover = ExtResource("13_swwvu")
+
+[node name="CharacterCreationCreate" type="TextureButton" parent="CharacterCreation/Window/VBoxContainer/ButtonBar/ButtonBarElements" index="1"]
+texture_filter = 1
+layout_mode = 2
size_flags_horizontal = 8
-texture_normal = ExtResource("4_wpax4")
-texture_pressed = ExtResource("5_7ogdv")
-texture_hover = ExtResource("6_l3yss")
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("22_82qbc")
+texture_pressed = ExtResource("19_dx2ir")
+texture_hover = ExtResource("20_tftjj")
[node name="BackgroundMusic" type="AudioStreamPlayer" parent="."]
stream = ExtResource("3_2nukd")
@@ -327,8 +383,12 @@ grow_vertical = 0
[connection signal="pressed" from="CharacterServer/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer/CharacterServerNextButton" to="." method="_on_character_server_next_button_pressed"]
[connection signal="pressed" from="CharacterSelection/VBoxContainer/CharacterSelectionBackButton" to="." method="_on_character_selection_back_button_pressed"]
[connection signal="pressed" from="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/CharacterSelectionBackButton" to="." method="_on_character_selection_back_button_pressed"]
-[connection signal="pressed" from="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/CharacterLogin" to="." method="_on_character_login_pressed"]
+[connection signal="pressed" from="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer/CharacterSelectionCreate" to="." method="_on_character_selection_create_pressed"]
+[connection signal="pressed" from="CharacterSelection/Window/VBoxContainer/ButtonBar/ButtonBarElements/HBoxContainer/CharacterLogin" to="." method="_on_character_login_pressed"]
+[connection signal="pressed" from="CharacterCreation/Window/VBoxContainer/ButtonBar/ButtonBarElements/CharacterCreationCancel" to="." method="_on_character_creation_cancel_pressed"]
+[connection signal="pressed" from="CharacterCreation/Window/VBoxContainer/ButtonBar/ButtonBarElements/CharacterCreationCreate" to="." method="_on_character_creation_create_pressed"]
[editable path="Login/Window"]
[editable path="CharacterServer/Window"]
[editable path="CharacterSelection/Window"]
+[editable path="CharacterCreation/Window"]
diff --git a/ui/login/character_creation.gd b/ui/login/character_creation.gd
new file mode 100644
index 0000000..f3989d1
--- /dev/null
+++ b/ui/login/character_creation.gd
@@ -0,0 +1,134 @@
+extends HBoxContainer
+
+
+var strength := 5:
+ set(value):
+ strength = value
+ %StatEntryStrength.value = value
+ %Polygon.polygon[0] = (
+ Vector2(100, 100) +
+ ((Vector2(100, 0) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var vitality := 5:
+ set(value):
+ vitality = value
+ %StatEntryVitality.value = value
+ %Polygon.polygon[1] = (
+ Vector2(100, 100) +
+ ((Vector2(200, 50) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var luck := 5:
+ set(value):
+ luck = value
+ %StatEntryLuck.value = value
+ %Polygon.polygon[2] = (
+ Vector2(100, 100) +
+ ((Vector2(200, 150) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var intelligence := 5:
+ set(value):
+ intelligence = value
+ %StatEntryIntelligence.value = value
+ %Polygon.polygon[3] = (
+ Vector2(100, 100) +
+ ((Vector2(100, 200) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var dexterity := 5:
+ set(value):
+ dexterity = value
+ %StatEntryDexterity.value = value
+ %Polygon.polygon[4] = (
+ Vector2(100, 100) +
+ ((Vector2(0, 150) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var agility := 5:
+ set(value):
+ agility = value
+ %StatEntryAgility.value = value
+ %Polygon.polygon[5] = (
+ Vector2(100, 100) +
+ ((Vector2(0, 50) - Vector2(100, 100)) / 9.0) * value
+ )
+
+var head_id := 1:
+ set(value):
+ head_id = value
+ %Head.texture = load(
+ "%s/%s/000.png" % [
+ "res://client_data/data/sprite",
+ Constants.FilePaths.get_player_head(Constants.Gender.Male, head_id),
+ ]
+ )
+
+var character_name: String
+
+
+func _on_strength_button_pressed() -> void:
+ if strength >= 9:
+ return
+
+ strength += 1
+ intelligence -= 1
+
+
+func _on_vitality_button_pressed() -> void:
+ if vitality >= 9:
+ return
+
+ vitality += 1
+ dexterity -= 1
+
+
+func _on_luck_button_pressed() -> void:
+ if luck >= 9:
+ return
+
+ luck += 1
+ agility -= 1
+
+
+func _on_intelligence_button_pressed() -> void:
+ if intelligence >= 9:
+ return
+
+ intelligence += 1
+ strength -= 1
+
+
+func _on_dexterity_button_pressed() -> void:
+ if dexterity >= 9:
+ return
+
+ dexterity += 1
+ vitality -= 1
+
+
+func _on_agility_button_pressed() -> void:
+ if agility >= 9:
+ return
+
+ agility += 1
+ luck -= 1
+
+
+func _on_appearance_left_button_pressed() -> void:
+ head_id = (head_id - 1) % (42 + 1)
+ # TODO: fix
+
+
+func _on_appearance_right_button_pressed() -> void:
+ head_id = (head_id + 1) % (42 + 1)
+
+
+func _on_appearance_up_button_pressed() -> void:
+ # TODO: change hair color
+ pass # Replace with function body.
+
+
+func _on_name_text_changed(new_text: String) -> void:
+ character_name = new_text
diff --git a/ui/login/character_creation.tscn b/ui/login/character_creation.tscn
new file mode 100644
index 0000000..dbecf32
--- /dev/null
+++ b/ui/login/character_creation.tscn
@@ -0,0 +1,304 @@
+[gd_scene load_steps=21 format=3 uid="uid://di8inwptr42fe"]
+
+[ext_resource type="Script" uid="uid://w10xfhscfuo0" path="res://ui/login/character_creation.gd" id="1_i6vfx"]
+[ext_resource type="Theme" uid="uid://c6y6r8kcnbb10" path="res://ui/theme_clear.tres" id="1_kuhsd"]
+[ext_resource type="Texture2D" uid="uid://687s0xq708kn" path="res://client_data/data/texture/유저인터페이스/basic_interface/arw_left.png" id="2_6pdc2"]
+[ext_resource type="Texture2D" uid="uid://0to1babdx1js" path="res://client_data/data/texture/유저인터페이스/basic_interface/arw_right.png" id="3_60s0a"]
+[ext_resource type="Texture2D" uid="uid://mvgh8pjg7qar" path="res://client_data/data/texture/유저인터페이스/basic_interface/arw_up.png" id="4_ufd68"]
+[ext_resource type="Texture2D" uid="uid://ci2liot5s8jnb" path="res://client_data/data/sprite/인간족/머리통/남/16_남/000.png" id="5_6pdc2"]
+[ext_resource type="Texture2D" uid="uid://be3ax80esna7b" path="res://client_data/data/sprite/인간족/몸통/남/초보자_남/000.png" id="6_60s0a"]
+[ext_resource type="Texture2D" uid="uid://c0kd66xiup6qq" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-str0.png" id="8_6pdc2"]
+[ext_resource type="Texture2D" uid="uid://c5k65ct7at0ns" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-str1.png" id="9_60s0a"]
+[ext_resource type="Texture2D" uid="uid://cnpgjr7mj4j14" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-vit0.png" id="10_6pdc2"]
+[ext_resource type="Texture2D" uid="uid://b4878jg8ex7tm" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-vit1.png" id="11_60s0a"]
+[ext_resource type="Texture2D" uid="uid://iviinm4jtx2j" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-luk0.png" id="12_6pdc2"]
+[ext_resource type="Texture2D" uid="uid://dx0w5jac6n12s" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-luk1.png" id="13_60s0a"]
+[ext_resource type="PackedScene" uid="uid://cff164qwvlsqd" path="res://ui/login/stat_entry.tscn" id="14_b1a0f"]
+[ext_resource type="Texture2D" uid="uid://qrudl7mwwjvq" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-int0.png" id="14_ufd68"]
+[ext_resource type="Texture2D" uid="uid://7vxidv2eb4uk" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-int1.png" id="15_uors6"]
+[ext_resource type="Texture2D" uid="uid://bd20v4ovvst4r" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-dex0.png" id="16_kbej0"]
+[ext_resource type="Texture2D" uid="uid://cyry27r8wpd7i" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-dex1.png" id="17_1iw4y"]
+[ext_resource type="Texture2D" uid="uid://cd7riu7j5lyri" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-agi0.png" id="18_225jb"]
+[ext_resource type="Texture2D" uid="uid://dcotmlbpx0iaa" path="res://client_data/data/texture/유저인터페이스/login_interface/arw-agi1.png" id="19_b1a0f"]
+
+[node name="CharacterCreation" type="HBoxContainer"]
+script = ExtResource("1_i6vfx")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="Control" type="Control" parent="VBoxContainer"]
+custom_minimum_size = Vector2(0, 20)
+layout_mode = 2
+
+[node name="AppearanceLeftButton" type="TextureButton" parent="VBoxContainer/Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 4
+anchor_top = 0.5
+anchor_bottom = 0.5
+offset_top = -5.5
+offset_right = 11.0
+offset_bottom = 5.5
+grow_vertical = 2
+scale = Vector2(1.5, 1.5)
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("2_6pdc2")
+
+[node name="AppearanceRightButton" type="TextureButton" parent="VBoxContainer/Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 6
+anchor_left = 1.0
+anchor_top = 0.5
+anchor_right = 1.0
+anchor_bottom = 0.5
+offset_left = -11.0
+offset_top = -5.5
+offset_bottom = 5.5
+grow_horizontal = 0
+grow_vertical = 2
+scale = Vector2(1.5, 1.5)
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("3_60s0a")
+
+[node name="AppearanceUpButton" type="TextureButton" parent="VBoxContainer/Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 5
+anchor_left = 0.5
+anchor_right = 0.5
+offset_left = -5.5
+offset_top = -8.0
+offset_right = 5.5
+offset_bottom = 3.0
+grow_horizontal = 2
+scale = Vector2(1.5, 1.5)
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("4_ufd68")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+theme_override_constants/separation = -8
+
+[node name="Head" type="TextureRect" parent="VBoxContainer/VBoxContainer"]
+unique_name_in_owner = true
+z_index = 1
+texture_filter = 1
+layout_mode = 2
+texture = ExtResource("5_6pdc2")
+stretch_mode = 5
+
+[node name="Body" type="TextureRect" parent="VBoxContainer/VBoxContainer"]
+texture_filter = 1
+layout_mode = 2
+texture = ExtResource("6_60s0a")
+stretch_mode = 5
+
+[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+layout_mode = 2
+
+[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
+texture_filter = 1
+layout_mode = 2
+theme = ExtResource("1_kuhsd")
+theme_type_variation = &"LabelLabel"
+text = "Name"
+
+[node name="Name" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_vertical = 4
+theme = ExtResource("1_kuhsd")
+
+[node name="Control" type="Control" parent="."]
+custom_minimum_size = Vector2(280, 280)
+layout_mode = 2
+
+[node name="Container" type="Node2D" parent="Control"]
+position = Vector2(40, 40)
+
+[node name="Polygon" type="Polygon2D" parent="Control/Container"]
+unique_name_in_owner = true
+color = Color(0.776471, 0.807843, 0.905882, 1)
+polygon = PackedVector2Array(100, 50, 148, 76, 148, 124, 100, 150, 52, 124, 52, 76)
+uv = PackedVector2Array(100, 0, 200, 50, 200, 150, 100, 200, 0, 150, 0, 75)
+
+[node name="LineCircle" type="Line2D" parent="Control/Container"]
+points = PackedVector2Array(100, 0, 200, 50, 200, 150, 100, 200, 0, 150, 0, 50)
+closed = true
+width = 1.0
+default_color = Color(0.776471, 0.807843, 0.905882, 1)
+
+[node name="LineNS" type="Line2D" parent="Control/Container"]
+points = PackedVector2Array(100, 0, 100, 200)
+closed = true
+width = 1.0
+default_color = Color(0.776471, 0.807843, 0.905882, 1)
+
+[node name="LineNESW" type="Line2D" parent="Control/Container"]
+points = PackedVector2Array(200, 50, 0, 150)
+closed = true
+width = 1.0
+default_color = Color(0.776471, 0.807843, 0.905882, 1)
+
+[node name="LineNWSE" type="Line2D" parent="Control/Container"]
+points = PackedVector2Array(0, 50, 200, 150)
+closed = true
+width = 1.0
+default_color = Color(0.776471, 0.807843, 0.905882, 1)
+
+[node name="StrengthButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 5
+anchor_left = 0.5
+anchor_right = 0.5
+offset_left = -19.0
+offset_top = 1.0
+offset_right = 21.0
+offset_bottom = 41.0
+grow_horizontal = 2
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("8_6pdc2")
+texture_pressed = ExtResource("9_60s0a")
+
+[node name="VitalityButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 1
+anchor_left = 1.0
+anchor_right = 1.0
+offset_left = -42.9999
+offset_top = 67.0
+offset_right = -2.99994
+offset_bottom = 107.0
+grow_horizontal = 0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("10_6pdc2")
+texture_pressed = ExtResource("11_60s0a")
+
+[node name="LuckButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 3
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = -44.0
+offset_top = -101.0
+offset_right = -4.0
+offset_bottom = -61.0
+grow_horizontal = 0
+grow_vertical = 0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("12_6pdc2")
+texture_pressed = ExtResource("13_60s0a")
+
+[node name="IntelligenceButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 7
+anchor_left = 0.5
+anchor_top = 1.0
+anchor_right = 0.5
+anchor_bottom = 1.0
+offset_left = -18.0
+offset_top = -40.0
+offset_right = 22.0
+grow_horizontal = 2
+grow_vertical = 0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("14_ufd68")
+texture_pressed = ExtResource("15_uors6")
+
+[node name="DexterityButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+anchors_preset = 2
+anchor_top = 1.0
+anchor_bottom = 1.0
+offset_left = 9.0
+offset_top = -104.0
+offset_right = 49.0
+offset_bottom = -64.0
+grow_vertical = 0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("16_kbej0")
+texture_pressed = ExtResource("17_1iw4y")
+
+[node name="AgilityButton" type="TextureButton" parent="Control"]
+texture_filter = 1
+layout_mode = 1
+offset_left = 8.0
+offset_top = 67.0
+offset_right = 48.0
+offset_bottom = 107.0
+mouse_default_cursor_shape = 2
+texture_normal = ExtResource("18_225jb")
+texture_pressed = ExtResource("19_b1a0f")
+
+[node name="VBoxContainer2" type="VBoxContainer" parent="."]
+layout_mode = 2
+theme_override_constants/separation = 1
+
+[node name="StatEntryStrength" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "STR"
+value = 5
+value_alignment = 1
+
+[node name="StatEntryAgility" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "AGI"
+value = 5
+value_alignment = 1
+
+[node name="StatEntryVitality" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "VIT"
+value = 5
+value_alignment = 1
+
+[node name="StatEntryIntelligence" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "INT"
+value = 5
+value_alignment = 1
+
+[node name="StatEntryDexterity" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "DEX"
+value = 5
+value_alignment = 1
+
+[node name="StatEntryLuck" parent="VBoxContainer2" instance=ExtResource("14_b1a0f")]
+unique_name_in_owner = true
+layout_mode = 2
+theme_override_constants/separation = 0
+label = "LUK"
+value = 5
+value_alignment = 1
+
+[connection signal="pressed" from="VBoxContainer/Control/AppearanceLeftButton" to="." method="_on_appearance_left_button_pressed"]
+[connection signal="pressed" from="VBoxContainer/Control/AppearanceRightButton" to="." method="_on_appearance_right_button_pressed"]
+[connection signal="pressed" from="VBoxContainer/Control/AppearanceUpButton" to="." method="_on_appearance_up_button_pressed"]
+[connection signal="text_changed" from="VBoxContainer/HBoxContainer/Name" to="." method="_on_name_text_changed"]
+[connection signal="pressed" from="Control/StrengthButton" to="." method="_on_strength_button_pressed"]
+[connection signal="pressed" from="Control/VitalityButton" to="." method="_on_vitality_button_pressed"]
+[connection signal="pressed" from="Control/LuckButton" to="." method="_on_luck_button_pressed"]
+[connection signal="pressed" from="Control/IntelligenceButton" to="." method="_on_intelligence_button_pressed"]
+[connection signal="pressed" from="Control/DexterityButton" to="." method="_on_dexterity_button_pressed"]
+[connection signal="pressed" from="Control/AgilityButton" to="." method="_on_agility_button_pressed"]
diff --git a/ui/login/character_selection_item.tscn b/ui/login/character_selection_item.tscn
index de28392..0dab44c 100644
--- a/ui/login/character_selection_item.tscn
+++ b/ui/login/character_selection_item.tscn
@@ -1,10 +1,10 @@
[gd_scene load_steps=7 format=3 uid="uid://rrd131rq74n5"]
[ext_resource type="Script" uid="uid://bsglhorusc7ug" path="res://ui/login/character_selection_item.gd" id="1_25yur"]
-[ext_resource type="Texture2D" uid="uid://danymuvfjf4o1" path="res://client_data/data/sprite/Àΰ£Á·/¸Ó¸®Åë/³²/16_³²/000.png" id="2_aqbfs"]
-[ext_resource type="Texture2D" uid="uid://cwqgdd00sf7pu" path="res://client_data/data/sprite/Àΰ£Á·/¸öÅë/³²/Ãʺ¸ÀÚ_³²/000.png" id="3_xv3pn"]
+[ext_resource type="Texture2D" uid="uid://ci2liot5s8jnb" path="res://client_data/data/sprite/인간족/머리통/남/16_남/000.png" id="2_u21ok"]
+[ext_resource type="Texture2D" uid="uid://be3ax80esna7b" path="res://client_data/data/sprite/인간족/몸통/남/초보자_남/000.png" id="3_vhrt2"]
[ext_resource type="PackedScene" uid="uid://knmmuhon34rh" path="res://ui/bmp_texture_rect.tscn" id="4_vhrt2"]
-[ext_resource type="Texture2D" uid="uid://1s8lrydoctbv" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/box_select.png" id="5_vhrt2"]
+[ext_resource type="Texture2D" uid="uid://bfe86fb6ytqg3" path="res://client_data/data/texture/유저인터페이스/login_interface/box_select.png" id="5_vhrt2"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u21ok"]
bg_color = Color(0, 0, 0, 0.0980392)
@@ -43,14 +43,14 @@ unique_name_in_owner = true
z_index = 1
texture_filter = 1
layout_mode = 2
-texture = ExtResource("2_aqbfs")
+texture = ExtResource("2_u21ok")
stretch_mode = 3
[node name="Body" type="TextureRect" parent="MarginContainer/VBoxContainer/Sprite"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
-texture = ExtResource("3_xv3pn")
+texture = ExtResource("3_vhrt2")
stretch_mode = 3
[node name="SelectionBorder" parent="." instance=ExtResource("4_vhrt2")]
diff --git a/ui/login/login_character_selection_list.gd b/ui/login/login_character_selection_list.gd
index bd19fa1..7ff43cf 100644
--- a/ui/login/login_character_selection_list.gd
+++ b/ui/login/login_character_selection_list.gd
@@ -76,6 +76,11 @@ func set_login_character_list(value: LoginCharacterList) -> void:
draw()
+func add_character_information(info: CharacterInformation) -> void:
+ login_character_list.add_character_information(info)
+ draw()
+
+
func draw():
if not login_character_list:
return
diff --git a/ui/login/login_character_selection_list.tscn b/ui/login/login_character_selection_list.tscn
index 02a9dc2..373dcde 100644
--- a/ui/login/login_character_selection_list.tscn
+++ b/ui/login/login_character_selection_list.tscn
@@ -4,12 +4,12 @@
[ext_resource type="Script" uid="uid://dmch4gi1khn2r" path="res://ui/login/login_character_selection_list.gd" id="1_togb6"]
[ext_resource type="PackedScene" uid="uid://rrd131rq74n5" path="res://ui/login/character_selection_item.tscn" id="2_k142l"]
[ext_resource type="PackedScene" uid="uid://cjcm2mai50thr" path="res://ui/bmp_texture_button.tscn" id="2_s7n6r"]
-[ext_resource type="Texture2D" uid="uid://cey66pn46da80" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_l_out.png" id="3_wnv6e"]
-[ext_resource type="Texture2D" uid="uid://bbs77ppnvcm23" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_l_press.png" id="4_rdaw5"]
-[ext_resource type="Texture2D" uid="uid://c4supu7w8gabw" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_l_over.png" id="5_1vggc"]
-[ext_resource type="Texture2D" uid="uid://dqweltumn8n7t" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_r_out.png" id="7_wnv6e"]
-[ext_resource type="Texture2D" uid="uid://bsdygckmj667w" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_r_press.png" id="8_rdaw5"]
-[ext_resource type="Texture2D" uid="uid://dqdmks4uak1go" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/select_character/chr_arrow_r_over.png" id="9_1vggc"]
+[ext_resource type="Texture2D" uid="uid://c1cgkiyacvr4k" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_l_out.png" id="3_wnv6e"]
+[ext_resource type="Texture2D" uid="uid://dar08umwslovr" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_l_press.png" id="4_wnv6e"]
+[ext_resource type="Texture2D" uid="uid://b38efa2wsrsiw" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_l_over.png" id="5_wnv6e"]
+[ext_resource type="Texture2D" uid="uid://cjmj1etf1wth7" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_r_out.png" id="7_1vggc"]
+[ext_resource type="Texture2D" uid="uid://deorq2yjwe4ie" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_r_press.png" id="8_1oc8k"]
+[ext_resource type="Texture2D" uid="uid://ccfm5ls854xpf" path="res://client_data/data/texture/유저인터페이스/styleshop/chr_arrow_r_over.png" id="9_muwiy"]
[node name="LoginCharacterSelectionList" type="VBoxContainer"]
theme_override_constants/separation = 32
@@ -31,8 +31,8 @@ unique_name_in_owner = true
texture_filter = 0
layout_mode = 2
texture_normal = ExtResource("3_wnv6e")
-texture_pressed = ExtResource("4_rdaw5")
-texture_hover = ExtResource("5_1vggc")
+texture_pressed = ExtResource("4_wnv6e")
+texture_hover = ExtResource("5_wnv6e")
stretch_mode = 5
[node name="CharacterList" type="HBoxContainer" parent="MarginContainer/HBoxContainer"]
@@ -50,9 +50,9 @@ layout_mode = 2
unique_name_in_owner = true
texture_filter = 0
layout_mode = 2
-texture_normal = ExtResource("7_wnv6e")
-texture_pressed = ExtResource("8_rdaw5")
-texture_hover = ExtResource("9_1vggc")
+texture_normal = ExtResource("7_1vggc")
+texture_pressed = ExtResource("8_1oc8k")
+texture_hover = ExtResource("9_muwiy")
stretch_mode = 5
[node name="CharacterSelectionStatus" parent="." instance=ExtResource("1_5anyi")]
diff --git a/ui/login/stat_entry.gd b/ui/login/stat_entry.gd
new file mode 100644
index 0000000..6d79ac0
--- /dev/null
+++ b/ui/login/stat_entry.gd
@@ -0,0 +1,18 @@
+@tool
+extends HBoxContainer
+
+
+@export var label: String:
+ set(value):
+ label = value
+ $Label.text = value
+
+@export var value: int:
+ set(v):
+ value = v
+ $Value.text = str(v)
+
+@export var value_alignment: HorizontalAlignment:
+ set(value):
+ value_alignment = value
+ $Value.horizontal_alignment = value
diff --git a/ui/login/stat_entry.tscn b/ui/login/stat_entry.tscn
new file mode 100644
index 0000000..91e9abe
--- /dev/null
+++ b/ui/login/stat_entry.tscn
@@ -0,0 +1,24 @@
+[gd_scene load_steps=3 format=3 uid="uid://cff164qwvlsqd"]
+
+[ext_resource type="Theme" uid="uid://c6y6r8kcnbb10" path="res://ui/theme_clear.tres" id="1_3i1yo"]
+[ext_resource type="Script" uid="uid://bjdgrc1kwcbtj" path="res://ui/login/stat_entry.gd" id="1_vt1a4"]
+
+[node name="StatEntry" type="HBoxContainer"]
+offset_right = 169.0
+offset_bottom = 20.0
+script = ExtResource("1_vt1a4")
+
+[node name="Label" type="Label" parent="."]
+custom_minimum_size = Vector2(45, 0)
+layout_mode = 2
+theme = ExtResource("1_3i1yo")
+theme_type_variation = &"CharacterSelectionStatusLabel"
+text = "Label"
+
+[node name="Value" type="Label" parent="."]
+custom_minimum_size = Vector2(120, 0)
+layout_mode = 2
+size_flags_horizontal = 3
+theme = ExtResource("1_3i1yo")
+theme_type_variation = &"CharacterSelectionStatusLabel2"
+text = "value"
diff --git a/ui/message_window.tscn b/ui/message_window.tscn
index 5c195bf..b46fbe3 100644
--- a/ui/message_window.tscn
+++ b/ui/message_window.tscn
@@ -4,9 +4,9 @@
[ext_resource type="PackedScene" uid="uid://cjcm2mai50thr" path="res://ui/bmp_texture_button.tscn" id="2_3gmlp"]
[ext_resource type="Theme" uid="uid://c6y6r8kcnbb10" path="res://ui/theme_clear.tres" id="2_prcjf"]
[ext_resource type="Script" uid="uid://o36qsov00kwb" path="res://ui/message_window.gd" id="2_rr7kw"]
-[ext_resource type="Texture2D" uid="uid://b1yjeotua8xj5" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/btn_close.bmp" id="3_prcjf"]
-[ext_resource type="Texture2D" uid="uid://d4mycn8dag5yl" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/inventory/btn_close_press.bmp" id="4_rr7kw"]
-[ext_resource type="Texture2D" uid="uid://ds70r0x5vvy6t" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/btn_close_a.bmp" id="5_ql6vf"]
+[ext_resource type="Texture2D" uid="uid://b0o61r0oau4k7" path="res://client_data/data/texture/유저인터페이스/btn_close.png" id="5_m5dfh"]
+[ext_resource type="Texture2D" uid="uid://6sx7uu2xjc4" path="res://client_data/data/texture/유저인터페이스/btn_close_b.png" id="6_m5dfh"]
+[ext_resource type="Texture2D" uid="uid://bdxjoywe2tdif" path="res://client_data/data/texture/유저인터페이스/btn_close_a.png" id="7_a7gey"]
[node name="MessageWindow" instance=ExtResource("1_m7647")]
offset_right = 63.0
@@ -40,8 +40,8 @@ text = "Message"
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 8
-texture_normal = ExtResource("3_prcjf")
-texture_pressed = ExtResource("4_rr7kw")
-texture_hover = ExtResource("5_ql6vf")
+texture_normal = ExtResource("5_m5dfh")
+texture_pressed = ExtResource("6_m5dfh")
+texture_hover = ExtResource("7_a7gey")
[connection signal="pressed" from="VBoxContainer/ButtonBar/ButtonBarElements/CloseButtonBottom" to="." method="_on_close_button_bottom_pressed"]
diff --git a/ui/theme.tres b/ui/theme.tres
index 69ad428..a2a513d 100644
--- a/ui/theme.tres
+++ b/ui/theme.tres
@@ -1,29 +1,20 @@
-[gd_resource type="Theme" load_steps=9 format=3 uid="uid://c5sm3yvuakj3b"]
-
-[ext_resource type="Texture2D" uid="uid://34bqfgx41tf0" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/bt_otp_over.png" id="1_iqtc0"]
-[ext_resource type="Texture2D" uid="uid://cehi7txcq4p2q" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/name-edit.bmp" id="1_rqugq"]
-[ext_resource type="Texture2D" uid="uid://b63yrv7hceon4" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/bt_otp_normal.png" id="2_7any1"]
-[ext_resource type="Texture2D" uid="uid://yew2ev1s10pk" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/login_interface/bt_otp_press.png" id="3_3fgq6"]
+[gd_resource type="Theme" load_steps=5 format=3 uid="uid://c5sm3yvuakj3b"]
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_y5ki2"]
-texture = ExtResource("1_iqtc0")
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_rtqkv"]
content_margin_left = 4.0
content_margin_top = 2.0
content_margin_right = 4.0
content_margin_bottom = 2.0
-texture = ExtResource("2_7any1")
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_47c11"]
-texture = ExtResource("3_3fgq6")
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_jf26r"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
-texture = ExtResource("1_rqugq")
[resource]
Button/colors/font_color = Color(0.133333, 0.133333, 0.133333, 1)
diff --git a/ui/theme_clear.tres b/ui/theme_clear.tres
index f5028a8..7fa57f0 100644
--- a/ui/theme_clear.tres
+++ b/ui/theme_clear.tres
@@ -52,7 +52,7 @@ content_margin_left = 3.0
content_margin_top = 0.0
content_margin_right = 3.0
content_margin_bottom = 0.0
-bg_color = Color(0.94902, 0.94902, 0.94902, 1)
+bg_color = Color(0.980392, 0.980392, 0.980392, 1)
border_width_left = 1
border_width_top = 1
border_width_right = 1
diff --git a/ui/window.tscn b/ui/window.tscn
index d27d7c5..f0c3d95 100644
--- a/ui/window.tscn
+++ b/ui/window.tscn
@@ -2,17 +2,17 @@
[ext_resource type="PackedScene" uid="uid://knmmuhon34rh" path="res://ui/bmp_texture_rect.tscn" id="1_4qpm3"]
[ext_resource type="Script" uid="uid://qdg2tjk8xmjt" path="res://ui/window.gd" id="1_hfgic"]
-[ext_resource type="Texture2D" uid="uid://22px3uxdg48t" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/titlebar_left.png" id="3_74c65"]
-[ext_resource type="Texture2D" uid="uid://dc2f7tx5xsico" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/titlebar_mid.bmp" id="3_mngfx"]
-[ext_resource type="Texture2D" uid="uid://dkqx0rjvi6aba" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/titlebar_right.png" id="5_qkejn"]
-[ext_resource type="Texture2D" uid="uid://clqwl83uoxeue" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/btnbar_mid1.bmp" id="6_mnaxm"]
-[ext_resource type="Texture2D" uid="uid://dmuuq7siyfv4r" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/sys_base_off.png" id="6_uftm1"]
+[ext_resource type="Texture2D" uid="uid://b40mcjrv0d0gi" path="res://client_data/data/texture/유저인터페이스/basic_interface/titlebar_left.png" id="3_74c65"]
+[ext_resource type="Texture2D" uid="uid://b3d3jmq8eso2v" path="res://client_data/data/texture/유저인터페이스/basic_interface/titlebar_mid.png" id="4_qkejn"]
+[ext_resource type="Texture2D" uid="uid://d22mmihwnjj0c" path="res://client_data/data/texture/유저인터페이스/basic_interface/titlebar_right.png" id="5_uftm1"]
+[ext_resource type="Texture2D" uid="uid://dwv8ca3u5xk3o" path="res://client_data/data/texture/유저인터페이스/basic_interface/sys_base_off.png" id="6_74c65"]
[ext_resource type="PackedScene" uid="uid://cjcm2mai50thr" path="res://ui/bmp_texture_button.tscn" id="7_m42e5"]
-[ext_resource type="Texture2D" uid="uid://dtaegqi7vx4ia" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/big_sys_close_off.png" id="8_vjbfl"]
-[ext_resource type="Texture2D" uid="uid://jwguf7fj0iyx" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/big_sys_close_on.png" id="9_kh8ey"]
-[ext_resource type="Texture2D" uid="uid://dif01o7ri7qop" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/btnbar_left1.png" id="10_ug6hj"]
-[ext_resource type="Texture2D" uid="uid://bp60ikxqixf6q" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/btnbar_right1.png" id="12_xgidv"]
-[ext_resource type="Texture2D" uid="uid://ch1t3k18kvhtk" path="res://client_data/data/texture/À¯ÀúÀÎÅÍÆäÀ̽º/basic_interface/btn_comparison_resize.png" id="13_sy0l2"]
+[ext_resource type="Texture2D" uid="uid://dt2t71nhoaelo" path="res://client_data/data/texture/유저인터페이스/basic_interface/big_sys_close_off.png" id="8_74c65"]
+[ext_resource type="Texture2D" uid="uid://dguxqcl61xj50" path="res://client_data/data/texture/유저인터페이스/basic_interface/big_sys_close_on.png" id="9_qkejn"]
+[ext_resource type="Texture2D" uid="uid://k78jsflcoc84" path="res://client_data/data/texture/유저인터페이스/basic_interface/btnbar_left1.png" id="10_74c65"]
+[ext_resource type="Texture2D" uid="uid://bn66ekthd5657" path="res://client_data/data/texture/유저인터페이스/basic_interface/btnbar_mid1.png" id="11_qkejn"]
+[ext_resource type="Texture2D" uid="uid://bwi568g2qyf3o" path="res://client_data/data/texture/유저인터페이스/basic_interface/btnbar_right1.png" id="12_uftm1"]
+[ext_resource type="Texture2D" uid="uid://cygyjjv5s6dx0" path="res://client_data/data/texture/유저인터페이스/basic_interface/btn_comparison_resize.png" id="13_74c65"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fufdc"]
bg_color = Color(1, 1, 1, 1)
@@ -45,11 +45,11 @@ texture = ExtResource("3_74c65")
[node name="Middle" parent="VBoxContainer/TitleBar/TitleBarBackground" instance=ExtResource("1_4qpm3")]
layout_mode = 2
size_flags_horizontal = 3
-texture = ExtResource("3_mngfx")
+texture = ExtResource("4_qkejn")
[node name="Right" parent="VBoxContainer/TitleBar/TitleBarBackground" instance=ExtResource("1_4qpm3")]
layout_mode = 2
-texture = ExtResource("5_qkejn")
+texture = ExtResource("5_uftm1")
[node name="TitleBarElements" type="MarginContainer" parent="VBoxContainer/TitleBar"]
unique_name_in_owner = true
@@ -67,15 +67,15 @@ theme_override_constants/margin_right = 2
[node name="BMPTextureRect" parent="VBoxContainer/TitleBar/TitleBarButtons" instance=ExtResource("1_4qpm3")]
layout_mode = 2
size_flags_horizontal = 0
-texture = ExtResource("6_uftm1")
+texture = ExtResource("6_74c65")
stretch_mode = 3
[node name="CloseButton" parent="VBoxContainer/TitleBar/TitleBarButtons" instance=ExtResource("7_m42e5")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 8
-texture_normal = ExtResource("8_vjbfl")
-texture_hover = ExtResource("9_kh8ey")
+texture_normal = ExtResource("8_74c65")
+texture_hover = ExtResource("9_qkejn")
stretch_mode = 5
[node name="Body" type="PanelContainer" parent="VBoxContainer"]
@@ -94,16 +94,16 @@ theme_override_constants/separation = 0
[node name="BMPTextureRect" parent="VBoxContainer/ButtonBar/ButtonBarBackground" instance=ExtResource("1_4qpm3")]
layout_mode = 2
-texture = ExtResource("10_ug6hj")
+texture = ExtResource("10_74c65")
[node name="BMPTextureRect2" parent="VBoxContainer/ButtonBar/ButtonBarBackground" instance=ExtResource("1_4qpm3")]
layout_mode = 2
size_flags_horizontal = 3
-texture = ExtResource("6_mnaxm")
+texture = ExtResource("11_qkejn")
[node name="BMPTextureRect3" parent="VBoxContainer/ButtonBar/ButtonBarBackground" instance=ExtResource("1_4qpm3")]
layout_mode = 2
-texture = ExtResource("12_xgidv")
+texture = ExtResource("12_uftm1")
[node name="ButtonBarElements" type="MarginContainer" parent="VBoxContainer/ButtonBar"]
unique_name_in_owner = true
@@ -120,7 +120,7 @@ layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 8
mouse_default_cursor_shape = 2
-texture = ExtResource("13_sy0l2")
+texture = ExtResource("13_74c65")
stretch_mode = 5
[connection signal="gui_input" from="VBoxContainer/TitleBar" to="." method="_on_title_bar_gui_input"]