647 lines
19 KiB
GDScript
647 lines
19 KiB
GDScript
extends Node3D
|
|
## Player/Body look loader. PASTE 2026-09-12 22:54 — 1P always gun-out eye ride
|
|
## 8-way = yaw CreatedBody toward the stick + Walk/Run.
|
|
## No AnimationTree. Waist fold AFTER clip via SkeletonModifier3D.
|
|
|
|
const MALE_FBX := "res://character assets/All together/FBX/Humans_Master.fbx"
|
|
const FEMALE_FBX := "res://character assets/All together/FBX women/All together.fbx"
|
|
|
|
const CLIP_ALIASES := {
|
|
"Idle": ["Idle"],
|
|
"Walk": ["Walk"],
|
|
"Run": ["Run"],
|
|
"Run_Back": ["Run_Back", "RunBack"],
|
|
"Run_Left": ["Run_Left", "RunLeft"],
|
|
"Run_Right": ["Run_Right", "RunRight"],
|
|
"Jump": ["Jump"],
|
|
"Interact": ["Interact", "Pickup", "PickUp"],
|
|
"Idle_Gun": ["Idle_Gun", "Idle_Gun_Pointing", "Gun_Idle"],
|
|
"AimIdle": ["Idle_Gun_Pointing", "Idle_Gun", "Gun_Idle", "Aim_Idle"],
|
|
"AimWalk": ["Walk_Shoot", "WalkShoot", "Gun_Walk", "Walk_Gun", "Walk"],
|
|
"Walk_Shoot": ["Walk_Shoot", "WalkShoot", "Gun_Walk", "Walk"],
|
|
"AimRun": ["Run_Shoot", "RunShoot", "Gun_Run", "Run_Gun", "Run"],
|
|
"Run_Shoot": ["Run_Shoot", "RunShoot", "Run"],
|
|
"Gun_Shoot": ["Gun_Shoot", "Pistol_Shoot", "Shoot", "Idle_Gun_Shoot"],
|
|
"Idle_Gun_Shoot": ["Idle_Gun_Shoot", "Gun_Shoot", "Idle_Gun_Pointing"],
|
|
}
|
|
|
|
var current: Node3D = null
|
|
var anim: AnimationPlayer = null
|
|
var last_keep: Array[String] = []
|
|
var _logged_names: bool = false
|
|
var hide_head: bool = false
|
|
var show_pistol: bool = false
|
|
|
|
var _skel: Skeleton3D = null
|
|
var _bone_abdomen: int = -1
|
|
var _bone_torso: int = -1
|
|
var _bone_chest: int = -1
|
|
var _bone_head: int = -1
|
|
var _bone_rarm: int = -1
|
|
var _bone_larm: int = -1
|
|
var _bone_rfore: int = -1
|
|
var _bone_lfore: int = -1
|
|
var _aim_pitch: float = 0.0
|
|
var _aim_enabled: bool = false
|
|
var _logged_bones: bool = false
|
|
var _mod: SkeletonModifier3D = null
|
|
var _dbg: float = 0.0
|
|
|
|
var tree: AnimationTree = null
|
|
var _walk_bs: AnimationNodeBlendSpace2D = null
|
|
var _run_bs: AnimationNodeBlendSpace2D = null
|
|
var _aim_walk_bs: AnimationNodeBlendSpace2D = null
|
|
var _aim_run_bs: AnimationNodeBlendSpace2D = null
|
|
var _mode: String = ""
|
|
var _blend_pos: Vector2 = Vector2.ZERO
|
|
var _run_amt: float = 0.0
|
|
var _aiming: bool = false
|
|
var _fire_lock: float = 0.0
|
|
var _ride_fp: bool = false
|
|
var _fp_cam: Camera3D = null
|
|
var _fp_pivot: Node3D = null
|
|
var arm_axis_id: int = 2
|
|
|
|
|
|
class AimBend extends SkeletonModifier3D:
|
|
var host: Node
|
|
func _process_modification() -> void:
|
|
if host and host.has_method("_apply_waist_now"):
|
|
host._apply_waist_now()
|
|
|
|
|
|
func apply_look(appearance: Dictionary) -> AnimationPlayer:
|
|
_clear_old()
|
|
var sex := str(appearance.get("sex", "male"))
|
|
var path := MALE_FBX if sex == "male" else FEMALE_FBX
|
|
if not ResourceLoader.exists(path):
|
|
push_warning("Look file missing: " + path)
|
|
return null
|
|
var packed = load(path)
|
|
if packed == null:
|
|
return null
|
|
var inst: Node = packed.instantiate()
|
|
if not (inst is Node3D):
|
|
return null
|
|
current = inst as Node3D
|
|
current.name = "CreatedBody"
|
|
current.position = Vector3.ZERO
|
|
current.rotation_degrees = Vector3.ZERO
|
|
current.scale = Vector3.ONE * 1.3
|
|
add_child(current)
|
|
last_keep = [
|
|
str(appearance.get("head", "Casual_Head")),
|
|
str(appearance.get("body", "Casual_Body")),
|
|
str(appearance.get("legs", "Casual_Legs")),
|
|
str(appearance.get("feet", "Casual_Feet")),
|
|
]
|
|
anim = current.find_child("AnimationPlayer", true, false) as AnimationPlayer
|
|
if anim:
|
|
anim.speed_scale = 1.0
|
|
anim.root_motion_track = NodePath()
|
|
_loop_locomotion(anim)
|
|
print("Look clips: ", anim.get_animation_list())
|
|
_cache_skeleton()
|
|
_build_loco_tree()
|
|
_apply_keep(current, last_keep)
|
|
call_deferred("_apply_keep_deferred")
|
|
return anim
|
|
|
|
|
|
func _resolve_clip(logical: String) -> String:
|
|
if anim == null:
|
|
return ""
|
|
var names: Array = CLIP_ALIASES.get(logical, [logical])
|
|
var candidates: Array[String] = []
|
|
for n in names:
|
|
candidates.append("CharacterArmature|%s" % n)
|
|
candidates.append(str(n))
|
|
for clip in candidates:
|
|
if anim.has_animation(clip):
|
|
return clip
|
|
for clip in anim.get_animation_list():
|
|
for n in names:
|
|
if clip.ends_with(str(n)) or clip.contains(str(n)):
|
|
return clip
|
|
return ""
|
|
|
|
|
|
func play_clip(logical: String, speed_scale: float = 1.0) -> void:
|
|
if anim == null:
|
|
return
|
|
if tree:
|
|
tree.active = false
|
|
var clip := _resolve_clip(logical)
|
|
if clip.is_empty():
|
|
return
|
|
var a: Animation = anim.get_animation(clip)
|
|
if a:
|
|
var looped := logical in [
|
|
"Idle", "Walk", "Run", "Run_Back", "Run_Left", "Run_Right",
|
|
"Idle_Gun", "AimIdle", "AimWalk", "AimRun", "Walk_Shoot", "Run_Shoot"
|
|
]
|
|
a.loop_mode = Animation.LOOP_LINEAR if looped else Animation.LOOP_NONE
|
|
anim.speed_scale = speed_scale
|
|
if anim.current_animation != clip:
|
|
anim.play(clip)
|
|
elif not anim.is_playing():
|
|
anim.play(clip)
|
|
_apply_keep(current, last_keep)
|
|
|
|
|
|
func _loop_locomotion(player: AnimationPlayer) -> void:
|
|
for logical in [
|
|
"Idle", "Walk", "Run", "Run_Back", "Run_Left", "Run_Right",
|
|
"Idle_Gun", "AimIdle", "AimWalk", "AimRun", "Walk_Shoot", "Run_Shoot"
|
|
]:
|
|
var clip := _resolve_clip(logical)
|
|
if clip.is_empty():
|
|
continue
|
|
var a: Animation = player.get_animation(clip)
|
|
if a:
|
|
a.loop_mode = Animation.LOOP_LINEAR
|
|
|
|
|
|
func _clip_node(logical: String) -> AnimationNodeAnimation:
|
|
var n := AnimationNodeAnimation.new()
|
|
var clip := _resolve_clip(logical)
|
|
if clip.is_empty():
|
|
clip = _resolve_clip("Idle")
|
|
if clip.is_empty():
|
|
clip = logical
|
|
n.animation = StringName(clip)
|
|
n.resource_name = logical
|
|
return n
|
|
|
|
|
|
func _add_pt(bs: AnimationNodeBlendSpace2D, logical: String, pos: Vector2, label: String) -> void:
|
|
var node := _clip_node(logical)
|
|
bs.add_blend_point(node, pos)
|
|
var idx: int = bs.get_blend_point_count() - 1
|
|
if bs.has_method("set_blend_point_name"):
|
|
bs.call("set_blend_point_name", idx, StringName(label))
|
|
|
|
|
|
func _make_space(_idle: String, fwd: String, back: String, left: String, right: String) -> AnimationNodeBlendSpace2D:
|
|
var bs := AnimationNodeBlendSpace2D.new()
|
|
# Discrete = one whole clip. Interpolating Walk with Run_Left is clown shoes.
|
|
bs.blend_mode = AnimationNodeBlendSpace2D.BLEND_MODE_DISCRETE_CARRY
|
|
bs.min_space = Vector2(-1.0, -1.0)
|
|
bs.max_space = Vector2(1.0, 1.0)
|
|
bs.snap = Vector2(0.0, 0.0)
|
|
_add_pt(bs, fwd, Vector2(0.0, -1.0), "fwd")
|
|
_add_pt(bs, back, Vector2(0.0, 1.0), "back")
|
|
_add_pt(bs, left, Vector2(-1.0, 0.0), "left")
|
|
_add_pt(bs, right, Vector2(1.0, 0.0), "right")
|
|
_add_pt(bs, fwd, Vector2(-0.71, -0.71), "fwd_left")
|
|
_add_pt(bs, fwd, Vector2(0.71, -0.71), "fwd_right")
|
|
_add_pt(bs, back, Vector2(-0.71, 0.71), "back_left")
|
|
_add_pt(bs, back, Vector2(0.71, 0.71), "back_right")
|
|
return bs
|
|
|
|
|
|
func _build_loco_tree() -> void:
|
|
# No AnimationTree. Quaternius has no Walk_Left/Walk_Right — blending
|
|
# Walk with Run_Left is clown shoes. One clip per octant instead.
|
|
if tree and is_instance_valid(tree):
|
|
tree.queue_free()
|
|
tree = null
|
|
_walk_bs = null
|
|
_run_bs = null
|
|
_aim_walk_bs = null
|
|
_aim_run_bs = null
|
|
_mode = "idle"
|
|
_blend_pos = Vector2.ZERO
|
|
_run_amt = 0.0
|
|
print("Loco clips walk=", _resolve_clip("Walk"),
|
|
" left=", _resolve_clip("Run_Left"),
|
|
" right=", _resolve_clip("Run_Right"),
|
|
" back=", _resolve_clip("Run_Back"),
|
|
" run=", _resolve_clip("Run"),
|
|
" aimIdle=", _resolve_clip("AimIdle"))
|
|
|
|
|
|
func play_fire(aiming_now: bool = false) -> void:
|
|
var logical: String = "Idle_Gun_Shoot" if aiming_now else "Gun_Shoot"
|
|
play_clip(logical)
|
|
var clip := _resolve_clip(logical)
|
|
var dur: float = 0.26
|
|
if anim and not clip.is_empty() and anim.has_animation(clip):
|
|
dur = maxf(0.16, anim.get_animation(clip).length * 0.82)
|
|
_fire_lock = dur
|
|
|
|
|
|
func set_loco(strafe: float, back: float, run_amount: float, delta: float = 0.016, aiming: bool = false) -> void:
|
|
_aiming = aiming
|
|
if tree and tree.active:
|
|
tree.active = false
|
|
if _fire_lock > 0.0:
|
|
_fire_lock = maxf(_fire_lock - delta, 0.0)
|
|
if aiming:
|
|
_face_move(0.0, delta, 14.0)
|
|
return
|
|
if aiming:
|
|
_face_move(0.0, delta, 14.0)
|
|
var aim_stick := Vector2(clampf(strafe, -1.0, 1.0), clampf(back, -1.0, 1.0))
|
|
_blend_pos = aim_stick.normalized() if aim_stick.length() >= 0.16 else Vector2.ZERO
|
|
_run_amt = run_amount if aim_stick.length() >= 0.16 else 0.0
|
|
if aim_stick.length() < 0.16:
|
|
_mode = "aim_idle"
|
|
play_clip("AimIdle")
|
|
elif run_amount > 0.5:
|
|
_mode = "aim_run"
|
|
play_clip("AimRun")
|
|
else:
|
|
_mode = "aim_walk"
|
|
play_clip("AimWalk")
|
|
return
|
|
var target := Vector2(clampf(strafe, -1.0, 1.0), clampf(back, -1.0, 1.0))
|
|
if target.length() < 0.16:
|
|
_face_move(0.0, delta, 10.0)
|
|
_blend_pos = Vector2.ZERO
|
|
_run_amt = 0.0
|
|
if show_pistol or hide_head:
|
|
_mode = "idle_gun"
|
|
play_clip("Idle_Gun")
|
|
else:
|
|
_mode = "idle"
|
|
play_clip("Idle")
|
|
return
|
|
_blend_pos = target.normalized()
|
|
_run_amt = 1.0 if run_amount > 0.5 else 0.0
|
|
# S / SA / SD — backpedal, no 180 spin
|
|
if _blend_pos.y > 0.2:
|
|
_face_move(0.0, delta, 12.0)
|
|
_mode = "Run_Back"
|
|
play_clip("Run_Back")
|
|
return
|
|
# Pure A / D — sidestep, keep facing camera-forward
|
|
if absf(_blend_pos.x) >= 0.62 and absf(_blend_pos.y) <= 0.42:
|
|
_face_move(0.0, delta, 14.0)
|
|
var side: String = "Run_Left" if _blend_pos.x < 0.0 else "Run_Right"
|
|
_mode = side
|
|
play_clip(side)
|
|
return
|
|
# W / WA / WD — face the stick + Walk/Run
|
|
var yaw := atan2(-_blend_pos.x, maxf(-_blend_pos.y, 0.001))
|
|
yaw = clampf(yaw, deg_to_rad(-90.0), deg_to_rad(90.0))
|
|
_face_move(yaw, delta, 16.0)
|
|
var logical: String = "Run" if _run_amt > 0.5 else "Walk"
|
|
_mode = logical
|
|
var pace: float = 0.92 + _run_amt * 0.18 if logical == "Run" else 0.88 + target.length() * 0.14
|
|
play_clip(logical, pace)
|
|
|
|
|
|
func _face_move(yaw: float, delta: float, rate: float) -> void:
|
|
if current == null or not is_instance_valid(current):
|
|
return
|
|
current.rotation.y = lerp_angle(current.rotation.y, yaw, clampf(rate * delta, 0.0, 1.0))
|
|
|
|
|
|
func set_first_person(fp: bool) -> void:
|
|
hide_head = fp
|
|
print("Look 1P=", fp, " idle=Idle_Gun" if fp else " idle=Idle")
|
|
if current != null and is_instance_valid(current):
|
|
_apply_keep(current, last_keep)
|
|
|
|
|
|
func set_pistol_drawn(drawn: bool) -> void:
|
|
show_pistol = drawn
|
|
if current != null and is_instance_valid(current):
|
|
_apply_keep(current, last_keep)
|
|
|
|
|
|
func set_meshes_visible(v: bool) -> void:
|
|
if current == null:
|
|
return
|
|
if v:
|
|
_apply_keep(current, last_keep)
|
|
else:
|
|
_hide_all_visuals(current)
|
|
|
|
|
|
func set_aim_pitch(radians: float) -> void:
|
|
if not _aim_enabled:
|
|
_aim_pitch = 0.0
|
|
return
|
|
_aim_pitch = clampf(radians, deg_to_rad(-50.0), deg_to_rad(65.0))
|
|
|
|
|
|
func get_head_global_pos() -> Vector3:
|
|
if _skel == null or not is_instance_valid(_skel) or _bone_head < 0:
|
|
return Vector3.INF
|
|
var pose: Transform3D = _skel.get_bone_global_pose(_bone_head)
|
|
return _skel.to_global(pose.origin)
|
|
|
|
|
|
func set_fp_ride(cam: Camera3D, pivot: Node3D, on: bool) -> void:
|
|
_fp_cam = cam
|
|
_fp_pivot = pivot
|
|
_ride_fp = on and cam != null
|
|
|
|
|
|
func set_aim_enabled(on: bool) -> void:
|
|
_aim_enabled = on
|
|
if not on:
|
|
_aim_pitch = 0.0
|
|
if _mod:
|
|
_mod.active = on
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
_dbg += delta
|
|
if _dbg < 4.0:
|
|
return
|
|
_dbg = 0.0
|
|
print("Loco dbg mode=", _mode,
|
|
" pos=", snapped(_blend_pos, Vector2(0.01, 0.01)),
|
|
" run=", snappedf(_run_amt, 0.01),
|
|
" aim=", _aiming,
|
|
" player=", anim.current_animation if anim else "?")
|
|
if _aim_enabled:
|
|
print("AimFold on pitch=", snappedf(rad_to_deg(_aim_pitch), 0.1),
|
|
" abd=", _bone_name(_bone_abdomen),
|
|
" torso=", _bone_name(_bone_torso),
|
|
" chest=", _bone_name(_bone_chest),
|
|
" mod=", _mod != null, " active=", _mod.active if _mod else false)
|
|
|
|
|
|
func _cache_skeleton() -> void:
|
|
_skel = null
|
|
_bone_abdomen = -1
|
|
_bone_torso = -1
|
|
_bone_chest = -1
|
|
_bone_head = -1
|
|
_bone_rarm = -1
|
|
_bone_larm = -1
|
|
_bone_rfore = -1
|
|
_bone_lfore = -1
|
|
_mod = null
|
|
if current == null:
|
|
return
|
|
_skel = current.find_child("Skeleton3D", true, false) as Skeleton3D
|
|
if _skel == null:
|
|
push_warning("Look: no Skeleton3D on CreatedBody")
|
|
return
|
|
_bone_abdomen = _find_exact(["Abdomen", "mixamorig_Spine", "mixamorig:Spine", "Spine"])
|
|
_bone_torso = _find_exact(["Torso", "mixamorig_Spine1", "mixamorig:Spine1", "Spine1"])
|
|
_bone_chest = _find_exact(["Chest", "mixamorig_Spine2", "mixamorig:Spine2", "Spine2", "UpperChest"])
|
|
_bone_head = _find_exact(["Head", "mixamorig_Head", "mixamorig:Head"])
|
|
_bone_rarm = _find_limb(["mixamorig_RightArm", "mixamorig:RightArm", "RightArm", "Arm.R"])
|
|
_bone_larm = _find_limb(["mixamorig_LeftArm", "mixamorig:LeftArm", "LeftArm", "Arm.L"])
|
|
_bone_rfore = _find_limb(["mixamorig_RightForeArm", "mixamorig:RightForeArm", "RightForeArm", "ForeArm.R"])
|
|
_bone_lfore = _find_limb(["mixamorig_LeftForeArm", "mixamorig:LeftForeArm", "LeftForeArm", "ForeArm.L"])
|
|
_mod = AimBend.new()
|
|
_mod.name = "AimBend"
|
|
_mod.host = self
|
|
_mod.active = false
|
|
_skel.add_child(_mod)
|
|
if not _logged_bones:
|
|
_logged_bones = true
|
|
print("Look skeleton=", _skel.name,
|
|
" abd=", _bone_name(_bone_abdomen),
|
|
" torso=", _bone_name(_bone_torso),
|
|
" chest=", _bone_name(_bone_chest),
|
|
" head=", _bone_name(_bone_head),
|
|
" rarm=", _bone_name(_bone_rarm),
|
|
" larm=", _bone_name(_bone_larm),
|
|
" modifier=AimBend")
|
|
|
|
|
|
func _find_exact(names: Array[String]) -> int:
|
|
if _skel == null:
|
|
return -1
|
|
for bone_key in names:
|
|
var idx: int = _skel.find_bone(bone_key)
|
|
if idx >= 0:
|
|
var low := _skel.get_bone_name(idx).to_lower()
|
|
var want_head := "head" in bone_key.to_lower()
|
|
if "shoulder" in low or "arm" in low or "neck" in low:
|
|
continue
|
|
if "head" in low and not want_head:
|
|
continue
|
|
return idx
|
|
return -1
|
|
|
|
|
|
func _find_limb(names: Array[String]) -> int:
|
|
if _skel == null:
|
|
return -1
|
|
for bone_key in names:
|
|
var idx: int = _skel.find_bone(bone_key)
|
|
if idx >= 0:
|
|
return idx
|
|
for i in range(_skel.get_bone_count()):
|
|
var raw: String = _skel.get_bone_name(i)
|
|
var low: String = raw.to_lower()
|
|
for bone_key in names:
|
|
var want: String = bone_key.to_lower()
|
|
if low == want or low.contains(want):
|
|
return i
|
|
return -1
|
|
|
|
|
|
func _bone_name(idx: int) -> String:
|
|
if _skel == null or idx < 0:
|
|
return str(idx)
|
|
return "%s(%d)" % [_skel.get_bone_name(idx), idx]
|
|
|
|
|
|
func _apply_waist_now() -> void:
|
|
if _skel == null:
|
|
return
|
|
if _aim_enabled:
|
|
var pitch: float = -_aim_pitch
|
|
if hide_head:
|
|
# 1P: lean the whole torso so the gun and shoes enter the lens.
|
|
_set_fold(_bone_abdomen, pitch * 0.40)
|
|
_set_fold(_bone_torso, pitch * 0.22)
|
|
_set_fold(_bone_chest, pitch * 0.08)
|
|
var axis: Vector3 = Vector3.FORWARD
|
|
match arm_axis_id:
|
|
0:
|
|
axis = Vector3.RIGHT
|
|
1:
|
|
axis = Vector3.UP
|
|
_:
|
|
axis = Vector3.FORWARD
|
|
var arm_amt: float = 0.22
|
|
if _aim_pitch > 0.0:
|
|
arm_amt = 0.36
|
|
_set_fold_axis(_bone_rarm, pitch * arm_amt, axis)
|
|
_set_fold_axis(_bone_larm, pitch * arm_amt * 0.55, axis)
|
|
else:
|
|
_set_fold(_bone_abdomen, pitch * 0.42)
|
|
_set_fold(_bone_torso, pitch * 0.38)
|
|
_set_fold(_bone_chest, pitch * 0.28)
|
|
_snap_fp_eye()
|
|
|
|
|
|
func _snap_fp_eye() -> void:
|
|
if not _ride_fp or _fp_pivot == null or _skel == null or _bone_head < 0:
|
|
return
|
|
var pose: Transform3D = _skel.get_bone_global_pose(_bone_head)
|
|
var xf: Transform3D = _skel.global_transform * pose
|
|
var body: Node3D = _fp_pivot.get_parent() as Node3D
|
|
var face := Vector3.FORWARD
|
|
var up := Vector3.UP
|
|
if body:
|
|
face = -body.global_transform.basis.z
|
|
face.y = 0.0
|
|
if face.length_squared() < 0.0001:
|
|
face = -body.global_transform.basis.z
|
|
face = face.normalized()
|
|
up = body.global_transform.basis.y.normalized()
|
|
else:
|
|
face = xf.basis.z.normalized()
|
|
up = xf.basis.y.normalized()
|
|
_fp_pivot.global_position = xf.origin + face * 0.03 + up * 0.12
|
|
|
|
|
|
func _set_fold(idx: int, angle: float) -> void:
|
|
_set_fold_axis(idx, angle, Vector3.RIGHT)
|
|
|
|
|
|
func _set_fold_axis(idx: int, angle: float, axis: Vector3) -> void:
|
|
if idx < 0 or _skel == null:
|
|
return
|
|
if absf(angle) < 0.0005:
|
|
return
|
|
var pose: Quaternion = _skel.get_bone_pose_rotation(idx)
|
|
_skel.set_bone_pose_rotation(idx, pose * Quaternion(axis.normalized(), angle))
|
|
|
|
|
|
func cycle_arm_axis() -> void:
|
|
arm_axis_id = (arm_axis_id + 1) % 3
|
|
print("ARM AXIS ", ["X (RIGHT)", "Y (UP)", "Z (FORWARD)"][arm_axis_id])
|
|
|
|
|
|
func _apply_keep_deferred() -> void:
|
|
if current != null and is_instance_valid(current):
|
|
_apply_keep(current, last_keep)
|
|
if _skel == null:
|
|
_cache_skeleton()
|
|
|
|
|
|
func _clear_old() -> void:
|
|
current = null
|
|
anim = null
|
|
if tree and is_instance_valid(tree):
|
|
tree.queue_free()
|
|
tree = null
|
|
_walk_bs = null
|
|
_run_bs = null
|
|
_aim_walk_bs = null
|
|
_aim_run_bs = null
|
|
_mode = ""
|
|
_blend_pos = Vector2.ZERO
|
|
_run_amt = 0.0
|
|
_aiming = false
|
|
_fire_lock = 0.0
|
|
_skel = null
|
|
_mod = null
|
|
_bone_abdomen = -1
|
|
_bone_torso = -1
|
|
_bone_chest = -1
|
|
_bone_head = -1
|
|
_bone_rarm = -1
|
|
_bone_larm = -1
|
|
_bone_rfore = -1
|
|
_bone_lfore = -1
|
|
_logged_bones = false
|
|
_aim_enabled = false
|
|
_aim_pitch = 0.0
|
|
_ride_fp = false
|
|
_fp_cam = null
|
|
_fp_pivot = null
|
|
for child in get_children():
|
|
child.hide()
|
|
child.queue_free()
|
|
|
|
|
|
func _hide_all_visuals(root: Node) -> void:
|
|
var stack: Array[Node] = [root]
|
|
while stack.size() > 0:
|
|
var n: Node = stack.pop_back()
|
|
if n is GeometryInstance3D:
|
|
n.visible = false
|
|
for c in n.get_children():
|
|
stack.append(c)
|
|
|
|
|
|
func _apply_keep(root: Node, keep: Array[String]) -> void:
|
|
if root == null or keep.is_empty():
|
|
return
|
|
var shown: PackedStringArray = []
|
|
var hidden: PackedStringArray = []
|
|
var stack: Array[Node] = [root]
|
|
while stack.size() > 0:
|
|
var n: Node = stack.pop_back()
|
|
if n is GeometryInstance3D:
|
|
var lab := _label(n)
|
|
var keep_this := _matches_keep(n, keep) and not _is_banned(lab)
|
|
if show_pistol and _is_pistol_label(lab):
|
|
keep_this = true
|
|
if hide_head and _is_head_label(lab):
|
|
keep_this = false
|
|
n.visible = keep_this
|
|
if keep_this:
|
|
shown.append(_label(n))
|
|
else:
|
|
hidden.append(_label(n))
|
|
for c in n.get_children():
|
|
stack.append(c)
|
|
if not _logged_names:
|
|
_logged_names = true
|
|
print("Look keep=", keep)
|
|
print("Look SHOWN (", shown.size(), "): ", shown)
|
|
print("Look HIDDEN (", hidden.size(), "): ", hidden)
|
|
|
|
|
|
func _label(n: Node) -> String:
|
|
var label := n.name
|
|
if n is MeshInstance3D:
|
|
var mi := n as MeshInstance3D
|
|
if mi.mesh and not mi.mesh.resource_name.is_empty():
|
|
label = "%s|%s" % [n.name, mi.mesh.resource_name]
|
|
return label
|
|
|
|
|
|
func _matches_keep(n: Node, keep: Array[String]) -> bool:
|
|
var labels: Array[String] = [n.name]
|
|
if n is MeshInstance3D:
|
|
var mi := n as MeshInstance3D
|
|
if mi.mesh and not mi.mesh.resource_name.is_empty():
|
|
labels.append(mi.mesh.resource_name)
|
|
for label in labels:
|
|
for k in keep:
|
|
if k.is_empty():
|
|
continue
|
|
if label == k or label.begins_with(k):
|
|
return true
|
|
return false
|
|
|
|
|
|
func _is_head_label(mesh_name: String) -> bool:
|
|
var low := mesh_name.to_lower()
|
|
if "pistol" in low:
|
|
return false
|
|
return ("_head" in low) or low.ends_with("head") or ("hair" in low) or ("hat" in low) or ("neck" in low)
|
|
|
|
|
|
func _is_pistol_label(mesh_name: String) -> bool:
|
|
return mesh_name.contains("Pistol") or mesh_name.contains("pistol")
|
|
|
|
|
|
func _is_banned(mesh_name: String) -> bool:
|
|
var n := mesh_name
|
|
return (
|
|
n.begins_with("Horse")
|
|
or n.contains("Horse")
|
|
or n.begins_with("King")
|
|
or n.begins_with("Witch")
|
|
or n.begins_with("Medieval")
|
|
or n.begins_with("Beach")
|
|
or n.begins_with("Sword")
|
|
)
|