commit
b77e579848
|
|
@ -101,3 +101,6 @@ InitTestScene*.unity*
|
||||||
/数据表
|
/数据表
|
||||||
/.vscode
|
/.vscode
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
|
/.dotnet-home
|
||||||
|
/[Aa]ssets/RawResources
|
||||||
|
/[Aa]ssets/RawResources.meta
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# 底座表
|
# Id 策划备注 EntityId Name AttackSpeed PropertyType Constraint PossibleTag
|
||||||
# Id EntityId Name AttackSpeed PropertyType Constraint PossibleTag
|
|
||||||
# int int string float[] AttackPropertyType string TagType[]
|
# int int string float[] AttackPropertyType string TagType[]
|
||||||
# 底座编号 策划备注 实体Id 底座名 攻击速度(秒/次) 攻击属性 属性约束 可能出现的Tag
|
# 底座编号 实体Id 底座名 攻击速度(秒/次) 攻击属性 属性约束 可能出现的Tag
|
||||||
1 301 元素底座 [2,1.5,1,0.8,0.7] Fire [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 301 元素底座 [2,1.5,1,0.8,0.7] Fire [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 301 控制底座 [4,3.8,3.6,3.4,3.2] Ice [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 301 控制底座 [4,3.8,3.6,3.4,3.2] Ice [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 301 穿透底座 [1,0,8,0,6,0.4,0.2] Physics [Pierce,Crit,Overpenetrate,Execution]
|
3 301 穿透底座 [1,0,8,0,6,0.4,0.2] Physics [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# 轴承表
|
# Id 策划备注 EntityId Name RotateSpeed AttackRange Constraint PossibleTag
|
||||||
# Id EntityId Name RotateSpeed AttackRange Constraint PossibleTag
|
|
||||||
# int int string float[] float[] string TagType[]
|
# int int string float[] float[] string TagType[]
|
||||||
# 轴承编号 策划备注 实体Id 轴承名 各品质旋转速度 各品质攻击范围 属性约束 可能出现的Tag
|
# 轴承编号 实体Id 轴承名 各品质旋转速度 各品质攻击范围 属性约束 可能出现的Tag
|
||||||
1 201 元素轴承 [10,12,13,14,15] [2,2,2,2,2] [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 201 元素轴承 [10,12,13,14,15] [2,2,2,2,2] [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 201 控制轴承 [20,25,30,32,35] [6,6.5,7,8,8] [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 201 控制轴承 [20,25,30,32,35] [6,6.5,7,8,8] [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 201 穿透轴承 [60,70,80,90,100] [4,4.5,5,5.5,6] [Pierce,Crit,Overpenetrate,Execution]
|
3 201 穿透轴承 [60,70,80,90,100] [4,4.5,5,5.5,6] [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Id 策划备注 EntityId BaseHp BaseDamage Speed DropCoin
|
||||||
|
# int int int int float int
|
||||||
|
# 敌人Id 敌人实体Id 基础血量 基地伤害 移动速度 掉落硬币
|
||||||
|
1 1001 500 5 1 5
|
||||||
|
2 1002 5000 50 0.5 100
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d2ccda2aca4a348428805b273ee2aaf2
|
guid: d860a5c1c09addf4ba7539e97c85c0bd
|
||||||
TextScriptImporter:
|
TextScriptImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
# 实体表
|
# Id 策划备注 AssetName
|
||||||
# Id AssetName
|
|
||||||
# int string
|
# int string
|
||||||
# 实体编号 策划备注 资源名称
|
# 实体编号 资源名称
|
||||||
101 测试枪口 TestMuzzle
|
101 测试枪口 TestMuzzle
|
||||||
201 测试轴承 TestBearing
|
201 测试轴承 TestBearing
|
||||||
301 测试底座 TestBase
|
301 测试底座 TestBase
|
||||||
|
1001 测试普通敌人 TestEnemy
|
||||||
|
1002 测试Boss TestBoss
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Id 列1 Title Description Options
|
||||||
|
# int string string string
|
||||||
|
# 事件编号 策划备注 事件题目 事件描述 事件选项
|
||||||
|
1 赌马 一名商人邀请你下注。赢了就能赚一笔。 [{"optionText":"下注 100(金)- 稳健:70% 赢 150","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":150}}],"probability":0.7},{"optionText":"下注 100(金)- 激进:30% 赢 250","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":250}}],"probability":0.3}]
|
||||||
|
2 工匠的熔炉 工匠以金币交换防御塔组件。 [{"optionText":"交出 3 个白色组件,获得 50 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":3,"Rarity":"White"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":3,"Rarity":"White"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"交出 2 个绿色组件,获得 70 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"Green"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"Green"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":70}}]},{"optionText":"交出 1 个蓝色组件,获得 80 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":1,"Rarity":"Blue"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":1,"Rarity":"Blue"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":80}}]},{"optionText":"拒绝","rewardEffects":[]}]
|
||||||
|
3 代价与回报 某种黑暗力量向你索取代价。 [{"optionText":"展示你的防御塔","requirements":[{"type":"TowerCountAtLeast","param":{"Count":2}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"离开","rewardEffects":[]}]
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d64aa32f74f749c42847421244466515
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Id 列1 LevelThemeType BaseHp StartCoin VictoryType VictoryParam RewardGold
|
||||||
|
# int LevelThemeType int int VictoryType string int
|
||||||
|
# 关卡号 策划备注 关卡所属主题类型 基地初始生命 初始硬币 胜利条件 胜利参数 奖励金币
|
||||||
|
1 平原1 Plain 100 100 PhasesCleared 30
|
||||||
|
2 平原2 Plain 100 100 PhasesCleared 30
|
||||||
|
3 平原3 Plain 100 100 PhasesCleared 40
|
||||||
|
4 平原4 Plain 100 100 BossDead 100
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 05abbe814ec127b46a5e1a640a899e09
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Id 列1 DurationSeconds EndType EndParam
|
||||||
|
# int int PhaseEndType string
|
||||||
|
# 关卡阶段号 策划备注 波次持续时间 波次结束条件 结束参数
|
||||||
|
1001 平原1.1 60 TimeElapsed 60
|
||||||
|
1002 平原1.2 60 TimeElapsed 60
|
||||||
|
1003 平原1.3 60 TimeElapsed 60
|
||||||
|
1004 平原1.4 60 EnemiesCleared 60
|
||||||
|
1005 平原1.* 0
|
||||||
|
2001 平原2.1 60 TimeElapsed 60
|
||||||
|
2002 平原2.2 60 TimeElapsed 60
|
||||||
|
2003 平原2.3 60 TimeElapsed 60
|
||||||
|
2004 平原2.4 60 EnemiesCleared 60
|
||||||
|
2005 平原2.* 0
|
||||||
|
3001 平原3.1 60 TimeElapsed 60
|
||||||
|
3002 平原3.2 60 TimeElapsed 60
|
||||||
|
3003 平原3.3 60 TimeElapsed 60
|
||||||
|
3004 平原3.4 60 EnemiesCleared 60
|
||||||
|
3005 平原3.* 0
|
||||||
|
4001 平原4.1 60 TimeElapsed 60
|
||||||
|
4002 平原4.2 60 TimeElapsed 60
|
||||||
|
4003 平原4.3 60 TimeElapsed 60
|
||||||
|
4004 平原4.4 60 TimeElapsed 60
|
||||||
|
4005 平原4.5 0 BossDead
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 392984cd87843f34989251453d4a455b
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Id 列1 SpawnPointId StartTime EntryType EnemyId Count Interval Duration Gap
|
||||||
|
# int int int EntryType int int float int float
|
||||||
|
# 阶段条目号 策划备注 敌人出生口Id 相对时间 条目类型 敌人Id 单次出怪数量 出怪间隔 持续时间 单怪出生时间间隔
|
||||||
|
1001001 1 5 Stream 1 3 5 60 0
|
||||||
|
1001002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
1002001 1 3 Stream 1 3 5 60 0
|
||||||
|
1002002 2 3 Burst 1 10 0 0 0.5
|
||||||
|
1003001 1 5 Stream 1 3 5 60 0
|
||||||
|
1003002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
1004001 1 3 Stream 1 3 5 60 0
|
||||||
|
1004002 2 3 Burst 1 10 0 0 0.5
|
||||||
|
1005001 1 5 Stream 1 3 5 60 0
|
||||||
|
1005002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
2001001 1 5 Stream 1 3 5 60 0
|
||||||
|
2002001 1 5 Burst 1 10 0 0 0.5
|
||||||
|
2003001 1 5 Stream 1 3 5 60 0
|
||||||
|
2004001 1 5 Burst 1 10 0 0 0.5
|
||||||
|
2005001 1 5 Stream 1 3 5 60 0
|
||||||
|
3001001 1 5 Stream 1 3 5 60 0
|
||||||
|
3001002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
3002001 1 3 Stream 1 3 5 60 0
|
||||||
|
3002002 2 3 Burst 1 10 0 0 0.5
|
||||||
|
3003001 1 5 Stream 1 3 5 60 0
|
||||||
|
3003002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
3004001 1 3 Stream 1 3 5 60 0
|
||||||
|
3004002 2 3 Burst 1 10 0 0 0.5
|
||||||
|
3005001 1 5 Stream 1 3 5 60 0
|
||||||
|
3005002 2 5 Burst 1 10 0 0 0.5
|
||||||
|
4001001 1 5 Stream 1 3 5 60 0
|
||||||
|
4002001 1 5 Burst 1 10 0 0 0.5
|
||||||
|
4003001 1 5 Stream 1 3 5 60 0
|
||||||
|
4004001 1 5 Burst 1 10 0 0 0.5
|
||||||
|
4005001 1 5 Boss 2 1 5 60 0
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 554836c1a57522a4e887ae48023708fc
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# 枪口表
|
# Id 策划备注 EntityId Name AttackDamage DamageRandomRate Method Constraint PossibleTag
|
||||||
# Id EntityId Name AttackDamage DamageRandomRate Method Constraint PossibleTag
|
# int int string int[] float AttackMethodType Constraint TagType[]
|
||||||
# int int string int[] float AttackMethodType string TagType[]
|
# 枪口编号 策划备注 实体Id 枪口名 各品质伤害 伤害浮动 攻击方式 Constraint 可能出现的Tag
|
||||||
# 枪口编号 策划备注 实体Id 枪口名 各品质伤害 伤害浮动 攻击方式 属性约束 可能出现的Tag
|
|
||||||
1 101 元素枪口 [20,30,40,50,80] 0.05 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno]
|
1 101 元素枪口 [20,30,40,50,80] 0.05 NormalBullet [Fire,BurnSpread,IgniteBurst,Inferno]
|
||||||
2 101 控制枪口 [30,50,70,90,100] 0.01 NormalBullet [Ice,FreezeMask,Shatter,AbsoluteZero]
|
2 101 控制枪口 [30,50,70,90,100] 0.01 NormalBullet [Ice,FreezeMask,Shatter,AbsoluteZero]
|
||||||
3 101 穿透枪口 [50,55,60,80,90] 0.02 NormalBullet [Pierce,Crit,Overpenetrate,Execution]
|
3 101 穿透枪口 [50,55,60,80,90] 0.02 NormalBullet [Pierce,Crit,Overpenetrate,Execution]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Id 列1 Rarity MinPrice MaxPrice
|
||||||
|
# int RarityType int int
|
||||||
|
# 定价编号 策划备注 商品品质 最低价格 最高价格
|
||||||
|
1 White 10 30
|
||||||
|
2 Green 50 70
|
||||||
|
3 Blue 100 120
|
||||||
|
4 Purple 150 170
|
||||||
|
5 Red 200 220
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 55f4464fd7c6ad14584b9b10aabc18ba
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -6,3 +6,5 @@
|
||||||
100 主菜单 MenuForm Default False True
|
100 主菜单 MenuForm Default False True
|
||||||
101 设置 SettingForm Default False True
|
101 设置 SettingForm Default False True
|
||||||
102 关于 AboutForm Default False True
|
102 关于 AboutForm Default False True
|
||||||
|
200 事件UI EventForm Default False True
|
||||||
|
201 背包UI RepoForm Default False True
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# 武器表
|
|
||||||
# Id Attack AttackInterval BulletId BulletSpeed BulletSoundId
|
|
||||||
# int int float int float int
|
|
||||||
# 武器编号 策划备注 攻击力 攻击间隔 子弹编号 子弹速度 子弹声音编号
|
|
||||||
30000 玩家武器 100 0.2 50000 20 10000
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3ecda907b9217e5499e31cb81a154ff4
|
guid: 7258f46ca3cc69546ad93c07bcd08582
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0cc71b1087c7dfd42a8233a7101fc27e
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e69bde6c996edd94397c98e836e81bf3
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 80277bb9563227842a4231e7178e88a7
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f4c8efb3dfa084241abdcb15db1f27e8
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &6419463151899262168
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 2220800525145552943}
|
||||||
|
- component: {fileID: 6893725239225079109}
|
||||||
|
- component: {fileID: 1122596187208388309}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: TestBoss
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &2220800525145552943
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
|
m_LocalScale: {x: 1.3, y: 1.3, z: 1.3}
|
||||||
|
m_ConstrainProportionsScale: 1
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!212 &6893725239225079109
|
||||||
|
SpriteRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 0
|
||||||
|
m_ReceiveShadows: 0
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 0
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 0
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_SortingLayerID: -1869315837
|
||||||
|
m_SortingLayer: 1
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||||
|
type: 3}
|
||||||
|
m_Color: {r: 0.7, g: 0.7, b: 0.7, a: 1}
|
||||||
|
m_FlipX: 0
|
||||||
|
m_FlipY: 0
|
||||||
|
m_DrawMode: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_AdaptiveModeThreshold: 0.5
|
||||||
|
m_SpriteTileMode: 0
|
||||||
|
m_WasSpriteAssigned: 1
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_SpriteSortPoint: 0
|
||||||
|
--- !u!114 &1122596187208388309
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 9baf591df06b9bb44ba5313457ba84b3, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b375b29295fee874795f6e0f2b9a6fe6
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &6419463151899262168
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 2220800525145552943}
|
||||||
|
- component: {fileID: 6893725239225079109}
|
||||||
|
- component: {fileID: 1122596187208388309}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: TestEnemy
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &2220800525145552943
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
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: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!212 &6893725239225079109
|
||||||
|
SpriteRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 0
|
||||||
|
m_ReceiveShadows: 0
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 0
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 0
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_SortingLayerID: -1869315837
|
||||||
|
m_SortingLayer: 1
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_Sprite: {fileID: -2413806693520163455, guid: a86470a33a6bf42c4b3595704624658b,
|
||||||
|
type: 3}
|
||||||
|
m_Color: {r: 0.7, g: 0.7, b: 0.7, a: 1}
|
||||||
|
m_FlipX: 0
|
||||||
|
m_FlipY: 0
|
||||||
|
m_DrawMode: 0
|
||||||
|
m_Size: {x: 1, y: 1}
|
||||||
|
m_AdaptiveModeThreshold: 0.5
|
||||||
|
m_SpriteTileMode: 0
|
||||||
|
m_WasSpriteAssigned: 1
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_SpriteSortPoint: 0
|
||||||
|
--- !u!114 &1122596187208388309
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6419463151899262168}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 9baf591df06b9bb44ba5313457ba84b3, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0cd1424cd9c330d4492a6d541cb4275e
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -321,7 +321,7 @@ Camera:
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_ClearFlags: 1
|
m_ClearFlags: 1
|
||||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
m_BackGroundColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
m_projectionMatrixMode: 1
|
m_projectionMatrixMode: 1
|
||||||
m_GateFitMode: 2
|
m_GateFitMode: 2
|
||||||
m_FOVAxisMode: 0
|
m_FOVAxisMode: 0
|
||||||
|
|
@ -343,10 +343,10 @@ Camera:
|
||||||
width: 1
|
width: 1
|
||||||
height: 1
|
height: 1
|
||||||
near clip plane: 0.3
|
near clip plane: 0.3
|
||||||
far clip plane: 1000
|
far clip plane: 50
|
||||||
field of view: 60
|
field of view: 60
|
||||||
orthographic: 0
|
orthographic: 1
|
||||||
orthographic size: 5
|
orthographic size: 7.5
|
||||||
m_Depth: -1
|
m_Depth: -1
|
||||||
m_CullingMask:
|
m_CullingMask:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
|
|
|
||||||
|
|
@ -343,10 +343,10 @@ Camera:
|
||||||
width: 1
|
width: 1
|
||||||
height: 1
|
height: 1
|
||||||
near clip plane: 0.3
|
near clip plane: 0.3
|
||||||
far clip plane: 1000
|
far clip plane: 50
|
||||||
field of view: 60
|
field of view: 60
|
||||||
orthographic: 0
|
orthographic: 1
|
||||||
orthographic size: 5
|
orthographic size: 7.5
|
||||||
m_Depth: -1
|
m_Depth: -1
|
||||||
m_CullingMask:
|
m_CullingMask:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
|
|
@ -371,7 +371,7 @@ Transform:
|
||||||
m_GameObject: {fileID: 961739749}
|
m_GameObject: {fileID: 961739749}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
m_LocalPosition: {x: 0, y: 0, z: -10}
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// Feedback: mailto:ellan@gameframework.cn
|
// Feedback: mailto:ellan@gameframework.cn
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
using CustomComponent;
|
||||||
using GeometryTD.CustomComponent;
|
using GeometryTD.CustomComponent;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
@ -17,12 +18,18 @@ public partial class GameEntry : MonoBehaviour
|
||||||
|
|
||||||
public static HPBarComponent HPBar { get; private set; }
|
public static HPBarComponent HPBar { get; private set; }
|
||||||
|
|
||||||
public static EnemyManagerComponent EnemyManager { get; private set; }
|
public static UIRouterComponent UIRouter { get; private set; }
|
||||||
|
|
||||||
|
public static EventNodeComponent EventNode { get; private set; }
|
||||||
|
|
||||||
|
public static CombatNodeComponent CombatNode { get; private set; }
|
||||||
|
|
||||||
private static void InitCustomComponents()
|
private static void InitCustomComponents()
|
||||||
{
|
{
|
||||||
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
||||||
HPBar = UnityGameFramework.Runtime.GameEntry.GetComponent<HPBarComponent>();
|
HPBar = UnityGameFramework.Runtime.GameEntry.GetComponent<HPBarComponent>();
|
||||||
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
|
UIRouter = UnityGameFramework.Runtime.GameEntry.GetComponent<UIRouterComponent>();
|
||||||
|
EventNode = UnityGameFramework.Runtime.GameEntry.GetComponent<EventNodeComponent>();
|
||||||
|
CombatNode = UnityGameFramework.Runtime.GameEntry.GetComponent<CombatNodeComponent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c942b083720d4d588063c5f1dde93428
|
||||||
|
timeCreated: 1772165636
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GameFramework.DataTable;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 鎴樻枟鑺傜偣缁勪欢
|
||||||
|
/// </summary>
|
||||||
|
public class CombatNodeComponent : GameFrameworkComponent
|
||||||
|
{
|
||||||
|
// Level.Id => Level
|
||||||
|
private readonly Dictionary<int, DRLevel> _levelsById = new();
|
||||||
|
|
||||||
|
// LevelId => LevelPhases
|
||||||
|
private readonly Dictionary<int, List<DRLevelPhase>> _phasesByLevelId = new();
|
||||||
|
|
||||||
|
// LevelPhase.Id => LevelSpawnEntries
|
||||||
|
private readonly Dictionary<int, List<DRLevelSpawnEntry>> _spawnEntriesByPhaseId = new();
|
||||||
|
private readonly Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> _selectedSpawnEntriesByPhaseId = new();
|
||||||
|
private readonly List<int> _levelIdBuffer = new();
|
||||||
|
private readonly CombatScheduler _combatScheduler = new CombatScheduler();
|
||||||
|
|
||||||
|
private bool _runtimeInitialized;
|
||||||
|
|
||||||
|
public LevelThemeType CurrentThemeType { get; private set; }
|
||||||
|
public DRLevel CurrentLevel { get; private set; }
|
||||||
|
|
||||||
|
public void OnInit(LevelThemeType themeType)
|
||||||
|
{
|
||||||
|
ShutdownBattleRuntime();
|
||||||
|
|
||||||
|
CurrentThemeType = themeType;
|
||||||
|
CurrentLevel = null;
|
||||||
|
_levelsById.Clear();
|
||||||
|
_phasesByLevelId.Clear();
|
||||||
|
_spawnEntriesByPhaseId.Clear();
|
||||||
|
_selectedSpawnEntriesByPhaseId.Clear();
|
||||||
|
_levelIdBuffer.Clear();
|
||||||
|
|
||||||
|
IDataTable<DRLevel> dtLevel = GameEntry.DataTable.GetDataTable<DRLevel>();
|
||||||
|
IDataTable<DRLevelPhase> dtLevelPhase = GameEntry.DataTable.GetDataTable<DRLevelPhase>();
|
||||||
|
IDataTable<DRLevelSpawnEntry> dtSpawnEntry = GameEntry.DataTable.GetDataTable<DRLevelSpawnEntry>();
|
||||||
|
if (dtLevel == null || dtLevelPhase == null || dtSpawnEntry == null)
|
||||||
|
{
|
||||||
|
Log.Warning("CombatNodeComponent init failed. Missing data table(s).");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRLevel[] levels = dtLevel.GetAllDataRows();
|
||||||
|
for (int i = 0; i < levels.Length; i++)
|
||||||
|
{
|
||||||
|
DRLevel level = levels[i];
|
||||||
|
if (level.LevelThemeType != themeType)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_levelsById[level.Id] = level;
|
||||||
|
_phasesByLevelId[level.Id] = new List<DRLevelPhase>();
|
||||||
|
_levelIdBuffer.Add(level.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
DRLevelPhase[] levelPhases = dtLevelPhase.GetAllDataRows();
|
||||||
|
for (int i = 0; i < levelPhases.Length; i++)
|
||||||
|
{
|
||||||
|
DRLevelPhase phase = levelPhases[i];
|
||||||
|
int levelId = phase.Id / 1000;
|
||||||
|
if (!_levelsById.ContainsKey(levelId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_phasesByLevelId.TryGetValue(levelId, out List<DRLevelPhase> phases))
|
||||||
|
{
|
||||||
|
phases = new List<DRLevelPhase>();
|
||||||
|
_phasesByLevelId[levelId] = phases;
|
||||||
|
}
|
||||||
|
|
||||||
|
phases.Add(phase);
|
||||||
|
_spawnEntriesByPhaseId[phase.Id] = new List<DRLevelSpawnEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
DRLevelSpawnEntry[] spawnEntries = dtSpawnEntry.GetAllDataRows();
|
||||||
|
for (int i = 0; i < spawnEntries.Length; i++)
|
||||||
|
{
|
||||||
|
DRLevelSpawnEntry spawnEntry = spawnEntries[i];
|
||||||
|
int phaseId = spawnEntry.Id / 1000;
|
||||||
|
int levelId = phaseId / 1000;
|
||||||
|
if (!_levelsById.ContainsKey(levelId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_spawnEntriesByPhaseId.TryGetValue(phaseId, out List<DRLevelSpawnEntry> entries))
|
||||||
|
{
|
||||||
|
entries = new List<DRLevelSpawnEntry>();
|
||||||
|
_spawnEntriesByPhaseId[phaseId] = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.Add(spawnEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (List<DRLevelPhase> phaseList in _phasesByLevelId.Values)
|
||||||
|
{
|
||||||
|
phaseList.Sort((left, right) => left.Id.CompareTo(right.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (List<DRLevelSpawnEntry> phaseSpawnEntries in _spawnEntriesByPhaseId.Values)
|
||||||
|
{
|
||||||
|
phaseSpawnEntries.Sort((left, right) =>
|
||||||
|
{
|
||||||
|
int timeCompare = left.StartTime.CompareTo(right.StartTime);
|
||||||
|
if (timeCompare != 0)
|
||||||
|
{
|
||||||
|
return timeCompare;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left.Id.CompareTo(right.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info(
|
||||||
|
"CombatNodeComponent initialized. Theme={0}, Levels={1}, Phases={2}, Entries={3}.",
|
||||||
|
themeType,
|
||||||
|
_levelsById.Count,
|
||||||
|
CountPhases(),
|
||||||
|
CountEntries());
|
||||||
|
|
||||||
|
EnsureBattleRuntimeInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartCombat()
|
||||||
|
{
|
||||||
|
if (!EnsureBattleRuntimeInitialized())
|
||||||
|
{
|
||||||
|
Log.Warning("CombatNodeComponent start failed. Missing scheduler runtime.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TrySelectRandomLevel(out DRLevel selectedLevel))
|
||||||
|
{
|
||||||
|
Log.Warning("CombatNodeComponent has no level cache. Call OnInit(levelThemeType) first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_phasesByLevelId.TryGetValue(selectedLevel.Id, out List<DRLevelPhase> phaseList) ||
|
||||||
|
phaseList == null ||
|
||||||
|
phaseList.Count <= 0)
|
||||||
|
{
|
||||||
|
Log.Warning("CombatNodeComponent start failed. Level '{0}' has no phase data.", selectedLevel.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedSpawnEntriesByPhaseId.Clear();
|
||||||
|
foreach (var phase in phaseList)
|
||||||
|
{
|
||||||
|
if (_spawnEntriesByPhaseId.TryGetValue(phase.Id, out List<DRLevelSpawnEntry> entries) &&
|
||||||
|
entries != null)
|
||||||
|
{
|
||||||
|
_selectedSpawnEntriesByPhaseId[phase.Id] = entries;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedSpawnEntriesByPhaseId[phase.Id] = new List<DRLevelSpawnEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentLevel = selectedLevel;
|
||||||
|
_combatScheduler.Start(selectedLevel, phaseList, _selectedSpawnEntriesByPhaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_runtimeInitialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combatScheduler.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnShutdown()
|
||||||
|
{
|
||||||
|
CurrentLevel = null;
|
||||||
|
ShutdownBattleRuntime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
OnShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EnsureBattleRuntimeInitialized()
|
||||||
|
{
|
||||||
|
if (_runtimeInitialized)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combatScheduler.OnInit();
|
||||||
|
_runtimeInitialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShutdownBattleRuntime()
|
||||||
|
{
|
||||||
|
if (!_runtimeInitialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_combatScheduler.OnDestroy();
|
||||||
|
_runtimeInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TrySelectRandomLevel(out DRLevel level)
|
||||||
|
{
|
||||||
|
level = null;
|
||||||
|
if (_levelIdBuffer.Count <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectedIndex = Random.Range(0, _levelIdBuffer.Count);
|
||||||
|
int selectedLevelId = _levelIdBuffer[selectedIndex];
|
||||||
|
return _levelsById.TryGetValue(selectedLevelId, out level);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CountPhases()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
foreach (List<DRLevelPhase> phases in _phasesByLevelId.Values)
|
||||||
|
{
|
||||||
|
count += phases.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CountEntries()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
foreach (List<DRLevelSpawnEntry> list in _spawnEntriesByPhaseId.Values)
|
||||||
|
{
|
||||||
|
count += list.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5e646500b9304d438746f2351c71a0f4
|
||||||
|
timeCreated: 1772103300
|
||||||
|
|
@ -0,0 +1,422 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using GameFramework.Event;
|
||||||
|
using GameFramework.Resource;
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using GeometryTD.Entity;
|
||||||
|
using GeometryTD.Entity.EntityData;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public class CombatScheduler
|
||||||
|
{
|
||||||
|
private enum SchedulerState : byte
|
||||||
|
{
|
||||||
|
Idle = 0,
|
||||||
|
WaitingForMap = 1,
|
||||||
|
RunningPhase = 2,
|
||||||
|
Completed = 3,
|
||||||
|
Failed = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<DRLevelPhase> _phaseBuffer = new List<DRLevelPhase>();
|
||||||
|
private readonly Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> _spawnEntriesByPhaseId =
|
||||||
|
new Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>>();
|
||||||
|
|
||||||
|
private readonly EnemyManager _enemyManager = new EnemyManager();
|
||||||
|
|
||||||
|
private EntityComponent _entity;
|
||||||
|
private DRLevel _currentLevel;
|
||||||
|
private DRLevelPhase _currentPhase;
|
||||||
|
private int _currentPhaseIndex;
|
||||||
|
private float _currentPhaseElapsed;
|
||||||
|
private SchedulerState _state = SchedulerState.Idle;
|
||||||
|
private int _loadingMapEntityId;
|
||||||
|
private int _loadedMapEntityId;
|
||||||
|
private bool _initialized;
|
||||||
|
private bool _isEntityEventSubscribed;
|
||||||
|
private MapEntity _currentMap;
|
||||||
|
|
||||||
|
public bool IsRunning => _state == SchedulerState.WaitingForMap || _state == SchedulerState.RunningPhase;
|
||||||
|
public bool IsCompleted => _state == SchedulerState.Completed;
|
||||||
|
public DRLevel CurrentLevel => _currentLevel;
|
||||||
|
public DRLevelPhase CurrentPhase => _currentPhase;
|
||||||
|
public MapEntity CurrentMap => _currentMap;
|
||||||
|
|
||||||
|
public void OnInit()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
_entity = GameEntry.Entity;
|
||||||
|
EnsureEntityEventSubscribed();
|
||||||
|
_enemyManager.OnInit(this);
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetRuntime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(
|
||||||
|
DRLevel level,
|
||||||
|
IReadOnlyList<DRLevelPhase> phases,
|
||||||
|
IReadOnlyDictionary<int, IReadOnlyList<DRLevelSpawnEntry>> spawnEntriesByPhaseId)
|
||||||
|
{
|
||||||
|
if (!_initialized || _entity == null)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Warning("CombatScheduler start failed. Runtime is not initialized.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level == null || phases == null || phases.Count <= 0)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Warning("CombatScheduler start failed. Invalid level or phase data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enemyManager.EndPhase();
|
||||||
|
HideCurrentMapIfNeeded();
|
||||||
|
ResetRuntime();
|
||||||
|
|
||||||
|
_currentLevel = level;
|
||||||
|
foreach (var phase in phases)
|
||||||
|
{
|
||||||
|
_phaseBuffer.Add(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spawnEntriesByPhaseId != null)
|
||||||
|
{
|
||||||
|
foreach (var pair in spawnEntriesByPhaseId)
|
||||||
|
{
|
||||||
|
_spawnEntriesByPhaseId[pair.Key] = pair.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_phaseBuffer.Count <= 0)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Warning("CombatScheduler start failed. Level '{0}' has no phase data.", level.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryShowMap(level))
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPhase = null;
|
||||||
|
_currentPhaseIndex = -1;
|
||||||
|
_currentPhaseElapsed = 0f;
|
||||||
|
_state = SchedulerState.WaitingForMap;
|
||||||
|
|
||||||
|
Log.Info("CombatScheduler started. Level={0}, PhaseCount={1}.", _currentLevel.Id, _phaseBuffer.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case SchedulerState.WaitingForMap:
|
||||||
|
if (_currentMap == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginNextPhase();
|
||||||
|
return;
|
||||||
|
case SchedulerState.RunningPhase:
|
||||||
|
UpdateCurrentPhase(elapseSeconds, realElapseSeconds);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enemyManager.OnDestroy();
|
||||||
|
HideCurrentMapIfNeeded();
|
||||||
|
ResetRuntime();
|
||||||
|
UnsubscribeEntityEvents();
|
||||||
|
_entity = null;
|
||||||
|
_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryShowMap(DRLevel level)
|
||||||
|
{
|
||||||
|
string mapAssetName = level.Id.ToString();
|
||||||
|
string mapAssetPath = AssetUtility.GetLevelMapAsset(mapAssetName);
|
||||||
|
if (GameEntry.Resource.HasAsset(mapAssetPath) == HasAssetResult.NotExist)
|
||||||
|
{
|
||||||
|
Log.Warning(
|
||||||
|
"CombatScheduler start failed. Level '{0}' map asset not found: '{1}'.",
|
||||||
|
level.Id,
|
||||||
|
mapAssetPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadingMapEntityId = _entity.GenerateSerialId();
|
||||||
|
_entity.ShowMap(new MapData(_loadingMapEntityId, level.Id, Vector3.zero), mapAssetName);
|
||||||
|
|
||||||
|
Log.Info(
|
||||||
|
"CombatScheduler loading map. Level={0}, Asset='{1}', EntityId={2}.",
|
||||||
|
level.Id,
|
||||||
|
mapAssetPath,
|
||||||
|
_loadingMapEntityId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCurrentPhase(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (_currentPhase == null)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Warning("CombatScheduler update failed. Current phase is null.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPhaseElapsed += elapseSeconds;
|
||||||
|
_enemyManager.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||||
|
|
||||||
|
if (!ShouldEndCurrentPhase())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompleteCurrentPhase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldEndCurrentPhase()
|
||||||
|
{
|
||||||
|
switch (_currentPhase.EndType)
|
||||||
|
{
|
||||||
|
case PhaseEndType.TimeElapsed:
|
||||||
|
return _currentPhaseElapsed >= ResolveTimeElapsedThreshold(_currentPhase);
|
||||||
|
case PhaseEndType.EnemiesCleared:
|
||||||
|
case PhaseEndType.BossDead:
|
||||||
|
return _enemyManager.IsPhaseSpawnCompleted && _enemyManager.AliveEnemyCount <= 0;
|
||||||
|
case PhaseEndType.None:
|
||||||
|
default:
|
||||||
|
if (_currentPhase.DurationSeconds > 0 && _currentPhaseElapsed >= _currentPhase.DurationSeconds)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _enemyManager.IsPhaseSpawnCompleted && _enemyManager.AliveEnemyCount <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float ResolveTimeElapsedThreshold(DRLevelPhase phase)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(phase.EndParam) &&
|
||||||
|
float.TryParse(phase.EndParam, NumberStyles.Float, CultureInfo.InvariantCulture,
|
||||||
|
out float parsedSeconds))
|
||||||
|
{
|
||||||
|
return parsedSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phase.DurationSeconds > 0)
|
||||||
|
{
|
||||||
|
return phase.DurationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompleteCurrentPhase()
|
||||||
|
{
|
||||||
|
_enemyManager.EndPhase();
|
||||||
|
Log.Info(
|
||||||
|
"CombatScheduler phase completed. Level={0}, Phase={1}, Elapsed={2:F2}s.",
|
||||||
|
_currentLevel != null ? _currentLevel.Id : 0,
|
||||||
|
_currentPhase != null ? _currentPhase.Id : 0,
|
||||||
|
_currentPhaseElapsed);
|
||||||
|
|
||||||
|
BeginNextPhase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BeginNextPhase()
|
||||||
|
{
|
||||||
|
_currentPhaseIndex++;
|
||||||
|
if (_currentPhaseIndex >= _phaseBuffer.Count)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Completed;
|
||||||
|
_currentPhase = null;
|
||||||
|
Log.Info("CombatScheduler level completed. Level={0}.", _currentLevel != null ? _currentLevel.Id : 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPhase = _phaseBuffer[_currentPhaseIndex];
|
||||||
|
_currentPhaseElapsed = 0f;
|
||||||
|
|
||||||
|
IReadOnlyList<DRLevelSpawnEntry> spawnEntries = null;
|
||||||
|
_spawnEntriesByPhaseId.TryGetValue(_currentPhase.Id, out spawnEntries);
|
||||||
|
_enemyManager.BeginPhase(_currentPhase, spawnEntries);
|
||||||
|
_state = SchedulerState.RunningPhase;
|
||||||
|
|
||||||
|
Log.Info(
|
||||||
|
"CombatScheduler phase started. Level={0}, Phase={1}, EndType={2}, Entries={3}.",
|
||||||
|
_currentLevel != null ? _currentLevel.Id : 0,
|
||||||
|
_currentPhase.Id,
|
||||||
|
_currentPhase.EndType,
|
||||||
|
spawnEntries != null ? spawnEntries.Count : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideCurrentMapIfNeeded()
|
||||||
|
{
|
||||||
|
if (_entity == null)
|
||||||
|
{
|
||||||
|
_currentMap = null;
|
||||||
|
_loadingMapEntityId = 0;
|
||||||
|
_loadedMapEntityId = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_loadingMapEntityId != 0)
|
||||||
|
{
|
||||||
|
EntityBase loadingMap = _entity.GetGameEntity(_loadingMapEntityId);
|
||||||
|
if (loadingMap != null)
|
||||||
|
{
|
||||||
|
_entity.HideEntity(loadingMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_loadedMapEntityId != 0)
|
||||||
|
{
|
||||||
|
EntityBase loadedMap = _entity.GetGameEntity(_loadedMapEntityId);
|
||||||
|
if (loadedMap != null)
|
||||||
|
{
|
||||||
|
_entity.HideEntity(loadedMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadingMapEntityId = 0;
|
||||||
|
_loadedMapEntityId = 0;
|
||||||
|
_currentMap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureEntityEventSubscribed()
|
||||||
|
{
|
||||||
|
if (_isEntityEventSubscribed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Subscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
||||||
|
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
_isEntityEventSubscribed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeEntityEvents()
|
||||||
|
{
|
||||||
|
if (!_isEntityEventSubscribed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
||||||
|
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
_isEntityEventSubscribed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetRuntime()
|
||||||
|
{
|
||||||
|
_phaseBuffer.Clear();
|
||||||
|
_spawnEntriesByPhaseId.Clear();
|
||||||
|
_currentLevel = null;
|
||||||
|
_currentPhase = null;
|
||||||
|
_currentPhaseIndex = -1;
|
||||||
|
_currentPhaseElapsed = 0f;
|
||||||
|
_state = SchedulerState.Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
|
||||||
|
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (!(e is ShowEntitySuccessEventArgs ne))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ne.EntityLogicType != typeof(MapEntity) || ne.Entity.Id != _loadingMapEntityId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadedMapEntityId = _loadingMapEntityId;
|
||||||
|
_loadingMapEntityId = 0;
|
||||||
|
_currentMap = ne.Entity.Logic as MapEntity;
|
||||||
|
if (_currentMap == null)
|
||||||
|
{
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Error("Loaded map entity logic is invalid. EntityId={0}.", ne.Entity.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info(
|
||||||
|
"Map ready. LevelId={0}, PathCells={1}, FoundationCells={2}, Spawners={3}, House={4}.",
|
||||||
|
_currentLevel != null ? _currentLevel.Id : 0,
|
||||||
|
_currentMap.PathCells.Count,
|
||||||
|
_currentMap.FoundationCells.Count,
|
||||||
|
_currentMap.Spawners.Length,
|
||||||
|
_currentMap.House != null ? _currentMap.House.name : "None");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShowEntityFailure(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (!(e is ShowEntityFailureEventArgs ne))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ne.EntityLogicType != typeof(MapEntity) || ne.EntityId != _loadingMapEntityId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadingMapEntityId = 0;
|
||||||
|
_currentMap = null;
|
||||||
|
_state = SchedulerState.Failed;
|
||||||
|
Log.Error(
|
||||||
|
"Map load failed. LevelId={0}, Asset='{1}', Error='{2}'.",
|
||||||
|
_currentLevel != null ? _currentLevel.Id : 0,
|
||||||
|
ne.EntityAssetName,
|
||||||
|
ne.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (!(e is HideEntityCompleteEventArgs ne))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ne.EntityId == _loadedMapEntityId)
|
||||||
|
{
|
||||||
|
_loadedMapEntityId = 0;
|
||||||
|
_currentMap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ne.EntityId == _loadingMapEntityId)
|
||||||
|
{
|
||||||
|
_loadingMapEntityId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 894291f7d20e5dc4bb7264dec378b1ce
|
guid: 9f43fd8050fd4239b4407a4fedcf0b7a
|
||||||
timeCreated: 1528026163
|
|
||||||
licenseType: Pro
|
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
defaultReferences: []
|
defaultReferences: []
|
||||||
executionOrder: 0
|
executionOrder: 0
|
||||||
|
|
@ -0,0 +1,465 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GameFramework.DataTable;
|
||||||
|
using GameFramework.Event;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using GeometryTD.Entity;
|
||||||
|
using GeometryTD.Entity.EntityData;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public class EnemyManager
|
||||||
|
{
|
||||||
|
private sealed class SpawnEntryRuntime
|
||||||
|
{
|
||||||
|
public DRLevelSpawnEntry Entry;
|
||||||
|
public bool Completed;
|
||||||
|
public float NextTriggerTime;
|
||||||
|
public float EndTime;
|
||||||
|
public int RemainingCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int DefaultEnemyConfigId = 1;
|
||||||
|
private const float MinStreamInterval = 0.05f;
|
||||||
|
private const float MinBurstGap = 0.01f;
|
||||||
|
|
||||||
|
private readonly List<Spawner> _spawners = new List<Spawner>();
|
||||||
|
private readonly Dictionary<int, Spawner> _spawnerByOrder = new Dictionary<int, Spawner>();
|
||||||
|
private readonly List<Vector3> _pathBuffer = new List<Vector3>();
|
||||||
|
private readonly List<SpawnEntryRuntime> _spawnRuntimes = new List<SpawnEntryRuntime>();
|
||||||
|
|
||||||
|
private CombatScheduler _combatScheduler;
|
||||||
|
private EntityComponent _entity;
|
||||||
|
private IDataTable<DREnemy> _drEnemy;
|
||||||
|
|
||||||
|
private int _spawnEnemyMaxCount = 5000;
|
||||||
|
private int _currentEnemyCount;
|
||||||
|
private int _nextSpawnerIndex;
|
||||||
|
private int _currentMapEntityId;
|
||||||
|
private bool _initialized;
|
||||||
|
private bool _enemyConfigMissingLogged;
|
||||||
|
|
||||||
|
private float _phaseElapsed;
|
||||||
|
private bool _isPhaseRunning;
|
||||||
|
|
||||||
|
public int AliveEnemyCount => _currentEnemyCount;
|
||||||
|
public bool IsPhaseSpawnCompleted { get; private set; } = true;
|
||||||
|
public bool IsPhaseRunning => _isPhaseRunning;
|
||||||
|
|
||||||
|
public void OnInit(CombatScheduler combatScheduler)
|
||||||
|
{
|
||||||
|
_combatScheduler = combatScheduler;
|
||||||
|
if (_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entity = GameEntry.Entity;
|
||||||
|
_drEnemy = GameEntry.DataTable.GetDataTable<DREnemy>();
|
||||||
|
_currentEnemyCount = 0;
|
||||||
|
_nextSpawnerIndex = 0;
|
||||||
|
_currentMapEntityId = 0;
|
||||||
|
_enemyConfigMissingLogged = false;
|
||||||
|
_spawners.Clear();
|
||||||
|
_spawnerByOrder.Clear();
|
||||||
|
_pathBuffer.Clear();
|
||||||
|
_spawnRuntimes.Clear();
|
||||||
|
_phaseElapsed = 0f;
|
||||||
|
_isPhaseRunning = false;
|
||||||
|
IsPhaseSpawnCompleted = true;
|
||||||
|
|
||||||
|
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Subscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
||||||
|
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginPhase(DRLevelPhase phase, IReadOnlyList<DRLevelSpawnEntry> spawnEntries)
|
||||||
|
{
|
||||||
|
if (!_initialized || _combatScheduler == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = phase;
|
||||||
|
EndPhase();
|
||||||
|
|
||||||
|
_phaseElapsed = 0f;
|
||||||
|
_isPhaseRunning = true;
|
||||||
|
IsPhaseSpawnCompleted = false;
|
||||||
|
RefreshSpawnerCache(true);
|
||||||
|
|
||||||
|
if (spawnEntries != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < spawnEntries.Count; i++)
|
||||||
|
{
|
||||||
|
SpawnEntryRuntime runtime = BuildSpawnRuntime(spawnEntries[i]);
|
||||||
|
if (runtime != null)
|
||||||
|
{
|
||||||
|
_spawnRuntimes.Add(runtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPhaseSpawnCompleted = _spawnRuntimes.Count <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
if (!_initialized || _combatScheduler == null || !_isPhaseRunning)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshSpawnerCache(false);
|
||||||
|
_phaseElapsed += elapseSeconds;
|
||||||
|
UpdateSpawnRuntimes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndPhase()
|
||||||
|
{
|
||||||
|
_isPhaseRunning = false;
|
||||||
|
_phaseElapsed = 0f;
|
||||||
|
_spawnRuntimes.Clear();
|
||||||
|
IsPhaseSpawnCompleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
_combatScheduler = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndPhase();
|
||||||
|
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
||||||
|
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
|
||||||
|
_spawners.Clear();
|
||||||
|
_spawnerByOrder.Clear();
|
||||||
|
_pathBuffer.Clear();
|
||||||
|
_currentMapEntityId = 0;
|
||||||
|
_nextSpawnerIndex = 0;
|
||||||
|
_combatScheduler = null;
|
||||||
|
_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpawnEntryRuntime BuildSpawnRuntime(DRLevelSpawnEntry entry)
|
||||||
|
{
|
||||||
|
if (entry == null || entry.EntryType == EntryType.None)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntryRuntime runtime = new SpawnEntryRuntime
|
||||||
|
{
|
||||||
|
Entry = entry,
|
||||||
|
Completed = false,
|
||||||
|
NextTriggerTime = Mathf.Max(0f, entry.StartTime),
|
||||||
|
EndTime = Mathf.Max(0f, entry.StartTime),
|
||||||
|
RemainingCount = Mathf.Max(0, entry.Count)
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (entry.EntryType)
|
||||||
|
{
|
||||||
|
case EntryType.Stream:
|
||||||
|
{
|
||||||
|
float duration = Mathf.Max(0f, entry.Duration);
|
||||||
|
runtime.EndTime = duration > 0f ? runtime.NextTriggerTime + duration : runtime.NextTriggerTime;
|
||||||
|
runtime.Completed = entry.Count <= 0;
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
|
case EntryType.Burst:
|
||||||
|
case EntryType.Boss:
|
||||||
|
runtime.Completed = runtime.RemainingCount <= 0;
|
||||||
|
return runtime;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSpawnRuntimes()
|
||||||
|
{
|
||||||
|
bool allCompleted = true;
|
||||||
|
for (int i = 0; i < _spawnRuntimes.Count; i++)
|
||||||
|
{
|
||||||
|
SpawnEntryRuntime runtime = _spawnRuntimes[i];
|
||||||
|
if (runtime.Completed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (runtime.Entry.EntryType)
|
||||||
|
{
|
||||||
|
case EntryType.Stream:
|
||||||
|
ProcessStreamRuntime(runtime);
|
||||||
|
break;
|
||||||
|
case EntryType.Burst:
|
||||||
|
case EntryType.Boss:
|
||||||
|
ProcessBurstRuntime(runtime);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
runtime.Completed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!runtime.Completed)
|
||||||
|
{
|
||||||
|
allCompleted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsPhaseSpawnCompleted = allCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessStreamRuntime(SpawnEntryRuntime runtime)
|
||||||
|
{
|
||||||
|
if (_phaseElapsed < runtime.NextTriggerTime)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int countPerWave = Mathf.Max(0, runtime.Entry.Count);
|
||||||
|
if (countPerWave <= 0)
|
||||||
|
{
|
||||||
|
runtime.Completed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float interval = runtime.Entry.Interval > 0f ? runtime.Entry.Interval : MinStreamInterval;
|
||||||
|
while (_phaseElapsed >= runtime.NextTriggerTime && runtime.NextTriggerTime <= runtime.EndTime)
|
||||||
|
{
|
||||||
|
SpawnEnemies(runtime.Entry, countPerWave);
|
||||||
|
runtime.NextTriggerTime += interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtime.NextTriggerTime > runtime.EndTime)
|
||||||
|
{
|
||||||
|
runtime.Completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessBurstRuntime(SpawnEntryRuntime runtime)
|
||||||
|
{
|
||||||
|
if (_phaseElapsed < runtime.NextTriggerTime)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtime.RemainingCount <= 0)
|
||||||
|
{
|
||||||
|
runtime.Completed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float gap = runtime.Entry.Gap;
|
||||||
|
if (gap <= 0f)
|
||||||
|
{
|
||||||
|
SpawnEnemies(runtime.Entry, runtime.RemainingCount);
|
||||||
|
runtime.RemainingCount = 0;
|
||||||
|
runtime.Completed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gap = Mathf.Max(gap, MinBurstGap);
|
||||||
|
while (_phaseElapsed >= runtime.NextTriggerTime && runtime.RemainingCount > 0)
|
||||||
|
{
|
||||||
|
SpawnEnemies(runtime.Entry, 1);
|
||||||
|
runtime.RemainingCount--;
|
||||||
|
runtime.NextTriggerTime += gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.Completed = runtime.RemainingCount <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnEnemies(DRLevelSpawnEntry entry, int spawnCount)
|
||||||
|
{
|
||||||
|
if (spawnCount <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spawner spawner = ResolveSpawner(entry.SpawnPointId);
|
||||||
|
if (spawner == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapEntity currentMap = _combatScheduler != null ? _combatScheduler.CurrentMap : null;
|
||||||
|
if (currentMap == null ||
|
||||||
|
!currentMap.TryFindPathWorldPoints(spawner, null, _pathBuffer) ||
|
||||||
|
_pathBuffer.Count <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DREnemy enemyConfig = GetEnemyConfig(entry.EnemyId);
|
||||||
|
if (enemyConfig == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < spawnCount; i++)
|
||||||
|
{
|
||||||
|
if (_currentEnemyCount >= _spawnEnemyMaxCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int enemyEntityId = _entity.GenerateSerialId();
|
||||||
|
EnemyData enemyData = new EnemyData(
|
||||||
|
enemyEntityId,
|
||||||
|
enemyConfig.EntityId,
|
||||||
|
_pathBuffer[0],
|
||||||
|
enemyConfig.BaseHp,
|
||||||
|
enemyConfig.Speed,
|
||||||
|
_pathBuffer);
|
||||||
|
_entity.ShowEnemy(enemyData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DREnemy GetEnemyConfig(int enemyId)
|
||||||
|
{
|
||||||
|
if (_drEnemy == null)
|
||||||
|
{
|
||||||
|
_drEnemy = GameEntry.DataTable.GetDataTable<DREnemy>();
|
||||||
|
if (_drEnemy == null)
|
||||||
|
{
|
||||||
|
if (!_enemyConfigMissingLogged)
|
||||||
|
{
|
||||||
|
Log.Warning("EnemyManagerComponent can not find DREnemy data table.");
|
||||||
|
_enemyConfigMissingLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enemyId > 0)
|
||||||
|
{
|
||||||
|
DREnemy targetConfig = _drEnemy.GetDataRow(enemyId);
|
||||||
|
if (targetConfig != null)
|
||||||
|
{
|
||||||
|
return targetConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DREnemy defaultConfig = _drEnemy.GetDataRow(DefaultEnemyConfigId);
|
||||||
|
if (defaultConfig != null)
|
||||||
|
{
|
||||||
|
return defaultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
DREnemy[] allConfigs = _drEnemy.GetAllDataRows();
|
||||||
|
if (allConfigs.Length > 0)
|
||||||
|
{
|
||||||
|
return allConfigs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_enemyConfigMissingLogged)
|
||||||
|
{
|
||||||
|
Log.Warning("EnemyManagerComponent found no enemy configs.");
|
||||||
|
_enemyConfigMissingLogged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Spawner ResolveSpawner(int spawnPointId)
|
||||||
|
{
|
||||||
|
if (spawnPointId > 0 && _spawnerByOrder.TryGetValue(spawnPointId, out Spawner mappedSpawner))
|
||||||
|
{
|
||||||
|
return mappedSpawner;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_spawners.Count <= 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spawner fallbackSpawner = _spawners[_nextSpawnerIndex % _spawners.Count];
|
||||||
|
_nextSpawnerIndex++;
|
||||||
|
return fallbackSpawner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshSpawnerCache(bool force)
|
||||||
|
{
|
||||||
|
MapEntity currentMap = _combatScheduler != null ? _combatScheduler.CurrentMap : null;
|
||||||
|
if (currentMap == null)
|
||||||
|
{
|
||||||
|
_spawners.Clear();
|
||||||
|
_spawnerByOrder.Clear();
|
||||||
|
_currentMapEntityId = 0;
|
||||||
|
_nextSpawnerIndex = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force && _currentMapEntityId == currentMap.Id && _spawners.Count > 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_spawners.Clear();
|
||||||
|
_spawnerByOrder.Clear();
|
||||||
|
_nextSpawnerIndex = 0;
|
||||||
|
_currentMapEntityId = currentMap.Id;
|
||||||
|
|
||||||
|
Spawner[] mapSpawners = currentMap.Spawners;
|
||||||
|
for (int i = 0; i < mapSpawners.Length; i++)
|
||||||
|
{
|
||||||
|
Spawner spawner = mapSpawners[i];
|
||||||
|
if (spawner == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentMap.TryGetDefaultPathCells(spawner, out _))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_spawners.Add(spawner);
|
||||||
|
if (spawner.SpawnOrder > 0 && !_spawnerByOrder.ContainsKey(spawner.SpawnOrder))
|
||||||
|
{
|
||||||
|
_spawnerByOrder[spawner.SpawnOrder] = spawner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_spawners.Sort((left, right) => left.SpawnOrder.CompareTo(right.SpawnOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (e is ShowEntitySuccessEventArgs ne)
|
||||||
|
{
|
||||||
|
if (ne.EntityLogicType == typeof(EnemyEntity))
|
||||||
|
{
|
||||||
|
_currentEnemyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShowEntityFailure(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (!(e is HideEntityCompleteEventArgs ne))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ne.EntityGroup.Name != "Enemy")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentEnemyCount = Mathf.Max(0, _currentEnemyCount - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
using GeometryTD.Entity;
|
|
||||||
using GeometryTD.Entity.EntityData;
|
|
||||||
using GameFramework.Event;
|
|
||||||
using GeometryTD;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityGameFramework.Runtime;
|
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
|
||||||
{
|
|
||||||
public class EnemyManagerComponent : GameFrameworkComponent
|
|
||||||
{
|
|
||||||
private EntityComponent _entity;
|
|
||||||
|
|
||||||
private float _spawnEnemyInterval = 5f;
|
|
||||||
private float _spawnEnemyTimer;
|
|
||||||
private float _spawnEnemyAccelerate = 0.5f;
|
|
||||||
private int _spawnEnemyCount = 5;
|
|
||||||
private int _spawnEnemyMaxCount = 5000;
|
|
||||||
private int _currentEnemyCount;
|
|
||||||
private int _spawnDistanceFromPlayer = 10;
|
|
||||||
private int _currentSpawnEnemyId = 0;
|
|
||||||
|
|
||||||
private Transform _player;
|
|
||||||
|
|
||||||
public void OnInit()
|
|
||||||
{
|
|
||||||
_entity = GameEntry.Entity;
|
|
||||||
|
|
||||||
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
|
||||||
GameEntry.Event.Subscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
|
||||||
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
_spawnEnemyTimer += elapseSeconds;
|
|
||||||
if (_spawnEnemyTimer < _spawnEnemyInterval) return;
|
|
||||||
SpawnEnemy();
|
|
||||||
_spawnEnemyTimer = 0;
|
|
||||||
_spawnEnemyInterval = Mathf.Max(0.1f, _spawnEnemyInterval - _spawnEnemyAccelerate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnDestroy()
|
|
||||||
{
|
|
||||||
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
|
||||||
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
|
||||||
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SpawnEnemy()
|
|
||||||
{
|
|
||||||
if (_player == null) return;
|
|
||||||
for (int i = 0; i < _spawnEnemyCount; i++)
|
|
||||||
{
|
|
||||||
if (_currentEnemyCount >= _spawnEnemyMaxCount) break;
|
|
||||||
_entity.ShowEnemy(new EnemyData(_currentSpawnEnemyId % _spawnEnemyMaxCount, 1001, _player,
|
|
||||||
GetRandomPosition(), 10, 2));
|
|
||||||
_currentSpawnEnemyId++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3 GetRandomPosition()
|
|
||||||
{
|
|
||||||
float x = Random.Range(-1f, 1f);
|
|
||||||
float z = Random.Range(-1f, 1f);
|
|
||||||
Vector3 dir = new Vector3(x, 0, z).normalized;
|
|
||||||
return _player.position + dir * _spawnDistanceFromPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
|
||||||
{
|
|
||||||
if (e is ShowEntitySuccessEventArgs ne)
|
|
||||||
{
|
|
||||||
if (ne.EntityLogicType == typeof(Enemy))
|
|
||||||
{
|
|
||||||
_currentEnemyCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ne.EntityLogicType == typeof(Player))
|
|
||||||
{
|
|
||||||
_player = ne.Entity.transform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnShowEntityFailure(object sender, GameEventArgs e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
|
||||||
{
|
|
||||||
if (e is HideEntityCompleteEventArgs ne)
|
|
||||||
{
|
|
||||||
if (ne.EntityGroup.Name == "Enemy")
|
|
||||||
{
|
|
||||||
_currentEnemyCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GameFramework.DataTable;
|
||||||
|
using GeometryTD.DataTable;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using GeometryTD.UI;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.CustomComponent
|
||||||
|
{
|
||||||
|
public class EventNodeComponent : GameFrameworkComponent
|
||||||
|
{
|
||||||
|
private readonly List<EventItem> _eventItems = new List<EventItem>();
|
||||||
|
|
||||||
|
private EventFormUseCase _eventFormUseCase;
|
||||||
|
private EventFormController _eventFormController;
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
|
public void OnInit()
|
||||||
|
{
|
||||||
|
_eventItems.Clear();
|
||||||
|
|
||||||
|
IDataTable<DREvent> dtEvent = GameEntry.DataTable.GetDataTable<DREvent>();
|
||||||
|
if (dtEvent == null)
|
||||||
|
{
|
||||||
|
Log.Warning("Event data table is not loaded.");
|
||||||
|
_initialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DREvent[] rows = dtEvent.GetAllDataRows();
|
||||||
|
foreach (var drEvent in rows)
|
||||||
|
{
|
||||||
|
EventOption[] options = ParseOptions(drEvent.OptionsRaw);
|
||||||
|
_eventItems.Add(new EventItem(drEvent.Id, drEvent.Title, drEvent.Description, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventFormUseCase == null)
|
||||||
|
{
|
||||||
|
_eventFormUseCase = new EventFormUseCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventFormController == null)
|
||||||
|
{
|
||||||
|
_eventFormController = new EventFormController();
|
||||||
|
_eventFormController.BindUseCase(_eventFormUseCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
Log.Info("EventNodeComponent initialized with {0} events.", _eventItems.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartEvent()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
OnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventItems.Count <= 0)
|
||||||
|
{
|
||||||
|
Log.Warning("EventNodeComponent has no event data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = Random.Range(0, _eventItems.Count);
|
||||||
|
EventItem randomEvent = _eventItems[index];
|
||||||
|
|
||||||
|
if (_eventFormUseCase == null || _eventFormController == null)
|
||||||
|
{
|
||||||
|
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventFormUseCase.SetCurrentEvent(randomEvent);
|
||||||
|
_eventFormController.OpenUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EventOption[] ParseOptions(string optionsRaw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(optionsRaw))
|
||||||
|
{
|
||||||
|
return System.Array.Empty<EventOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JArray array = JArray.Parse(optionsRaw);
|
||||||
|
List<EventOption> options = new List<EventOption>(array.Count);
|
||||||
|
|
||||||
|
for (int i = 0; i < array.Count; i++)
|
||||||
|
{
|
||||||
|
if (!(array[i] is JObject optionObj))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string optionText = optionObj.Value<string>("optionText") ?? string.Empty;
|
||||||
|
float probability = optionObj.Value<float?>("probability") ?? 1f;
|
||||||
|
|
||||||
|
EventRequirementBase[] requirements = ParseRequirements(optionObj["requirements"] as JArray);
|
||||||
|
EventEffectBase[] costEffects = ParseEffects(optionObj["costEffects"] as JArray, probability);
|
||||||
|
EventEffectBase[] rewardEffects = ParseEffects(optionObj["rewardEffects"] as JArray, probability);
|
||||||
|
|
||||||
|
options.Add(new EventOption(optionText, requirements, costEffects, rewardEffects, probability));
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.ToArray();
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Log.Warning("Failed to parse event options json. {0}", e.Message);
|
||||||
|
return System.Array.Empty<EventOption>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EventRequirementBase[] ParseRequirements(JArray requirementsArray)
|
||||||
|
{
|
||||||
|
if (requirementsArray == null || requirementsArray.Count == 0)
|
||||||
|
{
|
||||||
|
return System.Array.Empty<EventRequirementBase>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<EventRequirementBase> requirements = new List<EventRequirementBase>(requirementsArray.Count);
|
||||||
|
for (int i = 0; i < requirementsArray.Count; i++)
|
||||||
|
{
|
||||||
|
if (!(requirementsArray[i] is JObject reqObj))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string type = reqObj.Value<string>("type");
|
||||||
|
JObject param = reqObj["param"] as JObject;
|
||||||
|
EventRequirementBase requirement = EventRequirementFactory.Create(type, param);
|
||||||
|
if (requirement != null)
|
||||||
|
{
|
||||||
|
requirements.Add(requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return requirements.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EventEffectBase[] ParseEffects(JArray effectsArray, float probability)
|
||||||
|
{
|
||||||
|
if (effectsArray == null || effectsArray.Count == 0)
|
||||||
|
{
|
||||||
|
return System.Array.Empty<EventEffectBase>();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<EventEffectBase> effects = new List<EventEffectBase>(effectsArray.Count);
|
||||||
|
for (int i = 0; i < effectsArray.Count; i++)
|
||||||
|
{
|
||||||
|
if (!(effectsArray[i] is JObject effectObj))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string type = effectObj.Value<string>("type");
|
||||||
|
JObject param = effectObj["param"] as JObject;
|
||||||
|
EventEffectBase effect = EventEffectFactory.Create(type, param, probability);
|
||||||
|
if (effect != null)
|
||||||
|
{
|
||||||
|
effects.Add(effect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return effects.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a0c1c547ca24c95819f5f62f0bd3ea3
|
||||||
|
timeCreated: 1772102402
|
||||||
|
|
@ -10,6 +10,8 @@ namespace CustomComponent
|
||||||
{
|
{
|
||||||
private readonly Dictionary<UIFormType, IUIFormController> _routeControllers = new();
|
private readonly Dictionary<UIFormType, IUIFormController> _routeControllers = new();
|
||||||
|
|
||||||
|
private const string UINameSpace = "GeometryTD.UI";
|
||||||
|
|
||||||
public void BindUIUseCase(UIFormType uiFormType, IUIUseCase useCase)
|
public void BindUIUseCase(UIFormType uiFormType, IUIUseCase useCase)
|
||||||
{
|
{
|
||||||
IUIFormController controller = GetOrCreateController(uiFormType);
|
IUIFormController controller = GetOrCreateController(uiFormType);
|
||||||
|
|
@ -35,11 +37,12 @@ namespace CustomComponent
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
string typename = $"UI.{uiFormType}Controller";
|
string typename = $"{UINameSpace}.{uiFormType}Controller";
|
||||||
Type controllerType = Type.GetType(typename);
|
Type controllerType = Type.GetType(typename);
|
||||||
if (controllerType == null)
|
if (controllerType == null)
|
||||||
{
|
{
|
||||||
controller = new DefaultUIFormController(uiFormType);
|
controller = new DefaultUIFormController(uiFormType);
|
||||||
|
Log.Warning("Can not find UI Controller for type '{0}'.", typename);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人配置表
|
||||||
|
/// </summary>
|
||||||
|
public class DREnemy : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人编号
|
||||||
|
/// </summary>
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人实体编号
|
||||||
|
/// </summary>
|
||||||
|
public int EntityId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人基础血量
|
||||||
|
/// </summary>
|
||||||
|
public int BaseHp { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人基础基地伤害
|
||||||
|
/// </summary>
|
||||||
|
public int BaseDamage { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人移动速度
|
||||||
|
/// </summary>
|
||||||
|
public float Speed { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人掉落金币
|
||||||
|
/// </summary>
|
||||||
|
public int DropCoin { get; private set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
EntityId = int.Parse(columnStrings[index++]);
|
||||||
|
BaseHp = int.Parse(columnStrings[index++]);
|
||||||
|
BaseDamage = int.Parse(columnStrings[index++]);
|
||||||
|
Speed = float.Parse(columnStrings[index++]);
|
||||||
|
DropCoin = int.Parse(columnStrings[index++]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f9832b810954c4eb0f45ad735bb5a58
|
||||||
|
timeCreated: 1772101800
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 事件配置表
|
||||||
|
/// </summary>
|
||||||
|
public class DREvent : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取事件编号
|
||||||
|
/// </summary>
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取事件题目
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取事件描述
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取事件选项
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>原始字符串(如 JSON 文本),不在此处做解析。</remarks>
|
||||||
|
public string OptionsRaw { get; private set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
Title = columnStrings[index++];
|
||||||
|
Description = columnStrings[index++];
|
||||||
|
OptionsRaw = columnStrings[index++];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 57e29dfa7f1044a1acfbf3abde81859f
|
||||||
|
timeCreated: 1772097514
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 关卡表
|
||||||
|
/// </summary>
|
||||||
|
public class DRLevel : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡编号
|
||||||
|
/// </summary>
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡所属主题类型
|
||||||
|
/// </summary>
|
||||||
|
public LevelThemeType LevelThemeType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡初始基地生命
|
||||||
|
/// </summary>
|
||||||
|
public int BaseHp { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡初始硬币数量
|
||||||
|
/// </summary>
|
||||||
|
public int StartCoin { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡胜利条件
|
||||||
|
/// </summary>
|
||||||
|
public LevelVictoryType LevelVictoryType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡胜利奖励金币
|
||||||
|
/// </summary>
|
||||||
|
public int RewardGold { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡胜利条件参数
|
||||||
|
/// </summary>
|
||||||
|
public string VictoryParams { get; private set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
LevelThemeType = EnumUtility<LevelThemeType>.Get(columnStrings[index++]);
|
||||||
|
BaseHp = int.Parse(columnStrings[index++]);
|
||||||
|
StartCoin = int.Parse(columnStrings[index++]);
|
||||||
|
LevelVictoryType = EnumUtility<LevelVictoryType>.Get(columnStrings[index++]);
|
||||||
|
VictoryParams = columnStrings[index++];
|
||||||
|
RewardGold = int.Parse(columnStrings[index++]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e0eb47d184fe4204b38dc7c9e86da609
|
||||||
|
timeCreated: 1772089145
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 关卡阶段表
|
||||||
|
/// </summary>
|
||||||
|
public class DRLevelPhase : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取关卡阶段编号
|
||||||
|
/// </summary>
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取阶段持续时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int DurationSeconds { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取阶段结束条件类型
|
||||||
|
/// </summary>
|
||||||
|
public PhaseEndType EndType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取阶段结束参数
|
||||||
|
/// </summary>
|
||||||
|
public string EndParam { get; private set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
DurationSeconds = int.Parse(columnStrings[index++]);
|
||||||
|
EndType = ParsePhaseEndType(columnStrings[index++]);
|
||||||
|
EndParam = columnStrings[index++];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PhaseEndType ParsePhaseEndType(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
{
|
||||||
|
return PhaseEndType.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnumUtility<PhaseEndType>.Get(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f1fd79e88eca4bfbb38f5bcb8091e2dd
|
||||||
|
timeCreated: 1772090000
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
using System;
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 关卡阶段出怪条目表
|
||||||
|
/// </summary>
|
||||||
|
public class DRLevelSpawnEntry : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取阶段条目编号
|
||||||
|
/// </summary>
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人出生口编号
|
||||||
|
/// </summary>
|
||||||
|
public int SpawnPointId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取相对开始时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int StartTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取出怪条目类型
|
||||||
|
/// </summary>
|
||||||
|
public EntryType EntryType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取敌人编号
|
||||||
|
/// </summary>
|
||||||
|
public int EnemyId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取单次出怪数量
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取出怪间隔(秒)
|
||||||
|
/// </summary>
|
||||||
|
public float Interval { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取持续时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int Duration { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取单怪出生间隔(秒)
|
||||||
|
/// </summary>
|
||||||
|
public float Gap { get; private set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
SpawnPointId = int.Parse(columnStrings[index++]);
|
||||||
|
StartTime = int.Parse(columnStrings[index++]);
|
||||||
|
EntryType = ParseEntryType(columnStrings[index++]);
|
||||||
|
EnemyId = int.Parse(columnStrings[index++]);
|
||||||
|
Count = int.Parse(columnStrings[index++]);
|
||||||
|
Interval = float.Parse(columnStrings[index++]);
|
||||||
|
Duration = int.Parse(columnStrings[index++]);
|
||||||
|
Gap = float.Parse(columnStrings[index++]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntryType ParseEntryType(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(raw))
|
||||||
|
{
|
||||||
|
return EntryType.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnumUtility<EntryType>.Get(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b06804b242d247bd8a937cdce930cee1
|
||||||
|
timeCreated: 1772090001
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using GeometryTD.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.DataTable
|
||||||
|
{
|
||||||
|
public class DRShopPrice : DataRowBase
|
||||||
|
{
|
||||||
|
private int m_Id = 0;
|
||||||
|
|
||||||
|
public override int Id => m_Id;
|
||||||
|
|
||||||
|
public RarityType Rarity { get; set; }
|
||||||
|
|
||||||
|
public int MinPrice { get; set; }
|
||||||
|
|
||||||
|
public int MaxPrice { get; set; }
|
||||||
|
|
||||||
|
public override bool ParseDataRow(string dataRowString, object userData)
|
||||||
|
{
|
||||||
|
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
|
||||||
|
for (int i = 0; i < columnStrings.Length; i++)
|
||||||
|
{
|
||||||
|
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
index++;
|
||||||
|
m_Id = int.Parse(columnStrings[index++]);
|
||||||
|
index++;
|
||||||
|
Rarity = EnumUtility<RarityType>.Get(columnStrings[index++]);
|
||||||
|
MinPrice = int.Parse(columnStrings[index++]);
|
||||||
|
MaxPrice = int.Parse(columnStrings[index++]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7c47aaecd99446f2b8c9ec84c797005b
|
||||||
|
timeCreated: 1772105886
|
||||||
|
|
@ -33,6 +33,7 @@ namespace GeometryTD
|
||||||
public const int BulletAsset = 80;
|
public const int BulletAsset = 80;
|
||||||
public const int AsteroiAsset = 80;
|
public const int AsteroiAsset = 80;
|
||||||
public const int EnemyAsset = 80;
|
public const int EnemyAsset = 80;
|
||||||
|
public const int MapAsset = 85;
|
||||||
public const int EffectAsset = 80;
|
public const int EffectAsset = 80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家背包聚合数据。
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public sealed class BackpackInventoryData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 背包金币。
|
||||||
|
/// </summary>
|
||||||
|
public int Gold { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背包中的枪口组件实例。
|
||||||
|
/// </summary>
|
||||||
|
public List<MuzzleCompItemData> MuzzleComponents { get; set; } = new List<MuzzleCompItemData>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背包中的轴承组件实例。
|
||||||
|
/// </summary>
|
||||||
|
public List<BearingCompItemData> BearingComponents { get; set; } = new List<BearingCompItemData>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背包中的底座组件实例。
|
||||||
|
/// </summary>
|
||||||
|
public List<BaseCompItemData> BaseComponents { get; set; } = new List<BaseCompItemData>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背包中的防御塔实例。
|
||||||
|
/// </summary>
|
||||||
|
public List<DefenseTowerItemData> Towers { get; set; } = new List<DefenseTowerItemData>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c39f9ad23ee94de9af2b3d9729f47d7f
|
||||||
|
timeCreated: 1772105403
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔独立属性快照。
|
||||||
|
/// 注意:这里是塔实例的独立值,不通过组件引用实时计算。
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public sealed class DefenseTowerStatsData
|
||||||
|
{
|
||||||
|
public int AttackDamage { get; set; }
|
||||||
|
public float DamageRandomRate { get; set; }
|
||||||
|
public float RotateSpeed { get; set; }
|
||||||
|
public float AttackRange { get; set; }
|
||||||
|
public float AttackSpeed { get; set; }
|
||||||
|
public AttackMethodType AttackMethodType { get; set; }
|
||||||
|
public AttackPropertyType AttackPropertyType { get; set; }
|
||||||
|
public TagType[] Tags { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背包内防御塔实例数据。
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public sealed class DefenseTowerItemData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔实例唯一 Id。
|
||||||
|
/// </summary>
|
||||||
|
public long InstanceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔显示名称。
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔品质。
|
||||||
|
/// </summary>
|
||||||
|
public RarityType Rarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前耐久(0~100)。
|
||||||
|
/// </summary>
|
||||||
|
public float Endurance { get; set; } = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构成该防御塔的枪口组件实例 Id。
|
||||||
|
/// </summary>
|
||||||
|
public long MuzzleComponentInstanceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构成该防御塔的轴承组件实例 Id。
|
||||||
|
/// </summary>
|
||||||
|
public long BearingComponentInstanceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构成该防御塔的底座组件实例 Id。
|
||||||
|
/// </summary>
|
||||||
|
public long BaseComponentInstanceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔独立属性,不依赖组件对象引用。
|
||||||
|
/// </summary>
|
||||||
|
public DefenseTowerStatsData Stats { get; set; } = new DefenseTowerStatsData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ca5782ae52464616bf2b45704feea0d5
|
||||||
|
timeCreated: 1772105402
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class EventItem
|
||||||
|
{
|
||||||
|
public EventItem(int id, string title, string description, EventOption[] options)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Title = title;
|
||||||
|
Description = description;
|
||||||
|
Options = options ?? System.Array.Empty<EventOption>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; private set; }
|
||||||
|
public string Title { get; private set; }
|
||||||
|
public string Description { get; private set; }
|
||||||
|
public EventOption[] Options { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c2a6dce0183e4efdbada20c25be949c0
|
||||||
|
timeCreated: 1772097196
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class EventOption
|
||||||
|
{
|
||||||
|
public string OptionText { get; private set; }
|
||||||
|
public EventRequirementBase[] Requirements { get; private set; }
|
||||||
|
public EventEffectBase[] CostEffects { get; private set; }
|
||||||
|
public EventEffectBase[] RewardEffects { get; private set; }
|
||||||
|
public float Probability { get; private set; }
|
||||||
|
|
||||||
|
public EventOption(
|
||||||
|
string optionText,
|
||||||
|
EventRequirementBase[] requirements,
|
||||||
|
EventEffectBase[] costEffects,
|
||||||
|
EventEffectBase[] rewardEffects,
|
||||||
|
float probability = 1f)
|
||||||
|
{
|
||||||
|
OptionText = optionText;
|
||||||
|
Requirements = requirements ?? System.Array.Empty<EventRequirementBase>();
|
||||||
|
CostEffects = costEffects ?? System.Array.Empty<EventEffectBase>();
|
||||||
|
RewardEffects = rewardEffects ?? System.Array.Empty<EventEffectBase>();
|
||||||
|
Probability = probability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bcb612057a454fcf97f41fdf5c6d9b22
|
||||||
|
timeCreated: 1772097742
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 背包内组件实例基类(非 DataTable,表示玩家持有物)。
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public abstract class TowerCompItemData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 组件实例唯一 Id。
|
||||||
|
/// </summary>
|
||||||
|
public long InstanceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件配置 Id(对应 DataTable Id)。
|
||||||
|
/// </summary>
|
||||||
|
public int ConfigId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件槽位类型。
|
||||||
|
/// </summary>
|
||||||
|
public TowerCompSlotType SlotType { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件名称。
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件品质。
|
||||||
|
/// </summary>
|
||||||
|
public RarityType Rarity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件当前耐久(0~100)。
|
||||||
|
/// </summary>
|
||||||
|
public float Endurance { get; set; } = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件约束(先沿用 DataTable 原定义)。
|
||||||
|
/// </summary>
|
||||||
|
public string Constraint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件当前 Tag(实例态)。
|
||||||
|
/// </summary>
|
||||||
|
public TagType[] Tags { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class MuzzleCompItemData : TowerCompItemData
|
||||||
|
{
|
||||||
|
public MuzzleCompItemData()
|
||||||
|
{
|
||||||
|
SlotType = TowerCompSlotType.Muzzle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] AttackDamage { get; set; }
|
||||||
|
public float DamageRandomRate { get; set; }
|
||||||
|
public AttackMethodType AttackMethodType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class BearingCompItemData : TowerCompItemData
|
||||||
|
{
|
||||||
|
public BearingCompItemData()
|
||||||
|
{
|
||||||
|
SlotType = TowerCompSlotType.Bearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] RotateSpeed { get; set; }
|
||||||
|
public float[] AttackRange { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class BaseCompItemData : TowerCompItemData
|
||||||
|
{
|
||||||
|
public BaseCompItemData()
|
||||||
|
{
|
||||||
|
SlotType = TowerCompSlotType.Base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] AttackSpeed { get; set; }
|
||||||
|
public AttackPropertyType AttackPropertyType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class AccessoryItemData : TowerCompItemData
|
||||||
|
{
|
||||||
|
public AccessoryItemData()
|
||||||
|
{
|
||||||
|
SlotType = TowerCompSlotType.Accessory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f95a29d5bcf484aa053af1462365936
|
||||||
|
timeCreated: 1772105401
|
||||||
|
|
@ -3,7 +3,7 @@ namespace GeometryTD.Definition
|
||||||
public enum AttackPropertyType : byte
|
public enum AttackPropertyType : byte
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Physical = 1,
|
Physics = 1,
|
||||||
Fire = 2,
|
Fire = 2,
|
||||||
Water = 3,
|
Water = 3,
|
||||||
Earth = 4,
|
Earth = 4,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 出怪条目类型
|
||||||
|
/// </summary>
|
||||||
|
public enum EntryType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Stream = 1,
|
||||||
|
Burst = 2,
|
||||||
|
Boss = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14433e1048844c5d942c885c2f50ffe0
|
||||||
|
timeCreated: 1772090003
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public enum EventEffectType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
|
||||||
|
AddGold,
|
||||||
|
RemoveGold,
|
||||||
|
|
||||||
|
AddRandomComps,
|
||||||
|
RemoveRandomComps,
|
||||||
|
|
||||||
|
AddRandomCompsEndurance,
|
||||||
|
RemoveRandomCompsEndurance,
|
||||||
|
|
||||||
|
AddRandomTowersEndurance,
|
||||||
|
DamageRandomTowersEndurance,
|
||||||
|
|
||||||
|
TransformComponents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6f11ba35aa8f491f942964435b32cba6
|
||||||
|
timeCreated: 1772095333
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public enum EventRequirementType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
GoldAtLeast,
|
||||||
|
CompCountAtLeast,
|
||||||
|
TowerCountAtLeast,
|
||||||
|
HasRelic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f9da1af87a4f4115b9a5da706b4e1092
|
||||||
|
timeCreated: 1772095290
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public enum LevelThemeType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Plain,
|
||||||
|
Volcano,
|
||||||
|
Mountain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 06ec7c0ec8924c7988e521abdd97bffe
|
||||||
|
timeCreated: 1772089321
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public enum LevelVictoryType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
PhasesCleared,
|
||||||
|
BossDead,
|
||||||
|
TimeElapsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea9b7c078511464fa572923f66cc200e
|
||||||
|
timeCreated: 1772089447
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 关卡阶段结束类型
|
||||||
|
/// </summary>
|
||||||
|
public enum PhaseEndType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
TimeElapsed = 1,
|
||||||
|
EnemiesCleared = 2,
|
||||||
|
BossDead = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eed7685430c24cdaac639581331393f3
|
||||||
|
timeCreated: 1772090002
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public enum TowerCompSlotType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Muzzle = 1,
|
||||||
|
Bearing = 2,
|
||||||
|
Base = 3,
|
||||||
|
Accessory = 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a34cbb64aede4531b1d0e178352006bb
|
||||||
|
timeCreated: 1772105400
|
||||||
|
|
@ -26,5 +26,15 @@
|
||||||
/// 关于。
|
/// 关于。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AboutForm = 102,
|
AboutForm = 102,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件节点界面。
|
||||||
|
/// </summary>
|
||||||
|
EventForm = 200,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 仓库界面。
|
||||||
|
/// </summary>
|
||||||
|
RepoForm = 201,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a291a17879242cc8c7cec678aa5aaf1
|
||||||
|
timeCreated: 1772101365
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class AddGoldEffect : EventEffectBase
|
||||||
|
{
|
||||||
|
public override EventEffectType EffectType => EventEffectType.AddGold;
|
||||||
|
public override EventEffectParam Param => _param;
|
||||||
|
private AddGoldParam _param;
|
||||||
|
|
||||||
|
public AddGoldEffect(AddGoldParam param, float? probability = null)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
Probability = probability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddGoldParam : EventEffectParam
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
public AddGoldParam(int count)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 75cacc997d8f414ca73f381cad303b5b
|
||||||
|
timeCreated: 1772101474
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class AddRandomCompsEffect : EventEffectBase
|
||||||
|
{
|
||||||
|
public override EventEffectType EffectType => EventEffectType.AddRandomComps;
|
||||||
|
public override EventEffectParam Param => _param;
|
||||||
|
private AddRandomCompsParam _param;
|
||||||
|
|
||||||
|
public AddRandomCompsEffect(AddRandomCompsParam param, float? probability = null)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
Probability = probability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class AddRandomCompsParam : EventEffectParam
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public RarityType Rarity;
|
||||||
|
|
||||||
|
public AddRandomCompsParam(int count, RarityType rarity)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
Rarity = rarity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 35656d42c28448268d0547c19938b2d8
|
||||||
|
timeCreated: 1772101530
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class DamageRandomTowerEnduranceEffect : EventEffectBase
|
||||||
|
{
|
||||||
|
public override EventEffectType EffectType => EventEffectType.DamageRandomTowersEndurance;
|
||||||
|
public override EventEffectParam Param => _param;
|
||||||
|
private DamageRandomTowerEnduranceParam _param;
|
||||||
|
|
||||||
|
public DamageRandomTowerEnduranceEffect(DamageRandomTowerEnduranceParam param, float? probability = null)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
Probability = probability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DamageRandomTowerEnduranceParam : EventEffectParam
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 减少耐久的防御塔数量
|
||||||
|
/// </summary>
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 防御塔耐久减少的量
|
||||||
|
/// </summary>
|
||||||
|
public int Amount;
|
||||||
|
|
||||||
|
public DamageRandomTowerEnduranceParam(int count, int amount)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
Amount = amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a1b06f274c054844ad68b1db02d3737a
|
||||||
|
timeCreated: 1772101563
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public abstract class EventEffectBase
|
||||||
|
{
|
||||||
|
public abstract EventEffectType EffectType { get; }
|
||||||
|
public abstract EventEffectParam Param { get; }
|
||||||
|
public float? Probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventEffectParam
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2be5a7ff782240f79bde2dd0310d11ea
|
||||||
|
timeCreated: 1772101392
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class RemoveRandomCompsEffect : EventEffectBase
|
||||||
|
{
|
||||||
|
public override EventEffectType EffectType => EventEffectType.RemoveRandomComps;
|
||||||
|
public override EventEffectParam Param => _param;
|
||||||
|
private RemoveRandomCompsParam _param;
|
||||||
|
|
||||||
|
public RemoveRandomCompsEffect(RemoveRandomCompsParam param, float? probability = null)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
Probability = probability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveRandomCompsParam : EventEffectParam
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public RarityType Rarity;
|
||||||
|
|
||||||
|
public RemoveRandomCompsParam(int Count, RarityType Rarity)
|
||||||
|
{
|
||||||
|
this.Count = Count;
|
||||||
|
this.Rarity = Rarity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 77f8e0d1879b48108e6ad5faca07d7f7
|
||||||
|
timeCreated: 1772101548
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
using GeometryTD.CustomUtility;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public static class EventEffectFactory
|
||||||
|
{
|
||||||
|
public static EventEffectBase Create(string rawType, JObject param, float? probability = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(rawType))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEffectType type = EnumUtility<EventEffectType>.Get(rawType);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case EventEffectType.AddGold:
|
||||||
|
{
|
||||||
|
int count = GetInt(param, "Count");
|
||||||
|
return new AddGoldEffect(new AddGoldParam(count), probability);
|
||||||
|
}
|
||||||
|
case EventEffectType.RemoveRandomComps:
|
||||||
|
{
|
||||||
|
int count = GetInt(param, "Count");
|
||||||
|
RarityType rarity = EnumUtility<RarityType>.Get(GetString(param, "Rarity"));
|
||||||
|
return new RemoveRandomCompsEffect(new RemoveRandomCompsParam(count, rarity), probability);
|
||||||
|
}
|
||||||
|
case EventEffectType.AddRandomComps:
|
||||||
|
{
|
||||||
|
int count = GetInt(param, "Count");
|
||||||
|
RarityType rarity = EnumUtility<RarityType>.Get(GetString(param, "Rarity"));
|
||||||
|
return new AddRandomCompsEffect(new AddRandomCompsParam(count, rarity), probability);
|
||||||
|
}
|
||||||
|
case EventEffectType.DamageRandomTowersEndurance:
|
||||||
|
{
|
||||||
|
int count = GetInt(param, "Count");
|
||||||
|
int amount = GetInt(param, "Amount");
|
||||||
|
return new DamageRandomTowerEnduranceEffect(new DamageRandomTowerEnduranceParam(count, amount), probability);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log.Warning("Unsupported EventEffectType '{0}'.", rawType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetInt(JObject param, params string[] keys)
|
||||||
|
{
|
||||||
|
if (param == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string key in keys)
|
||||||
|
{
|
||||||
|
if (param.TryGetValue(key, out JToken token) && token.Type != JTokenType.Null)
|
||||||
|
{
|
||||||
|
return token.Value<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetString(JObject param, string key)
|
||||||
|
{
|
||||||
|
if (param == null || !param.TryGetValue(key, out JToken token) || token.Type == JTokenType.Null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return token.Value<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fae04d7a401d4c7fbe2de385c3f23580
|
||||||
|
timeCreated: 1772102401
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cee1f415e08743a9914efd2ecbc6273e
|
||||||
|
timeCreated: 1772100925
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
namespace GeometryTD.Definition
|
||||||
|
{
|
||||||
|
public class CompCountAtLeastRequirement : EventRequirementBase
|
||||||
|
{
|
||||||
|
public override EventRequirementType RequirementType => EventRequirementType.CompCountAtLeast;
|
||||||
|
public override EventRequirementParam Param => _param;
|
||||||
|
private CompCountAtLeastParam _param;
|
||||||
|
|
||||||
|
public CompCountAtLeastRequirement(CompCountAtLeastParam param)
|
||||||
|
{
|
||||||
|
_param = param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompCountAtLeastParam : EventRequirementParam
|
||||||
|
{
|
||||||
|
public int Count;
|
||||||
|
public RarityType Rarity;
|
||||||
|
|
||||||
|
public CompCountAtLeastParam(int count, RarityType rarity)
|
||||||
|
{
|
||||||
|
Count = count;
|
||||||
|
Rarity = rarity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue