Compare commits
14
Commits
e2fdb660ab
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91b8e177c5 | ||
|
|
051c075183 | ||
|
|
c61eb7fb68 | ||
|
|
02f4c67464 | ||
|
|
ba25ec1926 | ||
|
|
92e1ea0d55 | ||
|
|
1e4d3ef8d7 | ||
|
|
88ed35e757 | ||
|
|
421f8bf619 | ||
|
|
17ee268416 | ||
|
|
5fbeec2476 | ||
|
|
9c54905f85 | ||
|
|
b628a8537c | ||
|
|
091a29db63 |
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
name: code-hygiene
|
||||||
|
description: >-
|
||||||
|
Inspect and clean up code in the Seek-Battle-Evacuate (SBE) project according to AGENTS.md
|
||||||
|
null-checking rules, error handling specifications, and anti-defensive programming guidelines.
|
||||||
|
Use this skill whenever the user asks to format code per AGENTS.md, clean up redundant null checks,
|
||||||
|
remove over-defensive fallbacks, or review code hygiene.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SBE Code Hygiene & Defensive Cleanup
|
||||||
|
|
||||||
|
本技能用于规范 SBE 项目中的 C# 代码质量,消除过度防御(Over-Defensive Programming)、冗余判空与静默吞错,严格对齐 `AGENTS.md` 规范与敏捷“Fail Fast”(快速失败)原则。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、三项核心红线(AGENTS.md 必须遵守)
|
||||||
|
|
||||||
|
1. **声明处已初始化的变量,之后绝对不判空**
|
||||||
|
- 坏:字段或局部变量已有 `= new List<T>()` / `= new Dictionary<...>()`,后续还写 `if (_list != null)`。
|
||||||
|
- 好:直接使用 `_list.Add(...)`、`_list.Clear()`。
|
||||||
|
|
||||||
|
2. **严禁静态兜底与静默回退(Fail Fast 原则)**
|
||||||
|
- 坏:找不到数据、缺少配置或解析异常时,返回硬编码默认值(如 `global != null ? global.Time : 250` 或 `if (cfg == null) return 10;`)。
|
||||||
|
- 好:异常路径直接 `Log.Error(...)` 报错并中断执行(`return` / 抛异常),绝不用伪造数据让程序带病运行。
|
||||||
|
|
||||||
|
3. **UI 序列化字段与 View 自动绑定字段不做判空**
|
||||||
|
- 坏:`if (View != null && View.sortButton != null)`、`[SerializeField] private Button _btn; ... if (_btn != null)`。
|
||||||
|
- 好:默认信任 Inspector 和 `UIAssetsTools` 自动绑定。若没绑定本应报错暴露,不加空防御掩盖。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、六项常见“没必要”的过度防御与坏味道
|
||||||
|
|
||||||
|
在 Unity / UGF / C# 实际开发中,以下写法通常是冗余、降低代码可读性且增加维护负担的:
|
||||||
|
|
||||||
|
### 1. 集合遍历前的非空/长度双重检查
|
||||||
|
- **没必要**:
|
||||||
|
```csharp
|
||||||
|
if (list != null && list.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var item in list) { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **原则**:若列表规范为非空对象(即使为空也是包含 0 个元素的空列表),`foreach` 会自动直接跳过,根本无需任何 `if` 守卫:
|
||||||
|
```csharp
|
||||||
|
foreach (var item in list) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. LINQ 与内建构造返回值的非空检查
|
||||||
|
- **没必要**:
|
||||||
|
```csharp
|
||||||
|
var results = items.Where(x => x.Active).ToList();
|
||||||
|
if (results != null) { ... } // ToList() 永远返回新 List 实例,永不为 null
|
||||||
|
```
|
||||||
|
- **原则**:直接使用 LINQ 投影/转换结果。
|
||||||
|
|
||||||
|
### 3. 内部私有辅助方法的层层重复防御
|
||||||
|
- **没必要**:公有入口方法已经做了入参断言/校验,拆分出来的内部私有方法 `private void ApplyCore(Item item)` 每一层都再写一遍 `if (item == null) return;`。
|
||||||
|
- **原则**:私有方法属于内部信任边界,保持干净直白,将校验集中在业务入口。
|
||||||
|
|
||||||
|
### 4. 字典的双重查找(Double Lookup)
|
||||||
|
- **没必要**:
|
||||||
|
```csharp
|
||||||
|
if (_dict.ContainsKey(key))
|
||||||
|
{
|
||||||
|
var val = _dict[key];
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **原则**:统一使用 `TryGetValue`,减少一次哈希哈希计算与冗余判断:
|
||||||
|
```csharp
|
||||||
|
if (_dict.TryGetValue(key, out var val))
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 静默吞异常的空 Try-Catch
|
||||||
|
- **没必要**:
|
||||||
|
```csharp
|
||||||
|
try { DoSomething(); }
|
||||||
|
catch (Exception) { /* 留空,或者只 Log.Warning 假装没事 */ }
|
||||||
|
```
|
||||||
|
- **原则**:非预期异常必须 `Log.Error(...)` 暴露上下文并附带具体错误信息,绝不能掩盖致命逻辑错误或数据存盘缺失。
|
||||||
|
|
||||||
|
### 6. MonoBehaviour/实例方法内的自我判空
|
||||||
|
- **没必要**:在普通实例方法内部写 `if (this == null)`,或对由 Unity 生命周期保证的对象进行反复探测。
|
||||||
|
- **原则**:信任正常的组件调用生命周期。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、代码排查与清理步骤(Runbook)
|
||||||
|
|
||||||
|
当用户发出整理请求时,按以下流程执行:
|
||||||
|
|
||||||
|
1. **识别范围**:
|
||||||
|
- 检查 `git status` 涉及修改的文件,或针对用户指定的代码文件。
|
||||||
|
2. **逐项审查**:
|
||||||
|
- [ ] 检查是否有 `[SerializeField]`、`View.xxx` 的判空,有则移除。
|
||||||
|
- [ ] 检查声明处赋值(如 `= new ...`)字段的二次判空,有则移除。
|
||||||
|
- [ ] 检查是否有配置读取、存档读取的三元默认值回退(`?:` 魔法数字),改为 `Log.Error` 并中断。
|
||||||
|
- [ ] 检查 `foreach` 外层是否有冗余的 `if (list != null && list.Count > 0)` 嵌套,若列表非空则拍平。
|
||||||
|
- [ ] 检查是否有空 `catch` 吞错。
|
||||||
|
3. **推演逻辑边界(静态检查)**:
|
||||||
|
- 确认移除防御后不会导致不可预知的崩溃;
|
||||||
|
- 确认异常路径是否确实按规范输出了 `Log.Error`。
|
||||||
|
4. **汇报改动**:
|
||||||
|
- 列出修改的文件路径;
|
||||||
|
- 简要说明移除了哪些冗余防御或规范了哪些异常断言。
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# CodeGraph data files — local to each machine, not for committing.
|
||||||
|
# Ignore everything in .codegraph/ except this file itself, so transient
|
||||||
|
# files (the database, daemon.pid, sockets, logs) never show up in git.
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
+1
-5
@@ -88,9 +88,5 @@ crashlytics-build.properties
|
|||||||
|
|
||||||
~$*.xlsx
|
~$*.xlsx
|
||||||
Assets/GameMain/Configs/ResourceBuilder.xml
|
Assets/GameMain/Configs/ResourceBuilder.xml
|
||||||
/.dotnet
|
|
||||||
/.omx
|
|
||||||
/.vscode
|
/.vscode
|
||||||
/openspec/changes/archive
|
/Tools/make_atlas/input
|
||||||
/.omc
|
|
||||||
/.claude
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# AGENTS.md — SBE(Seek-Battle-Evacuate)
|
# AGENTS.md
|
||||||
|
|
||||||
单人 3d 类吸血鬼幸存者游戏原型。Unity 2022.3.62f3,框架为 GameFramework(UGF,源码在 `Assets/GameFramework/`),配置表用 Luban,插件有 UniTask、DOTween 等。
|
单人 3d 类吸血鬼幸存者游戏原型。Unity 2022.3.62f3,框架为 GameFramework(UGF,源码在 `Assets/GameFramework/`),配置表用 Luban,插件有 UniTask、DOTween 等。
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
- UGF 只对顶级 Form 调用 `OnInit/OnOpen/OnClose` 生命周期;**嵌套 Form 的这些方法永远不会执行**。事件绑定用幂等模式(`_listenersBound` 标志 + `EnsureXxx()`),放在能力入口(如 `Refresh`)里。
|
- UGF 只对顶级 Form 调用 `OnInit/OnOpen/OnClose` 生命周期;**嵌套 Form 的这些方法永远不会执行**。事件绑定用幂等模式(`_listenersBound` 标志 + `EnsureXxx()`),放在能力入口(如 `Refresh`)里。
|
||||||
- 子 Form 只提供能力(public 方法,幂等可重入),数据由调用方传入;只有顶级 Form 有编排权。子组件向上通信用 `GameEntry.Event.Fire` + `XxxEventArgs`(放 `Scripts/Base/Event/UIForm/`,`ReferencePool` + `Create`/`Clear`),禁止反向持有父 Form。
|
- 子 Form 只提供能力(public 方法,幂等可重入),数据由调用方传入;只有顶级 Form 有编排权。子组件向上通信用 `GameEntry.Event.Fire` + `XxxEventArgs`(放 `Scripts/Base/Event/UIForm/`,`ReferencePool` + `Create`/`Clear`),禁止反向持有父 Form。
|
||||||
|
|
||||||
## Luban 约定(详见 `Docs/Tech/01_LubanDataTable.md`)
|
## Luban 约定(详见 `docs/tech/01_LubanDataTable.md`)
|
||||||
|
|
||||||
- 表名小写 `tbxxxconfig`:Excel 表名、导出文件名、`Tables` 构造参数、`TableNames` 数组四处必须一致。
|
- 表名小写 `tbxxxconfig`:Excel 表名、导出文件名、`Tables` 构造参数、`TableNames` 数组四处必须一致。
|
||||||
- 访问入口 `GameEntry.Luban.Get<T>(id)` / `GetTable<T>()`;单行表(`mode=one`,如 `tbglobalconfig`)不注册访问器,走 `Tables.TbXxxConfig.Data`。
|
- 访问入口 `GameEntry.Luban.Get<T>(id)` / `GetTable<T>()`;单行表(`mode=one`,如 `tbglobalconfig`)不注册访问器,走 `Tables.TbXxxConfig.Data`。
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
- 不做静态回退(不用默认值/兜底逻辑静默继续);异常路径直接 `Log.Error(...)` 抛出(UGF 的 `Log`,支持格式串,如 `Log.Error("Entity data is invalid.")`)。
|
- 不做静态回退(不用默认值/兜底逻辑静默继续);异常路径直接 `Log.Error(...)` 抛出(UGF 的 `Log`,支持格式串,如 `Log.Error("Entity data is invalid.")`)。
|
||||||
- UI 的序列化字段(`[SerializeField]` 及 View 生成的引用)**不做判空检查**,默认已在 Inspector 中赋值。
|
- UI 的序列化字段(`[SerializeField]` 及 View 生成的引用)**不做判空检查**,默认已在 Inspector 中赋值。
|
||||||
- GameEntry 中提供的静态入口不对其进行判空,默认其不为空。
|
- GameEntry 中提供的静态入口不对其进行判空,默认其不为空。
|
||||||
|
- 对于从 Luban 中取出的数据,默认其是有效且不为空的,代码里不对其做任何有效性检测
|
||||||
|
|
||||||
## 代码风格
|
## 代码风格
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -9,7 +9,7 @@
|
|||||||
"attackRange": 1.5,
|
"attackRange": 1.5,
|
||||||
"speed": 3,
|
"speed": 3,
|
||||||
"coinDrop": 5,
|
"coinDrop": 5,
|
||||||
"expDrop": 1,
|
"expDrop": 20,
|
||||||
"dropPercent": 0.3,
|
"dropPercent": 0.3,
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"color": "#44FF44",
|
"color": "#44FF44",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"attackRange": 8,
|
"attackRange": 8,
|
||||||
"speed": 2.5,
|
"speed": 2.5,
|
||||||
"coinDrop": 4,
|
"coinDrop": 4,
|
||||||
"expDrop": 2,
|
"expDrop": 20,
|
||||||
"dropPercent": 0.2,
|
"dropPercent": 0.2,
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"color": "#FF44FF",
|
"color": "#FF44FF",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"attackRange": 1.2,
|
"attackRange": 1.2,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"coinDrop": 3,
|
"coinDrop": 3,
|
||||||
"expDrop": 1,
|
"expDrop": 25,
|
||||||
"dropPercent": 0.4,
|
"dropPercent": 0.4,
|
||||||
"scale": 0.5,
|
"scale": 0.5,
|
||||||
"color": "#FF4444",
|
"color": "#FF4444",
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"attackRange": 1.8,
|
"attackRange": 1.8,
|
||||||
"speed": 2.5,
|
"speed": 2.5,
|
||||||
"coinDrop": 6,
|
"coinDrop": 6,
|
||||||
"expDrop": 2,
|
"expDrop": 25,
|
||||||
"dropPercent": 0.3,
|
"dropPercent": 0.3,
|
||||||
"scale": 1.3,
|
"scale": 1.3,
|
||||||
"color": "#CCCCCC",
|
"color": "#CCCCCC",
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
"attackRange": 2.5,
|
"attackRange": 2.5,
|
||||||
"speed": 1.5,
|
"speed": 1.5,
|
||||||
"coinDrop": 10,
|
"coinDrop": 10,
|
||||||
"expDrop": 3,
|
"expDrop": 30,
|
||||||
"dropPercent": 0.25,
|
"dropPercent": 0.25,
|
||||||
"scale": 2,
|
"scale": 2,
|
||||||
"color": "#888888",
|
"color": "#888888",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"attackRange": 10,
|
"attackRange": 10,
|
||||||
"speed": 3,
|
"speed": 3,
|
||||||
"coinDrop": 5,
|
"coinDrop": 5,
|
||||||
"expDrop": 2,
|
"expDrop": 30,
|
||||||
"dropPercent": 0.3,
|
"dropPercent": 0.3,
|
||||||
"scale": 0.8,
|
"scale": 0.8,
|
||||||
"color": "#88CCFF",
|
"color": "#88CCFF",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"attackRange": 12,
|
"attackRange": 12,
|
||||||
"speed": 2,
|
"speed": 2,
|
||||||
"coinDrop": 8,
|
"coinDrop": 8,
|
||||||
"expDrop": 3,
|
"expDrop": 30,
|
||||||
"dropPercent": 0.2,
|
"dropPercent": 0.2,
|
||||||
"scale": 1.2,
|
"scale": 1.2,
|
||||||
"color": "#FF8800",
|
"color": "#FF8800",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
FollowCameraFollowCamera.prefabPlayer␍Player.prefab
|
FollowCameraFollowCamera.prefabPlayer␍Player.prefab
|
||||||
MeleeEnemyMeleeEnemy.prefabRemoteEnemyRemoteEnemy.prefabWeaponKnifeWeaponKnife.prefab␍WeaponHandgunWeaponHandgun.prefabWeaponSlashWeaponSlash.prefabWeaponLightningWeaponLightning.prefabWeaponLanceWeaponLance.prefab
|
MeleeEnemyMeleeEnemy.prefabRemoteEnemyRemoteEnemy.prefabWeaponKnifeWeaponKnife.prefab␍WeaponHandgunWeaponHandgun.prefabWeaponSlashWeaponSlash.prefabWeaponLightningWeaponLightning.prefabWeaponLanceWeaponLance.prefab
|
||||||
CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab
|
CoinEntityCoinEntity.prefab ExpEntityExpEntity.prefab
|
||||||
|
ProjectileProjectile.prefab
|
||||||
@@ -42,5 +42,9 @@
|
|||||||
{
|
{
|
||||||
"assetName": "ExpEntity",
|
"assetName": "ExpEntity",
|
||||||
"prefabPath": "ExpEntity.prefab"
|
"prefabPath": "ExpEntity.prefab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetName": "Projectile",
|
||||||
|
"prefabPath": "Projectile.prefab"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
HUDHpText {0} / {1}
|
||||||
|
HUDExpTextLv.{0}HUDCoinText <sprite name="coin" index=0> {0}HUDCPUTimeCostTextCPU Frame: {0:F2} msHUDEnemyCountText
|
||||||
|
Enemy: {0}LevelUpRefreshCostText(刷新 -{0} <sprite name="coin" index=0>␍ShopTitleText商店 (Lv.{0})ShopContinueButtonText继续 (Lv.{0})ShopPlayerCoinText<sprite name="coin"> {0}ShopRefreshCostText 刷新 -{0} <sprite name="coin">WeaponListTitle武器 ({0}/{1})␍PropListTitle道具 ({0})ShopGoodsCostText {0} <sprite name="coin" index=0>TooltipRecycleButtonText'出售 {0} <sprite name="coin" index=0>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 09ee293d7e6c7b5478b17ac940bcfb8e
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "HUDHpText",
|
||||||
|
"format": "{0} / {1}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HUDExpText",
|
||||||
|
"format": "Lv.{0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HUDCoinText",
|
||||||
|
"format": "<sprite name=\"coin\" index=0> {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HUDCPUTimeCostText",
|
||||||
|
"format": "CPU Frame: {0:F2} ms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HUDEnemyCountText",
|
||||||
|
"format": "Enemy: {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LevelUpRefreshCostText",
|
||||||
|
"format": "刷新 -{0} <sprite name=\"coin\" index=0>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ShopTitleText",
|
||||||
|
"format": "商店 (Lv.{0})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ShopContinueButtonText",
|
||||||
|
"format": "继续 (Lv.{0})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ShopPlayerCoinText",
|
||||||
|
"format": "<sprite name=\"coin\"> {0}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ShopRefreshCostText",
|
||||||
|
"format": "刷新 -{0} <sprite name=\"coin\">"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WeaponListTitle",
|
||||||
|
"format": "武器 ({0}/{1})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PropListTitle",
|
||||||
|
"format": "道具 ({0})"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ShopGoodsCostText",
|
||||||
|
"format": "{0} <sprite name=\"coin\" index=0>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TooltipRecycleButtonText",
|
||||||
|
"format": "出售 {0} <sprite name=\"coin\" index=0>"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5c1d742cd5d626e4e867ec0ca8482db7
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -1,5 +1,38 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"weaponCapacity": 6
|
"weaponCapacity": 6,
|
||||||
|
"propMaxCount": -1,
|
||||||
|
"playerEntity": "Player",
|
||||||
|
"absorbSpeed": 12,
|
||||||
|
"collectDistance": 0.5,
|
||||||
|
"defaultAbsorbRange": 4,
|
||||||
|
"weaponRecycleRate": 0.3,
|
||||||
|
"shopGoodsCount": 4,
|
||||||
|
"shopPriceRandomPercent": 0.05,
|
||||||
|
"shopMinPrice": 1,
|
||||||
|
"levelUpRefreshPriceBase": 2,
|
||||||
|
"levelUpRewardCount": 4,
|
||||||
|
"spawnEnemyMaxCount": 5000,
|
||||||
|
"enemySpawnDistanceMin": 14,
|
||||||
|
"enemySpawnDistanceMax": 18,
|
||||||
|
"weaponRotateSpeed": 5,
|
||||||
|
"weaponBaseCrit": 0.05,
|
||||||
|
"playerGroupName": "Player",
|
||||||
|
"enemyGroupName": "Enemy",
|
||||||
|
"projectileGroupName": "Projectile",
|
||||||
|
"dropGroupName": "Drop",
|
||||||
|
"effectGroupNmae": "Effect",
|
||||||
|
"minDamage": 1,
|
||||||
|
"maxDodgeRate": 900,
|
||||||
|
"minSpawnRateScale": 100,
|
||||||
|
"projectileLifeTime": 3000,
|
||||||
|
"projectileHeightOffset": 600,
|
||||||
|
"projectileSpeed": 12000,
|
||||||
|
"projectileEntity": "Projectile",
|
||||||
|
"damageTextYOffset": 1100,
|
||||||
|
"entityAsyncTimeout": 30000,
|
||||||
|
"critMultiplier": 1.5,
|
||||||
|
"enemyBodyRadius": 450,
|
||||||
|
"enemySeparationIterations": 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1 +1 @@
|
|||||||
┐ИХ┌▄Х┌┴2Х┌▄Х┌┴┐ЙГ┴≥И╫©2Г┴≥И╫©┐КХ└▒2Х└▒┐ЛИ╪╩Е╜░ 2И╪╩Е╜░┐МХ┘©2Х┘©┐НГ°╪Г²⌡2Г°╪Г²⌡┐ОХ┌▄Х┌┴─√Х┌▄Х┌┴┐ПГ┴≥И╫©─√Г┴≥И╫©┐ЯХ└▒─√Х└▒┐РИ╪╩Е╜░ ─√И╪╩Е╜░┐СХ┘©─√Х┘©┐ТГ°╪Г²⌡─√Г°╪Г²⌡┐УХ┌▄Х┌┴│,Х┌▄Х┌┴┐ЖГ┴≥И╫©│,Г┴≥И╫©┐ВХ└▒│,Х└▒┐ЬИ╪╩Е╜░ │,И╪╩Е╜░┐ЫХ┘©│,Х┘©┐ЗГ°╪Г²⌡│,Г°╪Г²⌡
|
┐ИХ┌▄Х┌┴млL=─хХ┌▄Х┌┴┐ЙГ┴≥И╫©млL=─хГ┴≥И╫©┐КХ└▒ млL=─хХ└▒┐ЛИ╪╩Е╜░млL=─хИ╪╩Е╜░┐МХ┘©млL=─хХ┘©┐НГ°╪Г²⌡млL=─хГ°╪Г²⌡┐ОХ┌▄Х┌┴ ≥>─хХ┌▄Х┌┴┐ПГ┴≥И╫© ≥>─хГ┴≥И╫©┐ЯХ└▒ ≥>─хХ└▒┐РИ╪╩Е╜░ ≥>─хИ╪╩Е╜░┐СХ┘© ≥>─хХ┘©┐ТГ°╪Г²⌡ ≥>─хГ°╪Г²⌡┐УХ┌▄Х┌┴ ≥≥>─хХ┌▄Х┌┴┐ЖГ┴≥И╫© ≥≥>─хГ┴≥И╫©┐ВХ└▒ ≥≥>─хХ└▒┐ЬИ╪╩Е╜░ ≥≥>─хИ╪╩Е╜░┐ЫХ┘© ≥≥>─хХ┘©┐ЗГ°╪Г²⌡ ≥≥>─хГ°╪Г²⌡
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
"id": 1001,
|
"id": 1001,
|
||||||
"title": "肌肉",
|
"title": "肌肉",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "肌肉"
|
"sprite": "肌肉"
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
"id": 1002,
|
"id": 1002,
|
||||||
"title": "牙齿",
|
"title": "牙齿",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "牙齿"
|
"sprite": "牙齿"
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
"id": 1003,
|
"id": 1003,
|
||||||
"title": "脑",
|
"title": "脑",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "脑"
|
"sprite": "脑"
|
||||||
@@ -42,11 +42,11 @@
|
|||||||
"id": 1004,
|
"id": 1004,
|
||||||
"title": "鼻子",
|
"title": "鼻子",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 9,
|
"statType": 8,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "鼻子"
|
"sprite": "鼻子"
|
||||||
@@ -55,11 +55,11 @@
|
|||||||
"id": 1005,
|
"id": 1005,
|
||||||
"title": "腿",
|
"title": "腿",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "腿"
|
"sprite": "腿"
|
||||||
@@ -68,11 +68,11 @@
|
|||||||
"id": 1006,
|
"id": 1006,
|
||||||
"title": "眼睛",
|
"title": "眼睛",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 50,
|
"value": 0.05,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "眼睛"
|
"sprite": "眼睛"
|
||||||
@@ -81,11 +81,11 @@
|
|||||||
"id": 1007,
|
"id": 1007,
|
||||||
"title": "肌肉",
|
"title": "肌肉",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "肌肉"
|
"sprite": "肌肉"
|
||||||
@@ -94,11 +94,11 @@
|
|||||||
"id": 1008,
|
"id": 1008,
|
||||||
"title": "牙齿",
|
"title": "牙齿",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "牙齿"
|
"sprite": "牙齿"
|
||||||
@@ -107,11 +107,11 @@
|
|||||||
"id": 1009,
|
"id": 1009,
|
||||||
"title": "脑",
|
"title": "脑",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "脑"
|
"sprite": "脑"
|
||||||
@@ -120,11 +120,11 @@
|
|||||||
"id": 1010,
|
"id": 1010,
|
||||||
"title": "鼻子",
|
"title": "鼻子",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 9,
|
"statType": 8,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "鼻子"
|
"sprite": "鼻子"
|
||||||
@@ -133,11 +133,11 @@
|
|||||||
"id": 1011,
|
"id": 1011,
|
||||||
"title": "腿",
|
"title": "腿",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "腿"
|
"sprite": "腿"
|
||||||
@@ -146,11 +146,11 @@
|
|||||||
"id": 1012,
|
"id": 1012,
|
||||||
"title": "眼睛",
|
"title": "眼睛",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "眼睛"
|
"sprite": "眼睛"
|
||||||
@@ -159,11 +159,11 @@
|
|||||||
"id": 1013,
|
"id": 1013,
|
||||||
"title": "肌肉",
|
"title": "肌肉",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "肌肉"
|
"sprite": "肌肉"
|
||||||
@@ -172,11 +172,11 @@
|
|||||||
"id": 1014,
|
"id": 1014,
|
||||||
"title": "牙齿",
|
"title": "牙齿",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 4,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "牙齿"
|
"sprite": "牙齿"
|
||||||
@@ -185,11 +185,11 @@
|
|||||||
"id": 1015,
|
"id": 1015,
|
||||||
"title": "脑",
|
"title": "脑",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "脑"
|
"sprite": "脑"
|
||||||
@@ -198,11 +198,11 @@
|
|||||||
"id": 1016,
|
"id": 1016,
|
||||||
"title": "鼻子",
|
"title": "鼻子",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 9,
|
"statType": 8,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "鼻子"
|
"sprite": "鼻子"
|
||||||
@@ -211,11 +211,11 @@
|
|||||||
"id": 1017,
|
"id": 1017,
|
||||||
"title": "腿",
|
"title": "腿",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "腿"
|
"sprite": "腿"
|
||||||
@@ -224,11 +224,11 @@
|
|||||||
"id": 1018,
|
"id": 1018,
|
||||||
"title": "眼睛",
|
"title": "眼睛",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 300,
|
"value": 0.3,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "眼睛"
|
"sprite": "眼睛"
|
||||||
|
|||||||
Binary file not shown.
@@ -4,16 +4,16 @@
|
|||||||
"name": "铁护腕",
|
"name": "铁护腕",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 120,
|
"price": 120,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 5,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 12,
|
"value": 12,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "铁护腕"
|
"sprite": "铁护腕"
|
||||||
@@ -23,21 +23,21 @@
|
|||||||
"name": "狂战士药剂",
|
"name": "狂战士药剂",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"price": 250,
|
"price": 250,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 53,
|
"value": 53,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 5,
|
"statType": 8,
|
||||||
"value": 90,
|
"value": 0.09,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 5,
|
||||||
"value": 11,
|
"value": 11,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "狂战士药剂"
|
"sprite": "狂战士药剂"
|
||||||
@@ -47,21 +47,21 @@
|
|||||||
"name": "龙鳞护符",
|
"name": "龙鳞护符",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"price": 330,
|
"price": 330,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 15,
|
"value": 15,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 7,
|
"statType": 5,
|
||||||
"value": 21,
|
"value": 21,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 23,
|
"value": 23,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "龙鳞护符"
|
"sprite": "龙鳞护符"
|
||||||
@@ -71,11 +71,11 @@
|
|||||||
"name": "泰坦壁垒",
|
"name": "泰坦壁垒",
|
||||||
"rarity": 5,
|
"rarity": 5,
|
||||||
"price": 423,
|
"price": 423,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 79,
|
"value": 79,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "泰坦壁垒"
|
"sprite": "泰坦壁垒"
|
||||||
@@ -85,16 +85,16 @@
|
|||||||
"name": "轻便短靴",
|
"name": "轻便短靴",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 120,
|
"price": 120,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 800,
|
"value": 0.8,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 5,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "轻便短靴"
|
"sprite": "轻便短靴"
|
||||||
@@ -104,21 +104,21 @@
|
|||||||
"name": "幽灵披风",
|
"name": "幽灵披风",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"price": 195,
|
"price": 195,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 8,
|
"statType": 13,
|
||||||
"value": 10,
|
"value": 0.1,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 5,
|
||||||
"value": 12,
|
"value": 12,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 44,
|
"value": 44,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "幽灵披风"
|
"sprite": "幽灵披风"
|
||||||
@@ -128,11 +128,11 @@
|
|||||||
"name": "精钢胸甲",
|
"name": "精钢胸甲",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"price": 335,
|
"price": 335,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 62,
|
"value": 62,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "精钢胸甲"
|
"sprite": "精钢胸甲"
|
||||||
@@ -142,16 +142,16 @@
|
|||||||
"name": "暗影之刃",
|
"name": "暗影之刃",
|
||||||
"rarity": 5,
|
"rarity": 5,
|
||||||
"price": 433,
|
"price": 433,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 6,
|
||||||
"value": 44,
|
"value": 44,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 8,
|
"statType": 13,
|
||||||
"value": 27,
|
"value": 0.27,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "暗影之刃"
|
"sprite": "暗影之刃"
|
||||||
@@ -161,21 +161,21 @@
|
|||||||
"name": "旅行者徽章",
|
"name": "旅行者徽章",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 121,
|
"price": 121,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 32,
|
"value": 32,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 6,
|
||||||
"value": 7,
|
"value": 7,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 8,
|
"value": 8,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "旅行者徽章"
|
"sprite": "旅行者徽章"
|
||||||
@@ -185,11 +185,11 @@
|
|||||||
"name": "疾风手套",
|
"name": "疾风手套",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"price": 198,
|
"price": 198,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 5,
|
"statType": 8,
|
||||||
"value": 190,
|
"value": 0.19,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "疾风手套"
|
"sprite": "疾风手套"
|
||||||
@@ -199,11 +199,11 @@
|
|||||||
"name": "巨人之力卷轴",
|
"name": "巨人之力卷轴",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"price": 341,
|
"price": 341,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 6,
|
||||||
"value": 57,
|
"value": 57,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "巨人之力卷轴"
|
"sprite": "巨人之力卷轴"
|
||||||
@@ -213,16 +213,16 @@
|
|||||||
"name": "不死鸟之心",
|
"name": "不死鸟之心",
|
||||||
"rarity": 5,
|
"rarity": 5,
|
||||||
"price": 443,
|
"price": 443,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 139,
|
"value": 139,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 27,
|
"value": 0.27,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "不死鸟之心"
|
"sprite": "不死鸟之心"
|
||||||
@@ -232,16 +232,16 @@
|
|||||||
"name": "幸运草",
|
"name": "幸运草",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 122,
|
"price": 122,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 6,
|
"statType": 9,
|
||||||
"value": 5,
|
"value": 0.05,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 8,
|
"statType": 13,
|
||||||
"value": 7,
|
"value": 0.07,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "幸运草"
|
"sprite": "幸运草"
|
||||||
@@ -251,16 +251,16 @@
|
|||||||
"name": "石肤戒指",
|
"name": "石肤戒指",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"price": 201,
|
"price": 201,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 600,
|
"value": 0.6,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 38,
|
"value": 38,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "石肤戒指"
|
"sprite": "石肤戒指"
|
||||||
@@ -270,16 +270,16 @@
|
|||||||
"name": "风行者胫甲",
|
"name": "风行者胫甲",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"price": 346,
|
"price": 346,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 1600,
|
"value": 1.6,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 32,
|
"value": 32,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "风行者胫甲"
|
"sprite": "风行者胫甲"
|
||||||
@@ -289,21 +289,21 @@
|
|||||||
"name": "幻影斗篷",
|
"name": "幻影斗篷",
|
||||||
"rarity": 5,
|
"rarity": 5,
|
||||||
"price": 454,
|
"price": 454,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 8,
|
"statType": 13,
|
||||||
"value": 28,
|
"value": 0.28,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 24,
|
"value": 24,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 5,
|
||||||
"value": 24,
|
"value": 24,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "幻影斗篷"
|
"sprite": "幻影斗篷"
|
||||||
@@ -313,11 +313,11 @@
|
|||||||
"name": "木质盾牌",
|
"name": "木质盾牌",
|
||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 124,
|
"price": 124,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 23,
|
"value": 23,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "木质盾牌"
|
"sprite": "木质盾牌"
|
||||||
@@ -327,16 +327,16 @@
|
|||||||
"name": "生命之戒",
|
"name": "生命之戒",
|
||||||
"rarity": 2,
|
"rarity": 2,
|
||||||
"price": 204,
|
"price": 204,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 7,
|
||||||
"value": 17,
|
"value": 17,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 69,
|
"value": 69,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "生命之戒"
|
"sprite": "生命之戒"
|
||||||
@@ -346,21 +346,21 @@
|
|||||||
"name": "精灵耳环",
|
"name": "精灵耳环",
|
||||||
"rarity": 3,
|
"rarity": 3,
|
||||||
"price": 352,
|
"price": 352,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 74,
|
"value": 74,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 7,
|
||||||
"value": 19,
|
"value": 19,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 8,
|
"statType": 13,
|
||||||
"value": 20,
|
"value": 0.2,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "精灵耳环"
|
"sprite": "精灵耳环"
|
||||||
@@ -370,21 +370,21 @@
|
|||||||
"name": "战神勋章",
|
"name": "战神勋章",
|
||||||
"rarity": 5,
|
"rarity": 5,
|
||||||
"price": 464,
|
"price": 464,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 5,
|
"statType": 8,
|
||||||
"value": 150,
|
"value": 0.15,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 3,
|
"statType": 7,
|
||||||
"value": 29,
|
"value": 29,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 4,
|
"statType": 12,
|
||||||
"value": 27,
|
"value": 27,
|
||||||
"isPercent": false
|
"modifierType": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sprite": "战神勋章"
|
"sprite": "战神勋章"
|
||||||
|
|||||||
Binary file not shown.
@@ -4,16 +4,16 @@
|
|||||||
"maxHealth": 100,
|
"maxHealth": 100,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"coin": 10,
|
"coin": 10,
|
||||||
"modifiers": [
|
"statModifierConfigs": [
|
||||||
{
|
{
|
||||||
"key": 2,
|
"statType": 14,
|
||||||
"value": 500,
|
"value": 0.5,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": 1,
|
"statType": 1,
|
||||||
"value": 200,
|
"value": 0.2,
|
||||||
"isPercent": true
|
"modifierType": 200
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"initialItems": [],
|
"initialItems": [],
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"maxHealth": 200,
|
"maxHealth": 200,
|
||||||
"speed": 4,
|
"speed": 4,
|
||||||
"coin": 5,
|
"coin": 5,
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"initialItems": [],
|
"initialItems": [],
|
||||||
"prefab": "Player",
|
"prefab": "Player",
|
||||||
"sprite": "老人"
|
"sprite": "老人"
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ee410edd089fef4d91515cb4a75589d
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"statType": 1,
|
||||||
|
"name": "最大生命",
|
||||||
|
"defaultBaseValue": 100,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 1,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 2,
|
||||||
|
"name": "生命恢复",
|
||||||
|
"defaultBaseValue": 5,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 3,
|
||||||
|
"name": "生命窃取",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 4,
|
||||||
|
"name": "伤害",
|
||||||
|
"defaultBaseValue": 1,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": -1,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 5,
|
||||||
|
"name": "近战伤害",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": false,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 6,
|
||||||
|
"name": "远程伤害",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": false,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 7,
|
||||||
|
"name": "元素伤害",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": false,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 8,
|
||||||
|
"name": "攻击速度",
|
||||||
|
"defaultBaseValue": 1,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": -0.8,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 9,
|
||||||
|
"name": "暴击率",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": true,
|
||||||
|
"maxValue": 0.9,
|
||||||
|
"isPercentDisplay": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 10,
|
||||||
|
"name": "工程学",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 11,
|
||||||
|
"name": "攻击范围",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 12,
|
||||||
|
"name": "护甲",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": false,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 13,
|
||||||
|
"name": "闪避率",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": true,
|
||||||
|
"maxValue": 0.8,
|
||||||
|
"isPercentDisplay": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 14,
|
||||||
|
"name": "移动速度",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0.5,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 15,
|
||||||
|
"name": "收获",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"statType": 16,
|
||||||
|
"name": "幸运",
|
||||||
|
"defaultBaseValue": 0,
|
||||||
|
"hasMin": true,
|
||||||
|
"minValue": 0,
|
||||||
|
"hasMax": false,
|
||||||
|
"maxValue": 0,
|
||||||
|
"isPercentDisplay": false
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b66a5cb55d1374a45b0f74f7baba5283
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
@@ -5,16 +5,17 @@
|
|||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 120,
|
"price": 120,
|
||||||
"attack": 100,
|
"attack": 100,
|
||||||
"cooldown": 1500,
|
"cooldown": 1.5,
|
||||||
"attackRange": 5000,
|
"damageType": 1,
|
||||||
|
"attackRange": 5,
|
||||||
"attackSoundId": 10000,
|
"attackSoundId": 10000,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
2000
|
2
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"prefab": "WeaponKnife",
|
"prefab": "WeaponKnife",
|
||||||
"sprite": "小刀"
|
"sprite": "小刀"
|
||||||
},
|
},
|
||||||
@@ -24,11 +25,12 @@
|
|||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 130,
|
"price": 130,
|
||||||
"attack": 120,
|
"attack": 120,
|
||||||
"cooldown": 1000,
|
"cooldown": 1,
|
||||||
"attackRange": 15000,
|
"damageType": 2,
|
||||||
|
"attackRange": 15,
|
||||||
"attackSoundId": 10000,
|
"attackSoundId": 10000,
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"prefab": "WeaponHandgun",
|
"prefab": "WeaponHandgun",
|
||||||
"sprite": "手枪"
|
"sprite": "手枪"
|
||||||
},
|
},
|
||||||
@@ -38,8 +40,9 @@
|
|||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 100,
|
"price": 100,
|
||||||
"attack": 150,
|
"attack": 150,
|
||||||
"cooldown": 2000,
|
"cooldown": 2,
|
||||||
"attackRange": 5000,
|
"damageType": 1,
|
||||||
|
"attackRange": 5,
|
||||||
"attackSoundId": 10000,
|
"attackSoundId": 10000,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
[
|
[
|
||||||
@@ -47,7 +50,7 @@
|
|||||||
120
|
120
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"prefab": "WeaponSlash",
|
"prefab": "WeaponSlash",
|
||||||
"sprite": "斧头"
|
"sprite": "斧头"
|
||||||
},
|
},
|
||||||
@@ -57,16 +60,17 @@
|
|||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 150,
|
"price": 150,
|
||||||
"attack": 80,
|
"attack": 80,
|
||||||
"cooldown": 3000,
|
"cooldown": 3,
|
||||||
"attackRange": 12000,
|
"damageType": 4,
|
||||||
|
"attackRange": 12,
|
||||||
"attackSoundId": 10000,
|
"attackSoundId": 10000,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
[
|
[
|
||||||
1,
|
1,
|
||||||
3000
|
3
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"prefab": "WeaponLightning",
|
"prefab": "WeaponLightning",
|
||||||
"sprite": "闪电"
|
"sprite": "闪电"
|
||||||
},
|
},
|
||||||
@@ -76,24 +80,25 @@
|
|||||||
"rarity": 1,
|
"rarity": 1,
|
||||||
"price": 100,
|
"price": 100,
|
||||||
"attack": 100,
|
"attack": 100,
|
||||||
"cooldown": 1500,
|
"cooldown": 1.5,
|
||||||
"attackRange": 5000,
|
"damageType": 1,
|
||||||
|
"attackRange": 5,
|
||||||
"attackSoundId": 10000,
|
"attackSoundId": 10000,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
[
|
[
|
||||||
3,
|
3,
|
||||||
700
|
0.7
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
4,
|
4,
|
||||||
4500
|
4.5
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
5,
|
5,
|
||||||
500
|
0.5
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"modifiers": [],
|
"statModifierConfigs": [],
|
||||||
"prefab": "WeaponLance",
|
"prefab": "WeaponLance",
|
||||||
"sprite": "长枪"
|
"sprite": "长枪"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,580 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using SepCore.Event;
|
|
||||||
using SepCore.Definition;
|
|
||||||
using SepCore.Entity;
|
|
||||||
using SepCore.Components;
|
|
||||||
using SepCore.CustomUtility;
|
|
||||||
using SepCore.Simulation;
|
|
||||||
using SepCore.EnemyManager;
|
|
||||||
using SepCore.Procedure;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityGameFramework.Runtime;
|
|
||||||
#if ENABLE_INPUT_SYSTEM
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
public class RuntimeDebugPanelComponent : MonoBehaviour
|
|
||||||
|
|
||||||
{
|
|
||||||
private const float MinSpawnRate = 0.1f;
|
|
||||||
private const float CornerTapWindow = 0.6f;
|
|
||||||
private const int RequiredCornerTapCount = 3;
|
|
||||||
private const int DebugHealAmount = 200;
|
|
||||||
|
|
||||||
[Header("Window Content")] [SerializeField]
|
|
||||||
private bool _showBuffSection = true;
|
|
||||||
|
|
||||||
[SerializeField] private bool _showBattleOverview = true;
|
|
||||||
[SerializeField] private bool _showCollisionStats = true;
|
|
||||||
[SerializeField] private bool _showSpawnControls = true;
|
|
||||||
[SerializeField] private bool _showBattleDurationControls = true;
|
|
||||||
[SerializeField] private bool _showPlayerWeaponControls = true;
|
|
||||||
[SerializeField] private bool _showPlayerHealthControls = true;
|
|
||||||
[SerializeField] private bool _showTips = true;
|
|
||||||
|
|
||||||
private Rect _windowRect;
|
|
||||||
private Vector2 _panelScroll;
|
|
||||||
private GUISkin _panelSkin;
|
|
||||||
private bool _isPanelVisible;
|
|
||||||
private int _windowId;
|
|
||||||
|
|
||||||
private string _searchText = string.Empty;
|
|
||||||
private int _selectedIndex;
|
|
||||||
private int _addCount = 1;
|
|
||||||
private Vector2 _buffScroll;
|
|
||||||
|
|
||||||
private float _spawnRateScaleInput = 1f;
|
|
||||||
private float _extendDurationSeconds = 30f;
|
|
||||||
|
|
||||||
private IReadOnlyList<PropConfig> _allProps;
|
|
||||||
private IReadOnlyList<PropConfig> _filteredProps;
|
|
||||||
private string[] _displayNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
private int _cornerTapCount;
|
|
||||||
private float _lastCornerTapTime = -10f;
|
|
||||||
private bool _lockPlayerHealthToMax;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
_windowId = GetInstanceID();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (IsTogglePressed())
|
|
||||||
{
|
|
||||||
_isPanelVisible = !_isPanelVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleCornerTapGesture();
|
|
||||||
if (_lockPlayerHealthToMax)
|
|
||||||
{
|
|
||||||
KeepPlayerHealthAtMax();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGUI()
|
|
||||||
{
|
|
||||||
GUISkin previousSkin = GUI.skin;
|
|
||||||
Matrix4x4 previousMatrix = GUI.matrix;
|
|
||||||
if (_panelSkin == null)
|
|
||||||
{
|
|
||||||
_panelSkin = Instantiate(previousSkin);
|
|
||||||
_panelSkin.label.fontSize = 18;
|
|
||||||
_panelSkin.label.wordWrap = true;
|
|
||||||
_panelSkin.button.fontSize = 18;
|
|
||||||
_panelSkin.button.padding = new RectOffset(12, 12, 10, 10);
|
|
||||||
_panelSkin.button.fixedHeight = 44f;
|
|
||||||
_panelSkin.textField.fontSize = 20;
|
|
||||||
_panelSkin.textField.fixedHeight = 44f;
|
|
||||||
_panelSkin.textField.padding = new RectOffset(10, 10, 10, 10);
|
|
||||||
_panelSkin.window.padding = new RectOffset(16, 16, 12, 16);
|
|
||||||
_panelSkin.verticalScrollbar.fixedWidth = 28f;
|
|
||||||
_panelSkin.verticalScrollbarThumb.fixedWidth = 28f;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect safeArea = Screen.safeArea;
|
|
||||||
float scale = Mathf.Max(0.1f, Mathf.Min(safeArea.width, safeArea.height) / 480f);
|
|
||||||
Rect bounds = new Rect(0f, 0f, safeArea.width / scale, safeArea.height / scale);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
GUI.skin = _panelSkin;
|
|
||||||
GUI.matrix = Matrix4x4.TRS(
|
|
||||||
new Vector3(safeArea.x, Screen.height - safeArea.yMax, 0f),
|
|
||||||
Quaternion.identity, new Vector3(scale, scale, 1f));
|
|
||||||
DrawToggleButton(bounds);
|
|
||||||
if (!_isPanelVisible) return;
|
|
||||||
|
|
||||||
float width = Mathf.Min(460f, bounds.width - 24f);
|
|
||||||
_windowRect = new Rect(bounds.width - width - 12f, 72f, width,
|
|
||||||
Mathf.Min(760f, bounds.height - 84f));
|
|
||||||
GUI.Window(_windowId, _windowRect, DrawWindow, string.Empty);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
GUI.matrix = previousMatrix;
|
|
||||||
GUI.skin = previousSkin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (_panelSkin != null) Destroy(_panelSkin);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawToggleButton(Rect bounds)
|
|
||||||
{
|
|
||||||
const float width = 120f;
|
|
||||||
const float height = 48f;
|
|
||||||
Rect buttonRect = new Rect(bounds.width - width - 12f, 12f, width, height);
|
|
||||||
if (GUI.Button(buttonRect, "DEBUG"))
|
|
||||||
{
|
|
||||||
_isPanelVisible = !_isPanelVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawWindow(int windowId)
|
|
||||||
{
|
|
||||||
if (_showBuffSection)
|
|
||||||
{
|
|
||||||
EnsurePropList();
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.BeginVertical();
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Label("Runtime Debug", GUILayout.ExpandWidth(true));
|
|
||||||
if (GUILayout.Button("Close", GUILayout.Width(90f))) _isPanelVisible = false;
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
GUILayout.Space(8f);
|
|
||||||
_panelScroll = GUILayout.BeginScrollView(_panelScroll, false, true);
|
|
||||||
|
|
||||||
bool hasPreviousSection = false;
|
|
||||||
if (_showBuffSection)
|
|
||||||
{
|
|
||||||
DrawBuffSection();
|
|
||||||
hasPreviousSection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasVisibleBattleSection())
|
|
||||||
{
|
|
||||||
if (hasPreviousSection)
|
|
||||||
{
|
|
||||||
GUILayout.Space(8f);
|
|
||||||
GUILayout.Label(string.Empty, GUI.skin.horizontalSlider);
|
|
||||||
GUILayout.Space(8f);
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawBattleSection();
|
|
||||||
hasPreviousSection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showTips)
|
|
||||||
{
|
|
||||||
if (hasPreviousSection)
|
|
||||||
{
|
|
||||||
GUILayout.Space(8f);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.Label("Tips: press F8 or tap top-left corner 3 times to toggle.");
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndScrollView();
|
|
||||||
GUILayout.EndVertical();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawBuffSection()
|
|
||||||
{
|
|
||||||
GUILayout.Label("Buff Debug");
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Label("Search", GUILayout.Width(78f));
|
|
||||||
_searchText = GUILayout.TextField(_searchText);
|
|
||||||
if (GUILayout.Button("Refresh", GUILayout.Width(100f)))
|
|
||||||
{
|
|
||||||
EnsurePropList(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
ApplyFilter(_searchText);
|
|
||||||
if (_displayNames.Length == 0)
|
|
||||||
{
|
|
||||||
GUILayout.Label("No Buff data. Enter battle and try again.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
|
||||||
_buffScroll = GUILayout.BeginScrollView(_buffScroll, GUILayout.Height(120f));
|
|
||||||
_selectedIndex = GUILayout.SelectionGrid(_selectedIndex, _displayNames, 1);
|
|
||||||
GUILayout.EndScrollView();
|
|
||||||
|
|
||||||
PropConfig selectedProp = GetSelectedProp();
|
|
||||||
if (selectedProp == null) return;
|
|
||||||
|
|
||||||
GUILayout.Label($"Selected: {selectedProp.Name} ({selectedProp.Rarity})");
|
|
||||||
GUILayout.Label(ItemDescUtility.CreatePropDescription(selectedProp.Modifiers), GUILayout.Height(40f));
|
|
||||||
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Label("Count", GUILayout.Width(78f));
|
|
||||||
string addCountText = GUILayout.TextField(_addCount.ToString(), GUILayout.Width(88f));
|
|
||||||
if (!int.TryParse(addCountText, out _addCount)) _addCount = 1;
|
|
||||||
_addCount = Mathf.Clamp(_addCount, 1, 99);
|
|
||||||
|
|
||||||
if (GUILayout.Button("Add Buff To Player", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
AddSelectedBuffToPlayer(selectedProp, _addCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawBattleSection()
|
|
||||||
{
|
|
||||||
GUILayout.Label("Battle Debug");
|
|
||||||
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
|
|
||||||
EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
|
|
||||||
Player player = FindPlayer();
|
|
||||||
HealthComponent playerHealth = player != null ? player.GetComponent<HealthComponent>() : null;
|
|
||||||
|
|
||||||
if (enemyManager == null)
|
|
||||||
{
|
|
||||||
GUILayout.Label("EnemyManager unavailable.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (procedure == null)
|
|
||||||
{
|
|
||||||
GUILayout.Label("ProcedureGame unavailable.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showBattleOverview)
|
|
||||||
{
|
|
||||||
GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}");
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
|
|
||||||
GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}");
|
|
||||||
}
|
|
||||||
|
|
||||||
SimulationWorld simulationWorld = GameEntry.SimulationWorld;
|
|
||||||
if (_showCollisionStats && simulationWorld != null)
|
|
||||||
{
|
|
||||||
GUILayout.Space(4f);
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Collision Queries: total {simulationWorld.LastCollisionQueryCount} (Projectile {simulationWorld.LastProjectileCollisionQueryCount} / Area {simulationWorld.LastAreaCollisionQueryCount})");
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Collision Candidates: total {simulationWorld.LastCollisionCandidateCount} (Projectile {simulationWorld.LastProjectileCollisionCandidateCount} / Area {simulationWorld.LastAreaCollisionCandidateCount})");
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Area Resolve: hits {simulationWorld.LastResolvedAreaHitCount}");
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Broad Phase: cell {simulationWorld.LastCollisionCellSize:F2}, hasEnemyTargets {(simulationWorld.LastCollisionHasEnemyTargets ? "Yes" : "No")}");
|
|
||||||
if (simulationWorld.LastCollisionCandidateCount != 0)
|
|
||||||
{
|
|
||||||
Log.Info($"LastCollisionCandidateCount:{simulationWorld.LastCollisionCandidateCount}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simulationWorld.LastResolvedAreaHitCount != 0)
|
|
||||||
{
|
|
||||||
Log.Info($"LastResolvedAreaHitCount:{simulationWorld.LastResolvedAreaHitCount}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showSpawnControls)
|
|
||||||
{
|
|
||||||
GUILayout.Space(12f);
|
|
||||||
GUILayout.Label("Enemy Spawn Rate");
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Label("Rate", GUILayout.Width(78f));
|
|
||||||
string rateText = GUILayout.TextField(_spawnRateScaleInput.ToString("F2"), GUILayout.Width(88f));
|
|
||||||
if (float.TryParse(rateText, out float parsedRate))
|
|
||||||
{
|
|
||||||
_spawnRateScaleInput = Mathf.Clamp(parsedRate, MinSpawnRate, 50f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button("Apply", GUILayout.Width(100f)))
|
|
||||||
{
|
|
||||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
|
|
||||||
if (GUILayout.Button("x0.5"))
|
|
||||||
{
|
|
||||||
_spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f);
|
|
||||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button("x2"))
|
|
||||||
{
|
|
||||||
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
|
|
||||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showBattleDurationControls)
|
|
||||||
{
|
|
||||||
GUILayout.Space(12f);
|
|
||||||
GUILayout.Label("Battle Duration");
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Label("Add Sec", GUILayout.Width(78f));
|
|
||||||
string durationText = GUILayout.TextField(_extendDurationSeconds.ToString("F0"), GUILayout.Width(88f));
|
|
||||||
if (float.TryParse(durationText, out float parsedDuration))
|
|
||||||
{
|
|
||||||
_extendDurationSeconds = Mathf.Clamp(parsedDuration, 1f, 3600f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button("Extend Battle", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
if (procedure.CurrentGameState is GameStateBattle gameState)
|
|
||||||
{
|
|
||||||
gameState.AddBattleDuration(_extendDurationSeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showPlayerWeaponControls)
|
|
||||||
{
|
|
||||||
GUILayout.Space(12f);
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}");
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUI.enabled = player != null;
|
|
||||||
if (GUILayout.Button("Disable Weapons", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
player.SetWeaponEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button("Enable Weapons", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
player.SetWeaponEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.enabled = true;
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_showPlayerHealthControls)
|
|
||||||
{
|
|
||||||
GUILayout.Space(4f);
|
|
||||||
GUILayout.Label(
|
|
||||||
$"Player HP: {(playerHealth == null ? "Unavailable" : $"{playerHealth.CurrentHealth}/{playerHealth.MaxHealth}")}");
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUI.enabled = playerHealth != null;
|
|
||||||
if (GUILayout.Button($"+{DebugHealAmount} HP", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
AddPlayerHealth(playerHealth, DebugHealAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GUILayout.Button(_lockPlayerHealthToMax ? "GodMode: ON" : "GodMode: OFF", GUILayout.Height(44f)))
|
|
||||||
{
|
|
||||||
_lockPlayerHealthToMax = !_lockPlayerHealthToMax;
|
|
||||||
if (_lockPlayerHealthToMax)
|
|
||||||
{
|
|
||||||
RestorePlayerHealthToMax(playerHealth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.enabled = true;
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HasVisibleBattleSection()
|
|
||||||
{
|
|
||||||
return _showBattleOverview ||
|
|
||||||
_showCollisionStats ||
|
|
||||||
_showSpawnControls ||
|
|
||||||
_showBattleDurationControls ||
|
|
||||||
_showPlayerWeaponControls ||
|
|
||||||
_showPlayerHealthControls;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsurePropList(bool force = false)
|
|
||||||
{
|
|
||||||
if (!force && _allProps != null && _allProps.Count > 0) return;
|
|
||||||
|
|
||||||
if (GameEntry.Luban == null)
|
|
||||||
{
|
|
||||||
_allProps = Array.Empty<PropConfig>();
|
|
||||||
_displayNames = Array.Empty<string>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_allProps = GameEntry.Luban.GetTable<PropConfig>();
|
|
||||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, Mathf.Max(0, _allProps.Count - 1));
|
|
||||||
ApplyFilter(_searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyFilter(string keyword)
|
|
||||||
{
|
|
||||||
if (_allProps == null || _allProps.Count == 0)
|
|
||||||
{
|
|
||||||
_displayNames = Array.Empty<string>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(keyword))
|
|
||||||
{
|
|
||||||
_filteredProps = _allProps;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string search = keyword.Trim();
|
|
||||||
_filteredProps = _allProps.Where(p =>
|
|
||||||
p != null &&
|
|
||||||
(p.Name?.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
||||||
p.Id.ToString().Contains(search))).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
_displayNames = _filteredProps.Select(p => $"[{p.Id}] {p.Name} ({p.Rarity})").ToArray();
|
|
||||||
if (_displayNames.Length == 0) _selectedIndex = 0;
|
|
||||||
else _selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PropConfig GetSelectedProp()
|
|
||||||
{
|
|
||||||
if (_filteredProps == null || _filteredProps.Count == 0) return null;
|
|
||||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _filteredProps.Count - 1);
|
|
||||||
return _filteredProps[_selectedIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Player FindPlayer()
|
|
||||||
{
|
|
||||||
return UnityEngine.Object.FindObjectOfType<Player>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void KeepPlayerHealthAtMax()
|
|
||||||
{
|
|
||||||
Player player = FindPlayer();
|
|
||||||
if (player == null) return;
|
|
||||||
|
|
||||||
HealthComponent playerHealth = player.GetComponent<HealthComponent>();
|
|
||||||
if (playerHealth == null) return;
|
|
||||||
|
|
||||||
RestorePlayerHealthToMax(playerHealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddPlayerHealth(HealthComponent playerHealth, int amount)
|
|
||||||
{
|
|
||||||
if (playerHealth == null || amount <= 0) return;
|
|
||||||
if (playerHealth.CurrentHealth <= 0) return;
|
|
||||||
|
|
||||||
int maxHealth = playerHealth.MaxHealth;
|
|
||||||
if (maxHealth <= 0) return;
|
|
||||||
|
|
||||||
int nextHealth = Mathf.Clamp(playerHealth.CurrentHealth + amount, 0, maxHealth);
|
|
||||||
if (nextHealth == playerHealth.CurrentHealth) return;
|
|
||||||
|
|
||||||
playerHealth.CurrentHealth = nextHealth;
|
|
||||||
PublishPlayerHealthChanged(playerHealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RestorePlayerHealthToMax(HealthComponent playerHealth)
|
|
||||||
{
|
|
||||||
if (playerHealth == null) return;
|
|
||||||
if (playerHealth.CurrentHealth <= 0) return;
|
|
||||||
|
|
||||||
int maxHealth = playerHealth.MaxHealth;
|
|
||||||
if (maxHealth <= 0 || playerHealth.CurrentHealth >= maxHealth) return;
|
|
||||||
|
|
||||||
playerHealth.CurrentHealth = maxHealth;
|
|
||||||
PublishPlayerHealthChanged(playerHealth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PublishPlayerHealthChanged(HealthComponent playerHealth)
|
|
||||||
{
|
|
||||||
if (playerHealth == null || GameEntry.Event == null) return;
|
|
||||||
|
|
||||||
GameEntry.Event.Fire(null,
|
|
||||||
PlayerHealthChangeEventArgs.Create(0, playerHealth.CurrentHealth, playerHealth.MaxHealth));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddSelectedBuffToPlayer(PropConfig prop, int count)
|
|
||||||
{
|
|
||||||
Player player = FindPlayer();
|
|
||||||
if (player == null || prop == null || prop.Modifiers == null) return;
|
|
||||||
|
|
||||||
int applyCount = Mathf.Clamp(count, 1, 99);
|
|
||||||
for (int i = 0; i < applyCount; i++)
|
|
||||||
{
|
|
||||||
player.AddProp(new PropItem(prop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleCornerTapGesture()
|
|
||||||
{
|
|
||||||
if (!TryGetTouchReleased(out Vector2 touchPosition)) return;
|
|
||||||
if (touchPosition.x > 80f || touchPosition.y < Screen.height - 80f) return;
|
|
||||||
|
|
||||||
float now = Time.unscaledTime;
|
|
||||||
if (now - _lastCornerTapTime > CornerTapWindow)
|
|
||||||
{
|
|
||||||
_cornerTapCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastCornerTapTime = now;
|
|
||||||
_cornerTapCount++;
|
|
||||||
|
|
||||||
if (_cornerTapCount >= RequiredCornerTapCount)
|
|
||||||
{
|
|
||||||
_cornerTapCount = 0;
|
|
||||||
_isPanelVisible = !_isPanelVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTogglePressed()
|
|
||||||
{
|
|
||||||
#if ENABLE_INPUT_SYSTEM
|
|
||||||
Keyboard keyboard = Keyboard.current;
|
|
||||||
if (keyboard == null) return false;
|
|
||||||
return keyboard.backquoteKey.wasPressedThisFrame || keyboard.f8Key.wasPressedThisFrame;
|
|
||||||
#else
|
|
||||||
return Input.GetKeyDown(KeyCode.BackQuote) || Input.GetKeyDown(KeyCode.F8);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryGetTouchReleased(out Vector2 touchPosition)
|
|
||||||
{
|
|
||||||
#if ENABLE_INPUT_SYSTEM
|
|
||||||
Touchscreen touchscreen = Touchscreen.current;
|
|
||||||
if (touchscreen == null)
|
|
||||||
{
|
|
||||||
touchPosition = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var touch = touchscreen.primaryTouch;
|
|
||||||
if (!touch.press.wasReleasedThisFrame)
|
|
||||||
{
|
|
||||||
touchPosition = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
touchPosition = touch.position.ReadValue();
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
if (Input.touchCount <= 0)
|
|
||||||
{
|
|
||||||
touchPosition = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Touch touch = Input.GetTouch(0);
|
|
||||||
if (touch.phase != TouchPhase.Ended)
|
|
||||||
{
|
|
||||||
touchPosition = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
touchPosition = touch.position;
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -115,7 +115,7 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 8253353784175897923}
|
- component: {fileID: 8253353784175897923}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: BulletHandgun
|
m_Name: Projectile
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
@@ -18,12 +18,55 @@ namespace SepCore.Definition
|
|||||||
{
|
{
|
||||||
public const string DefaultLayerName = "Default";
|
public const string DefaultLayerName = "Default";
|
||||||
public static readonly int DefaultLayerId = LayerMask.NameToLayer(DefaultLayerName);
|
public static readonly int DefaultLayerId = LayerMask.NameToLayer(DefaultLayerName);
|
||||||
|
public static readonly LayerMask DefaultLayerMask = 1 << DefaultLayerId;
|
||||||
|
|
||||||
|
public const string TransparentFXLayerName = "TransparentFX";
|
||||||
|
public static readonly int TransparentLayerId = LayerMask.NameToLayer(TransparentFXLayerName);
|
||||||
|
public static readonly LayerMask TransparentLayerMask = 1 << TransparentLayerId;
|
||||||
|
|
||||||
|
public const string IgnoreRaycastLayerName = "Ignore Raycast";
|
||||||
|
public static readonly int IgnoreLayerId = LayerMask.NameToLayer(IgnoreRaycastLayerName);
|
||||||
|
public static readonly LayerMask IgnoreLayerMask = 1 << IgnoreLayerId;
|
||||||
|
|
||||||
|
public const string WaterLayerName = "Water";
|
||||||
|
public static readonly int WaterLayerId = LayerMask.NameToLayer(WaterLayerName);
|
||||||
|
public static readonly LayerMask WaterLayerMask = 1 << WaterLayerId;
|
||||||
|
|
||||||
public const string UILayerName = "UI";
|
public const string UILayerName = "UI";
|
||||||
public static readonly int UILayerId = LayerMask.NameToLayer(UILayerName);
|
public static readonly int UILayerId = LayerMask.NameToLayer(UILayerName);
|
||||||
|
public static readonly LayerMask UILayerMask = 1 << UILayerId;
|
||||||
|
|
||||||
public const string TargetableObjectLayerName = "Targetable Object";
|
public const string TargetableObjectLayerName = "Targetable Object";
|
||||||
public static readonly int TargetableObjectLayerId = LayerMask.NameToLayer(TargetableObjectLayerName);
|
public static readonly int TargetableObjectLayerId = LayerMask.NameToLayer(TargetableObjectLayerName);
|
||||||
|
public static readonly LayerMask TargetableObjectLayerMask = 1 << TargetableObjectLayerId;
|
||||||
|
|
||||||
|
public const string PlayerLayerName = "Player";
|
||||||
|
public static readonly int PlayerLayerId = LayerMask.NameToLayer(PlayerLayerName);
|
||||||
|
public static readonly LayerMask PlayerLayerMask = 1 << PlayerLayerId;
|
||||||
|
|
||||||
|
public const string EnemyLayerName = "Enemy";
|
||||||
|
public static readonly int EnemyLayerId = LayerMask.NameToLayer(EnemyLayerName);
|
||||||
|
public static readonly LayerMask EnemyLayerMask = 1 << EnemyLayerId;
|
||||||
|
|
||||||
|
public const string PlayerBulletLayerName = "PlayerBullet";
|
||||||
|
public static readonly int PlayerBulletLayerId = LayerMask.NameToLayer(PlayerBulletLayerName);
|
||||||
|
public static readonly LayerMask PlayerBulletLayerMask = 1 << PlayerBulletLayerId;
|
||||||
|
|
||||||
|
public const string EnemyBulletLayerName = "EnemyBullet";
|
||||||
|
public static readonly int EnemyBulletLayerId = LayerMask.NameToLayer(EnemyBulletLayerName);
|
||||||
|
public static readonly LayerMask EnemyBulletLayerMask = 1 << EnemyBulletLayerId;
|
||||||
|
|
||||||
|
public const string PlayerWeaponLayerName = "PlayerWeapon";
|
||||||
|
public static readonly int PlayerWeaponLayerId = LayerMask.NameToLayer(PlayerWeaponLayerName);
|
||||||
|
public static readonly LayerMask PlayerWeaponLayerMask = 1 << PlayerWeaponLayerId;
|
||||||
|
|
||||||
|
public const string EnemyWeaponLayerName = "EnemyWeapon";
|
||||||
|
public static readonly int EnemyWeaponLayerId = LayerMask.NameToLayer(EnemyWeaponLayerName);
|
||||||
|
public static readonly LayerMask EnemyWeaponLayerMask = 1 << EnemyWeaponLayerId;
|
||||||
|
|
||||||
|
public const string DropLayerName = "Drop";
|
||||||
|
public static readonly int DropLayerId = LayerMask.NameToLayer(DropLayerName);
|
||||||
|
public static readonly LayerMask DropLayerMask = 1 << DropLayerId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public static class DamageTypeExtensions
|
||||||
|
{
|
||||||
|
public static StatType ToScalingStat(this DamageType damageType)
|
||||||
|
{
|
||||||
|
switch (damageType)
|
||||||
|
{
|
||||||
|
case DamageType.Melee:
|
||||||
|
return StatType.MeleeDamage;
|
||||||
|
case DamageType.Ranged:
|
||||||
|
return StatType.RangedDamage;
|
||||||
|
case DamageType.Element:
|
||||||
|
return StatType.ElementalDamage;
|
||||||
|
default:
|
||||||
|
return StatType.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 574d105bf89bf7546a682ed407708034
|
guid: 5e6128fa910e4a778c187123490b4119
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 属性查询接口。
|
||||||
|
/// 供战斗碰撞(ImpactData)与伤害管线解耦读取属性值。
|
||||||
|
/// </summary>
|
||||||
|
public interface IAttributeProvider
|
||||||
|
{
|
||||||
|
float GetValue(StatType statType);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: da25d8e0669f0194d8f56f4283c67b38
|
guid: b483f12479014631bb5914285260aa31
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,37 +1,45 @@
|
|||||||
//------------------------------------------------------------
|
|
||||||
// Game Framework
|
|
||||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
|
||||||
// Homepage: https://gameframework.cn/
|
|
||||||
// Feedback: mailto:ellan@gameframework.cn
|
|
||||||
//------------------------------------------------------------
|
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Definition
|
namespace SepCore.Definition
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Auto)]
|
[StructLayout(LayoutKind.Auto)]
|
||||||
public struct ImpactData
|
public struct ImpactData
|
||||||
{
|
{
|
||||||
public ImpactData(CampType camp /*, int health*/, int attackBase, StatProperty attackStat = null,
|
|
||||||
StatProperty defenseStat = null, StatProperty dodgeStat = null)
|
|
||||||
{
|
|
||||||
Camp = camp;
|
|
||||||
//Health = health;
|
|
||||||
AttackBase = attackBase;
|
|
||||||
AttackStat = attackStat;
|
|
||||||
DefenseStat = defenseStat;
|
|
||||||
DodgeStat = dodgeStat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CampType Camp { get; }
|
public CampType Camp { get; }
|
||||||
|
|
||||||
public int AttackBase { get; }
|
public int AttackBase { get; }
|
||||||
|
|
||||||
public StatProperty AttackStat { get; }
|
public DamageType DamageType { get; }
|
||||||
|
|
||||||
public StatProperty DefenseStat { get; }
|
public float ScalingRatio { get; }
|
||||||
|
|
||||||
public StatProperty DodgeStat { get; }
|
public float BaseCritChance { get; }
|
||||||
|
|
||||||
|
public float CritMultiplier { get; }
|
||||||
|
|
||||||
|
public IAttributeProvider AttributeProvider { get; }
|
||||||
|
|
||||||
|
public ImpactData(
|
||||||
|
CampType camp,
|
||||||
|
int attackBase,
|
||||||
|
IAttributeProvider attributeProvider = null,
|
||||||
|
DamageType damageType = DamageType.None,
|
||||||
|
float scalingRatio = 1.0f,
|
||||||
|
float baseCritChance = 0f,
|
||||||
|
float critMultiplier = 0f)
|
||||||
|
{
|
||||||
|
Camp = camp;
|
||||||
|
AttackBase = attackBase;
|
||||||
|
AttributeProvider = attributeProvider;
|
||||||
|
DamageType = damageType;
|
||||||
|
ScalingRatio = scalingRatio;
|
||||||
|
BaseCritChance = baseCritChance;
|
||||||
|
CritMultiplier = critMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpactData(CampType camp, int attackBase)
|
||||||
|
: this(camp, attackBase, null, DamageType.None, 1.0f, 0f, 0f)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,24 +4,24 @@ namespace SepCore.Definition
|
|||||||
{
|
{
|
||||||
public class PropItem
|
public class PropItem
|
||||||
{
|
{
|
||||||
private readonly List<modifier> _modifiers = new();
|
private readonly List<StatModifierConfig> _modifiers = new();
|
||||||
public string Title { get; private set; }
|
public string Title { get; private set; }
|
||||||
public string IconAssetName { get; private set; }
|
public string IconAssetName { get; private set; }
|
||||||
public Rarity Rarity { get; private set; }
|
public Rarity Rarity { get; private set; }
|
||||||
public IReadOnlyList<modifier> Modifiers => _modifiers;
|
public IReadOnlyList<StatModifierConfig> Modifiers => _modifiers;
|
||||||
|
|
||||||
public PropItem(PropConfig prop)
|
public PropItem(PropConfig prop)
|
||||||
{
|
{
|
||||||
if (prop.Modifiers != null)
|
if (prop.StatModifierConfigs != null)
|
||||||
{
|
{
|
||||||
_modifiers.AddRange(prop.Modifiers);
|
_modifiers.AddRange(prop.StatModifierConfigs);
|
||||||
}
|
}
|
||||||
Title = prop.Name;
|
Title = prop.Name;
|
||||||
Rarity = prop.Rarity;
|
Rarity = prop.Rarity;
|
||||||
IconAssetName = prop.Sprite;
|
IconAssetName = prop.Sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropItem(IEnumerable<modifier> modifiers)
|
public PropItem(IEnumerable<StatModifierConfig> modifiers)
|
||||||
{
|
{
|
||||||
if (modifiers != null)
|
if (modifiers != null)
|
||||||
{
|
{
|
||||||
@@ -29,7 +29,7 @@ namespace SepCore.Definition
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PropItem(IEnumerable<modifier> modifiers, Rarity rarity, string title, string iconAssetName)
|
public PropItem(IEnumerable<StatModifierConfig> modifiers, Rarity rarity, string title, string iconAssetName)
|
||||||
{
|
{
|
||||||
if (modifiers != null)
|
if (modifiers != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public sealed partial class StatConfig
|
||||||
|
{
|
||||||
|
public StatConfig()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatConfig(
|
||||||
|
StatType statType,
|
||||||
|
string name = "",
|
||||||
|
float defaultBaseValue = 0f,
|
||||||
|
bool hasMin = false,
|
||||||
|
float minValue = 0f,
|
||||||
|
bool hasMax = false,
|
||||||
|
float maxValue = 0f,
|
||||||
|
bool isPercentDisplay = false)
|
||||||
|
{
|
||||||
|
StatType = statType;
|
||||||
|
Name = name;
|
||||||
|
DefaultBaseValue = defaultBaseValue;
|
||||||
|
HasMin = hasMin;
|
||||||
|
MinValue = minValue;
|
||||||
|
HasMax = hasMax;
|
||||||
|
MaxValue = maxValue;
|
||||||
|
IsPercentDisplay = isPercentDisplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4d1a821e8ee1a9a4b912b70b0a1616eb
|
guid: 7b910e53a2454b5f903517865c36ad18
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 属性修饰器。
|
||||||
|
/// 包含修饰数值、修饰类型(固定值/百分比加算/独立乘区)以及来源对象(道具/武器/羁绊/特质/Buff)。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StatModifier
|
||||||
|
{
|
||||||
|
public float Value { get; }
|
||||||
|
public ModifierType Type { get; }
|
||||||
|
public object Source { get; }
|
||||||
|
|
||||||
|
public StatModifier(float value, ModifierType type, object source = null)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
Type = type;
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 Luban 导出的 modifier 数据结构转换。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mod">Luban modifier 数据实例</param>
|
||||||
|
/// <param name="source">来源对象引用</param>
|
||||||
|
public static StatModifier FromLuban(StatModifierConfig mod, object source = null)
|
||||||
|
{
|
||||||
|
if (mod == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
ModifierType type = mod.ModifierType;
|
||||||
|
return new StatModifier(mod.Value, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"[StatModifier Type={Type}, Value={Value}, Source={Source}]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: e82dc413a4cd277429ec1e3f2de69434
|
guid: 887b4b22fce86cb43b95cfdf3c6b967c
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public sealed partial class StatModifierConfig
|
||||||
|
{
|
||||||
|
public StatModifierConfig()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatModifierConfig(StatType statType, float value, ModifierType modifierType)
|
||||||
|
{
|
||||||
|
StatType = statType;
|
||||||
|
Value = value;
|
||||||
|
ModifierType = modifierType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace SepCore.Definition
|
|
||||||
{
|
|
||||||
public class StatProperty
|
|
||||||
{
|
|
||||||
public float Value;
|
|
||||||
public float Percent;
|
|
||||||
|
|
||||||
public StatProperty()
|
|
||||||
{
|
|
||||||
Value = 0f;
|
|
||||||
Percent = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StatProperty(float value, float percent)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
Percent = percent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 18e7bc9587114ae4a91c70f829543de8
|
|
||||||
timeCreated: 1770353735
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SepCore.Definition
|
|
||||||
{
|
|
||||||
public sealed partial class modifier
|
|
||||||
{
|
|
||||||
public StatType StatType => Key;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 实际参与数值计算的浮点数值。
|
|
||||||
/// 1. 百分比加成(IsPercent == true):表内千分比(如 200 代表 20%),转换为 0.2f。
|
|
||||||
/// 2. 非百分比加成(IsPercent == false):移动速度原为 float 放大了 1000 倍(如 800 代表 0.8f),除以 1000f;其余(如生命+53、攻击+10)直接使用原值。
|
|
||||||
/// </summary>
|
|
||||||
public float RealValue
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsPercent)
|
|
||||||
{
|
|
||||||
return Value / 1000f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Key == StatType.MovementSpeed)
|
|
||||||
{
|
|
||||||
return Value / 1000f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public modifier()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public modifier(StatType key, int value, bool isPercent)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
Value = value;
|
|
||||||
IsPercent = isPercent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public modifier(StatType key, float value, bool isPercent)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
Value = (isPercent || key == StatType.MovementSpeed)
|
|
||||||
? Mathf.RoundToInt(value * 1000f)
|
|
||||||
: Mathf.RoundToInt(value);
|
|
||||||
IsPercent = isPercent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToDisplayString()
|
|
||||||
{
|
|
||||||
if (IsPercent)
|
|
||||||
{
|
|
||||||
float percent = Value / 10f;
|
|
||||||
return $"{Key} +{percent:0.##}%";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Key == StatType.MovementSpeed)
|
|
||||||
{
|
|
||||||
float speed = Value / 1000f;
|
|
||||||
return $"{Key} +{speed:0.##}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{Key} +{Value}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,55 +8,47 @@ namespace SepCore.Entity
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class EnemyData : TargetableObjectData
|
public class EnemyData : TargetableObjectData
|
||||||
{
|
{
|
||||||
private readonly EnemyConfig _enemyConfig;
|
public EnemyConfig Config { get; }
|
||||||
|
|
||||||
public EnemyData(int entityId, EnemyConfig enemyConfig, int level)
|
public EnemyData(int entityId, EnemyConfig enemyConfig, int level)
|
||||||
: base(enemyConfig.Prefab, entityId, CampType.Enemy)
|
: base(enemyConfig.Prefab, entityId, CampType.Enemy)
|
||||||
{
|
{
|
||||||
_enemyConfig = enemyConfig;
|
Config = enemyConfig;
|
||||||
int effectiveLevel = Mathf.Max(1, level);
|
int effectiveLevel = Mathf.Max(1, level);
|
||||||
MaxHealthBase = enemyConfig.MaxHealth + enemyConfig.HpAddPerLevel * (effectiveLevel - 1);
|
MaxHealthBase = enemyConfig.MaxHealth + enemyConfig.HpAddPerLevel * (effectiveLevel - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnemyType EnemyType => _enemyConfig.EnemyType;
|
public EnemyType EnemyType => Config.EnemyType;
|
||||||
|
|
||||||
public string LogicType => _enemyConfig.LogicType;
|
public string LogicType => Config.LogicType;
|
||||||
|
|
||||||
public override int MaxHealthBase { get; }
|
public override int MaxHealthBase { get; }
|
||||||
|
|
||||||
public int AttackDamage => _enemyConfig.AttackDamage;
|
public int AttackDamage => Config.AttackDamage;
|
||||||
|
|
||||||
public float AttackCooldown => _enemyConfig.AttackCooldown;
|
public float AttackCooldown => Config.AttackCooldown;
|
||||||
|
|
||||||
public float AttackRange => _enemyConfig.AttackRange;
|
public float AttackRange => Config.AttackRange;
|
||||||
|
|
||||||
public float SpeedBase => _enemyConfig.Speed;
|
public float SpeedBase => Config.Speed;
|
||||||
|
|
||||||
public int DropCoin => _enemyConfig.CoinDrop;
|
public int DropCoin => Config.CoinDrop;
|
||||||
|
|
||||||
public int DropExp => _enemyConfig.ExpDrop;
|
public int DropExp => Config.ExpDrop;
|
||||||
|
|
||||||
public float DropPercent => _enemyConfig.DropPercent;
|
public float DropPercent => Config.DropPercent;
|
||||||
|
|
||||||
public float Scale => _enemyConfig.Scale;
|
public float Scale => Config.Scale;
|
||||||
|
|
||||||
public Color Color
|
public Color Color
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// 空/非法颜色值视为无染色(乘以白色)。
|
// 空/非法颜色值视为无染色(乘以白色)。
|
||||||
return ColorUtility.TryParseHtmlString(_enemyConfig.Color, out Color color)
|
return ColorUtility.TryParseHtmlString(Config.Color, out Color color)
|
||||||
? color
|
? color
|
||||||
: Color.white;
|
: Color.white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string> Params => null;
|
|
||||||
|
|
||||||
public bool TryGetParam(string key, out string value)
|
|
||||||
{
|
|
||||||
value = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace SepCore.Entity
|
|||||||
public class EnemyProjectileData : EntityDataBase
|
public class EnemyProjectileData : EntityDataBase
|
||||||
{
|
{
|
||||||
public EnemyProjectileData(int entityId, int ownerEntityId, CampType ownerCamp, int attackDamage,
|
public EnemyProjectileData(int entityId, int ownerEntityId, CampType ownerCamp, int attackDamage,
|
||||||
float speed, float lifeTime, Vector3 direction, string assetName = "BulletHandgun")
|
float speed, float lifeTime, Vector3 direction, string assetName = "Projectile")
|
||||||
: base(assetName, entityId)
|
: base(assetName, entityId)
|
||||||
{
|
{
|
||||||
OwnerEntityId = ownerEntityId;
|
OwnerEntityId = ownerEntityId;
|
||||||
|
|||||||
@@ -1,59 +1,56 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace SepCore.Entity
|
namespace SepCore.Entity
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class WeaponData : AccessoryObjectData
|
public class WeaponData : AccessoryObjectData
|
||||||
{
|
{
|
||||||
private readonly WeaponConfig _weaponConfig;
|
public WeaponConfig Config { get; }
|
||||||
|
|
||||||
public WeaponData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
public WeaponData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
||||||
: base(weaponConfig.Prefab, entityId, ownerId, ownerCamp)
|
: base(weaponConfig.Prefab, entityId, ownerId, ownerCamp)
|
||||||
{
|
{
|
||||||
_weaponConfig = weaponConfig;
|
Config = weaponConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeaponConfig Config => _weaponConfig;
|
public WeaponType WeaponType => Config.WeaponType;
|
||||||
|
|
||||||
public WeaponType WeaponType => _weaponConfig.WeaponType;
|
public string LogicType => Config.LogicType;
|
||||||
|
|
||||||
public string LogicType => _weaponConfig.LogicType;
|
public int Attack => Config.Attack;
|
||||||
|
|
||||||
public int Attack => _weaponConfig.Attack;
|
public DamageType DamageType => Config.DamageType;
|
||||||
|
|
||||||
public string Title => _weaponConfig.WeaponType.ToString();
|
public string Title => Config.WeaponType.ToString();
|
||||||
|
|
||||||
public string IconAssetName => _weaponConfig.Sprite;
|
public string Sprite => Config.Sprite;
|
||||||
|
|
||||||
public Rarity Rarity => _weaponConfig.Rarity;
|
public Rarity Rarity => Config.Rarity;
|
||||||
|
|
||||||
public int Price => _weaponConfig.Price;
|
public int Price => Config.Price;
|
||||||
|
|
||||||
public float Cooldown => _weaponConfig.Cooldown / 1000f;
|
public float Cooldown => Config.Cooldown;
|
||||||
|
|
||||||
public float AttackRange => _weaponConfig.AttackRange / 1000f;
|
public float AttackRange => Config.AttackRange;
|
||||||
|
|
||||||
public int AttackSoundId => _weaponConfig.AttackSoundId;
|
public int AttackSoundId => Config.AttackSoundId;
|
||||||
|
|
||||||
public IReadOnlyDictionary<ParamType, int> Parameters => _weaponConfig.Parameters;
|
public IReadOnlyDictionary<ParamType, float> Parameters => Config.Parameters;
|
||||||
|
|
||||||
public IReadOnlyList<modifier> Modifiers => _weaponConfig.Modifiers;
|
public IReadOnlyList<StatModifierConfig> Modifiers => Config.StatModifierConfigs;
|
||||||
|
|
||||||
public int GetParam(ParamType paramType, int defaultValue = 0)
|
public float GetParamFloat(ParamType paramType)
|
||||||
{
|
{
|
||||||
return _weaponConfig.Parameters.TryGetValue(paramType, out int value) ? value : defaultValue;
|
if (Config.Parameters.TryGetValue(paramType, out float val))
|
||||||
}
|
|
||||||
|
|
||||||
public float GetParamFloat(ParamType paramType, float defaultValue = 0f)
|
|
||||||
{
|
|
||||||
if (_weaponConfig.Parameters.TryGetValue(paramType, out int val))
|
|
||||||
{
|
{
|
||||||
return paramType == ParamType.SectorAngle ? val : val / 1000f;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
|
||||||
|
return 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponHandgunParamsData
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponHandgunData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponHandgunParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponHandgunData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponHandgunParamsData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponKnifeParamsData
|
|
||||||
{
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponKnifeData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponKnifeParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponKnifeData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponKnifeParamsData
|
|
||||||
{
|
|
||||||
HitRadius = GetParamFloat(ParamType.HitRadius)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponLanceParamsData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 横向半宽,表示前戳矩形判定的一半宽度。
|
|
||||||
/// </summary>
|
|
||||||
public float HitHalfWidth { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 旧字段兼容,未配置 HitHalfWidth 时回退使用。
|
|
||||||
/// </summary>
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 前戳判定盒体的总高度。
|
|
||||||
/// </summary>
|
|
||||||
public float HitHeight { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判定盒体中心相对战斗平面的高度偏移。
|
|
||||||
/// </summary>
|
|
||||||
public float HitCenterYOffset { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 前刺距离,同时驱动武器位移和命中长度。
|
|
||||||
/// </summary>
|
|
||||||
public float PierceLength { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 旧字段兼容,未配置 PierceLength 时回退使用。
|
|
||||||
/// </summary>
|
|
||||||
public float ThrustDistance { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判定起点相对武器当前位置的前置偏移。
|
|
||||||
/// </summary>
|
|
||||||
public float ForwardOffset { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 追踪目标时的转向速度。
|
|
||||||
/// </summary>
|
|
||||||
public float RotateSpeed { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向前突刺阶段耗时。
|
|
||||||
/// </summary>
|
|
||||||
public float AttackDuration { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收枪返回阶段耗时。
|
|
||||||
/// </summary>
|
|
||||||
public float ReturnDuration { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class WeaponLanceData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponLanceParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponLanceData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponLanceParamsData
|
|
||||||
{
|
|
||||||
HitHalfWidth = GetParamFloat(ParamType.HitHalfWidth),
|
|
||||||
PierceLength = GetParamFloat(ParamType.PierceLength),
|
|
||||||
HitHeight = GetParamFloat(ParamType.HitHeight)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponLightningParamsData
|
|
||||||
{
|
|
||||||
public float HitRadius { get; set; }
|
|
||||||
public float HoverHeight { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponLightningData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponLightningParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponLightningData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponLightningParamsData
|
|
||||||
{
|
|
||||||
HitRadius = GetParamFloat(ParamType.HitRadius),
|
|
||||||
HoverHeight = 0f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 25b0006918fd46959c7f6b8ec1bbc8ab
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SepCore.Definition;
|
|
||||||
|
|
||||||
namespace SepCore.Entity
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public sealed class WeaponSlashParamsData
|
|
||||||
{
|
|
||||||
public float SectorAngle { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WeaponSlashData : WeaponData
|
|
||||||
{
|
|
||||||
public WeaponSlashParamsData ParamsData { get; }
|
|
||||||
|
|
||||||
public WeaponSlashData(int entityId, WeaponConfig weaponConfig, int ownerId, CampType ownerCamp)
|
|
||||||
: base(entityId, weaponConfig, ownerId, ownerCamp)
|
|
||||||
{
|
|
||||||
ParamsData = new WeaponSlashParamsData
|
|
||||||
{
|
|
||||||
SectorAngle = GetParamFloat(ParamType.SectorAngle)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4aa6473525dc4c62aeee02ff562ec453
|
|
||||||
timeCreated: 1771484661
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害类型
|
||||||
|
/// </summary>
|
||||||
|
[System.Flags]
|
||||||
|
public enum DamageType : int
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 近战伤害
|
||||||
|
/// </summary>
|
||||||
|
Melee = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 远程伤害
|
||||||
|
/// </summary>
|
||||||
|
Ranged = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 元素伤害
|
||||||
|
/// </summary>
|
||||||
|
Element = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 178341689d25049458e8460d398db2dc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人状态
|
||||||
|
/// </summary>
|
||||||
|
public enum EnemyStateType : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击
|
||||||
|
/// </summary>
|
||||||
|
Attack = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 待机
|
||||||
|
/// </summary>
|
||||||
|
Idle = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索
|
||||||
|
/// </summary>
|
||||||
|
Seek = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// 追逐
|
||||||
|
/// </summary>
|
||||||
|
Chase = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// 死亡
|
||||||
|
/// </summary>
|
||||||
|
Dead = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d55e30fdec251d141bab19b1b87806b3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Luban;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public sealed partial class FormatText : Luban.BeanBase
|
||||||
|
{
|
||||||
|
public FormatText(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
Name = _buf.ReadString();
|
||||||
|
Format = _buf.ReadString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatText DeserializeFormatText(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
return new FormatText(_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文本名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
/// <summary>
|
||||||
|
/// 格式化字符串
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Format;
|
||||||
|
|
||||||
|
public const int __ID__ = -1977697820;
|
||||||
|
public override int GetTypeId() => __ID__;
|
||||||
|
|
||||||
|
public void ResolveRef(Tables tables)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "{ "
|
||||||
|
+ "name:" + Name + ","
|
||||||
|
+ "format:" + Format + ","
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 721766cc68542b640a2119ab0c73a833
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -17,6 +17,41 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
public GlobalConfig(ByteBuf _buf)
|
public GlobalConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
WeaponCapacity = _buf.ReadInt();
|
WeaponCapacity = _buf.ReadInt();
|
||||||
|
PropMaxCount = _buf.ReadInt();
|
||||||
|
PlayerEntity = _buf.ReadString();
|
||||||
|
PlayerEntity_Ref = null;
|
||||||
|
AbsorbSpeed = _buf.ReadFloat();
|
||||||
|
CollectDistance = _buf.ReadFloat();
|
||||||
|
DefaultAbsorbRange = _buf.ReadFloat();
|
||||||
|
WeaponRecycleRate = _buf.ReadFloat();
|
||||||
|
ShopGoodsCount = _buf.ReadInt();
|
||||||
|
ShopPriceRandomPercent = _buf.ReadFloat();
|
||||||
|
ShopMinPrice = _buf.ReadInt();
|
||||||
|
LevelUpRefreshPriceBase = _buf.ReadInt();
|
||||||
|
LevelUpRewardCount = _buf.ReadInt();
|
||||||
|
SpawnEnemyMaxCount = _buf.ReadInt();
|
||||||
|
EnemySpawnDistanceMin = _buf.ReadFloat();
|
||||||
|
EnemySpawnDistanceMax = _buf.ReadFloat();
|
||||||
|
WeaponRotateSpeed = _buf.ReadFloat();
|
||||||
|
WeaponBaseCrit = _buf.ReadFloat();
|
||||||
|
PlayerGroupName = _buf.ReadString();
|
||||||
|
EnemyGroupName = _buf.ReadString();
|
||||||
|
ProjectileGroupName = _buf.ReadString();
|
||||||
|
DropGroupName = _buf.ReadString();
|
||||||
|
EffectGroupNmae = _buf.ReadString();
|
||||||
|
MinDamage = _buf.ReadInt();
|
||||||
|
MaxDodgeRate = _buf.ReadInt();
|
||||||
|
MinSpawnRateScale = _buf.ReadInt();
|
||||||
|
ProjectileLifeTime = _buf.ReadInt();
|
||||||
|
ProjectileHeightOffset = _buf.ReadInt();
|
||||||
|
ProjectileSpeed = _buf.ReadInt();
|
||||||
|
ProjectileEntity = _buf.ReadString();
|
||||||
|
ProjectileEntity_Ref = null;
|
||||||
|
DamageTextYOffset = _buf.ReadInt();
|
||||||
|
EntityAsyncTimeout = _buf.ReadInt();
|
||||||
|
CritMultiplier = _buf.ReadFloat();
|
||||||
|
EnemyBodyRadius = _buf.ReadInt();
|
||||||
|
EnemySeparationIterations = _buf.ReadInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf)
|
public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf)
|
||||||
@@ -28,6 +63,140 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
/// 最大持有武器数量
|
/// 最大持有武器数量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int WeaponCapacity;
|
public readonly int WeaponCapacity;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大持有道具数量
|
||||||
|
/// </summary>
|
||||||
|
public readonly int PropMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家实体
|
||||||
|
/// </summary>
|
||||||
|
public readonly string PlayerEntity;
|
||||||
|
public EntityConfig PlayerEntity_Ref;
|
||||||
|
/// <summary>
|
||||||
|
/// 自动收集速度
|
||||||
|
/// </summary>
|
||||||
|
public readonly float AbsorbSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 自动收集距离
|
||||||
|
/// </summary>
|
||||||
|
public readonly float CollectDistance;
|
||||||
|
/// <summary>
|
||||||
|
/// 默认吸收距离
|
||||||
|
/// </summary>
|
||||||
|
public readonly float DefaultAbsorbRange;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器回收价比例
|
||||||
|
/// </summary>
|
||||||
|
public readonly float WeaponRecycleRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 商店商品数量
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ShopGoodsCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品价格浮动
|
||||||
|
/// </summary>
|
||||||
|
public readonly float ShopPriceRandomPercent;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品最低价
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ShopMinPrice;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励刷新基本价格
|
||||||
|
/// </summary>
|
||||||
|
public readonly int LevelUpRefreshPriceBase;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励候选数
|
||||||
|
/// </summary>
|
||||||
|
public readonly int LevelUpRewardCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大敌人数量
|
||||||
|
/// </summary>
|
||||||
|
public readonly int SpawnEnemyMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人生成最小距离
|
||||||
|
/// </summary>
|
||||||
|
public readonly float EnemySpawnDistanceMin;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人生成最大距离
|
||||||
|
/// </summary>
|
||||||
|
public readonly float EnemySpawnDistanceMax;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器旋转速度
|
||||||
|
/// </summary>
|
||||||
|
public readonly float WeaponRotateSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器基本暴击率
|
||||||
|
/// </summary>
|
||||||
|
public readonly float WeaponBaseCrit;
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家对象组名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string PlayerGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人对象组名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string EnemyGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物对象组名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string ProjectileGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 掉落物对象组名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string DropGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 特效对象组名
|
||||||
|
/// </summary>
|
||||||
|
public readonly string EffectGroupNmae;
|
||||||
|
/// <summary>
|
||||||
|
/// 最小伤害
|
||||||
|
/// </summary>
|
||||||
|
public readonly int MinDamage;
|
||||||
|
/// <summary>
|
||||||
|
/// 闪避率上限
|
||||||
|
/// </summary>
|
||||||
|
public readonly int MaxDodgeRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 最小刷怪速度倍数
|
||||||
|
/// </summary>
|
||||||
|
public readonly int MinSpawnRateScale;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物生命周期
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ProjectileLifeTime;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物生成高度
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ProjectileHeightOffset;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物移动速度
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ProjectileSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物实体
|
||||||
|
/// </summary>
|
||||||
|
public readonly string ProjectileEntity;
|
||||||
|
public EntityConfig ProjectileEntity_Ref;
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害数字高度偏移
|
||||||
|
/// </summary>
|
||||||
|
public readonly int DamageTextYOffset;
|
||||||
|
/// <summary>
|
||||||
|
/// 实体生成超时时间
|
||||||
|
/// </summary>
|
||||||
|
public readonly int EntityAsyncTimeout;
|
||||||
|
/// <summary>
|
||||||
|
/// 暴击伤害倍率
|
||||||
|
/// </summary>
|
||||||
|
public readonly float CritMultiplier;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人碰撞半径
|
||||||
|
/// </summary>
|
||||||
|
public readonly int EnemyBodyRadius;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人分离迭代次数
|
||||||
|
/// </summary>
|
||||||
|
public readonly int EnemySeparationIterations;
|
||||||
|
|
||||||
public const int __ID__ = -958250779;
|
public const int __ID__ = -958250779;
|
||||||
public override int GetTypeId() => __ID__;
|
public override int GetTypeId() => __ID__;
|
||||||
@@ -35,12 +204,78 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
PlayerEntity_Ref = tables.TbEntityConfig.GetOrDefault(PlayerEntity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ProjectileEntity_Ref = tables.TbEntityConfig.GetOrDefault(ProjectileEntity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "{ "
|
return "{ "
|
||||||
+ "weaponCapacity:" + WeaponCapacity + ","
|
+ "weaponCapacity:" + WeaponCapacity + ","
|
||||||
|
+ "propMaxCount:" + PropMaxCount + ","
|
||||||
|
+ "playerEntity:" + PlayerEntity + ","
|
||||||
|
+ "absorbSpeed:" + AbsorbSpeed + ","
|
||||||
|
+ "collectDistance:" + CollectDistance + ","
|
||||||
|
+ "defaultAbsorbRange:" + DefaultAbsorbRange + ","
|
||||||
|
+ "weaponRecycleRate:" + WeaponRecycleRate + ","
|
||||||
|
+ "shopGoodsCount:" + ShopGoodsCount + ","
|
||||||
|
+ "shopPriceRandomPercent:" + ShopPriceRandomPercent + ","
|
||||||
|
+ "shopMinPrice:" + ShopMinPrice + ","
|
||||||
|
+ "levelUpRefreshPriceBase:" + LevelUpRefreshPriceBase + ","
|
||||||
|
+ "levelUpRewardCount:" + LevelUpRewardCount + ","
|
||||||
|
+ "spawnEnemyMaxCount:" + SpawnEnemyMaxCount + ","
|
||||||
|
+ "enemySpawnDistanceMin:" + EnemySpawnDistanceMin + ","
|
||||||
|
+ "enemySpawnDistanceMax:" + EnemySpawnDistanceMax + ","
|
||||||
|
+ "weaponRotateSpeed:" + WeaponRotateSpeed + ","
|
||||||
|
+ "weaponBaseCrit:" + WeaponBaseCrit + ","
|
||||||
|
+ "playerGroupName:" + PlayerGroupName + ","
|
||||||
|
+ "enemyGroupName:" + EnemyGroupName + ","
|
||||||
|
+ "projectileGroupName:" + ProjectileGroupName + ","
|
||||||
|
+ "dropGroupName:" + DropGroupName + ","
|
||||||
|
+ "effectGroupNmae:" + EffectGroupNmae + ","
|
||||||
|
+ "minDamage:" + MinDamage + ","
|
||||||
|
+ "maxDodgeRate:" + MaxDodgeRate + ","
|
||||||
|
+ "minSpawnRateScale:" + MinSpawnRateScale + ","
|
||||||
|
+ "projectileLifeTime:" + ProjectileLifeTime + ","
|
||||||
|
+ "projectileHeightOffset:" + ProjectileHeightOffset + ","
|
||||||
|
+ "projectileSpeed:" + ProjectileSpeed + ","
|
||||||
|
+ "projectileEntity:" + ProjectileEntity + ","
|
||||||
|
+ "damageTextYOffset:" + DamageTextYOffset + ","
|
||||||
|
+ "entityAsyncTimeout:" + EntityAsyncTimeout + ","
|
||||||
|
+ "critMultiplier:" + CritMultiplier + ","
|
||||||
|
+ "enemyBodyRadius:" + EnemyBodyRadius + ","
|
||||||
|
+ "enemySeparationIterations:" + EnemySeparationIterations + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed partial class LevelConfig : Luban.BeanBase
|
|||||||
public LevelConfig(ByteBuf _buf)
|
public LevelConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
Id = _buf.ReadInt();
|
Id = _buf.ReadInt();
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);EnemySpawnConfigs = new System.Collections.Generic.List<enemySpawnConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { enemySpawnConfig _e0; _e0 = enemySpawnConfig.DeserializeenemySpawnConfig(_buf); EnemySpawnConfigs.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);EnemySpawnConfigs = new System.Collections.Generic.List<EnemySpawnConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { EnemySpawnConfig _e0; _e0 = EnemySpawnConfig.DeserializeEnemySpawnConfig(_buf); EnemySpawnConfigs.Add(_e0);}}
|
||||||
Duration = _buf.ReadInt();
|
Duration = _buf.ReadInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ public sealed partial class LevelConfig : Luban.BeanBase
|
|||||||
/// 关卡id
|
/// 关卡id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Id;
|
public readonly int Id;
|
||||||
public readonly System.Collections.Generic.List<enemySpawnConfig> EnemySpawnConfigs;
|
public readonly System.Collections.Generic.List<EnemySpawnConfig> EnemySpawnConfigs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 关卡时间
|
/// 关卡时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public sealed partial class LevelUpRewardConfig : Luban.BeanBase
|
|||||||
Id = _buf.ReadInt();
|
Id = _buf.ReadInt();
|
||||||
Title = _buf.ReadString();
|
Title = _buf.ReadString();
|
||||||
Rarity = (Rarity)_buf.ReadInt();
|
Rarity = (Rarity)_buf.ReadInt();
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Modifiers = new System.Collections.Generic.List<modifier>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { modifier _e0; _e0 = modifier.Deserializemodifier(_buf); Modifiers.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);StatModifierConfigs = new System.Collections.Generic.List<StatModifierConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { StatModifierConfig _e0; _e0 = StatModifierConfig.DeserializeStatModifierConfig(_buf); StatModifierConfigs.Add(_e0);}}
|
||||||
Sprite = _buf.ReadString();
|
Sprite = _buf.ReadString();
|
||||||
Sprite_Ref = null;
|
Sprite_Ref = null;
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ public sealed partial class LevelUpRewardConfig : Luban.BeanBase
|
|||||||
/// 道具品质
|
/// 道具品质
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Rarity Rarity;
|
public readonly Rarity Rarity;
|
||||||
public readonly System.Collections.Generic.List<modifier> Modifiers;
|
public readonly System.Collections.Generic.List<StatModifierConfig> StatModifierConfigs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图标
|
/// 图标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,7 +56,7 @@ public sealed partial class LevelUpRewardConfig : Luban.BeanBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var _e in Modifiers) { _e?.ResolveRef(tables); }
|
foreach (var _e in StatModifierConfigs) { _e?.ResolveRef(tables); }
|
||||||
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ public sealed partial class LevelUpRewardConfig : Luban.BeanBase
|
|||||||
+ "id:" + Id + ","
|
+ "id:" + Id + ","
|
||||||
+ "title:" + Title + ","
|
+ "title:" + Title + ","
|
||||||
+ "rarity:" + Rarity + ","
|
+ "rarity:" + Rarity + ","
|
||||||
+ "modifiers:" + Luban.StringUtil.CollectionToString(Modifiers) + ","
|
+ "statModifierConfigs:" + Luban.StringUtil.CollectionToString(StatModifierConfigs) + ","
|
||||||
+ "sprite:" + Sprite + ","
|
+ "sprite:" + Sprite + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 修饰器类型
|
||||||
|
/// </summary>
|
||||||
|
public enum ModifierType : int
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 固定值
|
||||||
|
/// </summary>
|
||||||
|
Flat = 100,
|
||||||
|
/// <summary>
|
||||||
|
/// 百分比累加
|
||||||
|
/// </summary>
|
||||||
|
PercentAdd = 200,
|
||||||
|
/// <summary>
|
||||||
|
/// 最终百分比独立乘区
|
||||||
|
/// </summary>
|
||||||
|
PercentMult = 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a95015722b22de41a817f1d17e7d560
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -20,7 +20,7 @@ public sealed partial class PropConfig : Luban.BeanBase
|
|||||||
Name = _buf.ReadString();
|
Name = _buf.ReadString();
|
||||||
Rarity = (Rarity)_buf.ReadInt();
|
Rarity = (Rarity)_buf.ReadInt();
|
||||||
Price = _buf.ReadInt();
|
Price = _buf.ReadInt();
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Modifiers = new System.Collections.Generic.List<modifier>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { modifier _e0; _e0 = modifier.Deserializemodifier(_buf); Modifiers.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);StatModifierConfigs = new System.Collections.Generic.List<StatModifierConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { StatModifierConfig _e0; _e0 = StatModifierConfig.DeserializeStatModifierConfig(_buf); StatModifierConfigs.Add(_e0);}}
|
||||||
Sprite = _buf.ReadString();
|
Sprite = _buf.ReadString();
|
||||||
Sprite_Ref = null;
|
Sprite_Ref = null;
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ public sealed partial class PropConfig : Luban.BeanBase
|
|||||||
/// 道具价格
|
/// 道具价格
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Price;
|
public readonly int Price;
|
||||||
public readonly System.Collections.Generic.List<modifier> Modifiers;
|
public readonly System.Collections.Generic.List<StatModifierConfig> StatModifierConfigs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 图标
|
/// 图标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,7 +62,7 @@ public sealed partial class PropConfig : Luban.BeanBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var _e in Modifiers) { _e?.ResolveRef(tables); }
|
foreach (var _e in StatModifierConfigs) { _e?.ResolveRef(tables); }
|
||||||
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ public sealed partial class PropConfig : Luban.BeanBase
|
|||||||
+ "name:" + Name + ","
|
+ "name:" + Name + ","
|
||||||
+ "rarity:" + Rarity + ","
|
+ "rarity:" + Rarity + ","
|
||||||
+ "price:" + Price + ","
|
+ "price:" + Price + ","
|
||||||
+ "modifiers:" + Luban.StringUtil.CollectionToString(Modifiers) + ","
|
+ "statModifierConfigs:" + Luban.StringUtil.CollectionToString(StatModifierConfigs) + ","
|
||||||
+ "sprite:" + Sprite + ","
|
+ "sprite:" + Sprite + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public sealed partial class RoleConfig : Luban.BeanBase
|
|||||||
MaxHealth = _buf.ReadInt();
|
MaxHealth = _buf.ReadInt();
|
||||||
Speed = _buf.ReadFloat();
|
Speed = _buf.ReadFloat();
|
||||||
Coin = _buf.ReadInt();
|
Coin = _buf.ReadInt();
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Modifiers = new System.Collections.Generic.List<modifier>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { modifier _e0; _e0 = modifier.Deserializemodifier(_buf); Modifiers.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);StatModifierConfigs = new System.Collections.Generic.List<StatModifierConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { StatModifierConfig _e0; _e0 = StatModifierConfig.DeserializeStatModifierConfig(_buf); StatModifierConfigs.Add(_e0);}}
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);InitialItems = new System.Collections.Generic.List<int>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { int _e0; _e0 = _buf.ReadInt(); InitialItems.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);InitialItems = new System.Collections.Generic.List<int>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { int _e0; _e0 = _buf.ReadInt(); InitialItems.Add(_e0);}}
|
||||||
Prefab = _buf.ReadString();
|
Prefab = _buf.ReadString();
|
||||||
Prefab_Ref = null;
|
Prefab_Ref = null;
|
||||||
@@ -38,9 +38,9 @@ public sealed partial class RoleConfig : Luban.BeanBase
|
|||||||
public readonly float Speed;
|
public readonly float Speed;
|
||||||
public readonly int Coin;
|
public readonly int Coin;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// key
|
/// statType
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly System.Collections.Generic.List<modifier> Modifiers;
|
public readonly System.Collections.Generic.List<StatModifierConfig> StatModifierConfigs;
|
||||||
public readonly System.Collections.Generic.List<int> InitialItems;
|
public readonly System.Collections.Generic.List<int> InitialItems;
|
||||||
public readonly string Prefab;
|
public readonly string Prefab;
|
||||||
public EntityConfig Prefab_Ref;
|
public EntityConfig Prefab_Ref;
|
||||||
@@ -56,7 +56,7 @@ public sealed partial class RoleConfig : Luban.BeanBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var _e in Modifiers) { _e?.ResolveRef(tables); }
|
foreach (var _e in StatModifierConfigs) { _e?.ResolveRef(tables); }
|
||||||
|
|
||||||
Prefab_Ref = tables.TbEntityConfig.GetOrDefault(Prefab);
|
Prefab_Ref = tables.TbEntityConfig.GetOrDefault(Prefab);
|
||||||
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
||||||
@@ -69,7 +69,7 @@ public sealed partial class RoleConfig : Luban.BeanBase
|
|||||||
+ "maxHealth:" + MaxHealth + ","
|
+ "maxHealth:" + MaxHealth + ","
|
||||||
+ "speed:" + Speed + ","
|
+ "speed:" + Speed + ","
|
||||||
+ "coin:" + Coin + ","
|
+ "coin:" + Coin + ","
|
||||||
+ "modifiers:" + Luban.StringUtil.CollectionToString(Modifiers) + ","
|
+ "statModifierConfigs:" + Luban.StringUtil.CollectionToString(StatModifierConfigs) + ","
|
||||||
+ "initialItems:" + Luban.StringUtil.CollectionToString(InitialItems) + ","
|
+ "initialItems:" + Luban.StringUtil.CollectionToString(InitialItems) + ","
|
||||||
+ "prefab:" + Prefab + ","
|
+ "prefab:" + Prefab + ","
|
||||||
+ "sprite:" + Sprite + ","
|
+ "sprite:" + Sprite + ","
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Luban;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public sealed partial class StatConfig : Luban.BeanBase
|
||||||
|
{
|
||||||
|
public StatConfig(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
StatType = (StatType)_buf.ReadInt();
|
||||||
|
Name = _buf.ReadString();
|
||||||
|
DefaultBaseValue = _buf.ReadFloat();
|
||||||
|
HasMin = _buf.ReadBool();
|
||||||
|
MinValue = _buf.ReadFloat();
|
||||||
|
HasMax = _buf.ReadBool();
|
||||||
|
MaxValue = _buf.ReadFloat();
|
||||||
|
IsPercentDisplay = _buf.ReadBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StatConfig DeserializeStatConfig(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
return new StatConfig(_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 属性类型
|
||||||
|
/// </summary>
|
||||||
|
public readonly StatType StatType;
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
/// <summary>
|
||||||
|
/// 默认值
|
||||||
|
/// </summary>
|
||||||
|
public readonly float DefaultBaseValue;
|
||||||
|
/// <summary>
|
||||||
|
/// 是否有最小值
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool HasMin;
|
||||||
|
/// <summary>
|
||||||
|
/// 最小值
|
||||||
|
/// </summary>
|
||||||
|
public readonly float MinValue;
|
||||||
|
/// <summary>
|
||||||
|
/// 是否有最大值
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool HasMax;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大值
|
||||||
|
/// </summary>
|
||||||
|
public readonly float MaxValue;
|
||||||
|
/// <summary>
|
||||||
|
/// UI 文本是否百分比
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsPercentDisplay;
|
||||||
|
|
||||||
|
public const int __ID__ = 807092950;
|
||||||
|
public override int GetTypeId() => __ID__;
|
||||||
|
|
||||||
|
public void ResolveRef(Tables tables)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "{ "
|
||||||
|
+ "statType:" + StatType + ","
|
||||||
|
+ "name:" + Name + ","
|
||||||
|
+ "defaultBaseValue:" + DefaultBaseValue + ","
|
||||||
|
+ "hasMin:" + HasMin + ","
|
||||||
|
+ "minValue:" + MinValue + ","
|
||||||
|
+ "hasMax:" + HasMax + ","
|
||||||
|
+ "maxValue:" + MaxValue + ","
|
||||||
|
+ "isPercentDisplay:" + IsPercentDisplay + ","
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2dfe101008e1ded4ea35681a5a656d2f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+14
-14
@@ -15,34 +15,34 @@ namespace SepCore.Definition
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修饰器
|
/// 修饰器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class modifier : Luban.BeanBase
|
public sealed partial class StatModifierConfig : Luban.BeanBase
|
||||||
{
|
{
|
||||||
public modifier(ByteBuf _buf)
|
public StatModifierConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
Key = (StatType)_buf.ReadInt();
|
StatType = (StatType)_buf.ReadInt();
|
||||||
Value = _buf.ReadInt();
|
Value = _buf.ReadFloat();
|
||||||
IsPercent = _buf.ReadBool();
|
ModifierType = (ModifierType)_buf.ReadInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static modifier Deserializemodifier(ByteBuf _buf)
|
public static StatModifierConfig DeserializeStatModifierConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
return new modifier(_buf);
|
return new StatModifierConfig(_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 属性类型
|
/// 属性类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly StatType Key;
|
public readonly StatType StatType;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 值
|
/// 值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Value;
|
public readonly float Value;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否为百分比
|
/// 修饰器类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly bool IsPercent;
|
public readonly ModifierType ModifierType;
|
||||||
|
|
||||||
public const int __ID__ = -615513385;
|
public const int __ID__ = 235118733;
|
||||||
public override int GetTypeId() => __ID__;
|
public override int GetTypeId() => __ID__;
|
||||||
|
|
||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
@@ -55,9 +55,9 @@ public sealed partial class modifier : Luban.BeanBase
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "{ "
|
return "{ "
|
||||||
+ "key:" + Key + ","
|
+ "statType:" + StatType + ","
|
||||||
+ "value:" + Value + ","
|
+ "value:" + Value + ","
|
||||||
+ "isPercent:" + IsPercent + ","
|
+ "modifierType:" + ModifierType + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,39 +23,67 @@ namespace SepCore.Definition
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 最大生命
|
/// 最大生命
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MaxHealth = 1,
|
MaxHp = 1,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移动速度
|
/// 生命恢复
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MovementSpeed = 2,
|
HpRegen = 2,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 攻击力
|
/// 生命窃取
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Attack = 3,
|
LifeSteal = 3,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 防御力
|
/// 伤害
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Defense = 4,
|
DamagePercent = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// 近战伤害
|
||||||
|
/// </summary>
|
||||||
|
MeleeDamage = 5,
|
||||||
|
/// <summary>
|
||||||
|
/// 远程伤害
|
||||||
|
/// </summary>
|
||||||
|
RangedDamage = 6,
|
||||||
|
/// <summary>
|
||||||
|
/// 元素伤害
|
||||||
|
/// </summary>
|
||||||
|
ElementalDamage = 7,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 攻击速度
|
/// 攻击速度
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AttackSpeed = 5,
|
AttackSpeed = 8,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 暴击率
|
/// 暴击率
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Critical = 6,
|
CritChance = 9,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 暴击伤害
|
/// 工程学
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CriticalDamage = 7,
|
Engineering = 10,
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击范围
|
||||||
|
/// </summary>
|
||||||
|
Range = 11,
|
||||||
|
/// <summary>
|
||||||
|
/// 护甲
|
||||||
|
/// </summary>
|
||||||
|
Armor = 12,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 闪避率
|
/// 闪避率
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Dodge = 8,
|
Dodge = 13,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自动吸收范围
|
/// 移动速度
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AbsorbRange = 9,
|
MoveSpeed = 14,
|
||||||
|
/// <summary>
|
||||||
|
/// 收获
|
||||||
|
/// </summary>
|
||||||
|
Harvesting = 15,
|
||||||
|
/// <summary>
|
||||||
|
/// 幸运
|
||||||
|
/// </summary>
|
||||||
|
Luck = 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public partial class Tables
|
|||||||
/// 实体配置
|
/// 实体配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TbEntityConfig TbEntityConfig {get; }
|
public TbEntityConfig TbEntityConfig {get; }
|
||||||
|
public TbFormatText TbFormatText {get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局零散配置(单例)
|
/// 全局零散配置(单例)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -60,11 +61,16 @@ public partial class Tables
|
|||||||
/// 界面配置
|
/// 界面配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TbWeaponConfig TbWeaponConfig {get; }
|
public TbWeaponConfig TbWeaponConfig {get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 角色属性配置
|
||||||
|
/// </summary>
|
||||||
|
public TbStatConfig TbStatConfig {get; }
|
||||||
|
|
||||||
public Tables(System.Func<string, ByteBuf> loader)
|
public Tables(System.Func<string, ByteBuf> loader)
|
||||||
{
|
{
|
||||||
TbEnemyConfig = new TbEnemyConfig(loader("tbenemyconfig"));
|
TbEnemyConfig = new TbEnemyConfig(loader("tbenemyconfig"));
|
||||||
TbEntityConfig = new TbEntityConfig(loader("tbentityconfig"));
|
TbEntityConfig = new TbEntityConfig(loader("tbentityconfig"));
|
||||||
|
TbFormatText = new TbFormatText(loader("tbformattext"));
|
||||||
TbGlobalConfig = new TbGlobalConfig(loader("tbglobalconfig"));
|
TbGlobalConfig = new TbGlobalConfig(loader("tbglobalconfig"));
|
||||||
TbGoodsConfig = new TbGoodsConfig(loader("tbgoodsconfig"));
|
TbGoodsConfig = new TbGoodsConfig(loader("tbgoodsconfig"));
|
||||||
TbLevelConfig = new TbLevelConfig(loader("tblevelconfig"));
|
TbLevelConfig = new TbLevelConfig(loader("tblevelconfig"));
|
||||||
@@ -80,6 +86,7 @@ public partial class Tables
|
|||||||
TbUIFormConfig = new TbUIFormConfig(loader("tbuiformconfig"));
|
TbUIFormConfig = new TbUIFormConfig(loader("tbuiformconfig"));
|
||||||
TbUISoundConfig = new TbUISoundConfig(loader("tbuisoundconfig"));
|
TbUISoundConfig = new TbUISoundConfig(loader("tbuisoundconfig"));
|
||||||
TbWeaponConfig = new TbWeaponConfig(loader("tbweaponconfig"));
|
TbWeaponConfig = new TbWeaponConfig(loader("tbweaponconfig"));
|
||||||
|
TbStatConfig = new TbStatConfig(loader("tbstatconfig"));
|
||||||
ResolveRef();
|
ResolveRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +94,7 @@ public partial class Tables
|
|||||||
{
|
{
|
||||||
TbEnemyConfig.ResolveRef(this);
|
TbEnemyConfig.ResolveRef(this);
|
||||||
TbEntityConfig.ResolveRef(this);
|
TbEntityConfig.ResolveRef(this);
|
||||||
|
TbFormatText.ResolveRef(this);
|
||||||
TbGlobalConfig.ResolveRef(this);
|
TbGlobalConfig.ResolveRef(this);
|
||||||
TbGoodsConfig.ResolveRef(this);
|
TbGoodsConfig.ResolveRef(this);
|
||||||
TbLevelConfig.ResolveRef(this);
|
TbLevelConfig.ResolveRef(this);
|
||||||
@@ -102,6 +110,7 @@ public partial class Tables
|
|||||||
TbUIFormConfig.ResolveRef(this);
|
TbUIFormConfig.ResolveRef(this);
|
||||||
TbUISoundConfig.ResolveRef(this);
|
TbUISoundConfig.ResolveRef(this);
|
||||||
TbWeaponConfig.ResolveRef(this);
|
TbWeaponConfig.ResolveRef(this);
|
||||||
|
TbStatConfig.ResolveRef(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Luban;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
public partial class TbFormatText
|
||||||
|
{
|
||||||
|
private readonly System.Collections.Generic.Dictionary<string, FormatText> _dataMap;
|
||||||
|
private readonly System.Collections.Generic.List<FormatText> _dataList;
|
||||||
|
|
||||||
|
public TbFormatText(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
_dataMap = new System.Collections.Generic.Dictionary<string, FormatText>();
|
||||||
|
_dataList = new System.Collections.Generic.List<FormatText>();
|
||||||
|
|
||||||
|
for(int n = _buf.ReadSize() ; n > 0 ; --n)
|
||||||
|
{
|
||||||
|
FormatText _v;
|
||||||
|
_v = FormatText.DeserializeFormatText(_buf);
|
||||||
|
_dataList.Add(_v);
|
||||||
|
_dataMap.Add(_v.Name, _v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public System.Collections.Generic.Dictionary<string, FormatText> DataMap => _dataMap;
|
||||||
|
public System.Collections.Generic.List<FormatText> DataList => _dataList;
|
||||||
|
|
||||||
|
public FormatText GetOrDefault(string key) => _dataMap.TryGetValue(key, out var v) ? v : null;
|
||||||
|
public FormatText Get(string key) => _dataMap[key];
|
||||||
|
public FormatText this[string key] => _dataMap[key];
|
||||||
|
|
||||||
|
public void ResolveRef(Tables tables)
|
||||||
|
{
|
||||||
|
foreach(var _v in _dataList)
|
||||||
|
{
|
||||||
|
_v.ResolveRef(tables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8574165ef5ddc674fb6c69eaab38a63e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -34,6 +34,138 @@ public partial class TbGlobalConfig
|
|||||||
/// 最大持有武器数量
|
/// 最大持有武器数量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int WeaponCapacity => _data.WeaponCapacity;
|
public int WeaponCapacity => _data.WeaponCapacity;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大持有道具数量
|
||||||
|
/// </summary>
|
||||||
|
public int PropMaxCount => _data.PropMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家实体
|
||||||
|
/// </summary>
|
||||||
|
public string PlayerEntity => _data.PlayerEntity;
|
||||||
|
/// <summary>
|
||||||
|
/// 自动收集速度
|
||||||
|
/// </summary>
|
||||||
|
public float AbsorbSpeed => _data.AbsorbSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 自动收集距离
|
||||||
|
/// </summary>
|
||||||
|
public float CollectDistance => _data.CollectDistance;
|
||||||
|
/// <summary>
|
||||||
|
/// 默认吸收距离
|
||||||
|
/// </summary>
|
||||||
|
public float DefaultAbsorbRange => _data.DefaultAbsorbRange;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器回收价比例
|
||||||
|
/// </summary>
|
||||||
|
public float WeaponRecycleRate => _data.WeaponRecycleRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 商店商品数量
|
||||||
|
/// </summary>
|
||||||
|
public int ShopGoodsCount => _data.ShopGoodsCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品价格浮动
|
||||||
|
/// </summary>
|
||||||
|
public float ShopPriceRandomPercent => _data.ShopPriceRandomPercent;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品最低价
|
||||||
|
/// </summary>
|
||||||
|
public int ShopMinPrice => _data.ShopMinPrice;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励刷新基本价格
|
||||||
|
/// </summary>
|
||||||
|
public int LevelUpRefreshPriceBase => _data.LevelUpRefreshPriceBase;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励候选数
|
||||||
|
/// </summary>
|
||||||
|
public int LevelUpRewardCount => _data.LevelUpRewardCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大敌人数量
|
||||||
|
/// </summary>
|
||||||
|
public int SpawnEnemyMaxCount => _data.SpawnEnemyMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人生成最小距离
|
||||||
|
/// </summary>
|
||||||
|
public float EnemySpawnDistanceMin => _data.EnemySpawnDistanceMin;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人生成最大距离
|
||||||
|
/// </summary>
|
||||||
|
public float EnemySpawnDistanceMax => _data.EnemySpawnDistanceMax;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器旋转速度
|
||||||
|
/// </summary>
|
||||||
|
public float WeaponRotateSpeed => _data.WeaponRotateSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器基本暴击率
|
||||||
|
/// </summary>
|
||||||
|
public float WeaponBaseCrit => _data.WeaponBaseCrit;
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家对象组名
|
||||||
|
/// </summary>
|
||||||
|
public string PlayerGroupName => _data.PlayerGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人对象组名
|
||||||
|
/// </summary>
|
||||||
|
public string EnemyGroupName => _data.EnemyGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物对象组名
|
||||||
|
/// </summary>
|
||||||
|
public string ProjectileGroupName => _data.ProjectileGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 掉落物对象组名
|
||||||
|
/// </summary>
|
||||||
|
public string DropGroupName => _data.DropGroupName;
|
||||||
|
/// <summary>
|
||||||
|
/// 特效对象组名
|
||||||
|
/// </summary>
|
||||||
|
public string EffectGroupNmae => _data.EffectGroupNmae;
|
||||||
|
/// <summary>
|
||||||
|
/// 最小伤害
|
||||||
|
/// </summary>
|
||||||
|
public int MinDamage => _data.MinDamage;
|
||||||
|
/// <summary>
|
||||||
|
/// 闪避率上限
|
||||||
|
/// </summary>
|
||||||
|
public int MaxDodgeRate => _data.MaxDodgeRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 最小刷怪速度倍数
|
||||||
|
/// </summary>
|
||||||
|
public int MinSpawnRateScale => _data.MinSpawnRateScale;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物生命周期
|
||||||
|
/// </summary>
|
||||||
|
public int ProjectileLifeTime => _data.ProjectileLifeTime;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物生成高度
|
||||||
|
/// </summary>
|
||||||
|
public int ProjectileHeightOffset => _data.ProjectileHeightOffset;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物移动速度
|
||||||
|
/// </summary>
|
||||||
|
public int ProjectileSpeed => _data.ProjectileSpeed;
|
||||||
|
/// <summary>
|
||||||
|
/// 投射物实体
|
||||||
|
/// </summary>
|
||||||
|
public string ProjectileEntity => _data.ProjectileEntity;
|
||||||
|
/// <summary>
|
||||||
|
/// 伤害数字高度偏移
|
||||||
|
/// </summary>
|
||||||
|
public int DamageTextYOffset => _data.DamageTextYOffset;
|
||||||
|
/// <summary>
|
||||||
|
/// 实体生成超时时间
|
||||||
|
/// </summary>
|
||||||
|
public int EntityAsyncTimeout => _data.EntityAsyncTimeout;
|
||||||
|
/// <summary>
|
||||||
|
/// 暴击伤害倍率
|
||||||
|
/// </summary>
|
||||||
|
public float CritMultiplier => _data.CritMultiplier;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人碰撞半径
|
||||||
|
/// </summary>
|
||||||
|
public int EnemyBodyRadius => _data.EnemyBodyRadius;
|
||||||
|
/// <summary>
|
||||||
|
/// 敌人分离迭代次数
|
||||||
|
/// </summary>
|
||||||
|
public int EnemySeparationIterations => _data.EnemySeparationIterations;
|
||||||
|
|
||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Luban;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 角色属性配置
|
||||||
|
/// </summary>
|
||||||
|
public partial class TbStatConfig
|
||||||
|
{
|
||||||
|
private readonly System.Collections.Generic.Dictionary<StatType, StatConfig> _dataMap;
|
||||||
|
private readonly System.Collections.Generic.List<StatConfig> _dataList;
|
||||||
|
|
||||||
|
public TbStatConfig(ByteBuf _buf)
|
||||||
|
{
|
||||||
|
_dataMap = new System.Collections.Generic.Dictionary<StatType, StatConfig>();
|
||||||
|
_dataList = new System.Collections.Generic.List<StatConfig>();
|
||||||
|
|
||||||
|
for(int n = _buf.ReadSize() ; n > 0 ; --n)
|
||||||
|
{
|
||||||
|
StatConfig _v;
|
||||||
|
_v = StatConfig.DeserializeStatConfig(_buf);
|
||||||
|
_dataList.Add(_v);
|
||||||
|
_dataMap.Add(_v.StatType, _v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public System.Collections.Generic.Dictionary<StatType, StatConfig> DataMap => _dataMap;
|
||||||
|
public System.Collections.Generic.List<StatConfig> DataList => _dataList;
|
||||||
|
|
||||||
|
public StatConfig GetOrDefault(StatType key) => _dataMap.TryGetValue(key, out var v) ? v : null;
|
||||||
|
public StatConfig Get(StatType key) => _dataMap[key];
|
||||||
|
public StatConfig this[StatType key] => _dataMap[key];
|
||||||
|
|
||||||
|
public void ResolveRef(Tables tables)
|
||||||
|
{
|
||||||
|
foreach(var _v in _dataList)
|
||||||
|
{
|
||||||
|
_v.ResolveRef(tables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 54c58e47ce828d246ae57a00a9704563
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -21,11 +21,12 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
|||||||
Rarity = (Rarity)_buf.ReadInt();
|
Rarity = (Rarity)_buf.ReadInt();
|
||||||
Price = _buf.ReadInt();
|
Price = _buf.ReadInt();
|
||||||
Attack = _buf.ReadInt();
|
Attack = _buf.ReadInt();
|
||||||
Cooldown = _buf.ReadInt();
|
Cooldown = _buf.ReadFloat();
|
||||||
AttackRange = _buf.ReadInt();
|
DamageType = (DamageType)_buf.ReadInt();
|
||||||
|
AttackRange = _buf.ReadFloat();
|
||||||
AttackSoundId = _buf.ReadInt();
|
AttackSoundId = _buf.ReadInt();
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Parameters = new System.Collections.Generic.Dictionary<ParamType, int>(n0 * 3 / 2);for(var i0 = 0 ; i0 < n0 ; i0++) { ParamType _k0; _k0 = (ParamType)_buf.ReadInt(); int _v0; _v0 = _buf.ReadInt(); Parameters.Add(_k0, _v0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Parameters = new System.Collections.Generic.Dictionary<ParamType, float>(n0 * 3 / 2);for(var i0 = 0 ; i0 < n0 ; i0++) { ParamType _k0; _k0 = (ParamType)_buf.ReadInt(); float _v0; _v0 = _buf.ReadFloat(); Parameters.Add(_k0, _v0);}}
|
||||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Modifiers = new System.Collections.Generic.List<modifier>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { modifier _e0; _e0 = modifier.Deserializemodifier(_buf); Modifiers.Add(_e0);}}
|
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);StatModifierConfigs = new System.Collections.Generic.List<StatModifierConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { StatModifierConfig _e0; _e0 = StatModifierConfig.DeserializeStatModifierConfig(_buf); StatModifierConfigs.Add(_e0);}}
|
||||||
Prefab = _buf.ReadString();
|
Prefab = _buf.ReadString();
|
||||||
Prefab_Ref = null;
|
Prefab_Ref = null;
|
||||||
Sprite = _buf.ReadString();
|
Sprite = _buf.ReadString();
|
||||||
@@ -60,11 +61,15 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 冷却
|
/// 冷却
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Cooldown;
|
public readonly float Cooldown;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 范围
|
/// 伤害类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int AttackRange;
|
public readonly DamageType DamageType;
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击范围
|
||||||
|
/// </summary>
|
||||||
|
public readonly float AttackRange;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 攻击音效编号
|
/// 攻击音效编号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -72,8 +77,8 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数类型
|
/// 参数类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly System.Collections.Generic.Dictionary<ParamType, int> Parameters;
|
public readonly System.Collections.Generic.Dictionary<ParamType, float> Parameters;
|
||||||
public readonly System.Collections.Generic.List<modifier> Modifiers;
|
public readonly System.Collections.Generic.List<StatModifierConfig> StatModifierConfigs;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 预制体
|
/// 预制体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -99,7 +104,8 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var _e in Modifiers) { _e?.ResolveRef(tables); }
|
|
||||||
|
foreach (var _e in StatModifierConfigs) { _e?.ResolveRef(tables); }
|
||||||
Prefab_Ref = tables.TbEntityConfig.GetOrDefault(Prefab);
|
Prefab_Ref = tables.TbEntityConfig.GetOrDefault(Prefab);
|
||||||
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
Sprite_Ref = tables.TbSpriteConfig.GetOrDefault(Sprite);
|
||||||
}
|
}
|
||||||
@@ -113,10 +119,11 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
|||||||
+ "price:" + Price + ","
|
+ "price:" + Price + ","
|
||||||
+ "attack:" + Attack + ","
|
+ "attack:" + Attack + ","
|
||||||
+ "cooldown:" + Cooldown + ","
|
+ "cooldown:" + Cooldown + ","
|
||||||
|
+ "damageType:" + DamageType + ","
|
||||||
+ "attackRange:" + AttackRange + ","
|
+ "attackRange:" + AttackRange + ","
|
||||||
+ "attackSoundId:" + AttackSoundId + ","
|
+ "attackSoundId:" + AttackSoundId + ","
|
||||||
+ "parameters:" + Luban.StringUtil.CollectionToString(Parameters) + ","
|
+ "parameters:" + Luban.StringUtil.CollectionToString(Parameters) + ","
|
||||||
+ "modifiers:" + Luban.StringUtil.CollectionToString(Modifiers) + ","
|
+ "statModifierConfigs:" + Luban.StringUtil.CollectionToString(StatModifierConfigs) + ","
|
||||||
+ "prefab:" + Prefab + ","
|
+ "prefab:" + Prefab + ","
|
||||||
+ "sprite:" + Sprite + ","
|
+ "sprite:" + Sprite + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace SepCore.Definition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 武器状态
|
||||||
|
/// </summary>
|
||||||
|
public enum WeaponStateType : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 无
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 攻击
|
||||||
|
/// </summary>
|
||||||
|
Attack = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 待机
|
||||||
|
/// </summary>
|
||||||
|
Idle = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索
|
||||||
|
/// </summary>
|
||||||
|
Seek = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// 瞄准
|
||||||
|
/// </summary>
|
||||||
|
Aim = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1065e2570786f184c9553db06202a6a7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -15,18 +15,18 @@ namespace SepCore.Definition
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 出怪配置
|
/// 出怪配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class enemySpawnConfig : Luban.BeanBase
|
public sealed partial class EnemySpawnConfig : Luban.BeanBase
|
||||||
{
|
{
|
||||||
public enemySpawnConfig(ByteBuf _buf)
|
public EnemySpawnConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
EnemyType = (EnemyType)_buf.ReadInt();
|
EnemyType = (EnemyType)_buf.ReadInt();
|
||||||
EnemyCount = _buf.ReadInt();
|
EnemyCount = _buf.ReadInt();
|
||||||
Interval = _buf.ReadInt();
|
Interval = _buf.ReadInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enemySpawnConfig DeserializeenemySpawnConfig(ByteBuf _buf)
|
public static EnemySpawnConfig DeserializeEnemySpawnConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
return new enemySpawnConfig(_buf);
|
return new EnemySpawnConfig(_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,7 +42,7 @@ public sealed partial class enemySpawnConfig : Luban.BeanBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int Interval;
|
public readonly int Interval;
|
||||||
|
|
||||||
public const int __ID__ = -1182208523;
|
public const int __ID__ = -2024984043;
|
||||||
public override int GetTypeId() => __ID__;
|
public override int GetTypeId() => __ID__;
|
||||||
|
|
||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ namespace SepCore.Editor
|
|||||||
EditorGUILayout.LabelField("Title", selectedProp.Name);
|
EditorGUILayout.LabelField("Title", selectedProp.Name);
|
||||||
EditorGUILayout.LabelField("Rarity", selectedProp.Rarity.ToString());
|
EditorGUILayout.LabelField("Rarity", selectedProp.Rarity.ToString());
|
||||||
EditorGUILayout.LabelField("Description");
|
EditorGUILayout.LabelField("Description");
|
||||||
EditorGUILayout.HelpBox(ItemDescUtility.CreatePropDescription(selectedProp.Modifiers), MessageType.None);
|
EditorGUILayout.HelpBox(ItemDescUtility.CreatePropDescription(selectedProp.StatModifierConfigs), MessageType.None);
|
||||||
EditorGUILayout.EndScrollView();
|
EditorGUILayout.EndScrollView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 32bf4a12bd0ac2847b19bbb76b7c3dd5
|
guid: 3c650e4873fd47548b7df088c59adfad
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 337bc362617c8994fb0248fa5d5fd4a8
|
guid: 10f3a6fad1dfdb444a2e57be170f9967
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(DynamicJoystick))]
|
||||||
|
public class DynamicJoystickEditor : JoystickEditor
|
||||||
|
{
|
||||||
|
private SerializedProperty moveThreshold;
|
||||||
|
|
||||||
|
protected override void OnEnable()
|
||||||
|
{
|
||||||
|
base.OnEnable();
|
||||||
|
moveThreshold = serializedObject.FindProperty("moveThreshold");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
|
||||||
|
if (background != null)
|
||||||
|
{
|
||||||
|
RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
|
||||||
|
backgroundRect.anchorMax = Vector2.zero;
|
||||||
|
backgroundRect.anchorMin = Vector2.zero;
|
||||||
|
backgroundRect.pivot = center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DrawValues()
|
||||||
|
{
|
||||||
|
base.DrawValues();
|
||||||
|
EditorGUILayout.PropertyField(moveThreshold, new GUIContent("Move Threshold", "The distance away from the center input has to be before the joystick begins to move."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52071c88a467b46438b3cbf159bf988b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(FloatingJoystick))]
|
||||||
|
public class FloatingJoystickEditor : JoystickEditor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
|
||||||
|
if (background != null)
|
||||||
|
{
|
||||||
|
RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
|
||||||
|
backgroundRect.anchorMax = Vector2.zero;
|
||||||
|
backgroundRect.anchorMin = Vector2.zero;
|
||||||
|
backgroundRect.pivot = center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 614d06243900d764f9c50b4da2c950a3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(Joystick), true)]
|
||||||
|
public class JoystickEditor : Editor
|
||||||
|
{
|
||||||
|
private SerializedProperty handleRange;
|
||||||
|
private SerializedProperty deadZone;
|
||||||
|
private SerializedProperty axisOptions;
|
||||||
|
private SerializedProperty snapX;
|
||||||
|
private SerializedProperty snapY;
|
||||||
|
protected SerializedProperty background;
|
||||||
|
private SerializedProperty handle;
|
||||||
|
|
||||||
|
protected Vector2 center = new Vector2(0.5f, 0.5f);
|
||||||
|
|
||||||
|
protected virtual void OnEnable()
|
||||||
|
{
|
||||||
|
handleRange = serializedObject.FindProperty("handleRange");
|
||||||
|
deadZone = serializedObject.FindProperty("deadZone");
|
||||||
|
axisOptions = serializedObject.FindProperty("axisOptions");
|
||||||
|
snapX = serializedObject.FindProperty("snapX");
|
||||||
|
snapY = serializedObject.FindProperty("snapY");
|
||||||
|
background = serializedObject.FindProperty("background");
|
||||||
|
handle = serializedObject.FindProperty("handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
DrawValues();
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
DrawComponents();
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
|
||||||
|
if(handle != null)
|
||||||
|
{
|
||||||
|
RectTransform handleRect = (RectTransform)handle.objectReferenceValue;
|
||||||
|
handleRect.anchorMax = center;
|
||||||
|
handleRect.anchorMin = center;
|
||||||
|
handleRect.pivot = center;
|
||||||
|
handleRect.anchoredPosition = Vector2.zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DrawValues()
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(handleRange, new GUIContent("Handle Range", "The distance the visual handle can move from the center of the joystick."));
|
||||||
|
EditorGUILayout.PropertyField(deadZone, new GUIContent("Dead Zone", "The distance away from the center input has to be before registering."));
|
||||||
|
EditorGUILayout.PropertyField(axisOptions, new GUIContent("Axis Options", "Which axes the joystick uses."));
|
||||||
|
EditorGUILayout.PropertyField(snapX, new GUIContent("Snap X", "Snap the horizontal input to a whole value."));
|
||||||
|
EditorGUILayout.PropertyField(snapY, new GUIContent("Snap Y", "Snap the vertical input to a whole value."));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DrawComponents()
|
||||||
|
{
|
||||||
|
EditorGUILayout.ObjectField(background, new GUIContent("Background", "The background's RectTransform component."));
|
||||||
|
EditorGUILayout.ObjectField(handle, new GUIContent("Handle", "The handle's RectTransform component."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d4b2b0d911a01a64ebfca9918e2b90b4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(VariableJoystick))]
|
||||||
|
public class VariableJoystickEditor : JoystickEditor
|
||||||
|
{
|
||||||
|
private SerializedProperty moveThreshold;
|
||||||
|
private SerializedProperty joystickType;
|
||||||
|
|
||||||
|
protected override void OnEnable()
|
||||||
|
{
|
||||||
|
base.OnEnable();
|
||||||
|
moveThreshold = serializedObject.FindProperty("moveThreshold");
|
||||||
|
joystickType = serializedObject.FindProperty("joystickType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
|
||||||
|
if (background != null)
|
||||||
|
{
|
||||||
|
RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
|
||||||
|
backgroundRect.pivot = center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DrawValues()
|
||||||
|
{
|
||||||
|
base.DrawValues();
|
||||||
|
EditorGUILayout.PropertyField(moveThreshold, new GUIContent("Move Threshold", "The distance away from the center input has to be before the joystick begins to move."));
|
||||||
|
EditorGUILayout.PropertyField(joystickType, new GUIContent("Joystick Type", "The type of joystick the variable joystick is current using."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 58b5a2db95af76c4c9d4116a09ec3697
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,961 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using SepCore.UI;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Callbacks;
|
||||||
|
using UnityEditor.Compilation;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.Editor
|
||||||
|
{
|
||||||
|
public static class UIAssetsTools
|
||||||
|
{
|
||||||
|
public const string GeneratedNamespace = "SepCore.UI";
|
||||||
|
public const string GeneratedDirectory = "Assets/GameMain/Scripts/Presentation";
|
||||||
|
|
||||||
|
private const string PendingGenerationKey = "UI.Serialization.PendingGeneration";
|
||||||
|
private static bool finalizeQueued;
|
||||||
|
|
||||||
|
private static readonly HashSet<string> CSharpKeywords = new HashSet<string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class",
|
||||||
|
"const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event",
|
||||||
|
"explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if",
|
||||||
|
"implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new",
|
||||||
|
"null", "object", "operator", "out", "override", "params", "private", "protected", "public",
|
||||||
|
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static",
|
||||||
|
"string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong",
|
||||||
|
"unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while"
|
||||||
|
};
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class PendingGenerationBatch
|
||||||
|
{
|
||||||
|
public List<PendingGeneration> entries = new List<PendingGeneration>();
|
||||||
|
public string primaryScriptPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class PendingGeneration
|
||||||
|
{
|
||||||
|
public string rootGlobalId;
|
||||||
|
public string prefabAssetPath;
|
||||||
|
public string prefabRootRelativePath;
|
||||||
|
public string scenePath;
|
||||||
|
public string hierarchyPath;
|
||||||
|
public string viewClassFullName;
|
||||||
|
public string formClassFullName;
|
||||||
|
public string viewScriptPath;
|
||||||
|
public string formScriptPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class GenerationUnit
|
||||||
|
{
|
||||||
|
public UISerializationRoot root;
|
||||||
|
public string viewClassName;
|
||||||
|
public string formClassName;
|
||||||
|
public string viewScriptPath;
|
||||||
|
public string formScriptPath;
|
||||||
|
public string viewSource;
|
||||||
|
public string formSource;
|
||||||
|
public bool viewChanged;
|
||||||
|
public bool formChanged;
|
||||||
|
|
||||||
|
public bool SourceChanged => viewChanged || formChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Binding
|
||||||
|
{
|
||||||
|
public Component component;
|
||||||
|
public string variableName;
|
||||||
|
public string csharpTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Assets/Create View Script", false, 2000)]
|
||||||
|
private static void GenerateFromAssetsMenu()
|
||||||
|
{
|
||||||
|
GenerateWithDialog(GetSelectedRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("Assets/Create View Script", true)]
|
||||||
|
private static bool ValidateAssetsMenu()
|
||||||
|
{
|
||||||
|
return GetSelectedRoot() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("GameObject/Create View Script", false, 30)]
|
||||||
|
private static void GenerateFromGameObjectMenu()
|
||||||
|
{
|
||||||
|
GenerateWithDialog(GetSelectedRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("GameObject/Create View Script", true)]
|
||||||
|
private static bool ValidateGameObjectMenu()
|
||||||
|
{
|
||||||
|
return GetSelectedRoot() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("CONTEXT/UISerializationRoot/Create View Script")]
|
||||||
|
private static void GenerateFromComponentMenu(MenuCommand command)
|
||||||
|
{
|
||||||
|
GenerateWithDialog(command.context as UISerializationRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GenerateWithDialog(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Generate(root, true);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
EditorUtility.DisplayDialog("Create UI View Script", exception.Message, "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Generate(UISerializationRoot root, bool confirmOverwrite)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Select a GameObject or Prefab with UISerializationRoot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UISerializationRoot> generationRoots = CollectGenerationRoots(root);
|
||||||
|
List<GenerationUnit> units = BuildGenerationUnits(generationRoots);
|
||||||
|
GenerationUnit primaryUnit = units[0];
|
||||||
|
|
||||||
|
List<string> changedExistingFiles = units
|
||||||
|
.Where(unit => unit.SourceChanged)
|
||||||
|
.SelectMany(unit => new[] { unit.viewScriptPath, unit.formScriptPath })
|
||||||
|
.Where(File.Exists)
|
||||||
|
.ToList();
|
||||||
|
if (confirmOverwrite && changedExistingFiles.Count > 0)
|
||||||
|
{
|
||||||
|
string message = "Overwrite the following generated View/Form scripts?\n\n" +
|
||||||
|
string.Join("\n", changedExistingFiles.ToArray());
|
||||||
|
if (!EditorUtility.DisplayDialog("Regenerate UI View/Form", message, "Overwrite", "Cancel"))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureFolder(GeneratedDirectory);
|
||||||
|
SaveDirtyRootAssets(units);
|
||||||
|
|
||||||
|
var pendingBatch = new PendingGenerationBatch
|
||||||
|
{
|
||||||
|
primaryScriptPath = primaryUnit.viewScriptPath
|
||||||
|
};
|
||||||
|
for (int i = 0; i < units.Count; i++)
|
||||||
|
{
|
||||||
|
pendingBatch.entries.Add(CreatePendingGeneration(units[i]));
|
||||||
|
}
|
||||||
|
SessionState.SetString(PendingGenerationKey, JsonUtility.ToJson(pendingBatch));
|
||||||
|
|
||||||
|
bool anySourceChanged = false;
|
||||||
|
for (int i = 0; i < units.Count; i++)
|
||||||
|
{
|
||||||
|
GenerationUnit unit = units[i];
|
||||||
|
if (!unit.SourceChanged)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureFolder(Path.GetDirectoryName(unit.viewScriptPath));
|
||||||
|
if (unit.viewChanged)
|
||||||
|
{
|
||||||
|
File.WriteAllText(unit.viewScriptPath, unit.viewSource, new UTF8Encoding(false));
|
||||||
|
anySourceChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unit.formChanged)
|
||||||
|
{
|
||||||
|
File.WriteAllText(unit.formScriptPath, unit.formSource, new UTF8Encoding(false));
|
||||||
|
anySourceChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anySourceChanged)
|
||||||
|
{
|
||||||
|
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnityEngine.Object scriptAsset = AssetDatabase.LoadAssetAtPath<MonoScript>(primaryUnit.viewScriptPath);
|
||||||
|
if (scriptAsset != null)
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(scriptAsset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anySourceChanged)
|
||||||
|
{
|
||||||
|
CompilationPipeline.RequestScriptCompilation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QueuePendingGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Generated " + units.Count + " UI View/Form script pair(s); primary script: " +
|
||||||
|
primaryUnit.viewScriptPath);
|
||||||
|
return primaryUnit.viewScriptPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetViewClassName(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetBaseName(root) + "View";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFormClassName(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetBaseName(root) + "Form";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetBaseName(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sourceName = root.gameObject.name.Replace("(Clone)", string.Empty).Trim();
|
||||||
|
if (sourceName.EndsWith("Form", StringComparison.Ordinal) && sourceName.Length > 4)
|
||||||
|
{
|
||||||
|
sourceName = sourceName.Substring(0, sourceName.Length - 4);
|
||||||
|
}
|
||||||
|
else if (sourceName.EndsWith("UI", StringComparison.Ordinal) && sourceName.Length > 2)
|
||||||
|
{
|
||||||
|
sourceName = sourceName.Substring(0, sourceName.Length - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
string identifier = ToPascalIdentifier(sourceName);
|
||||||
|
if (string.IsNullOrEmpty(identifier))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The UI root name cannot be converted to a C# class name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsValidIdentifier(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value) || CSharpKeywords.Contains(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!char.IsLetter(value[0]) && value[0] != '_')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
if (!char.IsLetterOrDigit(value[i]) && value[i] != '_')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
private static void InitializePendingGeneration()
|
||||||
|
{
|
||||||
|
QueuePendingGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DidReloadScripts]
|
||||||
|
private static void OnScriptsReloaded()
|
||||||
|
{
|
||||||
|
QueuePendingGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void QueuePendingGeneration()
|
||||||
|
{
|
||||||
|
if (finalizeQueued || string.IsNullOrEmpty(SessionState.GetString(PendingGenerationKey, string.Empty)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalizeQueued = true;
|
||||||
|
EditorApplication.delayCall += RunQueuedPendingGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunQueuedPendingGeneration()
|
||||||
|
{
|
||||||
|
finalizeQueued = false;
|
||||||
|
FinalizePendingGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FinalizePendingGeneration()
|
||||||
|
{
|
||||||
|
string json = SessionState.GetString(PendingGenerationKey, string.Empty);
|
||||||
|
if (string.IsNullOrEmpty(json))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingGenerationBatch batch = JsonUtility.FromJson<PendingGenerationBatch>(json);
|
||||||
|
if (batch == null || batch.entries == null || batch.entries.Count == 0)
|
||||||
|
{
|
||||||
|
SessionState.EraseString(PendingGenerationKey);
|
||||||
|
Debug.LogError("The pending UI View generation data is invalid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolvedViewTypes = new List<Type>(batch.entries.Count);
|
||||||
|
var resolvedFormTypes = new List<Type>(batch.entries.Count);
|
||||||
|
for (int i = 0; i < batch.entries.Count; i++)
|
||||||
|
{
|
||||||
|
PendingGeneration pending = batch.entries[i];
|
||||||
|
Type viewType = FindGeneratedType(pending.viewClassFullName);
|
||||||
|
if (viewType == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Generated UI view type was not found after compilation: " + pending.viewClassFullName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type formType = FindGeneratedType(pending.formClassFullName);
|
||||||
|
if (formType == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Generated UI form type was not found after compilation: " + pending.formClassFullName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedViewTypes.Add(viewType);
|
||||||
|
resolvedFormTypes.Add(formType);
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionState.EraseString(PendingGenerationKey);
|
||||||
|
UnityEngine.Object primaryTarget = null;
|
||||||
|
for (int i = 0; i < batch.entries.Count; i++)
|
||||||
|
{
|
||||||
|
PendingGeneration pending = batch.entries[i];
|
||||||
|
Type viewType = resolvedViewTypes[i];
|
||||||
|
Type formType = resolvedFormTypes[i];
|
||||||
|
if (!string.IsNullOrEmpty(pending.prefabAssetPath))
|
||||||
|
{
|
||||||
|
primaryTarget = ApplyToPrefab(pending, viewType, formType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot root = ResolveSceneRoot(pending);
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Could not resolve the UI root after compilation. The scripts were generated at " +
|
||||||
|
pending.viewScriptPath + " and " + pending.formScriptPath + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyGeneratedComponents(root, viewType, formType, true);
|
||||||
|
EditorSceneManager.MarkSceneDirty(root.gameObject.scene);
|
||||||
|
primaryTarget = root.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryTarget != null)
|
||||||
|
{
|
||||||
|
Selection.activeObject = primaryTarget;
|
||||||
|
EditorGUIUtility.PingObject(primaryTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Attached and populated " + batch.entries.Count + " generated UI View/Form pair(s).");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<UISerializationRoot> CollectGenerationRoots(UISerializationRoot selectedRoot)
|
||||||
|
{
|
||||||
|
bool persistentContext = EditorUtility.IsPersistent(selectedRoot);
|
||||||
|
UISerializationRoot generationRoot = GetCanonicalRoot(selectedRoot, persistentContext);
|
||||||
|
return new List<UISerializationRoot> { generationRoot };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UISerializationRoot GetCanonicalRoot(UISerializationRoot root, bool persistentContext)
|
||||||
|
{
|
||||||
|
if (!persistentContext || root == null)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot source = PrefabUtility.GetCorrespondingObjectFromSource(root);
|
||||||
|
return source != null ? source : root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<GenerationUnit> BuildGenerationUnits(List<UISerializationRoot> roots)
|
||||||
|
{
|
||||||
|
var units = new List<GenerationUnit>(roots.Count);
|
||||||
|
var classOwners = new Dictionary<string, UISerializationRoot>(StringComparer.Ordinal);
|
||||||
|
for (int i = 0; i < roots.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = roots[i];
|
||||||
|
root.RefreshItems();
|
||||||
|
string viewClassName = GetViewClassName(root);
|
||||||
|
string formClassName = GetFormClassName(root);
|
||||||
|
string baseName = GetBaseName(root);
|
||||||
|
UISerializationRoot existingOwner;
|
||||||
|
if (classOwners.TryGetValue(viewClassName, out existingOwner) && existingOwner != root)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Multiple serialization roots generate the same View class '" + viewClassName +
|
||||||
|
"'. Rename one of the UI roots.");
|
||||||
|
}
|
||||||
|
|
||||||
|
classOwners[viewClassName] = root;
|
||||||
|
if (classOwners.TryGetValue(formClassName, out existingOwner) && existingOwner != root)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Multiple serialization roots generate the same Form class '" + formClassName +
|
||||||
|
"'. Rename one of the UI roots.");
|
||||||
|
}
|
||||||
|
|
||||||
|
classOwners[formClassName] = root;
|
||||||
|
|
||||||
|
List<Binding> bindings = CollectBindings(root);
|
||||||
|
string folderPath = GeneratedDirectory + "/" + baseName;
|
||||||
|
string viewScriptPath = folderPath + "/" + viewClassName + ".cs";
|
||||||
|
string formScriptPath = folderPath + "/" + formClassName + ".cs";
|
||||||
|
string viewSource = BuildViewSource(viewClassName, bindings);
|
||||||
|
string formSource = BuildFormSource(formClassName, viewClassName);
|
||||||
|
units.Add(new GenerationUnit
|
||||||
|
{
|
||||||
|
root = root,
|
||||||
|
viewClassName = viewClassName,
|
||||||
|
formClassName = formClassName,
|
||||||
|
viewScriptPath = viewScriptPath,
|
||||||
|
formScriptPath = formScriptPath,
|
||||||
|
viewSource = viewSource,
|
||||||
|
formSource = formSource,
|
||||||
|
viewChanged = !File.Exists(viewScriptPath) || File.ReadAllText(viewScriptPath) != viewSource,
|
||||||
|
formChanged = !File.Exists(formScriptPath) || File.ReadAllText(formScriptPath) != formSource
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Binding> CollectBindings(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
var bindings = new List<Binding>();
|
||||||
|
var names = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
for (int i = 0; i < root.SerializationItems.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationItem item = root.SerializationItems[i];
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.RefreshComponents();
|
||||||
|
for (int j = 0; j < item.ComponentReferences.Count; j++)
|
||||||
|
{
|
||||||
|
UISerializationItem.ComponentReference reference = item.ComponentReferences[j];
|
||||||
|
if (reference == null || !reference.GenerateReference)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component component = reference.Component;
|
||||||
|
string variableName = NormalizeAndValidateVariableName(reference.VariableName, item.transform, names);
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A selected UI reference is missing on " + GetHierarchyPath(item.transform) + ".");
|
||||||
|
}
|
||||||
|
if (component.GetType().Namespace != null && component.GetType().Namespace.StartsWith("UnityEditor", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Editor-only components cannot be referenced by a runtime View: " + component.GetType().FullName + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings.Add(new Binding
|
||||||
|
{
|
||||||
|
component = component,
|
||||||
|
variableName = variableName,
|
||||||
|
csharpTypeName = GetCSharpTypeName(component.GetType())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < root.NestedFormReferences.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot.NestedFormReference reference = root.NestedFormReferences[i];
|
||||||
|
if (reference == null || !reference.GenerateReference)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot nestedRoot = reference.Root;
|
||||||
|
if (nestedRoot == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A nested UI Form reference is missing below " + GetHierarchyPath(root.transform) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
UGuiForm nestedForm = FindNestedForm(nestedRoot, out string nestedFormError);
|
||||||
|
if (nestedForm == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(nestedFormError);
|
||||||
|
}
|
||||||
|
|
||||||
|
string variableName = NormalizeAndValidateVariableName(reference.VariableName, nestedRoot.transform, names);
|
||||||
|
bindings.Add(new Binding
|
||||||
|
{
|
||||||
|
component = nestedForm,
|
||||||
|
variableName = variableName,
|
||||||
|
csharpTypeName = GetCSharpTypeName(nestedForm.GetType())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UGuiForm FindNestedForm(UISerializationRoot nestedRoot, out string error)
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
if (nestedRoot == null)
|
||||||
|
{
|
||||||
|
error = "The nested UI root is missing.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGuiForm[] forms = nestedRoot.GetComponents<UGuiForm>();
|
||||||
|
if (forms.Length == 0)
|
||||||
|
{
|
||||||
|
error = "Nested UI root '" + GetHierarchyPath(nestedRoot.transform) +
|
||||||
|
"' does not have a UGuiForm component. Generate its View & Form scripts first.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forms.Length > 1)
|
||||||
|
{
|
||||||
|
error = "Nested UI root '" + GetHierarchyPath(nestedRoot.transform) +
|
||||||
|
"' has multiple UGuiForm components.";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return forms[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeAndValidateVariableName(string value, Transform owner, HashSet<string> names)
|
||||||
|
{
|
||||||
|
string variableName = value == null ? string.Empty : value.Trim();
|
||||||
|
if (!IsValidIdentifier(variableName))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Invalid C# field name '" + variableName + "' on " + GetHierarchyPath(owner) + ".");
|
||||||
|
}
|
||||||
|
if (!names.Add(variableName))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Duplicate UI field name '" + variableName + "'.");
|
||||||
|
}
|
||||||
|
if (typeof(MonoBehaviour).GetMember(variableName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("UI field name '" + variableName + "' conflicts with a MonoBehaviour member.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return variableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildViewSource(string className, List<Binding> bindings)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(512 + bindings.Count * 96);
|
||||||
|
builder.Append("// <auto-generated />\n");
|
||||||
|
builder.Append("using UnityEngine;\n\n");
|
||||||
|
builder.Append("namespace ").Append(GeneratedNamespace).Append("\n{\n");
|
||||||
|
builder.Append(" [DisallowMultipleComponent]\n");
|
||||||
|
builder.Append(" public partial class ").Append(className).Append(" : MonoBehaviour\n {\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < bindings.Count; i++)
|
||||||
|
{
|
||||||
|
Binding binding = bindings[i];
|
||||||
|
builder.Append(" [SerializeField] public ")
|
||||||
|
.Append(binding.csharpTypeName)
|
||||||
|
.Append(' ')
|
||||||
|
.Append(binding.variableName)
|
||||||
|
.Append(";\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(" }\n}\n");
|
||||||
|
return NormalizeToLf(builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildFormSource(string className, string viewClassName)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(256);
|
||||||
|
builder.Append("// <auto-generated />\n");
|
||||||
|
builder.Append("using UnityEngine;\n\n");
|
||||||
|
builder.Append("namespace ").Append(GeneratedNamespace).Append("\n{\n");
|
||||||
|
builder.Append(" [DisallowMultipleComponent]\n");
|
||||||
|
builder.Append(" public partial class ").Append(className).Append(" : UGuiForm\n {\n");
|
||||||
|
builder.Append(" [SerializeField] private ").Append(viewClassName).Append(" _view = null;\n\n");
|
||||||
|
builder.Append(" public ").Append(viewClassName).Append(" View => _view;\n");
|
||||||
|
builder.Append(" }\n}\n");
|
||||||
|
return NormalizeToLf(builder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeToLf(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.Replace("\r\n", "\n").Replace('\r', '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PendingGeneration CreatePendingGeneration(GenerationUnit unit)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = unit.root;
|
||||||
|
bool isPrefabAsset = EditorUtility.IsPersistent(root) && PrefabUtility.IsPartOfPrefabAsset(root);
|
||||||
|
string prefabAssetPath = isPrefabAsset ? AssetDatabase.GetAssetPath(root.gameObject) : string.Empty;
|
||||||
|
string relativePath = string.Empty;
|
||||||
|
if (isPrefabAsset)
|
||||||
|
{
|
||||||
|
GameObject prefabRoot = AssetDatabase.LoadAssetAtPath<GameObject>(prefabAssetPath);
|
||||||
|
if (prefabRoot == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Could not load Prefab asset: " + prefabAssetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
relativePath = GetRelativePath(prefabRoot.transform, root.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalObjectId globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(root);
|
||||||
|
return new PendingGeneration
|
||||||
|
{
|
||||||
|
rootGlobalId = globalObjectId.ToString(),
|
||||||
|
prefabAssetPath = prefabAssetPath,
|
||||||
|
prefabRootRelativePath = relativePath,
|
||||||
|
scenePath = root.gameObject.scene.path,
|
||||||
|
hierarchyPath = GetHierarchyPath(root.transform),
|
||||||
|
viewClassFullName = GeneratedNamespace + "." + unit.viewClassName,
|
||||||
|
formClassFullName = GeneratedNamespace + "." + unit.formClassName,
|
||||||
|
viewScriptPath = unit.viewScriptPath,
|
||||||
|
formScriptPath = unit.formScriptPath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UnityEngine.Object ApplyToPrefab(PendingGeneration pending, Type viewType, Type formType)
|
||||||
|
{
|
||||||
|
GameObject contents = PrefabUtility.LoadPrefabContents(pending.prefabAssetPath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Transform rootTransform = string.IsNullOrEmpty(pending.prefabRootRelativePath)
|
||||||
|
? contents.transform
|
||||||
|
: contents.transform.Find(pending.prefabRootRelativePath);
|
||||||
|
UISerializationRoot root = rootTransform == null ? null : rootTransform.GetComponent<UISerializationRoot>();
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The Prefab no longer contains the expected UISerializationRoot at '" +
|
||||||
|
pending.prefabRootRelativePath + "': " + pending.prefabAssetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyGeneratedComponents(root, viewType, formType, false);
|
||||||
|
PrefabUtility.SaveAsPrefabAsset(contents, pending.prefabAssetPath);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
PrefabUtility.UnloadPrefabContents(contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.ImportAsset(pending.prefabAssetPath, ImportAssetOptions.ForceUpdate);
|
||||||
|
return AssetDatabase.LoadAssetAtPath<GameObject>(pending.prefabAssetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ApplyGeneratedComponentsToRoot(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.RefreshItems();
|
||||||
|
for (int i = 0; i < root.NestedFormReferences.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot nestedRoot = root.NestedFormReferences[i].Root;
|
||||||
|
if (nestedRoot != null)
|
||||||
|
{
|
||||||
|
ApplyGeneratedComponentsToRoot(nestedRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type viewType = FindGeneratedType(GeneratedNamespace + "." + GetViewClassName(root));
|
||||||
|
Type formType = FindGeneratedType(GeneratedNamespace + "." + GetFormClassName(root));
|
||||||
|
if (viewType == null || formType == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyGeneratedComponents(root, viewType, formType, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyGeneratedComponents(UISerializationRoot root, Type viewType, Type formType,
|
||||||
|
bool useUndo)
|
||||||
|
{
|
||||||
|
Component view = ApplyViewReferences(root, viewType, useUndo);
|
||||||
|
Component form = root.GetComponent(formType);
|
||||||
|
if (form == null)
|
||||||
|
{
|
||||||
|
form = useUndo ? Undo.AddComponent(root.gameObject, formType) : root.gameObject.AddComponent(formType);
|
||||||
|
}
|
||||||
|
else if (useUndo)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(form, "Populate UI form view reference");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializedForm = new SerializedObject(form);
|
||||||
|
serializedForm.UpdateIfRequiredOrScript();
|
||||||
|
SerializedProperty viewProperty = serializedForm.FindProperty("_view");
|
||||||
|
if (viewProperty == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Generated field was not found: _view on " + formType.FullName + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
viewProperty.objectReferenceValue = view;
|
||||||
|
serializedForm.ApplyModifiedProperties();
|
||||||
|
EditorUtility.SetDirty(form);
|
||||||
|
if (useUndo && PrefabUtility.IsPartOfPrefabInstance(form))
|
||||||
|
{
|
||||||
|
PrefabUtility.RecordPrefabInstancePropertyModifications(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Component ApplyViewReferences(UISerializationRoot root, Type viewType, bool useUndo)
|
||||||
|
{
|
||||||
|
root.RefreshItems();
|
||||||
|
List<Binding> bindings = CollectBindings(root);
|
||||||
|
Component view = root.GetComponent(viewType);
|
||||||
|
if (view == null)
|
||||||
|
{
|
||||||
|
view = useUndo ? Undo.AddComponent(root.gameObject, viewType) : root.gameObject.AddComponent(viewType);
|
||||||
|
}
|
||||||
|
else if (useUndo)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(view, "Populate UI view references");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializedView = new SerializedObject(view);
|
||||||
|
serializedView.UpdateIfRequiredOrScript();
|
||||||
|
for (int i = 0; i < bindings.Count; i++)
|
||||||
|
{
|
||||||
|
Binding binding = bindings[i];
|
||||||
|
SerializedProperty property = serializedView.FindProperty(binding.variableName);
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Generated field was not found: " + binding.variableName + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
property.objectReferenceValue = binding.component;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedView.ApplyModifiedProperties();
|
||||||
|
EditorUtility.SetDirty(view);
|
||||||
|
if (useUndo && PrefabUtility.IsPartOfPrefabInstance(view))
|
||||||
|
{
|
||||||
|
PrefabUtility.RecordPrefabInstancePropertyModifications(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type FindGeneratedType(string fullName)
|
||||||
|
{
|
||||||
|
return TypeCache.GetTypesDerivedFrom<MonoBehaviour>()
|
||||||
|
.FirstOrDefault(type => type.FullName == fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UISerializationRoot ResolveSceneRoot(PendingGeneration pending)
|
||||||
|
{
|
||||||
|
GlobalObjectId parsed;
|
||||||
|
if (GlobalObjectId.TryParse(pending.rootGlobalId, out parsed))
|
||||||
|
{
|
||||||
|
UnityEngine.Object resolved = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(parsed);
|
||||||
|
UISerializationRoot root = resolved as UISerializationRoot;
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject resolvedObject = resolved as GameObject;
|
||||||
|
if (resolvedObject != null)
|
||||||
|
{
|
||||||
|
root = resolvedObject.GetComponent<UISerializationRoot>();
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot[] loadedRoots = Resources.FindObjectsOfTypeAll<UISerializationRoot>();
|
||||||
|
for (int i = 0; i < loadedRoots.Length; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = loadedRoots[i];
|
||||||
|
if (root != null && !EditorUtility.IsPersistent(root) && root.gameObject.scene.path == pending.scenePath &&
|
||||||
|
GetHierarchyPath(root.transform) == pending.hierarchyPath)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UISerializationRoot GetSelectedRoot()
|
||||||
|
{
|
||||||
|
GameObject selected = Selection.activeGameObject;
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
selected = Selection.activeObject as GameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected == null ? null : selected.GetComponent<UISerializationRoot>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCSharpTypeName(Type type)
|
||||||
|
{
|
||||||
|
if (type.IsArray)
|
||||||
|
{
|
||||||
|
return GetCSharpTypeName(type.GetElementType()) + "[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
string definition = type.GetGenericTypeDefinition().FullName ?? type.Name;
|
||||||
|
int arityMarker = definition.IndexOf('`');
|
||||||
|
if (arityMarker >= 0)
|
||||||
|
{
|
||||||
|
definition = definition.Substring(0, arityMarker);
|
||||||
|
}
|
||||||
|
|
||||||
|
string arguments = string.Join(", ", type.GetGenericArguments().Select(GetCSharpTypeName).ToArray());
|
||||||
|
return "global::" + definition.Replace('+', '.') + "<" + arguments + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "global::" + (type.FullName ?? type.Name).Replace('+', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SaveDirtyRootAssets(List<GenerationUnit> units)
|
||||||
|
{
|
||||||
|
var savedPaths = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
for (int i = 0; i < units.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = units[i].root;
|
||||||
|
if (root == null || !EditorUtility.IsPersistent(root))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string assetPath = AssetDatabase.GetAssetPath(root);
|
||||||
|
if (string.IsNullOrEmpty(assetPath) || !savedPaths.Add(assetPath))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.SaveAssetIfDirty(AssetDatabase.GUIDFromAssetPath(assetPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToPascalIdentifier(string value)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(value.Length + 8);
|
||||||
|
bool capitalizeNext = true;
|
||||||
|
for (int i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
char current = value[i];
|
||||||
|
if (!char.IsLetterOrDigit(current) && current != '_')
|
||||||
|
{
|
||||||
|
capitalizeNext = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.Length == 0 && char.IsDigit(current))
|
||||||
|
{
|
||||||
|
builder.Append('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(capitalizeNext ? char.ToUpperInvariant(current) : current);
|
||||||
|
capitalizeNext = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRelativePath(Transform root, Transform target)
|
||||||
|
{
|
||||||
|
if (root == target)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = new Stack<string>();
|
||||||
|
Transform current = target;
|
||||||
|
while (current != null && current != root)
|
||||||
|
{
|
||||||
|
parts.Push(current.name);
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != root)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The serialization root is not inside the expected Prefab asset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join("/", parts.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetHierarchyPath(Transform target)
|
||||||
|
{
|
||||||
|
string path = target.name;
|
||||||
|
Transform current = target.parent;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
path = current.name + "/" + path;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureFolder(string folder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(folder))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder = folder.Replace('\\', '/');
|
||||||
|
string[] parts = folder.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string current = parts[0];
|
||||||
|
for (int i = 1; i < parts.Length; i++)
|
||||||
|
{
|
||||||
|
string next = current + "/" + parts[i];
|
||||||
|
if (!AssetDatabase.IsValidFolder(next))
|
||||||
|
{
|
||||||
|
AssetDatabase.CreateFolder(current, parts[i]);
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48c1b6017a08547aa94ad17c9611d7b2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using SepCore.UI;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.Editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(UISerializationItem))]
|
||||||
|
internal sealed class UISerializationItemEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
var item = (UISerializationItem)target;
|
||||||
|
item.RefreshComponents();
|
||||||
|
serializedObject.UpdateIfRequiredOrScript();
|
||||||
|
|
||||||
|
SerializedProperty references = serializedObject.FindProperty("componentReferences");
|
||||||
|
EditorGUILayout.LabelField("Components", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
var selectedNames = new HashSet<string>();
|
||||||
|
for (int i = 0; i < references.arraySize; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty entry = references.GetArrayElementAtIndex(i);
|
||||||
|
SerializedProperty component = entry.FindPropertyRelative("component");
|
||||||
|
SerializedProperty generate = entry.FindPropertyRelative("generateReference");
|
||||||
|
SerializedProperty variableName = entry.FindPropertyRelative("variableName");
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
generate.boolValue = EditorGUILayout.Toggle(generate.boolValue, GUILayout.Width(18f));
|
||||||
|
|
||||||
|
using (new EditorGUI.DisabledScope(true))
|
||||||
|
{
|
||||||
|
EditorGUILayout.ObjectField(component.objectReferenceValue, typeof(Component), true);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
if (generate.boolValue)
|
||||||
|
{
|
||||||
|
variableName.stringValue = EditorGUILayout.TextField("Variable Name", variableName.stringValue);
|
||||||
|
string normalized = variableName.stringValue == null ? string.Empty : variableName.stringValue.Trim();
|
||||||
|
if (!UIAssetsTools.IsValidIdentifier(normalized))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Enter a valid C# field name.", MessageType.Error);
|
||||||
|
}
|
||||||
|
else if (!selectedNames.Add(normalized))
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("This field name is already selected on this item.", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Refresh Components"))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(item, "Refresh UI serialization components");
|
||||||
|
item.RefreshComponents();
|
||||||
|
serializedObject.UpdateIfRequiredOrScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5f17238526324f3d98afa8e8c999814
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using SepCore.UI;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.Editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(UISerializationRoot))]
|
||||||
|
internal sealed class UISerializationRootEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
var root = (UISerializationRoot)target;
|
||||||
|
root.RefreshItems();
|
||||||
|
serializedObject.UpdateIfRequiredOrScript();
|
||||||
|
|
||||||
|
int referenceCount = 0;
|
||||||
|
bool hasValidationError = false;
|
||||||
|
var selectedNames = new HashSet<string>();
|
||||||
|
for (int i = 0; i < root.SerializationItems.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationItem item = root.SerializationItems[i];
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < item.ComponentReferences.Count; j++)
|
||||||
|
{
|
||||||
|
UISerializationItem.ComponentReference reference = item.ComponentReferences[j];
|
||||||
|
if (reference.GenerateReference)
|
||||||
|
{
|
||||||
|
referenceCount++;
|
||||||
|
string variableName = reference.VariableName == null ? string.Empty : reference.VariableName.Trim();
|
||||||
|
if (!UIAssetsTools.IsValidIdentifier(variableName) || !selectedNames.Add(variableName))
|
||||||
|
{
|
||||||
|
hasValidationError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedProperty nestedForms = serializedObject.FindProperty("nestedFormReferences");
|
||||||
|
for (int i = 0; i < nestedForms.arraySize; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty entry = nestedForms.GetArrayElementAtIndex(i);
|
||||||
|
if (entry.FindPropertyRelative("generateReference").boolValue)
|
||||||
|
{
|
||||||
|
referenceCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("View Class", UIAssetsTools.GetViewClassName(root));
|
||||||
|
EditorGUILayout.LabelField("Form Class", UIAssetsTools.GetFormClassName(root));
|
||||||
|
EditorGUILayout.LabelField("Owned Items", root.SerializationItems.Count.ToString());
|
||||||
|
EditorGUILayout.LabelField("Nested Forms", nestedForms.arraySize.ToString());
|
||||||
|
EditorGUILayout.LabelField("Generated References", referenceCount.ToString());
|
||||||
|
EditorGUILayout.Space(4f);
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Owned Items", EditorStyles.boldLabel);
|
||||||
|
for (int i = 0; i < root.SerializationItems.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationItem item = root.SerializationItems[i];
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||||||
|
EditorGUILayout.LabelField(GetRelativePath(root.transform, item.transform));
|
||||||
|
if (GUILayout.Button("Select", GUILayout.Width(56f)))
|
||||||
|
{
|
||||||
|
Selection.activeGameObject = item.gameObject;
|
||||||
|
EditorGUIUtility.PingObject(item.gameObject);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(4f);
|
||||||
|
EditorGUILayout.LabelField("Nested Forms", EditorStyles.boldLabel);
|
||||||
|
if (nestedForms.arraySize == 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Nested UISerializationRoot components will appear here as child Form references and form serialization boundaries.", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nestedForms.arraySize; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty entry = nestedForms.GetArrayElementAtIndex(i);
|
||||||
|
SerializedProperty nestedRootProperty = entry.FindPropertyRelative("root");
|
||||||
|
SerializedProperty generate = entry.FindPropertyRelative("generateReference");
|
||||||
|
SerializedProperty variableName = entry.FindPropertyRelative("variableName");
|
||||||
|
var nestedRoot = nestedRootProperty.objectReferenceValue as UISerializationRoot;
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
generate.boolValue = EditorGUILayout.Toggle(generate.boolValue, GUILayout.Width(18f));
|
||||||
|
using (new EditorGUI.DisabledScope(true))
|
||||||
|
{
|
||||||
|
EditorGUILayout.ObjectField(nestedRoot, typeof(UISerializationRoot), true);
|
||||||
|
}
|
||||||
|
if (nestedRoot != null && GUILayout.Button("Select", GUILayout.Width(56f)))
|
||||||
|
{
|
||||||
|
Selection.activeGameObject = nestedRoot.gameObject;
|
||||||
|
EditorGUIUtility.PingObject(nestedRoot.gameObject);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
if (nestedRoot != null)
|
||||||
|
{
|
||||||
|
UGuiForm nestedForm = UIAssetsTools.FindNestedForm(nestedRoot, out string nestedFormError);
|
||||||
|
if (nestedForm != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Form Class", nestedForm.GetType().FullName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox(nestedFormError, MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate.boolValue)
|
||||||
|
{
|
||||||
|
variableName.stringValue = EditorGUILayout.TextField("Variable Name", variableName.stringValue);
|
||||||
|
string normalized = variableName.stringValue == null ? string.Empty : variableName.stringValue.Trim();
|
||||||
|
if (!UIAssetsTools.IsValidIdentifier(normalized))
|
||||||
|
{
|
||||||
|
hasValidationError = true;
|
||||||
|
EditorGUILayout.HelpBox("Enter a valid C# field name.", MessageType.Error);
|
||||||
|
}
|
||||||
|
else if (!selectedNames.Add(normalized))
|
||||||
|
{
|
||||||
|
hasValidationError = true;
|
||||||
|
EditorGUILayout.HelpBox("This field name conflicts with another generated reference.", MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasValidationError)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Fix invalid or duplicate generated field names before creating View scripts.", MessageType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
EditorGUILayout.Space(4f);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Refresh Items"))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(root, "Refresh UI serialization items");
|
||||||
|
root.RefreshItems();
|
||||||
|
}
|
||||||
|
using (new EditorGUI.DisabledScope(hasValidationError))
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("Create View & Form Scripts"))
|
||||||
|
{
|
||||||
|
UIAssetsTools.GenerateWithDialog(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRelativePath(Transform root, Transform target)
|
||||||
|
{
|
||||||
|
if (target == root)
|
||||||
|
{
|
||||||
|
return root.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = target.name;
|
||||||
|
Transform current = target.parent;
|
||||||
|
while (current != null && current != root)
|
||||||
|
{
|
||||||
|
path = current.name + "/" + path;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[InitializeOnLoad]
|
||||||
|
internal static class UISerializationRootWatcher
|
||||||
|
{
|
||||||
|
private static bool refreshQueued;
|
||||||
|
|
||||||
|
static UISerializationRootWatcher()
|
||||||
|
{
|
||||||
|
EditorApplication.hierarchyChanged += QueueRefresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void QueueRefresh()
|
||||||
|
{
|
||||||
|
if (refreshQueued || EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshQueued = true;
|
||||||
|
EditorApplication.delayCall += RefreshLoadedRoots;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RefreshLoadedRoots()
|
||||||
|
{
|
||||||
|
refreshQueued = false;
|
||||||
|
UISerializationRoot[] roots = Resources.FindObjectsOfTypeAll<UISerializationRoot>();
|
||||||
|
for (int i = 0; i < roots.Length; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = roots[i];
|
||||||
|
if (root == null || EditorUtility.IsPersistent(root) || !root.gameObject.scene.IsValid() || !root.gameObject.scene.isLoaded)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.RefreshItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c6a8249053ff4cf0a368b8a25b9946b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user