Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
L
ldefense
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Packages
Packages
Container Registry
Analytics
CI / CD Analytics
Repository Analytics
Value Stream Analytics
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Gavin An
ldefense
Commits
ae4c3827
Commit
ae4c3827
authored
Jul 16, 2026
by
Gavin An
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
공격 애니매이션 분리
parent
92f679a6
Changes
23
Show whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
68 additions
and
113 deletions
+68
-113
sandbox/fit_sandbox.gd
sandbox/fit_sandbox.gd
+0
-8
scratch_test.gd
scratch_test.gd
+0
-5
src/autoload/game_manager.gd
src/autoload/game_manager.gd
+0
-2
src/core/main.gd
src/core/main.gd
+0
-1
src/core/stage_manager.gd
src/core/stage_manager.gd
+0
-6
src/core/unit_controller.gd
src/core/unit_controller.gd
+0
-6
src/entities/monsters/base_monster.gd
src/entities/monsters/base_monster.gd
+0
-2
src/entities/monsters/noble_man/noble_man_monster.gd
src/entities/monsters/noble_man/noble_man_monster.gd
+0
-3
src/entities/monsters/skeleton/skeleton_monster.gd
src/entities/monsters/skeleton/skeleton_monster.gd
+1
-6
src/entities/monsters/zombie/zombie_monster.gd
src/entities/monsters/zombie/zombie_monster.gd
+1
-7
src/entities/summoner/summoner.gd
src/entities/summoner/summoner.gd
+0
-10
src/entities/units/base_unit.gd
src/entities/units/base_unit.gd
+17
-19
src/entities/units/guardian/guardian_unit.gd
src/entities/units/guardian/guardian_unit.gd
+7
-2
src/entities/units/guardian/guardian_unit.tscn
src/entities/units/guardian/guardian_unit.tscn
+25
-1
src/entities/units/hero/base_hero.gd
src/entities/units/hero/base_hero.gd
+0
-3
src/entities/units/hero/dwarf/dwarf_hero.gd
src/entities/units/hero/dwarf/dwarf_hero.gd
+0
-13
src/entities/units/mage/mage_unit.gd
src/entities/units/mage/mage_unit.gd
+2
-5
src/entities/units/ranger/ranger_unit.gd
src/entities/units/ranger/ranger_unit.gd
+7
-2
src/entities/units/striker/striker_unit.gd
src/entities/units/striker/striker_unit.gd
+8
-2
src/entities/units/unit_helper.gd
src/entities/units/unit_helper.gd
+0
-3
src/ui/hud.gd
src/ui/hud.gd
+0
-5
src/ui/stage_selection/stage_selection.gd
src/ui/stage_selection/stage_selection.gd
+0
-1
src/ui/summoner_selection/summoner_selection.gd
src/ui/summoner_selection/summoner_selection.gd
+0
-1
No files found.
sandbox/fit_sandbox.gd
View file @
ae4c3827
...
@@ -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
"
)
scratch_test.gd
View file @
ae4c3827
...
@@ -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
+
" "
)
src/autoload/game_manager.gd
View file @
ae4c3827
...
@@ -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!"
)
src/core/main.gd
View file @
ae4c3827
...
@@ -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
:
...
...
src/core/stage_manager.gd
View file @
ae4c3827
...
@@ -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
src/core/unit_controller.gd
View file @
ae4c3827
...
@@ -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."
)
src/entities/monsters/base_monster.gd
View file @
ae4c3827
...
@@ -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
()
...
...
src/entities/monsters/noble_man/noble_man_monster.gd
View file @
ae4c3827
...
@@ -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
)
src/entities/monsters/skeleton/skeleton_monster.gd
View file @
ae4c3827
...
@@ -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
:
p
rint
(
"SkeletonMonster: Animation library already registered on model under another name."
)
p
ass
# 초기 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
)
src/entities/monsters/zombie/zombie_monster.gd
View file @
ae4c3827
...
@@ -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
)
src/entities/summoner/summoner.gd
View file @
ae4c3827
...
@@ -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
]
...
...
src/entities/units/base_unit.gd
View file @
ae4c3827
...
@@ -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
:
...
...
src/entities/units/guardian/guardian_unit.gd
View file @
ae4c3827
...
@@ -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
:
...
...
src/entities/units/guardian/guardian_unit.tscn
View file @
ae4c3827
[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"]
src/entities/units/hero/base_hero.gd
View file @
ae4c3827
...
@@ -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
# 스킬 시전 성공 시 쿨타임 가동
# 스킬 시전 성공 시 쿨타임 가동
...
...
src/entities/units/hero/dwarf/dwarf_hero.gd
View file @
ae4c3827
...
@@ -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
src/entities/units/mage/mage_unit.gd
View file @
ae4c3827
...
@@ -5,7 +5,7 @@ extends BaseUnit
...
@@ -5,7 +5,7 @@ extends BaseUnit
## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및
## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및
## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다.
## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다.
const
MAGE_ATTACK_ANIM
:
String
=
"C
ASTING
"
const
MAGE_ATTACK_ANIM
:
String
=
"C
asting
"
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
:
...
...
src/entities/units/ranger/ranger_unit.gd
View file @
ae4c3827
...
@@ -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
:
...
...
src/entities/units/striker/striker_unit.gd
View file @
ae4c3827
...
@@ -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
:
...
...
src/entities/units/unit_helper.gd
View file @
ae4c3827
...
@@ -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."
)
src/ui/hud.gd
View file @
ae4c3827
...
@@ -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
)
...
...
src/ui/stage_selection/stage_selection.gd
View file @
ae4c3827
...
@@ -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"
)
src/ui/summoner_selection/summoner_selection.gd
View file @
ae4c3827
...
@@ -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"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment