Commit 18b3c970 authored by Gavin An's avatar Gavin An

lv2 warrior

parent 62e0200e
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://f43v7kybs83p"
path="res://.godot/imported/legend_sword.glb-a98fe91307c49a71f3b65fdffb7ee586.scn"
[deps]
source_file="res://assets/models/items/weapons/legend_swrod/legend_sword.glb"
dest_files=["res://.godot/imported/legend_sword.glb-a98fe91307c49a71f3b65fdffb7ee586.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
mesh_library/create_categories_from_hierarchy=false
array_mesh/deduplicate_surfaces=true
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
gltf/texture_map_mode=1
[gd_scene format=3 uid="uid://bensic2qqxbib"]
[ext_resource type="PackedScene" uid="uid://f43v7kybs83p" path="res://assets/models/items/weapons/legend_swrod/legend_sword.glb" id="1_ebx1l"]
[node name="legend_sword" unique_id=1326889602 instance=ExtResource("1_ebx1l")]
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5ypuylgdfgn3"
path.s3tc="res://.godot/imported/legend_sword_0.jpg-659e25b356b6cf376a40f247c87402bf.s3tc.ctex"
path.etc2="res://.godot/imported/legend_sword_0.jpg-659e25b356b6cf376a40f247c87402bf.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "02a08007017bbecbd4d7480ab9117321"
}
[deps]
source_file="res://assets/models/items/weapons/legend_swrod/legend_sword_0.jpg"
dest_files=["res://.godot/imported/legend_sword_0.jpg-659e25b356b6cf376a40f247c87402bf.s3tc.ctex", "res://.godot/imported/legend_sword_0.jpg-659e25b356b6cf376a40f247c87402bf.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality_mode=0
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="scene"
importer_version=1
type="PackedScene"
uid="uid://k7vp4cdubqvt"
path="res://.godot/imported/rusty_sword.glb-906449b2fd11bbe94ee17fbee3eb8976.scn"
[deps]
source_file="res://assets/models/items/weapons/rusty_sword/rusty_sword.glb"
dest_files=["res://.godot/imported/rusty_sword.glb-906449b2fd11bbe94ee17fbee3eb8976.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
mesh_library/create_categories_from_hierarchy=false
array_mesh/deduplicate_surfaces=true
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
gltf/texture_map_mode=1
[gd_scene format=3 uid="uid://cyv0wuoolpd3n"]
[ext_resource type="PackedScene" uid="uid://k7vp4cdubqvt" path="res://assets/models/items/weapons/rusty_sword/rusty_sword.glb" id="1_7vbp6"]
[node name="rusty_sword" unique_id=1113078996 instance=ExtResource("1_7vbp6")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6707307, 0)
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dhgv578sq73g"
path.s3tc="res://.godot/imported/rusty_sword_0.jpg-18e7deea34bf6b826ac14392bc2d87d2.s3tc.ctex"
path.etc2="res://.godot/imported/rusty_sword_0.jpg-18e7deea34bf6b826ac14392bc2d87d2.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "ae97b87f90d07c40d77ba185f13096c0"
}
[deps]
source_file="res://assets/models/items/weapons/rusty_sword/rusty_sword_0.jpg"
dest_files=["res://.godot/imported/rusty_sword_0.jpg-18e7deea34bf6b826ac14392bc2d87d2.s3tc.ctex", "res://.godot/imported/rusty_sword_0.jpg-18e7deea34bf6b826ac14392bc2d87d2.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality_mode=0
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://07l7v2m8767p"
path.s3tc="res://.godot/imported/rusty_sword_1.jpg-b013bbad4dd1c908e836baf22ab3b488.s3tc.ctex"
path.etc2="res://.godot/imported/rusty_sword_1.jpg-b013bbad4dd1c908e836baf22ab3b488.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "37a0719eb51b6f9dea5d12e3735a3586"
}
[deps]
source_file="res://assets/models/items/weapons/rusty_sword/rusty_sword_1.jpg"
dest_files=["res://.godot/imported/rusty_sword_1.jpg-b013bbad4dd1c908e836baf22ab3b488.s3tc.ctex", "res://.godot/imported/rusty_sword_1.jpg-b013bbad4dd1c908e836baf22ab3b488.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality_mode=0
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://dydemwrglbqj4"
path.s3tc="res://.godot/imported/rusty_sword_2.jpg-a729c7bc755ee7d8c1478106c6992506.s3tc.ctex"
path.etc2="res://.godot/imported/rusty_sword_2.jpg-a729c7bc755ee7d8c1478106c6992506.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "c35cf8560000a2ce564044ae790328db"
}
[deps]
source_file="res://assets/models/items/weapons/rusty_sword/rusty_sword_2.jpg"
dest_files=["res://.godot/imported/rusty_sword_2.jpg-a729c7bc755ee7d8c1478106c6992506.s3tc.ctex", "res://.godot/imported/rusty_sword_2.jpg-a729c7bc755ee7d8c1478106c6992506.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality_mode=0
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal="res://assets/models/items/weapons/rusty_sword/rusty_sword_2.jpg"
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://cqqfncnk04tek"
path="res://.godot/imported/warrior_lv2.fbx-1f0a2bed21ca10dc3a4c94735414209c.scn"
[deps]
source_file="res://assets/models/units/warrior/lv2/warrior_lv2.fbx"
dest_files=["res://.godot/imported/warrior_lv2.fbx-1f0a2bed21ca10dc3a4c94735414209c.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
mesh_library/create_categories_from_hierarchy=false
array_mesh/deduplicate_surfaces=true
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://bv7ncjpocp61l"
path.s3tc="res://.godot/imported/warrior_lv2_0.png-c93070ee8d30502e322276896b68e664.s3tc.ctex"
path.etc2="res://.godot/imported/warrior_lv2_0.png-c93070ee8d30502e322276896b68e664.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "35564f52a7e717727ba9d3e40a5b2b56"
}
[deps]
source_file="res://assets/models/units/warrior/lv2/warrior_lv2_0.png"
dest_files=["res://.godot/imported/warrior_lv2_0.png-c93070ee8d30502e322276896b68e664.s3tc.ctex", "res://.godot/imported/warrior_lv2_0.png-c93070ee8d30502e322276896b68e664.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/high_quality_mode=0
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.
[gd_scene format=3 uid="uid://c1s1bkt10v0rm"]
[ext_resource type="PackedScene" uid="uid://cqqfncnk04tek" path="res://assets/models/units/warrior/lv2/warrior_lv2.fbx" id="1_24734"]
[ext_resource type="AnimationLibrary" uid="uid://ckbpilct5njdm" path="res://assets/models/units/warrior/lv2/warrior_lv2_anim.tres" id="2_mraeg"]
[node name="warrior_lv2" unique_id=209555916 instance=ExtResource("1_24734")]
transform = Transform3D(-1, 0, 8.742278e-08, 0, 1, 0, -8.742278e-08, 0, -1, 0, 0, 0)
[node name="Skeleton3D" parent="Armature" parent_id_path=PackedInt32Array(300451948) index="0" unique_id=756988671]
bones/0/position = Vector3(0.005554877, -0.009494688, 0.47116813)
bones/0/rotation = Quaternion(0.23249975, -0.26712173, -0.6824087, 0.6394594)
bones/1/rotation = Quaternion(0.7234922, 0.62721527, -0.2680713, -0.10629135)
bones/2/rotation = Quaternion(0.5981388, 0.23558679, 0.21044312, 0.73650706)
bones/3/rotation = Quaternion(-0.6395004, -0.110164024, 0.052672584, 0.75903153)
bones/4/rotation = Quaternion(-0.3774154, -0.076175876, 0.018043175, 0.92272925)
bones/5/rotation = Quaternion(0.86604196, -0.29047525, 0.3781625, 0.15029547)
bones/6/rotation = Quaternion(0.335736, 0.042799514, 0.35975316, 0.8694983)
bones/7/rotation = Quaternion(-0.29481465, 0.06656596, -0.077776246, 0.9500548)
bones/8/rotation = Quaternion(-0.39341575, 0.11448669, 0.0030793007, 0.9121992)
bones/9/rotation = Quaternion(0.43887874, 0.04674071, -0.07517398, 0.89417547)
bones/10/rotation = Quaternion(0.023472205, 0.08232797, -0.006741552, 0.99630606)
bones/11/rotation = Quaternion(-0.053637944, 0.03281779, -0.030403502, 0.9975579)
bones/12/rotation = Quaternion(0.49749446, 0.664029, -0.25682867, 0.49558428)
bones/13/rotation = Quaternion(0.45868546, 0.366621, 0.3251555, 0.74126285)
bones/14/rotation = Quaternion(-0.6462466, -0.18239486, 0.32295483, 0.6669315)
bones/15/rotation = Quaternion(-0.11474693, 0.1382993, -0.17996995, 0.967118)
bones/16/rotation = Quaternion(-0.5316632, 0.56663144, -0.35806727, -0.5177364)
bones/17/rotation = Quaternion(0.54099196, -0.21061343, -0.2466092, 0.7759856)
bones/18/rotation = Quaternion(-0.29649952, 0.005903572, -0.52833754, 0.79555815)
bones/19/rotation = Quaternion(-0.09178817, -0.4545592, 0.0333525, 0.8853466)
bones/20/rotation = Quaternion(0.2600408, 0.079001196, -0.0023141066, 0.9623577)
bones/21/rotation = Quaternion(0.21087794, 0.23234616, -0.1874919, 0.9308021)
bones/22/rotation = Quaternion(-0.16344544, -1.2067508e-08, 0.0022242933, 0.9865499)
bones/23/rotation = Quaternion(0.5279367, -5.4885643e-08, -0.007184475, 0.84925336)
[node name="AnimationPlayer" parent="." index="1" unique_id=2041813614]
libraries/ = ExtResource("2_mraeg")
This source diff could not be displayed because it is too large. You can view the blob instead.
[gd_scene format=3 uid="uid://ctkr27eimsgo1"]
[ext_resource type="Script" uid="uid://bsrwvw8lvd10l" path="res://src/entities/units/unit/warrior/lv2/warrior_lv2_unit.gd" id="1_m3p8c"]
[ext_resource type="PackedScene" uid="uid://c1s1bkt10v0rm" path="res://assets/models/units/warrior/lv2/warrior_lv_2.tscn" id="2_ml4by"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.4
height = 1.2
[sub_resource type="CapsuleMesh" id="CapsuleMesh_unit"]
radius = 0.4
height = 1.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ring"]
albedo_color = Color(0.098039, 0.901961, 0.098039, 1)
emission_enabled = true
emission = Color(0.098039, 0.901961, 0.098039, 1)
emission_energy_multiplier = 2.0
[sub_resource type="TorusMesh" id="TorusMesh_selection"]
material = SubResource("StandardMaterial3D_ring")
inner_radius = 0.65
outer_radius = 0.7
[node name="WarriorLv2FitTest" type="CharacterBody3D" unique_id=319330680]
script = ExtResource("1_m3p8c")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=88758424]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="VisualMesh" type="MeshInstance3D" parent="." unique_id=1091972406]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
visible = false
mesh = SubResource("CapsuleMesh_unit")
[node name="SelectionRing" type="MeshInstance3D" parent="." unique_id=888351282]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0)
mesh = SubResource("TorusMesh_selection")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=1139008088]
[node name="warrior_lv2" parent="." unique_id=1273649626 instance=ExtResource("2_ml4by")]
[editable path="warrior_lv2"]
...@@ -8,18 +8,22 @@ var unit_templates: Array[UnitData] = [] ...@@ -8,18 +8,22 @@ var unit_templates: Array[UnitData] = []
var hero_templates: Array[UnitData] = [] var hero_templates: Array[UnitData] = []
# 몬스터 리소스 템플릿 목록 # 몬스터 리소스 템플릿 목록
var monster_templates: Array[MonsterData] = [] var monster_templates: Array[MonsterData] = []
# 업그레이드 유닛 리소스 템플릿 목록
var upgrade_unit_templates: Array[UnitData] = []
func _ready() -> void: func _ready() -> void:
_initialize_unit_templates() _initialize_unit_templates()
_initialize_upgrade_unit_templates()
_initialize_hero_templates() _initialize_hero_templates()
_initialize_monster_templates() _initialize_monster_templates()
## 5종의 유닛 능력치 데이터 초기 셋업 ## 초기 유닛 능력치 데이터 초기 셋업
func _initialize_unit_templates() -> void: func _initialize_unit_templates() -> void:
# 1. Archer lv1 (원거리 유닛) # 1. Archer lv1 (원거리 유닛)
var archer_lv_1: UnitData = UnitData.new() var archer_lv_1: UnitData = UnitData.new()
archer_lv_1.unit_id = "Unit_Archer_lv1" archer_lv_1.unit_id = "Unit_Archer_lv1"
archer_lv_1.unit_name = "Archer_lv1" archer_lv_1.unit_name = "Archer_lv1"
archer_lv_1.unit_lv = 1
archer_lv_1.damage = 18.0 archer_lv_1.damage = 18.0
archer_lv_1.cooldown = 0.8 archer_lv_1.cooldown = 0.8
archer_lv_1.move_speed = 4.5 archer_lv_1.move_speed = 4.5
...@@ -32,6 +36,7 @@ func _initialize_unit_templates() -> void: ...@@ -32,6 +36,7 @@ func _initialize_unit_templates() -> void:
var mage: UnitData = UnitData.new() var mage: UnitData = UnitData.new()
mage.unit_id = "Unit_Mage" mage.unit_id = "Unit_Mage"
mage.unit_name = "Mage" mage.unit_name = "Mage"
mage.unit_lv = 1
mage.damage = 35.0 mage.damage = 35.0
mage.cooldown = 1.6 mage.cooldown = 1.6
mage.move_speed = 3.5 mage.move_speed = 3.5
...@@ -44,6 +49,7 @@ func _initialize_unit_templates() -> void: ...@@ -44,6 +49,7 @@ func _initialize_unit_templates() -> void:
var warrior_lv1: UnitData = UnitData.new() var warrior_lv1: UnitData = UnitData.new()
warrior_lv1.unit_id = "Unit_Warrior_Lv1" warrior_lv1.unit_id = "Unit_Warrior_Lv1"
warrior_lv1.unit_name = "Warrior Lv1" warrior_lv1.unit_name = "Warrior Lv1"
warrior_lv1.unit_lv = 1
warrior_lv1.damage = 20.0 warrior_lv1.damage = 20.0
warrior_lv1.cooldown = 1.2 warrior_lv1.cooldown = 1.2
warrior_lv1.move_speed = 5.0 warrior_lv1.move_speed = 5.0
...@@ -51,7 +57,28 @@ func _initialize_unit_templates() -> void: ...@@ -51,7 +57,28 @@ func _initialize_unit_templates() -> void:
warrior_lv1.is_ranged = false warrior_lv1.is_ranged = false
warrior_lv1.unit_color = Color(0.55, 0.62, 0.7, 1.0) warrior_lv1.unit_color = Color(0.55, 0.62, 0.7, 1.0)
unit_templates.append(warrior_lv1) unit_templates.append(warrior_lv1)
## 초기 유닛 능력치 데이터 초기 셋업
func _initialize_upgrade_unit_templates() -> void:
var warrior_lv2: UnitData = UnitData.new()
warrior_lv2.unit_id = "Unit_Warrior_Lv2"
warrior_lv2.unit_name = "Warrior Lv2"
warrior_lv2.unit_lv = 2
warrior_lv2.damage = 36.0
warrior_lv2.cooldown = 1.0
warrior_lv2.move_speed = 5.0
warrior_lv2.attack_range = 2.0
warrior_lv2.is_ranged = false
warrior_lv2.unit_color = Color(0.55, 0.62, 0.7, 1.0)
upgrade_unit_templates.append(warrior_lv2)
## ID에 해당하는 업그레이드 유닛 템플릿을 반환합니다.
## 템플릿은 읽기 전용으로 사용하며, 개별 유닛의 임시 변경이 필요할 때만 호출 측에서 복제합니다.
func get_upgrade_unit_template(unit_id: String) -> UnitData:
for template: UnitData in upgrade_unit_templates:
if template.unit_id == unit_id:
return template
return null
## 영웅 능력치 데이터 초기 셋업 ## 영웅 능력치 데이터 초기 셋업
func _initialize_hero_templates() -> void: func _initialize_hero_templates() -> void:
......
...@@ -421,22 +421,18 @@ func _try_move_selected_unit_to_tile(screen_pos: Vector2) -> void: ...@@ -421,22 +421,18 @@ func _try_move_selected_unit_to_tile(screen_pos: Vector2) -> void:
_show_unit_action_buttons() _show_unit_action_buttons()
## 업그레이드 버튼 처리입니다. ## 업그레이드 버튼 처리입니다.
## UnitData가 템플릿 Resource로 공유될 수 있으므로 선택 유닛에만 복제본을 할당한 뒤 능력치를 올립니다. ## 합성 규칙과 Lv2 생성은 각 유닛 스크립트에 위임하고, 컨트롤러는 UI 선택과 비용만 처리합니다.
func _on_upgrade_selected_unit_pressed() -> void: func _on_upgrade_selected_unit_pressed() -> void:
if not _selected_unit or not is_instance_valid(_selected_unit): if not _selected_unit or not is_instance_valid(_selected_unit):
_clear_selected_unit() _clear_selected_unit()
return return
if not _selected_unit is BaseUnit: if not _selected_unit.has_method("can_merge_to_lv2") or not _selected_unit.has_method("merge_to_lv2"):
return return
if not _selected_unit.call("can_merge_to_lv2"):
var selected_base_unit: BaseUnit = _selected_unit as BaseUnit
if not selected_base_unit.unit_data:
return return
if not GameManager.spend_gold(UNIT_UPGRADE_COST): if not GameManager.spend_gold(UNIT_UPGRADE_COST):
return return
if not _selected_unit.call("merge_to_lv2"):
selected_base_unit.unit_data = selected_base_unit.unit_data.duplicate(true) as UnitData GameManager.add_gold(UNIT_UPGRADE_COST)
selected_base_unit.unit_data.damage *= 1.18 return
selected_base_unit.unit_data.attack_range *= 1.04 _clear_selected_unit()
selected_base_unit.unit_data.cooldown = maxf(0.15, selected_base_unit.unit_data.cooldown * 0.94)
selected_base_unit.unit_data.move_speed *= 1.04
...@@ -5,6 +5,8 @@ extends Resource ...@@ -5,6 +5,8 @@ extends Resource
## - Godot의 Resource 클래스를 상속받아 유닛 데이터 템플릿을 생성 및 직렬화하기에 용이합니다. ## - Godot의 Resource 클래스를 상속받아 유닛 데이터 템플릿을 생성 및 직렬화하기에 용이합니다.
## - DataManager에서 각 유닛의 기본 능력치를 정의하고 생성할 때 템플릿 데이터로 활용됩니다. ## - DataManager에서 각 유닛의 기본 능력치를 정의하고 생성할 때 템플릿 데이터로 활용됩니다.
@export var unit_lv: int = 1
## 유닛 고유 식별자 ID (예: "Unit_Warrior_Lv1", "Unit_Mage") ## 유닛 고유 식별자 ID (예: "Unit_Warrior_Lv1", "Unit_Mage")
@export var unit_id: String = "" @export var unit_id: String = ""
......
...@@ -6,6 +6,9 @@ const MODEL_NAME: String = "warrior_lv1" ...@@ -6,6 +6,9 @@ const MODEL_NAME: String = "warrior_lv1"
const WARRIOR_ATTACK_ANIM: String = "Attack" const WARRIOR_ATTACK_ANIM: String = "Attack"
const WARRIOR_RUN_ANIM: String = "Run" const WARRIOR_RUN_ANIM: String = "Run"
const WARRIOR_IDLE_ANIM: String = "Idle" const WARRIOR_IDLE_ANIM: String = "Idle"
const WARRIOR_LV1_ID: String = "Unit_Warrior_Lv1"
const WARRIOR_LV2_ID: String = "Unit_Warrior_Lv2"
const WARRIOR_LV2_SCENE: PackedScene = preload("res://src/entities/units/unit/warrior/lv2/warrior_lv2_unit.tscn")
# 드워프 영웅의 전방 부채꼴 공격 방식을 워리어의 자동 공격에 맞게 사용합니다. # 드워프 영웅의 전방 부채꼴 공격 방식을 워리어의 자동 공격에 맞게 사용합니다.
# 워리어가 자동으로 선택한 주 대상을 바라본 뒤 공격하므로, 해당 방향을 중심으로 # 워리어가 자동으로 선택한 주 대상을 바라본 뒤 공격하므로, 해당 방향을 중심으로
...@@ -97,11 +100,11 @@ func _get_weapon_config() -> Dictionary: ...@@ -97,11 +100,11 @@ func _get_weapon_config() -> Dictionary:
return { return {
"bone_name": "RightHand", "bone_name": "RightHand",
"attachment_name": "RightHandAttachment", "attachment_name": "RightHandAttachment",
"weapon_scene_path": "res://assets/models/items/weapons/old_sword/old_sword.tscn", "weapon_scene_path": "res://assets/models/items/weapons/rusty_sword/rusty_sword.tscn",
"texture_path": "res://assets/models/items/weapons/old_sword/old_sword_0.png", "texture_path": "res://assets/models/items/weapons/rusty_sword/rusty_sword_0.png",
"weapon_position": Vector3(0, 0.085, -0.035), "weapon_position": Vector3(-0.1, 0.55, -0.1),
"weapon_rotation": Vector3(-20, 110, -90), "weapon_rotation": Vector3(6, 150, -20),
"weapon_scale": Vector3(0.75, 0.75, 0.75), "weapon_scale": Vector3(0.65, 0.65, 0.65),
} }
func _update_animations() -> void: func _update_animations() -> void:
...@@ -153,3 +156,87 @@ func _update_animations() -> void: ...@@ -153,3 +156,87 @@ func _update_animations() -> void:
if target_anim != "" and _model_anim_player.current_animation != target_anim: if target_anim != "" and _model_anim_player.current_animation != target_anim:
_model_anim_player.play(target_anim, blend_time, play_speed) _model_anim_player.play(target_anim, blend_time, play_speed)
## 이 유닛을 포함해 필드의 Warrior Lv1 세 기가 있을 때만 합성할 수 있습니다.
func can_merge_to_lv2() -> bool:
return _get_merge_materials().size() == 3
## Warrior Lv1 세 기를 Warrior Lv2 한 기로 교체합니다.
## 합성 결과는 클릭된 이 유닛의 위치, 회전, 타일 점유 상태를 이어받고,
## 능력치는 DataManager에 등록된 Lv2 템플릿을 그대로 사용합니다.
func merge_to_lv2() -> bool:
var merge_materials: Array[WarriorLv1Unit] = _get_merge_materials()
if merge_materials.size() != 3:
return false
var warrior_lv2_data: UnitData = DataManager.get_upgrade_unit_template(WARRIOR_LV2_ID)
if not warrior_lv2_data:
push_error("WarriorLv1Unit: Warrior Lv2 unit data is not registered in DataManager.")
return false
var spawn_parent: Node = get_parent()
if not spawn_parent:
push_error("WarriorLv1Unit: Cannot merge without a parent node.")
return false
var warrior_lv2: BaseUnit = WARRIOR_LV2_SCENE.instantiate() as BaseUnit
if not warrior_lv2:
push_error("WarriorLv1Unit: Failed to instantiate Warrior Lv2 scene.")
return false
var spawn_position: Vector3 = global_position
var spawn_rotation: Vector3 = global_rotation
var was_tile_bound: bool = is_tile_bound
var tile_cell: Vector3i = occupied_tile_cell
if was_tile_bound:
# 물리 이동 중 생길 수 있는 현재 좌표의 미세한 오차 대신, GridMap 셀의 실제 중심을 사용합니다.
spawn_position = _get_tile_center(tile_cell, spawn_position)
for material: WarriorLv1Unit in merge_materials:
material.queue_free()
warrior_lv2.unit_data = warrior_lv2_data
spawn_parent.add_child(warrior_lv2)
warrior_lv2.global_rotation = spawn_rotation
if was_tile_bound:
warrior_lv2.place_on_tile(spawn_position, tile_cell)
else:
warrior_lv2.global_position = spawn_position
return true
## 타일 셀의 월드 중심을 반환합니다. 배치 HUD와 같은 GridMap 변환식을 사용합니다.
func _get_tile_center(tile_cell: Vector3i, fallback_position: Vector3) -> Vector3:
var current_scene: Node = get_tree().current_scene
var grid_map: GridMap = _find_grid_map(current_scene)
if not grid_map:
push_warning("WarriorLv1Unit: GridMap not found; using the current unit position for merge.")
return fallback_position
return grid_map.to_global(grid_map.map_to_local(tile_cell))
func _find_grid_map(node: Node) -> GridMap:
if not node:
return null
if node is GridMap:
return node as GridMap
for child: Node in node.get_children():
var grid_map: GridMap = _find_grid_map(child)
if grid_map:
return grid_map
return null
## 클릭한 Lv1을 첫 재료로 고정하고, 나머지 재료는 필드의 다른 Lv1에서 찾습니다.
func _get_merge_materials() -> Array[WarriorLv1Unit]:
if not unit_data or unit_data.unit_id != WARRIOR_LV1_ID:
return []
var merge_materials: Array[WarriorLv1Unit] = [self]
for unit: Node in get_tree().get_nodes_in_group("units"):
if merge_materials.size() >= 3:
break
if unit == self or not is_instance_valid(unit) or not unit is WarriorLv1Unit:
continue
var warrior_lv1: WarriorLv1Unit = unit as WarriorLv1Unit
if warrior_lv1.unit_data and warrior_lv1.unit_data.unit_id == WARRIOR_LV1_ID:
merge_materials.append(warrior_lv1)
return merge_materials
...@@ -42,4 +42,22 @@ mesh = SubResource("TorusMesh_selection") ...@@ -42,4 +42,22 @@ mesh = SubResource("TorusMesh_selection")
[node name="warrior_lv1" parent="." unique_id=1752898427 instance=ExtResource("2_0bbpa")] [node name="warrior_lv1" parent="." unique_id=1752898427 instance=ExtResource("2_0bbpa")]
[node name="Skeleton3D" parent="warrior_lv1/Armature" parent_id_path=PackedInt32Array(1752898427, 99922453) index="0" unique_id=1902318440]
bones/0/position = Vector3(0.008906815, -0.016679693, 0.43917271)
bones/0/rotation = Quaternion(0.5769956, -0.6211797, -0.5235334, 0.084407754)
bones/1/rotation = Quaternion(0.34053355, 0.507711, -0.6953065, 0.3779094)
bones/2/rotation = Quaternion(0.60372627, 0.28453326, 0.26035574, 0.6976893)
bones/3/rotation = Quaternion(-0.5800057, -0.13442016, 0.031176323, 0.80284035)
bones/5/rotation = Quaternion(0.6090532, 0.28895468, 0.4301953, 0.6004094)
bones/6/rotation = Quaternion(0.37091696, -0.0070307143, 0.33286086, 0.8669342)
bones/7/rotation = Quaternion(-0.22090644, 0.06417959, -0.04645068, 0.9720718)
bones/11/rotation = Quaternion(-0.043871194, 0.042346865, -0.037475355, 0.9974356)
bones/12/rotation = Quaternion(0.47878474, 0.6548963, -0.26809373, 0.519617)
bones/13/rotation = Quaternion(0.45065325, 0.38060626, 0.33229533, 0.7359555)
bones/14/rotation = Quaternion(-0.64165676, -0.21268457, 0.3319805, 0.65789884)
bones/16/rotation = Quaternion(-0.51728547, 0.55821884, -0.39225844, -0.5166632)
bones/17/rotation = Quaternion(0.5371661, -0.2091015, -0.24741793, 0.77878976)
bones/18/rotation = Quaternion(-0.27791977, 0.023365228, -0.53834283, 0.79523695)
bones/21/rotation = Quaternion(0.17100015, 0.23039033, -0.18824059, 0.93927884)
[editable path="warrior_lv1"] [editable path="warrior_lv1"]
class_name WarriorLv2Unit
extends BaseUnit
## Warrior Lv2 유닛 고유 동작 및 비주얼 제어 클래스
const MODEL_NAME: String = "warrior_lv2"
const WARRIOR_ATTACK_ANIM: String = "Attack"
const WARRIOR_RUN_ANIM: String = "Run"
const WARRIOR_IDLE_ANIM: String = "Idle"
@export var area_attack_distance: float = 2.5
@export_range(1.0, 360.0, 1.0) var area_attack_angle_degrees: float = 115.0
func _setup_unit_visuals() -> void:
var node: Node3D = get_node_or_null(MODEL_NAME) as Node3D
if not node:
push_error("WarriorLv2Unit: Model node '" + MODEL_NAME + "' not found under children.")
return
_model_instance = node
UnitHelper.apply_skin(_model_instance, "res://assets/models/units/warrior/lv2/warrior_lv2_0.png")
attach_weapon(_model_instance)
_model_anim_player = UnitHelper.find_animation_player(_model_instance)
if _model_anim_player:
var anim_lib_path: String = "res://assets/models/units/warrior/lv2/warrior_lv2_anim.tres"
if ResourceLoader.exists(anim_lib_path):
var lib: AnimationLibrary = load(anim_lib_path) as AnimationLibrary
if lib:
if _model_anim_player.has_animation_library(""):
_model_anim_player.remove_animation_library("")
if not _model_anim_player.has_animation_library(MODEL_NAME):
_model_anim_player.add_animation_library(MODEL_NAME, lib)
UnitHelper.clean_root_animation_tracks(_model_anim_player)
func _get_attack_animation_name(_model_name: String) -> String:
return MODEL_NAME + "/" + WARRIOR_ATTACK_ANIM
## 공격 시작 순간 정면 부채꼴 안의 몬스터를 모두 타격 대상으로 확정합니다.
func _get_melee_attack_targets(primary_target: Node3D) -> Array[Node3D]:
var attack_origin: Vector3 = global_position
var attack_forward: Vector3 = -global_transform.basis.z
attack_forward.y = 0.0
if attack_forward.length_squared() <= 0.0001:
attack_forward = Vector3.FORWARD
else:
attack_forward = attack_forward.normalized()
var hit_monsters: Array[Node3D] = []
var half_angle_radians: float = deg_to_rad(area_attack_angle_degrees * 0.5)
var minimum_forward_dot: float = cos(half_angle_radians)
for monster: Node in get_tree().get_nodes_in_group("monsters"):
if not is_instance_valid(monster) or not monster is Node3D:
continue
if monster.get("is_dead") == true:
continue
var monster_3d: Node3D = monster as Node3D
var direction_from_warrior: Vector3 = monster_3d.global_position - attack_origin
direction_from_warrior.y = 0.0
var horizontal_distance: float = direction_from_warrior.length()
if horizontal_distance > area_attack_distance:
continue
if horizontal_distance <= 0.01:
hit_monsters.append(monster_3d)
continue
var direction_to_monster: Vector3 = direction_from_warrior / horizontal_distance
if attack_forward.dot(direction_to_monster) >= minimum_forward_dot:
hit_monsters.append(monster_3d)
if is_instance_valid(primary_target) and not hit_monsters.has(primary_target):
var primary_offset: Vector3 = primary_target.global_position - attack_origin
primary_offset.y = 0.0
if primary_offset.length() <= area_attack_distance:
hit_monsters.append(primary_target)
hit_monsters.sort_custom(func(a: Node3D, b: Node3D) -> bool:
return attack_origin.distance_squared_to(a.global_position) < attack_origin.distance_squared_to(b.global_position)
)
return hit_monsters
func _get_weapon_config() -> Dictionary:
return {
"bone_name": "RightHand",
"attachment_name": "RightHandAttachment",
"weapon_scene_path": "res://assets/models/items/weapons/old_sword/old_sword.tscn",
"texture_path": "res://assets/models/items/weapons/old_sword/old_sword_0.png",
"weapon_position": Vector3(0, 0.085, -0.035),
"weapon_rotation": Vector3(-20, 110, -90),
"weapon_scale": Vector3(0.75, 0.75, 0.75),
}
func _update_animations() -> void:
if not _model_anim_player:
return
var library_prefix: String = MODEL_NAME + "/"
var has_custom_anims: bool = _model_anim_player.has_animation(library_prefix + WARRIOR_IDLE_ANIM)
var target_anim: String = ""
var play_speed: float = 1.0
var blend_time: float = 0.2
if has_custom_anims:
var attack_anim: String = library_prefix + WARRIOR_ATTACK_ANIM
var run_anim: String = library_prefix + WARRIOR_RUN_ANIM
var idle_anim: String = library_prefix + WARRIOR_IDLE_ANIM
if _attack_lock_timer > 0.0:
var base_speed: float = 1.5
if 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
elif velocity.length() > 0.1:
target_anim = run_anim
play_speed = 1.2
else:
target_anim = idle_anim
if _model_anim_player.current_animation != target_anim:
_last_anim_state = target_anim.replace(library_prefix, "")
_model_anim_player.play(target_anim, blend_time, play_speed)
[gd_scene format=3 uid="uid://c8ylhv7latu3f"]
[ext_resource type="Script" uid="uid://bsrwvw8lvd10l" path="res://src/entities/units/unit/warrior/lv2/warrior_lv2_unit.gd" id="1_script"]
[ext_resource type="PackedScene" uid="uid://c1s1bkt10v0rm" path="res://assets/models/units/warrior/lv2/warrior_lv_2.tscn" id="2_model"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.4
height = 1.2
[sub_resource type="CapsuleMesh" id="CapsuleMesh_unit"]
radius = 0.4
height = 1.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ring"]
albedo_color = Color(0.098039, 0.901961, 0.098039, 1)
emission_enabled = true
emission = Color(0.098039, 0.901961, 0.098039, 1)
emission_energy_multiplier = 2.0
[sub_resource type="TorusMesh" id="TorusMesh_selection"]
material = SubResource("StandardMaterial3D_ring")
inner_radius = 0.65
outer_radius = 0.7
[node name="WarriorLv2Unit" type="CharacterBody3D" unique_id=319330680]
script = ExtResource("1_script")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=88758424]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="VisualMesh" type="MeshInstance3D" parent="." unique_id=1091972406]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
visible = false
mesh = SubResource("CapsuleMesh_unit")
[node name="SelectionRing" type="MeshInstance3D" parent="." unique_id=888351282]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0)
mesh = SubResource("TorusMesh_selection")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=1139008088]
[node name="warrior_lv2" parent="." unique_id=1273649626 instance=ExtResource("2_model")]
[editable path="warrior_lv2"]
...@@ -154,12 +154,12 @@ func begin_random_unit_placement() -> void: ...@@ -154,12 +154,12 @@ func begin_random_unit_placement() -> void:
## 새 유닛 타입이 추가되면 이 match에만 씬 매핑을 늘리면 배치 흐름은 그대로 재사용됩니다. ## 새 유닛 타입이 추가되면 이 match에만 씬 매핑을 늘리면 배치 흐름은 그대로 재사용됩니다.
func _get_scene_for_unit(unit_data: UnitData) -> PackedScene: func _get_scene_for_unit(unit_data: UnitData) -> PackedScene:
match unit_data.unit_id: match unit_data.unit_id:
"Unit_Archer_lv1", "Unit_Ranger": "Unit_Archer_lv1":
return ARCHER_LV1_SCENE return ARCHER_LV1_SCENE
"Unit_Mage":
return MAGE_SCENE
"Unit_Warrior_Lv1": "Unit_Warrior_Lv1":
return WARRIOR_LV1_SCENE return WARRIOR_LV1_SCENE
"Unit_Mage":
return MAGE_SCENE
_: _:
return _base_unit_scene return _base_unit_scene
......
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