Commit 589d7d59 authored by Gavin An's avatar Gavin An

영웅 시스템 추가

parent d155dc71
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cd6ffpkexhiql"
path="res://.godot/imported/dwarf.fbx-27c324cea2bfcd67340733ff5df13dd1.scn"
[deps]
source_file="res://assets/models/hero/dwarf/dwarf.fbx"
dest_files=["res://.godot/imported/dwarf.fbx-27c324cea2bfcd67340733ff5df13dd1.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c80w7d4fpvj4"
path.s3tc="res://.godot/imported/dwarf_0.png-a19cdb5d63853fe1900cd65a3edf2068.s3tc.ctex"
path.etc2="res://.godot/imported/dwarf_0.png-a19cdb5d63853fe1900cd65a3edf2068.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "dbb60c8a2b727899d9f295a090d94ab2"
}
[deps]
source_file="res://assets/models/hero/dwarf/dwarf_0.png"
dest_files=["res://.godot/imported/dwarf_0.png-a19cdb5d63853fe1900cd65a3edf2068.s3tc.ctex", "res://.godot/imported/dwarf_0.png-a19cdb5d63853fe1900cd65a3edf2068.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
This source diff could not be displayed because it is too large. You can view the blob instead.
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dsbt11ss58cj6"
path="res://.godot/imported/orcish_axe.glb-d573335f34c5e7ec2c033333f3121a40.scn"
[deps]
source_file="res://assets/models/items/weapons/orcish_axe/orcish_axe.glb"
dest_files=["res://.godot/imported/orcish_axe.glb-d573335f34c5e7ec2c033333f3121a40.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dvfqbajqbarbo"
path.s3tc="res://.godot/imported/orcish_axe_Baked_BaseColor.png-93011b66af7e42565859109e05e41b28.s3tc.ctex"
path.etc2="res://.godot/imported/orcish_axe_Baked_BaseColor.png-93011b66af7e42565859109e05e41b28.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "3ac5e6ee528fe4e227b66bc7aa01cdb7"
}
[deps]
source_file="res://assets/models/items/weapons/orcish_axe/orcish_axe_Baked_BaseColor.png"
dest_files=["res://.godot/imported/orcish_axe_Baked_BaseColor.png-93011b66af7e42565859109e05e41b28.s3tc.ctex", "res://.godot/imported/orcish_axe_Baked_BaseColor.png-93011b66af7e42565859109e05e41b28.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d1f5klhem6irc"
path="res://.godot/imported/dwarf_portrait.jpg-88098d38151fd8b2e19b3e7494307fcc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/hero/dwarf_portrait.jpg"
dest_files=["res://.godot/imported/dwarf_portrait.jpg-88098d38151fd8b2e19b3e7494307fcc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
...@@ -25,7 +25,7 @@ SceneChanger="*res://src/autoload/scene_changer.tscn" ...@@ -25,7 +25,7 @@ SceneChanger="*res://src/autoload/scene_changer.tscn"
window/size/viewport_width=1280 window/size/viewport_width=1280
window/size/viewport_height=720 window/size/viewport_height=720
window/size/mode=3 window/size/mode=2
window/stretch/mode="canvas_items" window/stretch/mode="canvas_items"
window/handheld/orientation=4 window/handheld/orientation=4
......
...@@ -4,11 +4,14 @@ extends Node ...@@ -4,11 +4,14 @@ extends Node
# 유닛 리소스 템플릿 목록 # 유닛 리소스 템플릿 목록
var unit_templates: Array[UnitData] = [] var unit_templates: Array[UnitData] = []
# 영웅 리소스 템플릿 목록
var hero_templates: Array[UnitData] = []
# 몬스터 리소스 템플릿 목록 # 몬스터 리소스 템플릿 목록
var monster_templates: Array[MonsterData] = [] var monster_templates: Array[MonsterData] = []
func _ready() -> void: func _ready() -> void:
_initialize_unit_templates() _initialize_unit_templates()
_initialize_hero_templates()
_initialize_monster_templates() _initialize_monster_templates()
## 5종의 유닛 능력치 데이터 초기 셋업 ## 5종의 유닛 능력치 데이터 초기 셋업
...@@ -62,6 +65,21 @@ func _initialize_unit_templates() -> void: ...@@ -62,6 +65,21 @@ func _initialize_unit_templates() -> void:
unit_templates.append(mage) unit_templates.append(mage)
## 영웅 능력치 데이터 초기 셋업
func _initialize_hero_templates() -> void:
# 1. Dwarf Hero (근접 공격형 강인한 영웅)
var dwarf: UnitData = UnitData.new()
dwarf.unit_id = "Hero_Dwarf"
dwarf.unit_name = "Dwarf Hero"
dwarf.damage = 40.0
dwarf.cooldown = 1.2
dwarf.move_speed = 4.5
dwarf.attack_range = 1.8
dwarf.is_ranged = false
dwarf.unit_color = Color(0.85, 0.12, 0.85, 1.0) # 보랏빛 하이라이트
hero_templates.append(dwarf)
## 몬스터 능력치 데이터 초기 셋업 ## 몬스터 능력치 데이터 초기 셋업
func _initialize_monster_templates() -> void: func _initialize_monster_templates() -> void:
# 1. Noble Man (기본 밸런스형 몬스터) # 1. Noble Man (기본 밸런스형 몬스터)
......
class_name BaseHero
extends BaseUnit
## 영웅의 공통 기반 시스템 정의 스크립트 (BaseHero)
## - BaseUnit을 상속받아 RTS 조작 및 탐색 기능을 공유하며, 레벨 및 스킬 시스템을 추가합니다.
# 영웅 레벨 및 경험치 관련 시그널
signal xp_changed(current_xp: int, needed_xp: int)
signal level_changed(new_level: int)
signal skill_cooldown_changed(skill_slot: String, remaining: float, duration: float)
# 영웅 능력치 한계 상수
const MAX_HERO_LEVEL: int = 20
# 레벨 및 경험치 상태 변수
var level: int = 1:
set(value):
level = value
level_changed.emit(level)
var xp: int = 0
var xp_to_next_level: int = 100
# 스킬 관리용 데이터 사전
var skill_cooldowns: Dictionary = {
"Q": 0.0,
"W": 0.0,
"E": 0.0,
"R": 0.0
}
var skill_max_cooldowns: Dictionary = {
"Q": 6.0,
"W": 12.0,
"E": 15.0,
"R": 50.0
}
var skill_mana_costs: Dictionary = {
"Q": 0,
"W": 0,
"E": 0,
"R": 0
}
# 3D 이중 선택 링 캐싱
@onready var _selection_ring_outer: MeshInstance3D = $SelectionRingOuter
func _ready() -> void:
super._ready()
# 영웅 그룹에 별도로 등록
add_to_group("heroes")
# 레벨 1 경험치 요구량 설정
xp_to_next_level = _calculate_xp_requirement(level)
xp_changed.emit(xp, xp_to_next_level)
level_changed.emit(level)
func _physics_process(delta: float) -> void:
super._physics_process(delta)
# 스킬 쿨타임 타이머 실시간 차감
for slot: String in skill_cooldowns.keys():
if skill_cooldowns[slot] > 0.0:
skill_cooldowns[slot] = maxf(0.0, skill_cooldowns[slot] - delta)
skill_cooldown_changed.emit(slot, skill_cooldowns[slot], skill_max_cooldowns[slot])
# 영웅이 선택되었을 때 이중 선택 링을 서로 반대 방향으로 회전시켜 세련된 연출 가동
if is_selected:
if _selection_ring:
_selection_ring.rotate_y(delta * 1.5)
if _selection_ring_outer:
_selection_ring_outer.rotate_y(-delta * 2.0)
## 경험치 획득 처리 함수
func gain_xp(amount: int) -> void:
if level >= MAX_HERO_LEVEL:
return
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:
xp -= xp_to_next_level
_level_up()
xp_to_next_level = _calculate_xp_requirement(level)
xp_changed.emit(xp, xp_to_next_level)
## 레벨업 시 능력치 증가 및 연출
func _level_up() -> void:
level += 1
print("★ LEVEL UP ★ Hero ", name, " reached Level ", level, "!")
# 레벨당 공격력(damage) 15% 및 사거리 등 추가 보강
if unit_data:
# 영웅은 런타임에 기획 데이터를 직접 스케일업
unit_data.damage = unit_data.damage + 5.0
unit_data.move_speed = unit_data.move_speed + 0.1
# 레벨업 시 체력을 가득 채우거나 특정 보상 지급 연출 가능
# 3D 공간 상에 레벨업 이펙트를 출력하는 코드 등 보강
_play_level_up_visual_effect()
## 레벨업 기하급수 경험치 연산 공식 (레벨 * 100)
func _calculate_xp_requirement(lvl: int) -> int:
return lvl * 100
## 레벨업 비주얼 파티클 연출용 가상 함수
func _play_level_up_visual_effect() -> void:
# 선택 링의 에너지를 잠시 폭발시킴
if _selection_ring_outer:
var mat: StandardMaterial3D = _selection_ring_outer.mesh.material as StandardMaterial3D
if mat:
var tween: Tween = create_tween()
tween.tween_property(mat, "emission_energy_multiplier", 10.0, 0.2)
tween.tween_property(mat, "emission_energy_multiplier", 2.0, 0.5)
## 키보드 스킬 단축키 입력 수신 처리
func _unhandled_input(event: InputEvent) -> void:
if GameManager.is_game_over or not is_selected:
return
if event is InputEventKey and event.is_pressed() and not event.is_echo():
var key_event: InputEventKey = event as InputEventKey
match key_event.keycode:
KEY_Q:
use_skill("Q")
KEY_W:
use_skill("W")
KEY_E:
use_skill("E")
KEY_R:
use_skill("R")
## 스킬 시도 및 쿨타임 검증
func use_skill(slot: String) -> void:
if skill_cooldowns.get(slot, 0.0) > 0.0:
print("Skill ", slot, " is on cooldown! (", skill_cooldowns[slot], "s remaining)")
return
# 스킬 시전 성공 시 쿨타임 가동
var success: bool = false
match slot:
"Q": success = _cast_skill_q()
"W": success = _cast_skill_w()
"E": success = _cast_skill_e()
"R": success = _cast_skill_r()
if success:
skill_cooldowns[slot] = skill_max_cooldowns[slot]
skill_cooldown_changed.emit(slot, skill_cooldowns[slot], skill_max_cooldowns[slot])
# 가상 함수 정의 (자식 클래스에서 영웅 스펙에 맞춰 재정의)
func _cast_skill_q() -> bool: return false
func _cast_skill_w() -> bool: return false
func _cast_skill_e() -> bool: return false
func _cast_skill_r() -> bool: return false
## 선택 상태 하이라이트 오버라이드
func select() -> void:
super.select()
if _selection_ring_outer:
_selection_ring_outer.visible = true
func deselect() -> void:
super.deselect()
if _selection_ring_outer:
_selection_ring_outer.visible = false
[gd_scene format=3 uid="uid://lswmiam1ftlw"]
[ext_resource type="PackedScene" uid="uid://cd6ffpkexhiql" path="res://assets/models/hero/dwarf/dwarf.fbx" id="1_l6u5b"]
[node name="dwarf" unique_id=290391373 instance=ExtResource("1_l6u5b")]
class_name DwarfHero
extends BaseHero
## 드워프 영웅 유닛 제어 클래스 (DwarfHero)
## - 4개의 고유 스킬(Q: 강격, W: 전장의 가속, E: 천둥벼락, R: 광폭화)을 실구현하고 애니메이션을 연동합니다.
# 버프 상태 추적 변수
var _is_avatar_active: bool = false
var _original_damage: float = 0.0
var _original_scale: Vector3 = Vector3.ONE
func _setup_unit_visuals() -> void:
# 자식 노드 중 드워프 3D 모델 노드 이름을 명시하여 인스턴스를 확보합니다.
var m_name: String = "dwarf"
var node: Node3D = get_node_or_null(m_name) as Node3D
if not node:
push_error("DwarfHero: Model node '" + m_name + "' not found under children.")
return
_model_instance = node
_original_scale = scale
# 드워프 영웅 전용 스킨 이미지와 무기 메쉬를 결합합니다.
UnitHelper.apply_skin(_model_instance, "res://assets/models/hero/dwarf/dwarf_0.png")
UnitHelper.attach_weapon(_model_instance, unit_data)
# dwarf.fbx 내에 내장된 AnimationPlayer 노드를 캐싱합니다.
_model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player:
print("DwarfHero: Linked embedded model & AnimationPlayer: ", m_name)
## 드워프 영웅 애니메이션 갱신 제어
func _update_animations() -> void:
if not _model_anim_player:
return
var has_custom_anims: bool = _model_anim_player.has_animation("Idle")
var target_anim: String = ""
var play_speed: float = 1.0
var blend_time: float = 0.25
if has_custom_anims:
# fbx 내장 애니메이션 이름 매핑 (Attack, Running, Idle_11)
var attack_anim: String = "Attack"
var run_anim: String = "Running"
var idle_anim: String = "Idle"
# 1. 공격 중일 때 (공격 선딜/모션 락 타이머가 작동 중인 경우)
if _attack_lock_timer > 0.0:
var base_speed: float = 1.2
if unit_data and unit_data.cooldown < 0.6:
base_speed = 0.6 / unit_data.cooldown
play_speed = clampf(base_speed, 1.0, 3.0)
target_anim = attack_anim
blend_time = 0.1
# 2. 이동 중일 때 (속도 벡터 크기가 0.1보다 클 경우)
elif velocity.length() > 0.1:
target_anim = run_anim
play_speed = 1.1
# 3. 그 외 기본 대기 상태일 때
else:
target_anim = idle_anim
play_speed = 1.0
if target_anim != "" and _model_anim_player.current_animation != target_anim:
_model_anim_player.play(target_anim, blend_time, play_speed)
else:
# Fallback 처리
var fallback_anim: String = "Armature|Armature|Basic_Jump|baselayer"
if not _model_anim_player.has_animation(fallback_anim):
return
if _attack_lock_timer > 0.0:
target_anim = fallback_anim
play_speed = 2.5
elif velocity.length() > 0.1:
target_anim = fallback_anim
play_speed = 1.5
else:
target_anim = ""
_model_anim_player.stop()
if target_anim != "" and _model_anim_player.current_animation != target_anim:
_model_anim_player.play(target_anim, blend_time, play_speed)
# ==========================================
# 영웅 고유 스킬 실구현
# ==========================================
## Q 스킬: 강격 (Heavy Strike)
## - 대상에게 강력한 피해(공격력의 3배)를 가하고 3초간 기절시킵니다.
func _cast_skill_q() -> bool:
if not _target_monster or not is_instance_valid(_target_monster):
print("Q Skill Failed: No target monster to strike!")
return false
var dist: float = global_position.distance_to(_target_monster.global_position)
if dist > unit_data.attack_range * 1.5:
print("Q Skill Failed: Target is too far!")
return false
print("Dwarf Cast Q: Heavy Strike on ", _target_monster.name)
# 공격 락 시간 임시 설정 및 공격 애니메이션 강제 재생
_attack_lock_timer = 0.8
_update_animations()
# 타겟 데미지 가하기 및 기절 상태 부여
var target_ref: Node3D = _target_monster
var damage_amount: float = unit_data.damage * 3.0
# 몬스터의 현재 속도를 백업하고 0으로 만들어 기절 상태로 만듭니다.
var orig_speed: float = target_ref.get("speed")
target_ref.set("speed", 0.0)
if target_ref.has_method("take_damage"):
target_ref.call("take_damage", damage_amount)
# 3초 후 기절 상태 해제 타이머 가동
get_tree().create_timer(3.0).timeout.connect(func() -> void:
if is_instance_valid(target_ref) and not target_ref.get("is_dead"):
target_ref.set("speed", orig_speed)
print("Monster ", target_ref.name, " recovered from Stun.")
)
# 이펙트 연출
if _selection_ring:
var mat = _selection_ring.mesh.material as StandardMaterial3D
if mat:
var tween = create_tween()
tween.tween_property(mat, "emission_energy_multiplier", 12.0, 0.1)
tween.tween_property(mat, "emission_energy_multiplier", 3.0, 0.4)
return true
## W 스킬: 전장의 가속 (Battle Haste)
## - 영웅 주변 10m 내 모든 아군 유닛의 공격 속도 배율을 1.5배로 5초간 강화합니다.
func _cast_skill_w() -> bool:
print("Dwarf Cast W: Battle Haste activated!")
var affected_units: Array[Node] = []
var all_units = get_tree().get_nodes_in_group("units")
for unit: Node in all_units:
if is_instance_valid(unit) and unit is Node3D and unit != self:
var dist: float = global_position.distance_to(unit.global_position)
if dist <= 10.0:
# 아군 공격 속도 배율 임시 강화 (base_unit.gd의 attack_speed_mult 참고)
unit.set("attack_speed_mult", 1.5)
affected_units.append(unit)
print(" Hasted unit: ", unit.name)
# 5초 뒤 버프 해제
get_tree().create_timer(5.0).timeout.connect(func() -> void:
for unit: Node in affected_units:
if is_instance_valid(unit):
unit.set("attack_speed_mult", 1.0)
print("Battle Haste duration ended. Stats restored.")
)
# 비주얼 스파크 효과
if _selection_ring_outer:
var mat = _selection_ring_outer.mesh.material as StandardMaterial3D
if mat:
var tween = create_tween()
tween.tween_property(mat, "emission_energy_multiplier", 8.0, 0.2)
tween.tween_property(mat, "emission_energy_multiplier", 2.0, 0.5)
return true
## E 스킬: 천둥벼락 (Thunder Clap)
## - 주변 8m 내 모든 몬스터에게 1.5배 범위 피해를 입히고 3초간 속도를 50% 느리게 만듭니다.
func _cast_skill_e() -> bool:
print("Dwarf Cast E: Thunder Clap triggered!")
var monsters = get_tree().get_nodes_in_group("monsters")
var damage_amount: float = unit_data.damage * 1.5
for monster: Node in monsters:
if is_instance_valid(monster) and monster is Node3D:
var m_3d = monster as Node3D
var dist: float = global_position.distance_to(m_3d.global_position)
if dist <= 8.0:
if m_3d.has_method("take_damage"):
m_3d.call("take_damage", damage_amount)
# 몬스터의 속도를 3초 동안 50% 둔화시킵니다.
var orig_speed: float = m_3d.get("speed")
m_3d.set("speed", orig_speed * 0.5)
var m_ref = m_3d
get_tree().create_timer(3.0).timeout.connect(func() -> void:
if is_instance_valid(m_ref) and not m_ref.get("is_dead"):
m_ref.set("speed", orig_speed)
)
# 천둥소리 느낌의 이중 링 파동 연출
if _selection_ring and _selection_ring_outer:
var mat1 = _selection_ring.mesh.material as StandardMaterial3D
var mat2 = _selection_ring_outer.mesh.material as StandardMaterial3D
if mat1 and mat2:
var tween = create_tween().set_parallel(true)
tween.tween_property(mat1, "emission_energy_multiplier", 15.0, 0.15)
tween.tween_property(mat2, "emission_energy_multiplier", 15.0, 0.15)
var tween_back = create_tween().set_parallel(true)
tween_back.tween_property(mat1, "emission_energy_multiplier", 3.0, 0.6)
tween_back.tween_property(mat2, "emission_energy_multiplier", 2.0, 0.6)
return true
## R 스킬: 드워프의 광폭화 (Avatar) - 궁극기
## - 10초 동안 영웅이 거대화(스케일 1.8배)되며, 공격력이 2배 증가하고 모든 체력을 완전 회복합니다.
func _cast_skill_r() -> bool:
if _is_avatar_active:
print("Avatar is already active!")
return false
print("Dwarf Cast R: AVATAR MODE ACTIVATE!")
_is_avatar_active = true
_original_damage = unit_data.damage
# 공격력 2배 상향 및 1.8배로 크기 확장
unit_data.damage = _original_damage * 2.0
var tween = create_tween().set_parallel(true)
tween.tween_property(self, "scale", _original_scale * 1.8, 0.5)
# 레벨업 효과 발광 극대화
if _selection_ring_outer:
var mat = _selection_ring_outer.mesh.material as StandardMaterial3D
if mat:
tween.tween_property(mat, "emission_energy_multiplier", 12.0, 0.5)
# 10초 뒤 광폭화 해제 및 크기/스펙 원상복귀
get_tree().create_timer(10.0).timeout.connect(func() -> void:
_is_avatar_active = false
if unit_data:
unit_data.damage = _original_damage
var tween_back = create_tween().set_parallel(true)
tween_back.tween_property(self, "scale", _original_scale, 0.5)
if _selection_ring_outer:
var mat = _selection_ring_outer.mesh.material as StandardMaterial3D
if mat:
tween_back.tween_property(mat, "emission_energy_multiplier", 2.0, 0.5)
print("Avatar Mode deactivated. Stats restored.")
)
return true
[gd_scene format=3 uid="uid://np5fiyv02pk"]
[ext_resource type="PackedScene" uid="uid://lswmiam1ftlw" path="res://src/entities/hero/dwarf/dwarf.tscn" id="1_b0qj8"]
[ext_resource type="Script" uid="uid://dsckp7uaawijh" path="res://src/entities/hero/dwarf/dwarf_hero.gd" id="1_cthrw"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_pe3j2"]
radius = 0.4
height = 1.2
[sub_resource type="CapsuleMesh" id="CapsuleMesh_0yhxm"]
radius = 0.4
height = 1.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_71vph"]
albedo_color = Color(0.85, 0.12, 0.85, 1)
emission_enabled = true
emission = Color(0.85, 0.12, 0.85, 1)
emission_energy_multiplier = 3.0
[sub_resource type="TorusMesh" id="TorusMesh_gniq3"]
material = SubResource("StandardMaterial3D_71vph")
inner_radius = 0.65
outer_radius = 0.7
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ueb8j"]
albedo_color = Color(1, 0.85, 0, 1)
emission_enabled = true
emission = Color(1, 0.85, 0, 1)
emission_energy_multiplier = 2.0
[sub_resource type="TorusMesh" id="TorusMesh_fcx2y"]
material = SubResource("StandardMaterial3D_ueb8j")
inner_radius = 0.75
outer_radius = 0.78
[node name="DwarfHero" type="CharacterBody3D" unique_id=1158278965]
script = ExtResource("1_cthrw")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1912534245]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_pe3j2")
[node name="VisualMesh" type="MeshInstance3D" parent="." unique_id=383881199]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
visible = false
mesh = SubResource("CapsuleMesh_0yhxm")
[node name="SelectionRing" type="MeshInstance3D" parent="." unique_id=2072891932]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0)
visible = false
mesh = SubResource("TorusMesh_gniq3")
[node name="SelectionRingOuter" type="MeshInstance3D" parent="." unique_id=2127703735]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.01, 0)
visible = false
mesh = SubResource("TorusMesh_fcx2y")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=536619037]
[node name="dwarf" parent="." unique_id=290391373 instance=ExtResource("1_b0qj8")]
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
[editable path="dwarf"]
...@@ -96,10 +96,18 @@ func _die() -> void: ...@@ -96,10 +96,18 @@ func _die() -> void:
# 골드 보상 획득 # 골드 보상 획득
GameManager.add_gold(gold_reward) GameManager.add_gold(gold_reward)
# 영웅에게 경험치 지급 (모든 생존한 영웅이 경험치를 얻음)
var heroes: Array[Node] = get_tree().get_nodes_in_group("heroes")
for hero: Node in heroes:
if is_instance_valid(hero) and hero.has_method("gain_xp"):
# 골드 획득량과 동일한 비율로 경험치를 지급하되, 최소 15 XP 보장
var xp_reward: int = max(15, gold_reward)
hero.call("gain_xp", xp_reward)
# 필드 활성 몬스터 수 감소 # 필드 활성 몬스터 수 감소
GameManager.decrement_monster_count() GameManager.decrement_monster_count()
print("Monster ", name, " died! Rewarded ", gold_reward, " gold.") print("Monster ", name, " died! Rewarded ", gold_reward, " gold and granted XP.")
# 씬에서 제거 # 씬에서 제거
queue_free() queue_free()
......
...@@ -205,7 +205,7 @@ func _find_nearest_target() -> void: ...@@ -205,7 +205,7 @@ func _find_nearest_target() -> void:
if nearest_monster: if nearest_monster:
_target_monster = nearest_monster _target_monster = nearest_monster
## 타겟에게 격 가함 ## 타겟에게 격 가함
func _try_attack() -> void: func _try_attack() -> void:
if _attack_timer > 0.0 or not _target_monster: if _attack_timer > 0.0 or not _target_monster:
return return
...@@ -282,7 +282,7 @@ func _show_attack_visual() -> void: ...@@ -282,7 +282,7 @@ func _show_attack_visual() -> void:
else: else:
print("Unit ", name, " strikes ", _target_monster.name) print("Unit ", name, " strikes ", _target_monster.name)
## 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold) ## h 버튼을 누르면 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold)
func hold_position() -> void: func hold_position() -> void:
_is_holding = true _is_holding = true
_target_monster = null _target_monster = null
......
[gd_scene format=3 uid="uid://qdvm4bgh48q6"] [gd_scene format=3 uid="uid://qdvm4bgh48q6"]
[ext_resource type="Script" path="res://src/entities/units/ranger/ranger_unit.gd" id="1_base_unit"] [ext_resource type="Script" uid="uid://dmo2ypkevm8ma" path="res://src/entities/units/ranger/ranger_unit.gd" id="1_base_unit"]
[ext_resource type="PackedScene" path="res://src/entities/units/ranger/ranger.tscn" id="2_ranger_scene"] [ext_resource type="PackedScene" uid="uid://ch457fojl2l5g" path="res://src/entities/units/ranger/ranger.tscn" id="2_ranger_scene"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.4 radius = 0.4
...@@ -42,30 +42,22 @@ mesh = SubResource("TorusMesh_selection") ...@@ -42,30 +42,22 @@ mesh = SubResource("TorusMesh_selection")
[node name="ranger" parent="." unique_id=1235075234 instance=ExtResource("2_ranger_scene")] [node name="ranger" parent="." unique_id=1235075234 instance=ExtResource("2_ranger_scene")]
[node name="Skeleton3D" parent="ranger/Armature" parent_id_path=PackedInt32Array(1235075234, 456601137) index="0" unique_id=1884876701] [node name="Skeleton3D" parent="ranger/Armature" parent_id_path=PackedInt32Array(1235075234, 737057940) index="0" unique_id=641195763]
bones/0/position = Vector3(0.03514373, 0.0034280196, 0.6635236) bones/0/position = Vector3(0.03514373, 0.0034280196, 0.6635236)
bones/0/rotation = Quaternion(0.20349285, -0.5581575, -0.767251, 0.24161276) bones/0/rotation = Quaternion(0.20349285, -0.5581575, -0.767251, 0.24161276)
bones/1/rotation = Quaternion(0.5304922, 0.57593215, -0.61864984, 0.06444053) bones/1/rotation = Quaternion(0.5304922, 0.57593215, -0.61864984, 0.06444053)
bones/2/rotation = Quaternion(0.59826106, 0.1739482, 0.06382933, 0.7795843) bones/2/rotation = Quaternion(0.59826106, 0.1739482, 0.06382933, 0.7795843)
bones/3/rotation = Quaternion(-0.6715887, -0.20650962, 0.092862934, 0.7054777) bones/3/rotation = Quaternion(-0.6715887, -0.20650962, 0.092862934, 0.7054777)
bones/4/rotation = Quaternion(-0.27814108, -0.049392484, 0.057641834, 0.95753616)
bones/5/rotation = Quaternion(0.8261204, 0.0028015971, 0.36372316, 0.4303752) bones/5/rotation = Quaternion(0.8261204, 0.0028015971, 0.36372316, 0.4303752)
bones/6/rotation = Quaternion(0.5529449, -0.009049176, 0.13317788, 0.82245594) bones/6/rotation = Quaternion(0.5529449, -0.009049176, 0.13317788, 0.82245594)
bones/7/rotation = Quaternion(-0.5409971, -0.107743785, 0.04905413, 0.83265066) bones/7/rotation = Quaternion(-0.5409971, -0.107743785, 0.04905413, 0.83265066)
bones/8/rotation = Quaternion(-0.26788568, 0.073798016, -0.031191472, 0.96011364)
bones/9/rotation = Quaternion(0.3045851, 0.05863059, 0.009242283, 0.950634)
bones/10/rotation = Quaternion(-0.025334273, 0.053094994, -0.033819344, 0.997695)
bones/11/rotation = Quaternion(-0.0011097626, -0.049749736, -0.0720563, 0.9961584) bones/11/rotation = Quaternion(-0.0011097626, -0.049749736, -0.0720563, 0.9961584)
bones/12/rotation = Quaternion(0.49171034, 0.6453104, -0.13713944, 0.5683207) bones/12/rotation = Quaternion(0.49171034, 0.6453104, -0.13713944, 0.5683207)
bones/13/rotation = Quaternion(0.28274685, -0.08206125, -0.5247269, 0.79873765) bones/13/rotation = Quaternion(0.28274685, -0.08206125, -0.5247269, 0.79873765)
bones/14/rotation = Quaternion(-0.060595874, -0.09316963, 0.093224026, 0.9894225) bones/14/rotation = Quaternion(-0.060595874, -0.09316963, 0.093224026, 0.9894225)
bones/15/rotation = Quaternion(0.002734622, 0.14336169, 0.053758636, 0.98820543)
bones/16/rotation = Quaternion(-0.22393328, 0.59772503, -0.31282783, -0.7033615) bones/16/rotation = Quaternion(-0.22393328, 0.59772503, -0.31282783, -0.7033615)
bones/17/rotation = Quaternion(0.2881412, 0.53661305, -0.6094496, 0.50753546) bones/17/rotation = Quaternion(0.2881412, 0.53661305, -0.6094496, 0.50753546)
bones/18/rotation = Quaternion(0.43169993, 0.23694667, -0.78221, 0.38162687) bones/18/rotation = Quaternion(0.43169993, 0.23694667, -0.78221, 0.38162687)
bones/19/rotation = Quaternion(-0.033562582, -0.33096007, -0.44027492, 0.8339646)
bones/20/rotation = Quaternion(0.019875612, 0.059954792, -0.025400154, 0.99768)
bones/21/rotation = Quaternion(0.15988994, 0.5659004, -0.5430847, 0.5993756) bones/21/rotation = Quaternion(0.15988994, 0.5659004, -0.5430847, 0.5993756)
bones/22/rotation = Quaternion(-0.23428716, -6.378701e-09, -0.002581273, 0.9721641)
[editable path="ranger"] [editable path="ranger"]
...@@ -2,7 +2,6 @@ class_name StrikerUnit ...@@ -2,7 +2,6 @@ class_name StrikerUnit
extends BaseUnit extends BaseUnit
## Striker 유닛 고유 동작 및 비주얼 제어 클래스 ## Striker 유닛 고유 동작 및 비주얼 제어 클래스
func _setup_unit_visuals() -> void: func _setup_unit_visuals() -> void:
# 자식 노드 중 스트라이커 모델 노드 명시적 확보 # 자식 노드 중 스트라이커 모델 노드 명시적 확보
var m_name: String = "striker" var m_name: String = "striker"
......
This diff is collapsed.
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