commit
b77e579848
|
|
@ -101,3 +101,6 @@ InitTestScene*.unity*
|
|||
/数据表
|
||||
/.vscode
|
||||
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[]
|
||||
# 底座编号 策划备注 实体Id 底座名 攻击速度(秒/次) 攻击属性 属性约束 可能出现的Tag
|
||||
# 底座编号 实体Id 底座名 攻击速度(秒/次) 攻击属性 属性约束 可能出现的Tag
|
||||
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]
|
||||
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[]
|
||||
# 轴承编号 策划备注 实体Id 轴承名 各品质旋转速度 各品质攻击范围 属性约束 可能出现的Tag
|
||||
# 轴承编号 实体Id 轴承名 各品质旋转速度 各品质攻击范围 属性约束 可能出现的Tag
|
||||
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]
|
||||
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
|
||||
guid: d2ccda2aca4a348428805b273ee2aaf2
|
||||
guid: d860a5c1c09addf4ba7539e97c85c0bd
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
# 实体表
|
||||
# Id AssetName
|
||||
# Id 策划备注 AssetName
|
||||
# int string
|
||||
# 实体编号 策划备注 资源名称
|
||||
# 实体编号 资源名称
|
||||
101 测试枪口 TestMuzzle
|
||||
201 测试轴承 TestBearing
|
||||
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
|
||||
# int int string int[] float AttackMethodType string TagType[]
|
||||
# 枪口编号 策划备注 实体Id 枪口名 各品质伤害 伤害浮动 攻击方式 属性约束 可能出现的Tag
|
||||
# Id 策划备注 EntityId Name AttackDamage DamageRandomRate Method Constraint PossibleTag
|
||||
# int int string int[] float AttackMethodType Constraint TagType[]
|
||||
# 枪口编号 策划备注 实体Id 枪口名 各品质伤害 伤害浮动 攻击方式 Constraint 可能出现的Tag
|
||||
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]
|
||||
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
|
||||
101 设置 SettingForm 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
|
||||
guid: 3ecda907b9217e5499e31cb81a154ff4
|
||||
guid: 7258f46ca3cc69546ad93c07bcd08582
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
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
|
||||
serializedVersion: 2
|
||||
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_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
|
|
@ -343,10 +343,10 @@ Camera:
|
|||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
far clip plane: 50
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
orthographic: 1
|
||||
orthographic size: 7.5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
|
|
|
|||
|
|
@ -343,10 +343,10 @@ Camera:
|
|||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
far clip plane: 50
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
orthographic: 1
|
||||
orthographic size: 7.5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
|
|
@ -371,7 +371,7 @@ Transform:
|
|||
m_GameObject: {fileID: 961739749}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using CustomComponent;
|
||||
using GeometryTD.CustomComponent;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
@ -16,13 +17,19 @@ public partial class GameEntry : MonoBehaviour
|
|||
public static BuiltinDataComponent BuiltinData { get; private set; }
|
||||
|
||||
public static HPBarComponent HPBar { get; private set; }
|
||||
|
||||
public static UIRouterComponent UIRouter { get; private set; }
|
||||
|
||||
public static EnemyManagerComponent EnemyManager { get; private set; }
|
||||
public static EventNodeComponent EventNode { get; private set; }
|
||||
|
||||
public static CombatNodeComponent CombatNode { get; private set; }
|
||||
|
||||
private static void InitCustomComponents()
|
||||
{
|
||||
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
||||
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
|
||||
guid: 894291f7d20e5dc4bb7264dec378b1ce
|
||||
timeCreated: 1528026163
|
||||
licenseType: Pro
|
||||
guid: 9f43fd8050fd4239b4407a4fedcf0b7a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
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
|
||||
|
|
@ -9,6 +9,8 @@ namespace CustomComponent
|
|||
public class UIRouterComponent : GameFrameworkComponent
|
||||
{
|
||||
private readonly Dictionary<UIFormType, IUIFormController> _routeControllers = new();
|
||||
|
||||
private const string UINameSpace = "GeometryTD.UI";
|
||||
|
||||
public void BindUIUseCase(UIFormType uiFormType, IUIUseCase useCase)
|
||||
{
|
||||
|
|
@ -35,11 +37,12 @@ namespace CustomComponent
|
|||
return controller;
|
||||
}
|
||||
|
||||
string typename = $"UI.{uiFormType}Controller";
|
||||
string typename = $"{UINameSpace}.{uiFormType}Controller";
|
||||
Type controllerType = Type.GetType(typename);
|
||||
if (controllerType == null)
|
||||
{
|
||||
controller = new DefaultUIFormController(uiFormType);
|
||||
Log.Warning("Can not find UI Controller for type '{0}'.", typename);
|
||||
}
|
||||
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 AsteroiAsset = 80;
|
||||
public const int EnemyAsset = 80;
|
||||
public const int MapAsset = 85;
|
||||
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
|
||||
{
|
||||
None = 0,
|
||||
Physical = 1,
|
||||
Physics = 1,
|
||||
Fire = 2,
|
||||
Water = 3,
|
||||
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>
|
||||
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