diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj
index 6147ddb..86eedbf 100644
--- a/Assembly-CSharp-Editor.csproj
+++ b/Assembly-CSharp-Editor.csproj
@@ -70,6 +70,7 @@
+
diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj
index 92d00f3..35fea36 100644
--- a/Assembly-CSharp.csproj
+++ b/Assembly-CSharp.csproj
@@ -79,9 +79,11 @@
+
+
@@ -125,6 +127,7 @@
+
diff --git a/Assets/Data/ScriptableObjects/Upgrade.meta b/Assets/Data/ScriptableObjects/Upgrade.meta
new file mode 100644
index 0000000..f605a03
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dc09279707549e041ae0219468b0a4a2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset
new file mode 100644
index 0000000..c011f19
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade1
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 1
+ memo: "\uCCB4\uB825 \uC99D\uAC00 Lv.1"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 200
+ requireUpgradeId:
+ effectStatList:
+ - player_max_hp
+ effectOpList:
+ - add
+ effectValueList:
+ - 50
+ sortOrder: 1
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset.meta
new file mode 100644
index 0000000..657be42
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade1.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b9e46f00da341b24ab07b314c89b7fd9
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset
new file mode 100644
index 0000000..3b8bc18
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade10
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 10
+ memo: "\uB178\uB3D9\uB825 \uC99D\uAC00 Lv.1"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 200
+ requireUpgradeId:
+ effectStatList:
+ - player_manpower
+ effectOpList:
+ - add
+ effectValueList:
+ - 5
+ sortOrder: 10
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset.meta
new file mode 100644
index 0000000..04eaf3a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade10.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c665e2cbdd230c04fb6d3e9fac091a65
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset
new file mode 100644
index 0000000..b4ad9e7
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade11
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 11
+ memo: "\uB178\uB3D9\uB825 \uC99D\uAC00 Lv.2"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 300
+ requireUpgradeId: 0a000000
+ effectStatList:
+ - player_manpower
+ effectOpList:
+ - add
+ effectValueList:
+ - 10
+ sortOrder: 11
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset.meta
new file mode 100644
index 0000000..2265349
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade11.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f5ad1c8a4ecf84a43b7c609c58592295
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset
new file mode 100644
index 0000000..f395916
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade12
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 12
+ memo: "\uB178\uB3D9\uB825 \uC99D\uAC00 Lv.3"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 400
+ requireUpgradeId: 070000000a000000
+ effectStatList:
+ - player_manpower
+ effectOpList:
+ - add
+ effectValueList:
+ - 20
+ sortOrder: 12
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset.meta
new file mode 100644
index 0000000..727620e
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade12.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 555ef1e6ff58ee442953d075e417c091
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset
new file mode 100644
index 0000000..55adf2c
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade13
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 13
+ memo: "\uC774\uB3D9\uC18D\uB3C4 \uC99D\uAC00 Lv.1"
+ upgradeCategory: scout
+ upgradeTarget: person
+ mana: 200
+ requireUpgradeId:
+ effectStatList:
+ - player_move_speed
+ effectOpList:
+ - mul
+ effectValueList:
+ - 1.1
+ sortOrder: 13
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset.meta
new file mode 100644
index 0000000..55e0134
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade13.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: aa2a6eecb804c3948a4fa7e75bacfca6
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset
new file mode 100644
index 0000000..82869a7
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade14
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 14
+ memo: "\uC774\uB3D9\uC18D\uB3C4 \uC99D\uAC00 Lv.2"
+ upgradeCategory: scout
+ upgradeTarget: person
+ mana: 300
+ requireUpgradeId: 0d000000
+ effectStatList:
+ - player_move_speed
+ effectOpList:
+ - mul
+ effectValueList:
+ - 1.2
+ sortOrder: 14
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset.meta
new file mode 100644
index 0000000..afedec6
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade14.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3216444533dfdc14891741f0b1c5a30a
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset
new file mode 100644
index 0000000..c8cd64a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade15
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 15
+ memo: "\uC774\uB3D9\uC18D\uB3C4 \uC99D\uAC00 Lv.3"
+ upgradeCategory: scout
+ upgradeTarget: person
+ mana: 400
+ requireUpgradeId: 0e000000
+ effectStatList:
+ - player_move_speed
+ effectOpList:
+ - mul
+ effectValueList:
+ - 1.3
+ sortOrder: 15
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset.meta
new file mode 100644
index 0000000..643d61e
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade15.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 227e825691ca7c9498e4af4cbb592faa
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset
new file mode 100644
index 0000000..588194a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade2
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 2
+ memo: "\uCCB4\uB825 \uC99D\uAC00 Lv.2"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 300
+ requireUpgradeId: 01000000
+ effectStatList:
+ - player_max_hp
+ effectOpList:
+ - add
+ effectValueList:
+ - 100
+ sortOrder: 2
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset.meta
new file mode 100644
index 0000000..9eaa181
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade2.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d989011e0aeec9a4b9ed3d9e49488c8a
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset
new file mode 100644
index 0000000..05c2369
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade3
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 3
+ memo: "\uCCB4\uB825 \uC99D\uAC00 Lv.3"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 400
+ requireUpgradeId: 0200000005000000
+ effectStatList:
+ - player_max_hp
+ effectOpList:
+ - add
+ effectValueList:
+ - 150
+ sortOrder: 3
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset.meta
new file mode 100644
index 0000000..15ca44b
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade3.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 29b2206b90bc89c4183c6fb0a1bb699e
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset
new file mode 100644
index 0000000..cba912e
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade4
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 4
+ memo: "\uACF5\uACA9\uB825 \uC99D\uAC00 Lv.1"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 200
+ requireUpgradeId:
+ effectStatList:
+ - player_atk_damage
+ effectOpList:
+ - add
+ effectValueList:
+ - 5
+ sortOrder: 4
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset.meta
new file mode 100644
index 0000000..109b960
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade4.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4c370fa5d360b034b977fa923d14f60b
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset
new file mode 100644
index 0000000..724c4a8
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade5
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 5
+ memo: "\uACF5\uACA9\uB825 \uC99D\uAC00 Lv.2"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 300
+ requireUpgradeId: 04000000
+ effectStatList:
+ - player_atk_damage
+ effectOpList:
+ - add
+ effectValueList:
+ - 10
+ sortOrder: 5
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset.meta
new file mode 100644
index 0000000..311fd20
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade5.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 49e9bbb379a103741bca937c322db0cb
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset
new file mode 100644
index 0000000..d4c158b
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade6
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 6
+ memo: "\uACF5\uACA9\uB825 \uC99D\uAC00 Lv.3"
+ upgradeCategory: combat
+ upgradeTarget: person
+ mana: 400
+ requireUpgradeId: 0200000005000000
+ effectStatList:
+ - player_atk_damage
+ effectOpList:
+ - add
+ effectValueList:
+ - 15
+ sortOrder: 6
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset.meta
new file mode 100644
index 0000000..09e038a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade6.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c6178f649bd6ad141888fb39c653bda9
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset
new file mode 100644
index 0000000..906e07a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade7
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 7
+ memo: "\uC790\uC6D0 \uCD5C\uB300 \uC6A9\uB7C9 \uC99D\uAC00 Lv.1"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 200
+ requireUpgradeId:
+ effectStatList:
+ - player_capacity
+ effectOpList:
+ - add
+ effectValueList:
+ - 20
+ sortOrder: 7
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset.meta
new file mode 100644
index 0000000..b206815
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade7.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 36cdb899e627bd54092fa9e4458ff6d2
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset
new file mode 100644
index 0000000..95284dd
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade8
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 8
+ memo: "\uC790\uC6D0 \uCD5C\uB300 \uC6A9\uB7C9 \uC99D\uAC00 Lv.2"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 300
+ requireUpgradeId: 07000000
+ effectStatList:
+ - player_capacity
+ effectOpList:
+ - add
+ effectValueList:
+ - 50
+ sortOrder: 8
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset.meta
new file mode 100644
index 0000000..e7d2d52
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade8.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9e8418449515bb142b06d88d7a0ac4e1
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset b/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset
new file mode 100644
index 0000000..6062d88
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset
@@ -0,0 +1,27 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f134815569ad014e9ccc81ddc443c4c, type: 3}
+ m_Name: Upgrade9
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.UpgradeData
+ id: 9
+ memo: "\uC790\uC6D0 \uCD5C\uB300 \uC6A9\uB7C9 \uC99D\uAC00 Lv.3"
+ upgradeCategory: harvest
+ upgradeTarget: person
+ mana: 400
+ requireUpgradeId: 070000000a000000
+ effectStatList:
+ - player_capacity
+ effectOpList:
+ - add
+ effectValueList:
+ - 100
+ sortOrder: 9
diff --git a/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset.meta b/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset.meta
new file mode 100644
index 0000000..36fb99c
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Upgrade/Upgrade9.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: eca8a6e3f11a84f44aa061493293e791
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Prefabs/Player/Player.prefab b/Assets/Prefabs/Player/Player.prefab
index c66c8b6..2a78b4a 100644
--- a/Assets/Prefabs/Player/Player.prefab
+++ b/Assets/Prefabs/Player/Player.prefab
@@ -23,6 +23,8 @@ GameObject:
- component: {fileID: -4348726977448206869}
- component: {fileID: 7148704114816793672}
- component: {fileID: 6581787771557727003}
+ - component: {fileID: 8704851935123448388}
+ - component: {fileID: 2146720647481692606}
m_Layer: 9
m_Name: Player
m_TagString: Untagged
@@ -85,10 +87,8 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NetworkPlayerController
ShowTopMostFoldoutHeaderGroup: 1
- moveSpeed: 5
rotationSpeed: 10
initialTeam: 1
- maxHealth: 100
damageEffectPrefab: {fileID: 0}
deathEffectPrefab: {fileID: 0}
resourcePickupPrefab: {fileID: 1627676033990080135, guid: 8c45964a69bf8fa4ba461ed217bc052f, type: 3}
@@ -158,7 +158,6 @@ MonoBehaviour:
interactableLayer:
serializedVersion: 2
m_Bits: 128
- workPower: 1
rayOrigin: {fileID: 0}
useForwardDirection: 1
playAnimations: 1
@@ -196,8 +195,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.AttackAction
ShowTopMostFoldoutHeaderGroup: 1
- attackRange: 3
- attackDamage: 100
attackCooldown: 1
attackableLayer:
serializedVersion: 2
@@ -249,7 +246,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerResourceInventory
ShowTopMostFoldoutHeaderGroup: 1
- maxResourceCapacity: 50
--- !u!114 &2148255267416253297
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -263,7 +259,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerVisionProvider
ShowTopMostFoldoutHeaderGroup: 1
- visionRange: 10
--- !u!95 &1698609800605343773
Animator:
serializedVersion: 7
@@ -393,6 +388,38 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.InteractableModalManager
interactableModal: {fileID: 7317980967521758771}
playerInteraction: {fileID: 8729870597719024730}
+--- !u!114 &8704851935123448388
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1314983689436087486}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: c2399ad44b71131439123eaec84abb08, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerUpgradeManager
+ ShowTopMostFoldoutHeaderGroup: 1
+--- !u!114 &2146720647481692606
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1314983689436087486}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 6aa26fe48ebdaae438d465df7c6a3bef, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerStats
+ baseMaxHp: 100
+ baseDamage: 10
+ baseCapacity: 100
+ baseManpower: 10
+ baseMoveSpeed: 5
+ baseSight: 10
+ baseAttackRange: 2
--- !u!1 &1862223349553492570
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity
index fd06c4d..a8d30b3 100644
--- a/Assets/Scenes/GameMain.unity
+++ b/Assets/Scenes/GameMain.unity
@@ -920,6 +920,7 @@ Transform:
- {fileID: 946527919}
- {fileID: 1701756768}
- {fileID: 1166878644}
+ - {fileID: 1247940084}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &672563220
@@ -2024,6 +2025,51 @@ Transform:
m_Children: []
m_Father: {fileID: 640318137}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1247940083
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1247940084}
+ - component: {fileID: 1247940085}
+ m_Layer: 0
+ m_Name: UpgradeDatabase
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1247940084
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1247940083}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 640318137}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1247940085
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1247940083}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 12780dc9ee390c742b6cbd405e59a916, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.UpgradeDatabase
+ _upgradeDataList: []
--- !u!224 &1282800775 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 8873176760615364646, guid: a470599cc0481164ab487ecf39b1ebd0, type: 3}
diff --git a/Assets/ScriptableObjects/Upgrade.meta b/Assets/ScriptableObjects/Upgrade.meta
new file mode 100644
index 0000000..c0a073b
--- /dev/null
+++ b/Assets/ScriptableObjects/Upgrade.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ba53e8dca24fd4346a54d32e8358a2bc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/AttackAction.cs b/Assets/Scripts/AttackAction.cs
index 6c8f5fd..db9ac1b 100644
--- a/Assets/Scripts/AttackAction.cs
+++ b/Assets/Scripts/AttackAction.cs
@@ -9,8 +9,6 @@ namespace Northbound
public class AttackAction : NetworkBehaviour, IAction
{
[Header("Attack Settings")]
- public float attackRange = 2f;
- public int attackDamage = 10;
public float attackCooldown = 0.5f;
public LayerMask attackableLayer = ~0;
@@ -33,12 +31,14 @@ namespace Northbound
private EquipmentSocket _equipmentSocket;
private bool _isAttacking = false;
private bool _isWeaponEquipped = false;
+ private PlayerStats _playerStats;
private void Awake()
{
_animator = GetComponent();
_teamMember = GetComponent();
_equipmentSocket = GetComponent();
+ _playerStats = GetComponent();
}
public bool CanExecute(ulong playerId)
@@ -80,7 +80,7 @@ namespace Northbound
private void PerformAttack()
{
Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position;
- Collider[] hits = Physics.OverlapSphere(attackOrigin, attackRange, attackableLayer);
+ Collider[] hits = Physics.OverlapSphere(attackOrigin, GetAttackRange(), attackableLayer);
foreach (Collider hit in hits)
{
@@ -115,7 +115,7 @@ namespace Northbound
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetNetworkId, out NetworkObject targetObj))
{
var damageable = targetObj.GetComponent();
- damageable?.TakeDamage(attackDamage, attackerNetworkId);
+ damageable?.TakeDamage(_playerStats?.GetDamage() ?? 10, attackerNetworkId);
}
}
@@ -248,7 +248,7 @@ namespace Northbound
{
Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position;
Gizmos.color = Color.red;
- Gizmos.DrawWireSphere(attackOrigin, attackRange);
+ Gizmos.DrawWireSphere(attackOrigin, GetAttackRange());
}
public override void OnDestroy()
@@ -263,5 +263,10 @@ namespace Northbound
}
public bool IsAttacking => _isAttacking;
+
+ ///
+ /// 공격 범위 반환 (PlayerStats 우선)
+ ///
+ public float GetAttackRange() => _playerStats?.GetAttackRange() ?? 2f;
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Scripts/Editor/CSVToSOImporter.cs b/Assets/Scripts/Editor/CSVToSOImporter.cs
index e3b2e05..42de3c6 100644
--- a/Assets/Scripts/Editor/CSVToSOImporter.cs
+++ b/Assets/Scripts/Editor/CSVToSOImporter.cs
@@ -1,3 +1,4 @@
+using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -45,12 +46,12 @@ namespace Northbound.Editor
}
Debug.Log($"[CSVToSOImporter] Import complete: {successCount} succeeded, {failCount} failed");
-
+
if (towerImported)
{
AutoConfigureBuildingManager();
}
-
+
AssetDatabase.Refresh();
}
@@ -61,7 +62,8 @@ namespace Northbound.Editor
{ "Monster", new MonsterPrefabSetup() },
{ "Creep", new CreepPrefabSetup() },
{ "Tower", new TowerPrefabSetup() },
- { "Player", new PlayerPrefabSetup() }
+ { "Player", new PlayerPrefabSetup() },
+ { "Upgrade", new UpgradePrefabSetup() }
};
}
@@ -82,14 +84,19 @@ namespace Northbound.Editor
}
IPrefabSetup prefabSetup = prefabSetups[typeName];
- string templateName = prefabSetup.GetTemplateName();
- string templatePath = $"Assets/Data/Templates/{templateName}.prefab";
- GameObject template = AssetDatabase.LoadAssetAtPath(templatePath);
- if (template == null)
+ // Upgrade는 템플릿이 필요 없음
+ GameObject template = null;
+ if (typeName != "Upgrade")
{
- Debug.LogError($"[CSVToSOImporter] Template not found: {templatePath}");
- return false;
+ string templateName = prefabSetup.GetTemplateName();
+ string templatePath = $"Assets/Data/Templates/{templateName}.prefab";
+ template = AssetDatabase.LoadAssetAtPath(templatePath);
+ if (template == null)
+ {
+ Debug.LogError($"[CSVToSOImporter] Template not found: {templatePath}");
+ return false;
+ }
}
string[] csvLines = File.ReadAllLines(csvPath);
@@ -123,7 +130,6 @@ namespace Northbound.Editor
Debug.Log($"[CSVToSOImporter] {typeName}: {successCount} prefabs created/updated");
}
- // If towers were imported, auto-configure BuildingManager
if (typeName == "Tower")
{
Debug.Log($"[CSVToSOImporter] Tower import complete!");
@@ -134,8 +140,19 @@ namespace Northbound.Editor
private static bool CreatePrefabFromRow(string typeName, string[] headers, string[] values, GameObject template, IPrefabSetup prefabSetup)
{
- string soPath = $"Assets/Data/ScriptableObjects/{typeName}";
- Directory.CreateDirectory(Path.Combine(Application.dataPath, $"ScriptableObjects/{typeName}"));
+ string soPath;
+ if (typeName == "Upgrade")
+ {
+ // Upgrade는 Resources 폴더에 저장 (런타임 자동 로드용)
+ soPath = "Assets/Resources/Data/ScriptableObjects/Upgrade";
+ Directory.CreateDirectory(Path.Combine(Application.dataPath, "Resources/Data/ScriptableObjects/Upgrade"));
+ }
+ else
+ {
+ soPath = $"Assets/Data/ScriptableObjects/{typeName}";
+ Directory.CreateDirectory(Path.Combine(Application.dataPath, $"ScriptableObjects/{typeName}"));
+ }
+
int id = 0;
for (int i = 0; i < headers.Length && i < values.Length; i++)
@@ -188,6 +205,11 @@ namespace Northbound.Editor
PlayerPrefabSetup.UpdatePlayerPrefab((PlayerData)data);
prefabObj = null;
}
+ else if (typeName == "Upgrade")
+ {
+ // Upgrade는 프리팹이 필요 없음
+ prefabObj = null;
+ }
else
{
string prefabPath = $"Assets/Prefabs/{typeName}/{typeName}{id}.prefab";
@@ -219,7 +241,6 @@ namespace Northbound.Editor
EditorUtility.SetDirty(data);
}
- // Force save assets to disk
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
@@ -236,7 +257,6 @@ namespace Northbound.Editor
return;
}
- // Load TowerData
string[] towerDataGuids = AssetDatabase.FindAssets("t:TowerData", new[] { "Assets/Data/ScriptableObjects" });
List allTowers = new List();
@@ -302,6 +322,26 @@ namespace Northbound.Editor
return type.IsValueType ? System.Activator.CreateInstance(type) : null;
}
+ // List 타입 처리 (세미콜론 구분)
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
+ {
+ System.Type elementType = type.GetGenericArguments()[0];
+ var elements = value.Split(';').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToList();
+
+ IList list = (IList)System.Activator.CreateInstance(type);
+
+ foreach (string element in elements)
+ {
+ object elementValue = ParseValue(element, elementType);
+ if (elementValue != null)
+ {
+ list.Add(elementValue);
+ }
+ }
+
+ return list;
+ }
+
if (type == typeof(int))
{
int result;
diff --git a/Assets/Scripts/Editor/PlayerPrefabSetup.cs b/Assets/Scripts/Editor/PlayerPrefabSetup.cs
index d1965a6..65a6631 100644
--- a/Assets/Scripts/Editor/PlayerPrefabSetup.cs
+++ b/Assets/Scripts/Editor/PlayerPrefabSetup.cs
@@ -45,52 +45,38 @@ namespace Northbound.Editor
private static void SetupPrefabComponents(GameObject prefab, PlayerData playerData)
{
- var networkController = prefab.GetComponent();
- if (networkController != null)
+ // PlayerStats에 모든 스탯 설정
+ var playerStats = prefab.GetComponent();
+ if (playerStats != null)
{
- SerializedObject so = new SerializedObject(networkController);
- so.FindProperty("moveSpeed").floatValue = playerData.moveSpeed;
- so.FindProperty("maxHealth").intValue = playerData.maxHp;
+ SerializedObject so = new SerializedObject(playerStats);
+ so.FindProperty("baseMaxHp").intValue = playerData.maxHp;
+ so.FindProperty("baseDamage").intValue = playerData.atkDamage;
+ so.FindProperty("baseCapacity").intValue = playerData.capacity;
+ so.FindProperty("baseManpower").floatValue = playerData.manpower;
+ so.FindProperty("baseMoveSpeed").floatValue = playerData.moveSpeed;
+ so.FindProperty("baseSight").floatValue = playerData.sight;
+ so.FindProperty("baseAttackRange").floatValue = playerData.atkRange;
so.ApplyModifiedProperties();
- Debug.Log($"[PlayerPrefabSetup] Updated NetworkPlayerController: moveSpeed={playerData.moveSpeed}, maxHealth={playerData.maxHp}");
+ Debug.Log($"[PlayerPrefabSetup] Updated PlayerStats: " +
+ $"maxHp={playerData.maxHp}, damage={playerData.atkDamage}, " +
+ $"capacity={playerData.capacity}, manpower={playerData.manpower}, " +
+ $"moveSpeed={playerData.moveSpeed}, sight={playerData.sight}, " +
+ $"attackRange={playerData.atkRange}");
+ }
+ else
+ {
+ Debug.LogWarning($"[PlayerPrefabSetup] PlayerStats component not found on prefab!");
}
+ // AttackAction의 attackCooldown은 별도 설정 (스탯이 아님)
var attackAction = prefab.GetComponent();
if (attackAction != null)
{
SerializedObject so = new SerializedObject(attackAction);
- so.FindProperty("attackRange").intValue = playerData.atkRange;
- so.FindProperty("attackDamage").intValue = playerData.atkDamage;
so.FindProperty("attackCooldown").floatValue = playerData.atkIntervalSec;
so.ApplyModifiedProperties();
- Debug.Log($"[PlayerPrefabSetup] Updated AttackAction: attackRange={playerData.atkRange}, attackDamage={playerData.atkDamage}, attackCooldown={playerData.atkIntervalSec}");
- }
-
- var visionProvider = prefab.GetComponent();
- if (visionProvider != null)
- {
- SerializedObject so = new SerializedObject(visionProvider);
- so.FindProperty("visionRange").floatValue = playerData.sight;
- so.ApplyModifiedProperties();
- Debug.Log($"[PlayerPrefabSetup] Updated PlayerVisionProvider: visionRange={playerData.sight}");
- }
-
- var resourceInventory = prefab.GetComponent();
- if (resourceInventory != null)
- {
- SerializedObject so = new SerializedObject(resourceInventory);
- so.FindProperty("maxResourceCapacity").intValue = playerData.capacity;
- so.ApplyModifiedProperties();
- Debug.Log($"[PlayerPrefabSetup] Updated PlayerResourceInventory: maxResourceCapacity={playerData.capacity}");
- }
-
- var playerInteraction = prefab.GetComponent();
- if (playerInteraction != null)
- {
- SerializedObject so = new SerializedObject(playerInteraction);
- so.FindProperty("workPower").floatValue = playerData.manpower;
- so.ApplyModifiedProperties();
- Debug.Log($"[PlayerPrefabSetup] Updated PlayerInteraction: workPower={playerData.manpower}");
+ Debug.Log($"[PlayerPrefabSetup] Updated AttackAction: attackCooldown={playerData.atkIntervalSec}");
}
EditorUtility.SetDirty(prefab);
diff --git a/Assets/Scripts/Editor/UpgradePrefabSetup.cs b/Assets/Scripts/Editor/UpgradePrefabSetup.cs
new file mode 100644
index 0000000..ee5f39e
--- /dev/null
+++ b/Assets/Scripts/Editor/UpgradePrefabSetup.cs
@@ -0,0 +1,23 @@
+using Northbound.Data;
+using UnityEditor;
+using UnityEngine;
+
+namespace Northbound.Editor
+{
+ ///
+ /// Upgrade 타입은 프리팹이 필요 없으므로 빈 구현
+ ///
+ public class UpgradePrefabSetup : IPrefabSetup
+ {
+ public string GetTemplateName()
+ {
+ return ""; // 템플릿 필요 없음
+ }
+
+ public void SetupPrefab(GameObject prefab, ScriptableObject data)
+ {
+ // Upgrade는 프리팹이 필요 없음
+ Debug.LogWarning($"[UpgradePrefabSetup] Upgrade 타입은 프리팹이 필요하지 않습니다.");
+ }
+ }
+}
diff --git a/Assets/Scripts/Editor/UpgradePrefabSetup.cs.meta b/Assets/Scripts/Editor/UpgradePrefabSetup.cs.meta
new file mode 100644
index 0000000..d6e05fe
--- /dev/null
+++ b/Assets/Scripts/Editor/UpgradePrefabSetup.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: f33f085cffff92c489c74fc11a89fa49
\ No newline at end of file
diff --git a/Assets/Scripts/NetworkPlayerController.cs b/Assets/Scripts/NetworkPlayerController.cs
index 4413146..c68e4c6 100644
--- a/Assets/Scripts/NetworkPlayerController.cs
+++ b/Assets/Scripts/NetworkPlayerController.cs
@@ -9,15 +9,11 @@ using Northbound;
public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable
{
[Header("Movement Settings")]
- public float moveSpeed = 5f;
public float rotationSpeed = 10f;
[Header("Team Settings")]
[SerializeField] private TeamType initialTeam = TeamType.Player;
- [Header("Health Settings")]
- [SerializeField] private int maxHealth = 100;
-
[Header("Visual Effects")]
[SerializeField] private GameObject damageEffectPrefab;
[SerializeField] private GameObject deathEffectPrefab;
@@ -50,8 +46,11 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
private PlayerInputActions _inputActions;
private Animator _animator;
private NetworkAnimator _networkAnimator;
+ private PlayerStats _playerStats;
// 이 플레이어가 로컬 플레이어인지 확인
+
+
public bool IsLocalPlayer => _ownerPlayerId.Value == NetworkManager.Singleton.LocalClientId;
public ulong OwnerPlayerId => _ownerPlayerId.Value;
@@ -64,6 +63,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
_controller = GetComponent();
_animator = GetComponent();
_networkAnimator = GetComponent();
+ _playerStats = GetComponent();
}
public override void OnNetworkSpawn()
@@ -80,7 +80,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
if (_currentHealth.Value == 0)
{
- _currentHealth.Value = maxHealth;
+ _currentHealth.Value = GetMaxHealth();
}
}
@@ -189,7 +189,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
if (_controller != null)
{
- _controller.Move(move * moveSpeed * Time.deltaTime);
+ _controller.Move(move * (_playerStats?.GetMoveSpeed() ?? 5f) * Time.deltaTime);
}
}
}
@@ -376,7 +376,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
if (!IsServer) return;
// 체력 회복
- _currentHealth.Value = maxHealth;
+ _currentHealth.Value = GetMaxHealth();
// 스폰 포인트로 이동
var spawnPoints = FindObjectsByType(FindObjectsSortMode.None);
@@ -423,11 +423,12 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
public int GetCurrentHealth() => _currentHealth.Value;
- public int GetMaxHealth() => maxHealth;
+ public int GetMaxHealth() => _playerStats?.GetMaxHp() ?? 100;
public float GetHealthPercentage()
{
- return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
+ int max = GetMaxHealth();
+ return max > 0 ? (float)_currentHealth.Value / max : 0f;
}
public bool IsDead() => _currentHealth.Value <= 0;
@@ -436,7 +437,7 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
{
if (!IsServer) return;
- int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value);
+ int healAmount = Mathf.Min(amount, GetMaxHealth() - _currentHealth.Value);
_currentHealth.Value += healAmount;
}
@@ -459,13 +460,13 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
{
string teamName = TeamManager.GetTeamName(_team.Value);
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
- $"Player: {gameObject.name}\nTeam: {teamName}\nHP: {_currentHealth.Value}/{maxHealth}");
+ $"Player: {gameObject.name}\nTeam: {teamName}\nHP: {_currentHealth.Value}/{GetMaxHealth()}");
}
else
{
string teamName = TeamManager.GetTeamName(initialTeam);
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
- $"Player: {gameObject.name}\nTeam: {teamName}\nHP: {maxHealth}/{maxHealth}");
+ $"Player: {gameObject.name}\nTeam: {teamName}");
}
#endif
}
diff --git a/Assets/Scripts/PlayerInteraction.cs b/Assets/Scripts/PlayerInteraction.cs
index fcca994..d77a748 100644
--- a/Assets/Scripts/PlayerInteraction.cs
+++ b/Assets/Scripts/PlayerInteraction.cs
@@ -13,7 +13,7 @@ namespace Northbound
[Header("Interaction Settings")]
public float interactionRange = 3f;
public LayerMask interactableLayer = ~0;
- public float workPower = 10f;
+
[Header("Detection")]
public Transform rayOrigin;
@@ -47,9 +47,10 @@ namespace Northbound
private Coroutine _interactionTimeoutCoroutine;
private NetworkPlayerController _networkPlayerController;
+ private PlayerStats _playerStats;
public bool IsInteracting => _isInteracting;
- public float WorkPower => workPower;
+ public float WorkPower => _playerStats?.GetManpower() ?? 10f;
public IInteractable CurrentUnavailableInteractable => _unavailableInteractable;
// 로컬 플레이어인지 확인
@@ -60,6 +61,8 @@ namespace Northbound
{
_networkPlayerController = GetComponent();
_networkAnimator = GetComponent();
+ _playerStats = GetComponent();
+
}
public override void OnNetworkSpawn()
diff --git a/Assets/Scripts/PlayerResourceInventory.cs b/Assets/Scripts/PlayerResourceInventory.cs
index bfef347..454fca6 100644
--- a/Assets/Scripts/PlayerResourceInventory.cs
+++ b/Assets/Scripts/PlayerResourceInventory.cs
@@ -5,13 +5,13 @@ namespace Northbound
{
public class PlayerResourceInventory : NetworkBehaviour
{
- public int maxResourceCapacity = 100;
private int _displayAmount = 0;
public int CurrentResourceAmount => _displayAmount;
- public int MaxResourceCapacity => maxResourceCapacity;
+ public int MaxResourceCapacity => _playerStats?.GetCapacity() ?? 100;
private NetworkPlayerController _networkPlayerController;
+ private PlayerStats _playerStats;
private bool IsLocalPlayer => _networkPlayerController != null && _networkPlayerController.IsLocalPlayer;
private ulong LocalPlayerId => _networkPlayerController != null ? _networkPlayerController.OwnerPlayerId : OwnerClientId;
@@ -19,6 +19,7 @@ namespace Northbound
private void Awake()
{
_networkPlayerController = GetComponent();
+ _playerStats = GetComponent();
}
[Rpc(SendTo.Server)]
@@ -33,7 +34,6 @@ namespace Northbound
public override void OnNetworkSpawn()
{
- // _ownerPlayerId 변경 이벤트 구독
if (_networkPlayerController != null)
{
_networkPlayerController.OnOwnerChanged += OnOwnerPlayerIdChanged;
@@ -50,8 +50,8 @@ namespace Northbound
private void TryInitialize()
{
if (!IsLocalPlayer) return;
-
- SetMaxCapacityServerRpc(maxResourceCapacity, LocalPlayerId);
+
+ SetMaxCapacityServerRpc(MaxResourceCapacity, LocalPlayerId);
RequestResourceUpdateServerRpc(LocalPlayerId);
}
diff --git a/Assets/Scripts/PlayerStats.cs b/Assets/Scripts/PlayerStats.cs
new file mode 100644
index 0000000..9b4b358
--- /dev/null
+++ b/Assets/Scripts/PlayerStats.cs
@@ -0,0 +1,144 @@
+using System.Collections.Generic;
+using UnityEngine;
+using Northbound.Data;
+
+namespace Northbound
+{
+ ///
+ /// 플레이어 스탯 계산 컴포넌트
+ /// 기본 스탯 + 업그레이드 보너스를 계산하여 반환
+ ///
+ public class PlayerStats : MonoBehaviour
+ {
+ [Header("Base Stats")]
+ [SerializeField] private int baseMaxHp = 100;
+ [SerializeField] private int baseDamage = 10;
+ [SerializeField] private int baseCapacity = 100;
+ [SerializeField] private float baseManpower = 10f;
+ [SerializeField] private float baseMoveSpeed = 5f;
+ [SerializeField] private float baseSight = 10f;
+ [SerializeField] private float baseAttackRange = 2f;
+
+ private PlayerUpgradeManager _upgradeManager;
+
+ private void Awake()
+ {
+ _upgradeManager = GetComponent();
+ }
+
+ ///
+ /// 특정 스탯에 대한 업그레이드 보너스 계산 (ADD만 지원)
+ ///
+ private float CalculateStatBonus(string statName)
+ {
+ float bonus = 0f;
+
+ if (_upgradeManager == null)
+ return bonus;
+
+ var ownedUpgrades = _upgradeManager.GetOwnedUpgradeData();
+
+ foreach (var upgrade in ownedUpgrades)
+ {
+ if (upgrade == null) continue;
+
+ // effectStatList에서 해당 스탯 찾기
+ for (int i = 0; i < upgrade.effectStatList.Count; i++)
+ {
+ if (i >= upgrade.effectOpList.Count || i >= upgrade.effectValueList.Count)
+ continue;
+
+ if (upgrade.effectStatList[i] == statName)
+ {
+ string op = upgrade.effectOpList[i];
+ float value = upgrade.effectValueList[i];
+
+ // ADD 연산만 지원
+ if (op == "add")
+ {
+ bonus += value;
+ }
+ }
+ }
+ }
+
+ return bonus;
+ }
+
+ ///
+ /// 최대 체력 반환
+ ///
+ public int GetMaxHp()
+ {
+ float bonus = CalculateStatBonus("player_max_hp");
+ return baseMaxHp + (int)bonus;
+ }
+
+ ///
+ /// 공격력 반환
+ ///
+ public int GetDamage()
+ {
+ float bonus = CalculateStatBonus("player_atk_damage");
+ return baseDamage + (int)bonus;
+ }
+
+ ///
+ /// 자원 용량 반환
+ ///
+ public int GetCapacity()
+ {
+ float bonus = CalculateStatBonus("player_capacity");
+ return baseCapacity + (int)bonus;
+ }
+
+ ///
+ /// 노동력(건설/채굴 작업량) 반환
+ ///
+ public float GetManpower()
+ {
+ float bonus = CalculateStatBonus("player_manpower");
+ return baseManpower + bonus;
+ }
+
+ ///
+ /// 이동 속도 반환
+ ///
+ public float GetMoveSpeed()
+ {
+ float bonus = CalculateStatBonus("player_move_speed");
+ // move_speed는 mul 연산이지만, 우선 ADD만 지원하므로 add로 처리
+ return baseMoveSpeed + bonus;
+ }
+
+ ///
+ /// 시야 범위 반환
+ ///
+ public float GetSight()
+ {
+ float bonus = CalculateStatBonus("player_sight");
+ return baseSight + bonus;
+ }
+
+ ///
+ /// 공격 범위 반환
+ ///
+ public float GetAttackRange()
+ {
+ float bonus = CalculateStatBonus("player_atk_range");
+ return baseAttackRange + bonus;
+ }
+
+ #region Base Stat Setters (에디터/초기화용)
+
+ public void SetBaseMaxHp(int value) => baseMaxHp = value;
+ public void SetBaseDamage(int value) => baseDamage = value;
+ public void SetBaseCapacity(int value) => baseCapacity = value;
+ public void SetBaseManpower(float value) => baseManpower = value;
+ public void SetBaseMoveSpeed(float value) => baseMoveSpeed = value;
+ public void SetBaseSight(float value) => baseSight = value;
+ public void SetBaseAttackRange(float value) => baseAttackRange = value;
+
+ #endregion
+ }
+}
diff --git a/Assets/Scripts/PlayerStats.cs.meta b/Assets/Scripts/PlayerStats.cs.meta
new file mode 100644
index 0000000..c6a29f5
--- /dev/null
+++ b/Assets/Scripts/PlayerStats.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 6aa26fe48ebdaae438d465df7c6a3bef
\ No newline at end of file
diff --git a/Assets/Scripts/PlayerUpgradeManager.cs b/Assets/Scripts/PlayerUpgradeManager.cs
new file mode 100644
index 0000000..df42eee
--- /dev/null
+++ b/Assets/Scripts/PlayerUpgradeManager.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+using Unity.Netcode;
+using UnityEngine;
+using Northbound.Data;
+
+namespace Northbound
+{
+ ///
+ /// 플레이어별 업그레이드 보유 상태를 관리하는 컴포넌트
+ ///
+ public class PlayerUpgradeManager : NetworkBehaviour
+ {
+ // 보유한 업그레이드 ID 목록 (네트워크 동기화)
+ private NetworkList _ownedUpgradeIds;
+
+ // 이벤트
+ public event Action OnUpgradePurchased; // upgradeId
+
+ private NetworkPlayerController _networkPlayerController;
+
+ public new bool IsLocalPlayer => _networkPlayerController != null && _networkPlayerController.IsLocalPlayer;
+ public ulong OwnerPlayerId => _networkPlayerController != null ? _networkPlayerController.OwnerPlayerId : OwnerClientId;
+
+ private void Awake()
+ {
+ _ownedUpgradeIds = new NetworkList();
+ _networkPlayerController = GetComponent();
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ base.OnNetworkSpawn();
+
+ if (_networkPlayerController != null)
+ {
+ _networkPlayerController.OnOwnerChanged += OnOwnerChanged;
+ }
+ }
+
+ public override void OnNetworkDespawn()
+ {
+ base.OnNetworkDespawn();
+
+ if (_networkPlayerController != null)
+ {
+ _networkPlayerController.OnOwnerChanged -= OnOwnerChanged;
+ }
+
+ _ownedUpgradeIds.Dispose();
+ }
+
+ private void OnOwnerChanged(ulong newOwnerId)
+ {
+ // 소유자 변경 시 필요한 처리
+ }
+
+ ///
+ /// 보유한 업그레이드 ID 목록 (복사본 반환)
+ ///
+ public List GetOwnedUpgradeIdList()
+ {
+ var list = new List();
+ foreach (int id in _ownedUpgradeIds)
+ {
+ list.Add(id);
+ }
+ return list;
+ }
+
+
+
+
+ ///
+ /// 특정 업그레이드를 보유하고 있는지 확인
+ ///
+ public bool HasUpgrade(int upgradeId)
+ {
+ foreach (int id in _ownedUpgradeIds)
+ {
+ if (id == upgradeId)
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// 보유한 업그레이드 ID를 HashSet으로 반환
+ ///
+ public HashSet GetOwnedUpgradeIdSet()
+ {
+ var set = new HashSet();
+ foreach (int id in _ownedUpgradeIds)
+ {
+ set.Add(id);
+ }
+ return set;
+ }
+
+ ///
+ /// 업그레이드 구매 요청 (클라이언트에서 호출)
+ ///
+ public void RequestPurchaseUpgrade(int upgradeId)
+ {
+ PurchaseUpgradeServerRpc(upgradeId);
+ }
+
+ [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
+ private void PurchaseUpgradeServerRpc(int upgradeId)
+ {
+ if (!CanPurchaseUpgrade(upgradeId, out string failReason))
+ {
+ Debug.LogWarning($"[PlayerUpgradeManager] 업그레이드 구매 실패: {failReason}");
+ return;
+ }
+
+ // 업그레이드 데이터 가져오기
+ var upgrade = UpgradeDatabase.Instance?.GetUpgradeById(upgradeId);
+ if (upgrade == null)
+ {
+ Debug.LogError($"[PlayerUpgradeManager] 업그레이드 ID {upgradeId}를 찾을 수 없습니다.");
+ return;
+ }
+
+ // 비용 차감
+ var core = CoreResourceManager.Instance?.mainCore;
+ if (core == null)
+ {
+ Debug.LogError("[PlayerUpgradeManager] 코어를 찾을 수 없습니다.");
+ return;
+ }
+
+ if (!core.CanConsumeResource(upgrade.mana))
+ {
+ Debug.LogWarning($"[PlayerUpgradeManager] 자원이 부족합니다. 필요: {upgrade.mana}");
+ return;
+ }
+
+ core.ConsumeResourceServerRpc(upgrade.mana);
+
+ // 업그레이드 추가
+ _ownedUpgradeIds.Add(upgradeId);
+
+ Debug.Log($"[PlayerUpgradeManager] 업그레이드 '{upgrade.memo}' 구매 완료! (ID: {upgradeId})");
+
+ // 이벤트 발생
+ OnUpgradePurchased?.Invoke(upgradeId);
+ }
+
+ ///
+ /// 업그레이드 구매 가능 여부 확인
+ ///
+ private bool CanPurchaseUpgrade(int upgradeId, out string failReason)
+ {
+ failReason = string.Empty;
+
+ var upgradeDatabase = UpgradeDatabase.Instance;
+ if (upgradeDatabase == null)
+ {
+ failReason = "UpgradeDatabase가 초기화되지 않았습니다.";
+ return false;
+ }
+
+ var upgrade = upgradeDatabase.GetUpgradeById(upgradeId);
+ if (upgrade == null)
+ {
+ failReason = $"업그레이드 ID {upgradeId}를 찾을 수 없습니다.";
+ return false;
+ }
+
+ // 이미 보유 중인지 확인
+ if (HasUpgrade(upgradeId))
+ {
+ failReason = "이미 보유한 업그레이드입니다.";
+ return false;
+ }
+
+ // 선행 조건 확인
+ if (!upgradeDatabase.ArePrerequisitesMet(upgradeId, GetOwnedUpgradeIdSet()))
+ {
+ failReason = "선행 업그레이드 조건을 충족하지 않았습니다.";
+ return false;
+ }
+
+ // 비용 확인
+ var core = CoreResourceManager.Instance?.mainCore;
+ if (core == null)
+ {
+ failReason = "코어를 찾을 수 없습니다.";
+ return false;
+ }
+
+ if (!core.CanConsumeResource(upgrade.mana))
+ {
+ failReason = $"자원이 부족합니다. 필요: {upgrade.mana}, 보유: {core.TotalResources}";
+ return false;
+ }
+
+ // 개인 업그레이드인지 확인
+ if (upgrade.upgradeTarget != "person")
+ {
+ failReason = "팀 업그레이드는 개인이 구매할 수 없습니다.";
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 업그레이드 구매 가능 여부 (외부용)
+ ///
+ public bool CanPurchase(int upgradeId)
+ {
+ return CanPurchaseUpgrade(upgradeId, out _);
+ }
+
+ ///
+ /// 구매 가능한 업그레이드 목록 반환
+ ///
+ public List GetPurchasableUpgrades()
+ {
+ var result = new List();
+ var upgradeDatabase = UpgradeDatabase.Instance;
+
+ if (upgradeDatabase == null) return result;
+
+ var personalUpgrades = upgradeDatabase.GetPersonalUpgrades();
+ var ownedIds = GetOwnedUpgradeIdSet();
+
+ foreach (var upgrade in personalUpgrades)
+ {
+ if (upgrade == null) continue;
+
+ // 이미 보유하면 제외
+ if (ownedIds.Contains(upgrade.id)) continue;
+
+ // 선행 조건 충족 시 구매 가능
+ if (upgradeDatabase.ArePrerequisitesMet(upgrade.id, ownedIds))
+ {
+ result.Add(upgrade);
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// 보유한 업그레이드 데이터 목록 반환
+ ///
+ public List GetOwnedUpgradeData()
+ {
+ var result = new List();
+ var upgradeDatabase = UpgradeDatabase.Instance;
+
+ if (upgradeDatabase == null) return result;
+
+ foreach (int id in _ownedUpgradeIds)
+ {
+ var upgrade = upgradeDatabase.GetUpgradeById(id);
+ if (upgrade != null)
+ {
+ result.Add(upgrade);
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/Assets/Scripts/PlayerUpgradeManager.cs.meta b/Assets/Scripts/PlayerUpgradeManager.cs.meta
new file mode 100644
index 0000000..064ed0f
--- /dev/null
+++ b/Assets/Scripts/PlayerUpgradeManager.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: c2399ad44b71131439123eaec84abb08
\ No newline at end of file
diff --git a/Assets/Scripts/PlayerVisionProvider.cs b/Assets/Scripts/PlayerVisionProvider.cs
index 72f5cdb..46f9799 100644
--- a/Assets/Scripts/PlayerVisionProvider.cs
+++ b/Assets/Scripts/PlayerVisionProvider.cs
@@ -8,11 +8,11 @@ namespace Northbound
///
public class PlayerVisionProvider : NetworkBehaviour, IVisionProvider
{
- [Header("Vision Settings")]
- public float visionRange = 10f;
+ private PlayerStats _playerStats;
public override void OnNetworkSpawn()
{
+ _playerStats = GetComponent();
if (IsServer)
{
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
@@ -28,14 +28,14 @@ namespace Northbound
}
public ulong GetOwnerId() => OwnerClientId;
- public float GetVisionRange() => visionRange;
+ public float GetVisionRange() => _playerStats?.GetSight() ?? 10f;
public Transform GetTransform() => transform;
public bool IsActive() => IsSpawned;
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
- Gizmos.DrawWireSphere(transform.position, visionRange);
+ Gizmos.DrawWireSphere(transform.position, GetVisionRange());
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Scripts/Resource.cs b/Assets/Scripts/Resource.cs
index 32b788c..dbf9cfb 100644
--- a/Assets/Scripts/Resource.cs
+++ b/Assets/Scripts/Resource.cs
@@ -334,13 +334,21 @@ namespace Northbound
return;
int playerAvailableSpace = resourceManager.GetAvailableSpace(playerId);
+ // 플레이어의 작업량 가져오기
+ float playerWorkPower = GetPlayerWorkPower(playerId);
int gatheredAmount = Mathf.Min(
- resourcesPerGathering,
+ (int)playerWorkPower,
_currentResources.Value,
playerAvailableSpace
);
+
+
+
+
+
+
if (gatheredAmount <= 0)
{
return;
@@ -411,8 +419,30 @@ namespace Northbound
{
return transform;
}
+ ///
+ /// 플레이어의 작업량 가져오기 (PlayerInteraction.WorkPower)
+ ///
+ private float GetPlayerWorkPower(ulong playerId)
+ {
+ // PlayerInteraction 컴포넌트에서 workPower 가져오기
+ if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
+ {
+ if (client.PlayerObject != null)
+ {
+ var playerInteraction = client.PlayerObject.GetComponent();
+ if (playerInteraction != null)
+ {
+ return playerInteraction.WorkPower;
+ }
+ }
+ }
+
+ // 기본값: 10
+ return 10f;
+ }
private Worker FindWorkerForPlayer(ulong playerId)
+
{
if (NetworkManager.Singleton == null || NetworkManager.Singleton.SpawnManager == null)
{
diff --git a/Assets/Scripts/UpgradeDatabase.cs b/Assets/Scripts/UpgradeDatabase.cs
new file mode 100644
index 0000000..acb1c33
--- /dev/null
+++ b/Assets/Scripts/UpgradeDatabase.cs
@@ -0,0 +1,177 @@
+using System.Collections.Generic;
+using UnityEngine;
+using Northbound.Data;
+
+namespace Northbound
+{
+ ///
+ /// 업그레이드 데이터를 자동으로 로드하고 관리하는 싱글톤
+ /// Resources 폴더에서 모든 UpgradeData를 자동으로 로드
+ ///
+ public class UpgradeDatabase : MonoBehaviour
+ {
+ private static UpgradeDatabase _instance;
+ public static UpgradeDatabase Instance => _instance;
+
+ // Resources에서 자동 로드된 데이터
+ private List _upgradeDataList = new List();
+
+ // 조회용 딕셔너리
+ private Dictionary _upgradeById = new Dictionary();
+ private Dictionary> _upgradesByCategory = new Dictionary>();
+
+ // Resources 경로
+ private const string UPGRADE_RESOURCES_PATH = "Data/ScriptableObjects/Upgrade";
+
+ private void Awake()
+ {
+ if (_instance != null && _instance != this)
+ {
+ Destroy(gameObject);
+ return;
+ }
+
+ _instance = this;
+ LoadUpgradeData();
+ BuildLookupTables();
+ }
+
+ ///
+ /// Resources에서 모든 UpgradeData 자동 로드
+ ///
+ private void LoadUpgradeData()
+ {
+ _upgradeDataList.Clear();
+
+ var upgrades = Resources.LoadAll(UPGRADE_RESOURCES_PATH);
+ _upgradeDataList.AddRange(upgrades);
+
+ Debug.Log($"[UpgradeDatabase] {_upgradeDataList.Count}개의 업그레이드 데이터를 로드했습니다.");
+ }
+
+ ///
+ /// 조회용 테이블 구축
+ ///
+ private void BuildLookupTables()
+ {
+ _upgradeById.Clear();
+ _upgradesByCategory.Clear();
+
+ foreach (var upgrade in _upgradeDataList)
+ {
+ if (upgrade == null) continue;
+
+ // ID로 조회
+ if (!_upgradeById.ContainsKey(upgrade.id))
+ {
+ _upgradeById[upgrade.id] = upgrade;
+ }
+
+ // 카테고리로 조회
+ string category = upgrade.upgradeCategory;
+ if (!_upgradesByCategory.ContainsKey(category))
+ {
+ _upgradesByCategory[category] = new List();
+ }
+ _upgradesByCategory[category].Add(upgrade);
+ }
+ }
+
+ ///
+ /// 데이터 다시 로드 (에디터에서 데이터 변경 시 호출)
+ ///
+ public void ReloadData()
+ {
+ LoadUpgradeData();
+ BuildLookupTables();
+ }
+
+ #region Getters
+
+ ///
+ /// ID로 업그레이드 데이터 조회
+ ///
+ public UpgradeData GetUpgradeById(int id)
+ {
+ if (_upgradeById.TryGetValue(id, out var upgrade))
+ {
+ return upgrade;
+ }
+ Debug.LogWarning($"[UpgradeDatabase] ID {id}에 해당하는 업그레이드를 찾을 수 없습니다.");
+ return null;
+ }
+
+ ///
+ /// 카테고리로 업그레이드 목록 조회
+ ///
+ public List GetUpgradesByCategory(string category)
+ {
+ if (_upgradesByCategory.TryGetValue(category, out var upgrades))
+ {
+ return new List(upgrades);
+ }
+ return new List();
+ }
+
+ ///
+ /// 개인 업그레이드 목록 조회
+ ///
+ public List GetPersonalUpgrades()
+ {
+ var result = new List();
+ foreach (var upgrade in _upgradeDataList)
+ {
+ if (upgrade != null && upgrade.upgradeTarget == "person")
+ {
+ result.Add(upgrade);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 팀 업그레이드 목록 조회
+ ///
+ public List GetTeamUpgrades()
+ {
+ var result = new List();
+ foreach (var upgrade in _upgradeDataList)
+ {
+ if (upgrade != null && upgrade.upgradeTarget == "share")
+ {
+ result.Add(upgrade);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// 모든 업그레이드 데이터 반환
+ ///
+ public List GetAllUpgrades()
+ {
+ return new List(_upgradeDataList);
+ }
+
+ ///
+ /// 선행 업그레이드 조건 충족 여부 확인
+ ///
+ public bool ArePrerequisitesMet(int upgradeId, HashSet ownedUpgradeIds)
+ {
+ var upgrade = GetUpgradeById(upgradeId);
+ if (upgrade == null) return false;
+
+ foreach (int requiredId in upgrade.requireUpgradeId)
+ {
+ if (!ownedUpgradeIds.Contains(requiredId))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/Scripts/UpgradeDatabase.cs.meta b/Assets/Scripts/UpgradeDatabase.cs.meta
new file mode 100644
index 0000000..ba41b19
--- /dev/null
+++ b/Assets/Scripts/UpgradeDatabase.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 12780dc9ee390c742b6cbd405e59a916
\ No newline at end of file
diff --git a/GameData/Monster.csv b/GameData/Monster.csv
index 8c803bd..d6baf39 100644
--- a/GameData/Monster.csv
+++ b/GameData/Monster.csv
@@ -1,6 +1,6 @@
id,memo,move_speed,max_hp,sight,atk_range,atk_damage,atk_interval_sec,cost,weight,model_path,animation_controller_path,wave_min,wave_max
-101,Grunt(기본),6.7,75,6,1,3,1.2,3,1,Assets/Models/Skeleton_Minion.fbx,Assets/Animations/MonsterAnimationController.controller,1,10
+101,Grunt(기본),6.7,75,6,1,3,1.2,3,1.0,Assets/Models/Skeleton_Minion.fbx,Assets/Animations/MonsterAnimationController.controller,1,10
102,Tank(느림/단단),3.3,125,6,1,4,1.5,5,0.5,Assets/Models/Druid.fbx,Assets/Animations/MonsterAnimationController.controller,3,15
103,Ranged(원거리/약함),5.3,65,6,5,2,1.4,3,0.2,Assets/Models/Skeleton_Warrior.fbx,Assets/Animations/MonsterAnimationController.controller,3,20
-104,Fast(빠름/약함),8.6,45,6,1,2,1,3,0.333,Assets/Models/Skeleton_Rogue.fbx,Assets/Animations/MonsterAnimationController.controller,3,20
+104,Fast(빠름/약함),8.6,45,6,1,2,1.0,3,0.333,Assets/Models/Skeleton_Rogue.fbx,Assets/Animations/MonsterAnimationController.controller,3,20
105,Elite(소수 정예),6.6,100,6,1,7,1.3,6,0.1,Assets/Models/Barbarian_Large.fbx,Assets/Animations/MonsterAnimationController.controller,5,20
diff --git a/GameData/Tower.csv b/GameData/Tower.csv
index ad9c97b..1c74d6a 100644
--- a/GameData/Tower.csv
+++ b/GameData/Tower.csv
@@ -1,12 +1,12 @@
id,memo,building_name,level,upgrade_to,tower_type,mana,manpower,size_x,size_y,size_z,max_hp,sight,atk_range,atk_damage,atk_interval_sec,model_path
-1,타워,Arrow Tower Lv.1,1,2,attack,100,10,4,10,4,50,10,5,3,2,Assets/Models/building_tower_B_blue.fbx
-2,타워,Arrow Tower Lv.2,2,3,attack,200,20,4,10,4,75,10,10,6,2,Assets/Models/building_tower_B_blue.fbx
-3,타워,Arrow Tower Lv.3,3,4,attack,300,30,4,10,4,100,15,10,9,2,Assets/Models/building_tower_B_blue.fbx
-4,타워,Arrow Tower Lv.4,4,5,attack,400,40,4,10,4,150,15,15,12,2,Assets/Models/building_tower_B_blue.fbx
+1,타워,Arrow Tower Lv.1,1,2.0,attack,100,10,4,10,4,50,10,5,3,2,Assets/Models/building_tower_B_blue.fbx
+2,타워,Arrow Tower Lv.2,2,3.0,attack,200,20,4,10,4,75,10,10,6,2,Assets/Models/building_tower_B_blue.fbx
+3,타워,Arrow Tower Lv.3,3,4.0,attack,300,30,4,10,4,100,15,10,9,2,Assets/Models/building_tower_B_blue.fbx
+4,타워,Arrow Tower Lv.4,4,5.0,attack,400,40,4,10,4,150,15,15,12,2,Assets/Models/building_tower_B_blue.fbx
5,타워,Arrow Tower Lv.5,5,,attack,500,50,4,10,4,200,20,15,15,2,Assets/Models/building_tower_B_blue.fbx
-6,벽,Wall Lv.1,1,7,defense,10,10,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
-7,벽,Wall Lv.2,2,8,defense,30,10,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
-8,벽,Wall Lv.3,3,9,defense,50,20,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
-9,벽,Wall Lv.4,4,10,defense,100,20,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
+6,벽,Wall Lv.1,1,7.0,defense,10,10,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
+7,벽,Wall Lv.2,2,8.0,defense,30,10,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
+8,벽,Wall Lv.3,3,9.0,defense,50,20,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
+9,벽,Wall Lv.4,4,10.0,defense,100,20,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
10,벽,Wall Lv.5,5,,defense,150,20,8,4,3,30,1,0,0,0,Assets/Models/wall_straight.fbx
11,와드,Ward Lv.1,1,,sight,10,10,1,1,1,10,5,0,0,0,Assets/Models/torch.fbx