Metin2 game client (P0–P11) + mobile asset pipeline

Networked client on the existing Godot 4.7 + libgr2 renderer:
- net: m2dev wire protocol (libsodium KX + XChaCha20), auth/select/game
  phases, EntityStore world model, ~all GC/CG headers. char create/delete,
  private shop / mall / cube, SHOP_GC_START_EX, guild, party (+ CG_PARTY_SET_STATE),
  quests, dragon soul, refine, safebox, exchange.
- UI: in-game windows migrated 1:1 from the reference uiscript/root .py —
  char status (/stat), inventory+equipment, select-item ([SELECT_ITEM] quest
  token), system-option + game-option + ESC system menu, private-shop 39-grid,
  party info board, shop tabs, atlas, minimap, quickbar, chat, …
- EterGrnLib polish: GR2 material blend/two-sided, LOD crossfade, motion-event
  dispatch, contact shadow, ray-AABB picking, weapon grip pre-transform.

Portable asset IO (A1) — all extension/libgr2/formats/mtproto reads routed
through godot::FileAccess (res:// PCK works on iOS/Android); standalone-lib
*_path() kept for the non-Godot CTests. AssetResolver + PropertyRegistry
switched to a baked index (bake_asset_index.gd) instead of std::filesystem.

Mobile builds: build-{android,ios}.sh, export-android.sh, pack-assets.sh,
gen-debug-keystore.sh. Assets ship as a zip mounted at runtime by
project/asset_pack.gd (adb push now; HTTP download is a drop-in later).

ctest 10/10, 34 GDScript suites, macOS/iOS/Android all build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EJxkHiNKS4kybHS3XKyAJ
This commit is contained in:
shen
2026-08-31 20:02:12 +09:00
co-authored by Claude Sonnet 5
parent f4917a2b3b
commit 47baf6c0c6
414 changed files with 69568 additions and 385 deletions
+84
View File
@@ -0,0 +1,84 @@
# Dialogs (P1) —— 基于 UiManager 的通用弹窗:confirm / alert / input。
#
# var dlg := preload("res://ui/dialogs.gd").new(ui_manager) # ui_manager: UiManager
# dlg.confirm("确定要删除吗?", func(): do_delete())
# dlg.alert("已保存")
# dlg.input("输入名字", func(t): rename(t), "默认值")
#
# 走 UiManager.open(root, modal=true)ESC / 取消 关闭,回调只在确认时触发。
extends RefCounted
var ui: CanvasLayer # UiManager
func _init(ui_manager: CanvasLayer) -> void:
ui = ui_manager
func alert(text: String, on_close: Callable = Callable()) -> void:
var d := _panel(320, 130)
_label(d, text, 20, 24, 280)
_button(d, "确定", 130, 88, func():
ui.close(d)
if on_close.is_valid(): on_close.call())
ui.open(d, true)
func confirm(text: String, on_ok: Callable, on_cancel: Callable = Callable()) -> void:
var d := _panel(340, 140)
_label(d, text, 20, 22, 300)
_button(d, "确定", 60, 96, func():
ui.close(d)
if on_ok.is_valid(): on_ok.call())
_button(d, "取消", 190, 96, func():
ui.close(d)
if on_cancel.is_valid(): on_cancel.call())
ui.open(d, true)
func input(prompt: String, on_submit: Callable, default_text := "") -> void:
var d := _panel(360, 150)
_label(d, prompt, 20, 18, 320)
var le := LineEdit.new()
le.text = default_text
le.position = Vector2(20, 52)
le.size = Vector2(320, 28)
d.add_child(le)
_button(d, "确定", 70, 104, func():
var t := le.text
ui.close(d)
if on_submit.is_valid(): on_submit.call(t))
_button(d, "取消", 200, 104, func(): ui.close(d))
le.grab_focus()
ui.open(d, true)
# --- 小工具 ---------------------------------------------------------------
func _panel(w: int, h: int) -> Control:
var p := Panel.new()
p.custom_minimum_size = Vector2(w, h)
p.size = Vector2(w, h)
p.position = Vector2((1920 - w) / 2.0, (1080 - h) / 2.0)
var sb := StyleBoxFlat.new()
sb.bg_color = Color(0.11, 0.12, 0.15, 0.97)
sb.border_color = Color(0.4, 0.36, 0.28)
sb.set_border_width_all(1)
sb.set_corner_radius_all(4)
p.add_theme_stylebox_override("panel", sb)
p.set_meta("is_titlebar", true) # 整块可拖
return p
func _label(parent: Control, text: String, x: int, y: int, w: int) -> Label:
var l := Label.new()
l.text = text
l.position = Vector2(x, y)
l.size = Vector2(w, 0)
l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
parent.add_child(l)
return l
func _button(parent: Control, text: String, x: int, y: int, on_press: Callable) -> Button:
var b := Button.new()
b.text = text
b.position = Vector2(x, y)
b.size = Vector2(90, 26)
b.pressed.connect(on_press)
parent.add_child(b)
return b