Commit ae4c3827 authored by Gavin An's avatar Gavin An

공격 애니매이션 분리

parent 92f679a6
...@@ -24,11 +24,3 @@ func _export_selected_transform() -> void: ...@@ -24,11 +24,3 @@ func _export_selected_transform() -> void:
var rot_rad: Vector3 = target_node.rotation var rot_rad: Vector3 = target_node.rotation
var scl: Vector3 = target_node.scale var scl: Vector3 = target_node.scale
print("\n==================================================")
print(" FIT SANDBOX OFFSET EXPORT (Target: %s)" % target_node.name)
print("==================================================")
print("# GDScript 붙여넣기용 코드:")
print("position = Vector3(%.4f, %.4f, %.4f)" % [pos.x, pos.y, pos.z])
print("rotation = Vector3(%.4f, %.4f, %.4f) # Degrees: (%.1f, %.1f, %.1f)" % [rot_rad.x, rot_rad.y, rot_rad.z, rot_deg.x, rot_deg.y, rot_deg.z])
print("scale = Vector3(%.4f, %.4f, %.4f)" % [scl.x, scl.y, scl.z])
print("==================================================\n")
...@@ -6,16 +6,11 @@ func _init() -> void: ...@@ -6,16 +6,11 @@ func _init() -> void:
var scene = load(path) as PackedScene var scene = load(path) as PackedScene
if scene: if scene:
var inst = scene.instantiate() var inst = scene.instantiate()
print("--- ARROW.GLB STRUCTURE ---")
_print_node_tree(inst, "") _print_node_tree(inst, "")
print("--- END ---")
else: else:
print("Failed to load scene")
else: else:
print("File not found")
quit() quit()
func _print_node_tree(node: Node, indent: String) -> void: func _print_node_tree(node: Node, indent: String) -> void:
print(indent + node.name + " (" + node.get_class() + ")")
for child in node.get_children(): for child in node.get_children():
_print_node_tree(child, indent + " ") _print_node_tree(child, indent + " ")
...@@ -81,7 +81,6 @@ func _check_game_over_conditions() -> void: ...@@ -81,7 +81,6 @@ func _check_game_over_conditions() -> void:
if active_monster_count > DEFEAT_MONSTER_LIMIT: if active_monster_count > DEFEAT_MONSTER_LIMIT:
is_game_over = true is_game_over = true
game_lost.emit() game_lost.emit()
print("GAME OVER: Monster limit exceeded!")
func _check_victory_condition() -> void: func _check_victory_condition() -> void:
if is_game_over: if is_game_over:
...@@ -125,4 +124,3 @@ func clear_all_units() -> void: ...@@ -125,4 +124,3 @@ func clear_all_units() -> void:
for unit: Node in units: for unit: Node in units:
if is_instance_valid(unit): if is_instance_valid(unit):
unit.queue_free() unit.queue_free()
print("GameManager: All units cleared!")
...@@ -16,7 +16,6 @@ func _enter_tree() -> void: ...@@ -16,7 +16,6 @@ func _enter_tree() -> void:
var nav_region: NavigationRegion3D = get_node_or_null("NavigationRegion3D") as NavigationRegion3D var nav_region: NavigationRegion3D = get_node_or_null("NavigationRegion3D") as NavigationRegion3D
if nav_region: if nav_region:
nav_region.add_child(map_inst) nav_region.add_child(map_inst)
print("Main: Loaded map scene '", map_path, "' successfully.")
else: else:
push_error("Main: NavigationRegion3D not found.") push_error("Main: NavigationRegion3D not found.")
else: else:
......
...@@ -48,7 +48,6 @@ func _ready() -> void: ...@@ -48,7 +48,6 @@ func _ready() -> void:
## 소환사 선택 완료 후 게임을 정식으로 가동하는 수동 엔트리 ## 소환사 선택 완료 후 게임을 정식으로 가동하는 수동 엔트리
func start_game() -> void: func start_game() -> void:
print("StageManager: Game started manually after summoner selection.")
_start_next_stage_cooldown(3.0) _start_next_stage_cooldown(3.0)
## 다음 스테이지 준비 대기 시간 시작 ## 다음 스테이지 준비 대기 시간 시작
...@@ -57,7 +56,6 @@ func _start_next_stage_cooldown(duration: float) -> void: ...@@ -57,7 +56,6 @@ func _start_next_stage_cooldown(duration: float) -> void:
_cooldown_timer.wait_time = duration _cooldown_timer.wait_time = duration
_cooldown_timer.start() _cooldown_timer.start()
stage_start_cooldown_started.emit(duration) stage_start_cooldown_started.emit(duration)
print("Stage Manager: Cooldown started for stage ", GameManager.current_stage, " - Duration: ", duration)
## 실제 스테이지 몬스터 스폰 가동 ## 실제 스테이지 몬스터 스폰 가동
func _start_stage_spawning() -> void: func _start_stage_spawning() -> void:
...@@ -69,7 +67,6 @@ func _start_stage_spawning() -> void: ...@@ -69,7 +67,6 @@ func _start_stage_spawning() -> void:
_monsters_spawned_this_stage = 0 _monsters_spawned_this_stage = 0
stage_started.emit(GameManager.current_stage) stage_started.emit(GameManager.current_stage)
print("Stage Manager: Stage ", GameManager.current_stage, " Started!")
_spawn_timer.start() _spawn_timer.start()
...@@ -117,7 +114,6 @@ func _on_spawn_timer_timeout() -> void: ...@@ -117,7 +114,6 @@ func _on_spawn_timer_timeout() -> void:
if _monsters_spawned_this_stage >= monsters_per_stage: if _monsters_spawned_this_stage >= monsters_per_stage:
_is_spawning = false _is_spawning = false
_spawn_timer.stop() _spawn_timer.stop()
print("Stage Manager: All monsters spawned for stage ", GameManager.current_stage)
## 몬스터 잔여 수 변동 시 다음 스테이지 이동 체크 ## 몬스터 잔여 수 변동 시 다음 스테이지 이동 체크
func _on_active_monster_count_changed(count: int) -> void: func _on_active_monster_count_changed(count: int) -> void:
...@@ -130,7 +126,6 @@ func _on_active_monster_count_changed(count: int) -> void: ...@@ -130,7 +126,6 @@ func _on_active_monster_count_changed(count: int) -> void:
if GameManager.current_stage >= GameManager.MAX_STAGE: if GameManager.current_stage >= GameManager.MAX_STAGE:
GameManager.is_game_over = true GameManager.is_game_over = true
GameManager.game_victory.emit() GameManager.game_victory.emit()
print("VICTORY: All stages cleared!")
else: else:
# 다음 스테이지로 상태 전이 # 다음 스테이지로 상태 전이
GameManager.current_stage += 1 GameManager.current_stage += 1
...@@ -148,5 +143,4 @@ func toggle_spawning_pause() -> bool: ...@@ -148,5 +143,4 @@ func toggle_spawning_pause() -> bool:
if _cooldown_timer: if _cooldown_timer:
_cooldown_timer.paused = is_spawning_paused _cooldown_timer.paused = is_spawning_paused
print("StageManager: Spawning Pause Toggled. Paused = ", is_spawning_paused)
return is_spawning_paused return is_spawning_paused
...@@ -26,7 +26,6 @@ func _ready() -> void: ...@@ -26,7 +26,6 @@ func _ready() -> void:
var nav_region: NavigationRegion3D = get_node_or_null("../NavigationRegion3D") as NavigationRegion3D var nav_region: NavigationRegion3D = get_node_or_null("../NavigationRegion3D") as NavigationRegion3D
if nav_region: if nav_region:
nav_region.bake_navigation_mesh() nav_region.bake_navigation_mesh()
print("UnitController: Navigation mesh baked successfully on start.")
func _unhandled_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
if GameManager.is_game_over: if GameManager.is_game_over:
...@@ -105,7 +104,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void: ...@@ -105,7 +104,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void:
selected_units.append(unit_node) selected_units.append(unit_node)
if unit_node.has_method("select"): if unit_node.has_method("select"):
unit_node.call("select") unit_node.call("select")
print("UnitController: Selected single unit: ", unit_node.name)
# 2. 드래그 다중 선택 처리 (Unproject 투영 이용) # 2. 드래그 다중 선택 처리 (Unproject 투영 이용)
else: else:
...@@ -126,7 +124,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void: ...@@ -126,7 +124,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void:
if unit.has_method("select"): if unit.has_method("select"):
unit.call("select") unit.call("select")
newly_selected_count += 1 newly_selected_count += 1
print("UnitController: Selected ", newly_selected_count, " new units via drag. Total: ", selected_units.size())
## 우클릭 액션 (이동 목적지 지정 또는 적 공격 타겟팅) ## 우클릭 액션 (이동 목적지 지정 또는 적 공격 타겟팅)
func _perform_action(mouse_pos: Vector2) -> void: func _perform_action(mouse_pos: Vector2) -> void:
...@@ -149,7 +146,6 @@ func _perform_action(mouse_pos: Vector2) -> void: ...@@ -149,7 +146,6 @@ func _perform_action(mouse_pos: Vector2) -> void:
for unit: Node in selected_units: for unit: Node in selected_units:
if is_instance_valid(unit) and unit.has_method("attack_target"): if is_instance_valid(unit) and unit.has_method("attack_target"):
unit.call("attack_target", monster_node) unit.call("attack_target", monster_node)
print("UnitController: Ordered units to attack monster: ", monster_node.name)
# 공격 지시 클릭 표시 (빨간색) 스폰 # 공격 지시 클릭 표시 (빨간색) 스폰
_spawn_click_indicator(monster_node.global_position, true) _spawn_click_indicator(monster_node.global_position, true)
...@@ -174,7 +170,6 @@ func _perform_action(mouse_pos: Vector2) -> void: ...@@ -174,7 +170,6 @@ func _perform_action(mouse_pos: Vector2) -> void:
var target_destination: Vector3 = hit_pos + offset var target_destination: Vector3 = hit_pos + offset
unit.call("move_to", target_destination) unit.call("move_to", target_destination)
print("UnitController: Ordered ", unit_count, " units to move to ", hit_pos)
# 지면 이동 클릭 표시 (초록색) 스폰 # 지면 이동 클릭 표시 (초록색) 스폰
_spawn_click_indicator(hit_pos, false) _spawn_click_indicator(hit_pos, false)
...@@ -260,4 +255,3 @@ func _perform_hold_position() -> void: ...@@ -260,4 +255,3 @@ func _perform_hold_position() -> void:
if is_instance_valid(unit) and unit.has_method("hold_position"): if is_instance_valid(unit) and unit.has_method("hold_position"):
unit.call("hold_position") unit.call("hold_position")
print("UnitController: Ordered ", selected_units.size(), " units to hold position.")
...@@ -84,7 +84,6 @@ func take_damage(amount: float) -> void: ...@@ -84,7 +84,6 @@ func take_damage(amount: float) -> void:
if _hp_bar: if _hp_bar:
_hp_bar.value = current_hp _hp_bar.value = current_hp
print("Monster ", name, " took ", amount, " damage. HP: ", current_hp, "/", max_hp)
if current_hp <= 0.0: if current_hp <= 0.0:
_die() _die()
...@@ -107,7 +106,6 @@ func _die() -> void: ...@@ -107,7 +106,6 @@ func _die() -> void:
# 필드 활성 몬스터 수 감소 # 필드 활성 몬스터 수 감소
GameManager.decrement_monster_count() GameManager.decrement_monster_count()
print("Monster ", name, " died! Rewarded ", gold_reward, " gold and granted XP.")
# 씬에서 제거 # 씬에서 제거
queue_free() queue_free()
......
...@@ -8,11 +8,9 @@ func _setup_skin_and_animations() -> void: ...@@ -8,11 +8,9 @@ func _setup_skin_and_animations() -> void:
_model_anim_player = _find_animation_player(_model_instance) _model_anim_player = _find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("NobleManMonster: Linked model AnimationPlayer")
var init_idle: String = _resolve_anim_name(_model_anim_player, "noble_man", "Idle") var init_idle: String = _resolve_anim_name(_model_anim_player, "noble_man", "Idle")
if init_idle != "": if init_idle != "":
_model_anim_player.play(init_idle) _model_anim_player.play(init_idle)
print("NobleManMonster: Playing initial Idle: ", init_idle)
## 몬스터 자식 메쉬에 noble_man_0.png 텍스처를 입히는 재귀 함수 ## 몬스터 자식 메쉬에 noble_man_0.png 텍스처를 입히는 재귀 함수
func _apply_noble_man_skin(node: Node) -> void: func _apply_noble_man_skin(node: Node) -> void:
...@@ -43,4 +41,3 @@ func _update_monster_animation() -> void: ...@@ -43,4 +41,3 @@ func _update_monster_animation() -> void:
if _last_anim_state != "walking": if _last_anim_state != "walking":
_last_anim_state = "walking" _last_anim_state = "walking"
_model_anim_player.play(anim_name, -1, 1.2) # 1.2배속 재생 _model_anim_player.play(anim_name, -1, 1.2) # 1.2배속 재생
print("NobleManMonster: Playing Walk animation: ", anim_name)
...@@ -8,8 +8,6 @@ func _setup_skin_and_animations() -> void: ...@@ -8,8 +8,6 @@ func _setup_skin_and_animations() -> void:
_model_anim_player = _find_animation_player(_model_instance) _model_anim_player = _find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("SkeletonMonster: Linked model AnimationPlayer")
# 스켈레톤 애니메이션 라이브러리 동적 로드 및 등록 # 스켈레톤 애니메이션 라이브러리 동적 로드 및 등록
var m_name: String = "skeleton" var m_name: String = "skeleton"
var anim_lib_path: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_animations.tres"
...@@ -30,15 +28,13 @@ func _setup_skin_and_animations() -> void: ...@@ -30,15 +28,13 @@ func _setup_skin_and_animations() -> void:
break break
if not already_registered: if not already_registered:
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("SkeletonMonster: Loaded and registered ", m_name, " animations library successfully.")
else: else:
print("SkeletonMonster: Animation library already registered on model under another name.") pass
# 초기 Idle 애니메이션 재생 시도 # 초기 Idle 애니메이션 재생 시도
var init_idle: String = _resolve_anim_name(_model_anim_player, m_name, "Idle") var init_idle: String = _resolve_anim_name(_model_anim_player, m_name, "Idle")
if init_idle != "": if init_idle != "":
_model_anim_player.play(init_idle) _model_anim_player.play(init_idle)
print("SkeletonMonster: Playing initial Idle: ", init_idle)
## 몬스터 자식 메쉬에 skeleton_0.png 텍스처를 입히는 재귀 함수 ## 몬스터 자식 메쉬에 skeleton_0.png 텍스처를 입히는 재귀 함수
func _apply_skeleton_skin(node: Node) -> void: func _apply_skeleton_skin(node: Node) -> void:
...@@ -69,4 +65,3 @@ func _update_monster_animation() -> void: ...@@ -69,4 +65,3 @@ func _update_monster_animation() -> void:
if _last_anim_state != "walking": if _last_anim_state != "walking":
_last_anim_state = "walking" _last_anim_state = "walking"
_model_anim_player.play(anim_name, -1, 1.4) # Skeleton은 속도가 빠르므로 1.4배속 재생 _model_anim_player.play(anim_name, -1, 1.4) # Skeleton은 속도가 빠르므로 1.4배속 재생
print("SkeletonMonster: Playing Walk animation: ", anim_name)
...@@ -8,8 +8,6 @@ func _setup_skin_and_animations() -> void: ...@@ -8,8 +8,6 @@ func _setup_skin_and_animations() -> void:
_model_anim_player = _find_animation_player(_model_instance) _model_anim_player = _find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("ZombieMonster: Linked model AnimationPlayer")
# 좀비 애니메이션 라이브러리 동적 로드 및 등록 # 좀비 애니메이션 라이브러리 동적 로드 및 등록
var m_name: String = "zombie" var m_name: String = "zombie"
var anim_lib_path: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_animations.tres"
...@@ -30,15 +28,12 @@ func _setup_skin_and_animations() -> void: ...@@ -30,15 +28,12 @@ func _setup_skin_and_animations() -> void:
break break
if not already_registered: if not already_registered:
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("ZombieMonster: Loaded and registered ", m_name, " animations library successfully.")
else: else:
print("ZombieMonster: Animation library already registered on model under another name.") pass
# 초기 Idle 애니메이션 재생 시도 # 초기 Idle 애니메이션 재생 시도
var init_idle: String = _resolve_anim_name(_model_anim_player, m_name, "Idle") var init_idle: String = _resolve_anim_name(_model_anim_player, m_name, "Idle")
if init_idle != "": if init_idle != "":
_model_anim_player.play(init_idle) _model_anim_player.play(init_idle)
print("ZombieMonster: Playing initial Idle: ", init_idle)
## 몬스터 자식 메쉬에 zombie_0.png 텍스처를 입히는 재귀 함수 ## 몬스터 자식 메쉬에 zombie_0.png 텍스처를 입히는 재귀 함수
func _apply_zombie_skin(node: Node) -> void: func _apply_zombie_skin(node: Node) -> void:
...@@ -69,4 +64,3 @@ func _update_monster_animation() -> void: ...@@ -69,4 +64,3 @@ func _update_monster_animation() -> void:
if _last_anim_state != "walking": if _last_anim_state != "walking":
_last_anim_state = "walking" _last_anim_state = "walking"
_model_anim_player.play(anim_name, -1, 1.2) # 1.2배속 재생 _model_anim_player.play(anim_name, -1, 1.2) # 1.2배속 재생
print("ZombieMonster: Playing Walk animation: ", anim_name)
...@@ -68,18 +68,15 @@ func cast_spell(spell_name: String) -> bool: ...@@ -68,18 +68,15 @@ func cast_spell(spell_name: String) -> bool:
return false return false
if not spell_cooldowns.has(spell_name): if not spell_cooldowns.has(spell_name):
print("Summoner: Spell '", spell_name, "' is not equipped.")
return false return false
# 쿨다운 중인지 체크 # 쿨다운 중인지 체크
if spell_cooldowns[spell_name] > 0.0: if spell_cooldowns[spell_name] > 0.0:
print("Summoner: Spell '", spell_name, "' is on cooldown (", spell_cooldowns[spell_name], "s remaining).")
return false return false
# 마나 비용 체크 # 마나 비용 체크
var cost = spell_mana_costs.get(spell_name, 0.0) var cost = spell_mana_costs.get(spell_name, 0.0)
if current_mana < cost: if current_mana < cost:
print("Summoner: Insufficient mana for '", spell_name, "'. Need: ", cost, ", Current: ", current_mana)
return false return false
# 주문 실행 분기 # 주문 실행 분기
...@@ -95,7 +92,6 @@ func cast_spell(spell_name: String) -> bool: ...@@ -95,7 +92,6 @@ func cast_spell(spell_name: String) -> bool:
spell_cooldowns[spell_name] = spell_max_cooldowns[spell_name] spell_cooldowns[spell_name] = spell_max_cooldowns[spell_name]
mana_changed.emit(current_mana, max_mana) mana_changed.emit(current_mana, max_mana)
spell_cooldown_changed.emit(spell_name, spell_cooldowns[spell_name], spell_max_cooldowns[spell_name]) spell_cooldown_changed.emit(spell_name, spell_cooldowns[spell_name], spell_max_cooldowns[spell_name])
print("Summoner: Casted spell '", spell_name, "' successfully.")
return true return true
return false return false
...@@ -104,7 +100,6 @@ func cast_spell(spell_name: String) -> bool: ...@@ -104,7 +100,6 @@ func cast_spell(spell_name: String) -> bool:
func _execute_haste() -> bool: func _execute_haste() -> bool:
var units = get_tree().get_nodes_in_group("units") var units = get_tree().get_nodes_in_group("units")
if units.is_empty(): if units.is_empty():
print("Summoner: No units on field to apply Haste.")
return false return false
# 5초간 모든 유닛의 공격 속도 배율을 2배로 상승 # 5초간 모든 유닛의 공격 속도 배율을 2배로 상승
...@@ -120,17 +115,14 @@ func _execute_haste() -> bool: ...@@ -120,17 +115,14 @@ func _execute_haste() -> bool:
for unit in current_units: for unit in current_units:
if is_instance_valid(unit) and "attack_speed_mult" in unit: if is_instance_valid(unit) and "attack_speed_mult" in unit:
unit.set("attack_speed_mult", 1.0) unit.set("attack_speed_mult", 1.0)
print("Summoner: Haste spell expired. Unit attack speeds reverted to normal.")
) )
print("Summoner: Applied Haste to ", units.size(), " units for 5 seconds.")
return true return true
## 2. 강타 주문 (Smite) ## 2. 강타 주문 (Smite)
func _execute_smite() -> bool: func _execute_smite() -> bool:
var monsters = get_tree().get_nodes_in_group("monsters") var monsters = get_tree().get_nodes_in_group("monsters")
if monsters.is_empty(): if monsters.is_empty():
print("Summoner: No active monsters for Smite.")
return false return false
# 살아있는 유효한 몬스터 필터링 # 살아있는 유효한 몬스터 필터링
...@@ -140,7 +132,6 @@ func _execute_smite() -> bool: ...@@ -140,7 +132,6 @@ func _execute_smite() -> bool:
active_monsters.append(m) active_monsters.append(m)
if active_monsters.is_empty(): if active_monsters.is_empty():
print("Summoner: No alive monsters found.")
return false return false
# 출구와 가까운 몬스터 (PathFollow3D의 progress가 큰 순서) 기준으로 정렬 # 출구와 가까운 몬스터 (PathFollow3D의 progress가 큰 순서) 기준으로 정렬
...@@ -152,7 +143,6 @@ func _execute_smite() -> bool: ...@@ -152,7 +143,6 @@ func _execute_smite() -> bool:
# 최전방 최대 5마리 타겟팅 # 최전방 최대 5마리 타겟팅
var targets_count = min(5, active_monsters.size()) var targets_count = min(5, active_monsters.size())
print("Summoner: Smite targeting ", targets_count, " monsters.")
for i in range(targets_count): for i in range(targets_count):
var monster = active_monsters[i] var monster = active_monsters[i]
......
...@@ -284,17 +284,6 @@ func _get_attack_animation_length() -> float: ...@@ -284,17 +284,6 @@ func _get_attack_animation_length() -> float:
return anim_length return anim_length
## 모델별 기본 공격 애니메이션 이름을 반환합니다.
func _get_attack_animation_name(model_name: String) -> String:
return ''
# var attack_key: String = "Slash"
# if model_name == "ranger":
# attack_key = "Draw"
# elif model_name == "mage":
# attack_key = "Casting"
# return model_name + "/" + attack_key
## 공격 타이밍 계산에 사용할 기본 재생 속도입니다. ## 공격 타이밍 계산에 사용할 기본 재생 속도입니다.
func _get_attack_timing_play_speed() -> float: func _get_attack_timing_play_speed() -> float:
var play_speed: float = 1.5 var play_speed: float = 1.5
...@@ -313,14 +302,6 @@ func _get_attack_lock_duration(anim_length: float, play_speed: float) -> float: ...@@ -313,14 +302,6 @@ func _get_attack_lock_duration(anim_length: float, play_speed: float) -> float:
func _get_attack_damage_delay(attack_lock_duration: float) -> float: func _get_attack_damage_delay(attack_lock_duration: float) -> float:
return attack_lock_duration * 0.5 return attack_lock_duration * 0.5
## 공격 관련 비주얼 이펙트 연출
func _show_attack_visual() -> void:
# 원거리 타격 시 빔 또는 스파크 이펙트 임시 렌더
if unit_data.is_ranged and _target_monster:
print("Unit ", name, " fired a projectile/spell at ", _target_monster.name)
else:
print("Unit ", name, " strikes ", _target_monster.name)
## h 버튼을 누르면 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold) ## h 버튼을 누르면 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold)
func hold_position() -> void: func hold_position() -> void:
_is_holding = true _is_holding = true
...@@ -402,6 +383,23 @@ func deselect() -> void: ...@@ -402,6 +383,23 @@ func deselect() -> void:
if _selection_ring: if _selection_ring:
_selection_ring.visible = false _selection_ring.visible = false
## 공격 관련 비주얼 이펙트 연출
func _show_attack_visual() -> void:
# 원거리 타격 시 빔 또는 스파크 이펙트 임시 렌더
# if unit_data.is_ranged and _target_monster:
# else:
pass
## 모델별 기본 공격 애니메이션 이름을 반환합니다.
func _get_attack_animation_name(model_name: String) -> String:
return ""
# var attack_key: String = "Slash"
# if model_name == "ranger":
# attack_key = "Draw"
# elif model_name == "mage":
# attack_key = "Casting"
# return model_name + "/" + attack_key
## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어 및 크로스페이드 블렌딩 ## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어 및 크로스페이드 블렌딩
## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어 (상속 클래스에서 재정의) ## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어 (상속 클래스에서 재정의)
func _update_animations() -> void: func _update_animations() -> void:
......
...@@ -2,6 +2,10 @@ class_name GuardianUnit ...@@ -2,6 +2,10 @@ class_name GuardianUnit
extends BaseUnit extends BaseUnit
## Guardian 유닛 고유 동작 및 비주얼 제어 클래스 ## Guardian 유닛 고유 동작 및 비주얼 제어 클래스
const GUARDIAN_ATTACK_ANIM: String = "Slash"
const GUARDIAN_RUN_ANIM: String = "Running"
const GUARDIAN_WALK_ANIM: String = "Walking"
const GUARDIAN_IDLE_ANIM: String = "Idle"
func _setup_unit_visuals() -> void: func _setup_unit_visuals() -> void:
# 자식 노드 중 가디언 모델 노드 명시적 확보 # 자식 노드 중 가디언 모델 노드 명시적 확보
...@@ -19,7 +23,6 @@ func _setup_unit_visuals() -> void: ...@@ -19,7 +23,6 @@ func _setup_unit_visuals() -> void:
# 애니메이션 플레이어 및 라이브러리 연동 # 애니메이션 플레이어 및 라이브러리 연동
_model_anim_player = UnitHelper.find_animation_player(_model_instance) _model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("GuardianUnit: Linked pre-configured model & AnimationPlayer: ", m_name)
var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres"
if ResourceLoader.exists(anim_lib_path): if ResourceLoader.exists(anim_lib_path):
...@@ -29,9 +32,11 @@ func _setup_unit_visuals() -> void: ...@@ -29,9 +32,11 @@ func _setup_unit_visuals() -> void:
_model_anim_player.remove_animation_library("") _model_anim_player.remove_animation_library("")
if not _model_anim_player.has_animation_library(m_name): if not _model_anim_player.has_animation_library(m_name):
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("GuardianUnit: Loaded and registered ", m_name, " animations library successfully.")
UnitHelper.clean_root_animation_tracks(_model_anim_player) UnitHelper.clean_root_animation_tracks(_model_anim_player)
func _get_attack_animation_name(_model_name: String) -> String:
return _model_name + "/" + GUARDIAN_ATTACK_ANIM
## 가디언 애니메이션 갱신 제어 ## 가디언 애니메이션 갱신 제어
func _update_animations() -> void: func _update_animations() -> void:
if not _model_anim_player: if not _model_anim_player:
......
[gd_scene format=3 uid="uid://cx4vbc5m3myqk"] [gd_scene format=3 uid="uid://cx4vbc5m3myqk"]
[ext_resource type="Script" path="res://src/entities/units/guardian/guardian_unit.gd" id="1_base_unit"] [ext_resource type="Script" uid="uid://b4ott7c4tx82j" path="res://src/entities/units/guardian/guardian_unit.gd" id="1_base_unit"]
[ext_resource type="PackedScene" uid="uid://dh00osxx32ll" path="res://src/entities/units/guardian/guardian.tscn" id="2_y84tp"] [ext_resource type="PackedScene" uid="uid://dh00osxx32ll" path="res://src/entities/units/guardian/guardian.tscn" id="2_y84tp"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
...@@ -42,4 +42,28 @@ mesh = SubResource("TorusMesh_selection") ...@@ -42,4 +42,28 @@ mesh = SubResource("TorusMesh_selection")
[node name="guardian" parent="." unique_id=1303946976 instance=ExtResource("2_y84tp")] [node name="guardian" parent="." unique_id=1303946976 instance=ExtResource("2_y84tp")]
[node name="Skeleton3D" parent="guardian/Armature" parent_id_path=PackedInt32Array(1303946976, 42386791) index="0" unique_id=1547704068]
bones/0/position = Vector3(0.007254056, -0.031982094, 0.8454071)
bones/0/rotation = Quaternion(0.16432424, 0.019133193, 0.87338674, -0.45806903)
bones/1/rotation = Quaternion(-0.12092076, -0.7872979, 0.4819965, 0.36499256)
bones/2/rotation = Quaternion(0.33750388, 0.025897268, 0.017135011, 0.9408119)
bones/3/rotation = Quaternion(-0.5022115, -0.02807892, 0.08618891, 0.8599806)
bones/4/rotation = Quaternion(-0.44314563, -0.035939246, 0.0013848549, 0.89572793)
bones/5/rotation = Quaternion(-0.5774472, -0.36547333, 0.5146526, 0.5178)
bones/6/rotation = Quaternion(0.3062435, -0.047538485, -0.037762463, 0.9500153)
bones/7/rotation = Quaternion(-0.5316294, -0.011670036, -0.10949787, 0.8397882)
bones/9/rotation = Quaternion(0.5004455, 0.5472967, 0.57435375, 0.34660998)
bones/10/rotation = Quaternion(0.054661293, 0.03426229, 0.026580319, 0.9975629)
bones/11/rotation = Quaternion(-0.04221783, -0.0024180731, 0.0061295675, 0.99908674)
bones/12/rotation = Quaternion(0.47379175, 0.6155848, -0.45582765, 0.43450877)
bones/13/rotation = Quaternion(0.50110704, 0.44870365, 0.113574795, 0.7312028)
bones/14/rotation = Quaternion(-0.13031836, -0.12740694, 0.12649705, 0.97508115)
bones/15/rotation = Quaternion(-0.028026814, -0.05217199, 0.03683791, 0.9975649)
bones/16/rotation = Quaternion(-0.48288807, 0.6040217, -0.4048437, -0.48793295)
bones/17/rotation = Quaternion(0.53795695, -0.1486786, -0.09833834, 0.82390934)
bones/18/rotation = Quaternion(-0.20201011, 0.2213894, -0.45648777, 0.8377337)
bones/19/rotation = Quaternion(0.04524021, -0.32150194, 0.08227339, 0.94224256)
bones/20/rotation = Quaternion(0.036245007, 0.071046256, 0.024107164, 0.9965228)
bones/21/rotation = Quaternion(0.37537965, 0.030331533, -0.03659956, 0.9256515)
[editable path="guardian"] [editable path="guardian"]
...@@ -75,7 +75,6 @@ func gain_xp(amount: int) -> void: ...@@ -75,7 +75,6 @@ func gain_xp(amount: int) -> void:
return return
xp += amount xp += amount
print("Hero ", name, " gained ", amount, " XP. (Current: ", xp, "/", xp_to_next_level, ")")
# 레벨업 체크 루프 # 레벨업 체크 루프
while xp >= xp_to_next_level and level < MAX_HERO_LEVEL: while xp >= xp_to_next_level and level < MAX_HERO_LEVEL:
...@@ -88,7 +87,6 @@ func gain_xp(amount: int) -> void: ...@@ -88,7 +87,6 @@ func gain_xp(amount: int) -> void:
## 레벨업 시 능력치 증가 및 연출 ## 레벨업 시 능력치 증가 및 연출
func _level_up() -> void: func _level_up() -> void:
level += 1 level += 1
print("★ LEVEL UP ★ Hero ", name, " reached Level ", level, "!")
# 레벨당 공격력(damage) 15% 및 사거리 등 추가 보강 # 레벨당 공격력(damage) 15% 및 사거리 등 추가 보강
if unit_data: if unit_data:
...@@ -134,7 +132,6 @@ func _unhandled_input(event: InputEvent) -> void: ...@@ -134,7 +132,6 @@ func _unhandled_input(event: InputEvent) -> void:
## 스킬 시도 및 쿨타임 검증 ## 스킬 시도 및 쿨타임 검증
func use_skill(slot: String) -> void: func use_skill(slot: String) -> void:
if skill_cooldowns.get(slot, 0.0) > 0.0: if skill_cooldowns.get(slot, 0.0) > 0.0:
print("Skill ", slot, " is on cooldown! (", skill_cooldowns[slot], "s remaining)")
return return
# 스킬 시전 성공 시 쿨타임 가동 # 스킬 시전 성공 시 쿨타임 가동
......
...@@ -38,8 +38,6 @@ func _setup_unit_visuals() -> void: ...@@ -38,8 +38,6 @@ func _setup_unit_visuals() -> void:
# dwarf.fbx 내에 내장된 AnimationPlayer 노드를 캐싱합니다. # dwarf.fbx 내에 내장된 AnimationPlayer 노드를 캐싱합니다.
_model_anim_player = UnitHelper.find_animation_player(_model_instance) _model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player:
print("DwarfHero: Linked embedded model & AnimationPlayer: ", m_name)
## 드워프는 일반 유닛의 "dwarf/Slash"가 아니라 FBX 내장 "Attack" 클립을 사용합니다. ## 드워프는 일반 유닛의 "dwarf/Slash"가 아니라 FBX 내장 "Attack" 클립을 사용합니다.
func _get_attack_animation_name(_model_name: String) -> String: func _get_attack_animation_name(_model_name: String) -> String:
...@@ -139,15 +137,12 @@ func _get_dwarf_attack_play_speed() -> float: ...@@ -139,15 +137,12 @@ func _get_dwarf_attack_play_speed() -> float:
## - 대상에게 강력한 피해(공격력의 3배)를 가하고 3초간 기절시킵니다. ## - 대상에게 강력한 피해(공격력의 3배)를 가하고 3초간 기절시킵니다.
func _cast_skill_q() -> bool: func _cast_skill_q() -> bool:
if not _target_monster or not is_instance_valid(_target_monster): if not _target_monster or not is_instance_valid(_target_monster):
print("Q Skill Failed: No target monster to strike!")
return false return false
var dist: float = global_position.distance_to(_target_monster.global_position) var dist: float = global_position.distance_to(_target_monster.global_position)
if dist > unit_data.attack_range * 1.5: if dist > unit_data.attack_range * 1.5:
print("Q Skill Failed: Target is too far!")
return false return false
print("Dwarf Cast Q: Heavy Strike on ", _target_monster.name)
# 공격 락 시간 임시 설정 및 공격 애니메이션 강제 재생 # 공격 락 시간 임시 설정 및 공격 애니메이션 강제 재생
_attack_lock_timer = 0.8 _attack_lock_timer = 0.8
...@@ -167,7 +162,6 @@ func _cast_skill_q() -> bool: ...@@ -167,7 +162,6 @@ func _cast_skill_q() -> bool:
get_tree().create_timer(3.0).timeout.connect(func() -> void: get_tree().create_timer(3.0).timeout.connect(func() -> void:
if is_instance_valid(target_ref) and not target_ref.get("is_dead"): if is_instance_valid(target_ref) and not target_ref.get("is_dead"):
target_ref.set("speed", orig_speed) target_ref.set("speed", orig_speed)
print("Monster ", target_ref.name, " recovered from Stun.")
) )
# 이펙트 연출 # 이펙트 연출
...@@ -183,7 +177,6 @@ func _cast_skill_q() -> bool: ...@@ -183,7 +177,6 @@ func _cast_skill_q() -> bool:
## W 스킬: 전장의 가속 (Battle Haste) ## W 스킬: 전장의 가속 (Battle Haste)
## - 영웅 주변 10m 내 모든 아군 유닛의 공격 속도 배율을 1.5배로 5초간 강화합니다. ## - 영웅 주변 10m 내 모든 아군 유닛의 공격 속도 배율을 1.5배로 5초간 강화합니다.
func _cast_skill_w() -> bool: func _cast_skill_w() -> bool:
print("Dwarf Cast W: Battle Haste activated!")
var affected_units: Array[Node] = [] var affected_units: Array[Node] = []
var all_units: Array[Node] = get_tree().get_nodes_in_group("units") var all_units: Array[Node] = get_tree().get_nodes_in_group("units")
...@@ -195,14 +188,12 @@ func _cast_skill_w() -> bool: ...@@ -195,14 +188,12 @@ func _cast_skill_w() -> bool:
# 아군 공격 속도 배율 임시 강화 (base_unit.gd의 attack_speed_mult 참고) # 아군 공격 속도 배율 임시 강화 (base_unit.gd의 attack_speed_mult 참고)
unit.set("attack_speed_mult", 1.5) unit.set("attack_speed_mult", 1.5)
affected_units.append(unit) affected_units.append(unit)
print(" Hasted unit: ", unit.name)
# 5초 뒤 버프 해제 # 5초 뒤 버프 해제
get_tree().create_timer(5.0).timeout.connect(func() -> void: get_tree().create_timer(5.0).timeout.connect(func() -> void:
for unit: Node in affected_units: for unit: Node in affected_units:
if is_instance_valid(unit): if is_instance_valid(unit):
unit.set("attack_speed_mult", 1.0) unit.set("attack_speed_mult", 1.0)
print("Battle Haste duration ended. Stats restored.")
) )
# 비주얼 스파크 효과 # 비주얼 스파크 효과
...@@ -218,7 +209,6 @@ func _cast_skill_w() -> bool: ...@@ -218,7 +209,6 @@ func _cast_skill_w() -> bool:
## E 스킬: 천둥벼락 (Thunder Clap) ## E 스킬: 천둥벼락 (Thunder Clap)
## - 주변 8m 내 모든 몬스터에게 1.5배 범위 피해를 입히고 3초간 속도를 50% 느리게 만듭니다. ## - 주변 8m 내 모든 몬스터에게 1.5배 범위 피해를 입히고 3초간 속도를 50% 느리게 만듭니다.
func _cast_skill_e() -> bool: func _cast_skill_e() -> bool:
print("Dwarf Cast E: Thunder Clap triggered!")
var monsters: Array[Node] = get_tree().get_nodes_in_group("monsters") var monsters: Array[Node] = get_tree().get_nodes_in_group("monsters")
var damage_amount: float = unit_data.damage * 1.5 var damage_amount: float = unit_data.damage * 1.5
...@@ -260,10 +250,8 @@ func _cast_skill_e() -> bool: ...@@ -260,10 +250,8 @@ func _cast_skill_e() -> bool:
## - 10초 동안 영웅이 거대화(스케일 1.8배)되며, 공격력이 2배 증가하고 모든 체력을 완전 회복합니다. ## - 10초 동안 영웅이 거대화(스케일 1.8배)되며, 공격력이 2배 증가하고 모든 체력을 완전 회복합니다.
func _cast_skill_r() -> bool: func _cast_skill_r() -> bool:
if _is_avatar_active: if _is_avatar_active:
print("Avatar is already active!")
return false return false
print("Dwarf Cast R: AVATAR MODE ACTIVATE!")
_is_avatar_active = true _is_avatar_active = true
_original_damage = unit_data.damage _original_damage = unit_data.damage
...@@ -293,7 +281,6 @@ func _cast_skill_r() -> bool: ...@@ -293,7 +281,6 @@ func _cast_skill_r() -> bool:
if mat: if mat:
tween_back.tween_property(mat, "emission_energy_multiplier", 2.0, 0.5) tween_back.tween_property(mat, "emission_energy_multiplier", 2.0, 0.5)
print("Avatar Mode deactivated. Stats restored.")
) )
return true return true
...@@ -5,7 +5,7 @@ extends BaseUnit ...@@ -5,7 +5,7 @@ extends BaseUnit
## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및 ## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및
## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다. ## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다.
const MAGE_ATTACK_ANIM: String = "CASTING" const MAGE_ATTACK_ANIM: String = "Casting"
const MAGE_RUN_ANIM: String = "Running" const MAGE_RUN_ANIM: String = "Running"
const MAGE_WALK_ANIM: String = "Walking" const MAGE_WALK_ANIM: String = "Walking"
const MAGE_IDLE_ANIM: String = "Idle" const MAGE_IDLE_ANIM: String = "Idle"
...@@ -29,7 +29,6 @@ func _setup_unit_visuals() -> void: ...@@ -29,7 +29,6 @@ func _setup_unit_visuals() -> void:
# 모델 씬 내부에 구성되어 있는 AnimationPlayer 노드를 탐색하여 변수에 캐싱합니다. # 모델 씬 내부에 구성되어 있는 AnimationPlayer 노드를 탐색하여 변수에 캐싱합니다.
_model_anim_player = UnitHelper.find_animation_player(_model_instance) _model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("MageUnit: Linked pre-configured model & AnimationPlayer: ", m_name)
# 마법사 유닛 전용으로 추출된 애니메이션 라이브러리 리소스 경로를 지정합니다. # 마법사 유닛 전용으로 추출된 애니메이션 라이브러리 리소스 경로를 지정합니다.
var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres"
...@@ -43,13 +42,11 @@ func _setup_unit_visuals() -> void: ...@@ -43,13 +42,11 @@ func _setup_unit_visuals() -> void:
# 마법사 전용 애니메이션 라이브러리가 등록되어 있지 않다면 라이브러리를 추가합니다. # 마법사 전용 애니메이션 라이브러리가 등록되어 있지 않다면 라이브러리를 추가합니다.
if not _model_anim_player.has_animation_library(m_name): if not _model_anim_player.has_animation_library(m_name):
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("MageUnit: Loaded and registered ", m_name, " animations library successfully.")
# 트랙 내 불필요한 루트 모션 경로 정리를 수행합니다. # 트랙 내 불필요한 루트 모션 경로 정리를 수행합니다.
UnitHelper.clean_root_animation_tracks(_model_anim_player) UnitHelper.clean_root_animation_tracks(_model_anim_player)
## 드워프는 일반 유닛의 "dwarf/Slash"가 아니라 FBX 내장 "Attack" 클립을 사용합니다.
func _get_attack_animation_name(_model_name: String) -> String: func _get_attack_animation_name(_model_name: String) -> String:
return MAGE_ATTACK_ANIM return _model_name + "/" + MAGE_ATTACK_ANIM
## 매 프레임 물리에 맞추어 마법사 유닛의 애니메이션 상태를 제어하는 함수 ## 매 프레임 물리에 맞추어 마법사 유닛의 애니메이션 상태를 제어하는 함수
func _update_animations() -> void: func _update_animations() -> void:
......
...@@ -2,6 +2,10 @@ class_name RangerUnit ...@@ -2,6 +2,10 @@ class_name RangerUnit
extends BaseUnit extends BaseUnit
## Ranger 유닛 고유 동작 및 비주얼 제어 클래스 ## Ranger 유닛 고유 동작 및 비주얼 제어 클래스
const RANGER_ATTACK_ANIM: String = "Draw"
const RANGER_RUN_ANIM: String = "Running"
const RANGER_WALK_ANIM: String = "Walking"
const RANGER_IDLE_ANIM: String = "Idle"
func _setup_unit_visuals() -> void: func _setup_unit_visuals() -> void:
# 자식 노드 중 레인저 모델 노드 명시적 확보 # 자식 노드 중 레인저 모델 노드 명시적 확보
...@@ -19,7 +23,6 @@ func _setup_unit_visuals() -> void: ...@@ -19,7 +23,6 @@ func _setup_unit_visuals() -> void:
# 애니메이션 플레이어 및 라이브러리 연동 # 애니메이션 플레이어 및 라이브러리 연동
_model_anim_player = UnitHelper.find_animation_player(_model_instance) _model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("RangerUnit: Linked pre-configured model & AnimationPlayer: ", m_name)
var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres"
if ResourceLoader.exists(anim_lib_path): if ResourceLoader.exists(anim_lib_path):
...@@ -29,9 +32,11 @@ func _setup_unit_visuals() -> void: ...@@ -29,9 +32,11 @@ func _setup_unit_visuals() -> void:
_model_anim_player.remove_animation_library("") _model_anim_player.remove_animation_library("")
if not _model_anim_player.has_animation_library(m_name): if not _model_anim_player.has_animation_library(m_name):
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("RangerUnit: Loaded and registered ", m_name, " animations library successfully.")
UnitHelper.clean_root_animation_tracks(_model_anim_player) UnitHelper.clean_root_animation_tracks(_model_anim_player)
func _get_attack_animation_name(_model_name: String) -> String:
return _model_name + "/" + RANGER_ATTACK_ANIM
## 레인저 애니메이션 갱신 제어 (Draw 애니메이션 적용) ## 레인저 애니메이션 갱신 제어 (Draw 애니메이션 적용)
func _update_animations() -> void: func _update_animations() -> void:
if not _model_anim_player: if not _model_anim_player:
......
...@@ -2,6 +2,11 @@ class_name StrikerUnit ...@@ -2,6 +2,11 @@ class_name StrikerUnit
extends BaseUnit extends BaseUnit
## Striker 유닛 고유 동작 및 비주얼 제어 클래스 ## Striker 유닛 고유 동작 및 비주얼 제어 클래스
const STRIKER_ATTACK_ANIM: String = "Slash"
const STRIKER_RUN_ANIM: String = "Running"
const STRIKER_WALK_ANIM: String = "Walking"
const STRIKER_IDLE_ANIM: String = "Idle"
func _setup_unit_visuals() -> void: func _setup_unit_visuals() -> void:
# 자식 노드 중 스트라이커 모델 노드 명시적 확보 # 자식 노드 중 스트라이커 모델 노드 명시적 확보
var m_name: String = "striker" var m_name: String = "striker"
...@@ -18,7 +23,6 @@ func _setup_unit_visuals() -> void: ...@@ -18,7 +23,6 @@ func _setup_unit_visuals() -> void:
# 애니메이션 플레이어 및 라이브러리 연동 # 애니메이션 플레이어 및 라이브러리 연동
_model_anim_player = UnitHelper.find_animation_player(_model_instance) _model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player: if _model_anim_player:
print("StrikerUnit: Linked pre-configured model & AnimationPlayer: ", m_name)
var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres" var anim_lib_path: String = "res://assets/models/units/" + m_name + "/" + m_name + "_animations.tres"
if ResourceLoader.exists(anim_lib_path): if ResourceLoader.exists(anim_lib_path):
...@@ -28,9 +32,11 @@ func _setup_unit_visuals() -> void: ...@@ -28,9 +32,11 @@ func _setup_unit_visuals() -> void:
_model_anim_player.remove_animation_library("") _model_anim_player.remove_animation_library("")
if not _model_anim_player.has_animation_library(m_name): if not _model_anim_player.has_animation_library(m_name):
_model_anim_player.add_animation_library(m_name, lib) _model_anim_player.add_animation_library(m_name, lib)
print("StrikerUnit: Loaded and registered ", m_name, " animations library successfully.")
UnitHelper.clean_root_animation_tracks(_model_anim_player) UnitHelper.clean_root_animation_tracks(_model_anim_player)
func _get_attack_animation_name(_model_name: String) -> String:
return _model_name + "/" + STRIKER_ATTACK_ANIM
## 스트라이커 애니메이션 갱신 제어 ## 스트라이커 애니메이션 갱신 제어
func _update_animations() -> void: func _update_animations() -> void:
if not _model_anim_player: if not _model_anim_player:
......
...@@ -97,7 +97,6 @@ static func attach_weapon(model_instance: Node3D, unit_data: UnitData) -> void: ...@@ -97,7 +97,6 @@ static func attach_weapon(model_instance: Node3D, unit_data: UnitData) -> void:
# 지정된 본이 존재하는지 확인 # 지정된 본이 존재하는지 확인
var bone_idx: int = skeleton.find_bone(bone_name) var bone_idx: int = skeleton.find_bone(bone_name)
if bone_idx == -1: if bone_idx == -1:
print("UnitHelper: Bone '", bone_name, "' not found in skeleton.")
return return
# 이미 장착된 무기가 있는지 확인하여 제거 (중복 장착 방지) # 이미 장착된 무기가 있는지 확인하여 제거 (중복 장착 방지)
...@@ -123,7 +122,6 @@ static func attach_weapon(model_instance: Node3D, unit_data: UnitData) -> void: ...@@ -123,7 +122,6 @@ static func attach_weapon(model_instance: Node3D, unit_data: UnitData) -> void:
# 무기 텍스처 매핑 # 무기 텍스처 매핑
apply_skin(weapon_inst, texture_path) apply_skin(weapon_inst, texture_path)
bone_attachment.add_child(weapon_inst) bone_attachment.add_child(weapon_inst)
print("UnitHelper: Attached weapon successfully. Unit: ", unit_data.unit_name, ", Weapon: ", weapon_scene_path.get_file())
## 모델 스켈레톤에서 특정 BoneAttachment3D 노드를 반환 (발사 위치 계산 등 외부 활용) ## 모델 스켈레톤에서 특정 BoneAttachment3D 노드를 반환 (발사 위치 계산 등 외부 활용)
static func get_bone_attachment(model_instance: Node3D, attachment_name: String) -> BoneAttachment3D: static func get_bone_attachment(model_instance: Node3D, attachment_name: String) -> BoneAttachment3D:
...@@ -160,4 +158,3 @@ static func clean_root_animation_tracks(anim_player: AnimationPlayer) -> void: ...@@ -160,4 +158,3 @@ static func clean_root_animation_tracks(anim_player: AnimationPlayer) -> void:
for track_idx: int in tracks_to_remove: for track_idx: int in tracks_to_remove:
anim.remove_track(track_idx) anim.remove_track(track_idx)
print("UnitHelper: Cleaned root and Armature transform tracks to preserve character body rotation.")
...@@ -56,7 +56,6 @@ func _ready() -> void: ...@@ -56,7 +56,6 @@ func _ready() -> void:
# 선택된 소환사 데이터가 존재하는지 검증 (디버그 모드 대안 포함) # 선택된 소환사 데이터가 존재하는지 검증 (디버그 모드 대안 포함)
if not GameManager.selected_summoner_data: if not GameManager.selected_summoner_data:
print("HUD: No summoner selected in GameManager. Redirecting to selection screen.")
SceneChanger.change_scene_to_file("res://src/ui/summoner_selection/summoner_selection.tscn") SceneChanger.change_scene_to_file("res://src/ui/summoner_selection/summoner_selection.tscn")
return return
...@@ -169,7 +168,6 @@ func _on_spawn_pressed() -> void: ...@@ -169,7 +168,6 @@ func _on_spawn_pressed() -> void:
if new_unit is Node3D: if new_unit is Node3D:
(new_unit as Node3D).global_position = spawn_offset (new_unit as Node3D).global_position = spawn_offset
print("Spawned random unit: ", chosen_template.unit_name, " at position ", spawn_offset)
# 상시 노출되는 게임 재시작 버튼 클릭 처리 # 상시 노출되는 게임 재시작 버튼 클릭 처리
func _on_restart_game_pressed() -> void: func _on_restart_game_pressed() -> void:
...@@ -328,13 +326,11 @@ func _on_summon_hero_pressed() -> void: ...@@ -328,13 +326,11 @@ func _on_summon_hero_pressed() -> void:
if GameManager.spend_gold(HERO_SPAWN_COST): if GameManager.spend_gold(HERO_SPAWN_COST):
# 현재 영웅 템플릿 중 첫 번째(Dwarf Hero) 선정 # 현재 영웅 템플릿 중 첫 번째(Dwarf Hero) 선정
if DataManager.hero_templates.is_empty(): if DataManager.hero_templates.is_empty():
print("HUD Error: No hero templates registered in DataManager!")
return return
var chosen_template: UnitData = DataManager.hero_templates[0] var chosen_template: UnitData = DataManager.hero_templates[0]
var hero_scene: PackedScene = load("res://src/entities/units/hero/dwarf/dwarf_hero.tscn") as PackedScene var hero_scene: PackedScene = load("res://src/entities/units/hero/dwarf/dwarf_hero.tscn") as PackedScene
if not hero_scene: if not hero_scene:
print("HUD Error: Failed to load dwarf_hero.tscn!")
return return
var new_hero: BaseHero = hero_scene.instantiate() as BaseHero var new_hero: BaseHero = hero_scene.instantiate() as BaseHero
...@@ -354,7 +350,6 @@ func _on_summon_hero_pressed() -> void: ...@@ -354,7 +350,6 @@ func _on_summon_hero_pressed() -> void:
new_hero.global_position = spawn_pos new_hero.global_position = spawn_pos
_active_hero = new_hero _active_hero = new_hero
print("Spawned Hero: Dwarf Hero at ", spawn_pos)
# 영웅 좌측 상단 HUD 상태창 구성 # 영웅 좌측 상단 HUD 상태창 구성
_setup_hero_ui(new_hero) _setup_hero_ui(new_hero)
......
...@@ -21,6 +21,5 @@ func _on_select_stage_2_pressed() -> void: ...@@ -21,6 +21,5 @@ func _on_select_stage_2_pressed() -> void:
_transition_to_main_game() _transition_to_main_game()
func _transition_to_main_game() -> void: func _transition_to_main_game() -> void:
print("StageSelection: Transitioning with stage_id: ", GameManager.selected_stage_id, " (Map: ", GameManager.selected_stage_map_path, ")")
# SceneChanger 페이드 트랜지션 시스템을 활용하여 메인 게임 씬으로 이동 # SceneChanger 페이드 트랜지션 시스템을 활용하여 메인 게임 씬으로 이동
SceneChanger.change_scene_to_file("res://src/core/main.tscn") SceneChanger.change_scene_to_file("res://src/core/main.tscn")
...@@ -47,6 +47,5 @@ func _on_select_warrior_pressed() -> void: ...@@ -47,6 +47,5 @@ func _on_select_warrior_pressed() -> void:
_transition_to_main_game() _transition_to_main_game()
func _transition_to_main_game() -> void: func _transition_to_main_game() -> void:
print("SummonerSelection: Selected summoner: ", GameManager.selected_summoner_data.summoner_name)
# 페이지 이동: 소환사 선택 완료 후 스테이지 선택 씬으로 이동 # 페이지 이동: 소환사 선택 완료 후 스테이지 선택 씬으로 이동
SceneChanger.change_scene_to_file("res://src/ui/stage_selection/stage_selection.tscn") SceneChanger.change_scene_to_file("res://src/ui/stage_selection/stage_selection.tscn")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment