完善客户端功能并同步差距文档
This commit is contained in:
+97
-21
@@ -14,9 +14,15 @@
|
||||
# 时装(costume):Metin2 不发独立的 costume 槽包,服务器把 costume 的 vnum 直接写进
|
||||
# 主角实体的 parts[] 数组(ARMOR/HAIR)。所以每个渲染槽优先取 parts[],为 0 才回退
|
||||
# get_equipment()。这样 costume「自动」生效,也是渲染别的玩家用的同一条路。
|
||||
#
|
||||
# §4.1 修改 4(部位遮挡):解出身体 shape 后过 PartHiding —— 动物 / 怪物时装
|
||||
# (shape 100-103)抑制武器 + 头发;婚纱(shape 201)非捧花时抑制武器;变身
|
||||
# (seam,暂无实体字段)会把身体 shape 归 0 且清武器 / 头发。刷新顺序对齐
|
||||
# InstanceBase.cpp ChangeArmor:身体 → 头发 → 武器(→ 盾)。
|
||||
extends Node
|
||||
|
||||
const RaceSpec = preload("res://ui/race_spec.gd")
|
||||
const PartHiding = preload("res://part_hiding.gd")
|
||||
|
||||
const WEAR_BODY := 0
|
||||
const WEAR_HEAD := 1
|
||||
@@ -40,10 +46,12 @@ var _spec: RefCounted # RaceSpec(惰性)
|
||||
var _spec_tried := false
|
||||
var _last_weapon_vnum := -1
|
||||
var _last_body_vnum := -1
|
||||
var _last_head_vnum := -1
|
||||
var _last_head_vnum := -2 # -1 是「发型被遮挡」的有效态,用 -2 当「未应用」哨兵
|
||||
var _last_shield_vnum := -1
|
||||
var _last_hair_part := -1
|
||||
var main_getter: Callable = Callable() # func() -> int:主角 vid(取 parts 用)
|
||||
var _remote_mode := false
|
||||
var _remote_parts: Array = [] # 远端 PC 的 awPart[ARMOR..HAIR]
|
||||
# armor vnum -> race .msm 的 shape index。默认:有 proto 用 item_proto values[3]
|
||||
# (对齐客户端 __ArmorVnumToShape,SHAPE_VALUE_SLOT_INDEX=3),否则回退 =vnum。应用方可覆写。
|
||||
var armor_shape_of: Callable = func(vnum: int) -> int: return _armor_shape_default(vnum)
|
||||
@@ -70,6 +78,24 @@ func setup(m2client: Node, il: RefCounted, model_getter: Callable, assets := "",
|
||||
if main_getter.is_valid() and int(main_getter.call()) == v:
|
||||
refresh())
|
||||
|
||||
# 远端 PC 不读取本地 get_equipment();参考端直接消费
|
||||
# GC_CHAR_ADDITIONAL_INFO / GC_CHARACTER_UPDATE 的 awPart[4]。
|
||||
func setup_remote(il: RefCounted, model_getter: Callable, assets := "",
|
||||
race_index := -1, parts := [], proto_node: Node = null) -> void:
|
||||
client = null
|
||||
item_list = il
|
||||
_model_getter = model_getter
|
||||
assets_root = assets
|
||||
race = race_index
|
||||
proto = proto_node
|
||||
_remote_mode = true
|
||||
_remote_parts = parts.duplicate() if parts is Array else []
|
||||
while _remote_parts.size() < 4:
|
||||
_remote_parts.append(0)
|
||||
if _remote_parts.size() > 4:
|
||||
_remote_parts = _remote_parts.slice(0, 4)
|
||||
refresh()
|
||||
|
||||
func set_race(r: int) -> void:
|
||||
race = r
|
||||
_spec = null
|
||||
@@ -81,36 +107,55 @@ func _on_inv_changed(window: int, _cell: int) -> void:
|
||||
|
||||
func refresh() -> void:
|
||||
var model: Node = _model_getter.call() if _model_getter.is_valid() else null
|
||||
if model == null or client == null:
|
||||
if model == null or (client == null and not _remote_mode):
|
||||
return
|
||||
var eq: Array = client.get_equipment()
|
||||
if eq.size() <= WEAR_WEAPON:
|
||||
var eq: Array = client.get_equipment() if client != null and client.has_method("get_equipment") else []
|
||||
if not _remote_mode and eq.size() <= WEAR_WEAPON:
|
||||
return
|
||||
var parts := _parts() # 主角 parts[](时装 / 别的玩家用);空 -> 全回退 eq
|
||||
|
||||
# 武器:parts[WEAPON] 优先,为 0 回退 WEAR_WEAPON
|
||||
var wpn_vnum := _eff(parts, PART_WEAPON, eq, WEAR_WEAPON)
|
||||
if wpn_vnum != _last_weapon_vnum:
|
||||
_last_weapon_vnum = wpn_vnum
|
||||
model.set("weapon_gr2", _resolve_weapon(wpn_vnum))
|
||||
|
||||
if eq.size() > WEAR_SHIELD:
|
||||
var sh_vnum := int(eq[WEAR_SHIELD].get("vnum", 0))
|
||||
if sh_vnum != _last_shield_vnum:
|
||||
_last_shield_vnum = sh_vnum
|
||||
model.set("shield_gr2", _resolve_weapon(sh_vnum))
|
||||
|
||||
# 身体:parts[ARMOR] 优先(= 普通盔甲或时装),为 0 回退 WEAR_BODY
|
||||
# --- 身体:parts[ARMOR] 优先(普通盔甲或时装),为 0 回退 WEAR_BODY -------------
|
||||
var body_vnum := _eff(parts, PART_ARMOR, eq, WEAR_BODY)
|
||||
# 遮挡判定用的 shape(对齐 __ArmorVnumToShape:proto values[3] 非 0,否则 =vnum)
|
||||
var raw_shape := int(armor_shape_of.call(body_vnum)) if armor_shape_of.is_valid() else body_vnum
|
||||
var is_poly := _is_poly()
|
||||
if body_vnum != _last_body_vnum:
|
||||
_last_body_vnum = body_vnum
|
||||
_apply_body(model, body_vnum)
|
||||
|
||||
# 头:parts[HEAD] 优先,为 0 回退 WEAR_HEAD;有模型的头盔覆盖发型槽,否则用发型
|
||||
# --- 头 / 发:动物·怪物时装(shape 100-103)或变身 -> 隐藏;否则头盔覆盖 / 发型 ----
|
||||
var hair_off := PartHiding.hair_hidden(raw_shape, is_poly)
|
||||
var head_vnum := _eff(parts, PART_HEAD, eq, WEAR_HEAD)
|
||||
if head_vnum != _last_head_vnum:
|
||||
_last_head_vnum = head_vnum
|
||||
_apply_head_or_hair(model, head_vnum)
|
||||
var head_key := -1 if hair_off else head_vnum
|
||||
if head_key != _last_head_vnum:
|
||||
_last_head_vnum = head_key
|
||||
if hair_off:
|
||||
model.set("hair_gr2", "")
|
||||
model.set("hair_skin", "")
|
||||
_last_hair_part = -1 # 恢复时强制重挂发型
|
||||
else:
|
||||
_apply_head_or_hair(model, head_vnum)
|
||||
|
||||
# --- 武器:parts[WEAPON] 优先,为 0 回退 WEAR_WEAPON;过 PartHiding 遮挡 ---------
|
||||
var wpn_vnum := _eff(parts, PART_WEAPON, eq, WEAR_WEAPON)
|
||||
var wpn_eff := PartHiding.effective_weapon(wpn_vnum, raw_shape, is_poly)
|
||||
if wpn_eff != _last_weapon_vnum:
|
||||
_last_weapon_vnum = wpn_eff
|
||||
model.set("weapon_gr2", _resolve_weapon(wpn_eff))
|
||||
|
||||
# --- 盾(左手挂点,同武器遮挡规则)--------------------------------------------
|
||||
if eq.size() > WEAR_SHIELD:
|
||||
var sh_vnum := int(eq[WEAR_SHIELD].get("vnum", 0))
|
||||
var sh_eff := 0 if PartHiding.weapon_hidden(raw_shape, is_poly, sh_vnum) else sh_vnum
|
||||
if sh_eff != _last_shield_vnum:
|
||||
_last_shield_vnum = sh_eff
|
||||
model.set("shield_gr2", _resolve_weapon(sh_eff))
|
||||
|
||||
# §4.1 修改 4 seam:40250 实体表暂无变身字段(m2_client.cpp entity dict 无 poly_race /
|
||||
# polymorph)。一旦 GC_CHARACTER_UPDATE / GC_CHAR_ADD_INFO 带上变身 race,这里改成读
|
||||
# client.get_entity(main_getter.call()).get("poly_race", 0) != 0 即可。
|
||||
func _is_poly() -> bool:
|
||||
return false
|
||||
|
||||
# 有效 vnum:主角 parts[part_idx] 非 0 就用它(时装 / 远端玩家),否则用装备槽
|
||||
func _eff(parts: Array, part_idx: int, eq: Array, wear_slot: int) -> int:
|
||||
@@ -119,6 +164,8 @@ func _eff(parts: Array, part_idx: int, eq: Array, wear_slot: int) -> int:
|
||||
return int(eq[wear_slot].get("vnum", 0)) if eq.size() > wear_slot else 0
|
||||
|
||||
func _parts() -> Array:
|
||||
if _remote_mode:
|
||||
return _remote_parts
|
||||
if not main_getter.is_valid() or client == null or not client.has_method("get_entity"):
|
||||
return []
|
||||
var vid: int = main_getter.call()
|
||||
@@ -164,6 +211,8 @@ func _apply_hair_from_parts(model: Node, force := false) -> void:
|
||||
model.set("hair_skin", ts)
|
||||
|
||||
func _hair_part() -> int:
|
||||
if _remote_mode:
|
||||
return int(_remote_parts[PART_HAIR]) if _remote_parts.size() > PART_HAIR else -1
|
||||
if not main_getter.is_valid() or client == null or not client.has_method("get_entity"):
|
||||
return -1
|
||||
var vid: int = main_getter.call()
|
||||
@@ -176,6 +225,10 @@ func _hair_part() -> int:
|
||||
return -1
|
||||
|
||||
func set_hair(index: int) -> void:
|
||||
if _remote_mode:
|
||||
_set_remote_part(PART_HAIR, index)
|
||||
refresh()
|
||||
return
|
||||
var model: Node = _model_getter.call() if _model_getter.is_valid() else null
|
||||
if model == null:
|
||||
return
|
||||
@@ -189,6 +242,29 @@ func set_hair(index: int) -> void:
|
||||
if ts != "":
|
||||
model.set("hair_skin", ts)
|
||||
|
||||
func set_armor(index: int) -> void:
|
||||
if not _remote_mode:
|
||||
return
|
||||
_set_remote_part(PART_ARMOR, index)
|
||||
refresh()
|
||||
|
||||
func set_weapon(index: int) -> void:
|
||||
if not _remote_mode:
|
||||
return
|
||||
_set_remote_part(PART_WEAPON, index)
|
||||
refresh()
|
||||
|
||||
func set_head(index: int) -> void:
|
||||
if not _remote_mode:
|
||||
return
|
||||
_set_remote_part(PART_HEAD, index)
|
||||
refresh()
|
||||
|
||||
func _set_remote_part(part: int, value: int) -> void:
|
||||
while _remote_parts.size() <= part:
|
||||
_remote_parts.append(0)
|
||||
_remote_parts[part] = value
|
||||
|
||||
# --- body ------------------------------------------------------------------
|
||||
|
||||
# armor vnum -> item_proto values[3](非 0);否则 =vnum(对齐 __ArmorVnumToShape)
|
||||
|
||||
Reference in New Issue
Block a user